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.
  *
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
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
9a160b79
 #include "syshead.h"
31ea2ee4
 
bf97c00f
 #include "base64.h"
9a160b79
 #include "manage.h"
270dc911
 #include "otime.h"
bf97c00f
 #include "run_command.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
81d882d5
 string_mod_remap_name(char *str, const unsigned int restrictive_flags)
e7412ca3
 {
81d882d5
     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, '_');
     }
e7412ca3
 }
 
d0811e64
 /*
  * Export the untrusted IP address and port to the environment
  */
 static void
81d882d5
 setenv_untrusted(struct tls_session *session)
d0811e64
 {
81d882d5
     setenv_link_socket_actual(session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT);
d0811e64
 }
 
daab0a9f
 
 /**
  *  Wipes the authentication token out of the memory, frees and cleans up related buffers and flags
  *
  *  @param multi  Pointer to a multi object holding the auth_token variables
  */
 static void
 wipe_auth_token(struct tls_multi *multi)
 {
     if(multi)
     {
         if (multi->auth_token)
         {
             secure_memzero(multi->auth_token, AUTH_TOKEN_SIZE);
             free(multi->auth_token);
         }
         multi->auth_token = NULL;
         multi->auth_token_sent = false;
     }
 }
 
 
d0811e64
 /*
  * Remove authenticated state from all sessions in the given tunnel
  */
82f925b6
 static void
81d882d5
 tls_deauthenticate(struct tls_multi *multi)
82f925b6
 {
81d882d5
     if (multi)
82f925b6
     {
daab0a9f
         wipe_auth_token(multi);
         for (int i = 0; i < TM_SIZE; ++i)
4cd4899e
         {
daab0a9f
             for (int j = 0; j < KS_SIZE; ++j)
4cd4899e
             {
81d882d5
                 multi->session[i].key[j].authenticated = false;
4cd4899e
             }
         }
82f925b6
     }
 }
 
e285cdb0
 /*
  * Set the given session's common_name
  */
36fae2ec
 static void
81d882d5
 set_common_name(struct tls_session *session, const char *common_name)
530af3ef
 {
81d882d5
     if (session->common_name)
530af3ef
     {
81d882d5
         free(session->common_name);
         session->common_name = NULL;
530af3ef
 #ifdef ENABLE_PF
81d882d5
         session->common_name_hashval = 0;
530af3ef
 #endif
     }
81d882d5
     if (common_name)
530af3ef
     {
81d882d5
         /* FIXME: Last alloc will never be freed */
         session->common_name = string_alloc(common_name, NULL);
530af3ef
 #ifdef ENABLE_PF
81d882d5
         {
             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;
             }
         }
530af3ef
 #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 *
81d882d5
 tls_common_name(const struct tls_multi *multi, const bool null)
530af3ef
 {
81d882d5
     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";
     }
530af3ef
 }
 
e285cdb0
 /*
  * Lock the common name for the given tunnel.
  */
530af3ef
 void
81d882d5
 tls_lock_common_name(struct tls_multi *multi)
530af3ef
 {
81d882d5
     const char *cn = multi->session[TM_ACTIVE].common_name;
     if (cn && !multi->locked_cn)
     {
         multi->locked_cn = string_alloc(cn, NULL);
     }
530af3ef
 }
 
e285cdb0
 /*
  * Lock the username for the given tunnel
  */
d0811e64
 static bool
81d882d5
 tls_lock_username(struct tls_multi *multi, const char *username)
d0811e64
 {
81d882d5
     if (multi->locked_username)
d0811e64
     {
81d882d5
         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));
d0811e64
 
81d882d5
             /* disable the tunnel */
             tls_deauthenticate(multi);
             return false;
         }
d0811e64
     }
81d882d5
     else
d0811e64
     {
81d882d5
         if (username)
         {
             multi->locked_username = string_alloc(username, NULL);
         }
d0811e64
     }
81d882d5
     return true;
d0811e64
 }
 
 const char *
81d882d5
 tls_username(const struct tls_multi *multi, const bool null)
d0811e64
 {
81d882d5
     const char *ret = NULL;
     if (multi)
     {
         ret = multi->locked_username;
     }
     if (ret && strlen(ret))
     {
         return ret;
     }
     else if (null)
     {
         return NULL;
     }
     else
     {
         return "UNDEF";
     }
d0811e64
 }
82f925b6
 
 void
81d882d5
 cert_hash_remember(struct tls_session *session, const int error_depth,
                    const struct buffer *cert_hash)
82f925b6
 {
81d882d5
     if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
82f925b6
     {
81d882d5
         if (!session->cert_hash_set)
         {
             ALLOC_OBJ_CLEAR(session->cert_hash_set, struct cert_hash_set);
         }
         if (!session->cert_hash_set->ch[error_depth])
         {
             ALLOC_OBJ(session->cert_hash_set->ch[error_depth], struct cert_hash);
         }
af1e4d26
 
81d882d5
         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
81d882d5
 cert_hash_free(struct cert_hash_set *chs)
82f925b6
 {
81d882d5
     if (chs)
82f925b6
     {
81d882d5
         int i;
         for (i = 0; i < MAX_CERT_DEPTH; ++i)
4cd4899e
         {
81d882d5
             free(chs->ch[i]);
4cd4899e
         }
81d882d5
         free(chs);
82f925b6
     }
 }
 
0c0c178a
 bool
81d882d5
 cert_hash_compare(const struct cert_hash_set *chs1, const struct cert_hash_set *chs2)
82f925b6
 {
81d882d5
     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;
             }
             else if (ch1 && ch2 && !memcmp(ch1->sha256_hash, ch2->sha256_hash,
                                            sizeof(ch1->sha256_hash)))
             {
                 continue;
             }
             else
             {
                 return false;
             }
         }
         return true;
     }
     else if (!chs1 && !chs2)
     {
         return true;
     }
     else
     {
         return false;
     }
82f925b6
 }
 
 static struct cert_hash_set *
81d882d5
 cert_hash_copy(const struct cert_hash_set *chs)
82f925b6
 {
81d882d5
     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);
                 memcpy(dest->ch[i]->sha256_hash, ch->sha256_hash,
                        sizeof(dest->ch[i]->sha256_hash));
             }
         }
     }
     return dest;
82f925b6
 }
 void
81d882d5
 tls_lock_cert_hash_set(struct tls_multi *multi)
82f925b6
 {
81d882d5
     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);
     }
82f925b6
 }
 
fe100528
 /*
06d22777
  * Returns the string associated with the given certificate type.
  */
 static const char *
81d882d5
 print_nsCertType(int type)
06d22777
 {
81d882d5
     switch (type)
06d22777
     {
81d882d5
         case NS_CERT_CHECK_SERVER:
             return "SERVER";
 
         case NS_CERT_CHECK_CLIENT:
             return "CLIENT";
 
         default:
             return "?";
06d22777
     }
 }
 
 /*
  * 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,
81d882d5
                  const char *subject, const char *common_name)
06d22777
 {
81d882d5
     /* verify certificate nsCertType */
     if (opt->ns_cert_type != NS_CERT_CHECK_NONE)
     {
         if (SUCCESS == x509_verify_ns_cert_type(peer_cert, opt->ns_cert_type))
         {
             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));
             return FAILURE;             /* Reject connection */
         }
     }
 
     /* verify certificate ku */
     if (opt->remote_cert_ku[0] != 0)
     {
         if (SUCCESS == x509_verify_cert_ku(peer_cert, opt->remote_cert_ku, MAX_PARMS))
         {
             msg(D_HANDSHAKE, "VERIFY KU OK");
         }
         else
         {
             msg(D_HANDSHAKE, "VERIFY KU ERROR");
             return FAILURE;             /* Reject connection */
         }
     }
 
     /* verify certificate eku */
     if (opt->remote_cert_eku != NULL)
     {
         if (SUCCESS == x509_verify_cert_eku(peer_cert, opt->remote_cert_eku))
         {
             msg(D_HANDSHAKE, "VERIFY EKU OK");
         }
876752ae
         else
         {
81d882d5
             msg(D_HANDSHAKE, "VERIFY EKU ERROR");
             return FAILURE;             /* Reject connection */
         }
876752ae
     }
 
81d882d5
     /* verify X509 name or username against --verify-x509-[user]name */
     if (opt->verify_x509_type != VERIFY_X509_NONE)
587f419b
     {
81d882d5
         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) )
587f419b
         {
81d882d5
             msg(D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject);
         }
         else
         {
             msg(D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s",
                 subject, opt->verify_x509_name);
             return FAILURE;             /* Reject connection */
         }
     }
 
     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,
81d882d5
                     const char *subject, const char *common_name,
                     const struct x509_track *x509_track)
fe100528
 {
81d882d5
     char envname[64];
     char *serial = NULL;
     struct gc_arena gc = gc_new();
fe100528
 
81d882d5
     /* Save X509 fields in environment */
     if (x509_track)
     {
         x509_setenv_track(x509_track, es, cert_depth, peer_cert);
     }
     else
     {
         x509_setenv(es, cert_depth, peer_cert);
     }
fe100528
 
81d882d5
     /* export subject name string as environmental variable */
     openvpn_snprintf(envname, sizeof(envname), "tls_id_%d", cert_depth);
     setenv_str(es, envname, subject);
fe100528
 
 #if 0
81d882d5
     /* export common name string as environmental variable */
     openvpn_snprintf(envname, sizeof(envname), "tls_common_name_%d", cert_depth);
     setenv_str(es, envname, common_name);
fe100528
 #endif
 
81d882d5
     /* export X509 cert fingerprints */
     {
         struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc);
         struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc);
fceecbab
 
81d882d5
         openvpn_snprintf(envname, sizeof(envname), "tls_digest_%d", cert_depth);
         setenv_str(es, envname,
                    format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc));
af1e4d26
 
81d882d5
         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
 
81d882d5
     /* export serial number as environmental variable */
     serial = backend_x509_get_serial(peer_cert, &gc);
     openvpn_snprintf(envname, sizeof(envname), "tls_serial_%d", cert_depth);
     setenv_str(es, envname, serial);
f80a52b0
 
81d882d5
     /* 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);
     setenv_str(es, envname, serial);
025f30d7
 
81d882d5
     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,
81d882d5
                         int cert_depth, openvpn_x509_cert_t *cert, char *subject)
75c67073
 {
81d882d5
     if (plugin_defined(plugins, OPENVPN_PLUGIN_TLS_VERIFY))
75c67073
     {
81d882d5
         int ret;
         struct argv argv = argv_new();
75c67073
 
81d882d5
         argv_printf(&argv, "%d %s", cert_depth, subject);
75c67073
 
81d882d5
         ret = plugin_call_ssl(plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, es, cert_depth, cert);
75c67073
 
81d882d5
         argv_reset(&argv);
75c67073
 
81d882d5
         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);
             return FAILURE;             /* Reject connection */
         }
75c67073
     }
81d882d5
     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
 {
81d882d5
     FILE *peercert_file;
     const char *peercert_filename = "";
8bb72fbc
 
3e0fd2b0
     /* create tmp file to store peer cert */
     if (!tmp_dir
b7bea782
         || !(peercert_filename = platform_create_temp_file(tmp_dir, "pcf", gc)))
81d882d5
     {
fb515a83
         msg(M_NONFATAL, "Failed to create peer cert file");
81d882d5
         return NULL;
     }
8bb72fbc
 
81d882d5
     /* write peer-cert in tmp-file */
     peercert_file = fopen(peercert_filename, "w+");
     if (!peercert_file)
8bb72fbc
     {
fb515a83
         msg(M_NONFATAL|M_ERRNO, "Failed to open temporary file: %s",
             peercert_filename);
81d882d5
         return NULL;
8bb72fbc
     }
 
81d882d5
     if (SUCCESS != x509_write_pem(peercert_file, peercert))
     {
fb515a83
         msg(M_NONFATAL, "Error writing PEM file containing certificate");
         (void) platform_unlink(peercert_filename);
         peercert_filename = NULL;
81d882d5
     }
8bb72fbc
 
81d882d5
     fclose(peercert_file);
     return peercert_filename;
8bb72fbc
 }
 
 
3e44ea55
 /*
  * run --tls-verify script
  */
8a840d83
 static result_t
3e44ea55
 verify_cert_call_command(const char *verify_command, struct env_set *es,
81d882d5
                          int cert_depth, openvpn_x509_cert_t *cert, char *subject, const char *verify_export_cert)
3e44ea55
 {
81d882d5
     const char *tmp_file = NULL;
     int ret;
     struct gc_arena gc = gc_new();
     struct argv argv = argv_new();
3e44ea55
 
81d882d5
     setenv_str(es, "script_type", "tls-verify");
3e44ea55
 
81d882d5
     if (verify_export_cert)
3e44ea55
     {
3e0fd2b0
         tmp_file = verify_cert_export_cert(cert, verify_export_cert, &gc);
         if (!tmp_file)
81d882d5
         {
3e0fd2b0
             ret = false;
             goto cleanup;
81d882d5
         }
3e0fd2b0
         setenv_str(es, "peer_cert", tmp_file);
3e44ea55
     }
 
81d882d5
     argv_parse_cmd(&argv, verify_command);
     argv_printf_cat(&argv, "%d %s", cert_depth, subject);
3e44ea55
 
81d882d5
     argv_msg_prefix(D_TLS_DEBUG, &argv, "TLS: executing verify command");
     ret = openvpn_run_script(&argv, es, 0, "--tls-verify script");
3e44ea55
 
81d882d5
     if (verify_export_cert)
3e44ea55
     {
81d882d5
         if (tmp_file)
         {
             platform_unlink(tmp_file);
         }
3e44ea55
     }
 
3e0fd2b0
 cleanup:
81d882d5
     gc_free(&gc);
     argv_reset(&argv);
3e44ea55
 
81d882d5
     if (ret)
3e44ea55
     {
81d882d5
         msg(D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
             cert_depth, subject);
         return SUCCESS;
3e44ea55
     }
 
81d882d5
     msg(D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
         cert_depth, subject);
     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
 {
81d882d5
     result_t ret = FAILURE;
     char fn[256];
     int fd = -1;
     struct gc_arena gc = gc_new();
025f30d7
 
81d882d5
     char *serial = backend_x509_get_serial(cert, &gc);
83c49a3e
 
81d882d5
     if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", crl_dir, OS_SPECIFIC_DIRSEP, serial))
83c49a3e
     {
81d882d5
         msg(D_HANDSHAKE, "VERIFY CRL: filename overflow");
         goto cleanup;
83c49a3e
     }
81d882d5
     fd = platform_open(fn, O_RDONLY, 0);
     if (fd >= 0)
83c49a3e
     {
81d882d5
         msg(D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial);
         goto cleanup;
83c49a3e
     }
 
81d882d5
     ret = SUCCESS;
83c49a3e
 
75b49e40
 cleanup:
 
81d882d5
     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
 {
81d882d5
     result_t ret = FAILURE;
     char *subject = NULL;
     char common_name[TLS_USERNAME_LEN+1] = {0}; /* null-terminated */
     const struct tls_options *opt;
     struct gc_arena gc = gc_new();
36fae2ec
 
81d882d5
     opt = session->opt;
     ASSERT(opt);
36fae2ec
 
81d882d5
     session->verified = false;
36fae2ec
 
81d882d5
     /* get the X509 name */
     subject = x509_get_subject(cert, &gc);
     if (!subject)
36fae2ec
     {
81d882d5
         msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 "
             "subject string from certificate", cert_depth);
         goto cleanup;
36fae2ec
     }
 
81d882d5
     /* enforce character class restrictions in X509 name */
     string_mod_remap_name(subject, X509_NAME_CHAR_CLASS);
     string_replace_leading(subject, '-', '_');
36fae2ec
 
81d882d5
     /* extract the username (default is CN) */
     if (SUCCESS != backend_x509_get_username(common_name, sizeof(common_name),
                                              opt->x509_username_field, cert))
36fae2ec
     {
81d882d5
         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);
             goto cleanup;
         }
36fae2ec
     }
 
81d882d5
     /* enforce character class restrictions in common name */
     string_mod_remap_name(common_name, COMMON_NAME_CHAR_CLASS);
36fae2ec
 
81d882d5
     /* warn if cert chain is too deep */
     if (cert_depth >= MAX_CERT_DEPTH)
36fae2ec
     {
81d882d5
         msg(D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", cert_depth, MAX_CERT_DEPTH);
         goto cleanup;                   /* Reject connection */
36fae2ec
     }
 
81d882d5
     /* verify level 1 cert, i.e. the CA that signed our leaf cert */
     if (cert_depth == 1 && opt->verify_hash)
36fae2ec
     {
2193d7c0
         struct buffer ca_hash = {0};
 
         switch (opt->verify_hash_algo)
         {
         case MD_SHA1:
             ca_hash = x509_get_sha1_fingerprint(cert, &gc);
             break;
 
         case MD_SHA256:
             ca_hash = x509_get_sha256_fingerprint(cert, &gc);
             break;
 
         default:
             /* This should normally not happen at all; the algorithm used
              * is parsed by add_option() [options.c] and set to a predefined
              * value in an enumerated type.  So if this unlikely scenario
              * happens, consider this a failure
              */
             msg(M_WARN, "Unexpected invalid algorithm used with "
                 "--verify-hash (%i)", opt->verify_hash_algo);
             ret = FAILURE;
             goto cleanup;
         }
 
         if (memcmp(BPTR(&ca_hash), opt->verify_hash, BLEN(&ca_hash)))
81d882d5
         {
             msg(D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
             goto cleanup;
         }
36fae2ec
     }
 
81d882d5
     /* save common name in session object */
     if (cert_depth == 0)
     {
         set_common_name(session, common_name);
     }
36fae2ec
 
81d882d5
     session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
36fae2ec
 
81d882d5
     /* export certificate values to the environment */
     verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name,
                         opt->x509_track);
36fae2ec
 
81d882d5
     /* export current untrusted IP */
     setenv_untrusted(session);
36fae2ec
 
81d882d5
     /* If this is the peer's own certificate, verify it */
     if (cert_depth == 0 && SUCCESS != verify_peer_cert(opt, cert, subject, common_name))
     {
         goto cleanup;
     }
36fae2ec
 
81d882d5
     /* call --tls-verify plug-in(s), if registered */
     if (SUCCESS != verify_cert_call_plugin(opt->plugins, opt->es, cert_depth, cert, subject))
     {
         goto cleanup;
     }
36fae2ec
 
81d882d5
     /* run --tls-verify script */
     if (opt->verify_command && SUCCESS != verify_cert_call_command(opt->verify_command,
                                                                    opt->es, cert_depth, cert, subject, opt->verify_export_cert))
     {
         goto cleanup;
     }
36fae2ec
 
81d882d5
     /* check peer cert against CRL */
     if (opt->crl_file)
36fae2ec
     {
81d882d5
         if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
         {
             if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert))
             {
                 goto cleanup;
             }
         }
         else
         {
             if (tls_verify_crl_missing(opt))
             {
                 msg(D_TLS_ERRORS, "VERIFY ERROR: CRL not loaded");
                 goto cleanup;
             }
         }
36fae2ec
     }
 
81d882d5
     msg(D_HANDSHAKE, "VERIFY OK: depth=%d, %s", cert_depth, subject);
     session->verified = true;
     ret = SUCCESS;
36fae2ec
 
75b49e40
 cleanup:
36fae2ec
 
81d882d5
     if (ret != SUCCESS)
75b49e40
     {
81d882d5
         tls_clear_error(); /* always? */
         session->verified = false; /* double sure? */
75b49e40
     }
81d882d5
     gc_free(&gc);
75b49e40
 
81d882d5
     return ret;
36fae2ec
 }
 
d0811e64
 /* ***************************************************************************
81d882d5
 * Functions for the management of deferred authentication when using
 * user/password authentication.
 *************************************************************************** */
d0811e64
 
 #ifdef ENABLE_DEF_AUTH
 /* key_state_test_auth_control_file return values,
81d882d5
  * NOTE: acf_merge indexing depends on these values */
d0811e64
 #define ACF_UNDEFINED 0
 #define ACF_SUCCEEDED 1
 #define ACF_DISABLED  2
 #define ACF_FAILED    3
 #endif
 
 #ifdef MANAGEMENT_DEF_AUTH
 void
81d882d5
 man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reason)
d0811e64
 {
81d882d5
     if (multi->client_reason)
d0811e64
     {
81d882d5
         free(multi->client_reason);
         multi->client_reason = NULL;
     }
     if (client_reason && strlen(client_reason))
     {
         /* FIXME: Last alloc will never be freed */
         multi->client_reason = string_alloc(client_reason, NULL);
d0811e64
     }
 }
 
 static inline unsigned int
81d882d5
 man_def_auth_test(const struct key_state *ks)
d0811e64
 {
81d882d5
     if (management_enable_def_auth(management))
     {
         return ks->mda_status;
     }
     else
     {
         return ACF_DISABLED;
     }
d0811e64
 }
81d882d5
 #endif /* ifdef MANAGEMENT_DEF_AUTH */
d0811e64
 
 #ifdef PLUGIN_DEF_AUTH
 
 /*
  * auth_control_file functions
  */
 
 void
81d882d5
 key_state_rm_auth_control_file(struct key_state *ks)
d0811e64
 {
81d882d5
     if (ks && ks->auth_control_file)
d0811e64
     {
81d882d5
         platform_unlink(ks->auth_control_file);
         free(ks->auth_control_file);
         ks->auth_control_file = NULL;
d0811e64
     }
 }
 
3e0fd2b0
 static bool
81d882d5
 key_state_gen_auth_control_file(struct key_state *ks, const struct tls_options *opt)
d0811e64
 {
81d882d5
     struct gc_arena gc = gc_new();
d0811e64
 
81d882d5
     key_state_rm_auth_control_file(ks);
b7bea782
     const char *acf = platform_create_temp_file(opt->tmp_dir, "acf", &gc);
81d882d5
     if (acf)
     {
         ks->auth_control_file = string_alloc(acf, NULL);
         setenv_str(opt->es, "auth_control_file", ks->auth_control_file);
3e0fd2b0
     }
d0811e64
 
81d882d5
     gc_free(&gc);
3e0fd2b0
     return acf;
d0811e64
 }
 
 static unsigned int
81d882d5
 key_state_test_auth_control_file(struct key_state *ks)
d0811e64
 {
81d882d5
     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;
d0811e64
 }
 
81d882d5
 #endif /* ifdef PLUGIN_DEF_AUTH */
d0811e64
 
 /*
  * Return current session authentication state.  Return
  * value is TLS_AUTHENTICATION_x.
  */
 
 int
81d882d5
 tls_authentication_status(struct tls_multi *multi, const int latency)
d0811e64
 {
81d882d5
     bool deferred = false;
     bool success = false;
     bool active = false;
d0811e64
 
 #ifdef ENABLE_DEF_AUTH
81d882d5
     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 */
d0811e64
     };
 #endif /* ENABLE_DEF_AUTH */
 
81d882d5
     if (multi)
d0811e64
     {
81d882d5
         int i;
d0811e64
 
 #ifdef ENABLE_DEF_AUTH
81d882d5
         if (latency && multi->tas_last && multi->tas_last + latency >= now)
         {
             return TLS_AUTHENTICATION_UNDEFINED;
         }
         multi->tas_last = now;
d0811e64
 #endif /* ENABLE_DEF_AUTH */
 
81d882d5
         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)
                 {
d0811e64
 #ifdef ENABLE_DEF_AUTH
81d882d5
                     unsigned int s1 = ACF_DISABLED;
                     unsigned int s2 = ACF_DISABLED;
d0811e64
 #ifdef PLUGIN_DEF_AUTH
81d882d5
                     s1 = key_state_test_auth_control_file(ks);
d0811e64
 #endif /* PLUGIN_DEF_AUTH */
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
                     s2 = man_def_auth_test(ks);
d0811e64
 #endif /* MANAGEMENT_DEF_AUTH */
81d882d5
                     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);
                     }
d0811e64
 #else /* !ENABLE_DEF_AUTH */
81d882d5
                     success = true;
d0811e64
 #endif /* ENABLE_DEF_AUTH */
81d882d5
                 }
             }
         }
d0811e64
     }
 
 #if 0
81d882d5
     dmsg(D_TLS_ERRORS, "TAS: a=%d s=%d d=%d", active, success, deferred);
d0811e64
 #endif
 
81d882d5
     if (success)
     {
         return TLS_AUTHENTICATION_SUCCEEDED;
     }
     else if (!active || deferred)
     {
         return TLS_AUTHENTICATION_DEFERRED;
     }
     else
     {
         return TLS_AUTHENTICATION_FAILED;
     }
d0811e64
 }
 
 #ifdef MANAGEMENT_DEF_AUTH
 /*
  * For deferred auth, this is where the management interface calls (on server)
  * to indicate auth failure/success.
  */
 bool
81d882d5
 tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason)
d0811e64
 {
81d882d5
     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;
d0811e64
 }
81d882d5
 #endif /* ifdef MANAGEMENT_DEF_AUTH */
d0811e64
 
 
 /* ****************************************************************************
  * 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
81d882d5
 verify_user_pass_script(struct tls_session *session, const struct user_pass *up)
d0811e64
 {
81d882d5
     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;
 
b7bea782
             tmp_file = platform_create_temp_file(session->opt->tmp_dir, "up",
                                                  &gc);
81d882d5
             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 */
         argv_parse_cmd(&argv, session->opt->auth_user_pass_verify_script);
         argv_printf_cat(&argv, "%s", tmp_file);
 
         /* 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)
     {
         platform_unlink(tmp_file);
     }
 
     argv_reset(&argv);
     gc_free(&gc);
     return ret;
d0811e64
 }
 
 /*
  * Verify the username and password using a plugin
  */
 static int
81d882d5
 verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, const char *raw_username)
d0811e64
 {
81d882d5
     int retval = OPENVPN_PLUGIN_FUNC_ERROR;
d0c4c442
 #ifdef PLUGIN_DEF_AUTH
81d882d5
     struct key_state *ks = &session->key[KS_PRIMARY];      /* primary key */
d0c4c442
 #endif
d0811e64
 
81d882d5
     /* Is username defined? */
     if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username))
d0811e64
     {
81d882d5
         /* set username/password in private env space */
         setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username));
         setenv_str(session->opt->es, "password", up->password);
d0811e64
 
81d882d5
         /* setenv incoming cert common name for script */
         setenv_str(session->opt->es, "common_name", session->common_name);
d0811e64
 
81d882d5
         /* setenv client real IP address */
         setenv_untrusted(session);
d0811e64
 
 #ifdef PLUGIN_DEF_AUTH
81d882d5
         /* generate filename for deferred auth control file */
3e0fd2b0
         if (!key_state_gen_auth_control_file(ks, session->opt))
         {
             msg (D_TLS_ERRORS, "TLS Auth Error (%s): "
                  "could not create deferred auth control file", __func__);
             goto cleanup;
         }
d0811e64
 #endif
 
81d882d5
         /* call command */
         retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
d0811e64
 
 #ifdef PLUGIN_DEF_AUTH
81d882d5
         /* purge auth control filename (and file itself) for non-deferred returns */
         if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
         {
             key_state_rm_auth_control_file(ks);
         }
d0811e64
 #endif
 
81d882d5
         setenv_del(session->opt->es, "password");
         if (raw_username)
         {
             setenv_str(session->opt->es, "username", up->username);
         }
d0811e64
     }
81d882d5
     else
d0811e64
     {
81d882d5
         msg(D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
d0811e64
     }
 
3e0fd2b0
 cleanup:
81d882d5
     return retval;
d0811e64
 }
 
 
 #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
81d882d5
 verify_user_pass_management(struct tls_session *session, const struct user_pass *up, const char *raw_username)
d0811e64
 {
81d882d5
     int retval = KMDA_ERROR;
     struct key_state *ks = &session->key[KS_PRIMARY];      /* primary key */
d0811e64
 
81d882d5
     /* Is username defined? */
     if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username))
d0811e64
     {
81d882d5
         /* set username/password in private env space */
         setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username));
         setenv_str(session->opt->es, "password", up->password);
d0811e64
 
81d882d5
         /* setenv incoming cert common name for script */
         setenv_str(session->opt->es, "common_name", session->common_name);
d0811e64
 
81d882d5
         /* setenv client real IP address */
         setenv_untrusted(session);
d0811e64
 
81d882d5
         if (management)
         {
             management_notify_client_needing_auth(management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
         }
d0811e64
 
81d882d5
         setenv_del(session->opt->es, "password");
         if (raw_username)
         {
             setenv_str(session->opt->es, "username", up->username);
         }
d0811e64
 
81d882d5
         retval = KMDA_SUCCESS;
d0811e64
     }
81d882d5
     else
d0811e64
     {
81d882d5
         msg(D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
d0811e64
     }
 
81d882d5
     return retval;
d0811e64
 }
81d882d5
 #endif /* ifdef MANAGEMENT_DEF_AUTH */
d0811e64
 
 /*
  * Main username/password verification entry point
  */
 void
 verify_user_pass(struct user_pass *up, struct tls_multi *multi,
81d882d5
                  struct tls_session *session)
d0811e64
 {
81d882d5
     int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
     bool s2 = true;
     struct key_state *ks = &session->key[KS_PRIMARY];      /* primary key */
d0811e64
 
81d882d5
     struct gc_arena gc = gc_new();
     char *raw_username = NULL;
e7412ca3
 
d0811e64
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
     int man_def_auth = KMDA_UNDEF;
d0811e64
 
81d882d5
     if (management_enable_def_auth(management))
     {
         man_def_auth = KMDA_DEF;
     }
d0811e64
 #endif
 
81d882d5
     /*
      * 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))
e7412ca3
     {
81d882d5
         ALLOC_ARRAY_CLEAR_GC(raw_username, char, USER_PASS_LEN, &gc);
         strcpy(raw_username, up->username);
         string_mod(raw_username, CC_PRINT, CC_CRLF, '_');
e7412ca3
     }
 
81d882d5
     /* enforce character class restrictions in username/password */
     string_mod_remap_name(up->username, COMMON_NAME_CHAR_CLASS);
     string_mod(up->password, CC_PRINT, CC_CRLF, '_');
 
     /* 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)
703c9784
     {
81d882d5
         unsigned int ssl_flags = session->opt->ssl_flags;
703c9784
 
81d882d5
         /* Ensure that the username has not changed */
         if (!tls_lock_username(multi, up->username))
703c9784
         {
daab0a9f
             /* auth-token cleared in tls_lock_username() on failure */
81d882d5
             ks->authenticated = false;
             goto done;
703c9784
         }
 
81d882d5
         /* 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)
703c9784
         {
81d882d5
             msg(D_HANDSHAKE, "Auth-token for client expired\n");
5d4cabff
             wipe_auth_token(multi);
81d882d5
             ks->authenticated = false;
             goto done;
703c9784
         }
 
81d882d5
         /* The core authentication of the token itself */
         if (memcmp_constant_time(multi->auth_token, up->password,
                                  strlen(multi->auth_token)) != 0)
703c9784
         {
81d882d5
             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]" : "");
703c9784
         }
81d882d5
         else
703c9784
         {
81d882d5
             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]" : "");
703c9784
         }
81d882d5
         goto done;
703c9784
     }
 
81d882d5
     /* call plugin(s) and/or script */
d0811e64
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
     if (man_def_auth == KMDA_DEF)
     {
         man_def_auth = verify_user_pass_management(session, up, raw_username);
     }
d0811e64
 #endif
81d882d5
     if (plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
     {
         s1 = verify_user_pass_plugin(session, up, raw_username);
     }
     if (session->opt->auth_user_pass_verify_script)
     {
         s2 = verify_user_pass_script(session, up);
     }
d0811e64
 
81d882d5
     /* check sizing of username if it will become our common name */
     if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen(up->username) > TLS_USERNAME_LEN)
d0811e64
     {
81d882d5
         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;
d0811e64
     }
 
81d882d5
     /* auth succeeded? */
     if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
d0811e64
 #ifdef PLUGIN_DEF_AUTH
81d882d5
          || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
d0811e64
 #endif
81d882d5
          ) && s2
d0811e64
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         && man_def_auth != KMDA_ERROR
d0811e64
 #endif
81d882d5
         && tls_lock_username(multi, up->username))
d0811e64
     {
81d882d5
         ks->authenticated = true;
d0811e64
 #ifdef PLUGIN_DEF_AUTH
81d882d5
         if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
         {
             ks->auth_deferred = true;
         }
d0811e64
 #endif
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (man_def_auth != KMDA_UNDEF)
         {
             ks->auth_deferred = true;
         }
d0811e64
 #endif
270dc911
 
81d882d5
         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
              */
5d4cabff
             ASSERT(openvpn_base64_encode(tok, AUTH_TOKEN_SIZE,
                                          &multi->auth_token) > AUTH_TOKEN_SIZE);
             multi->auth_token_tstamp = now;
             dmsg(D_SHOW_KEYS, "Generated token for client: %s",
                  multi->auth_token);
81d882d5
         }
 
         if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME))
         {
             set_common_name(session, up->username);
         }
5d4cabff
 
d0811e64
 #ifdef ENABLE_DEF_AUTH
81d882d5
         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]" : "");
d0811e64
 #else
81d882d5
         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]" : "");
d0811e64
 #endif
     }
81d882d5
     else
d0811e64
     {
81d882d5
         msg(D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer");
d0811e64
     }
e7412ca3
 
81d882d5
 done:
     gc_free(&gc);
d0811e64
 }
 
88aaf1ae
 void
 verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session)
 {
81d882d5
     struct key_state *ks = &session->key[KS_PRIMARY];      /* primary key */
c94eff3c
 
81d882d5
     /* While it shouldn't really happen, don't allow the common name to be NULL */
     if (!session->common_name)
     {
         set_common_name(session, "");
     }
530af3ef
 
81d882d5
     /* Don't allow the CN to change once it's been locked */
     if (ks->authenticated && multi->locked_cn)
530af3ef
     {
81d882d5
         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);
530af3ef
 
81d882d5
             /* change the common name back to its original value and disable the tunnel */
             set_common_name(session, multi->locked_cn);
             tls_deauthenticate(multi);
         }
530af3ef
     }
82f925b6
 
81d882d5
     /* Don't allow the cert hashes to change once they have been locked */
     if (ks->authenticated && multi->locked_cert_hash_set)
82f925b6
     {
81d882d5
         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);
82f925b6
 
81d882d5
             /* disable the tunnel */
             tls_deauthenticate(multi);
         }
82f925b6
     }
 
81d882d5
     /* verify --client-config-dir based authentication */
     if (ks->authenticated && session->opt->client_config_dir_exclusive)
88aaf1ae
     {
81d882d5
         struct gc_arena gc = gc_new();
88aaf1ae
 
81d882d5
         const char *cn = session->common_name;
b7bea782
         const char *path = platform_gen_path(session->opt->client_config_dir_exclusive,
                                              cn, &gc);
         if (!cn || !strcmp(cn, CCD_DEFAULT) || !platform_test_file(path))
81d882d5
         {
             ks->authenticated = false;
daab0a9f
             wipe_auth_token(multi);
81d882d5
             msg(D_TLS_ERRORS, "TLS Auth Error: --client-config-dir authentication failed for common name '%s' file='%s'",
                 session->common_name,
                 path ? path : "UNDEF");
         }
88aaf1ae
 
81d882d5
         gc_free(&gc);
88aaf1ae
     }
 }
fd036181
 
 void
 tls_x509_clear_env(struct env_set *es)
 {
     struct env_item *item = es->list;
     while (item)
     {
         struct env_item *next = item->next;
         if (item->string
             && 0 == strncmp("X509_", item->string, strlen("X509_")))
         {
             env_set_del(es, item->string);
         }
         item = next;
     }
 }