Commit ca6ec9dc authored by Jens Georg's avatar Jens Georg
Browse files

service: Validate host header

Make sure that the host header matches the ip:port of the context.

This is in line with UDA (Host header is required and must match the
location url) and DLNA 7.2.24.1 (All communication has to use ip
addresses and not names)

Prevents DNS rebinding attacs against agains UPnP services
parent 4a68246f
......@@ -43,6 +43,9 @@ gupnp_context_rewrite_uri_to_uri (GUPnPContext *context,
G_GNUC_INTERNAL gboolean
gupnp_context_ip_is_ours (GUPnPContext *context, const char *address);
G_GNUC_INTERNAL gboolean
gupnp_context_validate_host_header (GUPnPContext *context, const char *host);
G_END_DECLS
#endif /* GUPNP_CONTEXT_PRIVATE_H */
......@@ -1709,3 +1709,54 @@ out:
return retval;
}
gboolean
gupnp_context_validate_host_header (GUPnPContext *context,
const char *host_header)
{
gboolean retval = FALSE;
// Be lazy and let GUri do the heavy lifting here, such as stripping the
// [] from v6 addresses, splitting of the port etc.
char *uri_from_host = g_strconcat ("http://", host_header, NULL);
char *host = NULL;
int port = 0;
GError *error = NULL;
g_uri_split_network (uri_from_host,
G_URI_FLAGS_NONE,
NULL,
&host,
&port,
&error);
if (error != NULL) {
g_debug ("Failed to parse HOST header from request: %s",
error->message);
goto out;
}
const char *host_ip = gssdp_client_get_host_ip (GSSDP_CLIENT (context));
gint context_port = gupnp_context_get_port (context);
if (!g_str_equal (host, host_ip)) {
g_debug ("Mismatch between host header and host IP (%s, "
"expected: %s)",
host,
host_ip);
}
if (port != context_port) {
g_debug ("Mismatch between host header and host port (%d, "
"expected %d)",
port,
context_port);
}
retval = g_str_equal (host, host_ip) && port == context_port;
out:
g_clear_error (&error);
g_free (uri_from_host);
return retval;
}
......@@ -954,6 +954,19 @@ control_server_handler (SoupServer *server,
context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
const char *host_header =
soup_message_headers_get_one (msg->request_headers, "Host");
if (!gupnp_context_validate_host_header (context, host_header)) {
g_warning ("Host header mismatch, expected %s:%d, got %s",
gssdp_client_get_host_ip (GSSDP_CLIENT (context)),
gupnp_context_get_port (context),
host_header);
soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
return;
}
/* Get action name */
soap_action = soup_message_headers_get_one (msg->request_headers,
"SOAPAction");
......
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