/* Implement protocol checks and provide extra access control (the second factor in the 2 factors authentication). This program receives connections from the blackhole and spies the data sent from the client to the server. It does not talk to the server. It receives data and validates it. If valid it tells the blackhole (or conproxy) that it can send this data to the server. The protocol simply confirm to the blackhole that all data up to a given session offset may be sent to the server. The messages sent to the blackhole are: GO: It means everything is good so far, but protocheck won't be needed for the rest of the connection. Blackhole will disconnect and continue without it (for this session). FAI: It means something is wrong with this session. Blackhole will end the session. No more data will be sent to the server for this session. SEND: It means that up to an offset, everything is fine, so all data (up to this offset) must be sent to the server. If protocheck just ends the connection, it is equivalent to FAIL. ---- When used for second access control, It generates a UUID, and executes a command sending the UUID to the user, generally by email. The message sent to the user is usually a URL. The user clicks on the URL and the UUID is sent back to the server. Using the protocheck-2factors-control utility, the confirmation is received by this program, which then returns a GO to the blackhole server. The userid is retrieved from the "vserver" part of the proto line. After receiving a confirmed message from protocheck-2factors-control, it can either send a GO message to the blackhole and close the connection, or simply a SEND message. In that mode it continues to monitor the connection. Later, the protocheck-2factors-control can send a lock message. The session is normally idle. When the client sends more data, the 2 factor process restart. A new UUID is made and sent, expecting a confirmation from the user before the blackhole is allowed to send the received data to the server. Usually, this lock mecanism is tied to the end user screen saver. ---- See fdpass_loop() in fdpass.tlcc to see how it is used. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fdpass.h" using namespace std; enum CONNECT_TYPE {TYPE_NONE, TYPE_PIPE, TYPE_CONTROL, TYPE_CLIENT}; static bool proto_allnum(const char *pt) { bool ret = false; if (isdigit(*pt)){ ret = true; while (isdigit(*pt)) pt++; if (*pt != '\0') ret = false; } return ret; } struct URLDEF{ set getvars; set postvars; set cookies; void clear(){ getvars.clear(); postvars.clear(); cookies.clear(); } }; static void dumpvars (const char *title, const set &vars) { for (set::const_iterator it = vars.begin(); it != vars.end(); it++){ printf ("%s=%s\n",title,it->c_str()); } } struct HTTP_RULES { map urls; int load (const char *fname,_F_TCPSERVER_V1 *); void dump(){ for (map::iterator it=urls.begin(); it != urls.end(); it++){ printf ("GETURL=%s\n",it->first.c_str()); dumpvars ("GETVAR",it->second.getvars); dumpvars ("POSTVAR",it->second.postvars); dumpvars ("COOKIE",it->second.cookies); } } }; // Make sure a URL is in the map static void insert_url (const string &url, map &urls) { map::iterator it = urls.find(url); if (it == urls.end()){ urls[url] = URLDEF(); } } int HTTP_RULES::load (const char *fname, _F_TCPSERVER_V1 *c) { glocal bool some_errors = false; glocal map *urls = &urls; glocal string url; glocal _F_TCPSERVER_V1 *c = c; urls.clear(); (fname,true); line = str_skip(line); if (line[0] != '\0' && line[0] != '#'){ vector words; int n = str_splitline(line,'=',words); if (n == 2){ const char *word = words[0].c_str(); const char *val = words[1].c_str(); if (strcmp(word,"GETURL")==0){ glocal.url = val; insert_url (glocal.url,*glocal.urls); }else if (strcmp(word,"POSTURL")==0){ glocal.url = val; insert_url (glocal.url,*glocal.urls); }else if (strcmp(word,"PROPURL")==0){ glocal.url = val; insert_url (glocal.url,*glocal.urls); }else if (strcmp(word,"GETVAR")==0){ (*glocal.urls)[glocal.url].getvars.insert(val); }else if (strcmp(word,"POSTVAR")==0){ (*glocal.urls)[glocal.url].postvars.insert(val); }else if (strcmp(word,"COOKIE")==0){ (*glocal.urls)[glocal.url].cookies.insert(val); }else{ if (glocal.c != NULL){ glocal.c->sendf ("Invalid line in http check file %s: %s\n",info.filename,line); } tlmp_error ("Invalid line in http check file %s: %s\n",info.filename,line); glocal.some_errors = true; } }else{ if (glocal.c != NULL){ glocal.c->sendf ("Invalid line in http check file %s: %s\n",info.filename,line); } tlmp_error ("Invalid line in http check file %s: %s\n",info.filename,line); glocal.some_errors = true; } } return 0; tlmp_error ("Missing configuration file %s\n",fname); glocal.some_errors = true; return glocal.some_errors ? -1 : 0; } struct HTTP_CHECK{ HTTP_RULES rules; bool header_seen; bool is_get; int line_num; unsigned content_length; unsigned data_seen; string boundary; string end_boundary; bool in_data; // Inside a boundary section, just after the Content-disposition bool binary_data; // Inside a boundary section, probably some binary data bool previous_was_dispo;// Next line after Content-disposition URLDEF urldef; unsigned nbrequests; struct { string url; string reqtype; string sourceip; // Extracted from X-Forwarded-For } collect; void reset(){ header_seen = false; line_num = 0; is_get = false; content_length = 0; data_seen = 0; boundary.clear(); end_boundary.clear(); in_data = false; binary_data = false; previous_was_dispo = false; urldef.clear(); nbrequests++; } void reset_collect(){ collect.reqtype.clear(); collect.sourceip.clear(); collect.url.clear(); } HTTP_CHECK(){ reset(); reset_collect(); nbrequests=0; } bool is_reset() const { return line_num == 0; } bool seturldef (FILE *learn){ bool ret = false; map::iterator it = rules.urls.find(collect.url); if (it != rules.urls.end()){ urldef = it->second; ret = true; }else{ // Look for wildcard const char *pturl = collect.url.c_str(); for (it=rules.urls.begin(); it != rules.urls.end(); it++){ const char *pt = it->first.c_str(); if (*pt == '*'){ pt++; int len = strlen(pt); if (strncmp(pt,pturl,len)==0 && (pturl[len] == '/' || pturl[len] == '\0')){ urldef = it->second; ret = true; break; } } } if (!ret) urldef.clear(); } if (learn != NULL){ fprintf (learn,"GETURL=%s\n",collect.url.c_str()); ret = true; } return ret; } int spliturl (const string &word, FILE *learn, string &errmsg){ int ret = 0; const char *pturl = word.c_str(); const char *pt = strchr(pturl,'?'); bool found = false; if (pt == NULL){ collect.url = word; found = seturldef(learn); }else{ collect.url = string(pturl,pt-pturl); found = seturldef(learn); pt++; while (*pt != '\0'){ const char *start = pt; while (*pt != '\0' && *pt != '=') pt++; if (*pt == '='){ string var(start,pt-start); if (learn != NULL){ fprintf (learn,"GETVAR=%s\n",var.c_str()); }else if (urldef.getvars.count(var)==0){ errmsg = string_f("Unknown variable %s",var.c_str()); ret = -1; break; } while (*pt != '\0' && *pt != '&') pt++; if (*pt == '&') pt++; }else{ errmsg = "Incomplete variable"; ret = -1; break; } } } if (!found){ ret = -1; errmsg = string_f ("Unknown page %s",collect.url.c_str()); } return ret; } // Checks if a line is valid http. // real_length is the original length of the line because line is stripped // from \r\n int checkline(const char *line, FILE *learn, unsigned real_length, string &errmsg){ int ret = -1; line_num++; int len = strlen(line); if (len == 0){ if (!header_seen){ header_seen=true; if (line_num > 1){ if (is_get) reset(); // We can get another header ret = 0; real_length=0; }else{ errmsg = "Empty line"; } }else{ ret = 0; } }else if ((!header_seen && len > 1000) || (len > 2000 && !in_data)){ errmsg = string_f ("line too long %d: %200.200s",len,line); }else{ if (header_seen){ //if (learn) fprintf (learn,"CMP:%s:\n :%s:\n",line,boundary.c_str()); if (strcmp(line,boundary.c_str())==0){ in_data = false; ret = 0; }else if (strcmp(line,end_boundary.c_str())==0){ ret = 0; }else if (strncasecmp(line,"Content-Disposition:",20)==0){ vector words; int n = str_splitlineq(line+20,words); ret = 0; in_data = true; binary_data = false; previous_was_dispo = true; for (int i=0; i= ' ' && *pt != '"') pt++; if (*pt == '"'){ if (pt[1] != '\0') ret = -1; }else if (*pt != '\0'){ ret = -1; } }else{ ret = -1; } } if (ret == -1){ errmsg = string_f("Invalid content-disposition: %s",line); } }else if (previous_was_dispo && strncasecmp(line,"Content-Type:",13)==0){ // Probably a binary data ret = 0; binary_data = true; }else{ previous_was_dispo = false; if (in_data){ ret = 0; }else if (boundary.size()==0){ // Probably a JSON post (text/plain) ret = 0; }else{ errmsg = string_f("header_seen, but no match: %s",line); } } }else if (line_num == 1){ if (strncasecmp(line,"GET ",4)==0 || strncasecmp(line,"HEAD ",5)==0 || strncasecmp(line,"OPTIONS ",8)==0){ reset_collect(); collect.reqtype = "GET"; vector words; int n = str_splitline(str_skipword(line),' ',words); if (n == 2 && (strcasecmp(words[1].c_str(),"HTTP/1.1")==0 || strcasecmp(words[1].c_str(),"HTTP/1.0")==0)){ is_get = true; string msg; ret = spliturl(words[0],learn,msg); if (ret == -1){ errmsg = string_f ("Invalid GET (%s): %s",msg.c_str(),line); } }else{ errmsg = string_f ("Invalid GET, expected two sequences: %s",line); } }else if (strncasecmp(line,"POST ",5)==0){ reset_collect(); collect.reqtype = "POST"; vector words; int n = str_splitline(line+5,' ',words); if (n == 2 && (strcasecmp(words[1].c_str(),"HTTP/1.1")==0 || strcasecmp(words[1].c_str(),"HTTP/1.0")==0)){ is_get = false; string msg; ret = spliturl(words[0],learn,msg); if (ret != -1){ urldef = rules.urls[collect.url]; if (learn != NULL) fprintf (learn,"POSTURL=%s\n",collect.url.c_str()); }else{ errmsg = string_f ("Invalid POST (%s): %s",msg.c_str(),line); } }else{ errmsg = string_f ("Invalid POST: %s",line); } }else if (strncasecmp(line,"PROPFIND ",9)==0){ reset_collect(); collect.reqtype = "PROPFIND"; vector words; int n = str_splitline(line+9,' ',words); if (n == 2 && (strcasecmp(words[1].c_str(),"HTTP/1.1")==0 || strcasecmp(words[1].c_str(),"HTTP/1.0")==0)){ is_get = true; string msg; ret = spliturl(words[0],learn,msg); if (ret == -1){ errmsg = string_f ("Invalid PROPFIND (%s): %s",msg.c_str(),line); } if (learn != NULL) fprintf (learn,"PROPURL=%s\n",collect.url.c_str()); }else{ errmsg = string_f ("Invalid PROPFIND: %s",line); } }else{ errmsg = string_f ("Invalid first line (GET ?): %s",line); } }else if (strncasecmp(line,"content-length:",15)==0){ const char *pt = str_skip(line+15); if (proto_allnum(pt)){ content_length = atoi(pt); ret = 0; }else{ errmsg = string_f ("Invalid content-length: %s",line); } }else if (strncasecmp(line,"cookie:",7)==0){ const char *pt = str_skip(line+7); ret = 0; while (*pt != '\0'){ const char *start = pt; while (*pt != '\0' && *pt != '=') pt++; if (*pt == '=' && pt > start+1){ string cookie (start,pt-start); if (learn != NULL){ fprintf (learn,"COOKIE=%s\n",cookie.c_str()); }else if (urldef.cookies.count(cookie)==0){ ret = -1; break; } pt++; while (*pt >= ' ') pt++; }else{ ret = -1; break; } } if (ret == -1){ errmsg = string_f ("Invalid cookie: %s",line); } }else if (strncasecmp(line,"Content-Type:",13)==0){ const char *pt = str_skip(line+13); vector words; int n = str_splitline(pt,' ',words); ret = 0; for (int i=0; i 0){ ret = 0; break; }else if (!isalpha(car) && car != '-'){ break; } } if (ret == -1){ errmsg = string_f ("Invalid header line: %s",line); } } } if (header_seen){ data_seen += real_length; //tlmp_error ("content_length %u data_seen %u: %s\n",content_length,data_seen,line); if (data_seen >= content_length){ reset(); } } return ret; } }; struct HANDLE_INFO: public ARRAY_OBJ{ CONNECT_TYPE type; string uuid; // Waiting for a confirm on this UUID string from; time_t started; string user; STREAMP_BUF buf; bool locked; long long received; // How many bytes received while spying the protocol HTTP_CHECK hcheck; HANDLE_INFO(){ type = TYPE_NONE; started = time(NULL); locked = true; received = 0; } }; #include "proto/protocheck_2factors_control.protoh" /* Create a uniq ID combining the process number of this instance, a random value and the time in micro-seconds */ static string blackhole_makeid (int noproc) { string ret; char tmp[100]; struct timeval t; if (gettimeofday(&t,NULL)!=-1){ static int fd = -1; static bool error_shown = false; if (fd == -1){ fd = open ("/dev/urandom",O_RDONLY,0); } if (fd == -1){ if (!error_shown){ tlmp_error ("Can't open /dev/urandom (%s)\n",strerror(errno)); error_shown = true; } }else{ char buf[8]; if (read(fd,buf,8)!=8){ close (fd); fd = -1; if (!error_shown){ tlmp_error ("Can't read 8 bytes from /dev/urandom (%s)\n",strerror(errno)); error_shown = true; } }else{ for (int i=0; i<8; i++) snprintf (tmp+i*2,3,"%02x",buf[i]); int n = snprintf (tmp+16,sizeof(tmp)-16-1,"%08lx%08lx",t.tv_sec,t.tv_usec); snprintf (tmp+16+n,sizeof(tmp)-16-n-1,"-%d",noproc); ret = tmp; } } } return ret; } static void proto_showset (_F_TCPSERVER_V1 &c, const char *name, const set &vals) { c.sendf ("\t%s:",name); for (set::iterator it=vals.begin(); it != vals.end(); it++){ c.sendf (" %s",it->c_str()); } c.sendf ("\n"); } int main (int argc, char *argv[]) { glocal int ret = -1; glocal const char *port = "8200"; glocal const char *bindaddr = "0.0.0.0"; glocal const char *control = "/var/run/blackhole/protocheck-2factors.sock"; glocal const char *user = "blackhole"; glocal const char *command = "/usr/sbin/protocheck-2factors-sample.sh"; glocal bool daemon = false; glocal const char *pidfile = "/var/run/protocheck-2factors.pid"; glocal int timeout = 120; glocal bool follow_mode = false; // Don't use the GO command, spy the whole session glocal bool unlocked = false; glocal const char *file_http_rules = NULL; // This files contains directives for the HTTP // protocol checker glocal const char *learn_file = NULL; glocal const char *errorfile = NULL; glocal FILE *ferror = NULL; glocal unsigned max_http_errors = 1000; // Max number of http check error message (avoid filling disk) glocal.ret = (argc,argv); setproginfo ("protocheck-2factor",VERSION,"Interactive access control"); setarg ('c',"command","Command to execute for new client connection",glocal.command,false); setarg ('t',"timeout","Maximum time to wait for a confirmation",glocal.timeout,false); setarg ('f',"follow_mode","Follow the whole session, allows locking",glocal.follow_mode,false); setarg ('u',"unlocked","Connection is unlocked at startup",glocal.unlocked,false); setgrouparg ("Port"); setarg (' ',"port","Bind on this TCP port",glocal.port,false); setarg (' ',"bind","Bind on IP address",glocal.bindaddr,false); setarg (' ',"control","Unix socket control port",glocal.control,false); setgrouparg ("Protocols"); setarg (' ',"http","Check the HTTP protocol",glocal.file_http_rules,false); setarg (' ',"learnfile","Log inputs into this file",glocal.learn_file,false); setgrouparg ("Daemon options"); setarg (' ',"daemon","Run in background",glocal.daemon,false); setarg (' ',"pidfile","Write the daemon pid in this file",glocal.pidfile,false); setarg (' ',"user","Run as this user",glocal.user,false); setgrouparg ("Test options"); setarg (' ',"errorfile","Error message will be written to this file",glocal.errorfile,false); if (glocal.ferror != NULL){ time_t t = time(NULL); struct tm *tt = localtime(&t); fprintf (glocal.ferror,"%04d/%02d/%02d %02d:%02d:%02d: %s" ,tt->tm_year+1900,tt->tm_mon+1,tt->tm_mday ,tt->tm_hour,tt->tm_min,tt->tm_sec ,msg); fflush (glocal.ferror); }else if (glocal.daemon){ syslog (LOG_ERR,"%s",msg); }else{ fprintf (stderr,"%s",msg); } if (glocal.ferror != NULL){ fprintf (glocal.ferror,"WARN: %s",msg); fflush (glocal.ferror); }else if (glocal.daemon){ syslog (LOG_WARNING,"%s",msg); }else{ fprintf (stderr,"WARN: %s",msg); } int ret = -1; if (argc == 1 && strcmp(argv[0],"convert")==0){ if (glocal.learn_file == NULL){ tlmp_error ("Option --learnfile must be specified\n"); }else{ HTTP_RULES http_rules; if (http_rules.load(glocal.learn_file,NULL)==-1){ tlmp_error ("Some errors in file %s\n",glocal.learn_file); }else{ http_rules.dump(); ret = 0; } } } return ret; int ret = -1; glocal unsigned long http_nbrejects = 0; glocal unsigned long http_nbrequests = 0; glocal HTTP_RULES http_rules; glocal FILE *learn = NULL; glocal pid_t pidpipe = -1; glocal unsigned nb_http_errors = 0; if (glocal.file_http_rules != NULL){ if (glocal.http_rules.load(glocal.file_http_rules,NULL)==-1){ tlmp_error ("Some errors in file %s, Aborting\n",glocal.file_http_rules); exit (-1); } } if (glocal.learn_file != NULL){ glocal.learn = fopen (glocal.learn_file,"a"); if (glocal.learn == NULL){ tlmp_error ("Can't open learn file %s (%s), aborting\n",glocal.learn_file,strerror(errno)); exit (-1); } } if (glocal.errorfile != NULL){ glocal.ferror = fopen (glocal.errorfile,"a"); if (glocal.ferror == NULL){ tlmp_error ("Can't open errorfile %s (%s), aborting\n",glocal.errorfile,strerror(errno)); exit (-1); } } (glocal.bindaddr,glocal.port,5); HANDLE_INFO *c = new HANDLE_INFO; info.data = c; char addr[20]; ipnum_ip2a (from,addr); c->from = addr; if (strncmp(info.port,"unix:",5)==0 && strcmp(info.port+5,glocal.control)==0){ c->from = "unix:"; c->type = TYPE_CONTROL; }else{ if (strncmp(info.port,"unix:",5)==0){ c->from = info.port; } c->type = TYPE_CLIENT; setrawmode (true); c->hcheck.rules = glocal.http_rules; } HANDLE_INFO *c = (HANDLE_INFO*)info.data; if (c->type == TYPE_CLIENT){ glocal.http_nbrequests += c->hcheck.nbrequests; } HANDLE_INFO *c = (HANDLE_INFO*)info.data; if (c->type == TYPE_PIPE){ time_t now = time(NULL)-glocal.timeout; void *data; int fd = iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT && n->uuid.size() > 0 && n->started < now){ closeclient(fd); } fd = iter_next(data); } }else if (c->type == TYPE_CONTROL){ (this,line,endserver,endclient,no,c); glocal.TCPSERVER.sendf ("protocheck-2factors version %s\n",VERSION); glocal.TCPSERVER.sendf ("option: network %s %s\n",glocal.bindaddr,glocal.port); glocal.TCPSERVER.sendf ("option: unlocked=%d\n",glocal.unlocked); glocal.TCPSERVER.sendf ("option: follow_mode=%d\n",glocal.follow_mode); glocal.TCPSERVER.sendf ("option: command=%s\n",glocal.command); glocal.TCPSERVER.sendf ("option: http=%s\n" ,glocal.file_http_rules != NULL ? glocal.file_http_rules : ""); glocal.TCPSERVER.sendf ("option: learn_file=%s\n" ,glocal.learn_file != NULL ? glocal.learn_file : ""); unsigned long total_requests = glocal.http_nbrequests; { void *data; int fd = glocal.TCPSERVER.iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ total_requests += n->hcheck.nbrequests; } fd = glocal.TCPSERVER.iter_next(data); } } glocal.TCPSERVER.sendf ("requests: %lu\n",total_requests); glocal.TCPSERVER.sendf ("rejected: %lu\n",glocal.http_nbrejects); glocal.TCPSERVER.sendf ("http_errors: %u\n",glocal.nb_http_errors); time_t now = time(NULL); void *data; int fd = glocal.TCPSERVER.iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ if (n->uuid.empty()){ glocal.TCPSERVER.sendf ("client %d %s - - received=%Ld locked=%d\n" ,fd,n->user.c_str(),n->received,n->locked); }else{ glocal.TCPSERVER.sendf ("client %d %s %s %useconds received=%Ld\n" ,fd,n->user.c_str(),n->uuid.c_str(),(unsigned)(now-n->started),n->received); } } fd = glocal.TCPSERVER.iter_next(data); } endserver = true; if (glocal.pidpipe != -1) kill (glocal.pidpipe,SIGTERM); void *data; int fd = glocal.TCPSERVER.iter_init(data); bool found = false; while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT && strcmp(n->uuid.c_str(),uuid)==0){ found = true; if (glocal.follow_mode){ glocal.TCPSERVER.sendtof (fd,"SEND:%Ld\n",n->received); n->locked = false; n->uuid.clear(); }else{ glocal.TCPSERVER.sendto(fd,"GO\n"); glocal.TCPSERVER.closeclient (fd); } glocal.TCPSERVER.send ("Confirmed\n"); break; } fd = glocal.TCPSERVER.iter_next(data); } if (!found){ glocal.TCPSERVER.send ("Not matched\n"); } glocal.http_rules.load(filename,&glocal.TCPSERVER); for (map::iterator it=glocal.http_rules.urls.begin(); it != glocal.http_rules.urls.end(); it++){ glocal.TCPSERVER.sendf ("%s\n",it->first.c_str()); proto_showset (glocal.TCPSERVER,"cookies",it->second.cookies); proto_showset (glocal.TCPSERVER,"getvars",it->second.getvars); proto_showset (glocal.TCPSERVER,"postvars",it->second.postvars); } bool ison = atoi(on)==1; if (ison){ debug_seton(); }else{ debug_setoff(); } debug_setfdebug (filename); bool found = false; void *data; int fd = glocal.TCPSERVER.iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ if (strcmp(user,n->user.c_str())==0){ n->locked = true; glocal.TCPSERVER.sendf ("Locked\n"); found = true; break; } } fd = glocal.TCPSERVER.iter_next(data); } if (!found) glocal.TCPSERVER.send ("Not matched\n"); glocal.nb_http_errors = 0; tlmp_error ("Invalid control command %s\n",line); }else if (c->type == TYPE_CLIENT){ // We read the first line to find out who is the user // and do nothing with the rest of the stuff received by clients time_t t = time(NULL); struct tm *tt = localtime (&t); debug_printf ("%02d/%02d/%02d %02d:%02d:%02d user=%s line=%s\n" ,tt->tm_year+1900,tt->tm_mon+1,tt->tm_mday ,tt->tm_hour,tt->tm_min,tt->tm_sec ,c->user.c_str(),line); glocal HANDLE_INFO *c = c; glocal bool endclient = false; (c->buf,line,info.linelen); int ret = 0; if (glocal.c->user.empty()){ // We are waiting for a line starting with proto const char *line = (const char *)buf; for (int i=0; i words; int n = str_splitline (tmp.c_str(),' ',words); if (n != 5){ tlmp_error ("Invalid proto line: %s\n",line); glocal.endclient = true; }else{ glocal.c->user = words[2]; if (glocal.unlocked){ glocal.c->locked = false; }else{ glocal.c->uuid = blackhole_makeid(1); string cmd = string_f("%s %s %s",glocal.command,glocal.c->user.c_str(),glocal.c->uuid.c_str()); if (system(cmd.c_str())==-1){ tlmp_error ("Command failed: %s (%s)\n",cmd.c_str(),strerror(errno)); glocal.endclient = true; } } } } break; } } }else{ int used = len; if (glocal.file_http_rules != NULL){ const char *line = (const char *)buf; int start = 0; string first_error; used = 0; bool linefeed_seen = false; for (int i=0; i start && line[i-1] == '\r') last--; string tmp(line+start,last-start); string errmsg; int ok = glocal.c->hcheck.checkline(tmp.c_str(),glocal.learn,real_length,errmsg); if (glocal.learn != NULL){ fprintf (glocal.learn,"#%s ok=%d data_seen=%u content_length=%u %s\n" ,glocal.c->user.c_str(),ok ,glocal.c->hcheck.data_seen,glocal.c->hcheck.content_length ,tmp.c_str()); fflush (glocal.learn); }else if (ok==-1){ if (first_error.size()==0) first_error = errmsg; } start = used; } } if (len > 1000 && !linefeed_seen && glocal.c->hcheck.in_data && glocal.c->hcheck.binary_data){ // This is probably a binary file sent in a post. Nothing we can validate here. if (glocal.learn != NULL){ fprintf (glocal.learn,"No linefeed_seen len=%d\n",len); } used = len; } if (first_error.size() > 0){ glocal.nb_http_errors++; if (glocal.nb_http_errors < glocal.max_http_errors){ tlmp_error ("User %s, From %s Forward %s Url %s %s http protocol error: %s\n" ,glocal.c->user.c_str() ,glocal.c->from.c_str(),glocal.c->hcheck.collect.sourceip.c_str() ,glocal.c->hcheck.collect.reqtype.c_str(),glocal.c->hcheck.collect.url.c_str() ,first_error.c_str()); } glocal.endclient = true; glocal.http_nbrejects++; } } glocal.c->received += used; if (glocal.c->locked){ // We are receiving stuff on a locked connection // It is like a new login glocal.c->uuid = blackhole_makeid(1); glocal.c->started = time(NULL); string cmd = string_f("%s %s %s",glocal.command,glocal.c->user.c_str(),glocal.c->uuid.c_str()); if (system(cmd.c_str())==-1){ tlmp_error ("Command failed: %s (%s)\n",cmd.c_str(),strerror(errno)); glocal.endclient = true; } }else{ glocal.TCPSERVER.sendf ("SEND:%Ld\n",glocal.c->received); } ret = used; } return ret; endclient = glocal.endclient; } if (o.is_ok()){ bool some_errors = false; if (strncmp(glocal.port,"unix:",5)==0){ chmod (glocal.port+5,0666); } if (fdpass_setcontrol(o,glocal.control,glocal.user)==-1){ some_errors = true; } int tb[2]; if (pipe(tb)==-1){ some_errors = true; tlmp_error ("Can't setup pipe (%s)\n",strerror(errno)); } if (some_errors){ tlmp_error ("Some errors, ending\n"); }else{ if (glocal.daemon){ daemon_init (glocal.pidfile,glocal.user); } glocal.pidpipe = fork(); if (glocal.pidpipe==(pid_t)0){ for (int i=0; i<1024; i++){ if (i != tb[1]) close(i); } while (1){ sleep (5); write (tb[1],"1\n",2); } _exit (0); }else if (glocal.pidpipe == (pid_t)-1){ tlmp_error ("Can't fork (%s), ending\n",strerror(errno)); exit (-1); } close (tb[1]); HANDLE_INFO *n = new HANDLE_INFO; n->type = TYPE_PIPE; o.inject (tb[0],n); signal (SIGPIPE,SIG_IGN); o.loop(); if (glocal.learn != NULL) fclose (glocal.learn); ret = 0; } } return ret; return glocal.ret; }