/* * Copyright (c) 2000-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* * ssl-gw, CONNECT method proxy for OpenFWTK * * functionality duplicates squid-gw's connect method but i decided to * keep it because it is really small and does _not_ handle GET requests * (which behaviour is somehow useful when we deal with misbehaving programs). * We may also enforce some check if actual ssl negotiation appears in * the tunnel (not implemented yet). * * Another useful feature is we can plug-to to existing CONNECT proxy * thus keeping ACLs and accouting on the firewall but using parent proxy for * outbound connections. * * ACL handling differs from other proxies: we may specify host:port * pairs instead of just destination hostnames (credits to * Jean-Cristophe Touvet , some generic portions of code * used here) * * (C) Copyright 2000..2002 ArkanoiD */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "firewall.h" #include "firewall2.h" #include "fwfunc.h" #include "auth.h" static int check_dest(); #define MAXREQ 1023 static char **validests = (char **)0; static int extendperm = 0; static int cdscp = 0; static int sdscp = 0; static fwparm options[] = { { FWPARM_BOOL, "-extnd", (char*) &extendperm }, { FWPARM_LIST, "-dest", (char*) &validests }, { FWPARM_STRING, "-plug-to", proxy_stats.dst }, { FWPARM_PORT, "-port", (char*) &proxy_stats.port }, { FWPARM_STRING, "-authuser", proxy_stats.authuser }, { FWPARM_DSCP, "-client-dscp", (char*) &cdscp }, { FWPARM_DSCP, "-server-dscp", (char*) &sdscp }, { 0, 0, 0 } }; static int srvport; static char proxy_deny[] = "\ HTTP/1.0 403 Forbidden\r\n\ Content-Type: text/html\r\n\ \r\n\ Forbidden\n\

Proxy error: cannot connect to %s:%d

\n\ Either forbidden destination or host %s unknown."; static char proxy_error[] = "\ HTTP/1.0 500 Error\r\n\ Content-Type: text/html\r\n\ \r\n\ Error\n\

Proxy error: cannot connect to %s:%d

\n\ %s."; static char *client_headers[] = { "User-Agent: ", "Proxy-Connection: Keep-Alive", "Pragma: No-Cache", "Host: ", "Authorization: ", "Proxy-authorization: ", "Content-Length: 0", /* Other regular headers must go there */ NULL }; int main(argc,argv) int argc; char *argv[]; { Cfg *cf; char *port, *end; char request[MAXREQ+1]; char buf[MAXREQ+1]; char *srvnam; int serfd; int plug = 0; proxy_init(argc,argv); proxy_chroot_setugid(); sosetline(SO_LCRLF); proxy_stats.port = 3128; if ((cf=proxy_conf_hosts(proxy_confp,proxy_stats.rladdr, proxy_stats.riaddr))) proxy_parse_options(cf,options); else exit(0); if (cdscp) proxy_set_dscp(0,cdscp); /* Netscape sends "CONNECT host:port HTTP/1.0\r\n" then headers */ if(sogets(0, request, MAXREQ) <= 0) exit(1); request[strlen(request)-1] = '\0'; if(strncasecmp(request,"connect ",8) || (port = index(srvnam=xstrdup(request)+8,':')) == (char *)0 || (end = index(port,' ')) == (char *)0) { syslog(LLEV,"bad request %s",request); exit(1); } if (!*srvnam) { syslog(LLEV,"null destination"); exit(1); } *end = *(port++) = 0; srvport = (uint16_t) atoi(port); if (*proxy_stats.dst) plug = 1; else { strncpy(proxy_stats.dst,srvnam,sizeof(proxy_stats.dst)); proxy_stats.port = srvport; } /* Headers loop */ while(1) { char **p; char *x; if(sogets(0, buf, MAXREQ) <= 0) exit(1); if ((x = index(buf,'\r'))) *x = '\0'; if(!buf[0]) break; for (p = client_headers; *p; p++) { if (!strncasecmp(*p,buf,strlen(*p))) break; } if (! *p) syslog(LLEV,"unknown header %s",buf); } /* check to see if server is allowed */ if (check_dest(srvnam,srvport)) { char buf1[sizeof(proxy_deny)+2*MAXREQ]; snprintf(buf1,sizeof(buf1),proxy_deny,proxy_stats.dst,srvport,proxy_stats.dst); sosay(0,buf1); exit(1); } if (extendperm) { char buf1[sizeof(proxy_deny)+2*MAXREQ]; snprintf(buf1,sizeof(buf1),proxy_deny,proxy_stats.dst,srvport,proxy_stats.dst); switch (auth_perm(proxy_confp,proxy_stats.authuser,proxy_name, srvnam,NULL)) { case -1: syslog(LLEV,"no match for %.256s user=%.100s in netperm-table", proxy_name,proxy_stats.authuser); sosay(0,buf1); exit(1); case 1: syslog(LLEV,"deny host=%.512s/%.20s connect to %.512s user=%.100s", proxy_stats.rladdr,proxy_stats.riaddr, srvnam,proxy_stats.authuser); sosay(0,buf1); exit(1); } } proxy_update_status(); if (plug) syslog(LLEV,"permit host=%s/%s connect to %s:%d via=%s:%d", proxy_stats.rladdr,proxy_stats.riaddr,srvnam, srvport,proxy_stats.dst,proxy_stats.port); else syslog(LLEV,"permit host=%s/%s connect to %s:%d", proxy_stats.rladdr,proxy_stats.riaddr,srvnam, srvport); if((serfd = conn_server(proxy_stats.dst,proxy_stats.port,0,NULL)) < 0) { char buf1[sizeof(proxy_error)+MAXREQ+512]; snprintf(buf1,sizeof(buf1),proxy_error,proxy_stats.dst,proxy_stats.port, strerror(errno)); syslog(LLEV,"cannot connect to server %s:%d: %s", proxy_stats.dst,srvport,strerror(errno)); sosay(0,buf1); exit(1); } if (sdscp) proxy_set_dscp(serfd,sdscp); if (plug) { char buf1[512]; snprintf(request,MAXREQ,"CONNECT %s:%u HTTP/1.0\r\n", srvnam,srvport); if (sosay(serfd,request)) { syslog(LLEV,"fwtksyserr: parent proxy communication error %s",strerror(errno)); exit(1); } proxy_stats.outbytes += strlen(request); snprintf(buf1,64,"CONNECT %s:%u", srvnam,srvport); buf1[sizeof(buf1)-1] = '\0'; proxy_update_operation(buf1); } else { if(sosay(0,"HTTP/1.0 200 Ok\r\n")) { syslog(LLEV,"write client: %s",strerror(errno)); proxy_exit(); } } proxy_tunnel(0,serfd); /* We want final accounting to list real destination, not the parent proxy */ if (plug) { strncpy(proxy_stats.dst,srvnam,sizeof(proxy_stats.dst)-1); proxy_stats.port = srvport; } proxy_exit(); exit(-1); /* not reached */ } /* * next 2 functions (pretty generic though) are taken from Edelweb's ssl-gw * by Jean-Cristophe Touvet */ static int hostportmatch(pattern,string,port) char *pattern; char *string; int port; { char *p; if ((p = index (pattern,':')) != (char *)0) *p = 0; /* Put \0 if port number(s) */ if (! hostmatch(pattern,string)) { /* Host doesn't match: return false now */ if (p) *p = ':'; /* Put ':' back */ return 0; } /* Host matches */ if (! p) return 1; /* No port number(s): return true */ *p = ':'; /* Put ':' back */ do { if (! *++p) { syslog(LLEV,"fwtkcfgerr: port number expected (%s)",pattern); exit (1); } if (port == atoi (p)) return 1; } while ((p = index (p,':')) != (char *)0); return 0; } static int check_dest(char* srv,int port) { char **xp; int denied; if((denied = (validests != NULL))) { for(xp = validests; *xp != NULL; xp++) if( **xp == '!' && hostportmatch(*xp+1,srv,port)) break; else if(hostportmatch(*xp, srv, port)) { denied = 0; break; } } if (denied) syslog(LLEV,"deny host=%s/%s connect to %s:%d", proxy_stats.rladdr, proxy_stats.riaddr, proxy_stats.dst, srvport); return denied; }