[vlc-devel] [PATCH 2/5] access/http: Implement the cookie domain matching algorithm from RFC 6265

Antti Ajanki antti.ajanki at iki.fi
Tue Jul 22 12:09:03 CEST 2014


---
 modules/access/http.c |   94 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 77 insertions(+), 17 deletions(-)

diff --git a/modules/access/http.c b/modules/access/http.c
index 75ed1cf..2ef6f59 100644
--- a/modules/access/http.c
+++ b/modules/access/http.c
@@ -193,6 +193,7 @@ typedef struct http_cookie_t
     char *psz_name;
     char *psz_value;
     char *psz_domain;
+    bool b_host_only;
 } http_cookie_t;
 
 /* */
@@ -211,11 +212,14 @@ static int Request( access_t *p_access, uint64_t i_tell );
 static void Disconnect( access_t * );
 
 /* Small Cookie utilities. Cookies support is partial. */
-static http_cookie_t * cookie_parse( const char * cookie_header );
+static http_cookie_t * cookie_parse( const char * cookie_header, const char *request_host );
 static void cookie_destroy( http_cookie_t * cookie );
 static char * cookie_get_content( const char * cookie );
 static char * cookie_get_domain( const char * cookie );
-static void cookie_append( vlc_array_t * cookies, const char * cookie );
+static void cookie_append( vlc_array_t * cookies, http_cookie_t * cookie );
+static bool cookie_is_valid( const http_cookie_t * cookie, const char *host );
+static bool cookie_domain_matches( const http_cookie_t * cookie, const char *host );
+static bool cookie_domain_is_public_suffix( const char *domain );
 
 
 static void AuthReply( access_t *p_acces, const char *psz_prefix,
@@ -1201,8 +1205,7 @@ static int Request( access_t *p_access, uint64_t i_tell )
         {
             const http_cookie_t * cookie = vlc_array_item_at_index( p_sys->cookies, i );
 
-            /* FIXME: This is clearly not conforming to the rfc */
-            bool is_in_right_domain = (!cookie->psz_domain || strstr( p_sys->url.psz_host, cookie->psz_domain ));
+            bool is_in_right_domain = cookie_domain_matches( cookie, p_sys->url.psz_host );
 
             if( is_in_right_domain )
             {
@@ -1506,8 +1509,17 @@ static int Request( access_t *p_access, uint64_t i_tell )
         {
             if( p_sys->cookies )
             {
-                msg_Dbg( p_access, "Accepting Cookie: %s", p );
-                cookie_append( p_sys->cookies, p );
+                http_cookie_t *cookie = cookie_parse( p, p_sys->url.psz_host );
+                if ( cookie_is_valid( cookie, p_sys->url.psz_host ) )
+                {
+                    msg_Dbg( p_access, "Accepting Cookie: %s", p );
+                    cookie_append( p_sys->cookies, cookie );
+                }
+                else
+                {
+                    msg_Dbg( p_access, "Ignoring invalid cookie: %s", p );
+                    cookie_destroy(cookie);
+                }
             }
             else
                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
@@ -1583,7 +1595,7 @@ static void Disconnect( access_t *p_access )
  * them) (FIXME: only support the "domain=" param)
  *****************************************************************************/
 
-static http_cookie_t * cookie_parse( const char * cookie_header )
+static http_cookie_t * cookie_parse( const char * cookie_header, const char * request_host )
 {
     char *content = cookie_get_content( cookie_header );
     if ( !content )
@@ -1603,7 +1615,17 @@ static http_cookie_t * cookie_parse( const char * cookie_header )
         cookie->psz_name = strdup( content );
         cookie->psz_value = strdup( "" );
     }
+
     cookie->psz_domain = cookie_get_domain( cookie_header );
+    if ( !cookie->psz_domain || strlen(cookie->psz_domain) == 0 )
+    {
+        free(cookie->psz_domain);
+        cookie->psz_domain = strdup( request_host );
+        cookie->b_host_only = true;
+    }
+    else
+        cookie->b_host_only = false;
+
     return cookie;
 }
 
@@ -1641,9 +1663,10 @@ static char * cookie_get_domain( const char * cookie )
     /* Look for a ';' */
     while( *str )
     {
-        if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
+        if( !strncasecmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
         {
             str += sizeof(domain) - 1 /* minus \0 */;
+            while ( *str && *str == '.' ) str++;
             char * ret = strdup( str );
             /* Now remove the next ';' if present */
             char * ret_iter = ret;
@@ -1661,25 +1684,23 @@ static char * cookie_get_domain( const char * cookie )
     return NULL;
 }
 
-/* Add a cookie in cookies, checking to see how it should be added */
-static void cookie_append( vlc_array_t * cookies, const char * cookie_header )
+/* Add a cookie in cookies */
+static void cookie_append( vlc_array_t * cookies, http_cookie_t * cookie )
 {
     int i;
 
-    http_cookie_t *cookie = cookie_parse(cookie_header);
-    if( !cookie )
-        return;
+    assert( cookie->psz_name );
+    assert( cookie->psz_domain );
 
     for( i = 0; i < vlc_array_count( cookies ); i++ )
     {
         http_cookie_t *iter = vlc_array_item_at_index( cookies, i );
 
         assert( iter->psz_name );
+        assert( iter->psz_domain );
 
-        bool is_domain_matching = (
-                      ( !cookie->psz_domain && !iter->psz_domain ) ||
-                      ( cookie->psz_domain && iter->psz_domain &&
-                        !strcmp( cookie->psz_domain, iter->psz_domain ) ) );
+        bool is_domain_matching = 
+            strcasecmp( cookie->psz_domain, iter->psz_domain ) == 0;
 
         if( is_domain_matching && !strcmp( cookie->psz_name, iter->psz_name )  )
         {
@@ -1692,6 +1713,45 @@ static void cookie_append( vlc_array_t * cookies, const char * cookie_header )
     vlc_array_append( cookies, cookie );
 }
 
+/* Check if a cookie from host should be added to the cookie jar */
+static bool cookie_is_valid( const http_cookie_t * cookie, const char *host )
+{
+    return cookie && cookie->psz_name && strlen(cookie->psz_name) > 0 &&
+        cookie->psz_domain &&
+        !cookie_domain_is_public_suffix(cookie->psz_domain) &&
+        cookie_domain_matches(cookie, host);
+}
+
+static bool cookie_domain_matches( const http_cookie_t * cookie, const char *host )
+{
+    assert( !cookie || cookie->psz_domain );
+
+    if ( !cookie || !host )
+        return false;
+    if ( strcasecmp(cookie->psz_domain, host) == 0 )
+        return true;
+    else if ( cookie->b_host_only )
+        return false;
+
+    size_t host_len = strlen(host);
+    size_t cookie_domain_len = strlen(cookie->psz_domain);
+    int i = host_len - cookie_domain_len;
+    bool is_suffix = ( i > 0 ) &&
+        strcasecmp( &host[i], cookie->psz_domain ) == 0;
+    bool has_dot_before_suffix = host[i-1] == '.';
+    bool host_is_ip = strspn(host, "0123456789.") == host_len;
+    return is_suffix && has_dot_before_suffix && !host_is_ip;
+}
+
+static bool cookie_domain_is_public_suffix( const char *domain )
+{
+    // FIXME: should check if domain is one of "public suffixes" at
+    // http://publicsuffix.org/. The purpose of this check is to
+    // prevent a host from setting a "too wide" cookie, for example
+    // "example.com" should not be able to set a cookie for "com".
+    // Approximate by preventing all single component domains.
+    return domain && !strchr(domain, '.');
+}
 
 /*****************************************************************************
  * HTTP authentication
-- 
1.7.10.4




More information about the vlc-devel mailing list