diff options
author | Adriaan de Jong <dejong@fox-it.com> | 2011-07-01 14:15:11 +0200 |
---|---|---|
committer | David Sommerseth <davids@redhat.com> | 2011-10-22 14:35:16 +0200 |
commit | 53f97e1e9125aa9327c7ecf4a1b0b1a0c20cf2de (patch) | |
tree | db6651657f923cf0e7f2a6c34fd673ccb2e5c2fb /ssl_verify_polarssl.c | |
parent | 725336282db0c9f160d6ef577288e5a628959776 (diff) | |
download | openvpn-53f97e1e9125aa9327c7ecf4a1b0b1a0c20cf2de.tar.gz openvpn-53f97e1e9125aa9327c7ecf4a1b0b1a0c20cf2de.tar.xz openvpn-53f97e1e9125aa9327c7ecf4a1b0b1a0c20cf2de.zip |
Added PolarSSL support:
- Crypto library
- SSL library
- PKCS#11 support
For missing features, please see README.polarssl
Signed-off-by: Adriaan de Jong <dejong@fox-it.com>
Acked-by: James Yonan <james@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Signed-off-by: David Sommerseth <davids@redhat.com>
Diffstat (limited to 'ssl_verify_polarssl.c')
-rw-r--r-- | ssl_verify_polarssl.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/ssl_verify_polarssl.c b/ssl_verify_polarssl.c new file mode 100644 index 0000000..91a699a --- /dev/null +++ b/ssl_verify_polarssl.c @@ -0,0 +1,446 @@ +/* + * 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 PolarSSL backend + */ + +#include "ssl_verify.h" +#include <polarssl/sha1.h> + +#define MAX_SUBJECT_LENGTH 256 + +int +verify_callback (void *session_obj, x509_cert *cert, int cert_depth, + int preverify_ok) +{ + struct tls_session *session = (struct tls_session *) session_obj; + unsigned char *sha1_hash = NULL; + + ASSERT (cert); + ASSERT (session); + + session->verified = false; + + /* Remember certificate hash */ + sha1_hash = x509_get_sha1_hash(cert); + cert_hash_remember (session, cert_depth, sha1_hash); + x509_free_sha1_hash(sha1_hash); + + /* did peer present cert which was signed by our root cert? */ + if (!preverify_ok) + { + char subject[MAX_SUBJECT_LENGTH] = {0}; + + /* get the X509 name */ + if (x509parse_dn_gets( subject, MAX_SUBJECT_LENGTH, &cert->subject ) < 0) + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 " + "subject string from certificate", cert_depth); + else + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, %s", cert_depth, subject); + + return 1; + } + + /* + * verify_cert() returns 1 on success, 0 on failure. + * PolarSSL expects the opposite. + */ + return 0 == verify_cert(session, cert, cert_depth); +} + +#ifdef ENABLE_X509ALTUSERNAME +# warning "X509 alt user name not yet supported for PolarSSL" +#endif + +bool +x509_get_username (char *cn, int cn_len, + char *x509_username_field, x509_cert *cert) +{ + x509_name *name; + + ASSERT( cn != NULL ); + + name = &cert->subject; + + /* Find common name */ + while( name != NULL ) + { + if( memcmp( name->oid.p, OID_CN, OID_SIZE(OID_CN) ) == 0) + break; + + name = name->next; + } + + /* Not found, return an error if this is the peer's certificate */ + if( name == NULL ) + return 1; + + /* Found, extract CN */ + if (cn_len > name->val.len) + memcpy( cn, name->val.p, name->val.len ); + else + { + memcpy( cn, name->val.p, cn_len); + cn[cn_len-1] = '\0'; + } + + return 0; +} + +char * +x509_get_serial (x509_cert *cert) +{ + int ret = 0; + int i = 0; + char *buf = NULL; + size_t len = cert->serial.len * 3; + + buf = malloc(len); + ASSERT(buf); + + if(x509parse_serial_gets(buf, len-1, &cert->serial) < 0) + { + free(buf); + buf = NULL; + } + + return buf; +} + +void +x509_free_serial (char *serial) +{ + if (serial) + free(serial); +} + +unsigned char * +x509_get_sha1_hash (x509_cert *cert) +{ + unsigned char *sha1_hash = malloc(SHA_DIGEST_LENGTH); + sha1(cert->tbs.p, cert->tbs.len, sha1_hash); + return sha1_hash; +} + +void +x509_free_sha1_hash (unsigned char *hash) +{ + if (hash) + free(hash); +} + +char * +x509_get_subject(x509_cert *cert) +{ + char tmp_subject[MAX_SUBJECT_LENGTH] = {0}; + char *subject = NULL; + + int ret = 0; + + ret = x509parse_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); + if (ret > 0) + { + /* Allocate the required space for the subject */ + subject = malloc(ret + 1); + strncpy(subject, tmp_subject, ret+1); + } + + return subject; +} + +void +x509_free_subject (char *subject) +{ + if (subject) + free(subject); +} + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + */ +void +x509_setenv (struct env_set *es, int cert_depth, x509_cert_t *cert) +{ + int i, ret; + unsigned char c; + const x509_name *name; + char s[128]; + + name = &cert->subject; + + memset( s, 0, sizeof( s ) ); + + while( name != NULL ) + { + char name_expand[64+8]; + + if( name->oid.len == 2 && memcmp( name->oid.p, OID_X520, 2 ) == 0 ) + { + switch( name->oid.p[2] ) + { + case X520_COMMON_NAME: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_CN", + cert_depth); break; + + case X520_COUNTRY: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_C", + cert_depth); break; + + case X520_LOCALITY: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_L", + cert_depth); break; + + case X520_STATE: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_ST", + cert_depth); break; + + case X520_ORGANIZATION: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_O", + cert_depth); break; + + case X520_ORG_UNIT: + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_OU", + cert_depth); break; + + default: + openvpn_snprintf (name_expand, sizeof(name_expand), + "X509_%d_0x%02X", cert_depth, name->oid.p[2]); + break; + } + } + else if( name->oid.len == 8 && memcmp( name->oid.p, OID_PKCS9, 8 ) == 0 ) + { + switch( name->oid.p[8] ) + { + case PKCS9_EMAIL: + openvpn_snprintf (name_expand, sizeof(name_expand), + "X509_%d_emailAddress", cert_depth); break; + + default: + openvpn_snprintf (name_expand, sizeof(name_expand), + "X509_%d_0x%02X", cert_depth, name->oid.p[8]); + break; + } + } + 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 (es, name_expand, (char*)s); + + name = name->next; + } +} + +bool +x509_verify_ns_cert_type(const x509_cert *cert, const int usage) +{ + if (usage == NS_CERT_CHECK_NONE) + return true; + if (usage == NS_CERT_CHECK_CLIENT) + return ((cert->ext_types & EXT_NS_CERT_TYPE) + && (cert->ns_cert_type & NS_CERT_TYPE_SSL_CLIENT)); + if (usage == NS_CERT_CHECK_SERVER) + return ((cert->ext_types & EXT_NS_CERT_TYPE) + && (cert->ns_cert_type & NS_CERT_TYPE_SSL_SERVER)); + + return false; +} + +bool +x509_verify_cert_ku (x509_cert *cert, const unsigned * const expected_ku, + int expected_len) +{ + bool fFound = false; + + if(!(cert->ext_types & 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;!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 = true; + } + } + } + } + return fFound; +} + +bool +x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid) +{ + bool fFound = false; + + if (!(cert->ext_types & EXT_EXTENDED_KEY_USAGE)) + { + msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); + } + else + { + x509_sequence *oid_seq = &(cert->ext_key_usage); + + msg (D_HANDSHAKE, "Validating certificate extended key usage"); + while (oid_seq != NULL) + { + x509_buf *oid = &oid_seq->buf; + char oid_num_str[1024]; + const char *oid_str; + + oid_str = x509_oid_get_description(oid); + if (oid_str != NULL) + { + msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", + oid_str, expected_oid); + if (!strcmp (expected_oid, oid_str)) + { + fFound = true; + break; + } + } + + if (0 == x509_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 = true; + break; + } + } + oid_seq = oid_seq->next; + } + } + + return fFound; +} + +const char * +x509_write_cert(x509_cert *peercert, const char *tmp_dir, struct gc_arena *gc) +{ + FILE *peercert_file; + const char *peercert_filename=""; + + if(!tmp_dir) + return NULL; + + /* create tmp file to store peer cert */ + peercert_filename = create_temp_file (tmp_dir, "pcf", gc); + + /* write peer-cert in tmp-file */ + peercert_file = fopen(peercert_filename, "w+"); + if(!peercert_file) + { + msg (M_ERR, "Failed to open temporary file : %s", peercert_filename); + return NULL; + } + +// if(PEM_write_X509(peercert_file,peercert)<0) +// { + msg (M_ERR, "PolarSSL does not support writing peer certificate in PEM format"); + fclose(peercert_file); + return NULL; +// } + + fclose(peercert_file); + return peercert_filename; +} + +/* + * check peer cert against CRL + */ +bool +x509_verify_crl(const char *crl_file, x509_cert *cert, const char *subject) +{ + int retval = 0; + x509_crl crl = {0}; + + if (x509parse_crlfile(&crl, crl_file) != 0) + { + msg (M_ERR, "CRL: cannot read CRL from file %s", crl_file); + goto end; + } + + 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); + retval = 1; + goto end; + } + + if (0 != x509parse_revoked(cert, &crl)) + { + msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED", subject); + goto end; + } + + retval = 1; + msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); + +end: + x509_crl_free(&crl); + + if (!retval) + return true; + + return false; +} |