diff options
Diffstat (limited to 'src/testcurl/test_digestauth_emu_ext.c')
-rw-r--r-- | src/testcurl/test_digestauth_emu_ext.c | 868 |
1 files changed, 868 insertions, 0 deletions
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 */ | ||
144 | static 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 */ | ||
198 | static 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 | |||
255 | struct CBC | ||
256 | { | ||
257 | char *buf; | ||
258 | size_t pos; | ||
259 | size_t size; | ||
260 | }; | ||
261 | |||
262 | /* Global parameters */ | ||
263 | static int verbose; | ||
264 | static int oldapi; | ||
265 | |||
266 | /* Static helper variables */ | ||
267 | struct curl_slist *curl_headers; | ||
268 | |||
269 | static void | ||
270 | test_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 | |||
296 | static void | ||
297 | test_global_cleanup (void) | ||
298 | { | ||
299 | curl_slist_free_all (curl_headers); | ||
300 | curl_headers = NULL; | ||
301 | curl_global_cleanup (); | ||
302 | } | ||
303 | |||
304 | |||
305 | static size_t | ||
306 | copyBuffer (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 | |||
321 | static enum MHD_Result | ||
322 | ahc_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 | |||
592 | static CURL * | ||
593 | setupCURL (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 | ©Buffer)) || | ||
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 | |||
641 | static CURLcode | ||
642 | performQueryExternal (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 | */ | ||
746 | static unsigned int | ||
747 | check_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 | |||
794 | static unsigned int | ||
795 | testDigestAuthEmu (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 | |||
850 | int | ||
851 | main (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 | } | ||