You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1998/01/06 23:39:08 UTC

[PATCH] 1.3: security updates for mod_imap and mod_include

Here are the 1.3 versions of the mod_imap and mod_include patches to fix
the security bugs that are now fixed in 1.2.5. 

For mod_imap we have:

This is a bit large, but that's deliberate because I took the opportunity
to do the crap that we've been wanting done to mod_imap.

- liberal use of const to help find stack assignments

- remove all constant sized char arrays except input[]; replaced by pool
    string functions or by pointers into tokens inside the input[]
    array

- in particular, the use of read_quoted() had a stack overrun potential.
    Eliminated.

- These changes can chew memory when generating a menu.  I don't care,
    I'd rather have them do that than have them overrun the stack.  It
    shouldn't chew more than approx the size of the map file though.

- better error handling

For mod_include we have:

- There were a few strncpy()s that didn't terminate the string... add
safe_copy() which does strncpy the way it should be.

- switch many MAX_STRING_LENs with sizeof(foo) for the right foo, just in
case

- add const liberally to assist diagnosis

- fix two off-by-1 errors in get_tag() (it could be convinced to hammer
one byte past end of buffer)

- fix buffer overrun in get_directive()

- fix PR#1203 in a way that's fine for 1.2.x, but needs WIN32 support in
1.3

- test a few more error conditions and report them rather than doing
something lame

- buffer overrun and infinite loop in parse_string() eliminated

- removed unneeded test of palloc() and make_sub_pool() results against
NULL

- fix use of strncat which didn't \0 terminate the destination

- handle_else/handle_endif/handle_set/handle_printenv error messages
didn't include the filename

Dean

? src/modules/standard/mod_cgi.c.non_block
? src/modules/standard/out
Index: src/modules/standard/mod_imap.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_imap.c,v
retrieving revision 1.35
diff -u -r1.35 mod_imap.c
--- mod_imap.c	1997/10/26 20:20:04	1.35
+++ mod_imap.c	1998/01/06 22:22:15
@@ -97,8 +97,6 @@
 #include "util_script.h"
 
 #define IMAP_MAGIC_TYPE "application/x-httpd-imap"
-#define LARGEBUF 500
-#define SMALLBUF 256
 #define MAXVERTS 100
 #define X 0
 #define Y 1
@@ -159,7 +157,7 @@
     {NULL}
 };
 
-static int pointinrect(double point[2], double coords[MAXVERTS][2])
+static int pointinrect(const double point[2], const double coords[MAXVERTS][2])
 {
     double max[2], min[2];
     if (coords[0][X] > coords[1][X]) {
@@ -184,7 +182,7 @@
             (point[Y] >= min[1] && point[Y] <= max[1]));
 }
 
-static int pointincircle(double point[2], double coords[MAXVERTS][2])
+static int pointincircle(const double point[2], const double coords[MAXVERTS][2])
 {
     double radius1, radius2;
 
@@ -197,11 +195,12 @@
     return (radius2 <= radius1);
 }
 
-static int pointinpoly(double point[2], double pgon[MAXVERTS][2])
+static int pointinpoly(const double point[2], const double pgon[MAXVERTS][2])
 {
     int i, numverts, inside_flag, xflag0;
     int crossings;
-    double *p, *stop;
+    double *p;
+    const double *stop;
     double tx, ty, y;
 
     for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++);
@@ -271,7 +270,7 @@
 }
 
 
-static int is_closer(double point[2], double coords[MAXVERTS][2], double *closest)
+static int is_closer(const double point[2], const double coords[MAXVERTS][2], double *closest)
 {
     double dist_squared = ((point[X] - coords[0][X]) * (point[X] - coords[0][X]))
     + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
@@ -289,7 +288,7 @@
 
 }
 
-static double get_x_coord(char *args)
+static double get_x_coord(const char *args)
 {
     char *endptr;               /* we want it non-null */
     double x_coord = -1;        /* -1 is returned if no coordinate is given */
@@ -308,7 +307,7 @@
     return (-1);                /* else if no conversion was made, or if no args was given */
 }
 
-static double get_y_coord(char *args)
+static double get_y_coord(const char *args)
 {
     char *endptr;               /* we want it non-null */
     char *start_of_y = NULL;
@@ -336,107 +335,98 @@
 }
 
 
-static int read_quoted(char *string, char *quoted_part)
+/* See if string has a "quoted part", and if so set *quoted_part to
+ * the first character of the quoted part, then hammer a \0 onto the
+ * trailing quote, and set *string to point at the first character
+ * past the second quote.
+ *
+ * Otherwise set *quoted_part to NULL, and leave *string alone.
+ */
+static void read_quoted(char **string, char **quoted_part)
 {
-    char *starting_pos = string;
+    char *strp = *string;
 
-    while (isspace(*string))
-        string++;               /* go along string until non-whitespace */
+    /* assume there's no quoted part */
+    *quoted_part = NULL;
 
-    if (*string == '"') {       /* if that character is a double quote */
+    while (isspace(*strp))
+        strp++;               	/* go along string until non-whitespace */
 
-        string++;               /* step over it */
+    if (*strp == '"') {       	/* if that character is a double quote */
+        strp++;               	/* step over it */
+	*quoted_part = strp;  	/* note where the quoted part begins */
 
-        while (*string && *string != '"') {
-            *quoted_part++ = *string++;         /* copy the quoted portion */
+        while (*strp && *strp != '"') {
+	    ++strp;		/* skip the quoted portion */
         }
 
-        *quoted_part = '\0';    /* end the string with a SNUL */
+        *strp = '\0';    	/* end the string with a NUL */
 
-        string++;               /* step over the last double quote */
+        strp++;               	/* step over the last double quote */
+	*string = strp;
     }
-
-    return (string - starting_pos);     /* return the total characters read */
 }
 
 /*
- * url needs to point to a string with at least SMALLBUF memory allocated
+ * returns the mapped URL or NULL.
  */
-static void imap_url(request_rec *r, char *base, char *value, char *url)
+static char *imap_url(request_rec *r, const char *base, const char *value)
 {
 /* translates a value into a URL. */
     int slen, clen;
     char *string_pos = NULL;
+    const char *string_pos_const = NULL;
     char *directory = NULL;
     char *referer = NULL;
-    char my_base[SMALLBUF] =
-    {'\0'};
+    char *my_base;
 
     if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
-        if (r->server->port == DEFAULT_PORT) {
-            ap_snprintf(url, SMALLBUF,
-                        "http://%s%s", r->server->server_hostname, r->uri);
-        }
-        else {
-            ap_snprintf(url, SMALLBUF, "http://%s:%d%s", r->server->server_hostname,
-                        r->server->port, r->uri);
-        }
-        return;
+	return construct_url(r->pool, r->uri, r->server);
     }
 
     if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
-        strncpy(url, value, SMALLBUF - 1);
-        url[SMALLBUF - 1] = '\0';
-        return;                 /* these are handled elsewhere, so just copy them */
+        return pstrdup(r->pool, value);                 /* these are handled elsewhere, so just copy them */
     }
 
     if (!strcasecmp(value, "referer")) {
         referer = table_get(r->headers_in, "Referer");
         if (referer && *referer) {
-            strncpy(url, referer, SMALLBUF - 1);
-            url[SMALLBUF - 1] = '\0';
-            return;
+	    return pstrdup(r->pool, referer);
         }
         else {
-            *value = '\0';      /* if 'referer' but no referring page, null the value */
+	    /* XXX:  This used to do *value = '\0'; ... which is totally bogus
+	     * because it hammers the passed in value, which can be a string constant,
+	     * or part of a config, or whatever.  Total garbage.  This works around
+	     * that without changing the rest of this code much
+	     */
+            value = "";      /* if 'referer' but no referring page, null the value */
         }
     }
 
-    string_pos = value;
-    while (isalpha(*string_pos))
-        string_pos++;           /* go along the URL from the map until a non-letter */
-    if (*string_pos == ':') {
-        strncpy(url, value, SMALLBUF - 1);      /* if letters and then a colon (like http:) */
-        url[SMALLBUF - 1] = '\0';
-        return;                 /* it's an absolute URL, so use it! */
+    string_pos_const = value;
+    while (isalpha(*string_pos_const))
+	string_pos_const++;           /* go along the URL from the map until a non-letter */
+    if (*string_pos_const == ':') {
+	/* if letters and then a colon (like http:) */
+	/* it's an absolute URL, so use it! */
+	return pstrdup(r->pool, value);
     }
 
     if (!base || !*base) {
         if (value && *value) {
-            strncpy(url, value, SMALLBUF - 1);  /* no base: use what is given */
-            url[SMALLBUF - 1] = '\0';
+	    return pstrdup(r->pool, value); /* no base: use what is given */
         }
-        else {
-            if (r->server->port == DEFAULT_PORT) {
-                ap_snprintf(url, SMALLBUF, "http://%s/", r->server->server_hostname);
-            }
-            if (r->server->port != DEFAULT_PORT) {
-                ap_snprintf(url, SMALLBUF, "http://%s:%d/",
-                            r->server->server_hostname, r->server->port);
-            }                   /* no base, no value: pick a simple default */
-        }
-        return;
+	/* no base, no value: pick a simple default */
+	return construct_url(r->pool, "/", r->server);
     }
 
     /* must be a relative URL to be combined with base */
-    strncpy(my_base, base, sizeof(my_base) - 1);
-    my_base[sizeof(my_base) - 1] = '\0';
-    if (strchr(my_base, '/') == NULL && (!strncmp(value, "../", 3) || !strcmp(value, ".."))) {
-        url[0] = '\0';
+    if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) || !strcmp(value, ".."))) {
         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                     "invalid base directive in map file: %s", r->uri);
-        return;
+        return NULL;
     }
+    my_base = pstrdup(r->pool, base);
     string_pos = my_base;
     while (*string_pos) {
         if (*string_pos == '/' && *(string_pos + 1) == '/') {
@@ -486,10 +476,9 @@
             value += 2;         /* jump over the '..' that we found in the value */
         }
         else if (directory) {
-            url[0] = '\0';
             aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                         "invalid directory name in map file: %s", r->uri);
-            return;
+            return NULL;
         }
 
         if (!strncmp(value, "/../", 4) || !strcmp(value, "/.."))
@@ -500,12 +489,9 @@
     }                           /* by this point, value does not start with '..' */
 
     if (value && *value) {
-        ap_snprintf(url, SMALLBUF, "%s%s", my_base, value);
-    }
-    else {
-        ap_snprintf(url, SMALLBUF, "%s", my_base);
+	return pstrcat(r->pool, my_base, value, NULL);
     }
-    return;
+    return my_base;
 }
 
 static int imap_reply(request_rec *r, char *redirect)
@@ -613,46 +599,29 @@
 
 static int imap_handler(request_rec *r)
 {
-    char input[LARGEBUF] =
-    {'\0'};
-    /* size of input can not be lowered without changing hard-coded
-     * checks
-     */
-    char href_text[SMALLBUF] =
-    {'\0'};
-    char base[SMALLBUF] =
-    {'\0'};
-    char redirect[SMALLBUF] =
-    {'\0'};
-    char directive[SMALLBUF] =
-    {'\0'};
-    char value[SMALLBUF] =
-    {'\0'};
-    char mapdflt[SMALLBUF] =
-    {'\0'};
-    char closest[SMALLBUF] =
-    {'\0'};
+    char input[MAX_STRING_LEN];
+    char *directive;
+    char *value;
+    char *href_text;
+    char *base;
+    char *redirect;
+    char *mapdflt;
+    char *closest = NULL;
     double closest_yet = -1;
 
-    double testpoint[2] =
-    {-1, -1};
-    double pointarray[MAXVERTS + 1][2] =
-    {
-        {-1, -1}};
-    int vertex = 0;
+    double testpoint[2];
+    double pointarray[MAXVERTS + 1][2];
+    int vertex;
 
-    char *string_pos = NULL;
-    int chars_read = 0;
+    char *string_pos;
     int showmenu = 0;
 
     imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module);
 
-    char *imap_menu = icr->imap_menu ?
-    icr->imap_menu : IMAP_MENU_DEFAULT;
-    char *imap_default = icr->imap_default ?
-    icr->imap_default : IMAP_DEFAULT_DEFAULT;
-    char *imap_base = icr->imap_base ?
-    icr->imap_base : IMAP_BASE_DEFAULT;
+    char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
+    char *imap_default = icr->imap_default
+			    ?  icr->imap_default : IMAP_DEFAULT_DEFAULT;
+    char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
 
     configfile_t *imap; 
 
@@ -664,8 +633,12 @@
     if (!imap)
         return NOT_FOUND;
 
-    imap_url(r, NULL, imap_base, base);         /* set base according to default */
-    imap_url(r, NULL, imap_default, mapdflt);   /* and default to global default */
+    base = imap_url(r, NULL, imap_base);         /* set base according to default */
+    if (!base)
+	return HTTP_INTERNAL_SERVER_ERROR;
+    mapdflt = imap_url(r, NULL, imap_default);   /* and default to global default */
+    if (!mapdflt)
+	return HTTP_INTERNAL_SERVER_ERROR;
 
     testpoint[X] = get_x_coord(r->args);
     testpoint[Y] = get_y_coord(r->args);
@@ -684,15 +657,7 @@
         menu_header(r, imap_menu);
     }
 
-    while (!cfg_getline(input, LARGEBUF, imap)) {
-        string_pos = input;     /* always start at the beginning of line */
-
-        directive[0] = '\0';
-        value[0] = '\0';
-        href_text[0] = '\0';
-        redirect[0] = '\0';
-        chars_read = 0;         /* clear these before using */
-
+    while (!cfg_getline(input, sizeof(input), imap)) {
         if (!input[0]) {
             if (showmenu) {
                 menu_blank(r, imap_menu);
@@ -707,35 +672,56 @@
             continue;
         }                       /* blank lines and comments are ignored if we aren't printing a menu */
 
-
-        if (sscanf(input, "%255s %255s", directive, value) != 2) {
-            continue;           /* make sure we read two fields */
-        }
-        /* Now skip what we just read... we can't use ANSIism %n */
-        while (!(isspace(*string_pos)))         /* past directive */
-            string_pos++;
-        while (isspace(*string_pos))    /* and whitespace */
-            string_pos++;
-        while (!(isspace(*string_pos)))         /* and value... have to watch it */
-            string_pos++;       /* can have punctuation and stuff */
+	/* find the first two space delimited fields, recall that
+	 * cfg_getline has removed leading/trailing whitespace and
+	 * compressed the other whitespace down to one space a piece
+	 *
+	 * note that we're tokenizing as we go... if we were to use the
+	 * getword() class of functions we would end up allocating extra
+	 * memory for every line of the map file
+	 */
+        string_pos = input;
+	if (!*string_pos)		/* need at least two fields */
+	    goto need_2_fields;
+
+	directive = string_pos;
+	while (*string_pos && *string_pos != ' ')	/* past directive */
+	    ++string_pos;
+	if (!*string_pos)		/* need at least two fields */
+	    goto need_2_fields;
+	*string_pos++ = '\0';
+
+	if (!*string_pos)		/* need at least two fields */
+	    goto need_2_fields;
+	value = string_pos;
+	while (*string_pos && *string_pos != ' ')	/* past value */
+	    ++string_pos;
+	if (*string_pos == ' ') {
+	    *string_pos++ = '\0';
+	}
+	else {
+	    /* end of input, don't advance past it */
+	    *string_pos = '\0';
+	}
 
         if (!strncasecmp(directive, "base", 4)) {       /* base, base_uri */
-            imap_url(r, NULL, value, base);
+            base = imap_url(r, NULL, value);
+	    if (!base)
+		goto menu_bail;
             continue;           /* base is never printed to a menu */
         }
 
-        chars_read = read_quoted(string_pos, href_text);
-        string_pos += chars_read;       /* read the quoted href text if present */
+        read_quoted(&string_pos, &href_text);
 
         if (!strcasecmp(directive, "default")) {        /* default */
-            imap_url(r, NULL, value, mapdflt);
+            mapdflt = imap_url(r, NULL, value);
+	    if (!mapdflt)
+		goto menu_bail;
             if (showmenu) {     /* print the default if there's a menu */
-                if (!*href_text) {      /* if we didn't find a "href text" */
-                    strncpy(href_text, mapdflt, sizeof(href_text) - 1);         /* use the href itself as text */
-                    href_text[sizeof(href_text) - 1] = '\0';
-                }
-                imap_url(r, base, mapdflt, redirect);
-                menu_default(r, imap_menu, redirect, href_text);
+                redirect = imap_url(r, base, mapdflt);
+		if (!redirect)
+		    goto menu_bail;
+                menu_default(r, imap_menu, redirect, href_text ? href_text : mapdflt);
             }
             continue;
         }
@@ -762,13 +748,13 @@
         pointarray[vertex][X] = -1;     /* signals the end of vertices */
 
         if (showmenu) {
-            read_quoted(string_pos, href_text);         /* href text could be here instead */
-            if (!*href_text) {  /* if we didn't find a "href text" */
-                strncpy(href_text, value, sizeof(href_text) - 1);       /* use the href itself in the menu */
-                href_text[sizeof(href_text) - 1] = '\0';
-            }
-            imap_url(r, base, value, redirect);
-            menu_directive(r, imap_menu, redirect, href_text);
+	    if (!href_text) {
+		read_quoted(&string_pos, &href_text);         /* href text could be here instead */
+	    }
+            redirect = imap_url(r, base, value);
+	    if (!redirect)
+		goto menu_bail;
+            menu_directive(r, imap_menu, redirect, href_text ? href_text : value);
             continue;
         }
         /* note that we don't make it past here if we are making a menu */
@@ -781,7 +767,9 @@
 
             if (pointinpoly(testpoint, pointarray)) {
 		cfg_closefile(imap);
-                imap_url(r, base, value, redirect);
+                redirect = imap_url(r, base, value);
+		if (!redirect)
+		    return HTTP_INTERNAL_SERVER_ERROR;
                 return (imap_reply(r, redirect));
             }
             continue;
@@ -791,7 +779,9 @@
 
             if (pointincircle(testpoint, pointarray)) {
 		cfg_closefile(imap);
-                imap_url(r, base, value, redirect);
+                redirect = imap_url(r, base, value);
+		if (!redirect)
+		    return HTTP_INTERNAL_SERVER_ERROR;
                 return (imap_reply(r, redirect));
             }
             continue;
@@ -801,7 +791,9 @@
 
             if (pointinrect(testpoint, pointarray)) {
 		cfg_closefile(imap);
-                imap_url(r, base, value, redirect);
+                redirect = imap_url(r, base, value);
+		if (!redirect)
+		    return HTTP_INTERNAL_SERVER_ERROR;
                 return (imap_reply(r, redirect));
             }
             continue;
@@ -810,8 +802,7 @@
         if (!strcasecmp(directive, "point")) {  /* point */
 
             if (is_closer(testpoint, pointarray, &closest_yet)) {
-                strncpy(closest, value, sizeof(closest) - 1);   /* if the closest point yet save it */
-                closest[sizeof(closest) - 1] = '\0';
+		closest = pstrdup(r->pool, value);
             }
 
             continue;
@@ -826,17 +817,38 @@
         return OK;
     }
 
-    if (*closest) {             /* if a 'point' directive has been seen */
-        imap_url(r, base, closest, redirect);
+    if (closest) {             /* if a 'point' directive has been seen */
+        redirect = imap_url(r, base, closest);
+	if (!redirect)
+	    return HTTP_INTERNAL_SERVER_ERROR;
         return (imap_reply(r, redirect));
     }
 
-    if (*mapdflt) {             /* a default should be defined, even if only 'nocontent' */
-        imap_url(r, base, mapdflt, redirect);
+    if (mapdflt) {             /* a default should be defined, even if only 'nocontent' */
+        redirect = imap_url(r, base, mapdflt);
+	if (!redirect)
+	    return HTTP_INTERNAL_SERVER_ERROR;
         return (imap_reply(r, redirect));
     }
 
     return SERVER_ERROR;        /* If we make it this far, we failed. They lose! */
+
+need_2_fields:
+    aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+		"map file %s, line %d syntax error: requires at least two fields",
+		r->uri, imap->line_number);
+    /* fall through */
+menu_bail:
+    cfg_closefile(imap);
+    if (showmenu) {
+	/* There's not much else we can do ... we've already sent the headers
+	 * to the client.
+	 */
+	rputs("\n\n[an internal server error occured]\n", r);
+	menu_footer(r);
+	return OK;
+    }
+    return HTTP_INTERNAL_SERVER_ERROR;
 }
 
 
Index: src/modules/standard/mod_include.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_include.c,v
retrieving revision 1.59
diff -u -r1.59 mod_include.c
--- mod_include.c	1997/12/22 21:53:17	1.59
+++ mod_include.c	1998/01/06 22:22:16
@@ -97,6 +97,12 @@
 #define SIZEFMT_KMG 1
 
 
+static ap_inline void safe_copy(char *dest, const char *src, size_t max_len)
+{
+    strncpy(dest, src, max_len - 1);
+    dest[max_len - 1] = '\0';
+}
+
 /* ------------------------ Environment function -------------------------- */
 
 static void add_include_vars(request_rec *r, char *timefmt)
@@ -196,7 +202,7 @@
    c = (char)i; \
  }
 
-static int find_string(FILE *in, char *str, request_rec *r, int printing)
+static int find_string(FILE *in, const char *str, request_rec *r, int printing)
 {
     int x, l = strlen(str), p;
     char outbuf[OUTBUFSIZE];
@@ -261,8 +267,8 @@
 {
     int val, i, j;
     char *p = s;
-    char *ents;
-    static char *entlist[MAXENTLEN + 1] =
+    const char *ents;
+    static const char * const entlist[MAXENTLEN + 1] =
     {
         NULL,                   /* 0 */
         NULL,                   /* 1 */
@@ -344,9 +350,9 @@
 static char *get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode)
 {
     char *t = tag, *tag_val, c, term;
-    int n;
 
-    n = 0;
+    /* makes code below a little less cluttered */
+    --tagbuf_len;
 
     do {                        /* skip whitespace */
         GET_CHAR(in, c, NULL, p);
@@ -360,8 +366,7 @@
                 GET_CHAR(in, c, NULL, p);
             } while (isspace(c));
             if (c == '>') {
-                strncpy(tag, "done", tagbuf_len - 1);
-                tag[tagbuf_len - 1] = '\0';
+                safe_copy(tag, "done", tagbuf_len);
                 return tag;
             }
         }
@@ -370,8 +375,8 @@
 
     /* find end of tag name */
     while (1) {
-        if (++n == tagbuf_len) {
-            t[tagbuf_len - 1] = '\0';
+        if (t - tag == tagbuf_len) {
+            *t = '\0';
             return NULL;
         }
         if (c == '=' || isspace(c)) {
@@ -404,8 +409,8 @@
     term = c;
     while (1) {
         GET_CHAR(in, c, NULL, p);
-        if (++n == tagbuf_len) {
-            t[tagbuf_len - 1] = '\0';
+        if (t - tag == tagbuf_len) {
+            *t = '\0';
             return NULL;
         }
 /* Want to accept \" as a valid character within a string. */
@@ -428,10 +433,14 @@
     return pstrdup(p, tag_val);
 }
 
-static int get_directive(FILE *in, char *d, pool *p)
+static int get_directive(FILE *in, char *dest, size_t len, pool *p)
 {
+    char *d = dest;
     char c;
 
+    /* make room for nul terminator */
+    --len;
+
     /* skip initial whitespace */
     while (1) {
         GET_CHAR(in, c, 1, p);
@@ -441,6 +450,9 @@
     }
     /* now get directive */
     while (1) {
+	if (d - dest == len) {
+	    return 1;
+	}
         *d++ = tolower(c);
         GET_CHAR(in, c, 1, p);
         if (isspace(c)) {
@@ -454,16 +466,24 @@
 /*
  * Do variable substitution on strings
  */
-static void parse_string(request_rec *r, char *in, char *out, int length,
-                         int leave_name)
+static void parse_string(request_rec *r, const char *in, char *out,
+			size_t length, int leave_name)
 {
     char ch;
     char *next = out;
-    int numchars = 0;
+    char *end_out;
+
+    /* leave room for nul terminator */
+    end_out = out + length - 1;
 
     while ((ch = *in++) != '\0') {
         switch (ch) {
         case '\\':
+	    if (next == end_out) {
+		/* truncated */
+		*next = '\0';
+		return;
+	    }
             if (*in == '$') {
                 *next++ = *in++;
             }
@@ -473,78 +493,68 @@
             break;
         case '$':
             {
-                char var[MAX_STRING_LEN];
-                char vtext[MAX_STRING_LEN];
-                char *val;
-                int braces = 0;
-                int vlen, vtlen;
-                /* 
-                 * Keep the $ and { around because we do no substitution
-                 * if the variable isn't found
-                 */
-                vlen = vtlen = 0;
-                vtext[vtlen++] = ch;
-                if (*in == '{') {
-                    braces = 1;
-                    vtext[vtlen++] = *in++;
-                }
-                while (*in != '\0') {
-                    if (vlen == (MAX_STRING_LEN - 1)) {
-                        continue;
-                    }
-                    if (braces == 1) {
-                        if (*in == '}') {
-                            break;
-                        }
-                    }
-                    else if (!(isalpha((int) *in) || (*in == '_') ||
-                               isdigit((int) *in))) {
-                        break;
-                    }
-                    if (vtlen < (MAX_STRING_LEN - 1)) {
-                        vtext[vtlen++] = *in;
-                    }
-                    var[vlen++] = *in++;
-                }
-                var[vlen] = vtext[vtlen] = '\0';
-                if (braces == 1) {
-                    if (*in != '}') {
+		char var[MAX_STRING_LEN];
+		const char *start_of_var_name;
+		const char *end_of_var_name;	/* end of var name + 1 */
+		const char *expansion;
+		const char *val;
+		size_t l;
+
+		/* guess that the expansion won't happen */
+		expansion = in - 1;
+		if (*in == '{') {
+		    ++in;
+		    start_of_var_name = in;
+		    in = strchr(in, '}');
+		    if (in == NULL) {
                         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
-				    r->server, "Invalid variable \"%s%s\"",
-				    vtext, in);
+				    r->server, "Missing '}' on variable \"%s\"",
+				    expansion);
                         *next = '\0';
                         return;
                     }
-                    else {
-                        in++;
-                    }
-                }
-
-                val = (char *) NULL;
-                if (var[0] == '\0') {
-                    val = &vtext[0];
-                }
-                else {
-                    val = table_get(r->subprocess_env, &var[0]);
-                    if (!val && leave_name) {
-                        val = &vtext[0];
-                    }
-                }
-                while ((val != (char *) NULL) && (*val != '\0')) {
-                    *next++ = *val++;
-                    if (++numchars == (length - 1)) {
-                        break;
-                    }
-                }
+		    end_of_var_name = in;
+		    ++in;
+		}
+		else {
+		    start_of_var_name = in;
+		    while (isalnum(*in) || *in == '_') {
+			++in;
+		    }
+		    end_of_var_name = in;
+		}
+		/* what a pain, too bad there's no table_getn where you can
+		 * pass a non-nul terminated string */
+		l = end_of_var_name - start_of_var_name;
+		l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
+		memcpy(var, start_of_var_name, l);
+		var[l] = '\0';
+
+		val = table_get(r->subprocess_env, var);
+		if (val) {
+		    expansion = val;
+		    l = strlen(expansion);
+		}
+		else if (leave_name) {
+		    l = in - expansion;
+		}
+		else {
+		    break;	/* no expansion to be done */
+		}
+		l = (l > end_out - next) ? (end_out - next) : l;
+		memcpy(next, expansion, l);
+		next += l;
                 break;
             }
         default:
+	    if (next == end_out) {
+		/* truncated */
+		*next = '\0';
+		return;
+	    }
             *next++ = ch;
             break;
         }
-        if (++numchars == (length - 1)) {
-            break;
-        }
     }
     *next = '\0';
     return;
@@ -596,26 +606,48 @@
     return 0;
 }
 
-static int handle_include(FILE *in, request_rec *r, char *error, int noexec)
+/* ensure that path is relative, and does not contain ".." elements
+ * ensentially ensure that it does not match the regex:
+ * (^/|(^|/)\.\.(/|$))
+ * XXX: this needs os abstraction... consider c:..\foo in win32
+ */
+static int is_only_below(const char *path)
+{
+    if (path[0] == '/') {
+	return 0;
+    }
+    if (path[0] == '.' && path[1] == '.'
+	&& (path[2] == '\0' || path[2] == '/')) {
+	return 0;
+    }
+    while (*path) {
+	if (*path == '/' && path[1] == '.' && path[2] == '.'
+	    && (path[3] == '\0' || path[3] == '/')) {
+	    return 0;
+	}
+	++path;
+    }
+    return 1;
+}
+
+static int handle_include(FILE *in, request_rec *r, const char *error, int noexec)
 {
     char tag[MAX_STRING_LEN];
     char parsed_string[MAX_STRING_LEN];
     char *tag_val;
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) {
             request_rec *rr = NULL;
             char *error_fmt = NULL;
 
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             if (tag[0] == 'f') {
                 /* be safe; only files in this directory or below allowed */
-                char tmp[MAX_STRING_LEN + 2];
-                ap_snprintf(tmp, sizeof(tmp), "/%s/", parsed_string);
-                if (parsed_string[0] == '/' || strstr(tmp, "/../") != NULL) {
+		if (!is_only_below(parsed_string)) {
                     error_fmt = "unable to include file \"%s\" "
                         "in parsed file %s";
                 }
@@ -775,7 +807,7 @@
 }
 
 
-static int handle_exec(FILE *in, request_rec *r, char *error)
+static int handle_exec(FILE *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
@@ -783,11 +815,11 @@
     char parsed_string[MAX_STRING_LEN];
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         if (!strcmp(tag, "cmd")) {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 1);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
             if (include_cmd(parsed_string, r) == -1) {
                 aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                             "execution failure for parameter \"%s\" "
@@ -799,7 +831,7 @@
             chdir_file(r->filename);
         }
         else if (!strcmp(tag, "cgi")) {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             if (include_cgi(parsed_string, r) == -1) {
                 aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                             "invalid CGI ref \"%s\" in %s", tag_val, file);
@@ -821,13 +853,13 @@
 
 }
 
-static int handle_echo(FILE *in, request_rec *r, char *error)
+static int handle_echo(FILE *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         if (!strcmp(tag, "var")) {
@@ -853,7 +885,7 @@
 }
 
 #ifdef USE_PERL_SSI
-static int handle_perl(FILE *in, request_rec *r, char *error)
+static int handle_perl(FILE *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
@@ -867,7 +899,7 @@
         return DECLINED;
     }
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             break;
         }
         if (strnEQ(tag, "sub", 3)) {
@@ -898,27 +930,23 @@
     table *env = r->subprocess_env;
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) {
             return 1;
         }
         if (!strcmp(tag, "errmsg")) {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
-            strncpy(error, parsed_string, MAX_STRING_LEN - 1);
-            error[MAX_STRING_LEN - 1] = '\0';
+            parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
         }
         else if (!strcmp(tag, "timefmt")) {
             time_t date = r->request_time;
 
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
-            strncpy(tf, parsed_string, MAX_STRING_LEN - 1);
-            tf[MAX_STRING_LEN - 1] = '\0';
+            parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
             table_set(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0));
             table_set(env, "DATE_GMT", ht_time(r->pool, date, tf, 1));
             table_set(env, "LAST_MODIFIED",
                       ht_time(r->pool, r->finfo.st_mtime, tf, 0));
         }
         else if (!strcmp(tag, "sizefmt")) {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             decodehtml(parsed_string);
             if (!strcmp(parsed_string, "bytes")) {
                 *sizefmt = SIZEFMT_BYTES;
@@ -940,15 +968,14 @@
 }
 
 
-static int find_file(request_rec *r, char *directive, char *tag,
-                     char *tag_val, struct stat *finfo, char *error)
+static int find_file(request_rec *r, const char *directive, const char *tag,
+                     char *tag_val, struct stat *finfo, const char *error)
 {
-    char *dir = "./";
     char *to_send;
 
     if (!strcmp(tag, "file")) {
         getparents(tag_val);    /* get rid of any nasties */
-        to_send = make_full_path(r->pool, dir, tag_val);
+        to_send = make_full_path(r->pool, "./", tag_val);
         if (stat(to_send, finfo) == -1) {
             aplog_error(APLOG_MARK, APLOG_ERR, r->server,
                         "unable to get information about \"%s\" "
@@ -988,7 +1015,7 @@
 }
 
 
-static int handle_fsize(FILE *in, request_rec *r, char *error, int sizefmt)
+static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
@@ -996,14 +1023,14 @@
     char parsed_string[MAX_STRING_LEN];
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         else if (!strcmp(tag, "done")) {
             return 0;
         }
         else {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
                 if (sizefmt == SIZEFMT_KMG) {
                     send_size(finfo.st_size, r);
@@ -1029,7 +1056,7 @@
     }
 }
 
-static int handle_flastmod(FILE *in, request_rec *r, char *error, char *tf)
+static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
@@ -1037,14 +1064,14 @@
     char parsed_string[MAX_STRING_LEN];
 
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         else if (!strcmp(tag, "done")) {
             return 0;
         }
         else {
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
                 rputs(ht_time(r->pool, finfo.st_mtime, tf, 0), r);
             }
@@ -1079,7 +1106,10 @@
     char value[MAX_STRING_LEN];
 };
 
-static char *get_ptoken(request_rec *r, char *string, struct token *token)
+/* there is an implicit assumption here that string is at most MAX_STRING_LEN-1
+ * characters long...
+ */
+static const char *get_ptoken(request_rec *r, const char *string, struct token *token)
 {
     char ch;
     int next = 0;
@@ -1235,14 +1265,17 @@
  * cases.  And, without rewriting this completely, the easiest way
  * is to just branch to the return code which cleans it up.
  */
-static int parse_expr(request_rec *r, char *expr, char *error)
+/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
+ * characters long...
+ */
+static int parse_expr(request_rec *r, const char *expr, const char *error)
 {
     struct parse_node {
         struct parse_node *left, *right, *parent;
         struct token token;
         int value, done;
     }         *root, *current, *new;
-    char *parse;
+    const char *parse;
     char buffer[MAX_STRING_LEN];
     pool *expr_pool;
     int retval = 0;
@@ -1251,23 +1284,12 @@
         return (0);
     }
     root = current = (struct parse_node *) NULL;
-    if ((expr_pool = make_sub_pool(r->pool)) == (pool *) NULL) {
-        aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                    "out of memory processing file %s", r->filename);
-        rputs(error, r);
-        return (0);
-    }
+    expr_pool = make_sub_pool(r->pool);
 
     /* Create Parse Tree */
     while (1) {
         new = (struct parse_node *) palloc(expr_pool,
                                            sizeof(struct parse_node));
-        if (new == (struct parse_node *) NULL) {
-            aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                        "out of memory processing file %s", r->filename);
-            rputs(error, r);
-            goto RETURN;
-        }
         new->parent = new->left = new->right = (struct parse_node *) NULL;
         new->done = 0;
         if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) {
@@ -1287,10 +1309,13 @@
             case token_string:
                 if (current->token.value[0] != '\0') {
                     strncat(current->token.value, " ",
-                         MAX_STRING_LEN - strlen(current->token.value) - 1);
+                         sizeof(current->token.value)
+			    - strlen(current->token.value) - 1);
                 }
                 strncat(current->token.value, new->token.value,
-                        MAX_STRING_LEN - strlen(current->token.value) - 1);
+                         sizeof(current->token.value)
+			    - strlen(current->token.value) - 1);
+		current->token.value[sizeof(current->token.value) - 1] = '\0';
                 break;
             case token_eq:
             case token_ne:
@@ -1548,9 +1573,8 @@
 #ifdef DEBUG_INCLUDE
             rputs("     Evaluate string\n", r);
 #endif
-            parse_string(r, current->token.value, buffer, MAX_STRING_LEN, 0);
-            strncpy(current->token.value, buffer, MAX_STRING_LEN - 1);
-            current->token.value[MAX_STRING_LEN - 1] = '\0';
+            parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
+	    safe_copy(current->token.value, buffer, sizeof(current->token.value));
             current->value = (current->token.value[0] != '\0');
             current->done = 1;
             current = current->parent;
@@ -1573,10 +1597,9 @@
                 switch (current->left->token.type) {
                 case token_string:
                     parse_string(r, current->left->token.value,
-                                 buffer, MAX_STRING_LEN, 0);
-                    strncpy(current->left->token.value, buffer,
-                            MAX_STRING_LEN - 1);
-                    current->left->token.value[MAX_STRING_LEN - 1] = '\0';
+                                 buffer, sizeof(buffer), 0);
+                    safe_copy(current->left->token.value, buffer,
+                            sizeof(current->left->token.value));
 		    current->left->value = (current->left->token.value[0] != '\0');
                     current->left->done = 1;
                     break;
@@ -1589,10 +1612,9 @@
                 switch (current->right->token.type) {
                 case token_string:
                     parse_string(r, current->right->token.value,
-                                 buffer, MAX_STRING_LEN, 0);
-                    strncpy(current->right->token.value, buffer,
-                            MAX_STRING_LEN - 1);
-                    current->right->token.value[MAX_STRING_LEN - 1] = '\0';
+                                 buffer, sizeof(buffer), 0);
+                    safe_copy(current->right->token.value, buffer,
+                            sizeof(current->right->token.value));
 		    current->right->value = (current->right->token.value[0] != '\0');
                     current->right->done = 1;
                     break;
@@ -1637,13 +1659,13 @@
                 goto RETURN;
             }
             parse_string(r, current->left->token.value,
-                         buffer, MAX_STRING_LEN, 0);
-            strncpy(current->left->token.value, buffer, MAX_STRING_LEN - 1);
-            current->left->token.value[MAX_STRING_LEN - 1] = '\0';
+                         buffer, sizeof(buffer), 0);
+            safe_copy(current->left->token.value, buffer,
+			sizeof(current->left->token.value));
             parse_string(r, current->right->token.value,
-                         buffer, MAX_STRING_LEN, 0);
-            strncpy(current->right->token.value, buffer, MAX_STRING_LEN - 1);
-            current->right->token.value[MAX_STRING_LEN - 1] = '\0';
+                         buffer, sizeof(buffer), 0);
+            safe_copy(current->right->token.value, buffer,
+			sizeof(current->right->token.value));
             if (current->right->token.value[0] == '/') {
                 int len;
                 len = strlen(current->right->token.value);
@@ -1702,11 +1724,13 @@
                 goto RETURN;
             }
             parse_string(r, current->left->token.value,
-                         buffer, MAX_STRING_LEN, 0);
-            strncpy(current->left->token.value, buffer, MAX_STRING_LEN - 1);
+                         buffer, sizeof(buffer), 0);
+            safe_copy(current->left->token.value, buffer,
+			sizeof(current->left->token.value));
             parse_string(r, current->right->token.value,
-                         buffer, MAX_STRING_LEN, 0);
-            strncpy(current->right->token.value, buffer, MAX_STRING_LEN - 1);
+                         buffer, sizeof(buffer), 0);
+            safe_copy(current->right->token.value, buffer,
+			sizeof(current->right->token.value));
 #ifdef DEBUG_INCLUDE
             rvputs(r, "     Compare (", current->left->token.value,
                    ") with (", current->right->token.value, ")\n", NULL);
@@ -1803,19 +1827,27 @@
     return (retval);
 }
 
-static int handle_if(FILE *in, request_rec *r, char *error,
+static int handle_if(FILE *in, request_rec *r, const char *error,
                      int *conditional_status, int *printing)
 {
     char tag[MAX_STRING_LEN];
-    char *tag_val = '\0';
-    char *expr = '\0';
+    char *tag_val;
+    char *expr;
 
+    expr = NULL;
     while (1) {
-        tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0);
+        tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
         if (*tag == '\0') {
             return 1;
         }
         else if (!strcmp(tag, "done")) {
+	    if (expr == NULL) {
+		aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+			    "missing expr in if statement: %s",
+			    r->filename);
+		rputs(error, r);
+		return 1;
+	    }
             *printing = *conditional_status = parse_expr(r, expr, error);
 #ifdef DEBUG_INCLUDE
             rvputs(r, "**** if conditional_status=\"",
@@ -1838,15 +1870,16 @@
     }
 }
 
-static int handle_elif(FILE *in, request_rec *r, char *error,
+static int handle_elif(FILE *in, request_rec *r, const char *error,
                        int *conditional_status, int *printing)
 {
     char tag[MAX_STRING_LEN];
-    char *tag_val = '\0';
-    char *expr = '\0';
+    char *tag_val;
+    char *expr;
 
+    expr = NULL;
     while (1) {
-        tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0);
+        tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
         if (*tag == '\0') {
             return 1;
         }
@@ -1859,6 +1892,13 @@
                 *printing = 0;
                 return (0);
             }
+	    if (expr == NULL) {
+		aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+			    "missing expr in elif statement: %s",
+			    r->filename);
+		rputs(error, r);
+		return 1;
+	    }
             *printing = *conditional_status = parse_expr(r, expr, error);
 #ifdef DEBUG_INCLUDE
             rvputs(r, "**** elif conditional_status=\"",
@@ -1881,13 +1921,13 @@
     }
 }
 
-static int handle_else(FILE *in, request_rec *r, char *error,
+static int handle_else(FILE *in, request_rec *r, const char *error,
                        int *conditional_status, int *printing)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
 
-    if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+    if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
         return 1;
     }
     else if (!strcmp(tag, "done")) {
@@ -1901,7 +1941,8 @@
     }
     else {
         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                    "else directive does not take tags");
+                    "else directive does not take tags in %s",
+		    r->filename);
         if (*printing) {
             rputs(error, r);
         }
@@ -1909,13 +1950,13 @@
     }
 }
 
-static int handle_endif(FILE *in, request_rec *r, char *error,
+static int handle_endif(FILE *in, request_rec *r, const char *error,
                         int *conditional_status, int *printing)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
 
-    if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+    if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
         return 1;
     }
     else if (!strcmp(tag, "done")) {
@@ -1929,13 +1970,14 @@
     }
     else {
         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                    "endif directive does not take tags");
+                    "endif directive does not take tags in %s",
+		    r->filename);
         rputs(error, r);
         return -1;
     }
 }
 
-static int handle_set(FILE *in, request_rec *r, char *error)
+static int handle_set(FILE *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char parsed_string[MAX_STRING_LEN];
@@ -1944,7 +1986,7 @@
 
     var = (char *) NULL;
     while (1) {
-        if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+        if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
             return 1;
         }
         else if (!strcmp(tag, "done")) {
@@ -1956,30 +1998,31 @@
         else if (!strcmp(tag, "value")) {
             if (var == (char *) NULL) {
                 aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                            "variable must precede value in set directive");
+                            "variable must precede value in set directive in %s",
+			    r->filename);
                 rputs(error, r);
                 return -1;
             }
-            parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0);
+            parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
             table_set(r->subprocess_env, var, parsed_string);
         }
         else {
             aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                        "Invalid tag for set directive");
+                        "Invalid tag for set directive in %s", r->filename);
             rputs(error, r);
             return -1;
         }
     }
 }
 
-static int handle_printenv(FILE *in, request_rec *r, char *error)
+static int handle_printenv(FILE *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
     table_entry *elts = (table_entry *) r->subprocess_env->elts;
     int i;
 
-    if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
+    if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
         return 1;
     }
     else if (!strcmp(tag, "done")) {
@@ -1990,7 +2033,8 @@
     }
     else {
         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
-                    "printenv directive does not take tags");
+                    "printenv directive does not take tags in %s",
+		    r->filename);
         rputs(error, r);
         return -1;
     }
@@ -2012,10 +2056,8 @@
     int printing;
     int conditional_status;
 
-    strncpy(error, DEFAULT_ERROR_MSG, sizeof(error) - 1);
-    error[sizeof(error) - 1] = '\0';
-    strncpy(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt) - 1);
-    timefmt[sizeof(timefmt) - 1] = '\0';
+    safe_copy(error, DEFAULT_ERROR_MSG, sizeof(error));
+    safe_copy(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
     sizefmt = SIZEFMT_KMG;
 
 /*  Turn printing on */
@@ -2034,7 +2076,11 @@
 
     while (1) {
         if (!find_string(f, STARTING_SEQUENCE, r, printing)) {
-            if (get_directive(f, directive, r->pool)) {
+            if (get_directive(f, directive, sizeof(directive), r->pool)) {
+		aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+			    "mod_include: error reading directive in %s",
+			    r->filename);
+		rputs(error, r);
                 return;
             }
             if (!strcmp(directive, "if")) {