mirror of
https://github.com/curl/curl.git
synced 2026-08-05 13:26:21 +03:00
urlapi: same origin tests
Add new internal `curl_url_same_origin()` to check if a href has the same origin as a base URL. Add test cases in test1675 and use this in http2 push handling. Closes #21328
This commit is contained in:
parent
ce7174555d
commit
32a513e180
4 changed files with 126 additions and 13 deletions
25
lib/http2.c
25
lib/http2.c
|
|
@ -718,8 +718,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
|
|||
return second;
|
||||
}
|
||||
|
||||
static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
|
||||
struct curl_pushheaders *hp)
|
||||
static int set_transfer_url(struct Curl_easy *newhandle,
|
||||
struct curl_pushheaders *hp,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
const char *v;
|
||||
CURLUcode uc;
|
||||
|
|
@ -732,14 +733,6 @@ static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
|
|||
|
||||
v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
|
||||
if(v) {
|
||||
if(!via_ssl_conn) {
|
||||
/* PUSH over an insecure connection, accept only insecure schemes. */
|
||||
const struct Curl_scheme *scheme = Curl_get_scheme(v);
|
||||
if(!scheme || (scheme->flags & PROTOPT_SSL)) {
|
||||
rc = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
|
||||
if(uc) {
|
||||
rc = 1;
|
||||
|
|
@ -765,6 +758,13 @@ static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
|
|||
}
|
||||
}
|
||||
|
||||
/* We can only allow PUSH of resource from the same origin, e.g.
|
||||
* scheme + hostname + port */
|
||||
if(!Curl_url_same_origin(data->state.uh, u)) {
|
||||
rc = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
uc = curl_url_get(u, CURLUPART_URL, &url, 0);
|
||||
if(uc)
|
||||
rc = 4;
|
||||
|
|
@ -773,7 +773,7 @@ fail:
|
|||
if(rc)
|
||||
return rc;
|
||||
|
||||
Curl_bufref_set(&data->state.url, url, 0, curl_free);
|
||||
Curl_bufref_set(&newhandle->state.url, url, 0, curl_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -819,8 +819,7 @@ static int push_promise(struct Curl_cfilter *cf,
|
|||
heads.stream = stream;
|
||||
heads.frame = frame;
|
||||
|
||||
rv = set_transfer_url(newhandle,
|
||||
Curl_conn_is_ssl(cf->conn, cf->sockindex), &heads);
|
||||
rv = set_transfer_url(newhandle, &heads, data);
|
||||
if(rv) {
|
||||
CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d",
|
||||
frame->promised_stream_id, rv);
|
||||
|
|
|
|||
|
|
@ -63,4 +63,6 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace);
|
|||
#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE
|
||||
#define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS
|
||||
|
||||
bool Curl_url_same_origin(CURLU *base, CURLU *href);
|
||||
|
||||
#endif /* HEADER_CURL_URLAPI_INT_H */
|
||||
|
|
|
|||
33
lib/urlapi.c
33
lib/urlapi.c
|
|
@ -1991,3 +1991,36 @@ nomem:
|
|||
}
|
||||
return CURLUE_OK;
|
||||
}
|
||||
|
||||
bool Curl_url_same_origin(CURLU *base, CURLU *href)
|
||||
{
|
||||
const struct Curl_scheme *s = NULL;
|
||||
|
||||
/* base must be an absolute URL */
|
||||
if(!base->scheme || !base->host)
|
||||
return FALSE;
|
||||
if(href->scheme && !curl_strequal(base->scheme, href->scheme))
|
||||
return FALSE;
|
||||
if(href->host) {
|
||||
if(!curl_strequal(base->host, href->host))
|
||||
return FALSE;
|
||||
if(!curl_strequal(base->port, href->port)) {
|
||||
/* This may still match if only one has an explicit port
|
||||
* and it is the default for the scheme. */
|
||||
if(base->port && href->port)
|
||||
return FALSE;
|
||||
|
||||
s = Curl_get_scheme(base->scheme);
|
||||
if(!s) /* Cannot match default port for unknown scheme */
|
||||
return FALSE;
|
||||
|
||||
/* The port which is set must be the default one */
|
||||
if((base->port && (base->portnum != s->defport)) ||
|
||||
(href->port && (href->portnum != s->defport)))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(href->port) /* no host in href, then there must be no port */
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,5 +301,84 @@ static CURLcode test_unit1675(const char *arg)
|
|||
abort_if(fails, "parse_file tests failed");
|
||||
}
|
||||
|
||||
/* Test same origin check. For now, we can only do that when
|
||||
* schemes are supported by libcurl. */
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
{
|
||||
CURLU *base, *href;
|
||||
int fails = 0;
|
||||
unsigned int i;
|
||||
bool match;
|
||||
struct origin_test {
|
||||
const char *base;
|
||||
const char *scheme;
|
||||
const char *host;
|
||||
const char *port;
|
||||
const char *path;
|
||||
bool expect_match;
|
||||
};
|
||||
const struct origin_test tests[] = {
|
||||
{"http://host:123/x", "http", "host", "123", "/y", TRUE},
|
||||
{"http://host:123/x", NULL, "host", "123", "/y", TRUE},
|
||||
{"http://host:123/x", NULL, NULL, NULL, "/y", TRUE},
|
||||
{"http://host:80/x", "http", "host", "123", "/y", FALSE},
|
||||
{"http://host:80/x", "http", "host", NULL, "/y", TRUE},
|
||||
{"http://host/x", "http", "host", "80", "/y", TRUE},
|
||||
#ifdef USE_SSL
|
||||
{"http://host:123/x", "https", "host", "123", "/y", FALSE},
|
||||
{"https://host/x", "http", "host", "443", "/y", FALSE},
|
||||
{"https://host/x", "https", "host", "443", "/y", TRUE},
|
||||
#endif
|
||||
};
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(tests); i++) {
|
||||
CURLUcode uc;
|
||||
base = curl_url();
|
||||
href = curl_url();
|
||||
if(!base || !href) {
|
||||
curl_mfprintf(stderr, "%d: failed to allocate memory\n", i);
|
||||
fails++;
|
||||
goto loop_end;
|
||||
}
|
||||
uc = curl_url_set(base, CURLUPART_URL, tests[i].base, 0);
|
||||
if(uc) {
|
||||
curl_mfprintf(stderr, "failed to parse %d base %s -> %d\n", i,
|
||||
tests[i].base, uc);
|
||||
fails++;
|
||||
goto loop_end;
|
||||
}
|
||||
if(tests[i].scheme)
|
||||
uc = curl_url_set(href, CURLUPART_SCHEME, tests[i].scheme, 0);
|
||||
if(!uc && tests[i].host)
|
||||
uc = curl_url_set(href, CURLUPART_HOST, tests[i].host, 0);
|
||||
if(!uc && tests[i].port)
|
||||
uc = curl_url_set(href, CURLUPART_PORT, tests[i].port, 0);
|
||||
if(!uc && tests[i].path)
|
||||
uc = curl_url_set(href, CURLUPART_PATH, tests[i].path, 0);
|
||||
if(uc) {
|
||||
curl_mfprintf(stderr, "failed to parse %d href %s://%s:%s%s -> %d\n",
|
||||
i, tests[i].scheme, tests[i].host, tests[i].port,
|
||||
tests[i].path, uc);
|
||||
fails++;
|
||||
goto loop_end;
|
||||
}
|
||||
|
||||
match = Curl_url_same_origin(base, href);
|
||||
if(match != tests[i].expect_match) {
|
||||
curl_mfprintf(stderr, "ERROR: %d base %s and href %s://%s:%s%s %s\n",
|
||||
i, tests[i].base, tests[i].scheme, tests[i].host,
|
||||
tests[i].port, tests[i].path,
|
||||
match ? "matched" : "did not match");
|
||||
fails++;
|
||||
}
|
||||
|
||||
loop_end:
|
||||
curl_url_cleanup(base);
|
||||
curl_url_cleanup(href);
|
||||
}
|
||||
abort_if(fails, "same_origin tests failed");
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
UNITTEST_END_SIMPLE
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue