diff options
Diffstat (limited to 'src/openvpn/ssl_verify_mbedtls.c')
-rw-r--r-- | src/openvpn/ssl_verify_mbedtls.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c new file mode 100644 index 0000000..332f04b --- /dev/null +++ b/src/openvpn/ssl_verify_mbedtls.c @@ -0,0 +1,511 @@ +/* + * 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. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file Control Channel Verification Module mbed TLS backend + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) + +#include "crypto_mbedtls.h" +#include "ssl_verify.h" +#include <mbedtls/asn1.h> +#include <mbedtls/error.h> +#include <mbedtls/bignum.h> +#include <mbedtls/oid.h> +#include <mbedtls/sha1.h> + +#define MAX_SUBJECT_LENGTH 256 + +int +verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth, + uint32_t *flags) +{ + struct tls_session *session = (struct tls_session *) session_obj; + struct gc_arena gc = gc_new(); + + ASSERT (cert); + ASSERT (session); + + session->verified = false; + + /* Remember certificate hash */ + struct buffer cert_fingerprint = x509_get_sha256_fingerprint (cert, &gc); + cert_hash_remember (session, cert_depth, &cert_fingerprint); + + /* did peer present cert which was signed by our root cert? */ + if (*flags != 0) + { + 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 */ + } + else if (SUCCESS != verify_cert(session, cert, cert_depth)) + { + *flags |= MBEDTLS_X509_BADCERT_OTHER; + } + + gc_free(&gc); + + /* + * PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors. + */ + return 0; +} + +#ifdef ENABLE_X509ALTUSERNAME +# warning "X509 alt user name not yet supported for mbed TLS" +#endif + +result_t +backend_x509_get_username (char *cn, int cn_len, + char *x509_username_field, mbedtls_x509_crt *cert) +{ + mbedtls_x509_name *name; + + ASSERT( cn != NULL ); + + name = &cert->subject; + + /* Find common name */ + while( name != NULL ) + { + if (0 == memcmp (name->oid.p, MBEDTLS_OID_AT_CN, + MBEDTLS_OID_SIZE (MBEDTLS_OID_AT_CN))) + break; + + name = name->next; + } + + /* Not found, return an error if this is the peer's certificate */ + if( name == NULL ) + return FAILURE; + + /* Found, extract CN */ + if (cn_len > name->val.len) + { + memcpy( cn, name->val.p, name->val.len ); + cn[name->val.len] = '\0'; + } + else + { + memcpy( cn, name->val.p, cn_len); + cn[cn_len-1] = '\0'; + } + + return SUCCESS; +} + +char * +backend_x509_get_serial (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + 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))) + { + msg(M_WARN, "Failed to retrieve serial from certificate."); + goto end; + } + + /* Determine decimal representation length, allocate buffer */ + mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen); + buf = gc_malloc(buflen, true, gc); + + /* Write MPI serial as decimal string into buffer */ + if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen))) + { + msg(M_WARN, "Failed to write serial to string."); + buf = NULL; + goto end; + } + +end: + mbedtls_mpi_free(&serial_mpi); + return buf; +} + +char * +backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + char *buf = NULL; + size_t len = cert->serial.len * 3 + 1; + + buf = gc_malloc(len, true, gc); + + if(mbedtls_x509_serial_gets(buf, len-1, &cert->serial) < 0) + buf = NULL; + + return buf; +} + +static struct buffer +x509_get_fingerprint (const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert, + struct gc_arena *gc) +{ + const size_t md_size = mbedtls_md_get_size (md_info); + struct buffer fingerprint = alloc_buf_gc (md_size, gc); + mbedtls_md(md_info, cert->raw.p, cert->tbs.len, BPTR (&fingerprint)); + ASSERT (buf_inc_len(&fingerprint, md_size)); + return fingerprint; +} + +struct buffer +x509_get_sha1_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), + cert, gc); +} + +struct buffer +x509_get_sha256_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + cert, gc); +} + +char * +x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + char tmp_subject[MAX_SUBJECT_LENGTH] = {0}; + char *subject = NULL; + + int ret = 0; + + ret = mbedtls_x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); + if (ret > 0) + { + /* Allocate the required space for the subject */ + subject = string_alloc(tmp_subject, gc); + } + + return subject; +} + +static void +do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth) +{ + 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); +} + +static char * +asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc) +{ + size_t i; + char *val; + + for (i = 0; i < orig->len; ++i) + if (orig->p[i] == '\0') + return "ERROR: embedded null value"; + val = gc_malloc(orig->len+1, false, gc); + memcpy(val, orig->p, orig->len); + val[orig->len] = '\0'; + return val; +} + +static void +do_setenv_name(struct env_set *es, const struct x509_track *xt, + const mbedtls_x509_crt *cert, int depth, struct gc_arena *gc) +{ + const mbedtls_x509_name *xn; + for (xn = &cert->subject; xn != NULL; xn = xn->next) + { + 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); + } + } +} + +void +x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc) +{ + struct x509_track *xt; + ALLOC_OBJ_CLEAR_GC (xt, struct x509_track, gc); + if (*name == '+') + { + xt->flags |= XT_FULL_CHAIN; + ++name; + } + xt->name = name; + xt->next = *ll_head; + *ll_head = xt; +} + +void +x509_setenv_track (const struct x509_track *xt, struct env_set *es, + const int depth, mbedtls_x509_crt *cert) +{ + struct gc_arena gc = gc_new(); + while (xt) + { + 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; + } + gc_free(&gc); +} + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + */ +void +x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert) +{ + int i; + unsigned char c; + const mbedtls_x509_name *name; + char s[128]; + + name = &cert->subject; + + memset( s, 0, sizeof( s ) ); + + while( name != NULL ) + { + 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; + } +} + +result_t +x509_verify_ns_cert_type(const mbedtls_x509_crt *cert, const int usage) +{ + 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; +} + +result_t +x509_verify_cert_ku (mbedtls_x509_crt *cert, const unsigned * const expected_ku, + int expected_len) +{ + result_t fFound = FAILURE; + + if(!(cert->ext_types & MBEDTLS_X509_EXT_KEY_USAGE)) + { + msg (D_HANDSHAKE, "Certificate does not have key usage extension"); + } + else + { + int i; + unsigned nku = cert->key_usage; + + msg (D_HANDSHAKE, "Validating certificate key usage"); + for (i=0; SUCCESS != fFound && i<expected_len; i++) + { + if (expected_ku[i] != 0) + { + msg (D_HANDSHAKE, "++ Certificate has key usage %04x, expects " + "%04x", nku, expected_ku[i]); + + if (nku == expected_ku[i]) + { + fFound = SUCCESS; + } + } + } + } + return fFound; +} + +result_t +x509_verify_cert_eku (mbedtls_x509_crt *cert, const char * const expected_oid) +{ + result_t fFound = FAILURE; + + if (!(cert->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE)) + { + msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); + } + else + { + 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; + } + } + + return fFound; +} + +result_t +x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert) +{ + msg (M_WARN, "mbed TLS does not support writing peer certificate in PEM format"); + return FAILURE; +} + +bool +tls_verify_crl_missing(const struct tls_options *opt) +{ + if (opt->crl_file && !(opt->ssl_flags & SSLF_CRL_VERIFY_DIR) + && (opt->ssl_ctx.crl == NULL || opt->ssl_ctx.crl->version == 0)) + { + return true; + } + return false; +} + +#endif /* #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ |