gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] 03/03: digest_auth_example_adv: added new example


From: gnunet
Subject: [libmicrohttpd] 03/03: digest_auth_example_adv: added new example
Date: Sun, 28 Jan 2024 23:15:06 +0100

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

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit bc827fcc09ac66b87a6ac052982beda393aeec60
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Sun Jan 28 23:14:44 2024 +0100

    digest_auth_example_adv: added new example
    
    The new example demonstrates advanced usage of the digest auth API
---
 src/examples/Makefile.am               |    8 +-
 src/examples/digest_auth_example_adv.c | 1049 ++++++++++++++++++++++++++++++++
 2 files changed, 1056 insertions(+), 1 deletion(-)

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_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 */

-- 
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]