mirror of
https://github.com/apache/httpd.git
synced 2025-08-03 16:33:59 +00:00
Add ap_normalize_path() to replace ap_getparents() (with options).
include/httpd.h: Declare ap_normalize_path() and flags. AP_NORMALIZE_ALLOW_RELATIVE: Don't require that the path be absolute as per RFC 7230. This is needed for lookup subrequests. AP_NORMALIZE_NOT_ABOVE_ROOT: Check that directory traversal ("..") don't go above root, or initial directory with relative paths. AP_NORMALIZE_DECODE_UNRESERVED: Decode unreserved characters (like '.') first since they have the same semantics encoded and decoded. AP_NORMALIZE_MERGE_SLASHES: Merge multiple slahes into a single one. AP_NORMALIZE_DROP_PARAMETERS: Ignore path parameters (";foo=bar"). Not used by httpd but since ap_normalize_path() is taken from mod_jk's jk_servlet_normalize() it can allow them to use the upstream version now. server/util.c: Implement ap_normalize_path(). modules/dav/main/util.c: Replace call to ap_getparents() using ap_normalize_path() with AP_NORMALIZE_DECODE_UNRESERVED flag since the path comes from an obsolute URL (thus potentially %-encoded). modules/generators/mod_autoindex.c: Replace call to ap_getparents() using ap_normalize_path() with AP_NORMALIZE_ALLOW_RELATIVE and AP_NORMALIZE_NOT_ABOVE_ROOT flags to be consistent with original code. include/ap_mmn.h: MINOR bump for ap_normalize_path(). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1879074 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@ -632,6 +632,7 @@
|
||||
* 20200420.1 (2.5.1-dev) Add ap_filter_adopt_brigade()
|
||||
* 20200420.2 (2.5.1-dev) Add ap_proxy_worker_can_upgrade()
|
||||
* 20200420.3 (2.5.1-dev) Add ap_parse_strict_length()
|
||||
* 20200420.4 (2.5.1-dev) Add ap_normalize_path()
|
||||
*/
|
||||
|
||||
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
|
||||
@ -639,7 +640,7 @@
|
||||
#ifndef MODULE_MAGIC_NUMBER_MAJOR
|
||||
#define MODULE_MAGIC_NUMBER_MAJOR 20200420
|
||||
#endif
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */
|
||||
|
||||
/**
|
||||
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
|
||||
|
@ -1779,6 +1779,21 @@ AP_DECLARE(void) ap_no2slash(char *name)
|
||||
AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
|
||||
AP_FN_ATTR_NONNULL_ALL;
|
||||
|
||||
#define AP_NORMALIZE_ALLOW_RELATIVE (1u << 0)
|
||||
#define AP_NORMALIZE_NOT_ABOVE_ROOT (1u << 1)
|
||||
#define AP_NORMALIZE_DECODE_UNRESERVED (1u << 2)
|
||||
#define AP_NORMALIZE_MERGE_SLASHES (1u << 3)
|
||||
#define AP_NORMALIZE_DROP_PARAMETERS (1u << 4)
|
||||
|
||||
/**
|
||||
* Remove all ////, /./ and /xx/../ substrings from a path, and more
|
||||
* depending on passed in flags.
|
||||
* @param path The path to normalize
|
||||
* @param flags bitmask of AP_NORMALIZE_* flags
|
||||
* @return non-zero on success
|
||||
*/
|
||||
AP_DECLARE(int) ap_normalize_path(char *path, unsigned int flags)
|
||||
AP_FN_ATTR_NONNULL((1));
|
||||
|
||||
/**
|
||||
* Remove all ./ and xx/../ substrings from a file name. Also remove
|
||||
|
@ -664,7 +664,12 @@ static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
|
||||
/* note that parsed_uri.path is allocated; we can trash it */
|
||||
|
||||
/* clean up the URI a bit */
|
||||
ap_getparents(parsed_uri.path);
|
||||
if (!ap_normalize_path(parsed_uri.path,
|
||||
AP_NORMALIZE_DECODE_UNRESERVED)) {
|
||||
return dav_new_error(r->pool, HTTP_BAD_REQUEST,
|
||||
DAV_ERR_IF_TAGGED, rv,
|
||||
"Invalid URI path tagged If-header.");
|
||||
}
|
||||
|
||||
/* the resources we will compare to have unencoded paths */
|
||||
if (ap_unescape_url(parsed_uri.path) != OK) {
|
||||
|
@ -1266,8 +1266,9 @@ static struct ent *make_parent_entry(apr_int32_t autoindex_opts,
|
||||
if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) {
|
||||
return (NULL);
|
||||
}
|
||||
ap_getparents(p->name);
|
||||
if (!*p->name) {
|
||||
if (!ap_normalize_path(p->name, AP_NORMALIZE_ALLOW_RELATIVE |
|
||||
AP_NORMALIZE_NOT_ABOVE_ROOT)
|
||||
|| p->name[0] == '\0') {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
112
server/util.c
112
server/util.c
@ -77,7 +77,7 @@
|
||||
#include "test_char.h"
|
||||
|
||||
/* Win32/NetWare/OS2 need to check for both forward and back slashes
|
||||
* in ap_getparents() and ap_escape_url.
|
||||
* in ap_normalize_path() and ap_escape_url().
|
||||
*/
|
||||
#ifdef CASE_BLIND_FILESYSTEM
|
||||
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
|
||||
@ -492,6 +492,116 @@ AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward declare */
|
||||
static char x2c(const char *what);
|
||||
|
||||
#define IS_SLASH_OR_NUL(s) (s == '\0' || IS_SLASH(s))
|
||||
|
||||
/*
|
||||
* Inspired by mod_jk's jk_servlet_normalize().
|
||||
*/
|
||||
AP_DECLARE(int) ap_normalize_path(char *path, unsigned int flags)
|
||||
{
|
||||
int ret = 1;
|
||||
apr_size_t l = 1, w = 1;
|
||||
|
||||
if (!IS_SLASH(path[0])) {
|
||||
/* Besides "OPTIONS *", a request-target should start with '/'
|
||||
* per RFC 7230 section 5.3, so anything else is invalid.
|
||||
*/
|
||||
if (path[0] == '*' && path[1] == '\0') {
|
||||
return 1;
|
||||
}
|
||||
/* However, AP_NORMALIZE_ALLOW_RELATIVE can be used to bypass
|
||||
* this restriction (e.g. for subrequest file lookups).
|
||||
*/
|
||||
if (!(flags & AP_NORMALIZE_ALLOW_RELATIVE) || path[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = w = 0;
|
||||
}
|
||||
|
||||
while (path[l] != '\0') {
|
||||
/* RFC-3986 section 2.3:
|
||||
* For consistency, percent-encoded octets in the ranges of
|
||||
* ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
|
||||
* period (%2E), underscore (%5F), or tilde (%7E) should [...]
|
||||
* be decoded to their corresponding unreserved characters by
|
||||
* URI normalizers.
|
||||
*/
|
||||
if ((flags & AP_NORMALIZE_DECODE_UNRESERVED)
|
||||
&& path[l] == '%' && apr_isxdigit(path[l + 1])
|
||||
&& apr_isxdigit(path[l + 2])) {
|
||||
const char c = x2c(&path[l + 1]);
|
||||
if (apr_isalnum(c) || (c && strchr("-._~", c))) {
|
||||
/* Replace last char and fall through as the current
|
||||
* read position */
|
||||
l += 2;
|
||||
path[l] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & AP_NORMALIZE_DROP_PARAMETERS) && path[l] == ';') {
|
||||
do {
|
||||
l++;
|
||||
} while (!IS_SLASH_OR_NUL(path[l]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w == 0 || IS_SLASH(path[w - 1])) {
|
||||
/* Collapse ///// sequences to / */
|
||||
if ((flags & AP_NORMALIZE_MERGE_SLASHES) && IS_SLASH(path[l])) {
|
||||
do {
|
||||
l++;
|
||||
} while (IS_SLASH(path[l]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path[l] == '.') {
|
||||
/* Remove /./ segments */
|
||||
if (IS_SLASH_OR_NUL(path[l + 1])) {
|
||||
l++;
|
||||
if (path[l]) {
|
||||
l++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove /xx/../ segments */
|
||||
if (path[l + 1] == '.' && IS_SLASH_OR_NUL(path[l + 2])) {
|
||||
/* Wind w back to remove the previous segment */
|
||||
if (w > 1) {
|
||||
do {
|
||||
w--;
|
||||
} while (w && !IS_SLASH(path[w - 1]));
|
||||
}
|
||||
else {
|
||||
/* Already at root, ignore and return a failure
|
||||
* if asked to.
|
||||
*/
|
||||
if (flags & AP_NORMALIZE_NOT_ABOVE_ROOT) {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move l forward to the next segment */
|
||||
l += 2;
|
||||
if (path[l]) {
|
||||
l++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path[w++] = path[l++];
|
||||
}
|
||||
path[w] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse .. so we don't compromise security
|
||||
*/
|
||||
|
Reference in New Issue
Block a user