gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (32aa4d0d -> bc827fcc)


From: gnunet
Subject: [libmicrohttpd] branch master updated (32aa4d0d -> bc827fcc)
Date: Sun, 28 Jan 2024 23:15:03 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 32aa4d0d fix #8012
     new e981dee0 Fixed some doxy for digest auth
     new f7969b87 digest_auth_example: updated and fixed
     new bc827fcc digest_auth_example_adv: added new example

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/examples/Makefile.am               |    8 +-
 src/examples/digest_auth_example.c     |  134 ++--
 src/examples/digest_auth_example_adv.c | 1049 ++++++++++++++++++++++++++++++++
 src/include/microhttpd.h               |    7 +-
 src/microhttpd/digestauth.c            |    9 +-
 5 files changed, 1149 insertions(+), 58 deletions(-)
 create mode 100644 src/examples/digest_auth_example_adv.c

diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am
index e22fe7ed..cdf509e5 100644
--- a/src/examples/Makefile.am
+++ b/src/examples/Makefile.am
@@ -70,7 +70,8 @@ endif
 
 if ENABLE_DAUTH
 noinst_PROGRAMS += \
- digest_auth_example
+ digest_auth_example \
+ digest_auth_example_adv
 endif
 
 if ENABLE_BAUTH
@@ -215,6 +216,11 @@ digest_auth_example_SOURCES = \
 digest_auth_example_LDADD = \
  $(top_builddir)/src/microhttpd/libmicrohttpd.la
 
+digest_auth_example_adv_SOURCES = \
+ digest_auth_example_adv.c
+digest_auth_example_adv_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
 refuse_post_example_SOURCES = \
  refuse_post_example.c
 refuse_post_example_LDADD = \
diff --git a/src/examples/digest_auth_example.c 
b/src/examples/digest_auth_example.c
index 8d5a4041..7eaf60f1 100644
--- a/src/examples/digest_auth_example.c
+++ b/src/examples/digest_auth_example.c
@@ -1,7 +1,7 @@
 /*
      This file is part of libmicrohttpd
      Copyright (C) 2010 Christian Grothoff (and other contributing authors)
-     Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
+     Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
 
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public
@@ -27,7 +27,12 @@
 #include "platform.h"
 #include <microhttpd.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <errno.h>
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+#  include <wincrypt.h>
+#endif /* _WIN32 && ! __CYGWIN__ */
+
 
 #define PAGE \
   "<html><head><title>libmicrohttpd demo</title></head>" \
@@ -48,9 +53,10 @@ ahc_echo (void *cls,
           const char *upload_data, size_t *upload_data_size, void **req_cls)
 {
   struct MHD_Response *response;
-  char *username;
-  const char *password = "testpass";
-  const char *realm = "test@example.com";
+  /* Only one user has access to the page */
+  static const char *username = "testuser";
+  static const char *password = "testpass";
+  static const char *realm = "test@example.com";
   enum MHD_DigestAuthResult res_e;
   enum MHD_Result ret;
   static int already_called_marker;
@@ -68,26 +74,17 @@ ahc_echo (void *cls,
     return MHD_YES;
   }
 
-  username = MHD_digest_auth_get_username (connection);
-  if (NULL == username)
-  {
-    response =
-      MHD_create_response_from_buffer_static (strlen (DENIED),
-                                              DENIED);
-    ret = MHD_queue_auth_fail_response2 (connection, realm,
-                                         MY_OPAQUE_STR,
-                                         response,
-                                         MHD_NO,
-                                         MHD_DIGEST_ALG_MD5);
-    MHD_destroy_response (response);
-    return ret;
-  }
-  res_e = MHD_digest_auth_check3 (connection, realm,
-                                  username,
-                                  password,
-                                  300, 60, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
-                                  MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
-  MHD_free (username);
+  /* No need to call MHD_digest_auth_get_username3() as the only
+   * one user has an access. The username match is checked by
+   * MHD_digest_auth_check3() function. */
+  res_e = MHD_digest_auth_check3 (
+    connection,
+    realm,
+    username,
+    password,
+    0, 0,
+    MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
+    MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION);
   if (res_e != MHD_DAUTH_OK)
   {
     response =
@@ -95,12 +92,18 @@ ahc_echo (void *cls,
                                               DENIED);
     if (NULL == response)
       return MHD_NO;
-    ret = MHD_queue_auth_fail_response2 (connection, realm,
-                                         MY_OPAQUE_STR,
-                                         response,
-                                         (res_e == MHD_DAUTH_NONCE_STALE) ?
-                                         MHD_YES : MHD_NO,
-                                         MHD_DIGEST_ALG_MD5);
+    ret = MHD_queue_auth_required_response3 (
+      connection,
+      realm,
+      MY_OPAQUE_STR,
+      NULL,
+      response,
+      (res_e == MHD_DAUTH_NONCE_STALE) ? MHD_YES : MHD_NO,
+      MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
+      MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION,
+      MHD_NO,
+      MHD_YES);
+
     MHD_destroy_response (response);
     return ret;
   }
@@ -114,10 +117,7 @@ ahc_echo (void *cls,
 int
 main (int argc, char *const *argv)
 {
-  int fd;
   char rnd[8];
-  ssize_t len;
-  size_t off;
   struct MHD_Daemon *d;
   unsigned int port;
 
@@ -125,33 +125,67 @@ main (int argc, char *const *argv)
        (1 != sscanf (argv[1], "%u", &port)) ||
        (65535 < port) )
   {
-    printf ("%s PORT\n", argv[0]);
+    fprintf (stderr, "%s PORT\n", argv[0]);
     return 1;
   }
 
-  fd = open ("/dev/urandom", O_RDONLY);
-  if (-1 == fd)
-  {
-    fprintf (stderr, "Failed to open `%s': %s\n",
-             "/dev/urandom",
-             strerror (errno));
-    return 1;
-  }
-  off = 0;
-  while (off < 8)
+  if (1)
   {
-    len = read (fd, rnd, 8);
-    if (0 > len)
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+    int fd;
+    ssize_t len;
+    size_t off;
+
+    fd = open ("/dev/urandom", O_RDONLY);
+    if (-1 == fd)
     {
-      fprintf (stderr, "Failed to read `%s': %s\n",
+      fprintf (stderr, "Failed to open `%s': %s\n",
                "/dev/urandom",
                strerror (errno));
-      (void) close (fd);
       return 1;
     }
-    off += (size_t) len;
+    for (off = 0; off < sizeof(rnd); off += (size_t) len)
+    {
+      len = read (fd, rnd, 8);
+      if (0 > len)
+      {
+        fprintf (stderr, "Failed to read `%s': %s\n",
+                 "/dev/urandom",
+                 strerror (errno));
+        (void) close (fd);
+        return 1;
+      }
+    }
+    (void) close (fd);
+#else  /* Native W32 */
+    HCRYPTPROV cc;
+    BOOL b;
+
+    b = CryptAcquireContext (&cc,
+                             NULL,
+                             NULL,
+                             PROV_RSA_FULL,
+                             CRYPT_VERIFYCONTEXT);
+    if (FALSE == b)
+    {
+      fprintf (stderr,
+               "Failed to acquire crypto provider context: %lu\n",
+               (unsigned long) GetLastError ());
+      return 1;
+    }
+    b = CryptGenRandom (cc, sizeof(rnd), (BYTE *) rnd);
+    if (FALSE == b)
+    {
+      fprintf (stderr,
+               "Failed to generate 8 random bytes: %lu\n",
+               GetLastError ());
+    }
+    CryptReleaseContext (cc, 0);
+    if (FALSE == b)
+      return 1;
+#endif /* Native W32 */
   }
-  (void) close (fd);
+
   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
                         (uint16_t) port,
diff --git a/src/examples/digest_auth_example_adv.c 
b/src/examples/digest_auth_example_adv.c
new file mode 100644
index 00000000..0960a81d
--- /dev/null
+++ b/src/examples/digest_auth_example_adv.c
@@ -0,0 +1,1049 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
 USA
+*/
+/**
+ * @file digest_auth_example_adv.c
+ * @brief Advanced example for digest auth with libmicrohttpd
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+#  include <errno.h>
+#  include <fcntl.h>
+#  include <unistd.h>
+#else  /* Native W32 */
+#  include <wincrypt.h>
+#endif /* Native W32 */
+
+#define SEC_AREA1_URL "/secret_page/"
+#define SEC_AREA2_URL "/super_secret_page/"
+
+#define MAIN_PAGE \
+  "<html><head><title>Welcome to the site</title></head>" \
+  "<body><p><a href=\"" SEC_AREA1_URL "\">Restricted Page</a></p>" \
+  "<p><a href=\"" SEC_AREA2_URL "\">Very Restricted Page</a></p></body></html>"
+
+#define OPAQUE_DATA "ServerOpaqueData"
+
+#define REALM "authenticated_users@thishost"
+
+/**
+ * Force select "MD5" algorithm instead of MHD default (currently the same) if 
non-zero.
+ */
+static int force_md5 = 0;
+/**
+ * Force select "SHA-256" algorithm instead of MHD default (MD5) if non-zero.
+ */
+static int force_sha256 = 0;
+/**
+ * Force select "SHA-512/256" algorithm instead of MHD default (MD5) if 
non-zero.
+ */
+static int force_sha512_256 = 0;
+/**
+ * Disable fallback to (less secure) RFC2069 if non-zero.
+ */
+static int allow_rfc2069 = 0;
+
+/**
+ * The daemon's port
+ */
+static uint16_t daemon_port = 0;
+
+/**
+ * User record.
+ * This kind of data (or something similar) should be stored in some database
+ * or file.
+ */
+struct UserEntry
+{
+  /**
+   * The username.
+   * Static data is used in this example.
+   * In real application dynamic buffer or fixed size array could be used.
+   */
+  const char *username;
+#if 0 /* Disabled code */
+  /* The cleartext password is not stored in the database.
+     The more secure "userdigest" is used instead. */
+  /**
+   * The password.
+   * Static data is used in this example.
+   * In real application dynamic buffer or fixed size array could be used.
+   */
+  const char *password;
+#endif /* Disabled code */
+  /**
+   * The realm for this entry.
+   * Static data is used in this example.
+   * In real application dynamic buffer or fixed size array could be used.
+   */
+  const char *realm;
+
+  /**
+   * The MD5 hash of the username together with the realm.
+   * This hash can be used by the client to send the username in encrypted
+   * form.
+   * The purpose of userhash is to hide user identity when transmitting
+   * requests over insecure link.
+   */
+  uint8_t userhash_md5[MHD_MD5_DIGEST_SIZE];
+  /**
+   * The MD5 hash of the username with the password and the realm.
+   * It is used to verify that password used by the client matches password
+   * required by the server.
+   * The purpose of userhash is to avoid keeping the password in cleartext
+   * on the server side.
+   */
+  uint8_t userdigest_md5[MHD_MD5_DIGEST_SIZE];
+
+  /**
+   * The SHA-256 hash of the username together with the realm.
+   * This hash can be used by the client to send the username in encrypted
+   * form.
+   * The purpose of userhash is to hide user identity when transmitting
+   * requests over insecure link.
+   */
+  uint8_t userhash_sha256[MHD_SHA256_DIGEST_SIZE];
+  /**
+   * The SHA-256 hash of the username with the password and the realm.
+   * It is used to verify that password used by the client matches password
+   * required by the server.
+   * The purpose of userhash is to avoid keeping the password in cleartext
+   * on the server side.
+   */
+  uint8_t userdigest_sha256[MHD_SHA256_DIGEST_SIZE];
+
+  /**
+   * The SHA-512/256 hash of the username together with the realm.
+   * This hash can be used by the client to send the username in encrypted
+   * form.
+   * The purpose of userhash is to hide user identity when transmitting
+   * requests over insecure link.
+   */
+  uint8_t userhash_sha512_256[MHD_SHA512_256_DIGEST_SIZE];
+  /**
+   * The SHA-512/256 hash of the username with the password and the realm.
+   * It is used to verify that password used by the client matches password
+   * required by the server.
+   * The purpose of userhash is to avoid keeping the password in cleartext
+   * on the server side.
+   */
+  uint8_t userdigest_sha512_256[MHD_SHA512_256_DIGEST_SIZE];
+
+  /**
+   * User has access to "area 1" if non-zero
+   */
+  int allow_area_1;
+
+  /**
+   * User has access to "area 2" if non-zero
+   */
+  int allow_area_2;
+};
+
+/**
+ * The array of user entries.
+ * In real application it should be loaded from external sources
+ * at the application startup.
+ */
+static struct UserEntry user_ids[2];
+
+/**
+ * The number of entries used in @a user_ids.
+ */
+static size_t user_ids_used = 0;
+
+/**
+ * Add new user to the users database/array.
+ *
+ * This kind of function must be used only when the new user is introduced.
+ * It must not be used at the every start of the application. The database
+ * of users should be stored somewhere and reloaded when application is
+ * started.
+ *
+ * @param username the username of the new user
+ * @param password the password of the new user
+ * @param realm the realm (the protection space) for which the new user
+ *              is added
+ * @param allow_area_1 if non-zero than user has access to the "area 1"
+ * @param allow_area_2 if non-zero than user has access to the "area 2"
+ * @return non-zero on success,
+ *         zero on failure (like no more space in the database).
+ */
+static int
+add_new_user_entry (const char *const username,
+                    const char *const password,
+                    const char *const realm,
+                    int allow_area_1,
+                    int allow_area_2)
+{
+  struct UserEntry *entry;
+  enum MHD_Result res;
+
+  if ((sizeof(user_ids) / sizeof(user_ids[0])) <= user_ids_used)
+    return 0; /* No more space to add new entry */
+
+  entry = user_ids + user_ids_used;
+
+  entry->username = username;
+  entry->realm = realm;
+
+  res = MHD_YES;
+
+  if (MHD_NO != res)
+    res = MHD_digest_auth_calc_userhash (MHD_DIGEST_AUTH_ALGO3_MD5,
+                                         username,
+                                         realm,
+                                         entry->userhash_md5,
+                                         sizeof(entry->userhash_md5));
+  if (MHD_NO != res)
+    res = MHD_digest_auth_calc_userdigest (MHD_DIGEST_AUTH_ALGO3_MD5,
+                                           username,
+                                           realm,
+                                           password,
+                                           entry->userdigest_md5,
+                                           sizeof(entry->userdigest_md5));
+
+  if (MHD_NO != res)
+    res = MHD_digest_auth_calc_userhash (MHD_DIGEST_AUTH_ALGO3_SHA256,
+                                         username,
+                                         realm,
+                                         entry->userhash_sha256,
+                                         sizeof(entry->userhash_sha256));
+  if (MHD_NO != res)
+    res = MHD_digest_auth_calc_userdigest (MHD_DIGEST_AUTH_ALGO3_SHA256,
+                                           username,
+                                           realm,
+                                           password,
+                                           entry->userdigest_sha256,
+                                           sizeof(entry->userdigest_sha256));
+
+  if (MHD_NO != res)
+    res = MHD_digest_auth_calc_userhash (MHD_DIGEST_AUTH_ALGO3_SHA512_256,
+                                         username,
+                                         realm,
+                                         entry->userhash_sha512_256,
+                                         sizeof(entry->userhash_sha512_256));
+  if (MHD_NO != res)
+    res =
+      MHD_digest_auth_calc_userdigest (MHD_DIGEST_AUTH_ALGO3_SHA512_256,
+                                       username,
+                                       realm,
+                                       password,
+                                       entry->userdigest_sha512_256,
+                                       sizeof(entry->userdigest_sha512_256));
+
+  if (MHD_NO == res)
+    return 0; /* Failure exit point */
+
+  entry->allow_area_1 = allow_area_1;
+  entry->allow_area_2 = allow_area_2;
+
+  user_ids_used++;
+
+  return ! 0;
+}
+
+
+/**
+ * Find the user entry for specified username
+ * @param username the username to find
+ * @return NULL if no entry for specified username is found,
+ *         pointer to user entry if found
+ */
+static struct UserEntry *
+find_entry_by_username (const char *const username)
+{
+  size_t i;
+
+  for (i = 0; i < (sizeof(user_ids) / sizeof(user_ids[0])); ++i)
+  {
+    struct UserEntry *entry;
+
+    entry = user_ids + i;
+    if (0 == strcmp (username, entry->username))
+      return entry;
+  }
+  return NULL;
+}
+
+
+/**
+ * Find the user entry for specified userhash
+ * @param algo3 the algorithm used for userhash calculation
+ * @param userhash the userhash identifier to find
+ * @param userhash_size the size @a userhash in bytes
+ * @return NULL if no entry for specified userhash is found,
+ *         pointer to user entry if found
+ */
+static struct UserEntry *
+find_entry_by_userhash (enum MHD_DigestAuthAlgo3 algo3,
+                        const void *userhash,
+                        size_t userhash_size)
+{
+  size_t i;
+
+  if (MHD_digest_get_hash_size (algo3) != userhash_size)
+    return NULL; /* Wrong length of the userhash */
+
+  switch (algo3)
+  {
+  case MHD_DIGEST_AUTH_ALGO3_MD5:
+  case MHD_DIGEST_AUTH_ALGO3_MD5_SESSION: /* An extra case not used currently 
*/
+    if (sizeof(user_ids[0].userhash_md5) != userhash_size) /* Extra check. The 
size was checked before */
+      return NULL;
+    for (i = 0; i < (sizeof(user_ids) / sizeof(user_ids[0])); ++i)
+    {
+      struct UserEntry *entry;
+
+      entry = user_ids + i;
+      if (0 == memcmp (userhash, entry->userhash_md5,
+                       sizeof(entry->userhash_md5)))
+        return entry;
+    }
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_SHA256:
+  case MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION: /* An extra case not used 
currently */
+    if (sizeof(user_ids[0].userhash_sha256) != userhash_size) /* Extra check. 
The size was checked before */
+      return NULL;
+    for (i = 0; i < (sizeof(user_ids) / sizeof(user_ids[0])); ++i)
+    {
+      struct UserEntry *entry;
+
+      entry = user_ids + i;
+      if (0 == memcmp (userhash, entry->userhash_sha256,
+                       sizeof(entry->userhash_sha256)))
+        return entry;
+    }
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_SHA512_256:
+  case MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION: /* An extra case not used 
currently */
+    if (sizeof(user_ids[0].userhash_sha512_256) != userhash_size) /* Extra 
check. The size was checked before */
+      return NULL;
+    for (i = 0; i < (sizeof(user_ids) / sizeof(user_ids[0])); ++i)
+    {
+      struct UserEntry *entry;
+
+      entry = user_ids + i;
+      if (0 == memcmp (userhash, entry->userhash_sha512_256,
+                       sizeof(entry->userhash_sha512_256)))
+        return entry;
+    }
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_INVALID: /* Mute compiler warning. Impossible 
value in this context. */
+  default:
+    break;
+  }
+  return NULL;
+}
+
+
+/**
+ * Find the user entry for the user specified by provided username info
+ * @param user_info the pointer to the structure username info returned by MHD
+ * @return NULL if no entry for specified username info is found,
+ *         pointer to user entry if found
+ */
+static struct UserEntry *
+find_entry_by_userinfo (const struct MHD_DigestAuthUsernameInfo *username_info)
+{
+  if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD <= username_info->uname_type)
+    return find_entry_by_username (username_info->username);
+
+  if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == username_info->uname_type)
+    return find_entry_by_userhash (username_info->algo3,
+                                   username_info->userhash_bin,
+                                   username_info->userhash_hex_len / 2);
+
+  return NULL; /* Should be unreachable as all cases are covered before */
+}
+
+
+/**
+ * Send "Requested HTTP method is not supported" page
+ * @param c the connection structure
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_page_not_found (struct MHD_Connection *c)
+{
+  static const char page_content[] =
+    "<html><head><title>Page Not Found</title></head>" \
+    "<body>The requested page not found.</body></html>";
+  static const size_t page_content_len =
+    (sizeof(page_content) / sizeof(char)) - 1;
+  struct MHD_Response *resp;
+  enum MHD_Result ret;
+
+  resp = MHD_create_response_from_buffer_static (page_content_len,
+                                                 page_content);
+  if (NULL == resp)
+    return MHD_NO;
+
+  /* Ignore possible error when adding the header as the reply will work even
+     without this header. */
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE,
+                                  "text/html");
+
+  ret = MHD_queue_response (c, MHD_HTTP_NOT_FOUND, resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
+ * Get enum MHD_DigestAuthMultiAlgo3 value to be used for authentication.
+ * @return the algorithm number/value
+ */
+static enum MHD_DigestAuthMultiAlgo3
+get_m_algo (void)
+{
+  if (force_md5)
+    return MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (force_sha256)
+    return MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else if (force_sha512_256)
+    return MHD_DIGEST_AUTH_MULT_ALGO3_SHA512_256;
+  else
+    return MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+}
+
+
+/**
+ * Get enum MHD_DigestAuthMultiQOP value to be used for authentication.
+ * @return the "Quality Of Protection" number/value
+ */
+static enum MHD_DigestAuthMultiQOP
+get_m_QOP (void)
+{
+  if (allow_rfc2069)
+    return MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT;
+
+  return MHD_DIGEST_AUTH_MULT_QOP_AUTH;
+}
+
+
+/**
+ * Send "Authentication required" page
+ * @param c the connection structure
+ * @param stale if non-zero then "nonce stale" is indicated in the reply
+ * @param wrong_cred if non-zero then client is informed the previously
+ *                   it used wrong credentials
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_auth_required (struct MHD_Connection *c,
+                          int stale,
+                          int wrong_cred)
+{
+  static const char auth_required_content[] =
+    "<html><head><title>Authentication required</title></head>" \
+    "<body>The requested page needs authentication.</body></html>";
+  static const size_t auth_required_content_len =
+    (sizeof(auth_required_content) / sizeof(char)) - 1;
+  static const char wrong_creds_content[] =
+    "<html><head><title>Wrong credentials</title></head>" \
+    "<body>The provided credentials are incorrect.</body></html>";
+  static const size_t wrong_creds_content_len =
+    (sizeof(wrong_creds_content) / sizeof(char)) - 1;
+  struct MHD_Response *resp;
+  enum MHD_Result ret;
+
+  if (wrong_cred)
+    stale = 0; /* Force client to ask user for username and password */
+
+  if (! wrong_cred)
+    resp = MHD_create_response_from_buffer_static (auth_required_content_len,
+                                                   auth_required_content);
+  else
+    resp = MHD_create_response_from_buffer_static (wrong_creds_content_len,
+                                                   wrong_creds_content);
+  if (NULL == resp)
+    return MHD_NO;
+
+  /* Ignore possible error when adding the header as the reply will work even
+     without this header. */
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
+
+
+  ret = MHD_queue_auth_required_response3 (
+    c,
+    REALM,
+    OPAQUE_DATA, /* The "opaque data", not really useful */
+    SEC_AREA1_URL " " SEC_AREA2_URL, /* Space-separated list of URLs' initial 
parts */
+    resp,
+    stale,
+    get_m_QOP (),
+    get_m_algo (),
+    ! 0, /* Userhash support enabled */
+    ! 0 /* UTF-8 is preferred */);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
+ * Send "Forbidden" page
+ * @param c the connection structure
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_forbidden (struct MHD_Connection *c)
+{
+  static const char page_content[] =
+    "<html><head><title>Forbidden</title></head>" \
+    "<body>You do not have access to this page.</body></html>";
+  static const size_t page_content_len =
+    (sizeof(page_content) / sizeof(char)) - 1;
+  struct MHD_Response *resp;
+  enum MHD_Result ret;
+
+  resp = MHD_create_response_from_buffer_static (page_content_len, 
page_content)
+  ;
+  if (NULL == resp)
+    return MHD_NO;
+
+  /* Ignore possible error when adding the header as the reply will work even
+     without this header. */
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE,
+                                  "text/html");
+
+  ret = MHD_queue_response (c, MHD_HTTP_FORBIDDEN, resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
+ * Send "Area 1" pages
+ * @param c the connection structure
+ * @param url the requested URL
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_area1_pages (struct MHD_Connection *c,
+                        const char *url)
+{
+
+  if (0 == strcmp (url, SEC_AREA1_URL ""))
+  {
+    static const char page_content[] =
+      "<html><head><title>Restricted secret page</title></head>" \
+      "<body>Welcome to the restricted area</body></html>";
+    static const size_t page_content_len =
+      (sizeof(page_content) / sizeof(char)) - 1;
+    struct MHD_Response *resp;
+    enum MHD_Result ret;
+
+    resp = MHD_create_response_from_buffer_static (page_content_len,
+                                                   page_content);
+    if (NULL == resp)
+      return MHD_NO;
+
+    /* Ignore possible error when adding the header as the reply will work even
+       without this header. */
+    (void) MHD_add_response_header (resp, MHD_HTTP_HEADER_CONTENT_TYPE,
+                                    "text/html");
+
+    ret = MHD_queue_response (c, MHD_HTTP_OK, resp);
+    MHD_destroy_response (resp);
+    return ret;
+  }
+  /* If needed: add handlers for other URLs in this area */
+#if 0 /* Disabled code */
+  if (0 == strcmp (url, SEC_AREA1_URL "some_path/some_page"))
+  {
+    /* Add page creation/processing code */
+  }
+#endif /* Disabled code */
+
+  /* The requested URL is unknown */
+  return reply_with_page_not_found (c);
+}
+
+
+/**
+ * Send "Area 2" pages
+ * @param c the connection structure
+ * @param url the requested URL
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_area2_pages (struct MHD_Connection *c,
+                        const char *url)
+{
+
+  if (0 == strcmp (url, SEC_AREA2_URL ""))
+  {
+    static const char page_content[] =
+      "<html><head><title>Very restricted secret page</title></head>" \
+      "<body>Welcome to the super restricted area</body></html>";
+    static const size_t page_content_len =
+      (sizeof(page_content) / sizeof(char)) - 1;
+    struct MHD_Response *resp;
+    enum MHD_Result ret;
+
+    resp = MHD_create_response_from_buffer_static (page_content_len,
+                                                   page_content);
+    if (NULL == resp)
+      return MHD_NO;
+
+    /* Ignore possible error when adding the header as the reply will work even
+       without this header. */
+    (void) MHD_add_response_header (resp, MHD_HTTP_HEADER_CONTENT_TYPE,
+                                    "text/html");
+
+    ret = MHD_queue_response (c, MHD_HTTP_OK, resp);
+    MHD_destroy_response (resp);
+    return ret;
+  }
+  /* If needed: add handlers for other URLs in this area */
+#if 0 /* Disabled code */
+  if (0 == strcmp (url, SEC_AREA2_URL "other_path/other_page"))
+  {
+    /* Add page creation/processing code */
+  }
+#endif /* Disabled code */
+
+  /* The requested URL is unknown */
+  return reply_with_page_not_found (c);
+}
+
+
+/**
+ * Handle client's request for secured areas
+ * @param c the connection structure
+ * @param url the URL requested by the client
+ * @param sec_area_num the number of secured area
+ * @return MHD_YES if request was handled (either with "denied" or with
+ *                 "allowed" result),
+ *         MHD_NO if it was an error handling the request.
+ */
+static enum MHD_Result
+handle_sec_areas_req (struct MHD_Connection *c, const char *url, unsigned int
+                      sec_area_num)
+{
+  struct MHD_DigestAuthUsernameInfo *username_info;
+  struct UserEntry *user_entry;
+  void *userdigest;
+  size_t userdigest_size;
+  enum MHD_DigestAuthResult auth_res;
+
+  username_info = MHD_digest_auth_get_username3 (c);
+
+  if (NULL == username_info)
+    return reply_with_auth_required (c, 0, 0);
+
+  user_entry = find_entry_by_userinfo (username_info);
+
+  if (NULL == user_entry)
+    return reply_with_auth_required (c, 0, 1);
+
+  switch (username_info->algo3)
+  {
+  case MHD_DIGEST_AUTH_ALGO3_MD5:
+    userdigest = user_entry->userdigest_md5;
+    userdigest_size = sizeof(user_entry->userdigest_md5);
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_SHA256:
+    userdigest = user_entry->userdigest_sha256;
+    userdigest_size = sizeof(user_entry->userdigest_sha256);
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_SHA512_256:
+    userdigest = user_entry->userdigest_sha512_256;
+    userdigest_size = sizeof(user_entry->userdigest_sha512_256);
+    break;
+  case MHD_DIGEST_AUTH_ALGO3_MD5_SESSION:
+  case MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION:
+  case MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION:
+    /* Not supported currently and not used by MHD.
+       The client incorrectly used algorithm not advertised by the server. */
+    return reply_with_auth_required (c, 0, 1);
+  case MHD_DIGEST_AUTH_ALGO3_INVALID: /* Mute compiler warning */
+  default:
+    return MHD_NO; /* Should be unreachable */
+  }
+
+  auth_res = MHD_digest_auth_check_digest3 (
+    c,
+    REALM, /* Make sure to use the proper realm, not the realm provided by the 
client and returned by "user_entry" */
+    user_entry->username,
+    userdigest,
+    userdigest_size,
+    0, /* Use daemon's default value for nonce_timeout*/
+    0, /* Use daemon's default value for max_nc */
+    get_m_QOP (),
+    (enum MHD_DigestAuthMultiAlgo3) username_info->algo3 /* Direct cast from 
"single algorithm" to "multi-algorithm" is allowed */
+    );
+
+  if (MHD_DAUTH_OK != auth_res)
+  {
+    int need_just_refresh_nonce;
+    /* Actually MHD_DAUTH_NONCE_OTHER_COND should not be returned as
+       MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE is not used for the daemon.
+       To keep the code universal the MHD_DAUTH_NONCE_OTHER_COND is
+       still checked here. */
+    need_just_refresh_nonce =
+      (MHD_DAUTH_NONCE_STALE == auth_res)
+      || (MHD_DAUTH_NONCE_OTHER_COND == auth_res);
+    return reply_with_auth_required (c,
+                                     need_just_refresh_nonce,
+                                     ! need_just_refresh_nonce);
+  }
+
+  /* The user successfully authenticated */
+
+  /* Check whether access to the request area is allowed for the user */
+  if (1 == sec_area_num)
+  {
+    if (user_entry->allow_area_1)
+      return reply_with_area1_pages (c, url);
+    else
+      return reply_with_forbidden (c);
+  }
+  else if (2 == sec_area_num)
+  {
+    if (user_entry->allow_area_2)
+      return reply_with_area2_pages (c, url);
+    else
+      return reply_with_forbidden (c);
+  }
+
+  return MHD_NO; /* Should be unreachable */
+}
+
+
+/**
+ * Send the main page
+ * @param c the connection structure
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_main_page (struct MHD_Connection *c)
+{
+  static const char page_content[] = MAIN_PAGE;
+  static const size_t page_content_len =
+    (sizeof(page_content) / sizeof(char)) - 1;
+  struct MHD_Response *resp;
+  enum MHD_Result ret;
+
+  resp = MHD_create_response_from_buffer_static (page_content_len, 
page_content)
+  ;
+  if (NULL == resp)
+    return MHD_NO;
+
+  /* Ignore possible error when adding the header as the reply will work even
+     without this header. */
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE,
+                                  "text/html");
+
+  ret = MHD_queue_response (c, MHD_HTTP_OK, resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
+ * Send "Requested HTTP method is not supported" page
+ * @param c the connection structure
+ * @return MHD_YES if response was successfully queued,
+ *         MHD_NO otherwise
+ */
+static enum MHD_Result
+reply_with_method_not_supported (struct MHD_Connection *c)
+{
+  static const char page_content[] =
+    "<html><head><title>Requested HTTP Method Is Not Supported</title></head>" 
\
+    "<body>The requested HTTP method is not supported.</body></html>";
+  static const size_t page_content_len =
+    (sizeof(page_content) / sizeof(char)) - 1;
+  struct MHD_Response *resp;
+  enum MHD_Result ret;
+
+  resp = MHD_create_response_from_buffer_static (page_content_len, 
page_content)
+  ;
+  if (NULL == resp)
+    return MHD_NO;
+
+  /* Ignore possible error when adding the header as the reply will work even
+     without this header. */
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
+
+  ret = MHD_queue_response (c, MHD_HTTP_NOT_IMPLEMENTED, resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+static enum MHD_Result
+ahc_main (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **req_cls)
+{
+  static int already_called_marker;
+  size_t url_len;
+  (void) cls;               /* Unused. Silent compiler warning. */
+  (void) version;           /* Unused. Silent compiler warning. */
+  (void) upload_data;       /* Unused. Silent compiler warning. */
+
+  if ((0 != strcmp (method, MHD_HTTP_METHOD_GET))
+      && (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
+    return reply_with_method_not_supported (connection);
+
+  if (0 != *upload_data_size)
+    return MHD_NO; /* No upload expected for GET or HEAD */
+
+  if (&already_called_marker != *req_cls)
+  { /* Called for the first time, request not fully read yet */
+    *req_cls = &already_called_marker;
+    /* Wait for complete request */
+    return MHD_YES;
+  }
+
+  if (0 == strcmp (url, "/"))
+    return reply_with_main_page (connection);
+
+  url_len = strlen (url);
+
+  if ((strlen (SEC_AREA1_URL) <= url_len)
+      && (0 == memcmp (url, SEC_AREA1_URL, strlen (SEC_AREA1_URL))))
+    return handle_sec_areas_req (connection, url, 1); /* The requested URL is 
within SEC_AREA1_URL */
+
+  if ((strlen (SEC_AREA2_URL) <= url_len)
+      && (0 == memcmp (url, SEC_AREA2_URL, strlen (SEC_AREA2_URL))))
+    return handle_sec_areas_req (connection, url, 2); /* The requested URL is 
within SEC_AREA2_URL */
+
+  return reply_with_page_not_found (connection);
+}
+
+
+/**
+ * Add new users to the users "database".
+ *
+ * In real application this kind of function must NOT be called at
+ * the application startup. Instead similar function should be
+ * called only when new user is introduced. The users "database"
+ * should be stored somewhere and reloaded at the application
+ * startup.
+ *
+ * @return non-zero on success,
+ *         zero in case of error.
+ */
+static int
+add_new_users (void)
+{
+  if (! add_new_user_entry ("joepublic",
+                            "password",
+                            REALM,
+                            ! 0,
+                            0))
+    return 0;
+
+  if (! add_new_user_entry ("superadmin",
+                            "pA$$w0Rd",
+                            REALM,
+                            ! 0,
+                            ! 0))
+    return 0;
+
+  return ! 0;
+}
+
+
+static int
+check_params (int argc, char *const *const argv)
+{
+  size_t i;
+  unsigned int port_value;
+
+  if (2 > argc)
+    return 0;
+
+  for (i = 1; i < (unsigned int) argc; ++i)
+  {
+    if (0 == strcmp (argv[i], "--md5"))
+    { /* Force use MD5 */
+      force_md5 = ! 0;
+      force_sha256 = 0;
+      force_sha512_256 = 0;
+    }
+    else if (0 == strcmp (argv[i], "--sha256"))
+    { /* Force use SHA-256 instead of default MD5 */
+      force_md5 = 0;
+      force_sha256 = ! 0;
+      force_sha512_256 = 0;
+    }
+    else if (0 == strcmp (argv[i], "--sha512-256"))
+    { /* Force use SHA-512/256 instead of default MD5 */
+      force_md5 = 0;
+      force_sha256 = 0;
+      force_sha512_256 = ! 0;
+    }
+    else if (0 == strcmp (argv[i], "--allow-rfc2069"))
+      allow_rfc2069 = ! 0; /* Allow fallback to RFC2069. Not recommended! */
+    else if ((1 == sscanf (argv[i], "%u", &port_value))
+             && (0 < port_value) && (65535 >= port_value))
+      daemon_port = (uint16_t) port_value;
+    else
+    {
+      fprintf (stderr, "Unrecognized parameter: %s\n",
+               argv[i]);
+      return 0;
+    }
+  }
+
+  if (force_sha512_256)
+    printf (
+      "Note: when testing with curl/libcurl do not be surprised with failures 
as "
+      "libcurl incorrectly implements SHA-512/256 algorithm.\n");
+  return ! 0;
+}
+
+
+static uint8_t rand_data[8];
+
+/**
+ * Initialise random data
+ * @return non-zero if succeed,
+ *         zero if failed
+ */
+static int
+init_rand_data (void)
+{
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+  int fd;
+  ssize_t len;
+  size_t off;
+
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (-1 == fd)
+  {
+    fprintf (stderr, "Failed to open '%s': %s\n",
+             "/dev/urandom",
+             strerror (errno));
+    return 0;
+  }
+  for (off = 0; off < sizeof(rand_data); off += (size_t) len)
+  {
+    len = read (fd, rand_data, 8);
+    if (0 > len)
+    {
+      fprintf (stderr, "Failed to read '%s': %s\n",
+               "/dev/urandom",
+               strerror (errno));
+      (void) close (fd);
+      return 0;
+    }
+  }
+  (void) close (fd);
+#else  /* Native W32 */
+  HCRYPTPROV cc;
+  BOOL b;
+
+  b = CryptAcquireContext (&cc,
+                           NULL,
+                           NULL,
+                           PROV_RSA_FULL,
+                           CRYPT_VERIFYCONTEXT);
+  if (FALSE == b)
+  {
+    fprintf (stderr,
+             "Failed to acquire crypto provider context: %lu\n",
+             (unsigned long) GetLastError ());
+    return 0;
+  }
+  b = CryptGenRandom (cc, sizeof(rand_data), (BYTE *) rand_data);
+  if (FALSE == b)
+  {
+    fprintf (stderr,
+             "Failed to generate 8 random bytes: %lu\n",
+             GetLastError ());
+  }
+  CryptReleaseContext (cc, 0);
+  if (FALSE == b)
+    return 0;
+#endif /* Native W32 */
+
+  return ! 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (! check_params (argc, argv))
+  {
+    fprintf (stderr, "Usage: %s [--md5|--sha256|--sha512-256] "
+             "[--allow-rfc2069] PORT\n", argv[0]);
+    return 1;
+  }
+  if (! add_new_users ())
+  {
+    fprintf (stderr, "Failed to add new users to the users database.\n");
+    return 2;
+  }
+  if (! init_rand_data ())
+  {
+    fprintf (stderr, "Failed to initialise random data.\n");
+    return 2;
+  }
+
+  d = MHD_start_daemon (
+    MHD_USE_INTERNAL_POLLING_THREAD
+    | MHD_USE_THREAD_PER_CONNECTION
+    | MHD_USE_ERROR_LOG,
+    daemon_port,
+    NULL, NULL, &ahc_main, NULL,
+    MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof(rand_data), rand_data,
+    MHD_OPTION_NONCE_NC_SIZE, 500,
+    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 180,
+    MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  printf ("Running server on port %lu.\nPress ENTER to stop.\n",
+          (unsigned long) daemon_port);
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+/* End of digest_auth_example_adv.c */
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index b0b06d51..c24fbd8b 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -5521,7 +5521,8 @@ enum MHD_DigestAuthResult
   MHD_DAUTH_NONCE_WRONG = -33,
 
   /**
-   * The 'response' is wrong. May indicate an attack attempt.
+   * The 'response' is wrong. Typically it means that wrong password used.
+   * May indicate an attack attempt.
    */
   MHD_DAUTH_RESPONSE_WRONG = -34
 };
@@ -5592,8 +5593,8 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection,
  *                            if this function succeeds, then this buffer has
  *                            #MHD_digest_get_hash_size(algo3) bytes of
  *                            userdigest upon return
- * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
- *                       at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @param bin_buf_size the size of the @a userdigest_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3) bytes long
  * @return MHD_YES on success,
  *         MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is
  *         not supported (or external error has occurred,
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 58561abb..99e9872e 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -1923,8 +1923,8 @@ calc_userdigest (struct DigestAlgorithm *da,
  *                            if this function succeeds, then this buffer has
  *                            #MHD_digest_get_hash_size(algo3) bytes of
  *                            userdigest upon return
- * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
- *                       at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @param bin_buf_size the size of the @a userdigest_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3) bytes long
  * @return MHD_YES on success,
  *         MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is
  *         not supported (or external error has occurred,
@@ -3545,8 +3545,9 @@ queue_auth_required_response3_inner (struct 
MHD_Connection *connection,
 #endif /* HAVE_MESSAGES */
     return MHD_NO;
   }
-  malgo3 &= (enum MHD_DigestAuthMultiQOP)
-            (~((enum MHD_DigestAuthMultiQOP) 
MHD_DIGEST_AUTH_ALGO3_NON_SESSION));
+  malgo3 &=
+    (enum MHD_DigestAuthMultiQOP)
+    (~((enum MHD_DigestAuthMultiQOP) MHD_DIGEST_AUTH_ALGO3_NON_SESSION));
 #ifdef MHD_MD5_SUPPORT
   if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
     s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]