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