From 96bd872c0342fcc290e9162154d07371405cf384 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 11 Nov 2017 22:05:53 +0000 Subject: [http] Handle parsing of WWW-Authenticate header within authentication scheme Allow individual authentication schemes to parse WWW-Authenticate headers that do not comply with RFC2617. Signed-off-by: Michael Brown --- src/include/ipxe/http.h | 55 +++++++++++++++++++++++++----- src/net/tcp/httpauth.c | 66 ++++-------------------------------- src/net/tcp/httpbasic.c | 24 +++++++++++-- src/net/tcp/httpdigest.c | 87 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 157 insertions(+), 75 deletions(-) diff --git a/src/include/ipxe/http.h b/src/include/ipxe/http.h index a0dff7d0..0f42a22e 100644 --- a/src/include/ipxe/http.h +++ b/src/include/ipxe/http.h @@ -150,14 +150,18 @@ struct http_request_content { size_t len; }; -/** HTTP request authentication descriptor */ -struct http_request_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP request Basic authentication descriptor */ +struct http_request_auth_basic { /** Username */ const char *username; /** Password */ const char *password; +}; + +/** HTTP request Digest authentication descriptor */ +struct http_request_auth_digest { + /** Username */ + const char *username; /** Quality of protection */ const char *qop; /** Algorithm */ @@ -168,6 +172,19 @@ struct http_request_auth { char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; }; +/** HTTP request authentication descriptor */ +struct http_request_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authentication descriptor */ + struct http_request_auth_basic basic; + /** Digest authentication descriptor */ + struct http_request_auth_digest digest; + }; +}; + /** An HTTP request * * This represents a single request to be sent to a server, including @@ -235,10 +252,12 @@ struct http_response_content { struct http_content_encoding *encoding; }; -/** HTTP response authorization descriptor */ -struct http_response_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP response Basic authorization descriptor */ +struct http_response_auth_basic { +}; + +/** HTTP response Digest authorization descriptor */ +struct http_response_auth_digest { /** Realm */ const char *realm; /** Quality of protection */ @@ -251,6 +270,19 @@ struct http_response_auth { const char *opaque; }; +/** HTTP response authorization descriptor */ +struct http_response_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authorization descriptor */ + struct http_response_auth_basic basic; + /** Digest authorization descriptor */ + struct http_response_auth_digest digest; + }; +}; + /** An HTTP response * * This represents a single response received from the server, @@ -461,6 +493,13 @@ struct http_content_encoding { struct http_authentication { /** Name (e.g. "Digest") */ const char *name; + /** Parse remaining "WWW-Authenticate" header line + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ + int ( * parse ) ( struct http_transaction *http, char *line ); /** Perform authentication * * @v http HTTP transaction diff --git a/src/net/tcp/httpauth.c b/src/net/tcp/httpauth.c index bb327244..2c57e3d4 100644 --- a/src/net/tcp/httpauth.c +++ b/src/net/tcp/httpauth.c @@ -54,46 +54,6 @@ static struct http_authentication * http_authentication ( const char *name ) { return NULL; } -/** An HTTP "WWW-Authenticate" response field */ -struct http_www_authenticate_field { - /** Name */ - const char *name; - /** Offset */ - size_t offset; -}; - -/** Define an HTTP "WWW-Authenticate" response field */ -#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ - .name = #_name, \ - .offset = offsetof ( struct http_transaction, \ - response.auth._name ), \ - } - -/** - * Set HTTP "WWW-Authenticate" response field value - * - * @v http HTTP transaction - * @v field Response field - * @v value Field value - */ -static inline void -http_www_auth_field ( struct http_transaction *http, - struct http_www_authenticate_field *field, char *value ) { - char **ptr; - - ptr = ( ( ( void * ) http ) + field->offset ); - *ptr = value; -} - -/** HTTP "WWW-Authenticate" fields */ -static struct http_www_authenticate_field http_www_auth_fields[] = { - HTTP_WWW_AUTHENTICATE_FIELD ( realm ), - HTTP_WWW_AUTHENTICATE_FIELD ( qop ), - HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), - HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), - HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), -}; - /** * Parse HTTP "WWW-Authenticate" header * @@ -103,18 +63,15 @@ static struct http_www_authenticate_field http_www_auth_fields[] = { */ static int http_parse_www_authenticate ( struct http_transaction *http, char *line ) { - struct http_www_authenticate_field *field; struct http_authentication *auth; char *name; - char *key; - char *value; - unsigned int i; + int rc; /* Get scheme name */ name = http_token ( &line, NULL ); if ( ! name ) { DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", - http, value ); + http, line ); return -EPROTO; } @@ -132,22 +89,13 @@ static int http_parse_www_authenticate ( struct http_transaction *http, return 0; http->response.auth.auth = auth; - /* Process fields */ - while ( ( key = http_token ( &line, &value ) ) ) { - for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / - sizeof ( http_www_auth_fields[0] ) ) ; i++){ - field = &http_www_auth_fields[i]; - if ( strcasecmp ( key, field->name ) == 0 ) - http_www_auth_field ( http, field, value ); - } + /* Parse remaining header line */ + if ( ( rc = auth->parse ( http, line ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse %s WWW-Authenticate " + "\"%s\": %s\n", http, name, line, strerror ( rc ) ); + return rc; } - /* Allow HTTP request to be retried if the request had not - * already tried authentication. - */ - if ( ! http->request.auth.auth ) - http->response.flags |= HTTP_RESPONSE_RETRY; - return 0; } diff --git a/src/net/tcp/httpbasic.c b/src/net/tcp/httpbasic.c index 7ed7de9e..52a67063 100644 --- a/src/net/tcp/httpbasic.c +++ b/src/net/tcp/httpbasic.c @@ -42,6 +42,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Basic authentication" ) +/** + * Parse HTTP "WWW-Authenticate" header for Basic authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_basic_auth ( struct http_transaction *http, + char *line __unused ) { + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Perform HTTP Basic authentication * @@ -49,7 +68,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret rc Return status code */ static int http_basic_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; /* Record username and password */ if ( ! http->uri->user ) { @@ -73,7 +92,7 @@ static int http_basic_authenticate ( struct http_transaction *http ) { */ static int http_format_basic_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + strlen ( req->password ) ); char user_pw[ user_pw_len + 1 /* NUL */ ]; @@ -93,6 +112,7 @@ static int http_format_basic_auth ( struct http_transaction *http, /** HTTP Basic authentication scheme */ struct http_authentication http_basic_auth __http_authentication = { .name = "Basic", + .parse = http_parse_basic_auth, .authenticate = http_basic_authenticate, .format = http_format_basic_auth, }; diff --git a/src/net/tcp/httpdigest.c b/src/net/tcp/httpdigest.c index 626dd7e9..4074078c 100644 --- a/src/net/tcp/httpdigest.c +++ b/src/net/tcp/httpdigest.c @@ -45,6 +45,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Digest authentication" ) +/** An HTTP Digest "WWW-Authenticate" response field */ +struct http_digest_field { + /** Name */ + const char *name; + /** Offset */ + size_t offset; +}; + +/** Define an HTTP Digest "WWW-Authenticate" response field */ +#define HTTP_DIGEST_FIELD( _name ) { \ + .name = #_name, \ + .offset = offsetof ( struct http_transaction, \ + response.auth.digest._name ), \ + } + +/** + * Set HTTP Digest "WWW-Authenticate" response field value + * + * @v http HTTP transaction + * @v field Response field + * @v value Field value + */ +static inline void +http_digest_field ( struct http_transaction *http, + struct http_digest_field *field, char *value ) { + char **ptr; + + ptr = ( ( ( void * ) http ) + field->offset ); + *ptr = value; +} + +/** HTTP Digest "WWW-Authenticate" fields */ +static struct http_digest_field http_digest_fields[] = { + HTTP_DIGEST_FIELD ( realm ), + HTTP_DIGEST_FIELD ( qop ), + HTTP_DIGEST_FIELD ( algorithm ), + HTTP_DIGEST_FIELD ( nonce ), + HTTP_DIGEST_FIELD ( opaque ), +}; + +/** + * Parse HTTP "WWW-Authenticate" header for Digest authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_digest_auth ( struct http_transaction *http, + char *line ) { + struct http_digest_field *field; + char *key; + char *value; + unsigned int i; + + /* Process fields */ + while ( ( key = http_token ( &line, &value ) ) ) { + for ( i = 0 ; i < ( sizeof ( http_digest_fields ) / + sizeof ( http_digest_fields[0] ) ) ; i++){ + field = &http_digest_fields[i]; + if ( strcasecmp ( key, field->name ) == 0 ) + http_digest_field ( http, field, value ); + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Initialise HTTP Digest * @@ -95,13 +168,14 @@ static void http_digest_final ( struct md5_context *ctx, char *out, * @ret rc Return status code */ static int http_digest_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; static const char md5sess[] = "MD5-sess"; static const char md5[] = "MD5"; struct md5_context ctx; + const char *password; /* Check for required response parameters */ if ( ! rsp->realm ) { @@ -122,7 +196,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { return -EACCES_USERNAME; } req->username = http->uri->user; - req->password = ( http->uri->password ? http->uri->password : "" ); + password = ( http->uri->password ? http->uri->password : "" ); /* Handle quality of protection */ if ( rsp->qop ) { @@ -146,7 +220,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { http_digest_init ( &ctx ); http_digest_update ( &ctx, req->username ); http_digest_update ( &ctx, rsp->realm ); - http_digest_update ( &ctx, req->password ); + http_digest_update ( &ctx, password ); http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); if ( req->algorithm == md5sess ) { http_digest_init ( &ctx ); @@ -187,8 +261,8 @@ static int http_digest_authenticate ( struct http_transaction *http ) { */ static int http_format_digest_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; size_t used = 0; /* Sanity checks */ @@ -225,6 +299,7 @@ static int http_format_digest_auth ( struct http_transaction *http, /** HTTP Digest authentication scheme */ struct http_authentication http_digest_auth __http_authentication = { .name = "Digest", + .parse = http_parse_digest_auth, .authenticate = http_digest_authenticate, .format = http_format_digest_auth, }; -- cgit v1.2.3-55-g7522