1 /*
2 ** Copyright 2000-2008 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 #include "config.h"
6 #include "argparse.h"
7 #include "spipe.h"
8
9 #include "libcouriertls.h"
10 #include "tlscache.h"
11 #include "rfc1035/rfc1035.h"
12 #include "soxwrap/soxwrap.h"
13 #ifdef getc
14 #undef getc
15 #endif
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #include <netdb.h>
21 #if HAVE_DIRENT_H
22 #include <dirent.h>
23 #define NAMLEN(dirent) strlen((dirent)->d_name)
24 #else
25 #define dirent direct
26 #define NAMLEN(dirent) (dirent)->d_namlen
27 #if HAVE_SYS_NDIR_H
28 #include <sys/ndir.h>
29 #endif
30 #if HAVE_SYS_DIR_H
31 #include <sys/dir.h>
32 #endif
33 #if HAVE_NDIR_H
34 #include <ndir.h>
35 #endif
36 #endif
37 #if HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #if HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif
43 #include <errno.h>
44 #if HAVE_SYS_TYPES_H
45 #include <sys/types.h>
46 #endif
47 #if HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
50 #include <sys/socket.h>
51 #include <arpa/inet.h>
52
53 #if TIME_WITH_SYS_TIME
54 #include <sys/time.h>
55 #include <time.h>
56 #else
57 #if HAVE_SYS_TIME_H
58 #include <sys/time.h>
59 #else
60 #include <time.h>
61 #endif
62 #endif
63 #include <locale.h>
64
65 static const char rcsid[]="$Id: starttls.c,v 1.44 2009/06/27 16:32:38 mrsam Exp $";
66
67 /* Command-line options: */
68 const char *clienthost=0;
69 const char *clientport=0;
70
71 const char *server=0;
72 const char *localfd=0;
73 const char *remotefd=0;
74 const char *statusfd=0;
75 const char *tcpd=0;
76 const char *peer_verify_domain=0;
77 const char *fdprotocol=0;
78 static FILE *errfp;
79 static FILE *statusfp;
80
81 const char *printx509=0;
82
ssl_errmsg(const char * errmsg,void * dummy)83 static void ssl_errmsg(const char *errmsg, void *dummy)
84 {
85 fprintf(errfp, "%s\n", errmsg);
86 }
87
nonsslerror(const char * pfix)88 static void nonsslerror(const char *pfix)
89 {
90 fprintf(errfp, "%s: %s\n", pfix, strerror(errno));
91 }
92
docopy(ssl_handle ssl,int sslfd,int stdinfd,int stdoutfd)93 void docopy(ssl_handle ssl, int sslfd, int stdinfd, int stdoutfd)
94 {
95 struct tls_transfer_info transfer_info;
96
97 char from_ssl_buf[BUFSIZ], to_ssl_buf[BUFSIZ];
98 char *fromptr;
99 int rc;
100
101 fd_set fdr, fdw;
102 int maxfd=sslfd;
103
104 if (fcntl(stdinfd, F_SETFL, O_NONBLOCK)
105 || fcntl(stdoutfd, F_SETFL, O_NONBLOCK)
106 )
107 {
108 nonsslerror("fcntl");
109 return;
110 }
111
112 if (maxfd < stdinfd) maxfd=stdinfd;
113 if (maxfd < stdoutfd) maxfd=stdoutfd;
114
115 tls_transfer_init(&transfer_info);
116
117 transfer_info.readptr=fromptr=from_ssl_buf;
118
119 for (;;)
120 {
121 if (transfer_info.readptr == fromptr)
122 {
123 transfer_info.readptr=fromptr=from_ssl_buf;
124 transfer_info.readleft=sizeof(from_ssl_buf);
125 }
126 else
127 transfer_info.readleft=0;
128
129 FD_ZERO(&fdr);
130 FD_ZERO(&fdw);
131
132 rc=tls_transfer(&transfer_info, ssl, sslfd, &fdr, &fdw);
133
134 if (rc == 0)
135 continue;
136 if (rc < 0)
137 break;
138
139 if (!tls_inprogress(&transfer_info))
140 {
141 if (transfer_info.readptr > fromptr)
142 FD_SET(stdoutfd, &fdw);
143
144 if (transfer_info.writeleft == 0)
145 FD_SET(stdinfd, &fdr);
146 }
147
148 if (select(maxfd+1, &fdr, &fdw, 0, 0) <= 0)
149 {
150 if (errno != EINTR)
151 {
152 nonsslerror("select");
153 break;
154 }
155 continue;
156 }
157
158 if (FD_ISSET(stdoutfd, &fdw) &&
159 transfer_info.readptr > fromptr)
160 {
161 rc=write(stdoutfd, fromptr,
162 transfer_info.readptr - fromptr);
163
164 if (rc <= 0)
165 break;
166
167 fromptr += rc;
168 }
169
170 if (FD_ISSET(stdinfd, &fdr) && transfer_info.writeleft == 0)
171 {
172 rc=read(stdinfd, to_ssl_buf, sizeof(to_ssl_buf));
173 if (rc <= 0)
174 break;
175
176 transfer_info.writeptr=to_ssl_buf;
177 transfer_info.writeleft=rc;
178 }
179 }
180
181 tls_closing(&transfer_info);
182
183 for (;;)
184 {
185 FD_ZERO(&fdr);
186 FD_ZERO(&fdw);
187
188 if (tls_transfer(&transfer_info, ssl, sslfd, &fdr, &fdw) < 0)
189 break;
190
191 if (select(maxfd+1, &fdr, &fdw, 0, 0) <= 0)
192 {
193 if (errno != EINTR)
194 {
195 nonsslerror("select");
196 break;
197 }
198 continue;
199 }
200 }
201 }
202
203 struct dump_capture_subject {
204 char line[1024];
205 int line_size;
206
207 int set_subject;
208 int seen_subject;
209 int in_subject;
210 FILE *fp;
211 };
212
dump_to_fp(const char * p,int cnt,void * arg)213 static void dump_to_fp(const char *p, int cnt, void *arg)
214 {
215 struct dump_capture_subject *dcs=(struct dump_capture_subject *)arg;
216 char *n, *v;
217 char namebuf[64];
218
219 if (cnt < 0)
220 cnt=strlen(p);
221
222 if (dcs->fp && fwrite(p, cnt, 1, dcs->fp) != 1)
223 ; /* NOOP */
224
225 while (cnt)
226 {
227 if (*p != '\n')
228 {
229 if (dcs->line_size < sizeof(dcs->line)-1)
230 dcs->line[dcs->line_size++]=*p;
231
232 ++p;
233 --cnt;
234 continue;
235 }
236 dcs->line[dcs->line_size]=0;
237 ++p;
238 --cnt;
239 dcs->line_size=0;
240
241 if (strncmp(dcs->line, "Subject:", 8) == 0)
242 {
243 if (dcs->seen_subject)
244 continue;
245
246 dcs->seen_subject=1;
247 dcs->in_subject=1;
248 continue;
249 }
250
251 if (!dcs->in_subject)
252 continue;
253
254 if (dcs->line[0] != ' ')
255 {
256 dcs->in_subject=0;
257 continue;
258 }
259
260 for (n=dcs->line; *n; n++)
261 if (*n != ' ')
262 break;
263
264 for (v=n; *v; v++)
265 {
266 *v=toupper(*v);
267 if (*v == '=')
268 {
269 *v++=0;
270 break;
271 }
272 }
273
274 namebuf[snprintf(namebuf, sizeof(namebuf)-1,
275 "TLS_SUBJECT_%s", n)]=0;
276
277 if (dcs->set_subject)
278 setenv(namebuf, v, 1);
279 }
280 }
281
verify_connection(ssl_handle ssl,void * dummy)282 static int verify_connection(ssl_handle ssl, void *dummy)
283 {
284 FILE *printx509_fp=NULL;
285 int printx509_fd=0;
286 char *buf;
287
288 struct dump_capture_subject dcs;
289
290 memset(&dcs, 0, sizeof(dcs));
291
292 if (printx509)
293 {
294 printx509_fd=atoi(printx509);
295
296 printx509_fp=fdopen(printx509_fd, "w");
297 if (!printx509_fp)
298 nonsslerror("fdopen");
299 }
300
301 dcs.fp=printx509_fp;
302
303 dcs.set_subject=0;
304
305 if (tls_certificate_verified(ssl))
306 dcs.set_subject=1;
307
308 tls_dump_connection_info(ssl, server ? 1:0, dump_to_fp, &dcs);
309
310 if (printx509_fp)
311 {
312 fclose(printx509_fp);
313 }
314
315 if (statusfp)
316 {
317 fclose(statusfp);
318 statusfp=NULL;
319 errfp=stderr;
320 }
321
322 buf=tls_get_encryption_desc(ssl);
323
324 setenv("TLS_CONNECTED_PROTOCOL",
325 buf ? buf:"(unknown)", 1);
326
327 if (buf)
328 free(buf);
329 return 1;
330 }
331
332 /* ----------------------------------------------------------------------- */
333
startclient(int argn,int argc,char ** argv,int fd,int * stdin_fd,int * stdout_fd)334 static void startclient(int argn, int argc, char **argv, int fd,
335 int *stdin_fd, int *stdout_fd)
336 {
337 pid_t p;
338 int streampipe[2];
339
340 if (localfd)
341 {
342 *stdin_fd= *stdout_fd= atoi(localfd);
343 return;
344 }
345
346 if (argn >= argc) return; /* Interactive */
347
348 if (libmail_streampipe(streampipe))
349 {
350 nonsslerror("libmail_streampipe");
351 exit(1);
352 }
353 if ((p=fork()) == -1)
354 {
355 nonsslerror("fork");
356 close(streampipe[0]);
357 close(streampipe[1]);
358 exit(1);
359 }
360 if (p == 0)
361 {
362 char **argvec;
363 int n;
364
365 close(fd); /* Child process doesn't need it */
366 dup2(streampipe[1], 0);
367 dup2(streampipe[1], 1);
368 close(streampipe[0]);
369 close(streampipe[1]);
370
371 argvec=malloc(sizeof(char *)*(argc-argn+1));
372 if (!argvec)
373 {
374 nonsslerror("malloc");
375 exit(1);
376 }
377 for (n=0; n<argc-argn; n++)
378 argvec[n]=argv[argn+n];
379 argvec[n]=0;
380 execvp(argvec[0], argvec);
381 nonsslerror(argvec[0]);
382 exit(1);
383 }
384 close(streampipe[1]);
385
386 *stdin_fd= *stdout_fd= streampipe[0];
387 }
388
connectremote(const char * host,const char * port)389 static int connectremote(const char *host, const char *port)
390 {
391 int fd;
392
393 RFC1035_ADDR addr;
394 int af;
395 RFC1035_ADDR *addrs;
396 unsigned naddrs, n;
397
398 RFC1035_NETADDR addrbuf;
399 const struct sockaddr *saddr;
400 int saddrlen;
401 int port_num;
402
403 port_num=atoi(port);
404 if (port_num <= 0)
405 {
406 struct servent *servent;
407
408 servent=getservbyname(port, "tcp");
409
410 if (!servent)
411 {
412 fprintf(errfp, "%s: invalid port.\n", port);
413 return (-1);
414 }
415 port_num=servent->s_port;
416 }
417 else
418 port_num=htons(port_num);
419
420 if (rfc1035_aton(host, &addr) == 0) /* An explicit IP addr */
421 {
422 if ((addrs=malloc(sizeof(addr))) == 0)
423 {
424 nonsslerror("malloc");
425 return (-1);
426 }
427 memcpy(addrs, &addr, sizeof(addr));
428 naddrs=1;
429 }
430 else
431 {
432 if (rfc1035_a(&rfc1035_default_resolver, host, &addrs,
433 &naddrs))
434 {
435 fprintf(errfp, "%s: not found.\n", host);
436 return (-1);
437 }
438 }
439
440 if ((fd=rfc1035_mksocket(SOCK_STREAM, 0, &af)) < 0)
441 {
442 nonsslerror("socket");
443 return (-1);
444 }
445
446 for (n=0; n<naddrs; n++)
447 {
448 if (rfc1035_mkaddress(af, &addrbuf, addrs+n, port_num,
449 &saddr, &saddrlen)) continue;
450
451 if (sox_connect(fd, saddr, saddrlen) == 0)
452 break;
453 }
454 free(addrs);
455
456 if (n >= naddrs)
457 {
458 close(fd);
459 nonsslerror("connect");
460 return (-1);
461 }
462
463 return (fd);
464 }
465
connect_completed(ssl_handle ssl,int fd)466 static int connect_completed(ssl_handle ssl, int fd)
467 {
468 struct tls_transfer_info transfer_info;
469 tls_transfer_init(&transfer_info);
470
471 while (tls_connecting(ssl))
472 {
473 fd_set fdr, fdw;
474
475 FD_ZERO(&fdr);
476 FD_ZERO(&fdw);
477 if (tls_transfer(&transfer_info, ssl,
478 fd, &fdr, &fdw) < 0)
479 return (0);
480
481 if (!tls_connecting(ssl))
482 break;
483
484 if (select(fd+1, &fdr, &fdw, 0, 0) <= 0)
485 {
486 if (errno != EINTR)
487 {
488 nonsslerror("select");
489 return (0);
490 }
491 }
492 }
493 return (1);
494 }
495
dossl(int fd,int argn,int argc,char ** argv)496 static int dossl(int fd, int argn, int argc, char **argv)
497 {
498 ssl_context ctx;
499 ssl_handle ssl;
500
501 int stdin_fd, stdout_fd;
502 struct tls_info info= *tls_get_default_info();
503
504 info.peer_verify_domain=peer_verify_domain;
505 info.tls_err_msg=ssl_errmsg;
506 info.connect_callback= &verify_connection;
507 info.app_data=NULL;
508
509 ctx=tls_create(server ? 1:0, &info);
510 if (ctx == 0) return (1);
511
512 ssl=tls_connect(ctx, fd);
513
514 if (!ssl)
515 {
516 close(fd);
517 return (1);
518 }
519
520 if (!connect_completed(ssl, fd))
521 {
522 tls_disconnect(ssl, fd);
523 close(fd);
524 tls_destroy(ctx);
525 return 1;
526 }
527
528 stdin_fd=0;
529 stdout_fd=1;
530
531 startclient(argn, argc, argv, fd, &stdin_fd, &stdout_fd);
532
533 docopy(ssl, fd, stdin_fd, stdout_fd);
534
535 tls_disconnect(ssl, fd);
536 close(fd);
537 tls_destroy(ctx);
538 return (0);
539 }
540
541 struct protoreadbuf {
542 char buffer[512];
543 char *bufptr;
544 int bufleft;
545
546 char line[256];
547 } ;
548
549 #define PRB_INIT(p) ( (p)->bufptr=0, (p)->bufleft=0)
550
protoread(int fd,struct protoreadbuf * prb)551 static char protoread(int fd, struct protoreadbuf *prb)
552 {
553 fd_set fds;
554 struct timeval tv;
555
556 FD_ZERO(&fds);
557 FD_SET(fd, &fds);
558
559 tv.tv_sec=60;
560 tv.tv_usec=0;
561
562 if (select(fd+1, &fds, NULL, NULL, &tv) <= 0)
563 {
564 nonsslerror("select");
565 exit(1);
566 }
567
568 if ( (prb->bufleft=read(fd, prb->buffer, sizeof(prb->buffer))) <= 0)
569 {
570 errno=ECONNRESET;
571 nonsslerror("read");
572 exit(1);
573 }
574
575 prb->bufptr= prb->buffer;
576
577 --prb->bufleft;
578 return (*prb->bufptr++);
579 }
580
581 #define PRB_GETCH(fd,prb) ( (prb)->bufleft-- > 0 ? *(prb)->bufptr++:\
582 protoread( (fd), (prb)))
583
prb_getline(int fd,struct protoreadbuf * prb)584 static const char *prb_getline(int fd, struct protoreadbuf *prb)
585 {
586 int i=0;
587 char c;
588
589 while ((c=PRB_GETCH(fd, prb)) != '\n')
590 {
591 if ( i < sizeof (prb->line)-1)
592 prb->line[i++]=c;
593 }
594 prb->line[i]=0;
595 return (prb->line);
596 }
597
prb_write(int fd,struct protoreadbuf * prb,const char * p)598 static void prb_write(int fd, struct protoreadbuf *prb, const char *p)
599 {
600 printf("%s", p);
601 while (*p)
602 {
603 int l=write(fd, p, strlen(p));
604
605 if (l <= 0)
606 {
607 nonsslerror("write");
608 exit(1);
609 }
610 p += l;
611 }
612 }
613
goodimap(const char * p)614 static int goodimap(const char *p)
615 {
616 if (*p == 'x' && p[1] && isspace((int)(unsigned char)p[1]))
617 ++p;
618 else
619 {
620 if (*p != '*')
621 return (0);
622 ++p;
623 }
624 while (*p && isspace((int)(unsigned char)*p))
625 ++p;
626 if (strncasecmp(p, "BAD", 3) == 0)
627 {
628 exit(1);
629 }
630
631 if (strncasecmp(p, "BYE", 3) == 0)
632 {
633 exit(1);
634 }
635
636 if (strncasecmp(p, "NO", 2) == 0)
637 {
638 exit(1);
639 }
640
641 return (strncasecmp(p, "OK", 2) == 0);
642 }
643
imap_proto(int fd)644 static void imap_proto(int fd)
645 {
646 struct protoreadbuf prb;
647 const char *p;
648
649 PRB_INIT(&prb);
650
651 do
652 {
653 p=prb_getline(fd, &prb);
654 printf("%s\n", p);
655
656 } while (!goodimap(p));
657
658 prb_write(fd, &prb, "x STARTTLS\r\n");
659
660 do
661 {
662 p=prb_getline(fd, &prb);
663 printf("%s\n", p);
664 } while (!goodimap(p));
665 }
666
pop3_proto(int fd)667 static void pop3_proto(int fd)
668 {
669 struct protoreadbuf prb;
670 const char *p;
671
672 PRB_INIT(&prb);
673
674 p=prb_getline(fd, &prb);
675 printf("%s\n", p);
676
677 prb_write(fd, &prb, "STLS\r\n");
678
679 p=prb_getline(fd, &prb);
680 printf("%s\n", p);
681 }
682
smtp_proto(int fd)683 static void smtp_proto(int fd)
684 {
685 struct protoreadbuf prb;
686 const char *p;
687
688 char hostname[1024];
689
690 PRB_INIT(&prb);
691
692 do
693 {
694 p=prb_getline(fd, &prb);
695 printf("%s\n", p);
696 } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
697 isdigit((int)(unsigned char)p[1]) &&
698 isdigit((int)(unsigned char)p[2]) &&
699 (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
700 if (strchr("123", *p) == 0)
701 exit(1);
702
703 hostname[sizeof(hostname)-1]=0;
704 if (gethostname(hostname, sizeof(hostname)-1) < 0)
705 strcpy(hostname, "localhost");
706
707 prb_write(fd, &prb, "EHLO ");
708 prb_write(fd, &prb, hostname);
709 prb_write(fd, &prb, "\r\n");
710 do
711 {
712 p=prb_getline(fd, &prb);
713 printf("%s\n", p);
714 } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
715 isdigit((int)(unsigned char)p[1]) &&
716 isdigit((int)(unsigned char)p[2]) &&
717 (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
718 if (strchr("123", *p) == 0)
719 exit(1);
720
721 prb_write(fd, &prb, "STARTTLS\r\n");
722
723 do
724 {
725 p=prb_getline(fd, &prb);
726 printf("%s\n", p);
727 } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
728 isdigit((int)(unsigned char)p[1]) &&
729 isdigit((int)(unsigned char)p[2]) &&
730 (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
731 if (strchr("123", *p) == 0)
732 exit(1);
733
734 }
735
main(int argc,char ** argv)736 int main(int argc, char **argv)
737 {
738 int argn;
739 int fd;
740 static struct args arginfo[] = {
741 { "host", &clienthost },
742 { "localfd", &localfd},
743 { "port", &clientport },
744 { "printx509", &printx509},
745 { "remotefd", &remotefd},
746 { "server", &server},
747 { "tcpd", &tcpd},
748 { "verify", &peer_verify_domain},
749 { "statusfd", &statusfd},
750 { "protocol", &fdprotocol},
751 {0}};
752 void (*protocol_func)(int)=0;
753
754 setlocale(LC_ALL, "");
755 errfp=stderr;
756
757 argn=argparse(argc, argv, arginfo);
758
759 if (statusfd)
760 statusfp=fdopen(atoi(statusfd), "w");
761
762 if (statusfp)
763 errfp=statusfp;
764
765 if (fdprotocol)
766 {
767 if (strcmp(fdprotocol, "smtp") == 0)
768 protocol_func= &smtp_proto;
769 else if (strcmp(fdprotocol, "imap") == 0)
770 protocol_func= &imap_proto;
771 else if (strcmp(fdprotocol, "pop3") == 0)
772 protocol_func= &pop3_proto;
773 else
774 {
775 fprintf(stderr, "--protocol=%s - unknown protocol.\n",
776 fdprotocol);
777 exit(1);
778 }
779 }
780
781 if (tcpd)
782 {
783 dup2(2, 1);
784 fd=0;
785 }
786 else if (remotefd)
787 fd=atoi(remotefd);
788 else if (clienthost && clientport)
789 fd=connectremote(clienthost, clientport);
790 else
791 {
792 fprintf(errfp, "%s: specify remote location.\n",
793 argv[0]);
794 return (1);
795 }
796
797 if (fd < 0) return (1);
798 if (protocol_func)
799 (*protocol_func)(fd);
800
801 return (dossl(fd, argn, argc, argv));
802 }
803