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