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>