From: Jim Jagielski Date: Mon, 13 Mar 2000 20:27:29 +0000 (+0000) Subject: Backport the CSS security fixes to Apache 2.0a. Or is that forward X-Git-Tag: APACHE_2_0_ALPHA_2~96 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=70ef1e69d59b6a1db04870c4f0edeec730cd5ea4;p=apache Backport the CSS security fixes to Apache 2.0a. Or is that forward 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 --- diff --git a/include/http_core.h b/include/http_core.h index b3364756f3..efdcc1c51e 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -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 */ diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index 344e8ccd9d..ab0f0301e4 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -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; } diff --git a/modules/generators/mod_autoindex.c b/modules/generators/mod_autoindex.c index 5212b629ba..f6a76f13ea 100644 --- a/modules/generators/mod_autoindex.c +++ b/modules/generators/mod_autoindex.c @@ -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; diff --git a/modules/http/http_core.c b/modules/http/http_core.c index 126920f95c..0bb8447e2b 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -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 */ diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 7011be758d..e345e57eb1 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -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.

\n" - "

\n", field, "
\n", NULL)); + "
\n", ap_escape_html(r->pool, field), "
\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.

\n" - "

\n", copy, "
\n", NULL)); + "
\n", ap_escape_html(r->pool, copy), "
\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)); diff --git a/modules/loggers/mod_log_config.c b/modules/loggers/mod_log_config.c index 1231f4921e..067ca81709 100644 --- a/modules/loggers/mod_log_config.c +++ b/modules/loggers/mod_log_config.c @@ -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; diff --git a/modules/mappers/mod_actions.c b/modules/mappers/mod_actions.c index 4f2b60aa20..ec38d8cf71 100644 --- a/modules/mappers/mod_actions.c +++ b/modules/mappers/mod_actions.c @@ -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; diff --git a/modules/mappers/mod_speling.c b/modules/mappers/mod_speling.c index 0c8b1a87bc..48d12ea8b9 100644 --- a/modules/mappers/mod_speling.c +++ b/modules/mappers/mod_speling.c @@ -467,7 +467,7 @@ static int check_speling(request_rec *r) *(const char **)ap_push_array(t) = "The document name you requested ("; - *(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) = ") 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) = "
  • "; - *(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) = " ("; *(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 " "referring page " "about the broken link.\n"; } diff --git a/modules/metadata/mod_expires.c b/modules/metadata/mod_expires.c index 865c005326..7f2a38e996 100644 --- a/modules/metadata/mod_expires.c +++ b/modules/metadata/mod_expires.c @@ -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? */ diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 97b155a385..41d6b7a41e 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -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 " - "uri, "\">", - r->method, " ", r->uri, ".

    \n" - "Reason: ", message, "", NULL)); - - /* Allow the "error-notes" string to be printed by ap_send_error_response() */ + "pool, r->uri), + "\">", ap_escape_html(r->pool, r->method), + " ", + ap_escape_html(r->pool, r->uri), ".

    \n" + "Reason: ", + ap_escape_html(r->pool, message), + "", 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); diff --git a/server/log.c b/server/log.c index 244cf611d4..d1f0cdc278 100644 --- a/server/log.c +++ b/server/log.c @@ -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); } diff --git a/server/util.c b/server/util.c index dd0a11c5f1..063359f01d 100644 --- a/server/util.c +++ b/server/util.c @@ -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.