?? ssl.c
字號:
/* * 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-2004 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 TCP/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 "integer.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; /* 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_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; /* GLOBAL */static bool biofp_toggle; /* GLOBAL */static time_t biofp_last_open; /* GLOBAL */static const int biofp_reopen_interval = 600; /* GLOBAL */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. */ /* 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 minimum value */ frame_set_mtu_dynamic (frame, 0, SET_MTU_TUN);}/* * Allocate space in SSL objects * in which to store a struct tls_session * pointer back to parent. */static int mydata_index; /* GLOBAL */static voidssl_set_mydata_index (){ mydata_index = SSL_get_ex_new_index (0, "struct session *", NULL, NULL, NULL); ASSERT (mydata_index >= 0);}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 ssl_set_mydata_index ();}voidfree_ssl_lib (){#ifdef CRYPTO_MDEBUG FILE* fp = fopen ("sdlog", "w"); ASSERT (fp); CRYPTO_mem_leaks_fp (fp); fclose (fp);#endif uninit_crypto_lib (); 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:"); if (!gp) msg (M_FATAL, "TLS Error: Error reading PEM pass phrase for private key"); strncpynt (passbuf, gp, sizeof (passbuf)); memset (gp, 0, strlen (gp)); } if (buf) { if (!strlen (passbuf)) msg (M_FATAL, "TLS Error: Need PEM pass phrase 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);}/* * Extract common name from an X509 subject name. */static voidextract_common_name (char *out, int size, const char *subject){ /* * Example subject: * * /C=US/ST=CO/L=Denver/O=NTLP/CN=Test-CA/Email=jim@yonan.net * * The common name is 'Test-CA' */ char c; int state = 0; ASSERT (size > 0); out[--size] = '\0'; do { c = *subject++; if (state == 4) { if (c == '/') c = '\0'; if (size > 0) { *out++ = c; --size; } else break; } else if (c == '/') state = 1; else if (state == 1) { if (c == 'C') state = 2; else state = 0; } else if (state == 2) { if (c == 'N') state = 3; else state = 0; } else if (state == 3) { if (c == '=') state = 4; else state = 0; } } while (c != '\0');}static voidsetenv_untrusted (struct tls_session *session){ setenv_sockaddr ("untrusted", &session->untrusted_sockaddr);}/* * Our verify callback function -- check * that an incoming peer certificate is good. */static intverify_callback (int preverify_ok, X509_STORE_CTX * ctx){ char subject[256]; char envname[64]; SSL *ssl; struct tls_session *session; const struct tls_options *opt; const int max_depth = 8; /* acquire script mutex */ mutex_lock_static (L_SCRIPT); /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); ASSERT (ssl); session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index); ASSERT (session); opt = session->opt; ASSERT (opt); X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), subject, sizeof (subject)); subject[sizeof (subject) - 1] = '\0'; safe_string (subject);#if 0 /* print some debugging info */ msg (D_LOW, "LOCAL OPT: %s", opt->local_options); msg (D_LOW, "X509: %s", subject);#endif 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), subject); goto err; /* Reject connection */ } if (ctx->error_depth >= max_depth) msg (M_WARN, "TLS Warning: Convoluted certificate chain detected with depth [%d] greater than %d", ctx->error_depth, max_depth); /* export subject name string as environmental variable */ session->verify_maxlevel = max_int (session->verify_maxlevel, ctx->error_depth); openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth); setenv_str (envname, subject); /* export serial number as environmental variable */ { const int serial = (int) ASN1_INTEGER_get (X509_get_serialNumber (ctx->current_cert)); openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth); setenv_int (envname, serial); } /* export current untrusted IP */ setenv_untrusted (session); if (opt->verify_x509name && ctx->error_depth == 0) { if (strcmp (opt->verify_x509name, subject) == 0) msg (D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject); else { msg (D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s", subject, opt->verify_x509name); goto err; /* Reject connection */ } } if (opt->verify_command) { char command[512]; struct buffer out; int ret; setenv_str ("script_type", "tls-verify"); buf_set_write (&out, (uint8_t*)command, sizeof (command)); buf_printf (&out, "%s %d %s", opt->verify_command, ctx->error_depth, subject); msg (D_TLS_DEBUG, "TLS: executing verify command: %s", command); ret = openvpn_system (command); if (system_ok (ret)) { msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s", ctx->error_depth, subject); } else { if (!system_executed (ret)) msg (M_ERR, "Verify command failed to execute: %s", command); msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s", ctx->error_depth, subject); goto err; /* Reject connection */ } } if (opt->crl_file) { X509_CRL *crl=NULL; X509_REVOKED *revoked; BIO *in=NULL; int n,i,retval = 0; in=BIO_new(BIO_s_file()); if (in == NULL) { msg (M_ERR, "CRL: BIO err"); goto end; } if (BIO_read_filename(in, opt->crl_file) <= 0) { msg (M_ERR, "CRL: cannot read: %s", opt->crl_file); goto end; } crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL); if (crl == NULL) { msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file); goto end; } n = sk_num(X509_CRL_get_REVOKED(crl)); for (i = 0; i < n; i++) { revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i); if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) { msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject); goto end; } } retval = 1; msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); end: BIO_free(in); if (!retval) goto err; } msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject); /* save common name in session object */ if (ctx->error_depth == 0) { char common_name[TLS_CN_LEN]; extract_common_name (common_name, TLS_CN_LEN, subject); if (session->common_name) free (session->common_name); session->common_name = string_alloc (common_name, NULL); } mutex_unlock_static (L_SCRIPT); return 1; /* Accept connection */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -