aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-21 14:53:25 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-21 15:07:08 +0300
commit22796735892fae00fdc84fa5e2b29a2d6a2becdd (patch)
tree68fcf20cf74ed2e682e61b852a4705a9f1d0776b
parentaac0cc55ead7073d081576347ee92d88e262bc39 (diff)
downloadlibmicrohttpd-22796735892fae00fdc84fa5e2b29a2d6a2becdd.tar.gz
libmicrohttpd-22796735892fae00fdc84fa5e2b29a2d6a2becdd.zip
Added test for Digest Auth with username in extended notation
As libcurl does not support extended notation, it is not completely real test. Pregenerated fake nonce, cnonce and response are used so only parsing of request header and checking request header are tested. No real authorisation.
-rw-r--r--src/testcurl/.gitignore4
-rw-r--r--src/testcurl/Makefile.am29
-rw-r--r--src/testcurl/test_digestauth_emu_ext.c868
3 files changed, 891 insertions, 10 deletions
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index f3ffee14..e23f52c9 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -154,4 +154,6 @@ core
154/test_parse_cookies_invalid 154/test_parse_cookies_invalid
155/test_basicauth_preauth 155/test_basicauth_preauth
156/test_basicauth_oldapi 156/test_basicauth_oldapi
157/test_basicauth_preauth_oldapi \ No newline at end of file 157/test_basicauth_preauth_oldapi
158/test_digestauth_emu_ext
159/test_digestauth_emu_ext_oldapi
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 3bf81e7d..e46bbb2d 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -71,14 +71,6 @@ THREAD_ONLY_TESTS += \
71endif 71endif
72endif 72endif
73 73
74if ENABLE_DAUTH
75THREAD_ONLY_TESTS += \
76 test_digestauth \
77 test_digestauth_sha256 \
78 test_digestauth_with_arguments \
79 test_digestauth_concurrent
80endif
81
82if HEAVY_TESTS 74if HEAVY_TESTS
83if HAVE_POSIX_THREADS 75if HAVE_POSIX_THREADS
84THREAD_ONLY_TESTS += \ 76THREAD_ONLY_TESTS += \
@@ -158,7 +150,7 @@ check_PROGRAMS += \
158endif 150endif
159 151
160if HAVE_POSTPROCESSOR 152if HAVE_POSTPROCESSOR
161 check_PROGRAMS += \ 153check_PROGRAMS += \
162 test_post \ 154 test_post \
163 test_postform \ 155 test_postform \
164 test_post_loop \ 156 test_post_loop \
@@ -167,6 +159,19 @@ if HAVE_POSTPROCESSOR
167 test_post_loop11 159 test_post_loop11
168endif 160endif
169 161
162
163if ENABLE_DAUTH
164THREAD_ONLY_TESTS += \
165 test_digestauth \
166 test_digestauth_sha256 \
167 test_digestauth_with_arguments \
168 test_digestauth_concurrent
169
170check_PROGRAMS += \
171 test_digestauth_emu_ext \
172 test_digestauth_emu_ext_oldapi
173endif
174
170if HEAVY_TESTS 175if HEAVY_TESTS
171if HAVE_FORK_WAITPID 176if HAVE_FORK_WAITPID
172if HAVE_CURL_BINARY 177if HAVE_CURL_BINARY
@@ -268,6 +273,12 @@ test_digestauth_concurrent_CFLAGS = \
268test_digestauth_concurrent_LDADD = \ 273test_digestauth_concurrent_LDADD = \
269 @LIBGCRYPT_LIBS@ $(LDADD) $(PTHREAD_LIBS) $(LDADD) 274 @LIBGCRYPT_LIBS@ $(LDADD) $(PTHREAD_LIBS) $(LDADD)
270 275
276test_digestauth_emu_ext_SOURCES = \
277 test_digestauth_emu_ext.c
278
279test_digestauth_emu_ext_oldapi_SOURCES = \
280 test_digestauth_emu_ext.c
281
271test_get_iovec_SOURCES = \ 282test_get_iovec_SOURCES = \
272 test_get_iovec.c mhd_has_in_name.h 283 test_get_iovec.c mhd_has_in_name.h
273 284
diff --git a/src/testcurl/test_digestauth_emu_ext.c b/src/testcurl/test_digestauth_emu_ext.c
new file mode 100644
index 00000000..740f43eb
--- /dev/null
+++ b/src/testcurl/test_digestauth_emu_ext.c
@@ -0,0 +1,868 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2010 Christian Grothoff
4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
5
6 libmicrohttpd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 libmicrohttpd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with libmicrohttpd; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22/**
23 * @file test_digest_emu_ext.c
24 * @brief Testcase for MHD Digest Authorisation client's header parsing
25 * @author Karlson2k (Evgeny Grin)
26 *
27 * libcurl does not support extended notation for username, so this test
28 * "emulates" client request will all valid fields except nonce, cnonce and
29 * response (however syntactically these fields valid as well).
30 */
31
32#include "MHD_config.h"
33#include "platform.h"
34#include <curl/curl.h>
35#include <microhttpd.h>
36#include <stdlib.h>
37#include <string.h>
38#include <time.h>
39
40#ifndef WINDOWS
41#include <sys/socket.h>
42#include <unistd.h>
43#else
44#include <wincrypt.h>
45#endif
46
47#include "mhd_has_param.h"
48#include "mhd_has_in_name.h"
49
50#ifndef MHD_STATICSTR_LEN_
51/**
52 * Determine length of static string / macro strings at compile time.
53 */
54#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
55#endif /* ! MHD_STATICSTR_LEN_ */
56
57#ifndef CURL_VERSION_BITS
58#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
59#endif /* ! CURL_VERSION_BITS */
60#ifndef CURL_AT_LEAST_VERSION
61#define CURL_AT_LEAST_VERSION(x,y,z) \
62 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
63#endif /* ! CURL_AT_LEAST_VERSION */
64
65#ifndef _MHD_INSTRMACRO
66/* Quoted macro parameter */
67#define _MHD_INSTRMACRO(a) #a
68#endif /* ! _MHD_INSTRMACRO */
69#ifndef _MHD_STRMACRO
70/* Quoted expanded macro parameter */
71#define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
72#endif /* ! _MHD_STRMACRO */
73
74#if defined(HAVE___FUNC__)
75#define externalErrorExit(ignore) \
76 _externalErrorExit_func(NULL, __func__, __LINE__)
77#define externalErrorExitDesc(errDesc) \
78 _externalErrorExit_func(errDesc, __func__, __LINE__)
79#define libcurlErrorExit(ignore) \
80 _libcurlErrorExit_func(NULL, __func__, __LINE__)
81#define libcurlErrorExitDesc(errDesc) \
82 _libcurlErrorExit_func(errDesc, __func__, __LINE__)
83#define mhdErrorExit(ignore) \
84 _mhdErrorExit_func(NULL, __func__, __LINE__)
85#define mhdErrorExitDesc(errDesc) \
86 _mhdErrorExit_func(errDesc, __func__, __LINE__)
87#define checkCURLE_OK(libcurlcall) \
88 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
89 __func__, __LINE__)
90#elif defined(HAVE___FUNCTION__)
91#define externalErrorExit(ignore) \
92 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
93#define externalErrorExitDesc(errDesc) \
94 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
95#define libcurlErrorExit(ignore) \
96 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
97#define libcurlErrorExitDesc(errDesc) \
98 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
99#define mhdErrorExit(ignore) \
100 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
101#define mhdErrorExitDesc(errDesc) \
102 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
103#define checkCURLE_OK(libcurlcall) \
104 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
105 __FUNCTION__, __LINE__)
106#else
107#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
108#define externalErrorExitDesc(errDesc) \
109 _externalErrorExit_func(errDesc, NULL, __LINE__)
110#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
111#define libcurlErrorExitDesc(errDesc) \
112 _libcurlErrorExit_func(errDesc, NULL, __LINE__)
113#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
114#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
115#define checkCURLE_OK(libcurlcall) \
116 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__)
117#endif
118
119
120_MHD_NORETURN static void
121_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
122{
123 fflush (stdout);
124 if ((NULL != errDesc) && (0 != errDesc[0]))
125 fprintf (stderr, "%s", errDesc);
126 else
127 fprintf (stderr, "System or external library call failed");
128 if ((NULL != funcName) && (0 != funcName[0]))
129 fprintf (stderr, " in %s", funcName);
130 if (0 < lineNum)
131 fprintf (stderr, " at line %d", lineNum);
132
133 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
134 strerror (errno));
135#ifdef MHD_WINSOCK_SOCKETS
136 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
137#endif /* MHD_WINSOCK_SOCKETS */
138 fflush (stderr);
139 exit (99);
140}
141
142
143/* Not actually used in this test */
144static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
145
146_MHD_NORETURN static void
147_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
148{
149 fflush (stdout);
150 if ((NULL != errDesc) && (0 != errDesc[0]))
151 fprintf (stderr, "%s", errDesc);
152 else
153 fprintf (stderr, "CURL library call failed");
154 if ((NULL != funcName) && (0 != funcName[0]))
155 fprintf (stderr, " in %s", funcName);
156 if (0 < lineNum)
157 fprintf (stderr, " at line %d", lineNum);
158
159 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
160 strerror (errno));
161#ifdef MHD_WINSOCK_SOCKETS
162 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
163#endif /* MHD_WINSOCK_SOCKETS */
164 if (0 != libcurl_errbuf[0])
165 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
166
167 fflush (stderr);
168 exit (99);
169}
170
171
172_MHD_NORETURN static void
173_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
174{
175 fflush (stdout);
176 if ((NULL != errDesc) && (0 != errDesc[0]))
177 fprintf (stderr, "%s", errDesc);
178 else
179 fprintf (stderr, "MHD unexpected error");
180 if ((NULL != funcName) && (0 != funcName[0]))
181 fprintf (stderr, " in %s", funcName);
182 if (0 < lineNum)
183 fprintf (stderr, " at line %d", lineNum);
184
185 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
186 strerror (errno));
187#ifdef MHD_WINSOCK_SOCKETS
188 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
189#endif /* MHD_WINSOCK_SOCKETS */
190
191 fflush (stderr);
192 exit (8);
193}
194
195
196#if 0
197/* Function unused in this test */
198static void
199_checkCURLE_OK_func (CURLcode code, const char *curlFunc,
200 const char *funcName, int lineNum)
201{
202 if (CURLE_OK == code)
203 return;
204
205 fflush (stdout);
206 if ((NULL != curlFunc) && (0 != curlFunc[0]))
207 fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
208 curl_easy_strerror (code));
209 else
210 fprintf (stderr, "libcurl function call resulted in '%s'",
211 curl_easy_strerror (code));
212 if ((NULL != funcName) && (0 != funcName[0]))
213 fprintf (stderr, " in %s", funcName);
214 if (0 < lineNum)
215 fprintf (stderr, " at line %d", lineNum);
216
217 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
218 strerror (errno));
219 if (0 != libcurl_errbuf[0])
220 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
221
222 fflush (stderr);
223 exit (9);
224}
225
226
227#endif
228
229
230/* Could be increased to facilitate debugging */
231#define TIMEOUTS_VAL 10
232
233#define MHD_URI_BASE_PATH "/bar%20foo?key=value"
234
235#define REALM "TestRealm"
236/* "titkos szuperügynök" in UTF-8 */
237#define USERNAME "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
238/* percent-encoded username */
239#define USERNAME_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k"
240#define PASSWORD "fake pass"
241#define OPAQUE_VALUE "opaque-content"
242#define NONCE_EMU "badbadbadbadbadbadbadbadbadbadbadbadbadbadba"
243#define CNONCE_EMU "utututututututututututututututututututututs="
244#define RESPONSE_EMU "badbadbadbadbadbadbadbadbadbadba"
245
246
247#define PAGE \
248 "<html><head><title>libmicrohttpd demo page</title>" \
249 "</head><body>Access granted</body></html>"
250
251#define DENIED \
252 "<html><head><title>libmicrohttpd - Access denied</title>" \
253 "</head><body>Access denied</body></html>"
254
255struct CBC
256{
257 char *buf;
258 size_t pos;
259 size_t size;
260};
261
262/* Global parameters */
263static int verbose;
264static int oldapi;
265
266/* Static helper variables */
267struct curl_slist *curl_headers;
268
269static void
270test_global_init (void)
271{
272 libcurl_errbuf[0] = 0;
273
274 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
275 externalErrorExit ();
276
277 curl_headers = NULL;
278 curl_headers =
279 curl_slist_append (curl_headers,
280 "Authorization: "
281 "Digest username*=UTF-8''" USERNAME_PCTENC ", "
282 "realm=\"" REALM "\", "
283 "nonce=\"" NONCE_EMU "\", "
284 "uri=\"" MHD_URI_BASE_PATH "\", "
285 "cnonce=\"" CNONCE_EMU "\", "
286 "nc=00000001, "
287 "qop=auth, "
288 "response=\"" RESPONSE_EMU "\", "
289 "opaque=\"" OPAQUE_VALUE "\", "
290 "algorithm=MD5");
291 if (NULL == curl_headers)
292 externalErrorExit ();
293}
294
295
296static void
297test_global_cleanup (void)
298{
299 curl_slist_free_all (curl_headers);
300 curl_headers = NULL;
301 curl_global_cleanup ();
302}
303
304
305static size_t
306copyBuffer (void *ptr,
307 size_t size,
308 size_t nmemb,
309 void *ctx)
310{
311 struct CBC *cbc = ctx;
312
313 if (cbc->pos + size * nmemb > cbc->size)
314 mhdErrorExitDesc ("Wrong too large data"); /* overflow */
315 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
316 cbc->pos += size * nmemb;
317 return size * nmemb;
318}
319
320
321static enum MHD_Result
322ahc_echo (void *cls,
323 struct MHD_Connection *connection,
324 const char *url,
325 const char *method,
326 const char *version,
327 const char *upload_data,
328 size_t *upload_data_size,
329 void **req_cls)
330{
331 struct MHD_Response *response;
332 enum MHD_Result ret;
333 static int already_called_marker;
334 (void) cls; (void) url; /* Unused. Silent compiler warning. */
335 (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
336 (void) upload_data_size; /* Unused. Silent compiler warning. */
337
338 if (&already_called_marker != *req_cls)
339 { /* Called for the first time, request not fully read yet */
340 *req_cls = &already_called_marker;
341 /* Wait for complete request */
342 return MHD_YES;
343 }
344
345 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
346 mhdErrorExitDesc ("Unexpected HTTP method");
347
348 if (! oldapi)
349 {
350 struct MHD_DigestAuthUsernameInfo *creds;
351 struct MHD_DigestAuthInfo *dinfo;
352 enum MHD_DigestAuthResult check_res;
353
354 creds = MHD_digest_auth_get_username3 (connection);
355 if (NULL == creds)
356 mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
357 else if (NULL == creds->username)
358 mhdErrorExitDesc ("'username' is NULL");
359 else if (creds->username_len != MHD_STATICSTR_LEN_ (USERNAME))
360 {
361 fprintf (stderr, "'username_len' does not match.\n"
362 "Expected: %u\tRecieved: %u. ",
363 (unsigned) MHD_STATICSTR_LEN_ (USERNAME),
364 (unsigned) creds->username_len);
365 mhdErrorExitDesc ("Wrong 'username_len'");
366 }
367 else if (0 != memcmp (creds->username, USERNAME, creds->username_len))
368 {
369 fprintf (stderr, "'username' does not match.\n"
370 "Expected: '%s'\tRecieved: '%.*s'. ",
371 USERNAME,
372 (int) creds->username_len,
373 creds->username);
374 mhdErrorExitDesc ("Wrong 'username'");
375 }
376 else if (NULL != creds->userhash_bin)
377 mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
378 else if (0 != creds->userhash_bin_size)
379 mhdErrorExitDesc ("'userhash_bin_size' is NOT zero");
380 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != creds->uname_type)
381 {
382 fprintf (stderr, "Unexpected 'uname_type'.\n"
383 "Expected: %d\tRecieved: %d. ",
384 (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
385 (int) creds->uname_type);
386 mhdErrorExitDesc ("Wrong 'uname_type'");
387 }
388 MHD_free (creds);
389
390 dinfo = MHD_digest_auth_get_request_info3 (connection);
391 if (NULL == dinfo)
392 mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
393 else if (NULL == dinfo->username)
394 mhdErrorExitDesc ("'username' is NULL");
395 else if (dinfo->username_len != MHD_STATICSTR_LEN_ (USERNAME))
396 {
397 fprintf (stderr, "'username_len' does not match.\n"
398 "Expected: %u\tRecieved: %u. ",
399 (unsigned) MHD_STATICSTR_LEN_ (USERNAME),
400 (unsigned) dinfo->username_len);
401 mhdErrorExitDesc ("Wrong 'username_len'");
402 }
403 else if (0 != memcmp (dinfo->username, USERNAME, dinfo->username_len))
404 {
405 fprintf (stderr, "'username' does not match.\n"
406 "Expected: '%s'\tRecieved: '%.*s'. ",
407 USERNAME,
408 (int) dinfo->username_len,
409 dinfo->username);
410 mhdErrorExitDesc ("Wrong 'username'");
411 }
412 else if (NULL != dinfo->userhash_bin)
413 mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
414 else if (0 != dinfo->userhash_bin_size)
415 mhdErrorExitDesc ("'userhash_bin_size' is NOT zero");
416 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != dinfo->uname_type)
417 {
418 fprintf (stderr, "Unexpected 'uname_type'.\n"
419 "Expected: %d\tRecieved: %d. ",
420 (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
421 (int) dinfo->uname_type);
422 mhdErrorExitDesc ("Wrong 'uname_type'");
423 }
424 else if (MHD_DIGEST_AUTH_ALGO3_MD5 != dinfo->algo)
425 {
426 fprintf (stderr, "Unexpected 'algo'.\n"
427 "Expected: %d\tRecieved: %d. ",
428 (int) MHD_DIGEST_AUTH_ALGO3_MD5,
429 (int) dinfo->algo);
430 mhdErrorExitDesc ("Wrong 'algo'");
431 }
432 else if (MHD_STATICSTR_LEN_ (CNONCE_EMU) != dinfo->cnonce_len)
433 {
434 fprintf (stderr, "Unexpected 'cnonce_len'.\n"
435 "Expected: %d\tRecieved: %ld. ",
436 (int) MHD_STATICSTR_LEN_ (CNONCE_EMU),
437 (long) dinfo->cnonce_len);
438 mhdErrorExitDesc ("Wrong 'cnonce_len'");
439 }
440 else if (NULL == dinfo->opaque)
441 mhdErrorExitDesc ("'opaque' is NULL");
442 else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE))
443 {
444 fprintf (stderr, "'opaque_len' does not match.\n"
445 "Expected: %u\tRecieved: %u. ",
446 (unsigned) MHD_STATICSTR_LEN_ (OPAQUE_VALUE),
447 (unsigned) dinfo->opaque_len);
448 mhdErrorExitDesc ("Wrong 'opaque_len'");
449 }
450 else if (0 != memcmp (dinfo->opaque, OPAQUE_VALUE, dinfo->opaque_len))
451 {
452 fprintf (stderr, "'opaque' does not match.\n"
453 "Expected: '%s'\tRecieved: '%.*s'. ",
454 OPAQUE_VALUE,
455 (int) dinfo->opaque_len,
456 dinfo->opaque);
457 mhdErrorExitDesc ("Wrong 'opaque'");
458 }
459 else if (MHD_DIGEST_AUTH_QOP_AUTH != dinfo->qop)
460 {
461 fprintf (stderr, "Unexpected 'qop'.\n"
462 "Expected: %d\tRecieved: %d. ",
463 (int) MHD_DIGEST_AUTH_QOP_AUTH,
464 (int) dinfo->qop);
465 mhdErrorExitDesc ("Wrong 'qop'");
466 }
467 else if (NULL == dinfo->realm)
468 mhdErrorExitDesc ("'realm' is NULL");
469 else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM))
470 {
471 fprintf (stderr, "'realm_len' does not match.\n"
472 "Expected: %u\tRecieved: %u. ",
473 (unsigned) MHD_STATICSTR_LEN_ (REALM),
474 (unsigned) dinfo->realm_len);
475 mhdErrorExitDesc ("Wrong 'realm_len'");
476 }
477 else if (0 != memcmp (dinfo->realm, REALM, dinfo->realm_len))
478 {
479 fprintf (stderr, "'realm' does not match.\n"
480 "Expected: '%s'\tRecieved: '%.*s'. ",
481 OPAQUE_VALUE,
482 (int) dinfo->realm_len,
483 dinfo->realm);
484 mhdErrorExitDesc ("Wrong 'realm'");
485 }
486 MHD_free (dinfo);
487
488 check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME, PASSWORD,
489 300, MHD_DIGEST_ALG_MD5);
490
491 switch (check_res)
492 {
493 /* Valid results */
494 case MHD_DAUTH_NONCE_STALE:
495 if (verbose)
496 printf ("Got valid auth check result: MHD_DAUTH_NONCE_STALE.\n");
497 break;
498 case MHD_DAUTH_NONCE_WRONG:
499 if (verbose)
500 printf ("Got valid auth check result: MHD_DAUTH_NONCE_WRONG.\n");
501 break;
502
503 /* Invalid results */
504 case MHD_DAUTH_OK:
505 mhdErrorExitDesc ("'MHD_digest_auth_check3()' succeed, " \
506 "but it should not");
507 break;
508 case MHD_DAUTH_ERROR:
509 externalErrorExitDesc ("General error returned " \
510 "by 'MHD_digest_auth_check3()'");
511 break;
512 case MHD_DAUTH_WRONG_USERNAME:
513 mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
514 "MHD_DAUTH_WRONG_USERNAME");
515 break;
516 case MHD_DAUTH_RESPONSE_WRONG:
517 mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
518 "MHD_DAUTH_RESPONSE_WRONG");
519 break;
520 case MHD_DAUTH_WRONG_HEADER:
521 case MHD_DAUTH_WRONG_REALM:
522 case MHD_DAUTH_WRONG_URI:
523 case MHD_DAUTH_WRONG_QOP:
524 case MHD_DAUTH_WRONG_ALGO:
525 case MHD_DAUTH_TOO_LARGE:
526 fprintf (stderr, "'MHD_digest_auth_check3()' returned "
527 "unexpected result: %d. ",
528 check_res);
529 mhdErrorExitDesc ("Wrong returned code");
530 break;
531 default:
532 fprintf (stderr, "'MHD_digest_auth_check3()' returned "
533 "impossible result code: %d. ",
534 check_res);
535 mhdErrorExitDesc ("Impossible returned code");
536 }
537
538 response =
539 MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
540 (const void *) DENIED);
541 if (NULL == response)
542 mhdErrorExitDesc ("Response creation failed");
543 ret = MHD_queue_auth_fail_response2 (connection, REALM, OPAQUE_VALUE,
544 response, 0, MHD_DIGEST_ALG_MD5);
545 if (MHD_YES != ret)
546 mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
547 }
548 else
549 {
550 char *username;
551 int check_res;
552
553 username = MHD_digest_auth_get_username (connection);
554 if (NULL == username)
555 mhdErrorExitDesc ("'MHD_digest_auth_get_username()' returned NULL");
556 else if (0 != strcmp (username, USERNAME))
557 {
558 fprintf (stderr, "'username' does not match.\n"
559 "Expected: '%s'\tRecieved: '%s'. ",
560 USERNAME,
561 username);
562 mhdErrorExitDesc ("Wrong 'username'");
563 }
564 MHD_free (username);
565
566 check_res = MHD_digest_auth_check (connection, REALM, USERNAME, PASSWORD,
567 300);
568
569 if (MHD_INVALID_NONCE != check_res)
570 {
571 fprintf (stderr, "'MHD_digest_auth_check()' returned unexpected"
572 " result: %d. ", check_res);
573 mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check()' result");
574 }
575 response =
576 MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
577 (const void *) DENIED);
578 if (NULL == response)
579 mhdErrorExitDesc ("Response creation failed");
580
581 ret = MHD_queue_auth_fail_response (connection, REALM, OPAQUE_VALUE,
582 response, 0);
583 if (MHD_YES != ret)
584 mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
585 }
586
587 MHD_destroy_response (response);
588 return ret;
589}
590
591
592static CURL *
593setupCURL (void *cbc, int port)
594{
595 CURL *c;
596 char url[512];
597
598 if (1)
599 {
600 int res;
601 /* A workaround for some old libcurl versions, which ignore the specified
602 * port by CURLOPT_PORT when authorisation is used. */
603 res = snprintf (url, (sizeof(url) / sizeof(url[0])),
604 "http://127.0.0.1:%d%s", port, MHD_URI_BASE_PATH);
605 if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
606 externalErrorExitDesc ("Cannot form request URL");
607 }
608
609 c = curl_easy_init ();
610 if (NULL == c)
611 libcurlErrorExitDesc ("curl_easy_init() failed");
612
613 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
614 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
615 &copyBuffer)) ||
616 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
617 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
618 ((long) TIMEOUTS_VAL))) ||
619 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
620 CURL_HTTP_VERSION_1_1)) ||
621 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
622 ((long) TIMEOUTS_VAL))) ||
623 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
624 libcurl_errbuf)) ||
625 /* (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || */
626 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
627 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, curl_headers)) ||
628#if CURL_AT_LEAST_VERSION (7, 19, 4)
629 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
630#endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
631#if CURL_AT_LEAST_VERSION (7, 45, 0)
632 (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
633#endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
634 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
635 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
636 libcurlErrorExitDesc ("curl_easy_setopt() failed");
637 return c;
638}
639
640
641static CURLcode
642performQueryExternal (struct MHD_Daemon *d, CURL *c)
643{
644 CURLM *multi;
645 time_t start;
646 struct timeval tv;
647 CURLcode ret;
648
649 ret = CURLE_FAILED_INIT; /* will be replaced with real result */
650 multi = NULL;
651 multi = curl_multi_init ();
652 if (multi == NULL)
653 libcurlErrorExitDesc ("curl_multi_init() failed");
654 if (CURLM_OK != curl_multi_add_handle (multi, c))
655 libcurlErrorExitDesc ("curl_multi_add_handle() failed");
656
657 start = time (NULL);
658 while (time (NULL) - start <= TIMEOUTS_VAL)
659 {
660 fd_set rs;
661 fd_set ws;
662 fd_set es;
663 MHD_socket maxMhdSk;
664 int maxCurlSk;
665 int running;
666
667 maxMhdSk = MHD_INVALID_SOCKET;
668 maxCurlSk = -1;
669 FD_ZERO (&rs);
670 FD_ZERO (&ws);
671 FD_ZERO (&es);
672 if (NULL != multi)
673 {
674 curl_multi_perform (multi, &running);
675 if (0 == running)
676 {
677 struct CURLMsg *msg;
678 int msgLeft;
679 int totalMsgs = 0;
680 do
681 {
682 msg = curl_multi_info_read (multi, &msgLeft);
683 if (NULL == msg)
684 libcurlErrorExitDesc ("curl_multi_info_read() failed");
685 totalMsgs++;
686 if (CURLMSG_DONE == msg->msg)
687 ret = msg->data.result;
688 } while (msgLeft > 0);
689 if (1 != totalMsgs)
690 {
691 fprintf (stderr,
692 "curl_multi_info_read returned wrong "
693 "number of results (%d).\n",
694 totalMsgs);
695 externalErrorExit ();
696 }
697 curl_multi_remove_handle (multi, c);
698 curl_multi_cleanup (multi);
699 multi = NULL;
700 }
701 else
702 {
703 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
704 libcurlErrorExitDesc ("curl_multi_fdset() failed");
705 }
706 }
707 if (NULL == multi)
708 { /* libcurl has finished, check whether MHD still needs to perform cleanup */
709 if (0 != MHD_get_timeout64s (d))
710 break; /* MHD finished as well */
711 }
712 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
713 mhdErrorExitDesc ("MHD_get_fdset() failed");
714 tv.tv_sec = 0;
715 tv.tv_usec = 200000;
716#ifdef MHD_POSIX_SOCKETS
717 if (maxMhdSk > maxCurlSk)
718 maxCurlSk = maxMhdSk;
719#endif /* MHD_POSIX_SOCKETS */
720 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
721 {
722#ifdef MHD_POSIX_SOCKETS
723 if (EINTR != errno)
724 externalErrorExitDesc ("Unexpected select() error");
725#else
726 if ((WSAEINVAL != WSAGetLastError ()) ||
727 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
728 externalErrorExitDesc ("Unexpected select() error");
729 Sleep (200);
730#endif
731 }
732 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
733 mhdErrorExitDesc ("MHD_run_from_select() failed");
734 }
735
736 return ret;
737}
738
739
740/**
741 * Check request result
742 * @param curl_code the CURL easy return code
743 * @param pcbc the pointer struct CBC
744 * @return non-zero if success, zero if failed
745 */
746static unsigned int
747check_result (CURLcode curl_code, CURL *c, struct CBC *pcbc)
748{
749 long code;
750 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
751 libcurlErrorExit ();
752
753 if (401 != code)
754 {
755 fprintf (stderr, "Request returned wrong code: %ld.\n",
756 code);
757 return 0;
758 }
759
760 if (CURLE_OK != curl_code)
761 {
762 fflush (stdout);
763 if (0 != libcurl_errbuf[0])
764 fprintf (stderr, "Request failed. "
765 "libcurl error: '%s'.\n"
766 "libcurl error description: '%s'.\n",
767 curl_easy_strerror (curl_code),
768 libcurl_errbuf);
769 else
770 fprintf (stderr, "Request failed. "
771 "libcurl error: '%s'.\n",
772 curl_easy_strerror (curl_code));
773 fflush (stderr);
774 return 0;
775 }
776
777 if (pcbc->pos != strlen (DENIED))
778 {
779 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
780 (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
781 (unsigned) strlen (DENIED));
782 mhdErrorExitDesc ("Wrong returned data length");
783 }
784 if (0 != memcmp (DENIED, pcbc->buf, pcbc->pos))
785 {
786 fprintf (stderr, "Got invalid response '%.*s'. ",
787 (int) pcbc->pos, pcbc->buf);
788 mhdErrorExitDesc ("Wrong returned data");
789 }
790 return 1;
791}
792
793
794static unsigned int
795testDigestAuthEmu (void)
796{
797 struct MHD_Daemon *d;
798 uint16_t port;
799 struct CBC cbc;
800 char buf[2048];
801 CURL *c;
802 int failed = 0;
803
804 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
805 port = 0;
806 else
807 port = 4210;
808
809 d = MHD_start_daemon (MHD_USE_ERROR_LOG,
810 port, NULL, NULL,
811 &ahc_echo, NULL,
812 MHD_OPTION_END);
813 if (d == NULL)
814 return 1;
815 if (0 == port)
816 {
817 const union MHD_DaemonInfo *dinfo;
818
819 dinfo = MHD_get_daemon_info (d,
820 MHD_DAEMON_INFO_BIND_PORT);
821 if ( (NULL == dinfo) ||
822 (0 == dinfo->port) )
823 mhdErrorExitDesc ("MHD_get_daemon_info() failed");
824 port = (uint16_t) dinfo->port;
825 }
826
827 /* First request */
828 cbc.buf = buf;
829 cbc.size = sizeof (buf);
830 cbc.pos = 0;
831 memset (cbc.buf, 0, cbc.size);
832 c = setupCURL (&cbc, port);
833 if (check_result (performQueryExternal (d, c), c, &cbc))
834 {
835 if (verbose)
836 printf ("Got expected response.\n");
837 }
838 else
839 {
840 fprintf (stderr, "Request FAILED.\n");
841 failed = 1;
842 }
843 curl_easy_cleanup (c);
844
845 MHD_stop_daemon (d);
846 return failed ? 1 : 0;
847}
848
849
850int
851main (int argc, char *const *argv)
852{
853 unsigned int errorCount = 0;
854 (void) argc; (void) argv; /* Unused. Silent compiler warning. */
855
856 verbose = ! (has_param (argc, argv, "-q") ||
857 has_param (argc, argv, "--quiet") ||
858 has_param (argc, argv, "-s") ||
859 has_param (argc, argv, "--silent"));
860 oldapi = has_in_name (argv[0], "_oldapi");
861 test_global_init ();
862
863 errorCount += testDigestAuthEmu ();
864 if (errorCount != 0)
865 fprintf (stderr, "Error (code: %u)\n", errorCount);
866 test_global_cleanup ();
867 return (0 == errorCount) ? 0 : 1; /* 0 == pass */
868}