[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.