libmicrohttpd

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

test_digestauth_with_arguments.c (9721B)


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