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