]>
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 <berrange@redhat.com> | |
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 | ||
9a2fd434 DB |
27 | /* |
28 | * This stores some static data that is needed when | |
29 | * encoding extensions in the x509 certs | |
30 | */ | |
ecb98f5c | 31 | asn1_node pkix_asn1; |
9a2fd434 DB |
32 | |
33 | /* | |
34 | * To avoid consuming random entropy to generate keys, | |
35 | * here's one we prepared earlier :-) | |
36 | */ | |
37 | gnutls_x509_privkey_t privkey; | |
3e018afb DB |
38 | # define PRIVATE_KEY \ |
39 | "-----BEGIN RSA PRIVATE KEY-----\n" \ | |
40 | "MIIG5AIBAAKCAYEAyjWyLSNm5PZvYUKUcDWGqbLX10b2ood+YaFjWSnJrqx/q3qh\n" \ | |
41 | "rVGBJglD25AJENJsmZF3zPP1oMhfIxsXu63Hdkb6Rdlc2RUoUP34x9VC1izH25mR\n" \ | |
42 | "6c8DPDp1d6IraZ/llDMI1HsBFz0qGWtvOHgm815XG4PAr/N8rDsuqfv/cJ01KlnO\n" \ | |
43 | "0OdO5QRXCJf9g/dYd41MPu7wOXk9FqjQlmRoP59HgtJ+zUpE4z+Keruw9cMT9VJj\n" \ | |
44 | "0oT+pQ9ysenqeZ3gbT224T1khrEhT5kifhtFLNyDssRchUUWH0hiqoOO1vgb+850\n" \ | |
45 | "W6/1VdxvuPam48py4diSPi1Vip8NITCOBaX9FIpVp4Ruw4rTPVMNMjq9Cpx/DwMP\n" \ | |
46 | "9MbfXfnaVaZaMrmq67/zPhl0eVbUrecH2hQ3ZB9oIF4GkNskzlWF5+yPy6zqk304\n" \ | |
47 | "AKaiFR6jRyh3YfHo2XFqV8x/hxdsIEXOtEUGhSIcpynsW+ckUCartzu7xbhXjd4b\n" \ | |
48 | "kxJT89+riPFYij09AgMBAAECggGBAKyFkaZXXROeejrmHlV6JZGlp+fhgM38gkRz\n" \ | |
49 | "+Jp7P7rLLAY3E7gXIPQ91WqAAmwazFNdvHPd9USfkCQYmnAi/VoZhrCPmlsQZRxt\n" \ | |
50 | "A5QjjOnEvSPMa6SrXZxGWDCg6R8uMCb4P+FhrPWR1thnRDZOtRTQ+crc50p3mHgt\n" \ | |
51 | "6ktXWIJRbqnag8zSfQqCYGtRmhe8sfsWT+Yl4El4+jjaAVU/B364u7+PLmaiphGp\n" \ | |
52 | "BdJfTsTwEpgtGkPj+osDmhzXcZkfq3V+fz5JLkemsCiQKmn4VJRpg8c3ZmE8NPNt\n" \ | |
53 | "gRtGWZ4W3WKDvhotT65WpQx4+6R8Duux/blNPBmH1Upmwd7kj7GYFBArbCjgd9PT\n" \ | |
54 | "xgfCSUZpgOZHHkcgSB+022a8XncXna7WYYij28SLtwImFyu0nNtqECFQHH5u+k6C\n" \ | |
55 | "LRYBSN+3t3At8dQuk01NVrJBndmjmXRfxpqUtTdeaNgVpdUYRY98s30G68NYGSra\n" \ | |
56 | "aEvhhRSghkcLNetkobpY9pUgeqW/tQKBwQDZHHK9nDMt/zk1TxtILeUSitPXcv1/\n" \ | |
57 | "8ufXqO0miHdH23XuXhIEA6Ef26RRVGDGgpjkveDJK/1w5feJ4H/ni4Vclil/cm38\n" \ | |
58 | "OwRqjjd7ElHJX6JQbsxEx/gNTk5/QW1iAL9TXUalgepsSXYT6AJ0/CJv0jmJSJ36\n" \ | |
59 | "YoKMOM8uqzb2KhN6i+RlJRi5iY53kUhWTJq5ArWvNhUzQNSYODI4bNxlsKSBL2Ik\n" \ | |
60 | "LZ5QKHuaEjQet0IlPlfIb4PzMm8CHa/urOcCgcEA7m3zW/lL5bIFoKPjWig5Lbn1\n" \ | |
61 | "aHfrG2ngqzWtgWtfZqMH8OkZc1Mdhhmvd46titjiLjeI+UP/uHXR0068PnrNngzl\n" \ | |
62 | "tTgwlakzu+bWzqhBm1F+3/341st/FEk07r0P/3/PhezVjwfO8c8Exj7pLxH4wrH0\n" \ | |
63 | "ROHgDbClmlJRu6OO78wk1+Vapf5DWa8YfA+q+fdvr7KvgGyytheKMT/b/dsqOq7y\n" \ | |
64 | "qZPjmaJKWAvV3RWG8lWHFSdHx2IAHMHfGr17Y/w7AoHBALzwZeYebeekiVucGSjq\n" \ | |
65 | "T8SgLhT7zCIx+JMUPjVfYzaUhP/Iu7Lkma6IzWm9nW6Drpy5pUpMzwUWDCLfzU9q\n" \ | |
66 | "eseFIl337kEn9wLn+t5OpgAyCqYmlftxbqvdrrBN9uvnrJjWvqk/8wsDrw9JxAGc\n" \ | |
67 | "fjeD4nBXUqvYWLXApoR9mZoGKedmoH9pFig4zlO9ig8YITnKYuQ0k6SD0b8agJHc\n" \ | |
68 | "Ir0YSUDnRGgpjvFBGbeOCe+FGbohk/EpItJc3IAh5740lwKBwAdXd2DjokSmYKn7\n" \ | |
69 | "oeqKxofz6+yVlLW5YuOiuX78sWlVp87xPolgi84vSEnkKM/Xsc8+goc6YstpRVa+\n" \ | |
70 | "W+mImoA9YW1dF5HkLeWhTAf9AlgoAEIhbeIfTgBv6KNZSv7RDrDPBBxtXx/vAfSg\n" \ | |
71 | "x0ldwk0scZsVYXLKd67yzfV7KdGUdaX4N/xYgfZm/9gCG3+q8NN2KxVHQ5F71BOE\n" \ | |
72 | "JeABOaGo9WvnU+DNMIDZjHJMUWVw4MHz/a/UArDf/2CxaPVBNQKBwASg6j4ohSTk\n" \ | |
73 | "J7aE6RQ3OBmmDDpixcoCJt9u9SjHVYMlbs5CEJGVSczk0SG3y8P1lOWNDSRnMksZ\n" \ | |
74 | "xWnHdP/ogcuYMuvK7UACNAF0zNddtzOhzcpNmejFj+WCHYY/UmPr2/Kf6t7Cxk2K\n" \ | |
75 | "3cZ4tqWsiTmBT8Bknmah7L5DrhS+ZBJliDeFAA8fZHdMH0Xjr4UBp9kF90EMTdW1\n" \ | |
76 | "Xr5uz7ZrMsYpYQI7mmyqV9SSjUg4iBXwVSoag1iDJ1K8Qg/L7Semgg==\n" \ | |
77 | "-----END RSA PRIVATE KEY-----\n" | |
9a2fd434 DB |
78 | |
79 | /* | |
80 | * This loads the private key we defined earlier | |
81 | */ | |
82 | static gnutls_x509_privkey_t test_tls_load_key(void) | |
83 | { | |
84 | gnutls_x509_privkey_t key; | |
85 | const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, | |
86 | strlen(PRIVATE_KEY) }; | |
87 | int err; | |
88 | ||
89 | err = gnutls_x509_privkey_init(&key); | |
90 | if (err < 0) { | |
91 | g_critical("Failed to init key %s", gnutls_strerror(err)); | |
92 | abort(); | |
93 | } | |
94 | ||
95 | err = gnutls_x509_privkey_import(key, &data, | |
96 | GNUTLS_X509_FMT_PEM); | |
97 | if (err < 0) { | |
98 | if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && | |
99 | err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { | |
100 | g_critical("Failed to import key %s", gnutls_strerror(err)); | |
101 | abort(); | |
102 | } | |
103 | ||
104 | err = gnutls_x509_privkey_import_pkcs8( | |
105 | key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); | |
106 | if (err < 0) { | |
107 | g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); | |
108 | abort(); | |
109 | } | |
110 | } | |
111 | ||
112 | return key; | |
113 | } | |
114 | ||
115 | ||
116 | void test_tls_init(const char *keyfile) | |
117 | { | |
dbddad70 | 118 | qcrypto_init(&error_abort); |
9a2fd434 DB |
119 | |
120 | if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { | |
121 | abort(); | |
122 | } | |
123 | ||
124 | privkey = test_tls_load_key(); | |
125 | if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { | |
126 | abort(); | |
127 | } | |
128 | } | |
129 | ||
130 | ||
131 | void test_tls_cleanup(const char *keyfile) | |
132 | { | |
133 | asn1_delete_structure(&pkix_asn1); | |
134 | unlink(keyfile); | |
135 | } | |
136 | ||
137 | /* | |
138 | * Turns an ASN1 object into a DER encoded byte array | |
139 | */ | |
ecb98f5c | 140 | static void test_tls_der_encode(asn1_node src, |
9a2fd434 DB |
141 | const char *src_name, |
142 | gnutls_datum_t *res) | |
143 | { | |
144 | int size; | |
145 | char *data = NULL; | |
146 | ||
147 | size = 0; | |
148 | asn1_der_coding(src, src_name, NULL, &size, NULL); | |
149 | ||
150 | data = g_new0(char, size); | |
151 | ||
152 | asn1_der_coding(src, src_name, data, &size, NULL); | |
153 | ||
154 | res->data = (unsigned char *)data; | |
155 | res->size = size; | |
156 | } | |
157 | ||
158 | ||
159 | static void | |
160 | test_tls_get_ipaddr(const char *addrstr, | |
161 | char **data, | |
162 | int *datalen) | |
163 | { | |
164 | struct addrinfo *res; | |
165 | struct addrinfo hints; | |
166 | ||
167 | memset(&hints, 0, sizeof(hints)); | |
168 | hints.ai_flags = AI_NUMERICHOST; | |
169 | g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); | |
170 | ||
171 | *datalen = res->ai_addrlen; | |
172 | *data = g_new(char, *datalen); | |
173 | memcpy(*data, res->ai_addr, *datalen); | |
7b35030e | 174 | freeaddrinfo(res); |
9a2fd434 DB |
175 | } |
176 | ||
177 | /* | |
178 | * This is a fairly lame x509 certificate generator. | |
179 | * | |
180 | * Do not copy/use this code for generating real certificates | |
181 | * since it leaves out many things that you would want in | |
182 | * certificates for real world usage. | |
183 | * | |
184 | * This is good enough only for doing tests of the QEMU | |
185 | * TLS certificate code | |
186 | */ | |
187 | void | |
188 | test_tls_generate_cert(QCryptoTLSTestCertReq *req, | |
189 | gnutls_x509_crt_t ca) | |
190 | { | |
191 | gnutls_x509_crt_t crt; | |
192 | int err; | |
193 | static char buffer[1024 * 1024]; | |
194 | size_t size = sizeof(buffer); | |
195 | char serial[5] = { 1, 2, 3, 4, 0 }; | |
196 | gnutls_datum_t der; | |
197 | time_t start = time(NULL) + (60 * 60 * req->start_offset); | |
198 | time_t expire = time(NULL) + (60 * 60 * (req->expire_offset | |
199 | ? req->expire_offset : 24)); | |
200 | ||
201 | /* | |
202 | * Prepare our new certificate object | |
203 | */ | |
204 | err = gnutls_x509_crt_init(&crt); | |
205 | if (err < 0) { | |
206 | g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); | |
207 | abort(); | |
208 | } | |
209 | err = gnutls_x509_crt_set_key(crt, privkey); | |
210 | if (err < 0) { | |
211 | g_critical("Failed to set certificate key %s", gnutls_strerror(err)); | |
212 | abort(); | |
213 | } | |
214 | ||
215 | /* | |
216 | * A v3 certificate is required in order to be able | |
217 | * set any of the basic constraints, key purpose and | |
218 | * key usage data | |
219 | */ | |
220 | gnutls_x509_crt_set_version(crt, 3); | |
221 | ||
222 | if (req->country) { | |
223 | err = gnutls_x509_crt_set_dn_by_oid( | |
224 | crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, | |
225 | req->country, strlen(req->country)); | |
226 | if (err < 0) { | |
227 | g_critical("Failed to set certificate country name %s", | |
228 | gnutls_strerror(err)); | |
229 | abort(); | |
230 | } | |
231 | } | |
232 | if (req->cn) { | |
233 | err = gnutls_x509_crt_set_dn_by_oid( | |
234 | crt, GNUTLS_OID_X520_COMMON_NAME, 0, | |
235 | req->cn, strlen(req->cn)); | |
236 | if (err < 0) { | |
237 | g_critical("Failed to set certificate common name %s", | |
238 | gnutls_strerror(err)); | |
239 | abort(); | |
240 | } | |
241 | } | |
242 | ||
243 | /* | |
244 | * Setup the subject altnames, which are used | |
245 | * for hostname checks in live sessions | |
246 | */ | |
247 | if (req->altname1) { | |
248 | err = gnutls_x509_crt_set_subject_alt_name( | |
249 | crt, GNUTLS_SAN_DNSNAME, | |
250 | req->altname1, | |
251 | strlen(req->altname1), | |
252 | GNUTLS_FSAN_APPEND); | |
253 | if (err < 0) { | |
254 | g_critical("Failed to set certificate alt name %s", | |
255 | gnutls_strerror(err)); | |
256 | abort(); | |
257 | } | |
258 | } | |
259 | if (req->altname2) { | |
260 | err = gnutls_x509_crt_set_subject_alt_name( | |
261 | crt, GNUTLS_SAN_DNSNAME, | |
262 | req->altname2, | |
263 | strlen(req->altname2), | |
264 | GNUTLS_FSAN_APPEND); | |
265 | if (err < 0) { | |
266 | g_critical("Failed to set certificate %s alt name", | |
267 | gnutls_strerror(err)); | |
268 | abort(); | |
269 | } | |
270 | } | |
271 | ||
272 | /* | |
273 | * IP address need to be put into the cert in their | |
274 | * raw byte form, not strings, hence this is a little | |
275 | * more complicated | |
276 | */ | |
277 | if (req->ipaddr1) { | |
278 | char *data; | |
279 | int len; | |
280 | ||
281 | test_tls_get_ipaddr(req->ipaddr1, &data, &len); | |
282 | ||
283 | err = gnutls_x509_crt_set_subject_alt_name( | |
284 | crt, GNUTLS_SAN_IPADDRESS, | |
285 | data, len, GNUTLS_FSAN_APPEND); | |
286 | if (err < 0) { | |
287 | g_critical("Failed to set certificate alt name %s", | |
288 | gnutls_strerror(err)); | |
289 | abort(); | |
290 | } | |
291 | g_free(data); | |
292 | } | |
293 | if (req->ipaddr2) { | |
294 | char *data; | |
295 | int len; | |
296 | ||
297 | test_tls_get_ipaddr(req->ipaddr2, &data, &len); | |
298 | ||
299 | err = gnutls_x509_crt_set_subject_alt_name( | |
300 | crt, GNUTLS_SAN_IPADDRESS, | |
301 | data, len, GNUTLS_FSAN_APPEND); | |
302 | if (err < 0) { | |
303 | g_critical("Failed to set certificate alt name %s", | |
304 | gnutls_strerror(err)); | |
305 | abort(); | |
306 | } | |
307 | g_free(data); | |
308 | } | |
309 | ||
310 | ||
311 | /* | |
312 | * Basic constraints are used to decide if the cert | |
313 | * is for a CA or not. We can't use the convenient | |
314 | * gnutls API for setting this, since it hardcodes | |
315 | * the 'critical' field which we want control over | |
316 | */ | |
317 | if (req->basicConstraintsEnable) { | |
ecb98f5c | 318 | asn1_node ext = NULL; |
9a2fd434 DB |
319 | |
320 | asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); | |
321 | asn1_write_value(ext, "cA", | |
322 | req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); | |
323 | asn1_write_value(ext, "pathLenConstraint", NULL, 0); | |
324 | test_tls_der_encode(ext, "", &der); | |
325 | err = gnutls_x509_crt_set_extension_by_oid( | |
326 | crt, "2.5.29.19", | |
327 | der.data, der.size, | |
328 | req->basicConstraintsCritical); | |
329 | if (err < 0) { | |
330 | g_critical("Failed to set certificate basic constraints %s", | |
331 | gnutls_strerror(err)); | |
332 | g_free(der.data); | |
333 | abort(); | |
334 | } | |
335 | asn1_delete_structure(&ext); | |
336 | g_free(der.data); | |
337 | } | |
338 | ||
339 | /* | |
340 | * Next up the key usage extension. Again we can't | |
341 | * use the gnutls API since it hardcodes the extension | |
342 | * to be 'critical' | |
343 | */ | |
344 | if (req->keyUsageEnable) { | |
ecb98f5c | 345 | asn1_node ext = NULL; |
9a2fd434 DB |
346 | char str[2]; |
347 | ||
348 | str[0] = req->keyUsageValue & 0xff; | |
349 | str[1] = (req->keyUsageValue >> 8) & 0xff; | |
350 | ||
351 | asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); | |
352 | asn1_write_value(ext, "", str, 9); | |
353 | test_tls_der_encode(ext, "", &der); | |
354 | err = gnutls_x509_crt_set_extension_by_oid( | |
355 | crt, "2.5.29.15", | |
356 | der.data, der.size, | |
357 | req->keyUsageCritical); | |
358 | if (err < 0) { | |
359 | g_critical("Failed to set certificate key usage %s", | |
360 | gnutls_strerror(err)); | |
361 | g_free(der.data); | |
362 | abort(); | |
363 | } | |
364 | asn1_delete_structure(&ext); | |
365 | g_free(der.data); | |
366 | } | |
367 | ||
368 | /* | |
369 | * Finally the key purpose extension. This time | |
370 | * gnutls has the opposite problem, always hardcoding | |
371 | * it to be non-critical. So once again we have to | |
372 | * set this the hard way building up ASN1 data ourselves | |
373 | */ | |
374 | if (req->keyPurposeEnable) { | |
ecb98f5c | 375 | asn1_node ext = NULL; |
9a2fd434 DB |
376 | |
377 | asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); | |
378 | if (req->keyPurposeOID1) { | |
379 | asn1_write_value(ext, "", "NEW", 1); | |
380 | asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); | |
381 | } | |
382 | if (req->keyPurposeOID2) { | |
383 | asn1_write_value(ext, "", "NEW", 1); | |
384 | asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); | |
385 | } | |
386 | test_tls_der_encode(ext, "", &der); | |
387 | err = gnutls_x509_crt_set_extension_by_oid( | |
388 | crt, "2.5.29.37", | |
389 | der.data, der.size, | |
390 | req->keyPurposeCritical); | |
391 | if (err < 0) { | |
392 | g_critical("Failed to set certificate key purpose %s", | |
393 | gnutls_strerror(err)); | |
394 | g_free(der.data); | |
395 | abort(); | |
396 | } | |
397 | asn1_delete_structure(&ext); | |
398 | g_free(der.data); | |
399 | } | |
400 | ||
401 | /* | |
402 | * Any old serial number will do, so lets pick 5 | |
403 | */ | |
404 | err = gnutls_x509_crt_set_serial(crt, serial, 5); | |
405 | if (err < 0) { | |
406 | g_critical("Failed to set certificate serial %s", | |
407 | gnutls_strerror(err)); | |
408 | abort(); | |
409 | } | |
410 | ||
411 | err = gnutls_x509_crt_set_activation_time(crt, start); | |
412 | if (err < 0) { | |
413 | g_critical("Failed to set certificate activation %s", | |
414 | gnutls_strerror(err)); | |
415 | abort(); | |
416 | } | |
417 | err = gnutls_x509_crt_set_expiration_time(crt, expire); | |
418 | if (err < 0) { | |
419 | g_critical("Failed to set certificate expiration %s", | |
420 | gnutls_strerror(err)); | |
421 | abort(); | |
422 | } | |
423 | ||
424 | ||
425 | /* | |
426 | * If no 'ca' is set then we are self signing | |
427 | * the cert. This is done for the root CA certs | |
428 | */ | |
23c1595b DB |
429 | err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, |
430 | GNUTLS_DIG_SHA256, 0); | |
9a2fd434 DB |
431 | if (err < 0) { |
432 | g_critical("Failed to sign certificate %s", | |
433 | gnutls_strerror(err)); | |
434 | abort(); | |
435 | } | |
436 | ||
437 | /* | |
438 | * Finally write the new cert out to disk | |
439 | */ | |
440 | err = gnutls_x509_crt_export( | |
441 | crt, GNUTLS_X509_FMT_PEM, buffer, &size); | |
442 | if (err < 0) { | |
443 | g_critical("Failed to export certificate %s: %d", | |
444 | gnutls_strerror(err), err); | |
445 | abort(); | |
446 | } | |
447 | ||
448 | if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { | |
449 | g_critical("Failed to write certificate %s", | |
450 | req->filename); | |
451 | abort(); | |
452 | } | |
453 | ||
454 | req->crt = crt; | |
455 | } | |
456 | ||
457 | ||
458 | void test_tls_write_cert_chain(const char *filename, | |
459 | gnutls_x509_crt_t *certs, | |
460 | size_t ncerts) | |
461 | { | |
462 | size_t i; | |
463 | size_t capacity = 1024, offset = 0; | |
464 | char *buffer = g_new0(char, capacity); | |
465 | int err; | |
466 | ||
467 | for (i = 0; i < ncerts; i++) { | |
468 | size_t len = capacity - offset; | |
469 | retry: | |
470 | err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, | |
471 | buffer + offset, &len); | |
472 | if (err < 0) { | |
473 | if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { | |
474 | buffer = g_renew(char, buffer, offset + len); | |
475 | capacity = offset + len; | |
476 | goto retry; | |
477 | } | |
478 | g_critical("Failed to export certificate chain %s: %d", | |
479 | gnutls_strerror(err), err); | |
480 | abort(); | |
481 | } | |
482 | offset += len; | |
483 | } | |
484 | ||
485 | if (!g_file_set_contents(filename, buffer, offset, NULL)) { | |
486 | abort(); | |
487 | } | |
7b35030e | 488 | g_free(buffer); |
9a2fd434 DB |
489 | } |
490 | ||
491 | ||
492 | void test_tls_discard_cert(QCryptoTLSTestCertReq *req) | |
493 | { | |
494 | if (!req->crt) { | |
495 | return; | |
496 | } | |
497 | ||
498 | gnutls_x509_crt_deinit(req->crt); | |
499 | req->crt = NULL; | |
500 | ||
501 | if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { | |
502 | unlink(req->filename); | |
503 | } | |
504 | } |