[gupnp/wip/oldstable/24: 1/2] service: Validate host header




commit 05e964d48322ff23a65c6026d656e4494ace6ff9
Author: Jens Georg <mail jensge org>
Date:   Mon May 10 10:34:36 2021 +0200

    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

 libgupnp/gupnp-context-private.h |  3 +++
 libgupnp/gupnp-context.c         | 51 ++++++++++++++++++++++++++++++++++++++++
 libgupnp/gupnp-service.c         | 13 ++++++++++
 3 files changed, 67 insertions(+)
---
diff --git a/libgupnp/gupnp-context-private.h b/libgupnp/gupnp-context-private.h
index 801d679..5848d02 100644
--- a/libgupnp/gupnp-context-private.h
+++ b/libgupnp/gupnp-context-private.h
@@ -39,6 +39,9 @@ _gupnp_context_add_server_handler_with_data (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__ */
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index 1732bf4..0381474 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -1583,3 +1583,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;
+}
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 4235cab..50765f0 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -949,6 +949,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");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]