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