qml: restore the focus on the last album when navigating back to the album view
[vlc.git] / src / network / http_auth.c
blobff1796c25d7fea49c867c2329379434bf601bee3
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
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 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_http.h>
35 #include <vlc_md5.h>
36 #include <vlc_rand.h>
37 #include <vlc_strings.h>
39 #include "libvlc.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 );
50 if ( psz_header )
52 const char *psz_end;
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 );
59 else
61 return NULL;
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 );
70 if ( psz_header )
72 const char *psz_end;
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 );
81 else
83 return NULL;
87 static char *GenerateCnonce()
89 char ps_random[32];
90 struct md5_s md5;
92 vlc_rand_bytes( ps_random, sizeof( ps_random ) );
94 InitMD5( &md5 );
95 AddMD5( &md5, ps_random, sizeof( ps_random ) );
96 EndMD5( &md5 );
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;
109 char psz_inonce[9];
110 struct md5_s md5;
111 struct md5_s ent;
113 if ( p_auth->psz_realm == NULL )
115 msg_Warn( p_this, "Digest Authentication: "
116 "Mandatory 'realm' value not available" );
117 goto error;
120 /* H(A1) */
121 if ( p_auth->psz_HA1 )
123 psz_HA1 = strdup( p_auth->psz_HA1 );
124 if ( psz_HA1 == NULL )
125 goto error;
127 else
129 InitMD5( &md5 );
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 ) );
135 EndMD5( &md5 );
137 psz_HA1 = psz_md5_hash( &md5 );
138 if ( psz_HA1 == NULL )
139 goto error;
141 if ( p_auth->psz_algorithm &&
142 strcmp( p_auth->psz_algorithm, "MD5-sess" ) == 0 )
144 InitMD5( &md5 );
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 ) );
150 EndMD5( &md5 );
152 free( psz_HA1 );
154 psz_HA1 = psz_md5_hash( &md5 );
155 if ( psz_HA1 == NULL )
156 goto error;
158 p_auth->psz_HA1 = strdup( psz_HA1 );
159 if ( p_auth->psz_HA1 == NULL )
160 goto error;
164 /* H(A2) */
165 InitMD5( &md5 );
166 if ( *psz_method )
167 AddMD5( &md5, psz_method, strlen( psz_method ) );
168 AddMD5( &md5, ":", 1 );
169 if ( psz_path )
170 AddMD5( &md5, psz_path, strlen( psz_path ) );
171 else
172 AddMD5( &md5, "/", 1 );
173 if ( p_auth->psz_qop && strcmp( p_auth->psz_qop, "auth-int" ) == 0 )
175 InitMD5( &ent );
176 /* TODO: Support for "qop=auth-int" */
177 AddMD5( &ent, "", 0 );
178 EndMD5( &ent );
180 psz_ent = psz_md5_hash( &ent );
181 if ( psz_ent == NULL )
182 goto error;
184 AddMD5( &md5, ":", 1 );
185 AddMD5( &md5, psz_ent, 32 );
187 EndMD5( &md5 );
189 psz_HA2 = psz_md5_hash( &md5 );
190 if ( psz_HA2 == NULL )
191 goto error;
193 /* Request digest */
194 InitMD5( &md5 );
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 );
212 EndMD5( &md5 );
214 psz_result = psz_md5_hash( &md5 );
216 error:
217 free( psz_HA1 );
218 free( psz_HA2 );
219 free( psz_ent );
221 return psz_result;
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 )
257 /* FIXME */
258 return;
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" );
268 p_auth->i_nonce = 0;
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,
274 p_auth->psz_qop); */
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, ',' );
287 if ( psz_tmp )
288 *psz_tmp = '\0';
291 else
293 const char *psz_end = strchr( psz_header, ' ' );
294 if ( psz_end )
295 msg_Warn( p_this, "Unknown authentication scheme: '%*s'",
296 (int)(psz_end - psz_header), psz_header );
297 else
298 msg_Warn( p_this, "Unknown authentication scheme: '%s'",
299 psz_header );
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;
320 int i_nonce;
322 if ( psz_cnonce )
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;
329 goto error;
332 if ( psz_nc )
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 "
340 "value." );
341 i_err = VLC_EGENERIC;
342 goto error;
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
353 * digest.
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;
365 goto error;
369 if ( psz_nextnonce )
371 free( p_auth->psz_nonce );
372 p_auth->psz_nonce = psz_nextnonce;
373 psz_nextnonce = NULL;
376 error:
377 free( psz_nextnonce );
378 free( psz_qop );
379 free( psz_rspauth );
380 free( psz_cnonce );
381 free( psz_nc );
382 free( psz_digest );
384 return i_err;
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;
395 int i_rc;
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 );
406 goto error;
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 )
415 goto error;
418 ++p_auth->i_nonce;
420 psz_buffer = AuthDigest( p_this, p_auth, psz_method, psz_path,
421 psz_username, psz_password );
422 if ( psz_buffer == NULL )
423 goto error;
425 i_rc = asprintf( &psz_result,
426 "Digest "
427 /* Mandatory parameters */
428 "username=\"%s\", "
429 "realm=\"%s\", "
430 "nonce=\"%s\", "
431 "uri=\"%s\", "
432 "response=\"%s\", "
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 */
440 psz_username,
441 p_auth->psz_realm,
442 p_auth->psz_nonce,
443 psz_path ? psz_path : "/",
444 psz_buffer,
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",
460 p_auth->i_nonce
462 if ( i_rc < 0 )
463 goto error;
465 else
467 /* Basic Access Authentication */
468 i_rc = asprintf( &psz_buffer, "%s:%s", psz_username, psz_password );
469 if ( i_rc < 0 )
470 goto error;
472 psz_base64 = vlc_b64_encode( psz_buffer );
473 if ( psz_base64 == NULL )
474 goto error;
476 i_rc = asprintf( &psz_result, "Basic %s", psz_base64 );
477 if ( i_rc < 0 )
478 goto error;
481 error:
482 free( psz_buffer );
483 free( psz_base64 );
485 return psz_result;
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 );