From d5671216c406b9012374b14a49186f642ea9bb7f Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Tue, 15 Mar 2011 11:26:47 +0000 Subject: [PATCH] patch: cleanup and improve httpauth - fix parsing bug nonce/cnonce - handle line-folding - parsing speed increased by 620% - handle response w/o qop support --- include/re_httpauth.h | 40 ++----- src/httpauth/digest.c | 270 +++++++++++++++++++++--------------------- src/sip/auth.c | 2 - 3 files changed, 145 insertions(+), 167 deletions(-) diff --git a/include/re_httpauth.h b/include/re_httpauth.h index fc96f41..a548268 100644 --- a/include/re_httpauth.h +++ b/include/re_httpauth.h @@ -5,16 +5,7 @@ */ -/** Authentication type */ -enum httpauth_hdr { - HTTPAUTH_WWW, - HTTPAUTH_PROXY -}; - - -/* Client code */ struct httpauth_digest_chall { - enum httpauth_hdr hdr; struct pl realm; struct pl nonce; @@ -25,34 +16,23 @@ struct httpauth_digest_chall { struct pl qop; }; -int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, - const struct pl *hval); -int httpauth_digest_response_encode(struct mbuf *mb, const struct pl *hname, - const struct httpauth_digest_chall *chall, - const struct pl *username, - const struct pl *uri, - const struct pl *cnonce, - const struct pl *qop, - const struct pl *nc, - const uint8_t *digest); - - -/* Server code */ struct httpauth_digest_resp { struct pl realm; struct pl nonce; struct pl response; + struct pl username; + struct pl uri; + + /* optional */ struct pl nc; struct pl cnonce; struct pl qop; - struct pl username; - struct pl uri; }; -int httpauth_digest_authenticate(struct httpauth_digest_resp *resp, - bool *auth, const struct pl *method, - const char *ha1); -int httpauth_digest_decode_response(struct httpauth_digest_resp *resp, + +int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, + const struct pl *hval); +int httpauth_digest_response_decode(struct httpauth_digest_resp *resp, const struct pl *hval); -int httpauth_digest_encode_challenge(struct mbuf *mb, const struct pl *hname, - const struct pl *realm); +int httpauth_digest_response_auth(struct httpauth_digest_resp *resp, + const struct pl *method, const uint8_t *ha1); diff --git a/src/httpauth/digest.c b/src/httpauth/digest.c index ccee9d0..4677894 100644 --- a/src/httpauth/digest.c +++ b/src/httpauth/digest.c @@ -3,6 +3,7 @@ * * Copyright (C) 2010 Creytiv.com */ +#include #include #include #include @@ -11,179 +12,178 @@ #include -static const struct pl str_digest = PL("Digest"); - -static const struct pl param_algo = PL("algorithm"); -static const struct pl param_cnonce = PL("cnonce"); -static const struct pl param_nc = PL("nc"); -static const struct pl param_nonce = PL("nonce"); -static const struct pl param_opaque = PL("opaque"); -static const struct pl param_qop = PL("qop"); -static const struct pl param_realm = PL("realm"); -static const struct pl param_response = PL("response"); -static const struct pl param_uri = PL("uri"); -static const struct pl param_username = PL("username"); -static const struct pl param_stale = PL("stale"); +typedef void (digest_decode_h)(const struct pl *name, const struct pl *val, + void *arg); -static int param_get(const struct pl *pl, const struct pl *name, - struct pl *val) +static const struct pl param_algorithm = PL("algorithm"); +static const struct pl param_cnonce = PL("cnonce"); +static const struct pl param_nc = PL("nc"); +static const struct pl param_nonce = PL("nonce"); +static const struct pl param_opaque = PL("opaque"); +static const struct pl param_qop = PL("qop"); +static const struct pl param_realm = PL("realm"); +static const struct pl param_response = PL("response"); +static const struct pl param_uri = PL("uri"); +static const struct pl param_username = PL("username"); +static const struct pl param_stale = PL("stale"); + + +static void challenge_decode(const struct pl *name, const struct pl *val, + void *arg) { - char expr[32]; - int err; + struct httpauth_digest_chall *chall = arg; - if (re_snprintf(expr, sizeof(expr), "[ ]*%r=[^, ]+", name) < 0) - return EINVAL; - - err = re_regex(pl->p, pl->l, expr, NULL, val); - if (err) - return err; - - /* Optionally strip off quotes */ - (void)re_regex(val->p, val->l, "\"[^\"]+\"", val); - - return 0; + if (!pl_casecmp(name, ¶m_realm)) + chall->realm = *val; + else if (!pl_casecmp(name, ¶m_nonce)) + chall->nonce = *val; + else if (!pl_casecmp(name, ¶m_opaque)) + chall->opaque= *val; + else if (!pl_casecmp(name, ¶m_stale)) + chall->stale = *val; + else if (!pl_casecmp(name, ¶m_algorithm)) + chall->algorithm = *val; + else if (!pl_casecmp(name, ¶m_qop)) + chall->qop = *val; } -/* Client code */ +static void response_decode(const struct pl *name, const struct pl *val, + void *arg) +{ + struct httpauth_digest_resp *resp = arg; + + if (!pl_casecmp(name, ¶m_realm)) + resp->realm = *val; + else if (!pl_casecmp(name, ¶m_nonce)) + resp->nonce = *val; + else if (!pl_casecmp(name, ¶m_response)) + resp->response = *val; + else if (!pl_casecmp(name, ¶m_username)) + resp->username = *val; + else if (!pl_casecmp(name, ¶m_uri)) + resp->uri = *val; + else if (!pl_casecmp(name, ¶m_nc)) + resp->nc = *val; + else if (!pl_casecmp(name, ¶m_cnonce)) + resp->cnonce = *val; + else if (!pl_casecmp(name, ¶m_qop)) + resp->qop = *val; +} + + +static int digest_decode(const struct pl *hval, digest_decode_h *dech, + void *arg) +{ + struct pl r = *hval, start, end, name, val; + + if (re_regex(r.p, r.l, "[ \t\r\n]*Digest[ \t\r\n]+", &start, &end) || + start.p != r.p) + return EBADMSG; + + pl_advance(&r, end.p - r.p); + + while (!re_regex(r.p, r.l, + "[ \t\r\n,]+[a-z]+[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n,]*", + NULL, &name, NULL, NULL, &val)) { + + pl_advance(&r, val.p + val.l - r.p); + + dech(&name, &val, arg); + } + + return 0; +} int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, const struct pl *hval) { - struct pl scheme; int err; if (!chall || !hval) return EINVAL; - err = re_regex(hval->p, hval->l, "[^ \t]+[ \t]+", &scheme, NULL); + memset(chall, 0, sizeof(*chall)); + + err = digest_decode(hval, challenge_decode, chall); if (err) return err; - err = pl_casecmp(&scheme, &str_digest); - if (err) - return err; - - /* Mandatory */ - if (param_get(hval, ¶m_realm, &chall->realm) || - param_get(hval, ¶m_nonce, &chall->nonce)) - return EINVAL; - - /* Optional */ - (void)param_get(hval, ¶m_opaque, &chall->opaque); - (void)param_get(hval, ¶m_stale, &chall->stale); - (void)param_get(hval, ¶m_algo, &chall->algorithm); - (void)param_get(hval, ¶m_qop, &chall->qop); + if (!chall->realm.p || !chall->nonce.p) + return EBADMSG; return 0; } -int httpauth_digest_response_encode(struct mbuf *mb, const struct pl *hname, - const struct httpauth_digest_chall *chall, - const struct pl *username, - const struct pl *uri, - const struct pl *cnonce, - const struct pl *qop, - const struct pl *nc, - const uint8_t *digest) -{ - int err = 0; - - if (!mb || !hname || !chall) - return EINVAL; - - err |= mbuf_printf(mb, "%r: Digest ", hname); - - err |= mbuf_printf(mb, "username=\"%r\",realm=\"%r\",nonce=\"%r\"" - ",uri=\"%r\"", - username, &chall->realm, &chall->nonce, uri); - - err |= mbuf_printf(mb, ",response=\"%w\"", digest, MD5_SIZE); - - if (chall->opaque.p) - err |= mbuf_printf(mb, ",opaque=\"%r\"", &chall->opaque); - - if (chall->qop.p) { - err |= mbuf_printf(mb, ",cnonce=\"%r\",qop=%r,nc=%r", - cnonce, qop, nc); - } - - err |= mbuf_write_str(mb, "\r\n"); - - return err; -} - - -/* Server code */ - -int httpauth_digest_authenticate(struct httpauth_digest_resp *resp, - bool *auth, const struct pl *method, - const char *ha1) -{ - uint8_t a2[MD5_SIZE], d[MD5_SIZE]; - char hd[MD5_STR_SIZE]; - int err; - - if (!resp || !auth || !method) - return EINVAL; - - err = md5_printf(a2, "%r:%r", method, &resp->uri); - if (err) - return err; - - err = md5_printf(d, "%s:%r:%r:%r:%r:%w", ha1, &resp->nonce, &resp->nc, - &resp->cnonce, &resp->qop, a2, MD5_SIZE); - if (err) - return err; - - if (re_snprintf(hd, sizeof(hd), "%w", d, sizeof(d)) < 0) - return EINVAL; - - *auth = (pl_strcasecmp(&resp->response, hd) == 0); - - return 0; -} - - -int httpauth_digest_decode_response(struct httpauth_digest_resp *resp, +int httpauth_digest_response_decode(struct httpauth_digest_resp *resp, const struct pl *hval) { - struct pl scheme; + int err; if (!resp || !hval) return EINVAL; - if (re_regex(hval->p, hval->l, "[^ \t]+[ \t]+", &scheme, NULL) || - pl_casecmp(&scheme, &str_digest)) - return EINVAL; + memset(resp, 0, sizeof(*resp)); - if (param_get(hval, ¶m_realm, &resp->realm) || - param_get(hval, ¶m_nonce, &resp->nonce) || - param_get(hval, ¶m_response, &resp->response) || - param_get(hval, ¶m_nc, &resp->nc) || - param_get(hval, ¶m_cnonce, &resp->cnonce) || - param_get(hval, ¶m_qop, &resp->qop) || - param_get(hval, ¶m_username, &resp->username) || - param_get(hval, ¶m_uri, &resp->uri)) - return EINVAL; + err = digest_decode(hval, response_decode, resp); + if (err) + return err; + + if (!resp->realm.p || + !resp->nonce.p || + !resp->response.p || + !resp->username.p || + !resp->uri.p) + return EBADMSG; return 0; } -int httpauth_digest_encode_challenge(struct mbuf *mb, const struct pl *hname, - const struct pl *realm) +int httpauth_digest_response_auth(struct httpauth_digest_resp *resp, + const struct pl *method, const uint8_t *ha1) { - if (!mb || !hname || !realm) + uint8_t ha2[MD5_SIZE], digest[MD5_SIZE], response[MD5_SIZE]; + const char *p; + uint32_t i; + int err; + + if (!resp || !method || !ha1) return EINVAL; - return mbuf_printf(mb, - "%r: Digest realm=\"%r\", " - "nonce=\"%08lx%08lx%08lx%08lx\", " - "qop=\"auth\"\r\n", - hname, realm, - rand_u32(), rand_u32(), rand_u32(), rand_u32()); + if (resp->response.l != 32) + return EAUTH; + + err = md5_printf(ha2, "%r:%r", method, &resp->uri); + if (err) + return err; + + if (pl_isset(&resp->qop)) + err = md5_printf(digest, "%w:%r:%r:%r:%r:%w", + ha1, MD5_SIZE, + &resp->nonce, + &resp->nc, + &resp->cnonce, + &resp->qop, + ha2, sizeof(ha2)); + else + err = md5_printf(digest, "%w:%r:%w", + ha1, MD5_SIZE, + &resp->nonce, + ha2, sizeof(ha2)); + if (err) + return err; + + for (i=0, p=resp->response.p; ival)) { err = EBADMSG; goto out;