libmicrohttpd

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

test_digestauth.c (17206B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010 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.c
     24  * @brief  Testcase for libmicrohttpd Digest Auth
     25  * @author Amr Ali
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "mhd_options.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <errno.h>
     37 
     38 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     39    test into a marked, classifiable test error (TESTING.md, P5). */
     40 #include "mhd_panic_tripwire.h"
     41 
     42 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
     43   (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
     44 #define NEED_GCRYP_INIT 1
     45 #include <gcrypt.h>
     46 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
     47 
     48 #ifndef WINDOWS
     49 #include <sys/socket.h>
     50 #include <unistd.h>
     51 #else
     52 #include <wincrypt.h>
     53 #endif
     54 
     55 #ifndef CURL_VERSION_BITS
     56 #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
     57 #endif /* ! CURL_VERSION_BITS */
     58 #ifndef CURL_AT_LEAST_VERSION
     59 #define CURL_AT_LEAST_VERSION(x,y,z) \
     60   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
     61 #endif /* ! CURL_AT_LEAST_VERSION */
     62 
     63 #ifndef _MHD_INSTRMACRO
     64 /* Quoted macro parameter */
     65 #define _MHD_INSTRMACRO(a) #a
     66 #endif /* ! _MHD_INSTRMACRO */
     67 #ifndef _MHD_STRMACRO
     68 /* Quoted expanded macro parameter */
     69 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     70 #endif /* ! _MHD_STRMACRO */
     71 
     72 #if defined(HAVE___FUNC__)
     73 #define externalErrorExit(ignore) \
     74     _externalErrorExit_func(NULL, __func__, __LINE__)
     75 #define externalErrorExitDesc(errDesc) \
     76     _externalErrorExit_func(errDesc, __func__, __LINE__)
     77 #define libcurlErrorExit(ignore) \
     78     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     79 #define libcurlErrorExitDesc(errDesc) \
     80     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     81 #define mhdErrorExit(ignore) \
     82     _mhdErrorExit_func(NULL, __func__, __LINE__)
     83 #define mhdErrorExitDesc(errDesc) \
     84     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     85 #define checkCURLE_OK(libcurlcall) \
     86     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
     87                         __func__, __LINE__)
     88 #elif defined(HAVE___FUNCTION__)
     89 #define externalErrorExit(ignore) \
     90     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
     91 #define externalErrorExitDesc(errDesc) \
     92     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     93 #define libcurlErrorExit(ignore) \
     94     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
     95 #define libcurlErrorExitDesc(errDesc) \
     96     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     97 #define mhdErrorExit(ignore) \
     98     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
     99 #define mhdErrorExitDesc(errDesc) \
    100     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    101 #define checkCURLE_OK(libcurlcall) \
    102     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
    103                         __FUNCTION__, __LINE__)
    104 #else
    105 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    106 #define externalErrorExitDesc(errDesc) \
    107   _externalErrorExit_func(errDesc, NULL, __LINE__)
    108 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
    109 #define libcurlErrorExitDesc(errDesc) \
    110   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
    111 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    112 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    113 #define checkCURLE_OK(libcurlcall) \
    114   _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__)
    115 #endif
    116 
    117 
    118 _MHD_NORETURN static void
    119 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    120 {
    121   fflush (stdout);
    122   if ((NULL != errDesc) && (0 != errDesc[0]))
    123     fprintf (stderr, "%s", errDesc);
    124   else
    125     fprintf (stderr, "System or external library call failed");
    126   if ((NULL != funcName) && (0 != funcName[0]))
    127     fprintf (stderr, " in %s", funcName);
    128   if (0 < lineNum)
    129     fprintf (stderr, " at line %d", lineNum);
    130 
    131   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    132            strerror (errno));
    133 #ifdef MHD_WINSOCK_SOCKETS
    134   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    135 #endif /* MHD_WINSOCK_SOCKETS */
    136   fflush (stderr);
    137   exit (99);
    138 }
    139 
    140 
    141 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    142 
    143 _MHD_NORETURN static void
    144 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    145 {
    146   fflush (stdout);
    147   if ((NULL != errDesc) && (0 != errDesc[0]))
    148     fprintf (stderr, "%s", errDesc);
    149   else
    150     fprintf (stderr, "CURL library call failed");
    151   if ((NULL != funcName) && (0 != funcName[0]))
    152     fprintf (stderr, " in %s", funcName);
    153   if (0 < lineNum)
    154     fprintf (stderr, " at line %d", lineNum);
    155 
    156   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    157            strerror (errno));
    158 #ifdef MHD_WINSOCK_SOCKETS
    159   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    160 #endif /* MHD_WINSOCK_SOCKETS */
    161   if (0 != libcurl_errbuf[0])
    162     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    163 
    164   fflush (stderr);
    165   exit (99);
    166 }
    167 
    168 
    169 _MHD_NORETURN static void
    170 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    171 {
    172   fflush (stdout);
    173   if ((NULL != errDesc) && (0 != errDesc[0]))
    174     fprintf (stderr, "%s", errDesc);
    175   else
    176     fprintf (stderr, "MHD unexpected error");
    177   if ((NULL != funcName) && (0 != funcName[0]))
    178     fprintf (stderr, " in %s", funcName);
    179   if (0 < lineNum)
    180     fprintf (stderr, " at line %d", lineNum);
    181 
    182   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    183            strerror (errno));
    184 #ifdef MHD_WINSOCK_SOCKETS
    185   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    186 #endif /* MHD_WINSOCK_SOCKETS */
    187 
    188   fflush (stderr);
    189   exit (8);
    190 }
    191 
    192 
    193 static void
    194 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    195                      const char *funcName, int lineNum)
    196 {
    197   if (CURLE_OK == code)
    198     return;
    199 
    200   fflush (stdout);
    201   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    202     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    203              curl_easy_strerror (code));
    204   else
    205     fprintf (stderr, "libcurl function call resulted in '%s'",
    206              curl_easy_strerror (code));
    207   if ((NULL != funcName) && (0 != funcName[0]))
    208     fprintf (stderr, " in %s", funcName);
    209   if (0 < lineNum)
    210     fprintf (stderr, " at line %d", lineNum);
    211 
    212   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    213            strerror (errno));
    214   if (0 != libcurl_errbuf[0])
    215     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    216 
    217   fflush (stderr);
    218   exit (9);
    219 }
    220 
    221 
    222 /* Could be increased to facilitate debugging */
    223 #define TIMEOUTS_VAL 5
    224 
    225 #define MHD_URI_BASE_PATH "/bar%20foo%20without%20args"
    226 
    227 #define PAGE \
    228   "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
    229 
    230 #define DENIED \
    231   "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
    232 
    233 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
    234 
    235 struct CBC
    236 {
    237   char *buf;
    238   size_t pos;
    239   size_t size;
    240 };
    241 
    242 
    243 static size_t
    244 copyBuffer (void *ptr,
    245             size_t size,
    246             size_t nmemb,
    247             void *ctx)
    248 {
    249   struct CBC *cbc = ctx;
    250 
    251   if (cbc->pos + size * nmemb > cbc->size)
    252     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    253   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    254   cbc->pos += size * nmemb;
    255   return size * nmemb;
    256 }
    257 
    258 
    259 static enum MHD_Result
    260 ahc_echo (void *cls,
    261           struct MHD_Connection *connection,
    262           const char *url,
    263           const char *method,
    264           const char *version,
    265           const char *upload_data,
    266           size_t *upload_data_size,
    267           void **req_cls)
    268 {
    269   struct MHD_Response *response;
    270   char *username;
    271   const char *password = "testpass";
    272   const char *realm = "test@example.com";
    273   enum MHD_Result ret;
    274   int ret_i;
    275   static int already_called_marker;
    276   (void) cls; (void) url;                         /* Unused. Silent compiler warning. */
    277   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    278   (void) upload_data_size; (void) req_cls;        /* Unused. Silent compiler warning. */
    279 
    280   if (&already_called_marker != *req_cls)
    281   { /* Called for the first time, request not fully read yet */
    282     *req_cls = &already_called_marker;
    283     /* Wait for complete request */
    284     return MHD_YES;
    285   }
    286 
    287   username = MHD_digest_auth_get_username (connection);
    288   if ( (username == NULL) ||
    289        (0 != strcmp (username, "testuser")) )
    290   {
    291     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    292                                                        DENIED);
    293     if (NULL == response)
    294       mhdErrorExitDesc ("MHD_create_response_from_buffer failed");
    295     ret = MHD_queue_auth_fail_response2 (connection,
    296                                          realm,
    297                                          MY_OPAQUE,
    298                                          response,
    299                                          MHD_NO,
    300                                          MHD_DIGEST_ALG_MD5);
    301     if (MHD_YES != ret)
    302       mhdErrorExitDesc ("MHD_queue_auth_fail_response2 failed");
    303     MHD_destroy_response (response);
    304     return ret;
    305   }
    306   ret_i = MHD_digest_auth_check2 (connection,
    307                                   realm,
    308                                   username,
    309                                   password,
    310                                   300,
    311                                   MHD_DIGEST_ALG_MD5);
    312   MHD_free (username);
    313   if (ret_i != MHD_YES)
    314   {
    315     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    316                                                        DENIED);
    317     if (NULL == response)
    318       mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    319     ret = MHD_queue_auth_fail_response2 (connection,
    320                                          realm,
    321                                          MY_OPAQUE,
    322                                          response,
    323                                          (MHD_INVALID_NONCE == ret_i) ?
    324                                          MHD_YES : MHD_NO,
    325                                          MHD_DIGEST_ALG_MD5);
    326     if (MHD_YES != ret)
    327       mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    328     MHD_destroy_response (response);
    329     return ret;
    330   }
    331   response = MHD_create_response_from_buffer_static (strlen (PAGE),
    332                                                      PAGE);
    333   if (NULL == response)
    334     mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    335   ret = MHD_queue_response (connection,
    336                             MHD_HTTP_OK,
    337                             response);
    338   if (MHD_YES != ret)
    339     mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    340   MHD_destroy_response (response);
    341   return ret;
    342 }
    343 
    344 
    345 static CURL *
    346 setupCURL (void *cbc, uint16_t port)
    347 {
    348   CURL *c;
    349   char url[512];
    350 
    351   if (1)
    352   {
    353     int res;
    354     /* A workaround for some old libcurl versions, which ignore the specified
    355      * port by CURLOPT_PORT when digest authorisation is used. */
    356     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
    357                     "http://127.0.0.1:%u%s",
    358                     (unsigned int) port, MHD_URI_BASE_PATH);
    359     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
    360       externalErrorExitDesc ("Cannot form request URL");
    361   }
    362 
    363   c = curl_easy_init ();
    364   if (NULL == c)
    365     libcurlErrorExitDesc ("curl_easy_init() failed");
    366 
    367   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    368       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    369                                      libcurl_errbuf)) ||
    370       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    371                                      &copyBuffer)) ||
    372       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    373       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    374                                      ((long) TIMEOUTS_VAL))) ||
    375       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    376                                      ((long) TIMEOUTS_VAL))) ||
    377       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    378                                      CURL_HTTP_VERSION_1_1)) ||
    379       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    380 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    381       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    382 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    383       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    384 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    385 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    386       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    387 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    388       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
    389       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
    390     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    391   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)) ||
    392       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD,
    393                                      "testuser:testpass")))
    394     libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed");
    395   return c;
    396 }
    397 
    398 
    399 static unsigned int
    400 testDigestAuth (void)
    401 {
    402   CURL *c;
    403   struct MHD_Daemon *d;
    404   struct CBC cbc;
    405   char buf[2048];
    406   char rnd[8];
    407   uint16_t port;
    408 #ifndef WINDOWS
    409   int fd;
    410   size_t len;
    411   size_t off = 0;
    412 #endif /* ! WINDOWS */
    413 
    414   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    415     port = 0;
    416   else
    417     port = 1165;
    418 
    419   cbc.buf = buf;
    420   cbc.size = 2048;
    421   cbc.pos = 0;
    422 #ifndef WINDOWS
    423   fd = open ("/dev/urandom",
    424              O_RDONLY);
    425   if (-1 == fd)
    426     externalErrorExitDesc ("Failed to open '/dev/urandom'");
    427 
    428   while (off < 8)
    429   {
    430     len = (size_t) read (fd,
    431                          rnd + off,
    432                          8 - off);
    433     if (len == (size_t) -1)
    434       externalErrorExitDesc ("Failed to read '/dev/urandom'");
    435     off += len;
    436   }
    437   (void) close (fd);
    438 #else
    439   {
    440     HCRYPTPROV cc;
    441     BOOL b;
    442 
    443     b = CryptAcquireContext (&cc,
    444                              NULL,
    445                              NULL,
    446                              PROV_RSA_FULL,
    447                              CRYPT_VERIFYCONTEXT);
    448     if (b == 0)
    449       externalErrorExitDesc ("CryptAcquireContext() failed");
    450     b = CryptGenRandom (cc, 8, (BYTE *) rnd);
    451     if (b == 0)
    452       externalErrorExitDesc ("CryptGenRandom() failed");
    453     CryptReleaseContext (cc, 0);
    454   }
    455 #endif
    456   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    457                         port, NULL, NULL,
    458                         &ahc_echo, NULL,
    459                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
    460                         MHD_OPTION_NONCE_NC_SIZE, 300,
    461                         MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999,
    462                         MHD_OPTION_END);
    463   if (d == NULL)
    464     return 1;
    465   if (0 == port)
    466   {
    467     const union MHD_DaemonInfo *dinfo;
    468 
    469     dinfo = MHD_get_daemon_info (d,
    470                                  MHD_DAEMON_INFO_BIND_PORT);
    471     if ( (NULL == dinfo) ||
    472          (0 == dinfo->port) )
    473       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    474     port = dinfo->port;
    475   }
    476   c = setupCURL (&cbc, port);
    477 
    478   checkCURLE_OK (curl_easy_perform (c));
    479 
    480   curl_easy_cleanup (c);
    481   MHD_stop_daemon (d);
    482   if (cbc.pos != strlen (PAGE))
    483   {
    484     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    485              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    486              (unsigned) strlen (MHD_URI_BASE_PATH));
    487     mhdErrorExitDesc ("Wrong returned data length");
    488   }
    489   if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE)))
    490   {
    491     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    492     mhdErrorExitDesc ("Wrong returned data");
    493   }
    494   return 0;
    495 }
    496 
    497 
    498 int
    499 main (int argc, char *const *argv)
    500 {
    501   unsigned int errorCount = 0;
    502   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    503 
    504 #ifdef NEED_GCRYP_INIT
    505   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    506 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    507   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    508 #endif /* GCRYCTL_INITIALIZATION_FINISHED */
    509 #endif /* NEED_GCRYP_INIT */
    510   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    511     return 2;
    512   errorCount += testDigestAuth ();
    513   if (errorCount != 0)
    514     fprintf (stderr, "Error (code: %u)\n", errorCount);
    515   curl_global_cleanup ();
    516   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    517 }