libmicrohttpd2

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

auth_digest.c (111352B)


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