Annotation of embedaddon/axTLS/axtlswrap/axtlswrap.c, revision 1.1.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>