| 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 |
| |