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.
*
* 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
*/
/** |
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"
|
86d8cd68 |
#if defined(ENABLE_CRYPTO) && 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 |
86d8cd68 |
verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth,
uint32_t *flags) |
53f97e1e |
{
struct tls_session *session = (struct tls_session *) session_obj; |
00b973f8 |
struct gc_arena gc = gc_new(); |
53f97e1e |
ASSERT (cert);
ASSERT (session);
session->verified = false;
/* Remember certificate hash */ |
8e5613c2 |
cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc)); |
53f97e1e |
/* did peer present cert which was signed by our root cert? */ |
4a56d19f |
if (*flags != 0) |
53f97e1e |
{ |
00b973f8 |
char *subject = x509_get_subject(cert, &gc); |
53f97e1e |
|
00b973f8 |
if (subject) |
d572959d |
msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, %s", cert_depth, *flags, subject); |
00b973f8 |
else |
d572959d |
msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, could not extract X509 "
"subject string from certificate", *flags, cert_depth); |
53f97e1e |
|
4a56d19f |
/* Leave flags set to non-zero to indicate that the cert is not ok */
}
else if (SUCCESS != verify_cert(session, cert, cert_depth))
{ |
86d8cd68 |
*flags |= MBEDTLS_X509_BADCERT_OTHER; |
53f97e1e |
}
|
75b49e40 |
gc_free(&gc);
|
53f97e1e |
/* |
86d8cd68 |
* PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors. |
53f97e1e |
*/ |
4a56d19f |
return 0; |
53f97e1e |
}
#ifdef ENABLE_X509ALTUSERNAME |
86d8cd68 |
# warning "X509 alt user name not yet supported for mbed TLS" |
53f97e1e |
#endif
|
8a840d83 |
result_t |
03df3a99 |
backend_x509_get_username (char *cn, int cn_len, |
86d8cd68 |
char *x509_username_field, mbedtls_x509_crt *cert) |
53f97e1e |
{ |
86d8cd68 |
mbedtls_x509_name *name; |
53f97e1e |
ASSERT( cn != NULL );
name = &cert->subject;
/* Find common name */
while( name != NULL )
{ |
86d8cd68 |
if (0 == memcmp (name->oid.p, MBEDTLS_OID_AT_CN,
MBEDTLS_OID_SIZE (MBEDTLS_OID_AT_CN))) |
53f97e1e |
break;
name = name->next;
}
/* Not found, return an error if this is the peer's certificate */
if( name == NULL ) |
8a840d83 |
return FAILURE; |
53f97e1e |
/* Found, extract CN */
if (cn_len > name->val.len) |
63559e14 |
{
memcpy( cn, name->val.p, name->val.len );
cn[name->val.len] = '\0';
} |
53f97e1e |
else
{
memcpy( cn, name->val.p, cn_len);
cn[cn_len-1] = '\0';
}
|
8a840d83 |
return SUCCESS; |
53f97e1e |
}
char * |
86d8cd68 |
backend_x509_get_serial (mbedtls_x509_crt *cert, struct gc_arena *gc) |
53f97e1e |
{
char *buf = NULL; |
f80a52b0 |
size_t buflen = 0; |
86d8cd68 |
mbedtls_mpi serial_mpi = { 0 }; |
f80a52b0 |
|
86d8cd68 |
/* 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 |
{ |
d17d362d |
msg(M_WARN, "Failed to retrieve serial from certificate."); |
f80a52b0 |
return NULL;
}
/* Determine decimal representation length, allocate buffer */ |
86d8cd68 |
mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen); |
f80a52b0 |
buf = gc_malloc(buflen, true, gc);
/* Write MPI serial as decimal string into buffer */ |
86d8cd68 |
if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen))) |
f80a52b0 |
{ |
d17d362d |
msg(M_WARN, "Failed to write serial to string."); |
f80a52b0 |
return NULL;
}
return buf;
}
char * |
86d8cd68 |
backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc) |
f80a52b0 |
{
char *buf = NULL; |
2e2939e8 |
size_t len = cert->serial.len * 3 + 1; |
53f97e1e |
|
025f30d7 |
buf = gc_malloc(len, true, gc); |
53f97e1e |
|
86d8cd68 |
if(mbedtls_x509_serial_gets(buf, len-1, &cert->serial) < 0) |
025f30d7 |
buf = NULL; |
53f97e1e |
return buf;
}
unsigned char * |
86d8cd68 |
x509_get_sha1_hash (mbedtls_x509_crt *cert, struct gc_arena *gc) |
53f97e1e |
{ |
8e5613c2 |
unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); |
86d8cd68 |
mbedtls_sha1(cert->raw.p, cert->tbs.len, sha1_hash); |
53f97e1e |
return sha1_hash;
}
char * |
86d8cd68 |
x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc) |
53f97e1e |
{
char tmp_subject[MAX_SUBJECT_LENGTH] = {0};
char *subject = NULL;
int ret = 0;
|
86d8cd68 |
ret = mbedtls_x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); |
53f97e1e |
if (ret > 0)
{
/* Allocate the required space for the subject */ |
00b973f8 |
subject = string_alloc(tmp_subject, gc); |
53f97e1e |
}
return subject;
}
|
fab49d17 |
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 * |
86d8cd68 |
asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc) |
fab49d17 |
{
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, |
86d8cd68 |
const mbedtls_x509_crt *cert, int depth, struct gc_arena *gc) |
fab49d17 |
{ |
86d8cd68 |
const mbedtls_x509_name *xn; |
fab49d17 |
for (xn = &cert->subject; xn != NULL; xn = xn->next)
{
const char *xn_short_name = NULL; |
86d8cd68 |
if (0 == mbedtls_oid_get_attr_short_name (&xn->oid, &xn_short_name) && |
fab49d17 |
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 |
86d8cd68 |
x509_setenv_track (const struct x509_track *xt, struct env_set *es,
const int depth, mbedtls_x509_crt *cert) |
fab49d17 |
{
struct gc_arena gc = gc_new();
while (xt)
{
if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
{
if (0 == strcmp(xt->name, "SHA1"))
{
/* SHA1 fingerprint is not part of X509 structure */
unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
char *sha1_fingerprint = format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
}
else
{
do_setenv_name(es, xt, cert, depth, &gc);
}
}
xt = xt->next;
}
gc_free(&gc);
}
|
53f97e1e |
/*
* Save X509 fields to environment, using the naming convention:
*
* X509_{cert_depth}_{name}={value}
*/
void |
86d8cd68 |
x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert) |
53f97e1e |
{ |
be960aad |
int i; |
53f97e1e |
unsigned char c; |
86d8cd68 |
const mbedtls_x509_name *name; |
53f97e1e |
char s[128];
name = &cert->subject;
memset( s, 0, sizeof( s ) );
while( name != NULL )
{
char name_expand[64+8]; |
03df3a99 |
const char *shortname; |
53f97e1e |
|
86d8cd68 |
if( 0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname) ) |
53f97e1e |
{ |
03df3a99 |
openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_%s",
cert_depth, shortname); |
53f97e1e |
} |
03df3a99 |
else
{
openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?",
cert_depth);
}
for( i = 0; i < name->val.len; i++ ) |
53f97e1e |
{ |
03df3a99 |
if( i >= (int) sizeof( s ) - 1 )
break; |
53f97e1e |
|
03df3a99 |
c = name->val.p[i];
if( c < 32 || c == 127 || ( c > 128 && c < 160 ) )
s[i] = '?';
else s[i] = c; |
53f97e1e |
}
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, '_'); |
13b585e8 |
setenv_str_incr (es, name_expand, (char*)s); |
53f97e1e |
name = name->next;
}
}
|
8a840d83 |
result_t |
86d8cd68 |
x509_verify_ns_cert_type(const mbedtls_x509_crt *cert, const int usage) |
53f97e1e |
{
if (usage == NS_CERT_CHECK_NONE) |
8a840d83 |
return SUCCESS; |
53f97e1e |
if (usage == NS_CERT_CHECK_CLIENT) |
86d8cd68 |
return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
&& (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT)) ?
SUCCESS : FAILURE; |
53f97e1e |
if (usage == NS_CERT_CHECK_SERVER) |
86d8cd68 |
return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
&& (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER)) ?
SUCCESS : FAILURE; |
53f97e1e |
|
8a840d83 |
return FAILURE; |
53f97e1e |
}
|
8a840d83 |
result_t |
86d8cd68 |
x509_verify_cert_ku (mbedtls_x509_crt *cert, const unsigned * const expected_ku, |
53f97e1e |
int expected_len)
{ |
8a840d83 |
result_t fFound = FAILURE; |
53f97e1e |
|
86d8cd68 |
if(!(cert->ext_types & MBEDTLS_X509_EXT_KEY_USAGE)) |
53f97e1e |
{
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"); |
8a840d83 |
for (i=0; SUCCESS != fFound && i<expected_len; i++) |
53f97e1e |
{
if (expected_ku[i] != 0)
{
msg (D_HANDSHAKE, "++ Certificate has key usage %04x, expects "
"%04x", nku, expected_ku[i]);
if (nku == expected_ku[i])
{ |
8a840d83 |
fFound = SUCCESS; |
53f97e1e |
}
}
}
}
return fFound;
}
|
8a840d83 |
result_t |
86d8cd68 |
x509_verify_cert_eku (mbedtls_x509_crt *cert, const char * const expected_oid) |
53f97e1e |
{ |
8a840d83 |
result_t fFound = FAILURE; |
53f97e1e |
|
86d8cd68 |
if (!(cert->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE)) |
53f97e1e |
{
msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
}
else
{ |
86d8cd68 |
mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage); |
53f97e1e |
msg (D_HANDSHAKE, "Validating certificate extended key usage");
while (oid_seq != NULL)
{ |
86d8cd68 |
mbedtls_x509_buf *oid = &oid_seq->buf; |
53f97e1e |
char oid_num_str[1024];
const char *oid_str;
|
86d8cd68 |
if (0 == mbedtls_oid_get_extended_key_usage( oid, &oid_str )) |
53f97e1e |
{
msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s",
oid_str, expected_oid);
if (!strcmp (expected_oid, oid_str))
{ |
8a840d83 |
fFound = SUCCESS; |
53f97e1e |
break;
}
}
|
86d8cd68 |
if (0 < mbedtls_oid_get_numeric_string( oid_num_str, |
53f97e1e |
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))
{ |
8a840d83 |
fFound = SUCCESS; |
53f97e1e |
break;
}
}
oid_seq = oid_seq->next;
}
}
return fFound;
}
|
8a840d83 |
result_t |
86d8cd68 |
x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert) |
53f97e1e |
{ |
86d8cd68 |
msg (M_WARN, "mbed TLS does not support writing peer certificate in PEM format"); |
8a840d83 |
return FAILURE; |
53f97e1e |
}
/*
* check peer cert against CRL
*/ |
8a840d83 |
result_t |
86d8cd68 |
x509_verify_crl(const char *crl_file, const char *crl_inline,
mbedtls_x509_crt *cert, const char *subject) |
53f97e1e |
{ |
8a840d83 |
result_t retval = FAILURE; |
86d8cd68 |
mbedtls_x509_crl crl = {0}; |
767e4c56 |
struct gc_arena gc = gc_new();
char *serial; |
53f97e1e |
|
7a7a79f6 |
if (!strcmp (crl_file, INLINE_FILE_TAG) && crl_inline) |
53f97e1e |
{ |
86d8cd68 |
if (!mbed_ok(mbedtls_x509_crl_parse(&crl,
(const unsigned char *)crl_inline, strlen(crl_inline)+1))) |
7a7a79f6 |
{
msg (M_WARN, "CRL: cannot parse inline CRL");
goto end;
} |
53f97e1e |
} |
7a7a79f6 |
else
{ |
86d8cd68 |
if (!mbed_ok(mbedtls_x509_crl_parse_file(&crl, crl_file))) |
7a7a79f6 |
{
msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file);
goto end;
}
} |
53f97e1e |
if(cert->issuer_raw.len != crl.issuer_raw.len ||
memcmp(crl.issuer_raw.p, cert->issuer_raw.p, crl.issuer_raw.len) != 0)
{
msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of "
"certificate %s", crl_file, subject); |
8a840d83 |
retval = SUCCESS; |
53f97e1e |
goto end;
}
|
86d8cd68 |
if (!mbed_ok(mbedtls_x509_crt_is_revoked(cert, &crl))) |
53f97e1e |
{ |
767e4c56 |
serial = backend_x509_get_serial_hex(cert, &gc);
msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE")); |
53f97e1e |
goto end;
}
|
8a840d83 |
retval = SUCCESS; |
53f97e1e |
msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
end: |
767e4c56 |
gc_free(&gc); |
86d8cd68 |
mbedtls_x509_crl_free(&crl); |
8a840d83 |
return retval; |
53f97e1e |
} |
31ea2ee4 |
|
86d8cd68 |
#endif /* #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ |