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