libmicrohttpd

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

test_digestauth_with_arguments.c (9554B)


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