aboutsummaryrefslogtreecommitdiffstats
path: root/dirmngr/http.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dirmngr/http.c171
1 files changed, 169 insertions, 2 deletions
diff --git a/dirmngr/http.c b/dirmngr/http.c
index 6846107d0..d37faa87f 100644
--- a/dirmngr/http.c
+++ b/dirmngr/http.c
@@ -1347,6 +1347,8 @@ do_parse_uri (parsed_uri_t uri, int only_local_part,
uri->v6lit = 0;
uri->onion = 0;
uri->explicit_port = 0;
+ uri->off_host = 0;
+ uri->off_path = 0;
/* A quick validity check. */
if (strspn (p, VALID_URI_CHARS) != n)
@@ -1390,7 +1392,19 @@ do_parse_uri (parsed_uri_t uri, int only_local_part,
{
p += 2;
if ((p2 = strchr (p, '/')))
- *p2++ = 0;
+ {
+ if (p2 - uri->buffer > 10000)
+ return GPG_ERR_BAD_URI;
+ uri->off_path = p2 - uri->buffer;
+ *p2++ = 0;
+ }
+ else
+ {
+ n = (p - uri->buffer) + strlen (p);
+ if (n > 10000)
+ return GPG_ERR_BAD_URI;
+ uri->off_path = n;
+ }
/* Check for username/password encoding */
if ((p3 = strchr (p, '@')))
@@ -1409,11 +1423,19 @@ do_parse_uri (parsed_uri_t uri, int only_local_part,
*p3++ = '\0';
/* worst case, uri->host should have length 0, points to \0 */
uri->host = p + 1;
+ if (p - uri->buffer > 10000)
+ return GPG_ERR_BAD_URI;
+ uri->off_host = (p + 1) - uri->buffer;
uri->v6lit = 1;
p = p3;
}
else
- uri->host = p;
+ {
+ uri->host = p;
+ if (p - uri->buffer > 10000)
+ return GPG_ERR_BAD_URI;
+ uri->off_host = p - uri->buffer;
+ }
if ((p3 = strchr (p, ':')))
{
@@ -3490,3 +3512,148 @@ uri_query_lookup (parsed_uri_t uri, const char *key)
return NULL;
}
+
+
+/* Return true if both URI point to the same host. */
+static int
+same_host_p (parsed_uri_t a, parsed_uri_t b)
+{
+ return a->host && b->host && !ascii_strcasecmp (a->host, b->host);
+}
+
+
+/* Prepare a new URL for a HTTP redirect. INFO has flags controlling
+ * the operaion, STATUS_CODE is used for diagnostics, LOCATION is the
+ * value of the "Location" header, and R_URL reveives the new URL on
+ * success or NULL or error. Note that INFO->ORIG_URL is
+ * required. */
+gpg_error_t
+http_prepare_redirect (http_redir_info_t *info, unsigned int status_code,
+ const char *location, char **r_url)
+{
+ gpg_error_t err;
+ parsed_uri_t locuri;
+ parsed_uri_t origuri;
+ char *newurl;
+ char *p;
+
+ *r_url = NULL;
+
+ if (!info || !info->orig_url)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ if (!info->silent)
+ log_info (_("URL '%s' redirected to '%s' (%u)\n"),
+ info->orig_url, location? location:"[none]", status_code);
+
+ if (!info->redirects_left)
+ {
+ if (!info->silent)
+ log_error (_("too many redirections\n"));
+ return gpg_error (GPG_ERR_NO_DATA);
+ }
+ info->redirects_left--;
+
+ if (!location || !*location)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ err = http_parse_uri (&locuri, location, 0);
+ if (err)
+ return err;
+
+ /* Make sure that an onion address only redirects to another
+ * onion address, or that a https address only redirects to a
+ * https address. */
+ if (info->orig_onion && !locuri->onion)
+ {
+ http_release_parsed_uri (locuri);
+ return gpg_error (GPG_ERR_FORBIDDEN);
+ }
+ if (!info->allow_downgrade && info->orig_https && !locuri->use_tls)
+ {
+ http_release_parsed_uri (locuri);
+ return gpg_error (GPG_ERR_FORBIDDEN);
+ }
+
+ if (info->trust_location)
+ {
+ /* We trust the Location - return it verbatim. */
+ http_release_parsed_uri (locuri);
+ newurl = xtrystrdup (location);
+ if (!newurl)
+ {
+ err = gpg_error_from_syserror ();
+ http_release_parsed_uri (locuri);
+ return err;
+ }
+ }
+ else if ((err = http_parse_uri (&origuri, info->orig_url, 0)))
+ {
+ http_release_parsed_uri (locuri);
+ return err;
+ }
+ else if (same_host_p (origuri, locuri))
+ {
+ /* The host is the same and thus we can take the location
+ * verbatim. */
+ http_release_parsed_uri (origuri);
+ http_release_parsed_uri (locuri);
+ newurl = xtrystrdup (location);
+ if (!newurl)
+ {
+ err = gpg_error_from_syserror ();
+ http_release_parsed_uri (locuri);
+ return err;
+ }
+ }
+ else
+ {
+ /* We take only the host and port from the URL given in the
+ * Location. This limits the effects of redirection attacks by
+ * rogue hosts returning an URL to servers in the client's own
+ * network. We don't even include the userinfo because they
+ * should be considered similar to the path and query parts.
+ */
+ if (!(locuri->off_path - locuri->off_host))
+ {
+ http_release_parsed_uri (origuri);
+ http_release_parsed_uri (locuri);
+ return gpg_error (GPG_ERR_BAD_URI);
+ }
+ if (!(origuri->off_path - origuri->off_host))
+ {
+ http_release_parsed_uri (origuri);
+ http_release_parsed_uri (locuri);
+ return gpg_error (GPG_ERR_BAD_URI);
+ }
+
+ newurl = xtrymalloc (strlen (origuri->original)
+ + (locuri->off_path - locuri->off_host) + 1);
+ if (!newurl)
+ {
+ err = gpg_error_from_syserror ();
+ http_release_parsed_uri (origuri);
+ http_release_parsed_uri (locuri);
+ return err;
+ }
+ /* Build new URL from
+ * uriguri: scheme userinfo ---- ---- path rest
+ * locuri: ------ -------- host port ---- ----
+ */
+ p = newurl;
+ memcpy (p, origuri->original, origuri->off_host);
+ p += origuri->off_host;
+ memcpy (p, locuri->original + locuri->off_host,
+ (locuri->off_path - locuri->off_host));
+ p += locuri->off_path - locuri->off_host;
+ strcpy (p, origuri->original + origuri->off_path);
+
+ http_release_parsed_uri (origuri);
+ http_release_parsed_uri (locuri);
+ if (!info->silent)
+ log_info (_("redirection changed to '%s'\n"), newurl);
+ }
+
+ *r_url = newurl;
+ return 0;
+}