From 1ba0145fe6fe72f63af15e12716c13071523eaa4 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 29 Jan 2013 23:49:38 +0000 Subject: www-cgi/ucgitarget.c: Use `error' to report unusual filesystem object. There's nothing useful in `errno'. Let's not end up saying `Success' here. --- www-cgi/ucgitarget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index eb5fdab..4228951 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -94,7 +94,7 @@ int main(int argc, const char **argv) { if (scriptpath[scriptpathlen-1]=='~') error("bad char end"); r= stat(scriptpath,&stab); if (r) syserror("stat script"); if (S_ISREG(stab.st_mode)) break; - if (!S_ISDIR(stab.st_mode)) syserror("script not directory or file"); + if (!S_ISDIR(stab.st_mode)) error("script not directory or file"); lastslash= nextslash; } if (*nextslash) xsetenv("PATH_INFO",nextslash,1); -- cgit v1.2.3 From 44a77f4851d3c819f9b364018a9695f332758a71 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 29 Jan 2013 23:53:55 +0000 Subject: www-cgi/: Move `xrealloc' to `ucgicommon'. Both programs will want it soon enough. --- www-cgi/ucgi.h | 1 + www-cgi/ucgicommon.c | 8 ++++++++ www-cgi/ucgitarget.c | 8 -------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 5915321..5a51a77 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -32,6 +32,7 @@ void syserror(const char *m); void error(const char *m); void *xmalloc(size_t sz); void xsetenv(const char *en, const char *ev, int overwrite); +void *xrealloc(void *ptr, size_t sz); extern const char *const envok[]; extern const int nenvok; diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 4a8749a..43b7d94 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -103,6 +103,14 @@ void *xmalloc(size_t sz) { return r; } +void *xrealloc(void *ptr, size_t sz) { + void *r; + + r= realloc(ptr,sz); + if (!r) syserror("realloc failed"); + return r; +} + void xsetenv(const char *en, const char *ev, int overwrite) { if (setenv(en,ev,overwrite)) syserror("setenv"); } diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 4228951..e38ad30 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -32,14 +32,6 @@ #include "ucgi.h" -static void *xrealloc(void *ptr, size_t sz) { - void *r; - - r= realloc(ptr,sz); - if (!r) syserror("realloc failed"); - return r; -} - int main(int argc, const char **argv) { char *uservarn, *scriptpath, *newvar; const char *nextslash, *lastslash, *pathi, *ev, *ev2, *en, *scriptdir, *av; -- cgit v1.2.3 From 6a3086f1860a27894942124c1ecd62502bb4b670 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 29 Jan 2013 23:45:47 +0000 Subject: www-cgi/: Add some trivial tracing. The trace goes to standard output, and only happens when debugging is turned on, both operationally (e.g., though the `ucgi-debug' link or USERV_U_DEBUG variable) and at compile time (with the DEBUG macro, which you can set, e.g., with `make DEBUG="-g -DDEBUG"'. Trace output lines start with `;;'. I'll be grateful for this when I start shaking things up. --- www-cgi/ucgi.c | 18 ++++++++++++++++++ www-cgi/ucgi.h | 6 ++++++ www-cgi/ucgitarget.c | 21 +++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index c1239d0..374fea5 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -46,18 +46,26 @@ int main(int argc, const char **argv) { if (fputs("Content-Type: text/plain\n\n",stdout)==EOF || fflush(stdout)) syserror("write stdout"); if (dup2(1,2)<0) { perror("dup stdout to stderr"); exit(-1); } + D( printf(";;; UCGI\n"); ) } if (argc > MAX_ARGS) error("too many arguments"); pathi= getenv("PATH_INFO"); if (!pathi) error("PATH_INFO not found"); + D( if (debugmode) { + printf(";; find user name...\n" + ";; initial PATH_INFO = `%s'\n", + pathi); + } ) if (pathi[0] != '/' || pathi[1] != '~') error("PATH_INFO must start with /~"); slash2= strchr(pathi+2,'/'); if (!slash2) error("PATH_INFO must have more than one /"); usernamelen= slash2-(pathi+2); if (usernamelen > MAX_USERNAME_LEN) error("PATH_INFO username too long"); username= xmalloc(usernamelen+1); memcpy(username,pathi+2,usernamelen); username[usernamelen]= 0; + D( if (debugmode) + printf(";; user = `%s'; tail = `%s'\n", username, slash2); ) if (!isalpha(username[0])) error("username 1st character is not alphabetic"); xsetenv("PATH_INFO",slash2,1); @@ -81,6 +89,7 @@ int main(int argc, const char **argv) { arguments[nargs++]= 0; if (debugmode) { + D( fflush(stdout); ) child= fork(); if (child==-1) syserror("fork"); if (child) { rchild= waitpid(child,&status,0); @@ -90,6 +99,15 @@ int main(int argc, const char **argv) { } } + D( if (debugmode) { + int i; + + printf(";; final command line...\n"); + for (i = 0; arguments[i]; i++) + printf(";; %s\n", arguments[i]); + fflush(stdout); + } ) + execvp("userv",(char*const*)arguments); syserror("exec userv"); return -1; diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 5a51a77..0ab5a49 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -23,6 +23,12 @@ #include +#ifdef DEBUG +# define D(x) x +#else +# define D(x) +#endif + #define MAX_ARGS 1024 #define MAX_USERNAME_LEN 1024 #define MAX_SCRIPTPATH_LEN 1024 diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index e38ad30..9637235 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -44,6 +44,7 @@ int main(int argc, const char **argv) { ev= getenv("USERV_U_DEBUG"); if (ev && *ev) debugmode= 1; + D( if (debugmode) printf(";;; UCGITARGET\n"); ) if (argc > MAX_ARGS) error("too many arguments"); if (!*++argv) error("no script directory argument"); @@ -70,6 +71,11 @@ int main(int argc, const char **argv) { pathi= getenv("PATH_INFO"); if (!pathi) error("PATH_INFO not found"); 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"); if (lastslash[1]=='.' || lastslash[1]=='#' || !lastslash[1]) error("bad char begin"); @@ -84,11 +90,13 @@ int main(int argc, const char **argv) { memcpy(scriptpath+scriptdirlen,pathi,nextslash-pathi); scriptpath[scriptpathlen]= 0; if (scriptpath[scriptpathlen-1]=='~') error("bad char end"); + 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"); lastslash= nextslash; } + D( if (debugmode) printf(";; found script: tail = `%s'\n", nextslash); ) if (*nextslash) xsetenv("PATH_INFO",nextslash,1); else unsetenv("PATH_INFO"); @@ -113,6 +121,19 @@ int main(int argc, const char **argv) { 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; -- cgit v1.2.3 From 77a36cae0e59a1e17c12c71fd7ff49be0958b11d Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 30 Jan 2013 00:47:45 +0000 Subject: www-cgi/ucgi.c: A bit more machinery for building the command line. Move the state for building the command line into a structure, and introduce a function for adding an argument. We'll want this later. --- www-cgi/ucgi.c | 58 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index 374fea5..431d280 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -30,14 +30,35 @@ #include "ucgi.h" +struct buildargs { + const char **v; + int n, max; +}; + +static void addarg(struct buildargs *args, const char *a) { + if (args->n > args->max) error("too many arguments"); + args->v[args->n++]= a; +} + +static void add_userv_var(const char *en, const char *ev, + struct buildargs *args) { + size_t l; + char *a; + + l= strlen(ev); if (l > MAX_ENVVAR_VALUE) error("environment variable too long"); + a= xmalloc(strlen(en)+l+6); + sprintf(a,"-DE_%s=%s",en,ev); + addarg(args, a); +} + int main(int argc, const char **argv) { - char *defarg, *username; + char *username; const char *slash2, *pathi, *ev, *en, *av; const char *const *ep; - const char **arguments; size_t usernamelen, l; + struct buildargs args; pid_t child, rchild; - int nargs, status; + int status; l= strlen(argv[0]); if (l>6 && !strcmp(argv[0]+l-6,"-debug")) debugmode= 1; @@ -68,25 +89,22 @@ int main(int argc, const char **argv) { printf(";; user = `%s'; tail = `%s'\n", username, slash2); ) if (!isalpha(username[0])) error("username 1st character is not alphabetic"); xsetenv("PATH_INFO",slash2,1); + + args.n= 0; args.max= argc + nenvok + 10; + args.v= xmalloc(args.max * sizeof(*args.v)); - arguments= xmalloc(sizeof(const char*)*(nenvok+argc+10)); - nargs= 0; - - arguments[nargs++]= "userv"; - if (debugmode) arguments[nargs++]= "-DDEBUG=1"; - + addarg(&args, "userv"); + if (debugmode) addarg(&args, "-DDEBUG=1"); + for (ep= envok; (en= *ep); ep++) { ev= getenv(en); if (!ev) continue; - l= strlen(ev); if (l > MAX_ENVVAR_VALUE) error("environment variable too long"); - defarg= xmalloc(strlen(en)+l+6); - sprintf(defarg,"-DE_%s=%s",en,ev); - arguments[nargs++]= defarg; + add_userv_var(en, ev, &args); } - arguments[nargs++]= username; - arguments[nargs++]= "www-cgi"; - while ((av= (*++argv))) arguments[nargs++]= av; - arguments[nargs++]= 0; + addarg(&args, username); + addarg(&args, "www-cgi"); + while ((av= (*++argv))) addarg(&args, av); + addarg(&args, 0); if (debugmode) { D( fflush(stdout); ) @@ -103,12 +121,12 @@ int main(int argc, const char **argv) { int i; printf(";; final command line...\n"); - for (i = 0; arguments[i]; i++) - printf(";; %s\n", arguments[i]); + for (i = 0; args.v[i]; i++) + printf(";; %s\n", args.v[i]); fflush(stdout); } ) - execvp("userv",(char*const*)arguments); + execvp("userv",(char*const*)args.v); syserror("exec userv"); return -1; } -- cgit v1.2.3 From f601a2c663d9135dec19172a593502864d10393e Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 30 Jan 2013 00:09:47 +0000 Subject: www-cgi/: Centralize environment variable filtering. Rather than have a different loop in each program which trundles through a filter list picking up environment variables and doing things to the ones that match, invent a new function `filter_environment' which does the job, with extra steroids. The new function works the other way around: it iterates over the environment, comparing each variable to the filter list. It also supports some simple prefix-matching (`*' suffix) and blacklisting (`!' prefix) operations. Some new limits are introduced, on the maximum length of an environment variable name, and the total number of variables accepted by `ucgi': this is because these are no longer limited implicitly by the whitelist, since it may contain wildcards and suchlike. --- www-cgi/ucgi.c | 15 +++++------ www-cgi/ucgi.h | 9 +++++++ www-cgi/ucgicommon.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ www-cgi/ucgitarget.c | 25 +++++++----------- 4 files changed, 100 insertions(+), 24 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index 431d280..c4c072d 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -40,8 +40,9 @@ static void addarg(struct buildargs *args, const char *a) { args->v[args->n++]= a; } -static void add_userv_var(const char *en, const char *ev, - struct buildargs *args) { +static void add_userv_var(const char *fulln, + const char *en, const char *ev, void *p) { + struct buildargs *args= p; size_t l; char *a; @@ -53,8 +54,7 @@ static void add_userv_var(const char *en, const char *ev, int main(int argc, const char **argv) { char *username; - const char *slash2, *pathi, *ev, *en, *av; - const char *const *ep; + const char *slash2, *pathi, *av; size_t usernamelen, l; struct buildargs args; pid_t child, rchild; @@ -90,16 +90,13 @@ int main(int argc, const char **argv) { if (!isalpha(username[0])) error("username 1st character is not alphabetic"); xsetenv("PATH_INFO",slash2,1); - args.n= 0; args.max= argc + nenvok + 10; + args.n= 0; args.max= argc + MAX_ENVVARS + 10; args.v= xmalloc(args.max * sizeof(*args.v)); addarg(&args, "userv"); if (debugmode) addarg(&args, "-DDEBUG=1"); - for (ep= envok; (en= *ep); ep++) { - ev= getenv(en); if (!ev) continue; - add_userv_var(en, ev, &args); - } + filter_environment(FILTF_WILDCARD, "", envok, add_userv_var, &args); addarg(&args, username); addarg(&args, "www-cgi"); diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 0ab5a49..1d00b1b 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -32,7 +32,9 @@ #define MAX_ARGS 1024 #define MAX_USERNAME_LEN 1024 #define MAX_SCRIPTPATH_LEN 1024 +#define MAX_ENVVAR_NAME 128 #define MAX_ENVVAR_VALUE (1024*1024) +#define MAX_ENVVARS 256 void syserror(const char *m); void error(const char *m); @@ -40,6 +42,13 @@ void *xmalloc(size_t sz); void xsetenv(const char *en, const char *ev, int overwrite); void *xrealloc(void *ptr, size_t sz); +void filter_environment(unsigned flags, const char *prefix_in, + const char *const *patv, + void (*foundone)(const char *fulln, const char *en, + const char *ev, void *p), + void *p); +#define FILTF_WILDCARD 1u + extern const char *const envok[]; extern const int nenvok; extern int debugmode; diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 43b7d94..0facfbb 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -22,6 +22,8 @@ #include #include +#include + #include "ucgi.h" const char *const envok[]= { @@ -114,3 +116,76 @@ void *xrealloc(void *ptr, size_t sz) { void xsetenv(const char *en, const char *ev, int overwrite) { if (setenv(en,ev,overwrite)) syserror("setenv"); } + +void filter_environment(unsigned flags, const char *prefix_in, + const char *const *patv, + void (*foundone)(const char *fulln, + const char *en, const char *ev, + void *p), + void *p) +{ + char *const *ep; + const char *const *patp; + const char *en, *ev, *pat, *q; + char enbuf[MAX_ENVVAR_NAME]; + size_t n, pn = strlen(prefix_in); + int acceptp; + + D( if (debugmode) printf(";; filter_environment...\n"); ) + for (ep= environ; (en= *ep); ep++) { + D( if (debugmode) printf(";; consider env-var `%s'\n", en); ) + if (strncmp(en, prefix_in, pn) != 0 || !en[pn]) { + D( if (debugmode) printf(";; doesn't match prefix\n"); ) + goto next_ev; + } + for (patp= patv; (pat= *patp); patp++) { + q= en + pn; + acceptp= 1; + if (*pat == '!' && (flags & FILTF_WILDCARD)) { + acceptp= 0; pat++; + } + for (;;) { + if (!*pat) { + if (*q != '=') { + D( if (debugmode) + printf(";; mismatch `%s' (prefix)\n", *patp); ) + goto next_pat; + } + D( if (debugmode) printf(";; matched `%s'\n", *patp); ) + ev = q + 1; + break; + } else if (*pat == '*' && (flags & FILTF_WILDCARD)) { + q = strchr(q, '='); + if (!q) { + D( if (debugmode) + printf(";; mismatch `%s' (discard: no `=')\n", *patp); ) + goto next_ev; + } + D( if (debugmode) + printf(";; wildcard match for `%s'\n", *patp); ) + ev = q + 1; + break; + } else + if (*pat++ != *q++) { + D( if (debugmode) printf(";; mismatch `%s'\n", *patp); ) + goto next_pat; + } + } + if (acceptp) { + n= q - en; + if (n >= sizeof(enbuf)) + error("environment variable name too long"); + memcpy(enbuf, en, n); + enbuf[n]= 0; + D( if (debugmode) + printf(";; full = `%s'; tail = `%s'; value = `%s'\n", + enbuf, enbuf + pn, ev); ) + foundone(enbuf, enbuf + pn, ev, p); + } D( else if (debugmode) + printf(";; matched negated pattern\n"); ) + goto next_ev; + next_pat:; + } + next_ev:; + } +} diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 9637235..a4b5690 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -32,12 +32,17 @@ #include "ucgi.h" +static void setenvar(const char *fulln, + const char *en, const char *ep, void *p) { + xsetenv(en, ep, 1); + unsetenv(fulln); +} + int main(int argc, const char **argv) { - char *uservarn, *scriptpath, *newvar; - const char *nextslash, *lastslash, *pathi, *ev, *ev2, *en, *scriptdir, *av; - const char *const *ep; + char *scriptpath, *newvar; + const char *nextslash, *lastslash, *pathi, *ev, *ev2, *scriptdir, *av; const char **arguments; - size_t scriptdirlen, scriptpathlen, l, uservarnl; + size_t scriptdirlen, scriptpathlen, l; struct stat stab; int r, nargs; @@ -55,17 +60,7 @@ int main(int argc, const char **argv) { scriptdir= newvar; scriptdirlen= strlen(scriptdir); - uservarn= 0; - uservarnl= 0; - for (ep= envok; (en= *ep); ep++) { - l= strlen(en)+11; - if (uservarnl MAX_ENVVAR_VALUE) error("environment variable too long"); - if (setenv(en,ev,1)) syserror("setenv"); - unsetenv(uservarn); - } + filter_environment(0, "USERV_U_E_", envok, setenvar, 0); scriptpath= 0; pathi= getenv("PATH_INFO"); -- cgit v1.2.3 From a8e8db26410497a781ca36f59951201d55711f3a Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 30 Jan 2013 00:23:08 +0000 Subject: www-cgi/: Decentralize the whitelist of environment variables. There's no great need for `ucgi' to have a fierce whitelist of environment variables to be passed to the service. We'll assume that the webserver hasn't put any critical secrets in its environment with unfortunate names; and the service shouldn't put any trust in the caller's filtering anyway. If the webserver end takes a more relaxed approach, we can leave questions of policy regarding environment filtering largely up to the service -- which is the bit that users actually have some control over. To this end, therefore, move the main whitelist to `ucgitarget.c', and put small list, containing some wildcard patterns, in `ucgi.c'. --- www-cgi/ucgi.c | 19 +++++++++++++++++++ www-cgi/ucgi.h | 2 -- www-cgi/ucgicommon.c | 48 ------------------------------------------------ www-cgi/ucgitarget.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index c4c072d..006f8ae 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -30,6 +30,25 @@ #include "ucgi.h" +static const char *const envok[] = { + "AUTH_TYPE", + "CONTENT_TYPE", + "CONTENT_LENGTH", + "DOCUMENT_ROOT", + "GATEWAY_INTERFACE", + "HTTP_*", + "HTTPS", + "PATH_INFO", + "PATH_TRANSLATED", + "QUERY_STRING", + "REMOTE_*", + "REQUEST_METHOD", + "REQUEST_URI", + "SCRIPT_*", + "SERVER_*", + 0 +}; + struct buildargs { const char **v; int n, max; diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 1d00b1b..9d69ed9 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -49,8 +49,6 @@ void filter_environment(unsigned flags, const char *prefix_in, void *p); #define FILTF_WILDCARD 1u -extern const char *const envok[]; -extern const int nenvok; extern int debugmode; #endif diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 0facfbb..168641b 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -26,54 +26,6 @@ #include "ucgi.h" -const char *const 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", - "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", - 0 -}; -const int nenvok= sizeof(envok)/sizeof(envok[0]); - int debugmode= 0; static void outerror(void) { diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index a4b5690..9780e36 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -32,6 +32,53 @@ #include "ucgi.h" +static const char *const 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", + "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", + 0 +}; + static void setenvar(const char *fulln, const char *en, const char *ep, void *p) { xsetenv(en, ep, 1); -- cgit v1.2.3 From f7b4be5ac332970bb5937ae586cea6de64d14f66 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 30 Jan 2013 00:35:02 +0000 Subject: www-cgi/: Allow customization of the environment filters. Sites can now configure `ucgi's environment filters, and end users can configure `ucgitarget's filters. By default, `ucgi' will look in `/etc/userv/ucgi.env-filter', but if `UCGI_ENV_FILTER' is set in its environment, it will look there instead. The filter may contain wildcards and so on. By default, `ucgitarget' looks in `.userv/ucgitarget.env-filter', or `/etc/userv/ucgitarget.env-filter', if the former doesn't exist; but if passed a `-e FILTER' option on its command line, it will look in the file FILTER instead. This filter may /not/ contain wildcards. In both cases, if an explicitly named filter file can't be found then the program fails; if the default filter files can't be found then they fall back to built-in lists. The reason for the asymmetry in interfaces is: it's hard to pass command-line options to CGI scripts from webservers, but pretty easy to set environment variables; whereas it's hard to pass environment variables to a service program in a Userv configuration file, but easy to pass command-line arguments. --- www-cgi/ucgi.c | 13 +++++++++++-- www-cgi/ucgi.h | 4 ++++ www-cgi/ucgicommon.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ www-cgi/ucgitarget.c | 30 ++++++++++++++++++++++++---- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index 006f8ae..a2ebe64 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -30,7 +30,7 @@ #include "ucgi.h" -static const char *const envok[] = { +static const char *const default_envok[] = { "AUTH_TYPE", "CONTENT_TYPE", "CONTENT_LENGTH", @@ -73,7 +73,8 @@ static void add_userv_var(const char *fulln, int main(int argc, const char **argv) { char *username; - const char *slash2, *pathi, *av; + const char *slash2, *pathi, *ev, *av; + const char *const *envok; size_t usernamelen, l; struct buildargs args; pid_t child, rchild; @@ -91,6 +92,14 @@ int main(int argc, const char **argv) { if (argc > MAX_ARGS) error("too many arguments"); + ev= getenv("UCGI_ENV_FILTER"); + if (ev) + envok= load_filters(LOADF_MUST, ev, LF_END); + else { + envok= load_filters(0, "/etc/userv/ucgi.env-filter", LF_END); + if (!envok) envok= default_envok; + } + pathi= getenv("PATH_INFO"); if (!pathi) error("PATH_INFO not found"); D( if (debugmode) { diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 9d69ed9..765367a 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -42,6 +42,10 @@ void *xmalloc(size_t sz); void xsetenv(const char *en, const char *ev, int overwrite); void *xrealloc(void *ptr, size_t sz); +const char **load_filters(unsigned flags, const char *first, ...); +#define LOADF_MUST 1u +#define LF_END ((const char *)0) + void filter_environment(unsigned flags, const char *prefix_in, const char *const *patv, void (*foundone)(const char *fulln, const char *en, diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 168641b..db8c75d 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -18,6 +18,8 @@ * $Id$ */ +#include +#include #include #include #include @@ -69,6 +71,59 @@ void xsetenv(const char *en, const char *ev, int overwrite) { if (setenv(en,ev,overwrite)) syserror("setenv"); } +const char **load_filters(unsigned flags, const char *first, ...) +{ + va_list ap; + const char *name, *p, *q, **v; + char *pp; + size_t l, n, sz; + FILE *fp; + char buf[MAX_ENVVAR_NAME]; + + D( if (debugmode) printf(";; load_filters...\n"); ) + va_start(ap, first); + for (name= first; name; name= va_arg(ap, const char *)) { + fp= fopen(name, "r"); if (fp) goto opened; + D( if (debugmode) + printf(";; file `%s': %s\n", name, strerror(errno)); ) + if (errno != ENOENT) syserror("failed to open environment filters"); + } + va_end(ap); + if (flags & LOADF_MUST) syserror("failed to open environment filters"); + D( if (debugmode) printf(";; using default filters\n"); ) + return 0; + +opened: + va_end(ap); + D( if (debugmode) printf(";; file `%s': OK\n", name); ) + + n= 0; sz= 128; v= xmalloc(sz * sizeof(*v)); + for (;;) { + if (!fgets(buf, sizeof(buf), fp)) break; + l= strlen(buf); + if (buf[l - 1] == '\n') buf[--l]= 0; + if (l + 1 == sizeof(buf)) + error("line too long in environment filter file"); + p= buf; q= p + l; + while (isspace((unsigned char)*p)) p++; + while (q > p && isspace((unsigned char)q[-1])) q--; + if (*p == '#' || p == q) continue; + l= q - p; + pp= xmalloc(l + 1); + memcpy(pp, p, l); + pp[l]= 0; + v[n++]= pp; + D( if (debugmode) printf(";; filter: `%s'\n", pp); ) + if (n >= sz) { + sz *= 2; + v= xrealloc(v, sz * sizeof(*v)); + } + } + if (ferror(fp)) syserror("failed to read environment filters"); + fclose(fp); + return v; +} + void filter_environment(unsigned flags, const char *prefix_in, const char *const *patv, void (*foundone)(const char *fulln, diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 9780e36..6c579b6 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ #include "ucgi.h" -static const char *const envok[]= { +static const char *const default_envok[]= { "AUTH_TYPE", "CONTENT_LENGTH", "CONTENT_TYPE", @@ -85,13 +86,15 @@ static void setenvar(const char *fulln, unsetenv(fulln); } -int main(int argc, const char **argv) { +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 r, nargs; + int i, r, nargs; + const char *filters= 0; ev= getenv("USERV_U_DEBUG"); if (ev && *ev) debugmode= 1; @@ -99,7 +102,16 @@ int main(int argc, const char **argv) { D( if (debugmode) printf(";;; UCGITARGET\n"); ) if (argc > MAX_ARGS) error("too many arguments"); - if (!*++argv) error("no script directory argument"); + for (;;) { + i= getopt(argc, argv, "+e:"); if (i < 0) break; + switch (i) { + case 'e': filters= optarg; break; + default: error("bad command line"); break; + } + } + argc -= optind; argv += optind; + + if (!*argv) error("no script directory argument"); ev= getenv("HOME"); if (!ev) error("no HOME env. var"); l= strlen(*argv)+strlen(ev); newvar= xmalloc(l+2); @@ -107,6 +119,16 @@ int main(int argc, const char **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); + if (!envok) envok= default_envok; + } + filter_environment(0, "USERV_U_E_", envok, setenvar, 0); scriptpath= 0; -- cgit v1.2.3 From 564fbf9bcb4ff56ff2fcbc018fd5a85cdfeb4ea2 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 30 Jan 2013 01:14:36 +0000 Subject: www-cgi/: Yet more environment variables to be passed about. The `SSL_*' variables are used to pass information about SSL or TLS, including the cipher suite in use, and the status of client authentication. The `REDIRECT_*' variables are used to pass information about a failed request to a CGI script run as an Apache `ErrorDocument' or similar. --- www-cgi/ucgi.c | 2 ++ www-cgi/ucgitarget.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index a2ebe64..5e6a1b8 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -41,11 +41,13 @@ static const char *const default_envok[] = { "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", + "REDIRECT_*", "REMOTE_*", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_*", "SERVER_*", + "SSL_*", 0 }; diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 6c579b6..030f068 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -60,6 +60,11 @@ static const char *const default_envok[]= { "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", + "REDIRECT_HANDLER", + "REDIRECT_SCRIPT_URI", + "REDIRECT_SCRIPT_URL", + "REDIRECT_STATUS", + "REDIRECT_URL", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_USER", @@ -77,6 +82,10 @@ static const char *const default_envok[]= { "SERVER_PROTOCOL", "SERVER_SIGNATURE", "SERVER_SOFTWARE", + "SSL_CIPHER", + "SSL_CLIENT_S_DN", + "SSL_CLIENT_VERIFY", + "SSL_PROTOCOL", 0 }; -- cgit v1.2.3 From 92fc834ef77fe48782f2fb1b0f5c52a22127374c Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 19 Feb 2013 20:17:08 +0000 Subject: www-cgi/ucgicommon.c: Split matching out from `filter_environment'. Makes the code a bit easier to follow, and prepares the ground for the next change. --- www-cgi/ucgicommon.c | 112 +++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index db8c75d..4bba186 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -124,6 +124,54 @@ opened: return v; } +static int envvar_match(unsigned flags, const char *en, + const char *const *patv, + const char **ev) +{ + const char *const *patp; + const char *q, *pat; + int acceptp; + + for (patp= patv; (pat= *patp); patp++) { + q= en; + acceptp= 1; + if (*pat == '!' && (flags & FILTF_WILDCARD)) { acceptp= 0; pat++; } + + for (;;) { + if (!*pat) { + if (*q != '=') { + D( if (debugmode) + printf(";; mismatch `%s' (prefix)\n", *patp); ) + break; + } + D( if (debugmode) printf(";; matched pattern `%s'\n", *patp); ) + goto match; + } else if (*pat == '*' && (flags & FILTF_WILDCARD)) { + q = strchr(q, '='); + if (!q) { + D( if (debugmode) + printf(";; mismatch `%s' (discard: no `=')\n", *patp); ) + return -1; + } + D( if (debugmode) + printf(";; wildcard match for `%s'\n", *patp); ) + goto match; + } else { + if (*pat++ != *q++) { + D( if (debugmode) printf(";; mismatch `%s'\n", *patp); ) + break; + } + } + } + } + return 0; + +match: + if (!acceptp) return -1; + *ev= q + 1; + return +1; +} + void filter_environment(unsigned flags, const char *prefix_in, const char *const *patv, void (*foundone)(const char *fulln, @@ -132,67 +180,27 @@ void filter_environment(unsigned flags, const char *prefix_in, void *p) { char *const *ep; - const char *const *patp; - const char *en, *ev, *pat, *q; + const char *en, *ev; char enbuf[MAX_ENVVAR_NAME]; size_t n, pn = strlen(prefix_in); - int acceptp; D( if (debugmode) printf(";; filter_environment...\n"); ) for (ep= environ; (en= *ep); ep++) { D( if (debugmode) printf(";; consider env-var `%s'\n", en); ) if (strncmp(en, prefix_in, pn) != 0 || !en[pn]) { D( if (debugmode) printf(";; doesn't match prefix\n"); ) - goto next_ev; + continue; } - for (patp= patv; (pat= *patp); patp++) { - q= en + pn; - acceptp= 1; - if (*pat == '!' && (flags & FILTF_WILDCARD)) { - acceptp= 0; pat++; - } - for (;;) { - if (!*pat) { - if (*q != '=') { - D( if (debugmode) - printf(";; mismatch `%s' (prefix)\n", *patp); ) - goto next_pat; - } - D( if (debugmode) printf(";; matched `%s'\n", *patp); ) - ev = q + 1; - break; - } else if (*pat == '*' && (flags & FILTF_WILDCARD)) { - q = strchr(q, '='); - if (!q) { - D( if (debugmode) - printf(";; mismatch `%s' (discard: no `=')\n", *patp); ) - goto next_ev; - } - D( if (debugmode) - printf(";; wildcard match for `%s'\n", *patp); ) - ev = q + 1; - break; - } else - if (*pat++ != *q++) { - D( if (debugmode) printf(";; mismatch `%s'\n", *patp); ) - goto next_pat; - } - } - if (acceptp) { - n= q - en; - if (n >= sizeof(enbuf)) - error("environment variable name too long"); - memcpy(enbuf, en, n); - enbuf[n]= 0; - D( if (debugmode) - printf(";; full = `%s'; tail = `%s'; value = `%s'\n", - enbuf, enbuf + pn, ev); ) - foundone(enbuf, enbuf + pn, ev, p); - } D( else if (debugmode) - printf(";; matched negated pattern\n"); ) - goto next_ev; - next_pat:; + if (envvar_match(flags, en + pn, patv, &ev) > 0) { + n= strcspn(en, "="); + if (n >= sizeof(enbuf)) + error("environment variable name too long"); + memcpy(enbuf, en, n); + enbuf[n]= 0; + D( if (debugmode) + printf(";; full = `%s'; tail = `%s'; value = `%s'\n", + enbuf, enbuf + pn, ev); ) + foundone(enbuf, enbuf + pn, ev, p); } - next_ev:; } } -- cgit v1.2.3 From aa0bce91c04ffd1923a623819e5bbe1ccf609a81 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 19 Feb 2013 20:22:14 +0000 Subject: www-cgi: Introduce `?DEFAULTS' pattern to match the default set. --- www-cgi/ucgi.c | 9 ++++----- www-cgi/ucgi.h | 1 + www-cgi/ucgicommon.c | 16 +++++++++++++++- www-cgi/ucgitarget.c | 3 +-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index 5e6a1b8..980bec5 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -76,7 +76,7 @@ static void add_userv_var(const char *fulln, int main(int argc, const char **argv) { char *username; const char *slash2, *pathi, *ev, *av; - const char *const *envok; + const char *const *envok = 0; size_t usernamelen, l; struct buildargs args; pid_t child, rchild; @@ -97,10 +97,8 @@ int main(int argc, const char **argv) { ev= getenv("UCGI_ENV_FILTER"); if (ev) envok= load_filters(LOADF_MUST, ev, LF_END); - else { + else envok= load_filters(0, "/etc/userv/ucgi.env-filter", LF_END); - if (!envok) envok= default_envok; - } pathi= getenv("PATH_INFO"); if (!pathi) error("PATH_INFO not found"); @@ -126,7 +124,8 @@ int main(int argc, const char **argv) { addarg(&args, "userv"); if (debugmode) addarg(&args, "-DDEBUG=1"); - filter_environment(FILTF_WILDCARD, "", envok, add_userv_var, &args); + filter_environment(FILTF_WILDCARD, "", envok, default_envok, + add_userv_var, &args); addarg(&args, username); addarg(&args, "www-cgi"); diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 765367a..9fbf70c 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -48,6 +48,7 @@ const char **load_filters(unsigned flags, const char *first, ...); void filter_environment(unsigned flags, const char *prefix_in, const char *const *patv, + const char *const *defaults, void (*foundone)(const char *fulln, const char *en, const char *ev, void *p), void *p); diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 4bba186..73565b5 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -18,6 +18,7 @@ * $Id$ */ +#include #include #include #include @@ -126,16 +127,28 @@ opened: static int envvar_match(unsigned flags, const char *en, const char *const *patv, + const char *const *defaults, const char **ev) { const char *const *patp; const char *q, *pat; int acceptp; + int rc; + if (!patv) { patv= defaults; defaults= 0; } for (patp= patv; (pat= *patp); patp++) { q= en; acceptp= 1; if (*pat == '!' && (flags & FILTF_WILDCARD)) { acceptp= 0; pat++; } + else if (*pat == '?') { + if (strcmp(pat + 1, "DEFAULTS") == 0) { + assert(defaults); + rc= envvar_match(flags, en, defaults, 0, ev); + if (rc) return rc; + } else + error("unknown pattern directive"); + continue; + } for (;;) { if (!*pat) { @@ -174,6 +187,7 @@ match: void filter_environment(unsigned flags, const char *prefix_in, const char *const *patv, + const char *const *defaults, void (*foundone)(const char *fulln, const char *en, const char *ev, void *p), @@ -191,7 +205,7 @@ void filter_environment(unsigned flags, const char *prefix_in, D( if (debugmode) printf(";; doesn't match prefix\n"); ) continue; } - if (envvar_match(flags, en + pn, patv, &ev) > 0) { + if (envvar_match(flags, en + pn, patv, defaults, &ev) > 0) { n= strcspn(en, "="); if (n >= sizeof(enbuf)) error("environment variable name too long"); diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 030f068..2b6a222 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -135,10 +135,9 @@ int main(int argc, char **argv) { ".userv/ucgitarget.env-filter", "/etc/userv/ucgitarget.env-filter", LF_END); - if (!envok) envok= default_envok; } - filter_environment(0, "USERV_U_E_", envok, setenvar, 0); + filter_environment(0, "USERV_U_E_", envok, default_envok, setenvar, 0); scriptpath= 0; pathi= getenv("PATH_INFO"); -- cgit v1.2.3 From 0cd9d59dfadf1a9abbef4428ff4f90bf32c01bd4 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 28 Mar 2016 23:27:56 +0100 Subject: ucgi/: Return useful status codes when things go wrong. It's simply wrong for the ucgi machinery to report status 200 OK when it encounters trouble (e.g., the CGI program doesn't actually exist). So report a useful status code as part of `error' or `syserror'. We assume that the latter is always a server-side error; the former might be either, so we must annotate each call as appropriate. --- www-cgi/ucgi.c | 20 ++++++++++++-------- www-cgi/ucgi.h | 2 +- www-cgi/ucgicommon.c | 16 +++++++++------- www-cgi/ucgitarget.c | 28 +++++++++++++++------------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/www-cgi/ucgi.c b/www-cgi/ucgi.c index 980bec5..845eb44 100644 --- a/www-cgi/ucgi.c +++ b/www-cgi/ucgi.c @@ -57,7 +57,7 @@ struct buildargs { }; static void addarg(struct buildargs *args, const char *a) { - if (args->n > args->max) error("too many arguments"); + if (args->n > args->max) error("too many arguments", 500); args->v[args->n++]= a; } @@ -67,7 +67,8 @@ static void add_userv_var(const char *fulln, size_t l; char *a; - l= strlen(ev); if (l > MAX_ENVVAR_VALUE) error("environment variable too long"); + l= strlen(ev); + if (l > MAX_ENVVAR_VALUE) error("environment variable too long", 500); a= xmalloc(strlen(en)+l+6); sprintf(a,"-DE_%s=%s",en,ev); addarg(args, a); @@ -92,7 +93,7 @@ int main(int argc, const char **argv) { D( printf(";;; UCGI\n"); ) } - if (argc > MAX_ARGS) error("too many arguments"); + if (argc > MAX_ARGS) error("too many arguments", 500); ev= getenv("UCGI_ENV_FILTER"); if (ev) @@ -101,21 +102,24 @@ int main(int argc, const char **argv) { envok= load_filters(0, "/etc/userv/ucgi.env-filter", LF_END); pathi= getenv("PATH_INFO"); - if (!pathi) error("PATH_INFO not found"); + if (!pathi) error("PATH_INFO not found", 500); D( if (debugmode) { printf(";; find user name...\n" ";; initial PATH_INFO = `%s'\n", pathi); } ) - if (pathi[0] != '/' || pathi[1] != '~') error("PATH_INFO must start with /~"); - slash2= strchr(pathi+2,'/'); if (!slash2) error("PATH_INFO must have more than one /"); + if (pathi[0] != '/' || pathi[1] != '~') + error("PATH_INFO must start with /~", 400); + slash2= strchr(pathi+2,'/'); + if (!slash2) error("PATH_INFO must have more than one /", 400); usernamelen= slash2-(pathi+2); - if (usernamelen > MAX_USERNAME_LEN) error("PATH_INFO username too long"); + if (usernamelen > MAX_USERNAME_LEN) error("PATH_INFO username too long", 400); username= xmalloc(usernamelen+1); memcpy(username,pathi+2,usernamelen); username[usernamelen]= 0; D( if (debugmode) printf(";; user = `%s'; tail = `%s'\n", username, slash2); ) - if (!isalpha(username[0])) error("username 1st character is not alphabetic"); + if (!isalpha(username[0])) + error("username 1st character is not alphabetic", 400); xsetenv("PATH_INFO",slash2,1); args.n= 0; args.max= argc + MAX_ENVVARS + 10; diff --git a/www-cgi/ucgi.h b/www-cgi/ucgi.h index 9fbf70c..d4c84bf 100644 --- a/www-cgi/ucgi.h +++ b/www-cgi/ucgi.h @@ -37,7 +37,7 @@ #define MAX_ENVVARS 256 void syserror(const char *m); -void error(const char *m); +void error(const char *m, int st); void *xmalloc(size_t sz); void xsetenv(const char *en, const char *ev, int overwrite); void *xrealloc(void *ptr, size_t sz); diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 73565b5..2fa6260 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -37,18 +37,20 @@ static void outerror(void) { } void syserror(const char *m) { - if (printf("Content-Type: text/plain\n\n" + if (printf("Content-Type: text/plain\n" + "Status: 500\n\n" "ucgi: system call error:\n" "%s: %s\n", m,strerror(errno))==EOF || fflush(stdout)) outerror(); exit(0); } -void error(const char *m) { - if (printf("Content-Type: text/plain\n\n" +void error(const char *m, int st) { + if (printf("Content-Type: text/plain\n" + "Status: %d\n\n" "ucgi: error:\n" "%s\n", - m)==EOF || fflush(stdout)) outerror(); + st, m)==EOF || fflush(stdout)) outerror(); exit(0); } @@ -104,7 +106,7 @@ opened: l= strlen(buf); if (buf[l - 1] == '\n') buf[--l]= 0; if (l + 1 == sizeof(buf)) - error("line too long in environment filter file"); + error("line too long in environment filter file", 500); p= buf; q= p + l; while (isspace((unsigned char)*p)) p++; while (q > p && isspace((unsigned char)q[-1])) q--; @@ -146,7 +148,7 @@ static int envvar_match(unsigned flags, const char *en, rc= envvar_match(flags, en, defaults, 0, ev); if (rc) return rc; } else - error("unknown pattern directive"); + error("unknown pattern directive", 500); continue; } @@ -208,7 +210,7 @@ void filter_environment(unsigned flags, const char *prefix_in, if (envvar_match(flags, en + pn, patv, defaults, &ev) > 0) { n= strcspn(en, "="); if (n >= sizeof(enbuf)) - error("environment variable name too long"); + error("environment variable name too long", 500); memcpy(enbuf, en, n); enbuf[n]= 0; D( if (debugmode) diff --git a/www-cgi/ucgitarget.c b/www-cgi/ucgitarget.c index 2b6a222..866a2d5 100644 --- a/www-cgi/ucgitarget.c +++ b/www-cgi/ucgitarget.c @@ -109,19 +109,19 @@ int main(int argc, char **argv) { if (ev && *ev) debugmode= 1; D( if (debugmode) printf(";;; UCGITARGET\n"); ) - if (argc > MAX_ARGS) error("too many arguments"); + 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"); break; + default: error("bad command line", 500); break; } } argc -= optind; argv += optind; - if (!*argv) error("no script directory argument"); - ev= getenv("HOME"); if (!ev) error("no HOME env. var"); + 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); @@ -141,7 +141,7 @@ int main(int argc, char **argv) { scriptpath= 0; pathi= getenv("PATH_INFO"); - if (!pathi) error("PATH_INFO not found"); + if (!pathi) error("PATH_INFO not found", 500); lastslash= pathi; D( if (debugmode) { printf(";; find script name...\n" @@ -149,23 +149,25 @@ int main(int argc, char **argv) { pathi); } ) for (;;) { - if (*lastslash != '/') error("PATH_INFO expected slash not found"); - if (lastslash[1]=='.' || lastslash[1]=='#' || !lastslash[1]) error("bad char begin"); + 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"); - if (nextslash==lastslash+1) error("empty component in PATH_INFO"); - if (nextslash-pathi > MAX_SCRIPTPATH_LEN) error("PATH_INFO script path too long"); + 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"); + 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"); + 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); ) @@ -180,7 +182,7 @@ int main(int argc, char **argv) { ev= getenv("SCRIPT_NAME"); if (ev) { - ev2= getenv("USER"); if (!ev2) error("no USER variable"); + 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); -- cgit v1.2.3 From 540ab05fdd2b15a603e714bbff93eeb137cef009 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 17 May 2016 16:17:56 +0100 Subject: www-cgi/: Coding style fixes Some of the merged branch introduced new functions with the { on the next line. Fix them. Signed-off-by: Ian Jackson --- www-cgi/ucgicommon.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/www-cgi/ucgicommon.c b/www-cgi/ucgicommon.c index 2fa6260..6ae266d 100644 --- a/www-cgi/ucgicommon.c +++ b/www-cgi/ucgicommon.c @@ -74,8 +74,7 @@ void xsetenv(const char *en, const char *ev, int overwrite) { if (setenv(en,ev,overwrite)) syserror("setenv"); } -const char **load_filters(unsigned flags, const char *first, ...) -{ +const char **load_filters(unsigned flags, const char *first, ...) { va_list ap; const char *name, *p, *q, **v; char *pp; @@ -130,8 +129,7 @@ opened: static int envvar_match(unsigned flags, const char *en, const char *const *patv, const char *const *defaults, - const char **ev) -{ + const char **ev) { const char *const *patp; const char *q, *pat; int acceptp; @@ -193,8 +191,7 @@ void filter_environment(unsigned flags, const char *prefix_in, void (*foundone)(const char *fulln, const char *en, const char *ev, void *p), - void *p) -{ + void *p) { char *const *ep; const char *en, *ev; char enbuf[MAX_ENVVAR_NAME]; -- cgit v1.2.3 From ee8748e6588b1e10831a0bd55c92125b319ec6d8 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 17 May 2016 16:19:26 +0100 Subject: www-cgi/: Document customisable environment filters This feature was documented in the commit message, but not in the tree. Introduce a straight copy of the commit message from f7b4be5a "www-cgi/: Allow customization of the environment filters." Signed-off-by: Ian Jackson --- www-cgi/README.custom-env-filter | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 www-cgi/README.custom-env-filter diff --git a/www-cgi/README.custom-env-filter b/www-cgi/README.custom-env-filter new file mode 100644 index 0000000..245d071 --- /dev/null +++ b/www-cgi/README.custom-env-filter @@ -0,0 +1,23 @@ +www-cgi/: Allow customization of the environment filters. + +Sites can now configure `ucgi's environment filters, and end users can +configure `ucgitarget's filters. + +By default, `ucgi' will look in `/etc/userv/ucgi.env-filter', but if +`UCGI_ENV_FILTER' is set in its environment, it will look there +instead. The filter may contain wildcards and so on. + +By default, `ucgitarget' looks in `.userv/ucgitarget.env-filter', or +`/etc/userv/ucgitarget.env-filter', if the former doesn't exist; but if +passed a `-e FILTER' option on its command line, it will look in the +file FILTER instead. This filter may /not/ contain wildcards. + +In both cases, if an explicitly named filter file can't be found then +the program fails; if the default filter files can't be found then they +fall back to built-in lists. + +The reason for the asymmetry in interfaces is: it's hard to pass +command-line options to CGI scripts from webservers, but pretty easy to +set environment variables; whereas it's hard to pass environment +variables to a service program in a Userv configuration file, but easy +to pass command-line arguments. -- cgit v1.2.3 From 2c62631d2ccb51dacc14c75553f134410c0b2b5f Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 17 May 2016 16:21:38 +0100 Subject: www-cgi/: Document ?DEFAULTS environment filters This feature was documented in the commit message, but not in the tree. Introduce a straight copy of the commit message from aa0bce91 "www-cgi: Introduce `?DEFAULTS' pattern to match the default set."" Signed-off-by: Ian Jackson --- www-cgi/README.custom-env-filter | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www-cgi/README.custom-env-filter b/www-cgi/README.custom-env-filter index 245d071..5ebc1db 100644 --- a/www-cgi/README.custom-env-filter +++ b/www-cgi/README.custom-env-filter @@ -21,3 +21,6 @@ command-line options to CGI scripts from webservers, but pretty easy to set environment variables; whereas it's hard to pass environment variables to a service program in a Userv configuration file, but easy to pass command-line arguments. + + +www-cgi: Introduce `?DEFAULTS' pattern to match the default set. -- cgit v1.2.3 From d083b489fb37f322b4896a8f7c449ac4a1ea1f77 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 17 May 2016 16:25:17 +0100 Subject: www-cgi/: Clean up environment filtering doc Reword this a bit so it makes sense as a text doc, and make some minor formatting changes. Signed-off-by: Ian Jackson --- www-cgi/README.custom-env-filter | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/www-cgi/README.custom-env-filter b/www-cgi/README.custom-env-filter index 5ebc1db..58ef39b 100644 --- a/www-cgi/README.custom-env-filter +++ b/www-cgi/README.custom-env-filter @@ -1,14 +1,14 @@ -www-cgi/: Allow customization of the environment filters. +Allow customization of the environment filters. -Sites can now configure `ucgi's environment filters, and end users can -configure `ucgitarget's filters. +Sites can configure ucgi's environment filters, and end users can +configure ucgitarget's filters. -By default, `ucgi' will look in `/etc/userv/ucgi.env-filter', but if -`UCGI_ENV_FILTER' is set in its environment, it will look there +By default, ucgi will look in /etc/userv/ucgi.env-filter, but if +UCGI_ENV_FILTER is set in its environment, it will look there instead. The filter may contain wildcards and so on. -By default, `ucgitarget' looks in `.userv/ucgitarget.env-filter', or -`/etc/userv/ucgitarget.env-filter', if the former doesn't exist; but if +By default, ucgitarget looks in .userv/ucgitarget.env-filter, or +/etc/userv/ucgitarget.env-filter, if the former doesn't exist; but if passed a `-e FILTER' option on its command line, it will look in the file FILTER instead. This filter may /not/ contain wildcards. @@ -23,4 +23,5 @@ variables to a service program in a Userv configuration file, but easy to pass command-line arguments. -www-cgi: Introduce `?DEFAULTS' pattern to match the default set. +The `?DEFAULTS' pattern can be specified to match the default set +(which is different in `ucgi' and `ucgitarget'). -- cgit v1.2.3