From d4b55c4ceb3eb24f96218a45852ec7d0e1c38aee Mon Sep 17 00:00:00 2001
From: Dmitry Frank <mail@dmitryfrank.com>
Date: Tue, 22 Aug 2017 22:47:43 +0300
Subject: [PATCH] Separate digest auth from HTTP headers parsing

PUBLISHED_FROM=f97569ae2f31ab94ce4875eae4d0a198f719c388
---
 docs/c-api/http_server.h/intro.md             |  1 +
 .../http_server.h/mg_check_digest_auth.md     | 17 +++++++
 mongoose.c                                    | 47 +++++++++++++------
 mongoose.h                                    | 12 +++++
 4 files changed, 62 insertions(+), 15 deletions(-)
 create mode 100644 docs/c-api/http_server.h/mg_check_digest_auth.md

diff --git a/docs/c-api/http_server.h/intro.md b/docs/c-api/http_server.h/intro.md
index 98402e96a..0fc609dbc 100644
--- a/docs/c-api/http_server.h/intro.md
+++ b/docs/c-api/http_server.h/intro.md
@@ -3,6 +3,7 @@ title: "Server API reference"
 symbol_kind: "intro"
 decl_name: "http_server.h"
 items:
+  - { name: mg_check_digest_auth.md }
   - { name: mg_file_upload_handler.md }
   - { name: mg_get_http_basic_auth.md }
   - { name: mg_get_http_header.md }
diff --git a/docs/c-api/http_server.h/mg_check_digest_auth.md b/docs/c-api/http_server.h/mg_check_digest_auth.md
new file mode 100644
index 000000000..189f0c4a9
--- /dev/null
+++ b/docs/c-api/http_server.h/mg_check_digest_auth.md
@@ -0,0 +1,17 @@
+---
+title: "mg_check_digest_auth()"
+decl_name: "mg_check_digest_auth"
+symbol_kind: "func"
+signature: |
+  int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
+                           struct mg_str username, struct mg_str cnonce,
+                           struct mg_str response, struct mg_str qop,
+                           struct mg_str nc, struct mg_str nonce,
+                           struct mg_str auth_domain, FILE *fp);
+---
+
+Authenticates given response params against an opened password file.
+Returns 1 if authenticated, 0 otherwise.
+
+It's used by mg_http_check_digest_auth(). 
+
diff --git a/mongoose.c b/mongoose.c
index d199e3b2e..a765594a1 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -6973,14 +6973,13 @@ static int mg_check_nonce(const char *nonce) {
 int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
                               FILE *fp) {
   struct mg_str *hdr;
-  char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
-  char user[50], cnonce[64], response[40], uri[200], qop[20], nc[20], nonce[30];
-  char expected_response[33];
+  char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20],
+      nonce[30];
 
   /* Parse "Authorization:" header, fail fast on parse error */
   if (hm == NULL || fp == NULL ||
       (hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
-      mg_http_parse_header(hdr, "username", user, sizeof(user)) == 0 ||
+      mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 ||
       mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
       mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
       mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
@@ -6991,6 +6990,26 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
     return 0;
   }
 
+  /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
+
+  return mg_check_digest_auth(
+      hm->method,
+      mg_mk_str_n(
+          hm->uri.p,
+          hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
+      mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
+      mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
+      fp);
+}
+
+int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
+                         struct mg_str username, struct mg_str cnonce,
+                         struct mg_str response, struct mg_str qop,
+                         struct mg_str nc, struct mg_str nonce,
+                         struct mg_str auth_domain, FILE *fp) {
+  char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
+  char expected_response[33];
+
   /*
    * Read passwords file line by line. If should have htdigest format,
    * i.e. each line should be a colon-separated sequence:
@@ -6998,18 +7017,16 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
    */
   while (fgets(buf, sizeof(buf), fp) != NULL) {
     if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 &&
-        strcmp(user, f_user) == 0 &&
-        /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
-        strcmp(auth_domain, f_domain) == 0) {
-      /* User and domain matched, check the password */
-      mg_mkmd5resp(
-          hm->method.p, hm->method.len, hm->uri.p,
-          hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0),
-          f_ha1, strlen(f_ha1), nonce, strlen(nonce), nc, strlen(nc), cnonce,
-          strlen(cnonce), qop, strlen(qop), expected_response);
+        mg_vcmp(&username, f_user) == 0 &&
+        mg_vcmp(&auth_domain, f_domain) == 0) {
+      /* Username and domain matched, check the password */
+      mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
+                   nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
+                   qop.p, qop.len, expected_response);
       LOG(LL_DEBUG,
-          ("%s %s %s %s", user, f_domain, response, expected_response));
-      return mg_casecmp(response, expected_response) == 0;
+          ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain,
+           (int) response.len, response.p, expected_response));
+      return mg_ncasecmp(response.p, expected_response, response.len) == 0;
     }
   }
 
diff --git a/mongoose.h b/mongoose.h
index d23c70161..a94142741 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -4870,6 +4870,18 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
 int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
                               FILE *fp);
 
+/*
+ * Authenticates given response params against an opened password file.
+ * Returns 1 if authenticated, 0 otherwise.
+ *
+ * It's used by mg_http_check_digest_auth().
+ */
+int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
+                         struct mg_str username, struct mg_str cnonce,
+                         struct mg_str response, struct mg_str qop,
+                         struct mg_str nc, struct mg_str nonce,
+                         struct mg_str auth_domain, FILE *fp);
+
 /*
  * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
  * This function sends the buffer size as hex number + newline first, then
-- 
GitLab