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