libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

minimal_auth_digest.c (8324B)


      1 /* SPDX-License-Identifier: 0BSD */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2024 Evgeny Grin (Karlson2k)
      5 
      6   Permission to use, copy, modify, and/or distribute this software for
      7   any purpose with or without fee is hereby granted.
      8 
      9   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
     10   WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     11   OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
     12   FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
     13   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     14   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     15   OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16 */
     17 /**
     18  * @file minimal_auth_digest.c
     19  * @brief  Minimal example for Digest Authentication
     20  * @author Karlson2k (Evgeny Grin)
     21  */
     22 
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <microhttpd2.h>
     27 #if defined(_WIN32) && ! defined(__CYGWIN__)
     28 #  include <wincrypt.h> /* For entropy generation */
     29 #else
     30 #  include <sys/types.h>
     31 #  include <fcntl.h>  /* open() function */
     32 #  include <unistd.h> /* close() function */
     33 #endif /* _WIN32 && ! __CYGWIN__ */
     34 
     35 static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3)
     36 const struct MHD_Action *
     37 req_cb (void *cls,
     38         struct MHD_Request *MHD_RESTRICT request,
     39         const struct MHD_String *MHD_RESTRICT path,
     40         enum MHD_HTTP_Method method,
     41         uint_fast64_t upload_size)
     42 {
     43   static const char secret_page[] = "Welcome to the cave of treasures!\n";
     44   static const char auth_required_page[] =
     45     "You need to know the secret to get in.\n";
     46   static const char msg_forbidden_page[] =
     47     "You are not allowed to enter. Go away!\n";
     48   static const char msg_bad_header_page[] =
     49     "The Authorization header data is invalid\n";
     50   static const char realm[] = "The secret cave";
     51   static const char allowed_username[] = "alibaba";
     52   static const char allowed_password[] = "open sesam";
     53   static const size_t allowed_username_len =
     54     (sizeof(allowed_username) / sizeof(char) - 1);
     55   union MHD_RequestInfoDynamicData req_data;
     56   const struct MHD_AuthDigestInfo *uname; /* a shortcut */
     57   enum MHD_StatusCode res;
     58 
     59   (void) cls;
     60   (void) path;
     61   (void) method;
     62   (void) upload_size; /* Unused */
     63 
     64   res =
     65     MHD_request_get_info_dynamic (request,
     66                                   MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO,
     67                                   &req_data);
     68   if (MHD_SC_AUTH_ABSENT == res)
     69     return MHD_action_digest_auth_challenge_a (
     70       request,
     71       realm,
     72       "0",
     73       NULL,
     74       MHD_NO,
     75       MHD_DIGEST_AUTH_MULT_QOP_AUTH,
     76       MHD_DIGEST_AUTH_MULT_ALGO_ANY,
     77       MHD_NO,
     78       MHD_YES,
     79       MHD_response_from_buffer_static (
     80         MHD_HTTP_STATUS_UNAUTHORIZED,
     81         sizeof(auth_required_page) / sizeof(char) - 1,
     82         auth_required_page));
     83 
     84   if (MHD_SC_REQ_AUTH_DATA_BROKEN == res)
     85     return MHD_action_from_response (
     86       request,
     87       MHD_response_from_buffer_static (
     88         MHD_HTTP_STATUS_BAD_REQUEST,
     89         sizeof(msg_bad_header_page) / sizeof(char) - 1,
     90         msg_bad_header_page));
     91 
     92   if (MHD_SC_OK != res)
     93     return MHD_action_abort_request (request);
     94 
     95   /* Assign result to short-named variable for convenience */
     96   uname = req_data.v_auth_digest_info;
     97   if ((uname->username.len == allowed_username_len) &&
     98       (memcmp (allowed_username,
     99                uname->username.cstr,
    100                uname->username.len) == 0))
    101   {
    102     /* The client gave the correct username. Check the password match. */
    103     enum MHD_DigestAuthResult auth_res;
    104 
    105     auth_res = MHD_digest_auth_check (request,
    106                                       realm,
    107                                       allowed_username,
    108                                       allowed_password,
    109                                       0,
    110                                       MHD_DIGEST_AUTH_MULT_QOP_AUTH,
    111                                       MHD_DIGEST_AUTH_MULT_ALGO_ANY);
    112 
    113     if (MHD_DAUTH_OK == auth_res)
    114       /* User authenticated */
    115       return MHD_action_from_response (
    116         request,
    117         MHD_response_from_buffer_static (
    118           MHD_HTTP_STATUS_OK,
    119           sizeof(secret_page) / sizeof(char) - 1,
    120           secret_page));
    121 
    122     if (MHD_DAUTH_NONCE_STALE == auth_res)
    123       return MHD_action_digest_auth_challenge_a (
    124         request,
    125         realm,
    126         "0",
    127         NULL,
    128         MHD_YES /* Indicate "stale" nonce */,
    129         MHD_DIGEST_AUTH_MULT_QOP_AUTH,
    130         MHD_DIGEST_AUTH_MULT_ALGO_ANY,
    131         MHD_NO,
    132         MHD_YES,
    133         MHD_response_from_buffer_static (
    134           MHD_HTTP_STATUS_UNAUTHORIZED,
    135           sizeof(auth_required_page) / sizeof(char) - 1,
    136           auth_required_page));
    137 
    138     if (MHD_DAUTH_NONCE_WRONG <= auth_res)
    139       /* Wrong password or attack attempt */
    140       return MHD_action_from_response (
    141         request,
    142         MHD_response_from_buffer_static (
    143           MHD_HTTP_STATUS_FORBIDDEN,
    144           sizeof(msg_forbidden_page) / sizeof(char) - 1,
    145           msg_forbidden_page));
    146   }
    147   /* Wrong username */
    148 
    149   return MHD_action_from_response (
    150     request,
    151     MHD_response_from_buffer_static (
    152       MHD_HTTP_STATUS_FORBIDDEN,
    153       sizeof(msg_forbidden_page) / sizeof(char) - 1,
    154       msg_forbidden_page));
    155 }
    156 
    157 
    158 static char entropy_bytes[32];
    159 
    160 static int
    161 init_entropy_bytes (void);
    162 
    163 int
    164 main (int argc,
    165       char *const *argv)
    166 {
    167   struct MHD_Daemon *d;
    168   int port;
    169 
    170   if (argc != 2)
    171   {
    172     fprintf (stderr,
    173              "Usage:\n%s PORT\n",
    174              argv[0]);
    175     return 1;
    176   }
    177   port = atoi (argv[1]);
    178   if ((1 > port) || (65535 < port))
    179   {
    180     fprintf (stderr,
    181              "The PORT must be a numeric value between 1 and 65535.\n");
    182     return 2;
    183   }
    184   if (! init_entropy_bytes ())
    185     return 11;
    186 
    187   d = MHD_daemon_create (&req_cb,
    188                          NULL);
    189   if (NULL == d)
    190   {
    191     fprintf (stderr,
    192              "Failed to create MHD daemon.\n");
    193     return 3;
    194   }
    195   if (MHD_SC_OK !=
    196       MHD_DAEMON_SET_OPTIONS (
    197         d,
    198         MHD_D_OPTION_WM_WORKER_THREADS (1),
    199         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
    200                                 (uint_least16_t) port),
    201         MHD_D_OPTION_RANDOM_ENTROPY (sizeof(entropy_bytes),
    202                                      entropy_bytes)))
    203   {
    204     fprintf (stderr,
    205              "Failed to set MHD daemon run parameters.\n");
    206   }
    207   else
    208   {
    209     if (MHD_SC_OK !=
    210         MHD_daemon_start (d))
    211     {
    212       fprintf (stderr,
    213                "Failed to start MHD daemon.\n");
    214     }
    215     else
    216     {
    217       printf ("The MHD daemon is listening on port %d\n"
    218               "Press ENTER to stop.\n", port);
    219       (void) fgetc (stdin);
    220     }
    221   }
    222   printf ("Stopping... ");
    223   fflush (stdout);
    224   MHD_daemon_destroy (d);
    225   printf ("OK\n");
    226   return 0;
    227 }
    228 
    229 
    230 /**
    231  * Initialise random data
    232  * @return non-zero if succeed,
    233  *         zero if failed
    234  */
    235 static int
    236 init_entropy_bytes (void)
    237 {
    238 #if ! defined(_WIN32) || defined(__CYGWIN__)
    239   int fd;
    240   ssize_t len;
    241   size_t off;
    242 
    243   fd = open ("/dev/urandom", O_RDONLY);
    244   if (-1 == fd)
    245   {
    246     fd = open ("/dev/arandom", O_RDONLY);
    247     if (-1 == fd)
    248       fd = open ("/dev/random", O_RDONLY);
    249   }
    250   if (0 > fd)
    251   {
    252     fprintf (stderr, "Failed to open random data source.\n");
    253     return 0;
    254   }
    255   for (off = 0; off < sizeof (entropy_bytes); off += (size_t) len)
    256   {
    257     len = read (fd,
    258                 entropy_bytes + off,
    259                 sizeof (entropy_bytes) - off);
    260     if (0 >= len)
    261     {
    262       fprintf (stderr, "Failed to read random data source.\n");
    263       (void) close (fd);
    264       return 0;
    265     }
    266   }
    267   (void) close (fd);
    268   return ! 0;
    269 #else  /* Native W32 */
    270   HCRYPTPROV cc;
    271   BOOL b;
    272 
    273   b = CryptAcquireContext (&cc,
    274                            NULL,
    275                            NULL,
    276                            PROV_RSA_FULL,
    277                            CRYPT_VERIFYCONTEXT);
    278   if (FALSE == b)
    279   {
    280     fprintf (stderr,
    281              "Failed to acquire crypto provider context: %lu\n",
    282              (unsigned long) GetLastError ());
    283     return 0;
    284   }
    285   b = CryptGenRandom (cc, sizeof(entropy_bytes), (BYTE *) entropy_bytes);
    286   if (FALSE == b)
    287   {
    288     fprintf (stderr,
    289              "Failed to generate random bytes: %lu\n",
    290              GetLastError ());
    291   }
    292   CryptReleaseContext (cc, 0);
    293   return (FALSE != b);
    294 #endif /* Native W32 */
    295 }