OpenVPN
ssl_verify_mbedtls.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2025 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2010-2021 Sentyron B.V. <openvpn@sentyron.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "syshead.h"
34
35#if defined(ENABLE_CRYPTO_MBEDTLS)
36
37#include "crypto_mbedtls.h"
38#include "mbedtls_compat.h"
39#include "ssl_verify.h"
40#include <mbedtls/asn1.h>
41#include <mbedtls/error.h>
42#include <mbedtls/bignum.h>
43#include <mbedtls/oid.h>
44#include <mbedtls/sha1.h>
45
46#define MAX_SUBJECT_LENGTH 256
47
48int
49verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, uint32_t *flags)
50{
51 struct tls_session *session = (struct tls_session *)session_obj;
52 struct gc_arena gc = gc_new();
53
54 ASSERT(cert);
56
57 session->verified = false;
58
59 /* Remember certificate hash */
62
63 if (session->opt->verify_hash_no_ca)
64 {
65 /*
66 * If we decide to verify the peer certificate based on the fingerprint
67 * we ignore wrong dates and the certificate not being trusted.
68 * Any other problem with the certificate (wrong key, bad cert,...)
69 * will still trigger an error.
70 * Clearing these flags relies on verify_cert will later rejecting a
71 * certificate that has no matching fingerprint.
72 */
75 *flags = *flags & ~flags_ignore;
76 }
77
78 /* did peer present cert which was signed by our root cert? */
79 if (*flags != 0)
80 {
81 int ret = 0;
82 char errstr[512] = { 0 };
83 char *subject = x509_get_subject(cert, &gc);
84 char *serial = backend_x509_get_serial(cert, &gc);
85
86 ret = mbedtls_x509_crt_verify_info(errstr, sizeof(errstr) - 1, "", *flags);
87 if (ret <= 0
88 && snprintf(errstr, sizeof(errstr), "Could not retrieve error string, flags=%" PRIx32,
89 *flags)
90 >= sizeof(errstr))
91 {
92 errstr[0] = '\0';
93 }
94 else
95 {
97 }
98
99 if (subject)
100 {
101 msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s, serial=%s: %s", cert_depth,
102 subject, serial ? serial : "<not available>", errstr);
103 }
104 else
105 {
107 "VERIFY ERROR: depth=%d, (could not extract X509 "
108 "subject string from certificate): %s",
110 }
111
112 /* Leave flags set to non-zero to indicate that the cert is not ok */
113 }
114 else if (SUCCESS != verify_cert(session, cert, cert_depth))
115 {
117 }
118
119 gc_free(&gc);
120
121 /*
122 * PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors.
123 */
124 return 0;
125}
126
127#ifdef ENABLE_X509ALTUSERNAME
128#warning "X509 alt user name not yet supported for mbed TLS"
129#endif
130
132backend_x509_get_username(char *cn, size_t cn_len, char *x509_username_field, mbedtls_x509_crt *cert)
133{
134 mbedtls_x509_name *name;
135
136 ASSERT(cn != NULL);
137
138 name = &cert->subject;
139
140 /* Find common name */
141 while (name != NULL)
142 {
144 {
145 break;
146 }
147
148 name = name->next;
149 }
150
151 /* Not found, return an error if this is the peer's certificate */
152 if (name == NULL)
153 {
154 return FAILURE;
155 }
156
157 /* Found, extract CN */
158 if (cn_len > name->val.len)
159 {
160 memcpy(cn, name->val.p, name->val.len);
161 cn[name->val.len] = '\0';
162 }
163 else
164 {
165 memcpy(cn, name->val.p, cn_len);
166 cn[cn_len - 1] = '\0';
167 }
168
169 return SUCCESS;
170}
171
172char *
174{
175 char *buf = NULL;
176 size_t buflen = 0;
177 mbedtls_mpi serial_mpi = { 0 };
178
179 /* Transform asn1 integer serial into mbed TLS MPI */
181 if (!mbed_ok(mbedtls_mpi_read_binary(&serial_mpi, cert->serial.p, cert->serial.len)))
182 {
183 msg(M_WARN, "Failed to retrieve serial from certificate.");
184 goto end;
185 }
186
187 /* Determine decimal representation length, allocate buffer */
189 buf = gc_malloc(buflen, true, gc);
190
191 /* Write MPI serial as decimal string into buffer */
193 {
194 msg(M_WARN, "Failed to write serial to string.");
195 buf = NULL;
196 goto end;
197 }
198
199end:
201 return buf;
202}
203
204char *
206{
207 char *buf = NULL;
208 size_t len = cert->serial.len * 3 + 1;
209
210 buf = gc_malloc(len, true, gc);
211
212 if (mbedtls_x509_serial_gets(buf, len - 1, &cert->serial) < 0)
213 {
214 buf = NULL;
215 }
216
217 return buf;
218}
219
221backend_x509_write_pem(openvpn_x509_cert_t *cert, const char *filename)
222{
223 /* mbed TLS does not make it easy to write a certificate in PEM format.
224 * The only way is to directly access the DER encoded raw certificate
225 * and PEM encode it ourselves */
226
227 struct gc_arena gc = gc_new();
228 /* just do a very loose upper bound for the base64 based PEM encoding
229 * using 3 times the space for the base64 and 100 bytes for the
230 * headers and footer */
231 struct buffer pem = alloc_buf_gc(cert->raw.len * 3 + 100, &gc);
232
233 struct buffer der = {};
234 buf_set_read(&der, cert->raw.p, cert->raw.len);
235
236 if (!crypto_pem_encode("CERTIFICATE", &pem, &der, &gc))
237 {
238 goto err;
239 }
240
241 if (!buffer_write_file(filename, &pem))
242 {
243 goto err;
244 }
245
246 gc_free(&gc);
247 return SUCCESS;
248err:
249 msg(D_TLS_DEBUG_LOW, "Error writing X509 certificate to file %s", filename);
250 gc_free(&gc);
251 return FAILURE;
252}
253
254#if defined(__GNUC__) || defined(__clang__)
255#pragma GCC diagnostic push
256#pragma GCC diagnostic ignored "-Wconversion"
257#endif
258
259static struct buffer
261{
262 const size_t md_size = mbedtls_md_get_size(md_info);
264 mbedtls_md(md_info, cert->raw.p, cert->raw.len, BPTR(&fingerprint));
266 return fingerprint;
267}
268
269#if defined(__GNUC__) || defined(__clang__)
270#pragma GCC diagnostic pop
271#endif
272
273struct buffer
275{
276 return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), cert, gc);
277}
278
279struct buffer
281{
282 return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), cert, gc);
283}
284
285char *
286x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc)
287{
288 char tmp_subject[MAX_SUBJECT_LENGTH] = { 0 };
289 char *subject = NULL;
290
291 int ret = 0;
292
293 ret = mbedtls_x509_dn_gets(tmp_subject, MAX_SUBJECT_LENGTH - 1, &cert->subject);
294 if (ret > 0)
295 {
296 /* Allocate the required space for the subject */
297 subject = string_alloc(tmp_subject, gc);
298 }
299
300 return subject;
301}
302
303static void
304do_setenv_x509(struct env_set *es, const char *name, char *value, int depth)
305{
306 char *name_expand;
307 size_t name_expand_size;
308
309 string_mod(value, CC_ANY, CC_CRLF, '?');
310 msg(D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
311 name_expand_size = 64 + strlen(name);
312 name_expand = (char *)malloc(name_expand_size);
313 check_malloc_return(name_expand);
314 snprintf(name_expand, name_expand_size, "X509_%d_%s", depth, name);
315 setenv_str(es, name_expand, value);
316 free(name_expand);
317}
318
319static char *
320asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc)
321{
322 size_t i;
323 char *val;
324
325 if (!(orig->tag == MBEDTLS_ASN1_UTF8_STRING || orig->tag == MBEDTLS_ASN1_PRINTABLE_STRING
326 || orig->tag == MBEDTLS_ASN1_IA5_STRING))
327 {
328 /* Only support C-string compatible types */
329 return string_alloc("ERROR: unsupported ASN.1 string type", gc);
330 }
331
332 for (i = 0; i < orig->len; ++i)
333 {
334 if (orig->p[i] == '\0')
335 {
336 return string_alloc("ERROR: embedded null value", gc);
337 }
338 }
339 val = gc_malloc(orig->len + 1, false, gc);
340 memcpy(val, orig->p, orig->len);
341 val[orig->len] = '\0';
342 return val;
343}
344
345static void
346do_setenv_name(struct env_set *es, const struct x509_track *xt, const mbedtls_x509_crt *cert,
347 int depth, struct gc_arena *gc)
348{
349 const mbedtls_x509_name *xn;
350 for (xn = &cert->subject; xn != NULL; xn = xn->next)
351 {
352 const char *xn_short_name = NULL;
353 if (0 == mbedtls_oid_get_attr_short_name(&xn->oid, &xn_short_name)
354 && 0 == strcmp(xt->name, xn_short_name))
355 {
356 char *val_str = asn1_buf_to_c_string(&xn->val, gc);
357 do_setenv_x509(es, xt->name, val_str, depth);
358 }
359 }
360}
361
362void
363x509_track_add(const struct x509_track **ll_head, const char *name, msglvl_t msglevel,
364 struct gc_arena *gc)
365{
366 struct x509_track *xt;
367 ALLOC_OBJ_CLEAR_GC(xt, struct x509_track, gc);
368 if (*name == '+')
369 {
370 xt->flags |= XT_FULL_CHAIN;
371 ++name;
372 }
373 xt->name = name;
374 xt->next = *ll_head;
375 *ll_head = xt;
376}
377
378void
379x509_setenv_track(const struct x509_track *xt, struct env_set *es, const int depth,
380 mbedtls_x509_crt *cert)
381{
382 struct gc_arena gc = gc_new();
383 while (xt)
384 {
385 if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
386 {
387 if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256"))
388 {
389 /* Fingerprint is not part of X509 structure */
390 struct buffer cert_hash;
391 char *fingerprint;
392
393 if (0 == strcmp(xt->name, "SHA1"))
394 {
396 }
397 else
398 {
400 }
401
403 format_hex_ex(BPTR(&cert_hash), BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc);
405 }
406 else
407 {
408 do_setenv_name(es, xt, cert, depth, &gc);
409 }
410 }
411 xt = xt->next;
412 }
413 gc_free(&gc);
414}
415
416/*
417 * Save X509 fields to environment, using the naming convention:
418 *
419 * X509_{cert_depth}_{name}={value}
420 */
421void
423{
424 int i;
425 unsigned char c;
426 const mbedtls_x509_name *name;
427 char s[128] = { 0 };
428
429 name = &cert->subject;
430
431 while (name != NULL)
432 {
433 char name_expand[64 + 8];
434 const char *shortname;
435
436 if (0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname))
437 {
438 snprintf(name_expand, sizeof(name_expand), "X509_%d_%s", cert_depth, shortname);
439 }
440 else
441 {
442 snprintf(name_expand, sizeof(name_expand), "X509_%d_\?\?", cert_depth);
443 }
444
445 for (i = 0; i < name->val.len; i++)
446 {
447 if (i >= (int)sizeof(s) - 1)
448 {
449 break;
450 }
451
452 c = name->val.p[i];
453 if (c < 32 || c == 127 || (c > 128 && c < 160))
454 {
455 s[i] = '?';
456 }
457 else
458 {
459 s[i] = c;
460 }
461 }
462 s[i] = '\0';
463
464 /* Check both strings, set environment variable */
466 string_mod((char *)s, CC_PRINT, CC_CRLF, '_');
467 setenv_str_incr(es, name_expand, (char *)s);
468
469 name = name->next;
470 }
471}
472
473/* Dummy function because Netscape certificate types are not supported in OpenVPN with mbedtls.
474 * Returns SUCCESS if usage is NS_CERT_CHECK_NONE, FAILURE otherwise. */
477{
479 {
480 return SUCCESS;
481 }
482
483 return FAILURE;
484}
485
487x509_verify_cert_ku(mbedtls_x509_crt *cert, const unsigned *const expected_ku, int expected_len)
488{
489 msg(D_HANDSHAKE, "Validating certificate key usage");
490
492 {
493 msg(D_TLS_ERRORS, "ERROR: Certificate does not have key usage extension");
494 return FAILURE;
495 }
496
498 {
499 /* Extension required, value checked by TLS library */
500 return SUCCESS;
501 }
502
504 for (size_t i = 0; SUCCESS != fFound && i < expected_len; i++)
505 {
507 {
508 fFound = SUCCESS;
509 }
510 }
511
512 if (fFound != SUCCESS)
513 {
514 msg(D_TLS_ERRORS, "ERROR: Certificate has invalid key usage, expected one of:");
515 for (size_t i = 0; i < expected_len && expected_ku[i]; i++)
516 {
517 msg(D_TLS_ERRORS, " * %04x", expected_ku[i]);
518 }
519 }
520
521 return fFound;
522}
523
525x509_verify_cert_eku(mbedtls_x509_crt *cert, const char *const expected_oid)
526{
528
530 {
531 msg(D_HANDSHAKE, "Certificate does not have extended key usage extension");
532 }
533 else
534 {
535 mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage);
536
537 msg(D_HANDSHAKE, "Validating certificate extended key usage");
538 while (oid_seq != NULL)
539 {
541 char oid_num_str[1024];
542 const char *oid_str;
543
545 {
546 msg(D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", oid_str,
549 {
550 fFound = SUCCESS;
551 break;
552 }
553 }
554
556 {
557 msg(D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", oid_num_str,
560 {
561 fFound = SUCCESS;
562 break;
563 }
564 }
565 oid_seq = oid_seq->next;
566 }
567 }
568
569 return fFound;
570}
571
572bool
573tls_verify_crl_missing(const struct tls_options *opt)
574{
575 if (opt->crl_file && !(opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
576 && (opt->ssl_ctx.crl == NULL || opt->ssl_ctx.crl->version == 0))
577 {
578 return true;
579 }
580 return false;
581}
582
583#endif /* #if defined(ENABLE_CRYPTO_MBEDTLS) */
bool buffer_write_file(const char *filename, const struct buffer *buf)
Write buffer contents to file.
Definition buffer.c:301
char * format_hex_ex(const uint8_t *data, int size, int maxoutput, unsigned int space_break_flags, const char *separator, struct gc_arena *gc)
Definition buffer.c:483
void chomp(char *str)
Definition buffer.c:614
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:336
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:89
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
Modifies a string in place by replacing certain classes of characters of it with a specified characte...
Definition buffer.c:1046
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define CC_ANY
any character
Definition buffer.h:877
#define BPTR(buf)
Definition buffer.h:123
static bool buf_inc_len(struct buffer *buf, int inc)
Definition buffer.h:588
#define CC_CRLF
carriage return or newline
Definition buffer.h:914
static void buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
Definition buffer.h:348
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition buffer.h:1089
#define BLEN(buf)
Definition buffer.h:126
static void check_malloc_return(void *p)
Definition buffer.h:1095
static void gc_free(struct gc_arena *a)
Definition buffer.h:1025
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:885
#define FHE_CAPS
Definition buffer.h:498
static struct gc_arena gc_new(void)
Definition buffer.h:1017
bool crypto_pem_encode(const char *name, struct buffer *dst, const struct buffer *src, struct gc_arena *gc)
Encode binary data as PEM.
Data Channel Cryptography mbed TLS-specific backend interface.
#define mbed_ok(errval)
Check errval and log on error.
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition env_set.c:307
void setenv_str_incr(struct env_set *es, const char *name, const char *value)
Store the supplied name value pair in the env_set.
Definition env_set.c:329
#define D_TLS_DEBUG_LOW
Definition errlevel.h:76
#define D_X509_ATTR
Definition errlevel.h:102
#define D_HANDSHAKE
Definition errlevel.h:71
#define D_TLS_ERRORS
Definition errlevel.h:58
int verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, uint32_t *flags)
Verify that the remote OpenVPN peer's certificate allows setting up a VPN tunnel.
mbedtls compatibility stub.
static int mbedtls_x509_crt_has_ext_type(const mbedtls_x509_crt *ctx, int ext_type)
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
void usage(void)
Definition options.c:4863
#define SSLF_CRL_VERIFY_DIR
Definition ssl_common.h:429
result_t verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_depth)
Definition ssl_verify.c:577
void cert_hash_remember(struct tls_session *session, const int error_depth, const struct buffer *cert_hash)
Definition ssl_verify.c:194
Control Channel Verification Module.
#define OPENVPN_KU_REQUIRED
Require keyUsage to be present in cert (0xFFFF is an invalid KU value)
Definition ssl_verify.h:257
#define XT_FULL_CHAIN
Definition ssl_verify.h:241
#define NS_CERT_CHECK_NONE
Do not perform Netscape certificate type verification.
Definition ssl_verify.h:250
struct buffer x509_get_sha256_fingerprint(openvpn_x509_cert_t *cert, struct gc_arena *gc)
Retrieve the certificate's SHA256 fingerprint.
void x509_setenv_track(const struct x509_track *xt, struct env_set *es, const int depth, openvpn_x509_cert_t *x509)
void x509_setenv(struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert)
bool tls_verify_crl_missing(const struct tls_options *opt)
Return true iff a CRL is configured, but is not loaded.
result_t backend_x509_write_pem(openvpn_x509_cert_t *cert, const char *filename)
result_t x509_verify_ns_cert_type(openvpn_x509_cert_t *cert, const int usage)
char * backend_x509_get_serial_hex(openvpn_x509_cert_t *cert, struct gc_arena *gc)
result_t x509_verify_cert_ku(openvpn_x509_cert_t *x509, const unsigned *const expected_ku, int expected_len)
struct buffer x509_get_sha1_fingerprint(openvpn_x509_cert_t *cert, struct gc_arena *gc)
Retrieve the certificate's SHA1 fingerprint.
char * x509_get_subject(openvpn_x509_cert_t *cert, struct gc_arena *gc)
Definition test_pkcs11.c:68
char * backend_x509_get_serial(openvpn_x509_cert_t *cert, struct gc_arena *gc)
result_t backend_x509_get_username(char *common_name, size_t cn_len, char *x509_username_field, openvpn_x509_cert_t *peer_cert)
void x509_track_add(const struct x509_track **ll_head, const char *name, msglvl_t msglevel, struct gc_arena *gc)
result_t
Result of verification function.
@ FAILURE
@ SUCCESS
result_t x509_verify_cert_eku(openvpn_x509_cert_t *x509, const char *const expected_oid)
mbedtls_x509_crt openvpn_x509_cert_t
static void do_setenv_x509(struct env_set *es, const char *name, char *value, int depth)
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
Structure containing the hash for a single certificate.
Definition ssl_verify.h:58
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
struct tls_root_ctx ssl_ctx
Definition ssl_common.h:309
unsigned int ssl_flags
Definition ssl_common.h:435
const char * crl_file
Definition ssl_common.h:354
mbedtls_x509_crl * crl
Certificate Revocation List.
Security parameter state of a single session within a VPN tunnel.
Definition ssl_common.h:490
unsigned int flags
Definition ssl_verify.h:242
const struct x509_track * next
Definition ssl_verify.h:239
const char * name
Definition ssl_verify.h:240
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:131