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
, vlc_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 vlc_http_auth_ParseWwwAuthenticateHeader(
232 vlc_object_t
*p_this
, vlc_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 (int)(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 vlc_http_auth_ParseAuthenticationInfoHeader(
310 vlc_object_t
*p_this
, vlc_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( psz_digest
== NULL
|| 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 *vlc_http_auth_FormatAuthorizationHeader(
389 vlc_object_t
*p_this
, vlc_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\"", /* 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",
468 /* Basic Access Authentication */
469 i_rc
= asprintf( &psz_buffer
, "%s:%s", psz_username
, psz_password
);
473 psz_base64
= vlc_b64_encode( psz_buffer
);
474 if ( psz_base64
== NULL
)
477 i_rc
= asprintf( &psz_result
, "Basic %s", psz_base64
);
489 void vlc_http_auth_Init( vlc_http_auth_t
*p_auth
)
491 memset( p_auth
, 0, sizeof( *p_auth
) );
494 void vlc_http_auth_Deinit( vlc_http_auth_t
*p_auth
)
496 free( p_auth
->psz_realm
);
497 free( p_auth
->psz_domain
);
498 free( p_auth
->psz_nonce
);
499 free( p_auth
->psz_opaque
);
500 free( p_auth
->psz_stale
);
501 free( p_auth
->psz_algorithm
);
502 free( p_auth
->psz_qop
);
503 free( p_auth
->psz_cnonce
);
504 free( p_auth
->psz_HA1
);