?? ssl.c
字號:
/* * OpenVPN -- An application to securely tunnel IP networks * over a single UDP port, with support for TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002 James Yonan <jim@yonan.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * 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 UDP port. */#include "config.h"#if defined(USE_CRYPTO) && defined(USE_SSL)#include "syshead.h"#include "ssl.h"#include "error.h"#include "common.h"#include "socket.h"#include "thread.h"#include "misc.h"#include "fdmisc.h"#include "interval.h"#include "memdbg.h"#ifdef BIO_DEBUGstatic FILE *biofp;static bool biofp_toggle;static time_t biofp_last_open;static const int biofp_reopen_interval = 600;static voidclose_biofp(){ if (biofp) { ASSERT (!fclose (biofp)); biofp = NULL; }}static voidopen_biofp(){ const time_t current = time (NULL); const pid_t pid = getpid (); if (biofp_last_open + biofp_reopen_interval < current) close_biofp(); if (!biofp) { char fn[256]; snprintf(fn, sizeof(fn), "bio/%d-%d.log", pid, biofp_toggle); biofp = fopen (fn, "w"); ASSERT (biofp); biofp_last_open = time (NULL); biofp_toggle ^= 1; }}static voidbio_debug_data (const char *mode, BIO *bio, uint8_t *buf, int len, const char *desc){ if (len > 0) { open_biofp(); fprintf(biofp, "BIO_%s %s time=" time_format " bio=" ptr_format " len=%d data=%s\n", mode, desc, time (NULL), bio, len, format_hex (buf, len, 0)); fflush (biofp); }}static voidbio_debug_oc (const char *mode, BIO *bio){ open_biofp(); fprintf(biofp, "BIO %s time=" time_format " bio=" ptr_format "\n", mode, time (NULL), bio); fflush (biofp);}#endif/* * Max number of bytes we will add * for data structures common to both * data and control channel packets. * (opcode only). */voidtls_adjust_frame_parameters(struct frame *frame){ frame->extra_frame += 1; /* space for opcode */}/* * Max number of bytes we will add * to control channel packet. */static voidtls_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. */ /* set extra_frame */ tls_adjust_frame_parameters(frame); reliable_ack_adjust_frame_parameters(frame, CONTROL_SEND_ACK_MAX); frame->extra_frame += SID_SIZE; frame->extra_frame += sizeof (packet_id_type); /* finalize parameters based on data_channel_frame */ frame->mtu = MTU_EXTRA_SIZE (data_channel_frame) - frame->extra_frame; frame->extra_buffer += frame->extra_frame;}voidinit_ssl_lib (){ SSL_library_init (); SSL_load_error_strings (); OpenSSL_add_all_algorithms (); init_crypto_lib(); /* * If you build the OpenSSL library and OpenVPN with * CRYPTO_MDEBUG, you will get a listing of OpenSSL * memory leaks on program termination. */#ifdef CRYPTO_MDEBUG CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);#endif}voidfree_ssl_lib (){#ifdef CRYPTO_MDEBUG FILE* fp = fopen ("sdlog", "w"); ASSERT (fp); CRYPTO_mem_leaks_fp (fp); fclose (fp);#endif EVP_cleanup (); ERR_free_strings ();}/* * OpenSSL library calls back here if the private key * is protected by a password. */intpem_password_callback (char *buf, int size, int rwflag, void *u){#ifdef HAVE_GETPASS static char passbuf[256]; if (!strlen (passbuf)) { char *gp = getpass ("Enter PEM pass phrase:"); strncpynt (passbuf, gp, sizeof (passbuf)); memset (gp, 0, strlen (gp)); } if (buf) { if (!strlen (passbuf)) msg (M_FATAL, "TLS Error: need PEM password for private key"); strncpynt (buf, passbuf, size); CLEAR (passbuf); return strlen (buf); }#else msg (M_FATAL, "Sorry but I can't read a password from the console because this operating system or C library doesn't support the getpass() function");#endif return 0;}/* * OpenSSL callback to get a temporary RSA key, mostly * used for export ciphers. */static RSA *tmp_rsa_cb (SSL * s, int is_export, int keylength){ static RSA *rsa_tmp = NULL; if (rsa_tmp == NULL) { msg (D_HANDSHAKE, "Generating temp (%d bit) RSA key", keylength); rsa_tmp = RSA_generate_key (keylength, RSA_F4, NULL, NULL); } return (rsa_tmp);}/* make cp safe to be passed to system() */static voidsystem_safe_string (char *cp){ int c; while (c = *cp) { if (isalnum (c) || c == '/' || c == '.' || c == '@' || c == '_' || c == '-' || c == '=') ; else *cp = '.'; ++cp; }}/* * Our verify callback function -- check * that an incoming peer certificate is good. */static const char *verify_command;voidtls_set_verify_command (const char *cmd){ verify_command = cmd;}static intverify_callback (int preverify_ok, X509_STORE_CTX * ctx){ char txt[512]; X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), txt, sizeof (txt)); txt[sizeof (txt) - 1] = '\0'; system_safe_string (txt); if (!preverify_ok) { /* Remote site specified a certificate, but it's not correct */ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", ctx->error_depth, X509_verify_cert_error_string (ctx->error), txt); return 0; /* Reject connection */ } if (verify_command) { char command[512]; struct buffer out; int ret; buf_set_write (&out, command, sizeof (command)); buf_printf (&out, "%s %d %s", verify_command, ctx->error_depth, txt); msg (D_TLS_DEBUG, "executing verify command: %s", command); ret = openvpn_system (command); if (ret != -1 && WEXITSTATUS (ret) == 0) { msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s", ctx->error_depth, txt); return 1; /* Accept connection */ } else { if (ret == -1 || WEXITSTATUS (ret) == 127) msg (M_ERR, "Verify command failed to execute: %s", command); msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s", ctx->error_depth, txt); return 0; /* Reject connection */ } } else { msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, txt); return 1; /* Accept connection */ }}/* * Print debugging information on SSL/TLS session negotiation. */static voidinfo_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret){ if (where & SSL_CB_LOOP) { msg (D_HANDSHAKE_VERBOSE, "SSL state (%s): %s", where & SSL_ST_CONNECT ? "connect" : where & SSL_ST_ACCEPT ? "accept" : "undefined", SSL_state_string_long (s)); } else if (where & SSL_CB_ALERT) { msg (D_HANDSHAKE_VERBOSE, "SSL alert (%s): %s: %s", where & SSL_CB_READ ? "read" : "write", SSL_alert_type_string_long (ret), SSL_alert_desc_string_long (ret)); }}/* * Initialize SSL context. * All files are in PEM format. */SSL_CTX *init_ssl (bool server, const char *ca_file, const char *dh_file, const char *cert_file, const char *priv_key_file, const char *cipher_list){ SSL_CTX *ctx; DH *dh; BIO *bio; if (server) { ctx = SSL_CTX_new (TLSv1_server_method ()); if (ctx == NULL) msg (M_SSLERR, "SSL_CTX_new TLSv1_server_method"); SSL_CTX_set_tmp_rsa_callback (ctx, tmp_rsa_cb); /* Get Diffie Hellman Parameters */ if (!(bio = BIO_new_file (dh_file, "r"))) msg (M_SSLERR, "Cannot open %s for DH parameters", dh_file); dh = PEM_read_bio_DHparams (bio, NULL, NULL, NULL); BIO_free (bio); if (!dh) msg (M_SSLERR, "Cannot load DH parameters from %s", dh_file); if (!SSL_CTX_set_tmp_dh (ctx, dh)) msg (M_SSLERR, "SSL_CTX_set_tmp_dh"); msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with %d bit key", 8 * DH_size (dh)); DH_free (dh); } else /* if client */ { ctx = SSL_CTX_new (TLSv1_client_method ()); if (ctx == NULL) msg (M_SSLERR, "SSL_CTX_new TLSv1_client_method"); } /* Set SSL options */ SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_options (ctx, SSL_OP_SINGLE_DH_USE); /* Set callback for getting password from user to decrypt private key */ SSL_CTX_set_default_passwd_cb (ctx, pem_password_callback);#if 1 /* Load Certificate */ if (!SSL_CTX_use_certificate_file (ctx, cert_file, SSL_FILETYPE_PEM)) msg (M_SSLERR, "Cannot load certificate file %s", cert_file);#else /* Load Certificate -- for some reason, this function sometimes inexplicably fails during restarts with a PEM_R_NO_START_LINE error. */ if (!SSL_CTX_use_certificate_chain_file (ctx, cert_file)) msg (M_SSLERR, "Cannot load certificate chain file %s", cert_file);#endif /* Load Private Key */ if (!SSL_CTX_use_PrivateKey_file (ctx, priv_key_file, SSL_FILETYPE_PEM)) msg (M_SSLERR, "Cannot load private key file %s", priv_key_file); warn_if_group_others_accessible (priv_key_file); /* Check Private Key */ if (!SSL_CTX_check_private_key (ctx)) msg (M_SSLERR, "Private key does not match the certificate"); /* Load CA file for verifying peer supplied certificate */ if (!SSL_CTX_load_verify_locations (ctx, ca_file, NULL)) msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_CTX_load_verify_locations)", ca_file);#if 1 /* Load names of CAs from file and use it as a client CA list */ { STACK_OF(X509_NAME) *cert_names; cert_names = SSL_load_client_CA_file (ca_file); if (!cert_names) msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", ca_file); SSL_CTX_set_client_CA_list (ctx, cert_names); }#endif /* Require peer certificate verification */ SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); /* Connection information callback */ SSL_CTX_set_info_callback (ctx, info_callback); /* Allowable ciphers */ if (cipher_list) { if (!SSL_CTX_set_cipher_list (ctx, cipher_list)) msg (M_SSLERR, "Problem with cipher list: %s", cipher_list); } return ctx;}/* * Print a one line summary of SSL/TLS session handshake. */static voidprint_details (SSL * c_ssl, const char *prefix){ SSL_CIPHER *ciph; X509 *cert; char s1[256]; char s2[256]; s1[0] = s2[0] = 0; ciph = SSL_get_current_cipher (c_ssl); snprintf (s1, sizeof (s1), "%s %s, cipher %s %s", prefix, SSL_get_version (c_ssl), SSL_CIPHER_get_version (ciph), SSL_CIPHER_get_name (ciph)); cert = SSL_get_peer_certificate (c_ssl); if (cert != NULL) { EVP_PKEY *pkey = X509_get_pubkey (cert); if (pkey != NULL) { if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != NULL && pkey->pkey.rsa->n != NULL) { snprintf (s2, sizeof (s2), ", %d bit RSA", BN_num_bits (pkey->pkey.rsa->n)); } else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != NULL && pkey->pkey.dsa->p != NULL) { snprintf (s2, sizeof (s2), ", %d bit DSA", BN_num_bits (pkey->pkey.dsa->p)); } EVP_PKEY_free (pkey); } X509_free (cert); } /* The SSL API does not allow us to look at temporary RSA/DH keys, * otherwise we should print their lengths too */ msg (D_HANDSHAKE, "%s%s", s1, s2);}/* * Show the TLS ciphers that are available for us to use * in the OpenSSL library. */voidshow_available_tls_ciphers (){ SSL_CTX *ctx; SSL *ssl; const char *cipher_name; int priority = 0; ctx = SSL_CTX_new (TLSv1_method ()); if (!ctx) msg (M_SSLERR, "Cannot create SSL_CTX object"); ssl = SSL_new (ctx); if (!ssl) msg (M_SSLERR, "Cannot create SSL object"); printf ("Available TLS Ciphers,\n"); printf ("listed in order of preference:\n\n"); while (cipher_name = SSL_get_cipher_list (ssl, priority++))
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
減小字號
Ctrl + -