[vlc-devel] [PATCH 2/2] httpd: mitigate DNS rebinding attack by allowing to specify a domain white list.

Pierre Lamot pierre at videolabs.io
Wed Jan 31 18:38:02 CET 2018


---
 src/libvlc-module.c |   5 +++
 src/network/httpd.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 108 insertions(+), 6 deletions(-)

diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index cc816873f7..5ca902d67a 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -848,6 +848,10 @@ static const char *const ppsz_prefres[] = {
 #define KEY_LONGTEXT N_( \
    "This private key file (PEM format) is used for server-side TLS.")
 
+#define HTTP_DOMAIN_WHITELIST_TEXT N_("HTTP server list of allowed domain")
+#define HTTP_DOMAIN_WHITELIST_LONGTEXT N_( "By default, the HTTP server will accept requests " \
+    "from any domain. Specify a comma-separated list of domains to restrict them." )
+
 #define SOCKS_SERVER_TEXT N_("SOCKS server")
 #define SOCKS_SERVER_LONGTEXT N_( \
     "SOCKS proxy server to use. This must be of the form " \
@@ -1800,6 +1804,7 @@ vlc_module_begin ()
     add_loadfile( "http-cert", NULL, HTTP_CERT_TEXT, CERT_LONGTEXT, true )
     add_obsolete_string( "sout-http-cert" ) /* since 2.0.0 */
     add_loadfile( "http-key", NULL, HTTP_KEY_TEXT, KEY_LONGTEXT, true )
+    add_string( "http-domain-whitelist", NULL, HTTP_DOMAIN_WHITELIST_TEXT, HTTP_DOMAIN_WHITELIST_LONGTEXT, true )
     add_obsolete_string( "sout-http-key" ) /* since 2.0.0 */
     add_obsolete_string( "http-ca" ) /* since 3.0.0 */
     add_obsolete_string( "sout-http-ca" ) /* since 2.0.0 */
diff --git a/src/network/httpd.c b/src/network/httpd.c
index cfd947ad94..542cb20968 100644
--- a/src/network/httpd.c
+++ b/src/network/httpd.c
@@ -52,6 +52,9 @@
 #ifdef HAVE_POLL
 # include <poll.h>
 #endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
 
 #if defined(_WIN32)
 #   include <winsock2.h>
@@ -81,7 +84,9 @@ struct httpd_host_t
     int         *fds;
     unsigned     nfd;
     unsigned     port;
+
     char        *psz_origin;
+    vlc_array_t *domainwhitelist;
 
     vlc_thread_t thread;
     vlc_mutex_t lock;
@@ -860,12 +865,12 @@ void httpd_StreamDelete(httpd_stream_t *stream)
  *****************************************************************************/
 static void* httpd_HostThread(void *);
 static httpd_host_t *httpd_HostCreate(vlc_object_t *, const char *,
-                                       const char *, vlc_tls_creds_t *);
+                                       const char *, const char *, vlc_tls_creds_t *);
 
 /* create a new host */
 httpd_host_t *vlc_http_HostNew(vlc_object_t *p_this)
 {
-    return httpd_HostCreate(p_this, "http-host", "http-port", NULL);
+    return httpd_HostCreate(p_this, "http-host", "http-port", "http-domain-whitelist",  NULL);
 }
 
 httpd_host_t *vlc_https_HostNew(vlc_object_t *obj)
@@ -889,12 +894,12 @@ httpd_host_t *vlc_https_HostNew(vlc_object_t *obj)
     free(key);
     free(cert);
 
-    return httpd_HostCreate(obj, "http-host", "https-port", tls);
+    return httpd_HostCreate(obj, "http-host", "https-port", "http-domain-whitelist", tls);
 }
 
 httpd_host_t *vlc_rtsp_HostNew(vlc_object_t *p_this)
 {
-    return httpd_HostCreate(p_this, "rtsp-host", "rtsp-port", NULL);
+    return httpd_HostCreate(p_this, "rtsp-host", "rtsp-port", NULL, NULL);
 }
 
 static struct httpd
@@ -905,9 +910,48 @@ static struct httpd
     int          i_host;
 } httpd = { VLC_STATIC_MUTEX, NULL, 0 };
 
+static vlc_array_t *httpd_DomainListFromString(vlc_object_t *p_this, const char* variable)
+{
+    char *buf = var_InheritString (p_this, variable);
+    if ( buf == NULL )
+        return NULL;
+
+    vlc_array_t* domainwhitelist = (vlc_array_t*) malloc( sizeof( vlc_array_t ) );
+    if ( !domainwhitelist )
+        return NULL;
+    vlc_array_init(domainwhitelist);
+
+    char *p = buf;
+    if ( *p == '{' )
+        p++;
+
+    char *end = strchr( p, '}' );
+    if ( end != NULL )
+        *end = '\0';
+
+    while( p != NULL && *p )
+    {
+        const char *host;
+
+        p += strspn( p, ", " );
+        host = p;
+        end = strchr( p, ',' );
+        if ( end != NULL )
+            *( end++ ) = '\0';
+        p = end;
+
+        if ( *host == '\0' )
+            break;
+
+        vlc_array_append_or_abort( domainwhitelist, (void*) strdup( host ) );
+    }
+    free( buf );
+    return domainwhitelist;
+}
+
 static httpd_host_t *httpd_HostCreate(vlc_object_t *p_this,
                                        const char *hostvar,
-                                       const char *portvar,
+                                       const char *portvar, const char *domainwhitelistvar,
                                        vlc_tls_creds_t *p_tls)
 {
     httpd_host_t *host;
@@ -972,7 +1016,9 @@ static httpd_host_t *httpd_HostCreate(vlc_object_t *p_this,
     host->i_client = 0;
     host->client   = NULL;
     host->p_tls    = p_tls;
+
     host->psz_origin =  url.psz_host ? strdup( url.psz_host ) : NULL;
+    host->domainwhitelist = httpd_DomainListFromString( p_this, domainwhitelistvar );
 
     /* create the thread */
     if (vlc_clone(&host->thread, httpd_HostThread, host,
@@ -1044,6 +1090,15 @@ void httpd_HostDelete(httpd_host_t *host)
     vlc_mutex_destroy(&host->lock);
     if ( host->psz_origin )
         free( host->psz_origin );
+    if ( host->domainwhitelist )
+    {
+        int index = vlc_array_count(host->domainwhitelist);
+        while ( index-- )
+            free( vlc_array_item_at_index( host->domainwhitelist, index ) );
+        vlc_array_clear( host->domainwhitelist );
+        free( host->domainwhitelist );
+    }
+
     vlc_object_release(host);
     vlc_mutex_unlock(&httpd.mutex);
 }
@@ -1725,6 +1780,47 @@ static bool httpdOriginOk( const httpd_host_t *p_host, const httpd_message_t *p_
     return ret;
 }
 
+static bool httpIsDomainAllowed( const httpd_host_t *p_host, const httpd_message_t *p_query )
+{
+    if ( p_host->domainwhitelist == NULL )
+        return true;
+
+    const char* psz_host = httpd_MsgGet( p_query, "Host" );
+    if ( psz_host == NULL )
+        return true;
+    char* psz_hostname = strndup( psz_host, strcspn( psz_host, ":" ) );
+
+
+#ifdef AF_INET6
+    char tmp[sizeof(struct in6_addr)];
+#else
+    char tmp[sizeof(struct in_addr)];
+#endif
+
+    if ( (strcmp( psz_hostname, "localhost" ) == 0)
+         || (strcmp( psz_hostname , "localhost." ) == 0)
+         || (inet_pton( AF_INET, psz_hostname, &tmp ) == 1)
+#ifdef AF_INET6
+         || (inet_pton( AF_INET6, psz_hostname, &tmp ) == 1)
+#endif
+         )
+    {
+        free( psz_hostname );
+        return true;
+    }
+
+    int count = vlc_array_count( p_host->domainwhitelist );
+    while (count--) {
+        const char* psz_allowedhost = vlc_array_item_at_index( p_host->domainwhitelist, count );
+        if ( strcmp( psz_allowedhost, psz_hostname ) == 0 ) {
+            free( psz_hostname );
+            return true;
+        }
+    }
+
+    free( psz_hostname );
+    return false;
+}
 
 static void httpdLoop(httpd_host_t *host)
 {
@@ -1881,7 +1977,8 @@ static void httpdLoop(httpd_host_t *host)
 
                                 if ( query->i_proto == HTTPD_PROTO_HTTP )
                                 {
-                                    b_crsf_failed = !httpdOriginOk( host, query );
+                                    b_crsf_failed = !httpdOriginOk( host, query )
+                                                    || !httpIsDomainAllowed( host, query );
 
                                     if ( b_crsf_failed )
                                        break;
-- 
2.14.1



More information about the vlc-devel mailing list