Browse Source

[http] Add support for Digest authentication

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 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
 #include <ipxe/process.h>
43
 #include <ipxe/process.h>
44
 #include <ipxe/linebuf.h>
44
 #include <ipxe/linebuf.h>
45
 #include <ipxe/base64.h>
45
 #include <ipxe/base64.h>
46
+#include <ipxe/base16.h>
47
+#include <ipxe/md5.h>
46
 #include <ipxe/blockdev.h>
48
 #include <ipxe/blockdev.h>
47
 #include <ipxe/acpi.h>
49
 #include <ipxe/acpi.h>
48
 #include <ipxe/http.h>
50
 #include <ipxe/http.h>
99
 	HTTP_TRY_AGAIN = 0x0010,
101
 	HTTP_TRY_AGAIN = 0x0010,
100
 	/** Provide Basic authentication details */
102
 	/** Provide Basic authentication details */
101
 	HTTP_BASIC_AUTH = 0x0020,
103
 	HTTP_BASIC_AUTH = 0x0020,
104
+	/** Provide Digest authentication details */
105
+	HTTP_DIGEST_AUTH = 0x0040,
102
 };
106
 };
103
 
107
 
104
 /** HTTP receive state */
108
 /** HTTP receive state */
161
 	struct line_buffer linebuf;
165
 	struct line_buffer linebuf;
162
 	/** Receive data buffer (if applicable) */
166
 	/** Receive data buffer (if applicable) */
163
 	userptr_t rx_buffer;
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
 
185
 
175
 	uri_put ( http->uri );
186
 	uri_put ( http->uri );
176
 	empty_line_buffer ( &http->linebuf );
187
 	empty_line_buffer ( &http->linebuf );
188
+	free ( http->auth_realm );
189
+	free ( http->auth_nonce );
190
+	free ( http->auth_opaque );
177
 	free ( http );
191
 	free ( http );
178
 };
192
 };
179
 
193
 
473
 	return 0;
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
 /** An HTTP WWW-Authenticate header handler */
565
 /** An HTTP WWW-Authenticate header handler */
477
 struct http_auth_header_handler {
566
 struct http_auth_header_handler {
478
 	/** Scheme (e.g. "Basic") */
567
 	/** Scheme (e.g. "Basic") */
492
 		.scheme = "Basic",
581
 		.scheme = "Basic",
493
 		.rx = http_rx_basic_auth,
582
 		.rx = http_rx_basic_auth,
494
 	},
583
 	},
584
+	{
585
+		.scheme = "Digest",
586
+		.rx = http_rx_digest_auth,
587
+	},
495
 	{ NULL, NULL },
588
 	{ NULL, NULL },
496
 };
589
 };
497
 
590
 
882
 	return auth;
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
  * HTTP process
1053
  * HTTP process
887
  *
1054
  *
949
 			rc = -ENOMEM;
1116
 			rc = -ENOMEM;
950
 			goto err_auth;
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
 	} else {
1125
 	} else {
953
 		auth = NULL;
1126
 		auth = NULL;
954
 	}
1127
 	}

Loading…
Cancel
Save