]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[http] Handle parsing of WWW-Authenticate header within authentication scheme
authorMichael Brown <mcb30@ipxe.org>
Sat, 11 Nov 2017 22:05:53 +0000 (22:05 +0000)
committerMichael Brown <mcb30@ipxe.org>
Sun, 12 Nov 2017 18:52:04 +0000 (18:52 +0000)
Allow individual authentication schemes to parse WWW-Authenticate
headers that do not comply with RFC2617.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/http.h
src/net/tcp/httpauth.c
src/net/tcp/httpbasic.c
src/net/tcp/httpdigest.c

index a0dff7d007548a94c2ee1ab77d23b8f54b6f96fe..0f42a22e43a3314b4abae543eeac8693ca2076b9 100644 (file)
@@ -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
index bb327244d7a9dd5e6e11318a25c963c47e125a12..2c57e3d4810011cf2a8a36cdb0471678facedba7 100644 (file)
@@ -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;
 }
 
index 7ed7de9e7b2e17ea48e82353fc5c9393c5c89d79..52a67063d91d3aae0bcbb3ce7db1811dcc5c3fc9 100644 (file)
@@ -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,
 };
index 626dd7e9dc34417146e86800a00d7ca4b5da75a3..4074078c735cc0728a631da130933e31cf5d6087 100644 (file)
@@ -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,
 };