summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Orton <joe@manyfish.uk>2020-06-08 19:47:10 +0100
committerJoe Orton <jorton@apache.org>2020-06-18 08:01:27 +0100
commit86cd1877471ae0cb701167e737124ebc916313ae (patch)
tree00ba09401e7697015c1e556b3ba0f2ee6dc2e352
parent4f59da23e8d08bd6cf640f83812073ca968af527 (diff)
downloadneon-git-86cd1877471ae0cb701167e737124ebc916313ae.tar.gz
Support hashed usernames per RFC7616.
* src/ne_auth.c (struct auth_challenge): Add userhash field. (auth_session): Likewise. (digest_challenge): Calculate userhash if challenge used userhash=true. (request_digest): Use userhash, userhash=true as appropriate. (auth_challenge): Parse userhash parameter. (clean_session): Free userhash. * test/auth.c (verify_digest_header, make_digest_header, serve_digest): Add userhash support. (digest): Test for userhash=true and userhash=false.
-rw-r--r--src/ne_auth.c29
-rw-r--r--test/auth.c35
2 files changed, 61 insertions, 3 deletions
diff --git a/src/ne_auth.c b/src/ne_auth.c
index 9c3321a..a9580e2 100644
--- a/src/ne_auth.c
+++ b/src/ne_auth.c
@@ -122,6 +122,7 @@ struct auth_challenge {
unsigned int stale; /* if stale=true */
unsigned int got_qop; /* we were given a qop directive */
unsigned int qop_auth; /* "auth" token in qop attrib */
+ unsigned int userhash; /* got userhash=true */
auth_algorithm alg;
struct auth_challenge *next;
};
@@ -194,6 +195,7 @@ typedef struct {
char *opaque;
char **domains; /* list of paths given as domain. */
size_t ndomains; /* size of domains array */
+ char *userhash;
auth_qop qop;
auth_algorithm alg;
unsigned int nonce_count;
@@ -277,8 +279,9 @@ static void clean_session(auth_session *sess)
if (sess->cnonce) ne_free(sess->cnonce);
if (sess->opaque) ne_free(sess->opaque);
if (sess->realm) ne_free(sess->realm);
+ if (sess->userhash) ne_free(sess->userhash);
sess->realm = sess->basic = sess->cnonce = sess->nonce =
- sess->opaque = NULL;
+ sess->opaque = sess->userhash = NULL;
if (sess->stored_rdig) {
ne_md5_destroy_ctx(sess->stored_rdig);
sess->stored_rdig = NULL;
@@ -840,6 +843,21 @@ static int digest_challenge(auth_session *sess, int attempt,
/* Failed to get credentials */
return -1;
}
+
+ /* Calculate userhash for this (realm, username) if required.
+ * https://tools.ietf.org/html/rfc7616#section-3.4.4 */
+ if (parms->userhash) {
+ struct ne_md5_ctx *tmp;
+ char digest[33];
+
+ tmp = ne_md5_create_ctx();
+ ne_md5_process_bytes(sess->username, strlen(sess->username), tmp);
+ ne_md5_process_bytes(":", 1, tmp);
+ ne_md5_process_bytes(sess->realm, strlen(sess->realm), tmp);
+ ne_md5_finish_ascii(tmp, digest);
+
+ sess->userhash = ne_strdup(digest);
+ }
}
else {
/* Stale challenge: accept a new nonce or opaque. */
@@ -998,7 +1016,8 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
ret = ne_buffer_create();
ne_buffer_concat(ret,
- "Digest username=\"", sess->username, "\", "
+ "Digest username=\"",
+ sess->userhash ? sess->userhash : sess->username, "\", "
"realm=\"", sess->realm, "\", "
"nonce=\"", sess->nonce, "\", "
"uri=\"", req->uri, "\", "
@@ -1016,6 +1035,9 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
"nc=", nc_value, ", "
"qop=\"", qop_value, "\"", NULL);
}
+ if (sess->userhash) {
+ ne_buffer_czappend(ret, ", userhash=true");
+ }
ne_buffer_zappend(ret, "\r\n");
@@ -1390,6 +1412,9 @@ static int auth_challenge(auth_session *sess, int attempt,
else if (ne_strcasecmp(key, "domain") == 0) {
chall->domain = val;
}
+ else if (ne_strcasecmp(key, "userhash") == 0) {
+ chall->userhash = strcmp(val, "true") == 0;
+ }
}
sess->protocol = NULL;
diff --git a/test/auth.c b/test/auth.c
index 3055165..bdd1867 100644
--- a/test/auth.c
+++ b/test/auth.c
@@ -380,6 +380,8 @@ static void dup_header(char *header)
#define PARM_NEXTNONCE (0x0002)
#define PARM_RFC2617 (0x0004)
#define PARM_AINFO (0x0008)
+#define PARM_USERHASH (0x0010) /* userhash=true */
+#define PARM_UHFALSE (0x0020) /* userhash=false */
struct digest_parms {
const char *realm, *nonce, *opaque, *domain;
@@ -409,9 +411,11 @@ struct digest_parms {
struct digest_state {
const char *realm, *nonce, *uri, *username, *password, *algorithm, *qop,
*method, *opaque;
+ char userhash[33];
char *cnonce, *digest, *ncval;
long nc;
int count;
+ int uhash_bool;
};
#ifdef HAVE_OPENSSL11
@@ -580,6 +584,9 @@ static int verify_digest_header(struct digest_state *state,
else if (ne_strcasecmp(name, "response") == 0) {
state->digest = ne_strdup(val);
}
+ else if (ne_strcasecmp(name, "userhash") == 0 ) {
+ newstate.uhash_bool = strcmp(val, "true") == 0;
+ }
}
ONN("cnonce param missing or short for 2617-style auth",
@@ -587,8 +594,18 @@ static int verify_digest_header(struct digest_state *state,
&& (newstate.cnonce == NULL
|| strlen(newstate.cnonce) < 32));
+ if (parms->flags & PARM_USERHASH) {
+ ONN("userhash missing", !newstate.uhash_bool);
+
+ ONCMP(state->userhash, newstate.username,
+ "Digest username (userhash) field", "userhash");
+ }
+ else {
+ ONN("unexpected userhash=true sent", newstate.uhash_bool);
+ DIGCMP(username);
+ }
+
DIGCMP(realm);
- DIGCMP(username);
if (!parms->domain)
DIGCMP(uri);
DIGCMP(nonce);
@@ -698,6 +715,13 @@ static char *make_digest_header(struct digest_state *state,
ne_buffer_concat(buf, "domain=\"", parms->domain, "\", ", NULL);
}
+ if (parms->flags & PARM_USERHASH) {
+ ne_buffer_czappend(buf, "userhash=true, ");
+ }
+ else if (parms->flags & PARM_UHFALSE) {
+ ne_buffer_czappend(buf, "userhash=false, ");
+ }
+
if (parms->failure == fail_req0_stale
|| parms->failure == fail_req0_2069_stale
|| parms->stale == parms->num_requests) {
@@ -737,6 +761,10 @@ static int serve_digest(ne_socket *sock, void *userdata)
}
state.qop = "auth";
+ if (parms->flags & PARM_USERHASH) {
+ hash(parms, state.userhash, username, ":", parms->realm, NULL);
+ }
+
state.cnonce = state.digest = state.ncval = NULL;
parms->num_requests += parms->stale ? 1 : 0;
@@ -871,6 +899,11 @@ static int digest(void)
/* 2069 + stale */
{ "WallyWorld", "this-is-a-nonce", NULL, NULL, ALG_MD5, PARM_AINFO, 3, 2, fail_not },
+ /* RFC 7616-style */
+ { "WallyWorld", "new-day-new-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_USERHASH, 1, 0, fail_not },
+ /* ... userhash=false */
+ { "WallyWorld", "just-another-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_UHFALSE, 1, 0, fail_not },
+
/* RFC 2069-style */
{ "WallyWorld", "lah-di-da-di-dah", NULL, NULL, ALG_MD5, 0, 1, 0, fail_not },
{ "WallyWorld", "fee-fi-fo-fum", "opaque-string", NULL, ALG_MD5, 0, 1, 0, fail_not },