2010-11-03 11:34:14 +00:00
|
|
|
/**
|
|
|
|
* @file auth.c SIP Authentication
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Creytiv.com
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <re_types.h>
|
|
|
|
#include <re_mem.h>
|
|
|
|
#include <re_fmt.h>
|
|
|
|
#include <re_mbuf.h>
|
|
|
|
#include <re_uri.h>
|
|
|
|
#include <re_list.h>
|
|
|
|
#include <re_sa.h>
|
|
|
|
#include <re_sys.h>
|
|
|
|
#include <re_md5.h>
|
|
|
|
#include <re_httpauth.h>
|
2011-02-25 09:17:21 +00:00
|
|
|
#include <re_udp.h>
|
2010-11-03 11:34:14 +00:00
|
|
|
#include <re_sip.h>
|
|
|
|
#include "sip.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct sip_auth {
|
|
|
|
struct list realml;
|
|
|
|
sip_auth_h *authh;
|
|
|
|
void *arg;
|
|
|
|
bool ref;
|
|
|
|
int err;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct realm {
|
|
|
|
struct le le;
|
|
|
|
char *realm;
|
|
|
|
char *nonce;
|
|
|
|
char *qop;
|
|
|
|
char *opaque;
|
|
|
|
char *user;
|
|
|
|
char *pass;
|
|
|
|
uint32_t nc;
|
|
|
|
enum sip_hdrid hdr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int dummy_handler(char **user, char **pass, const char *rlm, void *arg)
|
|
|
|
{
|
|
|
|
(void)user;
|
|
|
|
(void)pass;
|
|
|
|
(void)rlm;
|
|
|
|
(void)arg;
|
|
|
|
|
|
|
|
return EAUTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void realm_destructor(void *arg)
|
|
|
|
{
|
|
|
|
struct realm *realm = arg;
|
|
|
|
|
|
|
|
list_unlink(&realm->le);
|
|
|
|
mem_deref(realm->realm);
|
|
|
|
mem_deref(realm->nonce);
|
|
|
|
mem_deref(realm->qop);
|
|
|
|
mem_deref(realm->opaque);
|
|
|
|
mem_deref(realm->user);
|
|
|
|
mem_deref(realm->pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void auth_destructor(void *arg)
|
|
|
|
{
|
|
|
|
struct sip_auth *auth = arg;
|
|
|
|
|
|
|
|
if (auth->ref)
|
|
|
|
mem_deref(auth->arg);
|
|
|
|
|
|
|
|
list_flush(&auth->realml);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mkdigest(uint8_t *digest, const struct realm *realm,
|
|
|
|
const char *met, const char *uri, uint64_t cnonce)
|
|
|
|
{
|
|
|
|
uint8_t ha1[MD5_SIZE], ha2[MD5_SIZE];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = md5_printf(ha1, "%s:%s:%s",
|
|
|
|
realm->user, realm->realm, realm->pass);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = md5_printf(ha2, "%s:%s", met, uri);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (realm->qop)
|
|
|
|
return md5_printf(digest, "%w:%s:%08x:%016llx:auth:%w",
|
|
|
|
ha1, sizeof(ha1),
|
|
|
|
realm->nonce,
|
|
|
|
realm->nc,
|
|
|
|
cnonce,
|
|
|
|
ha2, sizeof(ha2));
|
|
|
|
else
|
|
|
|
return md5_printf(digest, "%w:%s:%w",
|
|
|
|
ha1, sizeof(ha1),
|
|
|
|
realm->nonce,
|
|
|
|
ha2, sizeof(ha2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool cmp_handler(struct le *le, void *arg)
|
|
|
|
{
|
|
|
|
struct realm *realm = le->data;
|
|
|
|
struct pl *chrealm = arg;
|
|
|
|
|
2011-03-30 12:34:01 +00:00
|
|
|
/* handle multiple authenticate headers with equal realm value */
|
|
|
|
if (realm->nc == 1)
|
|
|
|
return false;
|
|
|
|
|
2010-11-03 11:34:14 +00:00
|
|
|
return 0 == pl_strcasecmp(chrealm, realm->realm);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
struct httpauth_digest_chall ch;
|
|
|
|
struct sip_auth *auth = arg;
|
|
|
|
struct realm *realm = NULL;
|
|
|
|
int err;
|
|
|
|
(void)msg;
|
|
|
|
|
|
|
|
if (httpauth_digest_challenge_decode(&ch, &hdr->val)) {
|
|
|
|
err = EBADMSG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl_isset(&ch.algorithm) && pl_strcasecmp(&ch.algorithm, "md5")) {
|
|
|
|
err = ENOSYS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
realm = list_ledata(list_apply(&auth->realml, true, cmp_handler,
|
|
|
|
&ch.realm));
|
|
|
|
if (!realm) {
|
|
|
|
realm = mem_zalloc(sizeof(*realm), realm_destructor);
|
|
|
|
if (!realm) {
|
|
|
|
err = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_append(&auth->realml, &realm->le, realm);
|
|
|
|
|
|
|
|
err = pl_strdup(&realm->realm, &ch.realm);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = auth->authh(&realm->user, &realm->pass,
|
|
|
|
realm->realm, auth->arg);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!pl_isset(&ch.stale) || pl_strcasecmp(&ch.stale, "true")) {
|
|
|
|
err = EAUTH;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
realm->nonce = mem_deref(realm->nonce);
|
|
|
|
realm->qop = mem_deref(realm->qop);
|
|
|
|
realm->opaque = mem_deref(realm->opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
realm->hdr = hdr->id;
|
|
|
|
realm->nc = 1;
|
|
|
|
|
|
|
|
err = pl_strdup(&realm->nonce, &ch.nonce);
|
|
|
|
|
|
|
|
if (pl_isset(&ch.qop))
|
|
|
|
err |= pl_strdup(&realm->qop, &ch.qop);
|
|
|
|
|
|
|
|
if (pl_isset(&ch.opaque))
|
|
|
|
err |= pl_strdup(&realm->opaque, &ch.opaque);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (err) {
|
|
|
|
mem_deref(realm);
|
|
|
|
auth->err = err;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg)
|
|
|
|
{
|
|
|
|
if (!auth || !msg)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
if (sip_msg_hdr_apply(msg, true, SIP_HDR_WWW_AUTHENTICATE,
|
|
|
|
auth_handler, auth))
|
|
|
|
return auth->err;
|
|
|
|
|
|
|
|
if (sip_msg_hdr_apply(msg, true, SIP_HDR_PROXY_AUTHENTICATE,
|
|
|
|
auth_handler, auth))
|
|
|
|
return auth->err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met,
|
|
|
|
const char *uri)
|
|
|
|
{
|
|
|
|
struct le *le;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (!mb || !auth || !met || !uri)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
for (le = auth->realml.head; le; le = le->next) {
|
|
|
|
|
|
|
|
const uint64_t cnonce = rand_u64();
|
|
|
|
struct realm *realm = le->data;
|
|
|
|
uint8_t digest[MD5_SIZE];
|
|
|
|
|
|
|
|
err = mkdigest(digest, realm, met, uri, cnonce);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (realm->hdr) {
|
|
|
|
|
|
|
|
case SIP_HDR_WWW_AUTHENTICATE:
|
|
|
|
err = mbuf_write_str(mb, "Authorization: ");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIP_HDR_PROXY_AUTHENTICATE:
|
|
|
|
err = mbuf_write_str(mb, "Proxy-Authorization: ");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
err |= mbuf_printf(mb, "Digest username=\"%s\"", realm->user);
|
|
|
|
err |= mbuf_printf(mb, ", realm=\"%s\"", realm->realm);
|
|
|
|
err |= mbuf_printf(mb, ", nonce=\"%s\"", realm->nonce);
|
|
|
|
err |= mbuf_printf(mb, ", uri=\"%s\"", uri);
|
|
|
|
err |= mbuf_printf(mb, ", response=\"%w\"",
|
|
|
|
digest, sizeof(digest));
|
|
|
|
|
|
|
|
if (realm->opaque)
|
|
|
|
err |= mbuf_printf(mb, ", opaque=\"%s\"",
|
|
|
|
realm->opaque);
|
|
|
|
|
|
|
|
if (realm->qop) {
|
|
|
|
err |= mbuf_printf(mb, ", cnonce=\"%016llx\"", cnonce);
|
|
|
|
err |= mbuf_write_str(mb, ", qop=auth");
|
2011-03-30 12:34:01 +00:00
|
|
|
err |= mbuf_printf(mb, ", nc=%08x", realm->nc);
|
2010-11-03 11:34:14 +00:00
|
|
|
}
|
|
|
|
|
2011-03-30 12:34:01 +00:00
|
|
|
++realm->nc;
|
|
|
|
|
2010-11-03 11:34:14 +00:00
|
|
|
err |= mbuf_write_str(mb, "\r\n");
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh,
|
|
|
|
void *arg, bool ref)
|
|
|
|
{
|
|
|
|
struct sip_auth *auth;
|
|
|
|
|
|
|
|
if (!authp)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
auth = mem_zalloc(sizeof(*auth), auth_destructor);
|
|
|
|
if (!auth)
|
|
|
|
return ENOMEM;
|
|
|
|
|
|
|
|
auth->authh = authh ? authh : dummy_handler;
|
|
|
|
auth->arg = ref ? mem_ref(arg) : arg;
|
|
|
|
auth->ref = ref;
|
|
|
|
|
|
|
|
*authp = auth;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-04 10:40:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
void sip_auth_reset(struct sip_auth *auth)
|
|
|
|
{
|
|
|
|
if (!auth)
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_flush(&auth->realml);
|
|
|
|
}
|