]>
Commit | Line | Data |
---|---|---|
9a2fd434 DB |
1 | /* |
2 | * Copyright (C) 2015 Red Hat, Inc. | |
3 | * | |
4 | * This library is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
8 | * | |
9 | * This library is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public | |
15 | * License along with this library. If not, see | |
16 | * <http://www.gnu.org/licenses/>. | |
17 | * | |
18 | * Author: Daniel P. Berrange <[email protected]> | |
19 | */ | |
20 | ||
681c28a3 | 21 | #include "qemu/osdep.h" |
9a2fd434 | 22 | |
9a2fd434 | 23 | #include "crypto-tls-x509-helpers.h" |
dbddad70 | 24 | #include "crypto/init.h" |
9a2fd434 DB |
25 | #include "qemu/sockets.h" |
26 | ||
27 | #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT | |
28 | ||
29 | /* | |
30 | * This stores some static data that is needed when | |
31 | * encoding extensions in the x509 certs | |
32 | */ | |
33 | ASN1_TYPE pkix_asn1; | |
34 | ||
35 | /* | |
36 | * To avoid consuming random entropy to generate keys, | |
37 | * here's one we prepared earlier :-) | |
38 | */ | |
39 | gnutls_x509_privkey_t privkey; | |
40 | # define PRIVATE_KEY \ | |
41 | "-----BEGIN PRIVATE KEY-----\n" \ | |
42 | "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \ | |
43 | "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \ | |
44 | "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \ | |
45 | "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \ | |
46 | "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \ | |
47 | "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \ | |
48 | "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \ | |
49 | "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \ | |
50 | "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \ | |
51 | "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \ | |
52 | "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \ | |
53 | "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \ | |
54 | "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \ | |
55 | "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \ | |
56 | "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \ | |
57 | "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \ | |
58 | "-----END PRIVATE KEY-----\n" | |
59 | ||
60 | /* | |
61 | * This loads the private key we defined earlier | |
62 | */ | |
63 | static gnutls_x509_privkey_t test_tls_load_key(void) | |
64 | { | |
65 | gnutls_x509_privkey_t key; | |
66 | const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, | |
67 | strlen(PRIVATE_KEY) }; | |
68 | int err; | |
69 | ||
70 | err = gnutls_x509_privkey_init(&key); | |
71 | if (err < 0) { | |
72 | g_critical("Failed to init key %s", gnutls_strerror(err)); | |
73 | abort(); | |
74 | } | |
75 | ||
76 | err = gnutls_x509_privkey_import(key, &data, | |
77 | GNUTLS_X509_FMT_PEM); | |
78 | if (err < 0) { | |
79 | if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && | |
80 | err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { | |
81 | g_critical("Failed to import key %s", gnutls_strerror(err)); | |
82 | abort(); | |
83 | } | |
84 | ||
85 | err = gnutls_x509_privkey_import_pkcs8( | |
86 | key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); | |
87 | if (err < 0) { | |
88 | g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); | |
89 | abort(); | |
90 | } | |
91 | } | |
92 | ||
93 | return key; | |
94 | } | |
95 | ||
96 | ||
97 | void test_tls_init(const char *keyfile) | |
98 | { | |
dbddad70 | 99 | qcrypto_init(&error_abort); |
9a2fd434 DB |
100 | |
101 | if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { | |
102 | abort(); | |
103 | } | |
104 | ||
105 | privkey = test_tls_load_key(); | |
106 | if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { | |
107 | abort(); | |
108 | } | |
109 | } | |
110 | ||
111 | ||
112 | void test_tls_cleanup(const char *keyfile) | |
113 | { | |
114 | asn1_delete_structure(&pkix_asn1); | |
115 | unlink(keyfile); | |
116 | } | |
117 | ||
118 | /* | |
119 | * Turns an ASN1 object into a DER encoded byte array | |
120 | */ | |
121 | static void test_tls_der_encode(ASN1_TYPE src, | |
122 | const char *src_name, | |
123 | gnutls_datum_t *res) | |
124 | { | |
125 | int size; | |
126 | char *data = NULL; | |
127 | ||
128 | size = 0; | |
129 | asn1_der_coding(src, src_name, NULL, &size, NULL); | |
130 | ||
131 | data = g_new0(char, size); | |
132 | ||
133 | asn1_der_coding(src, src_name, data, &size, NULL); | |
134 | ||
135 | res->data = (unsigned char *)data; | |
136 | res->size = size; | |
137 | } | |
138 | ||
139 | ||
140 | static void | |
141 | test_tls_get_ipaddr(const char *addrstr, | |
142 | char **data, | |
143 | int *datalen) | |
144 | { | |
145 | struct addrinfo *res; | |
146 | struct addrinfo hints; | |
147 | ||
148 | memset(&hints, 0, sizeof(hints)); | |
149 | hints.ai_flags = AI_NUMERICHOST; | |
150 | g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); | |
151 | ||
152 | *datalen = res->ai_addrlen; | |
153 | *data = g_new(char, *datalen); | |
154 | memcpy(*data, res->ai_addr, *datalen); | |
7b35030e | 155 | freeaddrinfo(res); |
9a2fd434 DB |
156 | } |
157 | ||
158 | /* | |
159 | * This is a fairly lame x509 certificate generator. | |
160 | * | |
161 | * Do not copy/use this code for generating real certificates | |
162 | * since it leaves out many things that you would want in | |
163 | * certificates for real world usage. | |
164 | * | |
165 | * This is good enough only for doing tests of the QEMU | |
166 | * TLS certificate code | |
167 | */ | |
168 | void | |
169 | test_tls_generate_cert(QCryptoTLSTestCertReq *req, | |
170 | gnutls_x509_crt_t ca) | |
171 | { | |
172 | gnutls_x509_crt_t crt; | |
173 | int err; | |
174 | static char buffer[1024 * 1024]; | |
175 | size_t size = sizeof(buffer); | |
176 | char serial[5] = { 1, 2, 3, 4, 0 }; | |
177 | gnutls_datum_t der; | |
178 | time_t start = time(NULL) + (60 * 60 * req->start_offset); | |
179 | time_t expire = time(NULL) + (60 * 60 * (req->expire_offset | |
180 | ? req->expire_offset : 24)); | |
181 | ||
182 | /* | |
183 | * Prepare our new certificate object | |
184 | */ | |
185 | err = gnutls_x509_crt_init(&crt); | |
186 | if (err < 0) { | |
187 | g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); | |
188 | abort(); | |
189 | } | |
190 | err = gnutls_x509_crt_set_key(crt, privkey); | |
191 | if (err < 0) { | |
192 | g_critical("Failed to set certificate key %s", gnutls_strerror(err)); | |
193 | abort(); | |
194 | } | |
195 | ||
196 | /* | |
197 | * A v3 certificate is required in order to be able | |
198 | * set any of the basic constraints, key purpose and | |
199 | * key usage data | |
200 | */ | |
201 | gnutls_x509_crt_set_version(crt, 3); | |
202 | ||
203 | if (req->country) { | |
204 | err = gnutls_x509_crt_set_dn_by_oid( | |
205 | crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, | |
206 | req->country, strlen(req->country)); | |
207 | if (err < 0) { | |
208 | g_critical("Failed to set certificate country name %s", | |
209 | gnutls_strerror(err)); | |
210 | abort(); | |
211 | } | |
212 | } | |
213 | if (req->cn) { | |
214 | err = gnutls_x509_crt_set_dn_by_oid( | |
215 | crt, GNUTLS_OID_X520_COMMON_NAME, 0, | |
216 | req->cn, strlen(req->cn)); | |
217 | if (err < 0) { | |
218 | g_critical("Failed to set certificate common name %s", | |
219 | gnutls_strerror(err)); | |
220 | abort(); | |
221 | } | |
222 | } | |
223 | ||
224 | /* | |
225 | * Setup the subject altnames, which are used | |
226 | * for hostname checks in live sessions | |
227 | */ | |
228 | if (req->altname1) { | |
229 | err = gnutls_x509_crt_set_subject_alt_name( | |
230 | crt, GNUTLS_SAN_DNSNAME, | |
231 | req->altname1, | |
232 | strlen(req->altname1), | |
233 | GNUTLS_FSAN_APPEND); | |
234 | if (err < 0) { | |
235 | g_critical("Failed to set certificate alt name %s", | |
236 | gnutls_strerror(err)); | |
237 | abort(); | |
238 | } | |
239 | } | |
240 | if (req->altname2) { | |
241 | err = gnutls_x509_crt_set_subject_alt_name( | |
242 | crt, GNUTLS_SAN_DNSNAME, | |
243 | req->altname2, | |
244 | strlen(req->altname2), | |
245 | GNUTLS_FSAN_APPEND); | |
246 | if (err < 0) { | |
247 | g_critical("Failed to set certificate %s alt name", | |
248 | gnutls_strerror(err)); | |
249 | abort(); | |
250 | } | |
251 | } | |
252 | ||
253 | /* | |
254 | * IP address need to be put into the cert in their | |
255 | * raw byte form, not strings, hence this is a little | |
256 | * more complicated | |
257 | */ | |
258 | if (req->ipaddr1) { | |
259 | char *data; | |
260 | int len; | |
261 | ||
262 | test_tls_get_ipaddr(req->ipaddr1, &data, &len); | |
263 | ||
264 | err = gnutls_x509_crt_set_subject_alt_name( | |
265 | crt, GNUTLS_SAN_IPADDRESS, | |
266 | data, len, GNUTLS_FSAN_APPEND); | |
267 | if (err < 0) { | |
268 | g_critical("Failed to set certificate alt name %s", | |
269 | gnutls_strerror(err)); | |
270 | abort(); | |
271 | } | |
272 | g_free(data); | |
273 | } | |
274 | if (req->ipaddr2) { | |
275 | char *data; | |
276 | int len; | |
277 | ||
278 | test_tls_get_ipaddr(req->ipaddr2, &data, &len); | |
279 | ||
280 | err = gnutls_x509_crt_set_subject_alt_name( | |
281 | crt, GNUTLS_SAN_IPADDRESS, | |
282 | data, len, GNUTLS_FSAN_APPEND); | |
283 | if (err < 0) { | |
284 | g_critical("Failed to set certificate alt name %s", | |
285 | gnutls_strerror(err)); | |
286 | abort(); | |
287 | } | |
288 | g_free(data); | |
289 | } | |
290 | ||
291 | ||
292 | /* | |
293 | * Basic constraints are used to decide if the cert | |
294 | * is for a CA or not. We can't use the convenient | |
295 | * gnutls API for setting this, since it hardcodes | |
296 | * the 'critical' field which we want control over | |
297 | */ | |
298 | if (req->basicConstraintsEnable) { | |
299 | ASN1_TYPE ext = ASN1_TYPE_EMPTY; | |
300 | ||
301 | asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); | |
302 | asn1_write_value(ext, "cA", | |
303 | req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); | |
304 | asn1_write_value(ext, "pathLenConstraint", NULL, 0); | |
305 | test_tls_der_encode(ext, "", &der); | |
306 | err = gnutls_x509_crt_set_extension_by_oid( | |
307 | crt, "2.5.29.19", | |
308 | der.data, der.size, | |
309 | req->basicConstraintsCritical); | |
310 | if (err < 0) { | |
311 | g_critical("Failed to set certificate basic constraints %s", | |
312 | gnutls_strerror(err)); | |
313 | g_free(der.data); | |
314 | abort(); | |
315 | } | |
316 | asn1_delete_structure(&ext); | |
317 | g_free(der.data); | |
318 | } | |
319 | ||
320 | /* | |
321 | * Next up the key usage extension. Again we can't | |
322 | * use the gnutls API since it hardcodes the extension | |
323 | * to be 'critical' | |
324 | */ | |
325 | if (req->keyUsageEnable) { | |
326 | ASN1_TYPE ext = ASN1_TYPE_EMPTY; | |
327 | char str[2]; | |
328 | ||
329 | str[0] = req->keyUsageValue & 0xff; | |
330 | str[1] = (req->keyUsageValue >> 8) & 0xff; | |
331 | ||
332 | asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); | |
333 | asn1_write_value(ext, "", str, 9); | |
334 | test_tls_der_encode(ext, "", &der); | |
335 | err = gnutls_x509_crt_set_extension_by_oid( | |
336 | crt, "2.5.29.15", | |
337 | der.data, der.size, | |
338 | req->keyUsageCritical); | |
339 | if (err < 0) { | |
340 | g_critical("Failed to set certificate key usage %s", | |
341 | gnutls_strerror(err)); | |
342 | g_free(der.data); | |
343 | abort(); | |
344 | } | |
345 | asn1_delete_structure(&ext); | |
346 | g_free(der.data); | |
347 | } | |
348 | ||
349 | /* | |
350 | * Finally the key purpose extension. This time | |
351 | * gnutls has the opposite problem, always hardcoding | |
352 | * it to be non-critical. So once again we have to | |
353 | * set this the hard way building up ASN1 data ourselves | |
354 | */ | |
355 | if (req->keyPurposeEnable) { | |
356 | ASN1_TYPE ext = ASN1_TYPE_EMPTY; | |
357 | ||
358 | asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); | |
359 | if (req->keyPurposeOID1) { | |
360 | asn1_write_value(ext, "", "NEW", 1); | |
361 | asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); | |
362 | } | |
363 | if (req->keyPurposeOID2) { | |
364 | asn1_write_value(ext, "", "NEW", 1); | |
365 | asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); | |
366 | } | |
367 | test_tls_der_encode(ext, "", &der); | |
368 | err = gnutls_x509_crt_set_extension_by_oid( | |
369 | crt, "2.5.29.37", | |
370 | der.data, der.size, | |
371 | req->keyPurposeCritical); | |
372 | if (err < 0) { | |
373 | g_critical("Failed to set certificate key purpose %s", | |
374 | gnutls_strerror(err)); | |
375 | g_free(der.data); | |
376 | abort(); | |
377 | } | |
378 | asn1_delete_structure(&ext); | |
379 | g_free(der.data); | |
380 | } | |
381 | ||
382 | /* | |
383 | * Any old serial number will do, so lets pick 5 | |
384 | */ | |
385 | err = gnutls_x509_crt_set_serial(crt, serial, 5); | |
386 | if (err < 0) { | |
387 | g_critical("Failed to set certificate serial %s", | |
388 | gnutls_strerror(err)); | |
389 | abort(); | |
390 | } | |
391 | ||
392 | err = gnutls_x509_crt_set_activation_time(crt, start); | |
393 | if (err < 0) { | |
394 | g_critical("Failed to set certificate activation %s", | |
395 | gnutls_strerror(err)); | |
396 | abort(); | |
397 | } | |
398 | err = gnutls_x509_crt_set_expiration_time(crt, expire); | |
399 | if (err < 0) { | |
400 | g_critical("Failed to set certificate expiration %s", | |
401 | gnutls_strerror(err)); | |
402 | abort(); | |
403 | } | |
404 | ||
405 | ||
406 | /* | |
407 | * If no 'ca' is set then we are self signing | |
408 | * the cert. This is done for the root CA certs | |
409 | */ | |
23c1595b DB |
410 | err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, |
411 | GNUTLS_DIG_SHA256, 0); | |
9a2fd434 DB |
412 | if (err < 0) { |
413 | g_critical("Failed to sign certificate %s", | |
414 | gnutls_strerror(err)); | |
415 | abort(); | |
416 | } | |
417 | ||
418 | /* | |
419 | * Finally write the new cert out to disk | |
420 | */ | |
421 | err = gnutls_x509_crt_export( | |
422 | crt, GNUTLS_X509_FMT_PEM, buffer, &size); | |
423 | if (err < 0) { | |
424 | g_critical("Failed to export certificate %s: %d", | |
425 | gnutls_strerror(err), err); | |
426 | abort(); | |
427 | } | |
428 | ||
429 | if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { | |
430 | g_critical("Failed to write certificate %s", | |
431 | req->filename); | |
432 | abort(); | |
433 | } | |
434 | ||
435 | req->crt = crt; | |
436 | } | |
437 | ||
438 | ||
439 | void test_tls_write_cert_chain(const char *filename, | |
440 | gnutls_x509_crt_t *certs, | |
441 | size_t ncerts) | |
442 | { | |
443 | size_t i; | |
444 | size_t capacity = 1024, offset = 0; | |
445 | char *buffer = g_new0(char, capacity); | |
446 | int err; | |
447 | ||
448 | for (i = 0; i < ncerts; i++) { | |
449 | size_t len = capacity - offset; | |
450 | retry: | |
451 | err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, | |
452 | buffer + offset, &len); | |
453 | if (err < 0) { | |
454 | if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { | |
455 | buffer = g_renew(char, buffer, offset + len); | |
456 | capacity = offset + len; | |
457 | goto retry; | |
458 | } | |
459 | g_critical("Failed to export certificate chain %s: %d", | |
460 | gnutls_strerror(err), err); | |
461 | abort(); | |
462 | } | |
463 | offset += len; | |
464 | } | |
465 | ||
466 | if (!g_file_set_contents(filename, buffer, offset, NULL)) { | |
467 | abort(); | |
468 | } | |
7b35030e | 469 | g_free(buffer); |
9a2fd434 DB |
470 | } |
471 | ||
472 | ||
473 | void test_tls_discard_cert(QCryptoTLSTestCertReq *req) | |
474 | { | |
475 | if (!req->crt) { | |
476 | return; | |
477 | } | |
478 | ||
479 | gnutls_x509_crt_deinit(req->crt); | |
480 | req->crt = NULL; | |
481 | ||
482 | if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { | |
483 | unlink(req->filename); | |
484 | } | |
485 | } | |
486 | ||
487 | #endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ |