1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "private-lib-tls-mbedtls.h"
27 #include <mbedtls/oid.h>
28
29 #if defined(LWS_PLAT_OPTEE) || defined(OPTEE_DEV_KIT)
30 struct tm {
31 int tm_sec; // seconds [0,61]
32 int tm_min; // minutes [0,59]
33 int tm_hour; // hour [0,23]
34 int tm_mday; // day of month [1,31]
35 int tm_mon; // month of year [0,11]
36 int tm_year; // years since 1900
37 int tm_wday; // day of week [0,6] (Sunday = 0)
38 int tm_yday; // day of year [0,365]
39 int tm_isdst; // daylight savings flag
40 };
mktime(struct tm * t)41 time_t mktime(struct tm *t)
42 {
43 return (time_t)0;
44 }
45 #endif
46
47 static time_t
lws_tls_mbedtls_time_to_unix(mbedtls_x509_time * xtime)48 lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
49 {
50 struct tm t;
51
52 if (!xtime || !xtime->MBEDTLS_PRIVATE(year) || xtime->MBEDTLS_PRIVATE(year) < 0)
53 return (time_t)(long long)-1;
54
55 memset(&t, 0, sizeof(t));
56
57 t.tm_year = xtime->MBEDTLS_PRIVATE(year) - 1900;
58 t.tm_mon = xtime->MBEDTLS_PRIVATE(mon) - 1; /* mbedtls months are 1+, tm are 0+ */
59 t.tm_mday = xtime->MBEDTLS_PRIVATE(day) - 1; /* mbedtls days are 1+, tm are 0+ */
60 t.tm_hour = xtime->MBEDTLS_PRIVATE(hour);
61 t.tm_min = xtime->MBEDTLS_PRIVATE(min);
62 t.tm_sec = xtime->MBEDTLS_PRIVATE(sec);
63 t.tm_isdst = -1;
64
65 return mktime(&t);
66 }
67
68 static int
lws_tls_mbedtls_get_x509_name(mbedtls_x509_name * name,union lws_tls_cert_info_results * buf,size_t len)69 lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
70 union lws_tls_cert_info_results *buf, size_t len)
71 {
72 int r = -1;
73
74 buf->ns.len = 0;
75
76 while (name) {
77 /*
78 if (MBEDTLS_OID_CMP(type, &name->oid)) {
79 name = name->next;
80 continue;
81 }
82 */
83 lws_strnncpy(&buf->ns.name[buf->ns.len],
84 (const char *)name->MBEDTLS_PRIVATE(val).MBEDTLS_PRIVATE(p),
85 name->MBEDTLS_PRIVATE(val).MBEDTLS_PRIVATE(len),
86 len - (size_t)buf->ns.len);
87 buf->ns.len = (int)strlen(buf->ns.name);
88
89 r = 0;
90 name = name->MBEDTLS_PRIVATE(next);
91 }
92
93 return r;
94 }
95
96
97 int
lws_tls_mbedtls_cert_info(mbedtls_x509_crt * x509,enum lws_tls_cert_info type,union lws_tls_cert_info_results * buf,size_t len)98 lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
99 union lws_tls_cert_info_results *buf, size_t len)
100 {
101 mbedtls_x509_buf skid;
102 lws_mbedtls_x509_authority akid;
103
104 if (!x509)
105 return -1;
106
107 if (!len)
108 len = sizeof(buf->ns.name);
109
110 switch (type) {
111 case LWS_TLS_CERT_INFO_VALIDITY_FROM:
112 buf->time = lws_tls_mbedtls_time_to_unix(&x509->MBEDTLS_PRIVATE(valid_from));
113 if (buf->time == (time_t)(long long)-1)
114 return -1;
115 break;
116
117 case LWS_TLS_CERT_INFO_VALIDITY_TO:
118 buf->time = lws_tls_mbedtls_time_to_unix(&x509->MBEDTLS_PRIVATE(valid_to));
119 if (buf->time == (time_t)(long long)-1)
120 return -1;
121 break;
122
123 case LWS_TLS_CERT_INFO_COMMON_NAME:
124 return lws_tls_mbedtls_get_x509_name(&x509->MBEDTLS_PRIVATE(subject), buf, len);
125
126 case LWS_TLS_CERT_INFO_ISSUER_NAME:
127 return lws_tls_mbedtls_get_x509_name(&x509->MBEDTLS_PRIVATE(issuer), buf, len);
128
129 case LWS_TLS_CERT_INFO_USAGE:
130 buf->usage = x509->MBEDTLS_PRIVATE(key_usage);
131 break;
132
133 case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
134 {
135 char *p = buf->ns.name;
136 size_t r = len, u;
137
138 switch (mbedtls_pk_get_type(&x509->MBEDTLS_PRIVATE(pk))) {
139 case MBEDTLS_PK_RSA:
140 {
141 mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->MBEDTLS_PRIVATE(pk));
142
143 if (mbedtls_mpi_write_string(&rsa->MBEDTLS_PRIVATE(N), 16, p, r, &u))
144 return -1;
145 r -= u;
146 p += u;
147 if (mbedtls_mpi_write_string(&rsa->MBEDTLS_PRIVATE(E), 16, p, r, &u))
148 return -1;
149
150 p += u;
151 buf->ns.len = lws_ptr_diff(p, buf->ns.name);
152 break;
153 }
154 case MBEDTLS_PK_ECKEY:
155 {
156 mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->MBEDTLS_PRIVATE(pk));
157
158 if (mbedtls_mpi_write_string(&ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16, p, r, &u))
159 return -1;
160 r -= u;
161 p += u;
162 if (mbedtls_mpi_write_string(&ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16, p, r, &u))
163 return -1;
164 r -= u;
165 p += u;
166 if (mbedtls_mpi_write_string(&ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 16, p, r, &u))
167 return -1;
168 p += u;
169 buf->ns.len = lws_ptr_diff(p, buf->ns.name);
170 break;
171 }
172 default:
173 lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
174 __func__,
175 mbedtls_pk_get_type(&x509->MBEDTLS_PRIVATE(pk)));
176
177 return -1;
178 }
179 break;
180 }
181 case LWS_TLS_CERT_INFO_DER_RAW:
182
183 buf->ns.len = (int)x509->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len);
184
185 if (len < x509->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))
186 /*
187 * The buffer is too small and the attempt failed, but
188 * the required object length is in buf->ns.len
189 */
190 return -1;
191
192 memcpy(buf->ns.name, x509->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
193 x509->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len));
194 break;
195
196 case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID:
197
198 memset(&akid, 0, sizeof(akid));
199 memset(&skid, 0, sizeof(skid));
200
201 lws_x509_get_crt_ext(x509, &skid, &akid);
202 if (akid.keyIdentifier.MBEDTLS_PRIVATE(tag) != MBEDTLS_ASN1_OCTET_STRING)
203 return 1;
204 buf->ns.len = (int)akid.keyIdentifier.MBEDTLS_PRIVATE(len);
205 if (!akid.keyIdentifier.MBEDTLS_PRIVATE(p) ||
206 len < (size_t)buf->ns.len)
207 return -1;
208 memcpy(buf->ns.name, akid.keyIdentifier.MBEDTLS_PRIVATE(p), (size_t)buf->ns.len);
209 break;
210
211 case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_ISSUER: {
212 mbedtls_x509_sequence * ip;
213
214 memset(&akid, 0, sizeof(akid));
215 memset(&skid, 0, sizeof(skid));
216
217 lws_x509_get_crt_ext(x509, &skid, &akid);
218
219 ip = &akid.authorityCertIssuer;
220
221 buf->ns.len = 0;
222
223 while (ip) {
224 if (akid.keyIdentifier.MBEDTLS_PRIVATE(tag) != MBEDTLS_ASN1_OCTET_STRING ||
225 !ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(p) ||
226 ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(len) < 9 ||
227 len < (size_t)ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(len) - 9u)
228 break;
229
230 memcpy(buf->ns.name + buf->ns.len, ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(p),
231 (size_t)ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(len) - 9);
232 buf->ns.len = buf->ns.len + (int)ip->MBEDTLS_PRIVATE(buf).MBEDTLS_PRIVATE(len) - 9;
233
234 ip = ip->MBEDTLS_PRIVATE(next);
235 }
236 break;
237 }
238 case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_SERIAL:
239
240 memset(&akid, 0, sizeof(akid));
241 memset(&skid, 0, sizeof(skid));
242
243 lws_x509_get_crt_ext(x509, &skid, &akid);
244
245 if (akid.authorityCertSerialNumber.MBEDTLS_PRIVATE(tag) != MBEDTLS_ASN1_OCTET_STRING)
246 return 1;
247 buf->ns.len = (int)akid.authorityCertSerialNumber.MBEDTLS_PRIVATE(len);
248 if (!akid.authorityCertSerialNumber.MBEDTLS_PRIVATE(p) ||
249 len < (size_t)buf->ns.len)
250 return -1;
251 memcpy(buf->ns.name, akid.authorityCertSerialNumber.
252 MBEDTLS_PRIVATE(p), (size_t)buf->ns.len);
253 break;
254
255 case LWS_TLS_CERT_INFO_SUBJECT_KEY_ID:
256
257 memset(&akid, 0, sizeof(akid));
258 memset(&skid, 0, sizeof(skid));
259
260 lws_x509_get_crt_ext(x509, &skid, &akid);
261
262 if (skid.MBEDTLS_PRIVATE(tag) != MBEDTLS_ASN1_OCTET_STRING)
263 return 1;
264 buf->ns.len = (int)skid.MBEDTLS_PRIVATE(len);
265 if (len < (size_t)buf->ns.len)
266 return -1;
267 memcpy(buf->ns.name, skid.MBEDTLS_PRIVATE(p), (size_t)buf->ns.len);
268 break;
269 default:
270 return -1;
271 }
272
273 return 0;
274 }
275
276 #if defined(LWS_WITH_NETWORK)
277 int
lws_tls_vhost_cert_info(struct lws_vhost * vhost,enum lws_tls_cert_info type,union lws_tls_cert_info_results * buf,size_t len)278 lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
279 union lws_tls_cert_info_results *buf, size_t len)
280 {
281 mbedtls_x509_crt *x509;
282
283 x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
284
285 return lws_tls_mbedtls_cert_info(x509, type, buf, len);
286 }
287
288 int
lws_tls_peer_cert_info(struct lws * wsi,enum lws_tls_cert_info type,union lws_tls_cert_info_results * buf,size_t len)289 lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
290 union lws_tls_cert_info_results *buf, size_t len)
291 {
292 mbedtls_x509_crt *x509;
293
294 wsi = lws_get_network_wsi(wsi);
295
296 x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
297
298 if (!x509)
299 return -1;
300
301 switch (type) {
302 case LWS_TLS_CERT_INFO_VERIFIED:
303 buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
304 return 0;
305 default:
306 return lws_tls_mbedtls_cert_info(x509, type, buf, len);
307 }
308
309 return -1;
310 }
311 #endif
312
313 int
lws_x509_info(struct lws_x509_cert * x509,enum lws_tls_cert_info type,union lws_tls_cert_info_results * buf,size_t len)314 lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
315 union lws_tls_cert_info_results *buf, size_t len)
316 {
317 return lws_tls_mbedtls_cert_info(&x509->cert, type, buf, len);
318 }
319
320 int
lws_x509_create(struct lws_x509_cert ** x509)321 lws_x509_create(struct lws_x509_cert **x509)
322 {
323 *x509 = lws_malloc(sizeof(**x509), __func__);
324
325 return !(*x509);
326 }
327
328 /*
329 * Parse one DER-encoded or one or more concatenated PEM-encoded certificates
330 * and add them to the chained list.
331 */
332
333 int
lws_x509_parse_from_pem(struct lws_x509_cert * x509,const void * pem,size_t len)334 lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len)
335 {
336 int ret;
337
338 mbedtls_x509_crt_init(&x509->cert);
339
340 ret = mbedtls_x509_crt_parse(&x509->cert, pem, len);
341 if (ret) {
342 if (ret > 0)
343 mbedtls_x509_crt_free(&x509->cert);
344 lwsl_err("%s: unable to parse PEM cert: -0x%x\n",
345 __func__, -ret);
346
347 return -1;
348 }
349
350 return 0;
351 }
352
353 int
lws_x509_verify(struct lws_x509_cert * x509,struct lws_x509_cert * trusted,const char * common_name)354 lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
355 const char *common_name)
356 {
357 uint32_t flags = 0;
358 int ret;
359
360 ret = mbedtls_x509_crt_verify_with_profile(&x509->cert, &trusted->cert,
361 NULL,
362 &mbedtls_x509_crt_profile_next,
363 common_name, &flags, NULL,
364 NULL);
365
366 if (ret) {
367 lwsl_err("%s: unable to parse PEM cert: -0x%x\n",
368 __func__, -ret);
369
370 return -1;
371 }
372
373 return 0;
374 }
375
376 #if defined(LWS_WITH_JOSE)
377
378 int
lws_x509_public_to_jwk(struct lws_jwk * jwk,struct lws_x509_cert * x509,const char * curves,int rsa_min_bits)379 lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
380 const char *curves, int rsa_min_bits)
381 {
382 int kt = (int)mbedtls_pk_get_type(&x509->cert.MBEDTLS_PRIVATE(pk)),
383 n, count = 0, ret = -1;
384 mbedtls_rsa_context *rsactx;
385 mbedtls_ecp_keypair *ecpctx;
386 mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
387
388 memset(jwk, 0, sizeof(*jwk));
389
390 switch (kt) {
391 case MBEDTLS_PK_RSA:
392 lwsl_notice("%s: RSA key\n", __func__);
393 jwk->kty = LWS_GENCRYPTO_KTY_RSA;
394 rsactx = mbedtls_pk_rsa(x509->cert.MBEDTLS_PRIVATE(pk));
395
396 mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = &rsactx->MBEDTLS_PRIVATE(E);
397 mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = &rsactx->MBEDTLS_PRIVATE(N);
398 mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->MBEDTLS_PRIVATE(D);
399 mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->MBEDTLS_PRIVATE(P);
400 mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->MBEDTLS_PRIVATE(Q);
401 mpi[LWS_GENCRYPTO_RSA_KEYEL_DP] = &rsactx->MBEDTLS_PRIVATE(DP);
402 mpi[LWS_GENCRYPTO_RSA_KEYEL_DQ] = &rsactx->MBEDTLS_PRIVATE(DQ);
403 mpi[LWS_GENCRYPTO_RSA_KEYEL_QI] = &rsactx->MBEDTLS_PRIVATE(QP);
404
405 count = LWS_GENCRYPTO_RSA_KEYEL_QI + 1;
406 n = LWS_GENCRYPTO_RSA_KEYEL_E;
407 break;
408
409 case MBEDTLS_PK_ECKEY:
410 lwsl_notice("%s: EC key\n", __func__);
411 jwk->kty = LWS_GENCRYPTO_KTY_EC;
412 ecpctx = mbedtls_pk_ec(x509->cert.MBEDTLS_PRIVATE(pk));
413 mpi[LWS_GENCRYPTO_EC_KEYEL_X] = &ecpctx->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X);
414 mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->MBEDTLS_PRIVATE(d);
415 mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = &ecpctx->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y);
416
417 if (lws_genec_confirm_curve_allowed_by_tls_id(curves,
418 (int)ecpctx->MBEDTLS_PRIVATE(grp).id, jwk))
419 /* already logged */
420 goto bail;
421
422 count = LWS_GENCRYPTO_EC_KEYEL_COUNT;
423 n = LWS_GENCRYPTO_EC_KEYEL_X;
424 break;
425 default:
426 lwsl_err("%s: key type %d not supported\n", __func__, kt);
427
428 return -1;
429 }
430
431 for (; n < count; n++) {
432 if (!mbedtls_mpi_size(mpi[n]))
433 continue;
434
435 jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk");
436 if (!jwk->e[n].buf)
437 goto bail;
438 jwk->e[n].len = (uint32_t)mbedtls_mpi_size(mpi[n]);
439 mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len);
440 }
441
442 ret = 0;
443
444 bail:
445 /* jwk destroy will clean up partials */
446 if (ret)
447 lws_jwk_destroy(jwk);
448
449 return ret;
450 }
451
452 int
lws_x509_jwk_privkey_pem(struct lws_context * cx,struct lws_jwk * jwk,void * pem,size_t len,const char * passphrase)453 lws_x509_jwk_privkey_pem(struct lws_context *cx, struct lws_jwk *jwk,
454 void *pem, size_t len, const char *passphrase)
455 {
456 mbedtls_rsa_context *rsactx;
457 mbedtls_ecp_keypair *ecpctx;
458 mbedtls_pk_context pk;
459 mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
460 int n, ret = -1, count = 0;
461
462 mbedtls_pk_init(&pk);
463
464 n = 0;
465 if (passphrase)
466 n = (int)strlen(passphrase);
467 n = mbedtls_pk_parse_key(&pk, pem, len, (uint8_t *)passphrase, (unsigned int)n
468 #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
469 , mbedtls_ctr_drbg_random, &cx->mcdc
470 #endif
471 );
472 if (n) {
473 lwsl_err("%s: parse PEM key failed: -0x%x\n", __func__, -n);
474
475 return -1;
476 }
477
478 /* the incoming private key type */
479 switch (mbedtls_pk_get_type(&pk)) {
480 case MBEDTLS_PK_RSA:
481 if (jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
482 lwsl_err("%s: RSA privkey, non-RSA jwk\n", __func__);
483 goto bail;
484 }
485 rsactx = mbedtls_pk_rsa(pk);
486 mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->MBEDTLS_PRIVATE(D);
487 mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->MBEDTLS_PRIVATE(P);
488 mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->MBEDTLS_PRIVATE(Q);
489 n = LWS_GENCRYPTO_RSA_KEYEL_D;
490 count = LWS_GENCRYPTO_RSA_KEYEL_Q + 1;
491 break;
492 case MBEDTLS_PK_ECKEY:
493 if (jwk->kty != LWS_GENCRYPTO_KTY_EC) {
494 lwsl_err("%s: EC privkey, non-EC jwk\n", __func__);
495 goto bail;
496 }
497 ecpctx = mbedtls_pk_ec(pk);
498 mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->MBEDTLS_PRIVATE(d);
499 n = LWS_GENCRYPTO_EC_KEYEL_D;
500 count = n + 1;
501 break;
502 default:
503 lwsl_err("%s: unusable key type %d\n", __func__,
504 mbedtls_pk_get_type(&pk));
505 goto bail;
506 }
507
508 for (; n < count; n++) {
509 if (!mbedtls_mpi_size(mpi[n])) {
510 lwsl_err("%s: empty privkey\n", __func__);
511 goto bail;
512 }
513
514 jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk");
515 if (!jwk->e[n].buf)
516 goto bail;
517 jwk->e[n].len = (uint32_t)mbedtls_mpi_size(mpi[n]);
518 mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len);
519 }
520
521 ret = 0;
522
523 bail:
524 mbedtls_pk_free(&pk);
525
526 return ret;
527 }
528 #endif
529
530 void
lws_x509_destroy(struct lws_x509_cert ** x509)531 lws_x509_destroy(struct lws_x509_cert **x509)
532 {
533 if (!*x509)
534 return;
535
536 mbedtls_x509_crt_free(&(*x509)->cert);
537
538 lws_free_set_NULL(*x509);
539 }
540