test_https_sni.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. This file is part of libmicrohttpd
  3. Copyright (C) 2013, 2016 Christian Grothoff
  4. libmicrohttpd is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. libmicrohttpd is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with libmicrohttpd; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. /**
  18. * @file test_https_sni.c
  19. * @brief Testcase for libmicrohttpd HTTPS with SNI operations
  20. * @author Christian Grothoff
  21. */
  22. #include "platform.h"
  23. #include "microhttpd.h"
  24. #include <limits.h>
  25. #include <sys/stat.h>
  26. #include <curl/curl.h>
  27. #ifdef MHD_HTTPS_REQUIRE_GRYPT
  28. #include <gcrypt.h>
  29. #endif /* MHD_HTTPS_REQUIRE_GRYPT */
  30. #include "tls_test_common.h"
  31. #include <gnutls/gnutls.h>
  32. /* This test only works with GnuTLS >= 3.0 */
  33. #if GNUTLS_VERSION_MAJOR >= 3
  34. #include <gnutls/abstract.h>
  35. /**
  36. * A hostname, server key and certificate.
  37. */
  38. struct Hosts
  39. {
  40. struct Hosts *next;
  41. const char *hostname;
  42. gnutls_pcert_st pcrt;
  43. gnutls_privkey_t key;
  44. };
  45. /**
  46. * Linked list of supported TLDs and respective certificates.
  47. */
  48. static struct Hosts *hosts;
  49. /* Load the certificate and the private key.
  50. * (This code is largely taken from GnuTLS).
  51. */
  52. static void
  53. load_keys(const char *hostname,
  54. const char *CERT_FILE,
  55. const char *KEY_FILE)
  56. {
  57. int ret;
  58. gnutls_datum_t data;
  59. struct Hosts *host;
  60. host = malloc (sizeof (struct Hosts));
  61. if (NULL == host)
  62. abort ();
  63. host->hostname = hostname;
  64. host->next = hosts;
  65. hosts = host;
  66. ret = gnutls_load_file (CERT_FILE, &data);
  67. if (ret < 0)
  68. {
  69. fprintf (stderr,
  70. "*** Error loading certificate file %s.\n",
  71. CERT_FILE);
  72. exit (1);
  73. }
  74. ret =
  75. gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
  76. 0);
  77. if (ret < 0)
  78. {
  79. fprintf (stderr,
  80. "*** Error loading certificate file: %s\n",
  81. gnutls_strerror (ret));
  82. exit (1);
  83. }
  84. gnutls_free (data.data);
  85. ret = gnutls_load_file (KEY_FILE, &data);
  86. if (ret < 0)
  87. {
  88. fprintf (stderr,
  89. "*** Error loading key file %s.\n",
  90. KEY_FILE);
  91. exit (1);
  92. }
  93. gnutls_privkey_init (&host->key);
  94. ret =
  95. gnutls_privkey_import_x509_raw (host->key,
  96. &data, GNUTLS_X509_FMT_PEM,
  97. NULL, 0);
  98. if (ret < 0)
  99. {
  100. fprintf (stderr,
  101. "*** Error loading key file: %s\n",
  102. gnutls_strerror (ret));
  103. exit (1);
  104. }
  105. gnutls_free (data.data);
  106. }
  107. /**
  108. * @param session the session we are giving a cert for
  109. * @param req_ca_dn NULL on server side
  110. * @param nreqs length of req_ca_dn, and thus 0 on server side
  111. * @param pk_algos NULL on server side
  112. * @param pk_algos_length 0 on server side
  113. * @param pcert list of certificates (to be set)
  114. * @param pcert_length length of pcert (to be set)
  115. * @param pkey the private key (to be set)
  116. */
  117. static int
  118. sni_callback (gnutls_session_t session,
  119. const gnutls_datum_t* req_ca_dn,
  120. int nreqs,
  121. const gnutls_pk_algorithm_t* pk_algos,
  122. int pk_algos_length,
  123. gnutls_pcert_st** pcert,
  124. unsigned int *pcert_length,
  125. gnutls_privkey_t * pkey)
  126. {
  127. char name[256];
  128. size_t name_len;
  129. struct Hosts *host;
  130. unsigned int type;
  131. name_len = sizeof (name);
  132. if (GNUTLS_E_SUCCESS !=
  133. gnutls_server_name_get (session,
  134. name,
  135. &name_len,
  136. &type,
  137. 0 /* index */))
  138. return -1;
  139. for (host = hosts; NULL != host; host = host->next)
  140. if (0 == strncmp (name, host->hostname, name_len))
  141. break;
  142. if (NULL == host)
  143. {
  144. fprintf (stderr,
  145. "Need certificate for %.*s\n",
  146. (int) name_len,
  147. name);
  148. return -1;
  149. }
  150. #if 0
  151. fprintf (stderr,
  152. "Returning certificate for %.*s\n",
  153. (int) name_len,
  154. name);
  155. #endif
  156. *pkey = host->key;
  157. *pcert_length = 1;
  158. *pcert = &host->pcrt;
  159. return 0;
  160. }
  161. /* perform a HTTP GET request via SSL/TLS */
  162. static int
  163. do_get (const char *url)
  164. {
  165. CURL *c;
  166. struct CBC cbc;
  167. CURLcode errornum;
  168. size_t len;
  169. struct curl_slist *dns_info;
  170. len = strlen (test_data);
  171. if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
  172. {
  173. fprintf (stderr, MHD_E_MEM);
  174. return -1;
  175. }
  176. cbc.size = len;
  177. cbc.pos = 0;
  178. c = curl_easy_init ();
  179. #if DEBUG_HTTPS_TEST
  180. curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
  181. #endif
  182. curl_easy_setopt (c, CURLOPT_URL, url);
  183. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  184. curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
  185. curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
  186. curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
  187. curl_easy_setopt (c, CURLOPT_FILE, &cbc);
  188. /* perform peer authentication */
  189. /* TODO merge into send_curl_req */
  190. curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
  191. curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 2);
  192. dns_info = curl_slist_append (NULL, "host1:4233:127.0.0.1");
  193. dns_info = curl_slist_append (dns_info, "host2:4233:127.0.0.1");
  194. curl_easy_setopt (c, CURLOPT_RESOLVE, dns_info);
  195. curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
  196. /* NOTE: use of CONNECTTIMEOUT without also
  197. setting NOSIGNAL results in really weird
  198. crashes on my system! */
  199. curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
  200. if (CURLE_OK != (errornum = curl_easy_perform (c)))
  201. {
  202. fprintf (stderr, "curl_easy_perform failed: `%s'\n",
  203. curl_easy_strerror (errornum));
  204. curl_easy_cleanup (c);
  205. free (cbc.buf);
  206. curl_slist_free_all (dns_info);
  207. return errornum;
  208. }
  209. curl_easy_cleanup (c);
  210. curl_slist_free_all (dns_info);
  211. if (memcmp (cbc.buf, test_data, len) != 0)
  212. {
  213. fprintf (stderr, "Error: local file & received file differ.\n");
  214. free (cbc.buf);
  215. return -1;
  216. }
  217. free (cbc.buf);
  218. return 0;
  219. }
  220. int
  221. main (int argc, char *const *argv)
  222. {
  223. unsigned int error_count = 0;
  224. struct MHD_Daemon *d;
  225. #ifdef MHD_HTTPS_REQUIRE_GRYPT
  226. gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
  227. #ifdef GCRYCTL_INITIALIZATION_FINISHED
  228. gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
  229. #endif
  230. #endif /* MHD_HTTPS_REQUIRE_GRYPT */
  231. if (0 != curl_global_init (CURL_GLOBAL_ALL))
  232. {
  233. fprintf (stderr, "Error: %s\n", strerror (errno));
  234. return 99;
  235. }
  236. if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version)
  237. {
  238. fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n");
  239. curl_global_cleanup ();
  240. return 77;
  241. }
  242. load_keys ("host1", ABS_SRCDIR "/host1.crt", ABS_SRCDIR "/host1.key");
  243. load_keys ("host2", ABS_SRCDIR "/host2.crt", ABS_SRCDIR "/host2.key");
  244. d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS | MHD_USE_ERROR_LOG,
  245. 4233,
  246. NULL, NULL,
  247. &http_ahc, NULL,
  248. MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
  249. MHD_OPTION_END);
  250. if (d == NULL)
  251. {
  252. fprintf (stderr, MHD_E_SERVER_INIT);
  253. return -1;
  254. }
  255. if (0 != do_get ("https://host1:4233/"))
  256. error_count++;
  257. if (0 != do_get ("https://host2:4233/"))
  258. error_count++;
  259. MHD_stop_daemon (d);
  260. curl_global_cleanup ();
  261. if (error_count != 0)
  262. fprintf (stderr, "Failed test: %s, error: %u.\n", argv[0], error_count);
  263. return (0 != error_count) ? 1 : 0;
  264. }
  265. #else
  266. int main ()
  267. {
  268. fprintf (stderr,
  269. "SNI not supported by GnuTLS < 3.0\n");
  270. return 77;
  271. }
  272. #endif