📄 openvpn.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 */#include "config.h"#include "syshead.h"#include "common.h"#include "error.h"#include "options.h"#include "socket.h"#include "buffer.h"#include "crypto.h"#include "ssl.h"#include "misc.h"#include "lzo.h"#include "tun.h"#include "gremlin.h"#include "shaper.h"#include "thread.h"#include "interval.h"#include "openvpn.h"#include "memdbg.h"/* Handle signals */static volatile int signal_received = 0;/* normal signal handler, when we are in event loop */static voidsignal_handler (int signum){ signal_received = signum; signal (signum, signal_handler);}/* temporary signal handler, before we are fully initialized */static voidsignal_handler_exit (int signum){ msg (M_FATAL | M_NOLOCK, "Signal %d received during initialization, exiting", signum);}/* * For debugging, dump a packet in * nominally human-readable form. */#if defined(USE_CRYPTO) && defined(USE_SSL)#define TLS_MODE (tls_multi != NULL)#define PROTO_DUMP(buf) protocol_dump(buf, \ PD_SHOW_DATA | \ (tls_multi ? PD_TLS : 0) | \ (options->tls_auth_file ? ks->key_type.hmac_length : 0) \ )#else#define TLS_MODE (false)#define PROTO_DUMP(buf) format_hex (BPTR (buf), BLEN (buf), 80)#endifstatic void print_frame_parms(int level, const struct frame *frame, const char* prefix){ msg (level, "%s: mtu=%d extra_frame=%d extra_buffer=%d extra_tun=%d", prefix, frame->mtu, frame->extra_frame, frame->extra_buffer, frame->extra_tun);}/* * Finish initialization of the frame MTU parameters * based on command line options and buffering requirements. */static voidframe_finalize(struct frame *frame, const struct options *options){ if (options->tun_mtu_defined) { frame->mtu = options->tun_mtu; } else { ASSERT (options->udp_mtu_defined); frame->mtu = options->udp_mtu - frame->extra_frame; } if (frame->mtu < MIN_TUN_MTU) { msg (M_WARN, "TUN MTU value must be at least %d", MIN_TUN_MTU); print_frame_parms (M_FATAL, frame, "MTU is too small"); } frame->extra_buffer += frame->extra_frame + frame->extra_tun;}#if defined(USE_PTHREAD) && defined(USE_CRYPTO)static void *test_crypto_thread (void *arg);#endif/* * Our global key schedules, packaged thusly * to facilitate --persist-key. */struct key_schedule{#ifdef USE_CRYPTO /* which cipher, HMAC digest, and key sizes are we using? */ struct key_type key_type; /* pre-shared static key, read from a file */ struct key_ctx_bi static_key;#ifdef USE_SSL /* our global SSL context */ SSL_CTX *ssl_ctx; /* optional authentication HMAC key for TLS control channel */ struct key_ctx_bi tls_auth_key;#endif /* USE_SSL */#endif /* USE_CRYPTO */};static voidkey_schedule_free(struct key_schedule* ks){#ifdef USE_CRYPTO free_key_ctx_bi (&ks->static_key);#ifdef USE_SSL if (ks->ssl_ctx) SSL_CTX_free (ks->ssl_ctx); free_key_ctx_bi (&ks->tls_auth_key);#endif /* USE_SSL */#endif /* USE_CRYPTO */ CLEAR (*ks);}/* * Do the work. Initialize and enter main event loop. * Called after command line has been parsed. * * first_time is true during our first call -- we may * be called multiple times due to SIGHUP or SIGUSR1. */static intopenvpn (const struct options *options, struct udp_socket_addr *udp_socket_addr, struct tuntap *tuntap, struct key_schedule *ks, bool first_time){ /* * Initialize garbage collection level. * When we pop the level at the end * of the routine, everything we * allocated with gc_malloc at our level * or recursively lower levels will * automatically be freed. */ const int gc_level = gc_new_level (); /* used by select() */ int fm; /* declare various buffers */ struct buffer to_tun = clear_buf (); struct buffer to_udp = clear_buf (); struct buffer buf = clear_buf (); struct buffer nullbuf = clear_buf (); /* tells us to free to_udp buffer after it has been written to UDP port */ bool free_to_udp; /* used by select() */ fd_set reads, writes; struct udp_socket udp_socket; /* socket used for UDP connection to remote */ struct sockaddr_in to_udp_addr; /* IP address of remote */ int max_rw_size_udp = 0; /* max size of packet we can send to remote */ /* MTU frame parameters */ struct frame frame; /* Always set to current time. */ time_t current; /* * Traffic shaper object. */ struct shaper shaper; /* * Statistics */ counter_type tun_read_bytes = 0; counter_type tun_write_bytes = 0; counter_type udp_read_bytes = 0; counter_type udp_write_bytes = 0; /* * Timer objects for ping and inactivity * timeout features. */ struct event_timeout inactivity_interval; struct event_timeout ping_send_interval; struct event_timeout ping_rec_interval; /* * This random string identifies an OpenVPN ping packet. * It should be of sufficient length and randomness * so as not to collide with other tunnel data. */ static const uint8_t ping_string[] = { 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb, 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 };#ifdef USE_CRYPTO /* * TLS-mode crypto objects. */#ifdef USE_SSL /* master OpenVPN SSL/TLS object */ struct tls_multi *tls_multi = NULL; /* an options string that must match on TLS client and server */ char *data_channel_options = NULL;#ifdef USE_PTHREAD /* local socket descriptor used to communicate with TLS thread */ int tls_thread_socket = 0; /* object sent to us by TLS thread */ struct tt_ret tt_ret;#else /* used to optimize calls to tls_multi_process in single-threaded mode */ struct interval tmp_int;#endif#endif /* workspace buffers used by crypto routines */ struct buffer encrypt_buf = clear_buf (); struct buffer decrypt_buf = clear_buf (); /* passed to encrypt or decrypt, contains all crypto-related command line options related to data channel encryption/decryption */ struct crypto_options crypto_options; /* used to keep track of data channel packet sequence numbers */ struct packet_id packet_id; /* our residual iv from all encrypts */ uint8_t iv[EVP_MAX_IV_LENGTH];#endif /* * LZO compression library objects. */#ifdef USE_LZO struct buffer lzo_compress_buf = clear_buf (); struct buffer lzo_decompress_buf = clear_buf (); struct lzo_compress_workspace lzo_compwork;#endif /* * Buffers used to read from TUN device * and UDP port. */ struct buffer read_udp_buf = clear_buf (); struct buffer read_tun_buf = clear_buf (); /* * Special handling if signal arrives before * we are properly initialized. */ signal (SIGINT, signal_handler_exit); signal (SIGTERM, signal_handler_exit); signal (SIGHUP, SIG_IGN); signal (SIGUSR1, SIG_IGN); signal (SIGUSR2, SIG_IGN); signal (SIGPIPE, SIG_IGN); msg (M_INFO, "%s", TITLE); CLEAR (udp_socket); CLEAR (frame); if (first_time) { /* initialize threading if pthread configure option enabled */ thread_init(); /* save process ID in a file */ write_pid (options->writepid); if (options->mlock) /* should we disable paging? */ do_mlockall (true); /* chroot if requested */ do_chroot (options->chroot_dir); }#ifdef USE_CRYPTO if (!options->test_crypto)#endif /* open the UDP socket */ udp_socket_init (&udp_socket, options->local, options->remote, options->local_port, options->remote_port, options->bind_local, options->remote_float, options->inetd, udp_socket_addr, options->ipchange, options->resolve_retry_seconds);#ifdef USE_CRYPTO /* Initialize crypto options */ CLEAR (crypto_options); CLEAR (packet_id); CLEAR (iv); /* Start with a random IV and carry forward the residuals */ if (options->iv) { randomize_iv (iv); crypto_options.iv = iv; } if (options->shared_secret_file) { /* * Static Key Mode (using a pre-shared key) */ /* Initialize packet ID tracking */ if (options->packet_id) { crypto_options.packet_id = &packet_id; crypto_options.packet_id_long_form = true; } if (!key_ctx_bi_defined (&ks->static_key)) { struct key key; /* Get cipher & hash algorithms */ init_key_type (&ks->key_type, options->ciphername, options->ciphername_defined, options->authname, options->authname_defined, options->keysize, options->test_crypto); /* Read cipher and hmac keys from shared secret file */ read_key_file (&key, options->shared_secret_file); /* Fix parity for DES keys and make sure not a weak key */ fixup_key (&key, &ks->key_type); if (!check_key (&key, &ks->key_type)) /* This should be a very improbable failure */ msg (M_FATAL, "Key in %s is bad. Try making a new key with --genkey.", options->shared_secret_file); /* Init cipher & hmac */ init_key_ctx (&ks->static_key.encrypt, &key, &ks->key_type, DO_ENCRYPT, "Static Encrypt"); init_key_ctx (&ks->static_key.decrypt, &key, &ks->key_type, DO_DECRYPT, "Static Decrypt"); /* Erase the key */ CLEAR (key); } else { msg (M_INFO, "Re-using pre-shared static key"); } /* Get key schedule */ crypto_options.key_ctx_bi = &ks->static_key; /* Compute MTU parameters */ crypto_adjust_frame_parameters(&frame, &ks->key_type, options->ciphername_defined, options->iv, options->packet_id, true); /* Sanity check on IV, sequence number, and cipher mode options */ check_replay_iv_consistency(&ks->key_type, options->packet_id, options->iv); /* * Test-crypto is a debugging tool * that basically does a loopback test * on the crypto subsystem. */ if (options->test_crypto) {#ifdef USE_PTHREAD if (first_time) work_thread_create(test_crypto_thread, (struct options*) options);#endif frame_finalize (&frame, options); test_crypto (&crypto_options, &frame); key_schedule_free (ks); signal_received = 0;#ifdef USE_PTHREAD if (first_time) work_thread_join ();#endif goto done; } }#ifdef USE_SSL else if (options->tls_server || options->tls_client) { /* * TLS-based dynamic key exchange mode */ struct tls_options to; bool packet_id_long_form; ASSERT (!options->test_crypto); /* Make sure we are either a TLS client or server but not both */ ASSERT (options->tls_server == !options->tls_client); /* Let user specify a script to verify the incoming certificate */ tls_set_verify_command (options->tls_verify); if (!ks->ssl_ctx) { /* * Initialize the OpenSSL library's global * SSL context. */ ks->ssl_ctx = init_ssl (options->tls_server, options->ca_file, options->dh_file, options->cert_file, options->priv_key_file, options->cipher_list); /* Get cipher & hash algorithms */ init_key_type (&ks->key_type, options->ciphername, options->ciphername_defined, options->authname, options->authname_defined, options->keysize, true); /* TLS handshake authentication (--tls-auth) */ if (options->tls_auth_file) get_tls_handshake_key (&ks->key_type, &ks->tls_auth_key, options->tls_auth_file); } else { msg (M_INFO, "Re-using SSL/TLS context"); } /* Sanity check on IV, sequence number, and cipher mode options */ check_replay_iv_consistency(&ks->key_type, options->packet_id, options->iv); /* In short form, unique datagram identifier is 32 bits, in long form 64 bits */ packet_id_long_form = cfb_ofb_mode (&ks->key_type); /* Compute MTU parameters */ crypto_adjust_frame_parameters(&frame, &ks->key_type, options->ciphername_defined, options->iv, options->packet_id, packet_id_long_form); tls_adjust_frame_parameters(&frame); /* Set all command-line TLS-related options */ CLEAR (to); to.ssl_ctx = ks->ssl_ctx; to.key_type = ks->key_type; to.server = options->tls_server; to.options = data_channel_options = options_string (options); to.packet_id = options->packet_id; to.packet_id_long_form = packet_id_long_form; to.transition_window = options->transition_window; to.handshake_window = options->handshake_window; to.packet_timeout = options->tls_timeout; to.renegotiate_bytes = options->renegotiate_bytes; to.renegotiate_packets = options->renegotiate_packets; to.renegotiate_seconds = options->renegotiate_seconds; to.single_session = options->single_session; to.disable_occ = options->disable_occ; /* TLS handshake authentication (--tls-auth) */ if (options->tls_auth_file) { to.tls_auth_key = ks->tls_auth_key; to.tls_auth.packet_id_long_form = true; crypto_adjust_frame_parameters(&to.frame, &ks->key_type, false, false, true, true); } /* * Initialize OpenVPN's master TLS-mode object. */ tls_multi = tls_multi_init (&to, &udp_socket); }#endif else { /* * No encryption or authentication. */ ASSERT (!options->test_crypto); free_key_ctx_bi (&ks->static_key); crypto_options.key_ctx_bi = &ks->static_key; msg (M_WARN, "******* WARNING *******: all encryption and authentication features disabled -- all data will be tunnelled as cleartext"); }#else /* USE_CRYPTO */ msg (M_WARN, "******* WARNING *******: OpenVPN built without OpenSSL -- encryption and authentication features disabled -- all data will be tunnelled as cleartext");#endif /* USE_CRYPTO */#ifdef USE_LZO /* * Initialize LZO compression library. */ if (options->comp_lzo) { lzo_compress_init (&lzo_compwork, options->comp_lzo_adaptive); lzo_adjust_frame_parameters (&frame); }#endif#if 0 /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -