src/openvpn/ssl_verify_openssl.c
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) */