patch: cleanup and improve httpauth
- fix parsing bug nonce/cnonce - handle line-folding - parsing speed increased by 620% - handle response w/o qop support
This commit is contained in:
parent
b3d47ec333
commit
d5671216c4
3 changed files with 145 additions and 167 deletions
|
@ -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);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <re_types.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_mbuf.h>
|
||||
|
@ -11,179 +12,178 @@
|
|||
#include <re_httpauth.h>
|
||||
|
||||
|
||||
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; i<sizeof(response); i++) {
|
||||
response[i] = ch_hex(*p++) << 4;
|
||||
response[i] += ch_hex(*p++);
|
||||
}
|
||||
|
||||
if (memcmp(digest, response, MD5_SIZE))
|
||||
return EAUTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -125,8 +125,6 @@ static bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
|
|||
int err;
|
||||
(void)msg;
|
||||
|
||||
memset(&ch, 0, sizeof(ch));
|
||||
|
||||
if (httpauth_digest_challenge_decode(&ch, &hdr->val)) {
|
||||
err = EBADMSG;
|
||||
goto out;
|
||||
|
|
Loading…
Add table
Reference in a new issue