9a160b79 |
/*
* 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> |
9a160b79 |
*
* 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. |
9a160b79 |
*/
/**
* @file Control Channel Verification Module OpenSSL implementation
*/
|
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_OPENSSL) |
31ea2ee4 |
|
67bdede6 |
#include "ssl_verify_openssl.h"
#include "error.h"
#include "ssl_openssl.h" |
9a160b79 |
#include "ssl_verify.h"
#include "ssl_verify_backend.h" |
f05665df |
#include "openssl_compat.h" |
67bdede6 |
|
19874982 |
#include <openssl/bn.h> |
be960aad |
#include <openssl/err.h> |
19874982 |
#include <openssl/x509v3.h> |
0a67e462 |
int |
81d882d5 |
verify_callback(int preverify_ok, X509_STORE_CTX *ctx) |
0a67e462 |
{ |
81d882d5 |
int ret = 0;
struct tls_session *session;
SSL *ssl;
struct gc_arena gc = gc_new();
/* get the tls_session pointer */
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
ASSERT(ssl);
session = (struct tls_session *) SSL_get_ex_data(ssl, mydata_index);
ASSERT(session);
|
88046ad9 |
X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
struct buffer cert_hash = x509_get_sha256_fingerprint(current_cert, &gc);
cert_hash_remember(session, X509_STORE_CTX_get_error_depth(ctx), &cert_hash); |
81d882d5 |
/* did peer present cert which was signed by our root cert? */
if (!preverify_ok) |
0a67e462 |
{ |
81d882d5 |
/* get the X509 name */ |
88046ad9 |
char *subject = x509_get_subject(current_cert, &gc); |
0a67e462 |
|
81d882d5 |
if (!subject)
{
subject = "(Failed to retrieve certificate subject)";
} |
0a67e462 |
|
81d882d5 |
/* Log and ignore missing CRL errors */ |
88046ad9 |
if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_UNABLE_TO_GET_CRL) |
81d882d5 |
{
msg(D_TLS_DEBUG_LOW, "VERIFY WARNING: depth=%d, %s: %s", |
88046ad9 |
X509_STORE_CTX_get_error_depth(ctx),
X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), |
81d882d5 |
subject);
ret = 1;
goto cleanup;
}
/* Remote site specified a certificate, but it's not correct */
msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", |
88046ad9 |
X509_STORE_CTX_get_error_depth(ctx),
X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), |
81d882d5 |
subject);
ERR_clear_error();
session->verified = false;
goto cleanup; |
0a67e462 |
}
|
88046ad9 |
if (SUCCESS != verify_cert(session, current_cert, X509_STORE_CTX_get_error_depth(ctx))) |
81d882d5 |
{
goto cleanup;
} |
75b49e40 |
|
81d882d5 |
ret = 1; |
75b49e40 |
cleanup: |
81d882d5 |
gc_free(&gc); |
00b973f8 |
|
81d882d5 |
return ret; |
0a67e462 |
} |
971790da |
|
dd4cdb9e |
#ifdef ENABLE_X509ALTUSERNAME |
d2a19185 |
bool x509_username_field_ext_supported(const char *fieldname)
{
int nid = OBJ_txt2nid(fieldname);
return nid == NID_subject_alt_name || nid == NID_issuer_alt_name;
}
|
dd4cdb9e |
static |
81d882d5 |
bool
extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) |
dd4cdb9e |
{ |
81d882d5 |
bool retval = false;
char *buf = 0; |
dd4cdb9e |
|
d2a19185 |
if (!x509_username_field_ext_supported(fieldname))
{
msg(D_TLS_ERRORS, |
0402c7fa |
"ERROR: --x509-username-field 'ext:%s' not supported", fieldname); |
d2a19185 |
return false;
}
int nid = OBJ_txt2nid(fieldname);
GENERAL_NAMES *extensions = X509_get_ext_d2i(cert, nid, NULL, NULL); |
81d882d5 |
if (extensions) |
dd4cdb9e |
{ |
81d882d5 |
int numalts;
int i;
/* get amount of alternatives,
* RFC2459 claims there MUST be at least
* one, but we don't depend on it...
*/ |
dd4cdb9e |
|
81d882d5 |
numalts = sk_GENERAL_NAME_num(extensions); |
dd4cdb9e |
|
81d882d5 |
/* loop through all alternatives */
for (i = 0; i<numalts; i++) |
dd4cdb9e |
{ |
81d882d5 |
/* get a handle to alternative name number i */
const GENERAL_NAME *name = sk_GENERAL_NAME_value(extensions, i ); |
dd4cdb9e |
|
81d882d5 |
switch (name->type) |
dd4cdb9e |
{ |
81d882d5 |
case GEN_EMAIL: |
cb4e35ec |
if (ASN1_STRING_to_UTF8((unsigned char **)&buf, name->d.ia5) < 0)
{
continue;
} |
81d882d5 |
if (strlen(buf) != name->d.ia5->length)
{
msg(D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero");
OPENSSL_free(buf);
}
else
{
strncpynt(out, buf, size);
OPENSSL_free(buf);
retval = true;
}
break;
default:
msg(D_TLS_DEBUG, "%s: ignoring general name field type %i",
__func__, name->type);
break; |
dd4cdb9e |
} |
81d882d5 |
} |
2d032c7f |
GENERAL_NAMES_free(extensions); |
dd4cdb9e |
} |
81d882d5 |
return retval; |
dd4cdb9e |
}
#endif /* ENABLE_X509ALTUSERNAME */
/*
* Extract a field from an X509 subject name.
*
* Example:
*
* /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net
*
* The common name is 'Test-CA'
*
* Return true on success, false on error (insufficient buffer size in 'out'
* to contain result is grounds for error).
*/ |
8a840d83 |
static result_t |
81d882d5 |
extract_x509_field_ssl(X509_NAME *x509, const char *field_name, char *out,
int size) |
dd4cdb9e |
{ |
81d882d5 |
int lastpos = -1;
int tmp = -1; |
280150a0 |
X509_NAME_ENTRY *x509ne = NULL;
ASN1_STRING *asn1 = NULL; |
81d882d5 |
unsigned char *buf = NULL; |
69311687 |
ASN1_OBJECT *field_name_obj = OBJ_txt2obj(field_name, 0);
if (field_name_obj == NULL)
{
msg(D_TLS_ERRORS, "Invalid X509 attribute name '%s'", field_name);
return FAILURE;
} |
81d882d5 |
ASSERT(size > 0);
*out = '\0'; |
4cd4899e |
do
{ |
81d882d5 |
lastpos = tmp; |
69311687 |
tmp = X509_NAME_get_index_by_OBJ(x509, field_name_obj, lastpos); |
81d882d5 |
} while (tmp > -1);
|
69311687 |
ASN1_OBJECT_free(field_name_obj);
|
81d882d5 |
/* Nothing found */
if (lastpos == -1)
{
return FAILURE;
} |
dd4cdb9e |
|
81d882d5 |
x509ne = X509_NAME_get_entry(x509, lastpos);
if (!x509ne)
{
return FAILURE;
} |
dd4cdb9e |
|
81d882d5 |
asn1 = X509_NAME_ENTRY_get_data(x509ne);
if (!asn1)
{
return FAILURE;
} |
2d032c7f |
if (ASN1_STRING_to_UTF8(&buf, asn1) < 0) |
81d882d5 |
{
return FAILURE;
} |
dd4cdb9e |
|
81d882d5 |
strncpynt(out, (char *)buf, size); |
dd4cdb9e |
|
81d882d5 |
{
const result_t ret = (strlen((char *)buf) < size) ? SUCCESS : FAILURE;
OPENSSL_free(buf);
return ret;
} |
dd4cdb9e |
}
|
8a840d83 |
result_t |
81d882d5 |
backend_x509_get_username(char *common_name, int cn_len,
char *x509_username_field, X509 *peer_cert) |
dd4cdb9e |
{
#ifdef ENABLE_X509ALTUSERNAME |
81d882d5 |
if (strncmp("ext:",x509_username_field,4) == 0) |
dd4cdb9e |
{ |
81d882d5 |
if (!extract_x509_extension(peer_cert, x509_username_field+4, common_name, cn_len))
{
return FAILURE;
}
}
else |
dd4cdb9e |
#endif |
81d882d5 |
if (FAILURE == extract_x509_field_ssl(X509_get_subject_name(peer_cert),
x509_username_field, common_name, cn_len))
{
return FAILURE;
} |
dd4cdb9e |
|
81d882d5 |
return SUCCESS; |
dd4cdb9e |
} |
fe100528 |
char * |
81d882d5 |
backend_x509_get_serial(openvpn_x509_cert_t *cert, struct gc_arena *gc) |
fe100528 |
{ |
81d882d5 |
ASN1_INTEGER *asn1_i;
BIGNUM *bignum;
char *openssl_serial, *serial; |
fe100528 |
|
81d882d5 |
asn1_i = X509_get_serialNumber(cert);
bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
openssl_serial = BN_bn2dec(bignum); |
025f30d7 |
|
81d882d5 |
serial = string_alloc(openssl_serial, gc); |
fe100528 |
|
81d882d5 |
BN_free(bignum);
OPENSSL_free(openssl_serial); |
fe100528 |
|
81d882d5 |
return serial; |
fe100528 |
}
|
f80a52b0 |
char * |
81d882d5 |
backend_x509_get_serial_hex(openvpn_x509_cert_t *cert, struct gc_arena *gc) |
f80a52b0 |
{ |
81d882d5 |
const ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert); |
f80a52b0 |
|
81d882d5 |
return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc); |
f80a52b0 |
}
|
af1e4d26 |
struct buffer |
81d882d5 |
x509_get_sha1_fingerprint(X509 *cert, struct gc_arena *gc) |
fceecbab |
{ |
17d1ab90 |
const EVP_MD *sha1 = EVP_sha1();
struct buffer hash = alloc_buf_gc(EVP_MD_size(sha1), gc);
X509_digest(cert, EVP_sha1(), BPTR(&hash), NULL);
ASSERT(buf_inc_len(&hash, EVP_MD_size(sha1))); |
81d882d5 |
return hash; |
af1e4d26 |
}
struct buffer |
81d882d5 |
x509_get_sha256_fingerprint(X509 *cert, struct gc_arena *gc) |
af1e4d26 |
{ |
17d1ab90 |
const EVP_MD *sha256 = EVP_sha256();
struct buffer hash = alloc_buf_gc(EVP_MD_size(sha256), gc); |
81d882d5 |
X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL); |
17d1ab90 |
ASSERT(buf_inc_len(&hash, EVP_MD_size(sha256))); |
81d882d5 |
return hash; |
fceecbab |
}
|
3cb348e4 |
char * |
81d882d5 |
x509_get_subject(X509 *cert, struct gc_arena *gc) |
5e86fd93 |
{ |
81d882d5 |
BIO *subject_bio = NULL;
BUF_MEM *subject_mem;
char *subject = NULL;
/*
* Generate the subject string in OpenSSL proprietary format,
* when in --compat-names mode
*/
if (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NAMES)) |
e7412ca3 |
{ |
81d882d5 |
subject = gc_malloc(256, false, gc);
X509_NAME_oneline(X509_get_subject_name(cert), subject, 256);
subject[255] = '\0';
return subject; |
e7412ca3 |
}
|
81d882d5 |
subject_bio = BIO_new(BIO_s_mem());
if (subject_bio == NULL)
{
goto err;
} |
5e86fd93 |
|
81d882d5 |
X509_NAME_print_ex(subject_bio, X509_get_subject_name(cert),
0, XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN
|ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_CTRL); |
5e86fd93 |
|
81d882d5 |
if (BIO_eof(subject_bio))
{
goto err;
} |
5e86fd93 |
|
81d882d5 |
BIO_get_mem_ptr(subject_bio, &subject_mem); |
00b973f8 |
|
3fbc9d2b |
subject = gc_malloc(subject_mem->length + 1, false, gc); |
5e86fd93 |
|
3fbc9d2b |
memcpy(subject, subject_mem->data, subject_mem->length);
subject[subject_mem->length] = '\0'; |
5e86fd93 |
|
1951b415 |
err: |
81d882d5 |
if (subject_bio)
{
BIO_free(subject_bio);
} |
1951b415 |
|
81d882d5 |
return subject; |
3cb348e4 |
}
|
f6608a15 |
/*
* x509-track implementation -- save X509 fields to environment,
* using the naming convention:
*
* X509_{cert_depth}_{name}={value}
*
* This function differs from x509_setenv below in the following ways:
*
* (1) Only explicitly named attributes in xt are saved, per usage
* of "x509-track" program options.
* (2) Only the level 0 cert info is saved unless the XT_FULL_CHAIN
* flag is set in xt->flags (corresponds with prepending a '+'
* to the name when specified by "x509-track" program option).
* (3) This function supports both X509 subject name fields as
* well as X509 V3 extensions.
* (4) This function can return the SHA1 fingerprint of a cert, e.g.
* x509-track "+SHA1"
* will return the SHA1 fingerprint for each certificate in the
* peer chain.
*/
|
72533628 |
void |
81d882d5 |
x509_track_add(const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc) |
72533628 |
{ |
81d882d5 |
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->nid = OBJ_txt2nid(name);
if (xt->nid != NID_undef) |
72533628 |
{ |
81d882d5 |
xt->next = *ll_head;
*ll_head = xt; |
72533628 |
} |
81d882d5 |
else |
72533628 |
{ |
81d882d5 |
msg(msglevel, "x509_track: no such attribute '%s'", name); |
72533628 |
}
} |
fe100528 |
/* worker method for setenv_x509_track */
static void |
81d882d5 |
do_setenv_x509(struct env_set *es, const char *name, char *value, int depth) |
fe100528 |
{ |
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); |
fe100528 |
}
void |
81d882d5 |
x509_setenv_track(const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509) |
fe100528 |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
X509_NAME *x509_name = X509_get_subject_name(x509);
const char nullc = '\0';
while (xt)
{
if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
{
switch (xt->nid)
{
case NID_sha1:
case NID_sha256:
{
struct buffer fp_buf;
char *fp_str = NULL;
if (xt->nid == NID_sha1)
{
fp_buf = x509_get_sha1_fingerprint(x509, &gc);
}
else
{
fp_buf = x509_get_sha256_fingerprint(x509, &gc);
}
fp_str = format_hex_ex(BPTR(&fp_buf), BLEN(&fp_buf), 0,
1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, fp_str, depth);
}
break;
default:
{
int i = X509_NAME_get_index_by_NID(x509_name, xt->nid, -1);
if (i >= 0)
{
X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
if (ent)
{
ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent); |
039a89c3 |
unsigned char *buf = NULL; |
2d032c7f |
if (ASN1_STRING_to_UTF8(&buf, val) >= 0) |
81d882d5 |
{
do_setenv_x509(es, xt->name, (char *)buf, depth);
OPENSSL_free(buf);
}
}
}
else
{
i = X509_get_ext_by_NID(x509, xt->nid, -1);
if (i >= 0)
{
X509_EXTENSION *ext = X509_get_ext(x509, i);
if (ext)
{
BIO *bio = BIO_new(BIO_s_mem());
if (bio)
{
if (X509V3_EXT_print(bio, ext, 0, 0))
{
if (BIO_write(bio, &nullc, 1) == 1)
{
char *str;
BIO_get_mem_data(bio, &str);
do_setenv_x509(es, xt->name, str, depth);
}
}
BIO_free(bio);
}
}
}
}
}
}
}
xt = xt->next;
}
gc_free(&gc); |
fe100528 |
}
/*
* 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, openvpn_x509_cert_t *peer_cert) |
fe100528 |
{ |
81d882d5 |
int i, n;
int fn_nid;
ASN1_OBJECT *fn;
ASN1_STRING *val;
X509_NAME_ENTRY *ent;
const char *objbuf; |
039a89c3 |
unsigned char *buf = NULL; |
81d882d5 |
char *name_expand;
size_t name_expand_size;
X509_NAME *x509 = X509_get_subject_name(peer_cert);
n = X509_NAME_entry_count(x509);
for (i = 0; i < n; ++i)
{
ent = X509_NAME_get_entry(x509, i);
if (!ent)
{
continue;
}
fn = X509_NAME_ENTRY_get_object(ent);
if (!fn)
{
continue;
}
val = X509_NAME_ENTRY_get_data(ent);
if (!val)
{
continue;
}
fn_nid = OBJ_obj2nid(fn);
if (fn_nid == NID_undef)
{
continue;
}
objbuf = OBJ_nid2sn(fn_nid);
if (!objbuf)
{
continue;
} |
2d032c7f |
if (ASN1_STRING_to_UTF8(&buf, val) < 0) |
81d882d5 |
{
continue;
}
name_expand_size = 64 + strlen(objbuf);
name_expand = (char *) malloc(name_expand_size);
check_malloc_return(name_expand);
openvpn_snprintf(name_expand, name_expand_size, "X509_%d_%s", cert_depth,
objbuf);
string_mod(name_expand, CC_PRINT, CC_CRLF, '_');
string_mod((char *)buf, CC_PRINT, CC_CRLF, '_');
setenv_str_incr(es, name_expand, (char *)buf);
free(name_expand);
OPENSSL_free(buf); |
fe100528 |
}
} |
06d22777 |
|
8a840d83 |
result_t |
17d1ab90 |
x509_verify_ns_cert_type(openvpn_x509_cert_t *peer_cert, const int usage) |
06d22777 |
{ |
81d882d5 |
if (usage == NS_CERT_CHECK_NONE)
{
return SUCCESS;
}
if (usage == NS_CERT_CHECK_CLIENT)
{ |
17d1ab90 |
/*
* Unfortunately, X509_check_purpose() does some weird thing that
* prevent it to take a const argument
*/
result_t result = X509_check_purpose(peer_cert, X509_PURPOSE_SSL_CLIENT, 0) ?
SUCCESS : FAILURE;
/*
* old versions of OpenSSL allow us to make the less strict check we used to
* do. If this less strict check pass, warn user that this might not be the
* case when its distribution will update to OpenSSL 1.1
*/
if (result == FAILURE)
{
ASN1_BIT_STRING *ns;
ns = X509_get_ext_d2i(peer_cert, NID_netscape_cert_type, NULL, NULL);
result = (ns && ns->length > 0 && (ns->data[0] & NS_SSL_CLIENT)) ? SUCCESS : FAILURE;
if (result == SUCCESS)
{
msg(M_WARN, "X509: Certificate is a client certificate yet it's purpose "
"cannot be verified (check may fail in the future)");
}
ASN1_BIT_STRING_free(ns);
}
return result; |
81d882d5 |
}
if (usage == NS_CERT_CHECK_SERVER)
{ |
17d1ab90 |
/*
* Unfortunately, X509_check_purpose() does some weird thing that
* prevent it to take a const argument
*/
result_t result = X509_check_purpose(peer_cert, X509_PURPOSE_SSL_SERVER, 0) ?
SUCCESS : FAILURE;
/*
* old versions of OpenSSL allow us to make the less strict check we used to
* do. If this less strict check pass, warn user that this might not be the
* case when its distribution will update to OpenSSL 1.1
*/
if (result == FAILURE)
{
ASN1_BIT_STRING *ns;
ns = X509_get_ext_d2i(peer_cert, NID_netscape_cert_type, NULL, NULL);
result = (ns && ns->length > 0 && (ns->data[0] & NS_SSL_SERVER)) ? SUCCESS : FAILURE;
if (result == SUCCESS)
{
msg(M_WARN, "X509: Certificate is a server certificate yet it's purpose "
"cannot be verified (check may fail in the future)");
}
ASN1_BIT_STRING_free(ns);
}
return result; |
81d882d5 |
}
return FAILURE; |
06d22777 |
} |
876752ae |
|
8a840d83 |
result_t |
81d882d5 |
x509_verify_cert_ku(X509 *x509, const unsigned *const expected_ku,
int expected_len) |
876752ae |
{ |
92a5b9fb |
ASN1_BIT_STRING *ku = X509_get_ext_d2i(x509, NID_key_usage, NULL, NULL); |
81d882d5 |
|
92a5b9fb |
if (ku == NULL) |
81d882d5 |
{ |
92a5b9fb |
msg(D_TLS_ERRORS, "Certificate does not have key usage extension");
return FAILURE; |
81d882d5 |
} |
92a5b9fb |
if (expected_ku[0] == OPENVPN_KU_REQUIRED) |
81d882d5 |
{ |
92a5b9fb |
/* Extension required, value checked by TLS library */ |
7b94d3bb |
ASN1_BIT_STRING_free(ku); |
92a5b9fb |
return SUCCESS;
} |
81d882d5 |
|
92a5b9fb |
unsigned nku = 0;
for (size_t i = 0; i < 8; i++)
{
if (ASN1_BIT_STRING_get_bit(ku, i)) |
81d882d5 |
{ |
92a5b9fb |
nku |= 1 << (7 - i); |
81d882d5 |
} |
92a5b9fb |
} |
81d882d5 |
|
92a5b9fb |
/*
* Fixup if no LSB bits
*/
if ((nku & 0xff) == 0)
{
nku >>= 8;
} |
81d882d5 |
|
92a5b9fb |
msg(D_HANDSHAKE, "Validating certificate key usage");
result_t fFound = FAILURE;
for (size_t i = 0; fFound != SUCCESS && i < expected_len; i++)
{
if (expected_ku[i] != 0 && (nku & expected_ku[i]) == expected_ku[i])
{
fFound = SUCCESS; |
81d882d5 |
}
}
|
92a5b9fb |
if (fFound != SUCCESS) |
81d882d5 |
{ |
92a5b9fb |
msg(D_TLS_ERRORS,
"ERROR: Certificate has key usage %04x, expected one of:", nku);
for (size_t i = 0; i < expected_len && expected_ku[i]; i++)
{
msg(D_TLS_ERRORS, " * %04x", expected_ku[i]);
} |
81d882d5 |
}
|
92a5b9fb |
ASN1_BIT_STRING_free(ku);
|
81d882d5 |
return fFound; |
876752ae |
}
|
8a840d83 |
result_t |
81d882d5 |
x509_verify_cert_eku(X509 *x509, const char *const expected_oid) |
587f419b |
{ |
81d882d5 |
EXTENDED_KEY_USAGE *eku = NULL;
result_t fFound = FAILURE; |
587f419b |
|
81d882d5 |
if ((eku = (EXTENDED_KEY_USAGE *) X509_get_ext_d2i(x509, NID_ext_key_usage,
NULL, NULL)) == NULL) |
587f419b |
{ |
81d882d5 |
msg(D_HANDSHAKE, "Certificate does not have extended key usage extension"); |
587f419b |
} |
81d882d5 |
else |
587f419b |
{ |
81d882d5 |
int i; |
587f419b |
|
81d882d5 |
msg(D_HANDSHAKE, "Validating certificate extended key usage");
for (i = 0; SUCCESS != fFound && i < sk_ASN1_OBJECT_num(eku); i++)
{
ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(eku, i);
char szOid[1024]; |
587f419b |
|
81d882d5 |
if (SUCCESS != fFound && OBJ_obj2txt(szOid, sizeof(szOid), oid, 0) != -1)
{
msg(D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s",
szOid, expected_oid);
if (!strcmp(expected_oid, szOid))
{
fFound = SUCCESS;
}
}
if (SUCCESS != fFound && OBJ_obj2txt(szOid, sizeof(szOid), oid, 1) != -1)
{
msg(D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s",
szOid, expected_oid);
if (!strcmp(expected_oid, szOid))
{
fFound = SUCCESS;
}
}
} |
587f419b |
}
|
81d882d5 |
if (eku != NULL)
{
sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
} |
587f419b |
|
81d882d5 |
return fFound; |
587f419b |
}
|
8a840d83 |
result_t |
8bb72fbc |
x509_write_pem(FILE *peercert_file, X509 *peercert) |
3e44ea55 |
{ |
81d882d5 |
if (PEM_write_X509(peercert_file, peercert) < 0) |
3e44ea55 |
{ |
fb515a83 |
msg(M_NONFATAL, "Failed to write peer certificate in PEM format"); |
81d882d5 |
return FAILURE; |
3e44ea55 |
} |
81d882d5 |
return SUCCESS; |
3e44ea55 |
}
|
160504a2 |
bool
tls_verify_crl_missing(const struct tls_options *opt) |
83c49a3e |
{ |
81d882d5 |
if (!opt->crl_file || (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)) |
160504a2 |
{ |
81d882d5 |
return false; |
83c49a3e |
}
|
81d882d5 |
X509_STORE *store = SSL_CTX_get_cert_store(opt->ssl_ctx.ctx);
if (!store)
{
crypto_msg(M_FATAL, "Cannot get certificate store");
} |
83c49a3e |
|
f05665df |
STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects(store);
for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) |
160504a2 |
{ |
f05665df |
X509_OBJECT *obj = sk_X509_OBJECT_value(objs, i); |
81d882d5 |
ASSERT(obj); |
47191f49 |
if (X509_OBJECT_get_type(obj) == X509_LU_CRL) |
81d882d5 |
{
return false;
} |
160504a2 |
} |
81d882d5 |
return true; |
83c49a3e |
} |
31ea2ee4 |
|
c7ca9133 |
#endif /* defined(ENABLE_CRYPTO_OPENSSL) */ |