test_https_sni.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. #include <gcrypt.h>
  28. #include "tls_test_common.h"
  29. #include <gnutls/gnutls.h>
  30. /* This test only works with GnuTLS >= 3.0 */
  31. #if GNUTLS_VERSION_MAJOR >= 3
  32. #include <gnutls/abstract.h>
  33. /**
  34. * A hostname, server key and certificate.
  35. */
  36. struct Hosts
  37. {
  38. struct Hosts *next;
  39. const char *hostname;
  40. gnutls_pcert_st pcrt;
  41. gnutls_privkey_t key;
  42. };
  43. /**
  44. * Linked list of supported TLDs and respective certificates.
  45. */
  46. static struct Hosts *hosts;
  47. /* Load the certificate and the private key.
  48. * (This code is largely taken from GnuTLS).
  49. */
  50. static void
  51. load_keys(const char *hostname,
  52. const char *CERT_FILE,
  53. const char *KEY_FILE)
  54. {
  55. int ret;
  56. gnutls_datum_t data;
  57. struct Hosts *host;
  58. host = malloc (sizeof (struct Hosts));
  59. if (NULL == host)
  60. abort ();
  61. host->hostname = hostname;
  62. host->next = hosts;
  63. hosts = host;
  64. ret = gnutls_load_file (CERT_FILE, &data);
  65. if (ret < 0)
  66. {
  67. fprintf (stderr,
  68. "*** Error loading certificate file %s.\n",
  69. CERT_FILE);
  70. exit (1);
  71. }
  72. ret =
  73. gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
  74. 0);
  75. if (ret < 0)
  76. {
  77. fprintf (stderr,
  78. "*** Error loading certificate file: %s\n",
  79. gnutls_strerror (ret));
  80. exit (1);
  81. }
  82. gnutls_free (data.data);
  83. ret = gnutls_load_file (KEY_FILE, &data);
  84. if (ret < 0)
  85. {
  86. fprintf (stderr,
  87. "*** Error loading key file %s.\n",
  88. KEY_FILE);
  89. exit (1);
  90. }
  91. gnutls_privkey_init (&host->key);
  92. ret =
  93. gnutls_privkey_import_x509_raw (host->key,
  94. &data, GNUTLS_X509_FMT_PEM,
  95. NULL, 0);
  96. if (ret < 0)
  97. {
  98. fprintf (stderr,
  99. "*** Error loading key file: %s\n",
  100. gnutls_strerror (ret));
  101. exit (1);
  102. }
  103. gnutls_free (data.data);
  104. }
  105. /**
  106. * @param session the session we are giving a cert for
  107. * @param req_ca_dn NULL on server side
  108. * @param nreqs length of req_ca_dn, and thus 0 on server side
  109. * @param pk_algos NULL on server side
  110. * @param pk_algos_length 0 on server side
  111. * @param pcert list of certificates (to be set)
  112. * @param pcert_length length of pcert (to be set)
  113. * @param pkey the private key (to be set)
  114. */
  115. static int
  116. sni_callback (gnutls_session_t session,
  117. const gnutls_datum_t* req_ca_dn,
  118. int nreqs,
  119. const gnutls_pk_algorithm_t* pk_algos,
  120. int pk_algos_length,
  121. gnutls_pcert_st** pcert,
  122. unsigned int *pcert_length,
  123. gnutls_privkey_t * pkey)
  124. {
  125. char name[256];
  126. size_t name_len;
  127. struct Hosts *host;
  128. unsigned int type;
  129. name_len = sizeof (name);
  130. if (GNUTLS_E_SUCCESS !=
  131. gnutls_server_name_get (session,
  132. name,
  133. &name_len,
  134. &type,
  135. 0 /* index */))
  136. return -1;
  137. for (host = hosts; NULL != host; host = host->next)
  138. if (0 == strncmp (name, host->hostname, name_len))
  139. break;
  140. if (NULL == host)
  141. {
  142. fprintf (stderr,
  143. "Need certificate for %.*s\n",
  144. (int) name_len,
  145. name);
  146. return -1;
  147. }
  148. #if 0
  149. fprintf (stderr,
  150. "Returning certificate for %.*s\n",
  151. (int) name_len,
  152. name);
  153. #endif
  154. *pkey = host->key;
  155. *pcert_length = 1;
  156. *pcert = &host->pcrt;
  157. return 0;
  158. }
  159. /* perform a HTTP GET request via SSL/TLS */
  160. static int
  161. do_get (const char *url)
  162. {
  163. CURL *c;
  164. struct CBC cbc;
  165. CURLcode errornum;
  166. size_t len;
  167. struct curl_slist *dns_info;
  168. len = strlen (test_data);
  169. if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
  170. {
  171. fprintf (stderr, MHD_E_MEM);
  172. return -1;
  173. }
  174. cbc.size = len;
  175. cbc.pos = 0;
  176. c = curl_easy_init ();
  177. #if DEBUG_HTTPS_TEST
  178. curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
  179. #endif
  180. curl_easy_setopt (c, CURLOPT_URL, url);
  181. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  182. curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
  183. curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
  184. curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
  185. curl_easy_setopt (c, CURLOPT_FILE, &cbc);
  186. /* perform peer authentication */
  187. /* TODO merge into send_curl_req */
  188. curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
  189. curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 2);
  190. dns_info = curl_slist_append (NULL, "host1:4233:127.0.0.1");
  191. dns_info = curl_slist_append (dns_info, "host2:4233:127.0.0.1");
  192. curl_easy_setopt (c, CURLOPT_RESOLVE, dns_info);
  193. curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
  194. /* NOTE: use of CONNECTTIMEOUT without also
  195. setting NOSIGNAL results in really weird
  196. crashes on my system! */
  197. curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
  198. if (CURLE_OK != (errornum = curl_easy_perform (c)))
  199. {
  200. fprintf (stderr, "curl_easy_perform failed: `%s'\n",
  201. curl_easy_strerror (errornum));
  202. curl_easy_cleanup (c);
  203. free (cbc.buf);
  204. curl_slist_free_all (dns_info);
  205. return errornum;
  206. }
  207. curl_easy_cleanup (c);
  208. curl_slist_free_all (dns_info);
  209. if (memcmp (cbc.buf, test_data, len) != 0)
  210. {
  211. fprintf (stderr, "Error: local file & received file differ.\n");
  212. free (cbc.buf);
  213. return -1;
  214. }
  215. free (cbc.buf);
  216. return 0;
  217. }
  218. int
  219. main (int argc, char *const *argv)
  220. {
  221. unsigned int error_count = 0;
  222. struct MHD_Daemon *d;
  223. gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
  224. #ifdef GCRYCTL_INITIALIZATION_FINISHED
  225. gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
  226. #endif
  227. if (0 != curl_global_init (CURL_GLOBAL_ALL))
  228. {
  229. fprintf (stderr, "Error: %s\n", strerror (errno));
  230. return 77;
  231. }
  232. load_keys ("host1", ABS_SRCDIR "/host1.crt", ABS_SRCDIR "/host1.key");
  233. load_keys ("host2", ABS_SRCDIR "/host2.crt", ABS_SRCDIR "/host2.key");
  234. d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | MHD_USE_DEBUG,
  235. 4233,
  236. NULL, NULL,
  237. &http_ahc, NULL,
  238. MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
  239. MHD_OPTION_END);
  240. if (d == NULL)
  241. {
  242. fprintf (stderr, MHD_E_SERVER_INIT);
  243. return -1;
  244. }
  245. if (0 != do_get ("https://host1:4233/"))
  246. error_count++;
  247. if (0 != do_get ("https://host2:4233/"))
  248. error_count++;
  249. MHD_stop_daemon (d);
  250. curl_global_cleanup ();
  251. return (0 != error_count) ? 1 : 0;
  252. }
  253. #else
  254. int main ()
  255. {
  256. fprintf (stderr,
  257. "SNI not supported by GnuTLS < 3.0\n");
  258. return 77;
  259. }
  260. #endif