libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_digestauth_sha256.c (10150B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010, 2018 Christian Grothoff
      4      Copyright (C) 2019-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file daemontest_digestauth_sha256.c
     24  * @brief  Testcase for libmicrohttpd Digest Auth with SHA256
     25  * @author Amr Ali
     26  * @author Christian Grothoff
     27  * @author Karlson2k (Evgeny Grin)
     28  */
     29 
     30 #include "mhd_options.h"
     31 #include "platform.h"
     32 #include <curl/curl.h>
     33 #include <microhttpd.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <time.h>
     37 #include <errno.h>
     38 
     39 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
     40   (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
     41 #define NEED_GCRYP_INIT 1
     42 #include <gcrypt.h>
     43 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
     44 
     45 #ifndef WINDOWS
     46 #include <sys/socket.h>
     47 #include <unistd.h>
     48 #else
     49 #include <wincrypt.h>
     50 #endif
     51 
     52 #define PAGE \
     53   "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
     54 
     55 #define DENIED \
     56   "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
     57 
     58 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
     59 
     60 struct CBC
     61 {
     62   char *buf;
     63   size_t pos;
     64   size_t size;
     65 };
     66 
     67 
     68 static size_t
     69 copyBuffer (void *ptr,
     70             size_t size,
     71             size_t nmemb,
     72             void *ctx)
     73 {
     74   struct CBC *cbc = ctx;
     75 
     76   if (cbc->pos + size * nmemb > cbc->size)
     77     return 0;                   /* overflow */
     78   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     79   cbc->pos += size * nmemb;
     80   return size * nmemb;
     81 }
     82 
     83 
     84 static enum MHD_Result
     85 ahc_echo (void *cls,
     86           struct MHD_Connection *connection,
     87           const char *url,
     88           const char *method,
     89           const char *version,
     90           const char *upload_data,
     91           size_t *upload_data_size,
     92           void **req_cls)
     93 {
     94   struct MHD_Response *response;
     95   char *username;
     96   const char *password = "testpass";
     97   const char *realm = "test@example.com";
     98   enum MHD_Result ret;
     99   int ret_i;
    100   static int already_called_marker;
    101   (void) cls; (void) url;                         /* Unused. Silent compiler warning. */
    102   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    103   (void) upload_data_size; (void) req_cls;        /* Unused. Silent compiler warning. */
    104 
    105   if (&already_called_marker != *req_cls)
    106   { /* Called for the first time, request not fully read yet */
    107     *req_cls = &already_called_marker;
    108     /* Wait for complete request */
    109     return MHD_YES;
    110   }
    111 
    112   username = MHD_digest_auth_get_username (connection);
    113   if ( (username == NULL) ||
    114        (0 != strcmp (username, "testuser")) )
    115   {
    116     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    117                                                        DENIED);
    118     ret = MHD_queue_auth_fail_response2 (connection,
    119                                          realm,
    120                                          MY_OPAQUE,
    121                                          response,
    122                                          MHD_NO,
    123                                          MHD_DIGEST_ALG_SHA256);
    124     MHD_destroy_response (response);
    125     return ret;
    126   }
    127   ret_i = MHD_digest_auth_check2 (connection,
    128                                   realm,
    129                                   username,
    130                                   password,
    131                                   300,
    132                                   MHD_DIGEST_ALG_SHA256);
    133   MHD_free (username);
    134   if (ret_i != MHD_YES)
    135   {
    136     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    137                                                        DENIED);
    138     if (NULL == response)
    139       return MHD_NO;
    140     ret = MHD_queue_auth_fail_response2 (connection,
    141                                          realm,
    142                                          MY_OPAQUE,
    143                                          response,
    144                                          (MHD_INVALID_NONCE == ret_i) ?
    145                                          MHD_YES : MHD_NO,
    146                                          MHD_DIGEST_ALG_SHA256);
    147     MHD_destroy_response (response);
    148     return ret;
    149   }
    150   response = MHD_create_response_from_buffer_static (strlen (PAGE),
    151                                                      PAGE);
    152   ret = MHD_queue_response (connection,
    153                             MHD_HTTP_OK,
    154                             response);
    155   MHD_destroy_response (response);
    156   return ret;
    157 }
    158 
    159 
    160 static unsigned int
    161 testDigestAuth (void)
    162 {
    163   CURL *c;
    164   CURLcode errornum;
    165   struct MHD_Daemon *d;
    166   struct CBC cbc;
    167   char buf[2048];
    168   char rnd[8];
    169   uint16_t port;
    170   char url[128];
    171 #ifndef WINDOWS
    172   int fd;
    173   size_t len;
    174   size_t off = 0;
    175 #endif /* ! WINDOWS */
    176 
    177   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    178     port = 0;
    179   else
    180     port = 1167;
    181 
    182   cbc.buf = buf;
    183   cbc.size = 2048;
    184   cbc.pos = 0;
    185 #ifndef WINDOWS
    186   fd = open ("/dev/urandom",
    187              O_RDONLY);
    188   if (-1 == fd)
    189   {
    190     fprintf (stderr,
    191              "Failed to open `%s': %s\n",
    192              "/dev/urandom",
    193              strerror (errno));
    194     return 1;
    195   }
    196   while (off < 8)
    197   {
    198     len = (size_t) read (fd,
    199                          rnd + off,
    200                          8 - off);
    201     if (len == (size_t) -1)
    202     {
    203       fprintf (stderr,
    204                "Failed to read `%s': %s\n",
    205                "/dev/urandom",
    206                strerror (errno));
    207       (void) close (fd);
    208       return 1;
    209     }
    210     off += len;
    211   }
    212   (void) close (fd);
    213 #else
    214   {
    215     HCRYPTPROV cc;
    216     BOOL b;
    217 
    218     b = CryptAcquireContext (&cc,
    219                              NULL,
    220                              NULL,
    221                              PROV_RSA_FULL,
    222                              CRYPT_VERIFYCONTEXT);
    223     if (b == 0)
    224     {
    225       fprintf (stderr,
    226                "Failed to acquire crypto provider context: %lu\n",
    227                GetLastError ());
    228       return 1;
    229     }
    230     b = CryptGenRandom (cc, 8, (BYTE *) rnd);
    231     if (b == 0)
    232     {
    233       fprintf (stderr,
    234                "Failed to generate 8 random bytes: %lu\n",
    235                GetLastError ());
    236     }
    237     CryptReleaseContext (cc, 0);
    238     if (b == 0)
    239       return 1;
    240   }
    241 #endif
    242   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    243                         port, NULL, NULL,
    244                         &ahc_echo, NULL,
    245                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
    246                         MHD_OPTION_NONCE_NC_SIZE, 300,
    247                         MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999,
    248                         MHD_OPTION_END);
    249   if (d == NULL)
    250     return 1;
    251   if (0 == port)
    252   {
    253     const union MHD_DaemonInfo *dinfo;
    254 
    255     dinfo = MHD_get_daemon_info (d,
    256                                  MHD_DAEMON_INFO_BIND_PORT);
    257     if ( (NULL == dinfo) ||
    258          (0 == dinfo->port) )
    259     {
    260       MHD_stop_daemon (d);
    261       return 32;
    262     }
    263     port = dinfo->port;
    264   }
    265   snprintf (url,
    266             sizeof (url),
    267             "http://127.0.0.1:%u/bar%%20foo?key=value",
    268             (unsigned int) port);
    269   c = curl_easy_init ();
    270   curl_easy_setopt (c, CURLOPT_URL, url);
    271   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    272   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    273   curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    274   curl_easy_setopt (c, CURLOPT_USERPWD, "testuser:testpass");
    275   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    276   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    277   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    278   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    279   /* NOTE: use of CONNECTTIMEOUT without also
    280      setting NOSIGNAL results in really weird
    281      crashes on my system!*/
    282   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    283   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    284   {
    285     fprintf (stderr,
    286              "curl_easy_perform failed: `%s'\n",
    287              curl_easy_strerror (errornum));
    288     curl_easy_cleanup (c);
    289     MHD_stop_daemon (d);
    290     return 2;
    291   }
    292   curl_easy_cleanup (c);
    293   MHD_stop_daemon (d);
    294   if (cbc.pos != strlen (PAGE))
    295     return 4;
    296   if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE)))
    297     return 8;
    298   return 0;
    299 }
    300 
    301 
    302 int
    303 main (int argc, char *const *argv)
    304 {
    305   unsigned int errorCount = 0;
    306   curl_version_info_data *d = curl_version_info (CURLVERSION_NOW);
    307   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    308 #if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62)
    309   if (1)
    310   {
    311     fprintf (stderr, "libcurl version 7.62.x has bug in processing"
    312              "URI with GET arguments for Digest Auth.\n");
    313     fprintf (stderr, "This test cannot be performed.\n");
    314     exit (77);
    315   }
    316 #endif /* libcurl version 7.62.x */
    317 
    318 #ifdef CURL_VERSION_SSPI
    319   if (0 != (d->features & CURL_VERSION_SSPI))
    320     return 77; /* Skip test, W32 SSPI doesn't support sha256 digest */
    321 #endif /* CURL_VERSION_SSPI */
    322 
    323   /* curl added SHA256 support in 7.57 = 7.0x39 */
    324   if (d->version_num < 0x073900)
    325     return 77; /* skip test, curl is too old */
    326 #ifdef NEED_GCRYP_INIT
    327   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    328 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    329   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    330 #endif /* GCRYCTL_INITIALIZATION_FINISHED */
    331 #endif /* NEED_GCRYP_INIT */
    332   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    333     return 2;
    334   errorCount += testDigestAuth ();
    335   if (errorCount != 0)
    336     fprintf (stderr, "Error (code: %u)\n", errorCount);
    337   curl_global_cleanup ();
    338   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    339 }