libmicrohttpd

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

test_digestauth2.c (54758B)


      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 test_digest2.c
     24  * @brief  Testcase for MHD Digest Authorisation
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     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 _WIN32
     44 #include <sys/socket.h>
     45 #include <unistd.h>
     46 #else
     47 #include <wincrypt.h>
     48 #endif
     49 
     50 #include "mhd_has_param.h"
     51 #include "mhd_has_in_name.h"
     52 
     53 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     54    test into a marked, classifiable test error (TESTING.md, P5). */
     55 #include "mhd_panic_tripwire.h"
     56 
     57 #ifndef MHD_STATICSTR_LEN_
     58 /**
     59  * Determine length of static string / macro strings at compile time.
     60  */
     61 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     62 #endif /* ! MHD_STATICSTR_LEN_ */
     63 
     64 #ifndef CURL_VERSION_BITS
     65 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     66 #endif /* ! CURL_VERSION_BITS */
     67 #ifndef CURL_AT_LEAST_VERSION
     68 #define CURL_AT_LEAST_VERSION(x,y,z) \
     69   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     70 #endif /* ! CURL_AT_LEAST_VERSION */
     71 
     72 #ifndef _MHD_INSTRMACRO
     73 /* Quoted macro parameter */
     74 #define _MHD_INSTRMACRO(a) #a
     75 #endif /* ! _MHD_INSTRMACRO */
     76 #ifndef _MHD_STRMACRO
     77 /* Quoted expanded macro parameter */
     78 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     79 #endif /* ! _MHD_STRMACRO */
     80 
     81 #if defined(HAVE___FUNC__)
     82 #define externalErrorExit(ignore) \
     83   _externalErrorExit_func (NULL, __func__, __LINE__)
     84 #define externalErrorExitDesc(errDesc) \
     85   _externalErrorExit_func (errDesc, __func__, __LINE__)
     86 #define libcurlErrorExit(ignore) \
     87   _libcurlErrorExit_func (NULL, __func__, __LINE__)
     88 #define libcurlErrorExitDesc(errDesc) \
     89   _libcurlErrorExit_func (errDesc, __func__, __LINE__)
     90 #define mhdErrorExit(ignore) \
     91   _mhdErrorExit_func (NULL, __func__, __LINE__)
     92 #define mhdErrorExitDesc(errDesc) \
     93   _mhdErrorExit_func (errDesc, __func__, __LINE__)
     94 #define checkCURLE_OK(libcurlcall) \
     95   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
     96                        __func__, __LINE__)
     97 #elif defined(HAVE___FUNCTION__)
     98 #define externalErrorExit(ignore) \
     99   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
    100 #define externalErrorExitDesc(errDesc) \
    101   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    102 #define libcurlErrorExit(ignore) \
    103   _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
    104 #define libcurlErrorExitDesc(errDesc) \
    105   _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    106 #define mhdErrorExit(ignore) \
    107   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
    108 #define mhdErrorExitDesc(errDesc) \
    109   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    110 #define checkCURLE_OK(libcurlcall) \
    111   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    112                        __FUNCTION__, __LINE__)
    113 #else
    114 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    115 #define externalErrorExitDesc(errDesc) \
    116   _externalErrorExit_func (errDesc, NULL, __LINE__)
    117 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
    118 #define libcurlErrorExitDesc(errDesc) \
    119   _libcurlErrorExit_func (errDesc, NULL, __LINE__)
    120 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    121 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    122 #define checkCURLE_OK(libcurlcall) \
    123   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
    124                        __LINE__)
    125 #endif
    126 
    127 
    128 _MHD_NORETURN static void
    129 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    130 {
    131   fflush (stdout);
    132   if ((NULL != errDesc) && (0 != errDesc[0]))
    133     fprintf (stderr, "%s", errDesc);
    134   else
    135     fprintf (stderr, "System or external library call failed");
    136   if ((NULL != funcName) && (0 != funcName[0]))
    137     fprintf (stderr, " in %s", funcName);
    138   if (0 < lineNum)
    139     fprintf (stderr, " at line %d", lineNum);
    140 
    141   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    142            strerror (errno));
    143 #ifdef MHD_WINSOCK_SOCKETS
    144   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    145 #endif /* MHD_WINSOCK_SOCKETS */
    146   fflush (stderr);
    147   exit (99);
    148 }
    149 
    150 
    151 /* Not actually used in this test */
    152 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    153 
    154 _MHD_NORETURN static void
    155 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    156 {
    157   fflush (stdout);
    158   if ((NULL != errDesc) && (0 != errDesc[0]))
    159     fprintf (stderr, "%s", errDesc);
    160   else
    161     fprintf (stderr, "CURL library call failed");
    162   if ((NULL != funcName) && (0 != funcName[0]))
    163     fprintf (stderr, " in %s", funcName);
    164   if (0 < lineNum)
    165     fprintf (stderr, " at line %d", lineNum);
    166 
    167   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    168            strerror (errno));
    169 #ifdef MHD_WINSOCK_SOCKETS
    170   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    171 #endif /* MHD_WINSOCK_SOCKETS */
    172   if (0 != libcurl_errbuf[0])
    173     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    174 
    175   fflush (stderr);
    176   exit (99);
    177 }
    178 
    179 
    180 _MHD_NORETURN static void
    181 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    182 {
    183   fflush (stdout);
    184   if ((NULL != errDesc) && (0 != errDesc[0]))
    185     fprintf (stderr, "%s", errDesc);
    186   else
    187     fprintf (stderr, "MHD unexpected error");
    188   if ((NULL != funcName) && (0 != funcName[0]))
    189     fprintf (stderr, " in %s", funcName);
    190   if (0 < lineNum)
    191     fprintf (stderr, " at line %d", lineNum);
    192 
    193   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    194            strerror (errno));
    195 #ifdef MHD_WINSOCK_SOCKETS
    196   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    197 #endif /* MHD_WINSOCK_SOCKETS */
    198 
    199   fflush (stderr);
    200   exit (8);
    201 }
    202 
    203 
    204 #if 0
    205 /* Function unused in this test */
    206 static void
    207 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    208                      const char *funcName, int lineNum)
    209 {
    210   if (CURLE_OK == code)
    211     return;
    212 
    213   fflush (stdout);
    214   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    215     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    216              curl_easy_strerror (code));
    217   else
    218     fprintf (stderr, "libcurl function call resulted in '%s'",
    219              curl_easy_strerror (code));
    220   if ((NULL != funcName) && (0 != funcName[0]))
    221     fprintf (stderr, " in %s", funcName);
    222   if (0 < lineNum)
    223     fprintf (stderr, " at line %d", lineNum);
    224 
    225   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    226            strerror (errno));
    227   if (0 != libcurl_errbuf[0])
    228     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    229 
    230   fflush (stderr);
    231   exit (9);
    232 }
    233 
    234 
    235 #endif
    236 
    237 
    238 /* Could be increased to facilitate debugging */
    239 #define TIMEOUTS_VAL 10
    240 
    241 #define MHD_URI_BASE_PATH "/bar%20foo?key=value"
    242 #define MHD_URI_BASE_PATH2 "/another_path"
    243 /* Should not fit buffer in the stack */
    244 #define MHD_URI_BASE_PATH3 \
    245   "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
    246   "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
    247   "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
    248   "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
    249   "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
    250   "/path?with%20some=parameters"
    251 
    252 #define REALM_VAL "TestRealm"
    253 #define USERNAME1 "test_user"
    254 /* The hex form of MD5("test_user:TestRealm") */
    255 #define USERHASH1_MD5_HEX "c53c601503ff176f18f623725fba4281"
    256 #define USERHASH1_MD5_BIN 0xc5, 0x3c, 0x60, 0x15, 0x03, 0xff, 0x17, 0x6f, \
    257   0x18, 0xf6, 0x23, 0x72, 0x5f, 0xba, 0x42, 0x81
    258 /* The hex form of SHA-256("test_user:TestRealm") */
    259 #define USERHASH1_SHA256_HEX \
    260   "090c7e06b77d6614cf5fe6cafa004d2e5f8fb36ba45a0e35eacb2eb7728f34de"
    261 /* The binary form of SHA-256("test_user:TestRealm") */
    262 #define USERHASH1_SHA256_BIN 0x09, 0x0c, 0x7e, 0x06, 0xb7, 0x7d, 0x66, 0x14, \
    263   0xcf, 0x5f, 0xe6, 0xca, 0xfa, 0x00, 0x4d, 0x2e, 0x5f, 0x8f, 0xb3, 0x6b, \
    264   0xa4, 0x5a, 0x0e, 0x35, 0xea, 0xcb, 0x2e, 0xb7, 0x72, 0x8f, 0x34, 0xde
    265 /* The hex form of MD5("test_user:TestRealm:test pass") */
    266 #define USERDIGEST1_MD5_BIN 0xd8, 0xb4, 0xa6, 0xd0, 0x01, 0x13, 0x07, 0xb7, \
    267   0x67, 0x94, 0xea, 0x66, 0x86, 0x03, 0x6b, 0x43
    268 /* The binary form of SHA-256("test_user:TestRealm:test pass") */
    269 #define USERDIGEST1_SHA256_BIN 0xc3, 0x4e, 0x16, 0x5a, 0x17, 0x0f, 0xe5, \
    270   0xac, 0x04, 0xf1, 0x6e, 0x46, 0x48, 0x2b, 0xa0, 0xc6, 0x56, 0xc1, 0xfb, \
    271   0x8f, 0x66, 0xa6, 0xd6, 0x3f, 0x91, 0x12, 0xf8, 0x56, 0xa5, 0xec, 0x6d, \
    272   0x6d
    273 #define PASSWORD_VALUE "test pass"
    274 #define OPAQUE_VALUE "opaque+content" /* Base64 character set */
    275 
    276 
    277 #define PAGE \
    278   "<html><head><title>libmicrohttpd demo page</title>" \
    279   "</head><body>Access granted</body></html>"
    280 
    281 #define DENIED \
    282   "<html><head><title>libmicrohttpd - Access denied</title>" \
    283   "</head><body>Access denied</body></html>"
    284 
    285 /* Global parameters */
    286 static int verbose;
    287 static int test_oldapi;
    288 static int test_userhash;
    289 static int test_userdigest;
    290 static int test_sha256;
    291 static int test_rfc2069;
    292 /* Bind DAuth nonces to everything except URI */
    293 static int test_bind_all;
    294 /* Bind DAuth nonces to URI */
    295 static int test_bind_uri;
    296 static int curl_uses_usehash;
    297 
    298 /* Static helper variables */
    299 static const char userhash1_md5_hex[] = USERHASH1_MD5_HEX;
    300 static const uint8_t userhash1_md5_bin[] = { USERHASH1_MD5_BIN };
    301 static const char userhash1_sha256_hex[] = USERHASH1_SHA256_HEX;
    302 static const uint8_t userhash1_sha256_bin[] = { USERHASH1_SHA256_BIN };
    303 static const char *userhash_hex;
    304 static size_t userhash_hex_len;
    305 static const uint8_t *userhash_bin;
    306 static const uint8_t userdigest1_md5_bin[] = { USERDIGEST1_MD5_BIN };
    307 static const uint8_t userdigest1_sha256_bin[] = { USERDIGEST1_SHA256_BIN };
    308 static const uint8_t *userdigest_bin;
    309 static size_t userdigest_bin_size;
    310 static const char *username_ptr;
    311 
    312 static void
    313 test_global_init (void)
    314 {
    315   libcurl_errbuf[0] = 0;
    316 
    317   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    318     externalErrorExit ();
    319 
    320   username_ptr = USERNAME1;
    321   if (! test_sha256)
    322   {
    323     userhash_hex = userhash1_md5_hex;
    324     userhash_hex_len = MHD_STATICSTR_LEN_ (userhash1_md5_hex);
    325     userhash_bin = userhash1_md5_bin;
    326     if ((userhash_hex_len / 2) != \
    327         (sizeof(userhash1_md5_bin) / sizeof(userhash1_md5_bin[0])))
    328       externalErrorExitDesc ("Wrong size of the 'userhash1_md5_bin' array");
    329     userdigest_bin = userdigest1_md5_bin;
    330     userdigest_bin_size =
    331       (sizeof(userdigest1_md5_bin) / sizeof(userdigest1_md5_bin[0]));
    332   }
    333   else
    334   {
    335     userhash_hex = userhash1_sha256_hex;
    336     userhash_hex_len = MHD_STATICSTR_LEN_ (userhash1_sha256_hex);
    337     userhash_bin = userhash1_sha256_bin;
    338     if ((userhash_hex_len / 2) != \
    339         (sizeof(userhash1_sha256_bin)   \
    340          / sizeof(userhash1_sha256_bin[0])))
    341       externalErrorExitDesc ("Wrong size of the 'userhash1_sha256_bin' array");
    342     userdigest_bin = userdigest1_sha256_bin;
    343     userdigest_bin_size =
    344       (sizeof(userdigest1_sha256_bin) / sizeof(userdigest1_sha256_bin[0]));
    345   }
    346 }
    347 
    348 
    349 static void
    350 test_global_cleanup (void)
    351 {
    352   curl_global_cleanup ();
    353 }
    354 
    355 
    356 static int
    357 gen_good_rnd (void *rnd_buf, size_t rnd_buf_size)
    358 {
    359   if (1024 < rnd_buf_size)
    360     externalErrorExitDesc ("Too large amount of random data " \
    361                            "is requested");
    362 #ifndef _WIN32
    363   if (1)
    364   {
    365     const int urand_fd = open ("/dev/urandom", O_RDONLY);
    366     if (0 <= urand_fd)
    367     {
    368       size_t pos = 0;
    369       do
    370       {
    371         ssize_t res = read (urand_fd,
    372                             ((uint8_t *) rnd_buf) + pos, rnd_buf_size - pos);
    373         if (0 > res)
    374           break;
    375         pos += (size_t) res;
    376       } while (rnd_buf_size > pos);
    377       (void) close (urand_fd);
    378 
    379       if (rnd_buf_size == pos)
    380         return ! 0; /* Success */
    381     }
    382   }
    383 #else  /* _WIN32 */
    384   if (1)
    385   {
    386     HCRYPTPROV cpr_hndl;
    387     if (CryptAcquireContextW (&cpr_hndl, NULL, NULL, PROV_RSA_FULL,
    388                               CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
    389     {
    390       if (CryptGenRandom (cpr_hndl, (DWORD) rnd_buf_size, (BYTE *) rnd_buf))
    391       {
    392         (void) CryptReleaseContext (cpr_hndl, 0);
    393         return ! 0; /* Success */
    394       }
    395       (void) CryptReleaseContext (cpr_hndl, 0);
    396     }
    397   }
    398 #endif /* _WIN32 */
    399   return 0; /* Failure */
    400 }
    401 
    402 
    403 struct CBC
    404 {
    405   char *buf;
    406   size_t pos;
    407   size_t size;
    408 };
    409 
    410 struct req_track
    411 {
    412   /**
    413    * The number of used URI, zero-based
    414    */
    415   unsigned int uri_num;
    416 
    417   /**
    418    * The number of request for URI.
    419    * This includes number of unauthorised requests.
    420    */
    421   unsigned int req_num;
    422 };
    423 
    424 
    425 static size_t
    426 copyBuffer (void *ptr,
    427             size_t size,
    428             size_t nmemb,
    429             void *ctx)
    430 {
    431   struct CBC *cbc = ctx;
    432 
    433   if (cbc->pos + size * nmemb > cbc->size)
    434     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    435   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    436   cbc->pos += size * nmemb;
    437   return size * nmemb;
    438 }
    439 
    440 
    441 static enum MHD_Result
    442 ahc_echo (void *cls,
    443           struct MHD_Connection *connection,
    444           const char *url,
    445           const char *method,
    446           const char *version,
    447           const char *upload_data,
    448           size_t *upload_data_size,
    449           void **req_cls)
    450 {
    451   struct MHD_Response *response;
    452   enum MHD_Result res;
    453   static int already_called_marker;
    454   struct req_track *const tr_p = (struct req_track *) cls;
    455   (void) url;              /* Unused. Silent compiler warning. */
    456   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    457   (void) upload_data_size; /* Unused. Silent compiler warning. */
    458 
    459   if (&already_called_marker != *req_cls)
    460   { /* Called for the first time, request not fully read yet */
    461     *req_cls = &already_called_marker;
    462     /* Wait for complete request */
    463     return MHD_YES;
    464   }
    465 
    466   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    467     mhdErrorExitDesc ("Unexpected HTTP method");
    468 
    469   tr_p->req_num++;
    470   if (2 < tr_p->req_num)
    471     mhdErrorExitDesc ("Received more than two requests for the same URI");
    472 
    473   response = NULL;
    474   if (! test_oldapi)
    475   {
    476     struct MHD_DigestAuthInfo *dinfo;
    477     const enum MHD_DigestAuthAlgo3 algo3 =
    478       test_sha256 ? MHD_DIGEST_AUTH_ALGO3_SHA256 : MHD_DIGEST_AUTH_ALGO3_MD5;
    479     const enum MHD_DigestAuthQOP qop =
    480       test_rfc2069 ? MHD_DIGEST_AUTH_QOP_NONE : MHD_DIGEST_AUTH_QOP_AUTH;
    481 
    482     dinfo = MHD_digest_auth_get_request_info3 (connection);
    483     if (NULL != dinfo)
    484     {
    485       /* Got any kind of Digest response. Check it, it must be valid */
    486       struct MHD_DigestAuthUsernameInfo *uname;
    487       enum MHD_DigestAuthResult check_res;
    488       enum MHD_DigestAuthResult expect_res;
    489 
    490       if (curl_uses_usehash)
    491       {
    492         if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != dinfo->uname_type)
    493         {
    494           fprintf (stderr, "Unexpected 'uname_type'.\n"
    495                    "Expected: %d\tRecieved: %d. ",
    496                    (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH,
    497                    (int) dinfo->uname_type);
    498           mhdErrorExitDesc ("Wrong 'uname_type'");
    499         }
    500         else if (dinfo->userhash_hex_len != userhash_hex_len)
    501         {
    502           fprintf (stderr, "'userhash_hex_len' does not match.\n"
    503                    "Expected: %u\tRecieved: %u. ",
    504                    (unsigned) userhash_hex_len,
    505                    (unsigned) dinfo->userhash_hex_len);
    506           mhdErrorExitDesc ("Wrong 'userhash_hex_len'");
    507         }
    508         else if (0 != memcmp (dinfo->userhash_hex, userhash_hex,
    509                               dinfo->userhash_hex_len))
    510         {
    511           fprintf (stderr, "'userhash_hex' does not match.\n"
    512                    "Expected: '%s'\tRecieved: '%.*s'. ",
    513                    userhash_hex,
    514                    (int) dinfo->userhash_hex_len,
    515                    dinfo->userhash_hex);
    516           mhdErrorExitDesc ("Wrong 'userhash_hex'");
    517         }
    518         else if (NULL == dinfo->userhash_bin)
    519           mhdErrorExitDesc ("'userhash_bin' is NULL");
    520         else if (0 != memcmp (dinfo->userhash_bin, userhash_bin,
    521                               dinfo->username_len / 2))
    522           mhdErrorExitDesc ("Wrong 'userhash_bin'");
    523         else if (NULL != dinfo->username)
    524           mhdErrorExitDesc ("'username' is NOT NULL");
    525         else if (0 != dinfo->username_len)
    526           mhdErrorExitDesc ("'username_len' is NOT zero");
    527       }
    528       else
    529       {
    530         if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != dinfo->uname_type)
    531         {
    532           fprintf (stderr, "Unexpected 'uname_type'.\n"
    533                    "Expected: %d\tRecieved: %d. ",
    534                    (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD,
    535                    (int) dinfo->uname_type);
    536           mhdErrorExitDesc ("Wrong 'uname_type'");
    537         }
    538         else if (NULL == dinfo->username)
    539           mhdErrorExitDesc ("'username' is NULL");
    540         else if (dinfo->username_len != strlen (username_ptr))
    541         {
    542           fprintf (stderr, "'username_len' does not match.\n"
    543                    "Expected: %u\tRecieved: %u. ",
    544                    (unsigned) strlen (username_ptr),
    545                    (unsigned) dinfo->username_len);
    546           mhdErrorExitDesc ("Wrong 'username_len'");
    547         }
    548         else if (0 != memcmp (dinfo->username, username_ptr,
    549                               dinfo->username_len))
    550         {
    551           fprintf (stderr, "'username' does not match.\n"
    552                    "Expected: '%s'\tRecieved: '%.*s'. ",
    553                    username_ptr,
    554                    (int) dinfo->username_len,
    555                    dinfo->username);
    556           mhdErrorExitDesc ("Wrong 'username'");
    557         }
    558         else if (NULL != dinfo->userhash_hex)
    559           mhdErrorExitDesc ("'userhash_hex' is NOT NULL");
    560         else if (0 != dinfo->userhash_hex_len)
    561           mhdErrorExitDesc ("'userhash_hex_len' is NOT zero");
    562         else if (NULL != dinfo->userhash_bin)
    563           mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
    564       }
    565       if (algo3 != dinfo->algo3)
    566       {
    567         fprintf (stderr, "Unexpected 'algo3'.\n"
    568                  "Expected: %d\tRecieved: %d. ",
    569                  (int) algo3,
    570                  (int) dinfo->algo3);
    571         mhdErrorExitDesc ("Wrong 'algo3'");
    572       }
    573       if (! test_rfc2069)
    574       {
    575         if (
    576 #if CURL_AT_LEAST_VERSION (7,37,1)
    577           10 >= dinfo->cnonce_len
    578 #else  /* libcurl before 7.37.1 */
    579           8 > dinfo->cnonce_len
    580 #endif /* libcurl before 7.37.1 */
    581           )
    582         {
    583           fprintf (stderr, "Unexpected small 'cnonce_len': %ld. ",
    584                    (long) dinfo->cnonce_len);
    585           mhdErrorExitDesc ("Wrong 'cnonce_len'");
    586         }
    587       }
    588       else
    589       {
    590         if (0 != dinfo->cnonce_len)
    591         {
    592           fprintf (stderr, "'cnonce_len' is not zero: %ld. ",
    593                    (long) dinfo->cnonce_len);
    594           mhdErrorExitDesc ("Wrong 'cnonce_len'");
    595         }
    596       }
    597       if (NULL == dinfo->opaque)
    598         mhdErrorExitDesc ("'opaque' is NULL");
    599       else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE))
    600       {
    601         fprintf (stderr, "'opaque_len' does not match.\n"
    602                  "Expected: %u\tRecieved: %u. ",
    603                  (unsigned) MHD_STATICSTR_LEN_ (OPAQUE_VALUE),
    604                  (unsigned) dinfo->opaque_len);
    605         mhdErrorExitDesc ("Wrong 'opaque_len'");
    606       }
    607       else if (0 != memcmp (dinfo->opaque, OPAQUE_VALUE, dinfo->opaque_len))
    608       {
    609         fprintf (stderr, "'opaque' does not match.\n"
    610                  "Expected: '%s'\tRecieved: '%.*s'. ",
    611                  OPAQUE_VALUE,
    612                  (int) dinfo->opaque_len,
    613                  dinfo->opaque);
    614         mhdErrorExitDesc ("Wrong 'opaque'");
    615       }
    616       else if (qop != dinfo->qop)
    617       {
    618         fprintf (stderr, "Unexpected 'qop'.\n"
    619                  "Expected: %d\tRecieved: %d. ",
    620                  (int) qop,
    621                  (int) dinfo->qop);
    622         mhdErrorExitDesc ("Wrong 'qop'");
    623       }
    624       else if (NULL == dinfo->realm)
    625         mhdErrorExitDesc ("'realm' is NULL");
    626       else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM_VAL))
    627       {
    628         fprintf (stderr, "'realm_len' does not match.\n"
    629                  "Expected: %u\tRecieved: %u. ",
    630                  (unsigned) MHD_STATICSTR_LEN_ (REALM_VAL),
    631                  (unsigned) dinfo->realm_len);
    632         mhdErrorExitDesc ("Wrong 'realm_len'");
    633       }
    634       else if (0 != memcmp (dinfo->realm, REALM_VAL, dinfo->realm_len))
    635       {
    636         fprintf (stderr, "'realm' does not match.\n"
    637                  "Expected: '%s'\tRecieved: '%.*s'. ",
    638                  OPAQUE_VALUE,
    639                  (int) dinfo->realm_len,
    640                  dinfo->realm);
    641         mhdErrorExitDesc ("Wrong 'realm'");
    642       }
    643       MHD_free (dinfo);
    644 
    645       uname = MHD_digest_auth_get_username3 (connection);
    646       if (NULL == uname)
    647         mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
    648       if (curl_uses_usehash)
    649       {
    650         if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != uname->uname_type)
    651         {
    652           fprintf (stderr, "Unexpected 'uname_type'.\n"
    653                    "Expected: %d\tRecieved: %d. ",
    654                    (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH,
    655                    (int) uname->uname_type);
    656           mhdErrorExitDesc ("Wrong 'uname_type'");
    657         }
    658         else if (uname->userhash_hex_len != userhash_hex_len)
    659         {
    660           fprintf (stderr, "'userhash_hex_len' does not match.\n"
    661                    "Expected: %u\tRecieved: %u. ",
    662                    (unsigned) userhash_hex_len,
    663                    (unsigned) uname->userhash_hex_len);
    664           mhdErrorExitDesc ("Wrong 'userhash_hex_len'");
    665         }
    666         else if (0 != memcmp (uname->userhash_hex, userhash_hex,
    667                               uname->userhash_hex_len))
    668         {
    669           fprintf (stderr, "'username' does not match.\n"
    670                    "Expected: '%s'\tRecieved: '%.*s'. ",
    671                    userhash_hex,
    672                    (int) uname->userhash_hex_len,
    673                    uname->userhash_hex);
    674           mhdErrorExitDesc ("Wrong 'userhash_hex'");
    675         }
    676         else if (NULL == uname->userhash_bin)
    677           mhdErrorExitDesc ("'userhash_bin' is NULL");
    678         else if (0 != memcmp (uname->userhash_bin, userhash_bin,
    679                               uname->username_len / 2))
    680           mhdErrorExitDesc ("Wrong 'userhash_bin'");
    681         else if (NULL != uname->username)
    682           mhdErrorExitDesc ("'username' is NOT NULL");
    683         else if (0 != uname->username_len)
    684           mhdErrorExitDesc ("'username_len' is NOT zero");
    685       }
    686       else
    687       {
    688         if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname->uname_type)
    689         {
    690           fprintf (stderr, "Unexpected 'uname_type'.\n"
    691                    "Expected: %d\tRecieved: %d. ",
    692                    (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD,
    693                    (int) uname->uname_type);
    694           mhdErrorExitDesc ("Wrong 'uname_type'");
    695         }
    696         else if (NULL == uname->username)
    697           mhdErrorExitDesc ("'username' is NULL");
    698         else if (uname->username_len != strlen (username_ptr))
    699         {
    700           fprintf (stderr, "'username_len' does not match.\n"
    701                    "Expected: %u\tRecieved: %u. ",
    702                    (unsigned) strlen (username_ptr),
    703                    (unsigned) uname->username_len);
    704           mhdErrorExitDesc ("Wrong 'username_len'");
    705         }
    706         else if (0 != memcmp (uname->username, username_ptr,
    707                               uname->username_len))
    708         {
    709           fprintf (stderr, "'username' does not match.\n"
    710                    "Expected: '%s'\tRecieved: '%.*s'. ",
    711                    username_ptr,
    712                    (int) uname->username_len,
    713                    uname->username);
    714           mhdErrorExitDesc ("Wrong 'username'");
    715         }
    716         else if (NULL != uname->userhash_hex)
    717           mhdErrorExitDesc ("'userhash_hex' is NOT NULL");
    718         else if (0 != uname->userhash_hex_len)
    719           mhdErrorExitDesc ("'userhash_hex_len' is NOT zero");
    720         else if (NULL != uname->userhash_bin)
    721           mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
    722       }
    723       if (algo3 != uname->algo3)
    724       {
    725         fprintf (stderr, "Unexpected 'algo3'.\n"
    726                  "Expected: %d\tRecieved: %d. ",
    727                  (int) algo3,
    728                  (int) uname->algo3);
    729         mhdErrorExitDesc ("Wrong 'algo3'");
    730       }
    731       MHD_free (uname);
    732 
    733       if (! test_userdigest)
    734         check_res =
    735           MHD_digest_auth_check3 (connection, REALM_VAL, username_ptr,
    736                                   PASSWORD_VALUE,
    737                                   50 * TIMEOUTS_VAL,
    738                                   0,
    739                                   (enum MHD_DigestAuthMultiQOP) qop,
    740                                   (enum MHD_DigestAuthMultiAlgo3) algo3);
    741       else
    742         check_res =
    743           MHD_digest_auth_check_digest3 (connection, REALM_VAL, username_ptr,
    744                                          userdigest_bin, userdigest_bin_size,
    745                                          50 * TIMEOUTS_VAL,
    746                                          0,
    747                                          (enum MHD_DigestAuthMultiQOP) qop,
    748                                          (enum MHD_DigestAuthMultiAlgo3) algo3);
    749 
    750       if (test_rfc2069)
    751       {
    752         if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
    753           expect_res = MHD_DAUTH_NONCE_STALE;
    754         else
    755           expect_res = MHD_DAUTH_OK;
    756       }
    757       else if (test_bind_uri)
    758       {
    759         if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
    760           expect_res = MHD_DAUTH_NONCE_OTHER_COND;
    761         else
    762           expect_res = MHD_DAUTH_OK;
    763       }
    764       else
    765         expect_res = MHD_DAUTH_OK;
    766 
    767       switch (check_res)
    768       {
    769       /* Conditionally valid results */
    770       case MHD_DAUTH_OK:
    771         if (expect_res == MHD_DAUTH_OK)
    772         {
    773           if (verbose)
    774             printf ("Got valid auth check result: MHD_DAUTH_OK.\n");
    775         }
    776         else
    777           mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    778                             "MHD_DAUTH_OK");
    779         break;
    780       case MHD_DAUTH_NONCE_STALE:
    781         if (expect_res == MHD_DAUTH_NONCE_STALE)
    782         {
    783           if (verbose)
    784             printf ("Got expected auth check result: MHD_DAUTH_NONCE_STALE.\n");
    785         }
    786         else
    787           mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    788                             "MHD_DAUTH_NONCE_STALE");
    789         break;
    790       case MHD_DAUTH_NONCE_OTHER_COND:
    791         if (expect_res == MHD_DAUTH_NONCE_OTHER_COND)
    792         {
    793           if (verbose)
    794             printf ("Got expected auth check result: "
    795                     "MHD_DAUTH_NONCE_OTHER_COND.\n");
    796         }
    797         else
    798           mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    799                             "MHD_DAUTH_NONCE_OTHER_COND");
    800         break;
    801       /* Invalid results */
    802       case MHD_DAUTH_NONCE_WRONG:
    803         mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    804                           "MHD_DAUTH_NONCE_WRONG");
    805         break;
    806       case MHD_DAUTH_ERROR:
    807         externalErrorExitDesc ("General error returned " \
    808                                "by 'MHD_digest_auth_check[_digest]3()'");
    809         break;
    810       case MHD_DAUTH_WRONG_USERNAME:
    811         mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    812                           "MHD_DAUTH_WRONG_USERNAME");
    813         break;
    814       case MHD_DAUTH_RESPONSE_WRONG:
    815         mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    816                           "MHD_DAUTH_RESPONSE_WRONG");
    817         break;
    818       case MHD_DAUTH_WRONG_HEADER:
    819         mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
    820                           "MHD_DAUTH_WRONG_HEADER");
    821         break;
    822       case MHD_DAUTH_WRONG_REALM:
    823       case MHD_DAUTH_WRONG_URI:
    824       case MHD_DAUTH_WRONG_QOP:
    825       case MHD_DAUTH_WRONG_ALGO:
    826       case MHD_DAUTH_TOO_LARGE:
    827         fprintf (stderr, "'MHD_digest_auth_check[_digest]3()' returned "
    828                  "unexpected result: %d. ",
    829                  check_res);
    830         mhdErrorExitDesc ("Wrong returned code");
    831         break;
    832       default:
    833         fprintf (stderr, "'MHD_digest_auth_check[_digest]3()' returned "
    834                  "impossible result code: %d. ",
    835                  check_res);
    836         mhdErrorExitDesc ("Impossible returned code");
    837       }
    838       fflush (stderr);
    839       fflush (stdout);
    840 
    841       if (MHD_DAUTH_OK == check_res)
    842       {
    843         response =
    844           MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    845                                                   (const void *) PAGE);
    846         if (NULL == response)
    847           mhdErrorExitDesc ("Response creation failed");
    848 
    849         if (MHD_YES !=
    850             MHD_queue_response (connection, MHD_HTTP_OK, response))
    851           mhdErrorExitDesc ("'MHD_queue_response()' failed");
    852       }
    853       else if ((MHD_DAUTH_NONCE_STALE == check_res) ||
    854                (MHD_DAUTH_NONCE_OTHER_COND == check_res))
    855       {
    856         response =
    857           MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    858                                                   (const void *) DENIED);
    859         if (NULL == response)
    860           mhdErrorExitDesc ("Response creation failed");
    861         res =
    862           MHD_queue_auth_required_response3 (connection, REALM_VAL,
    863                                              OPAQUE_VALUE,
    864                                              "/", response, 1,
    865                                              (enum MHD_DigestAuthMultiQOP) qop,
    866                                              (enum MHD_DigestAuthMultiAlgo3)
    867                                              algo3,
    868                                              test_userhash, 0);
    869         if (MHD_YES != res)
    870           mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed");
    871       }
    872       else
    873         externalErrorExitDesc ("Wrong 'check_res' value");
    874     }
    875     else
    876     {
    877       /* No Digest auth header */
    878       if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
    879       {
    880         fprintf (stderr, "Received request number %u for URI number %u "
    881                  "without Digest Authorisation header. ",
    882                  tr_p->req_num, tr_p->uri_num + 1);
    883         mhdErrorExitDesc ("Wrong requests sequence");
    884       }
    885 
    886       response =
    887         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    888                                                 (const void *) DENIED);
    889       if (NULL == response)
    890         mhdErrorExitDesc ("Response creation failed");
    891       res =
    892         MHD_queue_auth_required_response3 (
    893           connection, REALM_VAL, OPAQUE_VALUE, "/", response, 0,
    894           (enum MHD_DigestAuthMultiQOP) qop,
    895           (enum MHD_DigestAuthMultiAlgo3) algo3, test_userhash, 0);
    896       if (MHD_YES != res)
    897         mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed");
    898     }
    899   }
    900   else if (2 == test_oldapi)
    901   {
    902     /* Use old API v2 */
    903     char *username;
    904     int check_res;
    905     int expect_res;
    906 
    907     username = MHD_digest_auth_get_username (connection);
    908     if (NULL != username)
    909     { /* Has a valid username in header */
    910       if (0 != strcmp (username, username_ptr))
    911       {
    912         fprintf (stderr, "'username' does not match.\n"
    913                  "Expected: '%s'\tRecieved: '%s'. ",
    914                  username_ptr,
    915                  username);
    916         mhdErrorExitDesc ("Wrong 'username'");
    917       }
    918       MHD_free (username);
    919 
    920       if (! test_userdigest)
    921         check_res =
    922           MHD_digest_auth_check2 (connection, REALM_VAL, username_ptr,
    923                                   PASSWORD_VALUE,
    924                                   50 * TIMEOUTS_VAL,
    925                                   test_sha256 ?
    926                                   MHD_DIGEST_ALG_SHA256 : MHD_DIGEST_ALG_MD5);
    927       else
    928         check_res =
    929           MHD_digest_auth_check_digest2 (connection, REALM_VAL, username_ptr,
    930                                          userdigest_bin, userdigest_bin_size,
    931                                          50 * TIMEOUTS_VAL,
    932                                          test_sha256 ?
    933                                          MHD_DIGEST_ALG_SHA256 :
    934                                          MHD_DIGEST_ALG_MD5);
    935 
    936       if (test_bind_uri)
    937       {
    938         if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
    939           expect_res = MHD_INVALID_NONCE;
    940         else
    941           expect_res = MHD_YES;
    942       }
    943       else
    944         expect_res = MHD_YES;
    945 
    946       if (expect_res != check_res)
    947       {
    948         fprintf (stderr, "'MHD_digest_auth_check[_digest]2()' returned "
    949                  "unexpected result '%d', while expected is '%d. ",
    950                  check_res, expect_res);
    951         mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]2()' result");
    952       }
    953       response =
    954         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    955                                                 (const void *) PAGE);
    956       if (NULL == response)
    957         mhdErrorExitDesc ("Response creation failed");
    958 
    959       if (MHD_YES == expect_res)
    960       {
    961         if (MHD_YES !=
    962             MHD_queue_response (connection, MHD_HTTP_OK, response))
    963           mhdErrorExitDesc ("'MHD_queue_response()' failed");
    964       }
    965       else if (MHD_INVALID_NONCE == expect_res)
    966       {
    967         if (MHD_YES !=
    968             MHD_queue_auth_fail_response2 (connection, REALM_VAL, OPAQUE_VALUE,
    969                                            response, 1,
    970                                            test_sha256 ?
    971                                            MHD_DIGEST_ALG_SHA256 :
    972                                            MHD_DIGEST_ALG_MD5))
    973           mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
    974       }
    975       else
    976         externalErrorExitDesc ("Wrong 'check_res' value");
    977     }
    978     else
    979     {
    980       /* Has no valid username in header */
    981       if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
    982       {
    983         fprintf (stderr, "Received request number %u for URI number %u "
    984                  "without Digest Authorisation header. ",
    985                  tr_p->req_num, tr_p->uri_num + 1);
    986         mhdErrorExitDesc ("Wrong requests sequence");
    987       }
    988       response =
    989         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    990                                                 (const void *) DENIED);
    991       if (NULL == response)
    992         mhdErrorExitDesc ("Response creation failed");
    993 
    994       res = MHD_queue_auth_fail_response2 (connection, REALM_VAL, OPAQUE_VALUE,
    995                                            response, 0,
    996                                            test_sha256 ?
    997                                            MHD_DIGEST_ALG_SHA256 :
    998                                            MHD_DIGEST_ALG_MD5);
    999       if (MHD_YES != res)
   1000         mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
   1001     }
   1002   }
   1003   else if (1 == test_oldapi)
   1004   {
   1005     /* Use old API v1 */
   1006     char *username;
   1007     int check_res;
   1008     int expect_res;
   1009 
   1010     username = MHD_digest_auth_get_username (connection);
   1011     if (NULL != username)
   1012     { /* Has a valid username in header */
   1013       if (0 != strcmp (username, username_ptr))
   1014       {
   1015         fprintf (stderr, "'username' does not match.\n"
   1016                  "Expected: '%s'\tRecieved: '%s'. ",
   1017                  username_ptr,
   1018                  username);
   1019         mhdErrorExitDesc ("Wrong 'username'");
   1020       }
   1021       MHD_free (username);
   1022 
   1023       if (! test_userdigest)
   1024         check_res =
   1025           MHD_digest_auth_check (connection, REALM_VAL, username_ptr,
   1026                                  PASSWORD_VALUE,
   1027                                  50 * TIMEOUTS_VAL);
   1028       else
   1029         check_res =
   1030           MHD_digest_auth_check_digest (connection, REALM_VAL, username_ptr,
   1031                                         userdigest_bin,
   1032                                         50 * TIMEOUTS_VAL);
   1033 
   1034       if (test_bind_uri)
   1035       {
   1036         if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
   1037           expect_res = MHD_INVALID_NONCE;
   1038         else
   1039           expect_res = MHD_YES;
   1040       }
   1041       else
   1042         expect_res = MHD_YES;
   1043 
   1044       if (expect_res != check_res)
   1045       {
   1046         fprintf (stderr, "'MHD_digest_auth_check[_digest]()' returned "
   1047                  "unexpected result '%d', while expected is '%d. ",
   1048                  check_res, expect_res);
   1049         mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]()' result");
   1050       }
   1051 
   1052       response =
   1053         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
   1054                                                 (const void *) PAGE);
   1055       if (NULL == response)
   1056         mhdErrorExitDesc ("Response creation failed");
   1057 
   1058       if (MHD_YES == expect_res)
   1059       {
   1060         if (MHD_YES !=
   1061             MHD_queue_response (connection, MHD_HTTP_OK, response))
   1062           mhdErrorExitDesc ("'MHD_queue_response()' failed");
   1063       }
   1064       else if (MHD_INVALID_NONCE == expect_res)
   1065       {
   1066         if (MHD_YES !=
   1067             MHD_queue_auth_fail_response (connection, REALM_VAL, OPAQUE_VALUE,
   1068                                           response, 1))
   1069           mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
   1070       }
   1071       else
   1072         externalErrorExitDesc ("Wrong 'check_res' value");
   1073     }
   1074     else
   1075     {
   1076       /* Has no valid username in header */
   1077       if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
   1078       {
   1079         fprintf (stderr, "Received request number %u for URI number %u "
   1080                  "without Digest Authorisation header. ",
   1081                  tr_p->req_num, tr_p->uri_num + 1);
   1082         mhdErrorExitDesc ("Wrong requests sequence");
   1083       }
   1084       response =
   1085         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
   1086                                                 (const void *) DENIED);
   1087       if (NULL == response)
   1088         mhdErrorExitDesc ("Response creation failed");
   1089 
   1090       res = MHD_queue_auth_fail_response (connection, REALM_VAL, OPAQUE_VALUE,
   1091                                           response, 0);
   1092       if (MHD_YES != res)
   1093         mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
   1094     }
   1095   }
   1096   else
   1097     externalErrorExitDesc ("Wrong 'test_oldapi' value");
   1098 
   1099   MHD_destroy_response (response);
   1100   return MHD_YES;
   1101 }
   1102 
   1103 
   1104 /**
   1105  *
   1106  * @param c the CURL handle to use
   1107  * @param port the port to set
   1108  * @param uri_num the number of URI, should be 0, 1 or 2
   1109  */
   1110 static void
   1111 setCURL_rq_path (CURL *c, uint16_t port, unsigned int uri_num)
   1112 {
   1113   const char *req_path;
   1114   char uri[512];
   1115   int res;
   1116 
   1117   if (0 == uri_num)
   1118     req_path = MHD_URI_BASE_PATH;
   1119   else if (1 == uri_num)
   1120     req_path = MHD_URI_BASE_PATH2;
   1121   else
   1122     req_path = MHD_URI_BASE_PATH3;
   1123   /* A workaround for some old libcurl versions, which ignore the specified
   1124    * port by CURLOPT_PORT when authorisation is used. */
   1125   res = snprintf (uri, (sizeof(uri) / sizeof(uri[0])),
   1126                   "http://127.0.0.1:%u%s", (unsigned int) port,
   1127                   req_path);
   1128   if ((0 >= res) || ((sizeof(uri) / sizeof(uri[0])) <= (size_t) res))
   1129     externalErrorExitDesc ("Cannot form request URL");
   1130 
   1131   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, uri))
   1132     libcurlErrorExitDesc ("Cannot set request URL");
   1133 }
   1134 
   1135 
   1136 static CURL *
   1137 setupCURL (void *cbc, uint16_t port)
   1138 {
   1139   CURL *c;
   1140 
   1141   c = curl_easy_init ();
   1142   if (NULL == c)
   1143     libcurlErrorExitDesc ("curl_easy_init() failed");
   1144 
   1145   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
   1146       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
   1147                                      &copyBuffer)) ||
   1148       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
   1149       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
   1150                                      ((long) TIMEOUTS_VAL))) ||
   1151       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
   1152                                      CURL_HTTP_VERSION_1_1)) ||
   1153       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
   1154                                      ((long) TIMEOUTS_VAL))) ||
   1155       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
   1156                                      libcurl_errbuf)) ||
   1157       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
   1158       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH,
   1159                                      (long) CURLAUTH_DIGEST)) ||
   1160 #if CURL_AT_LEAST_VERSION (7,19,1)
   1161       /* Need version 7.19.1 for separate username and password */
   1162       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERNAME, username_ptr)) ||
   1163       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PASSWORD, PASSWORD_VALUE)) ||
   1164 #endif /* CURL_AT_LEAST_VERSION(7,19,1) */
   1165 #ifdef _DEBUG
   1166       (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
   1167 #endif /* _DEBUG */
   1168 #if CURL_AT_LEAST_VERSION (7, 85, 0)
   1169       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
   1170 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
   1171       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
   1172 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
   1173 #if CURL_AT_LEAST_VERSION (7, 45, 0)
   1174       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
   1175 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
   1176       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
   1177     libcurlErrorExitDesc ("curl_easy_setopt() failed");
   1178 
   1179   setCURL_rq_path (c, port, 0);
   1180 
   1181   return c;
   1182 }
   1183 
   1184 
   1185 static CURLcode
   1186 performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
   1187 {
   1188   CURLM *multi;
   1189   time_t start;
   1190   struct timeval tv;
   1191   CURLcode ret;
   1192 
   1193   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
   1194   if (NULL != *multi_reuse)
   1195     multi = *multi_reuse;
   1196   else
   1197   {
   1198     multi = curl_multi_init ();
   1199     if (multi == NULL)
   1200       libcurlErrorExitDesc ("curl_multi_init() failed");
   1201     *multi_reuse = multi;
   1202   }
   1203   if (CURLM_OK != curl_multi_add_handle (multi, c))
   1204     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
   1205 
   1206   start = time (NULL);
   1207   while (time (NULL) - start <= TIMEOUTS_VAL)
   1208   {
   1209     fd_set rs;
   1210     fd_set ws;
   1211     fd_set es;
   1212     MHD_socket maxMhdSk;
   1213     int maxCurlSk;
   1214     int running;
   1215 
   1216     maxMhdSk = MHD_INVALID_SOCKET;
   1217     maxCurlSk = -1;
   1218     FD_ZERO (&rs);
   1219     FD_ZERO (&ws);
   1220     FD_ZERO (&es);
   1221     if (NULL != multi)
   1222     {
   1223       curl_multi_perform (multi, &running);
   1224       if (0 == running)
   1225       {
   1226         struct CURLMsg *msg;
   1227         int msgLeft;
   1228         int totalMsgs = 0;
   1229         do
   1230         {
   1231           msg = curl_multi_info_read (multi, &msgLeft);
   1232           if (NULL == msg)
   1233             libcurlErrorExitDesc ("curl_multi_info_read() failed");
   1234           totalMsgs++;
   1235           if (CURLMSG_DONE == msg->msg)
   1236             ret = msg->data.result;
   1237         } while (msgLeft > 0);
   1238         if (1 != totalMsgs)
   1239         {
   1240           fprintf (stderr,
   1241                    "curl_multi_info_read returned wrong "
   1242                    "number of results (%d).\n",
   1243                    totalMsgs);
   1244           externalErrorExit ();
   1245         }
   1246         curl_multi_remove_handle (multi, c);
   1247         multi = NULL;
   1248       }
   1249       else
   1250       {
   1251         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
   1252           libcurlErrorExitDesc ("curl_multi_fdset() failed");
   1253       }
   1254     }
   1255     if (NULL == multi)
   1256     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
   1257       if (0 != MHD_get_timeout64s (d))
   1258         break; /* MHD finished as well */
   1259     }
   1260     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
   1261       mhdErrorExitDesc ("MHD_get_fdset() failed");
   1262     tv.tv_sec = 0;
   1263     tv.tv_usec = 200000;
   1264 #ifdef MHD_POSIX_SOCKETS
   1265     if (maxMhdSk > maxCurlSk)
   1266       maxCurlSk = maxMhdSk;
   1267 #endif /* MHD_POSIX_SOCKETS */
   1268     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
   1269     {
   1270 #ifdef MHD_POSIX_SOCKETS
   1271       if (EINTR != errno)
   1272         externalErrorExitDesc ("Unexpected select() error");
   1273 #else
   1274       if ((WSAEINVAL != WSAGetLastError ()) ||
   1275           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
   1276         externalErrorExitDesc ("Unexpected select() error");
   1277       Sleep (200);
   1278 #endif
   1279     }
   1280     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
   1281       mhdErrorExitDesc ("MHD_run_from_select() failed");
   1282   }
   1283 
   1284   return ret;
   1285 }
   1286 
   1287 
   1288 /**
   1289  * Check request result
   1290  * @param curl_code the CURL easy return code
   1291  * @param pcbc the pointer struct CBC
   1292  * @return non-zero if success, zero if failed
   1293  */
   1294 static unsigned int
   1295 check_result (CURLcode curl_code, CURL *c, struct CBC *pcbc)
   1296 {
   1297   long code;
   1298   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
   1299     libcurlErrorExit ();
   1300 
   1301   if (MHD_HTTP_OK != code)
   1302   {
   1303     fprintf (stderr, "Request returned wrong code: %ld.\n",
   1304              code);
   1305     return 0;
   1306   }
   1307 
   1308   if (CURLE_OK != curl_code)
   1309   {
   1310     fflush (stdout);
   1311     if (0 != libcurl_errbuf[0])
   1312       fprintf (stderr, "Request failed. "
   1313                "libcurl error: '%s'.\n"
   1314                "libcurl error description: '%s'.\n",
   1315                curl_easy_strerror (curl_code),
   1316                libcurl_errbuf);
   1317     else
   1318       fprintf (stderr, "Request failed. "
   1319                "libcurl error: '%s'.\n",
   1320                curl_easy_strerror (curl_code));
   1321     fflush (stderr);
   1322     return 0;
   1323   }
   1324 
   1325   if (pcbc->pos != MHD_STATICSTR_LEN_ (PAGE))
   1326   {
   1327     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
   1328              (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
   1329              (unsigned) MHD_STATICSTR_LEN_ (PAGE));
   1330     mhdErrorExitDesc ("Wrong returned data length");
   1331   }
   1332   if (0 != memcmp (PAGE, pcbc->buf, pcbc->pos))
   1333   {
   1334     fprintf (stderr, "Got invalid response '%.*s'. ",
   1335              (int) pcbc->pos, pcbc->buf);
   1336     mhdErrorExitDesc ("Wrong returned data");
   1337   }
   1338   return 1;
   1339 }
   1340 
   1341 
   1342 static unsigned int
   1343 testDigestAuth (void)
   1344 {
   1345   unsigned int dauth_nonce_bind;
   1346   struct MHD_Daemon *d;
   1347   uint16_t port;
   1348   struct CBC cbc;
   1349   struct req_track rq_tr;
   1350   char buf[2048];
   1351   CURL *c;
   1352   CURLM *multi_reuse;
   1353   int failed = 0;
   1354 
   1355   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
   1356     port = 0;
   1357   else
   1358     port = 4210;
   1359 
   1360   if (1)
   1361   {
   1362     uint8_t salt[8]; /* Use local variable to test MHD "copy" function */
   1363     if (! gen_good_rnd (salt, sizeof(salt)))
   1364     {
   1365       fprintf (stderr, "WARNING: the random buffer (used as salt value) is not "
   1366                "initialised completely, nonce generation may be "
   1367                "predictable in this test.\n");
   1368       fflush (stderr);
   1369     }
   1370 
   1371     dauth_nonce_bind = MHD_DAUTH_BIND_NONCE_NONE;
   1372     if (test_bind_all)
   1373       dauth_nonce_bind |=
   1374         (MHD_DAUTH_BIND_NONCE_CLIENT_IP | MHD_DAUTH_BIND_NONCE_REALM);
   1375     if (test_bind_uri)
   1376       dauth_nonce_bind |= MHD_DAUTH_BIND_NONCE_URI_PARAMS;
   1377 
   1378     d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
   1379                           port, NULL, NULL,
   1380                           &ahc_echo, &rq_tr,
   1381                           MHD_OPTION_DIGEST_AUTH_RANDOM_COPY,
   1382                           sizeof (salt), salt,
   1383                           MHD_OPTION_NONCE_NC_SIZE, 300,
   1384                           MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE,
   1385                           dauth_nonce_bind,
   1386                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
   1387                           MHD_OPTION_END);
   1388   }
   1389   if (d == NULL)
   1390     return 1;
   1391   if (0 == port)
   1392   {
   1393     const union MHD_DaemonInfo *dinfo;
   1394 
   1395     dinfo = MHD_get_daemon_info (d,
   1396                                  MHD_DAEMON_INFO_BIND_PORT);
   1397     if ( (NULL == dinfo) ||
   1398          (0 == dinfo->port) )
   1399       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   1400     port = dinfo->port;
   1401   }
   1402 
   1403   /* First request */
   1404   rq_tr.req_num = 0;
   1405   rq_tr.uri_num = 0;
   1406   cbc.buf = buf;
   1407   cbc.size = sizeof (buf);
   1408   cbc.pos = 0;
   1409   memset (cbc.buf, 0, cbc.size);
   1410   c = setupCURL (&cbc, port);
   1411   multi_reuse = NULL;
   1412   /* First request */
   1413   if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
   1414   {
   1415     fflush (stderr);
   1416     if (verbose)
   1417       printf ("Got first expected response.\n");
   1418     fflush (stdout);
   1419   }
   1420   else
   1421   {
   1422     fprintf (stderr, "First request FAILED.\n");
   1423     failed = 1;
   1424   }
   1425   cbc.pos = 0; /* Reset buffer position */
   1426   rq_tr.req_num = 0;
   1427   /* Second request */
   1428   setCURL_rq_path (c, port, ++rq_tr.uri_num);
   1429   if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
   1430   {
   1431     fflush (stderr);
   1432     if (verbose)
   1433       printf ("Got second expected response.\n");
   1434     fflush (stdout);
   1435   }
   1436   else
   1437   {
   1438     fprintf (stderr, "Second request FAILED.\n");
   1439     failed = 1;
   1440   }
   1441   cbc.pos = 0; /* Reset buffer position */
   1442   rq_tr.req_num = 0;
   1443   /* Third request */
   1444   if (NULL != multi_reuse)
   1445     curl_multi_cleanup (multi_reuse);
   1446   multi_reuse = NULL; /* Force new connection */
   1447   setCURL_rq_path (c, port, ++rq_tr.uri_num);
   1448   if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
   1449   {
   1450     fflush (stderr);
   1451     if (verbose)
   1452       printf ("Got third expected response.\n");
   1453     fflush (stdout);
   1454   }
   1455   else
   1456   {
   1457     fprintf (stderr, "Third request FAILED.\n");
   1458     failed = 1;
   1459   }
   1460 
   1461   curl_easy_cleanup (c);
   1462   if (NULL != multi_reuse)
   1463     curl_multi_cleanup (multi_reuse);
   1464 
   1465   MHD_stop_daemon (d);
   1466   return failed ? 1 : 0;
   1467 }
   1468 
   1469 
   1470 int
   1471 main (int argc, char *const *argv)
   1472 {
   1473 #if ! CURL_AT_LEAST_VERSION (7,19,1)
   1474   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
   1475   /* Need version 7.19.1 or newer for separate username and password */
   1476   fprintf (stderr, "Required libcurl at least version 7.19.1"
   1477            " to run this test.\n");
   1478   return 77;
   1479 #else  /* CURL_AT_LEAST_VERSION(7,19,1) */
   1480   unsigned int errorCount = 0;
   1481   const curl_version_info_data *const curl_info =
   1482     curl_version_info (CURLVERSION_NOW);
   1483   int curl_sspi;
   1484   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
   1485 
   1486 #ifdef NEED_GCRYP_INIT
   1487   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
   1488 #ifdef GCRYCTL_INITIALIZATION_FINISHED
   1489   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
   1490 #endif /* GCRYCTL_INITIALIZATION_FINISHED */
   1491 #endif /* NEED_GCRYP_INIT */
   1492   /* Test type and test parameters */
   1493   verbose = ! (has_param (argc, argv, "-q") ||
   1494                has_param (argc, argv, "--quiet") ||
   1495                has_param (argc, argv, "-s") ||
   1496                has_param (argc, argv, "--silent"));
   1497   test_oldapi = 0;
   1498   if (has_in_name (argv[0], "_oldapi1"))
   1499     test_oldapi = 1;
   1500   if (has_in_name (argv[0], "_oldapi2"))
   1501     test_oldapi = 2;
   1502   test_userhash = has_in_name (argv[0], "_userhash");
   1503   test_userdigest = has_in_name (argv[0], "_userdigest");
   1504   test_sha256 = has_in_name (argv[0], "_sha256");
   1505   test_rfc2069 = has_in_name (argv[0], "_rfc2069");
   1506   test_bind_all = has_in_name (argv[0], "_bind_all");
   1507   test_bind_uri = has_in_name (argv[0], "_bind_uri");
   1508 
   1509   /* Wrong test types combinations */
   1510   if (1 == test_oldapi)
   1511   {
   1512     if (test_sha256)
   1513       return 99;
   1514   }
   1515   if (test_oldapi)
   1516   {
   1517     if (test_userhash || test_rfc2069)
   1518       return 99;
   1519   }
   1520   if (test_rfc2069)
   1521   {
   1522     if (test_userhash)
   1523       return 99;
   1524   }
   1525 
   1526   /* Curl version and known bugs checks */
   1527   curl_sspi = 0;
   1528 #ifdef CURL_VERSION_SSPI
   1529   if (0 != (curl_info->features & CURL_VERSION_SSPI))
   1530     curl_sspi = 1;
   1531 #endif /* CURL_VERSION_SSPI */
   1532 
   1533   if ((CURL_VERSION_BITS (7,63,0) > curl_info->version_num) &&
   1534       (CURL_VERSION_BITS (7,62,0) <= curl_info->version_num) )
   1535   {
   1536     fprintf (stderr, "libcurl version 7.62.x has bug in processing "
   1537              "URI with GET arguments for Digest Auth.\n");
   1538     fprintf (stderr, "This test with libcurl %u.%u.%u cannot be performed.\n",
   1539              0xFF & (curl_info->version_num >> 16),
   1540              0xFF & (curl_info->version_num >> 8),
   1541              0xFF & (curl_info->version_num >> 0));
   1542     return 77;
   1543   }
   1544 
   1545   if (test_sha256)
   1546   {
   1547     if (curl_sspi)
   1548     {
   1549       fprintf (stderr, "Windows SSPI API does not support SHA-256 digests.\n");
   1550       return 77;
   1551     }
   1552     else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num)
   1553     {
   1554       fprintf (stderr, "Required libcurl at least version 7.57.0 "
   1555                "to run this test with SHA-256.\n");
   1556       fprintf (stderr, "This libcurl version %u.%u.%u "
   1557                "does not support SHA-256.\n",
   1558                0xFF & (curl_info->version_num >> 16),
   1559                0xFF & (curl_info->version_num >> 8),
   1560                0xFF & (curl_info->version_num >> 0));
   1561       return 77;
   1562     }
   1563   }
   1564 
   1565   if (test_userhash)
   1566   {
   1567     if (curl_sspi)
   1568     {
   1569       printf ("WARNING: Windows SSPI API does not support 'userhash'.\n");
   1570       printf ("This test just checks Digest Auth compatibility with "
   1571               "the clients without 'userhash' support "
   1572               "when 'userhash=true' is specified by MHD.\n");
   1573       curl_uses_usehash = 0;
   1574     }
   1575     else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num)
   1576     {
   1577       printf ("WARNING: libcurl before version 7.57.0 does not "
   1578               "support 'userhash'.\n");
   1579       printf ("This test just checks Digest Auth compatibility with "
   1580               "libcurl version %u.%u.%u without 'userhash' support "
   1581               "when 'userhash=true' is specified by MHD.\n",
   1582               0xFF & (curl_info->version_num >> 16),
   1583               0xFF & (curl_info->version_num >> 8),
   1584               0xFF & (curl_info->version_num >> 0));
   1585       curl_uses_usehash = 0;
   1586     }
   1587     else if (CURL_VERSION_BITS (7,81,0) > curl_info->version_num)
   1588     {
   1589       fprintf (stderr, "Required libcurl at least version 7.81.0 "
   1590                "to run this test with userhash.\n");
   1591       fprintf (stderr, "This libcurl version %u.%u.%u has broken digest "
   1592                "calculation when userhash is used.\n",
   1593                0xFF & (curl_info->version_num >> 16),
   1594                0xFF & (curl_info->version_num >> 8),
   1595                0xFF & (curl_info->version_num >> 0));
   1596       return 77;
   1597     }
   1598     else
   1599       curl_uses_usehash = ! 0;
   1600   }
   1601   else
   1602     curl_uses_usehash = 0;
   1603 
   1604   test_global_init ();
   1605 
   1606   errorCount += testDigestAuth ();
   1607   if (errorCount != 0)
   1608     fprintf (stderr, "Error (code: %u)\n", errorCount);
   1609   test_global_cleanup ();
   1610   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
   1611 #endif /* CURL_AT_LEAST_VERSION(7,19,1) */
   1612 }