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