1 /*****************************************************************************
2 * http_auth.c: HTTP authentication for clients as per RFC2617
3 *****************************************************************************
4 * Copyright (C) 2001-2008 VLC authors and VideoLAN
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7 * Christophe Massiot <massiot@via.ecp.fr>
8 * RĂ©mi Denis-Courmont <rem # videolan.org>
9 * Antoine Cellerier <dionoea at videolan dot org>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
37 #include <vlc_strings.h>
42 /*****************************************************************************
43 * "RFC 2617: Basic and Digest Access Authentication" header parsing
44 *****************************************************************************/
45 static char *AuthGetParam( const char *psz_header
, const char *psz_param
)
47 char psz_what
[strlen(psz_param
)+3];
48 sprintf( psz_what
, "%s=\"", psz_param
);
49 psz_header
= strstr( psz_header
, psz_what
);
53 psz_header
+= strlen( psz_what
);
54 psz_end
= strchr( psz_header
, '"' );
55 if ( !psz_end
) /* Invalid since we should have a closing quote */
56 return strdup( psz_header
);
57 return strndup( psz_header
, psz_end
- psz_header
);
65 static char *AuthGetParamNoQuotes( const char *psz_header
, const char *psz_param
)
67 char psz_what
[strlen(psz_param
)+2];
68 sprintf( psz_what
, "%s=", psz_param
);
69 psz_header
= strstr( psz_header
, psz_what
);
73 psz_header
+= strlen( psz_what
);
74 psz_end
= strchr( psz_header
, ',' );
75 /* XXX: Do we need to filter out trailing space between the value and
76 * the comma/end of line? */
77 if ( !psz_end
) /* Can be valid if this is the last parameter */
78 return strdup( psz_header
);
79 return strndup( psz_header
, psz_end
- psz_header
);
87 static char *GenerateCnonce()
92 vlc_rand_bytes( ps_random
, sizeof( ps_random
) );
95 AddMD5( &md5
, ps_random
, sizeof( ps_random
) );
98 return psz_md5_hash( &md5
);
101 static char *AuthDigest( vlc_object_t
*p_this
, vlc_http_auth_t
*p_auth
,
102 const char *psz_method
, const char *psz_path
,
103 const char *psz_username
, const char *psz_password
)
105 char *psz_HA1
= NULL
;
106 char *psz_HA2
= NULL
;
107 char *psz_ent
= NULL
;
108 char *psz_result
= NULL
;
113 if ( p_auth
->psz_realm
== NULL
)
115 msg_Warn( p_this
, "Digest Authentication: "
116 "Mandatory 'realm' value not available" );
121 if ( p_auth
->psz_HA1
)
123 psz_HA1
= strdup( p_auth
->psz_HA1
);
124 if ( psz_HA1
== NULL
)
130 AddMD5( &md5
, psz_username
, strlen( psz_username
) );
131 AddMD5( &md5
, ":", 1 );
132 AddMD5( &md5
, p_auth
->psz_realm
, strlen( p_auth
->psz_realm
) );
133 AddMD5( &md5
, ":", 1 );
134 AddMD5( &md5
, psz_password
, strlen( psz_password
) );
137 psz_HA1
= psz_md5_hash( &md5
);
138 if ( psz_HA1
== NULL
)
141 if ( p_auth
->psz_algorithm
&&
142 strcmp( p_auth
->psz_algorithm
, "MD5-sess" ) == 0 )
145 AddMD5( &md5
, psz_HA1
, 32 );
146 AddMD5( &md5
, ":", 1 );
147 AddMD5( &md5
, p_auth
->psz_nonce
, strlen( p_auth
->psz_nonce
) );
148 AddMD5( &md5
, ":", 1 );
149 AddMD5( &md5
, p_auth
->psz_cnonce
, strlen( p_auth
->psz_cnonce
) );
154 psz_HA1
= psz_md5_hash( &md5
);
155 if ( psz_HA1
== NULL
)
158 p_auth
->psz_HA1
= strdup( psz_HA1
);
159 if ( p_auth
->psz_HA1
== NULL
)
167 AddMD5( &md5
, psz_method
, strlen( psz_method
) );
168 AddMD5( &md5
, ":", 1 );
170 AddMD5( &md5
, psz_path
, strlen( psz_path
) );
172 AddMD5( &md5
, "/", 1 );
173 if ( p_auth
->psz_qop
&& strcmp( p_auth
->psz_qop
, "auth-int" ) == 0 )
176 /* TODO: Support for "qop=auth-int" */
177 AddMD5( &ent
, "", 0 );
180 psz_ent
= psz_md5_hash( &ent
);
181 if ( psz_ent
== NULL
)
184 AddMD5( &md5
, ":", 1 );
185 AddMD5( &md5
, psz_ent
, 32 );
189 psz_HA2
= psz_md5_hash( &md5
);
190 if ( psz_HA2
== NULL
)
195 AddMD5( &md5
, psz_HA1
, 32 );
196 AddMD5( &md5
, ":", 1 );
197 AddMD5( &md5
, p_auth
->psz_nonce
, strlen( p_auth
->psz_nonce
) );
198 AddMD5( &md5
, ":", 1 );
199 if ( p_auth
->psz_qop
&&
200 ( strcmp( p_auth
->psz_qop
, "auth" ) == 0 ||
201 strcmp( p_auth
->psz_qop
, "auth-int" ) == 0 ) )
203 snprintf( psz_inonce
, sizeof( psz_inonce
), "%08x", p_auth
->i_nonce
);
204 AddMD5( &md5
, psz_inonce
, 8 );
205 AddMD5( &md5
, ":", 1 );
206 AddMD5( &md5
, p_auth
->psz_cnonce
, strlen( p_auth
->psz_cnonce
) );
207 AddMD5( &md5
, ":", 1 );
208 AddMD5( &md5
, p_auth
->psz_qop
, strlen( p_auth
->psz_qop
) );
209 AddMD5( &md5
, ":", 1 );
211 AddMD5( &md5
, psz_HA2
, 32 );
214 psz_result
= psz_md5_hash( &md5
);
224 /* RFC2617, section 3.2.1 The WWW-Authenticate Response Header
226 * If a server receives a request for an access-protected object, and an
227 * acceptable Authorization header is not sent, the server responds with a "401
228 * Unauthorized" status code, and a WWW-Authenticate header [...]
230 void vlc_http_auth_ParseWwwAuthenticateHeader(
231 vlc_object_t
*p_this
, vlc_http_auth_t
*p_auth
,
232 const char *psz_header
)
234 static const char psz_basic_prefix
[] = "Basic ";
235 static const char psz_digest_prefix
[] = "Digest ";
237 /* FIXME: multiple auth methods can be listed (comma separated) */
239 if ( strncasecmp( psz_header
, psz_basic_prefix
,
240 sizeof( psz_basic_prefix
) - 1 ) == 0 )
242 /* 2 Basic Authentication Scheme */
243 msg_Dbg( p_this
, "Using Basic Authentication" );
244 psz_header
+= sizeof( psz_basic_prefix
) - 1;
245 p_auth
->psz_realm
= AuthGetParam( psz_header
, "realm" );
246 if ( p_auth
->psz_realm
== NULL
)
247 msg_Warn( p_this
, "Basic Authentication: "
248 "Mandatory 'realm' parameter is missing" );
250 else if ( strncasecmp( psz_header
, psz_digest_prefix
,
251 sizeof( psz_digest_prefix
) - 1 ) == 0 )
253 /* 3 Digest Access Authentication Scheme */
254 msg_Dbg( p_this
, "Using Digest Access Authentication" );
256 if ( p_auth
->psz_nonce
)
260 psz_header
+= sizeof( psz_digest_prefix
) - 1;
261 p_auth
->psz_realm
= AuthGetParam( psz_header
, "realm" );
262 p_auth
->psz_domain
= AuthGetParam( psz_header
, "domain" );
263 p_auth
->psz_nonce
= AuthGetParam( psz_header
, "nonce" );
264 p_auth
->psz_opaque
= AuthGetParam( psz_header
, "opaque" );
265 p_auth
->psz_stale
= AuthGetParamNoQuotes( psz_header
, "stale" );
266 p_auth
->psz_algorithm
= AuthGetParamNoQuotes( psz_header
, "algorithm" );
267 p_auth
->psz_qop
= AuthGetParam( psz_header
, "qop" );
270 /* printf("realm: |%s|\ndomain: |%s|\nnonce: |%s|\nopaque: |%s|\n"
271 "stale: |%s|\nalgorithm: |%s|\nqop: |%s|\n",
272 p_auth->psz_realm,p_auth->psz_domain,p_auth->psz_nonce,
273 p_auth->psz_opaque,p_auth->psz_stale,p_auth->psz_algorithm,
276 if ( p_auth
->psz_realm
== NULL
)
277 msg_Warn( p_this
, "Digest Access Authentication: "
278 "Mandatory 'realm' parameter is missing" );
279 if ( p_auth
->psz_nonce
== NULL
)
280 msg_Warn( p_this
, "Digest Access Authentication: "
281 "Mandatory 'nonce' parameter is missing" );
283 /* FIXME: parse the qop list */
284 if ( p_auth
->psz_qop
)
286 char *psz_tmp
= strchr( p_auth
->psz_qop
, ',' );
293 const char *psz_end
= strchr( psz_header
, ' ' );
295 msg_Warn( p_this
, "Unknown authentication scheme: '%*s'",
296 (int)(psz_end
- psz_header
), psz_header
);
298 msg_Warn( p_this
, "Unknown authentication scheme: '%s'",
303 /* RFC2617, section 3.2.3: The Authentication-Info Header
305 * The Authentication-Info header is used by the server to communicate some
306 * information regarding the successful authentication in the response.
308 int vlc_http_auth_ParseAuthenticationInfoHeader(
309 vlc_object_t
*p_this
, vlc_http_auth_t
*p_auth
,
310 const char *psz_header
, const char *psz_method
, const char *psz_path
,
311 const char *psz_username
, const char *psz_password
)
313 char *psz_nextnonce
= AuthGetParam( psz_header
, "nextnonce" );
314 char *psz_qop
= AuthGetParamNoQuotes( psz_header
, "qop" );
315 char *psz_rspauth
= AuthGetParam( psz_header
, "rspauth" );
316 char *psz_cnonce
= AuthGetParam( psz_header
, "cnonce" );
317 char *psz_nc
= AuthGetParamNoQuotes( psz_header
, "nc" );
318 char *psz_digest
= NULL
;
319 int i_err
= VLC_SUCCESS
;
324 if ( strcmp( psz_cnonce
, p_auth
->psz_cnonce
) != 0 )
326 msg_Err( p_this
, "HTTP Digest Access Authentication: server "
327 "replied with a different client nonce value." );
328 i_err
= VLC_EGENERIC
;
334 i_nonce
= strtol( psz_nc
, NULL
, 16 );
336 if ( i_nonce
!= p_auth
->i_nonce
)
338 msg_Err( p_this
, "HTTP Digest Access Authentication: server "
339 "replied with a different nonce count "
341 i_err
= VLC_EGENERIC
;
346 if ( psz_qop
&& p_auth
->psz_qop
&&
347 strcmp( psz_qop
, p_auth
->psz_qop
) != 0 )
348 msg_Warn( p_this
, "HTTP Digest Access Authentication: server "
349 "replied using a different 'quality of "
350 "protection' option" );
352 /* All the clear text values match, let's now check the response
355 * TODO: Support for "qop=auth-int"
357 psz_digest
= AuthDigest( p_this
, p_auth
, psz_method
, psz_path
,
358 psz_username
, psz_password
);
359 if( psz_digest
== NULL
|| strcmp( psz_digest
, psz_rspauth
) != 0 )
361 msg_Err( p_this
, "HTTP Digest Access Authentication: server "
362 "replied with an invalid response digest "
363 "(expected value: %s).", psz_digest
);
364 i_err
= VLC_EGENERIC
;
371 free( p_auth
->psz_nonce
);
372 p_auth
->psz_nonce
= psz_nextnonce
;
373 psz_nextnonce
= NULL
;
377 free( psz_nextnonce
);
387 char *vlc_http_auth_FormatAuthorizationHeader(
388 vlc_object_t
*p_this
, vlc_http_auth_t
*p_auth
,
389 const char *psz_method
, const char *psz_path
,
390 const char *psz_username
, const char *psz_password
)
392 char *psz_result
= NULL
;
393 char *psz_buffer
= NULL
;
394 char *psz_base64
= NULL
;
397 if ( p_auth
->psz_nonce
)
399 /* Digest Access Authentication */
400 if ( p_auth
->psz_algorithm
&&
401 strcmp( p_auth
->psz_algorithm
, "MD5" ) != 0 &&
402 strcmp( p_auth
->psz_algorithm
, "MD5-sess" ) != 0 )
404 msg_Err( p_this
, "Digest Access Authentication: "
405 "Unknown algorithm '%s'", p_auth
->psz_algorithm
);
409 if ( p_auth
->psz_qop
!= NULL
|| p_auth
->psz_cnonce
== NULL
)
411 free( p_auth
->psz_cnonce
);
413 p_auth
->psz_cnonce
= GenerateCnonce();
414 if ( p_auth
->psz_cnonce
== NULL
)
420 psz_buffer
= AuthDigest( p_this
, p_auth
, psz_method
, psz_path
,
421 psz_username
, psz_password
);
422 if ( psz_buffer
== NULL
)
425 i_rc
= asprintf( &psz_result
,
427 /* Mandatory parameters */
433 /* Optional parameters */
434 "%s%s%s" /* algorithm */
435 "%s%s%s" /* cnonce */
436 "%s%s%s" /* opaque */
437 "%s%s%s" /* message qop */
438 "%s=\"%08x\"", /* nonce count */
439 /* Mandatory parameters */
443 psz_path
? psz_path
: "/",
445 /* Optional parameters */
446 p_auth
->psz_algorithm
? "algorithm=\"" : "",
447 p_auth
->psz_algorithm
? p_auth
->psz_algorithm
: "",
448 p_auth
->psz_algorithm
? "\", " : "",
449 p_auth
->psz_cnonce
? "cnonce=\"" : "",
450 p_auth
->psz_cnonce
? p_auth
->psz_cnonce
: "",
451 p_auth
->psz_cnonce
? "\", " : "",
452 p_auth
->psz_opaque
? "opaque=\"" : "",
453 p_auth
->psz_opaque
? p_auth
->psz_opaque
: "",
454 p_auth
->psz_opaque
? "\", " : "",
455 p_auth
->psz_qop
? "qop=\"" : "",
456 p_auth
->psz_qop
? p_auth
->psz_qop
: "",
457 p_auth
->psz_qop
? "\", " : "",
458 /* "uglyhack" will be parsed as an unhandled extension */
459 p_auth
->i_nonce
? "nc" : "uglyhack",
467 /* Basic Access Authentication */
468 i_rc
= asprintf( &psz_buffer
, "%s:%s", psz_username
, psz_password
);
472 psz_base64
= vlc_b64_encode( psz_buffer
);
473 if ( psz_base64
== NULL
)
476 i_rc
= asprintf( &psz_result
, "Basic %s", psz_base64
);
488 void vlc_http_auth_Init( vlc_http_auth_t
*p_auth
)
490 memset( p_auth
, 0, sizeof( *p_auth
) );
493 void vlc_http_auth_Deinit( vlc_http_auth_t
*p_auth
)
495 free( p_auth
->psz_realm
);
496 free( p_auth
->psz_domain
);
497 free( p_auth
->psz_nonce
);
498 free( p_auth
->psz_opaque
);
499 free( p_auth
->psz_stale
);
500 free( p_auth
->psz_algorithm
);
501 free( p_auth
->psz_qop
);
502 free( p_auth
->psz_cnonce
);
503 free( p_auth
->psz_HA1
);