|
@@ -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
|
}
|