diff options
Diffstat (limited to 'openvpn/src/openvpn/ssl_verify_openssl.c')
-rw-r--r-- | openvpn/src/openvpn/ssl_verify_openssl.c | 622 |
1 files changed, 0 insertions, 622 deletions
diff --git a/openvpn/src/openvpn/ssl_verify_openssl.c b/openvpn/src/openvpn/ssl_verify_openssl.c deleted file mode 100644 index 658f5f33..00000000 --- a/openvpn/src/openvpn/ssl_verify_openssl.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * 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 OpenSSL implementation - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) - -#include "ssl_verify.h" -#include "ssl_verify_backend.h" -#include "ssl_openssl.h" -#include <openssl/x509v3.h> -#include <openssl/err.h> - -int -verify_callback (int preverify_ok, X509_STORE_CTX * ctx) -{ - 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); - - cert_hash_remember (session, ctx->error_depth, - x509_get_sha1_hash(ctx->current_cert, &gc)); - - /* did peer present cert which was signed by our root cert? */ - if (!preverify_ok) - { - /* get the X509 name */ - char *subject = x509_get_subject(ctx->current_cert, &gc); - - if (subject) - { - /* Remote site specified a certificate, but it's not correct */ - msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", - ctx->error_depth, - X509_verify_cert_error_string (ctx->error), - subject); - } - - ERR_clear_error(); - - session->verified = false; - goto cleanup; - } - - if (SUCCESS != verify_cert(session, ctx->current_cert, ctx->error_depth)) - goto cleanup; - - ret = 1; - -cleanup: - gc_free(&gc); - - return ret; -} - -#ifdef ENABLE_X509ALTUSERNAME -static -bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) -{ - bool retval = false; - X509_EXTENSION *pExt; - char *buf = 0; - int length = 0; - GENERAL_NAMES *extensions; - int nid = OBJ_txt2nid(fieldname); - - extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL); - if ( extensions ) - { - int numalts; - int i; - /* get amount of alternatives, - * RFC2459 claims there MUST be at least - * one, but we don't depend on it... - */ - - numalts = sk_GENERAL_NAME_num(extensions); - - /* loop through all alternatives */ - for (i=0; i<numalts; i++) - { - /* get a handle to alternative name number i */ - const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i ); - - switch (name->type) - { - case GEN_EMAIL: - ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5); - 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_ERRORS, "ASN1 ERROR: can not handle field type %i", - name->type); - break; - } - } - sk_GENERAL_NAME_free (extensions); - } - return retval; -} -#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). - */ -static result_t -extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, - int size) -{ - int lastpos = -1; - int tmp = -1; - X509_NAME_ENTRY *x509ne = 0; - ASN1_STRING *asn1 = 0; - unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - int nid = OBJ_txt2nid((char *)field_name); - - ASSERT (size > 0); - *out = '\0'; - do { - lastpos = tmp; - tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos); - } while (tmp > -1); - - /* Nothing found */ - if (lastpos == -1) - return FAILURE; - - x509ne = X509_NAME_get_entry(x509, lastpos); - if (!x509ne) - return FAILURE; - - asn1 = X509_NAME_ENTRY_get_data(x509ne); - if (!asn1) - return FAILURE; - tmp = ASN1_STRING_to_UTF8(&buf, asn1); - if (tmp <= 0) - return FAILURE; - - strncpynt(out, (char *)buf, size); - - { - const result_t ret = (strlen ((char *)buf) < size) ? SUCCESS: FAILURE; - OPENSSL_free (buf); - return ret; - } -} - -result_t -x509_get_username (char *common_name, int cn_len, - char * x509_username_field, X509 *peer_cert) -{ -#ifdef ENABLE_X509ALTUSERNAME - if (strncmp("ext:",x509_username_field,4) == 0) - { - if (!extract_x509_extension (peer_cert, x509_username_field+4, common_name, cn_len)) - return FAILURE; - } else -#endif - if (FAILURE == extract_x509_field_ssl (X509_get_subject_name (peer_cert), - x509_username_field, common_name, cn_len)) - return FAILURE; - - return SUCCESS; -} - -char * -x509_get_serial (openvpn_x509_cert_t *cert, struct gc_arena *gc) -{ - ASN1_INTEGER *asn1_i; - BIGNUM *bignum; - char *openssl_serial, *serial; - - asn1_i = X509_get_serialNumber(cert); - bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); - openssl_serial = BN_bn2dec(bignum); - - serial = string_alloc(openssl_serial, gc); - - BN_free(bignum); - OPENSSL_free(openssl_serial); - - return serial; -} - -unsigned char * -x509_get_sha1_hash (X509 *cert, struct gc_arena *gc) -{ - char *hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); - memcpy(hash, cert->sha1_hash, SHA_DIGEST_LENGTH); - return hash; -} - -char * -x509_get_subject (X509 *cert, struct gc_arena *gc) -{ - BIO *subject_bio = NULL; - BUF_MEM *subject_mem; - char *subject = NULL; - int maxlen = 0; - - /* - * Generate the subject string in OpenSSL proprietary format, - * when in --compat-names mode - */ - if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES)) - { - subject = gc_malloc (256, false, gc); - X509_NAME_oneline (X509_get_subject_name (cert), subject, 256); - subject[255] = '\0'; - return subject; - } - - subject_bio = BIO_new (BIO_s_mem ()); - if (subject_bio == NULL) - goto err; - - 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); - - if (BIO_eof (subject_bio)) - goto err; - - BIO_get_mem_ptr (subject_bio, &subject_mem); - - maxlen = subject_mem->length + 1; - subject = gc_malloc (maxlen, false, gc); - - memcpy (subject, subject_mem->data, maxlen); - subject[maxlen - 1] = '\0'; - -err: - if (subject_bio) - BIO_free (subject_bio); - - return subject; -} - - -#ifdef ENABLE_X509_TRACK - -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->nid = OBJ_txt2nid(name); - if (xt->nid != NID_undef) - { - xt->next = *ll_head; - *ll_head = xt; - } - else - msg(msglevel, "x509_track: no such attribute '%s'", name); -} - -/* worker method for setenv_x509_track */ -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); -} - -void -x509_setenv_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509) -{ - X509_NAME *x509_name = X509_get_subject_name (x509); - const char nullc = '\0'; - int i; - - while (xt) - { - if (depth == 0 || (xt->flags & XT_FULL_CHAIN)) - { - 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); - unsigned char *buf; - buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - if (ASN1_STRING_to_UTF8 (&buf, val) > 0) - { - 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; - } -} -#endif - -/* - * Save X509 fields to environment, using the naming convention: - * - * X509_{cert_depth}_{name}={value} - */ -void -x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *peer_cert) -{ - int i, n; - int fn_nid; - ASN1_OBJECT *fn; - ASN1_STRING *val; - X509_NAME_ENTRY *ent; - const char *objbuf; - unsigned char *buf; - 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; - buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - if (ASN1_STRING_to_UTF8 (&buf, val) <= 0) - 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 (es, name_expand, (char*)buf); - free (name_expand); - OPENSSL_free (buf); - } -} - -result_t -x509_verify_ns_cert_type(const openvpn_x509_cert_t *peer_cert, const int usage) -{ - if (usage == NS_CERT_CHECK_NONE) - return SUCCESS; - if (usage == NS_CERT_CHECK_CLIENT) - return ((peer_cert->ex_flags & EXFLAG_NSCERT) - && (peer_cert->ex_nscert & NS_SSL_CLIENT)) ? SUCCESS: FAILURE; - if (usage == NS_CERT_CHECK_SERVER) - return ((peer_cert->ex_flags & EXFLAG_NSCERT) - && (peer_cert->ex_nscert & NS_SSL_SERVER)) ? SUCCESS: FAILURE; - - return FAILURE; -} - -#if OPENSSL_VERSION_NUMBER >= 0x00907000L - -result_t -x509_verify_cert_ku (X509 *x509, const unsigned * const expected_ku, - int expected_len) -{ - ASN1_BIT_STRING *ku = NULL; - result_t fFound = FAILURE; - - if ((ku = (ASN1_BIT_STRING *) X509_get_ext_d2i (x509, NID_key_usage, NULL, - NULL)) == NULL) - { - msg (D_HANDSHAKE, "Certificate does not have key usage extension"); - } - else - { - unsigned nku = 0; - int i; - for (i = 0; i < 8; i++) - { - if (ASN1_BIT_STRING_get_bit (ku, i)) - nku |= 1 << (7 - i); - } - - /* - * Fixup if no LSB bits - */ - if ((nku & 0xff) == 0) - { - nku >>= 8; - } - - msg (D_HANDSHAKE, "Validating certificate key usage"); - for (i = 0; fFound != SUCCESS && 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; - } - } - } - - if (ku != NULL) - ASN1_BIT_STRING_free (ku); - - return fFound; -} - -result_t -x509_verify_cert_eku (X509 *x509, const char * const expected_oid) -{ - EXTENDED_KEY_USAGE *eku = NULL; - result_t fFound = FAILURE; - - if ((eku = (EXTENDED_KEY_USAGE *) X509_get_ext_d2i (x509, NID_ext_key_usage, - NULL, NULL)) == NULL) - { - msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); - } - else - { - int i; - - 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]; - - 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; - } - } - } - - if (eku != NULL) - sk_ASN1_OBJECT_pop_free (eku, ASN1_OBJECT_free); - - return fFound; -} - -result_t -x509_write_pem(FILE *peercert_file, X509 *peercert) -{ - if (PEM_write_X509(peercert_file, peercert) < 0) - { - msg (M_ERR, "Failed to write peer certificate in PEM format"); - return FAILURE; - } - return SUCCESS; -} - -#endif /* OPENSSL_VERSION_NUMBER */ - -/* - * check peer cert against CRL - */ -result_t -x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) -{ - X509_CRL *crl=NULL; - X509_REVOKED *revoked; - BIO *in=NULL; - int n,i; - result_t retval = FAILURE; - - in = BIO_new_file (crl_file, "r"); - - if (in == NULL) { - msg (M_ERR, "CRL: cannot read: %s", crl_file); - goto end; - } - crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL); - if (crl == NULL) { - msg (M_ERR, "CRL: cannot read CRL from file %s", crl_file); - goto end; - } - - if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(peer_cert)) != 0) { - msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of " - "certificate %s", crl_file, subject); - retval = SUCCESS; - goto end; - } - - n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); - for (i = 0; i < n; i++) { - revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); - if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(peer_cert)) == 0) { - msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject); - goto end; - } - } - - retval = SUCCESS; - msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); - -end: - BIO_free(in); - if (crl) - X509_CRL_free (crl); - - return retval; -} - -#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) */ |