mirror of https://github.com/ipxe/ipxe.git
[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>pull/6/head
parent
8f5d44b5c6
commit
5f2226aa36
|
@ -95,6 +95,10 @@ enum http_flags {
|
||||||
HTTP_CLIENT_KEEPALIVE = 0x0004,
|
HTTP_CLIENT_KEEPALIVE = 0x0004,
|
||||||
/** Server will keep connection alive */
|
/** Server will keep connection alive */
|
||||||
HTTP_SERVER_KEEPALIVE = 0x0008,
|
HTTP_SERVER_KEEPALIVE = 0x0008,
|
||||||
|
/** Discard the current request and try again */
|
||||||
|
HTTP_TRY_AGAIN = 0x0010,
|
||||||
|
/** Provide Basic authentication details */
|
||||||
|
HTTP_BASIC_AUTH = 0x0020,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** HTTP receive state */
|
/** HTTP receive state */
|
||||||
|
@ -257,10 +261,11 @@ static void http_done ( struct http_request *http ) {
|
||||||
assert ( http->chunk_remaining == 0 );
|
assert ( http->chunk_remaining == 0 );
|
||||||
|
|
||||||
/* Close partial transfer interface */
|
/* Close partial transfer interface */
|
||||||
|
if ( ! ( http->flags & HTTP_TRY_AGAIN ) )
|
||||||
intf_restart ( &http->partial, 0 );
|
intf_restart ( &http->partial, 0 );
|
||||||
|
|
||||||
/* Close everything unless we want to keep the connection alive */
|
/* Close everything unless we want to keep the connection alive */
|
||||||
if ( ! ( http->flags & HTTP_CLIENT_KEEPALIVE ) ) {
|
if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) {
|
||||||
http_close ( http, 0 );
|
http_close ( http, 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -277,6 +282,14 @@ static void http_done ( struct http_request *http ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http->flags &= ~HTTP_SERVER_KEEPALIVE;
|
http->flags &= ~HTTP_SERVER_KEEPALIVE;
|
||||||
|
|
||||||
|
/* Retry the request if applicable */
|
||||||
|
if ( http->flags & HTTP_TRY_AGAIN ) {
|
||||||
|
http->flags &= ~HTTP_TRY_AGAIN;
|
||||||
|
http->flags |= HTTP_TX_PENDING;
|
||||||
|
http->rx_state = HTTP_RX_RESPONSE;
|
||||||
|
process_add ( &http->process );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -338,7 +351,7 @@ static int http_rx_response ( struct http_request *http, char *response ) {
|
||||||
* @v value HTTP header value
|
* @v value HTTP header value
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int http_rx_location ( struct http_request *http, const char *value ) {
|
static int http_rx_location ( struct http_request *http, char *value ) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Redirect to new location */
|
/* Redirect to new location */
|
||||||
|
@ -360,8 +373,7 @@ static int http_rx_location ( struct http_request *http, const char *value ) {
|
||||||
* @v value HTTP header value
|
* @v value HTTP header value
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int http_rx_content_length ( struct http_request *http,
|
static int http_rx_content_length ( struct http_request *http, char *value ) {
|
||||||
const char *value ) {
|
|
||||||
struct block_device_capacity capacity;
|
struct block_device_capacity capacity;
|
||||||
size_t content_len;
|
size_t content_len;
|
||||||
char *endp;
|
char *endp;
|
||||||
|
@ -385,6 +397,10 @@ static int http_rx_content_length ( struct http_request *http,
|
||||||
if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
|
if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
|
||||||
http->remaining = content_len;
|
http->remaining = content_len;
|
||||||
|
|
||||||
|
/* Do nothing more if we are retrying the request */
|
||||||
|
if ( http->flags & HTTP_TRY_AGAIN )
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Use seek() to notify recipient of filesize */
|
/* Use seek() to notify recipient of filesize */
|
||||||
xfer_seek ( &http->xfer, http->remaining );
|
xfer_seek ( &http->xfer, http->remaining );
|
||||||
xfer_seek ( &http->xfer, 0 );
|
xfer_seek ( &http->xfer, 0 );
|
||||||
|
@ -406,8 +422,7 @@ static int http_rx_content_length ( struct http_request *http,
|
||||||
* @v value HTTP header value
|
* @v value HTTP header value
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int http_rx_transfer_encoding ( struct http_request *http,
|
static int http_rx_transfer_encoding ( struct http_request *http, char *value ){
|
||||||
const char *value ) {
|
|
||||||
|
|
||||||
if ( strcasecmp ( value, "chunked" ) == 0 ) {
|
if ( strcasecmp ( value, "chunked" ) == 0 ) {
|
||||||
/* Mark connection as using chunked transfer encoding */
|
/* Mark connection as using chunked transfer encoding */
|
||||||
|
@ -424,7 +439,7 @@ static int http_rx_transfer_encoding ( struct http_request *http,
|
||||||
* @v value HTTP header value
|
* @v value HTTP header value
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int http_rx_connection ( struct http_request *http, const char *value ) {
|
static int http_rx_connection ( struct http_request *http, char *value ) {
|
||||||
|
|
||||||
if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
|
if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
|
||||||
/* Mark connection as being kept alive by the server */
|
/* 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 ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle WWW-Authenticate Basic header
|
||||||
|
*
|
||||||
|
* @v http HTTP request
|
||||||
|
* @v params Parameters
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int http_rx_basic_auth ( struct http_request *http, char *params ) {
|
||||||
|
|
||||||
|
DBGC ( http, "HTTP %p Basic authentication required (%s)\n",
|
||||||
|
http, params );
|
||||||
|
|
||||||
|
/* If we received a 401 Unauthorized response, then retry
|
||||||
|
* using Basic authentication
|
||||||
|
*/
|
||||||
|
if ( ( http->code == 401 ) &&
|
||||||
|
( ! ( http->flags & HTTP_BASIC_AUTH ) ) &&
|
||||||
|
( http->uri->user != NULL ) ) {
|
||||||
|
http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An HTTP WWW-Authenticate header handler */
|
||||||
|
struct http_auth_header_handler {
|
||||||
|
/** Scheme (e.g. "Basic") */
|
||||||
|
const char *scheme;
|
||||||
|
/** Handle received parameters
|
||||||
|
*
|
||||||
|
* @v http HTTP request
|
||||||
|
* @v params Parameters
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int ( * rx ) ( struct http_request *http, char *params );
|
||||||
|
};
|
||||||
|
|
||||||
|
/** List of HTTP WWW-Authenticate header handlers */
|
||||||
|
static struct http_auth_header_handler http_auth_header_handlers[] = {
|
||||||
|
{
|
||||||
|
.scheme = "Basic",
|
||||||
|
.rx = http_rx_basic_auth,
|
||||||
|
},
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle HTTP WWW-Authenticate header
|
||||||
|
*
|
||||||
|
* @v http HTTP request
|
||||||
|
* @v value HTTP header value
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int http_rx_www_authenticate ( struct http_request *http, char *value ) {
|
||||||
|
struct http_auth_header_handler *handler;
|
||||||
|
char *separator;
|
||||||
|
char *scheme;
|
||||||
|
char *params;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Extract scheme */
|
||||||
|
separator = strchr ( value, ' ' );
|
||||||
|
if ( ! separator ) {
|
||||||
|
DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n",
|
||||||
|
http );
|
||||||
|
return -EINVAL_HEADER;
|
||||||
|
}
|
||||||
|
*separator = '\0';
|
||||||
|
scheme = value;
|
||||||
|
params = ( separator + 1 );
|
||||||
|
|
||||||
|
/* Hand off to header handler, if one exists */
|
||||||
|
for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){
|
||||||
|
if ( strcasecmp ( scheme, handler->scheme ) == 0 ) {
|
||||||
|
if ( ( rc = handler->rx ( http, params ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** An HTTP header handler */
|
/** An HTTP header handler */
|
||||||
struct http_header_handler {
|
struct http_header_handler {
|
||||||
/** Name (e.g. "Content-Length") */
|
/** Name (e.g. "Content-Length") */
|
||||||
|
@ -446,7 +543,7 @@ struct http_header_handler {
|
||||||
*
|
*
|
||||||
* If an error is returned, the download will be aborted.
|
* If an error is returned, the download will be aborted.
|
||||||
*/
|
*/
|
||||||
int ( * rx ) ( struct http_request *http, const char *value );
|
int ( * rx ) ( struct http_request *http, char *value );
|
||||||
};
|
};
|
||||||
|
|
||||||
/** List of HTTP header handlers */
|
/** List of HTTP header handlers */
|
||||||
|
@ -467,6 +564,10 @@ static struct http_header_handler http_header_handlers[] = {
|
||||||
.header = "Connection",
|
.header = "Connection",
|
||||||
.rx = http_rx_connection,
|
.rx = http_rx_connection,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.header = "WWW-Authenticate",
|
||||||
|
.rx = http_rx_www_authenticate,
|
||||||
|
},
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -488,8 +589,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
|
||||||
empty_line_buffer ( &http->linebuf );
|
empty_line_buffer ( &http->linebuf );
|
||||||
|
|
||||||
/* Handle response code */
|
/* Handle response code */
|
||||||
|
if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
|
||||||
if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
|
if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
|
||||||
return rc;
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/* Move to next state */
|
/* Move to next state */
|
||||||
if ( ( http->rx_state == HTTP_RX_HEADER ) &&
|
if ( ( http->rx_state == HTTP_RX_HEADER ) &&
|
||||||
|
@ -497,6 +600,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
|
||||||
DBGC ( http, "HTTP %p start of data\n", http );
|
DBGC ( http, "HTTP %p start of data\n", http );
|
||||||
http->rx_state = ( http->chunked ?
|
http->rx_state = ( http->chunked ?
|
||||||
HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
|
HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
|
||||||
|
if ( ( http->partial_len != 0 ) &&
|
||||||
|
( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) {
|
||||||
|
http->remaining = http->partial_len;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
DBGC ( http, "HTTP %p end of trailer\n", http );
|
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 ) {
|
||||||
/* Use seek() to notify recipient of new filesize */
|
/* Use seek() to notify recipient of new filesize */
|
||||||
DBGC ( http, "HTTP %p start of chunk of length %zd\n",
|
DBGC ( http, "HTTP %p start of chunk of length %zd\n",
|
||||||
http, http->chunk_remaining );
|
http, http->chunk_remaining );
|
||||||
xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
|
if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
|
||||||
|
xfer_seek ( &http->xfer,
|
||||||
|
( http->rx_len + http->chunk_remaining ) );
|
||||||
xfer_seek ( &http->xfer, http->rx_len );
|
xfer_seek ( &http->xfer, http->rx_len );
|
||||||
|
}
|
||||||
|
|
||||||
/* Start receiving data */
|
/* Start receiving data */
|
||||||
http->rx_state = HTTP_RX_DATA;
|
http->rx_state = HTTP_RX_DATA;
|
||||||
|
@ -630,7 +740,10 @@ static int http_socket_deliver ( struct http_request *http,
|
||||||
( http->remaining < data_len ) ) {
|
( http->remaining < data_len ) ) {
|
||||||
data_len = http->remaining;
|
data_len = http->remaining;
|
||||||
}
|
}
|
||||||
if ( http->rx_buffer != UNULL ) {
|
if ( http->flags & HTTP_TRY_AGAIN ) {
|
||||||
|
/* Discard all received data */
|
||||||
|
iob_pull ( iobuf, data_len );
|
||||||
|
} else if ( http->rx_buffer != UNULL ) {
|
||||||
/* Copy to partial transfer buffer */
|
/* Copy to partial transfer buffer */
|
||||||
copy_to_user ( http->rx_buffer, http->rx_len,
|
copy_to_user ( http->rx_buffer, http->rx_len,
|
||||||
iobuf->data, data_len );
|
iobuf->data, data_len );
|
||||||
|
@ -830,7 +943,7 @@ static void http_step ( struct http_request *http ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Construct authorisation, if applicable */
|
/* Construct authorisation, if applicable */
|
||||||
if ( http->uri->user ) {
|
if ( http->flags & HTTP_BASIC_AUTH ) {
|
||||||
auth = http_basic_auth ( http );
|
auth = http_basic_auth ( http );
|
||||||
if ( ! auth ) {
|
if ( ! auth ) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
@ -907,7 +1020,6 @@ static int http_partial_read ( struct http_request *http,
|
||||||
http->rx_buffer = buffer;
|
http->rx_buffer = buffer;
|
||||||
http->partial_start = offset;
|
http->partial_start = offset;
|
||||||
http->partial_len = len;
|
http->partial_len = len;
|
||||||
http->remaining = len;
|
|
||||||
|
|
||||||
/* Schedule request */
|
/* Schedule request */
|
||||||
http->rx_state = HTTP_RX_RESPONSE;
|
http->rx_state = HTTP_RX_RESPONSE;
|
||||||
|
|
Loading…
Reference in New Issue