Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:dirkmueller:acdc:as_python3_module
gupnp.20124
gupnp-validate-host-header.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gupnp-validate-host-header.patch of Package gupnp.20124
From 4daf644398e3cf037497bddcc7e9a9b86ea1eace 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 context: Use SoupURI instead of GUri Do not bump the implicit requirement to GLib 2.66 for this version --- libgupnp/gupnp-context-private.h | 8 ++++ libgupnp/gupnp-context.c | 63 ++++++++++++++++++++++++++ libgupnp/gupnp-service.c | 13 ++++++ tests/gtest/test-bugs.c | 78 ++++++++++++++++++++++++++++++-- 4 files changed, 157 insertions(+), 5 deletions(-) diff --git a/libgupnp/gupnp-context-private.h b/libgupnp/gupnp-context-private.h index 513d774..9716de3 100644 --- a/libgupnp/gupnp-context-private.h +++ b/libgupnp/gupnp-context-private.h @@ -36,6 +36,14 @@ _gupnp_context_add_server_handler_with_data (GUPnPContext *context, 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 --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c index 022910d..9c99ee2 100644 --- a/libgupnp/gupnp-context.c +++ b/libgupnp/gupnp-context.c @@ -1653,3 +1653,66 @@ gupnp_context_rewrite_uri (GUPnPContext *context, const char *plain_uri) return retval; } + +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 --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c index ce25ca5..0095648 100644 --- a/libgupnp/gupnp-service.c +++ b/libgupnp/gupnp-service.c @@ -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"); diff --git a/tests/gtest/test-bugs.c b/tests/gtest/test-bugs.c index 76d35d6..6f224d2 100644 --- a/tests/gtest/test-bugs.c +++ b/tests/gtest/test-bugs.c @@ -25,6 +25,7 @@ #include <libgupnp/gupnp.h> #include <libgupnp/gupnp-service-private.h> +#include <libgupnp/gupnp-context-private.h> typedef struct _TestBgo678701Service { GUPnPServiceProxy parent_instance; @@ -459,14 +460,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 (); } -- 2.26.2
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