summaryrefslogtreecommitdiffstats
path: root/tcpd/starttls.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /tcpd/starttls.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'tcpd/starttls.c')
-rw-r--r--tcpd/starttls.c807
1 files changed, 807 insertions, 0 deletions
diff --git a/tcpd/starttls.c b/tcpd/starttls.c
new file mode 100644
index 0000000..3e6f08e
--- /dev/null
+++ b/tcpd/starttls.c
@@ -0,0 +1,807 @@
+/*
+** Copyright 2000-2008 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+#include "config.h"
+#include "argparse.h"
+#include "spipe.h"
+
+#include "libcouriertls.h"
+#include "tlscache.h"
+#include "rfc1035/rfc1035.h"
+#include "soxwrap/soxwrap.h"
+#ifdef getc
+#undef getc
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <netdb.h>
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#include <locale.h>
+
+
+/* Command-line options: */
+const char *clienthost=0;
+const char *clientport=0;
+
+const char *server=0;
+const char *localfd=0;
+const char *remotefd=0;
+const char *statusfd=0;
+const char *tcpd=0;
+const char *peer_verify_domain=0;
+const char *fdprotocol=0;
+static FILE *errfp;
+static FILE *statusfp;
+
+const char *printx509=0;
+
+static void ssl_errmsg(const char *errmsg, void *dummy)
+{
+ fprintf(errfp, "%s\n", errmsg);
+}
+
+static void nonsslerror(const char *pfix)
+{
+ fprintf(errfp, "%s: %s\n", pfix, strerror(errno));
+}
+
+void docopy(ssl_handle ssl, int sslfd, int stdinfd, int stdoutfd)
+{
+ struct tls_transfer_info transfer_info;
+
+ char from_ssl_buf[BUFSIZ], to_ssl_buf[BUFSIZ];
+ char *fromptr;
+ int rc;
+
+ fd_set fdr, fdw;
+ int maxfd=sslfd;
+
+ if (fcntl(stdinfd, F_SETFL, O_NONBLOCK)
+ || fcntl(stdoutfd, F_SETFL, O_NONBLOCK)
+ )
+ {
+ nonsslerror("fcntl");
+ return;
+ }
+
+ if (maxfd < stdinfd) maxfd=stdinfd;
+ if (maxfd < stdoutfd) maxfd=stdoutfd;
+
+ tls_transfer_init(&transfer_info);
+
+ transfer_info.readptr=fromptr=from_ssl_buf;
+
+ for (;;)
+ {
+ if (transfer_info.readptr == fromptr)
+ {
+ transfer_info.readptr=fromptr=from_ssl_buf;
+ transfer_info.readleft=sizeof(from_ssl_buf);
+ }
+ else
+ transfer_info.readleft=0;
+
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+
+ rc=tls_transfer(&transfer_info, ssl, sslfd, &fdr, &fdw);
+
+ if (rc == 0)
+ continue;
+ if (rc < 0)
+ break;
+
+ if (!tls_inprogress(&transfer_info))
+ {
+ if (transfer_info.readptr > fromptr)
+ FD_SET(stdoutfd, &fdw);
+
+ if (transfer_info.writeleft == 0)
+ FD_SET(stdinfd, &fdr);
+ }
+
+ if (select(maxfd+1, &fdr, &fdw, 0, 0) <= 0)
+ {
+ if (errno != EINTR)
+ {
+ nonsslerror("select");
+ break;
+ }
+ continue;
+ }
+
+ if (FD_ISSET(stdoutfd, &fdw) &&
+ transfer_info.readptr > fromptr)
+ {
+ rc=write(stdoutfd, fromptr,
+ transfer_info.readptr - fromptr);
+
+ if (rc <= 0)
+ break;
+
+ fromptr += rc;
+ }
+
+ if (FD_ISSET(stdinfd, &fdr) && transfer_info.writeleft == 0)
+ {
+ rc=read(stdinfd, to_ssl_buf, sizeof(to_ssl_buf));
+ if (rc <= 0)
+ break;
+
+ transfer_info.writeptr=to_ssl_buf;
+ transfer_info.writeleft=rc;
+ }
+ }
+
+ tls_closing(&transfer_info);
+
+ for (;;)
+ {
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+
+ if (tls_transfer(&transfer_info, ssl, sslfd, &fdr, &fdw) < 0)
+ break;
+
+ if (select(maxfd+1, &fdr, &fdw, 0, 0) <= 0)
+ {
+ if (errno != EINTR)
+ {
+ nonsslerror("select");
+ break;
+ }
+ continue;
+ }
+ }
+}
+
+struct dump_capture_subject {
+ char line[1024];
+ int line_size;
+
+ int set_subject;
+ int seen_subject;
+ int in_subject;
+ FILE *fp;
+};
+
+static void dump_to_fp(const char *p, int cnt, void *arg)
+{
+ struct dump_capture_subject *dcs=(struct dump_capture_subject *)arg;
+ char *n, *v;
+ char namebuf[64];
+
+ if (cnt < 0)
+ cnt=strlen(p);
+
+ if (dcs->fp && fwrite(p, cnt, 1, dcs->fp) != 1)
+ ; /* NOOP */
+
+ while (cnt)
+ {
+ if (*p != '\n')
+ {
+ if (dcs->line_size < sizeof(dcs->line)-1)
+ dcs->line[dcs->line_size++]=*p;
+
+ ++p;
+ --cnt;
+ continue;
+ }
+ dcs->line[dcs->line_size]=0;
+ ++p;
+ --cnt;
+ dcs->line_size=0;
+
+ if (strncmp(dcs->line, "Subject:", 8) == 0)
+ {
+ if (dcs->seen_subject)
+ continue;
+
+ dcs->seen_subject=1;
+ dcs->in_subject=1;
+ continue;
+ }
+
+ if (!dcs->in_subject)
+ continue;
+
+ if (dcs->line[0] != ' ')
+ {
+ dcs->in_subject=0;
+ continue;
+ }
+
+ for (n=dcs->line; *n; n++)
+ if (*n != ' ')
+ break;
+
+ for (v=n; *v; v++)
+ {
+ *v=toupper(*v);
+ if (*v == '=')
+ {
+ *v++=0;
+ break;
+ }
+ }
+
+ namebuf[snprintf(namebuf, sizeof(namebuf)-1,
+ "TLS_SUBJECT_%s", n)]=0;
+
+ if (dcs->set_subject)
+ setenv(namebuf, v, 1);
+ }
+}
+
+static int verify_connection(ssl_handle ssl, void *dummy)
+{
+ FILE *printx509_fp=NULL;
+ int printx509_fd=0;
+ char *buf;
+
+ struct dump_capture_subject dcs;
+
+ memset(&dcs, 0, sizeof(dcs));
+
+ if (printx509)
+ {
+ printx509_fd=atoi(printx509);
+
+ printx509_fp=fdopen(printx509_fd, "w");
+ if (!printx509_fp)
+ nonsslerror("fdopen");
+ }
+
+ dcs.fp=printx509_fp;
+
+ dcs.set_subject=0;
+
+ if (tls_certificate_verified(ssl))
+ dcs.set_subject=1;
+
+ tls_dump_connection_info(ssl, server ? 1:0, dump_to_fp, &dcs);
+
+ if (printx509_fp)
+ {
+ fclose(printx509_fp);
+ }
+
+ if (statusfp)
+ {
+ fclose(statusfp);
+ statusfp=NULL;
+ errfp=stderr;
+ }
+
+ buf=tls_get_encryption_desc(ssl);
+
+ setenv("TLS_CONNECTED_PROTOCOL",
+ buf ? buf:"(unknown)", 1);
+
+ if (buf)
+ free(buf);
+ return 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void startclient(int argn, int argc, char **argv, int fd,
+ int *stdin_fd, int *stdout_fd)
+{
+pid_t p;
+int streampipe[2];
+
+ if (localfd)
+ {
+ *stdin_fd= *stdout_fd= atoi(localfd);
+ return;
+ }
+
+ if (argn >= argc) return; /* Interactive */
+
+ if (libmail_streampipe(streampipe))
+ {
+ nonsslerror("libmail_streampipe");
+ exit(1);
+ }
+ if ((p=fork()) == -1)
+ {
+ nonsslerror("fork");
+ close(streampipe[0]);
+ close(streampipe[1]);
+ exit(1);
+ }
+ if (p == 0)
+ {
+ char **argvec;
+ int n;
+
+ close(fd); /* Child process doesn't need it */
+ dup2(streampipe[1], 0);
+ dup2(streampipe[1], 1);
+ close(streampipe[0]);
+ close(streampipe[1]);
+
+ argvec=malloc(sizeof(char *)*(argc-argn+1));
+ if (!argvec)
+ {
+ nonsslerror("malloc");
+ exit(1);
+ }
+ for (n=0; n<argc-argn; n++)
+ argvec[n]=argv[argn+n];
+ argvec[n]=0;
+ execvp(argvec[0], argvec);
+ nonsslerror(argvec[0]);
+ exit(1);
+ }
+ close(streampipe[1]);
+
+ *stdin_fd= *stdout_fd= streampipe[0];
+}
+
+static int connectremote(const char *host, const char *port)
+{
+int fd;
+
+RFC1035_ADDR addr;
+int af;
+RFC1035_ADDR *addrs;
+unsigned naddrs, n;
+
+RFC1035_NETADDR addrbuf;
+const struct sockaddr *saddr;
+int saddrlen;
+int port_num;
+
+ port_num=atoi(port);
+ if (port_num <= 0)
+ {
+ struct servent *servent;
+
+ servent=getservbyname(port, "tcp");
+
+ if (!servent)
+ {
+ fprintf(errfp, "%s: invalid port.\n", port);
+ return (-1);
+ }
+ port_num=servent->s_port;
+ }
+ else
+ port_num=htons(port_num);
+
+ if (rfc1035_aton(host, &addr) == 0) /* An explicit IP addr */
+ {
+ if ((addrs=malloc(sizeof(addr))) == 0)
+ {
+ nonsslerror("malloc");
+ return (-1);
+ }
+ memcpy(addrs, &addr, sizeof(addr));
+ naddrs=1;
+ }
+ else
+ {
+ struct rfc1035_res res;
+ int rc;
+
+ rfc1035_init_resolv(&res);
+ rc=rfc1035_a(&res, host, &addrs, &naddrs);
+ rfc1035_destroy_resolv(&res);
+
+ if (rc)
+ {
+ fprintf(errfp, "%s: not found.\n", host);
+ return (-1);
+ }
+ }
+
+ if ((fd=rfc1035_mksocket(SOCK_STREAM, 0, &af)) < 0)
+ {
+ nonsslerror("socket");
+ return (-1);
+ }
+
+ for (n=0; n<naddrs; n++)
+ {
+ if (rfc1035_mkaddress(af, &addrbuf, addrs+n, port_num,
+ &saddr, &saddrlen)) continue;
+
+ if (sox_connect(fd, saddr, saddrlen) == 0)
+ break;
+ }
+ free(addrs);
+
+ if (n >= naddrs)
+ {
+ close(fd);
+ nonsslerror("connect");
+ return (-1);
+ }
+
+ return (fd);
+}
+
+static int connect_completed(ssl_handle ssl, int fd)
+{
+ struct tls_transfer_info transfer_info;
+ tls_transfer_init(&transfer_info);
+
+ while (tls_connecting(ssl))
+ {
+ fd_set fdr, fdw;
+
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+ if (tls_transfer(&transfer_info, ssl,
+ fd, &fdr, &fdw) < 0)
+ return (0);
+
+ if (!tls_connecting(ssl))
+ break;
+
+ if (select(fd+1, &fdr, &fdw, 0, 0) <= 0)
+ {
+ if (errno != EINTR)
+ {
+ nonsslerror("select");
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+static int dossl(int fd, int argn, int argc, char **argv)
+{
+ ssl_context ctx;
+ ssl_handle ssl;
+
+ int stdin_fd, stdout_fd;
+ struct tls_info info= *tls_get_default_info();
+
+ info.peer_verify_domain=peer_verify_domain;
+ info.tls_err_msg=ssl_errmsg;
+ info.connect_callback= &verify_connection;
+ info.app_data=NULL;
+
+ ctx=tls_create(server ? 1:0, &info);
+ if (ctx == 0) return (1);
+
+ ssl=tls_connect(ctx, fd);
+
+ if (!ssl)
+ {
+ close(fd);
+ return (1);
+ }
+
+ if (!connect_completed(ssl, fd))
+ {
+ tls_disconnect(ssl, fd);
+ close(fd);
+ tls_destroy(ctx);
+ return 1;
+ }
+
+ stdin_fd=0;
+ stdout_fd=1;
+
+ startclient(argn, argc, argv, fd, &stdin_fd, &stdout_fd);
+
+ docopy(ssl, fd, stdin_fd, stdout_fd);
+
+ tls_disconnect(ssl, fd);
+ close(fd);
+ tls_destroy(ctx);
+ return (0);
+}
+
+struct protoreadbuf {
+ char buffer[512];
+ char *bufptr;
+ int bufleft;
+
+ char line[256];
+} ;
+
+#define PRB_INIT(p) ( (p)->bufptr=0, (p)->bufleft=0)
+
+static char protoread(int fd, struct protoreadbuf *prb)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ tv.tv_sec=60;
+ tv.tv_usec=0;
+
+ if (select(fd+1, &fds, NULL, NULL, &tv) <= 0)
+ {
+ nonsslerror("select");
+ exit(1);
+ }
+
+ if ( (prb->bufleft=read(fd, prb->buffer, sizeof(prb->buffer))) <= 0)
+ {
+ errno=ECONNRESET;
+ nonsslerror("read");
+ exit(1);
+ }
+
+ prb->bufptr= prb->buffer;
+
+ --prb->bufleft;
+ return (*prb->bufptr++);
+}
+
+#define PRB_GETCH(fd,prb) ( (prb)->bufleft-- > 0 ? *(prb)->bufptr++:\
+ protoread( (fd), (prb)))
+
+static const char *prb_getline(int fd, struct protoreadbuf *prb)
+{
+ int i=0;
+ char c;
+
+ while ((c=PRB_GETCH(fd, prb)) != '\n')
+ {
+ if ( i < sizeof (prb->line)-1)
+ prb->line[i++]=c;
+ }
+ prb->line[i]=0;
+ return (prb->line);
+}
+
+static void prb_write(int fd, struct protoreadbuf *prb, const char *p)
+{
+ printf("%s", p);
+ while (*p)
+ {
+ int l=write(fd, p, strlen(p));
+
+ if (l <= 0)
+ {
+ nonsslerror("write");
+ exit(1);
+ }
+ p += l;
+ }
+}
+
+static int goodimap(const char *p)
+{
+ if (*p == 'x' && p[1] && isspace((int)(unsigned char)p[1]))
+ ++p;
+ else
+ {
+ if (*p != '*')
+ return (0);
+ ++p;
+ }
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+ if (strncasecmp(p, "BAD", 3) == 0)
+ {
+ exit(1);
+ }
+
+ if (strncasecmp(p, "BYE", 3) == 0)
+ {
+ exit(1);
+ }
+
+ if (strncasecmp(p, "NO", 2) == 0)
+ {
+ exit(1);
+ }
+
+ return (strncasecmp(p, "OK", 2) == 0);
+}
+
+static void imap_proto(int fd)
+{
+ struct protoreadbuf prb;
+ const char *p;
+
+ PRB_INIT(&prb);
+
+ do
+ {
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+
+ } while (!goodimap(p));
+
+ prb_write(fd, &prb, "x STARTTLS\r\n");
+
+ do
+ {
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+ } while (!goodimap(p));
+}
+
+static void pop3_proto(int fd)
+{
+ struct protoreadbuf prb;
+ const char *p;
+
+ PRB_INIT(&prb);
+
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+
+ prb_write(fd, &prb, "STLS\r\n");
+
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+}
+
+static void smtp_proto(int fd)
+{
+ struct protoreadbuf prb;
+ const char *p;
+
+ char hostname[1024];
+
+ PRB_INIT(&prb);
+
+ do
+ {
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+ } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
+ isdigit((int)(unsigned char)p[1]) &&
+ isdigit((int)(unsigned char)p[2]) &&
+ (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
+ if (strchr("123", *p) == 0)
+ exit(1);
+
+ hostname[sizeof(hostname)-1]=0;
+ if (gethostname(hostname, sizeof(hostname)-1) < 0)
+ strcpy(hostname, "localhost");
+
+ prb_write(fd, &prb, "EHLO ");
+ prb_write(fd, &prb, hostname);
+ prb_write(fd, &prb, "\r\n");
+ do
+ {
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+ } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
+ isdigit((int)(unsigned char)p[1]) &&
+ isdigit((int)(unsigned char)p[2]) &&
+ (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
+ if (strchr("123", *p) == 0)
+ exit(1);
+
+ prb_write(fd, &prb, "STARTTLS\r\n");
+
+ do
+ {
+ p=prb_getline(fd, &prb);
+ printf("%s\n", p);
+ } while ( ! ( isdigit((int)(unsigned char)p[0]) &&
+ isdigit((int)(unsigned char)p[1]) &&
+ isdigit((int)(unsigned char)p[2]) &&
+ (p[3] == 0 || isspace((int)(unsigned char)p[3]))));
+ if (strchr("123", *p) == 0)
+ exit(1);
+
+}
+
+int main(int argc, char **argv)
+{
+int argn;
+int fd;
+static struct args arginfo[] = {
+ { "host", &clienthost },
+ { "localfd", &localfd},
+ { "port", &clientport },
+ { "printx509", &printx509},
+ { "remotefd", &remotefd},
+ { "server", &server},
+ { "tcpd", &tcpd},
+ { "verify", &peer_verify_domain},
+ { "statusfd", &statusfd},
+ { "protocol", &fdprotocol},
+ {0}};
+void (*protocol_func)(int)=0;
+
+ setlocale(LC_ALL, "");
+ errfp=stderr;
+
+ argn=argparse(argc, argv, arginfo);
+
+ if (statusfd)
+ statusfp=fdopen(atoi(statusfd), "w");
+
+ if (statusfp)
+ errfp=statusfp;
+
+ if (fdprotocol)
+ {
+ if (strcmp(fdprotocol, "smtp") == 0)
+ protocol_func= &smtp_proto;
+ else if (strcmp(fdprotocol, "imap") == 0)
+ protocol_func= &imap_proto;
+ else if (strcmp(fdprotocol, "pop3") == 0)
+ protocol_func= &pop3_proto;
+ else
+ {
+ fprintf(stderr, "--protocol=%s - unknown protocol.\n",
+ fdprotocol);
+ exit(1);
+ }
+ }
+
+ if (tcpd)
+ {
+ dup2(2, 1);
+ fd=0;
+ }
+ else if (remotefd)
+ fd=atoi(remotefd);
+ else if (clienthost && clientport)
+ fd=connectremote(clienthost, clientport);
+ else
+ {
+ fprintf(errfp, "%s: specify remote location.\n",
+ argv[0]);
+ return (1);
+ }
+
+ if (fd < 0) return (1);
+ if (protocol_func)
+ (*protocol_func)(fd);
+
+ return (dossl(fd, argn, argc, argv));
+}