Home | History | Annotate | Download | only in https
      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 *
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    382 teardown_testcase (struct MHD_Daemon *d)
    383 {
    384   MHD_stop_daemon (d);
    385 }
    386 
    387 int
    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
    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
    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