From f601a2c663d9135dec19172a593502864d10393e Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Wed, 30 Jan 2013 00:09:47 +0000 Subject: [PATCH] www-cgi/: Centralize environment variable filtering. Organization: Straylight/Edgeware From: Mark Wooding 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"); -- [mdw]