⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssl.c

📁 OpenVPN -- A Secure tunneling daemon
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  OpenVPN -- An application to securely tunnel IP networks *             over a single UDP port, with support for SSL/TLS-based *             session authentication and key exchange, *             packet encryption, packet authentication, and *             packet compression. * *  Copyright (C) 2002-2003 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. */#ifdef WIN32#include "config-win32.h"#else#include "config.h"#endif#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 "options.h"#include "memdbg.h"#ifdef MEASURE_TLS_HANDSHAKE_STATSstatic int tls_handshake_success;static int tls_handshake_error;static int tls_packets_generated;static int tls_packets_sent;#define INCR_SENT       ++tls_packets_sent#define INCR_GENERATED  ++tls_packets_generated#define INCR_SUCCESS    ++tls_handshake_success#define INCR_ERROR      ++tls_handshake_errorvoidshow_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#define INCR_SENT#define INCR_GENERATED#define INCR_SUCCESS#define INCR_ERROR#endif#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];      openvpn_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_add_to_extra_frame (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_add_to_extra_frame (frame, SID_SIZE + sizeof (packet_id_type));  frame_set_mtu_dynamic (frame, MTU_SET_TO_MIN);  frame_finalize_derivative (frame, data_channel_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, (uint8_t*)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);  openvpn_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)	    {	      openvpn_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)	    {	      openvpn_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,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -