Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/sip/auth.c b/src/sip/auth.c
new file mode 100644
index 0000000..1357cad
--- /dev/null
+++ b/src/sip/auth.c
@@ -0,0 +1,325 @@
+/**
+ * @file sip/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>
+#include <re_msg.h>
+#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;
+
+ 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;
+}
+
+
+/**
+ * Update a SIP authentication state from a SIP message
+ *
+ * @param auth SIP Authentication state
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+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);
+ }
+
+ ++realm->nc;
+
+ err |= mbuf_write_str(mb, "\r\n");
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+
+/**
+ * Allocate a SIP authentication state
+ *
+ * @param authp Pointer to allocated SIP authentication state
+ * @param authh Authentication handler
+ * @param arg Handler argument
+ * @param ref True to mem_ref() argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+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;
+}
+
+
+/**
+ * Reset a SIP authentication state
+ *
+ * @param auth SIP Authentication state
+ */
+void sip_auth_reset(struct sip_auth *auth)
+{
+ if (!auth)
+ return;
+
+ list_flush(&auth->realml);
+}