src/openvpn/ssl_verify.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.
  *
  *  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
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
9a160b79
 #include "syshead.h"
31ea2ee4
 
ec828db6
 #ifdef ENABLE_CRYPTO
31ea2ee4
 
9a160b79
 #include "misc.h"
 #include "manage.h"
270dc911
 #include "otime.h"
 #include "base64.h"
9a160b79
 #include "ssl_verify.h"
 #include "ssl_verify_backend.h"
 
9b33b5a4
 #ifdef ENABLE_CRYPTO_OPENSSL
9a160b79
 #include "ssl_verify_openssl.h"
 #endif
88aaf1ae
 
d0811e64
 /** Maximum length of common name */
 #define TLS_USERNAME_LEN 64
 
e7412ca3
 /** Legal characters in an X509 name with --compat-names */
 #define X509_NAME_CHAR_CLASS   (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH|CC_COLON|CC_EQUAL)
 
 /** Legal characters in a common name with --compat-names */
 #define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH)
 
 static void
 string_mod_remap_name (char *str, const unsigned int restrictive_flags)
 {
   if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES)
       && !compat_flag (COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING))
     string_mod (str, restrictive_flags, 0, '_');
   else
     string_mod (str, CC_PRINT, CC_CRLF, '_');
 }
 
d0811e64
 /*
  * Export the untrusted IP address and port to the environment
  */
 static void
 setenv_untrusted (struct tls_session *session)
 {
   setenv_link_socket_actual (session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT);
 }
 
 /*
  * Remove authenticated state from all sessions in the given tunnel
  */
82f925b6
 static void
 tls_deauthenticate (struct tls_multi *multi)
 {
   if (multi)
     {
       int i, j;
       for (i = 0; i < TM_SIZE; ++i)
 	for (j = 0; j < KS_SIZE; ++j)
 	  multi->session[i].key[j].authenticated = false;
     }
 }
 
e285cdb0
 /*
  * Set the given session's common_name
  */
36fae2ec
 static void
530af3ef
 set_common_name (struct tls_session *session, const char *common_name)
 {
   if (session->common_name)
     {
       free (session->common_name);
       session->common_name = NULL;
 #ifdef ENABLE_PF
       session->common_name_hashval = 0;
 #endif
     }
   if (common_name)
     {
dc7be6d0
       /* FIXME: Last alloc will never be freed */
530af3ef
       session->common_name = string_alloc (common_name, NULL);
 #ifdef ENABLE_PF
       {
 	const uint32_t len = (uint32_t) strlen (common_name);
 	if (len)
 	  session->common_name_hashval = hash_func ((const uint8_t*)common_name, len+1, 0);
 	else
 	  session->common_name_hashval = 0;
       }
 #endif
     }
 }
 
e285cdb0
 /*
  * Retrieve the common name for the given tunnel's active session. If the
  * common name is NULL or empty, return NULL if null is true, or "UNDEF" if
  * null is false.
  */
530af3ef
 const char *
 tls_common_name (const struct tls_multi *multi, const bool null)
 {
   const char *ret = NULL;
   if (multi)
     ret = multi->session[TM_ACTIVE].common_name;
   if (ret && strlen (ret))
     return ret;
   else if (null)
     return NULL;
   else
     return "UNDEF";
 }
 
e285cdb0
 /*
  * Lock the common name for the given tunnel.
  */
530af3ef
 void
 tls_lock_common_name (struct tls_multi *multi)
 {
   const char *cn = multi->session[TM_ACTIVE].common_name;
   if (cn && !multi->locked_cn)
     multi->locked_cn = string_alloc (cn, NULL);
 }
 
e285cdb0
 /*
  * Lock the username for the given tunnel
  */
d0811e64
 static bool
 tls_lock_username (struct tls_multi *multi, const char *username)
 {
   if (multi->locked_username)
     {
       if (!username || strcmp (username, multi->locked_username))
 	{
 	  msg (D_TLS_ERRORS, "TLS Auth Error: username attempted to change from '%s' to '%s' -- tunnel disabled",
 	       multi->locked_username,
 	       np(username));
 
 	  /* disable the tunnel */
 	  tls_deauthenticate (multi);
 	  return false;
 	}
     }
   else
     {
       if (username)
 	multi->locked_username = string_alloc (username, NULL);
     }
   return true;
 }
 
 const char *
 tls_username (const struct tls_multi *multi, const bool null)
 {
   const char *ret = NULL;
   if (multi)
     ret = multi->locked_username;
   if (ret && strlen (ret))
     return ret;
   else if (null)
     return NULL;
   else
     return "UNDEF";
 }
82f925b6
 
 void
af1e4d26
 cert_hash_remember (struct tls_session *session, const int error_depth,
     const struct buffer *cert_hash)
82f925b6
 {
   if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
     {
       if (!session->cert_hash_set)
af1e4d26
 	{
 	  ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
 	}
82f925b6
       if (!session->cert_hash_set->ch[error_depth])
 	{
af1e4d26
 	  ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
82f925b6
 	}
af1e4d26
 
       struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
       ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash));
       memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash));
82f925b6
     }
 }
 
 void
 cert_hash_free (struct cert_hash_set *chs)
 {
   if (chs)
     {
       int i;
       for (i = 0; i < MAX_CERT_DEPTH; ++i)
 	free (chs->ch[i]);
       free (chs);
     }
 }
 
0c0c178a
 bool
82f925b6
 cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2)
 {
   if (chs1 && chs2)
     {
       int i;
       for (i = 0; i < MAX_CERT_DEPTH; ++i)
 	{
 	  const struct cert_hash *ch1 = chs1->ch[i];
 	  const struct cert_hash *ch2 = chs2->ch[i];
 
 	  if (!ch1 && !ch2)
 	    continue;
af1e4d26
 	  else if (ch1 && ch2 && !memcmp (ch1->sha256_hash, ch2->sha256_hash,
 	      sizeof(ch1->sha256_hash)))
82f925b6
 	    continue;
 	  else
 	    return false;
 	}
       return true;
     }
   else if (!chs1 && !chs2)
     return true;
   else
     return false;
 }
 
 static struct cert_hash_set *
 cert_hash_copy (const struct cert_hash_set *chs)
 {
   struct cert_hash_set *dest = NULL;
   if (chs)
     {
       int i;
       ALLOC_OBJ_CLEAR (dest, struct cert_hash_set);
       for (i = 0; i < MAX_CERT_DEPTH; ++i)
 	{
 	  const struct cert_hash *ch = chs->ch[i];
 	  if (ch)
 	    {
 	      ALLOC_OBJ (dest->ch[i], struct cert_hash);
af1e4d26
 	      memcpy (dest->ch[i]->sha256_hash, ch->sha256_hash,
 		  sizeof(dest->ch[i]->sha256_hash));
82f925b6
 	    }
 	}
     }
   return dest;
 }
 void
 tls_lock_cert_hash_set (struct tls_multi *multi)
 {
   const struct cert_hash_set *chs = multi->session[TM_ACTIVE].cert_hash_set;
   if (chs && !multi->locked_cert_hash_set)
     multi->locked_cert_hash_set = cert_hash_copy (chs);
 }
 
fe100528
 /*
06d22777
  * Returns the string associated with the given certificate type.
  */
 static const char *
 print_nsCertType (int type)
 {
   switch (type)
     {
     case NS_CERT_CHECK_SERVER:
       return "SERVER";
     case NS_CERT_CHECK_CLIENT:
       return "CLIENT";
     default:
       return "?";
     }
 }
 
 /*
  * Verify the peer's certificate fields.
  *
  * @param opt the tls options to verify against
  * @param peer_cert the peer's certificate
  * @param subject the peer's extracted subject name
  * @param subject the peer's extracted common name
  */
8a840d83
 static result_t
9b33b5a4
 verify_peer_cert(const struct tls_options *opt, openvpn_x509_cert_t *peer_cert,
06d22777
     const char *subject, const char *common_name)
 {
   /* verify certificate nsCertType */
   if (opt->ns_cert_type != NS_CERT_CHECK_NONE)
     {
8a840d83
       if (SUCCESS == x509_verify_ns_cert_type (peer_cert, opt->ns_cert_type))
06d22777
 	{
 	  msg (D_HANDSHAKE, "VERIFY OK: nsCertType=%s",
 	       print_nsCertType (opt->ns_cert_type));
 	}
       else
 	{
 	  msg (D_HANDSHAKE, "VERIFY nsCertType ERROR: %s, require nsCertType=%s",
 	       subject, print_nsCertType (opt->ns_cert_type));
8a840d83
 	  return FAILURE;		/* Reject connection */
06d22777
 	}
     }
 
876752ae
   /* verify certificate ku */
   if (opt->remote_cert_ku[0] != 0)
     {
8a840d83
       if (SUCCESS == x509_verify_cert_ku (peer_cert, opt->remote_cert_ku, MAX_PARMS))
876752ae
 	{
 	  msg (D_HANDSHAKE, "VERIFY KU OK");
 	}
         else
         {
 	  msg (D_HANDSHAKE, "VERIFY KU ERROR");
8a840d83
           return FAILURE;		/* Reject connection */
876752ae
 	}
     }
 
587f419b
   /* verify certificate eku */
   if (opt->remote_cert_eku != NULL)
     {
8a840d83
       if (SUCCESS == x509_verify_cert_eku (peer_cert, opt->remote_cert_eku))
587f419b
         {
 	  msg (D_HANDSHAKE, "VERIFY EKU OK");
 	}
       else
 	{
 	  msg (D_HANDSHAKE, "VERIFY EKU ERROR");
8a840d83
           return FAILURE;		/* Reject connection */
587f419b
 	}
     }
876752ae
 
9f0fc745
   /* verify X509 name or username against --verify-x509-[user]name */
   if (opt->verify_x509_type != VERIFY_X509_NONE)
a4c926bb
     {
9f0fc745
       if ( (opt->verify_x509_type == VERIFY_X509_SUBJECT_DN
             && strcmp (opt->verify_x509_name, subject) == 0)
         || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN
             && strcmp (opt->verify_x509_name, common_name) == 0)
         || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN_PREFIX
             && strncmp (opt->verify_x509_name, common_name,
                         strlen (opt->verify_x509_name)) == 0) )
a4c926bb
 	msg (D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject);
       else
 	{
 	  msg (D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s",
9f0fc745
 	       subject, opt->verify_x509_name);
8a840d83
 	  return FAILURE;		/* Reject connection */
a4c926bb
 	}
     }
 
8a840d83
   return SUCCESS;
06d22777
 }
 
 /*
fe100528
  * Export the subject, common_name, and raw certificate fields to the
  * environment for later verification by scripts and plugins.
  */
36fae2ec
 static void
9b33b5a4
 verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert_depth,
fab49d17
     const char *subject, const char *common_name,
     const struct x509_track *x509_track)
fe100528
 {
   char envname[64];
025f30d7
   char *serial = NULL;
   struct gc_arena gc = gc_new ();
fe100528
 
   /* Save X509 fields in environment */
   if (x509_track)
bb53a20a
     x509_setenv_track (x509_track, es, cert_depth, peer_cert);
fe100528
   else
bb53a20a
     x509_setenv (es, cert_depth, peer_cert);
fe100528
 
   /* export subject name string as environmental variable */
   openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", cert_depth);
   setenv_str (es, envname, subject);
 
 #if 0
   /* export common name string as environmental variable */
   openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", cert_depth);
   setenv_str (es, envname, common_name);
 #endif
 
af1e4d26
   /* export X509 cert fingerprints */
fe100528
   {
af1e4d26
     struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc);
     struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc);
fceecbab
 
fe100528
     openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth);
af1e4d26
     setenv_str (es, envname,
 	format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc));
 
     openvpn_snprintf (envname, sizeof(envname), "tls_digest_sha256_%d",
 	cert_depth);
     setenv_str (es, envname,
 	format_hex_ex(BPTR(&sha256), BLEN(&sha256), 0, 1, ":", &gc));
fe100528
   }
 
025f30d7
   /* export serial number as environmental variable */
03df3a99
   serial = backend_x509_get_serial(peer_cert, &gc);
025f30d7
   openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", cert_depth);
f80a52b0
   setenv_str (es, envname, serial);
 
   /* export serial number in hex as environmental variable */
   serial = backend_x509_get_serial_hex(peer_cert, &gc);
   openvpn_snprintf (envname, sizeof(envname), "tls_serial_hex_%d", cert_depth);
025f30d7
   setenv_str (es, envname, serial);
 
   gc_free(&gc);
fe100528
 }
 
75c67073
 /*
  * call --tls-verify plug-in(s)
  */
8a840d83
 static result_t
75c67073
 verify_cert_call_plugin(const struct plugin_list *plugins, struct env_set *es,
9b33b5a4
     int cert_depth, openvpn_x509_cert_t *cert, char *subject)
75c67073
 {
   if (plugin_defined (plugins, OPENVPN_PLUGIN_TLS_VERIFY))
     {
       int ret;
       struct argv argv = argv_new ();
 
       argv_printf (&argv, "%d %s", cert_depth, subject);
 
1876ccd0
       ret = plugin_call_ssl (plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, es, cert_depth, cert);
75c67073
 
       argv_reset (&argv);
 
       if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
 	{
 	  msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s",
 	      cert_depth, subject);
 	}
       else
 	{
 	  msg (D_HANDSHAKE, "VERIFY PLUGIN ERROR: depth=%d, %s",
 	      cert_depth, subject);
8a840d83
 	  return FAILURE;		/* Reject connection */
75c67073
 	}
     }
8a840d83
   return SUCCESS;
75c67073
 }
 
8bb72fbc
 static const char *
9b33b5a4
 verify_cert_export_cert(openvpn_x509_cert_t *peercert, const char *tmp_dir, struct gc_arena *gc)
8bb72fbc
 {
   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;
     }
 
8a840d83
   if (SUCCESS != x509_write_pem(peercert_file, peercert))
8bb72fbc
       msg (M_ERR, "Error writing PEM file containing certificate");
 
   fclose(peercert_file);
   return peercert_filename;
 }
 
 
3e44ea55
 /*
  * run --tls-verify script
  */
8a840d83
 static result_t
3e44ea55
 verify_cert_call_command(const char *verify_command, struct env_set *es,
9b33b5a4
     int cert_depth, openvpn_x509_cert_t *cert, char *subject, const char *verify_export_cert)
3e44ea55
 {
   const char *tmp_file = NULL;
   int ret;
b26341cd
   struct gc_arena gc = gc_new();
3e44ea55
   struct argv argv = argv_new ();
 
   setenv_str (es, "script_type", "tls-verify");
 
   if (verify_export_cert)
     {
8bb72fbc
       if ((tmp_file=verify_cert_export_cert(cert, verify_export_cert, &gc)))
3e44ea55
        {
          setenv_str(es, "peer_cert", tmp_file);
        }
     }
 
25360912
   argv_parse_cmd (&argv, verify_command);
   argv_printf_cat (&argv, "%d %s", cert_depth, subject);
3e44ea55
 
   argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
   ret = openvpn_run_script (&argv, es, 0, "--tls-verify script");
 
   if (verify_export_cert)
     {
        if (tmp_file)
14a131ac
           platform_unlink(tmp_file);
3e44ea55
     }
 
b26341cd
   gc_free(&gc);
3e44ea55
   argv_reset (&argv);
 
   if (ret)
     {
       msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
 	   cert_depth, subject);
8a840d83
       return SUCCESS;
3e44ea55
     }
 
   msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
        cert_depth, subject);
8a840d83
   return FAILURE;		/* Reject connection */
3e44ea55
 }
 
83c49a3e
 /*
  * check peer cert against CRL directory
  */
8a840d83
 static result_t
9b33b5a4
 verify_check_crl_dir(const char *crl_dir, openvpn_x509_cert_t *cert)
83c49a3e
 {
75b49e40
   result_t ret = FAILURE;
83c49a3e
   char fn[256];
75b49e40
   int fd = -1;
025f30d7
   struct gc_arena gc = gc_new();
 
03df3a99
   char *serial = backend_x509_get_serial(cert, &gc);
83c49a3e
 
   if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", crl_dir, OS_SPECIFIC_DIRSEP, serial))
     {
       msg (D_HANDSHAKE, "VERIFY CRL: filename overflow");
75b49e40
       goto cleanup;
83c49a3e
     }
14a131ac
   fd = platform_open (fn, O_RDONLY, 0);
83c49a3e
   if (fd >= 0)
     {
       msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial);
75b49e40
       goto cleanup;
83c49a3e
     }
 
75b49e40
   ret = SUCCESS;
83c49a3e
 
75b49e40
 cleanup:
 
   if (fd != -1)
     close(fd);
   gc_free(&gc);
   return ret;
83c49a3e
 }
82f925b6
 
8a840d83
 result_t
9b33b5a4
 verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_depth)
36fae2ec
 {
75b49e40
   result_t ret = FAILURE;
36fae2ec
   char *subject = NULL;
ecd934b1
   char common_name[TLS_USERNAME_LEN+1] = {0}; /* null-terminated */
36fae2ec
   const struct tls_options *opt;
00b973f8
   struct gc_arena gc = gc_new();
36fae2ec
 
   opt = session->opt;
   ASSERT (opt);
 
   session->verified = false;
 
   /* get the X509 name */
00b973f8
   subject = x509_get_subject(cert, &gc);
36fae2ec
   if (!subject)
     {
 	msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 "
 	    "subject string from certificate", cert_depth);
75b49e40
 	goto cleanup;
36fae2ec
     }
 
   /* enforce character class restrictions in X509 name */
e7412ca3
   string_mod_remap_name (subject, X509_NAME_CHAR_CLASS);
36fae2ec
   string_replace_leading (subject, '-', '_');
 
   /* extract the username (default is CN) */
ecd934b1
   if (SUCCESS != backend_x509_get_username (common_name, sizeof(common_name),
8a840d83
       opt->x509_username_field, cert))
36fae2ec
     {
       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",
 	       opt->x509_username_field,
 		 subject,
 		 TLS_USERNAME_LEN);
75b49e40
 	  goto cleanup;
36fae2ec
 	}
     }
 
   /* enforce character class restrictions in common name */
e7412ca3
   string_mod_remap_name (common_name, COMMON_NAME_CHAR_CLASS);
36fae2ec
 
   /* warn if cert chain is too deep */
   if (cert_depth >= MAX_CERT_DEPTH)
     {
       msg (D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", cert_depth, MAX_CERT_DEPTH);
75b49e40
       goto cleanup;			/* Reject connection */
36fae2ec
     }
 
   /* verify level 1 cert, i.e. the CA that signed our leaf cert */
   if (cert_depth == 1 && opt->verify_hash)
     {
af1e4d26
       struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc);
       if (memcmp (BPTR (&sha1_hash), opt->verify_hash, BLEN(&sha1_hash)))
36fae2ec
       {
 	msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
75b49e40
 	goto cleanup;
36fae2ec
       }
     }
 
   /* save common name in session object */
   if (cert_depth == 0)
     set_common_name (session, common_name);
 
   session->verify_maxlevel = max_int (session->verify_maxlevel, cert_depth);
 
   /* export certificate values to the environment */
fab49d17
   verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name,
       opt->x509_track);
36fae2ec
 
   /* export current untrusted IP */
   setenv_untrusted (session);
 
   /* If this is the peer's own certificate, verify it */
8a840d83
   if (cert_depth == 0 && SUCCESS != verify_peer_cert(opt, cert, subject, common_name))
75b49e40
     goto cleanup;
36fae2ec
 
   /* call --tls-verify plug-in(s), if registered */
8a840d83
   if (SUCCESS != verify_cert_call_plugin(opt->plugins, opt->es, cert_depth, cert, subject))
75b49e40
     goto cleanup;
36fae2ec
 
   /* run --tls-verify script */
8a840d83
   if (opt->verify_command && SUCCESS != verify_cert_call_command(opt->verify_command,
       opt->es, cert_depth, cert, subject, opt->verify_export_cert))
75b49e40
     goto cleanup;
36fae2ec
 
   /* check peer cert against CRL */
   if (opt->crl_file)
     {
       if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
160504a2
 	{
 	  if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert))
 	    goto cleanup;
 	}
36fae2ec
       else
160504a2
 	{
 	  if (tls_verify_crl_missing (opt))
 	    {
 	      msg (D_TLS_ERRORS, "VERIFY ERROR: CRL not loaded");
 	      goto cleanup;
 	    }
 	}
36fae2ec
     }
 
   msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", cert_depth, subject);
   session->verified = true;
75b49e40
   ret = SUCCESS;
36fae2ec
 
75b49e40
 cleanup:
36fae2ec
 
75b49e40
   if (ret != SUCCESS)
     {
       tls_clear_error(); /* always? */
       session->verified = false; /* double sure? */
     }
00b973f8
   gc_free(&gc);
75b49e40
 
   return ret;
36fae2ec
 }
 
d0811e64
 /* ***************************************************************************
  * Functions for the management of deferred authentication when using
  * user/password authentication.
  *************************************************************************** */
 
 #ifdef ENABLE_DEF_AUTH
 /* key_state_test_auth_control_file return values,
    NOTE: acf_merge indexing depends on these values */
 #define ACF_UNDEFINED 0
 #define ACF_SUCCEEDED 1
 #define ACF_DISABLED  2
 #define ACF_FAILED    3
 #endif
 
 #ifdef MANAGEMENT_DEF_AUTH
 void
 man_def_auth_set_client_reason (struct tls_multi *multi, const char *client_reason)
 {
   if (multi->client_reason)
     {
       free (multi->client_reason);
       multi->client_reason = NULL;
     }
   if (client_reason && strlen (client_reason))
dc7be6d0
     /* FIXME: Last alloc will never be freed */
d0811e64
     multi->client_reason = string_alloc (client_reason, NULL);
 }
 
 static inline unsigned int
 man_def_auth_test (const struct key_state *ks)
 {
   if (management_enable_def_auth (management))
     return ks->mda_status;
   else
     return ACF_DISABLED;
 }
 #endif
 
 #ifdef PLUGIN_DEF_AUTH
 
 /*
  * auth_control_file functions
  */
 
 void
 key_state_rm_auth_control_file (struct key_state *ks)
 {
   if (ks && ks->auth_control_file)
     {
14a131ac
       platform_unlink (ks->auth_control_file);
d0811e64
       free (ks->auth_control_file);
       ks->auth_control_file = NULL;
     }
 }
 
 static void
 key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options *opt)
 {
   struct gc_arena gc = gc_new ();
   const char *acf;
 
   key_state_rm_auth_control_file (ks);
   acf = create_temp_file (opt->tmp_dir, "acf", &gc);
   if (acf) {
     ks->auth_control_file = string_alloc (acf, NULL);
     setenv_str (opt->es, "auth_control_file", ks->auth_control_file);
   } /* FIXME: Should have better error handling? */
 
   gc_free (&gc);
 }
 
 static unsigned int
 key_state_test_auth_control_file (struct key_state *ks)
 {
   if (ks && ks->auth_control_file)
     {
       unsigned int ret = ks->auth_control_status;
       if (ret == ACF_UNDEFINED)
 	{
 	  FILE *fp = fopen (ks->auth_control_file, "r");
 	  if (fp)
 	    {
 	      const int c = fgetc (fp);
 	      if (c == '1')
 		ret = ACF_SUCCEEDED;
 	      else if (c == '0')
 		ret = ACF_FAILED;
 	      fclose (fp);
 	      ks->auth_control_status = ret;
 	    }
 	}
       return ret;
     }
   return ACF_DISABLED;
 }
 
 #endif
 
 /*
  * Return current session authentication state.  Return
  * value is TLS_AUTHENTICATION_x.
  */
 
 int
 tls_authentication_status (struct tls_multi *multi, const int latency)
 {
   bool deferred = false;
   bool success = false;
   bool active = false;
 
 #ifdef ENABLE_DEF_AUTH
   static const unsigned char acf_merge[] =
     {
       ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_UNDEFINED */
       ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_SUCCEEDED */
       ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_DISABLED */
       ACF_FAILED,    /* s1=ACF_UNDEFINED s2=ACF_FAILED */
       ACF_UNDEFINED, /* s1=ACF_SUCCEEDED s2=ACF_UNDEFINED */
       ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_SUCCEEDED */
       ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_DISABLED */
       ACF_FAILED,    /* s1=ACF_SUCCEEDED s2=ACF_FAILED */
       ACF_UNDEFINED, /* s1=ACF_DISABLED  s2=ACF_UNDEFINED */
       ACF_SUCCEEDED, /* s1=ACF_DISABLED  s2=ACF_SUCCEEDED */
       ACF_DISABLED,  /* s1=ACF_DISABLED  s2=ACF_DISABLED */
       ACF_FAILED,    /* s1=ACF_DISABLED  s2=ACF_FAILED */
       ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_UNDEFINED */
       ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_SUCCEEDED */
       ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_DISABLED */
       ACF_FAILED     /* s1=ACF_FAILED    s2=ACF_FAILED */
     };
 #endif /* ENABLE_DEF_AUTH */
 
   if (multi)
     {
       int i;
 
 #ifdef ENABLE_DEF_AUTH
       if (latency && multi->tas_last && multi->tas_last + latency >= now)
 	return TLS_AUTHENTICATION_UNDEFINED;
       multi->tas_last = now;
 #endif /* ENABLE_DEF_AUTH */
 
       for (i = 0; i < KEY_SCAN_SIZE; ++i)
 	{
 	  struct key_state *ks = multi->key_scan[i];
 	  if (DECRYPT_KEY_ENABLED (multi, ks))
 	    {
 	      active = true;
 	      if (ks->authenticated)
 		{
 #ifdef ENABLE_DEF_AUTH
 		  unsigned int s1 = ACF_DISABLED;
 		  unsigned int s2 = ACF_DISABLED;
 #ifdef PLUGIN_DEF_AUTH
 		  s1 = key_state_test_auth_control_file (ks);
 #endif /* PLUGIN_DEF_AUTH */
 #ifdef MANAGEMENT_DEF_AUTH
 		  s2 = man_def_auth_test (ks);
 #endif /* MANAGEMENT_DEF_AUTH */
 		  ASSERT (s1 < 4 && s2 < 4);
 		  switch (acf_merge[(s1<<2) + s2])
 		    {
 		    case ACF_SUCCEEDED:
 		    case ACF_DISABLED:
 		      success = true;
 		      ks->auth_deferred = false;
 		      break;
 		    case ACF_UNDEFINED:
 		      if (now < ks->auth_deferred_expire)
 			deferred = true;
 		      break;
 		    case ACF_FAILED:
 		      ks->authenticated = false;
 		      break;
 		    default:
 		      ASSERT (0);
 		    }
 #else /* !ENABLE_DEF_AUTH */
 		  success = true;
 #endif /* ENABLE_DEF_AUTH */
 		}
 	    }
 	}
     }
 
 #if 0
   dmsg (D_TLS_ERRORS, "TAS: a=%d s=%d d=%d", active, success, deferred);
 #endif
 
   if (success)
     return TLS_AUTHENTICATION_SUCCEEDED;
   else if (!active || deferred)
     return TLS_AUTHENTICATION_DEFERRED;
   else
     return TLS_AUTHENTICATION_FAILED;
 }
 
 #ifdef MANAGEMENT_DEF_AUTH
 /*
  * For deferred auth, this is where the management interface calls (on server)
  * to indicate auth failure/success.
  */
 bool
 tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason)
 {
   bool ret = false;
   if (multi)
     {
       int i;
       man_def_auth_set_client_reason (multi, client_reason);
       for (i = 0; i < KEY_SCAN_SIZE; ++i)
 	{
 	  struct key_state *ks = multi->key_scan[i];
 	  if (ks->mda_key_id == mda_key_id)
 	    {
 	      ks->mda_status = auth ? ACF_SUCCEEDED : ACF_FAILED;
 	      ret = true;
 	    }
 	}
     }
   return ret;
 }
 #endif
 
 
 /* ****************************************************************************
  * Functions to verify username and password
  *
  * Authenticate a client using username/password.
  * Runs on server.
  *
  * If you want to add new authentication methods,
  * this is the place to start.
  *************************************************************************** */
 
 /*
  * Verify the user name and password using a script
  */
 static bool
 verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
 {
   struct gc_arena gc = gc_new ();
   struct argv argv = argv_new ();
   const char *tmp_file = "";
   bool ret = false;
 
   /* Is username defined? */
   if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
     {
       /* Set environmental variables prior to calling script */
       setenv_str (session->opt->es, "script_type", "user-pass-verify");
 
       if (session->opt->auth_user_pass_verify_script_via_file)
 	{
 	  struct status_output *so;
 
 	  tmp_file = create_temp_file (session->opt->tmp_dir, "up", &gc);
           if( tmp_file ) {
 	    so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
 	    status_printf (so, "%s", up->username);
 	    status_printf (so, "%s", up->password);
 	    if (!status_close (so))
 	      {
 		msg (D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s",
 		     tmp_file);
 		goto done;
 	      }
           } else {
             msg (D_TLS_ERRORS, "TLS Auth Error: could not create write "
                  "username/password to temp file");
           }
 	}
       else
 	{
 	  setenv_str (session->opt->es, "username", up->username);
 	  setenv_str (session->opt->es, "password", up->password);
 	}
 
       /* setenv incoming cert common name for script */
       setenv_str (session->opt->es, "common_name", session->common_name);
 
       /* setenv client real IP address */
       setenv_untrusted (session);
 
       /* format command line */
25360912
       argv_parse_cmd (&argv, session->opt->auth_user_pass_verify_script);
       argv_printf_cat (&argv, "%s", tmp_file);
d0811e64
 
       /* call command */
       ret = openvpn_run_script (&argv, session->opt->es, 0,
 				"--auth-user-pass-verify");
 
       if (!session->opt->auth_user_pass_verify_script_via_file)
 	setenv_del (session->opt->es, "password");
     }
   else
     {
       msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
     }
 
  done:
   if (tmp_file && strlen (tmp_file) > 0)
14a131ac
     platform_unlink (tmp_file);
d0811e64
 
   argv_reset (&argv);
   gc_free (&gc);
   return ret;
 }
 
 /*
  * Verify the username and password using a plugin
  */
 static int
e7412ca3
 verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username)
d0811e64
 {
   int retval = OPENVPN_PLUGIN_FUNC_ERROR;
d0c4c442
 #ifdef PLUGIN_DEF_AUTH
d0811e64
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
d0c4c442
 #endif
d0811e64
 
   /* Is username defined? */
   if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
     {
       /* set username/password in private env space */
e7412ca3
       setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
d0811e64
       setenv_str (session->opt->es, "password", up->password);
 
       /* setenv incoming cert common name for script */
       setenv_str (session->opt->es, "common_name", session->common_name);
 
       /* setenv client real IP address */
       setenv_untrusted (session);
 
 #ifdef PLUGIN_DEF_AUTH
       /* generate filename for deferred auth control file */
       key_state_gen_auth_control_file (ks, session->opt);
 #endif
 
       /* call command */
1876ccd0
       retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
d0811e64
 
 #ifdef PLUGIN_DEF_AUTH
       /* purge auth control filename (and file itself) for non-deferred returns */
       if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
 	key_state_rm_auth_control_file (ks);
 #endif
 
       setenv_del (session->opt->es, "password");
e7412ca3
       if (raw_username)
         setenv_str (session->opt->es, "username", up->username);
d0811e64
     }
   else
     {
       msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
     }
 
   return retval;
 }
 
 
 #ifdef MANAGEMENT_DEF_AUTH
 /*
  * MANAGEMENT_DEF_AUTH internal ssl_verify.c status codes
  */
 #define KMDA_ERROR   0
 #define KMDA_SUCCESS 1
 #define KMDA_UNDEF   2
 #define KMDA_DEF     3
 
 static int
e7412ca3
 verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
d0811e64
 {
   int retval = KMDA_ERROR;
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
 
   /* Is username defined? */
   if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
     {
       /* set username/password in private env space */
e7412ca3
       setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
d0811e64
       setenv_str (session->opt->es, "password", up->password);
 
       /* setenv incoming cert common name for script */
       setenv_str (session->opt->es, "common_name", session->common_name);
 
       /* setenv client real IP address */
       setenv_untrusted (session);
 
       if (management)
 	management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
 
       setenv_del (session->opt->es, "password");
e7412ca3
       if (raw_username)
         setenv_str (session->opt->es, "username", up->username);
d0811e64
 
       retval = KMDA_SUCCESS;
     }
   else
     {
       msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
     }
 
   return retval;
 }
 #endif
 
 /*
  * Main username/password verification entry point
  */
 void
 verify_user_pass(struct user_pass *up, struct tls_multi *multi,
     struct tls_session *session)
 {
   int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
   bool s2 = true;
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
 
e7412ca3
   struct gc_arena gc = gc_new ();
   char *raw_username = NULL;
 
d0811e64
 #ifdef MANAGEMENT_DEF_AUTH
   int man_def_auth = KMDA_UNDEF;
 
   if (management_enable_def_auth (management))
     man_def_auth = KMDA_DEF;
 #endif
 
e7412ca3
   /*
    * Preserve the raw username before string_mod remapping, for plugins
    * and management clients when in --compat-names mode
    */
   if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES))
     {
       ALLOC_ARRAY_CLEAR_GC (raw_username, char, USER_PASS_LEN, &gc);
       strcpy (raw_username, up->username);
       string_mod (raw_username, CC_PRINT, CC_CRLF, '_');
     }
 
d0811e64
   /* enforce character class restrictions in username/password */
e7412ca3
   string_mod_remap_name (up->username, COMMON_NAME_CHAR_CLASS);
d0811e64
   string_mod (up->password, CC_PRINT, CC_CRLF, '_');
 
703c9784
   /* If server is configured with --auth-gen-token and we have an
    * authentication token for this client, this authentication
    * round will be done internally using the token instead of
    * calling any external authentication modules.
    */
   if (session->opt->auth_token_generate && multi->auth_token_sent
       && NULL != multi->auth_token)
     {
       unsigned int ssl_flags = session->opt->ssl_flags;
 
       /* Ensure that the username has not changed */
       if (!tls_lock_username(multi, up->username))
         {
           ks->authenticated = false;
           goto done;
         }
 
       /* If auth-token lifetime has been enabled,
        * ensure the token has not expired
        */
       if (session->opt->auth_token_lifetime > 0
           && (multi->auth_token_tstamp + session->opt->auth_token_lifetime) < now)
         {
           msg (D_HANDSHAKE, "Auth-token for client expired\n");
           ks->authenticated = false;
           goto done;
         }
 
       /* The core authentication of the token itself */
       if (memcmp_constant_time(multi->auth_token, up->password,
                  strlen(multi->auth_token)) != 0)
         {
009521ac
           secure_memzero (multi->auth_token, AUTH_TOKEN_SIZE);
703c9784
           free (multi->auth_token);
           multi->auth_token = NULL;
           multi->auth_token_sent = false;
           ks->authenticated = false;
           tls_deauthenticate (multi);
 
           msg (D_TLS_ERRORS, "TLS Auth Error: Auth-token verification "
                "failed for username '%s' %s", up->username,
                (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
         }
       else
         {
           ks->authenticated = true;
 
           if (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)
             set_common_name (session, up->username);
           msg (D_HANDSHAKE, "TLS: Username/auth-token authentication "
                "succeeded for username '%s' %s",
                up->username,
                (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
         }
       goto done;
     }
 
d0811e64
   /* call plugin(s) and/or script */
 #ifdef MANAGEMENT_DEF_AUTH
   if (man_def_auth == KMDA_DEF)
e7412ca3
     man_def_auth = verify_user_pass_management (session, up, raw_username);
d0811e64
 #endif
   if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
e7412ca3
     s1 = verify_user_pass_plugin (session, up, raw_username);
d0811e64
   if (session->opt->auth_user_pass_verify_script)
     s2 = verify_user_pass_script (session, up);
 
   /* check sizing of username if it will become our common name */
ecd934b1
   if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) > TLS_USERNAME_LEN)
d0811e64
     {
       msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN);
       s1 = OPENVPN_PLUGIN_FUNC_ERROR;
     }
 
   /* auth succeeded? */
   if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
 #ifdef PLUGIN_DEF_AUTH
        || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
 #endif
        ) && s2
 #ifdef MANAGEMENT_DEF_AUTH
        && man_def_auth != KMDA_ERROR
 #endif
       && tls_lock_username (multi, up->username))
     {
       ks->authenticated = true;
 #ifdef PLUGIN_DEF_AUTH
       if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
 	ks->auth_deferred = true;
 #endif
 #ifdef MANAGEMENT_DEF_AUTH
       if (man_def_auth != KMDA_UNDEF)
 	ks->auth_deferred = true;
 #endif
270dc911
 
       if ((session->opt->auth_token_generate) && (NULL == multi->auth_token))
 	{
 	  /* Server is configured with --auth-gen-token but no token has yet
 	   * been generated for this client.  Generate one and save it.
 	   */
 	  uint8_t tok[AUTH_TOKEN_SIZE];
 
 	  if (!rand_bytes(tok, AUTH_TOKEN_SIZE))
 	    {
 	      msg( M_FATAL, "Failed to get enough randomness for "
                    "authentication token");
 	    }
 
 	  /* The token should be longer than the input when
            * being base64 encoded
            */
 	  if( openvpn_base64_encode(tok, AUTH_TOKEN_SIZE,
                                     &multi->auth_token) < AUTH_TOKEN_SIZE)
 	    {
 	      msg(D_TLS_ERRORS, "BASE64 encoding of token failed. "
                   "No auth-token will be activated now");
 	      if (multi->auth_token)
 		{
009521ac
 		  secure_memzero (multi->auth_token, AUTH_TOKEN_SIZE);
270dc911
 		  free (multi->auth_token);
 		  multi->auth_token = NULL;
 		}
 	    }
 	  else
 	    {
 	      multi->auth_token_tstamp = now;
 	      dmsg (D_SHOW_KEYS, "Generated token for client: %s",
                     multi->auth_token);
 	    }
 	}
 
d0811e64
       if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME))
 	set_common_name (session, up->username);
 #ifdef ENABLE_DEF_AUTH
       msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
 	   ks->auth_deferred ? "deferred" : "succeeded",
 	   up->username,
 	   (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
 #else
       msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
 	   "succeeded",
 	   up->username,
 	   (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
 #endif
     }
   else
     {
       msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer");
     }
e7412ca3
 
703c9784
  done:
e7412ca3
   gc_free (&gc);
d0811e64
 }
 
88aaf1ae
 void
 verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session)
 {
c94eff3c
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
 
530af3ef
   /* While it shouldn't really happen, don't allow the common name to be NULL */
   if (!session->common_name)
     set_common_name (session, "");
 
   /* Don't allow the CN to change once it's been locked */
c94eff3c
   if (ks->authenticated && multi->locked_cn)
530af3ef
     {
       const char *cn = session->common_name;
       if (cn && strcmp (cn, multi->locked_cn))
 	{
 	  msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN attempted to change from '%s' to '%s' -- tunnel disabled",
 	       multi->locked_cn,
 	       cn);
 
 	  /* change the common name back to its original value and disable the tunnel */
 	  set_common_name (session, multi->locked_cn);
 	  tls_deauthenticate (multi);
 	}
     }
82f925b6
 
   /* Don't allow the cert hashes to change once they have been locked */
c94eff3c
   if (ks->authenticated && multi->locked_cert_hash_set)
82f925b6
     {
       const struct cert_hash_set *chs = session->cert_hash_set;
       if (chs && !cert_hash_compare (chs, multi->locked_cert_hash_set))
 	{
 	  msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN=%s client-provided SSL certs unexpectedly changed during mid-session reauth",
 	       session->common_name);
 
 	  /* disable the tunnel */
 	  tls_deauthenticate (multi);
 	}
     }
 
88aaf1ae
   /* verify --client-config-dir based authentication */
c94eff3c
   if (ks->authenticated && session->opt->client_config_dir_exclusive)
88aaf1ae
     {
       struct gc_arena gc = gc_new ();
 
       const char *cn = session->common_name;
       const char *path = gen_path (session->opt->client_config_dir_exclusive, cn, &gc);
       if (!cn || !strcmp (cn, CCD_DEFAULT) || !test_file (path))
 	{
 	  ks->authenticated = false;
 	  msg (D_TLS_ERRORS, "TLS Auth Error: --client-config-dir authentication failed for common name '%s' file='%s'",
 	       session->common_name,
 	       path ? path : "UNDEF");
 	}
 
       gc_free (&gc);
     }
 }
ec828db6
 #endif /* ENABLE_CRYPTO */