Commit 342e55ac by Edward Thomson

url: optionally allow off-site redirects

In redirect application logic, (optionally) allow off-site redirects.
parent c104a565
...@@ -315,6 +315,7 @@ static void remove_service_suffix( ...@@ -315,6 +315,7 @@ static void remove_service_suffix(
int git_net_url_apply_redirect( int git_net_url_apply_redirect(
git_net_url *url, git_net_url *url,
const char *redirect_location, const char *redirect_location,
bool allow_offsite,
const char *service_suffix) const char *service_suffix)
{ {
git_net_url tmp = GIT_NET_URL_INIT; git_net_url tmp = GIT_NET_URL_INIT;
...@@ -339,8 +340,8 @@ int git_net_url_apply_redirect( ...@@ -339,8 +340,8 @@ int git_net_url_apply_redirect(
/* Validate that this is a legal redirection */ /* Validate that this is a legal redirection */
if (original->scheme && if (original->scheme &&
strcmp(original->scheme, tmp.scheme) != 0 && strcmp(original->scheme, tmp.scheme) != 0 &&
strcmp(tmp.scheme, "https") != 0) { strcmp(tmp.scheme, "https") != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->scheme, tmp.scheme); original->scheme, tmp.scheme);
...@@ -349,6 +350,7 @@ int git_net_url_apply_redirect( ...@@ -349,6 +350,7 @@ int git_net_url_apply_redirect(
} }
if (original->host && if (original->host &&
!allow_offsite &&
git__strcasecmp(original->host, tmp.host) != 0) { git__strcasecmp(original->host, tmp.host) != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->host, tmp.host); original->host, tmp.host);
......
...@@ -46,6 +46,7 @@ extern bool git_net_url_is_ipv6(git_net_url *url); ...@@ -46,6 +46,7 @@ extern bool git_net_url_is_ipv6(git_net_url *url);
extern int git_net_url_apply_redirect( extern int git_net_url_apply_redirect(
git_net_url *url, git_net_url *url,
const char *redirect_location, const char *redirect_location,
bool allow_offsite,
const char *service_suffix); const char *service_suffix);
/** Swaps the contents of one URL for another. */ /** Swaps the contents of one URL for another. */
......
...@@ -233,7 +233,7 @@ static int handle_response( ...@@ -233,7 +233,7 @@ static int handle_response(
return -1; return -1;
} }
if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) { if (git_net_url_apply_redirect(&transport->server.url, response->location, false, stream->service->url) < 0) {
return -1; return -1;
} }
......
...@@ -1190,7 +1190,7 @@ replay: ...@@ -1190,7 +1190,7 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) { if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */ /* Upgrade to secure connection; disconnect and start over */
if (git_net_url_apply_redirect(&t->server.url, location8, s->service_url) < 0) { if (git_net_url_apply_redirect(&t->server.url, location8, false, s->service_url) < 0) {
git__free(location8); git__free(location8);
return -1; return -1;
} }
......
...@@ -17,9 +17,9 @@ void test_network_url_redirect__cleanup(void) ...@@ -17,9 +17,9 @@ void test_network_url_redirect__cleanup(void)
void test_network_url_redirect__redirect_http(void) void test_network_url_redirect__redirect_http(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"http://example.com/foo/bar/baz")); "http://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"http://example.com/foo/bar/baz", "bar/baz")); "http://example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "http"); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80"); cl_assert_equal_s(conndata.port, "80");
...@@ -31,9 +31,9 @@ void test_network_url_redirect__redirect_http(void) ...@@ -31,9 +31,9 @@ void test_network_url_redirect__redirect_http(void)
void test_network_url_redirect__redirect_ssl(void) void test_network_url_redirect__redirect_ssl(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz")); "https://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "bar/baz")); "https://example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443"); cl_assert_equal_s(conndata.port, "443");
...@@ -45,9 +45,9 @@ void test_network_url_redirect__redirect_ssl(void) ...@@ -45,9 +45,9 @@ void test_network_url_redirect__redirect_ssl(void)
void test_network_url_redirect__redirect_leaves_root_path(void) void test_network_url_redirect__redirect_leaves_root_path(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz")); "https://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "/foo/bar/baz")); "https://example.com/foo/bar/baz", false, "/foo/bar/baz"));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443"); cl_assert_equal_s(conndata.port, "443");
...@@ -59,9 +59,9 @@ void test_network_url_redirect__redirect_leaves_root_path(void) ...@@ -59,9 +59,9 @@ void test_network_url_redirect__redirect_leaves_root_path(void)
void test_network_url_redirect__redirect_encoded_username_password(void) void test_network_url_redirect__redirect_encoded_username_password(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz")); "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz")); "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443"); cl_assert_equal_s(conndata.port, "443");
...@@ -70,27 +70,42 @@ void test_network_url_redirect__redirect_encoded_username_password(void) ...@@ -70,27 +70,42 @@ void test_network_url_redirect__redirect_encoded_username_password(void)
cl_assert_equal_s(conndata.password, "pass@word%zyx%v"); cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
} }
void test_network_url_redirect__redirect_cross_host_allowed(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://bar.com/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://foo.com/bar/baz", true, NULL));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/bar/baz");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_url_redirect__redirect_cross_host_denied(void) void test_network_url_redirect__redirect_cross_host_denied(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz")); cl_git_pass(git_net_url_parse(&conndata,
"https://bar.com/bar/baz"));
cl_git_fail_with(git_net_url_apply_redirect(&conndata, cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"https://foo.com/bar/baz", NULL), "https://foo.com/bar/baz", false, NULL), -1);
-1);
} }
void test_network_url_redirect__redirect_http_downgrade_denied(void) void test_network_url_redirect__redirect_http_downgrade_denied(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz")); cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/baz"));
cl_git_fail_with(git_net_url_apply_redirect(&conndata, cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"http://foo.com/bar/baz", NULL), "http://foo.com/bar/baz", true, NULL), -1);
-1);
} }
void test_network_url_redirect__redirect_relative(void) void test_network_url_redirect__redirect_relative(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff")); cl_git_pass(git_net_url_parse(&conndata,
"http://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL)); "/zap/baz/biff?bam", true, NULL));
cl_assert_equal_s(conndata.scheme, "http"); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "foo.com"); cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "80"); cl_assert_equal_s(conndata.port, "80");
...@@ -101,9 +116,10 @@ void test_network_url_redirect__redirect_relative(void) ...@@ -101,9 +116,10 @@ void test_network_url_redirect__redirect_relative(void)
void test_network_url_redirect__redirect_relative_ssl(void) void test_network_url_redirect__redirect_relative_ssl(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff")); cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL)); "/zap/baz/biff?bam", true, NULL));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com"); cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443"); cl_assert_equal_s(conndata.port, "443");
...@@ -114,16 +130,18 @@ void test_network_url_redirect__redirect_relative_ssl(void) ...@@ -114,16 +130,18 @@ void test_network_url_redirect__redirect_relative_ssl(void)
void test_network_url_redirect__service_query_no_query_params_in_location(void) void test_network_url_redirect__service_query_no_query_params_in_location(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack")); cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs", "/info/refs?service=git-upload-pack")); "/baz/info/refs", true, "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz"); cl_assert_equal_s(conndata.path, "/baz");
} }
void test_network_url_redirect__service_query_with_query_params_in_location(void) void test_network_url_redirect__service_query_with_query_params_in_location(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack")); cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_apply_redirect(&conndata, cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack")); "/baz/info/refs?service=git-upload-pack", true, "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz"); cl_assert_equal_s(conndata.path, "/baz");
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment