From 8c75ed5d43266ad6e88bec37b18e785a51ffd79e Mon Sep 17 00:00:00 2001 From: Antoine Cellerier Date: Wed, 9 Apr 2008 22:46:24 +0200 Subject: [PATCH] Check the server's reply upon successful authentication and validate it (just to make sure that no one is spoofing the server's reply). This will probably fail if qop=auth-int (code needs to be fixed). --- modules/access/http.c | 279 +++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 101 deletions(-) diff --git a/modules/access/http.c b/modules/access/http.c index c2d41f6a45..c8c27dd224 100644 --- a/modules/access/http.c +++ b/modules/access/http.c @@ -209,6 +209,8 @@ static void AuthParseHeader( access_t *p_access, const char *psz_header, http_auth_t *p_auth ); static void AuthReply( access_t *p_acces, const char *psz_prefix, vlc_url_t *p_url, http_auth_t *p_auth ); +static int AuthCheckReply( access_t *p_access, const char *psz_header, + vlc_url_t *p_url, http_auth_t *p_auth ); static void AuthReset( http_auth_t *p_auth ); /***************************************************************************** @@ -1360,8 +1362,15 @@ static int Request( access_t *p_access, int64_t i_tell ) } else if( !strcasecmp( psz, "authentication-info" ) ) { - msg_Dbg( p_access, "Authentication info: %s", p ); - /* FIXME: use */ + msg_Dbg( p_access, "Authentication Info header: %s", p ); + if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) ) + goto error; + } + else if( !strcasecmp( psz, "proxy-authentication-info" ) ) + { + msg_Dbg( p_access, "Proxy Authentication Info header: %s", p ); + if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) ) + goto error; } free( psz ); @@ -1600,17 +1609,113 @@ static void AuthParseHeader( access_t *p_access, const char *psz_header, psz_header ); } } -static char *AuthAlgoMD5( const char *psz_data ) + +static char *AuthDigest( access_t *p_access, vlc_url_t *p_url, + http_auth_t *p_auth, const char *psz_method ) { + (void)p_access; + const char *psz_username = p_url->psz_username ?: ""; + const char *psz_password = p_url->psz_password ?: ""; + + char *psz_HA1 = NULL; + char *psz_HA2 = NULL; + char *psz_response = NULL; struct md5_s md5; - char *psz_md5; + + /* H(A1) */ + if( p_auth->psz_HA1 ) + { + psz_HA1 = strdup( p_auth->psz_HA1 ); + if( !psz_HA1 ) goto error; + } + else + { + InitMD5( &md5 ); + AddMD5( &md5, psz_username, strlen( psz_username ) ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_realm, strlen( p_auth->psz_realm ) ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, psz_password, strlen( psz_password ) ); + EndMD5( &md5 ); + + psz_HA1 = psz_md5_hash( &md5 ); + if( !psz_HA1 ) goto error; + + if( p_auth->psz_algorithm + && !strcmp( p_auth->psz_algorithm, "MD5-sess" ) ) + { + InitMD5( &md5 ); + AddMD5( &md5, psz_HA1, 32 ); + free( psz_HA1 ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); + EndMD5( &md5 ); + + psz_HA1 = psz_md5_hash( &md5 ); + if( !psz_HA1 ) goto error; + p_auth->psz_HA1 = strdup( psz_HA1 ); + if( !p_auth->psz_HA1 ) goto error; + } + } + + /* H(A2) */ + InitMD5( &md5 ); + if( *psz_method ) + AddMD5( &md5, psz_method, strlen( psz_method ) ); + AddMD5( &md5, ":", 1 ); + if( p_url->psz_path ) + AddMD5( &md5, p_url->psz_path, strlen( p_url->psz_path ) ); + else + AddMD5( &md5, "/", 1 ); + if( p_auth->psz_qop && !strcmp( p_auth->psz_qop, "auth-int" ) ) + { + char *psz_ent; + struct md5_s ent; + InitMD5( &ent ); + AddMD5( &ent, "", 0 ); /* XXX: entity-body. should be ok for GET */ + EndMD5( &ent ); + psz_ent = psz_md5_hash( &ent ); + if( !psz_ent ) goto error; + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, psz_ent, 32 ); + free( psz_ent ); + } + EndMD5( &md5 ); + psz_HA2 = psz_md5_hash( &md5 ); + if( !psz_HA2 ) goto error; + + /* Request digest */ InitMD5( &md5 ); - AddMD5( &md5, psz_data, strlen( psz_data ) ); + AddMD5( &md5, psz_HA1, 32 ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); + AddMD5( &md5, ":", 1 ); + if( p_auth->psz_qop + && ( !strcmp( p_auth->psz_qop, "auth" ) + || !strcmp( p_auth->psz_qop, "auth-int" ) ) ) + { + char psz_inonce[9]; + snprintf( psz_inonce, 9, "%08x", p_auth->i_nonce ); + AddMD5( &md5, psz_inonce, 8 ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); + AddMD5( &md5, ":", 1 ); + AddMD5( &md5, p_auth->psz_qop, strlen( p_auth->psz_qop ) ); + AddMD5( &md5, ":", 1 ); + } + AddMD5( &md5, psz_HA2, 32 ); EndMD5( &md5 ); - psz_md5 = psz_md5_hash( &md5 ); - return psz_md5; + psz_response = psz_md5_hash( &md5 ); + + error: + free( psz_HA1 ); + free( psz_HA2 ); + return psz_response; } + static void AuthReply( access_t *p_access, const char *psz_prefix, vlc_url_t *p_url, http_auth_t *p_auth ) { @@ -1623,10 +1728,7 @@ static void AuthReply( access_t *p_access, const char *psz_prefix, if( p_auth->psz_nonce ) { /* Digest Access Authentication */ - char *psz_HA1 = NULL; - char *psz_HA2 = NULL; - char *psz_response = NULL; - struct md5_s md5; + char *psz_response; if( p_auth->psz_algorithm && strcmp( p_auth->psz_algorithm, "MD5" ) @@ -1646,92 +1748,8 @@ static void AuthReply( access_t *p_access, const char *psz_prefix, } p_auth->i_nonce ++; - /* H(A1) */ - if( p_auth->psz_HA1 ) - { - psz_HA1 = strdup( p_auth->psz_HA1 ); - if( !psz_HA1 ) goto error; - } - else - { - InitMD5( &md5 ); - AddMD5( &md5, psz_username, strlen( psz_username ) ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_realm, strlen( p_auth->psz_realm ) ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, psz_password, strlen( psz_password ) ); - EndMD5( &md5 ); - - psz_HA1 = psz_md5_hash( &md5 ); - if( !psz_HA1 ) goto error; - - if( p_auth->psz_algorithm - && !strcmp( p_auth->psz_algorithm, "MD5-sess" ) ) - { - InitMD5( &md5 ); - AddMD5( &md5, psz_HA1, 32 ); - free( psz_HA1 ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); - EndMD5( &md5 ); - - psz_HA1 = psz_md5_hash( &md5 ); - if( !psz_HA1 ) goto error; - p_auth->psz_HA1 = strdup( psz_HA1 ); - if( !p_auth->psz_HA1 ) goto error; - } - } - - /* H(A2) */ - InitMD5( &md5 ); - AddMD5( &md5, "GET", 3 ); /* FIXME ? */ - AddMD5( &md5, ":", 1 ); - if( p_url->psz_path ) - AddMD5( &md5, p_url->psz_path, strlen( p_url->psz_path ) ); - else - AddMD5( &md5, "/", 1 ); - if( p_auth->psz_qop && !strcmp( p_auth->psz_qop, "auth-int" ) ) - { - char *psz_ent; - struct md5_s ent; - InitMD5( &ent ); - AddMD5( &ent, "", 0 ); /* XXX: entity-body. should be ok for GET */ - EndMD5( &ent ); - psz_ent = psz_md5_hash( &ent ); - if( !psz_ent ) goto error; - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, psz_ent, 32 ); - free( psz_ent ); - } - EndMD5( &md5 ); - psz_HA2 = psz_md5_hash( &md5 ); - if( !psz_HA2 ) goto error; - - /* Request digest */ - InitMD5( &md5 ); - AddMD5( &md5, psz_HA1, 32 ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) ); - AddMD5( &md5, ":", 1 ); - if( p_auth->psz_qop - && ( !strcmp( p_auth->psz_qop, "auth" ) - || !strcmp( p_auth->psz_qop, "auth-int" ) ) ) - { - char psz_inonce[9]; - snprintf( psz_inonce, 9, "%08x", p_auth->i_nonce ); - AddMD5( &md5, psz_inonce, 8 ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) ); - AddMD5( &md5, ":", 1 ); - AddMD5( &md5, p_auth->psz_qop, strlen( p_auth->psz_qop ) ); - AddMD5( &md5, ":", 1 ); - } - AddMD5( &md5, psz_HA2, 32 ); - EndMD5( &md5 ); - psz_response = psz_md5_hash( &md5 ); - if( !psz_response ) goto error; + psz_response = AuthDigest( p_access, p_url, p_auth, "GET" ); + if( !psz_response ) return; net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "%sAuthorization: Digest " @@ -1773,10 +1791,6 @@ static void AuthReply( access_t *p_access, const char *psz_prefix, p_auth->i_nonce ? "\"" : "\"" ); - - error: - free( psz_HA1 ); - free( psz_HA2 ); free( psz_response ); } else @@ -1797,6 +1811,69 @@ static void AuthReply( access_t *p_access, const char *psz_prefix, } } +static int AuthCheckReply( access_t *p_access, const char *psz_header, + vlc_url_t *p_url, http_auth_t *p_auth ) +{ + int i_ret = VLC_EGENERIC; + char *psz_nextnonce = AuthGetParam( psz_header, "nextnonce" ); + char *psz_qop = AuthGetParamNoQuotes( psz_header, "qop" ); + char *psz_rspauth = AuthGetParam( psz_header, "rspauth" ); + char *psz_cnonce = AuthGetParam( psz_header, "cnonce" ); + char *psz_nc = AuthGetParamNoQuotes( psz_header, "nc" ); + + if( psz_cnonce ) + { + char *psz_digest; + + if( strcmp( psz_cnonce, p_auth->psz_cnonce ) ) + { + msg_Err( p_access, "HTTP Digest Access Authentication: server replied with a different client nonce value." ); + goto error; + } + + if( psz_nc ) + { + int i_nonce; + i_nonce = strtol( psz_nc, NULL, 16 ); + if( i_nonce != p_auth->i_nonce ) + { + msg_Err( p_access, "HTTP Digest Access Authentication: server replied with a different nonce count value." ); + goto error; + } + } + + if( psz_qop && p_auth->psz_qop && strcmp( psz_qop, p_auth->psz_qop ) ) + msg_Warn( p_access, "HTTP Digest Access Authentication: server replied using a different 'quality of protection' option" ); + + /* All the clear text values match, let's now check the response + * digest */ + psz_digest = AuthDigest( p_access, p_url, p_auth, "" ); + if( strcmp( psz_digest, psz_rspauth ) ) + { + msg_Err( p_access, "HTTP Digest Access Authentication: server replied with an invalid response digest (expected value: %s).", psz_digest ); + free( psz_digest ); + goto error; + } + free( psz_digest ); + } + + if( psz_nextnonce ) + { + free( p_auth->psz_nonce ); + p_auth->psz_nonce = psz_nextnonce; + psz_nextnonce = NULL; + } + + i_ret = VLC_SUCCESS; + error: + free( psz_nextnonce ); + free( psz_qop ); + free( psz_rspauth ); + free( psz_cnonce ); + + return i_ret; +} + static void AuthReset( http_auth_t *p_auth ) { FREENULL( p_auth->psz_realm ); -- 2.39.2