Permalink
Browse files

Refactored: split verify_callback into two parts

 - One part is the actual callback, and is OpenSSL-specific
 - One part, verify_cert(), is called by the callback to process the actual
   verification
  • Loading branch information...
1 parent 77aa7ea commit 950b2182d8846d794ca1339b8d20ad7532801c5f @andj committed Jun 30, 2011
Showing with 105 additions and 72 deletions.
  1. +47 −72 ssl.c
  2. +15 −0 ssl_verify_backend.h
  3. +41 −0 ssl_verify_openssl.c
  4. +2 −0 ssl_verify_openssl.h
View
119 ssl.c
@@ -654,23 +654,14 @@ string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsig
*/
const char *
-get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
+write_peer_cert(X509 *peercert, const char *tmp_dir, struct gc_arena *gc)
{
- X509 *peercert;
FILE *peercert_file;
const char *peercert_filename="";
if(!tmp_dir)
return NULL;
- /* get peer cert */
- peercert = X509_STORE_CTX_get_current_cert(ctx);
- if(!peercert)
- {
- msg (M_ERR, "Unable to get peer certificate from current context");
- return NULL;
- }
-
/* create tmp file to store peer cert */
peercert_filename = create_temp_file (tmp_dir, "pcf", gc);
@@ -695,43 +686,36 @@ get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
char * x509_username_field; /* GLOBAL */
int
-verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
+verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth)
{
char *subject = NULL;
char envname[64];
- char common_name[TLS_USERNAME_LEN];
- SSL *ssl;
- struct tls_session *session;
+ char common_name[TLS_USERNAME_LEN] = {0};
const struct tls_options *opt;
- const int max_depth = MAX_CERT_DEPTH;
struct argv argv = argv_new ();
char *serial = NULL;
- /* 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);
opt = session->opt;
ASSERT (opt);
session->verified = false;
/* get the X509 name */
- subject = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), NULL, 0);
+ subject = X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0);
if (!subject)
{
- msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 subject string from certificate", ctx->error_depth);
- goto err;
+ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 "
+ "subject string from certificate", cert_depth);
+ goto err;
}
/* Save X509 fields in environment */
#ifdef ENABLE_X509_TRACK
if (opt->x509_track)
- setenv_x509_track (opt->x509_track, opt->es, ctx->error_depth, ctx->current_cert);
+ setenv_x509_track (opt->x509_track, opt->es, cert_depth, cert);
else
#endif
- setenv_x509 (opt->es, ctx->error_depth, X509_get_subject_name (ctx->current_cert));
+ setenv_x509 (opt->es, cert_depth, X509_get_subject_name (cert));
/* enforce character class restrictions in X509 name */
string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags);
@@ -752,11 +736,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
}
} else
#endif
- if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), x509_username_field, common_name, sizeof(common_name)))
+ if (!extract_x509_field_ssl (X509_get_subject_name (cert), x509_username_field, common_name, sizeof(common_name)))
{
- if (!ctx->error_depth)
+ if (!cert_depth)
{
- msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 subject string ('%s') -- note that the username length is limited to %d characters",
+ msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 "
+ "subject string ('%s') -- note that the username length is "
+ "limited to %d characters",
x509_username_field,
subject,
TLS_USERNAME_LEN);
@@ -767,66 +753,55 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags);
- cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash);
-
#if 0 /* print some debugging info */
{
struct gc_arena gc = gc_new ();
- msg (M_INFO, "LOCAL OPT[%d]: %s", ctx->error_depth, opt->local_options);
- msg (M_INFO, "X509[%d]: %s", ctx->error_depth, subject);
- msg (M_INFO, "SHA1[%d]: %s", ctx->error_depth, format_hex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
+ msg (M_INFO, "LOCAL OPT[%d]: %s", cert_depth, opt->local_options);
+ msg (M_INFO, "X509[%d]: %s", cert_depth, subject);
+ msg (M_INFO, "SHA1[%d]: %s", cert_depth, format_hex(cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
gc_free (&gc);
}
#endif
- /* did peer present cert which was signed our root cert? */
- if (!preverify_ok)
- {
- /* 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);
- goto err; /* Reject connection */
- }
-
/* warn if cert chain is too deep */
- if (ctx->error_depth >= max_depth)
+ if (cert_depth >= MAX_CERT_DEPTH)
{
- msg (D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", ctx->error_depth, max_depth);
+ msg (D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", cert_depth, MAX_CERT_DEPTH);
goto err; /* Reject connection */
}
/* verify level 1 cert, i.e. the CA that signed our leaf cert */
- if (ctx->error_depth == 1 && opt->verify_hash)
+ if (cert_depth == 1 && opt->verify_hash)
{
- if (memcmp (ctx->current_cert->sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
+ if (memcmp (cert->sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
{
msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
goto err;
}
}
/* save common name in session object */
- if (ctx->error_depth == 0)
+ if (cert_depth == 0)
set_common_name (session, common_name);
/* export subject name string as environmental variable */
- session->verify_maxlevel = max_int (session->verify_maxlevel, ctx->error_depth);
- openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth);
+ session->verify_maxlevel = max_int (session->verify_maxlevel, cert_depth);
+ openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", cert_depth);
setenv_str (opt->es, envname, subject);
#ifdef ENABLE_EUREPHIA
/* export X509 cert SHA1 fingerprint */
{
struct gc_arena gc = gc_new ();
- openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth);
+ openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth);
setenv_str (opt->es, envname,
- format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc));
+ format_hex_ex(cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc));
gc_free(&gc);
}
#endif
#if 0
/* export common name string as environmental variable */
- openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", ctx->error_depth);
+ openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", cert_depth);
setenv_str (opt->es, envname, common_name);
#endif
@@ -835,10 +810,10 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
{
ASN1_INTEGER *asn1_i;
BIGNUM *bignum;
- asn1_i = X509_get_serialNumber(ctx->current_cert);
+ asn1_i = X509_get_serialNumber(cert);
bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
serial = BN_bn2dec(bignum);
- openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth);
+ openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", cert_depth);
setenv_str (opt->es, envname, serial);
BN_free(bignum);
}
@@ -847,9 +822,9 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
setenv_untrusted (session);
/* verify certificate nsCertType */
- if (opt->ns_cert_type && ctx->error_depth == 0)
+ if (opt->ns_cert_type && cert_depth == 0)
{
- if (verify_nsCertType (ctx->current_cert, opt->ns_cert_type))
+ if (verify_nsCertType (cert, opt->ns_cert_type))
{
msg (D_HANDSHAKE, "VERIFY OK: nsCertType=%s",
print_nsCertType (opt->ns_cert_type));
@@ -865,9 +840,9 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
/* verify certificate ku */
- if (opt->remote_cert_ku[0] != 0 && ctx->error_depth == 0)
+ if (opt->remote_cert_ku[0] != 0 && cert_depth == 0)
{
- if (verify_cert_ku (ctx->current_cert, opt->remote_cert_ku, MAX_PARMS))
+ if (verify_cert_ku (cert, opt->remote_cert_ku, MAX_PARMS))
{
msg (D_HANDSHAKE, "VERIFY KU OK");
}
@@ -879,9 +854,9 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
}
/* verify certificate eku */
- if (opt->remote_cert_eku != NULL && ctx->error_depth == 0)
+ if (opt->remote_cert_eku != NULL && cert_depth == 0)
{
- if (verify_cert_eku (ctx->current_cert, opt->remote_cert_eku))
+ if (verify_cert_eku (cert, opt->remote_cert_eku))
{
msg (D_HANDSHAKE, "VERIFY EKU OK");
}
@@ -895,7 +870,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
#endif /* OPENSSL_VERSION_NUMBER */
/* verify X509 name or common name against --tls-remote */
- if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && ctx->error_depth == 0)
+ if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && cert_depth == 0)
{
if (strcmp (opt->verify_x509name, subject) == 0
|| strncmp (opt->verify_x509name, common_name, strlen (opt->verify_x509name)) == 0)
@@ -914,20 +889,20 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
int ret;
argv_printf (&argv, "%d %s",
- ctx->error_depth,
+ cert_depth,
subject);
- ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es, ctx->error_depth, ctx->current_cert);
+ ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es, cert_depth, cert);
if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s",
- ctx->error_depth, subject);
+ cert_depth, subject);
}
else
{
msg (D_HANDSHAKE, "VERIFY PLUGIN ERROR: depth=%d, %s",
- ctx->error_depth, subject);
+ cert_depth, subject);
goto err; /* Reject connection */
}
}
@@ -944,15 +919,15 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
if (opt->verify_export_cert)
{
gc = gc_new();
- if ((tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc)))
+ if ((tmp_file=write_peer_cert(cert, opt->verify_export_cert,&gc)))
{
setenv_str(opt->es, "peer_cert", tmp_file);
}
}
argv_printf (&argv, "%sc %d %s",
opt->verify_command,
- ctx->error_depth,
+ cert_depth,
subject);
argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
ret = openvpn_run_script (&argv, opt->es, 0, "--tls-verify script");
@@ -967,12 +942,12 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
if (ret)
{
msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
- ctx->error_depth, subject);
+ cert_depth, subject);
}
else
{
msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
- ctx->error_depth, subject);
+ cert_depth, subject);
goto err; /* Reject connection */
}
}
@@ -1020,7 +995,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
goto end;
}
- if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
+ if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(cert)) != 0) {
msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
retval = 1;
goto end;
@@ -1030,7 +1005,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
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(ctx->current_cert)) == 0) {
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
goto end;
}
@@ -1049,7 +1024,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
}
}
- msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
+ msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", cert_depth, subject);
session->verified = true;
done:
View
@@ -38,6 +38,21 @@
*/
/*
+ * Verify certificate for the given session. Performs OpenVPN-specific
+ * verification.
+ *
+ * This function must be called for every certificate in the certificate
+ * chain during the certificate verification stage of the handshake.
+ *
+ * @param session TLS Session associated with this tunnel
+ * @param cert Certificate to process
+ * @param cert_depth Depth of the current certificate
+ *
+ * @return \c 1 if verification was successful, \c 0 on failure.
+ */
+int verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth);
+
+/*
* Remember the given certificate hash, allowing the certificate chain to be
* locked between sessions.
*
View
@@ -32,3 +32,44 @@
#include "ssl_openssl.h"
#include <openssl/x509v3.h>
+int
+verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
+{
+ struct tls_session *session;
+ SSL *ssl;
+
+ /* 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, ctx->current_cert->sha1_hash);
+
+ /* did peer present cert which was signed by our root cert? */
+ if (!preverify_ok)
+ {
+ /* get the X509 name */
+ char *subject = X509_NAME_oneline (
+ X509_get_subject_name (ctx->current_cert), NULL, 0);
+
+ 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);
+ free (subject);
+ }
+
+ ERR_clear_error();
+
+ session->verified = false;
+
+ return 1;
+ }
+
+ return verify_cert(session, ctx->current_cert, ctx->error_depth);
+}
+
View
@@ -32,6 +32,8 @@
#define SSL_VERIFY_OPENSSL_H_
#include <openssl/x509.h>
+typedef X509 x509_cert_t;
+
/** @name Function for authenticating a new connection from a remote OpenVPN peer
* @{ */

0 comments on commit 950b218

Please sign in to comment.