re/src/sip/auth.c

302 lines
5.5 KiB
C
Raw Normal View History

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>
#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;
/* 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");
err |= mbuf_printf(mb, ", nc=%08x", realm->nc);
2010-11-03 11:34:14 +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);
}