/* /daemon/http.c * From the Dead Souls LPC Library * An http socket for the WWW (based on the Dead Souls http daemon) * Created by Descartes of Borg 950429 * Version: @(#) http.c 1.1@(#) * Last modified: 96/12/19 */ /* Note on how this works: * On Dead Souls, this http program is in fact not so much a "daemon" * as an "applet". The inet daemon starts a server program that * watches the http port. When a request comes in, that server program * fires up a *clone* of this here file. Therefore, you will not see * multiple-connection handling here. This http applet services a * single connection, and dies happy when its job is done. * * The CGI stuff is basically just sending the post and get requests to * LPC objects in the cgi/ directory. The key thing to remember if you're * going to play around here is that this object closes and dies on write. So * if you're going to receive post data in multiple chunks (as is common) * you will want to ensure it is either queued here until sent to the * cgi object, or that the cgi object somehow prevents this object from * writing its data to the client til all chunks are received and processed. * * -Crat 09Feb2008 */ #include <lib.h> #include <dirs.h> #include <logs.h> #include <daemons.h> #define FILE_BAD_CMD DIR_WWW_ERRORS "/badcmd.html" #define FILE_BAD_GATE DIR_WWW_ERRORS "/badgate.html" #define FILE_NOT_FOUND DIR_WWW_ERRORS "/notfound.html" #define ACCESS_DENIED DIR_WWW_ERRORS "/denied.html" #define DIRECTORY_LISTINGS #ifndef ENABLE_CGI #define ENABLE_CGI 1 #endif #ifndef WWW_DIR_LIST #define WWW_DIR_LIST 1 #endif #define TAB "    " inherit LIB_SOCKET; string ip = ""; string out = ""; string host; int port; mapping Cookie = ([]); string gateway, login_data, current_page, boundary, cookie, cmd, read_args, filename, user_agent; int ok_to_send, boundary_count; int logging = 1; void validate(){ if(!(int)master()->valid_apply(({ "SECURE", "ASSIST" })) && strsrch(base_name(previous_object()), DIR_WWW_GATEWAYS)){ string offender = identify(previous_object(-1)); debug("HTTPD SECURITY VIOLATION: "+offender+" ",get_stack(),"red"); log_file("/secure/security", "\n"+timestamp()+" HTTPD breach: "+offender+" "+get_stack()); error("HTTPD SECURITY VIOLATION: "+offender+" "+get_stack()); } } void eventSendData(string str){ buffer b; int i; validate(); if(!str || !sizeof(str)) return; str = strip_colours(str); b = allocate_buffer(strlen(str)); for(i=0; i<strlen(str); i++) { b[i] = str[i]; } eventWrite(b, 1); } void eventLogConnection(){ if(logging){ log_file(LOG_HTTP,ip+" "+timestamp()+" "+identify(read_args)+" "+current_page+"\n"); } } int authenticate(string path){ validate(); if(!Cookie || !sizeof(Cookie) || !Cookie["name"] || !Cookie["shib"]) return 0; if(!strsrch(path,REALMS_DIRS)){ if(!strsrch(path,REALMS_DIRS+"/"+Cookie["name"])){ if(WEB_SESSIONS_D->GetShibboleth(Cookie["name"]) == Cookie["shib"]){ return 1; } } } return 0; } string GetReferer(){ return current_page; } string GetBoundary(){ return boundary; } mapping GetCookie(){ validate(); return copy(Cookie); } string GetHost(){ return host; } int GetPort(){ return port; } private static void eventError(string name) { object file = new(LIB_FILE, name); eventWrite(file->GetBuffer(), 1); } void eventBlockIp(){ if(!strsrch(base_name(previous_object()), DIR_WWW_GATEWAYS) ){ INET_D->eventBlockIp(ip); } } string GetIp(){ return ip; } mixed GenerateIndex(string dir, string requested){ string ret = "<html>\n"; string prefix = ""; mixed *listing; if(!dir || !directory_exists(dir)) return 0; listing = get_dir(dir+"/"); if(!strsrch(dir,DIR_WWW)) dir = replace_string(dir,DIR_WWW,"",1); prefix = path_prefix(requested); if(!sizeof(prefix)) prefix = "/"; if(WEB_SESSIONS_D->GetSession(Cookie["name"])){ if(authenticate(dir) && requested[1..1] != "~"){ ret += "<FORM METHOD=POST ENCTYPE=\"multipart/form-data\" ACTION=\"/cgi/upload.html\">"; ret += "Upload file (text files only): <INPUT TYPE=FILE NAME=\"upfile\">"; ret += "<INPUT TYPE=SUBMIT VALUE=\"Submit\"></FORM>"; ret += "<FORM ACTION=\"/cgi/new\">Create a folder <INPUT name=\"dir\" VALUE=\""+dir+"/\" SIZE=40></FORM>"; ret += "<FORM ACTION=\"/cgi/new\">Create a file <INPUT name=\"file\" VALUE=\""+dir+"/\" SIZE=40></FORM>"; } } ret += "<a href=\""+prefix+"\">Parent Directory </a>\n"; ret +="<hr style=\"width: 100%; height: 2px;\"><br>\n"; foreach(string sub in listing){ string ed_req = "<a href=\"/cgi/edit.html?"+dir+"/"+sub+"\">Edit</a>"; ret +="<a href=\""+requested+"/"+sub+"\">"+sub+(directory_exists(dir+"/"+sub) ? "/" : "")+"</a>"; ret += (directory_exists(dir+"/"+sub) ? "" : TAB + ed_req); ret += "<br>\n"; } ret +="</html>"; ret = replace_string(ret,"//","/"); return ret; } void eventRemoveTmp(string file){ rm(file); } varargs private static mixed eventGetFile(string name, string type, string payload) { string array parts; string tmpfile, orig, requested; object file; name = explode(name, " ")[0]; if( name[0] != '/' ) { name = "/" + name; } requested = name; parts = explode(name = absolute_path("/", name), "/"); if( !sizeof(parts) ) { name = DIR_WWW "/index.html"; } else if( parts[0][0] == '~' ) { parts[0] = user_path(parts[0][1..]) + "/public_html"; name = implode(parts, "/"); } if(name == REALMS_DIRS || name == REALMS_DIRS +"/") name = DIR_WWW + "/index.html"; if( strsrch(name, DIR_WWW) && strsrch(name, REALMS_DIRS) ) { name = DIR_WWW + name; } if(!strsrch(name,REALMS_DIRS) && !grepp(name,"/public_html") ){ if(!authenticate(name)) eventError(FILE_NOT_FOUND); } orig = name; file = new(LIB_FILE, name); if( file->isDirectory() ) { file = new(LIB_FILE, name = name + "/index.html"); } if( ENABLE_CGI && !strsrch(name, DIR_WWW_GATEWAYS) ) { string id, args, str, foo; buffer b; if( sscanf(name, DIR_WWW_GATEWAYS "/%s?%s", id, args) != 2 ) { args = 0; sscanf(name, DIR_WWW_GATEWAYS "/%s", id); } if( sscanf(id, "%s.%s", foo, str) == 2 ) { id = foo+".c"; } if(payload) args = payload; if( catch(str = (DIR_WWW_GATEWAYS "/"+id)->gateway(args)) ) { eventError(FILE_BAD_GATE); return 1; } str = strip_colours(str); b = allocate_buffer(strlen(str)); for(int i=0; i<strlen(str); i++) { b[i] = str[i]; } eventWrite(b, 1); return 1; } else if( WWW_DIR_LIST && !file->isFile() ) { mixed ret = GenerateIndex(orig, requested); tmpfile = "/secure/tmp/webtemp.html"; if(ret){ write_file(tmpfile, ret,1); file = new(LIB_FILE, tmpfile); eventWrite(file->GetBuffer(),1); } else { eventError(FILE_NOT_FOUND); return 1; } } else if( !WWW_DIR_LIST && !file->isFile() ) { eventError(FILE_NOT_FOUND); return 1; } else { eventWrite(file->GetBuffer(), 1); } } int eventRead(buffer data) { string *args_tmp, *posted_file; string browser, *sock_stat; string str = read_buffer(data); int argsize, i, j; buffer b; if(!Cookie || !sizeof(Cookie)){ Cookie = ([]); Cookie["name"] = 0; Cookie["shib"] = 0; } ip = socket_ip(socket::GetDescriptor()); if(member_array(ip,INET_D->GetBlockedIps()) != -1){ eventError(ACCESS_DENIED); return 1; } if( !socket::eventRead(str) ) { return 0; } if( !str || str == "" ) { eventError(FILE_BAD_CMD); return 1; } if(!read_args) read_args = explode(replace_string(str, CARRIAGE_RETURN, ""), "\n")[0]; args_tmp = explode(replace_string(str, CARRIAGE_RETURN, ""), "\n"); foreach(mixed element in args_tmp){ int int1, int2; string junk1, junk2; junk2 = reverse_string(element); int2 = sscanf(junk2,"%s---%*s",junk1); if(!strsrch(element,"Cookie:") && !cookie) cookie = element; if(!strsrch(element,"Host:") && !host){ if(sscanf(element,"Host: %s:%s",junk1, junk2) != 2){ sscanf(element,"Host: %s",junk1); port = 80; } else port = atoi(junk2); host = junk1; } if(boundary && grepp(element,boundary)){ boundary_count++; } else if(boundary && int2 == 2){ junk2 = reverse_string(junk1); junk1 = replace_string(boundary,"-",""); if(boundary && sizeof(junk2) > 5 && first(junk1,sizeof(junk2)) == junk2){ boundary_count++; } } if(grepp(element, "boundary=")){ sscanf(element,"%sboundary=%s",junk1,boundary); args_tmp -= ({ element }); } if(!strsrch(element, "Referer:") && !current_page){ sscanf(element,"Referer: %s",current_page); } if(!strsrch(element, "User-Agent:") && !user_agent){ sscanf(element,"User-Agent: %s",user_agent); } if(!strsrch(element, "Content-Disposition: form-data;") && !filename){ if(sscanf(element,"%sfilename=\"%s\"",junk1, filename) != 2) sscanf(element,"%sfilename=\"%s\"%s",junk1, filename, junk2); if(filename && filename[1..2]==":\\"){ //lol mircosoft filename=last_string_element(filename,"\\"); } } if(!strsrch(element, "username=") && !login_data){ login_data = element; } } out += implode(args_tmp,"\n"); if(cookie){ string name, shib, junk1, junk2; if(sscanf(cookie,"%screweb=%s.%s;%s",junk1,name,shib,junk2) == 4 || sscanf(cookie,"%screweb=%s.%s",junk1,name,shib) == 3){ Cookie["name"] = name; Cookie["shib"] = shib; } } if(!cmd) sscanf(read_args, "%s %s", cmd, read_args); if(!read_args) sscanf(read_args, "%s %s", cmd, read_args); eventLogConnection(); switch(lower_case(cmd)) { string junk; case "get": eventGetFile(read_args); return 1; case "post": if(!ENABLE_CGI){ eventError(FILE_BAD_CMD); return 1; } if(!gateway) sscanf(read_args,"%s HTTP%s",gateway,junk); if(boundary_count && boundary_count > 1){ string junk1, junk2, tmp; #if 0 if(sscanf(out,"%s"+boundary+"%s--"+boundary+"%s",junk1,tmp,junk2) == 3){ out = tmp; args_tmp=explode(out,"\n"); if(filename) out = implode(args_tmp[2..],"\n"); else out = implode(args_tmp[1..],"\n"); } #endif sscanf(out,"%s--%s",tmp,junk1); if(grepp(out,boundary+"--")) eventGetFile(read_args, "POST", out); return 1; #if 0 if(!filename){ if( catch(str = ((DIR_WWW_GATEWAYS +"/save")->gateway(out)) ) ){ eventError(FILE_BAD_GATE); return 1; } } else if( catch(str = ((DIR_WWW_GATEWAYS +"/upload")->gateway(out, current_page, filename, Cookie["name"], Cookie["shib"]))) ) { eventError(FILE_BAD_GATE); return 1; } str = strip_colours(str); b = allocate_buffer(strlen(str)); for(j=0; j<strlen(str); j++) { b[j] = str[j]; } eventWrite(b, 1); #endif } else if(login_data){ eventGetFile("cgi/login.html?"+login_data); } return 1; default: eventError(FILE_BAD_CMD); return 1; } return 1; }