Browse Source

[http] Add support for Digest authentication

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
658c25aa82
1 changed files with 173 additions and 0 deletions
  1. 173
    0
      src/net/tcp/httpcore.c

+ 173
- 0
src/net/tcp/httpcore.c View File

@@ -43,6 +43,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
43 43
 #include <ipxe/process.h>
44 44
 #include <ipxe/linebuf.h>
45 45
 #include <ipxe/base64.h>
46
+#include <ipxe/base16.h>
47
+#include <ipxe/md5.h>
46 48
 #include <ipxe/blockdev.h>
47 49
 #include <ipxe/acpi.h>
48 50
 #include <ipxe/http.h>
@@ -99,6 +101,8 @@ enum http_flags {
99 101
 	HTTP_TRY_AGAIN = 0x0010,
100 102
 	/** Provide Basic authentication details */
101 103
 	HTTP_BASIC_AUTH = 0x0020,
104
+	/** Provide Digest authentication details */
105
+	HTTP_DIGEST_AUTH = 0x0040,
102 106
 };
103 107
 
104 108
 /** HTTP receive state */
@@ -161,6 +165,13 @@ struct http_request {
161 165
 	struct line_buffer linebuf;
162 166
 	/** Receive data buffer (if applicable) */
163 167
 	userptr_t rx_buffer;
168
+
169
+	/** Authentication realm (if any) */
170
+	char *auth_realm;
171
+	/** Authentication nonce (if any) */
172
+	char *auth_nonce;
173
+	/** Authentication opaque string (if any) */
174
+	char *auth_opaque;
164 175
 };
165 176
 
166 177
 /**
@@ -174,6 +185,9 @@ static void http_free ( struct refcnt *refcnt ) {
174 185
 
175 186
 	uri_put ( http->uri );
176 187
 	empty_line_buffer ( &http->linebuf );
188
+	free ( http->auth_realm );
189
+	free ( http->auth_nonce );
190
+	free ( http->auth_opaque );
177 191
 	free ( http );
178 192
 };
179 193
 
@@ -473,6 +487,81 @@ static int http_rx_basic_auth ( struct http_request *http, char *params ) {
473 487
 	return 0;
474 488
 }
475 489
 
490
+/**
491
+ * Parse Digest authentication parameter
492
+ *
493
+ * @v params		Parameters
494
+ * @v name		Parameter name (including trailing "=\"")
495
+ * @ret value		Parameter value, or NULL
496
+ */
497
+static char * http_digest_param ( char *params, const char *name ) {
498
+	char *key;
499
+	char *value;
500
+	char *terminator;
501
+
502
+	/* Locate parameter */
503
+	key = strstr ( params, name );
504
+	if ( ! key )
505
+		return NULL;
506
+
507
+	/* Extract value */
508
+	value = ( key + strlen ( name ) );
509
+	terminator = strchr ( value, '"' );
510
+	if ( ! terminator )
511
+		return NULL;
512
+	return strndup ( value, ( terminator - value ) );
513
+}
514
+
515
+/**
516
+ * Handle WWW-Authenticate Digest header
517
+ *
518
+ * @v http		HTTP request
519
+ * @v params		Parameters
520
+ * @ret rc		Return status code
521
+ */
522
+static int http_rx_digest_auth ( struct http_request *http, char *params ) {
523
+
524
+	DBGC ( http, "HTTP %p Digest authentication required (%s)\n",
525
+	       http, params );
526
+
527
+	/* If we received a 401 Unauthorized response, then retry
528
+	 * using Digest authentication
529
+	 */
530
+	if ( ( http->code == 401 ) &&
531
+	     ( ! ( http->flags & HTTP_DIGEST_AUTH ) ) &&
532
+	     ( http->uri->user != NULL ) ) {
533
+
534
+		/* Extract realm */
535
+		free ( http->auth_realm );
536
+		http->auth_realm = http_digest_param ( params, "realm=\"" );
537
+		if ( ! http->auth_realm ) {
538
+			DBGC ( http, "HTTP %p Digest prompt missing realm\n",
539
+			       http );
540
+			return -EINVAL_HEADER;
541
+		}
542
+
543
+		/* Extract nonce */
544
+		free ( http->auth_nonce );
545
+		http->auth_nonce = http_digest_param ( params, "nonce=\"" );
546
+		if ( ! http->auth_nonce ) {
547
+			DBGC ( http, "HTTP %p Digest prompt missing nonce\n",
548
+			       http );
549
+			return -EINVAL_HEADER;
550
+		}
551
+
552
+		/* Extract opaque */
553
+		free ( http->auth_opaque );
554
+		http->auth_opaque = http_digest_param ( params, "opaque=\"" );
555
+		if ( ! http->auth_opaque ) {
556
+			/* Not an error; "opaque" is optional */
557
+		}
558
+
559
+		http->flags |= ( HTTP_TRY_AGAIN | HTTP_DIGEST_AUTH );
560
+	}
561
+
562
+	return 0;
563
+}
564
+
476 565
 /** An HTTP WWW-Authenticate header handler */
477 566
 struct http_auth_header_handler {
478 567
 	/** Scheme (e.g. "Basic") */
@@ -492,6 +581,10 @@ static struct http_auth_header_handler http_auth_header_handlers[] = {
492 581
 		.scheme = "Basic",
493 582
 		.rx = http_rx_basic_auth,
494 583
 	},
584
+	{
585
+		.scheme = "Digest",
586
+		.rx = http_rx_digest_auth,
587
+	},
495 588
 	{ NULL, NULL },
496 589
 };
497 590
 
@@ -882,6 +975,80 @@ static char * http_basic_auth ( struct http_request *http ) {
882 975
 	return auth;
883 976
 }
884 977
 
978
+/**
979
+ * Generate HTTP Digest authorisation string
980
+ *
981
+ * @v http		HTTP request
982
+ * @v method		HTTP method (e.g. "GET")
983
+ * @v uri		HTTP request URI (e.g. "/index.html")
984
+ * @ret auth		Authorisation string, or NULL on error
985
+ *
986
+ * The authorisation string is dynamically allocated, and must be
987
+ * freed by the caller.
988
+ */
989
+static char * http_digest_auth ( struct http_request *http,
990
+				 const char *method, const char *uri ) {
991
+	const char *user = http->uri->user;
992
+	const char *password =
993
+		( http->uri->password ? http->uri->password : "" );
994
+	const char *realm = http->auth_realm;
995
+	const char *nonce = http->auth_nonce;
996
+	const char *opaque = http->auth_opaque;
997
+	static const char colon = ':';
998
+	uint8_t ctx[MD5_CTX_SIZE];
999
+	uint8_t digest[MD5_DIGEST_SIZE];
1000
+	char ha1[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
1001
+	char ha2[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
1002
+	char response[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
1003
+	char *auth;
1004
+	int len;
1005
+
1006
+	/* Sanity checks */
1007
+	assert ( user != NULL );
1008
+	assert ( realm != NULL );
1009
+	assert ( nonce != NULL );
1010
+
1011
+	/* Generate HA1 */
1012
+	digest_init ( &md5_algorithm, ctx );
1013
+	digest_update ( &md5_algorithm, ctx, user, strlen ( user ) );
1014
+	digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
1015
+	digest_update ( &md5_algorithm, ctx, realm, strlen ( realm ) );
1016
+	digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
1017
+	digest_update ( &md5_algorithm, ctx, password, strlen ( password ) );
1018
+	digest_final ( &md5_algorithm, ctx, digest );
1019
+	base16_encode ( digest, sizeof ( digest ), ha1 );
1020
+
1021
+	/* Generate HA2 */
1022
+	digest_init ( &md5_algorithm, ctx );
1023
+	digest_update ( &md5_algorithm, ctx, method, strlen ( method ) );
1024
+	digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
1025
+	digest_update ( &md5_algorithm, ctx, uri, strlen ( uri ) );
1026
+	digest_final ( &md5_algorithm, ctx, digest );
1027
+	base16_encode ( digest, sizeof ( digest ), ha2 );
1028
+
1029
+	/* Generate response */
1030
+	digest_init ( &md5_algorithm, ctx );
1031
+	digest_update ( &md5_algorithm, ctx, ha1, strlen ( ha1 ) );
1032
+	digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
1033
+	digest_update ( &md5_algorithm, ctx, nonce, strlen ( nonce ) );
1034
+	digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
1035
+	digest_update ( &md5_algorithm, ctx, ha2, strlen ( ha2 ) );
1036
+	digest_final ( &md5_algorithm, ctx, digest );
1037
+	base16_encode ( digest, sizeof ( digest ), response );
1038
+
1039
+	/* Generate the authorisation string */
1040
+	len = asprintf ( &auth, "Authorization: Digest username=\"%s\", "
1041
+			 "realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
1042
+			 "%s%s%sresponse=\"%s\"\r\n", user, realm, nonce, uri,
1043
+			 ( opaque ? "opaque=\"" : "" ),
1044
+			 ( opaque ? opaque : "" ),
1045
+			 ( opaque ? "\", " : "" ), response );
1046
+	if ( len < 0 )
1047
+		return NULL;
1048
+
1049
+	return auth;
1050
+}
1051
+
885 1052
 /**
886 1053
  * HTTP process
887 1054
  *
@@ -949,6 +1116,12 @@ static void http_step ( struct http_request *http ) {
949 1116
 			rc = -ENOMEM;
950 1117
 			goto err_auth;
951 1118
 		}
1119
+	} else if ( http->flags & HTTP_DIGEST_AUTH ) {
1120
+		auth = http_digest_auth ( http, method, uri );
1121
+		if ( ! auth ) {
1122
+			rc = -ENOMEM;
1123
+			goto err_auth;
1124
+		}
952 1125
 	} else {
953 1126
 		auth = NULL;
954 1127
 	}

Loading…
Cancel
Save