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