Browse 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 12 years ago
parent
commit
5f2226aa36
1 changed files with 128 additions and 16 deletions
  1. 128
    16
      src/net/tcp/httpcore.c

+ 128
- 16
src/net/tcp/httpcore.c View File

95
 	HTTP_CLIENT_KEEPALIVE = 0x0004,
95
 	HTTP_CLIENT_KEEPALIVE = 0x0004,
96
 	/** Server will keep connection alive */
96
 	/** Server will keep connection alive */
97
 	HTTP_SERVER_KEEPALIVE = 0x0008,
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
 /** HTTP receive state */
104
 /** HTTP receive state */
257
 	assert ( http->chunk_remaining == 0 );
261
 	assert ( http->chunk_remaining == 0 );
258
 
262
 
259
 	/* Close partial transfer interface */
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
 	/* Close everything unless we want to keep the connection alive */
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
 		http_close ( http, 0 );
269
 		http_close ( http, 0 );
265
 		return;
270
 		return;
266
 	}
271
 	}
277
 		}
282
 		}
278
 	}
283
 	}
279
 	http->flags &= ~HTTP_SERVER_KEEPALIVE;
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
  * @v value		HTTP header value
351
  * @v value		HTTP header value
339
  * @ret rc		Return status code
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
 	int rc;
355
 	int rc;
343
 
356
 
344
 	/* Redirect to new location */
357
 	/* Redirect to new location */
360
  * @v value		HTTP header value
373
  * @v value		HTTP header value
361
  * @ret rc		Return status code
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
 	struct block_device_capacity capacity;
377
 	struct block_device_capacity capacity;
366
 	size_t content_len;
378
 	size_t content_len;
367
 	char *endp;
379
 	char *endp;
385
 	if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
397
 	if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
386
 		http->remaining = content_len;
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
 	/* Use seek() to notify recipient of filesize */
404
 	/* Use seek() to notify recipient of filesize */
389
 	xfer_seek ( &http->xfer, http->remaining );
405
 	xfer_seek ( &http->xfer, http->remaining );
390
 	xfer_seek ( &http->xfer, 0 );
406
 	xfer_seek ( &http->xfer, 0 );
406
  * @v value		HTTP header value
422
  * @v value		HTTP header value
407
  * @ret rc		Return status code
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
 	if ( strcasecmp ( value, "chunked" ) == 0 ) {
427
 	if ( strcasecmp ( value, "chunked" ) == 0 ) {
413
 		/* Mark connection as using chunked transfer encoding */
428
 		/* Mark connection as using chunked transfer encoding */
424
  * @v value		HTTP header value
439
  * @v value		HTTP header value
425
  * @ret rc		Return status code
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
 	if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
444
 	if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
430
 		/* Mark connection as being kept alive by the server */
445
 		/* Mark connection as being kept alive by the server */
434
 	return 0;
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
 /** An HTTP header handler */
534
 /** An HTTP header handler */
438
 struct http_header_handler {
535
 struct http_header_handler {
439
 	/** Name (e.g. "Content-Length") */
536
 	/** Name (e.g. "Content-Length") */
446
 	 *
543
 	 *
447
 	 * If an error is returned, the download will be aborted.
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
 /** List of HTTP header handlers */
549
 /** List of HTTP header handlers */
467
 		.header = "Connection",
564
 		.header = "Connection",
468
 		.rx = http_rx_connection,
565
 		.rx = http_rx_connection,
469
 	},
566
 	},
567
+	{
568
+		.header = "WWW-Authenticate",
569
+		.rx = http_rx_www_authenticate,
570
+	},
470
 	{ NULL, NULL }
571
 	{ NULL, NULL }
471
 };
572
 };
472
 
573
 
488
 		empty_line_buffer ( &http->linebuf );
589
 		empty_line_buffer ( &http->linebuf );
489
 
590
 
490
 		/* Handle response code */
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
 		/* Move to next state */
597
 		/* Move to next state */
495
 		if ( ( http->rx_state == HTTP_RX_HEADER ) &&
598
 		if ( ( http->rx_state == HTTP_RX_HEADER ) &&
497
 			DBGC ( http, "HTTP %p start of data\n", http );
600
 			DBGC ( http, "HTTP %p start of data\n", http );
498
 			http->rx_state = ( http->chunked ?
601
 			http->rx_state = ( http->chunked ?
499
 					   HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
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
 			return 0;
607
 			return 0;
501
 		} else {
608
 		} else {
502
 			DBGC ( http, "HTTP %p end of trailer\n", http );
609
 			DBGC ( http, "HTTP %p end of trailer\n", http );
560
 	/* Use seek() to notify recipient of new filesize */
667
 	/* Use seek() to notify recipient of new filesize */
561
 	DBGC ( http, "HTTP %p start of chunk of length %zd\n",
668
 	DBGC ( http, "HTTP %p start of chunk of length %zd\n",
562
 	       http, http->chunk_remaining );
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
 	/* Start receiving data */
676
 	/* Start receiving data */
567
 	http->rx_state = HTTP_RX_DATA;
677
 	http->rx_state = HTTP_RX_DATA;
630
 			     ( http->remaining < data_len ) ) {
740
 			     ( http->remaining < data_len ) ) {
631
 				data_len = http->remaining;
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
 				/* Copy to partial transfer buffer */
747
 				/* Copy to partial transfer buffer */
635
 				copy_to_user ( http->rx_buffer, http->rx_len,
748
 				copy_to_user ( http->rx_buffer, http->rx_len,
636
 					       iobuf->data, data_len );
749
 					       iobuf->data, data_len );
830
 	}
943
 	}
831
 
944
 
832
 	/* Construct authorisation, if applicable */
945
 	/* Construct authorisation, if applicable */
833
-	if ( http->uri->user ) {
946
+	if ( http->flags & HTTP_BASIC_AUTH ) {
834
 		auth = http_basic_auth ( http );
947
 		auth = http_basic_auth ( http );
835
 		if ( ! auth ) {
948
 		if ( ! auth ) {
836
 			rc = -ENOMEM;
949
 			rc = -ENOMEM;
907
 	http->rx_buffer = buffer;
1020
 	http->rx_buffer = buffer;
908
 	http->partial_start = offset;
1021
 	http->partial_start = offset;
909
 	http->partial_len = len;
1022
 	http->partial_len = len;
910
-	http->remaining = len;
911
 
1023
 
912
 	/* Schedule request */
1024
 	/* Schedule request */
913
 	http->rx_state = HTTP_RX_RESPONSE;
1025
 	http->rx_state = HTTP_RX_RESPONSE;

Loading…
Cancel
Save