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>