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
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)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
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** unused)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@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
testDigestAuth()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
main(int argc,char * const * argv)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