chiark / gitweb /
www-cgi/: Centralize environment variable filtering.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 30 Jan 2013 00:09:47 +0000 (00:09 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 2 Feb 2013 13:31:03 +0000 (13:31 +0000)
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
www-cgi/ucgi.h
www-cgi/ucgicommon.c
www-cgi/ucgitarget.c

index 431d280..c4c072d 100644 (file)
@@ -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");
index 0ab5a49..1d00b1b 100644 (file)
@@ -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;
index 43b7d94..0facfbb 100644 (file)
@@ -22,6 +22,8 @@
 #include <string.h>
 #include <errno.h>
 
+#include <unistd.h>
+
 #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:;
+  }
+}
index 9637235..a4b5690 100644 (file)
 
 #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<l) { uservarn= xrealloc(uservarn,l); uservarnl= l; }
-    sprintf(uservarn,"USERV_U_E_%s",en);
-    ev= getenv(uservarn); if (!ev) continue;
-    if (strlen(ev) > 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");