diff options
| author | Arne Schwabe <arne@rfc2549.org> | 2014-12-14 23:40:31 +0100 | 
|---|---|---|
| committer | Arne Schwabe <arne@rfc2549.org> | 2014-12-14 23:40:31 +0100 | 
| commit | 370f94cd97e6869be267a69d1ab4943a3418f27c (patch) | |
| tree | 3f6f797a84e7acba708c5f075448558c9a2a552f | |
| parent | a56f8a82e5911fd7d67f7e51b680b2dc9d9d8901 (diff) | |
Update OpenVPN to current -master
--HG--
extra : rebase_source : 85910d09bae449f484bc15d94583e0ac12772282
| -rw-r--r-- | main/openvpn/.gitignore | 1 | ||||
| -rw-r--r-- | main/openvpn/config-version.h | 2 | ||||
| -rw-r--r-- | main/openvpn/doc/doxygen/doc_data_crypto.h | 4 | ||||
| -rw-r--r-- | main/openvpn/doc/doxygen/doc_mainpage.h | 2 | ||||
| -rw-r--r-- | main/openvpn/doc/doxygen/doc_protocol_overview.h | 69 | ||||
| -rw-r--r-- | main/openvpn/doc/openvpn.8 | 21 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/crypto.c | 65 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/crypto.h | 72 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/helper.c | 2 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/mudp.c | 4 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/multi.c | 28 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/multi.h | 8 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/options.c | 4 | ||||
| -rw-r--r-- | main/openvpn/src/plugins/down-root/down-root.c | 696 | 
14 files changed, 489 insertions, 489 deletions
| diff --git a/main/openvpn/.gitignore b/main/openvpn/.gitignore index 538c0208..06ff7c66 100644 --- a/main/openvpn/.gitignore +++ b/main/openvpn/.gitignore @@ -33,6 +33,7 @@ config.sub  configure  configure.h  depcomp +doxygen/  stamp-h1  install-sh  missing diff --git a/main/openvpn/config-version.h b/main/openvpn/config-version.h index a62a07ef..f89c974f 100644 --- a/main/openvpn/config-version.h +++ b/main/openvpn/config-version.h @@ -1,2 +1,2 @@ -#define CONFIGURE_GIT_REVISION "icsopenvpn_624-1c7808deeb82deb2" +#define CONFIGURE_GIT_REVISION "icsopenvpn_625-af9eb9424047f9f5"  #define CONFIGURE_GIT_FLAGS "" diff --git a/main/openvpn/doc/doxygen/doc_data_crypto.h b/main/openvpn/doc/doxygen/doc_data_crypto.h index ee72b8cd..640203f4 100644 --- a/main/openvpn/doc/doxygen/doc_data_crypto.h +++ b/main/openvpn/doc/doxygen/doc_data_crypto.h @@ -69,7 +69,5 @@   *   * @par Crypto algorithms   * This module uses the crypto algorithm implementations of the external - * OpenSSL library.  More precisely, it uses the OpenSSL library's \c - * EVP_Cipher* and \c HMAC_* set of functions to perform cryptographic - * operations on data channel packets. + * crypto library (currently either OpenSSL (default), or PolarSSL).   */ diff --git a/main/openvpn/doc/doxygen/doc_mainpage.h b/main/openvpn/doc/doxygen/doc_mainpage.h index 821b2e87..ed8e324e 100644 --- a/main/openvpn/doc/doxygen/doc_mainpage.h +++ b/main/openvpn/doc/doxygen/doc_mainpage.h @@ -29,7 +29,7 @@   */  /** - * @mainpage OpenVPN v2.1 source code documentation + * @mainpage OpenVPN source code documentation   *   * This documentation describes the internal structure of OpenVPN.  It was   * automatically generated from specially formatted comment blocks in diff --git a/main/openvpn/doc/doxygen/doc_protocol_overview.h b/main/openvpn/doc/doxygen/doc_protocol_overview.h index 26fed331..9edafcfb 100644 --- a/main/openvpn/doc/doxygen/doc_protocol_overview.h +++ b/main/openvpn/doc/doxygen/doc_protocol_overview.h @@ -5,7 +5,7 @@   *             packet encryption, packet authentication, and   *             packet compression.   * - *  Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com> + *  Copyright (C) 2010-2014 Fox Crypto B.V. <openvpn@fox-it.com>   *   *   *  This program is free software; you can redistribute it and/or modify @@ -61,24 +61,26 @@   * following describes the various opcodes available.   *   *  - Control channel messages: - *     - \c P_CONTROL_HARD_RESET_CLIENT_V1 -- %Key method 1, initial %key + *     - \ref P_CONTROL_HARD_RESET_CLIENT_V1 -- %Key method 1, initial %key   *       from client, forget previous state. - *     - \c P_CONTROL_HARD_RESET_SERVER_V1 -- %Key method 1, initial %key + *     - \ref P_CONTROL_HARD_RESET_SERVER_V1 -- %Key method 1, initial %key   *       from server, forget previous state. - *     - \c P_CONTROL_HARD_RESET_CLIENT_V2 -- %Key method 2, initial %key + *     - \ref P_CONTROL_HARD_RESET_CLIENT_V2 -- %Key method 2, initial %key   *       from client, forget previous state. - *     - \c P_CONTROL_HARD_RESET_SERVER_V2 -- %Key method 2, initial %key + *     - \ref P_CONTROL_HARD_RESET_SERVER_V2 -- %Key method 2, initial %key   *       from server, forget previous state. - *     - \c P_CONTROL_SOFT_RESET_V1 -- New %key, with a graceful + *     - \ref P_CONTROL_SOFT_RESET_V1 -- New %key, with a graceful   *       transition from old to new %key in the sense that a transition   *       window exists where both the old or new key_id can be used. - *     - \c P_CONTROL_V1 -- Control channel packet (usually TLS + *     - \ref P_CONTROL_V1 -- Control channel packet (usually TLS   *       ciphertext). - *     - \c P_ACK_V1 -- Acknowledgement for control channel packets + *     - \ref P_ACK_V1 -- Acknowledgement for control channel packets   *       received.   *  - Data channel messages: - *     - \c P_DATA_V1 -- Data channel packet containing data channel + *     - \ref P_DATA_V1 -- Data channel packet containing data channel   *       ciphertext. + *     - \ref P_DATA_V2 -- Data channel packet containing peer-id and data + *       channel ciphertext.   *   * @subsection network_protocol_external_key_id Session IDs and Key IDs   * @@ -139,10 +141,10 @@   * channel is used to exchange random %key material for bidirectional   * cipher and HMAC keys which will be used to secure data channel packets.   * OpenVPN currently implements two %key methods.  %Key method 1 directly - * derives keys using random bits obtained from the \c RAND_bytes() - * OpenSSL function.  %Key method 2 mixes random %key material from both - * sides of the connection using the TLS PRF mixing function.  %Key method - * 2 is the preferred method and is the default for OpenVPN 2.0. + * derives keys using random bits obtained from the \c rand_bytes() function. + * %Key method 2 mixes random %key material from both sides of the connection + * using the TLS PRF mixing function.  %Key method 2 is the preferred method and + * is the default for OpenVPN 2.0+.   *   * The @ref key_generation "Data channel key generation" related page   * describes the %key methods in more detail. @@ -173,27 +175,22 @@   *   * @section network_protocol_data Structure of data channel messages   * - * @subsection network_protocol_data_ciphertext Structure of ciphertext data channel messages - * - * The P_DATA_* payload represents encrypted, encapsulated tunnel packets - * which tend to be either IP packets or Ethernet frames. This is - * essentially the "payload" of the VPN. - * - * Data channel packets in ciphertext form consist of the following parts: - *  - HMAC of ciphertext IV + ciphertext (if not disabled by \c --auth - *    none). - *  - Ciphertext IV (size is cipher-dependent, if not disabled by \c - *    --no-iv). - *  - Tunnel packet ciphertext. - * - * @subsection network_protocol_data_plaintext Structure of plaintext data channel messages - * - * Data channel packets in plaintext form consist of the following parts: - *  - packet-id (4 or 8 bytes, if not disabled by --no-replay). - *     - In TLS mode, 4 bytes are used because the implementation can - *       force a TLS renegotation before \c 2^32 packets are sent. - *     - In pre-shared %key mode, 8 bytes are used (sequence number and \c - *       time_t value) to allow long-term %key usage without packet-id - *       collisions. - *  - User plaintext (n bytes). + * The P_DATA_* payload represents encapsulated tunnel packets which tend to be + * either IP packets or Ethernet frames. This is essentially the "payload" of + * the VPN. Data channel packets consist of a data channel header, and a + * payload. There are two possible formats: + * + * @par P_DATA_V1 + * P_DATA_V1 packets have a 1-byte header, carrying the \ref P_DATA_V1 \c opcode + * and \c key_id, followed by the payload:\n + * <tt> [ 5-bit opcode | 3-bit key_id ] [ payload ] </tt> + * + * @par P_DATA_V2 + * P_DATA_V2 packets have the same 1-byte opcode/key_id, but carrying the \ref + * P_DATA_V2 opcode, followed by a 3-byte peer-id, which uniquely identifies + * the peer:\n + * <tt> [ 5-bit opcode | 3-bit key_id ] [ 24-bit peer-id ] [ payload ] </tt> + * + * See @ref data_crypto for details on the data channel payload format. + *   */ diff --git a/main/openvpn/doc/openvpn.8 b/main/openvpn/doc/openvpn.8 index 96ba5554..532eda5c 100644 --- a/main/openvpn/doc/openvpn.8 +++ b/main/openvpn/doc/openvpn.8 @@ -4609,26 +4609,11 @@ bearing an incorrect HMAC signature can be dropped immediately without  response.  .B file -(required) is a key file which can be in one of two formats: - -.B (1) -An OpenVPN static key file generated by +(required) is a file in OpenVPN static key format which can be generated by  .B \-\-genkey -(required if -.B direction -parameter is used). - -.B (2) -A freeform passphrase file.  In this case the HMAC key will -be derived by taking a secure hash of this file, similar to -the -.BR md5sum (1) -or -.BR sha1sum (1) -commands. -OpenVPN will first try format (1), and if the file fails to parse as -a static key file, format (2) will be used. +Older versions (up to 2.3) supported a freeform passphrase file. +This is no longer supported in newer versions (2.4+).  See the  .B \-\-secret diff --git a/main/openvpn/src/openvpn/crypto.c b/main/openvpn/src/openvpn/crypto.c index ef2bde1d..eaef9643 100644 --- a/main/openvpn/src/openvpn/crypto.c +++ b/main/openvpn/src/openvpn/crypto.c @@ -770,22 +770,13 @@ get_tls_handshake_key (const struct key_type *key_type,  	  }  	else  	  { -	    int hash_size; -  	    CLEAR (key2); -	    /* failed, now try to get hash from a freeform file */ -	    hash_size = read_passphrase_hash (passphrase_file, -					      kt.digest, -					      key2.keys[0].hmac, -					      MAX_HMAC_KEY_LENGTH); -	    ASSERT (hash_size == kt.hmac_length); - -	    /* suceeded */ -	    key2.n = 1; +	    /* failed, now bail out */ -	    msg (M_INFO, -		 "Control Channel Authentication: using '%s' as a free-form passphrase file", +	    msg (M_ERR, +		 "Control Channel Authentication: File '%s' does not have OpenVPN Static Key format. " +		 "Using free-form passphrase file is not supported anymore",  		 passphrase_file);  	  }        } @@ -1012,54 +1003,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags)    gc_free (&gc);  } -int -read_passphrase_hash (const char *passphrase_file, -		      const md_kt_t *digest, -		      uint8_t *output, -		      int len) -{ -  md_ctx_t md; - -  ASSERT (len >= md_kt_size(digest)); -  memset (output, 0, len); - -  md_ctx_init(&md, digest); - -  /* read passphrase file */ -  { -    const int min_passphrase_size = 8; -    uint8_t buf[64]; -    int total_size = 0; -    int fd = platform_open (passphrase_file, O_RDONLY, 0); - -    if (fd == -1) -      msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file); - -    for (;;) -      { -	int size = read (fd, buf, sizeof (buf)); -	if (size == 0) -	  break; -	if (size == -1) -	  msg (M_ERR, "Read error on passphrase file: '%s'", -	       passphrase_file); -	md_ctx_update(&md, buf, size); -	total_size += size; -      } -    close (fd); - -    warn_if_group_others_accessible (passphrase_file); - -    if (total_size < min_passphrase_size) -      msg (M_FATAL, -	   "Passphrase file '%s' is too small (must have at least %d characters)", -	   passphrase_file, min_passphrase_size); -  } -  md_ctx_final(&md, output); -  md_ctx_cleanup(&md); -  return md_kt_size(digest); -} -  /*   * Write key to file, return number of random bits   * written. diff --git a/main/openvpn/src/openvpn/crypto.h b/main/openvpn/src/openvpn/crypto.h index bf2f8028..e4898278 100644 --- a/main/openvpn/src/openvpn/crypto.h +++ b/main/openvpn/src/openvpn/crypto.h @@ -6,7 +6,7 @@   *             packet compression.   *   *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> - *  Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com> + *  Copyright (C) 2010-2014 Fox Crypto B.V. <openvpn@fox-it.com>   *   *  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 + * <tt>[ xxx ]</tt> = unprotected \n + * <tt>[ - xxx - ]</tt> = authenticated \n + * <tt>[ * xxx * ]</tt> = encrypted and authenticated + * + * @par + * <b>CBC data channel cypto format</b> \n + * In CBC mode, both TLS-mode and static key mode are supported. The IV + * consists of random bits to provide unpredictable IVs. \n + * <i>CBC IV format:</i> \n + * <tt> [ - random - ] </tt> \n + * <i>CBC data channel crypto format in TLS-mode:</i> \n + * <tt> [ HMAC ] [ - IV - ] [ * packet ID * ] [ * packet payload * ] </tt> \n + * <i>CBC data channel crypto format in static key mode:</i> \n + * <tt> [ HMAC ] [ - IV - ] [ * packet ID * ] [ * timestamp * ] + * [ * packet payload * ] </tt> + * + * @par + * <b>CFB/OFB data channel crypto format</b> \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 + * <i>CFB/OFB IV format:</i> \n + * <tt>   [ - packet ID - ] [ - timestamp - ] [ - opt: zero-padding - ] </tt>\n + * <i>CFB/OFB data channel crypto format:</i> \n + * <tt>   [ HMAC ] [ - IV - ] [ * packet payload * ] </tt> + * + * @par + * <b>No-crypto data channel format</b> \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 + * <i>No-crypto data channel crypto format in TLS-mode:</i> \n + * <tt> [ HMAC ] [ - packet ID - ] [ - packet payload - ] </tt> \n + * <i>No-crypto data channel crypto format in static key mode:</i> \n + * <tt> [ HMAC ] [ - packet ID - ] [ - timestamp - ] [ - packet payload - ] </tt> + *   */  #ifndef CRYPTO_H diff --git a/main/openvpn/src/openvpn/helper.c b/main/openvpn/src/openvpn/helper.c index 0ed0b2ba..339e2aea 100644 --- a/main/openvpn/src/openvpn/helper.c +++ b/main/openvpn/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/main/openvpn/src/openvpn/mudp.c b/main/openvpn/src/openvpn/mudp.c index 853c08ce..3e3f7508 100644 --- a/main/openvpn/src/openvpn/mudp.c +++ b/main/openvpn/src/openvpn/mudp.c @@ -111,6 +111,10 @@ multi_get_create_instance_udp (struct multi_context *m, bool *floated)  			      break;  			    }  			} + +		      /* should not really end up here, since multi_create_instance returns null +		       * if amount of clients exceeds max_clients */ +		      ASSERT(i < m->max_clients);  		    }  		}  	      else diff --git a/main/openvpn/src/openvpn/multi.c b/main/openvpn/src/openvpn/multi.c index a4334b20..90b3d2dc 100644 --- a/main/openvpn/src/openvpn/multi.c +++ b/main/openvpn/src/openvpn/multi.c @@ -403,7 +403,7 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are  {    if (mi)      { -      struct buffer out = alloc_buf_gc (256, gc); +      struct buffer out = alloc_buf_gc (MULTI_PREFIX_MAX_LENGTH, gc);        const char *cn = tls_common_name (mi->context.c2.tls_multi, true);        if (cn) @@ -420,21 +420,27 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are  void  generate_prefix (struct multi_instance *mi)  { -  mi->msg_prefix = multi_instance_string (mi, true, &mi->gc); +  struct gc_arena gc = gc_new(); +  const char *prefix = multi_instance_string (mi, true, &gc); +  if (prefix) +    strncpynt(mi->msg_prefix, prefix, sizeof(mi->msg_prefix)); +  else +    mi->msg_prefix[0] = '\0';    set_prefix (mi); +  gc_free(&gc);  }  void  ungenerate_prefix (struct multi_instance *mi)  { -  mi->msg_prefix = NULL; +  mi->msg_prefix[0] = '\0';    set_prefix (mi);  }  static const char *  mi_prefix (const struct multi_instance *mi)  { -  if (mi && mi->msg_prefix) +  if (mi && mi->msg_prefix[0])      return mi->msg_prefix;    else      return "UNDEF_I"; @@ -815,8 +821,8 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int  	   */  	  status_printf (so, "TITLE%c%s", sep, title_string);  	  status_printf (so, "TIME%c%s%c%u", sep, time_string (now, 0, false, &gc_top), sep, (unsigned int)now); -	  status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cVirtual IPv6 Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername%cClient ID", -			 sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep); +	  status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cVirtual IPv6 Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername%cClient ID%cPeer ID", +			 sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep);  	  hash_iterator_init (m->hash, &hi);  	  while ((he = hash_iterator_next (&hi)))  	    { @@ -827,10 +833,11 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int  		{  		  status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s%c"  #ifdef MANAGEMENT_DEF_AUTH -				 "%lu", +				 "%lu"  #else -				 "", +				 ""  #endif +				 "%c%"PRIu32,  				 sep, tls_common_name (mi->context.c2.tls_multi, false),  				 sep, mroute_addr_print (&mi->real, &gc),  				 sep, print_in_addr_t (mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc), @@ -841,10 +848,11 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int  				 sep, (unsigned int)mi->created,  				 sep, tls_username (mi->context.c2.tls_multi, false),  #ifdef MANAGEMENT_DEF_AUTH -				 sep, mi->context.c2.mda_context.cid); +				 sep, mi->context.c2.mda_context.cid,  #else -				 sep); +				 sep,  #endif +				 sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX);  		}  	      gc_free (&gc);  	    } diff --git a/main/openvpn/src/openvpn/multi.h b/main/openvpn/src/openvpn/multi.h index ad7f7001..32b89d25 100644 --- a/main/openvpn/src/openvpn/multi.h +++ b/main/openvpn/src/openvpn/multi.h @@ -42,6 +42,8 @@  #include "mtcp.h"  #include "perf.h" +#define MULTI_PREFIX_MAX_LENGTH 256 +  /*   * Walk (don't run) through the routing table,   * deleting old entries, and possibly multi_instance @@ -80,7 +82,7 @@ struct multi_instance {    struct mroute_addr real;      /**< External network address of the                                   *   remote peer. */    ifconfig_pool_handle vaddr_handle; -  const char *msg_prefix; +  char msg_prefix[MULTI_PREFIX_MAX_LENGTH];    /* queued outgoing data in Server/TCP mode */    unsigned int tcp_rwflags; @@ -445,10 +447,10 @@ static inline void  set_prefix (struct multi_instance *mi)  {  #ifdef MULTI_DEBUG_EVENT_LOOP -  if (mi->msg_prefix) +  if (mi->msg_prefix[0])      printf ("[%s]\n", mi->msg_prefix);  #endif -  msg_set_prefix (mi->msg_prefix); +  msg_set_prefix (mi->msg_prefix[0] ? mi->msg_prefix : NULL);  }  static inline void diff --git a/main/openvpn/src/openvpn/options.c b/main/openvpn/src/openvpn/options.c index e096bec2..763e2cbc 100644 --- a/main/openvpn/src/openvpn/options.c +++ b/main/openvpn/src/openvpn/options.c @@ -2103,7 +2103,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 diff --git a/main/openvpn/src/plugins/down-root/down-root.c b/main/openvpn/src/plugins/down-root/down-root.c index d51d0e55..6931becf 100644 --- a/main/openvpn/src/plugins/down-root/down-root.c +++ b/main/openvpn/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. <sales@openvpn.net> + *  Copyright (C) 2002-2013 OpenVPN Technologies, Inc. <sales@openvpn.net> + *  Copyright (C) 2013      David Sommerseth <davids@redhat.com>   *   *  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 <fcntl.h>  #include <signal.h>  #include <syslog.h> +#include <errno.h> +#include <err.h>  #include <openvpn-plugin.h>  #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: +*/ | 
