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