1 /*****************************************************************************
2 * http_auth.c: HTTP authentication for clients as per RFC2617
3 *****************************************************************************
4 * Copyright (C) 2001-2008 VLC authors and VideoLAN
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Christophe Massiot <massiot@via.ecp.fr>
9 * RĂ©mi Denis-Courmont <rem # videolan.org>
10 * Antoine Cellerier <dionoea at videolan dot org>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
38 #include <vlc_strings.h>
43 /*****************************************************************************
44 * "RFC 2617: Basic and Digest Access Authentication" header parsing
45 *****************************************************************************/
46 static char *AuthGetParam( const char *psz_header, const char *psz_param )
48 char psz_what[strlen(psz_param)+3];
49 sprintf( psz_what, "%s=\"", psz_param );
50 psz_header = strstr( psz_header, psz_what );
54 psz_header += strlen( psz_what );
55 psz_end = strchr( psz_header, '"' );
56 if ( !psz_end ) /* Invalid since we should have a closing quote */
57 return strdup( psz_header );
58 return strndup( psz_header, psz_end - psz_header );
66 static char *AuthGetParamNoQuotes( const char *psz_header, const char *psz_param )
68 char psz_what[strlen(psz_param)+2];
69 sprintf( psz_what, "%s=", psz_param );
70 psz_header = strstr( psz_header, psz_what );
74 psz_header += strlen( psz_what );
75 psz_end = strchr( psz_header, ',' );
76 /* XXX: Do we need to filter out trailing space between the value and
77 * the comma/end of line? */
78 if ( !psz_end ) /* Can be valid if this is the last parameter */
79 return strdup( psz_header );
80 return strndup( psz_header, psz_end - psz_header );
88 static char *GenerateCnonce()
93 vlc_rand_bytes( ps_random, sizeof( ps_random ) );
96 AddMD5( &md5, ps_random, sizeof( ps_random ) );
99 return psz_md5_hash( &md5 );
102 static char *AuthDigest( vlc_object_t *p_this, http_auth_t *p_auth,
103 const char *psz_method, const char *psz_path,
104 const char *psz_username, const char *psz_password )
106 char *psz_HA1 = NULL;
107 char *psz_HA2 = NULL;
108 char *psz_ent = NULL;
109 char *psz_result = NULL;
114 if ( p_auth->psz_realm == NULL )
116 msg_Warn( p_this, "Digest Authentication: "
117 "Mandatory 'realm' value not available" );
122 if ( p_auth->psz_HA1 )
124 psz_HA1 = strdup( p_auth->psz_HA1 );
125 if ( psz_HA1 == NULL )
131 AddMD5( &md5, psz_username, strlen( psz_username ) );
132 AddMD5( &md5, ":", 1 );
133 AddMD5( &md5, p_auth->psz_realm, strlen( p_auth->psz_realm ) );
134 AddMD5( &md5, ":", 1 );
135 AddMD5( &md5, psz_password, strlen( psz_password ) );
138 psz_HA1 = psz_md5_hash( &md5 );
139 if ( psz_HA1 == NULL )
142 if ( p_auth->psz_algorithm &&
143 strcmp( p_auth->psz_algorithm, "MD5-sess" ) == 0 )
146 AddMD5( &md5, psz_HA1, 32 );
147 AddMD5( &md5, ":", 1 );
148 AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) );
149 AddMD5( &md5, ":", 1 );
150 AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) );
155 psz_HA1 = psz_md5_hash( &md5 );
156 if ( psz_HA1 == NULL )
159 p_auth->psz_HA1 = strdup( psz_HA1 );
160 if ( p_auth->psz_HA1 == NULL )
168 AddMD5( &md5, psz_method, strlen( psz_method ) );
169 AddMD5( &md5, ":", 1 );
171 AddMD5( &md5, psz_path, strlen( psz_path ) );
173 AddMD5( &md5, "/", 1 );
174 if ( p_auth->psz_qop && strcmp( p_auth->psz_qop, "auth-int" ) == 0 )
177 /* TODO: Support for "qop=auth-int" */
178 AddMD5( &ent, "", 0 );
181 psz_ent = psz_md5_hash( &ent );
182 if ( psz_ent == NULL )
185 AddMD5( &md5, ":", 1 );
186 AddMD5( &md5, psz_ent, 32 );
190 psz_HA2 = psz_md5_hash( &md5 );
191 if ( psz_HA2 == NULL )
196 AddMD5( &md5, psz_HA1, 32 );
197 AddMD5( &md5, ":", 1 );
198 AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) );
199 AddMD5( &md5, ":", 1 );
200 if ( p_auth->psz_qop &&
201 ( strcmp( p_auth->psz_qop, "auth" ) == 0 ||
202 strcmp( p_auth->psz_qop, "auth-int" ) == 0 ) )
204 snprintf( psz_inonce, sizeof( psz_inonce ), "%08x", p_auth->i_nonce );
205 AddMD5( &md5, psz_inonce, 8 );
206 AddMD5( &md5, ":", 1 );
207 AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) );
208 AddMD5( &md5, ":", 1 );
209 AddMD5( &md5, p_auth->psz_qop, strlen( p_auth->psz_qop ) );
210 AddMD5( &md5, ":", 1 );
212 AddMD5( &md5, psz_HA2, 32 );
215 psz_result = psz_md5_hash( &md5 );
225 /* RFC2617, section 3.2.1 The WWW-Authenticate Response Header
227 * If a server receives a request for an access-protected object, and an
228 * acceptable Authorization header is not sent, the server responds with a "401
229 * Unauthorized" status code, and a WWW-Authenticate header [...]
231 void http_auth_ParseWwwAuthenticateHeader(
232 vlc_object_t *p_this, http_auth_t *p_auth,
233 const char *psz_header )
235 static const char psz_basic_prefix[] = "Basic ";
236 static const char psz_digest_prefix[] = "Digest ";
238 /* FIXME: multiple auth methods can be listed (comma separated) */
240 if ( strncasecmp( psz_header, psz_basic_prefix,
241 sizeof( psz_basic_prefix ) - 1 ) == 0 )
243 /* 2 Basic Authentication Scheme */
244 msg_Dbg( p_this, "Using Basic Authentication" );
245 psz_header += sizeof( psz_basic_prefix ) - 1;
246 p_auth->psz_realm = AuthGetParam( psz_header, "realm" );
247 if ( p_auth->psz_realm == NULL )
248 msg_Warn( p_this, "Basic Authentication: "
249 "Mandatory 'realm' parameter is missing" );
251 else if ( strncasecmp( psz_header, psz_digest_prefix,
252 sizeof( psz_digest_prefix ) - 1 ) == 0 )
254 /* 3 Digest Access Authentication Scheme */
255 msg_Dbg( p_this, "Using Digest Access Authentication" );
257 if ( p_auth->psz_nonce )
261 psz_header += sizeof( psz_digest_prefix ) - 1;
262 p_auth->psz_realm = AuthGetParam( psz_header, "realm" );
263 p_auth->psz_domain = AuthGetParam( psz_header, "domain" );
264 p_auth->psz_nonce = AuthGetParam( psz_header, "nonce" );
265 p_auth->psz_opaque = AuthGetParam( psz_header, "opaque" );
266 p_auth->psz_stale = AuthGetParamNoQuotes( psz_header, "stale" );
267 p_auth->psz_algorithm = AuthGetParamNoQuotes( psz_header, "algorithm" );
268 p_auth->psz_qop = AuthGetParam( psz_header, "qop" );
271 /* printf("realm: |%s|\ndomain: |%s|\nnonce: |%s|\nopaque: |%s|\n"
272 "stale: |%s|\nalgorithm: |%s|\nqop: |%s|\n",
273 p_auth->psz_realm,p_auth->psz_domain,p_auth->psz_nonce,
274 p_auth->psz_opaque,p_auth->psz_stale,p_auth->psz_algorithm,
277 if ( p_auth->psz_realm == NULL )
278 msg_Warn( p_this, "Digest Access Authentication: "
279 "Mandatory 'realm' parameter is missing" );
280 if ( p_auth->psz_nonce == NULL )
281 msg_Warn( p_this, "Digest Access Authentication: "
282 "Mandatory 'nonce' parameter is missing" );
284 /* FIXME: parse the qop list */
285 if ( p_auth->psz_qop )
287 char *psz_tmp = strchr( p_auth->psz_qop, ',' );
294 const char *psz_end = strchr( psz_header, ' ' );
296 msg_Warn( p_this, "Unknown authentication scheme: '%*s'",
297 psz_end - psz_header, psz_header );
299 msg_Warn( p_this, "Unknown authentication scheme: '%s'",
304 /* RFC2617, section 3.2.3: The Authentication-Info Header
306 * The Authentication-Info header is used by the server to communicate some
307 * information regarding the successful authentication in the response.
309 int http_auth_ParseAuthenticationInfoHeader(
310 vlc_object_t *p_this, http_auth_t *p_auth,
311 const char *psz_header, const char *psz_method, const char *psz_path,
312 const char *psz_username, const char *psz_password )
314 char *psz_nextnonce = AuthGetParam( psz_header, "nextnonce" );
315 char *psz_qop = AuthGetParamNoQuotes( psz_header, "qop" );
316 char *psz_rspauth = AuthGetParam( psz_header, "rspauth" );
317 char *psz_cnonce = AuthGetParam( psz_header, "cnonce" );
318 char *psz_nc = AuthGetParamNoQuotes( psz_header, "nc" );
319 char *psz_digest = NULL;
320 int i_err = VLC_SUCCESS;
325 if ( strcmp( psz_cnonce, p_auth->psz_cnonce ) != 0 )
327 msg_Err( p_this, "HTTP Digest Access Authentication: server "
328 "replied with a different client nonce value." );
329 i_err = VLC_EGENERIC;
335 i_nonce = strtol( psz_nc, NULL, 16 );
337 if ( i_nonce != p_auth->i_nonce )
339 msg_Err( p_this, "HTTP Digest Access Authentication: server "
340 "replied with a different nonce count "
342 i_err = VLC_EGENERIC;
347 if ( psz_qop && p_auth->psz_qop &&
348 strcmp( psz_qop, p_auth->psz_qop ) != 0 )
349 msg_Warn( p_this, "HTTP Digest Access Authentication: server "
350 "replied using a different 'quality of "
351 "protection' option" );
353 /* All the clear text values match, let's now check the response
356 * TODO: Support for "qop=auth-int"
358 psz_digest = AuthDigest( p_this, p_auth, psz_method, psz_path,
359 psz_username, psz_password );
360 if ( strcmp( psz_digest, psz_rspauth ) != 0 )
362 msg_Err( p_this, "HTTP Digest Access Authentication: server "
363 "replied with an invalid response digest "
364 "(expected value: %s).", psz_digest );
365 i_err = VLC_EGENERIC;
372 free( p_auth->psz_nonce );
373 p_auth->psz_nonce = psz_nextnonce;
374 psz_nextnonce = NULL;
378 free( psz_nextnonce );
388 char *http_auth_FormatAuthorizationHeader(
389 vlc_object_t *p_this, http_auth_t *p_auth,
390 const char *psz_method, const char *psz_path,
391 const char *psz_username, const char *psz_password )
393 char *psz_result = NULL;
394 char *psz_buffer = NULL;
395 char *psz_base64 = NULL;
398 if ( p_auth->psz_nonce )
400 /* Digest Access Authentication */
401 if ( p_auth->psz_algorithm &&
402 strcmp( p_auth->psz_algorithm, "MD5" ) != 0 &&
403 strcmp( p_auth->psz_algorithm, "MD5-sess" ) != 0 )
405 msg_Err( p_this, "Digest Access Authentication: "
406 "Unknown algorithm '%s'", p_auth->psz_algorithm );
410 if ( p_auth->psz_qop != NULL || p_auth->psz_cnonce == NULL )
412 free( p_auth->psz_cnonce );
414 p_auth->psz_cnonce = GenerateCnonce();
415 if ( p_auth->psz_cnonce == NULL )
421 psz_buffer = AuthDigest( p_this, p_auth, psz_method, psz_path,
422 psz_username, psz_password );
423 if ( psz_buffer == NULL )
426 i_rc = asprintf( &psz_result,
428 /* Mandatory parameters */
434 /* Optional parameters */
435 "%s%s%s" /* algorithm */
436 "%s%s%s" /* cnonce */
437 "%s%s%s" /* opaque */
438 "%s%s%s" /* message qop */
439 "%s%08x%s", /* nonce count */
440 /* Mandatory parameters */
444 psz_path ? psz_path : "/",
446 /* Optional parameters */
447 p_auth->psz_algorithm ? "algorithm=\"" : "",
448 p_auth->psz_algorithm ? p_auth->psz_algorithm : "",
449 p_auth->psz_algorithm ? "\", " : "",
450 p_auth->psz_cnonce ? "cnonce=\"" : "",
451 p_auth->psz_cnonce ? p_auth->psz_cnonce : "",
452 p_auth->psz_cnonce ? "\", " : "",
453 p_auth->psz_opaque ? "opaque=\"" : "",
454 p_auth->psz_opaque ? p_auth->psz_opaque : "",
455 p_auth->psz_opaque ? "\", " : "",
456 p_auth->psz_qop ? "qop=\"" : "",
457 p_auth->psz_qop ? p_auth->psz_qop : "",
458 p_auth->psz_qop ? "\", " : "",
459 /* "uglyhack" will be parsed as an unhandled extension */
460 p_auth->i_nonce ? "nc=\"" : "uglyhack=\"",
462 p_auth->i_nonce ? "\"" : "\""
469 /* Basic Access Authentication */
470 i_rc = asprintf( &psz_buffer, "%s:%s", psz_username, psz_password );
474 psz_base64 = vlc_b64_encode( psz_buffer );
475 if ( psz_base64 == NULL )
478 i_rc = asprintf( &psz_result, "Basic %s", psz_base64 );
490 void http_auth_Init( http_auth_t *p_auth )
492 memset( p_auth, 0, sizeof( *p_auth ) );
495 void http_auth_Reset( http_auth_t *p_auth )
499 FREENULL( p_auth->psz_realm );
500 FREENULL( p_auth->psz_domain );
501 FREENULL( p_auth->psz_nonce );
502 FREENULL( p_auth->psz_opaque );
503 FREENULL( p_auth->psz_stale );
504 FREENULL( p_auth->psz_algorithm );
505 FREENULL( p_auth->psz_qop );
506 FREENULL( p_auth->psz_cnonce );
507 FREENULL( p_auth->psz_HA1 );