Parcourir la source

[http] Provide credentials only when requested by server

Provide HTTP Basic authentication credentials only in response to a
401 Unauthorized response from the server.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown il y a 12 ans
Parent
révision
5f2226aa36
1 fichiers modifiés avec 128 ajouts et 16 suppressions
  1. 128
    16
      src/net/tcp/httpcore.c

+ 128
- 16
src/net/tcp/httpcore.c Voir le fichier

@@ -95,6 +95,10 @@ enum http_flags {
95 95
 	HTTP_CLIENT_KEEPALIVE = 0x0004,
96 96
 	/** Server will keep connection alive */
97 97
 	HTTP_SERVER_KEEPALIVE = 0x0008,
98
+	/** Discard the current request and try again */
99
+	HTTP_TRY_AGAIN = 0x0010,
100
+	/** Provide Basic authentication details */
101
+	HTTP_BASIC_AUTH = 0x0020,
98 102
 };
99 103
 
100 104
 /** HTTP receive state */
@@ -257,10 +261,11 @@ static void http_done ( struct http_request *http ) {
257 261
 	assert ( http->chunk_remaining == 0 );
258 262
 
259 263
 	/* Close partial transfer interface */
260
-	intf_restart ( &http->partial, 0 );
264
+	if ( ! ( http->flags & HTTP_TRY_AGAIN ) )
265
+		intf_restart ( &http->partial, 0 );
261 266
 
262 267
 	/* Close everything unless we want to keep the connection alive */
263
-	if ( ! ( http->flags & HTTP_CLIENT_KEEPALIVE ) ) {
268
+	if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) {
264 269
 		http_close ( http, 0 );
265 270
 		return;
266 271
 	}
@@ -277,6 +282,14 @@ static void http_done ( struct http_request *http ) {
277 282
 		}
278 283
 	}
279 284
 	http->flags &= ~HTTP_SERVER_KEEPALIVE;
285
+
286
+	/* Retry the request if applicable */
287
+	if ( http->flags & HTTP_TRY_AGAIN ) {
288
+		http->flags &= ~HTTP_TRY_AGAIN;
289
+		http->flags |= HTTP_TX_PENDING;
290
+		http->rx_state = HTTP_RX_RESPONSE;
291
+		process_add ( &http->process );
292
+	}
280 293
 }
281 294
 
282 295
 /**
@@ -338,7 +351,7 @@ static int http_rx_response ( struct http_request *http, char *response ) {
338 351
  * @v value		HTTP header value
339 352
  * @ret rc		Return status code
340 353
  */
341
-static int http_rx_location ( struct http_request *http, const char *value ) {
354
+static int http_rx_location ( struct http_request *http, char *value ) {
342 355
 	int rc;
343 356
 
344 357
 	/* Redirect to new location */
@@ -360,8 +373,7 @@ static int http_rx_location ( struct http_request *http, const char *value ) {
360 373
  * @v value		HTTP header value
361 374
  * @ret rc		Return status code
362 375
  */
363
-static int http_rx_content_length ( struct http_request *http,
364
-				    const char *value ) {
376
+static int http_rx_content_length ( struct http_request *http, char *value ) {
365 377
 	struct block_device_capacity capacity;
366 378
 	size_t content_len;
367 379
 	char *endp;
@@ -385,6 +397,10 @@ static int http_rx_content_length ( struct http_request *http,
385 397
 	if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
386 398
 		http->remaining = content_len;
387 399
 
400
+	/* Do nothing more if we are retrying the request */
401
+	if ( http->flags & HTTP_TRY_AGAIN )
402
+		return 0;
403
+
388 404
 	/* Use seek() to notify recipient of filesize */
389 405
 	xfer_seek ( &http->xfer, http->remaining );
390 406
 	xfer_seek ( &http->xfer, 0 );
@@ -406,8 +422,7 @@ static int http_rx_content_length ( struct http_request *http,
406 422
  * @v value		HTTP header value
407 423
  * @ret rc		Return status code
408 424
  */
409
-static int http_rx_transfer_encoding ( struct http_request *http,
410
-				       const char *value ) {
425
+static int http_rx_transfer_encoding ( struct http_request *http, char *value ){
411 426
 
412 427
 	if ( strcasecmp ( value, "chunked" ) == 0 ) {
413 428
 		/* Mark connection as using chunked transfer encoding */
@@ -424,7 +439,7 @@ static int http_rx_transfer_encoding ( struct http_request *http,
424 439
  * @v value		HTTP header value
425 440
  * @ret rc		Return status code
426 441
  */
427
-static int http_rx_connection ( struct http_request *http, const char *value ) {
442
+static int http_rx_connection ( struct http_request *http, char *value ) {
428 443
 
429 444
 	if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
430 445
 		/* Mark connection as being kept alive by the server */
@@ -434,6 +449,88 @@ static int http_rx_connection ( struct http_request *http, const char *value ) {
434 449
 	return 0;
435 450
 }
436 451
 
452
+/**
453
+ * Handle WWW-Authenticate Basic header
454
+ *
455
+ * @v http		HTTP request
456
+ * @v params		Parameters
457
+ * @ret rc		Return status code
458
+ */
459
+static int http_rx_basic_auth ( struct http_request *http, char *params ) {
460
+
461
+	DBGC ( http, "HTTP %p Basic authentication required (%s)\n",
462
+	       http, params );
463
+
464
+	/* If we received a 401 Unauthorized response, then retry
465
+	 * using Basic authentication
466
+	 */
467
+	if ( ( http->code == 401 ) &&
468
+	     ( ! ( http->flags & HTTP_BASIC_AUTH ) ) &&
469
+	     ( http->uri->user != NULL ) ) {
470
+		http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH );
471
+	}
472
+
473
+	return 0;
474
+}
475
+
476
+/** An HTTP WWW-Authenticate header handler */
477
+struct http_auth_header_handler {
478
+	/** Scheme (e.g. "Basic") */
479
+	const char *scheme;
480
+	/** Handle received parameters
481
+	 *
482
+	 * @v http	HTTP request
483
+	 * @v params	Parameters
484
+	 * @ret rc	Return status code
485
+	 */
486
+	int ( * rx ) ( struct http_request *http, char *params );
487
+};
488
+
489
+/** List of HTTP WWW-Authenticate header handlers */
490
+static struct http_auth_header_handler http_auth_header_handlers[] = {
491
+	{
492
+		.scheme = "Basic",
493
+		.rx = http_rx_basic_auth,
494
+	},
495
+	{ NULL, NULL },
496
+};
497
+
498
+/**
499
+ * Handle HTTP WWW-Authenticate header
500
+ *
501
+ * @v http		HTTP request
502
+ * @v value		HTTP header value
503
+ * @ret rc		Return status code
504
+ */
505
+static int http_rx_www_authenticate ( struct http_request *http, char *value ) {
506
+	struct http_auth_header_handler *handler;
507
+	char *separator;
508
+	char *scheme;
509
+	char *params;
510
+	int rc;
511
+
512
+	/* Extract scheme */
513
+	separator = strchr ( value, ' ' );
514
+	if ( ! separator ) {
515
+		DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n",
516
+		       http );
517
+		return -EINVAL_HEADER;
518
+	}
519
+	*separator = '\0';
520
+	scheme = value;
521
+	params = ( separator + 1 );
522
+
523
+	/* Hand off to header handler, if one exists */
524
+	for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){
525
+		if ( strcasecmp ( scheme, handler->scheme ) == 0 ) {
526
+			if ( ( rc = handler->rx ( http, params ) ) != 0 )
527
+				return rc;
528
+			break;
529
+		}
530
+	}
531
+	return 0;
532
+}
533
+
437 534
 /** An HTTP header handler */
438 535
 struct http_header_handler {
439 536
 	/** Name (e.g. "Content-Length") */
@@ -446,7 +543,7 @@ struct http_header_handler {
446 543
 	 *
447 544
 	 * If an error is returned, the download will be aborted.
448 545
 	 */
449
-	int ( * rx ) ( struct http_request *http, const char *value );
546
+	int ( * rx ) ( struct http_request *http, char *value );
450 547
 };
451 548
 
452 549
 /** List of HTTP header handlers */
@@ -467,6 +564,10 @@ static struct http_header_handler http_header_handlers[] = {
467 564
 		.header = "Connection",
468 565
 		.rx = http_rx_connection,
469 566
 	},
567
+	{
568
+		.header = "WWW-Authenticate",
569
+		.rx = http_rx_www_authenticate,
570
+	},
470 571
 	{ NULL, NULL }
471 572
 };
472 573
 
@@ -488,8 +589,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
488 589
 		empty_line_buffer ( &http->linebuf );
489 590
 
490 591
 		/* Handle response code */
491
-		if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
492
-			return rc;
592
+		if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
593
+			if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
594
+				return rc;
595
+		}
493 596
 
494 597
 		/* Move to next state */
495 598
 		if ( ( http->rx_state == HTTP_RX_HEADER ) &&
@@ -497,6 +600,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
497 600
 			DBGC ( http, "HTTP %p start of data\n", http );
498 601
 			http->rx_state = ( http->chunked ?
499 602
 					   HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
603
+			if ( ( http->partial_len != 0 ) &&
604
+			     ( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) {
605
+				http->remaining = http->partial_len;
606
+			}
500 607
 			return 0;
501 608
 		} else {
502 609
 			DBGC ( http, "HTTP %p end of trailer\n", http );
@@ -560,8 +667,11 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) {
560 667
 	/* Use seek() to notify recipient of new filesize */
561 668
 	DBGC ( http, "HTTP %p start of chunk of length %zd\n",
562 669
 	       http, http->chunk_remaining );
563
-	xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
564
-	xfer_seek ( &http->xfer, http->rx_len );
670
+	if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
671
+		xfer_seek ( &http->xfer,
672
+			    ( http->rx_len + http->chunk_remaining ) );
673
+		xfer_seek ( &http->xfer, http->rx_len );
674
+	}
565 675
 
566 676
 	/* Start receiving data */
567 677
 	http->rx_state = HTTP_RX_DATA;
@@ -630,7 +740,10 @@ static int http_socket_deliver ( struct http_request *http,
630 740
 			     ( http->remaining < data_len ) ) {
631 741
 				data_len = http->remaining;
632 742
 			}
633
-			if ( http->rx_buffer != UNULL ) {
743
+			if ( http->flags & HTTP_TRY_AGAIN ) {
744
+				/* Discard all received data */
745
+				iob_pull ( iobuf, data_len );
746
+			} else if ( http->rx_buffer != UNULL ) {
634 747
 				/* Copy to partial transfer buffer */
635 748
 				copy_to_user ( http->rx_buffer, http->rx_len,
636 749
 					       iobuf->data, data_len );
@@ -830,7 +943,7 @@ static void http_step ( struct http_request *http ) {
830 943
 	}
831 944
 
832 945
 	/* Construct authorisation, if applicable */
833
-	if ( http->uri->user ) {
946
+	if ( http->flags & HTTP_BASIC_AUTH ) {
834 947
 		auth = http_basic_auth ( http );
835 948
 		if ( ! auth ) {
836 949
 			rc = -ENOMEM;
@@ -907,7 +1020,6 @@ static int http_partial_read ( struct http_request *http,
907 1020
 	http->rx_buffer = buffer;
908 1021
 	http->partial_start = offset;
909 1022
 	http->partial_len = len;
910
-	http->remaining = len;
911 1023
 
912 1024
 	/* Schedule request */
913 1025
 	http->rx_state = HTTP_RX_RESPONSE;

Chargement…
Annuler
Enregistrer