From 6149d88c5a2c58a9cc943ca02c36e8ee4e5d1751 Mon Sep 17 00:00:00 2001 From: Alberto Gonzalez Iniesta Date: Tue, 30 Jun 2015 08:22:29 +0200 Subject: Imported Upstream version 2.3.7 --- src/Makefile.in | 6 +- src/compat/Makefile.in | 6 +- src/openvpn/Makefile.in | 6 +- src/openvpn/crypto.c | 27 +- src/openvpn/crypto.h | 72 +++- src/openvpn/crypto_backend.h | 12 +- src/openvpn/crypto_openssl.c | 4 +- src/openvpn/crypto_polarssl.c | 4 +- src/openvpn/forward.c | 1 + src/openvpn/helper.c | 2 +- src/openvpn/init.c | 86 +++-- src/openvpn/init.h | 2 + src/openvpn/mtu.h | 6 + src/openvpn/openvpn.c | 4 + src/openvpn/options.c | 110 +++++- src/openvpn/options.h | 4 + src/openvpn/pkcs11.c | 5 - src/openvpn/pkcs11.h | 3 - src/openvpn/route.c | 23 +- src/openvpn/socket.c | 12 +- src/openvpn/ssl.c | 54 ++- src/openvpn/ssl.h | 3 +- src/openvpn/ssl_backend.h | 2 +- src/openvpn/ssl_common.h | 16 +- src/openvpn/ssl_openssl.c | 39 ++- src/openvpn/ssl_polarssl.c | 82 +++-- src/openvpn/ssl_verify.c | 6 +- src/openvpn/syshead.h | 4 - src/openvpn/tun.c | 119 ++++--- src/openvpn/tun.h | 3 + src/openvpnserv/Makefile.in | 6 +- src/plugins/Makefile.in | 6 +- src/plugins/auth-pam/Makefile.in | 6 +- src/plugins/down-root/Makefile.in | 6 +- src/plugins/down-root/down-root.c | 696 +++++++++++++++++++------------------- 35 files changed, 902 insertions(+), 541 deletions(-) (limited to 'src') diff --git a/src/Makefile.in b/src/Makefile.in index 2cd864f..d019af9 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -257,6 +257,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -361,9 +363,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/Makefile + $(AUTOMAKE) --foreign src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/compat/Makefile.in b/src/compat/Makefile.in index 4f2fcb3..8c5111e 100644 --- a/src/compat/Makefile.in +++ b/src/compat/Makefile.in @@ -249,6 +249,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -368,9 +370,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/compat/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/compat/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/compat/Makefile + $(AUTOMAKE) --foreign src/compat/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/openvpn/Makefile.in b/src/openvpn/Makefile.in index 686f79b..ea9547c 100644 --- a/src/openvpn/Makefile.in +++ b/src/openvpn/Makefile.in @@ -319,6 +319,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -470,9 +472,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am_ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/openvpn/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpn/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/openvpn/Makefile + $(AUTOMAKE) --foreign src/openvpn/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 0a5e83f..aa93a7b 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -403,11 +403,27 @@ crypto_adjust_frame_parameters(struct frame *frame, bool packet_id, bool packet_id_long_form) { - frame_add_to_extra_frame (frame, - (packet_id ? packet_id_size (packet_id_long_form) : 0) + - ((cipher_defined && use_iv) ? cipher_kt_iv_size (kt->cipher) : 0) + - (cipher_defined ? cipher_kt_block_size (kt->cipher) : 0) + /* worst case padding expansion */ - kt->hmac_length); + size_t crypto_overhead = 0; + + if (packet_id) + crypto_overhead += packet_id_size (packet_id_long_form); + + if (cipher_defined) + { + if (use_iv) + crypto_overhead += cipher_kt_iv_size (kt->cipher); + + if (cipher_kt_mode_cbc (kt->cipher)) + /* worst case padding expansion */ + crypto_overhead += cipher_kt_block_size (kt->cipher); + } + + crypto_overhead += kt->hmac_length; + + frame_add_to_extra_frame (frame, crypto_overhead); + + msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %zu bytes", + __func__, crypto_overhead); } /* @@ -787,6 +803,7 @@ get_tls_handshake_key (const struct key_type *key_type, msg (M_INFO, "Control Channel Authentication: using '%s' as a free-form passphrase file", passphrase_file); + msg (M_WARN, "DEPRECATED OPTION: Using freeform files for tls-auth is deprecated and is not supported in OpenVPN 2.4 or newer versions"); } } /* handle key direction */ diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h index bf2f802..e489827 100644 --- a/src/openvpn/crypto.h +++ b/src/openvpn/crypto.h @@ -6,7 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. - * Copyright (C) 2010 Fox Crypto B.V. + * Copyright (C) 2010-2014 Fox Crypto B.V. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -25,6 +25,76 @@ /** * @file Data Channel Cryptography Module + * + * @addtogroup data_crypto Data Channel Crypto module + * + * @par Crypto packet formats + * The Data Channel Crypto module supports a number of crypto modes and + * configurable options. The actual packet format depends on these options. A + * Data Channel packet can consist of: + * - \b Opcode, one byte specifying the packet type (see @ref network_protocol + * "Network protocol"). + * - \b Peer-id, if using the v2 data channel packet format (see @ref + * network_protocol "Network protocol"). + * - \b HMAC, covering the ciphertext IV + ciphertext. The HMAC size depends + * on the \c \-\-auth option. If \c \-\-auth \c none is specified, there is no + * HMAC at all. + * - \b Ciphertext \b IV, if not disabled by \c \-\-no-iv. The IV size depends on + * the \c \-\-cipher option. + * - \b Packet \b ID, a 32-bit incrementing packet counter that provides replay + * protection (if not disabled by \c \-\-no-replay). + * - \b Timestamp, a 32-bit timestamp of the current time. + * - \b Payload, the plain text network packet to be encrypted (unless + * encryption is disabled by using \c \-\-cipher \c none). The payload might + * already be compressed (see @ref compression "Compression module"). + * + * @par + * This section does not discuss the opcode and peer-id, since those do not + * depend on the data channel crypto. See @ref network_protocol + * "Network protocol" for more information on those. + * + * @par + * \e Legenda \n + * [ xxx ] = unprotected \n + * [ - xxx - ] = authenticated \n + * [ * xxx * ] = encrypted and authenticated + * + * @par + * CBC data channel cypto format \n + * In CBC mode, both TLS-mode and static key mode are supported. The IV + * consists of random bits to provide unpredictable IVs. \n + * CBC IV format: \n + * [ - random - ] \n + * CBC data channel crypto format in TLS-mode: \n + * [ HMAC ] [ - IV - ] [ * packet ID * ] [ * packet payload * ] \n + * CBC data channel crypto format in static key mode: \n + * [ HMAC ] [ - IV - ] [ * packet ID * ] [ * timestamp * ] + * [ * packet payload * ] + * + * @par + * CFB/OFB data channel crypto format \n + * CFB and OFB modes are only supported in TLS mode. In these modes, the IV + * consists of the packet counter and a timestamp. If the IV is more than 8 + * bytes long, the remaining space is filled with zeroes. The packet counter may + * not roll over within a single TLS sessions. This results in a unique IV for + * each packet, as required by the CFB and OFB cipher modes. + * + * @par + * CFB/OFB IV format: \n + * [ - packet ID - ] [ - timestamp - ] [ - opt: zero-padding - ] \n + * CFB/OFB data channel crypto format: \n + * [ HMAC ] [ - IV - ] [ * packet payload * ] + * + * @par + * No-crypto data channel format \n + * In no-crypto mode (\c \-\-cipher \c none is specified), both TLS-mode and + * static key mode are supported. No encryption will be performed on the packet, + * but packets can still be authenticated. This mode does not require an IV.\n + * No-crypto data channel crypto format in TLS-mode: \n + * [ HMAC ] [ - packet ID - ] [ - packet payload - ] \n + * No-crypto data channel crypto format in static key mode: \n + * [ HMAC ] [ - packet ID - ] [ - timestamp - ] [ - packet payload - ] + * */ #ifndef CRYPTO_H diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h index bc067a7..4e45df0 100644 --- a/src/openvpn/crypto_backend.h +++ b/src/openvpn/crypto_backend.h @@ -223,7 +223,7 @@ int cipher_kt_block_size (const cipher_kt_t *cipher_kt); /** * Returns the mode that the cipher runs in. * - * @param cipher_kt Static cipher parameters + * @param cipher_kt Static cipher parameters. May not be NULL. * * @return Cipher mode, either \c OPENVPN_MODE_CBC, \c * OPENVPN_MODE_OFB or \c OPENVPN_MODE_CFB @@ -233,22 +233,20 @@ int cipher_kt_mode (const cipher_kt_t *cipher_kt); /** * Check if the supplied cipher is a supported CBC mode cipher. * - * @param cipher Static cipher parameters. May not be NULL. + * @param cipher Static cipher parameters. * * @return true iff the cipher is a CBC mode cipher. */ -bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) - __attribute__((nonnull)); +bool cipher_kt_mode_cbc(const cipher_kt_t *cipher); /** * Check if the supplied cipher is a supported OFB or CFB mode cipher. * - * @param cipher Static cipher parameters. May not be NULL. + * @param cipher Static cipher parameters. * * @return true iff the cipher is a OFB or CFB mode cipher. */ -bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) - __attribute__((nonnull)); +bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher); /** diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index 4067701..348bdee 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -527,7 +527,7 @@ cipher_kt_mode (const EVP_CIPHER *cipher_kt) bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) { - return cipher_kt_mode(cipher) == OPENVPN_MODE_CBC + return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC #ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) @@ -538,7 +538,7 @@ cipher_kt_mode_cbc(const cipher_kt_t *cipher) bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) { - return (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || + return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB) #ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c index 8bf8d8d..af79029 100644 --- a/src/openvpn/crypto_polarssl.c +++ b/src/openvpn/crypto_polarssl.c @@ -419,13 +419,13 @@ cipher_kt_mode (const cipher_info_t *cipher_kt) bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) { - return cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; + return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; } bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) { - return (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || + return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB); } diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 7f0d083..217fbb3 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -331,6 +331,7 @@ void check_server_poll_timeout_dowork (struct context *c) { event_timeout_reset (&c->c2.server_poll_interval); + ASSERT(c->c2.tls_multi); if (!tls_initial_packet_received (c->c2.tls_multi)) { msg (M_INFO, "Server poll timeout, restarting"); diff --git a/src/openvpn/helper.c b/src/openvpn/helper.c index 0ed0b2b..339e2ae 100644 --- a/src/openvpn/helper.c +++ b/src/openvpn/helper.c @@ -534,7 +534,7 @@ helper_tcp_nodelay (struct options *o) } else { - ASSERT (0); + o->sockflags |= SF_TCP_NODELAY; } } #endif diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 18f506c..c99e775 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -910,7 +910,6 @@ do_genkey (const struct options * options) bool do_persist_tuntap (const struct options *options) { -#ifdef ENABLE_FEATURE_TUN_PERSIST if (options->persist_config) { /* sanity check on options for --mktun or --rmtun */ @@ -926,14 +925,21 @@ do_persist_tuntap (const struct options *options) ) msg (M_FATAL|M_OPTERR, "options --mktun or --rmtun should only be used together with --dev"); +#ifdef ENABLE_FEATURE_TUN_PERSIST tuncfg (options->dev, options->dev_type, options->dev_node, options->persist_mode, options->username, options->groupname, &options->tuntap_options); if (options->persist_mode && options->lladdr) set_lladdr(options->dev, options->lladdr, NULL); return true; - } +#else + msg( M_FATAL|M_OPTERR, + "options --mktun and --rmtun are not available on your operating " + "system. Please check 'man tun' (or 'tap'), whether your system " + "supports using 'ifconfig %s create' / 'destroy' to create/remove " + "persistant tunnel interfaces.", options->dev ); #endif + } return false; } @@ -941,23 +947,20 @@ do_persist_tuntap (const struct options *options) * Should we become a daemon? * Return true if we did it. */ -static bool -possibly_become_daemon (const struct options *options, const bool first_time) +bool +possibly_become_daemon (const struct options *options) { bool ret = false; - if (first_time && options->daemon) + if (options->daemon) { ASSERT (!options->inetd); - if (daemon (options->cd_dir != NULL, options->log) < 0) + /* Don't chdir immediately, but the end of the init sequence, if needed */ + if (daemon (1, options->log) < 0) msg (M_ERR, "daemon() failed or unsupported"); restore_signal_state (); if (options->log) set_std_files_to_null (true); -#if defined(ENABLE_PKCS11) - pkcs11_forkFixup (); -#endif - ret = true; } return ret; @@ -1483,6 +1486,9 @@ do_open_tun (struct context *c) msg (M_INFO, "Preserving previous TUN/TAP instance: %s", c->c1.tuntap->actual_name); + /* explicitly set the ifconfig_* env vars */ + do_ifconfig_setenv(c->c1.tuntap, c->c2.es); + /* run the up script if user specified --up-restart */ if (c->options.up_restart) run_up_down (c->options.up_script, @@ -1711,7 +1717,8 @@ pull_permission_mask (const struct context *c) | OPT_P_MESSAGES | OPT_P_EXPLICIT_NOTIFY | OPT_P_ECHO - | OPT_P_PULL_MODE; + | OPT_P_PULL_MODE + | OPT_P_PEER_ID; if (!c->options.route_nopull) flags |= (OPT_P_ROUTE | OPT_P_IPWIN32); @@ -1790,21 +1797,39 @@ do_deferred_options (struct context *c, const unsigned int found) msg (D_PUSH, "OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified"); if (found & OPT_P_SETENV) msg (D_PUSH, "OPTIONS IMPORT: environment modified"); + +#ifdef ENABLE_SSL + if (found & OPT_P_PEER_ID) + { + msg (D_PUSH, "OPTIONS IMPORT: peer-id set"); + c->c2.tls_multi->use_peer_id = true; + c->c2.tls_multi->peer_id = c->options.peer_id; + frame_add_to_extra_frame(&c->c2.frame, +3); /* peer-id overhead */ + if ( !c->options.ce.link_mtu_defined ) + { + frame_add_to_link_mtu(&c->c2.frame, +3); + msg (D_PUSH, "OPTIONS IMPORT: adjusting link_mtu to %d", + EXPANDED_SIZE(&c->c2.frame)); + } + else + { + msg (M_WARN, "OPTIONS IMPORT: WARNING: peer-id set, but link-mtu" + " fixed by config - reducing tun-mtu to %d, expect" + " MTU problems", TUN_MTU_SIZE(&c->c2.frame) ); + } + } +#endif } /* * Possible hold on initialization */ static bool -do_hold (struct context *c) +do_hold (void) { #ifdef ENABLE_MANAGEMENT if (management) { - /* if c is defined, daemonize before hold */ - if (c && c->options.daemon && management_should_daemonize (management)) - do_init_first_time (c); - /* block until management hold is released */ if (management_hold (management)) return true; @@ -1870,7 +1895,7 @@ socket_restart_pause (struct context *c) c->persist.restart_sleep_seconds = 0; /* do managment hold on context restart, i.e. second, third, fourth, etc. initialization */ - if (do_hold (NULL)) + if (do_hold ()) sec = 0; if (sec) @@ -1889,7 +1914,7 @@ do_startup_pause (struct context *c) if (!c->first_time) socket_restart_pause (c); else - do_hold (NULL); /* do management hold on first context initialization */ + do_hold (); /* do management hold on first context initialization */ } /* @@ -2244,6 +2269,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.tmp_dir = options->tmp_dir; if (options->ccd_exclusive) to.client_config_dir_exclusive = options->client_config_dir; + to.auth_user_pass_file = options->auth_user_pass_file; #endif #ifdef ENABLE_X509_TRACK @@ -2390,6 +2416,17 @@ do_init_frame (struct context *c) */ frame_finalize_options (c, NULL); + /* packets with peer-id (P_DATA_V2) need 3 extra bytes in frame (on client) + * and need link_mtu+3 bytes on socket reception (on server). + * + * accomodate receive path in f->extra_link + * send path in f->extra_buffer (+leave room for alignment) + * + * f->extra_frame is adjusted when peer-id option is push-received + */ + frame_add_to_extra_link(&c->c2.frame, 3); + frame_add_to_extra_buffer(&c->c2.frame, 8); + #ifdef ENABLE_FRAGMENT /* * Set frame parameter for fragment code. This is necessary because @@ -2726,7 +2763,7 @@ do_compute_occ_strings (struct context *c) static void do_init_first_time (struct context *c) { - if (c->first_time && !c->did_we_daemonize && !c->c0) + if (c->first_time && !c->c0) { struct context_0 *c0; @@ -2741,12 +2778,9 @@ do_init_first_time (struct context *c) /* get --writepid file descriptor */ get_pid_file (c->options.writepid, &c0->pid_state); - /* become a daemon if --daemon */ - c->did_we_daemonize = possibly_become_daemon (&c->options, c->first_time); - - /* should we disable paging? */ - if (c->options.mlock && c->did_we_daemonize) - platform_mlockall (true); /* call again in case we daemonized */ + /* perform postponed chdir if --daemon */ + if (c->did_we_daemonize && c->options.cd_dir == NULL) + platform_chdir("/"); /* save process ID in a file */ write_pid (&c0->pid_state); @@ -3187,7 +3221,7 @@ open_management (struct context *c) } /* initial management hold, called early, before first context initialization */ - do_hold (c); + do_hold (); if (IS_SIG (c)) { msg (M_WARN, "Signal received from management interface, exiting"); diff --git a/src/openvpn/init.h b/src/openvpn/init.h index 5a1d1dc..d1908ed 100644 --- a/src/openvpn/init.h +++ b/src/openvpn/init.h @@ -55,6 +55,8 @@ bool do_genkey (const struct options *options); bool do_persist_tuntap (const struct options *options); +bool possibly_become_daemon (const struct options *options); + void pre_setup (const struct options *options); void init_instance_handle_signals (struct context *c, const struct env_set *env, const unsigned int flags); diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index 29ec21f..bccd681 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -257,6 +257,12 @@ frame_headroom (const struct frame *f, const unsigned int flag_mask) * frame member adjustment functions */ +static inline void +frame_add_to_link_mtu (struct frame *frame, const int increment) +{ + frame->link_mtu += increment; +} + static inline void frame_add_to_extra_frame (struct frame *frame, const int increment) { diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index fd87fc1..2f327f3 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -229,6 +229,10 @@ openvpn_main (int argc, char *argv[]) if (do_test_crypto (&c.options)) break; + /* become a daemon if --daemon */ + if (c.first_time) + c.did_we_daemonize = possibly_become_daemon (&c.options); + #ifdef ENABLE_MANAGEMENT /* open management subsystem */ if (!open_management (&c)) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index fa53a17..ff4b07b 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -572,6 +572,7 @@ static const char usage_message[] = "--tls-version-min ['or-highest'] : sets the minimum TLS version we\n" " will accept from the peer. If version is unrecognized and 'or-highest'\n" " is specified, require max TLS version supported by SSL implementation.\n" + "--tls-version-max : sets the maximum TLS version we will use.\n" #ifndef ENABLE_CRYPTO_POLARSSL "--pkcs12 file : PKCS#12 file containing local private key, local certificate\n" " and optionally the root CA certificate.\n" @@ -739,7 +740,11 @@ static const char usage_message[] = #ifdef ENABLE_PKCS11 "\n" "PKCS#11 standalone options:\n" - "--show-pkcs11-ids provider [cert_private] : Show PKCS#11 available ids.\n" +#ifdef DEFAULT_PKCS11_MODULE + "--show-pkcs11-ids [provider] [cert_private] : Show PKCS#11 available ids.\n" +#else + "--show-pkcs11-ids provider [cert_private] : Show PKCS#11 available ids.\n" +#endif " --verb option can be added *BEFORE* this.\n" #endif /* ENABLE_PKCS11 */ "\n" @@ -1266,7 +1271,7 @@ option_iroute_ipv6 (struct options *o, ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc); - if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel ) < 0 ) + if ( !get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel )) { msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification", prefix_str); @@ -2125,7 +2130,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (options->ssl_flags & SSLF_OPT_VERIFY) msg (M_USAGE, "--opt-verify requires --mode server"); if (options->server_flags & SF_TCP_NODELAY_HELPER) - msg (M_USAGE, "--tcp-nodelay requires --mode server"); + msg (M_WARN, "WARNING: setting tcp-nodelay on the client side will not " + "affect the server. To have TCP_NODELAY in both direction use " + "tcp-nodelay in the server configuration instead."); if (options->auth_user_pass_verify_script) msg (M_USAGE, "--auth-user-pass-verify requires --mode server"); #if PORT_SHARE @@ -2330,6 +2337,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (pkcs11_id); MUST_BE_UNDEF (pkcs11_id_management); #endif +#if P2MP + MUST_BE_UNDEF (server_poll_timeout); +#endif if (pull) msg (M_USAGE, err, "--pull"); @@ -2393,7 +2403,7 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce) { #ifdef ENABLE_FRAGMENT if (ce->fragment) - o->ce.mssfix = ce->fragment; + ce->mssfix = ce->fragment; #else msg (M_USAGE, "--mssfix must specify a parameter"); #endif @@ -2463,6 +2473,15 @@ options_postprocess_mutate_invariant (struct options *options) #endif } #endif + +#ifdef DEFAULT_PKCS11_MODULE + /* If p11-kit is present on the system then load its p11-kit-proxy.so + by default if the user asks for PKCS#11 without otherwise specifying + the module to use. */ + if (!options->pkcs11_providers[0] && + (options->pkcs11_id || options->pkcs11_id_management)) + options->pkcs11_providers[0] = DEFAULT_PKCS11_MODULE; +#endif } static void @@ -2537,6 +2556,24 @@ options_postprocess_mutate (struct options *o) else options_postprocess_mutate_ce (o, &o->ce); +#ifdef ENABLE_CRYPTOAPI + if (o->cryptoapi_cert) + { + const int tls_version_max = + (o->ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & + SSLF_TLS_VERSION_MAX_MASK; + + if (tls_version_max == TLS_VER_UNSPEC || tls_version_max > TLS_VER_1_1) + { + msg(M_WARN, "Warning: cryptapicert used, setting maximum TLS " + "version to 1.1."); + o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK << + SSLF_TLS_VERSION_MAX_SHIFT); + o->ssl_flags |= (TLS_VER_1_1 << SSLF_TLS_VERSION_MAX_SHIFT); + } + } +#endif /* ENABLE_CRYPTOAPI */ + #if P2MP /* * Save certain parms before modifying options via --pull @@ -3718,12 +3755,21 @@ static char * read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) { char line[OPTION_LINE_SIZE]; - struct buffer buf = alloc_buf (10000); + struct buffer buf = alloc_buf (8*OPTION_LINE_SIZE); char *ret; while (in_src_get (is, line, sizeof (line))) { if (!strncmp (line, close_tag, strlen (close_tag))) break; + if (!buf_safe (&buf, strlen(line))) + { + /* Increase buffer size */ + struct buffer buf2 = alloc_buf (buf.capacity * 2); + ASSERT (buf_copy (&buf2, &buf)); + buf_clear (&buf); + free_buf (&buf); + buf = buf2; + } buf_printf (&buf, "%s", line); } ret = string_alloc (BSTR (&buf), gc); @@ -6570,14 +6616,29 @@ add_option (struct options *options, { int ver; VERIFY_PERMISSION (OPT_P_GENERAL); - ver = tls_version_min_parse(p[1], p[2]); + ver = tls_version_parse(p[1], p[2]); if (ver == TLS_VER_BAD) { msg (msglevel, "unknown tls-version-min parameter: %s", p[1]); goto err; } - options->ssl_flags &= ~(SSLF_TLS_VERSION_MASK << SSLF_TLS_VERSION_SHIFT); - options->ssl_flags |= (ver << SSLF_TLS_VERSION_SHIFT); + options->ssl_flags &= + ~(SSLF_TLS_VERSION_MIN_MASK << SSLF_TLS_VERSION_MIN_SHIFT); + options->ssl_flags |= (ver << SSLF_TLS_VERSION_MIN_SHIFT); + } + else if (streq (p[0], "tls-version-max") && p[1]) + { + int ver; + VERIFY_PERMISSION (OPT_P_GENERAL); + ver = tls_version_parse(p[1], NULL); + if (ver == TLS_VER_BAD) + { + msg (msglevel, "unknown tls-version-max parameter: %s", p[1]); + goto err; + } + options->ssl_flags &= + ~(SSLF_TLS_VERSION_MAX_MASK << SSLF_TLS_VERSION_MAX_SHIFT); + options->ssl_flags |= (ver << SSLF_TLS_VERSION_MAX_SHIFT); } #ifndef ENABLE_CRYPTO_POLARSSL else if (streq (p[0], "pkcs12") && p[1]) @@ -6903,11 +6964,34 @@ add_option (struct options *options, #endif /* ENABLE_SSL */ #endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 - else if (streq (p[0], "show-pkcs11-ids") && p[1]) + else if (streq (p[0], "show-pkcs11-ids")) { char *provider = p[1]; bool cert_private = (p[2] == NULL ? false : ( atoi (p[2]) != 0 )); +#ifdef DEFAULT_PKCS11_MODULE + if (!provider) + provider = DEFAULT_PKCS11_MODULE; + else if (!p[2]) + { + char *endp = NULL; + int i = strtol(provider, &endp, 10); + + if (*endp == 0) + { + /* There was one argument, and it was purely numeric. + Interpret it as the cert_private argument */ + provider = DEFAULT_PKCS11_MODULE; + cert_private = i; + } + } +#else + if (!provider) + { + msg (msglevel, "--show-pkcs11-ids requires a provider parameter"); + goto err; + } +#endif VERIFY_PERMISSION (OPT_P_GENERAL); set_debug_level (options->verbosity, SDL_CONSTRAIN); @@ -6966,7 +7050,6 @@ add_option (struct options *options, options->pkcs11_id_management = true; } #endif -#ifdef ENABLE_FEATURE_TUN_PERSIST else if (streq (p[0], "rmtun")) { VERIFY_PERMISSION (OPT_P_GENERAL); @@ -6979,7 +7062,12 @@ add_option (struct options *options, options->persist_config = true; options->persist_mode = 1; } -#endif + else if (streq (p[0], "peer-id") && p[1]) + { + VERIFY_PERMISSION (OPT_P_PEER_ID); + options->use_peer_id = true; + options->peer_id = atoi(p[1]); + } else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 2c18838..af9a47f 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -594,6 +594,9 @@ struct options bool show_net_up; int route_method; #endif + + bool use_peer_id; + uint32_t peer_id; }; #define streq(x, y) (!strcmp((x), (y))) @@ -629,6 +632,7 @@ struct options #define OPT_P_SOCKBUF (1<<25) #define OPT_P_SOCKFLAGS (1<<26) #define OPT_P_CONNECTION (1<<27) +#define OPT_P_PEER_ID (1<<28) #define OPT_P_DEFAULT (~(OPT_P_INSTANCE|OPT_P_PULL_MODE)) diff --git a/src/openvpn/pkcs11.c b/src/openvpn/pkcs11.c index 3a15ef6..a1f13c5 100644 --- a/src/openvpn/pkcs11.c +++ b/src/openvpn/pkcs11.c @@ -336,11 +336,6 @@ pkcs11_terminate () { ); } -void -pkcs11_forkFixup () { - pkcs11h_forkFixup (); -} - bool pkcs11_addProvider ( const char * const provider, diff --git a/src/openvpn/pkcs11.h b/src/openvpn/pkcs11.h index 4261871..b49401c 100644 --- a/src/openvpn/pkcs11.h +++ b/src/openvpn/pkcs11.h @@ -38,9 +38,6 @@ pkcs11_initialize ( void pkcs11_terminate (); -void -pkcs11_forkFixup (); - bool pkcs11_addProvider ( const char * const provider, diff --git a/src/openvpn/route.c b/src/openvpn/route.c index cc85e4d..2acfbe8 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -838,7 +838,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u if ( rl && rl->flags & RG_ENABLE ) { - if (!(rl->spec.flags & RTSA_REMOTE_ENDPOINT)) + if (!(rl->spec.flags & RTSA_REMOTE_ENDPOINT) && (rl->flags & RG_REROUTE_GW)) { msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err); } @@ -2601,7 +2601,7 @@ get_default_gateway (struct route_gateway_info *rgi) gc_free (&gc); } -#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) +#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||defined(TARGET_SOLARIS) #include #include @@ -2629,6 +2629,8 @@ get_default_gateway (struct route_gateway_info *rgi) struct sockaddr *gate = NULL, *sa; struct rt_msghdr *rtm_aux; +#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) + #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) {\ l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\ @@ -2636,6 +2638,18 @@ get_default_gateway (struct route_gateway_info *rgi) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) +#else /* TARGET_SOLARIS */ + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(sizeof(struct sockaddr_in)); memmove(cp, &(u), l); cp += l;\ + } + +#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in))) + +#endif + + #define rtm m_rtmsg.m_rtm CLEAR(*rgi); @@ -2655,9 +2669,12 @@ get_default_gateway (struct route_gateway_info *rgi) rtm.rtm_addrs = rtm_addrs; so_dst.sa_family = AF_INET; - so_dst.sa_len = sizeof(struct sockaddr_in); so_mask.sa_family = AF_INET; + +#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) + so_dst.sa_len = sizeof(struct sockaddr_in); so_mask.sa_len = sizeof(struct sockaddr_in); +#endif NEXTADDR(RTA_DST, so_dst); NEXTADDR(RTA_NETMASK, so_mask); diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 6e68c18..0424c0b 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -137,10 +137,6 @@ openvpn_getaddrinfo (unsigned int flags, ASSERT(res); -#if defined(HAVE_RES_INIT) - res_init (); -#endif - if (!hostname) hostname = "::"; @@ -197,6 +193,9 @@ openvpn_getaddrinfo (unsigned int flags, */ while (true) { +#ifndef WIN32 + res_init (); +#endif /* try hostname lookup */ hints.ai_flags = 0; dmsg (D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d", @@ -215,10 +214,13 @@ openvpn_getaddrinfo (unsigned int flags, } else { + /* turn success into failure (interrupted syscall) */ if (0 == status) { ASSERT(res); freeaddrinfo(*res); - res = NULL; + *res = NULL; + status = EAI_AGAIN; /* = temporary failure */ + errno = EINTR; } goto done; } diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index ac6818e..a17c738 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -271,7 +271,7 @@ tls_get_cipher_name_pair (const char * cipher_name, size_t len) { * Max number of bytes we will add * for data structures common to both * data and control channel packets. - * (opcode only). + * (opcode only). */ void tls_adjust_frame_parameters(struct frame *frame) @@ -458,7 +458,7 @@ ssl_put_auth_challenge (const char *cr_str) * return tls_version_max(). */ int -tls_version_min_parse(const char *vstr, const char *extra) +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) @@ -627,6 +627,8 @@ packet_opcode_name (int op) return "P_ACK_V1"; case P_DATA_V1: return "P_DATA_V1"; + case P_DATA_V2: + return "P_DATA_V2"; default: return "P_???"; } @@ -1053,6 +1055,9 @@ tls_multi_init (struct tls_options *tls_options) 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; } @@ -1828,6 +1833,8 @@ push_peer_info(struct buffer *buf, struct tls_session *session) #ifdef ENABLE_LZO_STUB buf_printf (&out, "IV_LZO_STUB=1\n"); #endif + /* support for P_DATA_V2 */ + buf_printf(&out, "IV_PROTO=2\n"); if (session->opt->push_peer_info_detail >= 2) { @@ -1898,9 +1905,9 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) if (auth_user_pass_enabled) { #ifdef ENABLE_CLIENT_CR - auth_user_pass_setup (NULL, session->opt->sci); + auth_user_pass_setup (session->opt->auth_user_pass_file, session->opt->sci); #else - auth_user_pass_setup (NULL, NULL); + auth_user_pass_setup (session->opt->auth_user_pass_file, NULL); #endif if (!write_string (buf, auth_user_pass.username, -1)) goto error; @@ -2028,7 +2035,11 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi ASSERT (session->opt->key_method == 2); /* discard leading uint32 */ - ASSERT (buf_advance (buf, 4)); + 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); @@ -2777,8 +2788,9 @@ tls_pre_decrypt (struct tls_multi *multi, key_id = c & P_KEY_ID_MASK; } - if (op == P_DATA_V1) - { /* data channel packet */ + if ((op == P_DATA_V1) || (op == P_DATA_V2)) + { + /* data channel packet */ for (i = 0; i < KEY_SCAN_SIZE; ++i) { struct key_state *ks = multi->key_scan[i]; @@ -2810,7 +2822,19 @@ tls_pre_decrypt (struct tls_multi *multi, opt->pid_persist = NULL; opt->flags &= multi->opt.crypto_flags_and; opt->flags |= multi->opt.crypto_flags_or; + ASSERT (buf_advance (buf, 1)); + if (op == P_DATA_V2) + { + if (buf->len < 4) + { + msg (D_TLS_ERRORS, "Protocol error: received P_DATA_V2 from %s but length is < 4", + print_link_socket_actual (from, &gc)); + goto error; + } + ASSERT (buf_advance (buf, 3)); + } + ++ks->n_packets; ks->n_bytes += buf->len; dmsg (D_TLS_KEYSELECT, @@ -3375,14 +3399,24 @@ tls_post_encrypt (struct tls_multi *multi, struct buffer *buf) { struct key_state *ks; uint8_t *op; + uint32_t peer; ks = multi->save_ks; multi->save_ks = NULL; if (buf->len > 0) { ASSERT (ks); - ASSERT (op = buf_prepend (buf, 1)); - *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id; + + if (!multi->opt.server && multi->use_peer_id) + { + peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 | (multi->peer_id & 0xFFFFFF)); + ASSERT (buf_write_prepend (buf, &peer, 4)); + } + else + { + ASSERT (op = buf_prepend (buf, 1)); + *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id; + } ++ks->n_packets; ks->n_bytes += buf->len; } @@ -3489,7 +3523,7 @@ protocol_dump (struct buffer *buffer, unsigned int flags, struct gc_arena *gc) key_id = c & P_KEY_ID_MASK; buf_printf (&out, "%s kid=%d", packet_opcode_name (op), key_id); - if (op == P_DATA_V1) + if ((op == P_DATA_V1) || (op == P_DATA_V2)) goto print_data; /* diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index cd7cae2..6a14768 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -61,6 +61,7 @@ #define P_CONTROL_V1 4 /* control channel packet (usually TLS ciphertext) */ #define P_ACK_V1 5 /* acknowledgement for packets received */ #define P_DATA_V1 6 /* data channel packet */ +#define P_DATA_V2 9 /* data channel packet with peer-id */ /* indicates key_method >= 2 */ #define P_CONTROL_HARD_RESET_CLIENT_V2 7 /* initial key from client, forget previous state */ @@ -68,7 +69,7 @@ /* define the range of legal opcodes */ #define P_FIRST_OPCODE 1 -#define P_LAST_OPCODE 8 +#define P_LAST_OPCODE 9 /* Should we aggregate TLS * acknowledgements, and tack them onto diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index fc23175..6d47bd0 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -114,7 +114,7 @@ void tls_clear_error(); #define TLS_VER_1_0 1 #define TLS_VER_1_1 2 #define TLS_VER_1_2 3 -int tls_version_min_parse(const char *vstr, const char *extra); +int tls_version_parse(const char *vstr, const char *extra); /** * Return the maximum TLS version (as a TLS_VER_x constant) diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 66b6492..449172d 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -277,6 +277,7 @@ struct tls_options const char *auth_user_pass_verify_script; bool auth_user_pass_verify_script_via_file; const char *tmp_dir; + const char *auth_user_pass_file; /* use the client-config-dir as a positive authenticator */ const char *client_config_dir_exclusive; @@ -291,8 +292,10 @@ struct tls_options # define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2) # define SSLF_OPT_VERIFY (1<<4) # define SSLF_CRL_VERIFY_DIR (1<<5) -# define SSLF_TLS_VERSION_SHIFT 6 -# define SSLF_TLS_VERSION_MASK 0xF /* (uses bit positions 6 to 9) */ +# define SSLF_TLS_VERSION_MIN_SHIFT 6 +# define SSLF_TLS_VERSION_MIN_MASK 0xF /* (uses bit positions 6 to 9) */ +# define SSLF_TLS_VERSION_MAX_SHIFT 10 +# define SSLF_TLS_VERSION_MAX_MASK 0xF /* (uses bit positions 10 to 13) */ unsigned int ssl_flags; #ifdef MANAGEMENT_DEF_AUTH @@ -488,6 +491,10 @@ struct tls_multi time_t tas_last; #endif + /* For P_DATA_V2 */ + uint32_t peer_id; + bool use_peer_id; + /* * Our session objects. */ @@ -498,4 +505,9 @@ struct tls_multi }; +#define SHOW_TLS_CIPHER_LIST_WARNING \ + "Be aware that that whether a cipher suite in this list can actually work\n" \ + "depends on the specific setup of both peers. See the man page entries of\n" \ + "--tls-cipher and --show-tls for more details.\n\n" + #endif /* SSL_COMMON_H_ */ diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index e77b736..be33caa 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -121,14 +121,15 @@ tmp_rsa_cb (SSL * s, int is_export, int keylength) void tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) { - const int tls_version_min = (ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK; + const int tls_version_max = + (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; ASSERT(NULL != ctx); - if (tls_version_min > TLS_VER_UNSPEC) - ctx->ctx = SSL_CTX_new (SSLv23_server_method ()); - else + if (tls_version_max == TLS_VER_1_0) ctx->ctx = SSL_CTX_new (TLSv1_server_method ()); + else + ctx->ctx = SSL_CTX_new (SSLv23_server_method ()); if (ctx->ctx == NULL) msg (M_SSLERR, "SSL_CTX_new SSLv23_server_method"); @@ -139,14 +140,15 @@ tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) void tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) { - const int tls_version_min = (ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK; + const int tls_version_max = + (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; ASSERT(NULL != ctx); - if (tls_version_min > TLS_VER_UNSPEC) - ctx->ctx = SSL_CTX_new (SSLv23_client_method ()); - else + if (tls_version_max == TLS_VER_1_0) ctx->ctx = SSL_CTX_new (TLSv1_client_method ()); + else + ctx->ctx = SSL_CTX_new (SSLv23_client_method ()); if (ctx->ctx == NULL) msg (M_SSLERR, "SSL_CTX_new SSLv23_client_method"); @@ -218,16 +220,27 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) /* process SSL options including minimum TLS version we will accept from peer */ { long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; - const int tls_version_min = (ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK; - if (tls_version_min > TLS_VER_1_0) + const int tls_ver_min = + (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK; + int tls_ver_max = + (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; + + if (tls_ver_max <= TLS_VER_UNSPEC) + tls_ver_max = tls_version_max(); + + if (tls_ver_min > TLS_VER_1_0 || tls_ver_max < TLS_VER_1_0) sslopt |= SSL_OP_NO_TLSv1; #ifdef SSL_OP_NO_TLSv1_1 - if (tls_version_min > TLS_VER_1_1) + if (tls_ver_min > TLS_VER_1_1 || tls_ver_max < TLS_VER_1_1) sslopt |= SSL_OP_NO_TLSv1_1; #endif #ifdef SSL_OP_NO_TLSv1_2 - if (tls_version_min > TLS_VER_1_2) + if (tls_ver_min > TLS_VER_1_2 || tls_ver_max < TLS_VER_1_2) sslopt |= SSL_OP_NO_TLSv1_2; +#endif +#ifdef SSL_OP_NO_COMPRESSION + /* Disable compression - flag not available in OpenSSL 0.9.8 */ + sslopt |= SSL_OP_NO_COMPRESSION; #endif SSL_CTX_set_options (ctx->ctx, sslopt); } @@ -1327,7 +1340,7 @@ show_available_tls_ciphers (const char *cipher_list) } } - printf ("\n"); + printf ("\n" SHOW_TLS_CIPHER_LIST_WARNING); SSL_free (ssl); SSL_CTX_free (tls_ctx.ctx); diff --git a/src/openvpn/ssl_polarssl.c b/src/openvpn/ssl_polarssl.c index 5718c8c..30c7395 100644 --- a/src/openvpn/ssl_polarssl.c +++ b/src/openvpn/ssl_polarssl.c @@ -654,6 +654,40 @@ tls_version_max(void) #endif } +/** + * Convert an OpenVPN tls-version variable to PolarSSl format (i.e. a major and + * minor ssl version number). + * + * @param tls_ver The tls-version variable to convert. + * @param major Returns the TLS major version in polarssl format. + * Must be a valid pointer. + * @param minor Returns the TLS minor version in polarssl format. + * Must be a valid pointer. + */ +static void tls_version_to_major_minor(int tls_ver, int *major, int *minor) { + ASSERT(major); + ASSERT(minor); + + switch (tls_ver) + { + case TLS_VER_1_0: + *major = SSL_MAJOR_VERSION_3; + *minor = SSL_MINOR_VERSION_1; + break; + case TLS_VER_1_1: + *major = SSL_MAJOR_VERSION_3; + *minor = SSL_MINOR_VERSION_2; + break; + case TLS_VER_1_2: + *major = SSL_MAJOR_VERSION_3; + *minor = SSL_MINOR_VERSION_3; + break; + default: + msg(M_FATAL, "%s: invalid TLS version %d", __func__, tls_ver); + break; + } +} + void key_state_ssl_init(struct key_state_ssl *ks_ssl, const struct tls_root_ctx *ssl_ctx, bool is_server, struct tls_session *session) { @@ -712,30 +746,32 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl, /* Initialize minimum TLS version */ { - const int tls_version_min = (session->opt->ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK; - int polar_major; - int polar_minor; - switch (tls_version_min) + const int tls_version_min = + (session->opt->ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & + SSLF_TLS_VERSION_MIN_MASK; + + /* default to TLS 1.0 */ + int major = SSL_MAJOR_VERSION_3; + int minor = SSL_MINOR_VERSION_1; + + if (tls_version_min > TLS_VER_UNSPEC) + tls_version_to_major_minor(tls_version_min, &major, &minor); + + ssl_set_min_version(ks_ssl->ctx, major, minor); + } + + /* Initialize maximum TLS version */ + { + const int tls_version_max = + (session->opt->ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & + SSLF_TLS_VERSION_MAX_MASK; + + if (tls_version_max > TLS_VER_UNSPEC) { - case TLS_VER_1_0: - default: - polar_major = SSL_MAJOR_VERSION_3; - polar_minor = SSL_MINOR_VERSION_1; - break; -#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_2) - case TLS_VER_1_1: - polar_major = SSL_MAJOR_VERSION_3; - polar_minor = SSL_MINOR_VERSION_2; - break; -#endif -#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_3) - case TLS_VER_1_2: - polar_major = SSL_MAJOR_VERSION_3; - polar_minor = SSL_MINOR_VERSION_3; - break; -#endif + int major, minor; + tls_version_to_major_minor(tls_version_max, &major, &minor); + ssl_set_max_version(ks_ssl->ctx, major, minor); } - ssl_set_min_version(ks_ssl->ctx, polar_major, polar_minor); } /* Initialise BIOs */ @@ -1059,7 +1095,7 @@ show_available_tls_ciphers (const char *cipher_list) printf ("%s\n", ssl_get_ciphersuite_name(*ciphers)); ciphers++; } - printf ("\n"); + printf ("\n" SHOW_TLS_CIPHER_LIST_WARNING); } void diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index c90c2c3..9693b81 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -596,7 +596,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep { result_t ret = FAILURE; char *subject = NULL; - char common_name[TLS_USERNAME_LEN] = {0}; + char common_name[TLS_USERNAME_LEN+1] = {0}; /* null-terminated */ const struct tls_options *opt; struct gc_arena gc = gc_new(); @@ -619,7 +619,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep string_replace_leading (subject, '-', '_'); /* extract the username (default is CN) */ - if (SUCCESS != x509_get_username (common_name, TLS_USERNAME_LEN, + if (SUCCESS != x509_get_username (common_name, sizeof(common_name), opt->x509_username_field, cert)) { if (!cert_depth) @@ -1165,7 +1165,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, s2 = verify_user_pass_script (session, up); /* check sizing of username if it will become our common name */ - if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_USERNAME_LEN) + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) > TLS_USERNAME_LEN) { msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN); s1 = OPENVPN_PLUGIN_FUNC_ERROR; diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index f957a10..7075b96 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -214,10 +214,6 @@ #ifdef TARGET_LINUX -#if defined(HAVE_NETINET_IF_ETHER_H) -#include -#endif - #ifdef HAVE_LINUX_IF_TUN_H #include #endif diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index ba4b15e..285e774 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -395,6 +395,49 @@ is_tun_p2p (const struct tuntap *tt) return tun; } +/* + * Set the ifconfig_* environment variables, both for IPv4 and IPv6 + */ +void +do_ifconfig_setenv (const struct tuntap *tt, struct env_set *es) +{ + struct gc_arena gc = gc_new (); + const char *ifconfig_local = print_in_addr_t (tt->local, 0, &gc); + const char *ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); + + /* + * Set environmental variables with ifconfig parameters. + */ + if (tt->did_ifconfig_setup) + { + bool tun = is_tun_p2p (tt); + + setenv_str (es, "ifconfig_local", ifconfig_local); + if (tun) + { + setenv_str (es, "ifconfig_remote", ifconfig_remote_netmask); + } + else + { + const char *ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc); + setenv_str (es, "ifconfig_netmask", ifconfig_remote_netmask); + setenv_str (es, "ifconfig_broadcast", ifconfig_broadcast); + } + } + + if (tt->did_ifconfig_ipv6_setup) + { + const char *ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); + const char *ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); + + setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local); + setenv_int (es, "ifconfig_ipv6_netbits", tt->netbits_ipv6); + setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote); + } + + gc_free (&gc); +} + /* * Init tun/tap object. * @@ -427,9 +470,6 @@ init_tun (const char *dev, /* --dev option */ if (ifconfig_local_parm && ifconfig_remote_netmask_parm) { bool tun = false; - const char *ifconfig_local = NULL; - const char *ifconfig_remote_netmask = NULL; - const char *ifconfig_broadcast = NULL; /* * We only handle TUN/TAP devices here, not --dev null devices. @@ -490,45 +530,20 @@ init_tun (const char *dev, /* --dev option */ check_subnet_conflict (tt->local, IPV4_NETMASK_HOST, "TUN/TAP adapter"); } - /* - * Set ifconfig parameters - */ - ifconfig_local = print_in_addr_t (tt->local, 0, &gc); - ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); - /* * If TAP-style interface, generate broadcast address. */ if (!tun) { tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask); - ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc); } - /* - * Set environmental variables with ifconfig parameters. - */ - if (es) - { - setenv_str (es, "ifconfig_local", ifconfig_local); - if (tun) - { - setenv_str (es, "ifconfig_remote", ifconfig_remote_netmask); - } - else - { - setenv_str (es, "ifconfig_netmask", ifconfig_remote_netmask); - setenv_str (es, "ifconfig_broadcast", ifconfig_broadcast); - } - } tt->did_ifconfig_setup = true; } if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm) { - const char *ifconfig_ipv6_local = NULL; - const char *ifconfig_ipv6_remote = NULL; /* * Convert arguments to binary IPv6 addresses. @@ -541,24 +556,14 @@ init_tun (const char *dev, /* --dev option */ } tt->netbits_ipv6 = ifconfig_ipv6_netbits_parm; - /* - * Set ifconfig parameters - */ - ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); - ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); - - /* - * Set environmental variables with ifconfig parameters. - */ - if (es) - { - setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local); - setenv_int (es, "ifconfig_ipv6_netbits", tt->netbits_ipv6); - setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote); - } tt->did_ifconfig_ipv6_setup = true; } + /* + * Set environmental variables with ifconfig parameters. + */ + if (es) do_ifconfig_setenv(tt, es); + gc_free (&gc); return tt; } @@ -618,6 +623,28 @@ void delete_route_connected_v6_net(struct tuntap * tt, } #endif +#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) +/* we can't use true subnet mode on tun on all platforms, as that + * conflicts with IPv6 (wants to use ND then, which we don't do), + * but the OSes want "a remote address that is different from ours" + * - so we construct one, normally the first in the subnet, but if + * this is the same as ours, use the second one. + * The actual address does not matter at all, as the tun interface + * is still point to point and no layer 2 resolution is done... + */ + +char * +create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) +{ + in_addr_t remote; + + remote = (tt->local & tt->remote_netmask) +1; + + if ( remote == tt->local ) remote ++; + + return print_in_addr_t (remote, 0, &gc); +} +#endif /* execute the ifconfig command through the shell */ void @@ -1114,7 +1141,7 @@ do_ifconfig (struct tuntap *tt, IFCONFIG_PATH, actual, ifconfig_local, - ifconfig_local, + create_arbitrary_remote( tt, &gc ), tun_mtu, ifconfig_remote_netmask ); @@ -1710,9 +1737,9 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) pi.flags = 0; if(iph->version == 6) - pi.proto = htons(ETH_P_IPV6); + pi.proto = htons(OPENVPN_ETH_P_IPV6); else - pi.proto = htons(ETH_P_IP); + pi.proto = htons(OPENVPN_ETH_P_IPV4); vect[0].iov_len = sizeof(pi); vect[0].iov_base = π diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 1931c52..7089f7c 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -241,6 +241,9 @@ void init_tun_post (struct tuntap *tt, const struct frame *frame, const struct tuntap_options *options); +void do_ifconfig_setenv (const struct tuntap *tt, + struct env_set *es); + void do_ifconfig (struct tuntap *tt, const char *actual, /* actual device name */ int tun_mtu, diff --git a/src/openvpnserv/Makefile.in b/src/openvpnserv/Makefile.in index 272fd90..495b569 100644 --- a/src/openvpnserv/Makefile.in +++ b/src/openvpnserv/Makefile.in @@ -261,6 +261,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -376,9 +378,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am_ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/openvpnserv/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpnserv/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/openvpnserv/Makefile + $(AUTOMAKE) --foreign src/openvpnserv/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/plugins/Makefile.in b/src/plugins/Makefile.in index 8b73edc..de927c9 100644 --- a/src/plugins/Makefile.in +++ b/src/plugins/Makefile.in @@ -257,6 +257,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -361,9 +363,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/plugins/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/plugins/Makefile + $(AUTOMAKE) --foreign src/plugins/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/plugins/auth-pam/Makefile.in b/src/plugins/auth-pam/Makefile.in index 1e1547a..aee6561 100644 --- a/src/plugins/auth-pam/Makefile.in +++ b/src/plugins/auth-pam/Makefile.in @@ -281,6 +281,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -404,9 +406,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/plugins/auth-pam/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/auth-pam/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/plugins/auth-pam/Makefile + $(AUTOMAKE) --foreign src/plugins/auth-pam/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/plugins/down-root/Makefile.in b/src/plugins/down-root/Makefile.in index 26c4912..f19e012 100644 --- a/src/plugins/down-root/Makefile.in +++ b/src/plugins/down-root/Makefile.in @@ -280,6 +280,8 @@ OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -398,9 +400,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/plugins/down-root/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/down-root/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/plugins/down-root/Makefile + $(AUTOMAKE) --foreign src/plugins/down-root/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/src/plugins/down-root/down-root.c b/src/plugins/down-root/down-root.c index d51d0e5..6931bec 100644 --- a/src/plugins/down-root/down-root.c +++ b/src/plugins/down-root/down-root.c @@ -5,7 +5,8 @@ * packet encryption, packet authentication, and * packet compression. * - * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. + * Copyright (C) 2002-2013 OpenVPN Technologies, Inc. + * Copyright (C) 2013 David Sommerseth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -40,14 +41,16 @@ #include #include #include +#include +#include #include #define DEBUG(verb) ((verb) >= 7) /* Command codes for foreground -> background communication */ -#define COMMAND_RUN_SCRIPT 0 -#define COMMAND_EXIT 1 +#define COMMAND_RUN_SCRIPT 1 +#define COMMAND_EXIT 2 /* Response codes for background -> foreground communication */ #define RESPONSE_INIT_SUCCEEDED 10 @@ -56,24 +59,24 @@ #define RESPONSE_SCRIPT_FAILED 13 /* Background process function */ -static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb); +static void down_root_server (const int fd, char * const * argv, char * const *envp, const int verb); /* * Plugin state, used by foreground */ struct down_root_context { - /* Foreground's socket to background process */ - int foreground_fd; + /* Foreground's socket to background process */ + int foreground_fd; - /* Process ID of background process */ - pid_t background_pid; + /* Process ID of background process */ + pid_t background_pid; - /* Verbosity level of OpenVPN */ - int verb; + /* Verbosity level of OpenVPN */ + int verb; - /* down command */ - char *command; + /* down command */ + char **command; }; /* @@ -84,21 +87,21 @@ struct down_root_context static const char * get_env (const char *name, const char *envp[]) { - if (envp) + if (envp) { - int i; - const int namelen = strlen (name); - for (i = 0; envp[i]; ++i) - { - if (!strncmp (envp[i], name, namelen)) - { - const char *cp = envp[i] + namelen; - if (*cp == '=') - return cp + 1; - } - } + int i; + const int namelen = strlen (name); + for (i = 0; envp[i]; ++i) + { + if (!strncmp (envp[i], name, namelen)) + { + const char *cp = envp[i] + namelen; + if (*cp == '=') + return cp + 1; + } + } } - return NULL; + return NULL; } /* @@ -107,13 +110,13 @@ get_env (const char *name, const char *envp[]) static int string_array_len (const char *array[]) { - int i = 0; - if (array) + int i = 0; + if (array) { - while (array[i]) - ++i; + while (array[i]) + ++i; } - return i; + return i; } /* @@ -123,23 +126,23 @@ string_array_len (const char *array[]) static int recv_control (int fd) { - unsigned char c; - const ssize_t size = read (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return c; - else - return -1; + unsigned char c; + const ssize_t size = read (fd, &c, sizeof (c)); + if (size == sizeof (c)) + return c; + else + return -1; } static int send_control (int fd, int code) { - unsigned char c = (unsigned char) code; - const ssize_t size = write (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return (int) size; - else - return -1; + unsigned char c = (unsigned char) code; + const ssize_t size = write (fd, &c, sizeof (c)); + if (size == sizeof (c)) + return (int) size; + else + return -1; } /* @@ -150,22 +153,22 @@ send_control (int fd, int code) static void daemonize (const char *envp[]) { - const char *daemon_string = get_env ("daemon", envp); - if (daemon_string && daemon_string[0] == '1') + const char *daemon_string = get_env ("daemon", envp); + if (daemon_string && daemon_string[0] == '1') { - const char *log_redirect = get_env ("daemon_log_redirect", envp); - int fd = -1; - if (log_redirect && log_redirect[0] == '1') - fd = dup (2); - if (daemon (0, 0) < 0) - { - fprintf (stderr, "DOWN-ROOT: daemonization failed\n"); - } - else if (fd >= 3) - { - dup2 (fd, 2); - close (fd); - } + const char *log_redirect = get_env ("daemon_log_redirect", envp); + int fd = -1; + if (log_redirect && log_redirect[0] == '1') + fd = dup (2); + if (daemon (0, 0) < 0) + { + warn ("DOWN-ROOT: daemonization failed"); + } + else if (fd >= 3) + { + dup2 (fd, 2); + close (fd); + } } } @@ -182,12 +185,12 @@ daemonize (const char *envp[]) static void close_fds_except (int keep) { - int i; - closelog (); - for (i = 3; i <= 100; ++i) + int i; + closelog (); + for (i = 3; i <= 100; ++i) { - if (i != keep) - close (i); + if (i != keep) + close (i); } } @@ -198,254 +201,261 @@ close_fds_except (int keep) static void set_signals (void) { - signal (SIGTERM, SIG_DFL); + signal (SIGTERM, SIG_DFL); - signal (SIGINT, SIG_IGN); - signal (SIGHUP, SIG_IGN); - signal (SIGUSR1, SIG_IGN); - signal (SIGUSR2, SIG_IGN); - signal (SIGPIPE, SIG_IGN); + signal (SIGINT, SIG_IGN); + signal (SIGHUP, SIG_IGN); + signal (SIGUSR1, SIG_IGN); + signal (SIGUSR2, SIG_IGN); + signal (SIGPIPE, SIG_IGN); } -/* - * convert system() return into a success/failure value - */ -int -system_ok (int stat) + +static void +free_context (struct down_root_context *context) { -#ifdef WIN32 - return stat == 0; -#else - return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0; -#endif + if (context) + { + if (context->command) + { + free (context->command); + } + free (context); + } } -static char * -build_command_line (const char *argv[]) +/* Run the script using execve(). As execve() replaces the + * current process with the new one, do a fork first before + * calling execve() + */ +static int +run_script(char * const *argv, char * const *envp) { - int size = 0; - int n = 0; - int i; - char *string; + pid_t pid; + int ret = 0; - /* precompute size */ - if (argv) + pid = fork(); + if (pid == (pid_t)0) /* child side */ { - for (i = 0; argv[i]; ++i) - { - size += (strlen (argv[i]) + 1); /* string length plus trailing space */ - ++n; - } + execve(argv[0], argv, envp); + /* If execve() fails to run, exit child with exit code 127 */ + err(127, "DOWN-ROOT: Failed execute: %s", argv[0]); } - ++size; /* for null terminator */ - - /* allocate memory */ - string = (char *) malloc (size); - if (!string) + else if (pid < (pid_t)0 ) { - fprintf (stderr, "DOWN-ROOT: out of memory\n"); - exit (1); + warn ("DOWN-ROOT: Failed to fork child to run %s", argv[0]); + return -1; } - string[0] = '\0'; - - /* build string */ - for (i = 0; i < n; ++i) + else /* parent side */ { - strcat (string, argv[i]); - if (i + 1 < n) - strcat (string, " "); + if( waitpid (pid, &ret, 0) != pid ) + { + /* waitpid does not return error information via errno */ + fprintf(stderr, "DOWN-ROOT: waitpid() failed, don't know exit code of child (%s)\n", argv[0]); + return -1; + } } - return string; + return ret; } -static void -free_context (struct down_root_context *context) +OPENVPN_EXPORT openvpn_plugin_handle_t +openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) { - if (context) + struct down_root_context *context; + int i = 0; + + /* + * Allocate our context + */ + context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); + if (!context) { - if (context->command) - free (context->command); - free (context); + warn ("DOWN-ROOT: Could not allocate memory for plug-in context"); + goto error; + } + context->foreground_fd = -1; + + /* + * Intercept the --up and --down callbacks + */ + *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); + + /* + * Make sure we have two string arguments: the first is the .so name, + * the second is the script command. + */ + if (string_array_len (argv) < 2) + { + fprintf (stderr, "DOWN-ROOT: need down script command\n"); + goto error; } -} -OPENVPN_EXPORT openvpn_plugin_handle_t -openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) -{ - struct down_root_context *context; - - /* - * Allocate our context - */ - context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); - if (!context) - goto error; - context->foreground_fd = -1; - - /* - * Intercept the --up and --down callbacks - */ - *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); - - /* - * Make sure we have two string arguments: the first is the .so name, - * the second is the script command. - */ - if (string_array_len (argv) < 2) + /* + * Save the arguments in our context + */ + context->command = calloc(string_array_len(argv), sizeof(char *)); + if (!context->command) + { + warn ("DOWN-ROOT: Could not allocate memory for command array"); + goto error; + } + + /* Ignore argv[0], as it contains just the plug-in file name */ + for (i = 1; i < string_array_len(argv); i++) + { + context->command[i-1] = (char *) argv[i]; + } + + /* + * Get verbosity level from environment + */ { - fprintf (stderr, "DOWN-ROOT: need down script command\n"); - goto error; + const char *verb_string = get_env ("verb", envp); + if (verb_string) + context->verb = atoi (verb_string); } - /* - * Save our argument in context - */ - context->command = build_command_line (&argv[1]); - - /* - * Get verbosity level from environment - */ - { - const char *verb_string = get_env ("verb", envp); - if (verb_string) - context->verb = atoi (verb_string); - } - - return (openvpn_plugin_handle_t) context; - - error: - free_context (context); - return NULL; + return (openvpn_plugin_handle_t) context; + +error: + free_context (context); + return NULL; } OPENVPN_EXPORT int openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ + if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ { - pid_t pid; - int fd[2]; - - /* - * Make a socket for foreground and background processes - * to communicate. - */ - if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) - { - fprintf (stderr, "DOWN-ROOT: socketpair call failed\n"); - return OPENVPN_PLUGIN_FUNC_ERROR; - } - - /* - * Fork off the privileged process. It will remain privileged - * even after the foreground process drops its privileges. - */ - pid = fork (); - - if (pid) - { - int status; - - /* - * Foreground Process - */ - - context->background_pid = pid; - - /* close our copy of child's socket */ - close (fd[1]); - - /* don't let future subprocesses inherit child socket */ - if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) - fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n"); - - /* wait for background child process to initialize */ - status = recv_control (fd[0]); - if (status == RESPONSE_INIT_SUCCEEDED) - { - context->foreground_fd = fd[0]; - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } - } - else - { - /* - * Background Process - */ - - /* close all parent fds except our socket back to parent */ - close_fds_except (fd[1]); - - /* Ignore most signals (the parent will receive them) */ - set_signals (); - - /* Daemonize if --daemon option is set. */ - daemonize (envp); - - /* execute the event loop */ - down_root_server (fd[1], context->command, argv, envp, context->verb); - - close (fd[1]); - exit (0); - return 0; /* NOTREACHED */ - } + pid_t pid; + int fd[2]; + + /* + * Make a socket for foreground and background processes + * to communicate. + */ + if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) + { + warn ("DOWN-ROOT: socketpair call failed"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* + * Fork off the privileged process. It will remain privileged + * even after the foreground process drops its privileges. + */ + pid = fork (); + + if (pid) + { + int status; + + /* + * Foreground Process + */ + + context->background_pid = pid; + + /* close our copy of child's socket */ + close (fd[1]); + + /* don't let future subprocesses inherit child socket */ + if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) + { + warn ("DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed"); + } + + /* wait for background child process to initialize */ + status = recv_control (fd[0]); + if (status == RESPONSE_INIT_SUCCEEDED) + { + context->foreground_fd = fd[0]; + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + } + else + { + /* + * Background Process + */ + + /* close all parent fds except our socket back to parent */ + close_fds_except (fd[1]); + + /* Ignore most signals (the parent will receive them) */ + set_signals (); + + /* Daemonize if --daemon option is set. */ + daemonize (envp); + + /* execute the event loop */ + down_root_server (fd[1], context->command, (char * const *) envp, context->verb); + + close (fd[1]); + exit (0); + return 0; /* NOTREACHED */ + } } - else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) + else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) { - if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) - { - fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n"); - } - else - { - const int status = recv_control (context->foreground_fd); - if (status == RESPONSE_SCRIPT_SUCCEEDED) - return OPENVPN_PLUGIN_FUNC_SUCCESS; - if (status == -1) - fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n"); - } + if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) + { + warn ("DOWN-ROOT: Error sending script execution signal to background process"); + } + else + { + const int status = recv_control (context->foreground_fd); + if (status == RESPONSE_SCRIPT_SUCCEEDED) + return OPENVPN_PLUGIN_FUNC_SUCCESS; + if (status == -1) + { + warn ("DOWN-ROOT: Error receiving script execution confirmation from background process"); + } + } } - return OPENVPN_PLUGIN_FUNC_ERROR; + return OPENVPN_PLUGIN_FUNC_ERROR; } OPENVPN_EXPORT void openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (DEBUG (context->verb)) - fprintf (stderr, "DOWN-ROOT: close\n"); + if (DEBUG (context->verb)) + fprintf (stderr, "DOWN-ROOT: close\n"); - if (context->foreground_fd >= 0) + if (context->foreground_fd >= 0) { - /* tell background process to exit */ - if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) - fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n"); - - /* wait for background process to exit */ - if (context->background_pid > 0) - waitpid (context->background_pid, NULL, 0); - - close (context->foreground_fd); - context->foreground_fd = -1; + /* tell background process to exit */ + if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) + { + warn ("DOWN-ROOT: Error signalling background process to exit"); + } + + /* wait for background process to exit */ + if (context->background_pid > 0) + waitpid (context->background_pid, NULL, 0); + + close (context->foreground_fd); + context->foreground_fd = -1; } - free_context (context); + free_context (context); } OPENVPN_EXPORT void openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (context && context->foreground_fd >= 0) + if (context && context->foreground_fd >= 0) { - /* tell background process to exit */ - send_control (context->foreground_fd, COMMAND_EXIT); - close (context->foreground_fd); - context->foreground_fd = -1; + /* tell background process to exit */ + send_control (context->foreground_fd, COMMAND_EXIT); + close (context->foreground_fd); + context->foreground_fd = -1; } } @@ -453,105 +463,85 @@ openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) * Background process -- runs with privilege. */ static void -down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb) +down_root_server (const int fd, char * const *argv, char * const *envp, const int verb) { - const char *p[3]; - char *command_line = NULL; - char *argv_cat = NULL; - int i; - - /* - * Do initialization - */ - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command); - - /* - * Tell foreground that we initialized successfully - */ - if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) + /* + * Do initialization + */ + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", argv[0]); + + /* + * Tell foreground that we initialized successfully + */ + if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n"); - goto done; + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [1]"); + goto done; } - /* - * Build command line - */ - if (string_array_len (argv) >= 2) - argv_cat = build_command_line (&argv[1]); - else - argv_cat = build_command_line (NULL); - p[0] = command; - p[1] = argv_cat; - p[2] = NULL; - command_line = build_command_line (p); - - /* - * Save envp in environment - */ - for (i = 0; envp[i]; ++i) + /* + * Event loop + */ + while (1) { - putenv ((char *)envp[i]); + int command_code; + int exit_code = -1; + + /* get a command from foreground process */ + command_code = recv_control (fd); + + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); + + switch (command_code) + { + case COMMAND_RUN_SCRIPT: + if ( (exit_code = run_script(argv, envp)) == 0 ) /* Succeeded */ + { + if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) + { + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [2]"); + goto done; + } + } + else /* Failed */ + { + fprintf(stderr, "DOWN-ROOT: BACKGROUND: %s exited with exit code %i\n", argv[0], exit_code); + if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) + { + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [3]"); + goto done; + } + } + break; + + case COMMAND_EXIT: + goto done; + + case -1: + warn ("DOWN-ROOT: BACKGROUND: read error on command channel"); + goto done; + + default: + fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", + command_code); + goto done; + } } - /* - * Event loop - */ - while (1) - { - int command_code; - int status; - - /* get a command from foreground process */ - command_code = recv_control (fd); - - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); - - switch (command_code) - { - case COMMAND_RUN_SCRIPT: - status = system (command_line); - if (system_ok (status)) /* Succeeded */ - { - if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) - { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n"); - goto done; - } - } - else /* Failed */ - { - if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) - { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n"); - goto done; - } - } - break; - - case COMMAND_EXIT: - goto done; - - case -1: - fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n"); - goto done; - - default: - fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", - command_code); - goto done; - } - } +done: + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); - done: - if (argv_cat) - free (argv_cat); - if (command_line) - free (command_line); - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); - - return; + return; } + + +/* +Local variables: +c-file-style: "bsd" +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ -- cgit v1.2.3