test_digestauth_with_arguments.c (9721B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010, 2012 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 daemontest_digestauth_with_arguments.c 24 * @brief Testcase for libmicrohttpd Digest Auth with arguments 25 * @author Amr Ali 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 #include "mhd_options.h" 29 #include "platform.h" 30 #include <curl/curl.h> 31 #include <microhttpd.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <time.h> 35 #include <errno.h> 36 37 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 38 test into a marked, classifiable test error (TESTING.md, P5). */ 39 #include "mhd_panic_tripwire.h" 40 41 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ 42 (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) 43 #define NEED_GCRYP_INIT 1 44 #include <gcrypt.h> 45 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ 46 47 #ifndef WINDOWS 48 #include <sys/socket.h> 49 #include <unistd.h> 50 #else 51 #include <wincrypt.h> 52 #endif 53 54 #define PAGE \ 55 "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>" 56 57 #define DENIED \ 58 "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>" 59 60 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" 61 62 struct CBC 63 { 64 char *buf; 65 size_t pos; 66 size_t size; 67 }; 68 69 static size_t 70 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 71 { 72 struct CBC *cbc = ctx; 73 74 if (cbc->pos + size * nmemb > cbc->size) 75 return 0; /* overflow */ 76 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 77 cbc->pos += size * nmemb; 78 return size * nmemb; 79 } 80 81 82 static enum MHD_Result 83 ahc_echo (void *cls, 84 struct MHD_Connection *connection, 85 const char *url, 86 const char *method, 87 const char *version, 88 const char *upload_data, size_t *upload_data_size, 89 void **req_cls) 90 { 91 struct MHD_Response *response; 92 char *username; 93 const char *password = "testpass"; 94 const char *realm = "test@example.com"; 95 enum MHD_Result ret; 96 int ret_i; 97 static int already_called_marker; 98 (void) cls; (void) url; /* Unused. Silent compiler warning. */ 99 (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ 100 (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */ 101 102 if (&already_called_marker != *req_cls) 103 { /* Called for the first time, request not fully read yet */ 104 *req_cls = &already_called_marker; 105 /* Wait for complete request */ 106 return MHD_YES; 107 } 108 109 username = MHD_digest_auth_get_username (connection); 110 if ( (username == NULL) || 111 (0 != strcmp (username, "testuser")) ) 112 { 113 response = MHD_create_response_from_buffer_static (strlen (DENIED), 114 DENIED); 115 ret = MHD_queue_auth_fail_response2 (connection, realm, 116 MY_OPAQUE, 117 response, 118 MHD_NO, 119 MHD_DIGEST_ALG_MD5); 120 MHD_destroy_response (response); 121 return ret; 122 } 123 ret_i = MHD_digest_auth_check2 (connection, 124 realm, 125 username, 126 password, 127 300, 128 MHD_DIGEST_ALG_MD5); 129 MHD_free (username); 130 if (ret_i != MHD_YES) 131 { 132 response = MHD_create_response_from_buffer_static (strlen (DENIED), 133 DENIED); 134 if (NULL == response) 135 fprintf (stderr, "MHD_create_response_from_buffer() failed.\n"); 136 ret = MHD_queue_auth_fail_response2 (connection, 137 realm, 138 MY_OPAQUE, 139 response, 140 (MHD_INVALID_NONCE == ret_i) ? 141 MHD_YES : MHD_NO, 142 MHD_DIGEST_ALG_MD5); 143 if (MHD_YES != ret) 144 fprintf (stderr, "MHD_queue_auth_fail_response2() failed.\n"); 145 MHD_destroy_response (response); 146 return ret; 147 } 148 response = MHD_create_response_from_buffer_static (strlen (PAGE), 149 PAGE); 150 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 151 MHD_destroy_response (response); 152 return ret; 153 } 154 155 156 static unsigned int 157 testDigestAuth (void) 158 { 159 CURL *c; 160 CURLcode errornum; 161 struct MHD_Daemon *d; 162 struct CBC cbc; 163 char buf[2048]; 164 char rnd[8]; 165 uint16_t port; 166 char url[128]; 167 #ifndef WINDOWS 168 int fd; 169 size_t len; 170 size_t off = 0; 171 #endif /* ! WINDOWS */ 172 173 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 174 port = 0; 175 else 176 port = 1160; 177 178 cbc.buf = buf; 179 cbc.size = 2048; 180 cbc.pos = 0; 181 #ifndef WINDOWS 182 fd = open ("/dev/urandom", O_RDONLY); 183 if (-1 == fd) 184 { 185 fprintf (stderr, "Failed to open `%s': %s\n", 186 "/dev/urandom", 187 strerror (errno)); 188 return 1; 189 } 190 while (off < 8) 191 { 192 len = (size_t) read (fd, rnd + off, 8 - off); 193 if (len == (size_t) -1) 194 { 195 fprintf (stderr, 196 "Failed to read `%s': %s\n", 197 "/dev/urandom", 198 strerror (errno)); 199 (void) close (fd); 200 return 1; 201 } 202 off += len; 203 } 204 (void) close (fd); 205 #else 206 { 207 HCRYPTPROV cc; 208 BOOL b; 209 b = CryptAcquireContext (&cc, NULL, NULL, PROV_RSA_FULL, 210 CRYPT_VERIFYCONTEXT); 211 if (b == 0) 212 { 213 fprintf (stderr, "Failed to acquire crypto provider context: %lu\n", 214 GetLastError ()); 215 return 1; 216 } 217 b = CryptGenRandom (cc, 8, (BYTE *) rnd); 218 if (b == 0) 219 { 220 fprintf (stderr, "Failed to generate 8 random bytes: %lu\n", 221 GetLastError ()); 222 } 223 CryptReleaseContext (cc, 0); 224 if (b == 0) 225 return 1; 226 } 227 #endif 228 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 229 port, NULL, NULL, &ahc_echo, NULL, 230 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd, 231 MHD_OPTION_NONCE_NC_SIZE, 300, 232 MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999, 233 MHD_OPTION_END); 234 if (d == NULL) 235 return 1; 236 if (0 == port) 237 { 238 const union MHD_DaemonInfo *dinfo; 239 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 240 if ((NULL == dinfo) || (0 == dinfo->port) ) 241 { 242 MHD_stop_daemon (d); return 32; 243 } 244 port = dinfo->port; 245 } 246 snprintf (url, 247 sizeof (url), 248 "http://127.0.0.1:%u/bar%%20foo?" 249 "key=value&more=even%%20more&empty&=no_key&&same=one&&same=two", 250 (unsigned int) port); 251 c = curl_easy_init (); 252 curl_easy_setopt (c, CURLOPT_URL, url); 253 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 254 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 255 curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 256 curl_easy_setopt (c, CURLOPT_USERPWD, "testuser:testpass"); 257 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 258 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 259 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 260 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 261 /* NOTE: use of CONNECTTIMEOUT without also 262 setting NOSIGNAL results in really weird 263 crashes on my system!*/ 264 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 265 if (CURLE_OK != (errornum = curl_easy_perform (c))) 266 { 267 fprintf (stderr, 268 "curl_easy_perform failed: `%s'\n", 269 curl_easy_strerror (errornum)); 270 curl_easy_cleanup (c); 271 MHD_stop_daemon (d); 272 return 2; 273 } 274 curl_easy_cleanup (c); 275 MHD_stop_daemon (d); 276 if (cbc.pos != strlen (PAGE)) 277 return 4; 278 if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE))) 279 return 8; 280 return 0; 281 } 282 283 284 int 285 main (int argc, char *const *argv) 286 { 287 unsigned int errorCount = 0; 288 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 289 #if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62) 290 if (1) 291 { 292 fprintf (stderr, "libcurl version 7.62.x has bug in processing" 293 "URI with GET arguments for Digest Auth.\n"); 294 fprintf (stderr, "This test cannot be performed.\n"); 295 exit (77); 296 } 297 #endif /* libcurl version 7.62.x */ 298 299 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 300 #ifdef HAVE_GCRYPT_H 301 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 302 #ifdef GCRYCTL_INITIALIZATION_FINISHED 303 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 304 #endif 305 #endif 306 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 307 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 308 return 2; 309 errorCount += testDigestAuth (); 310 if (errorCount != 0) 311 fprintf (stderr, "Error (code: %u)\n", errorCount); 312 curl_global_cleanup (); 313 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 314 }