Annotation of embedaddon/libpdel/io/ssl_fp.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include <sys/param.h>
! 42: #include <sys/syslog.h>
! 43:
! 44: #include <stdio.h>
! 45: #include <stdlib.h>
! 46: #include <stdarg.h>
! 47: #include <string.h>
! 48: #include <unistd.h>
! 49: #include <fcntl.h>
! 50: #include <errno.h>
! 51: #include <poll.h>
! 52: #include <pthread.h>
! 53:
! 54: #include <openssl/ssl.h>
! 55: #include <openssl/err.h>
! 56:
! 57: #include "structs/structs.h"
! 58: #include "structs/type/array.h"
! 59:
! 60: #include "util/typed_mem.h"
! 61: #include "io/ssl_fp.h"
! 62:
! 63: struct ssl_info {
! 64: int fd;
! 65: SSL *ssl;
! 66: SSL_CTX *ssl_ctx;
! 67: int error;
! 68: u_char server;
! 69: ssl_logger_t *logger;
! 70: void *logarg;
! 71: const char *mtype;
! 72: u_int timeout;
! 73: };
! 74:
! 75: /*
! 76: * Internal functions
! 77: */
! 78: static int ssl_read(void *cookie, char *buf, int len);
! 79: static int ssl_write(void *cookie, const char *buf, int len);
! 80: static int ssl_close(void *cookie);
! 81: static int ssl_check(struct ssl_info *s);
! 82: static int ssl_err(struct ssl_info *s, int ret);
! 83:
! 84: static ssl_logger_t null_logger;
! 85:
! 86: /*
! 87: * Create a FILE * from an SSL connection.
! 88: */
! 89: FILE *
! 90: ssl_fdopen(SSL_CTX *ssl_ctx, int fd, int server, const char *mtype,
! 91: ssl_logger_t *logger, void *logarg, u_int timeout)
! 92: {
! 93: struct ssl_info *s;
! 94: int flags;
! 95: FILE *fp;
! 96:
! 97: /* Create SSL info */
! 98: if ((s = MALLOC(mtype, sizeof(*s))) == NULL)
! 99: return (NULL);
! 100: memset(s, 0, sizeof(*s));
! 101: s->fd = fd;
! 102: s->ssl_ctx = ssl_ctx;
! 103: s->mtype = mtype;
! 104: s->server = !!server;
! 105: s->logger = (logger != NULL) ? logger : null_logger;
! 106: s->logarg = logarg;
! 107: s->timeout = timeout;
! 108:
! 109: /* Set fd I/O to non-blocking */
! 110: if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
! 111: FREE(mtype, s);
! 112: return (NULL);
! 113: }
! 114: #if 0
! 115: if ((flags & O_NONBLOCK) == 0
! 116: && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
! 117: FREE(mtype, s);
! 118: return (NULL);
! 119: }
! 120: #endif
! 121:
! 122: /* Create FILE * stream */
! 123: if ((fp = funopen(s, ssl_read, ssl_write, NULL, ssl_close)) == NULL) {
! 124: (void)fcntl(fd, F_SETFL, flags);
! 125: FREE(mtype, s);
! 126: return (NULL);
! 127: }
! 128:
! 129: /* Done */
! 130: return (fp);
! 131: }
! 132:
! 133: /*
! 134: * Read from an SSL connection.
! 135: */
! 136: static int
! 137: ssl_read(void *cookie, char *buf, int len)
! 138: {
! 139: struct ssl_info *const s = cookie;
! 140: int ret;
! 141:
! 142: /* Check connection */
! 143: if (ssl_check(s) == -1)
! 144: return (-1);
! 145:
! 146: tryread:
! 147: /* Try to read some more data */
! 148: if ((ret = SSL_read(s->ssl, buf, len)) <= 0) {
! 149: if (ssl_err(s, ret) == -1)
! 150: return (-1);
! 151: goto tryread;
! 152: }
! 153: return (ret);
! 154: }
! 155:
! 156: /*
! 157: * Write to an SSL connection.
! 158: */
! 159: static int
! 160: ssl_write(void *cookie, const char *buf, int len)
! 161: {
! 162: struct ssl_info *const s = cookie;
! 163: int ret;
! 164:
! 165: /* Check connection */
! 166: if (ssl_check(s) == -1)
! 167: return (-1);
! 168:
! 169: trywrite:
! 170: /* Try to write some more data */
! 171: if ((ret = SSL_write(s->ssl, buf, len)) <= 0) {
! 172: if (ssl_err(s, ret) == -1)
! 173: return (-1);
! 174: goto trywrite;
! 175: }
! 176: return (ret);
! 177: }
! 178:
! 179: /*
! 180: * Close an SSL connection.
! 181: */
! 182: static int
! 183: ssl_close(void *cookie)
! 184: {
! 185: struct ssl_info *const s = cookie;
! 186: int ret;
! 187:
! 188: while ((ret = SSL_shutdown(s->ssl)) <= 0) {
! 189: if (ret == -1 && ssl_err(s, ret) == -1) {
! 190: (void)close(s->fd);
! 191: return (-1);
! 192: }
! 193: }
! 194: (void)close(s->fd);
! 195: SSL_free(s->ssl);
! 196: FREE(s->mtype, s);
! 197: return (0);
! 198: }
! 199:
! 200: /*
! 201: * Check SSL connection context.
! 202: */
! 203: static int
! 204: ssl_check(struct ssl_info *s)
! 205: {
! 206: int ret;
! 207:
! 208: /* Check for previous error */
! 209: if (s->error != 0)
! 210: goto fail;
! 211:
! 212: /* Already set up SSL? */
! 213: if (s->ssl != NULL)
! 214: return (0);
! 215:
! 216: /* Create ssl connection context */
! 217: if ((s->ssl = SSL_new(s->ssl_ctx)) == NULL) {
! 218: ssl_log(s->logger, s->logarg);
! 219: s->error = ECONNABORTED;
! 220: goto fail;
! 221: }
! 222: SSL_set_fd(s->ssl, s->fd);
! 223:
! 224: /* Do initial SSL handshake */
! 225: if (s->server) {
! 226: while ((ret = SSL_accept(s->ssl)) <= 0) {
! 227: if (ssl_err(s, ret) == -1)
! 228: goto fail;
! 229: }
! 230: } else {
! 231: while ((ret = SSL_connect(s->ssl)) <= 0) {
! 232: if (ssl_err(s, ret) == -1)
! 233: goto fail;
! 234: }
! 235: }
! 236:
! 237: /* Ready */
! 238: return (0);
! 239:
! 240: fail:
! 241: /* Failed */
! 242: errno = s->error;
! 243: return (-1);
! 244: }
! 245:
! 246: /*
! 247: * Handle an error return from an SSL I/O operation.
! 248: *
! 249: * It might be an error, or it might just be that more raw data
! 250: * needs to be read or written. Return -1 in the former case,
! 251: * otherwise return zero and wait for the readable or writable
! 252: * event as appropriate.
! 253: */
! 254: static int
! 255: ssl_err(struct ssl_info *s, int ret)
! 256: {
! 257: int ret2;
! 258:
! 259: ret2 = SSL_get_error(s->ssl, ret);
! 260: switch (ret2) {
! 261: case SSL_ERROR_WANT_READ:
! 262: case SSL_ERROR_WANT_WRITE:
! 263: {
! 264: struct pollfd pfd;
! 265: int timeout;
! 266: int nfds;
! 267:
! 268: /* Set up read/write poll(2) event */
! 269: memset(&pfd, 0, sizeof(pfd));
! 270: pfd.fd = s->fd;
! 271: pfd.events = (ret2 == SSL_ERROR_WANT_READ) ?
! 272: POLLRDNORM : POLLWRNORM;
! 273:
! 274: /* Set up timeout */
! 275: timeout = INFTIM;
! 276: if (s->timeout != 0)
! 277: timeout = s->timeout * 1000;
! 278:
! 279: /* Wait for readability or writability */
! 280: if ((nfds = poll(&pfd, 1, timeout)) == -1) {
! 281: s->error = errno;
! 282: (*s->logger)(s->logarg, LOG_ERR,
! 283: "%s: %s", "poll", strerror(errno));
! 284: goto fail;
! 285: }
! 286:
! 287: /* Check for timeout */
! 288: if (nfds == 0) {
! 289: s->error = ETIMEDOUT;
! 290: goto fail;
! 291: }
! 292:
! 293: /* Done */
! 294: return (0);
! 295: }
! 296: case SSL_ERROR_NONE:
! 297: return (0);
! 298:
! 299: case SSL_ERROR_SYSCALL:
! 300: ssl_log(s->logger, s->logarg);
! 301: if (ret != 0) {
! 302: s->error = errno;
! 303: (*s->logger)(s->logarg, LOG_ERR,
! 304: "SSL system error: %s", strerror(errno));
! 305: goto fail;
! 306: }
! 307: /* fall through */
! 308:
! 309: case SSL_ERROR_ZERO_RETURN:
! 310: (*s->logger)(s->logarg, LOG_ERR, "SSL connection closed");
! 311: s->error = ECONNRESET;
! 312: goto fail;
! 313:
! 314: case SSL_ERROR_SSL:
! 315: ssl_log(s->logger, s->logarg);
! 316: s->error = EIO;
! 317: goto fail;
! 318:
! 319: case SSL_ERROR_WANT_X509_LOOKUP: /* should not happen */
! 320: default:
! 321: ssl_log(s->logger, s->logarg);
! 322: (*s->logger)(s->logarg, LOG_ERR,
! 323: "unknown return %d from SSL_get_error", ret2);
! 324: s->error = ECONNABORTED;
! 325: goto fail;
! 326: }
! 327:
! 328: fail:
! 329: /* Got an error */
! 330: errno = s->error;
! 331: return (-1);
! 332: }
! 333:
! 334: /*
! 335: * Log an SSL error.
! 336: */
! 337: void
! 338: ssl_log(ssl_logger_t *logger, void *logarg)
! 339: {
! 340: char buf[200];
! 341: const char *file;
! 342: const char *str;
! 343: const char *t;
! 344: int line, flags;
! 345: u_long e;
! 346:
! 347: while ((e = ERR_get_error_line_data(&file, &line, &str, &flags)) != 0) {
! 348: if (logger == NULL)
! 349: continue;
! 350: #if 0
! 351: (*logger)(logarg, LOG_ERR, "SSL: %s:%s:%d:%s\n",
! 352: ERR_error_string(e, buf), file, line,
! 353: (flags & ERR_TXT_STRING) ? str : "");
! 354: #else
! 355: strlcpy(buf, "SSL: ", sizeof(buf));
! 356: #if 0
! 357: /* Add library */
! 358: if ((t = ERR_lib_error_string(e)) != NULL) {
! 359: strlcat(buf, t, sizeof(buf));
! 360: strlcat(buf, ": ", sizeof(buf));
! 361: } else {
! 362: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 363: "lib=%u: ", ERR_GET_LIB(e));
! 364: }
! 365: #endif
! 366:
! 367: /* Add function */
! 368: if ((t = ERR_func_error_string(e)) != NULL) {
! 369: strlcat(buf, t, sizeof(buf));
! 370: strlcat(buf, ": ", sizeof(buf));
! 371: } else {
! 372: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 373: "func=%u: ", ERR_GET_FUNC(e));
! 374: }
! 375:
! 376: /* Add reason */
! 377: if ((t = ERR_reason_error_string(e)) != NULL) {
! 378: strlcat(buf, t, sizeof(buf));
! 379: } else {
! 380: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 381: "reason=%u", ERR_GET_REASON(e));
! 382: }
! 383:
! 384: /* Add error text, if any */
! 385: if ((flags & ERR_TXT_STRING) != 0) {
! 386: strlcat(buf, ": ", sizeof(buf));
! 387: strlcat(buf, str, sizeof(buf));
! 388: }
! 389: (*logger)(logarg, LOG_ERR, "%s", buf);
! 390: #endif
! 391: }
! 392: }
! 393:
! 394: static void
! 395: null_logger(void *arg, int sev, const char *fmt, ...)
! 396: {
! 397: return;
! 398: }
! 399:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>