From cc22b47ee74ce0aaffb8642e335012a6bbf76a0c Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 3 Sep 2011 22:37:16 -0400 Subject: [PATCH 1/7] Original patch ucspi-tls patch from 2005 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eaf313f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +command +compile From 2025f8cedf268218c10e4164a19a02d6ae6ad952 Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 3 Sep 2011 22:40:55 -0400 Subject: [PATCH 2/7] Original ucspi-tls patches from 2005 * Privilege separation in sslserver * Support for chroot(2) in sslserver * Support for changing UID and GID in sslserver * Support for delayed TLS in sslserver using the ucspi-tls protocol. See http://www.suspectclass.com/sgifford/ucspi-tls/ --- src/pathexec.h | 2 + src/pathexec_env.c | 6 ++ src/ssl.h | 2 +- src/sslserver.c | 239 +++++++++++++++++++++++++++++---------------- 4 files changed, 164 insertions(+), 85 deletions(-) diff --git a/src/pathexec.h b/src/pathexec.h index d0661a5..f4229d8 100644 --- a/src/pathexec.h +++ b/src/pathexec.h @@ -2,9 +2,11 @@ #ifndef PATHEXEC_H #define PATHEXEC_H +#include "stralloc.h" extern void pathexec_run(const char *,char * const *,char * const *); extern int pathexec_env(const char *,const char *); +extern int pathexec_multienv(stralloc *); extern void pathexec(char * const *); #endif diff --git a/src/pathexec_env.c b/src/pathexec_env.c index b4d1bd1..47f545d 100644 --- a/src/pathexec_env.c +++ b/src/pathexec_env.c @@ -22,6 +22,12 @@ int pathexec_env(const char *s,const char *t) return stralloc_cat(&plus,&tmp); } +int pathexec_multienv(stralloc *sa) +{ + if (!sa) return 1; + return stralloc_cat(&plus,sa); +} + void pathexec(char * const *argv) { char **e; diff --git a/src/ssl.h b/src/ssl.h index 907a3aa..a92d106 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -20,7 +20,7 @@ extern int ssl_verify(SSL *,const char *); extern int ssl_params(SSL_CTX *,const char *,int); extern int ssl_server_env(SSL *,stralloc *); extern int ssl_client_env(SSL *,stralloc *); -extern void ssl_error_str(); +extern char *ssl_error_str(int); extern int ssl_error(int (*)(const char *)); #define ssl_client() (ssl_context(SSLv23_client_method())) diff --git a/src/sslserver.c b/src/sslserver.c index b516e98..5b0c06c 100644 --- a/src/sslserver.c +++ b/src/sslserver.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "ssl.h" #include "uint16.h" @@ -39,10 +40,10 @@ #include "auto_certfile.h" #include "auto_keyfile.h" #include "auto_ciphers.h" +#include "fmt.h" int verbosity = 1; int flagkillopts = 1; -int flagafter = 0; int flagdelay = 0; const char *banner = ""; int flagremoteinfo = 1; @@ -51,6 +52,7 @@ int flagparanoid = 0; int flagclientcert = 0; int flagsslenv = 0; int flagtcpenv = 0; +int flagsslwait = 0; unsigned long timeout = 26; unsigned long ssltimeout = 26; unsigned int progtimeout = 3600; @@ -72,6 +74,9 @@ static stralloc remotehostsa; char *remotehost = 0; char *verifyhost = 0; +unsigned long uid = 0; +unsigned long gid = 0; + char strnum[FMT_ULONG]; char strnum2[FMT_ULONG]; @@ -106,6 +111,8 @@ stralloc envsa = {0}; X509 *cert; char buf[SSL_NAME_LEN]; +#define FATAL "sslserver: fatal: " + /* ---------------------------- child */ @@ -180,7 +187,93 @@ void doit(int t) { int j; SSL *ssl; int wstat; + int sslctl[2]; + char *s; + long tmp_long; + char ssl_cmd; + stralloc ssl_env = { 0 }; + int bytesleft; + char envbuf[8192]; + + if (pipe(pi) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); + if (pipe(po) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sslctl) == -1) strerr_die2sys(111,DROP,"unable to create socketpair: "); + + switch(fork()) { + case -1: + strerr_die2sys(111,DROP,"unable to fork: "); + case 0: + /* Child */ + break; + default: + /* Parent */ + + close(pi[0]); close(po[1]); close(sslctl[1]); + + if ((s=env_get("SSL_CHROOT"))) + if (chroot(s) == -1) + strerr_die2x(111,DROP,"unable to chroot"); + + if ((s=env_get("SSL_GID"))) { + scan_ulong(s,&tmp_long); + gid = tmp_long; + } + if (gid) if (prot_gid(gid) == -1) strerr_die2sys(111,FATAL,"unable to set gid: "); + + if ((s=env_get("SSL_UID"))) { + scan_ulong(s,&tmp_long); + uid = tmp_long; + } + if (uid) if (prot_uid(uid) == -1) + strerr_die2sys(111,FATAL,"unable to set uid: "); + + /* Read the TLS command socket. This will block until/unless + * TLS is requested. + */ + if (read(sslctl[0],&ssl_cmd,1) == 1) { + ssl = ssl_new(ctx,t); + if (!ssl) strerr_die2x(111,DROP,"unable to create SSL instance"); + if (ndelay_on(t) == -1) + strerr_die2sys(111,DROP,"unable to set socket options: "); + if (ssl_timeoutaccept(ssl,ssltimeout) == -1) + strerr_die3x(111,DROP,"unable to accept SSL: ",ssl_error_str(ssl_errno)); + } + + if (verbosity >= 2) { + strnum[fmt_ulong(strnum,getpid())] = 0; + strerr_warn3("sslserver: ssl ",strnum," accept ",0); + } + + if (flagclientcert) { + switch(ssl_verify(ssl,verifyhost)) { + case -1: + strerr_die2x(111,DROP,"unable to verify client certificate"); + case -2: + strerr_die2x(111,DROP,"no client certificate"); + case -3: + strerr_die2x(111,DROP,"client name does not match certificate"); + default: break; + } + } + + if (ssl_cmd == 'Y') { + ssl_server_env(ssl, &ssl_env); + stralloc_0(&ssl_env); /* Add another NUL */ + + for(bytesleft = ssl_env.len; bytesleft>0; bytesleft-=j) + if ( (j=write(sslctl[0], ssl_env.s, bytesleft)) < 0) + strerr_die2sys(111, FATAL, "unable to write SSL environment: "); + } + + if (ssl_io(ssl,pi[1],po[0],3600) != 0) + strerr_die3x(111,DROP,"unable to speak SSL: ",ssl_error_str(ssl_errno)); + if (wait_nohang(&wstat) > 0) + _exit(wait_exitcode(wstat)); + ssl_close(ssl); + _exit(0); + } + /* Child-only below this point */ remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; if (verbosity >= 2) { @@ -278,59 +371,11 @@ void doit(int t) { if (flagdeny) _exit(100); - if (pipe(pi) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); - if (pipe(po) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); - - ssl = ssl_new(ctx,t); - if (!ssl) strerr_die2x(111,DROP,"unable to create SSL instance"); - if (ndelay_on(t) == -1) - strerr_die2sys(111,DROP,"unable to set socket options: "); - if (ssl_timeoutaccept(ssl,ssltimeout) == -1) { - strerr_warn2(DROP,"unable to SSL accept:",&strerr_sys); - ssl_error(error_warn); - ssl_close(ssl); - _exit(111); - } - - if (verbosity >= 2) { - strnum[fmt_ulong(strnum,getpid())] = 0; - strerr_warn3("sslserver: ssl ",strnum," accept ",0); - } + if (gid) if (prot_gid(gid) == -1) + strerr_die2sys(111,FATAL,"unable to set gid: "); + if (uid) if (prot_uid(uid) == -1) + strerr_die2sys(111,FATAL,"unable to set uid: "); - if (flagclientcert) { - switch(ssl_verify(ssl,verifyhost)) { - case -1: - strerr_die2x(111,DROP,"unable to verify client certificate"); - case -2: - strerr_die2x(111,DROP,"no client certificate"); - case -3: - strerr_die2x(111,DROP,"client name does not match certificate"); - default: break; - } - } - - switch(j = fork()) { - case -1: - strerr_die2sys(111,DROP,"unable to fork: "); - case 0: - break; - default: - sig_ignore(sig_pipe); - sig_uncatch(sig_child); - sig_unblock(sig_child); - close(pi[0]); close(po[1]); - if (ssl_io(ssl,pi[1],po[0],progtimeout) != 0) { - strerr_warn2(DROP,"unable to speak SSL:",&strerr_sys); - ssl_error(error_warn); - ssl_close(ssl); - wait_pid(&wstat,j); - _exit(111); - } - ssl_close(ssl); - if (wait_pid(&wstat,j) > 0) - _exit(wait_exitcode(wstat)); - _exit(0); - } close(pi[1]); close(po[0]); sig_uncatch(sig_child); @@ -338,13 +383,32 @@ void doit(int t) { sig_uncatch(sig_term); sig_uncatch(sig_pipe); - if (flagsslenv && !ssl_server_env(ssl,0)) drop_nomem(); - ssl_close(ssl); - - if (fd_move(0,pi[0]) == -1) - strerr_die2sys(111,DROP,"unable to set up descriptor 0: "); - if (fd_move(1,po[1]) == -1) - strerr_die2sys(111,DROP,"unable to set up descriptor 1: "); + if (fcntl(sslctl[1],F_SETFD,0) == -1) + strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); + strnum[fmt_ulong(strnum,sslctl[1])]=0; + env("SSLCTLFD",strnum); + + if (fcntl(pi[0],F_SETFD,0) == -1) + strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); + strnum[fmt_ulong(strnum,pi[0])]=0; + env("SSLREADFD",strnum); + + if (fcntl(po[1],F_SETFD,0) == -1) + strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); + strnum[fmt_ulong(strnum,po[1])]=0; + env("SSLWRITEFD",strnum); + + if (flagsslwait) { + if (fd_copy(0,t) == -1) + strerr_die2sys(111,DROP,"unable to set up descriptor 0: "); + if (fd_copy(1,t) == -1) + strerr_die2sys(111,DROP,"unable to set up descriptor 1: "); + } else { + if (fd_move(0,pi[0]) == -1) + strerr_die2sys(111,DROP,"unable to set up descriptor 0: "); + if (fd_move(1,po[1]) == -1) + strerr_die2sys(111,DROP,"unable to set up descriptor 1: "); + } if (flagkillopts) socket_ipoptionskill(t); @@ -357,6 +421,22 @@ void doit(int t) { strerr_die2sys(111,DROP,"unable to print banner: "); } + if (!flagsslwait) { + ssl_cmd = flagsslenv ? 'Y' : 'y'; + if (write(sslctl[1], &ssl_cmd, 1) < 1) + strerr_die2sys(111,DROP,"unable to start SSL: "); + if (flagsslenv) { + while ((j=read(sslctl[1],envbuf,8192)) > 0) { + stralloc_catb(&ssl_env,envbuf,j); + if (ssl_env.len >= 2 && ssl_env.s[ssl_env.len-2]==0 && ssl_env.s[ssl_env.len-1]==0) + break; + } + if (j < 0) + strerr_die2sys(111,DROP,"unable to read SSL environment: "); + pathexec_multienv(&ssl_env); + } + } + pathexec(prog); strerr_die4sys(111,DROP,"unable to run ",*prog,": "); } @@ -365,13 +445,11 @@ void doit(int t) { /* ---------------------------- parent */ -#define FATAL "sslserver: fatal: " - void usage(void) { strerr_warn1("\ sslserver: usage: sslserver \ -[ -13UXpPhHrRoOdDqQviIeEsS ] \ +[ -13UXpPhHrRoOdDqQviIeEsSnN ] \ [ -c limit ] \ [ -x rules.cdb ] \ [ -B banner ] \ @@ -392,8 +470,6 @@ unsigned long numchildren = 0; int flag1 = 0; int flag3 = 0; unsigned long backlog = 20; -unsigned long uid = 0; -unsigned long gid = 0; void printstatus(void) { @@ -449,7 +525,7 @@ int main(int argc,char * const *argv) { int s; int t; - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:nN")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -485,8 +561,8 @@ int main(int argc,char * const *argv) { case 's': flagsslenv = 1; break; case 'E': flagtcpenv = 0; break; case 'e': flagtcpenv = 1; break; - case 'A': flagafter = 0; break; - case 'a': flagafter = 1; break; + case 'n': flagsslwait = 1; break; + case 'N': flagsslwait = 0; break; default: usage(); } argc -= optind; @@ -566,13 +642,6 @@ int main(int argc,char * const *argv) { strerr_die2sys(111,FATAL,"unable to listen: "); ndelay_off(s); - if (!flagafter) { - if (gid) if (prot_gid(gid) == -1) - strerr_die2sys(111,FATAL,"unable to set gid: "); - if (uid) if (prot_uid(uid) == -1) - strerr_die2sys(111,FATAL,"unable to set uid: "); - } - localportstr[fmt_ulong(localportstr,localport)] = 0; if (flag1) { buffer_init(&b,buffer_unixwrite,1,bspace,sizeof bspace); @@ -603,13 +672,6 @@ int main(int argc,char * const *argv) { if (!ssl_params(ctx,dhfile,rsalen)) strerr_die2x(111,FATAL,"unable to set cipher parameters"); - if (flagafter) { - if (gid) if (prot_gid(gid) == -1) - strerr_die2sys(111,FATAL,"unable to set gid: "); - if (uid) if (prot_uid(uid) == -1) - strerr_die2sys(111,FATAL,"unable to set uid: "); - } - if (!ssl_ciphers(ctx,ciphers)) strerr_die2x(111,FATAL,"unable to set cipher list"); @@ -624,8 +686,9 @@ int main(int argc,char * const *argv) { strerr_warn6("sslserver: param ",strnum," ",dhfile," ",strnum2,0); } - close(0); - close(1); + close(0); open_read("/dev/null"); + close(1); open_append("/dev/null"); + printstatus(); for (;;) { @@ -650,3 +713,11 @@ int main(int argc,char * const *argv) { close(t); } } + +/* taken from 0.68 */ +char *ssl_error_str(int e) +{ + SSL_load_error_strings(); + return ERR_error_string(e,0); +} + From 69a15e1579a1cd546b69b5d87d3419cfc4bfe2ac Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 3 Sep 2011 23:33:53 -0400 Subject: [PATCH 3/7] TLS support for client, ucspi-tls code refactoring and cleanup * Add delayed TLS support with ucspi-tls to sslclient (sponsored by Meixler Technologies, Inc.) * Refactor and improve ucspi-tls code * Add sample client code in ucspitls.[ch] * Add ucspi-ssl test program --- src/ucspissltest.c | 206 ++++++++++++++++++++++++++++++++++++++++++ src/ucspitls.c | 107 ++++++++++++++++++++++ src/ucspitls.h | 1 + src/ucspitls_master.c | 44 +++++++++ src/ucspitls_master.h | 1 + 5 files changed, 359 insertions(+) create mode 100644 src/ucspissltest.c create mode 100644 src/ucspitls.c create mode 100644 src/ucspitls.h create mode 100644 src/ucspitls_master.c create mode 100644 src/ucspitls_master.h diff --git a/src/ucspissltest.c b/src/ucspissltest.c new file mode 100644 index 0000000..4acdba1 --- /dev/null +++ b/src/ucspissltest.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ucspitls.h" +#include "sgetopt.h" + +void usage(char *progname, int exit_status) { + printf("Usage: %s [-yYsScCvqQ]\n", progname); + printf( + "\t-y: Delay SSL until SIGUSR1 is received\n" + "\t-Y: Disable delayed SSL; plaintext mode, or SSL handled in sslclient/sslserver (default)\n" + "\t-s: Send SSL environment\n" + "\t-S: Don't send SSL environment (default)\n" + "\t-c: Client mode, for use with sslclient\n" + "\t-C: Server mode, for use with sslserver (default)\n" + "\t-v: Verbose mode\n" + "\t-q: Quiet mode\n" + "\t-Q: Non-quiet, non-verbose output (default)\n" + ); + printf("Signals:\n" + "\tSIGUSR1: Switch to SSL mode (if running with -y)\n" + "\tSIGUSR2: Dump environment to stderr\n" + ); + printf("Examples:\n" + " Delayed TLS server with no environment (send SIGUSR1 to start TLS):\n" + "\tenv DHFILE=./dh1024.pem CERTFILE=./localhost.cert KEYFILE=./localhost.key ./sslserver -3 3 0 ) { + fprintf(stderr,"%s started, pid %d\n", argv[0], getpid()); + if (flagsslwait) { + fprintf(stderr,"Send USR1 to activate TLS\n"); + } + fprintf(stderr,"Send USR2 to dump environment\n"); + } + + /* Initialize data structures for select */ + FD_ZERO(&read_fds); + max_read_fd = -1; + for(i=0;i 0) { + fprintf(stderr,"SSL requested, starting\n"); + } + if (!ucspitls(flagsslenv,readfd,writefd)) { + fprintf(stderr,"SSL activation failed\n"); + return 127; + } + if (verbosity > 0) { + fprintf(stderr,"SSL complete\n"); + } + } + if (dump_env) { + dump_env = 0; + system("env >&2"); + } + + /* See what's readable with select(). Note this is not a + * super-efficient implementation, it really is designed for + * testing. */ + for(i=0;i 0) { + fprintf(stderr,"fd %d closed\n", fd_sets[i][0]); + } + return fd_sets[i][0]; + } else if (nr < 0) { + /* Error */ + fprintf(stderr,"read from fd %d failed: %s\n", fd_sets[i][0], strerror(errno)); + return fd_sets[i][0]; + } else { + /* Successfully read nr bytes */ + total_written = 0; + while (total_written < nr) { + nw = write(fd_sets[i][1], buf + total_written, nr - total_written); + if (nw <= 0) { + fprintf(stderr,"write to fd %d failed: %s\n", fd_sets[i][1], strerror(errno)); + return fd_sets[i][1]; + } + total_written += nw; + } + } + } + } + } + return 0; +} diff --git a/src/ucspitls.c b/src/ucspitls.c new file mode 100644 index 0000000..2ede81c --- /dev/null +++ b/src/ucspitls.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 16384 + +/* This is written in a simple style, not using the ucspi-ssl string + * libraries, so it is easy to embed in other programs. + */ + +/* Read a file descriptor from the environment, or return -1 on error +*/ +static int fdenv(const char *envname) { + char *fdstr; + long fd; + + if (!(fdstr=getenv(envname))) + return -1; + errno = 0; + fd = strtol(fdstr, NULL, 10); + if (errno != 0) return -1; + if (fd < 0 || fd > INT_MAX) return -1; + + return fd; +} + +/* Activate UCSPI-TLS */ +int ucspitls(int want_env, int readfd, int writefd) +{ + int fd; + char *addenv = malloc(BUFSIZE); + int curbufsize = BUFSIZE; + int readret; + int totalrb; + + /* Figure out our control FD */ + if ( (fd = fdenv("SSLCTLFD")) < 0) { + return 0; + } + + if (want_env) { + /* Request the environment on the SSL control FD */ + + if (write((int)fd, "Y", 1) < 1) + return 0; + } else { + /* Activate SSL but don't request the environment */ + if (write((int)fd, "y", 1) < 1) + return 0; + } + + + /* Read what is sent over the file descriptor. This should + * basically always happen in one read, so other situations are + * handled correctly but inefficiently. + */ + totalrb = 0; + addenv[0] = '\0'; + while ((readret = read(fd,addenv + totalrb,curbufsize - totalrb)) > 0) { + totalrb += readret; + if (totalrb == curbufsize) { + curbufsize += BUFSIZE; + addenv = realloc(addenv, curbufsize); + } + } + if (readret == -1) { + fprintf(stderr,"Error reading SSL environment: %s\n",strerror(errno)); + return 0; + } + + if (want_env) { + /* Parse the variables we read, and add them to the environment */ + char *nextenv = addenv; + while (*nextenv) { + char *val = strchr(nextenv,'='); + if (val && strncmp(nextenv,"SSL_",4) == 0) { + *val = '\0'; + ++val; + setenv(nextenv,val,1); + } else { + val = nextenv; // So we will start searching in the right place + } + nextenv = strchr(val, '\0') + 1; + } + } + + /* Now get the new file descriptors for reading and writing, and dup + * them to the client's read and write fd, respectively. The client + * should be sure to discard any buffered data from before encyption + * was activated, to avoid any issues like CVE-2011-0411. + */ + if ((fd = fdenv("SSLREADFD")) < 0) + return 0; + if (dup2((int)fd,readfd) == -1) + return 0; + + if ((fd = fdenv("SSLWRITEFD")) < 0) + return 0; + if (dup2((int)fd,writefd) == -1) + return 0; + + /* It worked! */ + return 1; +} diff --git a/src/ucspitls.h b/src/ucspitls.h new file mode 100644 index 0000000..5a54500 --- /dev/null +++ b/src/ucspitls.h @@ -0,0 +1 @@ +int ucspitls(int want_env, int readfd, int writefd); diff --git a/src/ucspitls_master.c b/src/ucspitls_master.c new file mode 100644 index 0000000..e6b4142 --- /dev/null +++ b/src/ucspitls_master.c @@ -0,0 +1,44 @@ +#include +#include +#include "wait.h" +#include "strerr.h" + +#define FATAL "Fatal error in SSL activation: " + +/* Returns UCSPI-TLS command character (y or Y) on success. + * On failure or child exit, exits directly and does not return. + */ +char ucspitls_master_wait_for_activation(int fd) { + char sslctl_read_ret; + char sslctl_cmd; + int wstat; + + /* Read the TLS command socket. This will block until/unless + * TLS is requested. + */ + while(1) { + sslctl_read_ret = read(fd,&sslctl_cmd,1); + if ( sslctl_read_ret == 1 ) { + /* SSL was requested */ + break; + } else if (sslctl_read_ret == 0) { + /* EOF from client, it must have exited */ + if (wait_nohang(&wstat) <= 0) { + strerr_die2sys(111, FATAL, "Error waiting for child socket: "); + } + _exit(wait_exitcode(wstat)); + } else if (sslctl_read_ret < 0) { + /* Error. Is it retryable? */ + if (errno == EAGAIN || errno == EINTR) { + /* Do nothing, let the loop run again */ + } else { + strerr_die2sys(111,FATAL,"Read error on SSL control descriptor"); + } + } else { + /* Too many characters read, should not really happen */ + strerr_die2x(111,FATAL,"Protocol error on SSL control descriptor: too many characters read"); + } + } + + return sslctl_cmd; +} diff --git a/src/ucspitls_master.h b/src/ucspitls_master.h new file mode 100644 index 0000000..acb84db --- /dev/null +++ b/src/ucspitls_master.h @@ -0,0 +1 @@ +char ucspitls_master_wait_for_activation(int fd); From eb45b288886ff80d3c9198154e77551617ed2295 Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 3 Sep 2011 23:34:28 -0400 Subject: [PATCH 4/7] TLS support for client, ucspi-tls code refactoring and cleanup * Add delayed TLS support with ucspi-tls to sslclient (sponsored by Meixle * Refactor and improve ucspi-tls code * Add sample client code in ucspitls.[ch] * Add ucspi-ssl test program --- src/CHANGES | 10 +++ src/Makefile | 27 ++++-- src/pathexec.h | 2 - src/pathexec_env.c | 6 -- src/sslclient.c | 200 +++++++++++++++++++++++++++++++-------------- src/sslperl.c | 1 + src/sslserver.c | 95 ++++++++++----------- 7 files changed, 219 insertions(+), 122 deletions(-) diff --git a/src/CHANGES b/src/CHANGES index 6b0bbc8..f856954 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -69,3 +69,13 @@ 20050717 Version 0.70. + +20110901 (sgifford@suspectclass.com) + Add support for UCSPI-TLS in server (Scott Gifford) + Add support for UCSPI-TLS in client (Scott Gifford, sponsored by Meixler Technologies, Inc.) + For details see: http://www.suspectclass.com/sgifford/ucspi-tls/ +\ No newline at end of file + Add privilege separation to sslserver + Add chroot(2) support to sslserver + Add switching user ID and group ID in sslsever + \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index eaab0aa..6b0f0f6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,7 +36,8 @@ clean: tai_pack.o taia_add.o taia_approx.o taia_frac.o taia_less.o taia_now.o \ taia_pack.o taia_sub.o taia_uint.o timeoutconn.o uint16_pack.o \ uint16_unpack.o uint32.h uint32_pack.o uint32_unpack.o uint64.h unix.a \ - wait_nohang.o wait_pid.o + wait_nohang.o wait_pid.o tryssl.o \ + ucspissltest ucspissltest.o ucspitls.o alloc.o: compile alloc.c alloc.h error.h ./compile alloc.c @@ -312,7 +313,7 @@ ip4_scan.o: compile ip4_scan.c scan.h ip4.h it: it-base it-sslperl sysdeps -it-base: sslclient sslserver https@ sslcat sslconnect sslprint sysdeps +it-base: sslclient sslserver https@ sslcat sslconnect sslprint sysdeps ucspissltest it-sslperl: sslperl sysdeps @@ -504,9 +505,9 @@ sslcat: home warn-auto.sh sslcat.sh chmod 755 sslcat sslclient: load sslclient.o remoteinfo.o timeoutconn.o ssl.a unix.a \ -auto_cafile.o auto_cadir.o auto_ciphers.o socket.lib ssl.lib +auto_cafile.o auto_cadir.o auto_ciphers.o wait_nohang.o ucspitls_master.o socket.lib ssl.lib ./load sslclient remoteinfo.o timeoutconn.o ssl.a unix.a auto_cafile.o \ - auto_cadir.o auto_ciphers.o `cat socket.lib` `cat ssl.lib` + auto_cadir.o auto_ciphers.o wait_nohang.o ucspitls_master.o `cat socket.lib` `cat ssl.lib` sslclient.o: compile sslclient.c ssl.h sig.h exit.h sgetopt.h uint16.h \ fmt.h scan.h str.h ip4.h uint16.h socket.h fd.h stralloc.h buffer.h \ @@ -562,10 +563,10 @@ sslprint.o: compile sslprint.c buffer.h env.h sslserver: load sslserver.o auto_cafile.o auto_ccafile.o auto_cadir.o \ auto_dhfile.o auto_certfile.o auto_keyfile.o auto_ciphers.o rules.o \ -remoteinfo.o timeoutconn.o cdb.a ssl.a unix.a socket.lib ssl.lib +remoteinfo.o timeoutconn.o wait_nohang.o ucspitls_master.o ucspitls.o cdb.a ssl.a unix.a socket.lib ssl.lib ./load sslserver auto_cafile.o auto_ccafile.o auto_cadir.o auto_dhfile.o \ auto_certfile.o auto_keyfile.o auto_ciphers.o rules.o remoteinfo.o \ - timeoutconn.o cdb.a ssl.a unix.a `cat socket.lib` `cat ssl.lib` + timeoutconn.o wait_nohang.o ucspitls_master.o ucspitls.o cdb.a ssl.a unix.a `cat socket.lib` `cat ssl.lib` sslserver.o: compile sslserver.c ssl.h uint16.h str.h byte.h fmt.h scan.h \ ip4.h fd.h exit.h env.h prot.h open.h wait.h stralloc.h alloc.h buffer.h \ @@ -575,7 +576,7 @@ auto_ccafile.h auto_dhfile.h auto_certfile.h auto_keyfile.h \ auto_ciphers.h stralloc.h gen_alloc.h buffer.h stralloc.h subgetopt.h \ uint16.h stralloc.h uint16.h stralloc.h stralloc.h iopause.h taia.h \ gen_alloc.h gen_alloc.h gen_alloc.h gen_alloc.h gen_alloc.h taia.h tai.h \ -tai.h uint64.h uint64.h +tai.h uint64.h uint64.h ucspitls.h ./compile sslserver.c str_chr.o: compile str_chr.c str.h @@ -738,3 +739,15 @@ wait_nohang.o: compile wait_nohang.c haswaitp.h wait_pid.o: compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +ucspissltest.o: ucspissltest.c ucspitls.h + ./compile ucspissltest.c + +ucspitls.o: ucspitls.c ucspitls.h + ./compile ucspitls.c + +ucspitls_master.o: ucspitls_master.c ucspitls_master.h wait.h strerr.h + ./compile ucspitls_master.c + +ucspissltest: load ucspissltest.o ucspitls.o sgetopt.o sgetopt.h subgetopt.h subgetopt.o buffer.o buffer_2.o + ./load ucspissltest ucspitls.o unix.a diff --git a/src/pathexec.h b/src/pathexec.h index f4229d8..d0661a5 100644 --- a/src/pathexec.h +++ b/src/pathexec.h @@ -2,11 +2,9 @@ #ifndef PATHEXEC_H #define PATHEXEC_H -#include "stralloc.h" extern void pathexec_run(const char *,char * const *,char * const *); extern int pathexec_env(const char *,const char *); -extern int pathexec_multienv(stralloc *); extern void pathexec(char * const *); #endif diff --git a/src/pathexec_env.c b/src/pathexec_env.c index 47f545d..b4d1bd1 100644 --- a/src/pathexec_env.c +++ b/src/pathexec_env.c @@ -22,12 +22,6 @@ int pathexec_env(const char *s,const char *t) return stralloc_cat(&plus,&tmp); } -int pathexec_multienv(stralloc *sa) -{ - if (!sa) return 1; - return stralloc_cat(&plus,sa); -} - void pathexec(char * const *argv) { char **e; diff --git a/src/sslclient.c b/src/sslclient.c index ed35bda..0feb3d2 100644 --- a/src/sslclient.c +++ b/src/sslclient.c @@ -31,6 +31,7 @@ #include "byte.h" #include "ndelay.h" #include "wait.h" +#include "ucspitls_master.h" #define FATAL "sslclient: fatal: " #define CONNECT "sslclient: unable to connect to " @@ -71,6 +72,7 @@ int flagremotehost = 1; int flag3 = 0; int flagsslenv = 0; int flagtcpenv = 0; +int flagsslwait = 0; unsigned long itimeout = 26; unsigned long ctimeout[2] = { 2, 58 }; unsigned int progtimeout = 3600; @@ -130,6 +132,62 @@ int passwd_cb(char *buf,int size,int rwflag,void *userdata) { return password.len; } +SSL *start_ssl(int s) { + int cloop; + SSL *ssl; + + ctx = ssl_client(); + ssl_errstr(); + if (!ctx) + strerr_die2x(111,FATAL,"unable to create SSL context"); + + switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) { + case -1: strerr_die2x(111,FATAL,"unable to load certificate"); + case -2: strerr_die2x(111,FATAL,"unable to load key pair"); + case -3: strerr_die2x(111,FATAL,"key does not match certificate"); + default: break; + } + + if (!ssl_ca(ctx,cafile,cadir,verifydepth)) + strerr_die2x(111,FATAL,"unable to load CA list"); + + if (!ssl_ciphers(ctx,ciphers)) + strerr_die2x(111,FATAL,"unable to set cipher list"); + + ssl = ssl_new(ctx,s); + if (!ssl) strerr_die2x(111,FATAL,"unable to create SSL instance"); + + for (cloop = 0;cloop < 2;++cloop) { + if (!ssl_timeoutconn(ssl,ctimeout[cloop])) goto SSLCONNECTED; + if (!cloop && ctimeout[1]) continue; + strerr_warn2(FATAL,"unable to SSL connect:",&strerr_sys); + ssl_error(error_warn); + } + return NULL; /* Failure */ + + SSLCONNECTED: + ndelay_off(s); + + if (verbosity >= 2) + strerr_warn1("sslclient: ssl connect",0); + + if (flagservercert) + switch(ssl_verify(ssl,flagname ? hostname : 0)) { + case -1: + strerr_die2x(111,FATAL,"unable to verify server certificate"); + case -2: + strerr_die2x(111,FATAL,"no server certificate"); + case -3: + strerr_die2x(111,FATAL,"server name does not match certificate"); + default: break; + } + + if (!flagdelay) + socket_tcpnodelay(s); /* if it fails, bummer */ + + return ssl; +} + int main(int argc,char * const *argv) { unsigned long u; int opt; @@ -137,8 +195,12 @@ int main(int argc,char * const *argv) { int j; int s; int cloop; - SSL *ssl; + SSL *ssl = NULL; int wstat; + int sslctl[2]; + char sslctl_cmd; + stralloc ssl_env = { 0 }; + buffer ssl_env_buf; dns_random_init(seed); @@ -146,7 +208,7 @@ int main(int argc,char * const *argv) { close(7); sig_ignore(sig_pipe); - while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:a:A:c:C:k:V:3eEsSnN0xXw:")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:a:A:c:C:k:V:3eEsSnN0xXw:yY")) != opteof) switch(opt) { case 'd': flagdelay = 1; break; case 'D': flagdelay = 0; break; @@ -181,6 +243,8 @@ int main(int argc,char * const *argv) { case 'n': flagname = 1; break; case 'x': flagservercert = 1; break; case 'X': flagservercert = 0; break; + case 'y': flagsslwait = 1; break; + case 'Y': flagsslwait = 0; break; default: usage(); } argv += optind; @@ -305,57 +369,24 @@ int main(int argc,char * const *argv) { } env("SSLREMOTEINFO",x); if (flagtcpenv) env("TCPREMOTEINFO",x); - - ctx = ssl_client(); - ssl_errstr(); - if (!ctx) - strerr_die2x(111,FATAL,"unable to create SSL context"); - - switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) { - case -1: strerr_die2x(111,FATAL,"unable to load certificate"); - case -2: strerr_die2x(111,FATAL,"unable to load key pair"); - case -3: strerr_die2x(111,FATAL,"key does not match certificate"); - default: break; - } - - if (!ssl_ca(ctx,cafile,cadir,verifydepth)) - strerr_die2x(111,FATAL,"unable to load CA list"); - - if (!ssl_ciphers(ctx,ciphers)) - strerr_die2x(111,FATAL,"unable to set cipher list"); - - ssl = ssl_new(ctx,s); - if (!ssl) strerr_die2x(111,FATAL,"unable to create SSL instance"); - - for (cloop = 0;cloop < 2;++cloop) { - if (!ssl_timeoutconn(ssl,ctimeout[cloop])) goto SSLCONNECTED; - if (!cloop && ctimeout[1]) continue; - strerr_warn2(FATAL,"unable to SSL connect:",&strerr_sys); - ssl_error(error_warn); - } - - _exit(111); - - SSLCONNECTED: - - ndelay_off(s); - - if (verbosity >= 2) - strerr_warn1("sslclient: ssl connect",0); - - if (flagservercert) - switch(ssl_verify(ssl,flagname ? hostname : 0)) { - case -1: - strerr_die2x(111,FATAL,"unable to verify server certificate"); - case -2: - strerr_die2x(111,FATAL,"no server certificate"); - case -3: - strerr_die2x(111,FATAL,"server name does not match certificate"); - default: break; + + if (flagsslwait) { + /* Create delayed SSL control socket */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sslctl) == -1) strerr_die2sys(111,FATAL,"unable to create socketpair: "); + + /* Copy the socket to file descriptors 6 and 7 right now, so we + * don't use them for something else later. + */ + if (fd_copy(6,s) == -1) + strerr_die2sys(111,FATAL,"unable to set up descriptor 6: "); + if (fd_copy(7,s) == -1) + strerr_die2sys(111,FATAL,"unable to set up descriptor 7: "); + } else { + /* If we aren't delaying SSL, start it now, so we won't run the client if it fails */ + if (!(ssl = start_ssl(s))) { + _exit(111); } - - if (!flagdelay) - socket_tcpnodelay(s); /* if it fails, bummer */ + } if (pipe(pi) == -1) strerr_die2sys(111,FATAL,"unable to create pipe: "); if (pipe(po) == -1) strerr_die2sys(111,FATAL,"unable to create pipe: "); @@ -372,11 +403,47 @@ int main(int argc,char * const *argv) { switch(opt = fork()) { case -1: + /* Error */ strerr_die2sys(111,FATAL,"unable to fork: "); case 0: + /* Child runs after switch */ break; default: + /* Parent */ close(pi[0]); close(po[1]); + if (flagsslwait) { + if (close(sslctl[1]) != 0) { + strerr_die2sys(111, FATAL, "Error closing SSL control socket: "); + } + + /* This will exit on a fatal error or if the client quits + * without activating SSL + */ + sslctl_cmd = ucspitls_master_wait_for_activation(sslctl[0]); + + /* If we got here, SSL has been requested. */ + if (!(ssl = start_ssl(s))) { + _exit(111); + } + + if (sslctl_cmd == 'Y') { + if (!ssl_client_env(ssl, &ssl_env)) nomem(); + stralloc_0(&ssl_env); /* Add another NUL */ + buffer_init(&ssl_env_buf,buffer_unixwrite,sslctl[0],NULL,0); + if (buffer_putflush(&ssl_env_buf, ssl_env.s, ssl_env.len) == -1) { + strerr_die2sys(111, FATAL, "unable to write SSL environment: "); + } + } else if (sslctl_cmd != 'y') { + strerr_die2x(111,FATAL,"Unrecognized command on SSL socket"); + } + if (close(sslctl[0]) != 0) { + strerr_die2sys(111, FATAL, "Error closing SSL control socket: "); + } + } + + if (verbosity >= 2) + strerr_warn1("sslclient: ssl_io starting",0); + if (ssl_io(ssl,pi[1],po[0],progtimeout)) { strerr_warn2(FATAL,"unable to speak SSL:",&strerr_sys); ssl_error(error_warn); @@ -389,17 +456,28 @@ int main(int argc,char * const *argv) { _exit(wait_exitcode(wstat)); _exit(0); } - ssl_close(ssl); close(pi[1]); close(po[0]); - - if (flagsslenv && !ssl_client_env(ssl,0)) nomem(); - - if (fd_move(6,pi[0]) == -1) - strerr_die2sys(111,FATAL,"unable to set up descriptor 6: "); - if (fd_move(7,po[1]) == -1) - strerr_die2sys(111,FATAL,"unable to set up descriptor 7: "); + + /* Child */ + close(pi[1]); close(po[0]); close(sslctl[0]); + + if (flagsslwait) { + strnum[fmt_ulong(strnum,sslctl[1])]=0; + env("SSLCTLFD",strnum); + strnum[fmt_ulong(strnum,pi[0])]=0; + env("SSLREADFD",strnum); + strnum[fmt_ulong(strnum,po[1])]=0; + env("SSLWRITEFD",strnum); + } else { + ssl_close(ssl); + if (fd_move(6,pi[0]) == -1) + strerr_die2sys(111,FATAL,"unable to set up descriptor 6: "); + if (fd_move(7,po[1]) == -1) + strerr_die2sys(111,FATAL,"unable to set up descriptor 7: "); + + if (flagsslenv && !ssl_client_env(ssl,0)) nomem(); + } sig_uncatch(sig_pipe); pathexec(argv); strerr_die4sys(111,FATAL,"unable to run ",*argv,": "); } - diff --git a/src/sslperl.c b/src/sslperl.c index 8cf7811..e32a1cb 100644 --- a/src/sslperl.c +++ b/src/sslperl.c @@ -13,6 +13,7 @@ #endif extern const char *self; +extern char **environ; /* ActiveState Perl requires this be called my_perl */ static PerlInterpreter *my_perl = 0; diff --git a/src/sslserver.c b/src/sslserver.c index 5b0c06c..52f4494 100644 --- a/src/sslserver.c +++ b/src/sslserver.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "ssl.h" @@ -41,6 +42,8 @@ #include "auto_keyfile.h" #include "auto_ciphers.h" #include "fmt.h" +#include "ucspitls_master.h" +#include "ucspitls.h" int verbosity = 1; int flagkillopts = 1; @@ -117,6 +120,7 @@ char buf[SSL_NAME_LEN]; /* ---------------------------- child */ #define DROP "sslserver: warning: dropping connection, " +#define DROPSSL "sslserver: warning: SSL disabled due to server error, " int flagdeny = 0; int flagallownorules = 0; @@ -189,12 +193,11 @@ void doit(int t) { int wstat; int sslctl[2]; char *s; - long tmp_long; - char ssl_cmd; + unsigned long tmp_long; + char sslctl_cmd; stralloc ssl_env = { 0 }; - int bytesleft; - char envbuf[8192]; - + buffer ssl_env_buf; + if (pipe(pi) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); if (pipe(po) == -1) strerr_die2sys(111,DROP,"unable to create pipe: "); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sslctl) == -1) strerr_die2sys(111,DROP,"unable to create socketpair: "); @@ -209,36 +212,37 @@ void doit(int t) { /* Parent */ close(pi[0]); close(po[1]); close(sslctl[1]); - + if ((s=env_get("SSL_CHROOT"))) if (chroot(s) == -1) - strerr_die2x(111,DROP,"unable to chroot"); + strerr_die2x(111,DROPSSL,"unable to chroot"); if ((s=env_get("SSL_GID"))) { scan_ulong(s,&tmp_long); gid = tmp_long; } - if (gid) if (prot_gid(gid) == -1) strerr_die2sys(111,FATAL,"unable to set gid: "); + if (gid) if (prot_gid(gid) == -1) strerr_die2sys(111,DROPSSL,"unable to set gid: "); if ((s=env_get("SSL_UID"))) { scan_ulong(s,&tmp_long); uid = tmp_long; } if (uid) if (prot_uid(uid) == -1) - strerr_die2sys(111,FATAL,"unable to set uid: "); + strerr_die2sys(111,DROPSSL,"unable to set uid: "); - /* Read the TLS command socket. This will block until/unless - * TLS is requested. + /* This will exit on a fatal error or if the client quits + * without activating SSL */ - if (read(sslctl[0],&ssl_cmd,1) == 1) { - ssl = ssl_new(ctx,t); - if (!ssl) strerr_die2x(111,DROP,"unable to create SSL instance"); - if (ndelay_on(t) == -1) - strerr_die2sys(111,DROP,"unable to set socket options: "); - if (ssl_timeoutaccept(ssl,ssltimeout) == -1) - strerr_die3x(111,DROP,"unable to accept SSL: ",ssl_error_str(ssl_errno)); - } - + sslctl_cmd = ucspitls_master_wait_for_activation(sslctl[0]); + + /* If we got here, SSL must have been activated */ + ssl = ssl_new(ctx,t); + if (!ssl) strerr_die2x(111,DROP,"unable to create SSL instance"); + if (ndelay_on(t) == -1) + strerr_die2sys(111,DROP,"unable to set socket options: "); + if (ssl_timeoutaccept(ssl,ssltimeout) == -1) + strerr_die3x(111,DROP,"unable to accept SSL: ",ssl_error_str(ssl_errno)); + if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; strerr_warn3("sslserver: ssl ",strnum," accept ",0); @@ -256,15 +260,21 @@ void doit(int t) { } } - if (ssl_cmd == 'Y') { + if (sslctl_cmd == 'Y') { ssl_server_env(ssl, &ssl_env); stralloc_0(&ssl_env); /* Add another NUL */ - - for(bytesleft = ssl_env.len; bytesleft>0; bytesleft-=j) - if ( (j=write(sslctl[0], ssl_env.s, bytesleft)) < 0) - strerr_die2sys(111, FATAL, "unable to write SSL environment: "); + buffer_init(&ssl_env_buf,buffer_unixwrite,sslctl[0],NULL,0); + if (buffer_putflush(&ssl_env_buf, ssl_env.s, ssl_env.len) == -1) { + strerr_die2sys(111, FATAL, "unable to write SSL environment: "); + } + } else if (sslctl_cmd != 'y') { + strerr_die2x(111,DROP,"Protocol error on SSL control descriptor: invalid command character read"); } + if (close(sslctl[0]) != 0) { + strerr_die2sys(111, DROP, "Error closing SSL control socket: "); + } + if (ssl_io(ssl,pi[1],po[0],3600) != 0) strerr_die3x(111,DROP,"unable to speak SSL: ",ssl_error_str(ssl_errno)); if (wait_nohang(&wstat) > 0) @@ -274,8 +284,11 @@ void doit(int t) { } /* Child-only below this point */ + if (close(sslctl[0]) != 0) { + strerr_die2sys(111, DROP, "Error closing SSL control socket: "); + } + remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; - if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; strerr_warn4("sslserver: pid ",strnum," from ",remoteipstr,0); @@ -386,18 +399,18 @@ void doit(int t) { if (fcntl(sslctl[1],F_SETFD,0) == -1) strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); strnum[fmt_ulong(strnum,sslctl[1])]=0; - env("SSLCTLFD",strnum); + setenv("SSLCTLFD",strnum,1); if (fcntl(pi[0],F_SETFD,0) == -1) strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); strnum[fmt_ulong(strnum,pi[0])]=0; - env("SSLREADFD",strnum); + setenv("SSLREADFD",strnum,1); if (fcntl(po[1],F_SETFD,0) == -1) strerr_die2sys(111,FATAL,"unable to clear close-on-exec flag"); strnum[fmt_ulong(strnum,po[1])]=0; - env("SSLWRITEFD",strnum); - + setenv("SSLWRITEFD",strnum,1); + if (flagsslwait) { if (fd_copy(0,t) == -1) strerr_die2sys(111,DROP,"unable to set up descriptor 0: "); @@ -422,19 +435,9 @@ void doit(int t) { } if (!flagsslwait) { - ssl_cmd = flagsslenv ? 'Y' : 'y'; - if (write(sslctl[1], &ssl_cmd, 1) < 1) - strerr_die2sys(111,DROP,"unable to start SSL: "); - if (flagsslenv) { - while ((j=read(sslctl[1],envbuf,8192)) > 0) { - stralloc_catb(&ssl_env,envbuf,j); - if (ssl_env.len >= 2 && ssl_env.s[ssl_env.len-2]==0 && ssl_env.s[ssl_env.len-1]==0) - break; - } - if (j < 0) - strerr_die2sys(111,DROP,"unable to read SSL environment: "); - pathexec_multienv(&ssl_env); - } + strnum[fmt_ulong(strnum,flagsslenv)] = 0; + strerr_warn2("flagsslenv: ", strnum, 0); + ucspitls(flagsslenv,0,1); } pathexec(prog); @@ -525,7 +528,7 @@ int main(int argc,char * const *argv) { int s; int t; - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:nN")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:nNyY")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -561,8 +564,8 @@ int main(int argc,char * const *argv) { case 's': flagsslenv = 1; break; case 'E': flagtcpenv = 0; break; case 'e': flagtcpenv = 1; break; - case 'n': flagsslwait = 1; break; - case 'N': flagsslwait = 0; break; + case 'n': case 'y': flagsslwait = 1; break; + case 'N': case 'Y': flagsslwait = 0; break; default: usage(); } argc -= optind; From 9cf8a3892cfb351692f69868725ccf36c2131db5 Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 8 Oct 2011 23:30:01 -0400 Subject: [PATCH 5/7] * Synchronous wait for child process in case we are not activated --- src/ucspitls_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucspitls_master.c b/src/ucspitls_master.c index e6b4142..c83d74e 100644 --- a/src/ucspitls_master.c +++ b/src/ucspitls_master.c @@ -23,7 +23,7 @@ char ucspitls_master_wait_for_activation(int fd) { break; } else if (sslctl_read_ret == 0) { /* EOF from client, it must have exited */ - if (wait_nohang(&wstat) <= 0) { + if ((wait_pid(&wstat,-1)) <= 0) { strerr_die2sys(111, FATAL, "Error waiting for child socket: "); } _exit(wait_exitcode(wstat)); From 2d3180ca13feee225522b64cf48356cc7c7efb0d Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Sat, 8 Oct 2011 23:38:05 -0400 Subject: [PATCH 6/7] * Don't retry after `SSL_ERROR_SSL` --- src/ssl_io.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ssl_io.c b/src/ssl_io.c index d60e55b..b903e41 100644 --- a/src/ssl_io.c +++ b/src/ssl_io.c @@ -104,6 +104,7 @@ int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { case SSL_ERROR_WANT_X509_LOOKUP: break; case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SSL: if (rightstatus == -1) goto done; close(fdleft); leftstatus = -1; @@ -116,10 +117,6 @@ int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { /* premature close */ if (errno == error_connreset && rightstatus == -1) goto done; goto bomb; - case SSL_ERROR_SSL: - if (errno == error_again || errno == error_intr) break; - if (!errno) break; - goto bomb; default: close(fdleft); leftstatus = -1; From af2aa8dbca9363d3a443e4d4a87615b621866a5f Mon Sep 17 00:00:00 2001 From: Scott Gifford Date: Mon, 10 Oct 2011 00:08:00 -0400 Subject: [PATCH 7/7] * Modify ssl_io to take an extensible ssl_io_opt structure instead of just the timeout. * Implement new -j/-J switch to activate "just_shutdown" option in ssl_io. This option causes the SSL connection to be shutdown before the SSL protocol has been fully shutdown. This is allowed by the specs, and seems to improve compatibility, and so is the default. * Document added options in documentation. --- src/UCSPI-SSL | 27 ++++++++++++++++++--------- src/ssl.h | 8 +++++++- src/ssl_io.c | 14 ++++++++------ src/sslclient.c | 13 +++++++++---- src/sslhandle.c | 13 +++++++++---- src/sslserver.c | 15 ++++++++++----- 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/UCSPI-SSL b/src/UCSPI-SSL index 69bd25e..f538323 100644 --- a/src/UCSPI-SSL +++ b/src/UCSPI-SSL @@ -37,12 +37,21 @@ lowercase. SSLREMOTEINFO is a connection-specific string supplied by the remote host via 931/1413/IDENT/TAP. SSL UCSPI tools take a -R option to turn off 931/1413/IDENT/TAP -querying, and a -r option to turn it back on. SSL UCSPI tools take a -I -option to turn off checking for a client certificate, and a -i option to -turn it back on. SSL UCSPI clients take a -p [locport] option to -require a particular TCP port on the local side of the connection. SSL -UCSPI servers take a -1 option to print the local port number (in -decimal, followed by a newline) to descriptor 1 before closing -descriptor 1 and after preparing to receive connections. SSL UCSPI -servers and clients take a -3 option to read a null-terminated key -password from file descriptor 3. +querying, and a -r option to turn it back on. SSL UCSPI tools take a +-I option to turn off checking for a client certificate, and a -i +option to turn it back on. SSL UCSPI tools take a -j option (the +default) to just shutdown the socket when we are done with the SSL +protocol, and a -J option to negotiate a full shutdown (both are +allowed by the protocol). SSL UCSPI clients take a -p [locport] +option to require a particular TCP port on the local side of the +connection. SSL UCSPI servers take a -1 option to print the local port +number (in decimal, followed by a newline) to descriptor 1 before +closing descriptor 1 and after preparing to receive connections. SSL +UCSPI servers recognize the environment variable SSL_CHROOT to put the +ssl handling process into a chroot jail, SSL_GID to run the ssl +handling process with the given group ID, and SSL_UID to run the ssl +handling process with the given user ID. UCSPI servers and clients +take a -3 option to read a null-terminated key password from file +descriptor 3. SSL UCSPI servers and clients take a -y option to delay +starting SSL until the program requests it, and a -Y option (the +default) to immediately begin negotiating SSL. diff --git a/src/ssl.h b/src/ssl.h index a92d106..d0b9268 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -6,8 +6,14 @@ #define SSL_NAME_LEN 256 +struct ssl_io_opt { + unsigned int timeout; + unsigned int just_shutdown; +}; +extern struct ssl_io_opt ssl_io_opt_default; + extern int ssl_errno; -extern int ssl_io(SSL *,int,int,unsigned int); +extern int ssl_io(SSL *,int,int,struct ssl_io_opt); extern SSL_CTX *ssl_context(SSL_METHOD *); extern int ssl_timeoutconn(SSL *,unsigned int); extern int ssl_timeoutaccept(SSL *,unsigned int); diff --git a/src/ssl_io.c b/src/ssl_io.c index b903e41..e01c149 100644 --- a/src/ssl_io.c +++ b/src/ssl_io.c @@ -18,7 +18,9 @@ static char rightbuf[16 * 1024]; static int rightlen; static int rightpos; -int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { +struct ssl_io_opt ssl_io_opt_default = { 3600, 1 }; + +int ssl_io(SSL *ssl,int fdleft,int fdright,struct ssl_io_opt opt) { struct taia now; struct taia deadline; iopause_fd x[4]; @@ -75,7 +77,7 @@ int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { } taia_now(&now); - taia_uint(&deadline,timeout); + taia_uint(&deadline,opt.timeout); taia_add(&deadline,&now,&deadline); iopause(x,xlen,&deadline,&now); for (r = 0;r < xlen;++r) @@ -183,8 +185,8 @@ int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { else if (r == 0) { close(fdright); rightstatus = -1; - if (ssl_shutdown(ssl)) goto done; - if (leftstatus == -1) goto done; + if (ssl_shutdown(ssl) < 0) goto bomb; + if (leftstatus == -1 || opt.just_shutdown) goto done; } else { rightstatus = 1; @@ -231,14 +233,14 @@ int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) { if (leftstatus != -1) close(fdleft); if (rightstatus != -1) close(fdright); if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl); - if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl); + if (!opt.just_shutdown && !ssl_shutdown_pending(ssl)) ssl_shutdown(ssl); shutdown(wfd,2); errno = r; return -1; done: if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl); - if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl); + if (!opt.just_shutdown && !ssl_shutdown_pending(ssl)) ssl_shutdown(ssl); shutdown(wfd,2); if (leftstatus != -1) close(fdleft); if (rightstatus != -1) close(fdright); diff --git a/src/sslclient.c b/src/sslclient.c index 0feb3d2..1130a5f 100644 --- a/src/sslclient.c +++ b/src/sslclient.c @@ -75,7 +75,7 @@ int flagtcpenv = 0; int flagsslwait = 0; unsigned long itimeout = 26; unsigned long ctimeout[2] = { 2, 58 }; -unsigned int progtimeout = 3600; +struct ssl_io_opt io_opt; char iplocal[4] = { 0,0,0,0 }; uint16 portlocal = 0; @@ -202,13 +202,16 @@ int main(int argc,char * const *argv) { stralloc ssl_env = { 0 }; buffer ssl_env_buf; + io_opt = ssl_io_opt_default; + io_opt.timeout = 3600; + dns_random_init(seed); close(6); close(7); sig_ignore(sig_pipe); - while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:a:A:c:C:k:V:3eEsSnN0xXw:yY")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:a:A:c:C:k:V:3eEsSnN0xXw:yYjJ")) != opteof) switch(opt) { case 'd': flagdelay = 1; break; case 'D': flagdelay = 0; break; @@ -225,7 +228,7 @@ int main(int argc,char * const *argv) { if (optarg[j] == '+') ++j; scan_ulong(optarg + j,&ctimeout[1]); break; - case 'w': scan_uint(optarg,&progtimeout); break; + case 'w': scan_uint(optarg,&io_opt.timeout); break; case 'i': if (!ip4_scan(optarg,iplocal)) usage(); break; case 'p': scan_ulong(optarg,&u); portlocal = u; break; case 'a': cafile = optarg; break; @@ -245,6 +248,8 @@ int main(int argc,char * const *argv) { case 'X': flagservercert = 0; break; case 'y': flagsslwait = 1; break; case 'Y': flagsslwait = 0; break; + case 'j': io_opt.just_shutdown = 1; break; + case 'J': io_opt.just_shutdown = 0; break; default: usage(); } argv += optind; @@ -444,7 +449,7 @@ int main(int argc,char * const *argv) { if (verbosity >= 2) strerr_warn1("sslclient: ssl_io starting",0); - if (ssl_io(ssl,pi[1],po[0],progtimeout)) { + if (ssl_io(ssl,pi[1],po[0],io_opt)) { strerr_warn2(FATAL,"unable to speak SSL:",&strerr_sys); ssl_error(error_warn); ssl_close(ssl); diff --git a/src/sslhandle.c b/src/sslhandle.c index 82678ad..b63f6c9 100644 --- a/src/sslhandle.c +++ b/src/sslhandle.c @@ -59,7 +59,7 @@ int flagsslenv = 0; int flagtcpenv = 0; unsigned long timeout = 26; unsigned long ssltimeout = 26; -unsigned int progtimeout = 3600; +struct ssl_io_opt io_opt; int selfpipe[2]; int flagexit = 0; @@ -384,7 +384,7 @@ int doit(int t) { close(pi[0]); close(po[1]); sig_uncatch(sig_child); sig_unblock(sig_child); - if (ssl_io(ssl,pi[1],po[0],progtimeout) == -1) { + if (ssl_io(ssl,pi[1],po[0],io_opt) == -1) { strerr_warn2(DROP,"unable to speak SSL:",0); ssl_error(error_warn); _exit(111); @@ -564,9 +564,12 @@ int main(int argc,char * const *argv) { char ch; struct taia deadline; struct taia stamp; + + io_opt = ssl_io_opt_default; + io_opt.timeout = 3600; self = argv[0]; - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAf:w:")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAf:w:jJ")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -604,7 +607,9 @@ int main(int argc,char * const *argv) { case 'A': flagafter = 0; break; case 'a': flagafter = 1; break; case 'f': lockfile = optarg; break; - case 'w': scan_uint(optarg,&progtimeout); break; + case 'w': scan_uint(optarg,&io_opt.timeout); break; + case 'j': io_opt.just_shutdown = 1; break; + case 'J': io_opt.just_shutdown = 0; break; default: usage(); } argc -= optind; diff --git a/src/sslserver.c b/src/sslserver.c index 52f4494..c6d4612 100644 --- a/src/sslserver.c +++ b/src/sslserver.c @@ -58,7 +58,7 @@ int flagtcpenv = 0; int flagsslwait = 0; unsigned long timeout = 26; unsigned long ssltimeout = 26; -unsigned int progtimeout = 3600; +static struct ssl_io_opt io_opt; static stralloc tcpremoteinfo; @@ -275,7 +275,7 @@ void doit(int t) { strerr_die2sys(111, DROP, "Error closing SSL control socket: "); } - if (ssl_io(ssl,pi[1],po[0],3600) != 0) + if (ssl_io(ssl,pi[1],po[0],io_opt) != 0) strerr_die3x(111,DROP,"unable to speak SSL: ",ssl_error_str(ssl_errno)); if (wait_nohang(&wstat) > 0) _exit(wait_exitcode(wstat)); @@ -527,8 +527,11 @@ int main(int argc,char * const *argv) { unsigned long u; int s; int t; - - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:nNyY")) != opteof) + + io_opt = ssl_io_opt_default; + io_opt.timeout = 3600; + + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:T:u:g:l:b:B:c:pPoO3IiEeSsaAw:nNyYuUjJ")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -550,7 +553,7 @@ int main(int argc,char * const *argv) { case 'r': flagremoteinfo = 1; break; case 't': scan_ulong(optarg,&timeout); break; case 'T': scan_ulong(optarg,&ssltimeout); break; - case 'w': scan_uint(optarg,&progtimeout); break; + case 'w': scan_uint(optarg,&io_opt.timeout); break; case 'U': x = env_get("UID"); if (x) scan_ulong(x,&uid); x = env_get("GID"); if (x) scan_ulong(x,&gid); break; case 'u': scan_ulong(optarg,&uid); break; @@ -566,6 +569,8 @@ int main(int argc,char * const *argv) { case 'e': flagtcpenv = 1; break; case 'n': case 'y': flagsslwait = 1; break; case 'N': case 'Y': flagsslwait = 0; break; + case 'j': io_opt.just_shutdown = 1; break; + case 'J': io_opt.just_shutdown = 0; break; default: usage(); } argc -= optind;