/* * Usage: as CGI script, but called by userv * environment variables are USERV_U_E_... */ /* * Copyright 1996-2013,2016 Ian Jackson * Copyright 1998 David Damerell * Copyright 1999,2003 * Chancellor Masters and Scholars of the University of Cambridge * Copyright 2010 Tony Finch * Copyright 2013,2016 Mark Wooding * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with userv-utils; if not, see http://www.gnu.org/licenses/. */ #include #include #include #include #include #include #include #include #include "ucgi.h" static const char *const default_envok[]= { "AUTH_TYPE", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CACHE_CONTROL", "HTTP_CONNECTION", "HTTP_CONTENT_ENCODING", "HTTP_COOKIE", "HTTP_DNT", "HTTP_HOST", "HTTP_KEEP_ALIVE", "HTTP_NEGOTIATE", "HTTP_PRAGMA", "HTTP_REFERER", "HTTP_USER_AGENT", "HTTP_VIA", "HTTP_X_FORWARDED_FOR", "HTTPS", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "REDIRECT_HANDLER", "REDIRECT_SCRIPT_URI", "REDIRECT_SCRIPT_URL", "REDIRECT_STATUS", "REDIRECT_URL", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_USER", "REMOTE_IDENT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SCRIPT_URI", "SCRIPT_URL", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SIGNATURE", "SERVER_SOFTWARE", "SSL_CIPHER", "SSL_CLIENT_S_DN", "SSL_CLIENT_VERIFY", "SSL_PROTOCOL", 0 }; static void setenvar(const char *fulln, const char *en, const char *ep, void *p) { xsetenv(en, ep, 1); unsetenv(fulln); } int main(int argc, char **argv) { char *scriptpath, *newvar; const char *nextslash, *lastslash, *pathi, *ev, *ev2, *scriptdir, *av; const char *const *envok; const char **arguments; size_t scriptdirlen, scriptpathlen, l; struct stat stab; int i, r, nargs; const char *filters= 0; ev= getenv("USERV_U_DEBUG"); if (ev && *ev) debugmode= 1; D( if (debugmode) printf(";;; UCGITARGET\n"); ) if (argc > MAX_ARGS) error("too many arguments", 500); for (;;) { i= getopt(argc, argv, "+e:"); if (i < 0) break; switch (i) { case 'e': filters= optarg; break; default: error("bad command line", 500); break; } } argc -= optind; argv += optind; if (!*argv) error("no script directory argument", 500); ev= getenv("HOME"); if (!ev) error("no HOME env. var", 500); l= strlen(*argv)+strlen(ev); newvar= xmalloc(l+2); sprintf(newvar,"%s/%s",ev,*argv); scriptdir= newvar; scriptdirlen= strlen(scriptdir); if (filters) envok= load_filters(LOADF_MUST, filters, LF_END); else { envok= load_filters(0, ".userv/ucgitarget.env-filter", "/etc/userv/ucgitarget.env-filter", LF_END); } filter_environment(0, "USERV_U_E_", envok, default_envok, setenvar, 0); scriptpath= 0; pathi= getenv("PATH_INFO"); if (!pathi) error("PATH_INFO not found", 500); lastslash= pathi; D( if (debugmode) { printf(";; find script name...\n" ";; PATH_INFO = `%s'\n", pathi); } ) for (;;) { if (*lastslash != '/') error("PATH_INFO expected slash not found", 400); if (lastslash[1]=='.' || lastslash[1]=='#' || !lastslash[1]) error("bad char begin", 400); nextslash= strchr(lastslash+1,'/'); if (!nextslash) nextslash= lastslash+1+strlen(lastslash+1); if (!nextslash) error("insufficient elements in PATH_INFO", 400); if (nextslash==lastslash+1) error("empty component in PATH_INFO", 400); if (nextslash-pathi > MAX_SCRIPTPATH_LEN) error("PATH_INFO script path too long", 400); scriptpathlen= scriptdirlen+(nextslash-pathi); scriptpath= xrealloc(scriptpath,scriptpathlen+1); strcpy(scriptpath,scriptdir); memcpy(scriptpath+scriptdirlen,pathi,nextslash-pathi); scriptpath[scriptpathlen]= 0; if (scriptpath[scriptpathlen-1]=='~') error("bad char end", 400); D( if (debugmode) printf(";; try `%s'\n", scriptpath); ) r= stat(scriptpath,&stab); if (r) syserror("stat script"); if (S_ISREG(stab.st_mode)) break; if (!S_ISDIR(stab.st_mode)) error("script not directory or file", 500); lastslash= nextslash; } D( if (debugmode) printf(";; found script: tail = `%s'\n", nextslash); ) if (*nextslash) xsetenv("PATH_INFO",nextslash,1); else unsetenv("PATH_INFO"); newvar= xmalloc(scriptpathlen+strlen(nextslash)+3); sprintf(newvar,"%s%s",scriptpath,nextslash); xsetenv("PATH_TRANSLATED",newvar,1); xsetenv("SCRIPT_FILENAME",scriptpath,1); ev= getenv("SCRIPT_NAME"); if (ev) { ev2= getenv("USER"); if (!ev2) error("no USER variable", 500); newvar= xmalloc(strlen(ev)+2+strlen(ev2)+scriptpathlen-scriptdirlen+2); sprintf(newvar,"%s/~%s%s",ev,ev2,scriptpath+scriptdirlen); xsetenv("SCRIPT_NAME",newvar,1); } arguments= xmalloc(sizeof(const char*)*(argc+5)); nargs= 0; arguments[nargs++]= scriptpath; while ((av= (*++argv))) arguments[nargs++]= av; arguments[nargs++]= 0; D( if (debugmode) { int i; printf(";; final environment...\n"); for (i = 0; environ[i]; i++) printf(";; %s\n", environ[i]); printf(";; final command line...\n"); for (i = 0; arguments[i]; i++) printf(";; %s\n", arguments[i]); fflush(stdout); } ) execvp(scriptpath,(char*const*)arguments); syserror("exec script"); return -1; }