Annotation of embedaddon/axTLS/axtlswrap/axtlswrap.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2009, Steve Bennett
        !             3:  * 
        !             4:  * All rights reserved.
        !             5:  * 
        !             6:  * Redistribution and use in source and binary forms, with or without 
        !             7:  * modification, are permitted provided that the following conditions are met:
        !             8:  *
        !             9:  * * Redistributions of source code must retain the above copyright notice, 
        !            10:  *   this list of conditions and the following disclaimer.
        !            11:  * * Redistributions in binary form must reproduce the above copyright notice, 
        !            12:  *   this list of conditions and the following disclaimer in the documentation 
        !            13:  *   and/or other materials provided with the distribution.
        !            14:  * * Neither the name of the axTLS project nor the names of its contributors 
        !            15:  *   may be used to endorse or promote products derived from this software 
        !            16:  *   without specific prior written permission.
        !            17:  *
        !            18:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
        !            19:  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
        !            20:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
        !            21:  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
        !            22:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
        !            23:  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
        !            24:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
        !            25:  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
        !            26:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
        !            27:  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
        !            28:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            29:  */
        !            30: 
        !            31: /*
        !            32:  * sslwrap re-implemented with axTLS - a way to wrap an existing webserver
        !            33:  * with axTLS.
        !            34:  */
        !            35:  
        !            36: #include <stdio.h>
        !            37: #include <stdlib.h>
        !            38: #include <unistd.h>
        !            39: #include <syslog.h>
        !            40: #include <errno.h>
        !            41: #include <string.h>
        !            42: #include <signal.h>
        !            43: #include <sys/poll.h>
        !            44: #include "os_port.h"
        !            45: #include "ssl.h"
        !            46: 
        !            47: /* If nothing is received or sent in this many seconds, give up */
        !            48: static int opt_timeout = 60;
        !            49: 
        !            50: static int opt_verbose = 0;
        !            51: 
        !            52: int main(int argc, char *argv[])
        !            53: {
        !            54:        int log_opts = LOG_PERROR;
        !            55:        int fd[2]; /* output from child */
        !            56:        int df[2]; /* input to child */
        !            57:        int pid;
        !            58:        unsigned char *readbuf;
        !            59:        int readlen;
        !            60: 
        !            61:        SSL_CTX *ssl_ctx;
        !            62:        SSL *ssl;
        !            63: 
        !            64:        /* This relies on stdin and stdout being one and the same */
        !            65:        int sslfd = fileno(stdin);
        !            66: 
        !            67:        while (argc > 2 && argv[1][0] == '-') 
        !            68:     {
        !            69:                if (argc > 3 && strcmp(argv[1], "-t") == 0) 
        !            70:         {
        !            71:                        opt_timeout = atoi(argv[2]);
        !            72:                        argv += 2;
        !            73:                        argc -= 2;
        !            74:                        continue;
        !            75:                }
        !            76: 
        !            77:                if (strcmp(argv[1], "-q") == 0) 
        !            78:         {
        !            79:                        log_opts = 0;
        !            80:                        argv++;
        !            81:                        argc--;
        !            82:                        continue;
        !            83:                }
        !            84: 
        !            85:                if (strcmp(argv[1], "-v") == 0) 
        !            86:         {
        !            87:                        opt_verbose++;
        !            88:                        argv++;
        !            89:                        argc--;
        !            90:                        continue;
        !            91:                }
        !            92:        }
        !            93: 
        !            94:        if (argc < 2) 
        !            95:     {
        !            96:                fprintf(stderr, "Usage: axtlswrap [-v] [-q] "
        !            97:                 "[-t timeout] command ...\n");
        !            98:                return 1;
        !            99:        }
        !           100: 
        !           101:        if (access(argv[1], X_OK) != 0) 
        !           102:     {
        !           103:                fprintf(stderr, "Not an executabled: %s\n", argv[1]);
        !           104:                return 1;
        !           105:        }
        !           106: 
        !           107:        openlog("axtlswrap", LOG_PID | log_opts, LOG_DAEMON);
        !           108: 
        !           109:        /* Create an SSL context with the required options */
        !           110:        ssl_ctx = ssl_ctx_new(opt_verbose > 1 ? 
        !           111:                     SSL_DISPLAY_STATES | SSL_DISPLAY_CERTS : 0, 1);
        !           112: 
        !           113:        if (ssl_ctx == NULL) 
        !           114:     {
        !           115:                syslog(LOG_ERR, "Failed to create SSL ctx");
        !           116:                return 1;
        !           117:        }
        !           118: 
        !           119:        /* And create an ssl session attached to sslfd */
        !           120:        ssl = ssl_server_new(ssl_ctx, sslfd);
        !           121:        if (ssl == NULL) 
        !           122:     {
        !           123:                syslog(LOG_ERR, "Failed to create SSL connection");
        !           124:                return 1;
        !           125:        }
        !           126: 
        !           127:        /* Get past the handshaking */
        !           128:        while ((readlen = ssl_read(ssl, &readbuf)) == SSL_OK) 
        !           129:     {
        !           130:                /* Still handshaking */
        !           131:        }
        !           132: 
        !           133:        if (readlen < 0) 
        !           134:     {
        !           135:                syslog(LOG_ERR, "SSL handshake failed: %d", readlen);
        !           136:                return 1;
        !           137:        }
        !           138: 
        !           139:        if (opt_verbose) 
        !           140:     {
        !           141:                syslog(LOG_INFO, "SSL handshake OK");
        !           142:        }
        !           143: 
        !           144:        /* Looks OK, we have data, so fork the child and start */
        !           145:        if (pipe(fd) < 0 || pipe(df) < 0) 
        !           146:     {
        !           147:                syslog(LOG_ERR, "pipe failed: %m");
        !           148:                return 1;
        !           149:        }
        !           150: 
        !           151:        /* Give some indication to the child that we are running SSL
        !           152:         * It would be possible to provide other details
        !           153:         * too. Perhaps as in: http://httpd.apache.org/docs/2.0/mod/mod_ssl.html
        !           154:         */
        !           155:        setenv("SSL_PROTOCOL", "TLSv1", 1);
        !           156: 
        !           157: #ifndef NOMMU
        !           158:        if (opt_verbose) 
        !           159:     {
        !           160:                pid = fork();
        !           161:        }
        !           162:        else
        !           163: #endif
        !           164:        pid = vfork();
        !           165:        if (pid < 0) 
        !           166:     {
        !           167:                syslog(LOG_ERR, "vfork failed: %m");
        !           168:                return 1;
        !           169:        }
        !           170: 
        !           171:        if (pid > 0) 
        !           172:     {
        !           173:                /* This is the parent */
        !           174:                unsigned char writebuf[4096];
        !           175:                int writelen = 0;
        !           176:                struct pollfd pfd[3];
        !           177:                int timeout_count = 0;
        !           178: 
        !           179:                int cwfd = df[1];       /* write to child */
        !           180:                int crfd = fd[0];       /* read from child */
        !           181: 
        !           182:                int child_alive = 1;
        !           183: 
        !           184:                /* Don't die on SIGPIPE */
        !           185:                signal(SIGPIPE, SIG_IGN);
        !           186: 
        !           187:                close(df[0]);
        !           188:                close(fd[1]);
        !           189: 
        !           190:                pfd[0].fd = sslfd;
        !           191:                pfd[1].fd = cwfd;
        !           192:                pfd[2].fd = crfd;
        !           193: 
        !           194:                /* While the child is alive or there is something to return...  */
        !           195:                while (child_alive || writelen > 0) 
        !           196:         {
        !           197:                        /* Work out what to read and what to write */
        !           198:                        int ret;
        !           199: 
        !           200:                        pfd[0].events = 0;
        !           201:                        pfd[0].revents = 0;
        !           202: 
        !           203:                        /* Only want to read ssl data if there is nothing else to do */
        !           204:                        if (readlen == 0) 
        !           205:             {
        !           206:                                /* can read ssl data */
        !           207:                                pfd[0].events |= POLLIN;
        !           208:                        }
        !           209: 
        !           210:                        if (writelen > 0) 
        !           211:             {
        !           212:                                /* can write ssl data - will block to do this */
        !           213:                                pfd[0].events |= POLLOUT;
        !           214:                        }
        !           215: 
        !           216:                        pfd[1].events = 0;
        !           217:                        pfd[1].revents = 0;
        !           218: 
        !           219:                        if (child_alive && readlen > 0) 
        !           220:             {
        !           221:                                pfd[1].events |= POLLOUT;
        !           222:                        }
        !           223: 
        !           224:                        pfd[2].events = 0;
        !           225:                        pfd[2].revents = 0;
        !           226: 
        !           227:                        if (child_alive && writelen == 0) 
        !           228:             {
        !           229:                                pfd[2].events |= POLLIN;
        !           230:                        }
        !           231: 
        !           232:                        /* Timeout after 1 second so we can increment timeout_count */
        !           233:                        ret = poll(pfd, 3, 1000);
        !           234: 
        !           235:                        if (ret < 0) 
        !           236:             {
        !           237:                                if (errno != EAGAIN) 
        !           238:                 {
        !           239:                                        /* Kill off the child */
        !           240:                                        kill(pid, SIGTERM);
        !           241:                                        break;
        !           242:                                }
        !           243: 
        !           244:                                continue;
        !           245:                        }
        !           246: 
        !           247:                        if (ret == 0) 
        !           248:             {
        !           249:                                if (++timeout_count >= opt_timeout) 
        !           250:                 {
        !           251:                                        /* Kill off the child */
        !           252:                                        kill(pid, SIGTERM);
        !           253:                                        break;
        !           254:                                }
        !           255: 
        !           256:                                continue;
        !           257:                        }
        !           258: 
        !           259:                        timeout_count = 0;
        !           260: 
        !           261:                        if (pfd[2].revents & POLLNVAL) 
        !           262:             {
        !           263:                                /* REVISIT: This can probably be removed */
        !           264:                                syslog(LOG_ERR, "Child closed output pipe");
        !           265:                                child_alive = 0;
        !           266:                        }
        !           267:                        else if (pfd[2].revents & POLLIN) 
        !           268:             {
        !           269:                                /* Can read from (3) */
        !           270:                                writelen = read(crfd, writebuf, sizeof(writebuf));
        !           271:                                if (writelen <= 0) 
        !           272:                 {
        !           273:                                        if (writelen < 0) 
        !           274:                     {
        !           275:                                                syslog(LOG_WARNING, "Failed to read from child: len=%d",
        !           276:                                 writelen);
        !           277:                                        }
        !           278:                                        break;
        !           279:                                }
        !           280:                        }
        !           281:                        else if ((pfd[2].revents & POLLHUP) && kill(pid, 0) == 0) 
        !           282:             {
        !           283:                                if (opt_verbose) 
        !           284:                 {
        !           285:                                        syslog(LOG_INFO, "Child died and pipe gave POLLHUP");
        !           286:                                }
        !           287: 
        !           288:                                child_alive = 0;
        !           289:                        }
        !           290: 
        !           291:                        if (writelen > 0) 
        !           292:             {
        !           293:                                const unsigned char *pt = writebuf;
        !           294:                                while (writelen > 0) 
        !           295:                 {
        !           296:                                        ret = ssl_write(ssl, pt, writelen);
        !           297:                                        if (ret <= 0) 
        !           298:                     {
        !           299:                                                syslog(LOG_WARNING, "Failed to write ssl: ret=%d", ret);
        !           300:                                                /* Kill off the child now */
        !           301:                                                kill(pid, SIGTERM);
        !           302:                                                writelen = -1;
        !           303:                                                break;
        !           304:                                        }
        !           305:                                        else 
        !           306:                     {
        !           307:                                                pt += ret;
        !           308:                                                writelen -= ret;
        !           309:                                        }
        !           310:                                }
        !           311:                                if (writelen < 0) 
        !           312:                 {
        !           313:                                        break;
        !           314:                                }
        !           315:                        }
        !           316:                        else if (pfd[0].revents & POLLIN) 
        !           317:             {
        !           318:                                readlen = ssl_read(ssl, &readbuf);
        !           319:                                if (readlen <= 0 && opt_verbose) 
        !           320:                 {
        !           321:                                        syslog(LOG_INFO, "ssl_read() returned %d", readlen);
        !           322:                                }
        !           323: 
        !           324:                                if (readlen < 0) 
        !           325:                 {
        !           326:                                        /* Kill off the child */
        !           327:                                        kill(pid, SIGTERM);
        !           328:                                        break;
        !           329:                                }
        !           330:                        }
        !           331: 
        !           332:                        if (pfd[1].revents & POLLNVAL) 
        !           333:             {
        !           334:                                /* REVISIT: This can probably be removed */
        !           335:                                syslog(LOG_ERR, "Child closed input pipe");
        !           336:                                readlen = -1;
        !           337:                                child_alive = 0;
        !           338:                        }
        !           339:                        else if (pfd[1].revents & POLLOUT) 
        !           340:             {
        !           341:                                const unsigned char *pt = readbuf;
        !           342:                                while (readlen > 0) 
        !           343:                 {
        !           344:                                        int len = write(cwfd, pt, readlen);
        !           345:                                        if (len <= 0) 
        !           346:                     {
        !           347:                                                syslog(LOG_WARNING, "Failed to write to child: len=%d", 
        !           348:                                 len);
        !           349:                                                break;
        !           350:                                        }
        !           351: 
        !           352:                                        readlen -= len;
        !           353:                                        pt += len;
        !           354:                                }
        !           355:                        }
        !           356: 
        !           357:                }
        !           358: 
        !           359:                ssl_free(ssl);
        !           360: #if 0
        !           361:                fprintf(stderr, "[%d] SSL done: timeout_count=%d, readlen=%d, writelen=%d, child_alive=%d\n",
        !           362:                        getpid(), timeout_count, readlen, writelen, child_alive);
        !           363: #endif
        !           364:                return 0;
        !           365:        }
        !           366: 
        !           367:        /* Child */
        !           368:        close(df[1]);
        !           369:        close(fd[0]);
        !           370: 
        !           371:        dup2(df[0],0);
        !           372:        dup2(fd[1],1);
        !           373: 
        !           374:        close(df[0]);
        !           375:        close(fd[1]);
        !           376: 
        !           377:        execv(argv[1], argv + 1);
        !           378:        _exit(1);
        !           379: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>