Skip to content

Commit

Permalink
[http] Add support for Digest authentication
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed May 22, 2012
1 parent 5f2226a commit 658c25a
Showing 1 changed file with 173 additions and 0 deletions.
173 changes: 173 additions & 0 deletions src/net/tcp/httpcore.c
Expand Up @@ -43,6 +43,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/process.h>
#include <ipxe/linebuf.h>
#include <ipxe/base64.h>
#include <ipxe/base16.h>
#include <ipxe/md5.h>
#include <ipxe/blockdev.h>
#include <ipxe/acpi.h>
#include <ipxe/http.h>
Expand Down Expand Up @@ -99,6 +101,8 @@ enum http_flags {
HTTP_TRY_AGAIN = 0x0010,
/** Provide Basic authentication details */
HTTP_BASIC_AUTH = 0x0020,
/** Provide Digest authentication details */
HTTP_DIGEST_AUTH = 0x0040,
};

/** HTTP receive state */
Expand Down Expand Up @@ -161,6 +165,13 @@ struct http_request {
struct line_buffer linebuf;
/** Receive data buffer (if applicable) */
userptr_t rx_buffer;

/** Authentication realm (if any) */
char *auth_realm;
/** Authentication nonce (if any) */
char *auth_nonce;
/** Authentication opaque string (if any) */
char *auth_opaque;
};

/**
Expand All @@ -174,6 +185,9 @@ static void http_free ( struct refcnt *refcnt ) {

uri_put ( http->uri );
empty_line_buffer ( &http->linebuf );
free ( http->auth_realm );
free ( http->auth_nonce );
free ( http->auth_opaque );
free ( http );
};

Expand Down Expand Up @@ -473,6 +487,81 @@ static int http_rx_basic_auth ( struct http_request *http, char *params ) {
return 0;
}

/**
* Parse Digest authentication parameter
*
* @v params Parameters
* @v name Parameter name (including trailing "=\"")
* @ret value Parameter value, or NULL
*/
static char * http_digest_param ( char *params, const char *name ) {
char *key;
char *value;
char *terminator;

/* Locate parameter */
key = strstr ( params, name );
if ( ! key )
return NULL;

/* Extract value */
value = ( key + strlen ( name ) );
terminator = strchr ( value, '"' );
if ( ! terminator )
return NULL;
return strndup ( value, ( terminator - value ) );
}

/**
* Handle WWW-Authenticate Digest header
*
* @v http HTTP request
* @v params Parameters
* @ret rc Return status code
*/
static int http_rx_digest_auth ( struct http_request *http, char *params ) {

DBGC ( http, "HTTP %p Digest authentication required (%s)\n",
http, params );

/* If we received a 401 Unauthorized response, then retry
* using Digest authentication
*/
if ( ( http->code == 401 ) &&
( ! ( http->flags & HTTP_DIGEST_AUTH ) ) &&
( http->uri->user != NULL ) ) {

/* Extract realm */
free ( http->auth_realm );
http->auth_realm = http_digest_param ( params, "realm=\"" );
if ( ! http->auth_realm ) {
DBGC ( http, "HTTP %p Digest prompt missing realm\n",
http );
return -EINVAL_HEADER;
}

/* Extract nonce */
free ( http->auth_nonce );
http->auth_nonce = http_digest_param ( params, "nonce=\"" );
if ( ! http->auth_nonce ) {
DBGC ( http, "HTTP %p Digest prompt missing nonce\n",
http );
return -EINVAL_HEADER;
}

/* Extract opaque */
free ( http->auth_opaque );
http->auth_opaque = http_digest_param ( params, "opaque=\"" );
if ( ! http->auth_opaque ) {
/* Not an error; "opaque" is optional */
}

http->flags |= ( HTTP_TRY_AGAIN | HTTP_DIGEST_AUTH );
}

return 0;
}

/** An HTTP WWW-Authenticate header handler */
struct http_auth_header_handler {
/** Scheme (e.g. "Basic") */
Expand All @@ -492,6 +581,10 @@ static struct http_auth_header_handler http_auth_header_handlers[] = {
.scheme = "Basic",
.rx = http_rx_basic_auth,
},
{
.scheme = "Digest",
.rx = http_rx_digest_auth,
},
{ NULL, NULL },
};

Expand Down Expand Up @@ -882,6 +975,80 @@ static char * http_basic_auth ( struct http_request *http ) {
return auth;
}

/**
* Generate HTTP Digest authorisation string
*
* @v http HTTP request
* @v method HTTP method (e.g. "GET")
* @v uri HTTP request URI (e.g. "/index.html")
* @ret auth Authorisation string, or NULL on error
*
* The authorisation string is dynamically allocated, and must be
* freed by the caller.
*/
static char * http_digest_auth ( struct http_request *http,
const char *method, const char *uri ) {
const char *user = http->uri->user;
const char *password =
( http->uri->password ? http->uri->password : "" );
const char *realm = http->auth_realm;
const char *nonce = http->auth_nonce;
const char *opaque = http->auth_opaque;
static const char colon = ':';
uint8_t ctx[MD5_CTX_SIZE];
uint8_t digest[MD5_DIGEST_SIZE];
char ha1[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
char ha2[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
char response[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ];
char *auth;
int len;

/* Sanity checks */
assert ( user != NULL );
assert ( realm != NULL );
assert ( nonce != NULL );

/* Generate HA1 */
digest_init ( &md5_algorithm, ctx );
digest_update ( &md5_algorithm, ctx, user, strlen ( user ) );
digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
digest_update ( &md5_algorithm, ctx, realm, strlen ( realm ) );
digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
digest_update ( &md5_algorithm, ctx, password, strlen ( password ) );
digest_final ( &md5_algorithm, ctx, digest );
base16_encode ( digest, sizeof ( digest ), ha1 );

/* Generate HA2 */
digest_init ( &md5_algorithm, ctx );
digest_update ( &md5_algorithm, ctx, method, strlen ( method ) );
digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
digest_update ( &md5_algorithm, ctx, uri, strlen ( uri ) );
digest_final ( &md5_algorithm, ctx, digest );
base16_encode ( digest, sizeof ( digest ), ha2 );

/* Generate response */
digest_init ( &md5_algorithm, ctx );
digest_update ( &md5_algorithm, ctx, ha1, strlen ( ha1 ) );
digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
digest_update ( &md5_algorithm, ctx, nonce, strlen ( nonce ) );
digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
digest_update ( &md5_algorithm, ctx, ha2, strlen ( ha2 ) );
digest_final ( &md5_algorithm, ctx, digest );
base16_encode ( digest, sizeof ( digest ), response );

/* Generate the authorisation string */
len = asprintf ( &auth, "Authorization: Digest username=\"%s\", "
"realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
"%s%s%sresponse=\"%s\"\r\n", user, realm, nonce, uri,
( opaque ? "opaque=\"" : "" ),
( opaque ? opaque : "" ),
( opaque ? "\", " : "" ), response );
if ( len < 0 )
return NULL;

return auth;
}

/**
* HTTP process
*
Expand Down Expand Up @@ -949,6 +1116,12 @@ static void http_step ( struct http_request *http ) {
rc = -ENOMEM;
goto err_auth;
}
} else if ( http->flags & HTTP_DIGEST_AUTH ) {
auth = http_digest_auth ( http, method, uri );
if ( ! auth ) {
rc = -ENOMEM;
goto err_auth;
}
} else {
auth = NULL;
}
Expand Down

0 comments on commit 658c25a

Please sign in to comment.