/* safecgi.c by Michael Thorpe 2006-07-04 */ #include #include #include #include #include #include #include #include #include #include #define AUTHED_UID 33 /* Only allow this real-uid to run this program. */ #define CGI_DIR "cgi-bin" #define WWW_DIR "www" #define LOG_FILE "cgi-bin/error.log" void do_404() { fputs("Content-type: text/plain\n",stdout); fputs("Status: 404\n\n",stdout); fputs("That URL was not found.\n",stdout); exit(-1); } void do_500(char *error) { fputs("Content-type: text/plain\n",stdout); fputs("Status: 404\n\n",stdout); fputs("An internal server error was encountered:\n",stdout); fputs(error,stdout); fputc('\n',stdout); exit(-1); } void do_cgi(char *path,char *homedir) { char *argv[2]; char *docroot,*fullpath,*req_uri,*s,*t; int i,drlen,rulen; drlen=strlen(homedir)+1+strlen(WWW_DIR); docroot=(char *)malloc(drlen+1); if(!docroot) do_500("Unable to allocate memory"); sprintf(docroot,"%s/%s",homedir,WWW_DIR); setenv("DOCUMENT_ROOT",docroot,1); i=strlen(homedir)+1+strlen(CGI_DIR); fullpath=(char *)malloc(i+strlen(path)+1); if(!fullpath) do_500("Unable to allocate memory"); sprintf(fullpath,"%s/%s%s",homedir,CGI_DIR,path); argv[0]=fullpath; argv[1]=0; req_uri=getenv("REQUEST_URI"); if(!req_uri) do_500("What, no REQUEST_URI?"); req_uri=strdup(req_uri); if(!req_uri) do_500("Unable to allocate memory"); t=strchr(req_uri,'?'); if(t) rulen=t-req_uri; else rulen=strlen(req_uri); for(s=fullpath+i;s;) { s=strchr(s+1,'/'); if(s) { setenv("PATH_INFO",s,1); i=rulen-strlen(s); if(i<0 || req_uri[i] != '/') do_500("PATH_INFO doesn't mesh with REQUEST_URI"); req_uri[i]='\0'; setenv("SCRIPT_NAME",req_uri,1); req_uri[i]='/'; t=(char *)malloc(drlen+strlen(s)+1); if(!t) do_500("Unable to allocate memory"); sprintf(t,"%s%s",docroot,s); setenv("PATH_TRANSLATED",t,1); free(t); *s='\0'; } else { unsetenv("PATH_INFO"); setenv("SCRIPT_NAME",req_uri,1); unsetenv("PATH_TRANSLATED"); } setenv("SCRIPT_FILENAME",fullpath,1); execv(fullpath,argv); if(s) *s='/'; } do_404(); } int main(int argc,char **argv) { char *path,*user; int i; struct passwd *pw; if(AUTHED_UID && AUTHED_UID != getuid()) { fprintf(stderr,"Only UID %d may run this program.\n",AUTHED_UID); return(1); } path=getenv("PATH_INFO"); if(!path || path[0] != '/') do_404(); for(i=1;path[i] != '/';i++) if(!isalnum(path[i])) do_500("Unknown user"); user=(char *)malloc(i); if(!user) do_500("Unable to allocate memory"); strncpy(user,path+1,i-1); user[i]='\0'; path+=i; if(chdir("/")) do_500("Unable to chdir to /"); pw=getpwnam(user); if(!pw) do_500("Unknown user"); if(initgroups(pw->pw_name,pw->pw_gid)) do_500("Unable to initgroups"); if(setregid(pw->pw_gid,pw->pw_gid)) do_500("Unable to change gids"); if(setreuid(pw->pw_uid,pw->pw_uid)) do_500("Unable to change uids"); if(chdir(pw->pw_dir)) do_404(); #ifdef LOG_FILE i=open(LOG_FILE,O_WRONLY|O_APPEND|O_CREAT,0644); if(i==-1) { perror("open"); return(-1); } if(i != 2) { dup2(i,2); close(i); } #endif setenv("HOME",pw->pw_dir,1); if(chdir(CGI_DIR)) do_404(); do_cgi(path,pw->pw_dir); return(-1); }