--- FILES 14 Jun 2002 08:47:30 -0000 1.1.1.1 +++ FILES 5 Apr 2004 15:34:57 -0000 1.2 @@ -216,6 +216,7 @@ warn-auto.sh warn-shsgr x86cpuid.c +fixcrio.1 dns_ip6.c dns_ipq6.c dns_nd6.c --- Makefile 14 Jun 2002 08:47:30 -0000 1.1.1.1 +++ Makefile 5 Apr 2004 15:34:57 -0000 1.3 @@ -1,5 +1,13 @@ # Don't edit Makefile! Use conf-* for configuration. +DEFINES=-DWITH_SSL +#add -DWITH_SSL to enable ssl support + +# LIBS for additional libraries and INCS for additional includes +LIBS=-lcrypto -lssl +#INCS=-I/usr/local/include +OPENSSLBIN=openssl + SHELL=/bin/sh default: it @@ -755,7 +763,7 @@ load tcpserver.o rules.o remoteinfo6.o timeoutconn6.o cdb.a dns.a \ time.a unix.a byte.a socket.lib ./load tcpserver rules.o remoteinfo6.o timeoutconn6.o cdb.a \ - dns.a time.a unix.a byte.a `cat socket.lib` + dns.a time.a unix.a byte.a $(LIBS) `cat socket.lib` tcpserver.o: \ compile tcpserver.c uint16.h str.h byte.h fmt.h scan.h ip4.h fd.h \ @@ -764,7 +772,7 @@ socket.h uint16.h ndelay.h remoteinfo.h stralloc.h uint16.h rules.h \ stralloc.h sig.h dns.h stralloc.h iopause.h taia.h tai.h uint64.h \ taia.h uint32.h - ./compile tcpserver.c + ./compile $(DEFINES) $(INCS) tcpserver.c time.a: \ makelib iopause.o tai_pack.o taia_add.o taia_approx.o taia_frac.o \ @@ -967,3 +975,18 @@ clean: rm -f `cat TARGETS` + +cert: + ${OPENSSLBIN} req -new -x509 -nodes \ + -out cert.pem -days 366 \ + -keyout cert.pem + +cert-req: + ${OPENSSLBIN} req -new -nodes \ + -out req.pem \ + -keyout cert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> `head -1 conf-qmail`/control/cert.pem" + + --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fixcrio.1 5 Apr 2004 15:34:57 -0000 1.1 @@ -0,0 +1,15 @@ +.TH fixcrio 1 +.SH NAME +fixcrio \- make sure that there is a CR before each LF +.SH SYNOPSIS +.B fixcrio +.I program +[ +.I arg ... +] +.SH DESCRIPTION +.B fixcrio +inserts CR at the end of each line of input where a CR is not already present. +It does not insert CR at the end of a partial final line. +.SH "SEE ALSO" +addcr(1) --- hier.c 14 Jun 2002 08:47:32 -0000 1.1.1.1 +++ hier.c 5 Apr 2004 15:34:57 -0000 1.2 @@ -26,6 +26,7 @@ c(auto_home,"bin","fixcrio",-1,-1,0755); c(auto_home,"bin","rblsmtpd",-1,-1,0755); + c(auto_home,"man/man1","fixcrio.1",-1,-1,0644); c(auto_home,"man/man1","tcpclient.1",-1,-1,0644); c(auto_home,"man/man1","tcpserver.1",-1,-1,0644); c(auto_home,"man/man1","tcprules.1",-1,-1,0644); } --- tcprules.c 14 Jun 2002 08:47:30 -0000 1.1.1.1 +++ tcprules.c 16 Mar 2004 15:12:26 -0000 1.2 @@ -94,6 +94,7 @@ int len; int fd; int i; + int e; char ch; fn = argv[1]; @@ -154,8 +155,16 @@ while (len) switch(*x) { case ',': + e = byte_chr(x + 1,len - 1,','); i = byte_chr(x,len,'='); - if (i == len) die_bad(); + if (i > e) { + if (e < 2 || x[1] != '!') die_bad(); + if (!stralloc_catb(&data,"-",1)) nomem(); + if (!stralloc_catb(&data,x + 2,e - 1)) nomem(); + if (!stralloc_0(&data)) nomem(); + x += e + 1; len -= e + 1; + break; + } if (!stralloc_catb(&data,"+",1)) nomem(); if (!stralloc_catb(&data,x + 1,i)) nomem(); x += i + 1; len -= i + 1; --- tcprulescheck.c 14 Jun 2002 08:47:30 -0000 1.1.1.1 +++ tcprulescheck.c 16 Mar 2004 15:12:27 -0000 1.2 @@ -22,6 +22,11 @@ buffer_puts(buffer_1,data + 1); buffer_puts(buffer_1,"\n"); break; + case '-': + buffer_puts(buffer_1,"unset environment variable "); + buffer_puts(buffer_1,data + 1); + buffer_puts(buffer_1,"\n"); + break; } ++next0; data += next0; datalen -= next0; --- tcpserver.1.orig 1 Jan 1970 00:00:00 -0000 +++ tcpserver.1 6 Apr 2004 11:49:45 -0000 1.2 @@ -4,7 +4,7 @@ .SH SYNOPSIS .B tcpserver [ -.B \-146jpPhHrRoOdDqQv +.B \-146UXpPhHrRoOdDqQsSv ] [ .B \-c\fIlimit @@ -31,6 +31,9 @@ .B \-t\fItimeout ] [ +.B \-n\fIcertfile +] +[ .B \-I\fIinterface ] .I host @@ -204,11 +207,31 @@ (Default.) Print error messages. .TP +.B \-s +Enable SSL/TLS mode. This modus needs a SSL enabled build and a certificat. +.TP +.B \-S +(Default.) +Don't enable SSL/TLS mode. +.TP +.B \-n\fIcertfile +Instead of the default ./cert.pem certificate us the specified +.IR certfile . +.TP .B \-v Verbose. Print all available messages. .SH "DATA-GATHERING OPTIONS" .TP +.B \-X +With +.BR -x\fIcdb , +allow connections even if +.I cdb +does not exist. +Normally the connection gets dropped. +.SH "DATA-GATHERING OPTIONS" +.TP .B \-p Paranoid. After looking up the remote host name, @@ -256,6 +279,13 @@ after .I timeout seconds. Default: 26. +.SH ENVIRONMENT +.TP +.B SSL_CIPHER +Specifies the ciphers that should be used in SSL/TLS mode. +See +.I openssl(1) +for more information. .SH "SEE ALSO" argv0(1), fixcr(1), @@ -264,3 +294,4 @@ tcprules(1), listen(2), tcp-environ(5) +openssl(1) --- tcpserver.c 14 Jun 2002 08:47:30 -0000 1.1.1.1 +++ tcpserver.c 1 Apr 2005 15:13:15 -0000 1.8 @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include "uint16.h" #include "str.h" #include "byte.h" @@ -39,6 +40,13 @@ int flagparanoid = 0; unsigned long timeout = 26; uint32 netif = 0; +#ifdef WITH_SSL +int flagssl = 0; +struct stralloc certfile = {0}; +#define CERTFILE "./cert.pem" + +void translate(SSL*, int, int, unsigned int); +#endif static stralloc tcpremoteinfo; @@ -130,6 +138,9 @@ env(data + 1,data + 1 + split + 1); } break; + case '-': + env(data + 1, (char *)0); + break; } ++next0; data += next0; datalen -= next0; @@ -271,6 +282,7 @@ void usage(void) { +#ifndef WITH_SSL strerr_warn1("\ tcpserver: usage: tcpserver \ [ -461UXpPhHrRoOdDqQv ] \ @@ -282,8 +294,23 @@ [ -b backlog ] \ [ -l localname ] \ [ -t timeout ] \ +host port program",0); +#else + strerr_warn1("\ +tcpserver: usage: tcpserver \ +[ -461UXpPhHrRoOdDqQsSv ] \ +[ -c limit ] \ +[ -x rules.cdb ] \ +[ -B banner ] \ +[ -g gid ] \ +[ -u uid ] \ +[ -b backlog ] \ +[ -l localname ] \ +[ -t timeout ] \ +[ -n certfile ] \ [ -I interface ] \ host port program",0); +#endif _exit(100); } @@ -334,7 +361,20 @@ int s; int t; +#ifdef WITH_SSL + BIO *sbio; + SSL *ssl; + SSL_CTX *ctx; + int pi2c[2], pi4c[2]; + + ctx = NULL; + + if (!stralloc_copys(&certfile, CERTFILE) || !stralloc_0(&certfile) ) + strerr_die2x(111,FATAL,"out of memory"); + while ((opt = getopt(argc,argv,"46dDvqQhHrRsS1UXx:t:u:g:l:b:B:c:n:I:pPoO")) != opteof) +#else while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof) +#endif switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -364,6 +404,14 @@ case '4': noipv6 = 1; break; case '6': forcev6 = 1; break; case 'l': localhost = optarg; break; +#ifdef WITH_SSL + case 's': flagssl = 1; break; + case 'S': flagssl = 0; break; + case 'n': if (!stralloc_copys(&certfile, optarg) || + !stralloc_0(&certfile) ) + strerr_die2x(111,FATAL,"out of memory"); + break; +#endif default: usage(); } argc -= optind; @@ -371,6 +419,11 @@ if (!verbosity) buffer_2->fd = -1; + + if (limit == 0) + strerr_die2x(100,FATAL,"limit may not be set to 0"); + if (limit > 65000) + strerr_die2x(100,FATAL,"limit way to high"); hostname = *argv++; if (!hostname) usage(); @@ -412,6 +465,25 @@ noipv6=1; } +#ifdef WITH_SSL + if (flagssl == 1) { + /* setup SSL context (load key and cert into ctx) */ + SSL_library_init(); + ctx=SSL_CTX_new(SSLv23_server_method()); + if (!ctx) strerr_die2x(111,FATAL,"unable to create SSL context"); + + /* set prefered ciphers */ + if (env_get("SSL_CIPHER")) + if (SSL_CTX_set_cipher_list(ctx, env_get("SSL_CIPHER")) == 0) + strerr_die2x(111,FATAL,"unable to set cipher list"); + + if(SSL_CTX_use_RSAPrivateKey_file(ctx, certfile.s, SSL_FILETYPE_PEM) != 1) + strerr_die2x(111,FATAL,"unable to load RSA private key"); + if(SSL_CTX_use_certificate_chain_file(ctx, certfile.s) != 1) + strerr_die2x(111,FATAL,"unable to load certificate"); + } +#endif + s = socket_tcp6(); if (s == -1) strerr_die2sys(111,FATAL,"unable to create socket: "); @@ -461,6 +533,39 @@ sig_unblock(sig_child); sig_uncatch(sig_term); sig_uncatch(sig_pipe); +#ifdef WITH_SSL + if (flagssl == 1) { + if (pipe(pi2c) != 0) + strerr_die2sys(111,DROP,"unable to create pipe: "); + if (pipe(pi4c) != 0) + strerr_die2sys(111,DROP,"unable to create pipe: "); + switch(fork()) { + case 0: + close(0); close(1); + close(pi2c[1]); + close(pi4c[0]); + if ((fd_move(0,pi2c[0]) == -1) || (fd_move(1,pi4c[1]) == -1)) + strerr_die2sys(111,DROP,"unable to set up descriptors: "); + /* signals are allready set in the parent */ + pathexec(argv); + strerr_die4sys(111,DROP,"unable to run ",*argv,": "); + case -1: + strerr_die2sys(111,DROP,"unable to fork: "); + default: + ssl = SSL_new(ctx); + if (!ssl) + strerr_die2x(111,DROP,"unable to set up SSL session"); + sbio = BIO_new_socket(0,BIO_NOCLOSE); + if (!sbio) + strerr_die2x(111,DROP,"unable to set up BIO socket"); + SSL_set_bio(ssl,sbio,sbio); + close(pi2c[0]); + close(pi4c[1]); + translate(ssl, pi2c[1], pi4c[0], 3600); + _exit(0); + } + } +#endif pathexec(argv); strerr_die4sys(111,DROP,"unable to run ",*argv,": "); case -1: @@ -470,3 +575,186 @@ close(t); } } + +#ifdef WITH_SSL +int ssl_timeoutio(int (*func)(), long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + int n; + const long end = t + time(NULL); + + do { + fd_set fds; + struct timeval tv; + + const int r = buf ? func(ssl, buf, len) : func(ssl); + if (r > 0) + return r; + + t = end - time(NULL); + if (t < 0) + break; + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&fds); + switch (SSL_get_error(ssl, r)) + { + default: + return r; /* some other error */ + case SSL_ERROR_WANT_READ: + FD_SET(rfd, &fds); + n = select(rfd + 1, &fds, NULL, NULL, &tv); + break; + case SSL_ERROR_WANT_WRITE: + FD_SET(wfd, &fds); + n = select(wfd + 1, NULL, &fds, NULL, &tv); + break; + } + + /* n is the number of descriptors that changed status */ + } + while (n > 0); + + if (n != -1) errno = error_timeout; + return -1; +} + +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) + return -1; + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { + ndelay_off(rfd); + ndelay_off(wfd); + } + else + SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) + return 0; + if (SSL_pending(ssl)) + return SSL_read(ssl, buf, len); + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); +} + +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) + return 0; + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); +} + +static int allwrite(int fd, char *buf, int len) +{ + int w; + + while (len) { + w = write(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +static int allwritessl(long t, int rfd, int wfd, SSL* ssl, char *buf, int len) +{ + int w; + + while (len) { + w = ssl_timeoutwrite(t, rfd, wfd, ssl, buf, len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +char tbuf[2048]; + +void translate(SSL* ssl, int clearout, int clearin, unsigned int iotimeout) +{ + struct taia now; + struct taia deadline; + iopause_fd iop[2]; + int flagexitasap; + int iopl; + int sslout, sslin; + int n, r; + + sslin = SSL_get_fd(ssl); + sslout = SSL_get_fd(ssl); + if (sslin == -1 || sslout == -1) + strerr_die2x(111,DROP,"unable to set up SSL connection"); + + flagexitasap = 0; + + if (ssl_timeoutaccept(timeout, sslin, sslout, ssl) <= 0) + strerr_die2x(111,DROP,"unable to accept SSL connection"); + + while (!flagexitasap) { + taia_now(&now); + taia_uint(&deadline,iotimeout); + taia_add(&deadline,&now,&deadline); + + /* fill iopause struct */ + iopl = 2; + iop[0].fd = sslin; + iop[0].events = IOPAUSE_READ; + iop[1].fd = clearin; + iop[1].events = IOPAUSE_READ; + + /* do iopause read */ + iopause(iop,iopl,&deadline,&now); + if (iop[0].revents) { + do { + /* data on sslin */ + n = ssl_timeoutread(iotimeout, sslin, sslout, ssl, tbuf, sizeof(tbuf)); + if ( n < 0 ) + strerr_die2sys(111,DROP,"unable to read form network: "); + if ( n == 0 ) + flagexitasap = 1; + r = allwrite(clearout, tbuf, n); + if ( r < 0 ) + strerr_die2sys(111,DROP,"unable to write to client: "); + /* + * if the data payload was longer than sizeof(tbuf) then SSL will have + * bytes processed and pending. We need to pick them up and write them + * to clearout. + */ + } while (SSL_pending(ssl)); + } + if (iop[1].revents) { + /* data on clearin */ + n = read(clearin, tbuf, sizeof(tbuf)); + if ( n < 0 ) + strerr_die2sys(111,DROP,"unable to read form client: "); + if ( n == 0 ) + flagexitasap = 1; + r = allwritessl(iotimeout, sslin, sslout, ssl, tbuf, n); + if ( r < 0 ) + strerr_die2sys(111,DROP,"unable to write to network: "); + } + if (!iop[0].revents && !iop[1].revents) + strerr_die2x(0, DROP,"timeout reached without input"); + } +} +#endif