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, &copyBuffer);
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