Blame SOURCES/0001-service-Validate-host-header.patch

7a5e00
From 1d399272672308b05c964aedd61c11a2d4e2a3ac Mon Sep 17 00:00:00 2001
7a5e00
From: Jens Georg <mail@jensge.org>
7a5e00
Date: Mon, 10 May 2021 10:34:36 +0200
7a5e00
Subject: [PATCH 1/3] service: Validate host header
7a5e00
7a5e00
Make sure that the host header matches the ip:port of the context.
7a5e00
7a5e00
This is in line with UDA (Host header is required and must match the
7a5e00
location url) and DLNA 7.2.24.1 (All communication has to use ip
7a5e00
addresses and not names)
7a5e00
7a5e00
Prevents DNS rebinding attacs against agains UPnP services
7a5e00
7a5e00
 Conflicts:
7a5e00
	libgupnp/gupnp-context-private.h
7a5e00
	libgupnp/gupnp-context.c
7a5e00
---
7a5e00
 libgupnp/gupnp-context-private.h |  3 ++
7a5e00
 libgupnp/gupnp-context.c         | 51 ++++++++++++++++++++++++++++++++
7a5e00
 libgupnp/gupnp-service.c         | 13 ++++++++
7a5e00
 3 files changed, 67 insertions(+)
7a5e00
7a5e00
diff --git a/libgupnp/gupnp-context-private.h b/libgupnp/gupnp-context-private.h
7a5e00
index c088563..f0fe52e 100644
7a5e00
--- a/libgupnp/gupnp-context-private.h
7a5e00
+++ b/libgupnp/gupnp-context-private.h
7a5e00
@@ -36,6 +36,9 @@ _gupnp_context_add_server_handler_with_data (GUPnPContext *context,
7a5e00
                                              const char *path,
7a5e00
                                              AclServerHandler *data);
7a5e00
 
7a5e00
+G_GNUC_INTERNAL gboolean
7a5e00
+gupnp_context_validate_host_header (GUPnPContext *context, const char *host);
7a5e00
+
7a5e00
 G_END_DECLS
7a5e00
 
7a5e00
 #endif /* __GUPNP_CONTEXT_PRIVATE_H__ */
7a5e00
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
7a5e00
index 641c59a..7f13f47 100644
7a5e00
--- a/libgupnp/gupnp-context.c
7a5e00
+++ b/libgupnp/gupnp-context.c
7a5e00
@@ -1558,3 +1558,54 @@ gupnp_context_remove_server_handler (GUPnPContext *context, const char *path)
7a5e00
 
7a5e00
         soup_server_remove_handler (context->priv->server, path);
7a5e00
 }
7a5e00
+
7a5e00
+gboolean
7a5e00
+gupnp_context_validate_host_header (GUPnPContext *context,
7a5e00
+                                    const char *host_header)
7a5e00
+{
7a5e00
+        gboolean retval = FALSE;
7a5e00
+        // Be lazy and let GUri do the heavy lifting here, such as stripping the
7a5e00
+        // [] from v6 addresses, splitting of the port etc.
7a5e00
+        char *uri_from_host = g_strconcat ("http://", host_header, NULL);
7a5e00
+
7a5e00
+        char *host = NULL;
7a5e00
+        int port = 0;
7a5e00
+        GError *error = NULL;
7a5e00
+
7a5e00
+        g_uri_split_network (uri_from_host,
7a5e00
+                             G_URI_FLAGS_NONE,
7a5e00
+                             NULL,
7a5e00
+                             &host,
7a5e00
+                             &port,
7a5e00
+                             &error);
7a5e00
+
7a5e00
+        if (error != NULL) {
7a5e00
+                g_debug ("Failed to parse HOST header from request: %s",
7a5e00
+                         error->message);
7a5e00
+                goto out;
7a5e00
+        }
7a5e00
+
7a5e00
+        const char *host_ip = gssdp_client_get_host_ip (GSSDP_CLIENT (context));
7a5e00
+        gint context_port = gupnp_context_get_port (context);
7a5e00
+
7a5e00
+        if (!g_str_equal (host, host_ip)) {
7a5e00
+                g_debug ("Mismatch between host header and host IP (%s, "
7a5e00
+                         "expected: %s)",
7a5e00
+                         host,
7a5e00
+                         host_ip);
7a5e00
+        }
7a5e00
+
7a5e00
+        if (port != context_port) {
7a5e00
+                g_debug ("Mismatch between host header and host port (%d, "
7a5e00
+                         "expected %d)",
7a5e00
+                         port,
7a5e00
+                         context_port);
7a5e00
+        }
7a5e00
+
7a5e00
+        retval = g_str_equal (host, host_ip) && port == context_port;
7a5e00
+
7a5e00
+out:
7a5e00
+        g_clear_error (&error);
7a5e00
+        g_free (uri_from_host);
7a5e00
+        return retval;
7a5e00
+}
7a5e00
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
7a5e00
index 0417852..1c7896b 100644
7a5e00
--- a/libgupnp/gupnp-service.c
7a5e00
+++ b/libgupnp/gupnp-service.c
7a5e00
@@ -949,6 +949,19 @@ control_server_handler (SoupServer                      *server,
7a5e00
 
7a5e00
         context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
7a5e00
 
7a5e00
+        const char *host_header =
7a5e00
+                soup_message_headers_get_one (msg->request_headers, "Host");
7a5e00
+
7a5e00
+        if (!gupnp_context_validate_host_header (context, host_header)) {
7a5e00
+                g_warning ("Host header mismatch, expected %s:%d, got %s",
7a5e00
+                           gssdp_client_get_host_ip (GSSDP_CLIENT (context)),
7a5e00
+                           gupnp_context_get_port (context),
7a5e00
+                           host_header);
7a5e00
+
7a5e00
+                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
7a5e00
+                return;
7a5e00
+        }
7a5e00
+
7a5e00
         /* Get action name */
7a5e00
         soap_action = soup_message_headers_get_one (msg->request_headers,
7a5e00
                                                     "SOAPAction");
7a5e00
-- 
7a5e00
2.31.1
7a5e00