aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/gen_auth.c
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-30 21:54:09 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-31 11:45:36 +0300
commit9039d65241daf512e7756319cd64d3d54750cb22 (patch)
tree9c109e217ad1014a89de5495fcebdc1723672e31 /src/microhttpd/gen_auth.c
parentc15077e312ffe934b242c7ecc6559c6eb4eb62cb (diff)
downloadlibmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.tar.gz
libmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.zip
authentication: reworked header parsing
Added single function to parse all enabled authentication schemes header strings. The parsing result is cached and reused thus avoiding repetitive header parsing. The new function correctly "unquotes" values (backslashes are removed) as required by RFC.
Diffstat (limited to 'src/microhttpd/gen_auth.c')
-rw-r--r--src/microhttpd/gen_auth.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/src/microhttpd/gen_auth.c b/src/microhttpd/gen_auth.c
new file mode 100644
index 00000000..e993fb20
--- /dev/null
+++ b/src/microhttpd/gen_auth.c
@@ -0,0 +1,511 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2022 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
20/**
21 * @file microhttpd/gen_auth.c
22 * @brief HTTP authorisation general functions
23 * @author Karlson2k (Evgeny Grin)
24 */
25
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
43#ifdef BAUTH_SUPPORT
44/**
45 * Parse request Authorization header parameters for Basic Authentication
46 * @param str the header string, everything after "Basic " substring
47 * @param str_len the length of @a str in characters
48 * @param[out] pbauth the pointer to the structure with Basic Authentication
49 * parameters
50 * @return true if parameters has been successfully parsed,
51 * false if format of the @a str is invalid
52 */
53static bool
54parse_bauth_params (const char *str,
55 size_t str_len,
56 struct MHD_RqBAuth *pbauth)
57{
58 size_t i;
59
60 i = 0;
61
62 /* Skip all whitespaces at start */
63 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
64 i++;
65
66 if (str_len > i)
67 {
68 size_t token68_start;
69 size_t token68_len;
70
71 /* 'i' points to the first non-whitespace char after scheme token */
72 token68_start = i;
73 /* Find end of the token. Token cannot contain whitespace. */
74 while (i < str_len && ' ' != str[i] && '\t' != str[i])
75 {
76 if (0 == str[0])
77 return false; /* Binary zero is not allowed */
78 i++;
79 }
80 token68_len = i - token68_start;
81 mhd_assert (0 != token68_len);
82
83 /* Skip all whitespaces */
84 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
85 i++;
86 /* Check whether any garbage is present at the end of the string */
87 if (str_len != i)
88 return false;
89 else
90 {
91 /* No more data in the string, only single token68. */
92 const struct _MHD_cstr_w_len tkn = { str + token68_start, token68_len};
93 memcpy (&pbauth->token68, &tkn, sizeof(tkn));
94 }
95 }
96 return true;
97}
98
99
100#endif /* BAUTH_SUPPORT */
101
102#ifdef DAUTH_SUPPORT
103
104/**
105 * Helper structure to map token name to position where to put token's value
106 */
107struct dauth_token_param
108{
109 const struct _MHD_cstr_w_len *const tk_name;
110 struct MHD_RqDAuthParam *const param;
111};
112
113/**
114 * Parse request Authorization header parameters for Digest Authentication
115 * @param str the header string, everything after "Digest " substring
116 * @param str_len the length of @a str in characters
117 * @param[out] pdauth the pointer to the structure with Digest Authentication
118 * parameters
119 * @return true if parameters has been successfully parsed,
120 * false if format of the @a str is invalid
121 */
122static bool
123parse_dauth_params (const char *str,
124 const size_t str_len,
125 struct MHD_RqDAuth *pdauth)
126{
127 static const struct _MHD_cstr_w_len nonce_tk = _MHD_S_STR_W_LEN ("nonce");
128 static const struct _MHD_cstr_w_len opaque_tk = _MHD_S_STR_W_LEN ("opaque");
129 static const struct _MHD_cstr_w_len algorithm_tk =
130 _MHD_S_STR_W_LEN ("algorithm");
131 static const struct _MHD_cstr_w_len response_tk =
132 _MHD_S_STR_W_LEN ("response");
133 static const struct _MHD_cstr_w_len username_tk =
134 _MHD_S_STR_W_LEN ("username");
135 static const struct _MHD_cstr_w_len username_ext_tk =
136 _MHD_S_STR_W_LEN ("username*");
137 static const struct _MHD_cstr_w_len realm_tk = _MHD_S_STR_W_LEN ("realm");
138 static const struct _MHD_cstr_w_len uri_tk = _MHD_S_STR_W_LEN ("uri");
139 static const struct _MHD_cstr_w_len qop_tk = _MHD_S_STR_W_LEN ("qop");
140 static const struct _MHD_cstr_w_len cnonce_tk = _MHD_S_STR_W_LEN ("cnonce");
141 static const struct _MHD_cstr_w_len nc_tk = _MHD_S_STR_W_LEN ("nc");
142 static const struct _MHD_cstr_w_len userhash_tk =
143 _MHD_S_STR_W_LEN ("userhash");
144 struct MHD_RqDAuthParam userhash;
145 struct dauth_token_param map[] = {
146 {&nonce_tk, &(pdauth->nonce)},
147 {&opaque_tk, &(pdauth->opaque)},
148 {&algorithm_tk, &(pdauth->algorithm)},
149 {&response_tk, &(pdauth->response)},
150 {&username_tk, &(pdauth->username)},
151 {&username_ext_tk, &(pdauth->username_ext)},
152 {&realm_tk, &(pdauth->realm)},
153 {&uri_tk, &(pdauth->uri)},
154 {&qop_tk, &(pdauth->qop)},
155 {&cnonce_tk, &(pdauth->cnonce)},
156 {&nc_tk, &(pdauth->nc)},
157 {&userhash_tk, &userhash}
158 };
159 size_t i;
160 size_t p;
161
162 memset (&userhash, 0, sizeof(userhash));
163 i = 0;
164
165 /* Skip all whitespaces at start */
166 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
167 i++;
168
169 while (str_len > i)
170 {
171 size_t left;
172 mhd_assert (' ' != str[i]);
173 mhd_assert ('\t' != str[i]);
174
175 left = str_len - i;
176 for (p = 0; p < sizeof(map) / sizeof(map[0]); p++)
177 {
178 struct dauth_token_param *const aparam = map + p;
179 if ( (aparam->tk_name->len < left) &&
180 MHD_str_equal_caseless_bin_n_ (str + i, aparam->tk_name->str,
181 aparam->tk_name->len) &&
182 (('=' == str[i + aparam->tk_name->len]) ||
183 (' ' == str[i + aparam->tk_name->len]) ||
184 ('\t' == str[i + aparam->tk_name->len])) )
185 {
186 size_t value_start;
187 size_t value_len;
188 bool quoted; /* Only mark as "quoted" if backslash-escape used */
189
190 quoted = false;
191 i += aparam->tk_name->len;
192 /* Skip all whitespaces before '=' */
193 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
194 i++;
195 if ((i == str_len) || ('=' != str[i]))
196 return false; /* No equal sign, broken data */
197 i++;
198 /* Skip all whitespaces after '=' */
199 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
200 i++;
201 if ((str_len > i) && ('"' == str[i]))
202 { /* Value is in quotation marks */
203 i++; /* Advance after the opening quote */
204 value_start = i;
205 while (str_len > i && '"' != str[i])
206 {
207 if ('\\' == str[i])
208 {
209 i++;
210 quoted = true; /* Have escaped chars */
211 }
212 if (0 == str[i])
213 return false; /* Binary zero in parameter value */
214 i++;
215 }
216 if (str_len <= i)
217 return false; /* No closing quote */
218 mhd_assert ('"' == str[i]);
219 value_len = i - value_start;
220 i++; /* Advance after the closing quote */
221 }
222 else
223 {
224 value_start = i;
225 while (str_len > i && ',' != str[i] &&
226 ' ' != str[i] && '\t' != str[i] && ';' != str[i])
227 {
228 if (0 == str[i])
229 return false; /* Binary zero in parameter value */
230 i++;
231 }
232 value_len = i - value_start;
233 }
234 /* Skip all whitespaces after parameter value */
235 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
236 i++;
237 if ((str_len > i) && (',' != str[i]))
238 return false; /* Garbage after parameter value */
239
240 /* Have valid parameter name and value */
241 mhd_assert (! quoted || 0 != value_len);
242 if (1)
243 {
244 const struct _MHD_cstr_w_len val = {str + value_start, value_len};
245 memcpy (&aparam->param->value, &val, sizeof(val));
246 }
247 aparam->param->quoted = quoted;
248
249 break; /* Found matching parameter name */
250 }
251 }
252 if (p == sizeof(map) / sizeof(map[0]))
253 {
254 /* No matching parameter name */
255 while (str_len > i && ',' != str[i])
256 {
257 if ('"' == str[i])
258 { /* Skip quoted part */
259 i++; /* Advance after the opening quote */
260 while (str_len > i && '"' != str[i])
261 {
262 if ('\\' == str[i])
263 i++; /* Skip escaped char */
264 i++;
265 }
266 if (str_len <= i)
267 return false; /* No closing quote */
268 mhd_assert ('"' == str[i]);
269 }
270 i++;
271 }
272 }
273 mhd_assert (str_len == i || ',' == str[i]);
274 if (str_len > i)
275 i++; /* Advance after ',' */
276 /* Skip all whitespaces before next parameter name */
277 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
278 i++;
279 }
280
281 /* Postprocess values */
282 if ((NULL != userhash.value.str) && (0 != userhash.value.len))
283 {
284 const char *param_str;
285 size_t param_len;
286 char buf[5 * 2]; /* 5 is the length of "false" (longer then "true") */
287 if (! userhash.quoted)
288 {
289 param_str = userhash.value.str;
290 param_len = userhash.value.len;
291 }
292 else
293 {
294 if (sizeof(buf) / sizeof(buf[0]) >= userhash.value.len)
295 {
296 param_len = MHD_str_unquote (userhash.value.str, userhash.value.len,
297 buf);
298 param_str = buf;
299 }
300 else
301 param_len = 0;
302 }
303 if ((param_len == 4) && MHD_str_equal_caseless_bin_n_ (param_str, "true",
304 4))
305 pdauth->userhash = true;
306 else
307 pdauth->userhash = false;
308 }
309 else
310 pdauth->userhash = false;
311
312 return true;
313}
314
315
316#endif /* DAUTH_SUPPORT */
317
318
319/**
320 * Parse request "Authorization" header
321 * @param c the connection to process
322 * @return true if any supported Authorisation scheme were found,
323 * false if no "Authorization" header found, no supported scheme found,
324 * or an error occurred.
325 */
326_MHD_static_inline bool
327parse_auth_rq_header_ (struct MHD_Connection *c)
328{
329 const char *h; /**< The "Authorization" header */
330 size_t h_len;
331 struct MHD_AuthRqHeader *rq_auth;
332 size_t i;
333
334 mhd_assert (NULL == c->rq_auth);
335 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
336 if (MHD_CONNECTION_HEADERS_PROCESSED > c->state)
337 return false;
338
339 if (MHD_NO ==
340 MHD_lookup_connection_value_n (c, MHD_HEADER_KIND,
341 MHD_HTTP_HEADER_AUTHORIZATION,
342 MHD_STATICSTR_LEN_ ( \
343 MHD_HTTP_HEADER_AUTHORIZATION), &h,
344 &h_len))
345 {
346 rq_auth = (struct MHD_AuthRqHeader *)
347 MHD_connection_alloc_memory_ (c,
348 sizeof (struct MHD_AuthRqHeader));
349 c->rq_auth = rq_auth;
350 if (NULL != rq_auth)
351 {
352 memset (rq_auth, 0, sizeof(struct MHD_AuthRqHeader));
353 rq_auth->auth_type = MHD_AUTHTYPE_NONE;
354 }
355 return false;
356 }
357
358 rq_auth = NULL;
359 i = 0;
360 /* Skip the leading whitespace */
361 while (i < h_len)
362 {
363 const char ch = h[i];
364 if ((' ' != ch) && ('\t' != ch))
365 break;
366 i++;
367 }
368 h += i;
369 h_len -= i;
370
371#ifdef DAUTH_SUPPORT
372 if (1)
373 {
374 static const struct _MHD_cstr_w_len scheme_token =
375 _MHD_S_STR_W_LEN (_MHD_AUTH_DIGEST_BASE);
376
377 if ((scheme_token.len <= h_len) &&
378 MHD_str_equal_caseless_bin_n_ (h, scheme_token.str, scheme_token.len))
379 {
380 i = scheme_token.len;
381 /* RFC 7235 require only space after scheme token */
382 if ( (h_len <= i) ||
383 ((' ' == h[i]) || ('\t' == h[i])) ) /* Actually tab should NOT be allowed */
384 { /* Matched Digest authorisation scheme */
385 i++; /* Advance to the next char (even if it is beyond the end of the string) */
386
387 rq_auth = (struct MHD_AuthRqHeader *)
388 MHD_connection_alloc_memory_ (c,
389 sizeof (struct MHD_AuthRqHeader)
390 + sizeof (struct MHD_RqDAuth));
391 c->rq_auth = rq_auth;
392 if (NULL == rq_auth)
393 {
394#ifdef HAVE_MESSAGES
395 MHD_DLOG (c->daemon,
396 _ ("Failed to allocate memory in connection pool to " \
397 "process \"" MHD_HTTP_HEADER_AUTHORIZATION "\" " \
398 "header.\n"));
399#endif /* HAVE_MESSAGES */
400 return false;
401 }
402 memset (rq_auth, 0, sizeof (struct MHD_AuthRqHeader)
403 + sizeof (struct MHD_RqDAuth));
404 rq_auth->params.dauth = (struct MHD_RqDAuth *) (rq_auth + 1);
405
406 if (h_len > i)
407 {
408 if (! parse_dauth_params (h + i, h_len - i, rq_auth->params.dauth))
409 {
410 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
411 return false;
412 }
413 }
414
415 rq_auth->auth_type = MHD_AUTHTYPE_DIGEST;
416 return true;
417 }
418 }
419 }
420#endif /* DAUTH_SUPPORT */
421#ifdef BAUTH_SUPPORT
422 if (1)
423 {
424 static const struct _MHD_cstr_w_len scheme_token =
425 _MHD_S_STR_W_LEN (_MHD_AUTH_BASIC_BASE);
426
427 if ((scheme_token.len <= h_len) &&
428 MHD_str_equal_caseless_bin_n_ (h, scheme_token.str, scheme_token.len))
429 {
430 i = scheme_token.len;
431 /* RFC 7235 require only space after scheme token */
432 if ( (h_len <= i) ||
433 ((' ' == h[i]) || ('\t' == h[i])) ) /* Actually tab should NOT be allowed */
434 { /* Matched Basic authorisation scheme */
435 i++; /* Advance to the next char (even if it is beyond the end of the string) */
436
437 rq_auth = (struct MHD_AuthRqHeader *)
438 MHD_connection_alloc_memory_ (c,
439 sizeof (struct MHD_AuthRqHeader)
440 + sizeof (struct MHD_RqBAuth));
441 c->rq_auth = rq_auth;
442 if (NULL == rq_auth)
443 {
444#ifdef HAVE_MESSAGES
445 MHD_DLOG (c->daemon,
446 _ ("Failed to allocate memory in connection pool to " \
447 "process \"" MHD_HTTP_HEADER_AUTHORIZATION "\" " \
448 "header.\n"));
449#endif /* HAVE_MESSAGES */
450 return false;
451 }
452 memset (rq_auth, 0, sizeof (struct MHD_AuthRqHeader)
453 + sizeof (struct MHD_RqBAuth));
454 rq_auth->params.bauth = (struct MHD_RqBAuth *) (rq_auth + 1);
455
456 if (h_len > i)
457 {
458 if (! parse_bauth_params (h + i, h_len - i, rq_auth->params.bauth))
459 {
460 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
461 return false;
462 }
463 }
464
465 rq_auth->auth_type = MHD_AUTHTYPE_BASIC;
466 return true;
467 }
468 }
469 }
470#endif /* BAUTH_SUPPORT */
471
472 if (NULL == rq_auth)
473 rq_auth = (struct MHD_AuthRqHeader *)
474 MHD_connection_alloc_memory_ (c,
475 sizeof (struct MHD_AuthRqHeader));
476 c->rq_auth = rq_auth;
477 if (NULL != rq_auth)
478 {
479 memset (rq_auth, 0, sizeof(struct MHD_AuthRqHeader));
480 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
481 }
482 return false;
483}
484
485
486/**
487 * Return request's Authentication type and parameters.
488 *
489 * Function return result of parsing of the request's "Authorization" header or
490 * returns cached parsing result if the header was already parsed for
491 * the current request.
492 * @param connection the connection to process
493 * @return the pointer to structure with Authentication type and parameters,
494 * NULL if no memory in memory pool or if called too early (before
495 * header has been received).
496 */
497const struct MHD_AuthRqHeader *
498MHD_get_auth_rq_params_ (struct MHD_Connection *connection)
499{
500 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
501
502 if (NULL != connection->rq_auth)
503 return connection->rq_auth;
504
505 if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
506 return NULL;
507
508 parse_auth_rq_header_ (connection);
509
510 return connection->rq_auth;
511}