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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, 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 FILE *
setup_ca_cert()31 setup_ca_cert ()
32 {
33 FILE *cert_fd;
34
35 if (NULL == (cert_fd = fopen (ca_cert_file_name, "wb+")))
36 {
37 fprintf (stderr, "Error: failed to open `%s': %s\n",
38 ca_cert_file_name, strerror (errno));
39 return NULL;
40 }
41 if (fwrite (ca_cert_pem, sizeof (char), strlen (ca_cert_pem) + 1, cert_fd)
42 != strlen (ca_cert_pem) + 1)
43 {
44 fprintf (stderr, "Error: failed to write `%s. %s'\n",
45 ca_cert_file_name, strerror (errno));
46 fclose (cert_fd);
47 return NULL;
48 }
49 if (fflush (cert_fd))
50 {
51 fprintf (stderr, "Error: failed to flush ca cert file stream. %s\n",
52 strerror (errno));
53 fclose (cert_fd);
54 return NULL;
55 }
56 return cert_fd;
57 }
58
59
60 /*
61 * test HTTPS transfer
62 */
63 int
test_daemon_get(void * cls,const char * cipher_suite,int proto_version,int port,int ver_peer)64 test_daemon_get (void *cls,
65 const char *cipher_suite,
66 int proto_version,
67 int port,
68 int ver_peer)
69 {
70 CURL *c;
71 struct CBC cbc;
72 CURLcode errornum;
73 CURLcode e;
74 char url[255];
75 size_t len;
76 (void) cls; /* Unused. Silence compiler warning. */
77
78 len = strlen (test_data);
79 if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
80 {
81 fprintf (stderr, MHD_E_MEM);
82 return -1;
83 }
84 cbc.size = len;
85 cbc.pos = 0;
86
87 /* construct url - this might use doc_path */
88 gen_test_file_url (url,
89 sizeof (url),
90 port);
91
92 c = curl_easy_init ();
93 #if DEBUG_HTTPS_TEST
94 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
95 #endif
96 if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_URL, url))) ||
97 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
98 CURL_HTTP_VERSION_1_0))) ||
99 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L))) ||
100 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L))) ||
101 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
102 ©Buffer))) ||
103 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FILE, &cbc))) ||
104 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) ||
105 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))))
106 {
107 fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
108 curl_easy_strerror (e));
109 curl_easy_cleanup (c);
110 free (cbc.buf);
111 return e;
112 }
113
114 /* TLS options */
115 if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSLVERSION,
116 proto_version))) ||
117 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST,
118 cipher_suite))) ||
119
120 /* perform peer authentication */
121 /* TODO merge into send_curl_req */
122 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER,
123 ver_peer))) ||
124 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))))
125 {
126 fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
127 curl_easy_strerror (e));
128 curl_easy_cleanup (c);
129 free (cbc.buf);
130 return e;
131 }
132 if (ver_peer &&
133 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name)))
134 {
135 fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
136 curl_easy_strerror (e));
137 curl_easy_cleanup (c);
138 free (cbc.buf);
139 return e;
140 }
141 if (CURLE_OK != (errornum = curl_easy_perform (c)))
142 {
143 fprintf (stderr, "curl_easy_perform failed: `%s'\n",
144 curl_easy_strerror (errornum));
145 curl_easy_cleanup (c);
146 free (cbc.buf);
147 return errornum;
148 }
149
150 curl_easy_cleanup (c);
151
152 if (memcmp (cbc.buf, test_data, len) != 0)
153 {
154 fprintf (stderr, "Error: local file & received file differ.\n");
155 free (cbc.buf);
156 return -1;
157 }
158
159 free (cbc.buf);
160 return 0;
161 }
162
163
164 void
print_test_result(int test_outcome,char * test_name)165 print_test_result (int test_outcome,
166 char *test_name)
167 {
168 if (test_outcome != 0)
169 fprintf (stderr,
170 "running test: %s [fail: %u]\n",
171 test_name, (unsigned
172 int)
173 test_outcome);
174 #if 0
175 else
176 fprintf (stdout,
177 "running test: %s [pass]\n",
178 test_name);
179 #endif
180 }
181
182
183 size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)184 copyBuffer (void *ptr,
185 size_t size,
186 size_t nmemb,
187 void *ctx)
188 {
189 struct CBC *cbc = ctx;
190
191 if (cbc->pos + size * nmemb > cbc->size)
192 return 0; /* overflow */
193 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
194 cbc->pos += size * nmemb;
195 return size * nmemb;
196 }
197
198
199 /**
200 * HTTP access handler call back
201 */
202 enum MHD_Result
http_ahc(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 ** ptr)203 http_ahc (void *cls,
204 struct MHD_Connection *connection,
205 const char *url,
206 const char *method,
207 const char *version,
208 const char *upload_data,
209 size_t *upload_data_size,
210 void **ptr)
211 {
212 static int aptr;
213 struct MHD_Response *response;
214 enum MHD_Result ret;
215 (void) cls; (void) url; (void) version; /* Unused. Silent compiler warning. */
216 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */
217
218 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
219 return MHD_NO; /* unexpected method */
220 if (&aptr != *ptr)
221 {
222 /* do never respond on first call */
223 *ptr = &aptr;
224 return MHD_YES;
225 }
226 *ptr = NULL; /* reset when done */
227 response = MHD_create_response_from_buffer (strlen (test_data),
228 (void *) test_data,
229 MHD_RESPMEM_PERSISTENT);
230 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
231 MHD_destroy_response (response);
232 return ret;
233 }
234
235
236 /* HTTP access handler call back */
237 enum MHD_Result
http_dummy_ahc(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 ** ptr)238 http_dummy_ahc (void *cls,
239 struct MHD_Connection *connection,
240 const char *url,
241 const char *method,
242 const char *version,
243 const char *upload_data,
244 size_t *upload_data_size,
245 void **ptr)
246 {
247 (void) cls;
248 (void) connection;
249 (void) url;
250 (void) method;
251 (void) version; /* Unused. Silent compiler warning. */
252 (void) upload_data;
253 (void) upload_data_size;
254 (void) ptr; /* Unused. Silent compiler warning. */
255 return 0;
256 }
257
258
259 /**
260 * send a test http request to the daemon
261 * @param url
262 * @param cbc - may be null
263 * @param cipher_suite
264 * @param proto_version
265 * @return
266 */
267 /* TODO have test wrap consider a NULL cbc */
268 int
send_curl_req(char * url,struct CBC * cbc,const char * cipher_suite,int proto_version)269 send_curl_req (char *url,
270 struct CBC *cbc,
271 const char *cipher_suite,
272 int proto_version)
273 {
274 CURL *c;
275 CURLcode errornum;
276 CURLcode e;
277 c = curl_easy_init ();
278 #if DEBUG_HTTPS_TEST
279 curl_easy_setopt (c, CURLOPT_VERBOSE, CURL_VERBOS_LEVEL);
280 #endif
281 if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_URL, url))) ||
282 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
283 CURL_HTTP_VERSION_1_0))) ||
284 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L))) ||
285 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L))) ||
286
287 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) ||
288
289 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))))
290 {
291 fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
292 curl_easy_strerror (e));
293 curl_easy_cleanup (c);
294 return e;
295 }
296 if (cbc != NULL)
297 {
298 if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
299 ©Buffer))) ||
300 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FILE, cbc))))
301 {
302 fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
303 curl_easy_strerror (e));
304 curl_easy_cleanup (c);
305 return e;
306 }
307 }
308
309 /* TLS options */
310 if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSLVERSION,
311 proto_version))) ||
312 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST,
313 cipher_suite))) ||
314 /* currently skip any peer authentication */
315 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L))) ||
316 (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))))
317 {
318 fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
319 curl_easy_strerror (e));
320 curl_easy_cleanup (c);
321 return e;
322 }
323
324 if (CURLE_OK != (errornum = curl_easy_perform (c)))
325 {
326 fprintf (stderr, "curl_easy_perform failed: `%s'\n",
327 curl_easy_strerror (errornum));
328 curl_easy_cleanup (c);
329 return errornum;
330 }
331 curl_easy_cleanup (c);
332
333 return CURLE_OK;
334 }
335
336
337 /**
338 * compile test file url pointing to the current running directory path
339 *
340 * @param[out] url - char buffer into which the url is compiled
341 * @param url_len number of bytes available in url
342 * @param port port to use for the test
343 * @return -1 on error
344 */
345 int
gen_test_file_url(char * url,size_t url_len,int port)346 gen_test_file_url (char *url,
347 size_t url_len,
348 int port)
349 {
350 int ret = 0;
351 char *doc_path;
352 size_t doc_path_len;
353 /* setup test file path, url */
354 #ifdef PATH_MAX
355 doc_path_len = PATH_MAX > 4096 ? 4096 : PATH_MAX;
356 #else /* ! PATH_MAX */
357 doc_path_len = 4096;
358 #endif /* ! PATH_MAX */
359 #ifdef WINDOWS
360 size_t i;
361 #endif /* ! WINDOWS */
362 if (NULL == (doc_path = malloc (doc_path_len)))
363 {
364 fprintf (stderr, MHD_E_MEM);
365 return -1;
366 }
367 if (NULL == getcwd (doc_path, doc_path_len))
368 {
369 fprintf (stderr,
370 "Error: failed to get working directory. %s\n",
371 strerror (errno));
372 free (doc_path);
373 return -1;
374 }
375 #ifdef WINDOWS
376 for (i = 0; i < doc_path_len; i++)
377 {
378 if (doc_path[i] == 0)
379 break;
380 if (doc_path[i] == '\\')
381 {
382 doc_path[i] = '/';
383 }
384 if (doc_path[i] != ':')
385 continue;
386 if (i == 0)
387 break;
388 doc_path[i] = doc_path[i - 1];
389 doc_path[i - 1] = '/';
390 }
391 #endif
392 /* construct url */
393 if (snprintf (url,
394 url_len,
395 "%s:%d%s/%s",
396 "https://127.0.0.1",
397 port,
398 doc_path,
399 "urlpath") >= (long long) url_len)
400 ret = -1;
401
402 free (doc_path);
403 return ret;
404 }
405
406
407 /**
408 * test HTTPS file transfer
409 */
410 int
test_https_transfer(void * cls,int port,const char * cipher_suite,int proto_version)411 test_https_transfer (void *cls,
412 int port,
413 const char *cipher_suite,
414 int proto_version)
415 {
416 int len;
417 int ret = 0;
418 struct CBC cbc;
419 char url[255];
420 (void) cls; /* Unused. Silent compiler warning. */
421
422 len = strlen (test_data);
423 if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
424 {
425 fprintf (stderr, MHD_E_MEM);
426 return -1;
427 }
428 cbc.size = len;
429 cbc.pos = 0;
430
431 if (gen_test_file_url (url,
432 sizeof (url),
433 port))
434 {
435 ret = -1;
436 goto cleanup;
437 }
438
439 if (CURLE_OK !=
440 send_curl_req (url, &cbc, cipher_suite, proto_version))
441 {
442 ret = -1;
443 goto cleanup;
444 }
445
446 /* compare test file & daemon response */
447 if ( (len != strlen (test_data)) ||
448 (memcmp (cbc.buf,
449 test_data,
450 len) != 0) )
451 {
452 fprintf (stderr, "Error: local file & received file differ.\n");
453 ret = -1;
454 }
455 cleanup:
456 free (cbc.buf);
457 return ret;
458 }
459
460
461 /**
462 * setup test case
463 *
464 * @param d
465 * @param daemon_flags
466 * @param arg_list
467 * @return port number on success or zero on failure
468 */
469 int
setup_testcase(struct MHD_Daemon ** d,int port,int daemon_flags,va_list arg_list)470 setup_testcase (struct MHD_Daemon **d, int port, int daemon_flags, va_list
471 arg_list)
472 {
473 *d = MHD_start_daemon_va (daemon_flags, port,
474 NULL, NULL, &http_ahc, NULL, arg_list);
475
476 if (*d == NULL)
477 {
478 fprintf (stderr, MHD_E_SERVER_INIT);
479 return 0;
480 }
481
482 if (0 == port)
483 {
484 const union MHD_DaemonInfo *dinfo;
485 dinfo = MHD_get_daemon_info (*d, MHD_DAEMON_INFO_BIND_PORT);
486 if ((NULL == dinfo) || (0 == dinfo->port) )
487 {
488 MHD_stop_daemon (*d);
489 return 0;
490 }
491 port = (int) dinfo->port;
492 }
493
494 return port;
495 }
496
497
498 void
teardown_testcase(struct MHD_Daemon * d)499 teardown_testcase (struct MHD_Daemon *d)
500 {
501 MHD_stop_daemon (d);
502 }
503
504
505 int
setup_session(gnutls_session_t * session,gnutls_certificate_credentials_t * xcred)506 setup_session (gnutls_session_t *session,
507 gnutls_certificate_credentials_t *xcred)
508 {
509 if (GNUTLS_E_SUCCESS == gnutls_init (session, GNUTLS_CLIENT))
510 {
511 if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (*session))
512 {
513 if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (xcred))
514 {
515 if (GNUTLS_E_SUCCESS == gnutls_credentials_set (*session,
516 GNUTLS_CRD_CERTIFICATE,
517 *xcred))
518 {
519 return 0;
520 }
521 gnutls_certificate_free_credentials (*xcred);
522 }
523 }
524 gnutls_deinit (*session);
525 }
526 return -1;
527 }
528
529
530 int
teardown_session(gnutls_session_t session,gnutls_certificate_credentials_t xcred)531 teardown_session (gnutls_session_t session,
532 gnutls_certificate_credentials_t xcred)
533 {
534 gnutls_deinit (session);
535 gnutls_certificate_free_credentials (xcred);
536 return 0;
537 }
538
539
540 /* TODO test_wrap: change sig to (setup_func, test, va_list test_arg) */
541 int
test_wrap(const char * test_name,int (* test_function)(void * cls,int port,const char * cipher_suite,int proto_version),void * cls,int port,int daemon_flags,const char * cipher_suite,int proto_version,...)542 test_wrap (const char *test_name, int
543 (*test_function)(void *cls, int port, const char *cipher_suite,
544 int proto_version), void *cls,
545 int port,
546 int daemon_flags, const char *cipher_suite, int proto_version, ...)
547 {
548 int ret;
549 va_list arg_list;
550 struct MHD_Daemon *d;
551 (void) cls; /* Unused. Silent compiler warning. */
552
553 va_start (arg_list, proto_version);
554 port = setup_testcase (&d, port, daemon_flags, arg_list);
555 if (0 == port)
556 {
557 va_end (arg_list);
558 fprintf (stderr, "Failed to setup testcase %s\n", test_name);
559 return -1;
560 }
561 #if 0
562 fprintf (stdout, "running test: %s ", test_name);
563 #endif
564 ret = test_function (NULL, port, cipher_suite, proto_version);
565 #if 0
566 if (ret == 0)
567 {
568 fprintf (stdout, "[pass]\n");
569 }
570 else
571 {
572 fprintf (stdout, "[fail]\n");
573 }
574 #endif
575 teardown_testcase (d);
576 va_end (arg_list);
577 return ret;
578 }
579
580
581 static int inited_tls_is_gnutls = 0;
582 static int inited_tls_is_openssl = 0;
583
584 int
curl_tls_is_gnutls(void)585 curl_tls_is_gnutls (void)
586 {
587 const char *tlslib;
588 if (inited_tls_is_gnutls)
589 return 1;
590 if (inited_tls_is_openssl)
591 return 0;
592
593 tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
594 if (NULL == tlslib)
595 return 0;
596 if (0 == strncmp (tlslib, "GnuTLS/", 7))
597 return 1;
598
599 /* Multi-backends handled during initialization by setting variable */
600 return 0;
601 }
602
603
604 int
curl_tls_is_nss(void)605 curl_tls_is_nss (void)
606 {
607 const char *tlslib;
608 if (inited_tls_is_gnutls)
609 return 0;
610 if (inited_tls_is_openssl)
611 return 0;
612
613 tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
614 if (NULL == tlslib)
615 return 0;
616 if (0 == strncmp (tlslib, "NSS/", 4))
617 return 1;
618
619 /* Handle multi-backends with selected backend */
620 if (NULL != strstr (tlslib," NSS/"))
621 return 1;
622
623 return 0;
624 }
625
626
627 int
curl_tls_is_schannel(void)628 curl_tls_is_schannel (void)
629 {
630 const char *tlslib;
631 if (inited_tls_is_gnutls)
632 return 0;
633 if (inited_tls_is_openssl)
634 return 0;
635
636 tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
637 if (NULL == tlslib)
638 return 0;
639 if ((0 == strncmp (tlslib, "Schannel", 8)) || (0 == strncmp (tlslib, "WinSSL",
640 6)))
641 return 1;
642
643 /* Handle multi-backends with selected backend */
644 if ((NULL != strstr (tlslib," Schannel")) || (NULL != strstr (tlslib,
645 " WinSSL")))
646 return 1;
647
648 return 0;
649 }
650
651
652 int
curl_tls_is_sectransport(void)653 curl_tls_is_sectransport (void)
654 {
655 const char *tlslib;
656 if (inited_tls_is_gnutls)
657 return 0;
658 if (inited_tls_is_openssl)
659 return 0;
660
661 tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
662 if (NULL == tlslib)
663 return 0;
664 if (0 == strncmp (tlslib, "SecureTransport", 15))
665 return 1;
666
667 /* Handle multi-backends with selected backend */
668 if (NULL != strstr (tlslib," SecureTransport"))
669 return 1;
670
671 return 0;
672 }
673
674
675 int
testsuite_curl_global_init(void)676 testsuite_curl_global_init (void)
677 {
678 CURLcode res;
679 #if LIBCURL_VERSION_NUM >= 0x073800
680 if (CURLSSLSET_OK != curl_global_sslset (CURLSSLBACKEND_GNUTLS, NULL, NULL))
681 {
682 CURLsslset e;
683 e = curl_global_sslset (CURLSSLBACKEND_OPENSSL, NULL, NULL);
684 if (CURLSSLSET_TOO_LATE == e)
685 fprintf (stderr, "WARNING: libcurl was already initialised.\n");
686 else if (CURLSSLSET_OK == e)
687 inited_tls_is_openssl = 1;
688 }
689 else
690 inited_tls_is_gnutls = 1;
691 #endif /* LIBCURL_VERSION_NUM >= 0x07380 */
692 res = curl_global_init (CURL_GLOBAL_ALL);
693 if (CURLE_OK != res)
694 {
695 fprintf (stderr, "libcurl initialisation error: %s\n", curl_easy_strerror (
696 res));
697 return 0;
698 }
699 return 1;
700 }
701