/* * Copyright (c) 2001-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* * Generic tcp plug proxy functionally similar to TIS plug-gw * * (C) Copyright 2001 ArkanoiD * * XXXX encryption routines are functionally embryionic, speed nightmare * and vulnerable to replay attacks. To be replaced with something more * useful later. Until that, be sure your passphrase is REALLY good * against dictionary attack. Only bf encryption and md5 hash implemented * so far. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SSL #include #include #include #endif #include "firewall.h" #include "auth.h" #ifdef OpenBSD #include #include #else #include #include #endif #include "fwfunc.h" #include "firewall2.h" #ifdef USE_SSL #include "sslfunc.h" #endif #ifndef FIONREAD /* It's probably here */ #include #endif #define CRYPT_NONE 000 #define CRYPT_BF 001 #define CRYPT_3DES 002 #define CRYPT_GOST 003 static char **validests = (char **)0; static int extendperm = 0; static int transparent = 0; static int privport = 0; static int net_write(); static BF_KEY cl_bfkey, srv_bfkey; static unsigned char cl_bfiiv[8] = "\0\0\0\0\0\0\0\0"; static unsigned char cl_bfoiv[8] = "\0\0\0\0\0\0\0\0"; static unsigned char srv_bfiiv[8] = "\0\0\0\0\0\0\0\0"; static unsigned char srv_bfoiv[8] = "\0\0\0\0\0\0\0\0"; static char cencm[128],sencm[128]; static char cenckey[1024],senckey[1024]; static int cdscp = 0; static int sdscp = 0; static char authrequser[AUTH_USIZ] = ""; #ifdef USE_SSL static int cssl = 0; static int sssl = 0; static char cvrfy[128], svrfy[128]; static SSL *cconn = NULL; static SSL *sconn = NULL; #endif extern char proxy_name[MAX_STR]; extern struct fwstats proxy_stats; extern Cfg *proxy_confp; extern int proxy_timeout; static fwparm options[] = { { FWPARM_STRING, "-plug-to", proxy_stats.dst }, { FWPARM_STRING, "-client-encrypt", cencm }, { FWPARM_STRING, "-client-md5key", cenckey }, { FWPARM_STRING, "-server-encrypt", sencm }, { FWPARM_STRING, "-server-md5key", senckey }, { FWPARM_BOOL, "-privport", (char*) &privport }, { FWPARM_PORT, "-port", (char*) &proxy_stats.port }, { FWPARM_STRING, "-authuser", proxy_stats.authuser }, { FWPARM_STRING, "-authreq", authrequser }, { FWPARM_BOOL, "-extnd", (char*) &extendperm }, { FWPARM_BOOL, "-transparent", (char*) &transparent }, { FWPARM_BOOL, "-dest", (char*) &validests }, { FWPARM_DSCP, "-client-dscp", (char*) &cdscp }, { FWPARM_DSCP, "-server-dscp", (char*) &sdscp }, #ifdef USE_SSL { FWPARM_BOOL, "-ssl-client", (char*) &cssl }, { FWPARM_BOOL, "-ssl-server", (char*) &sssl }, { FWPARM_STRING, "-client-verify", cvrfy }, { FWPARM_STRING, "-server-verify", svrfy }, #endif { 0, 0, 0 }}; int main(ac,av) int ac; char *av[]; { Cfg *cf; unsigned int myport; int match = 0; fd_set rfds; fd_set xfds; fd_set iced; int serfd; int privport = 0; static unsigned char buf[1024 * 4]; static unsigned char encbuf[1024 * 4]; int client_encrypt = CRYPT_NONE; int server_encrypt = CRYPT_NONE; MD5_CTX md5_ctx; unsigned char md5_digest[16]; uint16_t blksize; int srv_bfiinum = 0, srv_bfoinum = 0; int cl_bfiinum = 0, cl_bfoinum = 0; bzero(cencm,sizeof(cencm)); bzero(sencm,sizeof(sencm)); bzero(cenckey,sizeof(cenckey)); bzero(senckey,sizeof(senckey)); proxy_init(ac,av); while ((ac > 2) && (!strcmp(av[1], "-daemon") || !strcmp(av[1], "-fastdaemon") || !strcmp(av[1], "-as"))) { ac -= 2; av += 2; } #ifdef USE_SSL proxy_SSLinit(); #endif proxy_chroot_setugid(); /* if called with an argument, it is the connecting service name */ if(ac > 1) myport = str_to_port(av[1]); else { socklen_t len; struct sockaddr_in to_addr; bzero((char *)&to_addr, sizeof(to_addr)); len = sizeof(to_addr); if (getsockname(0, (struct sockaddr *)&to_addr, &len) < 0) { syslog(LLEV,"fwtksyserr: getsockname() failed: %s", strerror(errno)); exit(1); } myport = ntohs(to_addr.sin_port); } /* * We accept both new configuration format (more generic with * permit-hosts lines and options, having services distinguished * by argv[0]) and legacy one TIS plug-gw used to have, with * "port [options.. ]" syntax. * Quite PITA to parse because does not fit our new API well but.. * We lived that way for years. */ for (cf = cfg_get("port",proxy_confp); cf ; cf = cfg_get("port",NULL)) { int x; if(cf->argc < 2) { syslog(LLEV,"fwtkcfgerr: missing parameter, line %d",cf->ln); exit(1); } if(strcmp(cf->argv[0],"any")) { if(myport != str_to_port(cf->argv[0])) continue; } /* if ports match, now see if calling host matches */ for(x = 1; x < cf->argc - 1; x++) { if (cf->argv[x][0] == '-') break; /* end of host list */ if(hostmatch(cf->argv[x],proxy_stats.riaddr)) { proxy_parse_options(cf,options); match = 1; } if(cf->flags & PERM_DENY) { syslog (LLEV,"deny host=%s/%s use of gateway", proxy_stats.rladdr,proxy_stats.riaddr); exit(1); } } } if (!match && (cf = proxy_conf_hosts(proxy_confp,proxy_stats.rladdr, proxy_stats.riaddr))) { proxy_parse_options(cf,options); match = 1; } if (!match) exit(1); if (!proxy_stats.port) proxy_stats.port = myport; proxy_update_status(); if (cdscp) proxy_set_dscp(0,cdscp); /* * Authentication */ if (*authrequser && !strcmp(proxy_stats.authuser,"unauth")) { proxy_update_operation("AUTHENTICATION"); if (auth_open(proxy_confp)) { syslog(LLEV,"fwtksyserr: cannot connect to authentication server"); exit(1); } if (auth_recv(buf,sizeof(buf)) || strncmp((char*) buf,"Authsrv ready",13)) { syslog(LLEV,"fwtksyserr: cannot read from authentication server"); exit(1); } snprintf((char*) buf,sizeof(buf),"authorize %.128s '%.64s %.128s/%.128s'", authrequser,proxy_name,proxy_stats.rladdr, proxy_stats.riaddr); if (auth_send(buf)) { syslog(LLEV,"fwtksyserr: cannot write to authentication server"); exit(1); } if (auth_recv(buf,sizeof(buf))) { syslog(LLEV,"fwtksyserr: cannot read from authentication server"); exit(1); } if (strncmp((char*) buf, "ok", 2)) { syslog(LLEV,"proxy authentication failed, %.128s", buf); proxy_exit(); } syslog(LLEV,"authenticate user=%.100s",authrequser); strlcpy(proxy_stats.authuser,authrequser,sizeof(proxy_stats.authuser)); proxy_update_status(); } /* * Now we do set up encryption options */ #ifdef USE_SSL if (cssl) { cconn = proxy_SSLaccept(0, proxy_stats.rladdr, proxy_stats.riaddr, cvrfy); } #endif if (*cencm) { if (!strcmp(cencm,"blowfish")) client_encrypt = CRYPT_BF; else { syslog(LLEV,"fwtkcfgerr: encryption %s is not implemented",cencm); exit(1); } } if (*cenckey) { MD5_Init(&md5_ctx); MD5_Update(&md5_ctx,cenckey,strlen(cenckey)); MD5_Final(md5_digest,&md5_ctx); switch (client_encrypt) { case CRYPT_NONE: syslog(LLEV,"fwtkcfgerr: -client-encrypt should be specified with -client-md5key"); exit(1); case CRYPT_BF: BF_set_key(&cl_bfkey,16,md5_digest); } } if (*sencm) { if (!strcmp(sencm,"blowfish")) server_encrypt = CRYPT_BF; else { syslog(LLEV,"fwtkcfgerr: encryption %s is not implemented",sencm); exit(1); } } if (*senckey) { MD5_Init(&md5_ctx); MD5_Update(&md5_ctx,senckey,strlen(senckey)); MD5_Final(md5_digest,&md5_ctx); switch (server_encrypt) { case CRYPT_NONE: syslog(LLEV,"fwtkcfgerr: -server-encrypt should be specified with -client-md5key"); exit(1); case CRYPT_BF: BF_set_key(&srv_bfkey,16,md5_digest); } } if (transparent) proxy_get_transparent_dst(proxy_stats.dst,&proxy_stats.port); if (!*proxy_stats.dst) { syslog(LLEV,"fwtkcfgerr: not sure what host to connect to"); exit(1); } if (!proxy_stats.port) { syslog(LLEV,"fwtkcfgerr: not sure what port to connect to"); exit(1); } if (proxy_check_dest(validests,extendperm)) proxy_exit(); /* * All configuration is now set up, let's begin */ proxy_update_operation("CONNECTING"); if((serfd = conn_server(proxy_stats.dst,proxy_stats.port,privport,(char *)0)) < 0) { syslog(LLEV,"cannot connect to server %.512s/%d: %.128s",proxy_stats.dst,proxy_stats.port,strerror(errno)); exit(1); } if (sdscp) proxy_set_dscp(serfd,sdscp); #ifdef USE_SSL if (sssl) { sconn = proxy_SSLconnect(serfd,proxy_stats.dst,svrfy); } #endif FD_ZERO(&iced); FD_SET(0,&iced); FD_SET(serfd,&iced); while(FD_ISSET(0, &iced) && FD_ISSET(serfd, &iced)) { struct timeval timo; int x; (void)bcopy(&iced,&rfds,sizeof(fd_set)); (void)bcopy(&iced,&xfds,sizeof(fd_set)); timo.tv_sec = proxy_timeout; timo.tv_usec = 0; proxy_update_operation("IDLE"); if(select(serfd + 1,&rfds,(fd_set *)0,&xfds,&timo) <= 0) { syslog(LLEV,"Network timeout signal after %d seconds",proxy_timeout); break; } proxy_update_operation("DATA"); if(FD_ISSET(0, &xfds)) { if ((x = recv(0, buf, sizeof(buf), MSG_OOB)) < 0) { /* ignore OOB read errors */ } else { int x1; if (client_encrypt) syslog(LLEV,"securitywarning: OOB data received, client socket %d bytes",x); if ((x1=net_write(serfd, (char*) buf, x, MSG_OOB))!= x) { syslog(LLEV,"OOB write failed, wrote %d",x1); break; } proxy_stats.outbytes += x; } } if(FD_ISSET(serfd, &xfds)) { if ((x = recv(serfd, buf, sizeof(buf), MSG_OOB)) < 0) { /* ignore OOB read errors */ } else { if (server_encrypt) syslog(LLEV,"securitywarning: OOB data received, server socket"); if (net_write(0, (char*) buf, x, MSG_OOB) != x) break; proxy_stats.inbytes += x; } } if(FD_ISSET(0,&rfds)) { ioctl(0,FIONREAD,&x); if (x <= 0) { break; } #ifdef USE_SSL if (cssl) { x = proxy_SSLread(cconn,buf,sizeof(buf)-sizeof(blksize)); if (x <= 0) break; } else #endif if (!client_encrypt) { if((x = recv(0,buf,sizeof(buf) - sizeof(blksize),0)) <= 0) { break; } } else { if(soread(0,(char*)encbuf,sizeof(blksize)) < 0) break; BF_cfb64_encrypt( encbuf, (unsigned char *)&blksize, sizeof(blksize),&cl_bfkey, cl_bfiiv, &cl_bfiinum, BF_DECRYPT); if ((x = ntohs(blksize)) > sizeof(encbuf)) { syslog(LLEV,"fwtksyserr: buffer size exceeded on client socket, protocol failed"); exit(1); } if (soread(0,(char*)encbuf,x) < 0) { syslog(LLEV,"error reading encrypted client data"); break; } BF_cfb64_encrypt(encbuf, buf,x,&cl_bfkey, cl_bfiiv, &cl_bfiinum,BF_DECRYPT); } #ifdef USE_SSL if (sssl) { unsigned int w; w = proxy_SSLwrite(sconn,buf,x); if (w <= 0) break; } else #endif if (server_encrypt) { int x1; blksize = htons(x); BF_cfb64_encrypt((unsigned char *)&blksize, (unsigned char *)&x1, sizeof(blksize),&srv_bfkey, srv_bfoiv, &srv_bfoinum,BF_ENCRYPT); BF_cfb64_encrypt(buf,encbuf,x,&srv_bfkey,srv_bfoiv,&srv_bfoinum,BF_ENCRYPT); if(sowrite(serfd,(char*) &x1,sizeof(blksize)) < sizeof(blksize)) break; proxy_stats.outbytes += sizeof(blksize); bcopy(encbuf,buf,sizeof(buf)); } #ifdef USE_SSL if (!sssl) #endif if(FD_ISSET(serfd, &iced) && (sowrite(serfd,(char*) buf,x) != x)) break; proxy_stats.outbytes += x; } if(FD_ISSET(serfd,&rfds)) { ioctl(serfd,FIONREAD,&x); if (x <= 0) break; #ifdef USE_SSL if (sssl) { x = proxy_SSLread(sconn,buf,sizeof(buf)-sizeof(blksize)); if (x <= 0) break; } else { #endif if (!server_encrypt) { if((x = recv(serfd,buf,sizeof(buf)-sizeof(blksize),0)) <= 0) break; buf[x] = 0; } else { if(soread(serfd,(char*)encbuf,sizeof(blksize)) < 0) break; BF_cfb64_encrypt(encbuf, (unsigned char *)&blksize, sizeof(blksize),&srv_bfkey, srv_bfiiv,&srv_bfiinum, BF_DECRYPT); if ((x = ntohs(blksize)) > sizeof(encbuf)) { syslog(LLEV,"fwtksyserr: %d buffer size exceeded on server socket, protocol failed",x); exit(1); } if (soread(serfd,(char*)encbuf,x) < 0) { syslog(LLEV,"error reading encrypted server data"); break; } BF_cfb64_encrypt(encbuf,buf,x,&srv_bfkey,srv_bfiiv,&srv_bfiinum,BF_DECRYPT); } #ifdef USE_SSL } if (cssl) { unsigned int w; w = proxy_SSLwrite(cconn,buf,x); if (w <= 0) break; } else #endif if (client_encrypt) { int x1; blksize = htons(x); BF_cfb64_encrypt((unsigned char *)&blksize, (unsigned char *)&x1, sizeof(blksize), &cl_bfkey,cl_bfoiv, &cl_bfoinum,BF_ENCRYPT); BF_cfb64_encrypt(buf,encbuf,x,&cl_bfkey,cl_bfoiv,&cl_bfoinum,BF_ENCRYPT); if(sowrite(0,(char*) &x1,sizeof(blksize)) < sizeof(blksize)) break; bcopy(encbuf,buf,sizeof(buf)); proxy_stats.inbytes += sizeof(blksize); } #ifdef USE_SSL if (!cssl) #endif if(FD_ISSET(serfd, &iced) && (sowrite(0,(char*) buf,x) != x)) break; proxy_stats.inbytes += x; } } proxy_exit(); } /* * Those function are pretty generic so i don't think NAI will sue * me for using that ;) */ static int net_write(s, buf, len, flags) int s; char *buf; int len; int flags; { int nbytes = 0; int j; while (nbytes < len) { j = send(s, buf, len-nbytes, flags); if (j <= 0) return (nbytes ? nbytes : j); buf += j; nbytes += j; } return nbytes; }