Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP4:GA
gupnp
gupnp-validate-host-header.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gupnp-validate-host-header.patch of Package gupnp
From 05e964d48322ff23a65c6026d656e4494ace6ff9 Mon Sep 17 00:00:00 2001 From: Jens Georg <mail@jensge.org> Date: Mon, 10 May 2021 10:34:36 +0200 Subject: [PATCH] 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 Backported by Mike Gorse <mgorse@suse.com> --- diff -urp gupnp-1.0.2.orig/libgupnp/gupnp-context.c gupnp-1.0.2/libgupnp/gupnp-context.c --- gupnp-1.0.2.orig/libgupnp/gupnp-context.c 2017-03-20 06:46:54.000000000 -0500 +++ gupnp-1.0.2/libgupnp/gupnp-context.c 2021-06-07 14:23:10.125520332 -0500 @@ -1538,3 +1538,66 @@ gupnp_context_remove_server_handler (GUP soup_server_remove_handler (context->priv->server, path); } + +gboolean +validate_host_header (const char *host_header, + const char *host_ip, + guint context_port) +{ + + 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); + + const char *host = NULL; + int port = 0; + + SoupURI *uri = soup_uri_new (uri_from_host); + if (uri == NULL) { + g_debug ("Failed to parse HOST header %s from request", + host_header); + goto out; + } + host = soup_uri_get_host (uri); + port = soup_uri_get_port (uri); + + + // -1 means there was no :port; according to UDA this is allowed and + // defaults to 80, the HTTP port then + if (soup_uri_uses_default_port (uri)) { + port = 80; + } + + 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_pointer (&uri, soup_uri_free); + g_free (uri_from_host); + + return retval; +} + +gboolean +gupnp_context_validate_host_header (GUPnPContext *context, + const char *host_header) +{ + return validate_host_header ( + host_header, + gssdp_client_get_host_ip (GSSDP_CLIENT (context)), + gupnp_context_get_port (context)); +} diff -urp gupnp-1.0.2.orig/libgupnp/gupnp-context-private.h gupnp-1.0.2/libgupnp/gupnp-context-private.h --- gupnp-1.0.2.orig/libgupnp/gupnp-context-private.h 2016-04-02 15:33:18.000000000 -0500 +++ gupnp-1.0.2/libgupnp/gupnp-context-private.h 2021-06-07 14:22:39.077354262 -0500 @@ -36,6 +36,14 @@ _gupnp_context_add_server_handler_with_d const char *path, AclServerHandler *data); +G_GNUC_INTERNAL gboolean +gupnp_context_validate_host_header (GUPnPContext *context, const char *host); + +gboolean +validate_host_header (const char *host_header, + const char *host_ip, + guint context_port); + G_END_DECLS #endif /* __GUPNP_CONTEXT_PRIVATE_H__ */ diff -urp gupnp-1.0.2.orig/libgupnp/gupnp-service.c gupnp-1.0.2/libgupnp/gupnp-service.c --- gupnp-1.0.2.orig/libgupnp/gupnp-service.c 2017-03-20 06:46:54.000000000 -0500 +++ gupnp-1.0.2/libgupnp/gupnp-service.c 2021-06-07 14:24:55.274082626 -0500 @@ -948,6 +948,19 @@ control_server_handler (SoupServer 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"); diff -urp gupnp-1.0.2.orig/tests/gtest/test-bugs.c gupnp-1.0.2/tests/gtest/test-bugs.c --- gupnp-1.0.2.orig/tests/gtest/test-bugs.c 2017-03-20 06:46:54.000000000 -0500 +++ gupnp-1.0.2/tests/gtest/test-bugs.c 2021-06-07 14:25:49.098370389 -0500 @@ -24,6 +24,7 @@ #endif #include <libgupnp/gupnp.h> +#include <libgupnp/gupnp-context-private.h> struct _GUPnPServiceAction { @@ -468,14 +469,81 @@ test_bgo_743233 (void) g_object_unref (context); } +static void +test_ggo_24 (void) +{ + // IPv4 + g_assert ( + validate_host_header ("127.0.0.1:4711", "127.0.0.1", 4711)); + + g_assert ( + validate_host_header ("127.0.0.1", "127.0.0.1", 80)); + + g_assert_false ( + validate_host_header ("example.com", "127.0.0.1", 4711)); + + g_assert_false ( + validate_host_header ("example.com:80", "127.0.0.1", 4711)); + + g_assert_false ( + validate_host_header ("example.com:4711", "127.0.0.1", 4711)); + + g_assert_false ( + validate_host_header ("192.168.1.2:4711", "127.0.0.1", 4711)); + + g_assert_false ( + validate_host_header ("[fe80::01]", "127.0.0.1", 4711)); + + // Link ids should not be parsed + g_assert_false ( + validate_host_header ("[fe80::01%1]", "127.0.0.1", 4711)); + + g_assert_false ( + validate_host_header ("[fe80::01%eth0]", "127.0.0.1", 4711)); + + // IPv6 + g_assert ( + validate_host_header ("[::1]:4711", "::1", 4711)); + + g_assert ( + validate_host_header ("[::1]", "::1", 80)); + + // Host header needs to be enclosed in [] even without port + g_assert_false ( + validate_host_header ("::1", "::1", 80)); + + g_assert_false ( + validate_host_header ("example.com", "::1", 4711)); + + g_assert_false ( + validate_host_header ("example.com:80", "::1", 4711)); + + g_assert_false ( + validate_host_header ("example.com:4711", "::1", 4711)); + + g_assert_false ( + validate_host_header ("192.168.1.2:4711", "::1", 4711)); + + g_assert_false ( + validate_host_header ("[fe80::01]", "::1", 4711)); + + // Link ids should not be parsed + g_assert_false ( + validate_host_header ("[fe80::01%1]", "fe80::acab", 4711)); + + g_assert_false ( + validate_host_header ("[fe80::01%eth0]", "fe80::acab", 4711)); +} + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); - g_test_add_func ("/bugs/696762", test_bgo_696762); - g_test_add_func ("/bugs/678701", test_bgo_678701); - g_test_add_func ("/bugs/690400", test_bgo_690400); - g_test_add_func ("/bugs/722696", test_bgo_722696); - g_test_add_func ("/bugs/743233", test_bgo_743233); + g_test_add_func ("/bugs/bgo/696762", test_bgo_696762); + g_test_add_func ("/bugs/bgo/678701", test_bgo_678701); + g_test_add_func ("/bugs/bgo/690400", test_bgo_690400); + g_test_add_func ("/bugs/bgo/722696", test_bgo_722696); + g_test_add_func ("/bugs/bgo/743233", test_bgo_743233); + g_test_add_func ("/bugs/ggo/24", test_ggo_24); return g_test_run (); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor