contrib: protobuf: Require version 3.1.0+
[vlc.git] / src / network / http_auth.c
blob07064ce226072fae0fd5dafe4b8c0014f0486de7
1 /*****************************************************************************
2 * http_auth.c: HTTP authentication for clients as per RFC2617
3 *****************************************************************************
4 * Copyright (C) 2001-2008 VLC authors and VideoLAN
5 * $Id$
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 /*****************************************************************************
28 * Preamble
29 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_http.h>
36 #include <vlc_md5.h>
37 #include <vlc_rand.h>
38 #include <vlc_strings.h>
40 #include "libvlc.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 );
51 if ( psz_header )
53 const char *psz_end;
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 );
60 else
62 return NULL;
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 );
71 if ( psz_header )
73 const char *psz_end;
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 );
82 else
84 return NULL;
88 static char *GenerateCnonce()
90 char ps_random[32];
91 struct md5_s md5;
93 vlc_rand_bytes( ps_random, sizeof( ps_random ) );
95 InitMD5( &md5 );
96 AddMD5( &md5, ps_random, sizeof( ps_random ) );
97 EndMD5( &md5 );
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;
110 char psz_inonce[9];
111 struct md5_s md5;
112 struct md5_s ent;
114 if ( p_auth->psz_realm == NULL )
116 msg_Warn( p_this, "Digest Authentication: "
117 "Mandatory 'realm' value not available" );
118 goto error;
121 /* H(A1) */
122 if ( p_auth->psz_HA1 )
124 psz_HA1 = strdup( p_auth->psz_HA1 );
125 if ( psz_HA1 == NULL )
126 goto error;
128 else
130 InitMD5( &md5 );
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 ) );
136 EndMD5( &md5 );
138 psz_HA1 = psz_md5_hash( &md5 );
139 if ( psz_HA1 == NULL )
140 goto error;
142 if ( p_auth->psz_algorithm &&
143 strcmp( p_auth->psz_algorithm, "MD5-sess" ) == 0 )
145 InitMD5( &md5 );
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 ) );
151 EndMD5( &md5 );
153 free( psz_HA1 );
155 psz_HA1 = psz_md5_hash( &md5 );
156 if ( psz_HA1 == NULL )
157 goto error;
159 p_auth->psz_HA1 = strdup( psz_HA1 );
160 if ( p_auth->psz_HA1 == NULL )
161 goto error;
165 /* H(A2) */
166 InitMD5( &md5 );
167 if ( *psz_method )
168 AddMD5( &md5, psz_method, strlen( psz_method ) );
169 AddMD5( &md5, ":", 1 );
170 if ( psz_path )
171 AddMD5( &md5, psz_path, strlen( psz_path ) );
172 else
173 AddMD5( &md5, "/", 1 );
174 if ( p_auth->psz_qop && strcmp( p_auth->psz_qop, "auth-int" ) == 0 )
176 InitMD5( &ent );
177 /* TODO: Support for "qop=auth-int" */
178 AddMD5( &ent, "", 0 );
179 EndMD5( &ent );
181 psz_ent = psz_md5_hash( &ent );
182 if ( psz_ent == NULL )
183 goto error;
185 AddMD5( &md5, ":", 1 );
186 AddMD5( &md5, psz_ent, 32 );
188 EndMD5( &md5 );
190 psz_HA2 = psz_md5_hash( &md5 );
191 if ( psz_HA2 == NULL )
192 goto error;
194 /* Request digest */
195 InitMD5( &md5 );
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 );
213 EndMD5( &md5 );
215 psz_result = psz_md5_hash( &md5 );
217 error:
218 free( psz_HA1 );
219 free( psz_HA2 );
220 free( psz_ent );
222 return psz_result;
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 )
258 /* FIXME */
259 return;
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" );
269 p_auth->i_nonce = 0;
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,
275 p_auth->psz_qop); */
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, ',' );
288 if ( psz_tmp )
289 *psz_tmp = '\0';
292 else
294 const char *psz_end = strchr( psz_header, ' ' );
295 if ( psz_end )
296 msg_Warn( p_this, "Unknown authentication scheme: '%*s'",
297 (int)(psz_end - psz_header), psz_header );
298 else
299 msg_Warn( p_this, "Unknown authentication scheme: '%s'",
300 psz_header );
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;
321 int i_nonce;
323 if ( psz_cnonce )
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;
330 goto error;
333 if ( psz_nc )
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 "
341 "value." );
342 i_err = VLC_EGENERIC;
343 goto error;
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
354 * digest.
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;
366 goto error;
370 if ( psz_nextnonce )
372 free( p_auth->psz_nonce );
373 p_auth->psz_nonce = psz_nextnonce;
374 psz_nextnonce = NULL;
377 error:
378 free( psz_nextnonce );
379 free( psz_qop );
380 free( psz_rspauth );
381 free( psz_cnonce );
382 free( psz_nc );
383 free( psz_digest );
385 return i_err;
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;
396 int i_rc;
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 );
407 goto error;
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 )
416 goto error;
419 ++p_auth->i_nonce;
421 psz_buffer = AuthDigest( p_this, p_auth, psz_method, psz_path,
422 psz_username, psz_password );
423 if ( psz_buffer == NULL )
424 goto error;
426 i_rc = asprintf( &psz_result,
427 "Digest "
428 /* Mandatory parameters */
429 "username=\"%s\", "
430 "realm=\"%s\", "
431 "nonce=\"%s\", "
432 "uri=\"%s\", "
433 "response=\"%s\", "
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 */
441 psz_username,
442 p_auth->psz_realm,
443 p_auth->psz_nonce,
444 psz_path ? psz_path : "/",
445 psz_buffer,
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",
461 p_auth->i_nonce
463 if ( i_rc < 0 )
464 goto error;
466 else
468 /* Basic Access Authentication */
469 i_rc = asprintf( &psz_buffer, "%s:%s", psz_username, psz_password );
470 if ( i_rc < 0 )
471 goto error;
473 psz_base64 = vlc_b64_encode( psz_buffer );
474 if ( psz_base64 == NULL )
475 goto error;
477 i_rc = asprintf( &psz_result, "Basic %s", psz_base64 );
478 if ( i_rc < 0 )
479 goto error;
482 error:
483 free( psz_buffer );
484 free( psz_base64 );
486 return psz_result;
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 );