libmicrohttpd2

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

auth_digest.c (111738B)


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