src/openvpn/ssl_verify_mbedtls.c
53f97e1e
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
  *  Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com>
53f97e1e
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
53f97e1e
  */
 
 /**
86d8cd68
  * @file Control Channel Verification Module mbed TLS backend
53f97e1e
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
31ea2ee4
 #include "syshead.h"
 
c7ca9133
 #if defined(ENABLE_CRYPTO_MBEDTLS)
31ea2ee4
 
74586c65
 #include "crypto_mbedtls.h"
53f97e1e
 #include "ssl_verify.h"
86d8cd68
 #include <mbedtls/asn1.h>
 #include <mbedtls/error.h>
 #include <mbedtls/bignum.h>
 #include <mbedtls/oid.h>
 #include <mbedtls/sha1.h>
53f97e1e
 
 #define MAX_SUBJECT_LENGTH 256
 
 int
81d882d5
 verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth,
                 uint32_t *flags)
53f97e1e
 {
81d882d5
     struct tls_session *session = (struct tls_session *) session_obj;
     struct gc_arena gc = gc_new();
53f97e1e
 
81d882d5
     ASSERT(cert);
     ASSERT(session);
53f97e1e
 
81d882d5
     session->verified = false;
53f97e1e
 
81d882d5
     /* Remember certificate hash */
     struct buffer cert_fingerprint = x509_get_sha256_fingerprint(cert, &gc);
     cert_hash_remember(session, cert_depth, &cert_fingerprint);
53f97e1e
 
81d882d5
     /* did peer present cert which was signed by our root cert? */
     if (*flags != 0)
53f97e1e
     {
81d882d5
         int ret = 0;
         char errstr[512] = { 0 };
         char *subject = x509_get_subject(cert, &gc);
 
         ret = mbedtls_x509_crt_verify_info(errstr, sizeof(errstr)-1, "", *flags);
         if (ret <= 0 && !openvpn_snprintf(errstr, sizeof(errstr),
                                           "Could not retrieve error string, flags=%" PRIx32, *flags))
         {
             errstr[0] = '\0';
         }
         else
         {
             chomp(errstr);
         }
 
         if (subject)
         {
             msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s: %s",
                 cert_depth, subject, errstr);
         }
         else
         {
             msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, (could not extract X509 "
                 "subject string from certificate): %s", cert_depth, errstr);
         }
 
         /* Leave flags set to non-zero to indicate that the cert is not ok */
4a56d19f
     }
81d882d5
     else if (SUCCESS != verify_cert(session, cert, cert_depth))
4a56d19f
     {
81d882d5
         *flags |= MBEDTLS_X509_BADCERT_OTHER;
53f97e1e
     }
 
81d882d5
     gc_free(&gc);
75b49e40
 
81d882d5
     /*
      * PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors.
      */
     return 0;
53f97e1e
 }
 
 #ifdef ENABLE_X509ALTUSERNAME
81d882d5
 #warning "X509 alt user name not yet supported for mbed TLS"
53f97e1e
 #endif
 
8a840d83
 result_t
81d882d5
 backend_x509_get_username(char *cn, int cn_len,
                           char *x509_username_field, mbedtls_x509_crt *cert)
53f97e1e
 {
81d882d5
     mbedtls_x509_name *name;
53f97e1e
 
81d882d5
     ASSERT( cn != NULL );
53f97e1e
 
81d882d5
     name = &cert->subject;
53f97e1e
 
81d882d5
     /* Find common name */
     while (name != NULL)
     {
         if (0 == memcmp(name->oid.p, MBEDTLS_OID_AT_CN,
                         MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN)))
         {
             break;
         }
53f97e1e
 
81d882d5
         name = name->next;
     }
53f97e1e
 
81d882d5
     /* Not found, return an error if this is the peer's certificate */
     if (name == NULL)
     {
         return FAILURE;
     }
53f97e1e
 
81d882d5
     /* Found, extract CN */
     if (cn_len > name->val.len)
63559e14
     {
81d882d5
         memcpy( cn, name->val.p, name->val.len );
         cn[name->val.len] = '\0';
63559e14
     }
81d882d5
     else
53f97e1e
     {
81d882d5
         memcpy( cn, name->val.p, cn_len);
         cn[cn_len-1] = '\0';
53f97e1e
     }
 
81d882d5
     return SUCCESS;
53f97e1e
 }
 
 char *
81d882d5
 backend_x509_get_serial(mbedtls_x509_crt *cert, struct gc_arena *gc)
53f97e1e
 {
81d882d5
     char *buf = NULL;
     size_t buflen = 0;
     mbedtls_mpi serial_mpi = { 0 };
 
     /* Transform asn1 integer serial into mbed TLS MPI */
     mbedtls_mpi_init(&serial_mpi);
     if (!mbed_ok(mbedtls_mpi_read_binary(&serial_mpi, cert->serial.p,
                                          cert->serial.len)))
f80a52b0
     {
81d882d5
         msg(M_WARN, "Failed to retrieve serial from certificate.");
         goto end;
f80a52b0
     }
 
81d882d5
     /* Determine decimal representation length, allocate buffer */
     mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen);
     buf = gc_malloc(buflen, true, gc);
f80a52b0
 
81d882d5
     /* Write MPI serial as decimal string into buffer */
     if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen)))
f80a52b0
     {
81d882d5
         msg(M_WARN, "Failed to write serial to string.");
         buf = NULL;
         goto end;
f80a52b0
     }
 
cd538f2c
 end:
81d882d5
     mbedtls_mpi_free(&serial_mpi);
     return buf;
f80a52b0
 }
 
 char *
81d882d5
 backend_x509_get_serial_hex(mbedtls_x509_crt *cert, struct gc_arena *gc)
f80a52b0
 {
81d882d5
     char *buf = NULL;
     size_t len = cert->serial.len * 3 + 1;
53f97e1e
 
81d882d5
     buf = gc_malloc(len, true, gc);
53f97e1e
 
81d882d5
     if (mbedtls_x509_serial_gets(buf, len-1, &cert->serial) < 0)
     {
         buf = NULL;
     }
53f97e1e
 
81d882d5
     return buf;
53f97e1e
 }
 
af1e4d26
 static struct buffer
81d882d5
 x509_get_fingerprint(const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert,
                      struct gc_arena *gc)
53f97e1e
 {
81d882d5
     const size_t md_size = mbedtls_md_get_size(md_info);
     struct buffer fingerprint = alloc_buf_gc(md_size, gc);
21a540f9
     mbedtls_md(md_info, cert->raw.p, cert->raw.len, BPTR(&fingerprint));
81d882d5
     ASSERT(buf_inc_len(&fingerprint, md_size));
     return fingerprint;
af1e4d26
 }
 
 struct buffer
81d882d5
 x509_get_sha1_fingerprint(mbedtls_x509_crt *cert, struct gc_arena *gc)
af1e4d26
 {
81d882d5
     return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
                                 cert, gc);
af1e4d26
 }
 
 struct buffer
81d882d5
 x509_get_sha256_fingerprint(mbedtls_x509_crt *cert, struct gc_arena *gc)
af1e4d26
 {
81d882d5
     return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
                                 cert, gc);
53f97e1e
 }
 
 char *
86d8cd68
 x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc)
53f97e1e
 {
81d882d5
     char tmp_subject[MAX_SUBJECT_LENGTH] = {0};
     char *subject = NULL;
53f97e1e
 
81d882d5
     int ret = 0;
53f97e1e
 
81d882d5
     ret = mbedtls_x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject );
     if (ret > 0)
53f97e1e
     {
81d882d5
         /* Allocate the required space for the subject */
         subject = string_alloc(tmp_subject, gc);
53f97e1e
     }
 
81d882d5
     return subject;
53f97e1e
 }
 
fab49d17
 static void
81d882d5
 do_setenv_x509(struct env_set *es, const char *name, char *value, int depth)
fab49d17
 {
81d882d5
     char *name_expand;
     size_t name_expand_size;
 
     string_mod(value, CC_ANY, CC_CRLF, '?');
     msg(D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
     name_expand_size = 64 + strlen(name);
     name_expand = (char *) malloc(name_expand_size);
     check_malloc_return(name_expand);
     openvpn_snprintf(name_expand, name_expand_size, "X509_%d_%s", depth, name);
     setenv_str(es, name_expand, value);
     free(name_expand);
fab49d17
 }
 
 static char *
86d8cd68
 asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc)
fab49d17
 {
81d882d5
     size_t i;
     char *val;
 
0007b2db
     if (!(orig->tag == MBEDTLS_ASN1_UTF8_STRING
           || orig->tag == MBEDTLS_ASN1_PRINTABLE_STRING
           || orig->tag == MBEDTLS_ASN1_IA5_STRING))
     {
         /* Only support C-string compatible types */
         return string_alloc("ERROR: unsupported ASN.1 string type", gc);
     }
 
81d882d5
     for (i = 0; i < orig->len; ++i)
4cd4899e
     {
81d882d5
         if (orig->p[i] == '\0')
         {
42639294
             return string_alloc("ERROR: embedded null value", gc);
81d882d5
         }
4cd4899e
     }
81d882d5
     val = gc_malloc(orig->len+1, false, gc);
     memcpy(val, orig->p, orig->len);
     val[orig->len] = '\0';
     return val;
fab49d17
 }
 
 static void
 do_setenv_name(struct env_set *es, const struct x509_track *xt,
81d882d5
                const mbedtls_x509_crt *cert, int depth, struct gc_arena *gc)
fab49d17
 {
81d882d5
     const mbedtls_x509_name *xn;
     for (xn = &cert->subject; xn != NULL; xn = xn->next)
fab49d17
     {
81d882d5
         const char *xn_short_name = NULL;
         if (0 == mbedtls_oid_get_attr_short_name(&xn->oid, &xn_short_name)
             && 0 == strcmp(xt->name, xn_short_name))
         {
             char *val_str = asn1_buf_to_c_string(&xn->val, gc);
             do_setenv_x509(es, xt->name, val_str, depth);
         }
fab49d17
     }
 }
 
 void
81d882d5
 x509_track_add(const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc)
fab49d17
 {
81d882d5
     struct x509_track *xt;
     ALLOC_OBJ_CLEAR_GC(xt, struct x509_track, gc);
     if (*name == '+')
fab49d17
     {
81d882d5
         xt->flags |= XT_FULL_CHAIN;
         ++name;
fab49d17
     }
81d882d5
     xt->name = name;
     xt->next = *ll_head;
     *ll_head = xt;
fab49d17
 }
 
 void
81d882d5
 x509_setenv_track(const struct x509_track *xt, struct env_set *es,
                   const int depth, mbedtls_x509_crt *cert)
fab49d17
 {
81d882d5
     struct gc_arena gc = gc_new();
     while (xt)
fab49d17
     {
81d882d5
         if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
         {
             if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256"))
             {
                 /* Fingerprint is not part of X509 structure */
                 struct buffer cert_hash;
                 char *fingerprint;
 
                 if (0 == strcmp(xt->name, "SHA1"))
                 {
                     cert_hash = x509_get_sha1_fingerprint(cert, &gc);
                 }
                 else
                 {
                     cert_hash = x509_get_sha256_fingerprint(cert, &gc);
                 }
 
                 fingerprint = format_hex_ex(BPTR(&cert_hash),
                                             BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc);
                 do_setenv_x509(es, xt->name, fingerprint, depth);
             }
             else
             {
                 do_setenv_name(es, xt, cert, depth, &gc);
             }
         }
         xt = xt->next;
fab49d17
     }
81d882d5
     gc_free(&gc);
fab49d17
 }
 
53f97e1e
 /*
  * Save X509 fields to environment, using the naming convention:
  *
  * X509_{cert_depth}_{name}={value}
  */
 void
81d882d5
 x509_setenv(struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)
53f97e1e
 {
81d882d5
     int i;
     unsigned char c;
     const mbedtls_x509_name *name;
     char s[128] = { 0 };
53f97e1e
 
81d882d5
     name = &cert->subject;
53f97e1e
 
81d882d5
     while (name != NULL)
53f97e1e
     {
81d882d5
         char name_expand[64+8];
         const char *shortname;
 
         if (0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname) )
         {
             openvpn_snprintf(name_expand, sizeof(name_expand), "X509_%d_%s",
                              cert_depth, shortname);
         }
         else
         {
             openvpn_snprintf(name_expand, sizeof(name_expand), "X509_%d_\?\?",
                              cert_depth);
         }
 
         for (i = 0; i < name->val.len; i++)
         {
             if (i >= (int) sizeof( s ) - 1)
             {
                 break;
             }
 
             c = name->val.p[i];
             if (c < 32 || c == 127 || ( c > 128 && c < 160 ) )
             {
                 s[i] = '?';
             }
             else
             {
                 s[i] = c;
             }
         }
         s[i] = '\0';
 
         /* Check both strings, set environment variable */
         string_mod(name_expand, CC_PRINT, CC_CRLF, '_');
         string_mod((char *)s, CC_PRINT, CC_CRLF, '_');
         setenv_str_incr(es, name_expand, (char *)s);
 
         name = name->next;
53f97e1e
     }
 }
 
8a840d83
 result_t
17d1ab90
 x509_verify_ns_cert_type(mbedtls_x509_crt *cert, const int usage)
53f97e1e
 {
81d882d5
     if (usage == NS_CERT_CHECK_NONE)
     {
         return SUCCESS;
     }
     if (usage == NS_CERT_CHECK_CLIENT)
     {
         return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
                 && (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT)) ?
                SUCCESS : FAILURE;
     }
     if (usage == NS_CERT_CHECK_SERVER)
     {
         return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
                 && (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER)) ?
                SUCCESS : FAILURE;
     }
 
     return FAILURE;
53f97e1e
 }
 
8a840d83
 result_t
81d882d5
 x509_verify_cert_ku(mbedtls_x509_crt *cert, const unsigned *const expected_ku,
                     int expected_len)
53f97e1e
 {
92a5b9fb
     msg(D_HANDSHAKE, "Validating certificate key usage");
53f97e1e
 
81d882d5
     if (!(cert->ext_types & MBEDTLS_X509_EXT_KEY_USAGE))
53f97e1e
     {
92a5b9fb
         msg(D_TLS_ERRORS,
             "ERROR: Certificate does not have key usage extension");
         return FAILURE;
53f97e1e
     }
92a5b9fb
 
     if (expected_ku[0] == OPENVPN_KU_REQUIRED)
53f97e1e
     {
92a5b9fb
         /* Extension required, value checked by TLS library */
         return SUCCESS;
     }
81d882d5
 
92a5b9fb
     result_t fFound = FAILURE;
     for (size_t i = 0; SUCCESS != fFound && i<expected_len; i++)
     {
         if (expected_ku[i] != 0
             && 0 == mbedtls_x509_crt_check_key_usage(cert, expected_ku[i]))
81d882d5
         {
92a5b9fb
             fFound = SUCCESS;
         }
     }
81d882d5
 
92a5b9fb
     if (fFound != SUCCESS)
     {
         msg(D_TLS_ERRORS,
             "ERROR: Certificate has key usage %04x, expected one of:",
             cert->key_usage);
         for (size_t i = 0; i < expected_len && expected_ku[i]; i++)
         {
             msg(D_TLS_ERRORS, " * %04x", expected_ku[i]);
81d882d5
         }
53f97e1e
     }
92a5b9fb
 
81d882d5
     return fFound;
53f97e1e
 }
 
8a840d83
 result_t
81d882d5
 x509_verify_cert_eku(mbedtls_x509_crt *cert, const char *const expected_oid)
53f97e1e
 {
81d882d5
     result_t fFound = FAILURE;
53f97e1e
 
81d882d5
     if (!(cert->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE))
53f97e1e
     {
81d882d5
         msg(D_HANDSHAKE, "Certificate does not have extended key usage extension");
53f97e1e
     }
81d882d5
     else
53f97e1e
     {
81d882d5
         mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage);
 
         msg(D_HANDSHAKE, "Validating certificate extended key usage");
         while (oid_seq != NULL)
         {
             mbedtls_x509_buf *oid = &oid_seq->buf;
             char oid_num_str[1024];
             const char *oid_str;
 
             if (0 == mbedtls_oid_get_extended_key_usage( oid, &oid_str ))
             {
                 msg(D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s",
                     oid_str, expected_oid);
                 if (!strcmp(expected_oid, oid_str))
                 {
                     fFound = SUCCESS;
                     break;
                 }
             }
 
             if (0 < mbedtls_oid_get_numeric_string( oid_num_str,
                                                     sizeof(oid_num_str), oid))
             {
                 msg(D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s",
                     oid_num_str, expected_oid);
                 if (!strcmp(expected_oid, oid_num_str))
                 {
                     fFound = SUCCESS;
                     break;
                 }
             }
             oid_seq = oid_seq->next;
         }
53f97e1e
     }
 
     return fFound;
 }
 
8a840d83
 result_t
86d8cd68
 x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert)
53f97e1e
 {
81d882d5
     msg(M_WARN, "mbed TLS does not support writing peer certificate in PEM format");
8a840d83
     return FAILURE;
53f97e1e
 }
 
160504a2
 bool
 tls_verify_crl_missing(const struct tls_options *opt)
53f97e1e
 {
81d882d5
     if (opt->crl_file && !(opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
         && (opt->ssl_ctx.crl == NULL || opt->ssl_ctx.crl->version == 0))
53f97e1e
     {
81d882d5
         return true;
53f97e1e
     }
81d882d5
     return false;
53f97e1e
 }
31ea2ee4
 
c7ca9133
 #endif /* #if defined(ENABLE_CRYPTO_MBEDTLS) */