From: Eric Covener Date: Mon, 24 Jun 2024 17:19:44 +0000 (+0000) Subject: add UNCList directive on Windows X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=12542a80324b69ad6a1a489e1b697398551a5fe0;p=thirdparty%2Fapache%2Fhttpd.git add UNCList directive on Windows git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918549 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml index f50e4a812d1..52e2020b859 100644 --- a/docs/manual/mod/core.xml +++ b/docs/manual/mod/core.xml @@ -5318,6 +5318,33 @@ recognized methods to modules.

+ +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 7c4f166e097..6a5744891f6 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -771,6 +771,9 @@ typedef struct { apr_int32_t flush_max_pipelined; unsigned int strict_host_check; unsigned int merge_slashes; +#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 9782a8ad824..6b7e10223e3 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -2847,6 +2847,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 d4d868d7d8f..35853824603 100644 --- a/server/core.c +++ b/server/core.c @@ -4678,6 +4678,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 */ @@ -4781,6 +4800,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 */ @@ -6001,6 +6024,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);