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