summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2017-11-11 23:05:53 +0100
committerMichael Brown2017-11-12 19:52:04 +0100
commit96bd872c0342fcc290e9162154d07371405cf384 (patch)
treedcae7f76bfc6a5d86f839d545c85849367fd7b5d
parent[http] Gracefully handle offers of multiple authentication schemes (diff)
downloadipxe-96bd872c0342fcc290e9162154d07371405cf384.tar.gz
ipxe-96bd872c0342fcc290e9162154d07371405cf384.tar.xz
ipxe-96bd872c0342fcc290e9162154d07371405cf384.zip
[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 <mcb30@ipxe.org>
-rw-r--r--src/include/ipxe/http.h55
-rw-r--r--src/net/tcp/httpauth.c66
-rw-r--r--src/net/tcp/httpbasic.c24
-rw-r--r--src/net/tcp/httpdigest.c87
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
@@ -43,13 +43,32 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
"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
*
* @v http HTTP transaction
* @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,
};