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);
+}