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>