diff -Nur ucspi-tcp-0.88.orig/fixcrio.1 ucspi-tcp-0.88/fixcrio.1 --- ucspi-tcp-0.88.orig/fixcrio.1 1970-01-01 01:00:00.000000000 +0100 +++ ucspi-tcp-0.88/fixcrio.1 2006-03-12 18:20:34.000000000 +0100 @@ -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) diff -Nur ucspi-tcp-0.88.orig/Makefile ucspi-tcp-0.88/Makefile --- ucspi-tcp-0.88.orig/Makefile 2006-03-12 18:20:02.000000000 +0100 +++ ucspi-tcp-0.88/Makefile 2006-03-12 18:27:18.000000000 +0100 @@ -1,13 +1,25 @@ # 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 +default: it man addcr: \ load addcr.o unix.a byte.a ./load addcr unix.a byte.a +addcr.0: \ +addcr.1 + nroff -man addcr.1 > addcr.0 + addcr.o: \ compile addcr.c buffer.h exit.h ./compile addcr.c @@ -24,6 +36,10 @@ load argv0.o unix.a byte.a ./load argv0 unix.a byte.a +argv0.0: \ +argv0.1 + nroff -man argv0.1 > argv0.0 + argv0.o: \ compile argv0.c pathexec.h strerr.h ./compile argv0.c @@ -153,6 +169,10 @@ > choose chmod 755 choose +clean: \ +TARGETS + rm -f `cat TARGETS` + commands.o: \ compile commands.c buffer.h stralloc.h gen_alloc.h str.h case.h \ commands.h @@ -172,10 +192,18 @@ > date@ chmod 755 date@ +date@.0: \ +date@.1 + nroff -man date@.1 > date@.0 + delcr: \ load delcr.o unix.a byte.a ./load delcr unix.a byte.a +delcr.0: \ +delcr.1 + nroff -man delcr.1 > delcr.0 + delcr.o: \ compile delcr.c buffer.h exit.h ./compile delcr.c @@ -296,10 +324,18 @@ > finger@ chmod 755 finger@ +finger@.0: \ +finger@.1 + nroff -man finger@.1 > finger@.0 + fixcrio: \ load fixcrio.o time.a unix.a byte.a ./load fixcrio time.a unix.a byte.a +fixcrio.0: \ +fixcrio.1 + nroff -man fixcrio.1 > fixcrio.0 + fixcrio.o: \ compile fixcrio.c sig.h buffer.h strerr.h byte.h readwrite.h exit.h \ iopause.h taia.h tai.h uint64.h pathexec.h @@ -350,6 +386,10 @@ > http@ chmod 755 http@ +http@.0: \ +http@.1 + nroff -man http@.1 > http@.0 + install: \ load install.o hier.o auto_home.o unix.a byte.a ./load install hier.o auto_home.o unix.a byte.a @@ -413,6 +453,11 @@ ) > makelib chmod 755 makelib +man: \ +tcpclient.0 tcpserver.0 tcprules.0 tcprulescheck.0 tcp-environ.0 \ +who@.0 date@.0 finger@.0 http@.0 tcpcat.0 mconnect.0 fixcrio.0 addcr.0 \ +delcr.0 argv0.0 recordio.0 + mconnect: \ warn-auto.sh mconnect.sh conf-home cat warn-auto.sh mconnect.sh \ @@ -429,6 +474,10 @@ readwrite.h exit.h ./compile mconnect-io.c +mconnect.0: \ +mconnect.1 + nroff -man mconnect.1 > mconnect.0 + ndelay_off.o: \ compile ndelay_off.c ndelay.h ./compile ndelay_off.c @@ -499,6 +548,10 @@ exit.h fmt.h iopause.h taia.h tai.h uint64.h pathexec.h ./compile recordio.c +recordio.0: \ +recordio.1 + nroff -man recordio.1 > recordio.0 + remoteinfo.o: \ compile remoteinfo.c fmt.h buffer.h socket.h uint16.h error.h \ iopause.h taia.h tai.h uint64.h timeoutconn.h uint16.h remoteinfo.h \ @@ -712,6 +765,10 @@ compile taia_uint.c taia.h tai.h uint64.h ./compile taia_uint.c +tcp-environ.0: \ +tcp-environ.5 + nroff -man tcp-environ.5 > tcp-environ.0 + tcpcat: \ warn-auto.sh tcpcat.sh conf-home cat warn-auto.sh tcpcat.sh \ @@ -719,12 +776,20 @@ > tcpcat chmod 755 tcpcat +tcpcat.0: \ +tcpcat.1 + nroff -man tcpcat.1 > tcpcat.0 + tcpclient: \ load tcpclient.o remoteinfo6.o dns.a time.a unix.a \ byte.a socket.lib byte.h timeoutconn6.o ./load tcpclient remoteinfo6.o timeoutconn6.o dns.a time.a \ unix.a byte.a `cat socket.lib` +tcpclient.0: \ +tcpclient.1 + nroff -man tcpclient.1 > tcpclient.0 + tcpclient.o: \ compile tcpclient.c sig.h exit.h sgetopt.h subgetopt.h uint16.h fmt.h \ scan.h str.h ip4.h uint16.h socket.h uint16.h fd.h stralloc.h \ @@ -737,6 +802,10 @@ load tcprules.o cdb.a unix.a byte.a ./load tcprules cdb.a unix.a byte.a +tcprules.0: \ +tcprules.1 + nroff -man tcprules.1 > tcprules.0 + tcprules.o: \ compile tcprules.c strerr.h stralloc.h gen_alloc.h getln.h buffer.h \ stralloc.h buffer.h exit.h fmt.h byte.h cdb_make.h buffer.h uint32.h @@ -746,6 +815,10 @@ load tcprulescheck.o rules.o cdb.a unix.a byte.a ./load tcprulescheck rules.o cdb.a unix.a byte.a +tcprulescheck.0: \ +tcprulescheck.1 + nroff -man tcprulescheck.1 > tcprulescheck.0 + tcprulescheck.o: \ compile tcprulescheck.c byte.h buffer.h strerr.h env.h rules.h \ stralloc.h gen_alloc.h @@ -755,7 +828,11 @@ 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.0: \ +tcpserver.1 + nroff -man tcpserver.1 > tcpserver.0 tcpserver.o: \ compile tcpserver.c uint16.h str.h byte.h fmt.h scan.h ip4.h fd.h \ @@ -764,7 +841,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 \ @@ -965,5 +1042,19 @@ compile socket_v6loopback.c ./compile socket_v6loopback.c -clean: - rm -f `cat TARGETS` +who@.0: \ +who@.1 + nroff -man who@.1 > who@.0 + +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" diff -Nur ucspi-tcp-0.88.orig/tcprules.c ucspi-tcp-0.88/tcprules.c --- ucspi-tcp-0.88.orig/tcprules.c 2006-03-12 18:20:02.000000000 +0100 +++ ucspi-tcp-0.88/tcprules.c 2006-03-12 18:20:34.000000000 +0100 @@ -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; diff -Nur ucspi-tcp-0.88.orig/tcprulescheck.c ucspi-tcp-0.88/tcprulescheck.c --- ucspi-tcp-0.88.orig/tcprulescheck.c 2006-03-12 18:20:02.000000000 +0100 +++ ucspi-tcp-0.88/tcprulescheck.c 2006-03-12 18:20:34.000000000 +0100 @@ -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; diff -Nur ucspi-tcp-0.88.orig/tcpserver.c ucspi-tcp-0.88/tcpserver.c --- ucspi-tcp-0.88.orig/tcpserver.c 2006-03-12 18:20:02.000000000 +0100 +++ ucspi-tcp-0.88/tcpserver.c 2006-03-12 18:35:37.000000000 +0100 @@ -1,6 +1,7 @@ #include #include #include +#include #include "uint16.h" #include "str.h" #include "byte.h" @@ -40,6 +41,14 @@ 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; uint16 localport; @@ -130,6 +139,9 @@ env(data + 1,data + 1 + split + 1); } break; + case '-': + env(data + 1, (char *)0); + break; } ++next0; data += next0; datalen -= next0; @@ -271,10 +283,28 @@ void usage(void) { +#ifndef WITH_SSL strerr_warn1("\ tcpserver: usage: tcpserver \ [ -461UXpPhHrRoOdDqQv ] \ [ -c limit ] \ +[ -C [address[/len]:]limit ] \ +[ -e name=var ] \ +[ -x rules.cdb ] \ +[ -B banner ] \ +[ -g gid ] \ +[ -u uid ] \ +[ -b backlog ] \ +[ -l localname ] \ +[ -t timeout ] \ +host port program",0); +#else + strerr_warn1("\ +tcpserver: usage: tcpserver \ +[ -461UXpPhHrRoOdDqQsSv ] \ +[ -c limit ] \ +[ -C [address[/len]:]limit ] \ +[ -e name=var ] \ [ -x rules.cdb ] \ [ -B banner ] \ [ -g gid ] \ @@ -283,7 +313,9 @@ [ -l localname ] \ [ -t timeout ] \ [ -I interface ] \ +[ -n certfile ] \ host port program",0); +#endif _exit(100); } @@ -308,12 +340,106 @@ _exit(0); } +struct conn { + int pid; + char remoteip[4]; +} *conns; + +struct ip_limelt { + char ip[4]; + char mask[4]; + unsigned long limit; + unsigned long count; +}; + +#include "gen_alloc.h" +#include "gen_allocdefs.h" +GEN_ALLOC_typedef(ip_limit,struct ip_limelt,l,len,a) +GEN_ALLOC_readyplus(ip_limit, struct ip_limelt,l,len,a,i,n,x,10, + ip_limit_rp) + +ip_limit ipl = {0}; +unsigned long limit_ip = 0; + +void +ip_limit_add(char *str) +{ + unsigned int n, len; + unsigned long ul = 0; + struct ip_limelt lim; + + byte_zero(&lim, sizeof(lim)); + n = str_chr(str, ':'); + if (str[n] == ':') { + str[n] = 0; + scan_ulong(str + n + 1, &ul); + lim.limit = ul; + /* parse ip */ + ul = 32; + n = str_chr(str, '/'); + if (str[n] == '/') + scan_ulong(str + n + 1, &ul); + if (ul > 32) + strerr_die2x(111,FATAL,"ip prefix len > 32"); + if (ip4_scan(str, lim.ip) == 0) + strerr_die2x(111,FATAL,"bad ip address"); + for (n = 0; n < 4; n++) + if (ul > 8) { + lim.mask[n] = 0xff; + ul -= 8; + } else { + lim.mask[n] = 0xff << (8 - ul); + ul = 0; + } + if (!ip_limit_rp(&ipl,1)) + strerr_die2x(111,FATAL,"out of memory"); + ipl.l[ipl.len++] = lim; + } else { + scan_ulong(str, &ul); + limit_ip = ul; + } +} + +int +ip_limit_check(char ip[4], int d) +{ + unsigned long c; + unsigned int l; + int i; + + for (l = 0; l < ipl.len; l++) + if ((ip[0] & ipl.l[l].mask[0]) == (ipl.l[l].ip[0] & ipl.l[l].mask[0]) && + (ip[1] & ipl.l[l].mask[1]) == (ipl.l[l].ip[1] & ipl.l[l].mask[1]) && + (ip[2] & ipl.l[l].mask[2]) == (ipl.l[l].ip[2] & ipl.l[l].mask[2]) && + (ip[3] & ipl.l[l].mask[3]) == (ipl.l[l].ip[3] & ipl.l[l].mask[3])) { + if (ipl.l[l].count + d > ipl.l[l].limit) + return 1; + ipl.l[l].count += d; + return 0; + } + + /* global per ip limit */ + for (l = 0, c= 0; l < limit; l++) + if (!byte_diff(conns[l].remoteip, sizeof(ip), ip)) + c++; + if (limit_ip != 0 && c + d > limit_ip) + return 1; + + return 0; +} + void sigchld() { int wstat; int pid; - + unsigned int i; + while ((pid = wait_nohang(&wstat)) > 0) { + for (i = 0; i < limit; i++) + if (conns[i].pid == pid) { + ip_limit_check(conns[i].remoteip, -1); + byte_zero(&conns[i], sizeof(struct conn)); + } if (verbosity >= 2) { strnum[fmt_ulong(strnum,pid)] = 0; strnum2[fmt_ulong(strnum2,wstat)] = 0; @@ -330,14 +456,32 @@ int opt; struct servent *se; char *x; + char *iplimenv; + int flagiplim; unsigned long u; int s; int t; + unsigned int i; + int pid; +#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:I:C:e:n: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; + case 'C': ip_limit_add(optarg); break; case 'X': flagallownorules = 1; break; case 'x': fnrules = optarg; break; case 'B': banner = optarg; break; @@ -364,6 +508,18 @@ case '4': noipv6 = 1; break; case '6': forcev6 = 1; break; case 'l': localhost = optarg; break; + case 'e': iplimenv = optarg; + if (iplimenv[str_chr(iplimenv, '=')] != '=') + strerr_die2x(100,FATAL, "no '=' in ip limit env-var"); + 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 +527,15 @@ 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"); + conns = (struct conn *)alloc(limit * sizeof(struct conn)); + if (!conns) + strerr_die2x(111,FATAL,"out of memory"); + byte_zero(conns, limit * sizeof(struct conn)); hostname = *argv++; if (!hostname) usage(); @@ -412,6 +577,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: "); @@ -450,9 +634,31 @@ if (t == -1) continue; ++numchildren; printstatus(); + + /* per ip handling */ + flagiplim = 0; + if (ip_limit_check(remoteip, 1)) { + remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; + if (iplimenv) { + strerr_warn4(DROP,"too many conections from ",remoteipstr, + " only flagged",0); + flagiplim = 1; + } else { + strerr_warn3(DROP,"too many conections from ",remoteipstr,0); + --numchildren; printstatus(); + close(t); + continue; + } + } - switch(fork()) { + switch(pid = fork()) { case 0: + if (flagiplim) { + int split; + split = str_chr(iplimenv,'='); + iplimenv[split] = 0; + env(iplimenv,iplimenv + split + 1); + } close(s); doit(t); if ((fd_move(0,t) == -1) || (fd_copy(1,0) == -1)) @@ -461,12 +667,159 @@ 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: strerr_warn2(DROP,"unable to fork: ",&strerr_sys); --numchildren; printstatus(); + break; + default: + for (i = 0; i < limit; i++) + if (conns[i].pid == 0) { + conns[i].pid = pid; + byte_copy(conns[i].remoteip, sizeof(remoteip), remoteip); + break; + } } close(t); } } + +#ifdef WITH_SSL +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(SSL* ssl, char *buf, int len) +{ + int w; + + while (len) { + w = SSL_write(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_accept(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_read(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(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