libmicrohttpd

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

test_digestauth_sha256.c (10317B)


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