]> granicus.if.org Git - apache/commitdiff
Backport the CSS security fixes to Apache 2.0a. Or is that forward
authorJim Jagielski <jim@apache.org>
Mon, 13 Mar 2000 20:27:29 +0000 (20:27 +0000)
committerJim Jagielski <jim@apache.org>
Mon, 13 Mar 2000 20:27:29 +0000 (20:27 +0000)
port? My sense of direction is all confused.

PR:
Obtained from:
Submitted by:
Reviewed by:

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@84751 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
include/http_core.h
modules/filters/mod_include.c
modules/generators/mod_autoindex.c
modules/http/http_core.c
modules/http/http_protocol.c
modules/loggers/mod_log_config.c
modules/mappers/mod_actions.c
modules/mappers/mod_speling.c
modules/metadata/mod_expires.c
modules/proxy/proxy_util.c
server/log.c
server/util.c

index b3364756f3fbc87e59b882e671330504c04ac920..efdcc1c51e7b82fb3d55525df3414c5bce9dcc33 100644 (file)
@@ -245,6 +245,15 @@ typedef struct {
      */
     unsigned d_is_fnmatch : 1;
 
+    /* should we force a charset on any outgoing parameterless content-type?
+     * if so, which charset?
+     */
+#define ADD_DEFAULT_CHARSET_OFF   (0)
+#define ADD_DEFAULT_CHARSET_ON    (1)
+#define ADD_DEFAULT_CHARSET_UNSET (2)
+    unsigned add_default_charset : 2;
+    char *add_default_charset_name;
+
     unsigned long limit_req_body;  /* limit on bytes in request msg body */
 
     /* logging options */
index 344e8ccd9d5cd18e424a85da86de6ba39e313ca1..ab0f0301e43644938eb1f2a5d600c82bd1067ef8 100644 (file)
@@ -943,6 +943,9 @@ static int handle_echo(ap_file_t *in, request_rec *r, const char *error)
 {
     char tag[MAX_STRING_LEN];
     char *tag_val;
+    enum {E_NONE, E_URL, E_ENTITY} encode;
+
+    encode = E_ENTITY;
 
     while (1) {
         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
@@ -952,7 +955,15 @@ static int handle_echo(ap_file_t *in, request_rec *r, const char *error)
             const char *val = ap_table_get(r->subprocess_env, tag_val);
 
             if (val) {
-                ap_rputs(val, r);
+               if (encode == E_NONE) {
+                   ap_rputs(val, r);
+               }
+               else if (encode == E_URL) {
+                   ap_rputs(ap_escape_uri(r->pool, val), r);
+               }
+               else if (encode == E_ENTITY) {
+                   ap_rputs(ap_escape_html(r->pool, val), r);
+               }
             }
             else {
                 ap_rputs("(none)", r);
@@ -961,6 +972,19 @@ static int handle_echo(ap_file_t *in, request_rec *r, const char *error)
         else if (!strcmp(tag, "done")) {
             return 0;
         }
+       else if (!strcmp(tag, "encoding")) {
+           if (!strcasecmp(tag_val, "none")) encode = E_NONE;
+           else if (!strcasecmp(tag_val, "url")) encode = E_URL;
+           else if (!strcasecmp(tag_val, "entity")) encode = E_ENTITY;
+           else {
+               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                           "unknown value \"%s\" to parameter \"encoding\" of "
+                           "tag echo in %s",
+                           tag_val, r->filename);
+               ap_rputs(error, r);
+           }
+       }
+
         else {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                         "unknown parameter \"%s\" to tag echo in %s",
@@ -2138,7 +2162,8 @@ static int handle_printenv(ap_file_t *in, request_rec *r, const char *error)
     }
     else if (!strcmp(tag, "done")) {
         for (i = 0; i < arr->nelts; ++i) {
-            ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL);
+            ap_rvputs(r, ap_escape_html(r->pool, elts[i].key), "=", 
+               ap_escape_html(r->pool, elts[i].val), "\n", NULL);
         }
         return 0;
     }
index 5212b629ba16bf15e834713037d4e1b00b4b743f..f6a76f13eac67f65f653f46e42564dfef73d690a 100644 (file)
@@ -690,7 +690,7 @@ struct ent {
 
 static char *find_item(request_rec *r, ap_array_header_t *list, int path_only)
 {
-    const char *content_type = r->content_type;
+    const char *content_type = ap_field_noparam(r->pool, r->content_type);
     const char *content_encoding = r->content_encoding;
     char *path = r->filename;
 
index 126920f95ca3cba9b2301b34cba494e5800a0e4d..0bb8447e2bbe68d9d96e08cf133ca747e194a71a 100644 (file)
@@ -146,6 +146,9 @@ static void *create_core_dir_config(ap_context_t *a, char *dir)
 
     conf->server_signature = srv_sig_unset;
 
+    conf->add_default_charset = ADD_DEFAULT_CHARSET_UNSET;
+    conf->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
+
     return (void *)conf;
 }
 
@@ -257,6 +260,14 @@ static void *merge_core_dir_configs(ap_context_t *a, void *basev, void *newv)
        conf->server_signature = new->server_signature;
     }
 
+    if (new->add_default_charset != ADD_DEFAULT_CHARSET_UNSET) {
+       conf->add_default_charset = new->add_default_charset;
+    }
+
+    if (new->add_default_charset_name) {
+       conf->add_default_charset_name = new->add_default_charset_name;
+    }
+
     return (void*)conf;
 }
 
@@ -1000,6 +1011,27 @@ static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, char *arg)
 }
 #endif /*GPROF*/
 
+static const char *set_add_default_charset(cmd_parms *cmd, 
+       core_dir_config *d, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+    if (!strcasecmp(arg, "Off")) {
+       d->add_default_charset = ADD_DEFAULT_CHARSET_OFF;
+    }
+    else if (!strcasecmp(arg, "On")) {
+       d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
+       d->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
+    }
+    else {
+       d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
+       d->add_default_charset_name = arg;
+    }
+    return NULL;
+}
+
 static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg)
 {
     void *sconf = cmd->server->module_config;
@@ -2294,6 +2326,8 @@ static const command_rec core_cmds[] = {
 { "GprofDir", set_gprof_dir, NULL, RSRC_CONF, TAKE1,
   "Directory to plop gmon.out files" },
 #endif
+{ "AddDefaultCharset", set_add_default_charset, NULL, OR_FILEINFO, 
+  TAKE1, "The name of the default charset to add to any Content-Type without one or 'Off' to disable" },
 
 /* Old resource config file commands */
   
index 7011be758d58e4a242c6ce56c15da8c9ec9f07f4..e345e57eb180bc8db0de95591bdaede125aedc0f 100644 (file)
@@ -90,6 +90,43 @@ HOOK_STRUCT(
   } while (0)
 
 
+/*
+ * Builds the content-type that should be sent to the client from the
+ * content-type specified.  The following rules are followed:
+ *    - if type is NULL, type is set to ap_default_type(r)
+ *    - if charset adding is disabled, stop processing and return type.
+ *    - then, if there are no parameters on type, add the default charset
+ *    - return type
+ */
+static const char *make_content_type(request_rec *r, const char *type) {
+    char *needcset[] = {
+       "text/plain",
+       "text/html",
+       NULL };
+    char **pcset;
+    core_dir_config *conf = (core_dir_config *)ap_get_module_config(
+       r->per_dir_config, &core_module);
+    if (!type) type = ap_default_type(r);
+    if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) return type;
+
+    if (ap_strcasestr(type, "charset=") != NULL) {
+       /* already has parameter, do nothing */
+       /* XXX we don't check the validity */
+       ;
+    } else {
+       /* see if it makes sense to add the charset. At present,
+        * we only add it if the Content-type is one of needcset[]
+        */
+       for (pcset = needcset; *pcset ; pcset++)
+           if (ap_strcasestr(type, *pcset) != NULL) {
+               type = ap_pstrcat(r->pool, type, "; charset=", 
+                   conf->add_default_charset_name, NULL);
+               break;
+           }
+    }
+    return type;
+}
+
 static int parse_byterange(char *range, long clength, long *start, long *end)
 {
     char *dash = strchr(range, '-');
@@ -240,7 +277,7 @@ static int internal_byterange(int realreq, long *tlength, request_rec *r,
                                   length);
 
     if (r->byterange > 1) {
-        const char *ct = r->content_type ? r->content_type : ap_default_type(r);
+        const char *ct = make_content_type(r, r->content_type);
         char ts[MAX_STRING_LEN];
 
         ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end,
@@ -897,7 +934,7 @@ static void get_mime_headers(request_rec *r)
             r->status = HTTP_BAD_REQUEST;
             ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
                 "Size of a request header field exceeds server limit.<P>\n"
-                "<PRE>\n", field, "</PRE>\n", NULL));
+                "<PRE>\n", ap_escape_html(r->pool, field), "</PRE>\n", NULL));
             return;
         }
         copy = ap_palloc(r->pool, len + 1);
@@ -907,7 +944,7 @@ static void get_mime_headers(request_rec *r)
             r->status = HTTP_BAD_REQUEST;       /* or abort the bad request */
             ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
                 "Request header field is missing colon separator.<P>\n"
-                "<PRE>\n", copy, "</PRE>\n", NULL));
+                "<PRE>\n", ap_escape_html(r->pool, copy), "</PRE>\n", NULL));
             return;
         }
 
@@ -1604,10 +1641,8 @@ API_EXPORT(void) ap_send_http_header(request_rec *r)
         ap_table_setn(r->headers_out, "Content-Type",
                   ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
                           "byteranges; boundary=", r->boundary, NULL));
-    else if (r->content_type)
-        ap_table_setn(r->headers_out, "Content-Type", r->content_type);
-    else
-        ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r));
+    else ap_table_setn(r->headers_out, "Content-Type", make_content_type(r, 
+       r->content_type));
 
     if (r->content_encoding)
         ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
@@ -2493,7 +2528,7 @@ API_EXPORT(void) ap_send_error_response(request_rec *r, int recursive_error)
         r->content_languages = NULL;
         r->content_encoding = NULL;
         r->clength = 0;
-        r->content_type = "text/html";
+        r->content_type = "text/html; charset=iso-8859-1";
 
         if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
             ap_table_setn(r->headers_out, "Allow", make_allow(r));
index 1231f4921e5a3a3d144f5d3e33bc53a2dbd24b86..067ca817097dd0b57d7895f477535118c3f277f0 100644 (file)
@@ -364,7 +364,7 @@ static const char *log_header_out(request_rec *r, char *a)
 {
     const char *cp = ap_table_get(r->headers_out, a);
     if (!strcasecmp(a, "Content-type") && r->content_type) {
-        cp = r->content_type;
+        cp = ap_field_noparam(r->pool, r->content_type);
     }
     if (cp) {
         return cp;
index 4f2b60aa2029f698e4361233d411cf61b6823b5a..ec38d8cf71b405d912b7c3402099725be6d76515 100644 (file)
@@ -159,7 +159,8 @@ static int action_handler(request_rec *r)
 {
     action_dir_config *conf = (action_dir_config *)
         ap_get_module_config(r->per_dir_config, &action_module);
-    const char *t, *action = r->handler ? r->handler : r->content_type;
+    const char *t, *action = r->handler ? r->handler : 
+       ap_field_noparam(r->pool, r->content_type);
     const char *script;
     int i;
 
index 0c8b1a87bce57a4b13fe6bab9fe4c4cb8e9dc96c..48d12ea8b9391a28f2a3f6d25826a48f95caca46 100644 (file)
@@ -467,7 +467,7 @@ static int check_speling(request_rec *r)
 
            *(const char **)ap_push_array(t) =
                          "The document name you requested (<code>";
-           *(const char **)ap_push_array(t) = r->uri;
+           *(const char **)ap_push_array(t) = ap_escape_html(sub_pool, r->uri);
            *(const char **)ap_push_array(t) =
                           "</code>) could not be found on this server.\n"
                           "However, we found documents with names similar "
@@ -486,15 +486,15 @@ static int check_speling(request_rec *r)
                                      ? r->parsed_uri.query : "",
                                  NULL);
                *(const char **)ap_push_array(v) = "\"";
-               *(const char **)ap_push_array(v) = vuri;
+               *(const char **)ap_push_array(v) = ap_escape_uri(sub_pool, vuri);
                *(const char **)ap_push_array(v) = "\";\"";
                *(const char **)ap_push_array(v) = reason;
                *(const char **)ap_push_array(v) = "\"";
 
                *(const char **)ap_push_array(t) = "<li><a href=\"";
-               *(const char **)ap_push_array(t) = vuri;
+               *(const char **)ap_push_array(t) = ap_escape_uri(sub_pool, vuri);
                *(const char **)ap_push_array(t) = "\">";
-               *(const char **)ap_push_array(t) = vuri;
+               *(const char **)ap_push_array(t) = ap_escape_html(sub_pool, vuri);
                *(const char **)ap_push_array(t) = "</a> (";
                *(const char **)ap_push_array(t) = reason;
                *(const char **)ap_push_array(t) = ")\n";
@@ -521,7 +521,7 @@ static int check_speling(request_rec *r)
                 *(const char **)ap_push_array(t) =
                               "Please consider informing the owner of the "
                               "<a href=\"";
-                *(const char **)ap_push_array(t) = ref;
+               *(const char **)ap_push_array(t) = ap_escape_uri(sub_pool, ref);
                 *(const char **)ap_push_array(t) = "\">referring page</a> "
                               "about the broken link.\n";
            }
index 865c005326c1be88c78cee659c5d30662272c76e..7f2a38e9964edb061db0501b048b99c34aa64e7f 100644 (file)
@@ -441,7 +441,8 @@ static int add_expires(request_rec *r)
     if (r->content_type == NULL)
         code = NULL;
     else
-        code = (char *) ap_table_get(conf->expiresbytype, r->content_type);
+        code = (char *) ap_table_get(conf->expiresbytype, 
+               ap_field_noparam(r->pool, r->content_type));
 
     if (code == NULL) {
         /* no expires defined for that type, is there a default? */
index 97b155a3854b605af1e9d73cd6e161b9645b63c8..41d6b7a41ea3b91062aed560db2285d9ab50ffc9 100644 (file)
@@ -822,11 +822,15 @@ int ap_proxyerror(request_rec *r, int statuscode, const char *message)
     ap_table_setn(r->notes, "error-notes",
                  ap_pstrcat(r->pool, 
                             "The proxy server could not handle the request "
-                            "<EM><A HREF=\"", r->uri, "\">",
-                            r->method, "&nbsp;", r->uri, "</A></EM>.<P>\n"
-                            "Reason: <STRONG>", message, "</STRONG>", NULL));
-
-    /* Allow the "error-notes" string to be printed by ap_send_error_response() */
+                            "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
+                            "\">", ap_escape_html(r->pool, r->method),
+                            "&nbsp;", 
+                            ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
+                            "Reason: <STRONG>",
+                            ap_escape_html(r->pool, message), 
+                            "</STRONG>", NULL));
+
+    /* Allow "error-notes" string to be printed by ap_send_error_response() */
     ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*"));
 
     r->status_line = ap_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
index 244cf611d40ccc931f04f47bb8cb9e6c270e13f9..d1f0cdc278d24452c7d6a2abf5418a0f29158237 100644 (file)
@@ -469,7 +469,8 @@ API_EXPORT(void) ap_log_rerror(const char *file, int line, int level,
     if (((level & APLOG_LEVELMASK) <= APLOG_WARNING)
        && (ap_table_get(r->notes, "error-notes") == NULL)) {
        ap_table_setn(r->notes, "error-notes",
-                     ap_pvsprintf(r->pool, fmt, args));
+                     ap_escape_html(r->pool, ap_pvsprintf(r->pool, fmt, 
+                     args)));
     }
     va_end(args);
 }
index dd0a11c5f19dcb70c18f8379408e64ed38c1dfe2..063359f01da0bfa0181ee5b57ebff05e44121039 100644 (file)
@@ -104,6 +104,8 @@ API_EXPORT(char *) ap_field_noparam(ap_context_t *p, const char *intype)
 {
     const char *semi;
 
+    if (intype == NULL) return NULL;
+
     semi = strchr(intype, ';');
     if (semi == NULL) {
        return ap_pstrdup(p, intype);
@@ -275,6 +277,38 @@ API_EXPORT(void) ap_pregfree(ap_context_t *p, regex_t * reg)
     ap_unblock_alarms();
 }
 
+/*
+ * Similar to standard strstr() but we ignore case in this version.
+ * Based on the strstr() implementation further below.
+ */
+API_EXPORT(char *) ap_strcasestr(const char *s1, const char *s2)
+{
+    char *p1, *p2;
+    if (*s2 == '\0') {
+       /* an empty s2 */
+        return((char *)s1);
+    }
+    while(1) {
+       for ( ; (*s1 != '\0') && (ap_tolower(*s1) != ap_tolower(*s2)); s1++);
+       if (*s1 == '\0') return(NULL);
+       /* found first character of s2, see if the rest matches */
+        p1 = (char *)s1;
+        p2 = (char *)s2;
+        while (ap_tolower(*++p1) == ap_tolower(*++p2)) {
+            if (*p1 == '\0') {
+                /* both strings ended together */
+                return((char *)s1);
+            }
+        }
+        if (*p2 == '\0') {
+            /* second string ended, a match */
+            break;
+        }
+       /* didn't find a match here, try starting at next character in s1 */
+        s1++;
+    }
+    return((char *)s1);
+}
 /* 
  * Apache stub function for the regex libraries regexec() to make sure the
  * whole regex(3) API is available through the Apache (exported) namespace.