• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  This file is part of libmicrohttpd
3  Copyright (C) 2007 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 tls_test_common.c
23  * @brief  Common tls test functions
24  * @author Sagie Amir
25  */
26 #include "tls_test_common.h"
27 #include "tls_test_keys.h"
28 
29 
30 int curl_check_version (const char *req_version, ...);
31 
32 FILE *
setup_ca_cert()33 setup_ca_cert ()
34 {
35   FILE *cert_fd;
36 
37   if (NULL == (cert_fd = fopen (ca_cert_file_name, "wb+")))
38     {
39       fprintf (stderr, "Error: failed to open `%s': %s\n",
40                ca_cert_file_name, strerror (errno));
41       return NULL;
42     }
43   if (fwrite (ca_cert_pem, sizeof (char), strlen (ca_cert_pem) + 1, cert_fd)
44       != strlen (ca_cert_pem) + 1)
45     {
46       fprintf (stderr, "Error: failed to write `%s. %s'\n",
47                ca_cert_file_name, strerror (errno));
48       fclose (cert_fd);
49       return NULL;
50     }
51   if (fflush (cert_fd))
52     {
53       fprintf (stderr, "Error: failed to flush ca cert file stream. %s\n",
54                strerror (errno));
55       fclose (cert_fd);
56       return NULL;
57     }
58   return cert_fd;
59 }
60 
61 
62 /*
63  * test HTTPS transfer
64  */
65 int
test_daemon_get(void * cls,const char * cipher_suite,int proto_version,int port,int ver_peer)66 test_daemon_get (void *cls,
67 		 const char *cipher_suite, int proto_version,
68 		 int port,
69 		 int ver_peer)
70 {
71   CURL *c;
72   struct CBC cbc;
73   CURLcode errornum;
74   char url[255];
75   size_t len;
76 
77   len = strlen (test_data);
78   if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
79     {
80       fprintf (stderr, MHD_E_MEM);
81       return -1;
82     }
83   cbc.size = len;
84   cbc.pos = 0;
85 
86   /* construct url - this might use doc_path */
87   gen_test_file_url (url, port);
88 
89   c = curl_easy_init ();
90 #if DEBUG_HTTPS_TEST
91   curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
92 #endif
93   curl_easy_setopt (c, CURLOPT_URL, url);
94   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
95   curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
96   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
97   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
98   curl_easy_setopt (c, CURLOPT_FILE, &cbc);
99 
100   /* TLS options */
101   curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
102   curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
103 
104   /* perform peer authentication */
105   /* TODO merge into send_curl_req */
106   curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, ver_peer);
107   curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name);
108   curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
109   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
110 
111   /* NOTE: use of CONNECTTIMEOUT without also
112      setting NOSIGNAL results in really weird
113      crashes on my system! */
114   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
115   if (CURLE_OK != (errornum = curl_easy_perform (c)))
116     {
117       fprintf (stderr, "curl_easy_perform failed: `%s'\n",
118                curl_easy_strerror (errornum));
119       curl_easy_cleanup (c);
120       free (cbc.buf);
121       return errornum;
122     }
123 
124   curl_easy_cleanup (c);
125 
126   if (memcmp (cbc.buf, test_data, len) != 0)
127     {
128       fprintf (stderr, "Error: local file & received file differ.\n");
129       free (cbc.buf);
130       return -1;
131     }
132 
133   free (cbc.buf);
134   return 0;
135 }
136 
137 
138 void
print_test_result(int test_outcome,char * test_name)139 print_test_result (int test_outcome, char *test_name)
140 {
141 #if 0
142   if (test_outcome != 0)
143     fprintf (stderr, "running test: %s [fail]\n", test_name);
144   else
145     fprintf (stdout, "running test: %s [pass]\n", test_name);
146 #endif
147 }
148 
149 size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)150 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
151 {
152   struct CBC *cbc = ctx;
153 
154   if (cbc->pos + size * nmemb > cbc->size)
155     return 0;                   /* overflow */
156   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
157   cbc->pos += size * nmemb;
158   return size * nmemb;
159 }
160 
161 /**
162  *  HTTP access handler call back
163  */
164 int
http_ahc(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * upload_data,const char * version,size_t * upload_data_size,void ** ptr)165 http_ahc (void *cls, struct MHD_Connection *connection,
166           const char *url, const char *method, const char *upload_data,
167           const char *version, size_t *upload_data_size, void **ptr)
168 {
169   static int aptr;
170   struct MHD_Response *response;
171   int ret;
172 
173   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
174     return MHD_NO;              /* unexpected method */
175   if (&aptr != *ptr)
176     {
177       /* do never respond on first call */
178       *ptr = &aptr;
179       return MHD_YES;
180     }
181   *ptr = NULL;                  /* reset when done */
182   response = MHD_create_response_from_buffer (strlen (test_data),
183 					      (void *) test_data,
184 					      MHD_RESPMEM_PERSISTENT);
185   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
186   MHD_destroy_response (response);
187   return ret;
188 }
189 
190 /* HTTP access handler call back */
191 int
http_dummy_ahc(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * upload_data,const char * version,size_t * upload_data_size,void ** ptr)192 http_dummy_ahc (void *cls, struct MHD_Connection *connection,
193                 const char *url, const char *method, const char *upload_data,
194                 const char *version, size_t *upload_data_size,
195                 void **ptr)
196 {
197   return 0;
198 }
199 
200 /**
201  * send a test http request to the daemon
202  * @param url
203  * @param cbc - may be null
204  * @param cipher_suite
205  * @param proto_version
206  * @return
207  */
208 /* TODO have test wrap consider a NULL cbc */
209 int
send_curl_req(char * url,struct CBC * cbc,const char * cipher_suite,int proto_version)210 send_curl_req (char *url, struct CBC * cbc, const char *cipher_suite,
211                int proto_version)
212 {
213   CURL *c;
214   CURLcode errornum;
215   c = curl_easy_init ();
216 #if DEBUG_HTTPS_TEST
217   curl_easy_setopt (c, CURLOPT_VERBOSE, CURL_VERBOS_LEVEL);
218 #endif
219   curl_easy_setopt (c, CURLOPT_URL, url);
220   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
221   curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L);
222   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L);
223 
224   if (cbc != NULL)
225     {
226       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
227       curl_easy_setopt (c, CURLOPT_FILE, cbc);
228     }
229 
230   /* TLS options */
231   curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
232   curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
233 
234   /* currently skip any peer authentication */
235   curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
236   curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
237 
238   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
239 
240   /* NOTE: use of CONNECTTIMEOUT without also
241      setting NOSIGNAL results in really weird
242      crashes on my system! */
243   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
244   if (CURLE_OK != (errornum = curl_easy_perform (c)))
245     {
246       fprintf (stderr, "curl_easy_perform failed: `%s'\n",
247                curl_easy_strerror (errornum));
248       curl_easy_cleanup (c);
249       return errornum;
250     }
251   curl_easy_cleanup (c);
252 
253   return CURLE_OK;
254 }
255 
256 
257 /**
258  * compile test file url pointing to the current running directory path
259  *
260  * @param url - char buffer into which the url is compiled
261  * @param port port to use for the test
262  * @return -1 on error
263  */
264 int
gen_test_file_url(char * url,int port)265 gen_test_file_url (char *url, int port)
266 {
267   int ret = 0;
268   char *doc_path;
269   size_t doc_path_len;
270   /* setup test file path, url */
271   doc_path_len = PATH_MAX > 4096 ? 4096 : PATH_MAX;
272   if (NULL == (doc_path = malloc (doc_path_len)))
273     {
274       fprintf (stderr, MHD_E_MEM);
275       return -1;
276     }
277   if (getcwd (doc_path, doc_path_len) == NULL)
278     {
279       fprintf (stderr, "Error: failed to get working directory. %s\n",
280                strerror (errno));
281       ret = -1;
282     }
283 #ifdef WINDOWS
284   {
285     int i;
286     for (i = 0; i < doc_path_len; i++)
287     {
288       if (doc_path[i] == 0)
289         break;
290       if (doc_path[i] == '\\')
291       {
292         doc_path[i] = '/';
293       }
294       if (doc_path[i] != ':')
295         continue;
296       if (i == 0)
297         break;
298       doc_path[i] = doc_path[i - 1];
299       doc_path[i - 1] = '/';
300     }
301   }
302 #endif
303   /* construct url - this might use doc_path */
304   if (sprintf (url, "%s:%d%s/%s", "https://127.0.0.1", port,
305                doc_path, "urlpath") < 0)
306     ret = -1;
307 
308   free (doc_path);
309   return ret;
310 }
311 
312 /**
313  * test HTTPS file transfer
314  */
315 int
test_https_transfer(void * cls,const char * cipher_suite,int proto_version)316 test_https_transfer (void *cls, const char *cipher_suite, int proto_version)
317 {
318   int len;
319   int ret = 0;
320   struct CBC cbc;
321   char url[255];
322 
323   len = strlen (test_data);
324   if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
325     {
326       fprintf (stderr, MHD_E_MEM);
327       return -1;
328     }
329   cbc.size = len;
330   cbc.pos = 0;
331 
332   if (gen_test_file_url (url, DEAMON_TEST_PORT))
333     {
334       ret = -1;
335       goto cleanup;
336     }
337 
338   if (CURLE_OK != send_curl_req (url, &cbc, cipher_suite, proto_version))
339     {
340       ret = -1;
341       goto cleanup;
342     }
343 
344   /* compare test file & daemon responce */
345   if ( (len != strlen (test_data)) ||
346        (memcmp (cbc.buf,
347 		test_data,
348 		len) != 0) )
349     {
350       fprintf (stderr, "Error: local file & received file differ.\n");
351       ret = -1;
352     }
353 cleanup:
354   free (cbc.buf);
355   return ret;
356 }
357 
358 /**
359  * setup test case
360  *
361  * @param d
362  * @param daemon_flags
363  * @param arg_list
364  * @return
365  */
366 int
setup_testcase(struct MHD_Daemon ** d,int daemon_flags,va_list arg_list)367 setup_testcase (struct MHD_Daemon **d, int daemon_flags, va_list arg_list)
368 {
369   *d = MHD_start_daemon_va (daemon_flags, DEAMON_TEST_PORT,
370                             NULL, NULL, &http_ahc, NULL, arg_list);
371 
372   if (*d == NULL)
373     {
374       fprintf (stderr, MHD_E_SERVER_INIT);
375       return -1;
376     }
377 
378   return 0;
379 }
380 
381 void
teardown_testcase(struct MHD_Daemon * d)382 teardown_testcase (struct MHD_Daemon *d)
383 {
384   MHD_stop_daemon (d);
385 }
386 
387 int
setup_session(gnutls_session_t * session,gnutls_datum_t * key,gnutls_datum_t * cert,gnutls_certificate_credentials_t * xcred)388 setup_session (gnutls_session_t * session,
389                gnutls_datum_t * key,
390                gnutls_datum_t * cert,
391 	       gnutls_certificate_credentials_t * xcred)
392 {
393   int ret;
394   const char *err_pos;
395 
396   gnutls_certificate_allocate_credentials (xcred);
397   key->size = strlen (srv_key_pem) + 1;
398   key->data = malloc (key->size);
399   if (NULL == key->data)
400      {
401        gnutls_certificate_free_credentials (*xcred);
402 	return -1;
403      }
404   memcpy (key->data, srv_key_pem, key->size);
405   cert->size = strlen (srv_self_signed_cert_pem) + 1;
406   cert->data = malloc (cert->size);
407   if (NULL == cert->data)
408     {
409         gnutls_certificate_free_credentials (*xcred);
410 	free (key->data);
411 	return -1;
412     }
413   memcpy (cert->data, srv_self_signed_cert_pem, cert->size);
414   gnutls_certificate_set_x509_key_mem (*xcred, cert, key,
415 				       GNUTLS_X509_FMT_PEM);
416   gnutls_init (session, GNUTLS_CLIENT);
417   ret = gnutls_priority_set_direct (*session,
418 				    "NORMAL", &err_pos);
419   if (ret < 0)
420     {
421        gnutls_deinit (*session);
422        gnutls_certificate_free_credentials (*xcred);
423        free (key->data);
424        return -1;
425     }
426   gnutls_credentials_set (*session,
427 			  GNUTLS_CRD_CERTIFICATE,
428 			  *xcred);
429   return 0;
430 }
431 
432 int
teardown_session(gnutls_session_t session,gnutls_datum_t * key,gnutls_datum_t * cert,gnutls_certificate_credentials_t xcred)433 teardown_session (gnutls_session_t session,
434                   gnutls_datum_t * key,
435                   gnutls_datum_t * cert,
436                   gnutls_certificate_credentials_t xcred)
437 {
438   free (key->data);
439   key->data = NULL;
440   key->size = 0;
441   free (cert->data);
442   cert->data = NULL;
443   cert->size = 0;
444   gnutls_deinit (session);
445   gnutls_certificate_free_credentials (xcred);
446   return 0;
447 }
448 
449 /* TODO test_wrap: change sig to (setup_func, test, va_list test_arg) */
450 int
test_wrap(const char * test_name,int (* test_function)(void * cls,const char * cipher_suite,int proto_version),void * cls,int daemon_flags,const char * cipher_suite,int proto_version,...)451 test_wrap (const char *test_name, int
452            (*test_function) (void * cls, const char *cipher_suite,
453                              int proto_version), void * cls,
454            int daemon_flags, const char *cipher_suite, int proto_version, ...)
455 {
456   int ret;
457   va_list arg_list;
458   struct MHD_Daemon *d;
459 
460   va_start (arg_list, proto_version);
461   if (setup_testcase (&d, daemon_flags, arg_list) != 0)
462     {
463       va_end (arg_list);
464       fprintf (stderr, "Failed to setup testcase %s\n", test_name);
465       return -1;
466     }
467 #if 0
468   fprintf (stdout, "running test: %s ", test_name);
469 #endif
470   ret = test_function (NULL, cipher_suite, proto_version);
471 #if 0
472   if (ret == 0)
473     {
474       fprintf (stdout, "[pass]\n");
475     }
476   else
477     {
478       fprintf (stdout, "[fail]\n");
479     }
480 #endif
481   teardown_testcase (d);
482   va_end (arg_list);
483   return ret;
484 }
485