Coverage Report

Created: 2026-03-08 06:25

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