diff options
author | Joe Orton <jorton@redhat.com> | 2022-07-12 15:53:50 +0100 |
---|---|---|
committer | Joe Orton <jorton@redhat.com> | 2022-07-20 14:05:04 +0100 |
commit | 2b8e2e4e3568d10cdb3ef07b3ae4c699540479ea (patch) | |
tree | 996df0e2b2d6c37eeed146a05f31b1c42bc4d723 | |
parent | 93faea82fe49bfcf22d99c0e9f954ae714315de5 (diff) | |
download | neon-git-2b8e2e4e3568d10cdb3ef07b3ae4c699540479ea.tar.gz |
* src/ne_auth.c: Abstract out hash algorithm handling for Digest.
(struct hashalg): New structure, replacing auth_algorithm enum,
alg_to_hash and alg_to_name arrays.
(struct auth_challenge): Use hashalg pointer.
(get_digest_h_urp, digest_challenge): Adjust accordingly.
* test/auth.c (digest_failures, fail_challenge): Tweak tests
to reliably trigger desired failure cases.
-rw-r--r-- | src/ne_auth.c | 122 | ||||
-rw-r--r-- | test/auth.c | 14 |
2 files changed, 58 insertions, 78 deletions
diff --git a/src/ne_auth.c b/src/ne_auth.c index 9284fa4..b3728e2 100644 --- a/src/ne_auth.c +++ b/src/ne_auth.c @@ -89,35 +89,22 @@ #define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth" #define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth" -typedef enum { - auth_alg_md5 = 0, - auth_alg_md5_sess, - auth_alg_sha256, - auth_alg_sha256_sess, - auth_alg_sha512_256, - auth_alg_sha512_256_sess, - auth_alg_unknown -} auth_algorithm; - -static const unsigned int alg_to_hash[] = { - NE_HASH_MD5, - NE_HASH_MD5, - NE_HASH_SHA256, - NE_HASH_SHA256, - NE_HASH_SHA512_256, - NE_HASH_SHA512_256, - 0 -}; -static const char *const alg_to_name[] = { - "MD5", - "MD5-sess", - "SHA-256", - "SHA-256-sess", - "SHA-512-256", - "SHA-512-256-sess", - "(unknown)", +static const struct hashalg { + const char *name; + unsigned int hash; + unsigned int sess; /* _session variant */ +} hashalgs[] = { + { "MD5", NE_HASH_MD5, 0 }, /* This must remain first in the array. */ + { "MD5-sess", NE_HASH_MD5, 1 }, + { "SHA-256", NE_HASH_SHA256, 0 }, + { "SHA-256-sess", NE_HASH_SHA256, 1 }, + { "SHA-512-256", NE_HASH_SHA512_256, 0 }, + { "SHA-512-256-sess", NE_HASH_SHA512_256, 1 } }; +#define HASHALG_MD5 (&hashalgs[0]) +#define NUM_HASHALGS (sizeof(hashalgs)/sizeof(hashalgs[0])) + /* Selected method of qop which the client is using */ typedef enum { auth_qop_none, @@ -147,7 +134,7 @@ struct auth_challenge { unsigned int got_qop; /* we were given a qop directive */ unsigned int qop_auth; /* "auth" token in qop attrib */ enum { userhash_none=0, userhash_true=1, userhash_false=2} userhash; - auth_algorithm alg; + const struct hashalg *alg; struct auth_challenge *next; }; @@ -228,7 +215,7 @@ typedef struct { char *userhash; char *username_star; auth_qop qop; - auth_algorithm alg; + const struct hashalg *alg; unsigned int nonce_count; /* The hex representation of the H(A1) value */ char *h_a1; @@ -929,8 +916,7 @@ static int unsafe_username(const char *username) /* Returns the H(username:realm:password) used in the Digest H(A1) * calculation. */ static char *get_digest_h_urp(auth_session *sess, ne_buffer **errmsg, - unsigned int hash, int attempt, - struct auth_challenge *parms) + int attempt, struct auth_challenge *parms) { char password[ABUFSIZE], *h_urp; @@ -942,7 +928,7 @@ static char *get_digest_h_urp(auth_session *sess, ne_buffer **errmsg, /* Calculate userhash for this (realm, username) if required. * https://tools.ietf.org/html/rfc7616#section-3.4.4 */ if (parms->userhash == userhash_true) { - sess->userhash = ne_strhash(hash, sess->username, ":", + sess->userhash = ne_strhash(parms->alg->hash, sess->username, ":", sess->realm, NULL); } else { @@ -967,7 +953,7 @@ static char *get_digest_h_urp(auth_session *sess, ne_buffer **errmsg, /* H(A1) calculation identical for 2069 or 2617/7616: * https://tools.ietf.org/html/rfc7616#section-3.4.2 */ - h_urp = ne_strhash(hash, sess->username, ":", sess->realm, ":", + h_urp = ne_strhash(parms->alg->hash, sess->username, ":", sess->realm, ":", password, NULL); ne__strzero(password, sizeof password); @@ -980,14 +966,23 @@ static int digest_challenge(auth_session *sess, int attempt, struct auth_challenge *parms, const char *uri, ne_buffer **errmsg) { - unsigned int hash; char *p, *h_urp = NULL; - if (parms->alg == auth_alg_unknown) { + /* Handle 2069 legacy Digest case. */ + if (parms->alg == NULL && !parms->got_qop) { + if ((parms->handler->protomask & NE_AUTH_LEGACY_DIGEST) == 0) { + challenge_error(errmsg, _("legacy Digest challenge not supported")); + return -1; + } + /* It can only be MD5. */ + parms->alg = HASHALG_MD5; + } + + if (parms->alg == NULL) { challenge_error(errmsg, _("unknown algorithm in Digest challenge")); return -1; } - else if (parms->alg == auth_alg_md5_sess && !parms->qop_auth) { + else if (parms->alg->sess && !parms->qop_auth) { challenge_error(errmsg, _("incompatible algorithm in Digest challenge")); return -1; } @@ -1006,18 +1001,12 @@ static int digest_challenge(auth_session *sess, int attempt, challenge_error(errmsg, _("stale Digest challenge with new algorithm or realm")); return -1; } - else if (!parms->got_qop - && (parms->handler->protomask & NE_AUTH_LEGACY_DIGEST) == 0) { - challenge_error(errmsg, _("legacy Digest challenge not supported")); - return -1; - } - hash = alg_to_hash[parms->alg]; - p = ne_strhash(hash, "", NULL); + p = ne_strhash(parms->alg->hash, "", NULL); if (p == NULL) { challenge_error(errmsg, _("%s algorithm in Digest challenge not supported"), - alg_to_name[parms->alg]); + parms->alg->name); return -1; } ne_free(p); @@ -1038,7 +1027,7 @@ static int digest_challenge(auth_session *sess, int attempt, sess->alg = parms->alg; sess->cnonce = get_cnonce(); - h_urp = get_digest_h_urp(sess, errmsg, hash, attempt, parms); + h_urp = get_digest_h_urp(sess, errmsg, attempt, parms); if (h_urp == NULL) { return -1; } @@ -1065,10 +1054,9 @@ static int digest_challenge(auth_session *sess, int attempt, } if (h_urp) { - if (sess->alg == auth_alg_md5_sess || sess->alg == auth_alg_sha256_sess - || sess->alg == auth_alg_sha512_256_sess) { - sess->h_a1 = ne_strhash(hash, h_urp, ":", sess->nonce, ":", - sess->cnonce, NULL); + if (sess->alg->sess) { + sess->h_a1 = ne_strhash(parms->alg->hash, h_urp, ":", + sess->nonce, ":", sess->cnonce, NULL); zero_and_free(h_urp); NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Session H(A1) is [%s]\n", sess->h_a1); } @@ -1119,7 +1107,7 @@ static char *request_digest(auth_session *sess, struct auth_request *req) char nc_value[9] = {0}; const char *qop_value = "auth"; /* qop-value */ ne_buffer *ret; - unsigned int hash = alg_to_hash[sess->alg]; + unsigned int hash = sess->alg->hash; /* Do not submit credentials if an auth domain is defined and this * request-uri fails outside it. */ @@ -1161,7 +1149,7 @@ static char *request_digest(auth_session *sess, struct auth_request *req) "nonce=\"", sess->nonce, "\", " "uri=\"", req->uri, "\", " "response=\"", response, "\", " - "algorithm=\"", alg_to_name[sess->alg], "\"", + "algorithm=\"", sess->alg->name, "\"", NULL); if (sess->username_star) { ne_buffer_concat(ret, ", username*=", sess->username_star, NULL); @@ -1339,7 +1327,7 @@ static int verify_digest_response(struct auth_request *req, auth_session *sess, * the response-digest field. */ if (qop == auth_qop_auth && ret == NE_OK) { char *h_a2, *response; - unsigned int hash = alg_to_hash[sess->alg]; + unsigned int hash = sess->alg->hash; h_a2 = ne_strhash(hash, ":", req->uri, NULL); response = ne_strhash(hash, sess->h_a1, ":", sess->response_rhs, @@ -1532,27 +1520,15 @@ static int auth_challenge(auth_session *sess, int attempt, const char *uri, /* Truth value */ chall->stale = (ne_strcasecmp(val, "true") == 0); } else if (ne_strcasecmp(key, "algorithm") == 0) { - if (ne_strcasecmp(val, "md5") == 0) { - chall->alg = auth_alg_md5; - } - else if (ne_strcasecmp(val, "md5-sess") == 0) { - chall->alg = auth_alg_md5_sess; - } - else if (ne_strcasecmp(val, "sha-256") == 0) { - chall->alg = auth_alg_sha256; - } - else if (ne_strcasecmp(val, "sha-256-sess") == 0) { - chall->alg = auth_alg_sha256_sess; - } - else if (ne_strcasecmp(val, "sha-512-256") == 0) { - chall->alg = auth_alg_sha512_256; - } - else if (ne_strcasecmp(val, "sha-512-256-sess") == 0) { - chall->alg = auth_alg_sha512_256_sess; - } - else { - chall->alg = auth_alg_unknown; - } + unsigned int n; + + chall->alg = NULL; + for (n = 0; n < NUM_HASHALGS; n++) { + if (ne_strcasecmp(val, hashalgs[n].name) == 0) { + chall->alg = &hashalgs[n]; + break; + } + } } else if (ne_strcasecmp(key, "qop") == 0) { /* iterate over each token in the value */ do { diff --git a/test/auth.c b/test/auth.c index e985703..d0f1f8c 100644 --- a/test/auth.c +++ b/test/auth.c @@ -1085,26 +1085,30 @@ static int digest_failures(void) parms.realm = "WallyWorld"; parms.nonce = "random-invented-string"; parms.opaque = NULL; - parms.flags = PARM_AINFO; parms.num_requests = 1; for (n = 0; fails[n].message; n++) { ne_session *sess; int ret; + unsigned protocol = NE_AUTH_DIGEST; parms.failure = fails[n].mode; + parms.flags = PARM_AINFO; + + if (parms.failure == fail_req0_2069_stale) protocol |= NE_AUTH_LEGACY_DIGEST; if (parms.failure == fail_req0_2069_stale || parms.failure == fail_2069_weak) parms.flags &= ~PARM_RFC2617; else parms.flags |= PARM_RFC2617; - NE_DEBUG(NE_DBG_HTTP, ">>> New Digest failure test, " - "expecting failure '%s'\n", fails[n].message); + NE_DEBUG(NE_DBG_HTTP, ">>> New Digest failure test %u, " + "expecting failure '%s', protocol %x\n", n, + fails[n].message, protocol); CALL(session_server(&sess, serve_digest, &parms)); - ne_set_server_auth(sess, auth_cb, NULL); + ne_add_server_auth(sess, protocol, auth_cb, NULL); ret = any_2xx_request(sess, "/fish"); ONV(ret == NE_OK, @@ -1170,7 +1174,7 @@ static int fail_challenge(void) "domain=\"http://[::1/\"", "could not parse domain" }, /* Multiple challenge failure cases: */ - { "Basic, Digest", + { "Basic, Digest realm=\"foo\", algorithm=MD5, qop=auth", "missing parameter in Digest challenge, missing realm in Basic challenge" }, { "Digest realm=\"foo\", algorithm=MD5, qop=auth, nonce=\"foo\"," |