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/httpauth/digest.c b/src/httpauth/digest.c
new file mode 100644
index 0000000..584511e
--- /dev/null
+++ b/src/httpauth/digest.c
@@ -0,0 +1,214 @@
+/**
+ * @file digest.c HTTP Digest authentication (RFC 2617)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_md5.h>
+#include <re_sys.h>
+#include <re_httpauth.h>
+
+
+typedef void (digest_decode_h)(const struct pl *name, const struct pl *val,
+ void *arg);
+
+
+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)
+{
+ struct httpauth_digest_chall *chall = arg;
+
+ 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;
+}
+
+
+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;
+}
+
+
+/**
+ * Decode a Digest challenge
+ *
+ * @param chall Digest challenge object to decode into
+ * @param hval Header value to decode from
+ *
+ * @return 0 if successfully decoded, otherwise errorcode
+ */
+int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall,
+ const struct pl *hval)
+{
+ int err;
+
+ if (!chall || !hval)
+ return EINVAL;
+
+ memset(chall, 0, sizeof(*chall));
+
+ err = digest_decode(hval, challenge_decode, chall);
+ if (err)
+ return err;
+
+ if (!chall->realm.p || !chall->nonce.p)
+ return EBADMSG;
+
+ return 0;
+}
+
+
+/**
+ * Decode a Digest response
+ *
+ * @param resp Digest response object to decode into
+ * @param hval Header value to decode from
+ *
+ * @return 0 if successfully decoded, otherwise errorcode
+ */
+int httpauth_digest_response_decode(struct httpauth_digest_resp *resp,
+ const struct pl *hval)
+{
+ int err;
+
+ if (!resp || !hval)
+ return EINVAL;
+
+ memset(resp, 0, sizeof(*resp));
+
+ 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;
+}
+
+
+/**
+ * Authenticate a digest response
+ *
+ * @param resp Digest response
+ * @param method Request method
+ * @param ha1 HA1 value from MD5(username:realm:password)
+ *
+ * @return 0 if successfully authenticated, otherwise errorcode
+ */
+int httpauth_digest_response_auth(const struct httpauth_digest_resp *resp,
+ const struct pl *method, const uint8_t *ha1)
+{
+ 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;
+
+ 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, (size_t)MD5_SIZE,
+ &resp->nonce,
+ &resp->nc,
+ &resp->cnonce,
+ &resp->qop,
+ ha2, sizeof(ha2));
+ else
+ err = md5_printf(digest, "%w:%r:%w",
+ ha1, (size_t)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;
+}