libmicrohttpd

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

test_dauth_malformed.c (41424B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2026 Christian Grothoff
      4 
      5      libmicrohttpd is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 2, or (at your
      8      option) any later version.
      9 
     10      libmicrohttpd is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libmicrohttpd; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 /**
     21  * @file test_dauth_malformed.c
     22  * @brief  Testcase for malformed Digest "Authorization:" request headers
     23  * @author Christian Grothoff
     24  *
     25  * The malformed headers used by this test cannot be produced with libcurl,
     26  * therefore all the requests are sent over a raw socket.
     27  *
     28  * The test is a regression test for two remotely triggerable defects:
     29  *
     30  * - An unrecognised "algorithm=" token is parsed to
     31  *   #MHD_DIGEST_AUTH_ALGO3_INVALID, the numeric value of which is zero.  The
     32  *   "is the client's algorithm allowed by the application" bit-mask test is
     33  *   passed trivially by a zero value, so an invalid algorithm used to reach
     34  *   digest_init_one_time(), which aborts the process with MHD_PANIC().
     35  *
     36  * - The "response=" value was hex-decoded into a #MAX_DIGEST bytes long
     37  *   buffer on the stack, while the only length check allowed four times as
     38  *   many characters as the digest size, resulting in an out-of-bounds stack
     39  *   write of up to #MAX_DIGEST bytes.  Reaching the decoder requires an
     40  *   otherwise complete and valid Digest handshake, hence the two-step
     41  *   exchange performed by this test.
     42  *
     43  * A number of adjacent malformed, empty, over-long and duplicated parameters
     44  * is checked as well.  Every sub-case must leave the daemons alive and must
     45  * be answered with a regular HTTP error reply.
     46  *
     47  * Two daemons are used: one that issues SHA-256 challenges and one that
     48  * issues the challenges with the default "any non-session" algorithm.  This
     49  * keeps the length of the nonces recorded by a single daemon uniform, which
     50  * is what a real-world application does as well.
     51  *
     52  * The test honours the daemon option matrix of mhd_opt_matrix.h: when one of
     53  * the MHD_TEST_* environment variables selects a profile, its connection
     54  * memory limit, client discipline level, threading mode and polling backend
     55  * are applied to both daemons.  Without those variables nothing changes.  A
     56  * Digest handshake needs room for a 76 character nonce plus the realm, the
     57  * username and the response, so a profile asking for less than
     58  * #MIN_POOL_FOR_TEST bytes is raised to that value and the adjustment is
     59  * reported.
     60  */
     61 
     62 #include "MHD_config.h"
     63 #include "platform.h"
     64 #include <microhttpd.h>
     65 #include <stdlib.h>
     66 #include <string.h>
     67 #include <stdarg.h>
     68 #include <stdint.h>
     69 #include <errno.h>
     70 
     71 #ifdef HAVE_STRINGS_H
     72 #include <strings.h>
     73 #endif /* HAVE_STRINGS_H */
     74 
     75 #ifndef WINDOWS
     76 #include <unistd.h>
     77 #include <sys/socket.h>
     78 #endif
     79 
     80 #include "mhd_sockets.h" /* only macros used */
     81 #include "mhd_opt_matrix.h"
     82 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     83    test into a marked, classifiable test error (TESTING.md, P5). */
     84 #include "mhd_panic_tripwire.h"
     85 
     86 /**
     87  * The smallest connection memory pool this test can work with: a complete
     88  * Digest "Authorization:" header plus the "WWW-Authenticate:" challenge of
     89  * the reply do not fit into a smaller pool.
     90  */
     91 #define MIN_POOL_FOR_TEST 4096
     92 
     93 #ifndef MHD_STATICSTR_LEN_
     94 /**
     95  * Determine length of static string / macro strings at compile time.
     96  */
     97 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     98 #endif /* ! MHD_STATICSTR_LEN_ */
     99 
    100 #if defined(DAUTH_SUPPORT) && defined(MHD_SHA256_SUPPORT)
    101 
    102 /**
    103  * The realm used by the test daemons.
    104  */
    105 #define TEST_REALM "TestRealm"
    106 
    107 /**
    108  * The username known to the test daemons.
    109  */
    110 #define TEST_USERNAME "testuser"
    111 
    112 /**
    113  * The password known to the test daemons.
    114  */
    115 #define TEST_PASSWORD "testpass"
    116 
    117 /**
    118  * The URL used by all the requests.
    119  */
    120 #define TEST_URL "/auth"
    121 
    122 /**
    123  * The size of the buffer used to build a request.
    124  */
    125 #define REQ_BUF_SIZE 32768
    126 
    127 /**
    128  * The size of the buffer used to receive a reply.
    129  */
    130 #define RPLY_BUF_SIZE 8192
    131 
    132 /**
    133  * The length of the over-long parameter values.
    134  */
    135 #define LONG_PARAM_LEN 4000
    136 
    137 /**
    138  * Socket send/receive timeout in seconds.
    139  */
    140 #define SOCK_TIMEOUT_SEC 5
    141 
    142 /**
    143  * The number of the "nonce"/"nc" pairs recorded by a daemon.
    144  */
    145 #define NONCE_NC_SIZE 100
    146 
    147 
    148 /**
    149  * The port of the daemon issuing SHA-256 challenges.
    150  */
    151 static uint16_t port_sha256;
    152 
    153 /**
    154  * The port of the daemon issuing "any non-session" (MD5) challenges.
    155  */
    156 static uint16_t port_any;
    157 
    158 /**
    159  * The port used by the sub-case being executed.
    160  */
    161 static uint16_t test_port;
    162 
    163 /**
    164  * The number of the sub-case being executed.
    165  */
    166 static unsigned int case_num;
    167 
    168 /**
    169  * The number of the failed sub-cases.
    170  */
    171 static unsigned int failures;
    172 
    173 /**
    174  * The 'nc' value for the next request, incremented for every request so that
    175  * a "nonce"/"nc" combination is never re-used.
    176  */
    177 static unsigned int nc_counter;
    178 
    179 /**
    180  * Run only the sub-cases with a name containing this string, NULL for all.
    181  */
    182 static const char *case_filter;
    183 
    184 /**
    185  * Set to non-zero by the daemon if a malformed request was authenticated.
    186  */
    187 static volatile int wrongly_authenticated;
    188 
    189 /**
    190  * The profile selected by the environment, NULL if the built-in daemon
    191  * configuration is used (which is the case for a stock "make check").
    192  */
    193 static const struct MHD_OptMatrixProfile *test_prof;
    194 
    195 /**
    196  * The (possibly adjusted) copy of the profile @a test_prof points to.
    197  */
    198 static struct MHD_OptMatrixProfile test_prof_copy;
    199 
    200 
    201 _MHD_NORETURN static void
    202 hard_error (const char *msg)
    203 {
    204   fprintf (stderr,
    205            "Hard error: %s\n",
    206            msg);
    207   fflush (stderr);
    208   exit (99);
    209 }
    210 
    211 
    212 /**
    213  * Fill @a buf with @a len hexadecimal digits and zero-terminate it.
    214  *
    215  * @param[out] buf the buffer to fill, must be at least @a len + 1 bytes long
    216  * @param len the number of the hexadecimal digits to generate
    217  * @return the @a buf pointer
    218  */
    219 static char *
    220 fill_hex (char *buf,
    221           size_t len)
    222 {
    223   static const char hex_chars[] = "0123456789abcdef";
    224   size_t i;
    225 
    226   for (i = 0; i < len; ++i)
    227     buf[i] = hex_chars[i % (MHD_STATICSTR_LEN_ (hex_chars))];
    228   buf[len] = 0;
    229   return buf;
    230 }
    231 
    232 
    233 /**
    234  * Fill @a buf with @a len copies of @a chr and zero-terminate it.
    235  *
    236  * @param[out] buf the buffer to fill, must be at least @a len + 1 bytes long
    237  * @param chr the character to repeat
    238  * @param len the number of the characters to generate
    239  * @return the @a buf pointer
    240  */
    241 static char *
    242 fill_chr (char *buf,
    243           char chr,
    244           size_t len)
    245 {
    246   memset (buf, chr, len);
    247   buf[len] = 0;
    248   return buf;
    249 }
    250 
    251 
    252 /* * * * * * * * * * * *  The daemon side  * * * * * * * * * * * */
    253 
    254 static enum MHD_Result
    255 ahc_check (void *cls,
    256            struct MHD_Connection *connection,
    257            const char *url,
    258            const char *method,
    259            const char *version,
    260            const char *upload_data,
    261            size_t *upload_data_size,
    262            void **req_cls)
    263 {
    264   static int marker;
    265   const enum MHD_DigestAuthMultiAlgo3 *const challenge_algo = cls;
    266   struct MHD_Response *response;
    267   enum MHD_DigestAuthResult check_res;
    268   enum MHD_Result ret;
    269   (void) url; (void) method; (void) version; (void) upload_data;
    270   (void) upload_data_size;
    271 
    272   if (&marker != *req_cls)
    273   { /* The first call for the request */
    274     *req_cls = &marker;
    275     return MHD_YES;
    276   }
    277 
    278   check_res =
    279     MHD_digest_auth_check3 (connection,
    280                             TEST_REALM,
    281                             TEST_USERNAME,
    282                             TEST_PASSWORD,
    283                             300,
    284                             0,
    285                             MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
    286                             MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION);
    287   if (MHD_DAUTH_OK == check_res)
    288   { /* None of the sub-cases must be authenticated */
    289     static const char page[] = "Authenticated";
    290 
    291     wrongly_authenticated = 1;
    292     response =
    293       MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (page),
    294                                        (void *) page,
    295                                        MHD_RESPMEM_PERSISTENT);
    296     if (NULL == response)
    297       return MHD_NO;
    298     ret = MHD_queue_response (connection,
    299                               MHD_HTTP_OK,
    300                               response);
    301     MHD_destroy_response (response);
    302     return ret;
    303   }
    304 
    305   if (1)
    306   {
    307     static const char page[] = "Access denied";
    308 
    309     response =
    310       MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (page),
    311                                        (void *) page,
    312                                        MHD_RESPMEM_PERSISTENT);
    313     if (NULL == response)
    314       return MHD_NO;
    315     ret =
    316       MHD_queue_auth_required_response3 (connection,
    317                                          TEST_REALM,
    318                                          "test-opaque",
    319                                          NULL,
    320                                          response,
    321                                          (MHD_DAUTH_NONCE_STALE == check_res) ?
    322                                          MHD_YES : MHD_NO,
    323                                          MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
    324                                          *challenge_algo,
    325                                          MHD_YES,
    326                                          MHD_NO);
    327     MHD_destroy_response (response);
    328   }
    329   return ret;
    330 }
    331 
    332 
    333 /* * * * * * * * * * * *  The raw client side  * * * * * * * * * * * */
    334 
    335 /**
    336  * Send @a req to the daemon at #test_port and receive the complete reply.
    337  *
    338  * @param req the request to send, zero-terminated
    339  * @param[out] rply the buffer for the reply
    340  * @param rply_size the size of the @a rply buffer
    341  * @return the number of the received bytes,
    342  *         -1 if no reply has been received
    343  */
    344 static ssize_t
    345 exchange (const char *req,
    346           char *rply,
    347           size_t rply_size)
    348 {
    349   MHD_socket sckt;
    350   struct sockaddr_in sa;
    351   struct timeval tmo;
    352   size_t req_len;
    353   size_t sent;
    354   size_t received;
    355 
    356   sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    357   if (MHD_INVALID_SOCKET == sckt)
    358     hard_error ("socket() failed");
    359 
    360   tmo.tv_sec = SOCK_TIMEOUT_SEC;
    361   tmo.tv_usec = 0;
    362   if ( (0 != setsockopt (sckt, SOL_SOCKET, SO_RCVTIMEO,
    363                          (const void *) &tmo, sizeof (tmo))) ||
    364        (0 != setsockopt (sckt, SOL_SOCKET, SO_SNDTIMEO,
    365                          (const void *) &tmo, sizeof (tmo))) )
    366     hard_error ("setsockopt() failed");
    367 
    368   memset (&sa, 0, sizeof (sa));
    369   sa.sin_family = AF_INET;
    370   sa.sin_port = htons (test_port);
    371   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    372   if (0 != connect (sckt, (const struct sockaddr *) &sa, sizeof (sa)))
    373   {
    374     MHD_socket_close_chk_ (sckt);
    375     return -1;
    376   }
    377 
    378   req_len = strlen (req);
    379   sent = 0;
    380   while (sent < req_len)
    381   {
    382     ssize_t res;
    383 
    384     res = send (sckt, req + sent, req_len - sent, 0);
    385     if (0 >= res)
    386       break;
    387     sent += (size_t) res;
    388   }
    389   if (sent != req_len)
    390   {
    391     MHD_socket_close_chk_ (sckt);
    392     return -1;
    393   }
    394 
    395   received = 0;
    396   while (received + 1 < rply_size)
    397   {
    398     ssize_t res;
    399 
    400     res = recv (sckt, rply + received, rply_size - received - 1, 0);
    401     if (0 >= res)
    402       break;
    403     received += (size_t) res;
    404   }
    405   rply[received] = 0;
    406   MHD_socket_close_chk_ (sckt);
    407   if (0 == received)
    408     return -1;
    409   return (ssize_t) received;
    410 }
    411 
    412 
    413 /**
    414  * Extract the HTTP status code from a raw reply.
    415  *
    416  * @param rply the reply, zero-terminated
    417  * @return the status code, zero if the reply is not a valid HTTP reply
    418  */
    419 static unsigned int
    420 get_status (const char *rply)
    421 {
    422   unsigned int status;
    423 
    424   if (MHD_STATICSTR_LEN_ ("HTTP/1.1 000") > strlen (rply))
    425     return 0;
    426   if (0 != memcmp (rply, "HTTP/1.", MHD_STATICSTR_LEN_ ("HTTP/1.")))
    427     return 0;
    428   status = 0;
    429   if (1 != sscanf (rply + MHD_STATICSTR_LEN_ ("HTTP/1.1 "), "%3u", &status))
    430     return 0;
    431   return status;
    432 }
    433 
    434 
    435 /**
    436  * Request a challenge from the daemon at #test_port and extract the "nonce"
    437  * value from it.
    438  *
    439  * @param[out] nonce the buffer for the "nonce" value
    440  * @param nonce_size the size of the @a nonce buffer
    441  * @return non-zero on success, zero on failure
    442  */
    443 static int
    444 get_nonce (char *nonce,
    445            size_t nonce_size)
    446 {
    447   static const char nonce_tk[] = "nonce=\"";
    448   static const char req[] =
    449     "GET " TEST_URL " HTTP/1.1\r\n"
    450     "Host: 127.0.0.1\r\n"
    451     "Connection: close\r\n"
    452     "\r\n";
    453   char rply[RPLY_BUF_SIZE];
    454   const char *start;
    455   const char *end;
    456   size_t len;
    457 
    458   if (0 >= exchange (req, rply, sizeof (rply)))
    459     return 0;
    460   if (MHD_HTTP_UNAUTHORIZED != get_status (rply))
    461     return 0;
    462   start = strstr (rply, nonce_tk);
    463   if (NULL == start)
    464     return 0;
    465   start += MHD_STATICSTR_LEN_ (nonce_tk);
    466   end = strchr (start, '"');
    467   if (NULL == end)
    468     return 0;
    469   len = (size_t) (end - start);
    470   if (len + 1 > nonce_size)
    471     return 0;
    472   memcpy (nonce, start, len);
    473   nonce[len] = 0;
    474   return 0 != len;
    475 }
    476 
    477 
    478 /**
    479  * Obtain a "nonce" or fail the whole test.
    480  *
    481  * @param[out] nonce the buffer for the "nonce" value
    482  * @param nonce_size the size of the @a nonce buffer
    483  */
    484 static void
    485 need_nonce (char *nonce,
    486             size_t nonce_size)
    487 {
    488   if (! get_nonce (nonce, nonce_size))
    489     hard_error ("cannot obtain a 'nonce' from the daemon");
    490 }
    491 
    492 
    493 /**
    494  * Run a single sub-case: send a request with the given "Authorization:"
    495  * header value and check that the daemon replies with a regular HTTP error.
    496  *
    497  * @param name the name of the sub-case, used for reporting
    498  * @param auth_fmt the printf-style format of the "Authorization:" header
    499  *                 value, NULL to send no "Authorization:" header
    500  * @param ... the arguments for the @a auth_fmt
    501  */
    502 static void
    503 run_case (const char *name,
    504           const char *auth_fmt,
    505           ...)
    506 {
    507   static char req[REQ_BUF_SIZE];
    508   static char auth[REQ_BUF_SIZE - 512];
    509   char rply[RPLY_BUF_SIZE];
    510   unsigned int status;
    511 
    512   ++case_num;
    513   if ( (NULL != case_filter) &&
    514        (NULL == strstr (name, case_filter)) )
    515     return;
    516 
    517   /* Report the sub-case before it is executed: if the daemon aborts the
    518      process, the last reported sub-case is the failing one. */
    519   fprintf (stderr,
    520            "[%02u] %s\n",
    521            case_num,
    522            name);
    523   fflush (stderr);
    524 
    525   if (NULL != auth_fmt)
    526   {
    527     va_list ap;
    528 
    529     va_start (ap, auth_fmt);
    530     vsnprintf (auth, sizeof (auth), auth_fmt, ap);
    531     va_end (ap);
    532     snprintf (req, sizeof (req),
    533               "GET " TEST_URL " HTTP/1.1\r\n"
    534               "Host: 127.0.0.1\r\n"
    535               "Connection: close\r\n"
    536               "Authorization: %s\r\n"
    537               "\r\n",
    538               auth);
    539   }
    540   else
    541     snprintf (req, sizeof (req),
    542               "GET " TEST_URL " HTTP/1.1\r\n"
    543               "Host: 127.0.0.1\r\n"
    544               "Connection: close\r\n"
    545               "\r\n");
    546 
    547   if (0 >= exchange (req, rply, sizeof (rply)))
    548   {
    549     fprintf (stderr,
    550              "FAILED sub-case '%s': no reply received.\n",
    551              name);
    552     fflush (stderr);
    553     ++failures;
    554     return;
    555   }
    556   status = get_status (rply);
    557   /* Any regular HTTP error reply is fine: the point of the test is that the
    558      daemon neither crashes nor accepts the malformed credentials. */
    559   if ( (MHD_HTTP_UNAUTHORIZED != status) &&
    560        (MHD_HTTP_BAD_REQUEST != status) &&
    561        (MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE != status) )
    562   {
    563     fprintf (stderr,
    564              "FAILED sub-case '%s': unexpected HTTP status %u.\n",
    565              name,
    566              status);
    567     fflush (stderr);
    568     ++failures;
    569     return;
    570   }
    571   if (0 != wrongly_authenticated)
    572   {
    573     fprintf (stderr,
    574              "FAILED sub-case '%s': the malformed request was "
    575              "authenticated.\n",
    576              name);
    577     fflush (stderr);
    578     wrongly_authenticated = 0;
    579     ++failures;
    580   }
    581 }
    582 
    583 
    584 /**
    585  * Build the common part of an otherwise valid Digest "Authorization:" header.
    586  *
    587  * @param[out] buf the buffer to fill
    588  * @param buf_size the size of the @a buf
    589  * @param nonce the "nonce" value received from the daemon
    590  */
    591 static void
    592 make_valid_params (char *buf,
    593                    size_t buf_size,
    594                    const char *nonce)
    595 {
    596   snprintf (buf, buf_size,
    597             "username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    598             "nonce=\"%s\", uri=\"" TEST_URL "\", qop=auth, nc=%08x, "
    599             "cnonce=\"0123456789abcdef\"",
    600             nonce,
    601             ++nc_counter);
    602 }
    603 
    604 
    605 /* * * * * * * * * * * *  The sub-cases  * * * * * * * * * * * */
    606 
    607 /**
    608  * Sub-cases for the missing #MHD_DIGEST_AUTH_ALGO3_INVALID check.
    609  *
    610  * An unknown "algorithm=" token is parsed to
    611  * #MHD_DIGEST_AUTH_ALGO3_INVALID, which has the numeric value of zero and
    612  * therefore satisfies the "allowed algorithms" bit-mask test.  Without the
    613  * explicit check for the invalid value the request reaches
    614  * digest_init_one_time() and aborts the process with MHD_PANIC().
    615  *
    616  * The algorithm is checked before anything else, so most of the sub-cases
    617  * need no other parameter at all.
    618  */
    619 static void
    620 test_invalid_algorithm (void)
    621 {
    622   char nonce[256];
    623   char params[512];
    624   char resp[128];
    625 
    626   fill_hex (resp, 64);
    627 
    628   test_port = port_any;
    629 
    630   run_case ("bug-c/algo-bogus-alone",
    631             "Digest algorithm=BOGUS");
    632   run_case ("bug-c/algo-md5-x",
    633             "Digest algorithm=MD5-x");
    634   run_case ("bug-c/algo-sha512",
    635             "Digest algorithm=SHA-512");
    636   run_case ("bug-c/algo-sha1",
    637             "Digest algorithm=SHA1");
    638   run_case ("bug-c/algo-sha256-typo",
    639             "Digest algorithm=SHA256");
    640   run_case ("bug-c/algo-empty-quotes",
    641             "Digest algorithm=\"\"");
    642   run_case ("bug-c/algo-empty-token",
    643             "Digest algorithm=");
    644   run_case ("bug-c/algo-empty-token-comma",
    645             "Digest algorithm=, nonce=\"deadbeef\"");
    646   run_case ("bug-c/algo-quoted-bogus",
    647             "Digest algorithm=\"BOGUS\"");
    648   run_case ("bug-c/algo-space",
    649             "Digest algorithm = BOGUS");
    650   /* A backslash inside the value marks the parameter as "quoted", which
    651      selects the second, separate branch of the algorithm token parser. */
    652   run_case ("bug-c/algo-escaped-bogus",
    653             "Digest algorithm=\"\\BOGUS\"");
    654   run_case ("bug-c/algo-escaped-md5-x",
    655             "Digest algorithm=\"MD5\\-x\"");
    656   /* The "-sess" variants are valid tokens, but they are rejected by the
    657      bit-mask test as they are not allowed by the application. */
    658   run_case ("bug-c/algo-md5-sess",
    659             "Digest algorithm=MD5-sess");
    660   run_case ("bug-c/algo-sha256-sess",
    661             "Digest algorithm=SHA-256-sess");
    662   run_case ("bug-c/algo-sha512-256-sess",
    663             "Digest algorithm=SHA-512-256-sess");
    664   run_case ("bug-c/algo-escaped-md5-sess",
    665             "Digest algorithm=\"MD5\\-sess\"");
    666   run_case ("bug-c/algo-escaped-sha512-256",
    667             "Digest algorithm=\"SHA\\-512-256\"");
    668   /* The last occurrence of a duplicated parameter is the effective one. */
    669   run_case ("bug-c/algo-duplicated",
    670             "Digest algorithm=MD5, algorithm=BOGUS");
    671   run_case ("bug-c/algo-duplicated-rev",
    672             "Digest algorithm=BOGUS, algorithm=MD5");
    673 
    674   /* The same, but with all the other parameters present and valid, so that
    675      nothing else can short-circuit the check. */
    676   test_port = port_sha256;
    677 
    678   need_nonce (nonce, sizeof (nonce));
    679   make_valid_params (params, sizeof (params), nonce);
    680   run_case ("bug-c/algo-bogus-full",
    681             "Digest %s, algorithm=BOGUS, response=\"%s\"",
    682             params,
    683             resp);
    684 
    685   need_nonce (nonce, sizeof (nonce));
    686   make_valid_params (params, sizeof (params), nonce);
    687   run_case ("bug-c/algo-empty-full",
    688             "Digest %s, algorithm=\"\", response=\"%s\"",
    689             params,
    690             resp);
    691 
    692   need_nonce (nonce, sizeof (nonce));
    693   make_valid_params (params, sizeof (params), nonce);
    694   run_case ("bug-c/algo-escaped-bogus-full",
    695             "Digest %s, algorithm=\"\\BOGUS\", response=\"%s\"",
    696             params,
    697             resp);
    698 }
    699 
    700 
    701 /**
    702  * Sub-cases for the out-of-bounds stack write in the "response=" hex decoder.
    703  *
    704  * The decoder writes (len + 1) / 2 bytes into a #MAX_DIGEST (32) bytes long
    705  * stack buffer, while the length sanity check accepts up to digest_size * 4
    706  * (128) characters.  All the other parameters must be valid to reach the
    707  * decoder, therefore a fresh "nonce" is obtained for every sub-case.
    708  */
    709 static void
    710 test_overlong_response (void)
    711 {
    712   /* 128 and 127 characters produce the maximum overflow of 32 bytes, 66 and
    713      65 characters produce the minimum overflow of a single byte, 64
    714      characters fit exactly and must not overflow. */
    715   static const size_t resp_lengths[] = { 128, 127, 100, 66, 65, 64 };
    716   char nonce[256];
    717   char params[512];
    718   char resp[512];
    719   char name[128];
    720   size_t i;
    721 
    722   test_port = port_sha256;
    723 
    724   for (i = 0; i < sizeof (resp_lengths) / sizeof (resp_lengths[0]); ++i)
    725   {
    726     const size_t len = resp_lengths[i];
    727 
    728     /* The value without the quotation marks */
    729     need_nonce (nonce, sizeof (nonce));
    730     make_valid_params (params, sizeof (params), nonce);
    731     snprintf (name, sizeof (name),
    732               "bug-d/sha256-response-%u-bare",
    733               (unsigned) len);
    734     run_case (name,
    735               "Digest %s, algorithm=SHA-256, response=%s",
    736               params,
    737               fill_hex (resp, len));
    738 
    739     /* The same value in the quotation marks */
    740     need_nonce (nonce, sizeof (nonce));
    741     make_valid_params (params, sizeof (params), nonce);
    742     snprintf (name, sizeof (name),
    743               "bug-d/sha256-response-%u-quoted",
    744               (unsigned) len);
    745     run_case (name,
    746               "Digest %s, algorithm=SHA-256, response=\"%s\"",
    747               params,
    748               fill_hex (resp, len));
    749   }
    750 
    751   /* A backslash marks the value as "quoted", so it is unquoted into the
    752      temporal buffer first and only then decoded. */
    753   need_nonce (nonce, sizeof (nonce));
    754   make_valid_params (params, sizeof (params), nonce);
    755   run_case ("bug-d/sha256-response-127-escaped",
    756             "Digest %s, algorithm=SHA-256, response=\"\\%s\"",
    757             params,
    758             fill_hex (resp, 127));
    759 
    760   /* The SHA-512/256 algorithm uses the same digest size as SHA-256 and the
    761      same nonce length, so it reaches the decoder as well. */
    762 #ifdef MHD_SHA512_256_SUPPORT
    763   need_nonce (nonce, sizeof (nonce));
    764   make_valid_params (params, sizeof (params), nonce);
    765   run_case ("bug-d/sha512-256-response-128-bare",
    766             "Digest %s, algorithm=SHA-512-256, response=%s",
    767             params,
    768             fill_hex (resp, 128));
    769 #endif /* MHD_SHA512_256_SUPPORT */
    770 
    771   /* The RFC2069 mode: no "qop", no "nc" and no "cnonce". */
    772   need_nonce (nonce, sizeof (nonce));
    773   run_case ("bug-d/sha256-response-128-rfc2069",
    774             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    775             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, "
    776             "response=%s",
    777             nonce,
    778             fill_hex (resp, 128));
    779 
    780   /* The value is not a valid hexadecimal string, the decoder must bail out
    781      before writing anything. */
    782   need_nonce (nonce, sizeof (nonce));
    783   make_valid_params (params, sizeof (params), nonce);
    784   run_case ("bug-d/sha256-response-128-non-hex",
    785             "Digest %s, algorithm=SHA-256, response=%s",
    786             params,
    787             fill_chr (resp, 'z', 128));
    788 
    789   /* Only the last character is not a hexadecimal digit, so the decoder
    790      writes 63 bytes before it detects the error. */
    791   need_nonce (nonce, sizeof (nonce));
    792   make_valid_params (params, sizeof (params), nonce);
    793   fill_hex (resp, 128);
    794   resp[127] = 'z';
    795   run_case ("bug-d/sha256-response-128-tail-non-hex",
    796             "Digest %s, algorithm=SHA-256, response=%s",
    797             params,
    798             resp);
    799 
    800   /* Duplicated parameter: the last (over-long) value is the effective one. */
    801   need_nonce (nonce, sizeof (nonce));
    802   make_valid_params (params, sizeof (params), nonce);
    803   run_case ("bug-d/sha256-response-duplicated",
    804             "Digest %s, algorithm=SHA-256, response=\"00112233\", response=%s",
    805             params,
    806             fill_hex (resp, 128));
    807 
    808 #ifdef MHD_MD5_SUPPORT
    809   /* The MD5 variant: the decoder writes 32 bytes, which overflows the buffer
    810      only in an MD5-only build, where MAX_DIGEST is 16. */
    811   test_port = port_any;
    812 
    813   need_nonce (nonce, sizeof (nonce));
    814   make_valid_params (params, sizeof (params), nonce);
    815   run_case ("bug-d/md5-response-64-bare",
    816             "Digest %s, algorithm=MD5, response=%s",
    817             params,
    818             fill_hex (resp, 64));
    819 
    820   need_nonce (nonce, sizeof (nonce));
    821   make_valid_params (params, sizeof (params), nonce);
    822   run_case ("bug-d/md5-response-63-bare",
    823             "Digest %s, algorithm=MD5, response=%s",
    824             params,
    825             fill_hex (resp, 63));
    826 
    827   need_nonce (nonce, sizeof (nonce));
    828   make_valid_params (params, sizeof (params), nonce);
    829   run_case ("bug-d/md5-response-34-quoted",
    830             "Digest %s, algorithm=MD5, response=\"%s\"",
    831             params,
    832             fill_hex (resp, 34));
    833 #endif /* MHD_MD5_SUPPORT */
    834 }
    835 
    836 
    837 /**
    838  * Sub-cases for the adjacent malformed, empty, over-long and duplicated
    839  * parameters.
    840  */
    841 static void
    842 test_adjacent (void)
    843 {
    844   static char big[LONG_PARAM_LEN + 1];
    845   char nonce[256];
    846   char resp[128];
    847 
    848   fill_hex (resp, 64);
    849 
    850   /* No or unusable "Authorization:" header.  These never reach the
    851      "nonce" handling, the daemon used does not matter. */
    852   test_port = port_any;
    853 
    854   run_case ("adj/no-auth-header", NULL);
    855   run_case ("adj/scheme-only", "Digest");
    856   run_case ("adj/scheme-and-space", "Digest ");
    857   run_case ("adj/only-commas", "Digest ,,,,");
    858   run_case ("adj/basic-scheme", "Basic dGVzdHVzZXI6dGVzdHBhc3M=");
    859   run_case ("adj/unknown-scheme", "Bogus xyz");
    860   run_case ("adj/unterminated-quote",
    861             "Digest username=\"" TEST_USERNAME);
    862   run_case ("adj/unterminated-quote-escape",
    863             "Digest username=\"" TEST_USERNAME "\\\"");
    864   run_case ("adj/no-equal-sign", "Digest username");
    865   run_case ("adj/leading-equal-sign", "Digest =value");
    866   run_case ("adj/semicolon-in-value", "Digest username=a;b");
    867   run_case ("adj/garbage-after-value", "Digest username=\"a\" garbage");
    868   run_case ("adj/unknown-parameter",
    869             "Digest bogus=\"x\", username=\"" TEST_USERNAME "\"");
    870 
    871   /* Over-long parameter values */
    872   run_case ("adj/username-too-long",
    873             "Digest username=\"%s\", realm=\"" TEST_REALM "\"",
    874             fill_chr (big, 'u', LONG_PARAM_LEN));
    875   run_case ("adj/realm-too-long",
    876             "Digest username=\"" TEST_USERNAME "\", realm=\"%s\"",
    877             fill_chr (big, 'r', LONG_PARAM_LEN));
    878   run_case ("adj/uri-too-long",
    879             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    880             "uri=\"%s\"",
    881             fill_chr (big, 'p', LONG_PARAM_LEN));
    882   run_case ("adj/cnonce-too-long",
    883             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    884             "qop=auth, nc=00000001, cnonce=\"%s\"",
    885             fill_chr (big, 'c', LONG_PARAM_LEN));
    886   run_case ("adj/nc-too-long",
    887             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    888             "qop=auth, nc=%s, cnonce=\"abc\"",
    889             fill_hex (big, LONG_PARAM_LEN));
    890   run_case ("adj/opaque-too-long",
    891             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    892             "opaque=\"%s\"",
    893             fill_chr (big, 'o', LONG_PARAM_LEN));
    894   run_case ("adj/nonce-too-long",
    895             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    896             "nonce=\"%s\"",
    897             fill_hex (big, LONG_PARAM_LEN));
    898   run_case ("adj/response-too-long",
    899             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    900             "response=\"%s\"",
    901             fill_hex (big, LONG_PARAM_LEN));
    902   run_case ("adj/username-ext-too-long",
    903             "Digest username*=UTF-8''%s, realm=\"" TEST_REALM "\"",
    904             fill_chr (big, 'e', LONG_PARAM_LEN));
    905 
    906   /* The extended ("username*") notation */
    907   run_case ("adj/username-ext-truncated",
    908             "Digest username*=U, realm=\"" TEST_REALM "\"");
    909   run_case ("adj/username-ext-no-lang",
    910             "Digest username*=UTF-8', realm=\"" TEST_REALM "\"");
    911   run_case ("adj/username-ext-empty",
    912             "Digest username*=UTF-8'', realm=\"" TEST_REALM "\"");
    913   run_case ("adj/username-ext-broken-pct",
    914             "Digest username*=UTF-8''%%, realm=\"" TEST_REALM "\"");
    915   run_case ("adj/username-ext-broken-pct2",
    916             "Digest username*=UTF-8''%%z, realm=\"" TEST_REALM "\"");
    917   run_case ("adj/username-ext-broken-pct3",
    918             "Digest username*=UTF-8''%%zz, realm=\"" TEST_REALM "\"");
    919   run_case ("adj/username-ext-trailing-pct",
    920             "Digest username*=UTF-8''abc%%, realm=\"" TEST_REALM "\"");
    921   run_case ("adj/username-ext-bad-charset",
    922             "Digest username*=ISO-8859-1''abc, realm=\"" TEST_REALM "\"");
    923   run_case ("adj/username-ext-quoted",
    924             "Digest username*=\"UTF-8''\\abc\", realm=\"" TEST_REALM "\"");
    925   run_case ("adj/username-ext-and-plain",
    926             "Digest username=\"" TEST_USERNAME "\", "
    927             "username*=UTF-8''" TEST_USERNAME ", realm=\"" TEST_REALM "\"");
    928 
    929   /* The "userhash" handling */
    930   run_case ("adj/userhash-username-too-short",
    931             "Digest userhash=true, username=\"abcd\", "
    932             "realm=\"" TEST_REALM "\"");
    933   run_case ("adj/userhash-username-empty",
    934             "Digest userhash=true, username=\"\", "
    935             "realm=\"" TEST_REALM "\"");
    936   run_case ("adj/userhash-username-too-long",
    937             "Digest userhash=true, username=\"%s\", "
    938             "realm=\"" TEST_REALM "\"",
    939             fill_hex (big, 200));
    940   run_case ("adj/userhash-username-ext",
    941             "Digest userhash=true, username*=UTF-8''abc, "
    942             "realm=\"" TEST_REALM "\"");
    943   run_case ("adj/userhash-no-username",
    944             "Digest userhash=true, realm=\"" TEST_REALM "\"");
    945 
    946   /* The "nc" handling */
    947   run_case ("adj/nc-zero",
    948             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    949             "qop=auth, nc=00000000, cnonce=\"abc\"");
    950   run_case ("adj/nc-non-hex",
    951             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    952             "qop=auth, nc=zzzzzzzz, cnonce=\"abc\"");
    953   run_case ("adj/nc-empty",
    954             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    955             "qop=auth, nc=, cnonce=\"abc\"");
    956   run_case ("adj/nc-overflow",
    957             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    958             "qop=auth, nc=ffffffffffffffffffffffff, cnonce=\"abc\"");
    959   run_case ("adj/qop-auth-no-nc",
    960             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    961             "qop=auth, cnonce=\"abc\"");
    962   run_case ("adj/qop-auth-no-cnonce",
    963             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    964             "qop=auth, nc=00000001");
    965   run_case ("adj/cnonce-empty",
    966             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    967             "qop=auth, nc=00000001, cnonce=\"\"");
    968 
    969   /* Malformed "nonce" values.  These are rejected by the length check
    970      before the "nonce"/"nc" map is consulted. */
    971   run_case ("adj/nonce-short",
    972             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    973             "nonce=\"deadbeef\", uri=\"" TEST_URL "\", qop=auth, "
    974             "nc=00000001, cnonce=\"abc\", response=\"00112233\"");
    975   run_case ("adj/nonce-empty",
    976             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    977             "nonce=\"\", uri=\"" TEST_URL "\", qop=auth, "
    978             "nc=00000001, cnonce=\"abc\", response=\"00112233\"");
    979 
    980   /* Duplicated parameters */
    981   run_case ("adj/duplicated-username",
    982             "Digest username=\"a\", username=\"" TEST_USERNAME "\", "
    983             "realm=\"" TEST_REALM "\"");
    984   run_case ("adj/duplicated-realm",
    985             "Digest username=\"" TEST_USERNAME "\", realm=\"x\", "
    986             "realm=\"" TEST_REALM "\"");
    987   run_case ("adj/duplicated-userhash",
    988             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    989             "userhash=false, userhash=true");
    990 
    991   /* The sub-cases below use the SHA-256 algorithm and therefore need the
    992      daemon that issues the SHA-256 (76 characters long) nonces. */
    993   test_port = port_sha256;
    994 
    995   run_case ("adj/nonce-not-generated-by-mhd",
    996             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
    997             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, "
    998             "qop=auth, nc=00000001, cnonce=\"abc\", response=\"%s\"",
    999             fill_hex (big, 76),
   1000             resp);
   1001 
   1002   need_nonce (nonce, sizeof (nonce));
   1003   run_case ("adj/uri-mismatch",
   1004             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1005             "nonce=\"%s\", uri=\"/other\", algorithm=SHA-256, qop=auth, "
   1006             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1007             nonce,
   1008             ++nc_counter,
   1009             resp);
   1010 
   1011   need_nonce (nonce, sizeof (nonce));
   1012   run_case ("adj/uri-empty",
   1013             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1014             "nonce=\"%s\", uri=\"\", algorithm=SHA-256, qop=auth, "
   1015             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1016             nonce,
   1017             ++nc_counter,
   1018             resp);
   1019 
   1020   need_nonce (nonce, sizeof (nonce));
   1021   run_case ("adj/uri-with-args",
   1022             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1023             "nonce=\"%s\", uri=\"" TEST_URL "?a=b\", algorithm=SHA-256, "
   1024             "qop=auth, nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1025             nonce,
   1026             ++nc_counter,
   1027             resp);
   1028 
   1029   need_nonce (nonce, sizeof (nonce));
   1030   run_case ("adj/duplicated-nonce",
   1031             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1032             "nonce=\"deadbeef\", nonce=\"%s\", uri=\"" TEST_URL "\", "
   1033             "algorithm=SHA-256, qop=auth, nc=%08x, cnonce=\"abc\", "
   1034             "response=\"%s\"",
   1035             nonce,
   1036             ++nc_counter,
   1037             resp);
   1038 
   1039   need_nonce (nonce, sizeof (nonce));
   1040   run_case ("adj/qop-auth-int",
   1041             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1042             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, "
   1043             "qop=auth-int, nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1044             nonce,
   1045             ++nc_counter,
   1046             resp);
   1047 
   1048   need_nonce (nonce, sizeof (nonce));
   1049   run_case ("adj/qop-bogus",
   1050             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1051             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, "
   1052             "qop=BOGUS, nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1053             nonce,
   1054             ++nc_counter,
   1055             resp);
   1056 
   1057   need_nonce (nonce, sizeof (nonce));
   1058   run_case ("adj/userhash-64-non-hex",
   1059             "Digest userhash=true, username=\"%s\", realm=\"" TEST_REALM "\", "
   1060             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, qop=auth, "
   1061             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1062             fill_chr (big, 'z', 64),
   1063             nonce,
   1064             ++nc_counter,
   1065             resp);
   1066 
   1067   need_nonce (nonce, sizeof (nonce));
   1068   run_case ("adj/userhash-128-hex",
   1069             "Digest userhash=true, username=\"%s\", realm=\"" TEST_REALM "\", "
   1070             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, qop=auth, "
   1071             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1072             fill_hex (big, 128),
   1073             nonce,
   1074             ++nc_counter,
   1075             resp);
   1076 
   1077   /* An empty "qop" value ("qop=" or "qop=\"\"") is parsed to
   1078      #MHD_DIGEST_AUTH_QOP_INVALID, whose numeric value is zero, so it used to
   1079      pass the "allowed QOP" bit-mask test in exactly the same way an unknown
   1080      "algorithm" token did before commit bd49ce93, and the empty value then
   1081      reached get_unquoted_param(), which asserts that the value is not empty.
   1082      Fixed in commit 3b898eae; these two sub-cases are its regression test. */
   1083   need_nonce (nonce, sizeof (nonce));
   1084   run_case ("adj/qop-empty",
   1085             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1086             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, qop=, "
   1087             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1088             nonce,
   1089             ++nc_counter,
   1090             resp);
   1091 
   1092   need_nonce (nonce, sizeof (nonce));
   1093   run_case ("adj/qop-empty-quotes",
   1094             "Digest username=\"" TEST_USERNAME "\", realm=\"" TEST_REALM "\", "
   1095             "nonce=\"%s\", uri=\"" TEST_URL "\", algorithm=SHA-256, qop=\"\", "
   1096             "nc=%08x, cnonce=\"abc\", response=\"%s\"",
   1097             nonce,
   1098             ++nc_counter,
   1099             resp);
   1100 }
   1101 
   1102 
   1103 /**
   1104  * Start a test daemon.
   1105  *
   1106  * @param challenge_algo the pointer to the algorithm used for the challenges
   1107  * @param[out] pport the pointer to store the port the daemon listens on
   1108  * @return the daemon
   1109  */
   1110 static struct MHD_Daemon *
   1111 start_test_daemon (const enum MHD_DigestAuthMultiAlgo3 *challenge_algo,
   1112                    uint16_t *pport)
   1113 {
   1114   struct MHD_Daemon *daemon;
   1115   const union MHD_DaemonInfo *dinfo;
   1116   struct MHD_OptMatrixProfile prof;
   1117   struct MHD_OptionItem ops[8];
   1118   unsigned int flags;
   1119 
   1120   if (NULL != test_prof)
   1121     prof = *test_prof;
   1122   else
   1123   {
   1124     memset (&prof, 0, sizeof (prof));
   1125     prof.name = "built-in";
   1126     prof.threading = MHD_OPT_MATRIX_THR_INTERNAL;
   1127     prof.poll_backend = MHD_OPT_MATRIX_POLL_SELECT;
   1128   }
   1129   if (0 == mhd_opt_matrix_fill_options (&prof, ops,
   1130                                         (unsigned int) (sizeof (ops)
   1131                                                         / sizeof (ops[0]))))
   1132     hard_error ("The daemon option array is too small");
   1133   /* The client of this test is a plain blocking socket client, so external
   1134      polling is served with an internal polling thread instead. */
   1135   flags = mhd_opt_matrix_daemon_flags (&prof, MHD_USE_ERROR_LOG, 0);
   1136 
   1137   daemon = MHD_start_daemon (flags,
   1138                              0,
   1139                              NULL, NULL,
   1140                              &ahc_check, (void *) challenge_algo,
   1141                              MHD_OPTION_ARRAY, ops,
   1142                              MHD_OPTION_CONNECTION_TIMEOUT,
   1143                              (unsigned int) 10,
   1144                              MHD_OPTION_NONCE_NC_SIZE,
   1145                              (unsigned int) NONCE_NC_SIZE,
   1146                              MHD_OPTION_END);
   1147   if (NULL == daemon)
   1148     hard_error ("MHD_start_daemon() failed");
   1149   dinfo = MHD_get_daemon_info (daemon, MHD_DAEMON_INFO_BIND_PORT);
   1150   if ( (NULL == dinfo) || (0 == dinfo->port) )
   1151   {
   1152     MHD_stop_daemon (daemon);
   1153     hard_error ("MHD_get_daemon_info() failed");
   1154   }
   1155   *pport = dinfo->port;
   1156   return daemon;
   1157 }
   1158 
   1159 
   1160 int
   1161 main (int argc, char *const *argv)
   1162 {
   1163   static const enum MHD_DigestAuthMultiAlgo3 algo_sha256 =
   1164     MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
   1165   static const enum MHD_DigestAuthMultiAlgo3 algo_any =
   1166     MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
   1167   struct MHD_Daemon *daemon_sha256;
   1168   struct MHD_Daemon *daemon_any;
   1169   char nonce[256];
   1170 
   1171   if (1 < argc)
   1172     case_filter = argv[1];
   1173 
   1174   mhd_opt_matrix_print_notice ("test_dauth_malformed");
   1175   test_prof = mhd_opt_matrix_from_env ();
   1176   if (NULL != test_prof)
   1177   {
   1178     char desc[256];
   1179 
   1180     test_prof_copy = *test_prof;
   1181     test_prof = &test_prof_copy;
   1182     if (! mhd_opt_matrix_profile_supported (test_prof))
   1183     {
   1184       printf ("test_dauth_malformed: the selected profile %s is not "
   1185               "supported by this build, the test is skipped.\n",
   1186               mhd_opt_matrix_describe (test_prof, desc, sizeof (desc)));
   1187       return 77;
   1188     }
   1189     if (mhd_opt_matrix_raise_mem_limit (&test_prof_copy, MIN_POOL_FOR_TEST))
   1190       printf ("test_dauth_malformed: the connection memory limit of the "
   1191               "profile was raised to %u, the smallest pool this test can "
   1192               "work with.\n", (unsigned) MIN_POOL_FOR_TEST);
   1193   }
   1194 
   1195   daemon_sha256 = start_test_daemon (&algo_sha256, &port_sha256);
   1196   daemon_any = start_test_daemon (&algo_any, &port_any);
   1197 
   1198   test_invalid_algorithm ();
   1199   test_overlong_response ();
   1200   test_adjacent ();
   1201 
   1202   /* Both daemons must still be alive and functional. */
   1203   test_port = port_sha256;
   1204   if (! get_nonce (nonce, sizeof (nonce)))
   1205   {
   1206     fprintf (stderr,
   1207              "FAILED: the SHA-256 daemon does not respond after the test.\n");
   1208     ++failures;
   1209   }
   1210   test_port = port_any;
   1211   if (! get_nonce (nonce, sizeof (nonce)))
   1212   {
   1213     fprintf (stderr,
   1214              "FAILED: the default daemon does not respond after the test.\n");
   1215     ++failures;
   1216   }
   1217 
   1218   MHD_stop_daemon (daemon_any);
   1219   MHD_stop_daemon (daemon_sha256);
   1220 
   1221   if (0 != failures)
   1222   {
   1223     fprintf (stderr,
   1224              "Test FAILED: %u of %u sub-cases failed.\n",
   1225              failures,
   1226              case_num);
   1227     return 1;
   1228   }
   1229   printf ("Test PASSED: %u sub-cases.\n",
   1230           case_num);
   1231   return 0;
   1232 }
   1233 
   1234 
   1235 #else  /* ! DAUTH_SUPPORT || ! MHD_SHA256_SUPPORT */
   1236 
   1237 
   1238 int
   1239 main (void)
   1240 {
   1241   fprintf (stderr,
   1242            "Digest Auth or SHA-256 support is not enabled in this MHD build, "
   1243            "the test is skipped.\n");
   1244   return 77;
   1245 }
   1246 
   1247 
   1248 #endif /* ! DAUTH_SUPPORT || ! MHD_SHA256_SUPPORT */