summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Orton <jorton@redhat.com>2022-07-12 15:53:50 +0100
committerJoe Orton <jorton@redhat.com>2022-07-20 14:05:04 +0100
commit2b8e2e4e3568d10cdb3ef07b3ae4c699540479ea (patch)
tree996df0e2b2d6c37eeed146a05f31b1c42bc4d723
parent93faea82fe49bfcf22d99c0e9f954ae714315de5 (diff)
downloadneon-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.c122
-rw-r--r--test/auth.c14
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\","