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