GNU libmicrohttpd 1.0.1
Loading...
Searching...
No Matches
gen_auth.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2022-2023 Evgeny Grin (Karlson2k)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library.
17 If not, see <http://www.gnu.org/licenses/>.
18*/
19
26#include "gen_auth.h"
27#include "internal.h"
28#include "connection.h"
29#include "mhd_str.h"
30#include "mhd_assert.h"
31
32#ifdef BAUTH_SUPPORT
33#include "basicauth.h"
34#endif /* BAUTH_SUPPORT */
35#ifdef DAUTH_SUPPORT
36#include "digestauth.h"
37#endif /* DAUTH_SUPPORT */
38
39#if ! defined(BAUTH_SUPPORT) && ! defined(DAUTH_SUPPORT)
40#error This file requires Basic or Digest authentication support
41#endif
42
47{
53};
54
64static bool
66 struct _MHD_str_w_len *auth_value)
67{
68 const struct MHD_HTTP_Req_Header *h;
69 const char *token;
70 size_t token_len;
71
72 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
74 return false;
75
76#ifdef DAUTH_SUPPORT
77 if (MHD_AUTHTYPE_DIGEST == type)
78 {
81 }
82 else /* combined with the next line */
83#endif /* DAUTH_SUPPORT */
84#ifdef BAUTH_SUPPORT
85 if (MHD_AUTHTYPE_BASIC == type)
86 {
89 }
90 else /* combined with the next line */
91#endif /* BAUTH_SUPPORT */
92 {
93 mhd_assert (0);
94 return false;
95 }
96
97 for (h = c->rq.headers_received; NULL != h; h = h->next)
98 {
99 if (MHD_HEADER_KIND != h->kind)
100 continue;
102 continue;
103 if (token_len > h->value_size)
104 continue;
106 h->header,
109 continue;
110 if (! MHD_str_equal_caseless_bin_n_ (h->value, token, token_len))
111 continue;
112 /* Match only if token string is full header value or token is
113 * followed by space or tab
114 * Note: RFC 9110 (and RFC 7234) allows only space character, but
115 * tab is supported here as well for additional flexibility and uniformity
116 * as tabs are supported as separators between parameters.
117 */
118 if ((token_len == h->value_size) ||
119 (' ' == h->value[token_len]) || ('\t' == h->value[token_len]))
120 {
121 if (token_len != h->value_size)
122 { /* Skip whitespace */
123 auth_value->str = h->value + token_len + 1;
124 auth_value->len = h->value_size - (token_len + 1);
125 }
126 else
127 { /* No whitespace to skip */
128 auth_value->str = h->value + token_len;
129 auth_value->len = h->value_size - token_len;
130 }
131 return true; /* Found a match */
132 }
133 }
134 return false; /* No matching header has been found */
135}
136
137
138#ifdef BAUTH_SUPPORT
139
140
150static bool
151parse_bauth_params (const char *str,
152 size_t str_len,
153 struct MHD_RqBAuth *pbauth)
154{
155 size_t i;
156
157 i = 0;
158
159 /* Skip all whitespaces at start */
160 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
161 i++;
162
163 if (str_len > i)
164 {
165 size_t token68_start;
166 size_t token68_len;
167
168 /* 'i' points to the first non-whitespace char after scheme token */
169 token68_start = i;
170 /* Find end of the token. Token cannot contain whitespace. */
171 while (i < str_len && ' ' != str[i] && '\t' != str[i])
172 {
173 if (0 == str[i])
174 return false; /* Binary zero is not allowed */
175 if ((',' == str[i]) || (';' == str[i]))
176 return false; /* Only single token68 is allowed */
177 i++;
178 }
179 token68_len = i - token68_start;
180 mhd_assert (0 != token68_len);
181
182 /* Skip all whitespaces */
183 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
184 i++;
185 /* Check whether any garbage is present at the end of the string */
186 if (str_len != i)
187 return false;
188 else
189 {
190 /* No more data in the string, only single token68. */
191 pbauth->token68.str = str + token68_start;
192 pbauth->token68.len = token68_len;
193 }
194 }
195 return true;
196}
197
198
211const struct MHD_RqBAuth *
212MHD_get_rq_bauth_params_ (struct MHD_Connection *connection)
213{
214 struct _MHD_str_w_len h_auth_value;
215 struct MHD_RqBAuth *bauth;
216
217 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
218
219 if (connection->rq.bauth_tried)
220 return connection->rq.bauth;
221
222 if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
223 return NULL;
224
225 if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_BASIC, &h_auth_value))
226 {
227 connection->rq.bauth_tried = true;
228 connection->rq.bauth = NULL;
229 return NULL;
230 }
231
232 bauth =
233 (struct MHD_RqBAuth *)
234 MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqBAuth));
235
236 if (NULL == bauth)
237 {
238#ifdef HAVE_MESSAGES
239 MHD_DLOG (connection->daemon,
240 _ ("Not enough memory in the connection's pool to allocate " \
241 "for Basic Authorization header parsing.\n"));
242#endif /* HAVE_MESSAGES */
243 return NULL;
244 }
245
246 memset (bauth, 0, sizeof(struct MHD_RqBAuth));
247 if (parse_bauth_params (h_auth_value.str, h_auth_value.len, bauth))
248 connection->rq.bauth = bauth;
249 else
250 {
251#ifdef HAVE_MESSAGES
252 MHD_DLOG (connection->daemon,
253 _ ("The Basic Authorization client's header has "
254 "incorrect format.\n"));
255#endif /* HAVE_MESSAGES */
256 connection->rq.bauth = NULL;
257 /* Memory in the pool remains allocated until next request */
258 }
259 connection->rq.bauth_tried = true;
260 return connection->rq.bauth;
261}
262
263
264#endif /* BAUTH_SUPPORT */
265
266#ifdef DAUTH_SUPPORT
267
268
275static enum MHD_DigestAuthAlgo3
276get_rq_dauth_algo (const struct MHD_RqDAuthParam *const algo_param)
277{
278 if (NULL == algo_param->value.str)
279 return MHD_DIGEST_AUTH_ALGO3_MD5; /* Assume MD5 by default */
280
281 if (algo_param->quoted)
282 {
283 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
284 algo_param->value.len, \
287 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
288 algo_param->value.len, \
291 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
292 algo_param->value.len, \
295 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
296 algo_param->value.len, \
299
300 /* Algorithms below are not supported by MHD for authentication */
301
303 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
304 algo_param->value.len, \
308 if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
309 algo_param->value.len, \
312
313 /* No known algorithm has been detected */
315 }
316 /* The algorithm value is not quoted */
318 algo_param->value.str, \
319 algo_param->value.len))
322 algo_param->value.str, \
323 algo_param->value.len))
326 algo_param->value.str, \
327 algo_param->value.len))
329
330 /* Algorithms below are not supported by MHD for authentication */
331
333 algo_param->value.str, \
334 algo_param->value.len))
337 algo_param->value.str, \
338 algo_param->value.len))
341 algo_param->value.str, \
342 algo_param->value.len))
344
345 /* No known algorithm has been detected */
347}
348
349
355static enum MHD_DigestAuthQOP
356get_rq_dauth_qop (const struct MHD_RqDAuthParam *const qop_param)
357{
358 if (NULL == qop_param->value.str)
360 if (qop_param->quoted)
361 {
362 if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
363 qop_param->value.len, \
366 if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
367 qop_param->value.len, \
370 }
371 else
372 {
374 qop_param->value.str, \
375 qop_param->value.len))
378 qop_param->value.str, \
379 qop_param->value.len))
381 }
382 /* No know QOP has been detected */
384}
385
386
396static bool
397parse_dauth_params (const char *str,
398 const size_t str_len,
399 struct MHD_RqDAuth *pdauth)
400{
401 /* The tokens */
402 static const struct _MHD_cstr_w_len nonce_tk = _MHD_S_STR_W_LEN ("nonce");
403 static const struct _MHD_cstr_w_len opaque_tk = _MHD_S_STR_W_LEN ("opaque");
404 static const struct _MHD_cstr_w_len algorithm_tk =
405 _MHD_S_STR_W_LEN ("algorithm");
406 static const struct _MHD_cstr_w_len response_tk =
407 _MHD_S_STR_W_LEN ("response");
408 static const struct _MHD_cstr_w_len username_tk =
409 _MHD_S_STR_W_LEN ("username");
410 static const struct _MHD_cstr_w_len username_ext_tk =
411 _MHD_S_STR_W_LEN ("username*");
412 static const struct _MHD_cstr_w_len realm_tk = _MHD_S_STR_W_LEN ("realm");
413 static const struct _MHD_cstr_w_len uri_tk = _MHD_S_STR_W_LEN ("uri");
414 static const struct _MHD_cstr_w_len qop_tk = _MHD_S_STR_W_LEN ("qop");
415 static const struct _MHD_cstr_w_len cnonce_tk = _MHD_S_STR_W_LEN ("cnonce");
416 static const struct _MHD_cstr_w_len nc_tk = _MHD_S_STR_W_LEN ("nc");
417 static const struct _MHD_cstr_w_len userhash_tk =
418 _MHD_S_STR_W_LEN ("userhash");
419 /* The locally processed parameters */
420 struct MHD_RqDAuthParam userhash;
421 struct MHD_RqDAuthParam algorithm;
422 /* Indexes */
423 size_t i;
424 size_t p;
425 /* The list of the tokens.
426 The order of the elements matches the next array. */
427 static const struct _MHD_cstr_w_len *const tk_names[] = {
428 &nonce_tk, /* 0 */
429 &opaque_tk, /* 1 */
430 &algorithm_tk, /* 2 */
431 &response_tk, /* 3 */
432 &username_tk, /* 4 */
433 &username_ext_tk, /* 5 */
434 &realm_tk, /* 6 */
435 &uri_tk, /* 7 */
436 &qop_tk, /* 8 */
437 &cnonce_tk, /* 9 */
438 &nc_tk, /* 10 */
439 &userhash_tk /* 11 */
440 };
441 /* The list of the parameters.
442 The order of the elements matches the previous array. */
443 struct MHD_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
444
445 params[0 ] = &(pdauth->nonce); /* 0 */
446 params[1 ] = &(pdauth->opaque); /* 1 */
447 params[2 ] = &algorithm; /* 2 */
448 params[3 ] = &(pdauth->response); /* 3 */
449 params[4 ] = &(pdauth->username); /* 4 */
450 params[5 ] = &(pdauth->username_ext); /* 5 */
451 params[6 ] = &(pdauth->realm); /* 6 */
452 params[7 ] = &(pdauth->uri); /* 7 */
453 params[8 ] = &(pdauth->qop_raw); /* 8 */
454 params[9 ] = &(pdauth->cnonce); /* 9 */
455 params[10] = &(pdauth->nc); /* 10 */
456 params[11] = &userhash; /* 11 */
457
458 mhd_assert ((sizeof(tk_names) / sizeof(tk_names[0])) == \
459 (sizeof(params) / sizeof(params[0])));
460 memset (&userhash, 0, sizeof(userhash));
461 memset (&algorithm, 0, sizeof(algorithm));
462 i = 0;
463
464 /* Skip all whitespaces at start */
465 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
466 i++;
467
468 while (str_len > i)
469 {
470 size_t left;
471 mhd_assert (' ' != str[i]);
472 mhd_assert ('\t' != str[i]);
473
474 left = str_len - i;
475 if ('=' == str[i])
476 return false; /* The equal sign is not allowed as the first character */
477 for (p = 0; p < (sizeof(tk_names) / sizeof(tk_names[0])); ++p)
478 {
479 const struct _MHD_cstr_w_len *const tk_name = tk_names[p];
480 struct MHD_RqDAuthParam *const param = params[p];
481 if ( (tk_name->len <= left) &&
482 MHD_str_equal_caseless_bin_n_ (str + i, tk_name->str,
483 tk_name->len) &&
484 ((tk_name->len == left) ||
485 ('=' == str[i + tk_name->len]) ||
486 (' ' == str[i + tk_name->len]) ||
487 ('\t' == str[i + tk_name->len]) ||
488 (',' == str[i + tk_name->len]) ||
489 (';' == str[i + tk_name->len])) )
490 {
491 size_t value_start;
492 size_t value_len;
493 bool quoted; /* Only mark as "quoted" if backslash-escape used */
494
495 if (tk_name->len == left)
496 return false; /* No equal sign after parameter name, broken data */
497
498 quoted = false;
499 i += tk_name->len;
500 /* Skip all whitespaces before '=' */
501 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
502 i++;
503 if ((i == str_len) || ('=' != str[i]))
504 return false; /* No equal sign, broken data */
505 i++;
506 /* Skip all whitespaces after '=' */
507 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
508 i++;
509 if ((str_len > i) && ('"' == str[i]))
510 { /* Value is in quotation marks */
511 i++; /* Advance after the opening quote */
512 value_start = i;
513 while (str_len > i && '"' != str[i])
514 {
515 if ('\\' == str[i])
516 {
517 i++;
518 quoted = true; /* Have escaped chars */
519 }
520 if (0 == str[i])
521 return false; /* Binary zero in parameter value */
522 i++;
523 }
524 if (str_len <= i)
525 return false; /* No closing quote */
526 mhd_assert ('"' == str[i]);
527 value_len = i - value_start;
528 i++; /* Advance after the closing quote */
529 }
530 else
531 {
532 value_start = i;
533 while (str_len > i && ',' != str[i] &&
534 ' ' != str[i] && '\t' != str[i] && ';' != str[i])
535 {
536 if (0 == str[i])
537 return false; /* Binary zero in parameter value */
538 i++;
539 }
540 if (';' == str[i])
541 return false; /* Semicolon in parameter value */
542 value_len = i - value_start;
543 }
544 /* Skip all whitespaces after parameter value */
545 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
546 i++;
547 if ((str_len > i) && (',' != str[i]))
548 return false; /* Garbage after parameter value */
549
550 /* Have valid parameter name and value */
551 mhd_assert (! quoted || 0 != value_len);
552 param->value.str = str + value_start;
553 param->value.len = value_len;
554 param->quoted = quoted;
555
556 break; /* Found matching parameter name */
557 }
558 }
559 if (p == (sizeof(tk_names) / sizeof(tk_names[0])))
560 {
561 /* No matching parameter name */
562 while (str_len > i && ',' != str[i])
563 {
564 if ((0 == str[i]) || (';' == str[i]))
565 return false; /* Not allowed characters */
566 if ('"' == str[i])
567 { /* Skip quoted part */
568 i++; /* Advance after the opening quote */
569 while (str_len > i && '"' != str[i])
570 {
571 if (0 == str[i])
572 return false; /* Binary zero is not allowed */
573 if ('\\' == str[i])
574 i++; /* Skip escaped char */
575 i++;
576 }
577 if (str_len <= i)
578 return false; /* No closing quote */
579 mhd_assert ('"' == str[i]);
580 }
581 i++;
582 }
583 }
584 mhd_assert (str_len == i || ',' == str[i]);
585 if (str_len > i)
586 i++; /* Advance after ',' */
587 /* Skip all whitespaces before next parameter name */
588 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
589 i++;
590 }
591
592 /* Postprocess values */
593
594 if (NULL != userhash.value.str)
595 {
596 if (userhash.quoted)
597 pdauth->userhash =
598 MHD_str_equal_caseless_quoted_s_bin_n (userhash.value.str, \
599 userhash.value.len, \
600 "true");
601 else
602 pdauth->userhash =
603 MHD_str_equal_caseless_s_bin_n_ ("true", userhash.value.str, \
604 userhash.value.len);
605
606 }
607 else
608 pdauth->userhash = false;
609
610 pdauth->algo3 = get_rq_dauth_algo (&algorithm);
611 pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
612
613 return true;
614}
615
616
629const struct MHD_RqDAuth *
630MHD_get_rq_dauth_params_ (struct MHD_Connection *connection)
631{
632 struct _MHD_str_w_len h_auth_value;
633 struct MHD_RqDAuth *dauth;
634
635 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
636
637 if (connection->rq.dauth_tried)
638 return connection->rq.dauth;
639
640 if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
641 return NULL;
642
643 if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_DIGEST, &h_auth_value))
644 {
645 connection->rq.dauth_tried = true;
646 connection->rq.dauth = NULL;
647 return NULL;
648 }
649
650 dauth =
651 (struct MHD_RqDAuth *)
652 MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqDAuth));
653
654 if (NULL == dauth)
655 {
656#ifdef HAVE_MESSAGES
657 MHD_DLOG (connection->daemon,
658 _ ("Not enough memory in the connection's pool to allocate " \
659 "for Digest Authorization header parsing.\n"));
660#endif /* HAVE_MESSAGES */
661 return NULL;
662 }
663
664 memset (dauth, 0, sizeof(struct MHD_RqDAuth));
665 if (parse_dauth_params (h_auth_value.str, h_auth_value.len, dauth))
666 connection->rq.dauth = dauth;
667 else
668 {
669#ifdef HAVE_MESSAGES
670 MHD_DLOG (connection->daemon,
671 _ ("The Digest Authorization client's header has "
672 "incorrect format.\n"));
673#endif /* HAVE_MESSAGES */
674 connection->rq.dauth = NULL;
675 /* Memory in the pool remains allocated until next request */
676 }
677 connection->rq.dauth_tried = true;
678 return connection->rq.dauth;
679}
680
681
682#endif /* DAUTH_SUPPORT */
#define _MHD_AUTH_BASIC_BASE
Definition basicauth.h:36
void * MHD_connection_alloc_memory_(struct MHD_Connection *connection, size_t size)
Definition connection.c:651
Methods for managing connections.
#define MHD_TOKEN_AUTH_INT_
Definition digestauth.h:80
#define _MHD_SHA256_TOKEN
Definition digestauth.h:60
#define _MHD_MD5_TOKEN
Definition digestauth.h:55
#define _MHD_SHA512_256_TOKEN
Definition digestauth.h:65
#define MHD_TOKEN_AUTH_
Definition digestauth.h:75
#define _MHD_SESS_TOKEN
Definition digestauth.h:70
#define _MHD_AUTH_DIGEST_BASE
Definition digestauth.h:50
static bool find_auth_rq_header_(const struct MHD_Connection *c, enum MHD_AuthType type, struct _MHD_str_w_len *auth_value)
Definition gen_auth.c:65
MHD_AuthType
Definition gen_auth.c:47
@ MHD_AUTHTYPE_BASIC
Definition gen_auth.c:49
@ MHD_AUTHTYPE_UNKNOWN
Definition gen_auth.c:51
@ MHD_AUTHTYPE_DIGEST
Definition gen_auth.c:50
@ MHD_AUTHTYPE_INVALID
Definition gen_auth.c:52
@ MHD_AUTHTYPE_NONE
Definition gen_auth.c:48
Declarations for HTTP authorisation general functions.
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition microhttpd.h:578
#define mhd_assert(CHK)
Definition mhd_assert.h:39
#define MHD_STATICSTR_LEN_(macro)
Definition mhd_str.h:45
#define NULL
#define _(String)
Definition mhd_options.h:42
#define _MHD_S_STR_W_LEN(str)
MHD internal shared structures.
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
macros for mhd_assert()
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:749
Header for string manipulating helpers.
#define MHD_str_equal_caseless_s_bin_n_(a, s, l)
Definition mhd_str.h:122
MHD_DigestAuthAlgo3
@ MHD_DIGEST_AUTH_ALGO3_MD5_SESSION
@ MHD_DIGEST_AUTH_ALGO3_MD5
@ MHD_DIGEST_AUTH_ALGO3_SHA256
@ MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION
@ MHD_DIGEST_AUTH_ALGO3_INVALID
@ MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION
@ MHD_DIGEST_AUTH_ALGO3_SHA512_256
MHD_DigestAuthQOP
@ MHD_DIGEST_AUTH_QOP_AUTH
@ MHD_DIGEST_AUTH_QOP_INVALID
@ MHD_DIGEST_AUTH_QOP_NONE
@ MHD_DIGEST_AUTH_QOP_AUTH_INT
@ MHD_HEADER_KIND
struct MHD_Request rq
Definition internal.h:1365
enum MHD_CONNECTION_STATE state
Definition internal.h:1565
struct MHD_Daemon * daemon
Definition internal.h:675
enum MHD_ValueKind kind
Definition internal.h:396
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
struct MHD_HTTP_Header * headers_received
Definition internal.h:388
struct _MHD_str_w_len token68
Definition basicauth.h:40
const char *const str
const size_t len
const char * str