1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010 Christian Grothoff 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. 19 */ 20 21 /** 22 * @file daemontest_digestauth.c 23 * @brief Testcase for libmicrohttpd Digest Auth 24 * @author Amr Ali 25 */ 26 27 #include "MHD_config.h" 28 #include "platform.h" 29 #include <curl/curl.h> 30 #include <microhttpd.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 #ifdef HAVE_GCRYPT_H 35 #include <gcrypt.h> 36 #endif 37 38 #ifndef WINDOWS 39 #include <sys/socket.h> 40 #include <unistd.h> 41 #else 42 #include <wincrypt.h> 43 #endif 44 45 #define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>" 46 47 #define DENIED "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>" 48 49 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" 50 51 struct CBC 52 { 53 char *buf; 54 size_t pos; 55 size_t size; 56 }; 57 58 static size_t 59 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 60 { 61 struct CBC *cbc = ctx; 62 63 if (cbc->pos + size * nmemb > cbc->size) 64 return 0; /* overflow */ 65 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 66 cbc->pos += size * nmemb; 67 return size * nmemb; 68 } 69 70 static int 71 ahc_echo (void *cls, 72 struct MHD_Connection *connection, 73 const char *url, 74 const char *method, 75 const char *version, 76 const char *upload_data, size_t *upload_data_size, 77 void **unused) 78 { 79 struct MHD_Response *response; 80 char *username; 81 const char *password = "testpass"; 82 const char *realm = "test (at) example.com"; 83 int ret; 84 85 username = MHD_digest_auth_get_username(connection); 86 if ( (username == NULL) || 87 (0 != strcmp (username, "testuser")) ) 88 { 89 response = MHD_create_response_from_buffer(strlen (DENIED), 90 DENIED, 91 MHD_RESPMEM_PERSISTENT); 92 ret = MHD_queue_auth_fail_response(connection, realm, 93 MY_OPAQUE, 94 response, 95 MHD_NO); 96 MHD_destroy_response(response); 97 return ret; 98 } 99 ret = MHD_digest_auth_check(connection, realm, 100 username, 101 password, 102 300); 103 free(username); 104 if ( (ret == MHD_INVALID_NONCE) || 105 (ret == MHD_NO) ) 106 { 107 response = MHD_create_response_from_buffer(strlen (DENIED), 108 DENIED, 109 MHD_RESPMEM_PERSISTENT); 110 if (NULL == response) 111 return MHD_NO; 112 ret = MHD_queue_auth_fail_response(connection, realm, 113 MY_OPAQUE, 114 response, 115 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO); 116 MHD_destroy_response(response); 117 return ret; 118 } 119 response = MHD_create_response_from_buffer(strlen(PAGE), PAGE, 120 MHD_RESPMEM_PERSISTENT); 121 ret = MHD_queue_response(connection, MHD_HTTP_OK, response); 122 MHD_destroy_response(response); 123 return ret; 124 } 125 126 127 static int 128 testDigestAuth () 129 { 130 int fd; 131 CURL *c; 132 CURLcode errornum; 133 struct MHD_Daemon *d; 134 struct CBC cbc; 135 size_t len; 136 size_t off = 0; 137 char buf[2048]; 138 char rnd[8]; 139 140 cbc.buf = buf; 141 cbc.size = 2048; 142 cbc.pos = 0; 143 #ifndef WINDOWS 144 fd = open("/dev/urandom", O_RDONLY); 145 if (-1 == fd) 146 { 147 fprintf(stderr, "Failed to open `%s': %s\n", 148 "/dev/urandom", 149 strerror(errno)); 150 return 1; 151 } 152 while (off < 8) 153 { 154 len = read(fd, rnd, 8); 155 if (len == -1) 156 { 157 fprintf(stderr, "Failed to read `%s': %s\n", 158 "/dev/urandom", 159 strerror(errno)); 160 (void) close(fd); 161 return 1; 162 } 163 off += len; 164 } 165 (void) close(fd); 166 #else 167 { 168 HCRYPTPROV cc; 169 BOOL b; 170 b = CryptAcquireContext (&cc, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 171 if (b == 0) 172 { 173 fprintf (stderr, "Failed to acquire crypto provider context: %lu\n", 174 GetLastError ()); 175 return 1; 176 } 177 b = CryptGenRandom (cc, 8, rnd); 178 if (b == 0) 179 { 180 fprintf (stderr, "Failed to generate 8 random bytes: %lu\n", 181 GetLastError ()); 182 } 183 CryptReleaseContext (cc, 0); 184 if (b == 0) 185 return 1; 186 } 187 #endif 188 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 189 1337, NULL, NULL, &ahc_echo, PAGE, 190 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd, 191 MHD_OPTION_NONCE_NC_SIZE, 300, 192 MHD_OPTION_END); 193 if (d == NULL) 194 return 1; 195 c = curl_easy_init (); 196 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1337/"); 197 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 198 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 199 curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 200 curl_easy_setopt (c, CURLOPT_USERPWD, "testuser:testpass"); 201 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 202 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 203 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 204 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 205 /* NOTE: use of CONNECTTIMEOUT without also 206 setting NOSIGNAL results in really weird 207 crashes on my system!*/ 208 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 209 if (CURLE_OK != (errornum = curl_easy_perform (c))) 210 { 211 fprintf (stderr, 212 "curl_easy_perform failed: `%s'\n", 213 curl_easy_strerror (errornum)); 214 curl_easy_cleanup (c); 215 MHD_stop_daemon (d); 216 return 2; 217 } 218 curl_easy_cleanup (c); 219 MHD_stop_daemon (d); 220 if (cbc.pos != strlen (PAGE)) 221 return 4; 222 if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE))) 223 return 8; 224 return 0; 225 } 226 227 228 229 int 230 main (int argc, char *const *argv) 231 { 232 unsigned int errorCount = 0; 233 234 #ifdef HAVE_GCRYPT_H 235 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 236 #ifdef GCRYCTL_INITIALIZATION_FINISHED 237 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 238 #endif 239 #endif 240 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 241 return 2; 242 errorCount += testDigestAuth (); 243 if (errorCount != 0) 244 fprintf (stderr, "Error (code: %u)\n", errorCount); 245 curl_global_cleanup (); 246 return errorCount != 0; /* 0 == pass */ 247 } 248