]> Git Repo - qemu.git/blob - tests/crypto-tls-x509-helpers.c
Merge remote-tracking branch 'remotes/berrange/tags/pull-qcrypto-next-2016-02-02...
[qemu.git] / tests / crypto-tls-x509-helpers.c
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
21 #include <stdlib.h>
22 #include <fcntl.h>
23
24 #include "config-host.h"
25 #include "crypto-tls-x509-helpers.h"
26 #include "qemu/sockets.h"
27
28 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
29
30 /*
31  * This stores some static data that is needed when
32  * encoding extensions in the x509 certs
33  */
34 ASN1_TYPE pkix_asn1;
35
36 /*
37  * To avoid consuming random entropy to generate keys,
38  * here's one we prepared earlier :-)
39  */
40 gnutls_x509_privkey_t privkey;
41 # define PRIVATE_KEY                                              \
42     "-----BEGIN PRIVATE KEY-----\n"                               \
43     "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n"     \
44     "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n"     \
45     "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n"     \
46     "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n"     \
47     "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n"     \
48     "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n"     \
49     "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n"     \
50     "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n"     \
51     "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n"     \
52     "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n"     \
53     "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n"     \
54     "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n"     \
55     "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n"     \
56     "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n"     \
57     "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n"     \
58     "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n"         \
59     "-----END PRIVATE KEY-----\n"
60
61 /*
62  * This loads the private key we defined earlier
63  */
64 static gnutls_x509_privkey_t test_tls_load_key(void)
65 {
66     gnutls_x509_privkey_t key;
67     const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY,
68                                   strlen(PRIVATE_KEY) };
69     int err;
70
71     err = gnutls_x509_privkey_init(&key);
72     if (err < 0) {
73         g_critical("Failed to init key %s", gnutls_strerror(err));
74         abort();
75     }
76
77     err = gnutls_x509_privkey_import(key, &data,
78                                      GNUTLS_X509_FMT_PEM);
79     if (err < 0) {
80         if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR &&
81             err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
82             g_critical("Failed to import key %s", gnutls_strerror(err));
83             abort();
84         }
85
86         err = gnutls_x509_privkey_import_pkcs8(
87             key, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
88         if (err < 0) {
89             g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err));
90             abort();
91         }
92     }
93
94     return key;
95 }
96
97
98 void test_tls_init(const char *keyfile)
99 {
100     gnutls_global_init();
101
102     if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) {
103         abort();
104     }
105
106     privkey = test_tls_load_key();
107     if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) {
108         abort();
109     }
110 }
111
112
113 void test_tls_cleanup(const char *keyfile)
114 {
115     asn1_delete_structure(&pkix_asn1);
116     unlink(keyfile);
117 }
118
119 /*
120  * Turns an ASN1 object into a DER encoded byte array
121  */
122 static void test_tls_der_encode(ASN1_TYPE src,
123                                 const char *src_name,
124                                 gnutls_datum_t *res)
125 {
126   int size;
127   char *data = NULL;
128
129   size = 0;
130   asn1_der_coding(src, src_name, NULL, &size, NULL);
131
132   data = g_new0(char, size);
133
134   asn1_der_coding(src, src_name, data, &size, NULL);
135
136   res->data = (unsigned char *)data;
137   res->size = size;
138 }
139
140
141 static void
142 test_tls_get_ipaddr(const char *addrstr,
143                     char **data,
144                     int *datalen)
145 {
146     struct addrinfo *res;
147     struct addrinfo hints;
148
149     memset(&hints, 0, sizeof(hints));
150     hints.ai_flags = AI_NUMERICHOST;
151     g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0);
152
153     *datalen = res->ai_addrlen;
154     *data = g_new(char, *datalen);
155     memcpy(*data, res->ai_addr, *datalen);
156     freeaddrinfo(res);
157 }
158
159 /*
160  * This is a fairly lame x509 certificate generator.
161  *
162  * Do not copy/use this code for generating real certificates
163  * since it leaves out many things that you would want in
164  * certificates for real world usage.
165  *
166  * This is good enough only for doing tests of the QEMU
167  * TLS certificate code
168  */
169 void
170 test_tls_generate_cert(QCryptoTLSTestCertReq *req,
171                        gnutls_x509_crt_t ca)
172 {
173     gnutls_x509_crt_t crt;
174     int err;
175     static char buffer[1024 * 1024];
176     size_t size = sizeof(buffer);
177     char serial[5] = { 1, 2, 3, 4, 0 };
178     gnutls_datum_t der;
179     time_t start = time(NULL) + (60 * 60 * req->start_offset);
180     time_t expire = time(NULL) + (60 * 60 * (req->expire_offset
181                                              ? req->expire_offset : 24));
182
183     /*
184      * Prepare our new certificate object
185      */
186     err = gnutls_x509_crt_init(&crt);
187     if (err < 0) {
188         g_critical("Failed to initialize certificate %s", gnutls_strerror(err));
189         abort();
190     }
191     err = gnutls_x509_crt_set_key(crt, privkey);
192     if (err < 0) {
193         g_critical("Failed to set certificate key %s", gnutls_strerror(err));
194         abort();
195     }
196
197     /*
198      * A v3 certificate is required in order to be able
199      * set any of the basic constraints, key purpose and
200      * key usage data
201      */
202     gnutls_x509_crt_set_version(crt, 3);
203
204     if (req->country) {
205         err = gnutls_x509_crt_set_dn_by_oid(
206             crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
207             req->country, strlen(req->country));
208         if (err < 0) {
209             g_critical("Failed to set certificate country name %s",
210                        gnutls_strerror(err));
211             abort();
212         }
213     }
214     if (req->cn) {
215         err = gnutls_x509_crt_set_dn_by_oid(
216             crt, GNUTLS_OID_X520_COMMON_NAME, 0,
217             req->cn, strlen(req->cn));
218         if (err < 0) {
219             g_critical("Failed to set certificate common name %s",
220                        gnutls_strerror(err));
221             abort();
222         }
223     }
224
225     /*
226      * Setup the subject altnames, which are used
227      * for hostname checks in live sessions
228      */
229     if (req->altname1) {
230         err = gnutls_x509_crt_set_subject_alt_name(
231             crt, GNUTLS_SAN_DNSNAME,
232             req->altname1,
233             strlen(req->altname1),
234             GNUTLS_FSAN_APPEND);
235         if (err < 0) {
236             g_critical("Failed to set certificate alt name %s",
237                        gnutls_strerror(err));
238             abort();
239         }
240     }
241     if (req->altname2) {
242         err = gnutls_x509_crt_set_subject_alt_name(
243             crt, GNUTLS_SAN_DNSNAME,
244             req->altname2,
245             strlen(req->altname2),
246             GNUTLS_FSAN_APPEND);
247         if (err < 0) {
248             g_critical("Failed to set certificate %s alt name",
249                        gnutls_strerror(err));
250             abort();
251         }
252     }
253
254     /*
255      * IP address need to be put into the cert in their
256      * raw byte form, not strings, hence this is a little
257      * more complicated
258      */
259     if (req->ipaddr1) {
260         char *data;
261         int len;
262
263         test_tls_get_ipaddr(req->ipaddr1, &data, &len);
264
265         err = gnutls_x509_crt_set_subject_alt_name(
266             crt, GNUTLS_SAN_IPADDRESS,
267             data, len, GNUTLS_FSAN_APPEND);
268         if (err < 0) {
269             g_critical("Failed to set certificate alt name %s",
270                        gnutls_strerror(err));
271             abort();
272         }
273         g_free(data);
274     }
275     if (req->ipaddr2) {
276         char *data;
277         int len;
278
279         test_tls_get_ipaddr(req->ipaddr2, &data, &len);
280
281         err = gnutls_x509_crt_set_subject_alt_name(
282             crt, GNUTLS_SAN_IPADDRESS,
283             data, len, GNUTLS_FSAN_APPEND);
284         if (err < 0) {
285             g_critical("Failed to set certificate alt name %s",
286                        gnutls_strerror(err));
287             abort();
288         }
289         g_free(data);
290     }
291
292
293     /*
294      * Basic constraints are used to decide if the cert
295      * is for a CA or not. We can't use the convenient
296      * gnutls API for setting this, since it hardcodes
297      * the 'critical' field which we want control over
298      */
299     if (req->basicConstraintsEnable) {
300         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
301
302         asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext);
303         asn1_write_value(ext, "cA",
304                          req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1);
305         asn1_write_value(ext, "pathLenConstraint", NULL, 0);
306         test_tls_der_encode(ext, "", &der);
307         err = gnutls_x509_crt_set_extension_by_oid(
308             crt, "2.5.29.19",
309             der.data, der.size,
310             req->basicConstraintsCritical);
311         if (err < 0) {
312             g_critical("Failed to set certificate basic constraints %s",
313                        gnutls_strerror(err));
314             g_free(der.data);
315             abort();
316         }
317         asn1_delete_structure(&ext);
318         g_free(der.data);
319     }
320
321     /*
322      * Next up the key usage extension. Again we can't
323      * use the gnutls API since it hardcodes the extension
324      * to be 'critical'
325      */
326     if (req->keyUsageEnable) {
327         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
328         char str[2];
329
330         str[0] = req->keyUsageValue & 0xff;
331         str[1] = (req->keyUsageValue >> 8) & 0xff;
332
333         asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext);
334         asn1_write_value(ext, "", str, 9);
335         test_tls_der_encode(ext, "", &der);
336         err = gnutls_x509_crt_set_extension_by_oid(
337             crt, "2.5.29.15",
338             der.data, der.size,
339             req->keyUsageCritical);
340         if (err < 0) {
341             g_critical("Failed to set certificate key usage %s",
342                        gnutls_strerror(err));
343             g_free(der.data);
344             abort();
345         }
346         asn1_delete_structure(&ext);
347         g_free(der.data);
348     }
349
350     /*
351      * Finally the key purpose extension. This time
352      * gnutls has the opposite problem, always hardcoding
353      * it to be non-critical. So once again we have to
354      * set this the hard way building up ASN1 data ourselves
355      */
356     if (req->keyPurposeEnable) {
357         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
358
359         asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext);
360         if (req->keyPurposeOID1) {
361             asn1_write_value(ext, "", "NEW", 1);
362             asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1);
363         }
364         if (req->keyPurposeOID2) {
365             asn1_write_value(ext, "", "NEW", 1);
366             asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1);
367         }
368         test_tls_der_encode(ext, "", &der);
369         err = gnutls_x509_crt_set_extension_by_oid(
370             crt, "2.5.29.37",
371             der.data, der.size,
372             req->keyPurposeCritical);
373         if (err < 0) {
374             g_critical("Failed to set certificate key purpose %s",
375                        gnutls_strerror(err));
376             g_free(der.data);
377             abort();
378         }
379         asn1_delete_structure(&ext);
380         g_free(der.data);
381     }
382
383     /*
384      * Any old serial number will do, so lets pick 5
385      */
386     err = gnutls_x509_crt_set_serial(crt, serial, 5);
387     if (err < 0) {
388         g_critical("Failed to set certificate serial %s",
389                    gnutls_strerror(err));
390         abort();
391     }
392
393     err = gnutls_x509_crt_set_activation_time(crt, start);
394     if (err < 0) {
395         g_critical("Failed to set certificate activation %s",
396                    gnutls_strerror(err));
397         abort();
398     }
399     err = gnutls_x509_crt_set_expiration_time(crt, expire);
400     if (err < 0) {
401         g_critical("Failed to set certificate expiration %s",
402                    gnutls_strerror(err));
403         abort();
404     }
405
406
407     /*
408      * If no 'ca' is set then we are self signing
409      * the cert. This is done for the root CA certs
410      */
411     err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey);
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     }
469     g_free(buffer);
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 */
This page took 0.051861 seconds and 4 git commands to generate.