From 370f94cd97e6869be267a69d1ab4943a3418f27c Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Sun, 14 Dec 2014 23:40:31 +0100 Subject: Update OpenVPN to current -master --HG-- extra : rebase_source : 85910d09bae449f484bc15d94583e0ac12772282 --- main/openvpn/.gitignore | 1 + main/openvpn/config-version.h | 2 +- main/openvpn/doc/doxygen/doc_data_crypto.h | 4 +- main/openvpn/doc/doxygen/doc_mainpage.h | 2 +- main/openvpn/doc/doxygen/doc_protocol_overview.h | 69 ++- main/openvpn/doc/openvpn.8 | 21 +- main/openvpn/src/openvpn/crypto.c | 65 +-- main/openvpn/src/openvpn/crypto.h | 72 ++- main/openvpn/src/openvpn/helper.c | 2 +- main/openvpn/src/openvpn/mudp.c | 4 + main/openvpn/src/openvpn/multi.c | 28 +- main/openvpn/src/openvpn/multi.h | 8 +- main/openvpn/src/openvpn/options.c | 4 +- main/openvpn/src/plugins/down-root/down-root.c | 696 +++++++++++------------ 14 files changed, 489 insertions(+), 489 deletions(-) (limited to 'main/openvpn') 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. + * Copyright (C) 2010-2014 Fox Crypto B.V. * * * 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 + * [ 5-bit opcode | 3-bit key_id ] [ payload ] + * + * @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 + * [ 5-bit opcode | 3-bit key_id ] [ 24-bit peer-id ] [ payload ] + * + * 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. - * 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/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. + * 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