From: Eric Covener Date: Mon, 24 Jun 2024 17:52:15 +0000 (+0000) Subject: Merge r1918549 from trunk: X-Git-Tag: 2.4.60-rc1-candidate~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6376801292e7f3aedaf75645481b7b996c682842;p=thirdparty%2Fapache%2Fhttpd.git Merge r1918549 from trunk: add UNCList directive on Windows git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1918558 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml index b18b5c68697..659d92405cf 100644 --- a/docs/manual/mod/core.xml +++ b/docs/manual/mod/core.xml @@ -5171,6 +5171,33 @@ hostname or IP address different sections are combined when a request is received + +UNCList +Controls what UNC host names can be accessed by the server + +UNCListhostname ... +unset +server config + +Added in 2.4.60, Windows only. + + +

During request processing, requests to access a filesystem path that + resolves to a UNC path will fail unless the hostname in the UNC path + has been specified by this directive. The intent is to limit access to + paths derived from untrusted inputs.

+ +Security +UNC paths accessed outside of request processing, such as during startup, +are not checked against the hosts configured with this directive.

+
+
+
+ + +UNCList + + MergeTrailers Determines whether trailers are merged into headers diff --git a/include/http_core.h b/include/http_core.h index 948034f5f2a..9fb9f5135c3 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -756,6 +756,9 @@ typedef struct { apr_size_t flush_max_threshold; apr_int32_t flush_max_pipelined; unsigned int strict_host_check; +#ifdef WIN32 + apr_array_header_t *unc_list; +#endif } core_server_config; /* for AddOutputFiltersByType in core.c */ diff --git a/include/httpd.h b/include/httpd.h index 799cf9705e9..826c46ef591 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -2656,6 +2656,31 @@ AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path, */ AP_DECLARE(int) ap_is_chunked(apr_pool_t *p, const char *line); + +/** + * apr_filepath_merge with an allow-list + * Merge additional file path onto the previously processed rootpath + * @param newpath the merged paths returned + * @param rootpath the root file path (NULL uses the current working path) + * @param addpath the path to add to the root path + * @param flags the desired APR_FILEPATH_ rules to apply when merging + * @param p the pool to allocate the new path string from + * @remark if the flag APR_FILEPATH_TRUENAME is given, and the addpath + * contains wildcard characters ('*', '?') on platforms that don't support + * such characters within filenames, the paths will be merged, but the + * result code will be APR_EPATHWILD, and all further segments will not + * reflect the true filenames including the wildcard and following segments. + */ +AP_DECLARE(apr_status_t) ap_filepath_merge(char **newpath, + const char *rootpath, + const char *addpath, + apr_int32_t flags, + apr_pool_t *p); + +#ifdef WIN32 +#define apr_filepath_merge ap_filepath_merge +#endif + #ifdef __cplusplus } #endif diff --git a/server/core.c b/server/core.c index e5e059e6ede..f511bba4897 100644 --- a/server/core.c +++ b/server/core.c @@ -4426,6 +4426,25 @@ static const char *set_merge_trailers(cmd_parms *cmd, void *dummy, int arg) return NULL; } +#ifdef WIN32 +static const char *set_unc_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) +{ + core_server_config *sconf = ap_get_core_module_config(cmd->server->module_config); + int i; + const char *err; + + if ((err = ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST)) != NULL) + return err; + + sconf->unc_list = apr_array_make(cmd->pool, argc, sizeof(char *)); + + for (i = 0; i < argc; i++) { + *(char **)apr_array_push(sconf->unc_list) = apr_pstrdup(cmd->pool, argv[i]); + } + + return NULL; +} +#endif /* Note --- ErrorDocument will now work from .htaccess files. * The AllowOverride of Fileinfo allows webmasters to turn it off */ @@ -4527,6 +4546,10 @@ AP_INIT_TAKE1("FlushMaxThreshold", set_flush_max_threshold, NULL, RSRC_CONF, AP_INIT_TAKE1("FlushMaxPipelined", set_flush_max_pipelined, NULL, RSRC_CONF, "Maximum number of pipelined responses (pending) above which they are " "flushed to the network"), +#ifdef WIN32 +AP_INIT_TAKE_ARGV("UNCList", set_unc_list, NULL, RSRC_CONF, + "Controls what UNC hosts may be looked up"), +#endif /* Old server config file commands */ @@ -5632,6 +5655,84 @@ AP_CORE_DECLARE(apr_status_t) ap_get_pollfd_from_conn(conn_rec *c, return ap_run_get_pollfd_from_conn(c, pfd, ptimeout); } +#ifdef WIN32 +static apr_status_t check_unc(const char *path, apr_pool_t *p) +{ + int i; + char *s, *teststring; + apr_status_t rv = APR_EACCES; + core_server_config *sconf = NULL; + + if (!ap_server_conf) { + return APR_SUCCESS; /* this early, if we have a UNC, it's specified by an admin */ + } + + if (!path || (path != ap_strstr_c(path, "\\\\") && + path != ap_strstr_c(path, "//"))) { + return APR_SUCCESS; /* not a UNC */ + } + + sconf = ap_get_core_module_config(ap_server_conf->module_config); + s = teststring = apr_pstrdup(p, path); + *s++ = '/'; + *s++ = '/'; + + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, + "ap_filepath_merge: check converted path %s allowed %d", + teststring, + sconf->unc_list ? sconf->unc_list->nelts : 0); + + for (i = 0; sconf->unc_list && i < sconf->unc_list->nelts; i++) { + char *configured_unc = ((char **)sconf->unc_list->elts)[i]; + apr_uri_t uri; + if (APR_SUCCESS == apr_uri_parse(p, teststring, &uri) && + (uri.hostinfo == NULL || + !ap_cstr_casecmp(uri.hostinfo, configured_unc))) { + rv = APR_SUCCESS; + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, + "ap_filepath_merge: match %s %s", + uri.hostinfo, configured_unc); + break; + } + else { + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, + "ap_filepath_merge: no match %s %s", uri.hostinfo, + configured_unc); + } + } + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(10504) + "ap_filepath_merge: UNC path %s not allowed by UNCList", teststring); + } + + return rv; +} +#endif + +AP_DECLARE(apr_status_t) ap_filepath_merge(char **newpath, + const char *rootpath, + const char *addpath, + apr_int32_t flags, + apr_pool_t *p) +{ +#ifdef WIN32 + apr_status_t rv; + + if (APR_SUCCESS != (rv = check_unc(rootpath, p))) { + return rv; + } + if (APR_SUCCESS != (rv = check_unc(addpath, p))) { + return rv; + } +#undef apr_filepath_merge +#endif + return apr_filepath_merge(newpath, rootpath, addpath, flags, p); +#ifdef WIN32 +#define apr_filepath_merge ap_filepath_merge +#endif +} + + static void register_hooks(apr_pool_t *p) { errorlog_hash = apr_hash_make(p);