libmicrohttpd2

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

auth_digest.c (111931B)


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