blob: 7406ce2e9e087df2420d58d9e51d87ee2118bc7f [file] [log] [blame]
From 51bfbee51fd0376b5a66c944134af3e9972d8592 Mon Sep 17 00:00:00 2001
From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
Date: Sun, 6 Sep 2020 11:22:48 +0200
Subject: [PATCH] upnphttp.c: fix CallStranger a.k.a. CVE-2020-12695
Import CheckCallback function from miniupnpd source code:
https://github.com/miniupnp/miniupnp/commit/0d9634658860c3c8c209e466cc0ef7002bad3b0a
IPv6 code was kept even if minidlna does not support it currently.
This code is licensed under BSD-3-Clause like minidlna.
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
[Upstream status:
https://sourceforge.net/p/minidlna/support-requests/71]
---
upnphttp.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 82 insertions(+), 10 deletions(-)
diff --git a/upnphttp.c b/upnphttp.c
index 974434e..3be793e 100644
--- a/upnphttp.c
+++ b/upnphttp.c
@@ -742,6 +742,70 @@ check_event(struct upnphttp *h)
return type;
}
+/**
+ * returns 0 if the callback header value is not valid
+ * 1 if it is valid.
+ */
+static int
+checkCallbackURL(struct upnphttp * h)
+{
+ char addrstr[48];
+ int ipv6;
+ const char * p;
+ int i;
+
+ if(!h->req_Callback || h->req_CallbackLen < 8)
+ return 0;
+ if(memcmp(h->req_Callback, "http://", 7) != 0)
+ return 0;
+ ipv6 = 0;
+ i = 0;
+ p = h->req_Callback + 7;
+ if(*p == '[') {
+ p++;
+ ipv6 = 1;
+ while(*p != ']' && i < (sizeof(addrstr)-1)
+ && p < (h->req_Callback + h->req_CallbackLen))
+ addrstr[i++] = *(p++);
+ } else {
+ while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
+ && p < (h->req_Callback + h->req_CallbackLen))
+ addrstr[i++] = *(p++);
+ }
+ addrstr[i] = '\0';
+ if(ipv6) {
+ struct in6_addr addr;
+ if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
+ return 0;
+#ifdef ENABLE_IPV6
+ if(!h->ipv6
+ || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
+ return 0;
+#else
+ return 0;
+#endif
+ } else {
+ struct in_addr addr;
+ if(inet_pton(AF_INET, addrstr, &addr) <= 0)
+ return 0;
+#ifdef ENABLE_IPV6
+ if(h->ipv6) {
+ if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
+ return 0;
+ if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
+ return 0;
+ } else {
+ if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
+ return 0;
+ }
+#else
+ if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
+ return 0;
+#endif
+ }
+ return 1;
+}
+
static void
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
{
@@ -759,17 +823,25 @@ ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
* - respond HTTP/x.x 200 OK
* - Send the initial event message */
/* Server:, SID:; Timeout: Second-(xx|infinite) */
- sid = upnpevents_addSubscriber(path, h->req_Callback,
- h->req_CallbackLen, h->req_Timeout);
- h->respflags = FLAG_TIMEOUT;
- if (sid)
- {
- DPRINTF(E_DEBUG, L_HTTP, "generated sid=%s\n", sid);
- h->respflags |= FLAG_SID;
- h->req_SID = sid;
- h->req_SIDLen = strlen(sid);
+ /* Check that the callback URL is on the same IP as
+ * the request, and not on the internet, nor on ourself (DOS attack ?) */
+ if(checkCallbackURL(h)) {
+ sid = upnpevents_addSubscriber(path, h->req_Callback,
+ h->req_CallbackLen, h->req_Timeout);
+ h->respflags = FLAG_TIMEOUT;
+ if (sid)
+ {
+ DPRINTF(E_DEBUG, L_HTTP, "generated sid=%s\n", sid);
+ h->respflags |= FLAG_SID;
+ h->req_SID = sid;
+ h->req_SIDLen = strlen(sid);
+ }
+ BuildResp_upnphttp(h, 0, 0);
+ } else {
+ DPRINTF(E_WARN, L_HTTP, "Invalid Callback in SUBSCRIBE %.*s",
+ h->req_CallbackLen, h->req_Callback);
+ BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
}
- BuildResp_upnphttp(h, 0, 0);
}
else if (type == E_RENEW)
{
--
2.28.0