| /* |
| * 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-2018 OpenVPN Inc <sales@openvpn.net> |
| * Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> |
| * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net> |
| * |
| * 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; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| /** |
| * @file Control Channel SSL/Data channel negotiation Module |
| */ |
| |
| /* |
| * The routines in this file deal with dynamically negotiating |
| * the data channel HMAC and cipher keys through a TLS session. |
| * |
| * Both the TLS session and the data channel are multiplexed |
| * over the same TCP/UDP port. |
| */ |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| #include "win32.h" |
| |
| #if defined(ENABLE_CRYPTO) |
| |
| #include "error.h" |
| #include "common.h" |
| #include "socket.h" |
| #include "misc.h" |
| #include "fdmisc.h" |
| #include "interval.h" |
| #include "perf.h" |
| #include "status.h" |
| #include "gremlin.h" |
| #include "pkcs11.h" |
| #include "route.h" |
| #include "tls_crypt.h" |
| |
| #include "ssl.h" |
| #include "ssl_verify.h" |
| #include "ssl_backend.h" |
| |
| #include "memdbg.h" |
| |
| #ifndef ENABLE_OCC |
| static const char ssl_default_options_string[] = "V0 UNDEF"; |
| #endif |
| |
| static inline const char * |
| local_options_string(const struct tls_session *session) |
| { |
| #ifdef ENABLE_OCC |
| return session->opt->local_options; |
| #else |
| return ssl_default_options_string; |
| #endif |
| } |
| |
| #ifdef MEASURE_TLS_HANDSHAKE_STATS |
| |
| static int tls_handshake_success; /* GLOBAL */ |
| static int tls_handshake_error; /* GLOBAL */ |
| static int tls_packets_generated; /* GLOBAL */ |
| static int tls_packets_sent; /* GLOBAL */ |
| |
| #define INCR_SENT ++tls_packets_sent |
| #define INCR_GENERATED ++tls_packets_generated |
| #define INCR_SUCCESS ++tls_handshake_success |
| #define INCR_ERROR ++tls_handshake_error |
| |
| void |
| show_tls_performance_stats(void) |
| { |
| msg(D_TLS_DEBUG_LOW, "TLS Handshakes, success=%f%% (good=%d, bad=%d), retransmits=%f%%", |
| (double) tls_handshake_success / (tls_handshake_success + tls_handshake_error) * 100.0, |
| tls_handshake_success, tls_handshake_error, |
| (double) (tls_packets_sent - tls_packets_generated) / tls_packets_generated * 100.0); |
| } |
| #else /* ifdef MEASURE_TLS_HANDSHAKE_STATS */ |
| |
| #define INCR_SENT |
| #define INCR_GENERATED |
| #define INCR_SUCCESS |
| #define INCR_ERROR |
| |
| #endif /* ifdef MEASURE_TLS_HANDSHAKE_STATS */ |
| |
| /** |
| * SSL/TLS Cipher suite name translation table |
| */ |
| static const tls_cipher_name_pair tls_cipher_name_translation_table[] = { |
| {"ADH-SEED-SHA", "TLS-DH-anon-WITH-SEED-CBC-SHA"}, |
| {"AES128-GCM-SHA256", "TLS-RSA-WITH-AES-128-GCM-SHA256"}, |
| {"AES128-SHA256", "TLS-RSA-WITH-AES-128-CBC-SHA256"}, |
| {"AES128-SHA", "TLS-RSA-WITH-AES-128-CBC-SHA"}, |
| {"AES256-GCM-SHA384", "TLS-RSA-WITH-AES-256-GCM-SHA384"}, |
| {"AES256-SHA256", "TLS-RSA-WITH-AES-256-CBC-SHA256"}, |
| {"AES256-SHA", "TLS-RSA-WITH-AES-256-CBC-SHA"}, |
| {"CAMELLIA128-SHA256", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"CAMELLIA128-SHA", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"CAMELLIA256-SHA256", "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"CAMELLIA256-SHA", "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"DES-CBC3-SHA", "TLS-RSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"DES-CBC-SHA", "TLS-RSA-WITH-DES-CBC-SHA"}, |
| {"DH-DSS-SEED-SHA", "TLS-DH-DSS-WITH-SEED-CBC-SHA"}, |
| {"DHE-DSS-AES128-GCM-SHA256", "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256"}, |
| {"DHE-DSS-AES128-SHA256", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256"}, |
| {"DHE-DSS-AES128-SHA", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA"}, |
| {"DHE-DSS-AES256-GCM-SHA384", "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384"}, |
| {"DHE-DSS-AES256-SHA256", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256"}, |
| {"DHE-DSS-AES256-SHA", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA"}, |
| {"DHE-DSS-CAMELLIA128-SHA256", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"DHE-DSS-CAMELLIA128-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"DHE-DSS-CAMELLIA256-SHA256", "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"DHE-DSS-CAMELLIA256-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"DHE-DSS-SEED-SHA", "TLS-DHE-DSS-WITH-SEED-CBC-SHA"}, |
| {"DHE-RSA-AES128-GCM-SHA256", "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256"}, |
| {"DHE-RSA-AES128-SHA256", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256"}, |
| {"DHE-RSA-AES128-SHA", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA"}, |
| {"DHE-RSA-AES256-GCM-SHA384", "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384"}, |
| {"DHE-RSA-AES256-SHA256", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256"}, |
| {"DHE-RSA-AES256-SHA", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA"}, |
| {"DHE-RSA-CAMELLIA128-SHA256", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"DHE-RSA-CAMELLIA128-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"DHE-RSA-CAMELLIA256-SHA256", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"DHE-RSA-CAMELLIA256-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"DHE-RSA-CHACHA20-POLY1305", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256"}, |
| {"DHE-RSA-SEED-SHA", "TLS-DHE-RSA-WITH-SEED-CBC-SHA"}, |
| {"DH-RSA-SEED-SHA", "TLS-DH-RSA-WITH-SEED-CBC-SHA"}, |
| {"ECDH-ECDSA-AES128-GCM-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256"}, |
| {"ECDH-ECDSA-AES128-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256"}, |
| {"ECDH-ECDSA-AES128-SHA", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA"}, |
| {"ECDH-ECDSA-AES256-GCM-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384"}, |
| {"ECDH-ECDSA-AES256-SHA256", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA256"}, |
| {"ECDH-ECDSA-AES256-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384"}, |
| {"ECDH-ECDSA-AES256-SHA", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA"}, |
| {"ECDH-ECDSA-CAMELLIA128-SHA256", "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"ECDH-ECDSA-CAMELLIA128-SHA", "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"ECDH-ECDSA-CAMELLIA256-SHA256", "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"ECDH-ECDSA-CAMELLIA256-SHA", "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"ECDH-ECDSA-DES-CBC3-SHA", "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"ECDH-ECDSA-DES-CBC-SHA", "TLS-ECDH-ECDSA-WITH-DES-CBC-SHA"}, |
| {"ECDH-ECDSA-RC4-SHA", "TLS-ECDH-ECDSA-WITH-RC4-128-SHA"}, |
| {"ECDHE-ECDSA-AES128-GCM-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"}, |
| {"ECDHE-ECDSA-AES128-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256"}, |
| {"ECDHE-ECDSA-AES128-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA384"}, |
| {"ECDHE-ECDSA-AES128-SHA", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA"}, |
| {"ECDHE-ECDSA-AES256-GCM-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384"}, |
| {"ECDHE-ECDSA-AES256-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA256"}, |
| {"ECDHE-ECDSA-AES256-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384"}, |
| {"ECDHE-ECDSA-AES256-SHA", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA"}, |
| {"ECDHE-ECDSA-CAMELLIA128-SHA256", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"ECDHE-ECDSA-CAMELLIA128-SHA", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"ECDHE-ECDSA-CAMELLIA256-SHA256", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"ECDHE-ECDSA-CAMELLIA256-SHA", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"ECDHE-ECDSA-CHACHA20-POLY1305", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"}, |
| {"ECDHE-ECDSA-DES-CBC3-SHA", "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"ECDHE-ECDSA-DES-CBC-SHA", "TLS-ECDHE-ECDSA-WITH-DES-CBC-SHA"}, |
| {"ECDHE-ECDSA-RC4-SHA", "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA"}, |
| {"ECDHE-RSA-AES128-GCM-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"}, |
| {"ECDHE-RSA-AES128-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256"}, |
| {"ECDHE-RSA-AES128-SHA384", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA384"}, |
| {"ECDHE-RSA-AES128-SHA", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA"}, |
| {"ECDHE-RSA-AES256-GCM-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384"}, |
| {"ECDHE-RSA-AES256-SHA256", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA256"}, |
| {"ECDHE-RSA-AES256-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384"}, |
| {"ECDHE-RSA-AES256-SHA", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA"}, |
| {"ECDHE-RSA-CAMELLIA128-SHA256", "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"ECDHE-RSA-CAMELLIA128-SHA", "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"ECDHE-RSA-CAMELLIA256-SHA256", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"ECDHE-RSA-CAMELLIA256-SHA", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"ECDHE-RSA-CHACHA20-POLY1305", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"}, |
| {"ECDHE-RSA-DES-CBC3-SHA", "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"ECDHE-RSA-DES-CBC-SHA", "TLS-ECDHE-RSA-WITH-DES-CBC-SHA"}, |
| {"ECDHE-RSA-RC4-SHA", "TLS-ECDHE-RSA-WITH-RC4-128-SHA"}, |
| {"ECDH-RSA-AES128-GCM-SHA256", "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256"}, |
| {"ECDH-RSA-AES128-SHA256", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256"}, |
| {"ECDH-RSA-AES128-SHA384", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA384"}, |
| {"ECDH-RSA-AES128-SHA", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA"}, |
| {"ECDH-RSA-AES256-GCM-SHA384", "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384"}, |
| {"ECDH-RSA-AES256-SHA256", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA256"}, |
| {"ECDH-RSA-AES256-SHA384", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384"}, |
| {"ECDH-RSA-AES256-SHA", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA"}, |
| {"ECDH-RSA-CAMELLIA128-SHA256", "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256"}, |
| {"ECDH-RSA-CAMELLIA128-SHA", "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA"}, |
| {"ECDH-RSA-CAMELLIA256-SHA256", "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, |
| {"ECDH-RSA-CAMELLIA256-SHA", "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA"}, |
| {"ECDH-RSA-DES-CBC3-SHA", "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"ECDH-RSA-DES-CBC-SHA", "TLS-ECDH-RSA-WITH-DES-CBC-SHA"}, |
| {"ECDH-RSA-RC4-SHA", "TLS-ECDH-RSA-WITH-RC4-128-SHA"}, |
| {"EDH-DSS-DES-CBC3-SHA", "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA"}, |
| {"EDH-DSS-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA"}, |
| {"EDH-RSA-DES-CBC3-SHA", "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"EDH-RSA-DES-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA"}, |
| {"EXP-DES-CBC-SHA", "TLS-RSA-EXPORT-WITH-DES40-CBC-SHA"}, |
| {"EXP-EDH-DSS-DES-CBC-SHA", "TLS-DH-DSS-EXPORT-WITH-DES40-CBC-SHA"}, |
| {"EXP-EDH-RSA-DES-CBC-SHA", "TLS-DH-RSA-EXPORT-WITH-DES40-CBC-SHA"}, |
| {"EXP-RC2-CBC-MD5", "TLS-RSA-EXPORT-WITH-RC2-CBC-40-MD5"}, |
| {"EXP-RC4-MD5", "TLS-RSA-EXPORT-WITH-RC4-40-MD5"}, |
| {"NULL-MD5", "TLS-RSA-WITH-NULL-MD5"}, |
| {"NULL-SHA256", "TLS-RSA-WITH-NULL-SHA256"}, |
| {"NULL-SHA", "TLS-RSA-WITH-NULL-SHA"}, |
| {"PSK-3DES-EDE-CBC-SHA", "TLS-PSK-WITH-3DES-EDE-CBC-SHA"}, |
| {"PSK-AES128-CBC-SHA", "TLS-PSK-WITH-AES-128-CBC-SHA"}, |
| {"PSK-AES256-CBC-SHA", "TLS-PSK-WITH-AES-256-CBC-SHA"}, |
| {"PSK-RC4-SHA", "TLS-PSK-WITH-RC4-128-SHA"}, |
| {"RC4-MD5", "TLS-RSA-WITH-RC4-128-MD5"}, |
| {"RC4-SHA", "TLS-RSA-WITH-RC4-128-SHA"}, |
| {"SEED-SHA", "TLS-RSA-WITH-SEED-CBC-SHA"}, |
| {"SRP-DSS-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-3DES-EDE-CBC-SHA"}, |
| {"SRP-DSS-AES-128-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-128-CBC-SHA"}, |
| {"SRP-DSS-AES-256-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-256-CBC-SHA"}, |
| {"SRP-RSA-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-3DES-EDE-CBC-SHA"}, |
| {"SRP-RSA-AES-128-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA"}, |
| {"SRP-RSA-AES-256-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA"}, |
| #ifdef ENABLE_CRYPTO_OPENSSL |
| /* OpenSSL-specific group names */ |
| {"DEFAULT", "DEFAULT"}, |
| {"ALL", "ALL"}, |
| {"HIGH", "HIGH"}, {"!HIGH", "!HIGH"}, |
| {"MEDIUM", "MEDIUM"}, {"!MEDIUM", "!MEDIUM"}, |
| {"LOW", "LOW"}, {"!LOW", "!LOW"}, |
| {"ECDH", "ECDH"}, {"!ECDH", "!ECDH"}, |
| {"ECDSA", "ECDSA"}, {"!ECDSA", "!ECDSA"}, |
| {"EDH", "EDH"}, {"!EDH", "!EDH"}, |
| {"EXP", "EXP"}, {"!EXP", "!EXP"}, |
| {"RSA", "RSA"}, {"!RSA", "!RSA"}, |
| {"kRSA", "kRSA"}, {"!kRSA", "!kRSA"}, |
| {"SRP", "SRP"}, {"!SRP", "!SRP"}, |
| #endif |
| {NULL, NULL} |
| }; |
| |
| /** |
| * Update the implicit IV for a key_ctx_bi based on TLS session ids and cipher |
| * used. |
| * |
| * Note that the implicit IV is based on the HMAC key, but only in AEAD modes |
| * where the HMAC key is not used for an actual HMAC. |
| * |
| * @param ctx Encrypt/decrypt key context |
| * @param key HMAC key, used to calculate implicit IV |
| * @param key_len HMAC key length |
| */ |
| static void |
| key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len); |
| |
| const tls_cipher_name_pair * |
| tls_get_cipher_name_pair(const char *cipher_name, size_t len) |
| { |
| const tls_cipher_name_pair *pair = tls_cipher_name_translation_table; |
| |
| while (pair->openssl_name != NULL) |
| { |
| if ((strlen(pair->openssl_name) == len && 0 == memcmp(cipher_name, pair->openssl_name, len)) |
| || (strlen(pair->iana_name) == len && 0 == memcmp(cipher_name, pair->iana_name, len))) |
| { |
| return pair; |
| } |
| pair++; |
| } |
| |
| /* No entry found, return NULL */ |
| return NULL; |
| } |
| |
| /** |
| * Limit the reneg_bytes value when using a small-block (<128 bytes) cipher. |
| * |
| * @param cipher The current cipher (may be NULL). |
| * @param reneg_bytes Pointer to the current reneg_bytes, updated if needed. |
| * May *not* be NULL. |
| */ |
| static void |
| tls_limit_reneg_bytes(const cipher_kt_t *cipher, int *reneg_bytes) |
| { |
| if (cipher && (cipher_kt_block_size(cipher) < 128/8)) |
| { |
| if (*reneg_bytes == -1) /* Not user-specified */ |
| { |
| msg(M_WARN, "WARNING: cipher with small block size in use, " |
| "reducing reneg-bytes to 64MB to mitigate SWEET32 attacks."); |
| *reneg_bytes = 64 * 1024 * 1024; |
| } |
| } |
| } |
| |
| /* |
| * Max number of bytes we will add |
| * for data structures common to both |
| * data and control channel packets. |
| * (opcode only). |
| */ |
| void |
| tls_adjust_frame_parameters(struct frame *frame) |
| { |
| frame_add_to_extra_frame(frame, 1); /* space for opcode */ |
| } |
| |
| /* |
| * Max number of bytes we will add |
| * to control channel packet. |
| */ |
| static void |
| tls_init_control_channel_frame_parameters(const struct frame *data_channel_frame, |
| struct frame *frame) |
| { |
| /* |
| * frame->extra_frame is already initialized with tls_auth buffer requirements, |
| * if --tls-auth is enabled. |
| */ |
| |
| /* inherit link MTU and extra_link from data channel */ |
| frame->link_mtu = data_channel_frame->link_mtu; |
| frame->extra_link = data_channel_frame->extra_link; |
| |
| /* set extra_frame */ |
| tls_adjust_frame_parameters(frame); |
| reliable_ack_adjust_frame_parameters(frame, CONTROL_SEND_ACK_MAX); |
| frame_add_to_extra_frame(frame, SID_SIZE + sizeof(packet_id_type)); |
| |
| /* set dynamic link MTU to cap control channel packets at 1250 bytes */ |
| ASSERT(TUN_LINK_DELTA(frame) < min_int(frame->link_mtu, 1250)); |
| frame->link_mtu_dynamic = min_int(frame->link_mtu, 1250) - TUN_LINK_DELTA(frame); |
| } |
| |
| void |
| init_ssl_lib(void) |
| { |
| tls_init_lib(); |
| |
| crypto_init_lib(); |
| } |
| |
| void |
| free_ssl_lib(void) |
| { |
| crypto_uninit_lib(); |
| prng_uninit(); |
| |
| tls_free_lib(); |
| } |
| |
| /* |
| * OpenSSL library calls pem_password_callback if the |
| * private key is protected by a password. |
| */ |
| |
| static struct user_pass passbuf; /* GLOBAL */ |
| |
| void |
| pem_password_setup(const char *auth_file) |
| { |
| if (!strlen(passbuf.password)) |
| { |
| get_user_pass(&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY); |
| } |
| } |
| |
| int |
| pem_password_callback(char *buf, int size, int rwflag, void *u) |
| { |
| if (buf) |
| { |
| /* prompt for password even if --askpass wasn't specified */ |
| pem_password_setup(NULL); |
| strncpynt(buf, passbuf.password, size); |
| purge_user_pass(&passbuf, false); |
| |
| return strlen(buf); |
| } |
| return 0; |
| } |
| |
| /* |
| * Auth username/password handling |
| */ |
| |
| static bool auth_user_pass_enabled; /* GLOBAL */ |
| static struct user_pass auth_user_pass; /* GLOBAL */ |
| static struct user_pass auth_token; /* GLOBAL */ |
| |
| #ifdef ENABLE_CLIENT_CR |
| static char *auth_challenge; /* GLOBAL */ |
| #endif |
| |
| void |
| auth_user_pass_setup(const char *auth_file, const struct static_challenge_info *sci) |
| { |
| auth_user_pass_enabled = true; |
| if (!auth_user_pass.defined && !auth_token.defined) |
| { |
| #if AUTO_USERID |
| get_user_pass_auto_userid(&auth_user_pass, auth_file); |
| #else |
| #ifdef ENABLE_CLIENT_CR |
| if (auth_challenge) /* dynamic challenge/response */ |
| { |
| get_user_pass_cr(&auth_user_pass, |
| auth_file, |
| UP_TYPE_AUTH, |
| GET_USER_PASS_MANAGEMENT|GET_USER_PASS_DYNAMIC_CHALLENGE, |
| auth_challenge); |
| } |
| else if (sci) /* static challenge response */ |
| { |
| int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_STATIC_CHALLENGE; |
| if (sci->flags & SC_ECHO) |
| { |
| flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO; |
| } |
| get_user_pass_cr(&auth_user_pass, |
| auth_file, |
| UP_TYPE_AUTH, |
| flags, |
| sci->challenge_text); |
| } |
| else |
| #endif /* ifdef ENABLE_CLIENT_CR */ |
| get_user_pass(&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT); |
| #endif /* if AUTO_USERID */ |
| } |
| } |
| |
| /* |
| * Disable password caching |
| */ |
| void |
| ssl_set_auth_nocache(void) |
| { |
| passbuf.nocache = true; |
| auth_user_pass.nocache = true; |
| /* wait for push-reply, because auth-token may still need the username */ |
| auth_user_pass.wait_for_push = true; |
| } |
| |
| /* |
| * Set an authentication token |
| */ |
| void |
| ssl_set_auth_token(const char *token) |
| { |
| set_auth_token(&auth_user_pass, &auth_token, token); |
| } |
| |
| /* |
| * Cleans an auth token and checks if it was active |
| */ |
| bool |
| ssl_clean_auth_token (void) |
| { |
| bool wasdefined = auth_token.defined; |
| purge_user_pass(&auth_token, true); |
| return wasdefined; |
| } |
| |
| /* |
| * Forget private key password AND auth-user-pass username/password. |
| */ |
| void |
| ssl_purge_auth(const bool auth_user_pass_only) |
| { |
| if (!auth_user_pass_only) |
| { |
| #ifdef ENABLE_PKCS11 |
| pkcs11_logout(); |
| #endif |
| purge_user_pass(&passbuf, true); |
| } |
| purge_user_pass(&auth_user_pass, true); |
| #ifdef ENABLE_CLIENT_CR |
| ssl_purge_auth_challenge(); |
| #endif |
| } |
| |
| #ifdef ENABLE_CLIENT_CR |
| |
| void |
| ssl_purge_auth_challenge(void) |
| { |
| free(auth_challenge); |
| auth_challenge = NULL; |
| } |
| |
| void |
| ssl_put_auth_challenge(const char *cr_str) |
| { |
| ssl_purge_auth_challenge(); |
| auth_challenge = string_alloc(cr_str, NULL); |
| } |
| |
| #endif |
| |
| /* |
| * Parse a TLS version string, returning a TLS_VER_x constant. |
| * If version string is not recognized and extra == "or-highest", |
| * return tls_version_max(). |
| */ |
| int |
| tls_version_parse(const char *vstr, const char *extra) |
| { |
| const int max_version = tls_version_max(); |
| if (!strcmp(vstr, "1.0") && TLS_VER_1_0 <= max_version) |
| { |
| return TLS_VER_1_0; |
| } |
| else if (!strcmp(vstr, "1.1") && TLS_VER_1_1 <= max_version) |
| { |
| return TLS_VER_1_1; |
| } |
| else if (!strcmp(vstr, "1.2") && TLS_VER_1_2 <= max_version) |
| { |
| return TLS_VER_1_2; |
| } |
| else if (!strcmp(vstr, "1.3") && TLS_VER_1_3 <= max_version) |
| { |
| return TLS_VER_1_3; |
| } |
| else if (extra && !strcmp(extra, "or-highest")) |
| { |
| return max_version; |
| } |
| else |
| { |
| return TLS_VER_BAD; |
| } |
| } |
| |
| /** |
| * Load (or possibly reload) the CRL file into the SSL context. |
| * No reload is performed under the following conditions: |
| * - the CRL file was passed inline |
| * - the CRL file was not modified since the last (re)load |
| * |
| * @param ssl_ctx The TLS context to use when reloading the CRL |
| * @param crl_file The file name to load the CRL from, or |
| * "[[INLINE]]" in the case of inline files. |
| * @param crl_inline A string containing the CRL |
| */ |
| static void |
| tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, |
| const char *crl_file_inline) |
| { |
| /* if something goes wrong with stat(), we'll store 0 as mtime */ |
| platform_stat_t crl_stat = {0}; |
| |
| /* |
| * an inline CRL can't change at runtime, therefore there is no need to |
| * reload it. It will be reloaded upon config change + SIGHUP. |
| * Use always '1' as dummy timestamp in this case: it will trigger the |
| * first load, but will prevent any future reload. |
| */ |
| if (crl_file_inline) |
| { |
| crl_stat.st_mtime = 1; |
| } |
| else if (platform_stat(crl_file, &crl_stat) < 0) |
| { |
| msg(M_WARN, "WARNING: Failed to stat CRL file, not (re)loading CRL."); |
| return; |
| } |
| |
| /* |
| * Store the CRL if this is the first time or if the file was changed since |
| * the last load. |
| * Note: Windows does not support tv_nsec. |
| */ |
| if ((ssl_ctx->crl_last_size == crl_stat.st_size) |
| && (ssl_ctx->crl_last_mtime == crl_stat.st_mtime)) |
| { |
| return; |
| } |
| |
| ssl_ctx->crl_last_mtime = crl_stat.st_mtime; |
| ssl_ctx->crl_last_size = crl_stat.st_size; |
| backend_tls_ctx_reload_crl(ssl_ctx, crl_file, crl_file_inline); |
| } |
| |
| /* |
| * Initialize SSL context. |
| * All files are in PEM format. |
| */ |
| void |
| init_ssl(const struct options *options, struct tls_root_ctx *new_ctx) |
| { |
| ASSERT(NULL != new_ctx); |
| |
| tls_clear_error(); |
| |
| if (options->tls_server) |
| { |
| tls_ctx_server_new(new_ctx); |
| |
| if (options->dh_file) |
| { |
| tls_ctx_load_dh_params(new_ctx, options->dh_file, |
| options->dh_file_inline); |
| } |
| } |
| else /* if client */ |
| { |
| tls_ctx_client_new(new_ctx); |
| } |
| |
| /* Restrict allowed certificate crypto algorithms */ |
| tls_ctx_set_cert_profile(new_ctx, options->tls_cert_profile); |
| |
| /* Allowable ciphers */ |
| /* Since @SECLEVEL also influences loading of certificates, set the |
| * cipher restrictions before loading certificates */ |
| tls_ctx_restrict_ciphers(new_ctx, options->cipher_list); |
| tls_ctx_restrict_ciphers_tls13(new_ctx, options->cipher_list_tls13); |
| |
| if (!tls_ctx_set_options(new_ctx, options->ssl_flags)) |
| { |
| goto err; |
| } |
| |
| if (options->pkcs12_file) |
| { |
| if (0 != tls_ctx_load_pkcs12(new_ctx, options->pkcs12_file, |
| options->pkcs12_file_inline, !options->ca_file)) |
| { |
| goto err; |
| } |
| } |
| #ifdef ENABLE_PKCS11 |
| else if (options->pkcs11_providers[0]) |
| { |
| if (!tls_ctx_use_pkcs11(new_ctx, options->pkcs11_id_management, options->pkcs11_id)) |
| { |
| msg(M_WARN, "Cannot load certificate \"%s\" using PKCS#11 interface", |
| options->pkcs11_id); |
| goto err; |
| } |
| } |
| #endif |
| #ifdef ENABLE_CRYPTOAPI |
| else if (options->cryptoapi_cert) |
| { |
| tls_ctx_load_cryptoapi(new_ctx, options->cryptoapi_cert); |
| } |
| #endif |
| #ifdef MANAGMENT_EXTERNAL_KEY |
| else if ((options->management_flags & MF_EXTERNAL_KEY) |
| && (options->cert_file || options->management_flags & MF_EXTERNAL_CERT)) |
| { |
| if (options->cert_file) |
| { |
| tls_ctx_use_external_private_key(new_ctx, options->cert_file, |
| options->cert_file_inline); |
| } |
| else |
| { |
| char *external_certificate = management_query_cert(management, |
| options->management_certificate); |
| tls_ctx_use_external_private_key(new_ctx, INLINE_FILE_TAG, |
| external_certificate); |
| free(external_certificate); |
| } |
| } |
| #endif |
| else |
| { |
| /* Load Certificate */ |
| if (options->cert_file) |
| { |
| tls_ctx_load_cert_file(new_ctx, options->cert_file, options->cert_file_inline); |
| } |
| |
| /* Load Private Key */ |
| if (options->priv_key_file) |
| { |
| if (0 != tls_ctx_load_priv_file(new_ctx, options->priv_key_file, options->priv_key_file_inline)) |
| { |
| goto err; |
| } |
| } |
| } |
| |
| if (options->ca_file || options->ca_path) |
| { |
| tls_ctx_load_ca(new_ctx, options->ca_file, options->ca_file_inline, |
| options->ca_path, options->tls_server); |
| } |
| |
| /* Load extra certificates that are part of our own certificate |
| * chain but shouldn't be included in the verify chain */ |
| if (options->extra_certs_file) |
| { |
| tls_ctx_load_extra_certs(new_ctx, options->extra_certs_file, options->extra_certs_file_inline); |
| } |
| |
| /* Check certificate notBefore and notAfter */ |
| tls_ctx_check_cert_time(new_ctx); |
| |
| /* Read CRL */ |
| if (options->crl_file && !(options->ssl_flags & SSLF_CRL_VERIFY_DIR)) |
| { |
| tls_ctx_reload_crl(new_ctx, options->crl_file, options->crl_file_inline); |
| } |
| |
| /* Once keys and cert are loaded, load ECDH parameters */ |
| if (options->tls_server) |
| { |
| tls_ctx_load_ecdh_params(new_ctx, options->ecdh_curve); |
| } |
| |
| #ifdef ENABLE_CRYPTO_MBEDTLS |
| /* Personalise the random by mixing in the certificate */ |
| tls_ctx_personalise_random(new_ctx); |
| #endif |
| |
| tls_clear_error(); |
| return; |
| |
| err: |
| tls_clear_error(); |
| tls_ctx_free(new_ctx); |
| return; |
| } |
| |
| /* |
| * Map internal constants to ascii names. |
| */ |
| static const char * |
| state_name(int state) |
| { |
| switch (state) |
| { |
| case S_UNDEF: |
| return "S_UNDEF"; |
| |
| case S_INITIAL: |
| return "S_INITIAL"; |
| |
| case S_PRE_START: |
| return "S_PRE_START"; |
| |
| case S_START: |
| return "S_START"; |
| |
| case S_SENT_KEY: |
| return "S_SENT_KEY"; |
| |
| case S_GOT_KEY: |
| return "S_GOT_KEY"; |
| |
| case S_ACTIVE: |
| return "S_ACTIVE"; |
| |
| case S_NORMAL_OP: |
| return "S_NORMAL_OP"; |
| |
| case S_ERROR: |
| return "S_ERROR"; |
| |
| default: |
| return "S_???"; |
| } |
| } |
| |
| static const char * |
| packet_opcode_name(int op) |
| { |
| switch (op) |
| { |
| case P_CONTROL_HARD_RESET_CLIENT_V1: |
| return "P_CONTROL_HARD_RESET_CLIENT_V1"; |
| |
| case P_CONTROL_HARD_RESET_SERVER_V1: |
| return "P_CONTROL_HARD_RESET_SERVER_V1"; |
| |
| case P_CONTROL_HARD_RESET_CLIENT_V2: |
| return "P_CONTROL_HARD_RESET_CLIENT_V2"; |
| |
| case P_CONTROL_HARD_RESET_SERVER_V2: |
| return "P_CONTROL_HARD_RESET_SERVER_V2"; |
| |
| case P_CONTROL_SOFT_RESET_V1: |
| return "P_CONTROL_SOFT_RESET_V1"; |
| |
| case P_CONTROL_V1: |
| return "P_CONTROL_V1"; |
| |
| case P_ACK_V1: |
| return "P_ACK_V1"; |
| |
| case P_DATA_V1: |
| return "P_DATA_V1"; |
| |
| case P_DATA_V2: |
| return "P_DATA_V2"; |
| |
| default: |
| return "P_???"; |
| } |
| } |
| |
| static const char * |
| session_index_name(int index) |
| { |
| switch (index) |
| { |
| case TM_ACTIVE: |
| return "TM_ACTIVE"; |
| |
| case TM_UNTRUSTED: |
| return "TM_UNTRUSTED"; |
| |
| case TM_LAME_DUCK: |
| return "TM_LAME_DUCK"; |
| |
| default: |
| return "TM_???"; |
| } |
| } |
| |
| /* |
| * For debugging. |
| */ |
| static const char * |
| print_key_id(struct tls_multi *multi, struct gc_arena *gc) |
| { |
| int i; |
| struct buffer out = alloc_buf_gc(256, gc); |
| |
| for (i = 0; i < KEY_SCAN_SIZE; ++i) |
| { |
| struct key_state *ks = multi->key_scan[i]; |
| buf_printf(&out, " [key#%d state=%s id=%d sid=%s]", i, |
| state_name(ks->state), ks->key_id, |
| session_id_print(&ks->session_id_remote, gc)); |
| } |
| |
| return BSTR(&out); |
| } |
| |
| bool |
| is_hard_reset(int op, int key_method) |
| { |
| if (!key_method || key_method == 1) |
| { |
| if (op == P_CONTROL_HARD_RESET_CLIENT_V1 || op == P_CONTROL_HARD_RESET_SERVER_V1) |
| { |
| return true; |
| } |
| } |
| |
| if (!key_method || key_method >= 2) |
| { |
| if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** @addtogroup control_processor |
| * @{ */ |
| |
| /** @name Functions for initialization and cleanup of key_state structures |
| * @{ */ |
| |
| /** |
| * Initialize a \c key_state structure. |
| * @ingroup control_processor |
| * |
| * This function initializes a \c key_state structure associated with a \c |
| * tls_session. It sets up the structure's SSL-BIO, sets the object's \c |
| * key_state.state to \c S_INITIAL, and sets the session ID and key ID two |
| * appropriate values based on the \c tls_session's internal state. It |
| * also initializes a new set of structures for the \link reliable |
| * Reliability Layer\endlink. |
| * |
| * @param session - A pointer to the \c tls_session structure |
| * associated with the \a ks argument. |
| * @param ks - A pointer to the \c key_state structure to be |
| * initialized. This structure should already have |
| * been allocated before calling this function. |
| */ |
| static void |
| key_state_init(struct tls_session *session, struct key_state *ks) |
| { |
| update_time(); |
| |
| CLEAR(*ks); |
| |
| /* |
| * Build TLS object that reads/writes ciphertext |
| * to/from memory BIOs. |
| */ |
| key_state_ssl_init(&ks->ks_ssl, &session->opt->ssl_ctx, session->opt->server, |
| session); |
| |
| /* Set control-channel initiation mode */ |
| ks->initial_opcode = session->initial_opcode; |
| session->initial_opcode = P_CONTROL_SOFT_RESET_V1; |
| ks->state = S_INITIAL; |
| ks->key_id = session->key_id; |
| |
| /* |
| * key_id increments to KEY_ID_MASK then recycles back to 1. |
| * This way you know that if key_id is 0, it is the first key. |
| */ |
| ++session->key_id; |
| session->key_id &= P_KEY_ID_MASK; |
| if (!session->key_id) |
| { |
| session->key_id = 1; |
| } |
| |
| /* allocate key source material object */ |
| ALLOC_OBJ_CLEAR(ks->key_src, struct key_source2); |
| |
| /* allocate reliability objects */ |
| ALLOC_OBJ_CLEAR(ks->send_reliable, struct reliable); |
| ALLOC_OBJ_CLEAR(ks->rec_reliable, struct reliable); |
| ALLOC_OBJ_CLEAR(ks->rec_ack, struct reliable_ack); |
| |
| /* allocate buffers */ |
| ks->plaintext_read_buf = alloc_buf(TLS_CHANNEL_BUF_SIZE); |
| ks->plaintext_write_buf = alloc_buf(TLS_CHANNEL_BUF_SIZE); |
| ks->ack_write_buf = alloc_buf(BUF_SIZE(&session->opt->frame)); |
| reliable_init(ks->send_reliable, BUF_SIZE(&session->opt->frame), |
| FRAME_HEADROOM(&session->opt->frame), TLS_RELIABLE_N_SEND_BUFFERS, |
| ks->key_id ? false : session->opt->xmit_hold); |
| reliable_init(ks->rec_reliable, BUF_SIZE(&session->opt->frame), |
| FRAME_HEADROOM(&session->opt->frame), TLS_RELIABLE_N_REC_BUFFERS, |
| false); |
| reliable_set_timeout(ks->send_reliable, session->opt->packet_timeout); |
| |
| /* init packet ID tracker */ |
| if (session->opt->replay) |
| { |
| packet_id_init(&ks->crypto_options.packet_id, |
| session->opt->replay_window, session->opt->replay_time, "SSL", |
| ks->key_id); |
| } |
| |
| ks->crypto_options.pid_persist = NULL; |
| |
| #ifdef MANAGEMENT_DEF_AUTH |
| ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++; |
| #endif |
| } |
| |
| |
| /** |
| * Cleanup a \c key_state structure. |
| * @ingroup control_processor |
| * |
| * This function cleans up a \c key_state structure. It frees the |
| * associated SSL-BIO, and the structures allocated for the \link reliable |
| * Reliability Layer\endlink. |
| * |
| * @param ks - A pointer to the \c key_state structure to be |
| * cleaned up. |
| * @param clear - Whether the memory allocated for the \a ks object |
| * should be overwritten with 0s. |
| */ |
| static void |
| key_state_free(struct key_state *ks, bool clear) |
| { |
| ks->state = S_UNDEF; |
| |
| key_state_ssl_free(&ks->ks_ssl); |
| |
| free_key_ctx_bi(&ks->crypto_options.key_ctx_bi); |
| free_buf(&ks->plaintext_read_buf); |
| free_buf(&ks->plaintext_write_buf); |
| free_buf(&ks->ack_write_buf); |
| buffer_list_free(ks->paybuf); |
| |
| if (ks->send_reliable) |
| { |
| reliable_free(ks->send_reliable); |
| free(ks->send_reliable); |
| } |
| |
| if (ks->rec_reliable) |
| { |
| reliable_free(ks->rec_reliable); |
| free(ks->rec_reliable); |
| } |
| |
| if (ks->rec_ack) |
| { |
| free(ks->rec_ack); |
| } |
| |
| if (ks->key_src) |
| { |
| free(ks->key_src); |
| } |
| |
| packet_id_free(&ks->crypto_options.packet_id); |
| |
| #ifdef PLUGIN_DEF_AUTH |
| key_state_rm_auth_control_file(ks); |
| #endif |
| |
| if (clear) |
| { |
| secure_memzero(ks, sizeof(*ks)); |
| } |
| } |
| |
| /** @} name Functions for initialization and cleanup of key_state structures */ |
| |
| /** @} addtogroup control_processor */ |
| |
| |
| /** |
| * Returns whether or not the server should check for username/password |
| * |
| * @param session The current TLS session |
| * |
| * @return true if username and password verification is enabled, |
| * false if not. |
| */ |
| static inline bool |
| tls_session_user_pass_enabled(struct tls_session *session) |
| { |
| return (session->opt->auth_user_pass_verify_script |
| || plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
| #ifdef MANAGEMENT_DEF_AUTH |
| || management_enable_def_auth(management) |
| #endif |
| ); |
| } |
| |
| |
| /** @addtogroup control_processor |
| * @{ */ |
| |
| /** @name Functions for initialization and cleanup of tls_session structures |
| * @{ */ |
| |
| /** |
| * Initialize a \c tls_session structure. |
| * @ingroup control_processor |
| * |
| * This function initializes a \c tls_session structure. This includes |
| * generating a random session ID, and initializing the \c KS_PRIMARY \c |
| * key_state in the \c tls_session.key array. |
| * |
| * @param multi - A pointer to the \c tls_multi structure |
| * associated with the \a session argument. |
| * @param session - A pointer to the \c tls_session structure to be |
| * initialized. This structure should already have |
| * been allocated before calling this function. |
| */ |
| static void |
| tls_session_init(struct tls_multi *multi, struct tls_session *session) |
| { |
| struct gc_arena gc = gc_new(); |
| |
| dmsg(D_TLS_DEBUG, "TLS: tls_session_init: entry"); |
| |
| CLEAR(*session); |
| |
| /* Set options data to point to parent's option structure */ |
| session->opt = &multi->opt; |
| |
| /* Randomize session # if it is 0 */ |
| while (!session_id_defined(&session->session_id)) |
| { |
| session_id_random(&session->session_id); |
| } |
| |
| /* Are we a TLS server or client? */ |
| ASSERT(session->opt->key_method >= 1); |
| if (session->opt->key_method == 1) |
| { |
| session->initial_opcode = session->opt->server ? |
| P_CONTROL_HARD_RESET_SERVER_V1 : P_CONTROL_HARD_RESET_CLIENT_V1; |
| } |
| else /* session->opt->key_method >= 2 */ |
| { |
| session->initial_opcode = session->opt->server ? |
| P_CONTROL_HARD_RESET_SERVER_V2 : P_CONTROL_HARD_RESET_CLIENT_V2; |
| } |
| |
| /* Initialize control channel authentication parameters */ |
| session->tls_wrap = session->opt->tls_wrap; |
| session->tls_wrap.work = alloc_buf(BUF_SIZE(&session->opt->frame)); |
| |
| /* initialize packet ID replay window for --tls-auth */ |
| packet_id_init(&session->tls_wrap.opt.packet_id, |
| session->opt->replay_window, |
| session->opt->replay_time, |
| "TLS_WRAP", session->key_id); |
| |
| /* load most recent packet-id to replay protect on --tls-auth */ |
| packet_id_persist_load_obj(session->tls_wrap.opt.pid_persist, |
| &session->tls_wrap.opt.packet_id); |
| |
| key_state_init(session, &session->key[KS_PRIMARY]); |
| |
| dmsg(D_TLS_DEBUG, "TLS: tls_session_init: new session object, sid=%s", |
| session_id_print(&session->session_id, &gc)); |
| |
| gc_free(&gc); |
| } |
| |
| /** |
| * Clean up a \c tls_session structure. |
| * @ingroup control_processor |
| * |
| * This function cleans up a \c tls_session structure. This includes |
| * cleaning up all associated \c key_state structures. |
| * |
| * @param session - A pointer to the \c tls_session structure to be |
| * cleaned up. |
| * @param clear - Whether the memory allocated for the \a session |
| * object should be overwritten with 0s. |
| */ |
| static void |
| tls_session_free(struct tls_session *session, bool clear) |
| { |
| int i; |
| |
| if (packet_id_initialized(&session->tls_wrap.opt.packet_id)) |
| { |
| packet_id_free(&session->tls_wrap.opt.packet_id); |
| } |
| |
| free_buf(&session->tls_wrap.work); |
| |
| for (i = 0; i < KS_SIZE; ++i) |
| { |
| key_state_free(&session->key[i], false); |
| } |
| |
| if (session->common_name) |
| { |
| free(session->common_name); |
| } |
| |
| cert_hash_free(session->cert_hash_set); |
| |
| if (clear) |
| { |
| secure_memzero(session, sizeof(*session)); |
| } |
| } |
| |
| /** @} name Functions for initialization and cleanup of tls_session structures */ |
| |
| /** @} addtogroup control_processor */ |
| |
| |
| static void |
| move_session(struct tls_multi *multi, int dest, int src, bool reinit_src) |
| { |
| msg(D_TLS_DEBUG_LOW, "TLS: move_session: dest=%s src=%s reinit_src=%d", |
| session_index_name(dest), |
| session_index_name(src), |
| reinit_src); |
| ASSERT(src != dest); |
| ASSERT(src >= 0 && src < TM_SIZE); |
| ASSERT(dest >= 0 && dest < TM_SIZE); |
| tls_session_free(&multi->session[dest], false); |
| multi->session[dest] = multi->session[src]; |
| |
| if (reinit_src) |
| { |
| tls_session_init(multi, &multi->session[src]); |
| } |
| else |
| { |
| secure_memzero(&multi->session[src], sizeof(multi->session[src])); |
| } |
| |
| dmsg(D_TLS_DEBUG, "TLS: move_session: exit"); |
| } |
| |
| static void |
| reset_session(struct tls_multi *multi, struct tls_session *session) |
| { |
| tls_session_free(session, false); |
| tls_session_init(multi, session); |
| } |
| |
| /* |
| * Used to determine in how many seconds we should be |
| * called again. |
| */ |
| static inline void |
| compute_earliest_wakeup(interval_t *earliest, interval_t seconds_from_now) |
| { |
| if (seconds_from_now < *earliest) |
| { |
| *earliest = seconds_from_now; |
| } |
| if (*earliest < 0) |
| { |
| *earliest = 0; |
| } |
| } |
| |
| /* |
| * Return true if "lame duck" or retiring key has expired and can |
| * no longer be used. |
| */ |
| static inline bool |
| lame_duck_must_die(const struct tls_session *session, interval_t *wakeup) |
| { |
| const struct key_state *lame = &session->key[KS_LAME_DUCK]; |
| if (lame->state >= S_INITIAL) |
| { |
| const time_t local_now = now; |
| ASSERT(lame->must_die); /* a lame duck key must always have an expiration */ |
| if (local_now < lame->must_die) |
| { |
| compute_earliest_wakeup(wakeup, lame->must_die - local_now); |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| } |
| else if (lame->state == S_ERROR) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| struct tls_multi * |
| tls_multi_init(struct tls_options *tls_options) |
| { |
| struct tls_multi *ret; |
| |
| ALLOC_OBJ_CLEAR(ret, struct tls_multi); |
| |
| /* get command line derived options */ |
| ret->opt = *tls_options; |
| |
| /* set up list of keys to be scanned by data channel encrypt and decrypt routines */ |
| ASSERT(SIZE(ret->key_scan) == 3); |
| ret->key_scan[0] = &ret->session[TM_ACTIVE].key[KS_PRIMARY]; |
| ret->key_scan[1] = &ret->session[TM_ACTIVE].key[KS_LAME_DUCK]; |
| ret->key_scan[2] = &ret->session[TM_LAME_DUCK].key[KS_LAME_DUCK]; |
| |
| /* By default not use P_DATA_V2 */ |
| ret->use_peer_id = false; |
| |
| return ret; |
| } |
| |
| void |
| tls_multi_init_finalize(struct tls_multi *multi, const struct frame *frame) |
| { |
| tls_init_control_channel_frame_parameters(frame, &multi->opt.frame); |
| |
| /* initialize the active and untrusted sessions */ |
| |
| tls_session_init(multi, &multi->session[TM_ACTIVE]); |
| |
| if (!multi->opt.single_session) |
| { |
| tls_session_init(multi, &multi->session[TM_UNTRUSTED]); |
| } |
| } |
| |
| /* |
| * Initialize and finalize a standalone tls-auth verification object. |
| */ |
| |
| struct tls_auth_standalone * |
| tls_auth_standalone_init(struct tls_options *tls_options, |
| struct gc_arena *gc) |
| { |
| struct tls_auth_standalone *tas; |
| |
| ALLOC_OBJ_CLEAR_GC(tas, struct tls_auth_standalone, gc); |
| |
| tas->tls_wrap = tls_options->tls_wrap; |
| |
| /* |
| * Standalone tls-auth is in read-only mode with respect to TLS |
| * control channel state. After we build a new client instance |
| * object, we will process this session-initiating packet for real. |
| */ |
| tas->tls_wrap.opt.flags |= CO_IGNORE_PACKET_ID; |
| |
| /* get initial frame parms, still need to finalize */ |
| tas->frame = tls_options->frame; |
| |
| return tas; |
| } |
| |
| void |
| tls_auth_standalone_finalize(struct tls_auth_standalone *tas, |
| const struct frame *frame) |
| { |
| tls_init_control_channel_frame_parameters(frame, &tas->frame); |
| } |
| |
| /* |
| * Set local and remote option compatibility strings. |
| * Used to verify compatibility of local and remote option |
| * sets. |
| */ |
| void |
| tls_multi_init_set_options(struct tls_multi *multi, |
| const char *local, |
| const char *remote) |
| { |
| #ifdef ENABLE_OCC |
| /* initialize options string */ |
| multi->opt.local_options = local; |
| multi->opt.remote_options = remote; |
| #endif |
| } |
| |
| /* |
| * Cleanup a tls_multi structure and free associated memory allocations. |
| */ |
| void |
| tls_multi_free(struct tls_multi *multi, bool clear) |
| { |
| int i; |
| |
| ASSERT(multi); |
| |
| #ifdef MANAGEMENT_DEF_AUTH |
| man_def_auth_set_client_reason(multi, NULL); |
| |
| #endif |
| #if P2MP_SERVER |
| free(multi->peer_info); |
| #endif |
| |
| if (multi->locked_cn) |
| { |
| free(multi->locked_cn); |
| } |
| |
| if (multi->locked_username) |
| { |
| free(multi->locked_username); |
| } |
| |
| cert_hash_free(multi->locked_cert_hash_set); |
| |
| if (multi->auth_token) |
| { |
| secure_memzero(multi->auth_token, AUTH_TOKEN_SIZE); |
| free(multi->auth_token); |
| } |
| |
| free(multi->remote_ciphername); |
| |
| for (i = 0; i < TM_SIZE; ++i) |
| { |
| tls_session_free(&multi->session[i], false); |
| } |
| |
| if (clear) |
| { |
| secure_memzero(multi, sizeof(*multi)); |
| } |
| |
| free(multi); |
| } |
| |
| |
| /* |
| * Move a packet authentication HMAC + related fields to or from the front |
| * of the buffer so it can be processed by encrypt/decrypt. |
| */ |
| |
| /* |
| * Dependent on hmac size, opcode size, and session_id size. |
| * Will assert if too small. |
| */ |
| #define SWAP_BUF_SIZE 256 |
| |
| static bool |
| swap_hmac(struct buffer *buf, const struct crypto_options *co, bool incoming) |
| { |
| const struct key_ctx *ctx; |
| |
| ASSERT(co); |
| |
| ctx = (incoming ? &co->key_ctx_bi.decrypt : &co->key_ctx_bi.encrypt); |
| ASSERT(ctx->hmac); |
| |
| { |
| /* hmac + packet_id (8 bytes) */ |
| const int hmac_size = hmac_ctx_size(ctx->hmac) + packet_id_size(true); |
| |
| /* opcode + session_id */ |
| const int osid_size = 1 + SID_SIZE; |
| |
| int e1, e2; |
| uint8_t *b = BPTR(buf); |
| uint8_t buf1[SWAP_BUF_SIZE]; |
| uint8_t buf2[SWAP_BUF_SIZE]; |
| |
| if (incoming) |
| { |
| e1 = osid_size; |
| e2 = hmac_size; |
| } |
| else |
| { |
| e1 = hmac_size; |
| e2 = osid_size; |
| } |
| |
| ASSERT(e1 <= SWAP_BUF_SIZE && e2 <= SWAP_BUF_SIZE); |
| |
| if (buf->len >= e1 + e2) |
| { |
| memcpy(buf1, b, e1); |
| memcpy(buf2, b + e1, e2); |
| memcpy(b, buf2, e2); |
| memcpy(b + e2, buf1, e1); |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| } |
| |
| #undef SWAP_BUF_SIZE |
| |
| /* |
| * Write a control channel authentication record. |
| */ |
| static void |
| write_control_auth(struct tls_session *session, |
| struct key_state *ks, |
| struct buffer *buf, |
| struct link_socket_actual **to_link_addr, |
| int opcode, |
| int max_ack, |
| bool prepend_ack) |
| { |
| uint8_t header = ks->key_id | (opcode << P_OPCODE_SHIFT); |
| struct buffer null = clear_buf(); |
| |
| ASSERT(link_socket_actual_defined(&ks->remote_addr)); |
| ASSERT(reliable_ack_write |
| (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack)); |
| |
| if (session->tls_wrap.mode == TLS_WRAP_AUTH |
| || session->tls_wrap.mode == TLS_WRAP_NONE) |
| { |
| ASSERT(session_id_write_prepend(&session->session_id, buf)); |
| ASSERT(buf_write_prepend(buf, &header, sizeof(header))); |
| } |
| if (session->tls_wrap.mode == TLS_WRAP_AUTH) |
| { |
| /* no encryption, only write hmac */ |
| openvpn_encrypt(buf, null, &session->tls_wrap.opt); |
| ASSERT(swap_hmac(buf, &session->tls_wrap.opt, false)); |
| } |
| else if (session->tls_wrap.mode == TLS_WRAP_CRYPT) |
| { |
| ASSERT(buf_init(&session->tls_wrap.work, buf->offset)); |
| ASSERT(buf_write(&session->tls_wrap.work, &header, sizeof(header))); |
| ASSERT(session_id_write(&session->session_id, &session->tls_wrap.work)); |
| if (tls_crypt_wrap(buf, &session->tls_wrap.work, &session->tls_wrap.opt)) |
| { |
| /* Don't change the original data in buf, it's used by the reliability |
| * layer to resend on failure. */ |
| *buf = session->tls_wrap.work; |
| } |
| else |
| { |
| buf->len = 0; |
| return; |
| } |
| } |
| *to_link_addr = &ks->remote_addr; |
| } |
| |
| /* |
| * Read a control channel authentication record. |
| */ |
| static bool |
| read_control_auth(struct buffer *buf, |
| struct tls_wrap_ctx *ctx, |
| const struct link_socket_actual *from) |
| { |
| struct gc_arena gc = gc_new(); |
| bool ret = false; |
| |
| if (ctx->mode == TLS_WRAP_AUTH) |
| { |
| struct buffer null = clear_buf(); |
| |
| /* move the hmac record to the front of the packet */ |
| if (!swap_hmac(buf, &ctx->opt, true)) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: cannot locate HMAC in incoming packet from %s", |
| print_link_socket_actual(from, &gc)); |
| gc_free(&gc); |
| return false; |
| } |
| |
| /* authenticate only (no decrypt) and remove the hmac record |
| * from the head of the buffer */ |
| openvpn_decrypt(buf, null, &ctx->opt, NULL, BPTR(buf)); |
| if (!buf->len) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: incoming packet authentication failed from %s", |
| print_link_socket_actual(from, &gc)); |
| goto cleanup; |
| } |
| |
| } |
| else if (ctx->mode == TLS_WRAP_CRYPT) |
| { |
| struct buffer tmp = alloc_buf_gc(buf_forward_capacity_total(buf), &gc); |
| if (!tls_crypt_unwrap(buf, &tmp, &ctx->opt)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: tls-crypt unwrapping failed from %s", |
| print_link_socket_actual(from, &gc)); |
| goto cleanup; |
| } |
| ASSERT(buf_init(buf, buf->offset)); |
| ASSERT(buf_copy(buf, &tmp)); |
| buf_clear(&tmp); |
| } |
| |
| if (ctx->mode == TLS_WRAP_NONE || ctx->mode == TLS_WRAP_AUTH) |
| { |
| /* advance buffer pointer past opcode & session_id since our caller |
| * already read it */ |
| buf_advance(buf, SID_SIZE + 1); |
| } |
| |
| ret = true; |
| cleanup: |
| gc_free(&gc); |
| return ret; |
| } |
| |
| /* |
| * For debugging, print contents of key_source2 structure. |
| */ |
| |
| static void |
| key_source_print(const struct key_source *k, |
| const char *prefix) |
| { |
| struct gc_arena gc = gc_new(); |
| |
| VALGRIND_MAKE_READABLE((void *)k->pre_master, sizeof(k->pre_master)); |
| VALGRIND_MAKE_READABLE((void *)k->random1, sizeof(k->random1)); |
| VALGRIND_MAKE_READABLE((void *)k->random2, sizeof(k->random2)); |
| |
| dmsg(D_SHOW_KEY_SOURCE, |
| "%s pre_master: %s", |
| prefix, |
| format_hex(k->pre_master, sizeof(k->pre_master), 0, &gc)); |
| dmsg(D_SHOW_KEY_SOURCE, |
| "%s random1: %s", |
| prefix, |
| format_hex(k->random1, sizeof(k->random1), 0, &gc)); |
| dmsg(D_SHOW_KEY_SOURCE, |
| "%s random2: %s", |
| prefix, |
| format_hex(k->random2, sizeof(k->random2), 0, &gc)); |
| |
| gc_free(&gc); |
| } |
| |
| static void |
| key_source2_print(const struct key_source2 *k) |
| { |
| key_source_print(&k->client, "Client"); |
| key_source_print(&k->server, "Server"); |
| } |
| |
| /* |
| * Generate the hash required by for the \c tls1_PRF function. |
| * |
| * @param md_kt Message digest to use |
| * @param sec Secret to base the hash on |
| * @param sec_len Length of the secret |
| * @param seed Seed to hash |
| * @param seed_len Length of the seed |
| * @param out Output buffer |
| * @param olen Length of the output buffer |
| */ |
| static void |
| tls1_P_hash(const md_kt_t *md_kt, |
| const uint8_t *sec, |
| int sec_len, |
| const uint8_t *seed, |
| int seed_len, |
| uint8_t *out, |
| int olen) |
| { |
| struct gc_arena gc = gc_new(); |
| int chunk; |
| hmac_ctx_t *ctx; |
| hmac_ctx_t *ctx_tmp; |
| uint8_t A1[MAX_HMAC_KEY_LENGTH]; |
| unsigned int A1_len; |
| |
| #ifdef ENABLE_DEBUG |
| const int olen_orig = olen; |
| const uint8_t *out_orig = out; |
| #endif |
| |
| ctx = hmac_ctx_new(); |
| ctx_tmp = hmac_ctx_new(); |
| |
| dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc)); |
| dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc)); |
| |
| chunk = md_kt_size(md_kt); |
| A1_len = md_kt_size(md_kt); |
| |
| hmac_ctx_init(ctx, sec, sec_len, md_kt); |
| hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt); |
| |
| hmac_ctx_update(ctx,seed,seed_len); |
| hmac_ctx_final(ctx, A1); |
| |
| for (;; ) |
| { |
| hmac_ctx_reset(ctx); |
| hmac_ctx_reset(ctx_tmp); |
| hmac_ctx_update(ctx,A1,A1_len); |
| hmac_ctx_update(ctx_tmp,A1,A1_len); |
| hmac_ctx_update(ctx,seed,seed_len); |
| |
| if (olen > chunk) |
| { |
| hmac_ctx_final(ctx, out); |
| out += chunk; |
| olen -= chunk; |
| hmac_ctx_final(ctx_tmp, A1); /* calc the next A1 value */ |
| } |
| else /* last one */ |
| { |
| hmac_ctx_final(ctx, A1); |
| memcpy(out,A1,olen); |
| break; |
| } |
| } |
| hmac_ctx_cleanup(ctx); |
| hmac_ctx_free(ctx); |
| hmac_ctx_cleanup(ctx_tmp); |
| hmac_ctx_free(ctx_tmp); |
| secure_memzero(A1, sizeof(A1)); |
| |
| dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex(out_orig, olen_orig, 0, &gc)); |
| gc_free(&gc); |
| } |
| |
| /* |
| * Use the TLS PRF function for generating data channel keys. |
| * This code is based on the OpenSSL library. |
| * |
| * TLS generates keys as such: |
| * |
| * master_secret[48] = PRF(pre_master_secret[48], "master secret", |
| * ClientHello.random[32] + ServerHello.random[32]) |
| * |
| * key_block[] = PRF(SecurityParameters.master_secret[48], |
| * "key expansion", |
| * SecurityParameters.server_random[32] + |
| * SecurityParameters.client_random[32]); |
| * |
| * Notes: |
| * |
| * (1) key_block contains a full set of 4 keys. |
| * (2) The pre-master secret is generated by the client. |
| */ |
| static void |
| tls1_PRF(const uint8_t *label, |
| int label_len, |
| const uint8_t *sec, |
| int slen, |
| uint8_t *out1, |
| int olen) |
| { |
| struct gc_arena gc = gc_new(); |
| const md_kt_t *md5 = md_kt_get("MD5"); |
| const md_kt_t *sha1 = md_kt_get("SHA1"); |
| int len,i; |
| const uint8_t *S1,*S2; |
| uint8_t *out2; |
| |
| out2 = (uint8_t *) gc_malloc(olen, false, &gc); |
| |
| len = slen/2; |
| S1 = sec; |
| S2 = &(sec[len]); |
| len += (slen&1); /* add for odd, make longer */ |
| |
| tls1_P_hash(md5,S1,len,label,label_len,out1,olen); |
| tls1_P_hash(sha1,S2,len,label,label_len,out2,olen); |
| |
| for (i = 0; i<olen; i++) |
| { |
| out1[i] ^= out2[i]; |
| } |
| |
| secure_memzero(out2, olen); |
| |
| dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc)); |
| |
| gc_free(&gc); |
| } |
| |
| static void |
| openvpn_PRF(const uint8_t *secret, |
| int secret_len, |
| const char *label, |
| const uint8_t *client_seed, |
| int client_seed_len, |
| const uint8_t *server_seed, |
| int server_seed_len, |
| const struct session_id *client_sid, |
| const struct session_id *server_sid, |
| uint8_t *output, |
| int output_len) |
| { |
| /* concatenate seed components */ |
| |
| struct buffer seed = alloc_buf(strlen(label) |
| + client_seed_len |
| + server_seed_len |
| + SID_SIZE * 2); |
| |
| ASSERT(buf_write(&seed, label, strlen(label))); |
| ASSERT(buf_write(&seed, client_seed, client_seed_len)); |
| ASSERT(buf_write(&seed, server_seed, server_seed_len)); |
| |
| if (client_sid) |
| { |
| ASSERT(buf_write(&seed, client_sid->id, SID_SIZE)); |
| } |
| if (server_sid) |
| { |
| ASSERT(buf_write(&seed, server_sid->id, SID_SIZE)); |
| } |
| |
| /* compute PRF */ |
| tls1_PRF(BPTR(&seed), BLEN(&seed), secret, secret_len, output, output_len); |
| |
| buf_clear(&seed); |
| free_buf(&seed); |
| |
| VALGRIND_MAKE_READABLE((void *)output, output_len); |
| } |
| |
| /* |
| * Using source entropy from local and remote hosts, mix into |
| * master key. |
| */ |
| static bool |
| generate_key_expansion(struct key_ctx_bi *key, |
| const struct key_type *key_type, |
| const struct key_source2 *key_src, |
| const struct session_id *client_sid, |
| const struct session_id *server_sid, |
| bool server) |
| { |
| uint8_t master[48] = { 0 }; |
| struct key2 key2 = { 0 }; |
| bool ret = false; |
| |
| if (key->initialized) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: key already initialized"); |
| goto exit; |
| } |
| |
| /* debugging print of source key material */ |
| key_source2_print(key_src); |
| |
| /* compute master secret */ |
| openvpn_PRF(key_src->client.pre_master, |
| sizeof(key_src->client.pre_master), |
| KEY_EXPANSION_ID " master secret", |
| key_src->client.random1, |
| sizeof(key_src->client.random1), |
| key_src->server.random1, |
| sizeof(key_src->server.random1), |
| NULL, |
| NULL, |
| master, |
| sizeof(master)); |
| |
| /* compute key expansion */ |
| openvpn_PRF(master, |
| sizeof(master), |
| KEY_EXPANSION_ID " key expansion", |
| key_src->client.random2, |
| sizeof(key_src->client.random2), |
| key_src->server.random2, |
| sizeof(key_src->server.random2), |
| client_sid, |
| server_sid, |
| (uint8_t *)key2.keys, |
| sizeof(key2.keys)); |
| |
| key2.n = 2; |
| |
| key2_print(&key2, key_type, "Master Encrypt", "Master Decrypt"); |
| |
| /* check for weak keys */ |
| for (int i = 0; i < 2; ++i) |
| { |
| fixup_key(&key2.keys[i], key_type); |
| if (!check_key(&key2.keys[i], key_type)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Bad dynamic key generated"); |
| goto exit; |
| } |
| } |
| |
| /* Initialize OpenSSL key contexts */ |
| int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL; |
| init_key_ctx_bi(key, &key2, key_direction, key_type, "Data Channel"); |
| |
| /* Initialize implicit IVs */ |
| key_ctx_update_implicit_iv(&key->encrypt, key2.keys[(int)server].hmac, |
| MAX_HMAC_KEY_LENGTH); |
| key_ctx_update_implicit_iv(&key->decrypt, key2.keys[1-(int)server].hmac, |
| MAX_HMAC_KEY_LENGTH); |
| |
| ret = true; |
| |
| exit: |
| secure_memzero(&master, sizeof(master)); |
| secure_memzero(&key2, sizeof(key2)); |
| |
| return ret; |
| } |
| |
| static void |
| key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) |
| { |
| const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt(ctx->cipher); |
| |
| /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */ |
| if (cipher_kt_mode_aead(cipher_kt)) |
| { |
| size_t impl_iv_len = 0; |
| ASSERT(cipher_kt_iv_size(cipher_kt) >= OPENVPN_AEAD_MIN_IV_LEN); |
| impl_iv_len = cipher_kt_iv_size(cipher_kt) - sizeof(packet_id_type); |
| ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH); |
| ASSERT(impl_iv_len <= key_len); |
| memcpy(ctx->implicit_iv, key, impl_iv_len); |
| ctx->implicit_iv_len = impl_iv_len; |
| } |
| } |
| |
| bool |
| tls_item_in_cipher_list(const char *item, const char *list) |
| { |
| char *tmp_ciphers = string_alloc(list, NULL); |
| char *tmp_ciphers_orig = tmp_ciphers; |
| |
| const char *token = strtok(tmp_ciphers, ":"); |
| while (token) |
| { |
| if (0 == strcmp(token, item)) |
| { |
| break; |
| } |
| token = strtok(NULL, ":"); |
| } |
| free(tmp_ciphers_orig); |
| |
| return token != NULL; |
| } |
| |
| void |
| tls_poor_mans_ncp(struct options *o, const char *remote_ciphername) |
| { |
| if (o->ncp_enabled && remote_ciphername |
| && 0 != strcmp(o->ciphername, remote_ciphername)) |
| { |
| if (tls_item_in_cipher_list(remote_ciphername, o->ncp_ciphers)) |
| { |
| o->ciphername = string_alloc(remote_ciphername, &o->gc); |
| msg(D_TLS_DEBUG_LOW, "Using peer cipher '%s'", o->ciphername); |
| } |
| } |
| } |
| |
| /** |
| * Generate data channel keys for the supplied TLS session. |
| * |
| * This erases the source material used to generate the data channel keys, and |
| * can thus be called only once per session. |
| */ |
| static bool |
| tls_session_generate_data_channel_keys(struct tls_session *session) |
| { |
| bool ret = false; |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| const struct session_id *client_sid = session->opt->server ? |
| &ks->session_id_remote : &session->session_id; |
| const struct session_id *server_sid = !session->opt->server ? |
| &ks->session_id_remote : &session->session_id; |
| |
| ASSERT(ks->authenticated); |
| |
| ks->crypto_options.flags = session->opt->crypto_flags; |
| if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, |
| &session->opt->key_type, ks->key_src, client_sid, server_sid, |
| session->opt->server)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed"); |
| goto cleanup; |
| } |
| tls_limit_reneg_bytes(session->opt->key_type.cipher, |
| &session->opt->renegotiate_bytes); |
| |
| ret = true; |
| cleanup: |
| secure_memzero(ks->key_src, sizeof(*ks->key_src)); |
| return ret; |
| } |
| |
| bool |
| tls_session_update_crypto_params(struct tls_session *session, |
| struct options *options, struct frame *frame, |
| struct frame *frame_fragment) |
| { |
| if (session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized) |
| { |
| /* keys already generated, nothing to do */ |
| return true; |
| } |
| |
| if (!session->opt->server |
| && 0 != strcmp(options->ciphername, session->opt->config_ciphername) |
| && !tls_item_in_cipher_list(options->ciphername, options->ncp_ciphers)) |
| { |
| msg(D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", |
| options->ciphername, session->opt->config_ciphername, |
| options->ncp_ciphers); |
| /* undo cipher push, abort connection setup */ |
| options->ciphername = session->opt->config_ciphername; |
| return false; |
| } |
| |
| if (strcmp(options->ciphername, session->opt->config_ciphername)) |
| { |
| msg(D_HANDSHAKE, "Data Channel: using negotiated cipher '%s'", |
| options->ciphername); |
| if (options->keysize) |
| { |
| msg(D_HANDSHAKE, "NCP: overriding user-set keysize with default"); |
| options->keysize = 0; |
| } |
| } |
| |
| init_key_type(&session->opt->key_type, options->ciphername, |
| options->authname, options->keysize, true, true); |
| |
| bool packet_id_long_form = cipher_kt_mode_ofb_cfb(session->opt->key_type.cipher); |
| session->opt->crypto_flags &= ~(CO_PACKET_ID_LONG_FORM); |
| if (packet_id_long_form) |
| { |
| session->opt->crypto_flags |= CO_PACKET_ID_LONG_FORM; |
| } |
| |
| /* Update frame parameters: undo worst-case overhead, add actual overhead */ |
| frame_remove_from_extra_frame(frame, crypto_max_overhead()); |
| crypto_adjust_frame_parameters(frame, &session->opt->key_type, |
| options->use_iv, options->replay, packet_id_long_form); |
| frame_finalize(frame, options->ce.link_mtu_defined, options->ce.link_mtu, |
| options->ce.tun_mtu_defined, options->ce.tun_mtu); |
| frame_init_mssfix(frame, options); |
| frame_print(frame, D_MTU_INFO, "Data Channel MTU parms"); |
| |
| /* |
| * mssfix uses data channel framing, which at this point contains |
| * actual overhead. Fragmentation logic uses frame_fragment, which |
| * still contains worst case overhead. Replace it with actual overhead |
| * to prevent unneeded fragmentation. |
| */ |
| |
| if (frame_fragment) |
| { |
| frame_remove_from_extra_frame(frame_fragment, crypto_max_overhead()); |
| crypto_adjust_frame_parameters(frame_fragment, &session->opt->key_type, |
| options->use_iv, options->replay, packet_id_long_form); |
| frame_set_mtu_dynamic(frame_fragment, options->ce.fragment, SET_MTU_UPPER_BOUND); |
| frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms"); |
| } |
| |
| return tls_session_generate_data_channel_keys(session); |
| } |
| |
| static bool |
| random_bytes_to_buf(struct buffer *buf, |
| uint8_t *out, |
| int outlen) |
| { |
| if (!rand_bytes(out, outlen)) |
| { |
| msg(M_FATAL, "ERROR: Random number generator cannot obtain entropy for key generation [SSL]"); |
| } |
| if (!buf_write(buf, out, outlen)) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| static bool |
| key_source2_randomize_write(struct key_source2 *k2, |
| struct buffer *buf, |
| bool server) |
| { |
| struct key_source *k = &k2->client; |
| if (server) |
| { |
| k = &k2->server; |
| } |
| |
| CLEAR(*k); |
| |
| if (!server) |
| { |
| if (!random_bytes_to_buf(buf, k->pre_master, sizeof(k->pre_master))) |
| { |
| return false; |
| } |
| } |
| |
| if (!random_bytes_to_buf(buf, k->random1, sizeof(k->random1))) |
| { |
| return false; |
| } |
| if (!random_bytes_to_buf(buf, k->random2, sizeof(k->random2))) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int |
| key_source2_read(struct key_source2 *k2, |
| struct buffer *buf, |
| bool server) |
| { |
| struct key_source *k = &k2->client; |
| |
| if (!server) |
| { |
| k = &k2->server; |
| } |
| |
| CLEAR(*k); |
| |
| if (server) |
| { |
| if (!buf_read(buf, k->pre_master, sizeof(k->pre_master))) |
| { |
| return 0; |
| } |
| } |
| |
| if (!buf_read(buf, k->random1, sizeof(k->random1))) |
| { |
| return 0; |
| } |
| if (!buf_read(buf, k->random2, sizeof(k->random2))) |
| { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void |
| flush_payload_buffer(struct key_state *ks) |
| { |
| struct buffer *b; |
| |
| while ((b = buffer_list_peek(ks->paybuf))) |
| { |
| key_state_write_plaintext_const(&ks->ks_ssl, b->data, b->len); |
| buffer_list_pop(ks->paybuf); |
| } |
| } |
| |
| /* true if no in/out acknowledgements pending */ |
| #define FULL_SYNC \ |
| (reliable_empty(ks->send_reliable) && reliable_ack_empty(ks->rec_ack)) |
| |
| /* |
| * Move the active key to the lame duck key and reinitialize the |
| * active key. |
| */ |
| static void |
| key_state_soft_reset(struct tls_session *session) |
| { |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ |
| |
| ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */ |
| key_state_free(ks_lame, false); |
| *ks_lame = *ks; |
| |
| key_state_init(session, ks); |
| ks->session_id_remote = ks_lame->session_id_remote; |
| ks->remote_addr = ks_lame->remote_addr; |
| } |
| |
| /* |
| * Read/write strings from/to a struct buffer with a u16 length prefix. |
| */ |
| |
| static bool |
| write_empty_string(struct buffer *buf) |
| { |
| if (!buf_write_u16(buf, 0)) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| static bool |
| write_string(struct buffer *buf, const char *str, const int maxlen) |
| { |
| const int len = strlen(str) + 1; |
| if (len < 1 || (maxlen >= 0 && len > maxlen)) |
| { |
| return false; |
| } |
| if (!buf_write_u16(buf, len)) |
| { |
| return false; |
| } |
| if (!buf_write(buf, str, len)) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| static bool |
| read_string(struct buffer *buf, char *str, const unsigned int capacity) |
| { |
| const int len = buf_read_u16(buf); |
| if (len < 1 || len > (int)capacity) |
| { |
| return false; |
| } |
| if (!buf_read(buf, str, len)) |
| { |
| return false; |
| } |
| str[len-1] = '\0'; |
| return true; |
| } |
| |
| static char * |
| read_string_alloc(struct buffer *buf) |
| { |
| const int len = buf_read_u16(buf); |
| char *str; |
| |
| if (len < 1) |
| { |
| return NULL; |
| } |
| str = (char *) malloc(len); |
| check_malloc_return(str); |
| if (!buf_read(buf, str, len)) |
| { |
| free(str); |
| return NULL; |
| } |
| str[len-1] = '\0'; |
| return str; |
| } |
| |
| /* |
| * Handle the reading and writing of key data to and from |
| * the TLS control channel (cleartext). |
| */ |
| |
| static bool |
| key_method_1_write(struct buffer *buf, struct tls_session *session) |
| { |
| struct key key; |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| |
| ASSERT(session->opt->key_method == 1); |
| ASSERT(buf_init(buf, 0)); |
| |
| generate_key_random(&key, &session->opt->key_type); |
| if (!check_key(&key, &session->opt->key_type)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Bad encrypting key generated"); |
| return false; |
| } |
| |
| if (!write_key(&key, &session->opt->key_type, buf)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: write_key failed"); |
| return false; |
| } |
| |
| init_key_ctx(&ks->crypto_options.key_ctx_bi.encrypt, &key, |
| &session->opt->key_type, OPENVPN_OP_ENCRYPT, |
| "Data Channel Encrypt"); |
| secure_memzero(&key, sizeof(key)); |
| |
| /* send local options string */ |
| { |
| const char *local_options = local_options_string(session); |
| const int optlen = strlen(local_options) + 1; |
| if (!buf_write(buf, local_options, optlen)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: KM1 write options failed"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static bool |
| push_peer_info(struct buffer *buf, struct tls_session *session) |
| { |
| struct gc_arena gc = gc_new(); |
| bool ret = false; |
| |
| #ifdef ENABLE_PUSH_PEER_INFO |
| if (session->opt->push_peer_info_detail > 0) |
| { |
| struct env_set *es = session->opt->es; |
| struct env_item *e; |
| struct buffer out = alloc_buf_gc(512*3, &gc); |
| |
| /* push version */ |
| buf_printf(&out, "IV_VER=%s\n", PACKAGE_VERSION); |
| |
| /* push platform */ |
| #if defined(TARGET_LINUX) |
| buf_printf(&out, "IV_PLAT=linux\n"); |
| #elif defined(TARGET_SOLARIS) |
| buf_printf(&out, "IV_PLAT=solaris\n"); |
| #elif defined(TARGET_OPENBSD) |
| buf_printf(&out, "IV_PLAT=openbsd\n"); |
| #elif defined(TARGET_DARWIN) |
| buf_printf(&out, "IV_PLAT=mac\n"); |
| #elif defined(TARGET_NETBSD) |
| buf_printf(&out, "IV_PLAT=netbsd\n"); |
| #elif defined(TARGET_FREEBSD) |
| buf_printf(&out, "IV_PLAT=freebsd\n"); |
| #elif defined(TARGET_ANDROID) |
| buf_printf(&out, "IV_PLAT=android\n"); |
| #elif defined(_WIN32) |
| buf_printf(&out, "IV_PLAT=win\n"); |
| #endif |
| |
| /* support for P_DATA_V2 */ |
| buf_printf(&out, "IV_PROTO=2\n"); |
| |
| /* support for Negotiable Crypto Paramters */ |
| if (session->opt->ncp_enabled |
| && (session->opt->mode == MODE_SERVER || session->opt->pull)) |
| { |
| buf_printf(&out, "IV_NCP=2\n"); |
| } |
| |
| /* push compression status */ |
| #ifdef USE_COMP |
| comp_generate_peer_info_string(&session->opt->comp_options, &out); |
| #endif |
| |
| if (session->opt->push_peer_info_detail >= 2) |
| { |
| /* push mac addr */ |
| struct route_gateway_info rgi; |
| get_default_gateway(&rgi); |
| if (rgi.flags & RGI_HWADDR_DEFINED) |
| { |
| buf_printf(&out, "IV_HWADDR=%s\n", format_hex_ex(rgi.hwaddr, 6, 0, 1, ":", &gc)); |
| } |
| buf_printf(&out, "IV_SSL=%s\n", get_ssl_library_version() ); |
| #if defined(_WIN32) |
| buf_printf(&out, "IV_PLAT_VER=%s\n", win32_version_string(&gc, false)); |
| #endif |
| } |
| |
| /* push env vars that begin with UV_, IV_PLAT_VER and IV_GUI_VER */ |
| for (e = es->list; e != NULL; e = e->next) |
| { |
| if (e->string) |
| { |
| if ((((strncmp(e->string, "UV_", 3)==0 |
| || strncmp(e->string, "IV_PLAT_VER=", sizeof("IV_PLAT_VER=")-1)==0) |
| && session->opt->push_peer_info_detail >= 2) |
| || (strncmp(e->string,"IV_GUI_VER=",sizeof("IV_GUI_VER=")-1)==0)) |
| && buf_safe(&out, strlen(e->string)+1)) |
| { |
| buf_printf(&out, "%s\n", e->string); |
| } |
| } |
| } |
| |
| if (!write_string(buf, BSTR(&out), -1)) |
| { |
| goto error; |
| } |
| } |
| else |
| #endif /* ifdef ENABLE_PUSH_PEER_INFO */ |
| { |
| if (!write_empty_string(buf)) /* no peer info */ |
| { |
| goto error; |
| } |
| } |
| ret = true; |
| |
| error: |
| gc_free(&gc); |
| return ret; |
| } |
| |
| static bool |
| key_method_2_write(struct buffer *buf, struct tls_session *session) |
| { |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| |
| ASSERT(session->opt->key_method == 2); |
| ASSERT(buf_init(buf, 0)); |
| |
| /* write a uint32 0 */ |
| if (!buf_write_u32(buf, 0)) |
| { |
| goto error; |
| } |
| |
| /* write key_method + flags */ |
| if (!buf_write_u8(buf, (session->opt->key_method & KEY_METHOD_MASK))) |
| { |
| goto error; |
| } |
| |
| /* write key source material */ |
| if (!key_source2_randomize_write(ks->key_src, buf, session->opt->server)) |
| { |
| goto error; |
| } |
| |
| /* write options string */ |
| { |
| if (!write_string(buf, local_options_string(session), TLS_OPTIONS_LEN)) |
| { |
| goto error; |
| } |
| } |
| |
| /* write username/password if specified */ |
| if (auth_user_pass_enabled) |
| { |
| #ifdef ENABLE_CLIENT_CR |
| auth_user_pass_setup(session->opt->auth_user_pass_file, session->opt->sci); |
| #else |
| auth_user_pass_setup(session->opt->auth_user_pass_file, NULL); |
| #endif |
| struct user_pass *up = &auth_user_pass; |
| |
| /* |
| * If we have a valid auth-token, send that instead of real |
| * username/password |
| */ |
| if (auth_token.defined) |
| up = &auth_token; |
| |
| if (!write_string(buf, up->username, -1)) |
| { |
| goto error; |
| } |
| else if (!write_string(buf, up->password, -1)) |
| { |
| goto error; |
| } |
| /* if auth-nocache was specified, the auth_user_pass object reaches |
| * a "complete" state only after having received the push-reply |
| * message. |
| * |
| * For this reason, skip the purge operation here if no push-reply |
| * message has been received yet. |
| * |
| * This normally happens upon first negotiation only. |
| */ |
| if (!auth_user_pass.wait_for_push) |
| { |
| purge_user_pass(&auth_user_pass, false); |
| } |
| } |
| else |
| { |
| if (!write_empty_string(buf)) /* no username */ |
| { |
| goto error; |
| } |
| if (!write_empty_string(buf)) /* no password */ |
| { |
| goto error; |
| } |
| } |
| |
| if (!push_peer_info(buf, session)) |
| { |
| goto error; |
| } |
| |
| /* Generate tunnel keys if we're a TLS server. |
| * If we're a p2mp server and IV_NCP >= 2 is negotiated, the first key |
| * generation is postponed until after the pull/push, so we can process pushed |
| * cipher directives. |
| */ |
| if (session->opt->server && !(session->opt->ncp_enabled |
| && session->opt->mode == MODE_SERVER && ks->key_id <= 0)) |
| { |
| if (ks->authenticated) |
| { |
| if (!tls_session_generate_data_channel_keys(session)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed"); |
| goto error; |
| } |
| } |
| } |
| |
| return true; |
| |
| error: |
| msg(D_TLS_ERRORS, "TLS Error: Key Method #2 write failed"); |
| secure_memzero(ks->key_src, sizeof(*ks->key_src)); |
| return false; |
| } |
| |
| static bool |
| key_method_1_read(struct buffer *buf, struct tls_session *session) |
| { |
| int status; |
| struct key key; |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| |
| ASSERT(session->opt->key_method == 1); |
| |
| if (!session->verified) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: Certificate verification failed (key-method 1)"); |
| goto error; |
| } |
| |
| status = read_key(&key, &session->opt->key_type, buf); |
| if (status != 1) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: Error reading data channel key from plaintext buffer"); |
| goto error; |
| } |
| |
| if (!check_key(&key, &session->opt->key_type)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Bad decrypting key received from peer"); |
| goto error; |
| } |
| |
| if (buf->len < 1) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Missing options string"); |
| goto error; |
| } |
| |
| #ifdef ENABLE_OCC |
| /* compare received remote options string |
| * with our locally computed options string */ |
| if (!session->opt->disable_occ |
| && !options_cmp_equal_safe((char *) BPTR(buf), session->opt->remote_options, buf->len)) |
| { |
| options_warning_safe((char *) BPTR(buf), session->opt->remote_options, buf->len); |
| } |
| #endif |
| |
| buf_clear(buf); |
| |
| init_key_ctx(&ks->crypto_options.key_ctx_bi.decrypt, &key, |
| &session->opt->key_type, OPENVPN_OP_DECRYPT, |
| "Data Channel Decrypt"); |
| secure_memzero(&key, sizeof(key)); |
| ks->authenticated = true; |
| return true; |
| |
| error: |
| buf_clear(buf); |
| secure_memzero(&key, sizeof(key)); |
| return false; |
| } |
| |
| static bool |
| key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_session *session) |
| { |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| |
| int key_method_flags; |
| bool username_status, password_status; |
| |
| struct gc_arena gc = gc_new(); |
| char *options; |
| struct user_pass *up = NULL; |
| |
| /* allocate temporary objects */ |
| ALLOC_ARRAY_CLEAR_GC(options, char, TLS_OPTIONS_LEN, &gc); |
| |
| ASSERT(session->opt->key_method == 2); |
| |
| /* discard leading uint32 */ |
| if (!buf_advance(buf, 4)) |
| { |
| msg(D_TLS_ERRORS, "TLS ERROR: Plaintext buffer too short (%d bytes).", |
| buf->len); |
| goto error; |
| } |
| |
| /* get key method */ |
| key_method_flags = buf_read_u8(buf); |
| if ((key_method_flags & KEY_METHOD_MASK) != 2) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS ERROR: Unknown key_method/flags=%d received from remote host", |
| key_method_flags); |
| goto error; |
| } |
| |
| /* get key source material (not actual keys yet) */ |
| if (!key_source2_read(ks->key_src, buf, session->opt->server)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Error reading remote data channel key source entropy from plaintext buffer"); |
| goto error; |
| } |
| |
| /* get options */ |
| if (!read_string(buf, options, TLS_OPTIONS_LEN)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Failed to read required OCC options string"); |
| goto error; |
| } |
| |
| ks->authenticated = false; |
| |
| /* always extract username + password fields from buf, even if not |
| * authenticating for it, because otherwise we can't get at the |
| * peer_info data which follows behind |
| */ |
| ALLOC_OBJ_CLEAR_GC(up, struct user_pass, &gc); |
| username_status = read_string(buf, up->username, USER_PASS_LEN); |
| password_status = read_string(buf, up->password, USER_PASS_LEN); |
| |
| #if P2MP_SERVER |
| /* get peer info from control channel */ |
| free(multi->peer_info); |
| multi->peer_info = read_string_alloc(buf); |
| if (multi->peer_info) |
| { |
| output_peer_info_env(session->opt->es, multi->peer_info); |
| } |
| |
| free(multi->remote_ciphername); |
| multi->remote_ciphername = |
| options_string_extract_option(options, "cipher", NULL); |
| |
| if (tls_peer_info_ncp_ver(multi->peer_info) < 2) |
| { |
| /* Peer does not support NCP, but leave NCP enabled if the local and |
| * remote cipher do not match to attempt 'poor-man's NCP'. |
| */ |
| if (multi->remote_ciphername == NULL |
| || 0 == strcmp(multi->remote_ciphername, multi->opt.config_ciphername)) |
| { |
| session->opt->ncp_enabled = false; |
| } |
| } |
| #endif /* if P2MP_SERVER */ |
| |
| if (tls_session_user_pass_enabled(session)) |
| { |
| /* Perform username/password authentication */ |
| if (!username_status || !password_status) |
| { |
| CLEAR(*up); |
| if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: Auth Username/Password was not provided by peer"); |
| goto error; |
| } |
| } |
| |
| verify_user_pass(up, multi, session); |
| } |
| else |
| { |
| /* Session verification should have occurred during TLS negotiation*/ |
| if (!session->verified) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: Certificate verification failed (key-method 2)"); |
| goto error; |
| } |
| ks->authenticated = true; |
| } |
| |
| /* clear username and password from memory */ |
| secure_memzero(up, sizeof(*up)); |
| |
| /* Perform final authentication checks */ |
| if (ks->authenticated) |
| { |
| verify_final_auth_checks(multi, session); |
| } |
| |
| #ifdef ENABLE_OCC |
| /* check options consistency */ |
| if (!session->opt->disable_occ |
| && !options_cmp_equal(options, session->opt->remote_options)) |
| { |
| options_warning(options, session->opt->remote_options); |
| if (session->opt->ssl_flags & SSLF_OPT_VERIFY) |
| { |
| msg(D_TLS_ERRORS, "Option inconsistency warnings triggering disconnect due to --opt-verify"); |
| ks->authenticated = false; |
| } |
| } |
| #endif |
| |
| buf_clear(buf); |
| |
| /* |
| * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final |
| * veto opportunity over authentication decision. |
| */ |
| if (ks->authenticated && plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) |
| { |
| key_state_export_keying_material(&ks->ks_ssl, session); |
| |
| if (plugin_call(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS) |
| { |
| ks->authenticated = false; |
| } |
| |
| setenv_del(session->opt->es, "exported_keying_material"); |
| } |
| |
| /* |
| * Generate tunnel keys if we're a client. |
| * If --pull is enabled, the first key generation is postponed until after the |
| * pull/push, so we can process pushed cipher directives. |
| */ |
| if (!session->opt->server && (!session->opt->pull || ks->key_id > 0)) |
| { |
| if (!tls_session_generate_data_channel_keys(session)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed"); |
| goto error; |
| } |
| } |
| |
| gc_free(&gc); |
| return true; |
| |
| error: |
| secure_memzero(ks->key_src, sizeof(*ks->key_src)); |
| if (up) |
| { |
| secure_memzero(up, sizeof(*up)); |
| } |
| buf_clear(buf); |
| gc_free(&gc); |
| return false; |
| } |
| |
| static int |
| auth_deferred_expire_window(const struct tls_options *o) |
| { |
| int ret = o->handshake_window; |
| const int r2 = o->renegotiate_seconds / 2; |
| |
| if (o->renegotiate_seconds && r2 < ret) |
| { |
| ret = r2; |
| } |
| return ret; |
| } |
| |
| /* |
| * This is the primary routine for processing TLS stuff inside the |
| * the main event loop. When this routine exits |
| * with non-error status, it will set *wakeup to the number of seconds |
| * when it wants to be called again. |
| * |
| * Return value is true if we have placed a packet in *to_link which we |
| * want to send to our peer. |
| */ |
| static bool |
| tls_process(struct tls_multi *multi, |
| struct tls_session *session, |
| struct buffer *to_link, |
| struct link_socket_actual **to_link_addr, |
| struct link_socket_info *to_link_socket_info, |
| interval_t *wakeup) |
| { |
| struct gc_arena gc = gc_new(); |
| struct buffer *buf; |
| bool state_change = false; |
| bool active = false; |
| struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ |
| struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ |
| |
| /* Make sure we were initialized and that we're not in an error state */ |
| ASSERT(ks->state != S_UNDEF); |
| ASSERT(ks->state != S_ERROR); |
| ASSERT(session_id_defined(&session->session_id)); |
| |
| /* Should we trigger a soft reset? -- new key, keeps old key for a while */ |
| if (ks->state >= S_ACTIVE |
| && ((session->opt->renegotiate_seconds |
| && now >= ks->established + session->opt->renegotiate_seconds) |
| || (session->opt->renegotiate_bytes > 0 |
| && ks->n_bytes >= session->opt->renegotiate_bytes) |
| || (session->opt->renegotiate_packets |
| && ks->n_packets >= session->opt->renegotiate_packets) |
| || (packet_id_close_to_wrapping(&ks->crypto_options.packet_id.send)))) |
| { |
| msg(D_TLS_DEBUG_LOW, |
| "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d", |
| (int)(ks->established + session->opt->renegotiate_seconds - now), |
| ks->n_bytes, session->opt->renegotiate_bytes, |
| ks->n_packets, session->opt->renegotiate_packets); |
| key_state_soft_reset(session); |
| } |
| |
| /* Kill lame duck key transition_window seconds after primary key negotiation */ |
| if (lame_duck_must_die(session, wakeup)) |
| { |
| key_state_free(ks_lame, true); |
| msg(D_TLS_DEBUG_LOW, "TLS: tls_process: killed expiring key"); |
| } |
| |
| do |
| { |
| update_time(); |
| |
| dmsg(D_TLS_DEBUG, "TLS: tls_process: chg=%d ks=%s lame=%s to_link->len=%d wakeup=%d", |
| state_change, |
| state_name(ks->state), |
| state_name(ks_lame->state), |
| to_link->len, |
| *wakeup); |
| |
| state_change = false; |
| |
| /* |
| * TLS activity is finished once we get to S_ACTIVE, |
| * though we will still process acknowledgements. |
| * |
| * CHANGED with 2.0 -> now we may send tunnel configuration |
| * info over the control channel. |
| */ |
| |
| /* Initial handshake */ |
| if (ks->state == S_INITIAL) |
| { |
| buf = reliable_get_buf_output_sequenced(ks->send_reliable); |
| if (buf) |
| { |
| ks->must_negotiate = now + session->opt->handshake_window; |
| ks->auth_deferred_expire = now + auth_deferred_expire_window(session->opt); |
| |
| /* null buffer */ |
| reliable_mark_active_outgoing(ks->send_reliable, buf, ks->initial_opcode); |
| INCR_GENERATED; |
| |
| ks->state = S_PRE_START; |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s", |
| session_id_print(&session->session_id, &gc)); |
| |
| #ifdef ENABLE_MANAGEMENT |
| if (management && ks->initial_opcode != P_CONTROL_SOFT_RESET_V1) |
| { |
| management_set_state(management, |
| OPENVPN_STATE_WAIT, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL); |
| } |
| #endif |
| } |
| } |
| |
| /* Are we timed out on receive? */ |
| if (now >= ks->must_negotiate) |
| { |
| if (ks->state < S_ACTIVE) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: TLS key negotiation failed to occur within %d seconds (check your network connectivity)", |
| session->opt->handshake_window); |
| goto error; |
| } |
| else /* assume that ks->state == S_ACTIVE */ |
| { |
| dmsg(D_TLS_DEBUG_MED, "STATE S_NORMAL_OP"); |
| ks->state = S_NORMAL_OP; |
| ks->must_negotiate = 0; |
| } |
| } |
| |
| /* Wait for Initial Handshake ACK */ |
| if (ks->state == S_PRE_START && FULL_SYNC) |
| { |
| ks->state = S_START; |
| state_change = true; |
| |
| /* |
| * Attempt CRL reload before TLS negotiation. Won't be performed if |
| * the file was not modified since the last reload |
| */ |
| if (session->opt->crl_file |
| && !(session->opt->ssl_flags & SSLF_CRL_VERIFY_DIR)) |
| { |
| tls_ctx_reload_crl(&session->opt->ssl_ctx, |
| session->opt->crl_file, session->opt->crl_file_inline); |
| } |
| |
| /* New connection, remove any old X509 env variables */ |
| tls_x509_clear_env(session->opt->es); |
| |
| dmsg(D_TLS_DEBUG_MED, "STATE S_START"); |
| } |
| |
| /* Wait for ACK */ |
| if (((ks->state == S_GOT_KEY && !session->opt->server) |
| || (ks->state == S_SENT_KEY && session->opt->server))) |
| { |
| if (FULL_SYNC) |
| { |
| ks->established = now; |
| dmsg(D_TLS_DEBUG_MED, "STATE S_ACTIVE"); |
| if (check_debug_level(D_HANDSHAKE)) |
| { |
| print_details(&ks->ks_ssl, "Control Channel:"); |
| } |
| state_change = true; |
| ks->state = S_ACTIVE; |
| INCR_SUCCESS; |
| |
| /* Set outgoing address for data channel packets */ |
| link_socket_set_outgoing_addr(NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es); |
| |
| /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */ |
| flush_payload_buffer(ks); |
| |
| #ifdef MEASURE_TLS_HANDSHAKE_STATS |
| show_tls_performance_stats(); |
| #endif |
| } |
| } |
| |
| /* Reliable buffer to outgoing TCP/UDP (send up to CONTROL_SEND_ACK_MAX ACKs |
| * for previously received packets) */ |
| if (!to_link->len && reliable_can_send(ks->send_reliable)) |
| { |
| int opcode; |
| struct buffer b; |
| |
| buf = reliable_send(ks->send_reliable, &opcode); |
| ASSERT(buf); |
| b = *buf; |
| INCR_SENT; |
| |
| write_control_auth(session, ks, &b, to_link_addr, opcode, |
| CONTROL_SEND_ACK_MAX, true); |
| *to_link = b; |
| active = true; |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "Reliable -> TCP/UDP"); |
| break; |
| } |
| |
| /* Write incoming ciphertext to TLS object */ |
| buf = reliable_get_buf_sequenced(ks->rec_reliable); |
| if (buf) |
| { |
| int status = 0; |
| if (buf->len) |
| { |
| status = key_state_write_ciphertext(&ks->ks_ssl, buf); |
| if (status == -1) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: Incoming Ciphertext -> TLS object write error"); |
| goto error; |
| } |
| } |
| else |
| { |
| status = 1; |
| } |
| if (status == 1) |
| { |
| reliable_mark_deleted(ks->rec_reliable, buf, true); |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "Incoming Ciphertext -> TLS"); |
| } |
| } |
| |
| /* Read incoming plaintext from TLS object */ |
| buf = &ks->plaintext_read_buf; |
| if (!buf->len) |
| { |
| int status; |
| |
| ASSERT(buf_init(buf, 0)); |
| status = key_state_read_plaintext(&ks->ks_ssl, buf, TLS_CHANNEL_BUF_SIZE); |
| update_time(); |
| if (status == -1) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: TLS object -> incoming plaintext read error"); |
| goto error; |
| } |
| if (status == 1) |
| { |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "TLS -> Incoming Plaintext"); |
| |
| /* More data may be available, wake up again asap to check. */ |
| *wakeup = 0; |
| } |
| } |
| |
| /* Send Key */ |
| buf = &ks->plaintext_write_buf; |
| if (!buf->len && ((ks->state == S_START && !session->opt->server) |
| || (ks->state == S_GOT_KEY && session->opt->server))) |
| { |
| if (session->opt->key_method == 1) |
| { |
| if (!key_method_1_write(buf, session)) |
| { |
| goto error; |
| } |
| } |
| else if (session->opt->key_method == 2) |
| { |
| if (!key_method_2_write(buf, session)) |
| { |
| goto error; |
| } |
| } |
| else |
| { |
| ASSERT(0); |
| } |
| |
| state_change = true; |
| dmsg(D_TLS_DEBUG_MED, "STATE S_SENT_KEY"); |
| ks->state = S_SENT_KEY; |
| } |
| |
| /* Receive Key */ |
| buf = &ks->plaintext_read_buf; |
| if (buf->len |
| && ((ks->state == S_SENT_KEY && !session->opt->server) |
| || (ks->state == S_START && session->opt->server))) |
| { |
| if (session->opt->key_method == 1) |
| { |
| if (!key_method_1_read(buf, session)) |
| { |
| goto error; |
| } |
| } |
| else if (session->opt->key_method == 2) |
| { |
| if (!key_method_2_read(buf, multi, session)) |
| { |
| goto error; |
| } |
| } |
| else |
| { |
| ASSERT(0); |
| } |
| |
| state_change = true; |
| dmsg(D_TLS_DEBUG_MED, "STATE S_GOT_KEY"); |
| ks->state = S_GOT_KEY; |
| } |
| |
| /* Write outgoing plaintext to TLS object */ |
| buf = &ks->plaintext_write_buf; |
| if (buf->len) |
| { |
| int status = key_state_write_plaintext(&ks->ks_ssl, buf); |
| if (status == -1) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS ERROR: Outgoing Plaintext -> TLS object write error"); |
| goto error; |
| } |
| if (status == 1) |
| { |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "Outgoing Plaintext -> TLS"); |
| } |
| } |
| |
| /* Outgoing Ciphertext to reliable buffer */ |
| if (ks->state >= S_START) |
| { |
| buf = reliable_get_buf_output_sequenced(ks->send_reliable); |
| if (buf) |
| { |
| int status = key_state_read_ciphertext(&ks->ks_ssl, buf, PAYLOAD_SIZE_DYNAMIC(&multi->opt.frame)); |
| if (status == -1) |
| { |
| msg(D_TLS_ERRORS, |
| "TLS Error: Ciphertext -> reliable TCP/UDP transport read error"); |
| goto error; |
| } |
| if (status == 1) |
| { |
| reliable_mark_active_outgoing(ks->send_reliable, buf, P_CONTROL_V1); |
| INCR_GENERATED; |
| state_change = true; |
| dmsg(D_TLS_DEBUG, "Outgoing Ciphertext -> Reliable"); |
| } |
| } |
| } |
| } |
| while (state_change); |
| |
| update_time(); |
| |
| /* Send 1 or more ACKs (each received control packet gets one ACK) */ |
| if (!to_link->len && !reliable_ack_empty(ks->rec_ack)) |
| { |
| struct buffer buf = ks->ack_write_buf; |
| ASSERT(buf_init(&buf, FRAME_HEADROOM(&multi->opt.frame))); |
| write_control_auth(session, ks, &buf, to_link_addr, P_ACK_V1, |
| RELIABLE_ACK_SIZE, false); |
| *to_link = buf; |
| active = true; |
| dmsg(D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); |
| } |
| |
| /* When should we wake up again? */ |
| { |
| if (ks->state >= S_INITIAL) |
| { |
| compute_earliest_wakeup(wakeup, |
| reliable_send_timeout(ks->send_reliable)); |
| |
| if (ks->must_negotiate) |
| { |
| compute_earliest_wakeup(wakeup, ks->must_negotiate - now); |
| } |
| } |
| |
| if (ks->established && session->opt->renegotiate_seconds) |
| { |
| compute_earliest_wakeup(wakeup, |
| ks->established + session->opt->renegotiate_seconds - now); |
| } |
| |
| /* prevent event-loop spinning by setting minimum wakeup of 1 second */ |
| if (*wakeup <= 0) |
| { |
| *wakeup = 1; |
| |
| /* if we had something to send to remote, but to_link was busy, |
| * let caller know we need to be called again soon */ |
| active = true; |
| } |
| |
| dmsg(D_TLS_DEBUG, "TLS: tls_process: timeout set to %d", *wakeup); |
| |
| gc_free(&gc); |
| return active; |
| } |
| |
| error: |
| tls_clear_error(); |
| ks->state = S_ERROR; |
| msg(D_TLS_ERRORS, "TLS Error: TLS handshake failed"); |
| INCR_ERROR; |
| gc_free(&gc); |
| return false; |
| } |
| |
| /* |
| * Called by the top-level event loop. |
| * |
| * Basically decides if we should call tls_process for |
| * the active or untrusted sessions. |
| */ |
| |
| int |
| tls_multi_process(struct tls_multi *multi, |
| struct buffer *to_link, |
| struct link_socket_actual **to_link_addr, |
| struct link_socket_info *to_link_socket_info, |
| interval_t *wakeup) |
| { |
| struct gc_arena gc = gc_new(); |
| |