summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wooding <mdw@distorted.org.uk>2013-01-30 00:09:47 +0000
committerMark Wooding <mdw@distorted.org.uk>2013-02-02 13:31:03 +0000
commitf601a2c663d9135dec19172a593502864d10393e (patch)
tree31c4ffd0a297740bb447da51919a44c47160866b
parent77a36cae0e59a1e17c12c71fd7ff49be0958b11d (diff)
downloaduserv-utils-f601a2c663d9135dec19172a593502864d10393e.tar.gz
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.
-rw-r--r--www-cgi/ucgi.c15
-rw-r--r--www-cgi/ucgi.h9
-rw-r--r--www-cgi/ucgicommon.c75
-rw-r--r--www-cgi/ucgitarget.c25
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 <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:;
+ }
+}
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<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");