libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

auth_digest.c (110620B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2014-2025 Evgeny Grin (Karlson2k)
      5   Copyright (C) 2010, 2011, 2012, 2015, 2018 Christian Grothoff
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 
     40 /**
     41  * @file src/mhd2/auth_digest.c
     42  * @brief  The implementation of the Digest Authorization internal functions
     43  * @author Karlson2k (Evgeny Grin)
     44  * Based on the MHD v0.xx code by Amr Ali, Matthieu Speder, Christian Grothoff,
     45  * Dirk Brinkmeier and Evgeny Grin.
     46  */
     47 
     48 #include "mhd_sys_options.h"
     49 
     50 #include "mhd_digest_auth_data.h"
     51 
     52 #include "mhd_assert.h"
     53 #include "mhd_unreachable.h"
     54 #include "mhd_assume.h"
     55 
     56 #include <string.h>
     57 #include "sys_malloc.h"
     58 
     59 #include "mhd_str_macros.h"
     60 #include "mhd_bithelpers.h"
     61 #include "mhd_arr_num_elems.h"
     62 #include "mhd_cntnr_ptr.h"
     63 #include "mhd_limits.h"
     64 #include "mhd_rng.h"
     65 
     66 #include "mhd_str_types.h"
     67 #include "mhd_buffer.h"
     68 #include "mhd_daemon.h"
     69 #include "mhd_request.h"
     70 #include "mhd_connection.h"
     71 
     72 #ifdef MHD_SUPPORT_SHA512_256
     73 #  include "mhd_sha512_256.h"
     74 #endif /* MHD_SUPPORT_SHA512_256 */
     75 #ifdef MHD_SUPPORT_SHA256
     76 #  include "mhd_sha256.h"
     77 #endif
     78 #ifdef MHD_SUPPORT_MD5
     79 #  include "mhd_md5.h"
     80 #endif
     81 
     82 #include "mhd_str.h"
     83 #include "mhd_mono_clock.h"
     84 #include "mhd_atomic_counter.h"
     85 #include "mhd_locks.h"
     86 
     87 #include "request_auth_get.h"
     88 #include "daemon_funcs.h"
     89 #include "stream_funcs.h"
     90 #include "stream_process_request.h"
     91 
     92 #include "auth_digest.h"
     93 
     94 /*
     95  * The maximum size of the hash digest, in bytes
     96  */
     97 #if defined(MHD_SUPPORT_SHA512_256)
     98 #  define mhd_MAX_DIGEST mhd_SHA512_256_DIGEST_SIZE
     99 #elif defined(MHD_SUPPORT_SHA256)
    100 #  define mhd_MAX_DIGEST mhd_SHA256_DIGEST_SIZE
    101 #else
    102 #  define mhd_MAX_DIGEST mhd_MD5_DIGEST_SIZE
    103 #endif
    104 
    105 /**
    106  * MD5 algorithm identifier for Digest Auth headers
    107  */
    108 #define mhd_MD5_TOKEN "MD5"
    109 
    110 /**
    111  * SHA-256 algorithm identifier for Digest Auth headers
    112  */
    113 #define mhd_SHA256_TOKEN "SHA-256"
    114 
    115 /**
    116  * SHA-512/256 algorithm for Digest Auth headers.
    117  */
    118 #define mhd_SHA512_256_TOKEN "SHA-512-256"
    119 
    120 /**
    121  * The suffix token for "session" algorithms for Digest Auth headers.
    122  */
    123 #define mhd_SESS_TOKEN "-sess"
    124 
    125 /**
    126  * The "auth" token for QOP for Digest Auth headers.
    127  */
    128 #define mhd_TOKEN_AUTH "auth"
    129 
    130 /**
    131  * The "auth-int" token for QOP for Digest Auth headers.
    132  */
    133 #define mhd_TOKEN_AUTH_INT "auth-int"
    134 
    135 
    136 /**
    137  * The required prefix of parameter with the extended notation
    138  */
    139 #define mhd_DAUTH_EXT_PARAM_PREFIX "UTF-8'"
    140 
    141 /**
    142  * The minimal size of the prefix for parameter with the extended notation
    143  */
    144 #define mhd_DAUTH_EXT_PARAM_MIN_LEN \
    145         mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX "'")
    146 
    147 /**
    148  * The maximum supported size for Digest Auth parameters, like "realm",
    149  * "username" etc.
    150  * This limitation is used only for quoted parameters.
    151  * Parameters without quoted backslash character will be processed as long
    152  * as they fit connection memory pool (buffer) size.
    153  */
    154 #define mhd_AUTH_DIGEST_MAX_PARAM_SIZE (65535)
    155 
    156 /**
    157  * Parameter of request's Digest Authorization header
    158  */
    159 struct mhd_RqDAuthParam
    160 {
    161   /**
    162    * The string with length, NOT zero-terminated
    163    */
    164   struct MHD_StringNullable value;
    165   /**
    166    * True if string must be "unquoted" before processing.
    167    * This member is false if the string is used in DQUOTE marks, but no
    168    * backslash-escape is used in the string.
    169    */
    170   bool quoted;
    171 };
    172 
    173 /**
    174  * Client's Digest Authorization header parameters
    175  */
    176 struct mhd_AuthDigesReqParams
    177 {
    178   struct mhd_RqDAuthParam nonce;
    179   struct mhd_RqDAuthParam opaque;
    180   struct mhd_RqDAuthParam response;
    181   struct mhd_RqDAuthParam username;
    182   struct mhd_RqDAuthParam username_ext;
    183   struct mhd_RqDAuthParam realm;
    184   struct mhd_RqDAuthParam uri;
    185   /* The raw QOP value, used in the 'response' calculation */
    186   struct mhd_RqDAuthParam qop_raw;
    187   struct mhd_RqDAuthParam cnonce;
    188   struct mhd_RqDAuthParam nc;
    189 
    190   /* Decoded values are below */
    191   bool userhash; /* True if 'userhash' parameter has value 'true'. */
    192   enum MHD_DigestAuthAlgo algo;
    193   enum MHD_DigestAuthQOP qop;
    194 };
    195 
    196 /**
    197  * Digest context data
    198  */
    199 union DigestCtx
    200 {
    201 #ifdef MHD_SUPPORT_SHA512_256
    202   struct mhd_Sha512_256Ctx sha512_256_ctx;
    203 #endif /* MHD_SUPPORT_SHA512_256 */
    204 #ifdef MHD_SUPPORT_SHA256
    205   struct mhd_Sha256Ctx sha256_ctx;
    206 #endif /* MHD_SUPPORT_SHA256 */
    207 #ifdef MHD_SUPPORT_MD5
    208   struct mhd_Md5Ctx md5_ctx;
    209 #endif /* MHD_SUPPORT_MD5 */
    210 };
    211 
    212 mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
    213 
    214 /**
    215  * Generate simple hash.
    216  * Very limited avalanche effect. To be used mainly for the table slot choice.
    217  * @param data_size the size of the data to hash
    218  * @param data the data to hash
    219  * @return the hash value
    220  */
    221 static MHD_FN_PAR_NONNULL_ALL_
    222 MHD_FN_PAR_IN_SIZE_ (2, 1) uint_fast64_t
    223 simple_hash (size_t data_size,
    224              const uint8_t *restrict data)
    225 {
    226   static const uint_fast64_t c[] = { /* Some fractional parts of Euler's number */
    227     UINT64_C (0xCC64D3484C3475A1),
    228     UINT64_C (0xCF4DEBCB9ED801F2),
    229     UINT64_C (0x0C8737A803CF46AD),
    230     UINT64_C (0x294C9E0E0F9F14AB),
    231     UINT64_C (0xAD786D855D4EBB1A)
    232   };
    233   uint_fast64_t res;
    234   size_t i;
    235 
    236   res = UINT64_C (0x8316A8FE31A2228E); /* Some fractional part of Pi */
    237   i = 0;
    238   while (1)
    239   {
    240     uint_fast64_t a = 0;
    241 
    242     if (8 <= data_size)
    243       memcpy (&a, data, 8);
    244     else
    245       memcpy (&a, data, data_size);
    246     a ^= c[(i++) % mhd_ARR_NUM_ELEMS (c)];
    247     a = (uint_fast64_t)mhd_ROTR64 ((uint64_t)a, \
    248                                    (unsigned int)(res >> 58u));
    249     res ^= a;
    250     if (8 >= data_size)
    251       break;
    252     data_size -= 8;
    253     data += 8;
    254   }
    255   return res;
    256 }
    257 
    258 
    259 /**
    260  * Find index of the provided nonce in the nonces table
    261  * @param nonce the nonce to use
    262  * @param arr_size the size of the nonces table
    263  * @return the index
    264  */
    265 static MHD_FN_PAR_NONNULL_ALL_ size_t
    266 nonce_to_index (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
    267                 size_t arr_size)
    268 {
    269   uint_fast64_t hash;
    270   hash = simple_hash (mhd_AUTH_DIGEST_NONCE_BIN_SIZE,
    271                       nonce);
    272   if (arr_size == (arr_size & UINT32_C (0xFFFFFFFF)))
    273   { /* 'arr_size' <=32-bit */
    274     hash = (hash ^ (hash >> 32)) & UINT32_C (0xFFFFFFFF); /* Fold hash */
    275     if (arr_size == (arr_size & UINT16_C (0xFFFF)))
    276     { /* 'arr_size' <=16-bit */
    277       hash = (hash ^ (hash >> 16)) & UINT16_C (0xFFFF); /* Fold hash */
    278       if (arr_size == (arr_size & 0xFFu))
    279         hash = (hash ^ (hash >> 8)) & 0xFFu; /* 'arr_size' <=8-bit, fold hash */
    280     }
    281   }
    282   return ((size_t)hash) % arr_size;
    283 }
    284 
    285 
    286 mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
    287 
    288 
    289 /**
    290  * Generate a new nonce
    291  * @param d the daemon to use (must match @a c connection)
    292  * @param c the connection to generate nonce for
    293  * @param[out] out_buf the output buffer to pull full nonce, including
    294  *                     "expiration" tail
    295  * @param[out] expir the expiration mark, duplicated for convenience
    296  * @return 'true' if succeed,
    297  *         'false' if failed
    298  */
    299 static MHD_FN_PAR_NONNULL_ALL_
    300 MHD_FN_PAR_OUT_ (3)
    301 MHD_FN_PAR_OUT_ (4) bool
    302 gen_new_nonce (struct MHD_Daemon *restrict d,
    303                struct MHD_Connection *restrict c,
    304                uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
    305                uint_fast32_t *restrict expir)
    306 {
    307   uint_fast64_t expiration;
    308 
    309   mhd_assert (!mhd_D_HAS_MASTER (d));  /* only master daemon should be used */
    310   mhd_assert (d == c->daemon);
    311   mhd_assert (0 != d->auth_dg.cfg.nonce_tmout);
    312 
    313   expiration = mhd_monotonic_msec_counter ()
    314                + d->auth_dg.cfg.nonce_tmout * (uint_fast64_t)1000;
    315 
    316   if (!mhd_rng (mhd_AUTH_DIGEST_NONCE_BIN_SIZE,
    317                 out_buf))
    318   {
    319     /* Fallback to generating nonce from application-provided
    320        entropy. Note: this should fail if we do not have
    321        application-provided entropy. */
    322     size_t gen_num;
    323     union DigestCtx d_ctx;
    324 
    325     gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
    326 
    327 #if defined(MHD_SUPPORT_SHA512_256)
    328     mhd_SHA512_256_init (&(d_ctx.sha512_256_ctx));
    329     mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
    330                            d->auth_dg.entropy.size,
    331                            (const uint8_t *)d->auth_dg.entropy.data);
    332     mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
    333                            sizeof(gen_num),
    334                            (const uint8_t *)&gen_num);
    335     if (0 != c->sk.addr.size)
    336       mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
    337                              c->sk.addr.size,
    338                              (const uint8_t *)c->sk.addr.data);
    339     mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
    340                            sizeof(expiration),
    341                            (const uint8_t *)&expiration);
    342     mhd_SHA512_256_finish_deinit (&(d_ctx.sha512_256_ctx), \
    343                                   out_buf);
    344     if (mhd_SHA512_256_has_err (&(d_ctx.sha512_256_ctx)))
    345       return false;
    346 #elif defined(MHD_SUPPORT_SHA256)
    347     mhd_SHA256_init (&(d_ctx.sha256_ctx));
    348     mhd_SHA256_update (&(d_ctx.sha256_ctx),
    349                        d->auth_dg.entropy.size,
    350                        (const void *)d->auth_dg.entropy.data);
    351     mhd_SHA256_update (&(d_ctx.sha256_ctx),
    352                        sizeof(gen_num),
    353                        (const void *)&gen_num);
    354     if (0 != c->sk.addr.size)
    355       mhd_SHA256_update (&(d_ctx.sha256_ctx),
    356                          c->sk.addr.size,
    357                          (const void *)c->sk.addr.data);
    358     mhd_SHA256_update (&(d_ctx.sha256_ctx),
    359                        sizeof(expiration),
    360                        (const void *)&expiration);
    361     mhd_SHA256_finish_deinit (&(d_ctx.sha256_ctx), \
    362                               out_buf);
    363     if (mhd_SHA256_has_err (&(d_ctx.sha256_ctx)))
    364       return false;
    365 #else  /* MHD_SUPPORT_MD5 */
    366 #  ifndef MHD_SUPPORT_MD5
    367 #    error At least one hashing algorithm must be enabled
    368 #  endif
    369     mhd_MD5_init (&(d_ctx.md5_ctx));
    370     mhd_MD5_update (&(d_ctx.md5_ctx),
    371                     d->auth_dg.entropy.size,
    372                     (const void *)d->auth_dg.entropy.data);
    373     mhd_MD5_update (&(d_ctx.md5_ctx),
    374                     sizeof(gen_num),
    375                     (const void *)&gen_num);
    376     if (0 != c->sk.addr.size)
    377       mhd_MD5_update (&(d_ctx.md5_ctx),
    378                       c->sk.addr.size,
    379                       (const void *)c->sk.addr.data);
    380     mhd_MD5_update (&(d_ctx.md5_ctx),
    381                     sizeof(expiration),
    382                     (const void *)&expiration);
    383     mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
    384                            out_buf);
    385     if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
    386       return false;
    387 
    388     /* One more hash, for the second part */
    389     gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
    390 
    391     mhd_MD5_init (&(d_ctx.md5_ctx));
    392     mhd_MD5_update (&(d_ctx.md5_ctx),
    393                     d->auth_dg.entropy.size,
    394                     (const void *)d->auth_dg.entropy.data);
    395     mhd_MD5_update (&(d_ctx.md5_ctx),
    396                     sizeof(gen_num),
    397                     (const void *)&gen_num);
    398     if (0 != c->sk.addr.size)
    399       mhd_MD5_update (&(d_ctx.md5_ctx),
    400                       c->sk.addr.size,
    401                       (const void *)c->sk.addr.data);
    402     mhd_MD5_update (&(d_ctx.md5_ctx),
    403                     sizeof(expiration),
    404                     (const void *)&expiration);
    405     mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
    406                            out_buf + mhd_MD5_DIGEST_SIZE);
    407     if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
    408       return false;
    409 #endif /* MHD_SUPPORT_MD5 */
    410 
    411   }
    412 
    413   *expir = (uint_fast32_t)(expiration / 1000u);
    414   mhd_PUT_32BIT_LE_UNALIGN (out_buf + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE, \
    415                             (uint32_t)(*expir & UINT32_C (0xFFFFFFFF)));
    416 
    417   return true;
    418 }
    419 
    420 
    421 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    422 MHD_FN_PAR_OUT_ (2) bool
    423 mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c,
    424                                char out_buf[mhd_AUTH_DIGEST_NONCE_LEN])
    425 {
    426   static const int max_retries = 3;
    427   struct MHD_Daemon *restrict d = mhd_daemon_get_master_daemon (c->daemon);
    428   uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
    429   uint_fast32_t expir;
    430   bool nonce_generated;
    431   int i;
    432 
    433   mhd_assert (0 != d->auth_dg.cfg.nonces_num);
    434   mhd_assert (NULL != d->auth_dg.nonces);
    435 
    436   nonce_generated = false;
    437   for (i = 0; i < max_retries; ++i)
    438   {
    439     bool good_nonce;
    440     struct mhd_DaemonAuthDigestNonceData *nonce_slot;
    441     if (!gen_new_nonce (d,
    442                         c,
    443                         nonce_bin,
    444                         &expir))
    445       continue; /* Failed, re-try */
    446 
    447     nonce_generated = true;
    448     nonce_slot = d->auth_dg.nonces
    449                  + nonce_to_index (nonce_bin,
    450                                    d->auth_dg.cfg.nonces_num);
    451     if (!mhd_mutex_lock (&(d->auth_dg.nonces_lock)))
    452       return false; /* Failure exit point */
    453     /* Check whether the same nonce has been used before */
    454     good_nonce = (0 != memcmp (nonce_slot->nonce,
    455                                nonce_bin,
    456                                sizeof(nonce_slot->nonce)));
    457     if (good_nonce)
    458     {
    459       memcpy (nonce_slot->nonce,
    460               nonce_bin,
    461               sizeof(nonce_slot->nonce));
    462       nonce_slot->valid_time = expir;
    463       nonce_slot->max_recvd_nc = 0;
    464       nonce_slot->nmask = 0;
    465     }
    466     else
    467     {
    468       /* Check whether the same nonce has been used with different expiration
    469          time. */
    470       nonce_generated = (nonce_slot->valid_time == expir);
    471     }
    472     mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
    473     if (good_nonce)
    474       break;
    475   }
    476   if (!nonce_generated)
    477     return false; /* Failure exit point */
    478 
    479   /* Use the generated nonce even if it is duplicated.
    480      One of the clients will just get "nonce stale" response with
    481      the new nonce. */
    482   (void)mhd_bin_to_hex (nonce_bin,
    483                         sizeof(nonce_bin),
    484                         out_buf);
    485   return true; /* Success exit point */
    486 }
    487 
    488 
    489 /**
    490  * Get client's Digest Authorization algorithm type.
    491  * If no algorithm is specified by client, MD5 is assumed.
    492  * @param algo_param the Digest Authorization 'algorithm' parameter
    493  * @return the algorithm type
    494  */
    495 static enum MHD_DigestAuthAlgo
    496 get_rq_dauth_algo (const struct mhd_RqDAuthParam *const algo_param)
    497 {
    498   if (NULL == algo_param->value.cstr)
    499     return MHD_DIGEST_AUTH_ALGO_MD5; /* Assume MD5 by default */
    500 
    501   if (algo_param->quoted)
    502   {
    503     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    504                                                algo_param->value.len, \
    505                                                mhd_MD5_TOKEN))
    506       return MHD_DIGEST_AUTH_ALGO_MD5;
    507     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    508                                                algo_param->value.len, \
    509                                                mhd_SHA256_TOKEN))
    510       return MHD_DIGEST_AUTH_ALGO_SHA256;
    511     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    512                                                algo_param->value.len, \
    513                                                mhd_SHA512_256_TOKEN))
    514       return MHD_DIGEST_AUTH_ALGO_SHA512_256;
    515 
    516     /* Algorithms below are not supported by MHD for authentication */
    517 
    518     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    519                                                algo_param->value.len, \
    520                                                mhd_MD5_TOKEN mhd_SESS_TOKEN))
    521       return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
    522     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    523                                                algo_param->value.len, \
    524                                                mhd_SHA256_TOKEN \
    525                                                mhd_SESS_TOKEN))
    526       return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
    527     if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
    528                                                algo_param->value.len, \
    529                                                mhd_SHA512_256_TOKEN \
    530                                                mhd_SESS_TOKEN))
    531       return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
    532 
    533     /* No known algorithm has been detected */
    534     return MHD_DIGEST_AUTH_ALGO_INVALID;
    535   }
    536   /* The algorithm value is not quoted */
    537   if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN, \
    538                                    algo_param->value.cstr, \
    539                                    algo_param->value.len))
    540     return MHD_DIGEST_AUTH_ALGO_MD5;
    541   if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN, \
    542                                    algo_param->value.cstr, \
    543                                    algo_param->value.len))
    544     return MHD_DIGEST_AUTH_ALGO_SHA256;
    545   if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN, \
    546                                    algo_param->value.cstr, \
    547                                    algo_param->value.len))
    548     return MHD_DIGEST_AUTH_ALGO_SHA512_256;
    549 
    550   /* Algorithms below are not supported by MHD for authentication */
    551 
    552   if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN mhd_SESS_TOKEN, \
    553                                    algo_param->value.cstr, \
    554                                    algo_param->value.len))
    555     return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
    556   if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN mhd_SESS_TOKEN, \
    557                                    algo_param->value.cstr, \
    558                                    algo_param->value.len))
    559     return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
    560   if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN mhd_SESS_TOKEN, \
    561                                    algo_param->value.cstr, \
    562                                    algo_param->value.len))
    563     return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
    564 
    565   /* No known algorithm has been detected */
    566   return MHD_DIGEST_AUTH_ALGO_INVALID;
    567 }
    568 
    569 
    570 /**
    571  * Get QOP ('quality of protection') type.
    572  * @param qop_param the Digest Authorization 'QOP' parameter
    573  * @return detected QOP ('quality of protection') type.
    574  */
    575 static enum MHD_DigestAuthQOP
    576 get_rq_dauth_qop (const struct mhd_RqDAuthParam *const qop_param)
    577 {
    578   if (NULL == qop_param->value.cstr)
    579     return MHD_DIGEST_AUTH_QOP_NONE;
    580   if (qop_param->quoted)
    581   {
    582     if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
    583                                                qop_param->value.len, \
    584                                                mhd_TOKEN_AUTH))
    585       return MHD_DIGEST_AUTH_QOP_AUTH;
    586     if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
    587                                                qop_param->value.len, \
    588                                                mhd_TOKEN_AUTH_INT))
    589       return MHD_DIGEST_AUTH_QOP_AUTH_INT;
    590   }
    591   else
    592   {
    593     if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH, \
    594                                      qop_param->value.cstr, \
    595                                      qop_param->value.len))
    596       return MHD_DIGEST_AUTH_QOP_AUTH;
    597     if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH_INT, \
    598                                      qop_param->value.cstr, \
    599                                      qop_param->value.len))
    600       return MHD_DIGEST_AUTH_QOP_AUTH_INT;
    601   }
    602   /* No know QOP has been detected */
    603   return MHD_DIGEST_AUTH_QOP_INVALID;
    604 }
    605 
    606 
    607 /**
    608  * Parse request Authorization header parameters for Digest Authentication
    609  * @param val the header string, everything after "Digest " substring
    610  * @param[out] pdauth the pointer to the structure with Digest Authentication
    611  *               parameters
    612  * @return true if parameters has been successfully parsed,
    613  *         false if format of the @a str is invalid
    614  */
    615 static MHD_FN_PAR_NONNULL_ALL_
    616 MHD_FN_PAR_OUT_ (2) bool
    617 parse_dauth_params (const struct MHD_String *restrict val,
    618                     struct mhd_AuthDigesReqParams *restrict pdauth)
    619 {
    620   /* The tokens */
    621   static const struct MHD_String nonce_tk = mhd_MSTR_INIT ("nonce");
    622   static const struct MHD_String opaque_tk = mhd_MSTR_INIT ("opaque");
    623   static const struct MHD_String algorithm_tk = mhd_MSTR_INIT ("algorithm");
    624   static const struct MHD_String response_tk = mhd_MSTR_INIT ("response");
    625   static const struct MHD_String username_tk = mhd_MSTR_INIT ("username");
    626   static const struct MHD_String username_ext_tk = mhd_MSTR_INIT ("username*");
    627   static const struct MHD_String realm_tk = mhd_MSTR_INIT ("realm");
    628   static const struct MHD_String uri_tk = mhd_MSTR_INIT ("uri");
    629   static const struct MHD_String qop_tk = mhd_MSTR_INIT ("qop");
    630   static const struct MHD_String cnonce_tk = mhd_MSTR_INIT ("cnonce");
    631   static const struct MHD_String nc_tk = mhd_MSTR_INIT ("nc");
    632   static const struct MHD_String userhash_tk = mhd_MSTR_INIT ("userhash");
    633   /* The locally processed parameters */
    634   struct mhd_RqDAuthParam userhash = { {0, NULL}, false};
    635   struct mhd_RqDAuthParam algorithm = { {0, NULL}, false};
    636   /* Indexes */
    637   size_t i;
    638   size_t p;
    639   /* The list of the tokens.
    640      The order of the elements matches the next array. */
    641   static const struct MHD_String *const tk_names[] = {
    642     &nonce_tk,          /* 0 */
    643     &opaque_tk,         /* 1 */
    644     &algorithm_tk,      /* 2 */
    645     &response_tk,       /* 3 */
    646     &username_tk,       /* 4 */
    647     &username_ext_tk,   /* 5 */
    648     &realm_tk,          /* 6 */
    649     &uri_tk,            /* 7 */
    650     &qop_tk,            /* 8 */
    651     &cnonce_tk,         /* 9 */
    652     &nc_tk,             /* 10 */
    653     &userhash_tk        /* 11 */
    654   };
    655   /* The list of the parameters.
    656      The order of the elements matches the previous array. */
    657   struct mhd_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
    658 
    659   params[0] = &(pdauth->nonce);            /* 0 */
    660   params[1] = &(pdauth->opaque);           /* 1 */
    661   params[2] = &algorithm;                  /* 2 */
    662   params[3] = &(pdauth->response);         /* 3 */
    663   params[4] = &(pdauth->username);         /* 4 */
    664   params[5] = &(pdauth->username_ext);     /* 5 */
    665   params[6] = &(pdauth->realm);            /* 6 */
    666   params[7] = &(pdauth->uri);              /* 7 */
    667   params[8] = &(pdauth->qop_raw);          /* 8 */
    668   params[9] = &(pdauth->cnonce);           /* 9 */
    669   params[10] = &(pdauth->nc);              /* 10 */
    670   params[11] = &userhash;                  /* 11 */
    671 
    672   mhd_assert (mhd_ARR_NUM_ELEMS (tk_names) == \
    673               mhd_ARR_NUM_ELEMS (params));
    674   i = 0;
    675 
    676   mhd_assert (' ' != val->cstr[0]);
    677   mhd_assert ('\t' != val->cstr[0]);
    678 
    679   while (val->len > i)
    680   {
    681     size_t left;
    682     mhd_assert (' ' != val->cstr[i]);
    683     mhd_assert ('\t' != val->cstr[i]);
    684 
    685     left = val->len - i;
    686     if ('=' == val->cstr[i])
    687       return false; /* The equal sign is not allowed as the first character */
    688     for (p = 0; p < mhd_ARR_NUM_ELEMS (tk_names); ++p)
    689     {
    690       const struct MHD_String *const tk_name = tk_names[p];
    691       struct mhd_RqDAuthParam *const param = params[p];
    692       if ((tk_name->len <= left)
    693           && mhd_str_equal_caseless_bin_n (val->cstr + i, tk_name->cstr,
    694                                            tk_name->len)
    695           && ((tk_name->len == left)
    696               || ('=' == val->cstr[i + tk_name->len])
    697               || (' ' == val->cstr[i + tk_name->len])
    698               || ('\t' == val->cstr[i + tk_name->len])
    699               || (',' == val->cstr[i + tk_name->len])
    700               || (';' == val->cstr[i + tk_name->len])))
    701       {
    702         size_t value_start;
    703         size_t value_len;
    704         bool quoted; /* Only mark as "quoted" if backslash-escape used */
    705 
    706         if (tk_name->len == left)
    707           return false; /* No equal sign after parameter name, broken data */
    708 
    709         quoted = false;
    710         i += tk_name->len;
    711         /* Skip all whitespaces before '=' */
    712         while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
    713           i++;
    714         if ((i == val->len) || ('=' != val->cstr[i]))
    715           return false; /* No equal sign, broken data */
    716         i++;
    717         /* Skip all whitespaces after '=' */
    718         while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
    719           i++;
    720         if ((val->len > i) && ('"' == val->cstr[i]))
    721         { /* Value is in quotation marks */
    722           i++; /* Advance after the opening quote */
    723           value_start = i;
    724           while (val->len > i && '"' != val->cstr[i])
    725           {
    726             if ('\\' == val->cstr[i])
    727             {
    728               i++;
    729               quoted = true; /* Have escaped chars */
    730             }
    731             if (0 == val->cstr[i])
    732               return false; /* Binary zero in parameter value */
    733             i++;
    734           }
    735           if (val->len <= i)
    736             return false; /* No closing quote */
    737           mhd_assert ('"' == val->cstr[i]);
    738           value_len = i - value_start;
    739           i++; /* Advance after the closing quote */
    740         }
    741         else
    742         {
    743           value_start = i;
    744           while ((val->len > i) && (',' != val->cstr[i])
    745                  && (' ' != val->cstr[i]) && ('\t' != val->cstr[i])
    746                  && (';' != val->cstr[i]))
    747           {
    748             if (0 == val->cstr[i])
    749               return false;  /* Binary zero in parameter value */
    750             i++;
    751           }
    752           if (';' == val->cstr[i])
    753             return false;  /* Semicolon in parameter value */
    754           value_len = i - value_start;
    755         }
    756         /* Skip all whitespaces after parameter value */
    757         while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
    758           i++;
    759         if ((val->len > i) && (',' != val->cstr[i]))
    760           return false; /* Garbage after parameter value */
    761 
    762         /* Have valid parameter name and value */
    763         mhd_assert (!quoted || 0 != value_len);
    764         param->value.cstr = val->cstr + value_start;
    765         param->value.len = value_len;
    766         param->quoted = quoted;
    767 
    768         break; /* Found matching parameter name */
    769       }
    770     }
    771     if (p == mhd_ARR_NUM_ELEMS (tk_names))
    772     {
    773       /* No matching parameter name */
    774       while (val->len > i && ',' != val->cstr[i])
    775       {
    776         if ((0 == val->cstr[i]) || (';' == val->cstr[i]))
    777           return false; /* Not allowed characters */
    778         if ('"' == val->cstr[i])
    779         { /* Skip quoted part */
    780           i++; /* Advance after the opening quote */
    781           while (val->len > i && '"' != val->cstr[i])
    782           {
    783             if (0 == val->cstr[i])
    784               return false;  /* Binary zero is not allowed */
    785             if ('\\' == val->cstr[i])
    786               i++;           /* Skip escaped char */
    787             i++;
    788           }
    789           if (val->len <= i)
    790             return false; /* No closing quote */
    791           mhd_assert ('"' == val->cstr[i]);
    792         }
    793         i++;
    794       }
    795     }
    796     mhd_assert (val->len == i || ',' == val->cstr[i]);
    797     if (val->len > i)
    798       i++; /* Advance after ',' */
    799     /* Skip all whitespaces before next parameter name */
    800     while (i < val->len && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
    801       i++;
    802   }
    803 
    804   /* Postprocess values */
    805 
    806   if (NULL != userhash.value.cstr)
    807   {
    808     if (userhash.quoted)
    809       pdauth->userhash =
    810         mhd_str_equal_caseless_quoted_s_bin_n (userhash.value.cstr, \
    811                                                userhash.value.len, \
    812                                                "true");
    813     else
    814       pdauth->userhash =
    815         mhd_str_equal_caseless_n_st ("true", userhash.value.cstr, \
    816                                      userhash.value.len);
    817 
    818   }
    819   else
    820     pdauth->userhash = false;
    821 
    822   pdauth->algo = get_rq_dauth_algo (&algorithm);
    823   pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
    824 
    825   return true;
    826 }
    827 
    828 
    829 /**
    830  * Find and pre-parse request's Digest Authorisation parameters.
    831  *
    832  * Function returns result of pre-parsing of the request's "Authorization"
    833  * header or returns cached result if the header has been already pre-parsed for
    834  * the current request.
    835  * @param req the request to process
    836  * @return #MHD_SC_OK if succeed,
    837  *         #MHD_SC_AUTH_ABSENT if request has no Digest Authorisation,
    838  *         #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if not enough memory,
    839  *         #MHD_SC_REQ_AUTH_DATA_BROKEN if the header is broken.
    840  */
    841 static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
    842 get_rq_auth_digest_params (struct MHD_Request *restrict req)
    843 {
    844   struct MHD_String h_auth_value;
    845   struct mhd_AuthDigesReqParams *dauth;
    846 
    847   mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
    848               mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
    849   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
    850               mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
    851 
    852   if (NULL != req->auth.digest.rqp)
    853     return MHD_SC_OK;
    854 
    855   if (!mhd_request_get_auth_header_value (req,
    856                                           mhd_AUTH_HDR_DIGEST,
    857                                           &h_auth_value))
    858     return MHD_SC_AUTH_ABSENT;
    859 
    860   dauth =
    861     (struct mhd_AuthDigesReqParams *)
    862     mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, \
    863                                             struct MHD_Connection, \
    864                                             rq),
    865                              sizeof (struct mhd_AuthDigesReqParams));
    866 
    867   if (NULL == dauth)
    868     return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
    869 
    870   memset (dauth, 0, sizeof(struct mhd_AuthDigesReqParams));
    871   if (!parse_dauth_params (&h_auth_value,
    872                            dauth))
    873     return MHD_SC_REQ_AUTH_DATA_BROKEN;
    874 
    875   req->auth.digest.rqp = dauth;
    876 
    877   return MHD_SC_OK;
    878 }
    879 
    880 
    881 /**
    882  * Get username type used by the client.
    883  * This function does not check whether userhash can be decoded or
    884  * extended notation (if used) is valid.
    885  * @param params the Digest Authorization parameters
    886  * @return the type of username
    887  */
    888 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ enum MHD_DigestAuthUsernameType
    889 get_rq_uname_type (const struct mhd_AuthDigesReqParams *params)
    890 {
    891   if (NULL != params->username.value.cstr)
    892   {
    893     if (NULL == params->username_ext.value.cstr)
    894       return params->userhash ?
    895              MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH :
    896              MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
    897     else  /* Both 'username' and 'username*' are used */
    898       return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
    899   }
    900   else if (NULL != params->username_ext.value.cstr)
    901   {
    902     if (!params->username_ext.quoted && !params->userhash
    903         && (mhd_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len))
    904       return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
    905     else
    906       return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
    907   }
    908 
    909   return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING;
    910 }
    911 
    912 
    913 /**
    914  * Get total size required for 'username' and 'userhash_bin'
    915  * @param params the Digest Authorization parameters
    916  * @param uname_type the type of username
    917  * @return the total size required for 'username' and
    918  *         'userhash_bin' is userhash is used
    919  */
    920 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ size_t
    921 get_rq_unames_size (const struct mhd_AuthDigesReqParams *params,
    922                     enum MHD_DigestAuthUsernameType uname_type)
    923 {
    924   size_t s;
    925 
    926   mhd_assert (get_rq_uname_type (params) == uname_type);
    927   s = 0;
    928   if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
    929       || (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type))
    930   {
    931     s += params->username.value.len + 1; /* Add one byte for zero-termination */
    932     if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
    933       s += (params->username.value.len + 1) / 2;
    934   }
    935   else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
    936     s += params->username_ext.value.len
    937          - mhd_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */
    938   return s;
    939 }
    940 
    941 
    942 /**
    943  * Get unquoted version of Digest Authorization parameter.
    944  * This function automatically zero-teminate the result.
    945  * @param param the parameter to extract
    946  * @param[out] buf the output buffer, must have enough size to hold the result,
    947  *                 the recommended size is 'param->value.len + 1'
    948  * @return the size of the result, not including the terminating zero
    949  */
    950 static MHD_FN_PAR_NONNULL_ALL_
    951 MHD_FN_PAR_OUT_ (2) size_t
    952 get_rq_param_unquoted_copy_z (const struct mhd_RqDAuthParam *restrict param,
    953                               char *restrict buf)
    954 {
    955   size_t len;
    956   mhd_assert (NULL != param->value.cstr);
    957   if (!param->quoted)
    958   {
    959     memcpy (buf, param->value.cstr, param->value.len);
    960     buf[param->value.len] = 0;
    961     return param->value.len;
    962   }
    963 
    964   len = mhd_str_unquote (param->value.cstr, param->value.len, buf);
    965   mhd_assert (0 != len);
    966   mhd_assert (len < param->value.len);
    967   buf[len] = 0;
    968   return len;
    969 }
    970 
    971 
    972 /**
    973  * Get decoded version of username from extended notation.
    974  * This function automatically zero-teminate the result.
    975  * @param uname_ext the string of client's 'username*' parameter value
    976  * @param uname_ext_len the length of @a uname_ext in chars
    977  * @param[out] buf the output buffer to put decoded username value
    978  * @param buf_size the size of @a buf
    979  * @return the number of characters copied to the output buffer or
    980  *         -1 if wrong extended notation is used.
    981  */
    982 static MHD_FN_PAR_NONNULL_ALL_
    983 MHD_FN_PAR_IN_SIZE_ (1, 2)
    984 MHD_FN_PAR_OUT_SIZE_ (3, 4) ssize_t
    985 get_rq_extended_uname_copy_z (const char *restrict uname_ext,
    986                               size_t uname_ext_len,
    987                               char *restrict buf,
    988                               size_t buf_size)
    989 {
    990   size_t r;
    991   size_t w;
    992   if ((size_t)SSIZE_MAX < uname_ext_len)
    993     return -1; /* Too long input string */
    994 
    995   if (mhd_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len)
    996     return -1; /* Required prefix is missing */
    997 
    998   if (!mhd_str_equal_caseless_bin_n (
    999         uname_ext,
   1000         mhd_DAUTH_EXT_PARAM_PREFIX,
   1001         mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX)))
   1002     return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */
   1003 
   1004   r = mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX);
   1005   /* Skip language tag */
   1006   while (r < uname_ext_len && '\'' != uname_ext[r])
   1007   {
   1008     const char chr = uname_ext[r];
   1009     if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr)
   1010         || (';' == chr))
   1011       return -1; /* Wrong char in language tag */
   1012     r++;
   1013   }
   1014   if (r >= uname_ext_len)
   1015     return -1; /* The end of the language tag was not found */
   1016   r++; /* Advance to the next char */
   1017 
   1018   w = mhd_str_pct_decode_strict_n (uname_ext + r, uname_ext_len - r,
   1019                                    buf, buf_size);
   1020   if ((0 == w) && (0 != uname_ext_len - r))
   1021     return -1; /* Broken percent encoding */
   1022   buf[w] = 0; /* Zero terminate the result */
   1023   mhd_assert (SSIZE_MAX > w);
   1024   return (ssize_t)w;
   1025 }
   1026 
   1027 
   1028 /**
   1029  * Get copy of username used by the client.
   1030  * @param params the Digest Authorization parameters
   1031  * @param uname_type the type of username
   1032  * @param[out] uname_info the pointer to the structure to be filled
   1033  * @param buf the buffer to be used for usernames
   1034  * @param buf_size the size of the @a buf
   1035  * @return the size of the @a buf used by pointers in @a unames structure
   1036  */
   1037 static size_t
   1038 get_rq_uname (const struct mhd_AuthDigesReqParams *restrict params,
   1039               enum MHD_DigestAuthUsernameType uname_type,
   1040               struct MHD_AuthDigestInfo *restrict uname_info,
   1041               uint8_t *restrict buf,
   1042               size_t buf_size)
   1043 {
   1044   size_t buf_used;
   1045 
   1046   buf_used = 0;
   1047   mhd_assert (get_rq_uname_type (params) == uname_type);
   1048   mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type);
   1049   mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type);
   1050 
   1051   uname_info->username.cstr = NULL;
   1052   uname_info->username.len = 0;
   1053   uname_info->userhash_hex.cstr = NULL;
   1054   uname_info->userhash_hex.len = 0;
   1055   uname_info->userhash_bin = NULL;
   1056 
   1057   if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
   1058   {
   1059     // TODO: Avoid copying string if not quoted
   1060     uname_info->username.cstr = (char *)(buf + buf_used);
   1061     uname_info->username.len =
   1062       get_rq_param_unquoted_copy_z (&params->username,
   1063                                     (char *)(buf + buf_used));
   1064     buf_used += uname_info->username.len + 1;
   1065     uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
   1066   }
   1067   else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
   1068   {
   1069     size_t res;
   1070 
   1071     uname_info->userhash_hex.cstr = (char *)(buf + buf_used);
   1072     uname_info->userhash_hex.len =
   1073       get_rq_param_unquoted_copy_z (&params->username,
   1074                                     (char *)(buf + buf_used));
   1075     buf_used += uname_info->userhash_hex.len + 1;
   1076     uname_info->userhash_bin = (uint8_t *)(buf + buf_used);
   1077     res = mhd_hex_to_bin (uname_info->userhash_hex.cstr,
   1078                           uname_info->userhash_hex.len,
   1079                           (uint8_t *)(buf + buf_used));
   1080     if (res != uname_info->userhash_hex.len / 2)
   1081     {
   1082       uname_info->userhash_bin = NULL;
   1083       uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
   1084     }
   1085     else
   1086     {
   1087       /* Avoid pointers outside allocated region when the size is zero */
   1088       if (0 == res)
   1089         uname_info->userhash_bin = (const uint8_t *)uname_info->username.cstr;
   1090       uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH;
   1091       buf_used += res;
   1092     }
   1093   }
   1094   else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
   1095   {
   1096     ssize_t res;
   1097     res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
   1098                                         params->username_ext.value.len,
   1099                                         (char *)(buf + buf_used),
   1100                                         buf_size - buf_used);
   1101     if (0 > res)
   1102       uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
   1103     else
   1104     {
   1105       uname_info->username.cstr = (char *)(buf + buf_used);
   1106       uname_info->username.len = (size_t)res;
   1107       uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
   1108       buf_used += uname_info->username.len + 1;
   1109     }
   1110   }
   1111   else
   1112   {
   1113     mhd_assert (0);
   1114     uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
   1115   }
   1116   mhd_assert (buf_size >= buf_used);
   1117   return buf_used;
   1118 }
   1119 
   1120 
   1121 /**
   1122  * Result of request's Digest Authorization 'nc' value extraction
   1123  */
   1124 enum MHD_FIXED_ENUM_ mhd_GetRqNCResult
   1125 {
   1126   mhd_GET_RQ_NC_NONE = MHD_DIGEST_AUTH_NC_NONE,    /**< No 'nc' value */
   1127   mhd_GET_RQ_NC_VALID = MHD_DIGEST_AUTH_NC_NUMBER, /**< Readable 'nc' value */
   1128   mhd_GET_RQ_NC_TOO_LONG = MHD_DIGEST_AUTH_NC_TOO_LONG, /**< The 'nc' value is too long */
   1129   mhd_GET_RQ_NC_TOO_LARGE = MHD_DIGEST_AUTH_NC_TOO_LARGE, /**< The 'nc' value is too big to fit uint32_t */
   1130   mhd_GET_RQ_NC_BROKEN = 0                         /**< The 'nc' value is not a number */
   1131 };
   1132 
   1133 
   1134 /**
   1135  * Get 'nc' value from request's Authorization header
   1136  * @param params the request digest authentication
   1137  * @param[out] nc the pointer to put nc value to
   1138  * @return enum value indicating the result
   1139  */
   1140 static enum mhd_GetRqNCResult
   1141 get_rq_nc (const struct mhd_AuthDigesReqParams *params,
   1142            uint_fast32_t *nc)
   1143 {
   1144   const struct mhd_RqDAuthParam *const nc_param =
   1145     &params->nc;
   1146   char unq[16];
   1147   const char *val;
   1148   size_t val_len;
   1149   size_t res;
   1150   uint64_t nc_val;
   1151 
   1152   if (NULL == nc_param->value.cstr)
   1153     return mhd_GET_RQ_NC_NONE;
   1154 
   1155   if (0 == nc_param->value.len)
   1156     return mhd_GET_RQ_NC_BROKEN;
   1157 
   1158   if (!nc_param->quoted)
   1159   {
   1160     val = nc_param->value.cstr;
   1161     val_len = nc_param->value.len;
   1162   }
   1163   else
   1164   {
   1165     /* Actually no backslashes must be used in 'nc' */
   1166     if (sizeof(unq) < params->nc.value.len)
   1167       return mhd_GET_RQ_NC_TOO_LONG;
   1168     val_len = mhd_str_unquote (nc_param->value.cstr, nc_param->value.len, unq);
   1169     if (0 == val_len)
   1170       return mhd_GET_RQ_NC_BROKEN;
   1171     val = unq;
   1172   }
   1173 
   1174   res = mhd_strx_to_uint64_n (val,
   1175                               val_len,
   1176                               &nc_val);
   1177   if (0 == res)
   1178   {
   1179     const char f = val[0];
   1180     if ((('9' >= f) && ('0' <= f))
   1181         || (('F' >= f) && ('A' <= f))
   1182         || (('a' <= f) && ('f' >= f)))
   1183       return mhd_GET_RQ_NC_TOO_LARGE;
   1184     else
   1185       return mhd_GET_RQ_NC_BROKEN;
   1186   }
   1187   if (val_len != res)
   1188     return mhd_GET_RQ_NC_BROKEN;
   1189   if (nc_val != (nc_val & UINT64_C (0xFFFFFFFF)))
   1190     return mhd_GET_RQ_NC_TOO_LARGE;
   1191   *nc = (uint_fast32_t)nc_val;
   1192   return mhd_GET_RQ_NC_VALID;
   1193 }
   1194 
   1195 
   1196 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1197 enum MHD_StatusCode
   1198 find_and_parse_auth_digest_info (struct MHD_Request *restrict req)
   1199 {
   1200   enum MHD_StatusCode res;
   1201   struct MHD_AuthDigestInfo *info;
   1202   enum MHD_DigestAuthUsernameType uname_type;
   1203   size_t unif_buf_size;
   1204   uint8_t *unif_buf_ptr;
   1205   size_t unif_buf_used;
   1206   enum mhd_GetRqNCResult nc_res;
   1207 
   1208   mhd_assert (NULL == req->auth.digest.info);
   1209 
   1210   res = get_rq_auth_digest_params (req);
   1211   if (MHD_SC_OK != res)
   1212     return res;
   1213 
   1214   unif_buf_size = 0;
   1215 
   1216   uname_type = get_rq_uname_type (req->auth.digest.rqp);
   1217 
   1218   unif_buf_size += get_rq_unames_size (req->auth.digest.rqp,
   1219                                        uname_type);
   1220 
   1221   if (NULL != req->auth.digest.rqp->opaque.value.cstr)
   1222     unif_buf_size += req->auth.digest.rqp->opaque.value.len + 1;  /* Add one for zero-termination */
   1223   if (NULL != req->auth.digest.rqp->realm.value.cstr)
   1224     unif_buf_size += req->auth.digest.rqp->realm.value.len + 1;   /* Add one for zero-termination */
   1225   info =
   1226     (struct MHD_AuthDigestInfo *)
   1227     mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq),
   1228                              (sizeof(struct MHD_AuthDigestInfo))
   1229                              + unif_buf_size);
   1230   if (NULL == info)
   1231     return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
   1232 
   1233   memset (info,
   1234           0,
   1235           (sizeof(struct MHD_AuthDigestInfo)) + unif_buf_size);
   1236 #ifndef HAVE_NULL_PTR_ALL_ZEROS
   1237   info->username.cstr = NULL;
   1238   info->userhash_hex.cstr = NULL;
   1239   info->userhash_bin = NULL;
   1240   info->opaque.cstr = NULL;
   1241   info->realm.cstr = NULL;
   1242 #endif
   1243 
   1244   unif_buf_ptr = (uint8_t *)(info + 1);
   1245   unif_buf_used = 0;
   1246 
   1247   info->algo = req->auth.digest.rqp->algo;
   1248 
   1249   if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type)
   1250       && (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type))
   1251     unif_buf_used +=
   1252       get_rq_uname (req->auth.digest.rqp, uname_type, info,
   1253                     unif_buf_ptr + unif_buf_used,
   1254                     unif_buf_size - unif_buf_used);
   1255   else
   1256     info->uname_type = uname_type;
   1257 
   1258   if (NULL != req->auth.digest.rqp->opaque.value.cstr)
   1259   {
   1260     info->opaque.cstr = (char *)(unif_buf_ptr + unif_buf_used);
   1261     info->opaque.len =
   1262       get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->opaque),
   1263                                     (char *)(unif_buf_ptr + unif_buf_used));
   1264     unif_buf_used += info->opaque.len + 1;
   1265   }
   1266   if (NULL != req->auth.digest.rqp->realm.value.cstr)
   1267   {
   1268     info->realm.cstr = (char *)(unif_buf_ptr + unif_buf_used);
   1269     info->realm.len =
   1270       get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->realm),
   1271                                     (char *)(unif_buf_ptr + unif_buf_used));
   1272     unif_buf_used += info->realm.len + 1;
   1273   }
   1274 
   1275   mhd_assert (unif_buf_size >= unif_buf_used);
   1276 
   1277   info->qop = req->auth.digest.rqp->qop;
   1278 
   1279   if (NULL != req->auth.digest.rqp->cnonce.value.cstr)
   1280     info->cnonce_len = req->auth.digest.rqp->cnonce.value.len;
   1281   else
   1282     info->cnonce_len = 0;
   1283 
   1284   nc_res = get_rq_nc (req->auth.digest.rqp, &info->nc);
   1285   if (mhd_GET_RQ_NC_VALID == nc_res)
   1286   {
   1287     if (0 == info->nc)
   1288       info->nc_type = MHD_DIGEST_AUTH_NC_ZERO;
   1289     else
   1290       info->nc_type = MHD_DIGEST_AUTH_NC_NUMBER;
   1291   }
   1292   else
   1293   {
   1294     info->nc = 0;
   1295     if (mhd_GET_RQ_NC_BROKEN == nc_res)
   1296       info->nc_type = MHD_DIGEST_AUTH_NC_NONE;
   1297     else
   1298       info->nc_type = (enum MHD_DigestAuthNC)nc_res;
   1299   }
   1300 
   1301   req->auth.digest.info = info;
   1302 
   1303   mhd_assert (uname_type == info->uname_type);
   1304 
   1305   if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type)
   1306       || (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type)
   1307       || ((mhd_GET_RQ_NC_BROKEN == nc_res)))
   1308     return MHD_SC_REQ_AUTH_DATA_BROKEN;
   1309 
   1310   return MHD_SC_OK;
   1311 }
   1312 
   1313 
   1314 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1315 MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
   1316 mhd_request_get_auth_digest_info (
   1317   struct MHD_Request *restrict req,
   1318   const struct MHD_AuthDigestInfo **restrict v_auth_digest_info)
   1319 {
   1320   mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
   1321               mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
   1322   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
   1323               mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
   1324 
   1325   if (MHD_SC_OK != req->auth.digest.parse_result)
   1326     return req->auth.digest.parse_result;
   1327 
   1328   if (NULL == req->auth.digest.info)
   1329     req->auth.digest.parse_result = find_and_parse_auth_digest_info (req);
   1330 
   1331   if (MHD_SC_OK != req->auth.digest.parse_result)
   1332     return req->auth.digest.parse_result; /* Failure exit point */
   1333 
   1334   mhd_assert (NULL != req->auth.digest.info);
   1335   *v_auth_digest_info = req->auth.digest.info;
   1336 
   1337   return MHD_SC_OK; /* Success exit point */
   1338 }
   1339 
   1340 
   1341 /**
   1342  * Get base hash calculation algorithm from #MHD_DigestAuthAlgo value.
   1343  * @param algo the MHD_DigestAuthAlgo value
   1344  * @return the base hash calculation algorithm
   1345  */
   1346 mhd_static_inline enum MHD_DigestBaseAlgo
   1347 get_base_digest_algo (enum MHD_DigestAuthAlgo algo)
   1348 {
   1349   unsigned int base_algo;
   1350 
   1351   base_algo =
   1352     ((unsigned int)algo)
   1353     & ~((unsigned int)
   1354         (MHD_DIGEST_AUTH_ALGO_NON_SESSION
   1355          | MHD_DIGEST_AUTH_ALGO_SESSION));
   1356   return (enum MHD_DigestBaseAlgo)base_algo;
   1357 }
   1358 
   1359 
   1360 /**
   1361  * Get digest size in bytes for specified algorithm.
   1362  *
   1363  * Internal inline version.
   1364  * @param algo the algorithm to check
   1365  * @return the size of the digest (in bytes) or zero if the input value is not
   1366  *         supported/valid
   1367  */
   1368 mhd_static_inline size_t
   1369 digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
   1370 {
   1371 #ifdef MHD_SUPPORT_MD5
   1372   mhd_assert (MHD_MD5_DIGEST_SIZE == mhd_MD5_DIGEST_SIZE);
   1373 #endif /* MHD_SUPPORT_MD5 */
   1374 #ifdef MHD_SUPPORT_SHA256
   1375   mhd_assert (MHD_SHA256_DIGEST_SIZE == mhd_SHA256_DIGEST_SIZE);
   1376 #endif /* MHD_SUPPORT_SHA256 */
   1377 #ifdef MHD_SUPPORT_SHA512_256
   1378   mhd_assert (MHD_SHA512_256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
   1379 #  ifdef MHD_SUPPORT_SHA256
   1380   mhd_assert (mhd_SHA256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
   1381 #  endif /* MHD_SUPPORT_SHA256 */
   1382 #endif /* MHD_SUPPORT_SHA512_256 */
   1383   /* Only one algorithm must be specified */
   1384   mhd_assert (1 == \
   1385               (((0 != (((unsigned int)algo) \
   1386                        & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)   \
   1387                + ((0 != (((unsigned int)algo) \
   1388                          & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)   \
   1389                + ((0 != (((unsigned int)algo) \
   1390                          & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)));
   1391 #ifdef MHD_SUPPORT_MD5
   1392   if (0 != (((unsigned int)algo)
   1393             & ((unsigned int)MHD_DIGEST_BASE_ALGO_MD5)))
   1394     return MHD_MD5_DIGEST_SIZE;
   1395   else
   1396 #endif /* MHD_SUPPORT_MD5 */
   1397 #if defined(MHD_SUPPORT_SHA256) && defined(MHD_SUPPORT_SHA512_256)
   1398   if (0 != (((unsigned int)algo)
   1399             & (((unsigned int)MHD_DIGEST_BASE_ALGO_SHA256)
   1400                | ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA512_256))))
   1401     return MHD_SHA256_DIGEST_SIZE; /* The same as mhd_SHA512_256_DIGEST_SIZE */
   1402   else
   1403 #elif defined(MHD_SUPPORT_SHA256)
   1404   if (0 != (((unsigned int)algo)
   1405             & ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA256)))
   1406     return MHD_SHA256_DIGEST_SIZE;
   1407   else
   1408 #elif defined(MHD_SUPPORT_SHA512_256)
   1409   if (0 != (((unsigned int)algo)
   1410             & ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA512_256)))
   1411     return MHD_SHA512_256_DIGEST_SIZE;
   1412   else
   1413 #endif /* MHD_SUPPORT_SHA512_256 */
   1414     (void)0;  /* Unsupported algorithm */
   1415 
   1416   return 0; /* Wrong input or unsupported algorithm */
   1417 }
   1418 
   1419 
   1420 /**
   1421  * Get digest size for specified algorithm.
   1422  *
   1423  * The size of the digest specifies the size of the userhash, userdigest
   1424  * and other parameters which size depends on used hash algorithm.
   1425  * @param algo the algorithm to check
   1426  * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
   1427  *         #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
   1428  *         or zero if the input value is not supported or not valid
   1429  * @sa #MHD_digest_auth_calc_userdigest()
   1430  * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
   1431  * @note Available since #MHD_VERSION 0x00097701
   1432  * @ingroup authentication
   1433  */
   1434 MHD_EXTERN_ MHD_FN_CONST_ size_t
   1435 MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
   1436 {
   1437   return digest_get_hash_size (algo);
   1438 }
   1439 
   1440 
   1441 /**
   1442  * The digest calculation structure.
   1443  */
   1444 struct DigestAlgorithm
   1445 {
   1446   /**
   1447    * A context for the digest algorithm, already initialized to be
   1448    * useful for @e init, @e update and @e digest.
   1449    */
   1450   union DigestCtx ctx;
   1451 
   1452   /**
   1453    * The hash calculation algorithm.
   1454    */
   1455   enum MHD_DigestBaseAlgo algo;
   1456 
   1457   /**
   1458    * Buffer for hex-print of the final digest.
   1459    */
   1460 #ifndef NDEBUG
   1461   bool uninitialised; /**< The structure has been not set-up */
   1462   bool algo_selected; /**< The algorithm has been selected */
   1463   bool ready_for_hashing; /**< The structure is ready to hash data */
   1464   bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */
   1465 #endif /* NDEBUG */
   1466 };
   1467 
   1468 
   1469 /**
   1470  * Return the size of the digest.
   1471  * @param da the digest calculation structure to identify
   1472  * @return the size of the digest.
   1473  */
   1474 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ unsigned int
   1475 digest_get_size (struct DigestAlgorithm *da)
   1476 {
   1477   mhd_assert (!da->uninitialised);
   1478   mhd_assert (da->algo_selected);
   1479 #ifdef MHD_SUPPORT_MD5
   1480   if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
   1481     return mhd_MD5_DIGEST_SIZE;
   1482 #endif /* MHD_SUPPORT_MD5 */
   1483 #ifdef MHD_SUPPORT_SHA256
   1484   if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
   1485     return mhd_SHA256_DIGEST_SIZE;
   1486 #endif /* MHD_SUPPORT_SHA256 */
   1487 #ifdef MHD_SUPPORT_SHA512_256
   1488   if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
   1489     return mhd_SHA512_256_DIGEST_SIZE;
   1490 #endif /* MHD_SUPPORT_SHA512_256 */
   1491   mhd_UNREACHABLE ();
   1492   return 0;
   1493 }
   1494 
   1495 
   1496 #if defined(mhd_MD5_HAS_DEINIT) \
   1497   || defined(mhd_SHA256_HAS_DEINIT) \
   1498   || defined(mhd_SHA512_256_HAS_DEINIT)
   1499 /**
   1500  * Indicates presence of digest_deinit() function
   1501  */
   1502 #  define mhd_DIGEST_HAS_DEINIT 1
   1503 #endif /* mhd_MD5_HAS_DEINIT || mhd_SHA256_HAS_DEINIT */
   1504 
   1505 #ifdef mhd_DIGEST_HAS_DEINIT
   1506 /**
   1507  * Zero-initialise digest calculation structure.
   1508  *
   1509  * This initialisation is enough to safely call #digest_deinit() only.
   1510  * To make any real digest calculation, #digest_setup_and_init() must be called.
   1511  * @param da the digest calculation
   1512  */
   1513 mhd_static_inline void
   1514 digest_setup_zero (struct DigestAlgorithm *da)
   1515 {
   1516 #  ifndef NDEBUG
   1517   da->uninitialised = false;
   1518   da->algo_selected = false;
   1519   da->ready_for_hashing = false;
   1520   da->hashing = false;
   1521 #  endif /* _DEBUG */
   1522   da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
   1523 }
   1524 
   1525 
   1526 /**
   1527  * De-initialise digest calculation structure.
   1528  *
   1529  * This function must be called if #digest_setup_and_init() was called for
   1530  * @a da.
   1531  * This function must not be called if @a da was not initialised by
   1532  * #digest_setup_and_init() or by #digest_setup_zero().
   1533  * @param da the digest calculation
   1534  */
   1535 mhd_static_inline void
   1536 digest_deinit (struct DigestAlgorithm *da)
   1537 {
   1538   mhd_assert (!da->uninitialised);
   1539 #  ifdef mhd_MD5_HAS_DEINIT
   1540   if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
   1541     mhd_MD5_deinit (&(da->ctx.md5_ctx));
   1542   else
   1543 #  endif /* mhd_MD5_HAS_DEINIT */
   1544 #  ifdef mhd_SHA256_HAS_DEINIT
   1545   if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
   1546     mhd_SHA256_deinit (&(da->ctx.sha256_ctx));
   1547   else
   1548 #  endif /* mhd_SHA256_HAS_DEINIT */
   1549 #  ifdef mhd_SHA512_256_HAS_DEINIT
   1550   if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
   1551     mhd_SHA512_256_deinit (&(da->ctx.sha512_256_ctx));
   1552   else
   1553 #  endif /* mhd_SHA512_256_HAS_DEINIT */
   1554   (void)0;
   1555   digest_setup_zero (da);
   1556 }
   1557 
   1558 
   1559 #else  /* ! mhd_DIGEST_HAS_DEINIT */
   1560 #  define digest_setup_zero(da) ((void) 0)
   1561 #  define digest_deinit(da) ((void) 0)
   1562 #endif /* ! mhd_DIGEST_HAS_DEINIT */
   1563 
   1564 
   1565 /**
   1566  * Set-up the digest calculation structure and initialise with initial values.
   1567  *
   1568  * If @a da was successfully initialised, #digest_deinit() must be called
   1569  * after finishing using of the @a da.
   1570  *
   1571  * This function must not be called more than once for any @a da.
   1572  *
   1573  * @param da the structure to set-up
   1574  * @param algo the algorithm to use for digest calculation
   1575  * @return boolean 'true' if successfully set-up,
   1576  *         false otherwise.
   1577  */
   1578 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ bool
   1579 digest_init_one_time (struct DigestAlgorithm *da,
   1580                       enum MHD_DigestBaseAlgo algo)
   1581 {
   1582 #ifndef NDEBUG
   1583   da->uninitialised = false;
   1584   da->algo_selected = false;
   1585   da->ready_for_hashing = false;
   1586   da->hashing = false;
   1587 #endif /* _DEBUG */
   1588   switch (algo)
   1589   {
   1590   case MHD_DIGEST_BASE_ALGO_MD5:
   1591 #ifdef MHD_SUPPORT_MD5
   1592     da->algo = MHD_DIGEST_BASE_ALGO_MD5;
   1593 #  ifndef NDEBUG
   1594     da->algo_selected = true;
   1595 #  endif
   1596     mhd_MD5_init (&(da->ctx.md5_ctx));
   1597 #  ifndef NDEBUG
   1598     da->ready_for_hashing = true;
   1599 #  endif
   1600     return true;
   1601 #endif /* MHD_SUPPORT_MD5 */
   1602     break;
   1603 
   1604   case MHD_DIGEST_BASE_ALGO_SHA256:
   1605 #ifdef MHD_SUPPORT_SHA256
   1606     da->algo = MHD_DIGEST_BASE_ALGO_SHA256;
   1607 #  ifndef NDEBUG
   1608     da->algo_selected = true;
   1609 #  endif
   1610     mhd_SHA256_init (&(da->ctx.sha256_ctx));
   1611 #  ifndef NDEBUG
   1612     da->ready_for_hashing = true;
   1613 #  endif
   1614     return true;
   1615 #endif /* MHD_SUPPORT_SHA256 */
   1616     break;
   1617 
   1618   case MHD_DIGEST_BASE_ALGO_SHA512_256:
   1619 #ifdef MHD_SUPPORT_SHA512_256
   1620     da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256;
   1621 #  ifndef NDEBUG
   1622     da->algo_selected = true;
   1623 #  endif
   1624     mhd_SHA512_256_init (&(da->ctx.sha512_256_ctx));
   1625 #  ifndef NDEBUG
   1626     da->ready_for_hashing = true;
   1627 #  endif
   1628     return true;
   1629 #endif /* MHD_SUPPORT_SHA512_256 */
   1630     break;
   1631 
   1632   case MHD_DIGEST_BASE_ALGO_INVALID:
   1633   default:
   1634     mhd_UNREACHABLE ();
   1635     break;
   1636   }
   1637   da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
   1638   return false; /* Unsupported or bad algorithm */
   1639 }
   1640 
   1641 
   1642 /**
   1643  * Hash more data for digest calculation.
   1644  * @param da the digest calculation
   1645  * @param size the size of the @a data in bytes
   1646  * @param data the data to process
   1647  */
   1648 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1649 MHD_FN_PAR_IN_SIZE_ (3, 2) void
   1650 digest_update (struct DigestAlgorithm *restrict da,
   1651                size_t size,
   1652                const void *restrict data)
   1653 {
   1654   mhd_assert (!da->uninitialised);
   1655   mhd_assert (da->algo_selected);
   1656   mhd_assert (da->ready_for_hashing);
   1657   switch (da->algo)
   1658   {
   1659   case MHD_DIGEST_BASE_ALGO_MD5:
   1660 #ifdef MHD_SUPPORT_MD5
   1661     mhd_MD5_update (&da->ctx.md5_ctx,
   1662                     size,
   1663                     (const uint8_t *)data);
   1664 #else
   1665     mhd_UNREACHABLE ();
   1666 #endif
   1667     break;
   1668   case MHD_DIGEST_BASE_ALGO_SHA256:
   1669 #ifdef MHD_SUPPORT_SHA256
   1670     mhd_SHA256_update (&da->ctx.sha256_ctx,
   1671                        size,
   1672                        (const uint8_t *)data);
   1673 #else
   1674     mhd_UNREACHABLE ();
   1675 #endif
   1676     break;
   1677   case MHD_DIGEST_BASE_ALGO_SHA512_256:
   1678 #ifdef MHD_SUPPORT_SHA512_256
   1679     mhd_SHA512_256_update (&da->ctx.sha512_256_ctx,
   1680                            size,
   1681                            (const uint8_t *)data);
   1682 #else
   1683     mhd_UNREACHABLE ();
   1684 #endif
   1685     break;
   1686   case MHD_DIGEST_BASE_ALGO_INVALID:
   1687   default:
   1688     mhd_UNREACHABLE ();
   1689     break;
   1690   }
   1691 #ifndef NDEBUG
   1692   da->hashing = true;
   1693 #endif
   1694 }
   1695 
   1696 
   1697 /**
   1698  * Feed digest calculation with more data from string.
   1699  * @param da the digest calculation
   1700  * @param str the zero-terminated string to process
   1701  */
   1702 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1703 MHD_FN_PAR_CSTR_ (2) void
   1704 digest_update_str (struct DigestAlgorithm *restrict da,
   1705                    const char *restrict str)
   1706 {
   1707   digest_update (da,
   1708                  strlen (str),
   1709                  (const uint8_t *)str);
   1710 }
   1711 
   1712 
   1713 /**
   1714  * Feed digest calculation with more data from string.
   1715  * @param da the digest calculation
   1716  * @param buf the sized buffer with the data
   1717  */
   1718 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1719 MHD_FN_PAR_CSTR_ (2) void
   1720 digest_update_cbuf (struct DigestAlgorithm *restrict da,
   1721                     const struct mhd_BufferConst *restrict buf)
   1722 {
   1723   digest_update (da,
   1724                  buf->size,
   1725                  (const uint8_t *)buf->data);
   1726 }
   1727 
   1728 
   1729 /**
   1730  * Feed digest calculation with more data from string.
   1731  * @param da the digest calculation
   1732  * @param buf the sized buffer with the data
   1733  */
   1734 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1735 MHD_FN_PAR_CSTR_ (2) void
   1736 digest_update_buf (struct DigestAlgorithm *restrict da,
   1737                    const struct mhd_Buffer *restrict buf)
   1738 {
   1739   digest_update (da,
   1740                  buf->size,
   1741                  (const uint8_t *)buf->data);
   1742 }
   1743 
   1744 
   1745 /**
   1746  * Feed digest calculation with single colon ':' character.
   1747  * @param da the digest calculation
   1748  */
   1749 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void
   1750 digest_update_with_colon (struct DigestAlgorithm *da)
   1751 {
   1752   static const uint8_t colon = (uint8_t)':';
   1753   digest_update (da,
   1754                  1,
   1755                  &colon);
   1756 }
   1757 
   1758 
   1759 /**
   1760  * Finally calculate hash (the digest).
   1761  * @param da the digest calculation
   1762  * @param[out] digest the pointer to the buffer to put calculated digest,
   1763  *                    must be at least digest_get_size(da) bytes large
   1764  */
   1765 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1766 MHD_FN_PAR_OUT_ (2) void
   1767 digest_calc_hash (struct DigestAlgorithm *da,
   1768                   uint8_t *digest)
   1769 {
   1770   mhd_assert (!da->uninitialised);
   1771   mhd_assert (da->algo_selected);
   1772   mhd_assert (da->ready_for_hashing);
   1773   switch (da->algo)
   1774   {
   1775   case MHD_DIGEST_BASE_ALGO_MD5:
   1776 #ifdef MHD_SUPPORT_MD5
   1777     mhd_MD5_finish (&da->ctx.md5_ctx, digest);
   1778 #  ifndef NDEBUG
   1779     da->ready_for_hashing = false;
   1780 #  endif /* _DEBUG */
   1781 #else  /* ! MHD_SUPPORT_MD5 */
   1782     mhd_UNREACHABLE ();
   1783 #endif /* ! MHD_SUPPORT_MD5 */
   1784     break;
   1785 
   1786   case MHD_DIGEST_BASE_ALGO_SHA256:
   1787 #ifdef MHD_SUPPORT_SHA256
   1788     mhd_SHA256_finish (&da->ctx.sha256_ctx, digest);
   1789 #  ifndef NDEBUG
   1790     da->ready_for_hashing = false;
   1791 #  endif /* _DEBUG */
   1792 #else  /* ! MHD_SUPPORT_SHA256 */
   1793     mhd_UNREACHABLE ();
   1794 #endif /* ! MHD_SUPPORT_SHA256 */
   1795     break;
   1796 
   1797   case MHD_DIGEST_BASE_ALGO_SHA512_256:
   1798 #ifdef MHD_SUPPORT_SHA512_256
   1799     mhd_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest);
   1800 #  ifndef NDEBUG
   1801     da->ready_for_hashing = false;
   1802 #  endif /* _DEBUG */
   1803 #else  /* ! MHD_SUPPORT_SHA512_256 */
   1804     mhd_UNREACHABLE ();
   1805 #endif /* ! MHD_SUPPORT_SHA512_256 */
   1806     break;
   1807 
   1808   case MHD_DIGEST_BASE_ALGO_INVALID:
   1809   default:
   1810     mhd_UNREACHABLE ();
   1811     break;
   1812   }
   1813 #ifndef NDEBUG
   1814   da->hashing = false;
   1815 #endif /* _DEBUG */
   1816 }
   1817 
   1818 
   1819 /**
   1820  * Reset the digest calculation structure and prepare for new calculation.
   1821  *
   1822  * @param da the structure to reset
   1823  */
   1824 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void
   1825 digest_reset (struct DigestAlgorithm *da)
   1826 {
   1827   mhd_assert (!da->uninitialised);
   1828   mhd_assert (da->algo_selected);
   1829   mhd_assert (!da->hashing);
   1830   switch (da->algo)
   1831   {
   1832   case MHD_DIGEST_BASE_ALGO_MD5:
   1833 #ifdef MHD_SUPPORT_MD5
   1834     mhd_assert (!da->ready_for_hashing);
   1835     mhd_MD5_reset (&(da->ctx.md5_ctx));
   1836 #  ifndef NDEBUG
   1837     da->ready_for_hashing = true;
   1838 #  endif /* _DEBUG */
   1839 #else  /* ! MHD_SUPPORT_MD5 */
   1840     mhd_UNREACHABLE ();
   1841 #endif /* ! MHD_SUPPORT_MD5 */
   1842     break;
   1843 
   1844   case MHD_DIGEST_BASE_ALGO_SHA256:
   1845 #ifdef MHD_SUPPORT_SHA256
   1846     mhd_assert (!da->ready_for_hashing);
   1847     mhd_SHA256_reset (&(da->ctx.sha256_ctx));
   1848 #  ifndef NDEBUG
   1849     da->ready_for_hashing = true;
   1850 #  endif /* _DEBUG */
   1851 #else  /* ! MHD_SUPPORT_SHA256 */
   1852     mhd_UNREACHABLE ();
   1853 #endif /* ! MHD_SUPPORT_SHA256 */
   1854     break;
   1855 
   1856   case MHD_DIGEST_BASE_ALGO_SHA512_256:
   1857 #ifdef MHD_SUPPORT_SHA512_256
   1858     mhd_assert (!da->ready_for_hashing);
   1859     mhd_SHA512_256_reset (&(da->ctx.sha512_256_ctx));
   1860 #  ifndef NDEBUG
   1861     da->ready_for_hashing = true;
   1862 #  endif /* _DEBUG */
   1863 #else  /* ! MHD_SUPPORT_SHA512_256 */
   1864     mhd_UNREACHABLE ();
   1865 #endif /* ! MHD_SUPPORT_SHA512_256 */
   1866     break;
   1867 
   1868   case MHD_DIGEST_BASE_ALGO_INVALID:
   1869   default:
   1870 #ifndef NDEBUG
   1871     da->ready_for_hashing = false;
   1872 #endif
   1873     mhd_UNREACHABLE ();
   1874     break;
   1875   }
   1876 }
   1877 
   1878 
   1879 #if defined(mhd_MD5_HAS_EXT_ERROR) \
   1880   || defined(mhd_SHA256_HAS_EXT_ERROR) \
   1881   || defined(mhd_SHA512_256_HAS_EXT_ERROR)
   1882 /**
   1883  * Indicates that digest algorithm has external error status
   1884  */
   1885 #  define mhd_DIGEST_HAS_EXT_ERROR 1
   1886 #endif /* mhd_MD5_HAS_EXT_ERROR || mhd_SHA256_HAS_EXT_ERROR
   1887           || mhd_SHA512_256_HAS_EXT_ERROR*/
   1888 
   1889 #ifdef mhd_DIGEST_HAS_EXT_ERROR
   1890 /**
   1891  * Get external error state.
   1892  *
   1893  * When external digest calculation used, an error may occur during
   1894  * initialisation or hashing data. This function checks whether external
   1895  * error has been reported for digest calculation.
   1896  * @param da the digest calculation
   1897  * @return 'true' if external error occurs,
   1898  *         'false' otherwise
   1899  */
   1900 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ bool
   1901 digest_has_error (struct DigestAlgorithm *da)
   1902 {
   1903   mhd_assert (!da->uninitialised);
   1904   mhd_assert (da->algo_selected);
   1905   switch (da->algo)
   1906   {
   1907   case MHD_DIGEST_BASE_ALGO_MD5:
   1908 #  ifdef MHD_SUPPORT_MD5
   1909     return mhd_MD5_has_err (&(da->ctx.md5_ctx));
   1910 #  else  /* ! MHD_SUPPORT_MD5 */
   1911     mhd_UNREACHABLE ();
   1912 #  endif /* ! MHD_SUPPORT_MD5 */
   1913     break;
   1914 
   1915   case MHD_DIGEST_BASE_ALGO_SHA256:
   1916 #  ifdef MHD_SUPPORT_SHA256
   1917     return mhd_SHA256_has_err (&(da->ctx.sha256_ctx));
   1918 #  else  /* ! MHD_SUPPORT_SHA256 */
   1919     mhd_UNREACHABLE ();
   1920 #  endif /* ! MHD_SUPPORT_SHA256 */
   1921     break;
   1922 
   1923   case MHD_DIGEST_BASE_ALGO_SHA512_256:
   1924 #  ifdef MHD_SUPPORT_SHA512_256
   1925     return mhd_SHA512_256_has_err (&(da->ctx.sha512_256_ctx));
   1926 #  else  /* ! MHD_SUPPORT_SHA512_256 */
   1927     mhd_UNREACHABLE ();
   1928 #  endif /* ! MHD_SUPPORT_SHA512_256 */
   1929     break;
   1930 
   1931   case MHD_DIGEST_BASE_ALGO_INVALID:
   1932   default:
   1933     break;
   1934   }
   1935   mhd_UNREACHABLE ();
   1936   return true;
   1937 }
   1938 
   1939 
   1940 #else  /* ! mhd_DIGEST_HAS_EXT_ERROR */
   1941 #  define digest_has_error(da) (((void) (da)), ! ! 0)
   1942 #endif /* ! mhd_DIGEST_HAS_EXT_ERROR */
   1943 
   1944 
   1945 /**
   1946  * Calculate userdigest, return it as binary data.
   1947  *
   1948  * It is equal to H(A1) for non-session algorithms.
   1949  *
   1950  * MHD internal version.
   1951  *
   1952  * @param da the digest algorithm
   1953  * @param username the username to use
   1954  * @param username_len the length of the @a username
   1955  * @param realm the realm to use
   1956  * @param realm_len the length of the @a realm
   1957  * @param password the password, must be zero-terminated
   1958  * @param[out] ha1_bin the output buffer, must have at least
   1959  *                     #digest_get_size(da) bytes available
   1960  */
   1961 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   1962 MHD_FN_PAR_IN_SIZE_ (2, 3) MHD_FN_PAR_IN_SIZE_ (4, 5)
   1963 MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_OUT_ (7) void
   1964 calc_userdigest (struct DigestAlgorithm *restrict da,
   1965                  const char *restrict username, const size_t username_len,
   1966                  const char *restrict realm, const size_t realm_len,
   1967                  const char *restrict password,
   1968                  uint8_t *ha1_bin)
   1969 {
   1970   mhd_assert (!da->uninitialised);
   1971   mhd_assert (da->algo_selected);
   1972   mhd_assert (!da->hashing);
   1973   digest_update (da, username_len, username);
   1974   digest_update_with_colon (da);
   1975   digest_update (da, realm_len, realm);
   1976   digest_update_with_colon (da);
   1977   digest_update_str (da, password);
   1978   digest_calc_hash (da, ha1_bin);
   1979 }
   1980 
   1981 
   1982 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
   1983 MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
   1984 MHD_FN_PAR_OUT_SIZE_ (6, 5) enum MHD_StatusCode
   1985 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
   1986                                  const char *MHD_RESTRICT username,
   1987                                  const char *MHD_RESTRICT realm,
   1988                                  const char *MHD_RESTRICT password,
   1989                                  size_t bin_buf_size,
   1990                                  void *MHD_RESTRICT userdigest_bin)
   1991 {
   1992   struct DigestAlgorithm da;
   1993   enum MHD_StatusCode ret;
   1994   if (!digest_init_one_time (&da, get_base_digest_algo (algo)))
   1995     return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
   1996 
   1997   if (digest_get_size (&da) > bin_buf_size)
   1998     ret = MHD_SC_OUT_BUFF_TOO_SMALL;
   1999   else
   2000   {
   2001     calc_userdigest (&da,
   2002                      username,
   2003                      strlen (username),
   2004                      realm,
   2005                      strlen (realm),
   2006                      password,
   2007                      (uint8_t *)userdigest_bin);
   2008     ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
   2009   }
   2010   digest_deinit (&da);
   2011 
   2012   return ret;
   2013 }
   2014 
   2015 
   2016 /**
   2017  * Calculate userhash, return it as binary data.
   2018  *
   2019  * MHD internal version.
   2020  *
   2021  * @param da the digest algorithm
   2022  * @param username_len the length of the @a username
   2023  * @param username the username to use
   2024  * @param realm_len the length of the @a realm
   2025  * @param realm the realm to use
   2026  * @param[out] digest_bin the output buffer, must have at least
   2027  *                        #MHD_digest_get_hash_size(algo) bytes available
   2028  */
   2029 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
   2030 MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) MHD_FN_PAR_OUT_ (6) void
   2031 calc_userhash (struct DigestAlgorithm *da,
   2032                const size_t username_len,
   2033                const char *username,
   2034                const size_t realm_len,
   2035                const char *realm,
   2036                uint8_t *digest_bin)
   2037 {
   2038   mhd_assert (!da->uninitialised);
   2039   mhd_assert (da->algo_selected);
   2040   mhd_assert (!da->hashing);
   2041   digest_update (da, username_len, username);
   2042   digest_update_with_colon (da);
   2043   digest_update (da, realm_len, realm);
   2044   digest_calc_hash (da, digest_bin);
   2045 }
   2046 
   2047 
   2048 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
   2049 MHD_FN_PAR_CSTR_ (2)
   2050 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5, 4) enum MHD_StatusCode
   2051 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
   2052                                const char *MHD_RESTRICT username,
   2053                                const char *MHD_RESTRICT realm,
   2054                                size_t bin_buf_size,
   2055                                void *MHD_RESTRICT userhash_bin)
   2056 {
   2057   struct DigestAlgorithm da;
   2058   enum MHD_StatusCode ret;
   2059 
   2060   if (!digest_init_one_time (&da, get_base_digest_algo (algo)))
   2061     return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
   2062   if (digest_get_size (&da) > bin_buf_size)
   2063     ret = MHD_SC_OUT_BUFF_TOO_SMALL;
   2064   else
   2065   {
   2066     calc_userhash (&da,
   2067                    strlen (username),
   2068                    username,
   2069                    strlen (realm),
   2070                    realm,
   2071                    (uint8_t *)userhash_bin);
   2072 
   2073     ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
   2074   }
   2075   digest_deinit (&da);
   2076 
   2077   return ret;
   2078 }
   2079 
   2080 
   2081 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
   2082 MHD_FN_PAR_CSTR_ (2)
   2083 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5, 4) enum MHD_StatusCode
   2084 MHD_digest_auth_calc_userhash_hex (
   2085   enum MHD_DigestAuthAlgo algo,
   2086   const char *MHD_RESTRICT username,
   2087   const char *MHD_RESTRICT realm,
   2088   size_t hex_buf_size,
   2089   char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
   2090 {
   2091   uint8_t userhash_bin[mhd_MAX_DIGEST] = { 0u /* mute compiler warning */ };
   2092   size_t digest_size;
   2093   enum MHD_StatusCode res;
   2094 
   2095   digest_size = digest_get_hash_size (algo);
   2096   if (digest_size * 2 + 1 > hex_buf_size)
   2097     return MHD_SC_OUT_BUFF_TOO_SMALL;
   2098   res = MHD_digest_auth_calc_userhash (algo,
   2099                                        username,
   2100                                        realm,
   2101                                        sizeof(userhash_bin),
   2102                                        userhash_bin);
   2103   if (MHD_SC_OK != res)
   2104     return res;
   2105 
   2106   (void)mhd_bin_to_hex_z (userhash_bin,
   2107                           digest_size,
   2108                           userhash_hex);
   2109   return MHD_SC_OK;
   2110 }
   2111 
   2112 
   2113 /**
   2114  * Extract timestamp from the given nonce.
   2115  * @param nonce the nonce to check in binary form
   2116  * @return 'true' if timestamp was extracted,
   2117  *         'false' if nonce does not have valid timestamp.
   2118  */
   2119 mhd_static_inline uint_fast32_t
   2120 get_nonce_timestamp (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE])
   2121 {
   2122   return (uint_fast32_t)
   2123          mhd_GET_32BIT_LE_UNALIGN (nonce + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE);
   2124 }
   2125 
   2126 
   2127 /**
   2128  * The result of nonce-nc map array check.
   2129  */
   2130 enum mhd_CheckNonceNC
   2131 {
   2132   /**
   2133    * The nonce and NC are OK (valid and NC was not used before).
   2134    */
   2135   mhd_CHECK_NONCENC_OK = MHD_DAUTH_OK,
   2136 
   2137   /**
   2138    * The 'nonce' is too old, has been overwritten with newer 'nonce' in
   2139    * the same slot or 'nc' value has been used already.
   2140    * The validity of the 'nonce' was not be checked.
   2141    */
   2142   mhd_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE,
   2143 
   2144   /**
   2145    * The 'nonce' is wrong, it was not generated before.
   2146    */
   2147   mhd_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG
   2148 };
   2149 
   2150 
   2151 /**
   2152  * Check nonce-nc map array with the new nonce counter.
   2153  *
   2154  * @param d the master daemon object
   2155  * @param noncelen the length of @a nonce, in characters
   2156  * @param nonce the pointer that referenced hex nonce, does not need to be
   2157  *              zero-terminated
   2158  * @param nc the nonce counter
   2159  * @param time_now the current timestamp
   2160  * @return #MHD_DAUTH_NONCENC_OK if successful,
   2161  *         #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array
   2162  *         is available),
   2163  *         #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map
   2164  *         array, while it should.
   2165  */
   2166 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   2167 MHD_FN_PAR_IN_SIZE_ (3, 2) enum mhd_CheckNonceNC
   2168 check_nonce_nc (struct MHD_Daemon *restrict d,
   2169                 size_t noncelen,
   2170                 const char *restrict nonce,
   2171                 uint_fast32_t nc,
   2172                 uint_fast32_t time_now)
   2173 {
   2174   uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
   2175   struct mhd_DaemonAuthDigestNonceData *nonce_slot;
   2176   uint_fast32_t valid_time;
   2177   uint_fast32_t slot_valid_time;
   2178   enum mhd_CheckNonceNC ret;
   2179 
   2180   mhd_assert (!mhd_D_HAS_MASTER (d));  /* only master daemon should be used */
   2181   mhd_assert (0 != noncelen);
   2182   mhd_assert (0 != nc);
   2183   if (mhd_AUTH_DIGEST_NONCE_LEN != noncelen)
   2184     return mhd_CHECK_NONCENC_WRONG;
   2185 
   2186   if (mhd_AUTH_DIGEST_NONCE_BIN_SIZE !=
   2187       mhd_hex_to_bin (nonce,
   2188                       mhd_AUTH_DIGEST_NONCE_LEN,
   2189                       nonce_bin))
   2190     return mhd_CHECK_NONCENC_WRONG;
   2191 
   2192   if ((NULL != memchr (nonce, 'A', mhd_AUTH_DIGEST_NONCE_LEN))
   2193       || (NULL != memchr (nonce, 'B', mhd_AUTH_DIGEST_NONCE_LEN))
   2194       || (NULL != memchr (nonce, 'C', mhd_AUTH_DIGEST_NONCE_LEN))
   2195       || (NULL != memchr (nonce, 'D', mhd_AUTH_DIGEST_NONCE_LEN))
   2196       || (NULL != memchr (nonce, 'E', mhd_AUTH_DIGEST_NONCE_LEN))
   2197       || (NULL != memchr (nonce, 'F', mhd_AUTH_DIGEST_NONCE_LEN)))
   2198     return mhd_CHECK_NONCENC_WRONG;   /* Upper case chars are not produced by MHD */
   2199 
   2200   valid_time = get_nonce_timestamp (nonce_bin);
   2201 
   2202   nonce_slot = d->auth_dg.nonces
   2203                + nonce_to_index (nonce_bin,
   2204                                  d->auth_dg.cfg.nonces_num);
   2205 
   2206   mhd_mutex_lock_chk (&(d->auth_dg.nonces_lock));
   2207 
   2208   slot_valid_time = nonce_slot->valid_time;
   2209   if ((0 == memcmp (nonce_slot->nonce,
   2210                     nonce_bin,
   2211                     sizeof(nonce_slot->nonce)))
   2212       && (slot_valid_time == valid_time))
   2213   {
   2214     /* The nonce matches the stored nonce */
   2215     if (nonce_slot->max_recvd_nc < nc)
   2216     {
   2217       /* 'nc' is larger, shift bitmask and bump limit */
   2218       const uint_fast32_t jump_size =
   2219         (uint_fast32_t)nc - nonce_slot->max_recvd_nc;
   2220       if (64 > jump_size)
   2221       {
   2222         /* small jump, less than mask width */
   2223         nonce_slot->nmask <<= jump_size;
   2224         /* Set bit for the old 'nc' value */
   2225         nonce_slot->nmask |= (UINT64_C (1) << (jump_size - 1));
   2226       }
   2227       else if (64 == jump_size)
   2228         nonce_slot->nmask = (UINT64_C (1) << 63);
   2229       else
   2230         nonce_slot->nmask = 0; /* big jump, unset all bits in the mask */
   2231       nonce_slot->max_recvd_nc = nc;
   2232       ret = mhd_CHECK_NONCENC_OK;
   2233     }
   2234     else if (nonce_slot->max_recvd_nc == nc)
   2235       /* 'nc' was already used */
   2236       ret = mhd_CHECK_NONCENC_STALE;
   2237     else /* (nonce_slot->max_recvd_nc > nc) */
   2238     {
   2239       /* Out-of-order 'nc' value. Check whether it was used before */
   2240       const uint_fast32_t nc_delta = nonce_slot->max_recvd_nc - nc;
   2241 
   2242       mhd_ASSUME (0u != nc_delta); /* Guaranteed by if() branches */
   2243 
   2244       if (64u >= nc_delta)
   2245       {
   2246         const uint_fast64_t nc_bit = (UINT64_C (1) << (nc_delta - 1));
   2247 
   2248         mhd_ASSUME (0u != nc_bit);
   2249         mhd_ASSUME (0u != (nc_bit & UINT64_C (0xFFFFFFFFFFFFFFFF)));
   2250 
   2251         if (0u == (nc_bit & nonce_slot->nmask))
   2252         {
   2253           /* 'nc' has not been used before. Set the bit. */
   2254           nonce_slot->nmask |= nc_bit;
   2255           ret = mhd_CHECK_NONCENC_OK;
   2256         }
   2257         else
   2258           ret = mhd_CHECK_NONCENC_STALE; /* 'nc' has been used before */
   2259       }
   2260       else
   2261         ret = mhd_CHECK_NONCENC_STALE; /* 'nc' is too old (more than 64 value before) */
   2262     }
   2263   }
   2264   else
   2265   {
   2266     /* The nonce does not match the stored nonce */
   2267     if (((valid_time - slot_valid_time) & UINT32_C (0xFFFFFFFF)) <=
   2268         ((slot_valid_time - valid_time) & UINT32_C (0xFFFFFFFF)))
   2269     {
   2270       /* The stored nonce was generated before the checked nonce */
   2271       ret = mhd_CHECK_NONCENC_WRONG;
   2272     }
   2273     else
   2274     {
   2275       /* The stored nonce was generated after the checked nonce */
   2276       const uint_fast32_t nonce_gen_time =
   2277         ((valid_time - d->auth_dg.cfg.nonce_tmout) & UINT32_C (0xFFFFFFFF));
   2278       if (((time_now - nonce_gen_time) & UINT32_C (0xFFFFFFFF)) <
   2279           ((nonce_gen_time - time_now) & UINT32_C (0xFFFFFFFF)))
   2280         ret = mhd_CHECK_NONCENC_WRONG; /* The nonce is generated in "future" */
   2281       else
   2282         /* Probably the nonce has been overwritten with a newer nonce */
   2283         ret = mhd_CHECK_NONCENC_STALE;
   2284     }
   2285   }
   2286 
   2287   mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
   2288 
   2289   return ret;
   2290 }
   2291 
   2292 
   2293 struct test_header_param
   2294 {
   2295   struct MHD_Request *request;
   2296   size_t num_get_params;
   2297 };
   2298 
   2299 /**
   2300  * Test if the given key-value pair is in the headers for the
   2301  * given request.
   2302  *
   2303  * @param cls the test context
   2304  * @param name the name of the key
   2305  * @param value the value of the key
   2306  * @return 'true' if the key-value pair is in the headers,
   2307  *         'false' if not
   2308  */
   2309 static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool
   2310 test_header (void *restrict cls,
   2311              const struct MHD_String *restrict name,
   2312              const struct MHD_StringNullable *restrict value)
   2313 {
   2314   struct test_header_param *const param = (struct test_header_param *)cls;
   2315   struct MHD_Request *req = param->request;
   2316   struct mhd_RequestField *pos;
   2317   size_t i;
   2318 
   2319   param->num_get_params++;
   2320   i = 0;
   2321   for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
   2322        NULL != pos;
   2323        pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
   2324   {
   2325     if (MHD_VK_URI_QUERY_PARAM != pos->field.kind)
   2326       continue;
   2327     if (++i == param->num_get_params)
   2328     {
   2329       if (name->len != pos->field.nv.name.len)
   2330         return false;
   2331       if (value->len != pos->field.nv.value.len)
   2332         return false;
   2333       if (0 != name->len)
   2334       {
   2335         mhd_assert (NULL != name->cstr);
   2336         mhd_assert (NULL != pos->field.nv.name.cstr);
   2337         if (0 != memcmp (name->cstr,
   2338                          pos->field.nv.name.cstr,
   2339                          name->len))
   2340           return false;
   2341       }
   2342       if (0 != value->len)
   2343       {
   2344         mhd_assert (NULL != value->cstr);
   2345         mhd_assert (NULL != pos->field.nv.value.cstr);
   2346         if (0 != memcmp (value->cstr,
   2347                          pos->field.nv.value.cstr,
   2348                          value->len))
   2349           return false;
   2350       }
   2351       return true;
   2352     }
   2353   }
   2354   return false;
   2355 }
   2356 
   2357 
   2358 /**
   2359  * Check that the arguments given by the client as part
   2360  * of the authentication header match the arguments we
   2361  * got as part of the HTTP request URI.
   2362  *
   2363  * @param req the request with get arguments to compare against
   2364  * @param args the copy of argument URI string (after "?" in URI), will be
   2365  *             modified by this function
   2366  * @return 'true' if the arguments match,
   2367  *         'false' if not
   2368  */
   2369 static MHD_FN_PAR_NONNULL_ALL_
   2370 MHD_FN_PAR_CSTR_ (3)
   2371 MHD_FN_PAR_INOUT_SIZE_ (3, 2) bool
   2372 check_argument_match (struct MHD_Request *restrict req,
   2373                       size_t args_len,
   2374                       char *restrict args)
   2375 {
   2376   struct mhd_RequestField *pos;
   2377   struct test_header_param param;
   2378 
   2379   param.request = req;
   2380   param.num_get_params = 0;
   2381   if (!mhd_parse_uri_args (args_len,
   2382                            args,
   2383                            &test_header,
   2384                            &param))
   2385     return false;
   2386 
   2387   /* Check that the number of arguments matches */
   2388   for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
   2389        NULL != pos;
   2390        pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
   2391   {
   2392     if (MHD_VK_URI_QUERY_PARAM != pos->field.kind)
   2393       continue;
   2394     param.num_get_params--;
   2395   }
   2396 
   2397   if (0 != param.num_get_params)
   2398     return false; /* argument count mismatch */
   2399 
   2400   return true;
   2401 }
   2402 
   2403 
   2404 /**
   2405  * Check that the URI provided by the client as part
   2406  * of the authentication header match the real HTTP request URI.
   2407  *
   2408  * @param req the request to compare URI
   2409  * @param uri the copy of URI in the authentication header, should point to
   2410  *            modifiable buffer at least @a uri_len + 1 characters long,
   2411  *            will be modified by this function, not valid upon return
   2412  * @param uri_len the length of the @a uri string in characters
   2413  * @return boolean true if the URIs match,
   2414  *         boolean false if not
   2415  */
   2416 static MHD_FN_PAR_NONNULL_ALL_
   2417 MHD_FN_PAR_INOUT_ (3) bool
   2418 check_uri_match (struct MHD_Request *restrict req,
   2419                  const size_t uri_len,
   2420                  char *restrict uri)
   2421 {
   2422   char *qmark;
   2423   char *args;
   2424   size_t url_len; /* The part before '?' char */
   2425   size_t args_len;
   2426 
   2427   if (uri_len != req->req_target_len)
   2428     return false;
   2429 
   2430   uri[uri_len] = 0;
   2431   qmark = (char *)memchr (uri,
   2432                           '?',
   2433                           uri_len);
   2434   if (NULL != qmark)
   2435   {
   2436     *qmark = 0;
   2437     url_len = (size_t)(qmark - uri);
   2438   }
   2439   else
   2440     url_len = uri_len;
   2441 
   2442   /* Need to unescape URI before comparing with req->url */
   2443   url_len = mhd_str_pct_decode_lenient_n (uri,
   2444                                           url_len,
   2445                                           uri,
   2446                                           url_len,
   2447                                           NULL);
   2448   if ((url_len != req->url_len)
   2449       || (0 != memcmp (uri,
   2450                        req->url,
   2451                        url_len)))
   2452     return false;
   2453 
   2454   args = (NULL != qmark) ? (qmark + 1) : uri + uri_len;
   2455   args_len = (size_t)(uri + uri_len - args);
   2456 
   2457   if (!check_argument_match (req,
   2458                              args_len,
   2459                              args))
   2460     return false;
   2461 
   2462   return true;
   2463 }
   2464 
   2465 
   2466 /**
   2467  * The size of the unquoting buffer in stack
   2468  */
   2469 #define mhd_STATIC_UNQ_BUFFER_SIZE 128
   2470 
   2471 
   2472 /**
   2473  * Get the pointer to buffer with required size
   2474  * @param tmp1 the first buffer with fixed size
   2475  * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
   2476  * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
   2477  * @param required_size the required size in buffer
   2478  * @return the pointer to the buffer or NULL if failed to allocate buffer with
   2479  *         requested size
   2480  */
   2481 static MHD_FN_PAR_NONNULL_ALL_
   2482 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) char *
   2483 get_buffer_for_size (char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
   2484                      char **restrict ptmp2,
   2485                      size_t *restrict ptmp2_size,
   2486                      size_t required_size)
   2487 {
   2488   mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
   2489   mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
   2490   mhd_assert ((0 == *ptmp2_size) \
   2491               || (mhd_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
   2492 
   2493   if (required_size <= mhd_STATIC_UNQ_BUFFER_SIZE)
   2494     return tmp1;
   2495 
   2496   if (required_size <= *ptmp2_size)
   2497     return *ptmp2;
   2498 
   2499   if (required_size > mhd_AUTH_DIGEST_MAX_PARAM_SIZE)
   2500     return NULL;
   2501   if (NULL != *ptmp2)
   2502     free (*ptmp2);
   2503   *ptmp2 = (char *)malloc (required_size);
   2504   if (NULL == *ptmp2)
   2505     *ptmp2_size = 0;
   2506   else
   2507     *ptmp2_size = required_size;
   2508   return *ptmp2;
   2509 }
   2510 
   2511 
   2512 /**
   2513   * The result of parameter unquoting
   2514   */
   2515 enum mhd_GetUnqResult
   2516 {
   2517   mhd_UNQ_OK = MHD_DAUTH_OK,               /**< Got unquoted string */
   2518   mhd_UNQ_TOO_LARGE = MHD_DAUTH_TOO_LARGE, /**< The string is too large to unquote */
   2519   mhd_UNQ_OUT_OF_MEM = MHD_DAUTH_ERROR     /**< Out of memory error */
   2520 };
   2521 
   2522 /**
   2523  * Get Digest authorisation parameter as unquoted string.
   2524  * @param param the parameter to process
   2525  * @param[in,out] tmp1 the small buffer in stack
   2526  * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
   2527  * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
   2528  * @param[out] unquoted the pointer to store the result, NOT zero terminated
   2529  * @return enum code indicating result of the process
   2530  */
   2531 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   2532 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
   2533 MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
   2534 get_unquoted_param (const struct mhd_RqDAuthParam *param,
   2535                     char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
   2536                     char **restrict ptmp2,
   2537                     size_t *restrict ptmp2_size,
   2538                     struct mhd_BufferConst *restrict unquoted)
   2539 {
   2540   char *str;
   2541   size_t len;
   2542   mhd_assert (NULL != param->value.cstr);
   2543   mhd_assert (0 != param->value.len);
   2544 
   2545   if (!param->quoted)
   2546   {
   2547     unquoted->data = param->value.cstr;
   2548     unquoted->size = param->value.len;
   2549     return mhd_UNQ_OK;
   2550   }
   2551   /* The value is present and is quoted, needs to be copied and unquoted */
   2552   str = get_buffer_for_size (tmp1,
   2553                              ptmp2,
   2554                              ptmp2_size,
   2555                              param->value.len);
   2556   if (NULL == str)
   2557     return (param->value.len > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
   2558            mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
   2559 
   2560   len = mhd_str_unquote (param->value.cstr,
   2561                          param->value.len,
   2562                          str);
   2563   unquoted->data = str;
   2564   unquoted->size = len;
   2565   mhd_assert (0 != unquoted->size);
   2566   mhd_assert (unquoted->size < param->value.len);
   2567   return mhd_UNQ_OK;
   2568 }
   2569 
   2570 
   2571 /**
   2572  * Get copy of Digest authorisation parameter as unquoted string.
   2573  * @param param the parameter to process
   2574  * @param[in,out] tmp1 the small buffer in stack
   2575  * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
   2576  * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
   2577  * @param[out] unquoted the pointer to store the result, NOT zero terminated,
   2578  *                      but with enough space to zero-terminate
   2579  * @return enum code indicating result of the process
   2580  */
   2581 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   2582 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
   2583 MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
   2584 get_unquoted_param_copy (const struct mhd_RqDAuthParam *param,
   2585                          char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
   2586                          char **restrict ptmp2,
   2587                          size_t *restrict ptmp2_size,
   2588                          struct mhd_Buffer *restrict unquoted)
   2589 {
   2590   mhd_assert (NULL != param->value.cstr);
   2591   mhd_assert (0 != param->value.len);
   2592 
   2593   /* The value is present and is quoted, needs to be copied and unquoted */
   2594   /* Allocate buffer with one more additional byte for zero-termination */
   2595   unquoted->data =
   2596     get_buffer_for_size (tmp1,
   2597                          ptmp2,
   2598                          ptmp2_size,
   2599                          param->value.len + 1);
   2600 
   2601   if (NULL == unquoted->data)
   2602     return (param->value.len + 1 > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
   2603            mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
   2604 
   2605   if (!param->quoted)
   2606   {
   2607     memcpy (unquoted->data,
   2608             param->value.cstr,
   2609             param->value.len);
   2610     unquoted->size = param->value.len;
   2611     return mhd_UNQ_OK;
   2612   }
   2613 
   2614   unquoted->size =
   2615     mhd_str_unquote (param->value.cstr,
   2616                      param->value.len,
   2617                      unquoted->data);
   2618   mhd_assert (0 != unquoted->size);
   2619   mhd_assert (unquoted->size < param->value.len);
   2620   return mhd_UNQ_OK;
   2621 }
   2622 
   2623 
   2624 /**
   2625  * Check whether Digest Auth request parameter is equal to given string
   2626  * @param param the parameter to check
   2627  * @param str_len the length of the @a str
   2628  * @param str the string to compare with, does not need to be zero-terminated
   2629  * @return true is parameter is equal to the given string,
   2630  *         false otherwise
   2631  */
   2632 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   2633 MHD_FN_PAR_IN_SIZE_ (3, 2) bool
   2634 is_param_equal (const struct mhd_RqDAuthParam *restrict param,
   2635                 const size_t str_len,
   2636                 const char *restrict str)
   2637 {
   2638   mhd_assert (NULL != param->value.cstr);
   2639   mhd_assert (0 != param->value.len);
   2640   if (param->quoted)
   2641     return mhd_str_equal_quoted_bin_n (param->value.cstr,
   2642                                        param->value.len,
   2643                                        str,
   2644                                        str_len);
   2645   return (str_len == param->value.len)
   2646          && (0 == memcmp (str, param->value.cstr, str_len));
   2647 }
   2648 
   2649 
   2650 /**
   2651  * Check whether Digest Auth request parameter is caseless equal to given string
   2652  * @param param the parameter to check
   2653  * @param str_len the length of the @a str
   2654  * @param str the string to compare with, does not need to be zero-terminated
   2655  * @return true is parameter is caseless equal to the given string,
   2656  *         false otherwise
   2657  */
   2658 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   2659 MHD_FN_PAR_IN_SIZE_ (3, 2) bool
   2660 is_param_equal_caseless (const struct mhd_RqDAuthParam *restrict param,
   2661                          const size_t str_len,
   2662                          const char *restrict str)
   2663 {
   2664   mhd_assert (NULL != param->value.cstr);
   2665   mhd_assert (0 != param->value.len);
   2666   if (param->quoted)
   2667     return mhd_str_equal_caseless_quoted_bin_n (param->value.cstr,
   2668                                                 param->value.len,
   2669                                                 str,
   2670                                                 str_len);
   2671   return (str_len == param->value.len)
   2672          && (mhd_str_equal_caseless_bin_n (str, param->value.cstr, str_len));
   2673 }
   2674 
   2675 
   2676 /**
   2677  * Authenticates the authorization header sent by the client
   2678  *
   2679  * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
   2680  * @a mqop and the client uses this mode, then server generated nonces are
   2681  * used as one-time nonces because nonce-count is not supported in this old RFC.
   2682  * Communication in this mode is very inefficient, especially if the client
   2683  * requests several resources one-by-one as for every request new nonce must be
   2684  * generated and client repeat all requests twice (the first time to get a new
   2685  * nonce and the second time to perform an authorised request).
   2686  *
   2687  * @param req the request handle
   2688  * @param realm the realm for authorization of the client
   2689  * @param username the username to be authenticated, must be in clear text
   2690  *                 even if userhash is used by the client
   2691  * @param password the password used in the authentication,
   2692  *                 must be NULL if @a userdigest is not NULL
   2693  * @param userdigest the precalculated binary hash of the string
   2694  *                   "username:realm:password",
   2695  *                   must be NULL if @a password is not NULL
   2696  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
   2697  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
   2698  *               returned;
   2699  *               unlike #digest_auth_check_all() zero is treated as "no limit"
   2700  * @param mqop the QOP to use
   2701  * @param malgo digest algorithms allowed to use, fail if algorithm specified
   2702  *               by the client is not allowed by this parameter
   2703  * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
   2704  *                  to be freed if not NULL upon return
   2705  * @return #MHD_DAUTH_OK if authenticated,
   2706  *         error code otherwise.
   2707  * @ingroup authentication
   2708  */
   2709 static MHD_FN_MUST_CHECK_RESULT_
   2710 MHD_FN_PAR_NONNULL_ (1)
   2711 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
   2712 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
   2713 MHD_FN_PAR_CSTR_ (4)
   2714 enum MHD_DigestAuthResult
   2715 digest_auth_check_all_inner (struct MHD_Request *restrict req,
   2716                              const char *restrict realm,
   2717                              const char *restrict username,
   2718                              const char *restrict password,
   2719                              const uint8_t *restrict userdigest,
   2720                              uint_fast32_t max_nc,
   2721                              enum MHD_DigestAuthMultiQOP mqop,
   2722                              enum MHD_DigestAuthMultiAlgo malgo,
   2723                              char **pbuf,
   2724                              struct DigestAlgorithm *da)
   2725 {
   2726   struct MHD_Daemon *const daemon =
   2727     mhd_daemon_get_master_daemon (
   2728       mhd_CNTNR_PTR (req, struct MHD_Connection, rq)->daemon);
   2729   enum MHD_DigestAuthAlgo c_algo; /**< Client's algorithm */
   2730   enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */
   2731   unsigned int digest_size;
   2732   uint8_t hash1_bin[mhd_MAX_DIGEST];
   2733   uint8_t hash2_bin[mhd_MAX_DIGEST];
   2734   uint_fast32_t nc;
   2735   const struct mhd_AuthDigesReqParams *restrict params;
   2736   /**
   2737    * Temporal buffer in stack for unquoting and other needs
   2738    */
   2739   char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE];
   2740   char **const ptmp2 = pbuf;     /**< Temporal malloc'ed buffer for unquoting */
   2741   size_t tmp2_size; /**< The size of @a tmp2 buffer */
   2742   struct mhd_BufferConst unquoted;
   2743   struct mhd_Buffer unq_copy;
   2744   enum mhd_GetUnqResult unq_res;
   2745   size_t username_len;
   2746   size_t realm_len;
   2747 
   2748   mhd_assert ((NULL == password) != (NULL == userdigest));
   2749 
   2750   tmp2_size = 0;
   2751 
   2752   if (1)
   2753   {
   2754     enum MHD_StatusCode res;
   2755 
   2756     res = get_rq_auth_digest_params (req);
   2757     if (MHD_SC_OK != res)
   2758     {
   2759       if (MHD_SC_AUTH_ABSENT == res)
   2760         return MHD_DAUTH_HEADER_MISSING;
   2761       else if (MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA == res)
   2762         return MHD_DAUTH_ERROR;
   2763       else if (MHD_SC_REQ_AUTH_DATA_BROKEN == res)
   2764         return MHD_DAUTH_HEADER_BROKEN;
   2765       else
   2766         mhd_UNREACHABLE ();
   2767     }
   2768     params = req->auth.digest.rqp;
   2769   }
   2770   mhd_assert (NULL != params);
   2771 
   2772   /* ** Initial parameters checks and setup ** */
   2773   /* Get client's algorithm */
   2774   c_algo = params->algo;
   2775   /* Check whether client's algorithm is allowed by function parameter */
   2776   if (((unsigned int)c_algo) !=
   2777       (((unsigned int)c_algo) & ((unsigned int)malgo)))
   2778     return MHD_DAUTH_WRONG_ALGO;
   2779   /* Check whether client's algorithm is supported */
   2780   if (0 != (((unsigned int)c_algo) & MHD_DIGEST_AUTH_ALGO_SESSION))
   2781     return MHD_DAUTH_UNSUPPORTED_ALGO;
   2782 #ifndef MHD_SUPPORT_MD5
   2783   if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_MD5))
   2784     return MHD_DAUTH_UNSUPPORTED_ALGO;
   2785 #endif /* ! MHD_SUPPORT_MD5 */
   2786 #ifndef MHD_SUPPORT_SHA256
   2787   if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_SHA256))
   2788     return MHD_DAUTH_UNSUPPORTED_ALGO;
   2789 #endif /* ! MHD_SUPPORT_SHA256 */
   2790 #ifndef MHD_SUPPORT_SHA512_256
   2791   if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
   2792     return MHD_DAUTH_UNSUPPORTED_ALGO;
   2793 #endif /* ! MHD_SUPPORT_SHA512_256 */
   2794   if (!digest_init_one_time (da, get_base_digest_algo (c_algo)))
   2795     mhd_UNREACHABLE ();
   2796   /* Check 'mqop' value */
   2797   c_qop = params->qop;
   2798   /* Check whether client's QOP is allowed by function parameter */
   2799   if (((unsigned int)c_qop) !=
   2800       (((unsigned int)c_qop) & ((unsigned int)mqop)))
   2801     return MHD_DAUTH_WRONG_QOP;
   2802   if (0 != (((unsigned int)c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
   2803     return MHD_DAUTH_UNSUPPORTED_QOP;
   2804 
   2805   digest_size = digest_get_size (da);
   2806 
   2807   /* ** A quick check for presence of all required parameters ** */
   2808 
   2809   if ((NULL == params->username.value.cstr)
   2810       && (NULL == params->username_ext.value.cstr))
   2811     return MHD_DAUTH_HEADER_BROKEN;
   2812   else if ((NULL != params->username.value.cstr)
   2813            && (NULL != params->username_ext.value.cstr))
   2814     return MHD_DAUTH_HEADER_BROKEN; /* Parameters cannot be used together */
   2815   else if ((NULL != params->username_ext.value.cstr)
   2816            && (mhd_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
   2817     return MHD_DAUTH_HEADER_BROKEN;  /* Broken extended notation */
   2818   else if (params->userhash && (NULL == params->username.value.cstr))
   2819     return MHD_DAUTH_HEADER_BROKEN;  /* Userhash cannot be used with extended notation */
   2820   else if (params->userhash && (digest_size * 2 > params->username.value.len))
   2821     return MHD_DAUTH_WRONG_USERNAME;  /* Too few chars for correct userhash */
   2822   else if (params->userhash && (digest_size * 4 < params->username.value.len))
   2823     return MHD_DAUTH_WRONG_USERNAME;  /* Too many chars for correct userhash */
   2824 
   2825   if (NULL == params->realm.value.cstr)
   2826     return MHD_DAUTH_HEADER_BROKEN;
   2827   else if (((NULL == userdigest) || params->userhash)
   2828            && (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
   2829     return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */
   2830 
   2831   if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   2832   {
   2833     if (NULL == params->nc.value.cstr)
   2834       return MHD_DAUTH_HEADER_BROKEN;
   2835     else if (0 == params->nc.value.len)
   2836       return MHD_DAUTH_HEADER_BROKEN;
   2837     else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
   2838       return MHD_DAUTH_HEADER_BROKEN;
   2839 
   2840     if (NULL == params->cnonce.value.cstr)
   2841       return MHD_DAUTH_HEADER_BROKEN;
   2842     else if (0 == params->cnonce.value.len)
   2843       return MHD_DAUTH_HEADER_BROKEN;
   2844     else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
   2845       return MHD_DAUTH_TOO_LARGE;
   2846   }
   2847 
   2848   /* The QOP parameter was checked already */
   2849 
   2850   if (NULL == params->uri.value.cstr)
   2851     return MHD_DAUTH_HEADER_BROKEN;
   2852   else if (0 == params->uri.value.len)
   2853     return MHD_DAUTH_HEADER_BROKEN;
   2854   else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
   2855     return MHD_DAUTH_TOO_LARGE;
   2856 
   2857   if (NULL == params->nonce.value.cstr)
   2858     return MHD_DAUTH_HEADER_BROKEN;
   2859   else if (0 == params->nonce.value.len)
   2860     return MHD_DAUTH_HEADER_BROKEN;
   2861   else if (mhd_AUTH_DIGEST_NONCE_LEN * 2 < params->nonce.value.len)
   2862     return MHD_DAUTH_NONCE_WRONG;
   2863 
   2864   if (NULL == params->response.value.cstr)
   2865     return MHD_DAUTH_HEADER_BROKEN;
   2866   else if (0 == params->response.value.len)
   2867     return MHD_DAUTH_HEADER_BROKEN;
   2868   else if (digest_size * 4 < params->response.value.len)
   2869     return MHD_DAUTH_RESPONSE_WRONG;
   2870 
   2871   /* ** Check simple parameters match ** */
   2872 
   2873   /* Check 'algorithm' */
   2874   /* The 'algorithm' was checked at the start of the function */
   2875   /* 'algorithm' valid */
   2876 
   2877   /* Check 'qop' */
   2878   /* The 'qop' was checked at the start of the function */
   2879   /* 'qop' valid */
   2880 
   2881   /* Check 'realm' */
   2882   realm_len = strlen (realm);
   2883   if (!is_param_equal (&params->realm,
   2884                        realm_len,
   2885                        realm))
   2886     return MHD_DAUTH_WRONG_REALM;
   2887   /* 'realm' valid */
   2888 
   2889   /* Check 'username' */
   2890   username_len = strlen (username);
   2891   if (!params->userhash)
   2892   {
   2893     if (NULL != params->username.value.cstr)
   2894     { /* Username in standard notation */
   2895       if (!is_param_equal (&params->username, username_len, username))
   2896         return MHD_DAUTH_WRONG_USERNAME;
   2897     }
   2898     else
   2899     { /* Username in extended notation */
   2900       char *r_uname;
   2901       size_t buf_size = params->username_ext.value.len;
   2902       ssize_t res;
   2903 
   2904       mhd_assert (NULL != params->username_ext.value.cstr);
   2905       mhd_assert (mhd_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */
   2906       buf_size += 1; /* For zero-termination */
   2907       buf_size -= mhd_DAUTH_EXT_PARAM_MIN_LEN;
   2908       r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
   2909       if (NULL == r_uname)
   2910         return (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
   2911                MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
   2912       res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
   2913                                           params->username_ext.value.len,
   2914                                           r_uname, buf_size);
   2915       if (0 > res)
   2916         return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */
   2917       if ((username_len != (size_t)res)
   2918           || (0 != memcmp (username, r_uname, username_len)))
   2919         return MHD_DAUTH_WRONG_USERNAME;
   2920     }
   2921   }
   2922   else
   2923   { /* Userhash */
   2924     mhd_assert (NULL != params->username.value.cstr);
   2925     calc_userhash (da,
   2926                    username_len,
   2927                    username,
   2928                    realm_len,
   2929                    realm,
   2930                    hash1_bin);
   2931     if (digest_has_error (da))
   2932       return MHD_DAUTH_ERROR;
   2933     mhd_assert (sizeof (tmp1) >= (2 * digest_size));
   2934     mhd_bin_to_hex (hash1_bin, digest_size, tmp1);
   2935     if (!is_param_equal_caseless (&params->username, 2 * digest_size, tmp1))
   2936       return MHD_DAUTH_WRONG_USERNAME;
   2937     /* To simplify the logic, the digest is reset here instead of resetting
   2938        before the next hash calculation. */
   2939     digest_reset (da);
   2940   }
   2941   /* 'username' valid */
   2942 
   2943   /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
   2944 
   2945   /* Get 'nc' digital value */
   2946   nc = 0;
   2947   switch (get_rq_nc (params,
   2948                      &nc))
   2949   {
   2950   case mhd_GET_RQ_NC_NONE:
   2951     if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   2952       return MHD_DAUTH_HEADER_BROKEN;
   2953     nc = 1; /* Force 'nc' value */
   2954     break;
   2955   case mhd_GET_RQ_NC_VALID:
   2956     if (MHD_DIGEST_AUTH_QOP_NONE == c_qop)
   2957       return MHD_DAUTH_HEADER_BROKEN;
   2958     break;
   2959   case mhd_GET_RQ_NC_TOO_LONG:
   2960   case mhd_GET_RQ_NC_TOO_LARGE:
   2961     return MHD_DAUTH_NONCE_STALE;
   2962     break;
   2963   case mhd_GET_RQ_NC_BROKEN:
   2964     return MHD_DAUTH_HEADER_BROKEN;
   2965     break;
   2966   default:
   2967     mhd_UNREACHABLE ();
   2968     break;
   2969   }
   2970   if (0 == nc)
   2971     return MHD_DAUTH_HEADER_BROKEN;
   2972   if (0 == max_nc)
   2973     max_nc = daemon->auth_dg.cfg.def_max_nc;
   2974   if (max_nc < nc)
   2975     return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
   2976   /* Got 'nc' digital value */
   2977 
   2978   /* Get 'nonce' with basic checks */
   2979   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
   2980                                 &unquoted);
   2981   if (mhd_UNQ_TOO_LARGE == unq_res)
   2982     return MHD_DAUTH_TOO_LARGE;
   2983   if (mhd_UNQ_OUT_OF_MEM == unq_res)
   2984     return MHD_DAUTH_ERROR;
   2985 
   2986 
   2987   switch (check_nonce_nc (daemon,
   2988                           unquoted.size,
   2989                           unquoted.data,
   2990                           nc,
   2991                           (uint_fast32_t)
   2992                           ((mhd_monotonic_msec_counter () / 1000)
   2993                            & UINT32_C (0xFFFFFFFF))))
   2994   {
   2995   case mhd_CHECK_NONCENC_OK:
   2996     break;
   2997   case mhd_CHECK_NONCENC_STALE:
   2998     return MHD_DAUTH_NONCE_STALE;
   2999   case mhd_CHECK_NONCENC_WRONG:
   3000     return MHD_DAUTH_NONCE_WRONG;
   3001   default:
   3002     mhd_UNREACHABLE ();
   3003     break;
   3004   }
   3005   /* The nonce was generated by MHD, is not stale and nonce-nc combination has
   3006      not been used before */
   3007 
   3008   /* ** Build H(A2) and check URI match in the header and in the request ** */
   3009 
   3010   /* Get 'uri' */
   3011   mhd_assert (!da->hashing);
   3012   digest_update (da, req->method.len, req->method.cstr);
   3013   digest_update_with_colon (da);
   3014 #if 0
   3015   /* TODO: add support for "auth-int" */
   3016   digest_update_str (da, hentity);
   3017   digest_update_with_colon (da);
   3018 #endif
   3019   unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size,
   3020                                      &unq_copy);
   3021   if (mhd_UNQ_TOO_LARGE == unq_res)
   3022     return MHD_DAUTH_TOO_LARGE;
   3023   if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3024     return MHD_DAUTH_ERROR;
   3025 
   3026   digest_update_buf (da, &unq_copy);
   3027   /* The next check will modify copied URI string */
   3028   if (!check_uri_match (req, unq_copy.size, unq_copy.data))
   3029     return MHD_DAUTH_WRONG_URI;
   3030   digest_calc_hash (da, hash2_bin);
   3031 #ifdef mhd_DIGEST_HAS_EXT_ERROR
   3032   /* Skip digest calculation external error check, the next one checks both */
   3033 #endif /* mhd_DIGEST_HAS_EXT_ERROR */
   3034   /* Got H(A2) */
   3035 
   3036   /* ** Build H(A1) ** */
   3037   if (NULL == userdigest)
   3038   {
   3039     mhd_assert (!da->hashing);
   3040     digest_reset (da);
   3041     calc_userdigest (da,
   3042                      username, username_len,
   3043                      realm, realm_len,
   3044                      password,
   3045                      hash1_bin);
   3046   }
   3047   /* TODO: support '-sess' versions */
   3048 #ifdef mhd_DIGEST_HAS_EXT_ERROR
   3049   if (digest_has_error (da))
   3050     return MHD_DAUTH_ERROR;
   3051 #endif /* mhd_DIGEST_HAS_EXT_ERROR */
   3052   /* Got H(A1) */
   3053 
   3054   /* **  Check 'response' ** */
   3055 
   3056   mhd_assert (!da->hashing);
   3057   digest_reset (da);
   3058   /* Update digest with H(A1) */
   3059   mhd_assert (sizeof (tmp1) >= (digest_size * 2));
   3060   if (NULL == userdigest)
   3061     mhd_bin_to_hex (hash1_bin, digest_size, tmp1);
   3062   else
   3063     mhd_bin_to_hex (userdigest, digest_size, tmp1);
   3064   digest_update (da, digest_size * 2, (const uint8_t *)tmp1);
   3065 
   3066   /* H(A1) is not needed anymore, reuse the buffer.
   3067    * Use hash1_bin for the client's 'response' decoded to binary form. */
   3068   unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
   3069                                 &unquoted);
   3070   if (mhd_UNQ_TOO_LARGE == unq_res)
   3071     return MHD_DAUTH_TOO_LARGE;
   3072   if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3073     return MHD_DAUTH_ERROR;
   3074   if (digest_size != mhd_hex_to_bin (unquoted.data, unquoted.size, hash1_bin))
   3075     return MHD_DAUTH_RESPONSE_WRONG;
   3076 
   3077   /* Update digest with ':' */
   3078   digest_update_with_colon (da);
   3079   /* Update digest with 'nonce' text value */
   3080   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
   3081                                 &unquoted);
   3082   if (mhd_UNQ_TOO_LARGE == unq_res)
   3083     return MHD_DAUTH_TOO_LARGE;
   3084   if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3085     return MHD_DAUTH_ERROR;
   3086   digest_update_cbuf (da, &unquoted);
   3087   /* Update digest with ':' */
   3088   digest_update_with_colon (da);
   3089   if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   3090   {
   3091     /* Update digest with 'nc' text value */
   3092     unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
   3093                                   &unquoted);
   3094     if (mhd_UNQ_TOO_LARGE == unq_res)
   3095       return MHD_DAUTH_TOO_LARGE;
   3096     if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3097       return MHD_DAUTH_ERROR;
   3098     digest_update_cbuf (da, &unquoted);
   3099     /* Update digest with ':' */
   3100     digest_update_with_colon (da);
   3101     /* Update digest with 'cnonce' value */
   3102     unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
   3103                                   &unquoted);
   3104     if (mhd_UNQ_TOO_LARGE == unq_res)
   3105       return MHD_DAUTH_TOO_LARGE;
   3106     if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3107       return MHD_DAUTH_ERROR;
   3108     digest_update_cbuf (da, &unquoted);
   3109     /* Update digest with ':' */
   3110     digest_update_with_colon (da);
   3111     /* Update digest with 'qop' value */
   3112     unq_res = get_unquoted_param (&params->qop_raw, tmp1, ptmp2, &tmp2_size,
   3113                                   &unquoted);
   3114     if (mhd_UNQ_TOO_LARGE == unq_res)
   3115       return MHD_DAUTH_TOO_LARGE;
   3116     if (mhd_UNQ_OUT_OF_MEM == unq_res)
   3117       return MHD_DAUTH_ERROR;
   3118     digest_update_cbuf (da, &unquoted);
   3119     /* Update digest with ':' */
   3120     digest_update_with_colon (da);
   3121   }
   3122   /* Update digest with H(A2) */
   3123   mhd_bin_to_hex (hash2_bin, digest_size, tmp1);
   3124   digest_update (da, digest_size * 2, (const uint8_t *)tmp1);
   3125 
   3126   /* H(A2) is not needed anymore, reuse the buffer.
   3127    * Use hash2_bin for the calculated response in binary form */
   3128   digest_calc_hash (da, hash2_bin);
   3129 #ifdef mhd_DIGEST_HAS_EXT_ERROR
   3130   if (digest_has_error (da))
   3131     return MHD_DAUTH_ERROR;
   3132 #endif /* mhd_DIGEST_HAS_EXT_ERROR */
   3133 
   3134   if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
   3135     return MHD_DAUTH_RESPONSE_WRONG;
   3136 
   3137   return MHD_DAUTH_OK;
   3138 }
   3139 
   3140 
   3141 /**
   3142  * Authenticates the authorization header sent by the client
   3143  *
   3144  * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
   3145  * @a mqop and the client uses this mode, then server generated nonces are
   3146  * used as one-time nonces because nonce-count is not supported in this old RFC.
   3147  * Communication in this mode is very inefficient, especially if the client
   3148  * requests several resources one-by-one as for every request new nonce must be
   3149  * generated and client repeat all requests twice (the first time to get a new
   3150  * nonce and the second time to perform an authorised request).
   3151  *
   3152  * @param req the request handle
   3153  * @param realm the realm for authorization of the client
   3154  * @param username the username to be authenticated, must be in clear text
   3155  *                 even if userhash is used by the client
   3156  * @param password the password used in the authentication,
   3157  *                 must be NULL if @a userdigest is not NULL
   3158  * @param userdigest the precalculated binary hash of the string
   3159  *                   "username:realm:password",
   3160  *                   must be NULL if @a password is not NULL
   3161  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
   3162  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
   3163  *               returned;
   3164  *               if set to zero then daemon's default value is used
   3165  * @param mqop the QOP to use
   3166  * @param malgo digest algorithms allowed to use, fail if algorithm specified
   3167  *               by the client is not allowed by this parameter
   3168  * @return #MHD_DAUTH_OK if authenticated,
   3169  *         error code otherwise.
   3170  * @ingroup authentication
   3171  */
   3172 static enum MHD_DigestAuthResult
   3173 digest_auth_check_all (struct MHD_Request *restrict req,
   3174                        const char *restrict realm,
   3175                        const char *restrict username,
   3176                        const char *restrict password,
   3177                        const uint8_t *restrict userdigest,
   3178                        uint_fast32_t max_nc,
   3179                        enum MHD_DigestAuthMultiQOP mqop,
   3180                        enum MHD_DigestAuthMultiAlgo malgo)
   3181 {
   3182   enum MHD_DigestAuthResult res;
   3183   char *buf;
   3184   struct DigestAlgorithm da;
   3185 
   3186   buf = NULL;
   3187   digest_setup_zero (&da);
   3188   res = digest_auth_check_all_inner (req,
   3189                                      realm,
   3190                                      username,
   3191                                      password,
   3192                                      userdigest,
   3193                                      max_nc,
   3194                                      mqop,
   3195                                      malgo,
   3196                                      &buf,
   3197                                      &da);
   3198   digest_deinit (&da);
   3199   if (NULL != buf)
   3200     free (buf);
   3201 
   3202   return res;
   3203 }
   3204 
   3205 
   3206 /**
   3207  * Authenticates the authorization header sent by the client.
   3208  *
   3209  * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
   3210  * @a mqop and the client uses this mode, then server generated nonces are
   3211  * used as one-time nonces because nonce-count is not supported in this old RFC.
   3212  * Communication in this mode is very inefficient, especially if the client
   3213  * requests several resources one-by-one as for every request a new nonce must
   3214  * be generated and client repeats all requests twice (first time to get a new
   3215  * nonce and second time to perform an authorised request).
   3216  *
   3217  * @param request the request
   3218  * @param realm the realm for authorization of the client
   3219  * @param username the username to be authenticated, must be in clear text
   3220  *                 even if userhash is used by the client
   3221  * @param password the password matching the @a username (and the @a realm)
   3222  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
   3223  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
   3224  *               returned;
   3225  *               if zero is specified then daemon default value is used.
   3226  * @param mqop the QOP to use
   3227  * @param malgo digest algorithms allowed to use, fail if algorithm used
   3228  *               by the client is not allowed by this parameter
   3229  * @return #MHD_DAUTH_OK if authenticated,
   3230  *         the error code otherwise
   3231  * @ingroup authentication
   3232  */
   3233 MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
   3234 MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
   3235 enum MHD_DigestAuthResult
   3236 MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
   3237                        const char *MHD_RESTRICT realm,
   3238                        const char *MHD_RESTRICT username,
   3239                        const char *MHD_RESTRICT password,
   3240                        uint_fast32_t max_nc,
   3241                        enum MHD_DigestAuthMultiQOP mqop,
   3242                        enum MHD_DigestAuthMultiAlgo malgo)
   3243 {
   3244   return digest_auth_check_all (request,
   3245                                 realm,
   3246                                 username,
   3247                                 password,
   3248                                 NULL,
   3249                                 max_nc,
   3250                                 mqop,
   3251                                 malgo);
   3252 }
   3253 
   3254 
   3255 MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
   3256 MHD_FN_PAR_CSTR_ (2)
   3257 MHD_FN_PAR_CSTR_ (3)
   3258 MHD_FN_PAR_IN_SIZE_ (5, 4) enum MHD_DigestAuthResult
   3259 MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
   3260                               const char *MHD_RESTRICT realm,
   3261                               const char *MHD_RESTRICT username,
   3262                               size_t userdigest_size,
   3263                               const void *MHD_RESTRICT userdigest,
   3264                               uint_fast32_t max_nc,
   3265                               enum MHD_DigestAuthMultiQOP mqop,
   3266                               enum MHD_DigestAuthMultiAlgo malgo)
   3267 {
   3268   if (1 != (((0 != (((unsigned int)malgo) \
   3269                     & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)
   3270             + ((0 != (((unsigned int)malgo) \
   3271                       & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)
   3272             + ((0 != (((unsigned int)malgo) \
   3273                       & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)))
   3274     return MHD_DAUTH_UNSUPPORTED_ALGO;
   3275 
   3276 #ifndef MHD_SUPPORT_MD5
   3277   if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_MD5))
   3278     return MHD_DAUTH_UNSUPPORTED_ALGO;
   3279 #endif /* ! MHD_SUPPORT_MD5 */
   3280 #ifndef MHD_SUPPORT_SHA256
   3281   if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_SHA256))
   3282     return MHD_DAUTH_UNSUPPORTED_ALGO;
   3283 #endif /* ! MHD_SUPPORT_SHA256 */
   3284 #ifndef MHD_SUPPORT_SHA512_256
   3285   if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
   3286     return MHD_DAUTH_UNSUPPORTED_ALGO;
   3287 #endif /* ! MHD_SUPPORT_SHA512_256 */
   3288 
   3289   if (digest_get_hash_size ((enum MHD_DigestAuthAlgo)malgo) !=
   3290       userdigest_size)
   3291     return MHD_DAUTH_INVALID_USERDIGEST_SIZE;
   3292 
   3293   return digest_auth_check_all (request,
   3294                                 realm,
   3295                                 username,
   3296                                 NULL,
   3297                                 (const uint8_t *)userdigest,
   3298                                 max_nc,
   3299                                 mqop,
   3300                                 malgo);
   3301 }