diff options
Diffstat (limited to 'app/openvpn/src')
25 files changed, 817 insertions, 725 deletions
diff --git a/app/openvpn/src/openvpn/crypto.c b/app/openvpn/src/openvpn/crypto.c index 69df29de..eaef9643 100644 --- a/app/openvpn/src/openvpn/crypto.c +++ b/app/openvpn/src/openvpn/crypto.c @@ -223,30 +223,6 @@ err: return; } -int verify_hmac(struct buffer *buf, struct key_ctx *ctx, int offset) -{ - uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; /* HMAC of ciphertext computed locally */ - int hmac_len = 0; - - hmac_ctx_reset(ctx->hmac); - /* Assume the length of the input HMAC */ - hmac_len = hmac_ctx_size (ctx->hmac); - - /* Authentication fails if insufficient data in packet for HMAC */ - if (buf->len - offset < hmac_len) - return 0; - - hmac_ctx_update (ctx->hmac, BPTR (buf) + hmac_len + offset, - BLEN (buf) - hmac_len - offset); - hmac_ctx_final (ctx->hmac, local_hmac); - - /* Compare locally computed HMAC with packet HMAC */ - if (memcmp_constant_time (local_hmac, BPTR (buf) + offset, hmac_len) == 0) - return hmac_len; - - return 0; -} - /* * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. * @@ -273,9 +249,25 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, /* Verify the HMAC */ if (ctx->hmac) { - int hmac_len = verify_hmac(buf, ctx, 0); - if (hmac_len == 0) + int hmac_len; + uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; /* HMAC of ciphertext computed locally */ + + hmac_ctx_reset(ctx->hmac); + + /* Assume the length of the input HMAC */ + hmac_len = hmac_ctx_size (ctx->hmac); + + /* Authentication fails if insufficient data in packet for HMAC */ + if (buf->len < hmac_len) + CRYPT_ERROR ("missing authentication info"); + + hmac_ctx_update (ctx->hmac, BPTR (buf) + hmac_len, BLEN (buf) - hmac_len); + hmac_ctx_final (ctx->hmac, local_hmac); + + /* Compare locally computed HMAC with packet HMAC */ + if (memcmp_constant_time (local_hmac, BPTR (buf), hmac_len)) CRYPT_ERROR ("packet HMAC authentication failed"); + ASSERT (buf_advance (buf, hmac_len)); } @@ -400,28 +392,6 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, } /* - * This verifies if a packet and its HMAC fit to a crypto context. - * - * On success true is returned. - */ -bool -crypto_test_hmac (struct buffer *buf, const struct crypto_options *opt) -{ - if (buf->len > 0 && opt->key_ctx_bi) - { - struct key_ctx *ctx = &opt->key_ctx_bi->decrypt; - - /* Verify the HMAC */ - if (ctx->hmac) - { - /* sizeof(uint32_t) comes from peer_id (3 bytes) and opcode (1 byte) */ - return verify_hmac(buf, ctx, sizeof(uint32_t)) != 0; - } - } - return false; -} - -/* * How many bytes will we add to frame buffer for a given * set of crypto options? */ @@ -800,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); } } @@ -1042,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/app/openvpn/src/openvpn/crypto.h b/app/openvpn/src/openvpn/crypto.h index 3c4e59d7..e4898278 100644 --- a/app/openvpn/src/openvpn/crypto.h +++ b/app/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 @@ -275,9 +345,6 @@ bool openvpn_decrypt (struct buffer *buf, struct buffer work, const struct crypto_options *opt, const struct frame* frame); - -bool crypto_test_hmac (struct buffer *buf, const struct crypto_options *opt); - /** @} name Functions for performing security operations on data channel packets */ void crypto_adjust_frame_parameters(struct frame *frame, diff --git a/app/openvpn/src/openvpn/crypto_backend.h b/app/openvpn/src/openvpn/crypto_backend.h index 87498785..4e45df00 100644 --- a/app/openvpn/src/openvpn/crypto_backend.h +++ b/app/openvpn/src/openvpn/crypto_backend.h @@ -237,8 +237,7 @@ int cipher_kt_mode (const cipher_kt_t *cipher_kt); * * @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. @@ -247,8 +246,7 @@ bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) * * @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/app/openvpn/src/openvpn/forward.c b/app/openvpn/src/openvpn/forward.c index 0bbdedb0..5709ee51 100644 --- a/app/openvpn/src/openvpn/forward.c +++ b/app/openvpn/src/openvpn/forward.c @@ -722,20 +722,11 @@ read_incoming_link (struct context *c) perf_pop (); } -/* - * Input: c->c2.buf - * Output: c->c2.to_tun - */ - -void -process_incoming_link (struct context *c) +bool +process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated) { struct gc_arena gc = gc_new (); - bool decrypt_status; - struct link_socket_info *lsi = get_link_socket_info (c); - const uint8_t *orig_buf = c->c2.buf.data; - - perf_push (PERF_PROC_IN_LINK); + bool decrypt_status = false; if (c->c2.buf.len > 0) { @@ -805,7 +796,7 @@ process_incoming_link (struct context *c) * will load crypto_options with the correct encryption key * and return false. */ - if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options)) + if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options, floated)) { interval_action (&c->c2.tmp_int); @@ -832,11 +823,25 @@ process_incoming_link (struct context *c) /* decryption errors are fatal in TCP mode */ register_signal (c, SIGUSR1, "decryption-error"); /* SOFT-SIGUSR1 -- decryption error in TCP mode */ msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); - goto done; } - +#else /* ENABLE_CRYPTO */ + decrypt_status = true; #endif /* ENABLE_CRYPTO */ + } + else + { + buf_reset (&c->c2.to_tun); + } + gc_free (&gc); + return decrypt_status; +} + +void +process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf) +{ + if (c->c2.buf.len > 0) + { #ifdef ENABLE_FRAGMENT if (c->c2.fragment) fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); @@ -903,9 +908,20 @@ process_incoming_link (struct context *c) { buf_reset (&c->c2.to_tun); } - done: +} + +void +process_incoming_link (struct context *c) +{ + perf_push (PERF_PROC_IN_LINK); + + struct link_socket_info *lsi = get_link_socket_info (c); + const uint8_t *orig_buf = c->c2.buf.data; + + process_incoming_link_part1(c, lsi, false); + process_incoming_link_part2(c, lsi, orig_buf); + perf_pop (); - gc_free (&gc); } /* diff --git a/app/openvpn/src/openvpn/forward.h b/app/openvpn/src/openvpn/forward.h index 1830a00b..af3b0a67 100644 --- a/app/openvpn/src/openvpn/forward.h +++ b/app/openvpn/src/openvpn/forward.h @@ -127,12 +127,11 @@ void encrypt_sign (struct context *c, bool comp_frag); */ void read_incoming_link (struct context *c); - /** - * Process a packet read from the external network interface. + * Starts processing a packet read from the external network interface. * @ingroup external_multiplexer * - * This function controls the processing of a data channel packet which + * This function starts the processing of a data channel packet which * has come out of a VPN tunnel. It's high-level structure is as follows: * - Verify that a nonzero length packet has been received from a valid * source address for the given context \a c. @@ -146,6 +145,25 @@ void read_incoming_link (struct context *c); * - Call \c openvpn_decrypt() of the \link data_crypto Data Channel * Crypto module\endlink to authenticate and decrypt the packet using * the security parameters loaded by \c tls_pre_decrypt() above. + * + * @param c - The context structure of the VPN tunnel associated with the + * packet. + * @param lsi - link_socket_info obtained from context before processing. + * @param floated - Flag indicates that peer has floated. + * + * @return true if packet is authenticated, false otherwise. + */ +bool process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated); + +/** + * Continues processing a packet read from the external network interface. + * @ingroup external_multiplexer + * + * This function continues the processing of a data channel packet which + * has come out of a VPN tunnel. It must be called after + * \c process_incoming_link_part1() function. + * + * It's high-level structure is as follows: * - Call \c fragment_incoming() of the \link fragmentation Data Channel * Fragmentation module\endlink to reassemble the packet if it's * fragmented. @@ -158,9 +176,11 @@ void read_incoming_link (struct context *c); * * @param c - The context structure of the VPN tunnel associated with the * packet. + * @param lsi - link_socket_info obtained from context before processing. + * @param orig_buf - Pointer to a buffer data. + * */ -void process_incoming_link (struct context *c); - +void process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf); /** * Write a packet to the external network interface. diff --git a/app/openvpn/src/openvpn/helper.c b/app/openvpn/src/openvpn/helper.c index 0ed0b2ba..339e2aea 100644 --- a/app/openvpn/src/openvpn/helper.c +++ b/app/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/app/openvpn/src/openvpn/init.c b/app/openvpn/src/openvpn/init.c index 7cec8d9b..b5c81f87 100644 --- a/app/openvpn/src/openvpn/init.c +++ b/app/openvpn/src/openvpn/init.c @@ -1797,12 +1797,14 @@ do_deferred_options (struct context *c, const unsigned int found) 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; } +#endif } /* diff --git a/app/openvpn/src/openvpn/mtu.c b/app/openvpn/src/openvpn/mtu.c index 13f3f6c6..3665a34d 100644 --- a/app/openvpn/src/openvpn/mtu.c +++ b/app/openvpn/src/openvpn/mtu.c @@ -158,8 +158,7 @@ set_mtu_discover_type (int sd, int mtu_type) if (mtu_type >= 0) { #if defined(HAVE_SETSOCKOPT) && defined(SOL_IP) && defined(IP_MTU_DISCOVER) - if (setsockopt - (sd, SOL_IP, IP_MTU_DISCOVER, &mtu_type, sizeof (mtu_type))) + if (setsockopt (sd, SOL_IP, IP_MTU_DISCOVER, (void *) &mtu_type, sizeof (mtu_type))) msg (M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket", mtu_type); #else @@ -288,7 +287,7 @@ void set_sock_extended_error_passing (int sd) { int on = 1; - if (setsockopt (sd, SOL_IP, IP_RECVERR, &on, sizeof (on))) + if (setsockopt (sd, SOL_IP, IP_RECVERR, (void *) &on, sizeof (on))) msg (M_WARN | M_ERRNO, "Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)"); } diff --git a/app/openvpn/src/openvpn/mudp.c b/app/openvpn/src/openvpn/mudp.c index 51227a90..3e3f7508 100644 --- a/app/openvpn/src/openvpn/mudp.c +++ b/app/openvpn/src/openvpn/mudp.c @@ -33,67 +33,19 @@ #if P2MP_SERVER #include "multi.h" +#include <inttypes.h> #include "forward-inline.h" #include "memdbg.h" /* - * Update instance with new peer address - */ -void -update_floated(struct multi_context *m, struct multi_instance *mi, - struct mroute_addr real, uint32_t hv) -{ - struct mroute_addr real_old; - - real_old = mi->real; - generate_prefix (mi); - - /* remove before modifying mi->real, since it also modifies key in hash */ - hash_remove(m->hash, &real_old); - hash_remove(m->iter, &real_old); - - /* update address */ - memcpy(&mi->real, &real, sizeof(real)); - - mi->context.c2.from = m->top.c2.from; - mi->context.c2.to_link_addr = &mi->context.c2.from; - - /* switch to new log prefix */ - generate_prefix (mi); - /* inherit buffers */ - mi->context.c2.buffers = m->top.c2.buffers; - - /* inherit parent link_socket and link_socket_info */ - mi->context.c2.link_socket = m->top.c2.link_socket; - mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from; - - /* fix remote_addr in tls structure */ - tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from); - mi->did_open_context = true; - - hash_add(m->hash, &mi->real, mi, false); - hash_add(m->iter, &mi->real, mi, false); - - mi->did_real_hash = true; -#ifdef MANAGEMENT_DEF_AUTH - hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid); - hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false); -#endif - -#ifdef MANAGEMENT_DEF_AUTH - mi->did_cid_hash = true; -#endif -} - -/* * Get a client instance based on real address. If * the instance doesn't exist, create it while * maintaining real address hash table atomicity. */ struct multi_instance * -multi_get_create_instance_udp (struct multi_context *m) +multi_get_create_instance_udp (struct multi_context *m, bool *floated) { struct gc_arena gc = gc_new (); struct mroute_addr real; @@ -108,32 +60,25 @@ multi_get_create_instance_udp (struct multi_context *m) uint8_t* ptr = BPTR(&m->top.c2.buf); uint8_t op = ptr[0] >> P_OPCODE_SHIFT; uint32_t peer_id; - bool hmac_mismatch = false; + int i; - if (op == P_DATA_V2) + /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */ + if (op == P_DATA_V2 && m->top.c2.buf.len >= (1 + 3)) { - peer_id = ntohl((*(uint32_t*)ptr)) & 0xFFFFFF; + peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF; if ((peer_id < m->max_clients) && (m->instances[peer_id])) { mi = m->instances[peer_id]; - if (!link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from)) - { - msg(D_MULTI_MEDIUM, "float from %s to %s", - print_link_socket_actual (&mi->context.c2.from, &gc), print_link_socket_actual (&m->top.c2.from, &gc)); + *floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from); - /* peer-id is not trusted, so check hmac */ - hmac_mismatch = !(crypto_test_hmac(&m->top.c2.buf, &mi->context.c2.crypto_options)); - if (hmac_mismatch) - { - mi = NULL; - msg (D_MULTI_MEDIUM, "HMAC mismatch for peer-id %d", peer_id); - } - else - { - update_floated(m, mi, real, hv); - } - } + if (*floated) + { + /* reset prefix, since here we are not sure peer is the one it claims to be */ + ungenerate_prefix(mi); + msg (D_MULTI_ERRORS, "Untrusted peer %" PRIu32 " wants to float to %s", peer_id, + mroute_addr_print (&real, &gc)); + } } } else @@ -144,7 +89,7 @@ multi_get_create_instance_udp (struct multi_context *m) mi = (struct multi_instance *) he->value; } } - if (!mi && !hmac_mismatch) + if (!mi) { if (!m->top.c2.tls_auth_standalone || tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf)) @@ -157,8 +102,7 @@ multi_get_create_instance_udp (struct multi_context *m) hash_add_fast (hash, bucket, &mi->real, hv, mi); mi->did_real_hash = true; - int i; - for (i = 0; i < m->max_clients; ++ i) + for (i = 0; i < m->max_clients; ++i) { if (!m->instances[i]) { @@ -167,6 +111,10 @@ multi_get_create_instance_udp (struct multi_context *m) 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/app/openvpn/src/openvpn/mudp.h b/app/openvpn/src/openvpn/mudp.h index 97f961b3..1f15d9d2 100644 --- a/app/openvpn/src/openvpn/mudp.h +++ b/app/openvpn/src/openvpn/mudp.h @@ -65,7 +65,7 @@ void tunnel_server_udp (struct context *top); * packet's source address or if one was a newly created successfully. * NULL if one did not yet exist and a new one was not created. */ -struct multi_instance *multi_get_create_instance_udp (struct multi_context *m); +struct multi_instance *multi_get_create_instance_udp (struct multi_context *m, bool *floated); #endif #endif diff --git a/app/openvpn/src/openvpn/multi.c b/app/openvpn/src/openvpn/multi.c index bd5948c8..90b3d2dc 100644 --- a/app/openvpn/src/openvpn/multi.c +++ b/app/openvpn/src/openvpn/multi.c @@ -39,6 +39,7 @@ #include "gremlin.h" #include "mstats.h" #include "ssl_verify.h" +#include <inttypes.h> #include "memdbg.h" @@ -402,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) @@ -419,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"; @@ -814,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))) { @@ -826,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), @@ -840,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); } @@ -2104,6 +2113,70 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un return ret; } +void multi_process_float (struct multi_context* m, struct multi_instance* mi) +{ + struct mroute_addr real; + struct hash *hash = m->hash; + struct gc_arena gc = gc_new (); + + if (!mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) + goto done; + + const uint32_t hv = hash_value (hash, &real); + struct hash_bucket *bucket = hash_bucket (hash, hv); + + struct hash_element *he = hash_lookup_fast (hash, bucket, &real, hv); + if (he) + { + struct multi_instance *ex_mi = (struct multi_instance *) he->value; + + const char *cn = tls_common_name (mi->context.c2.tls_multi, true); + const char *ex_cn = tls_common_name (ex_mi->context.c2.tls_multi, true); + if (cn && ex_cn && strcmp (cn, ex_cn)) + { + msg (D_MULTI_MEDIUM, "prevent float to %s", + multi_instance_string (ex_mi, false, &gc)); + + mi->context.c2.buf.len = 0; + + goto done; + } + + msg (D_MULTI_MEDIUM, "closing instance %s", multi_instance_string (ex_mi, false, &gc)); + multi_close_instance(m, ex_mi, false); + } + + msg (D_MULTI_MEDIUM, "peer %" PRIu32 " floated from %s to %s", mi->context.c2.tls_multi->peer_id, + mroute_addr_print (&mi->real, &gc), print_link_socket_actual (&m->top.c2.from, &gc)); + + ASSERT (hash_remove(m->hash, &mi->real)); + ASSERT (hash_remove(m->iter, &mi->real)); + + /* change external network address of the remote peer */ + mi->real = real; + generate_prefix (mi); + + mi->context.c2.from = m->top.c2.from; + mi->context.c2.to_link_addr = &mi->context.c2.from; + + /* inherit parent link_socket and link_socket_info */ + mi->context.c2.link_socket = m->top.c2.link_socket; + mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from; + + tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from); + + ASSERT (hash_add (m->hash, &mi->real, mi, false)); + ASSERT (hash_add (m->iter, &mi->real, mi, false)); + +#ifdef MANAGEMENT_DEF_AUTH + hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid); + hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false); +#endif + +done: + gc_free (&gc); +} + /* * Process packets in the TCP/UDP socket -> TUN/TAP interface direction, * i.e. client -> server direction. @@ -2118,6 +2191,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins unsigned int mroute_flags; struct multi_instance *mi; bool ret = true; + bool floated = false; if (m->pending) return true; @@ -2127,7 +2201,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins #ifdef MULTI_DEBUG_EVENT_LOOP printf ("TCP/UDP -> TUN [%d]\n", BLEN (&m->top.c2.buf)); #endif - multi_set_pending (m, multi_get_create_instance_udp (m)); + multi_set_pending (m, multi_get_create_instance_udp (m, &floated)); } else multi_set_pending (m, instance); @@ -2145,13 +2219,30 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins c->c2.buf = m->top.c2.buf; /* transfer from-addr from top-level context buffer to instance */ - c->c2.from = m->top.c2.from; + if (!floated) + c->c2.from = m->top.c2.from; } if (BLEN (&c->c2.buf) > 0) { + struct link_socket_info *lsi; + const uint8_t *orig_buf; + /* decrypt in instance context */ - process_incoming_link (c); + + perf_push (PERF_PROC_IN_LINK); + lsi = get_link_socket_info (c); + orig_buf = c->c2.buf.data; + if (process_incoming_link_part1(c, lsi, floated)) + { + if (floated) + { + multi_process_float (m, m->pending); + } + + process_incoming_link_part2(c, lsi, orig_buf); + } + perf_pop (); if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN) { diff --git a/app/openvpn/src/openvpn/multi.h b/app/openvpn/src/openvpn/multi.h index 0446fbfc..32b89d25 100644 --- a/app/openvpn/src/openvpn/multi.h +++ b/app/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; @@ -125,7 +127,8 @@ struct multi_context { # define MC_WORK_THREAD (MC_MULTI_THREADED_WORKER|MC_MULTI_THREADED_SCHEDULER) int thread_mode; - struct multi_instance** instances; + struct multi_instance** instances; /**< Array of multi_instances. An instance can be + * accessed using peer-id as an index. */ struct hash *hash; /**< VPN tunnel instances indexed by real * address of the remote peer. */ @@ -220,6 +223,16 @@ void multi_close_instance (struct multi_context *m, struct multi_instance *mi, b bool multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags); +/** + * Handles peer floating. + * + * If peer is floated to a taken address, either drops packet + * (if peer that owns address has different CN) or disconnects + * existing peer. Updates multi_instance with new address, + * updates hashtables in multi_context. + */ +void multi_process_float (struct multi_context* m, struct multi_instance* mi); + #define MPP_PRE_SELECT (1<<0) #define MPP_CONDITIONAL_PRE_SELECT (1<<1) #define MPP_CLOSE_ON_SIGNAL (1<<2) @@ -421,6 +434,12 @@ multi_route_defined (const struct multi_context *m, } /* + * Takes prefix away from multi_instance. + */ +void +ungenerate_prefix (struct multi_instance *mi); + +/* * Set a msg() function prefix with our current client instance ID. */ @@ -428,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/app/openvpn/src/openvpn/options.c b/app/openvpn/src/openvpn/options.c index 1ca4ad57..763e2cbc 100644 --- a/app/openvpn/src/openvpn/options.c +++ b/app/openvpn/src/openvpn/options.c @@ -570,6 +570,7 @@ static const char usage_message[] = "--tls-version-min <version> ['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 <version> : 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" @@ -1976,9 +1977,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (ce->proto == PROTO_TCP_SERVER && (options->connection_list->len > 1)) msg (M_USAGE, "TCP server mode allows at most one --remote address"); - if (options->routes && ((options->routes->flags & RG_BLOCK_LOCAL) && (options->routes->flags & RG_UNBLOCK_LOCAL))) - msg (M_USAGE, "unblock-local and block-local options of redirect-gateway/redirect-private are mutatlly exclusive"); - #if P2MP_SERVER /* @@ -2038,7 +2036,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne #endif if (options->routes && (options->routes->flags & RG_ENABLE)) msg (M_USAGE, "--redirect-gateway cannot be used with --mode server (however --push \"redirect-gateway\" is fine)"); - if (options->route_delay_defined) msg (M_USAGE, "--route-delay cannot be used with --mode server"); if (options->up_delay) @@ -2106,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 @@ -3898,8 +3897,7 @@ apply_push_options (struct options *options, struct buffer *buf, unsigned int permission_mask, unsigned int *option_types_found, - struct env_set *es, - struct tls_multi *tls_multi) + struct env_set *es) { char line[OPTION_PARM_SIZE]; int line_num = 0; @@ -5325,8 +5323,6 @@ add_option (struct options *options, options->routes->flags |= RG_BYPASS_DNS; else if (streq (p[j], "block-local")) options->routes->flags |= RG_BLOCK_LOCAL; - else if (streq (p[j], "unblock-local")) - options->routes->flags |= RG_UNBLOCK_LOCAL; else { msg (msglevel, "unknown --%s flag: %s", p[0], p[j]); @@ -6568,14 +6564,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]) diff --git a/app/openvpn/src/openvpn/options.h b/app/openvpn/src/openvpn/options.h index d5f7e95d..a51b8ab5 100644 --- a/app/openvpn/src/openvpn/options.h +++ b/app/openvpn/src/openvpn/options.h @@ -716,13 +716,11 @@ void options_postprocess (struct options *options); void pre_pull_save (struct options *o); void pre_pull_restore (struct options *o, struct gc_arena *gc); -struct tls_multi; bool apply_push_options (struct options *options, struct buffer *buf, unsigned int permission_mask, unsigned int *option_types_found, - struct env_set *es, - struct tls_multi* tls_multi); + struct env_set *es); void options_detach (struct options *o); diff --git a/app/openvpn/src/openvpn/push.c b/app/openvpn/src/openvpn/push.c index c7844499..385be1d5 100644 --- a/app/openvpn/src/openvpn/push.c +++ b/app/openvpn/src/openvpn/push.c @@ -475,8 +475,7 @@ process_incoming_push_msg (struct context *c, &buf, permission_mask, option_types_found, - c->c2.es, - c->c2.tls_multi)) + c->c2.es)) switch (c->options.push_continuation) { case 0: diff --git a/app/openvpn/src/openvpn/route.c b/app/openvpn/src/openvpn/route.c index c330169a..1cb98c03 100644 --- a/app/openvpn/src/openvpn/route.c +++ b/app/openvpn/src/openvpn/route.c @@ -277,7 +277,7 @@ init_route (struct route_ipv4 *r, /* get_special_addr replaces specialaddr with a special ip addr like gw. getaddrinfo is called to convert a a addrinfo struct */ - if(get_special_addr (rl, ro->network, &special.s_addr, &status)) + if(get_special_addr (rl, ro->network, (in_addr_t *) &special.s_addr, &status)) { special.s_addr = htonl(special.s_addr); ret = openvpn_getaddrinfo(0, inet_ntoa(special), NULL, 0, NULL, @@ -520,51 +520,6 @@ add_block_local_item (struct route_list *rl, } static void -add_unblock_local (struct route_list *rl) -{ - const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED); - - if (rl->flags & RG_UNBLOCK_LOCAL - && (rl->rgi.flags & rgi_needed) == rgi_needed) - { - /* unblock access to local subnet */ - struct route_ipv4 *r; - - ALLOC_OBJ_GC (r, struct route_ipv4, &rl->gc); - int i; - - CLEAR(*r); - r->flags = RT_DEFINED; - r->network = rl->rgi.gateway.addr & rl->rgi.gateway.netmask; - r->netmask = rl->rgi.gateway.netmask; - r->gateway = rl->rgi.gateway.addr; - r->next = rl->routes; - rl->routes = r; - - /* Additional local networks */ - for (i = 0; i < rl->rgi.n_addrs; ++i) - { - const struct route_gateway_address *gwa = &rl->rgi.addrs[i]; - - /* omit the add/subnet in &rl->rgi which we processed above */ - if (!((rl->rgi.gateway.addr & rl->rgi.gateway.netmask) == (gwa->addr & gwa->netmask) - && rl->rgi.gateway.netmask == gwa->netmask)) - { - ALLOC_OBJ_GC (r, struct route_ipv4, &rl->gc); - CLEAR(*r); - r->flags = RT_DEFINED; - r->network = gwa->addr & gwa->netmask; - r->netmask = gwa->netmask; - r->gateway = gwa->addr; - r->next = rl->routes; - rl->routes=r; - } - } - } -} - - -static void add_block_local (struct route_list *rl) { const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED); @@ -595,8 +550,6 @@ add_block_local (struct route_list *rl) } } - - bool init_route_list (struct route_list *rl, const struct route_option_list *opt, @@ -665,8 +618,6 @@ init_route_list (struct route_list *rl, } } - - add_unblock_local (rl); if (rl->flags & RG_ENABLE) { add_block_local (rl); @@ -863,12 +814,10 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u { msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err); } -#ifndef TARGET_ANDROID else if (!(rl->rgi.flags & RGI_ADDR_DEFINED)) { msg (M_WARN, "%s Cannot read current default gateway from system", err); } -#endif else if (!(rl->spec.flags & RTSA_REMOTE_HOST)) { msg (M_WARN, "%s Cannot obtain current remote host address", err); @@ -2538,6 +2487,7 @@ get_default_gateway (struct route_gateway_info *rgi) CLEAR(*rgi); +#ifndef TARGET_ANDROID /* get default gateway IP addr */ { FILE *fp = fopen ("/proc/net/route", "r"); @@ -2594,6 +2544,12 @@ get_default_gateway (struct route_gateway_info *rgi) } } } +#else + /* Android, set some pseudo GW, addr is in host byte order */ + rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w'; + rgi->flags |= RGI_ADDR_DEFINED; + strcpy(best_name, "android-gw"); +#endif /* scan adapter list */ if (rgi->flags & RGI_ADDR_DEFINED) diff --git a/app/openvpn/src/openvpn/route.h b/app/openvpn/src/openvpn/route.h index 2b1ae3e8..f3c01501 100644 --- a/app/openvpn/src/openvpn/route.h +++ b/app/openvpn/src/openvpn/route.h @@ -88,7 +88,6 @@ struct route_option { #define RG_REROUTE_GW (1<<5) #define RG_AUTO_LOCAL (1<<6) #define RG_BLOCK_LOCAL (1<<7) -#define RG_UNBLOCK_LOCAL (1<<8) struct route_option_list { unsigned int flags; /* RG_x flags */ diff --git a/app/openvpn/src/openvpn/socket.c b/app/openvpn/src/openvpn/socket.c index c649d627..331a9d9f 100644 --- a/app/openvpn/src/openvpn/socket.c +++ b/app/openvpn/src/openvpn/socket.c @@ -729,7 +729,7 @@ static inline void socket_set_mark (int sd, int mark) { #if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK - if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, &mark, sizeof (mark)) != 0) + if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof (mark)) != 0) msg (M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark); #endif } @@ -1081,6 +1081,14 @@ socket_listen_accept (socket_descriptor_t sd, return new_sd; } +/* older mingw versions and WinXP do not have this define, + * but Vista and up support the functionality - just define it here + */ +#ifdef WIN32 +# ifndef IPV6_V6ONLY +# define IPV6_V6ONLY 27 +# endif +#endif void socket_bind (socket_descriptor_t sd, struct addrinfo *local, @@ -1117,7 +1125,7 @@ socket_bind (socket_descriptor_t sd, int v6only = ipv6only ? 1: 0; /* setsockopt must have an "int" */ msg (M_INFO, "setsockopt(IPV6_V6ONLY=%d)", v6only); - if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))) + if (setsockopt (sd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only))) { msg (M_NONFATAL|M_ERRNO, "Setting IPV6_V6ONLY=%d failed", v6only); } @@ -1211,7 +1219,7 @@ openvpn_connect (socket_descriptor_t sd, } } #else - status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); + status = connect (sd, remote, af_addr_size(remote->sa_family)); if (status) status = openvpn_errno (); #endif @@ -2658,7 +2666,7 @@ proto_is_tcp(int proto) { if (proto < 0 || proto >= PROTO_N) ASSERT(0); - return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER || proto == PROTO_TCP_CLIENT; + return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER; } int @@ -2916,6 +2924,7 @@ link_socket_read_udp_posix (struct link_socket *sock, #endif buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0, &from->dest.addr.sa, &fromlen); + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ if (buf->len >= 0 && expectedlen && fromlen != expectedlen) bad_address_length (fromlen, expectedlen); return buf->len; @@ -3060,10 +3069,7 @@ socket_recv_queue (struct link_socket *sock, int maxsize) if (proto_is_udp(sock->info.proto)) { sock->reads.addr_defined = true; - if (sock->info.af == AF_INET) - sock->reads.addrlen = sizeof (sock->reads.addr); - else - sock->reads.addrlen = sizeof (sock->reads.addr6); + sock->reads.addrlen = sizeof (sock->reads.addr6); status = WSARecvFrom( sock->sd, wsabuf, @@ -3095,9 +3101,10 @@ socket_recv_queue (struct link_socket *sock, int maxsize) if (!status) /* operation completed immediately? */ { - int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family); - if (sock->reads.addr_defined && sock->reads.addrlen != addrlen) - bad_address_length (sock->reads.addrlen, addrlen); + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ + int af_len = af_addr_size (sock->info.af); + if (sock->reads.addr_defined && af_len && sock->reads.addrlen != af_len) + bad_address_length (sock->reads.addrlen, af_len); sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; /* since we got an immediate return, we must signal the event object ourselves */ @@ -3159,7 +3166,7 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li { /* set destination address for UDP writes */ sock->writes.addr_defined = true; - if (sock->info.af == AF_INET6) + if (to->dest.addr.sa.sa_family == AF_INET6) { sock->writes.addr6 = to->dest.addr.in6; sock->writes.addrlen = sizeof (sock->writes.addr6); diff --git a/app/openvpn/src/openvpn/ssl.c b/app/openvpn/src/openvpn/ssl.c index f79f42d9..cdc8eb19 100644 --- a/app/openvpn/src/openvpn/ssl.c +++ b/app/openvpn/src/openvpn/ssl.c @@ -454,7 +454,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) @@ -2036,7 +2036,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); @@ -2773,7 +2777,8 @@ bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt) + struct crypto_options *opt, + bool floated) { struct gc_arena gc = gc_new (); bool ret = false; @@ -2817,7 +2822,7 @@ tls_pre_decrypt (struct tls_multi *multi, #ifdef ENABLE_DEF_AUTH && !ks->auth_deferred #endif - && link_socket_actual_match (from, &ks->remote_addr)) + && (floated || link_socket_actual_match (from, &ks->remote_addr))) { /* return appropriate data channel decrypt key in opt */ opt->key_ctx_bi = &ks->key; @@ -3492,27 +3497,30 @@ tls_rec_payload (struct tls_multi *multi, return ret; } -/* Update the remote_addr, needed if a client floats. */ void -tls_update_remote_addr (struct tls_multi *multi, -const struct link_socket_actual *from) +tls_update_remote_addr (struct tls_multi *multi, const struct link_socket_actual *addr) { struct gc_arena gc = gc_new (); - int i; + int i, j; - for (i = 0; i < KEY_SCAN_SIZE; ++i) + for (i = 0; i < TM_SIZE; ++i) { - struct key_state *ks = multi->key_scan[i]; - if (DECRYPT_KEY_ENABLED (multi, ks) && ks->authenticated && link_socket_actual_defined(&ks->remote_addr)) - { - if (link_socket_actual_match (from, &ks->remote_addr)) - continue; - dmsg (D_TLS_KEYSELECT, - "TLS: tls_update_remote_addr from IP=%s to IP=%s", + struct tls_session *session = &multi->session[i]; + + for (j = 0; j < KS_SIZE; ++j) + { + struct key_state *ks = &session->key[j]; + + if (!link_socket_actual_defined(&ks->remote_addr) || + link_socket_actual_match (addr, &ks->remote_addr)) + continue; + + dmsg (D_TLS_KEYSELECT, "TLS: tls_update_remote_addr from IP=%s to IP=%s", print_link_socket_actual (&ks->remote_addr, &gc), - print_link_socket_actual (from, &gc)); - memcpy(&ks->remote_addr, from, sizeof(*from)); - } + print_link_socket_actual (addr, &gc)); + + ks->remote_addr = *addr; + } } gc_free (&gc); } diff --git a/app/openvpn/src/openvpn/ssl.h b/app/openvpn/src/openvpn/ssl.h index a338745e..7e5a203e 100644 --- a/app/openvpn/src/openvpn/ssl.h +++ b/app/openvpn/src/openvpn/ssl.h @@ -306,7 +306,8 @@ int tls_multi_process (struct tls_multi *multi, bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt); + struct crypto_options *opt, + bool floated); /**************************************************************************/ @@ -431,11 +432,14 @@ bool tls_send_payload (struct tls_multi *multi, bool tls_rec_payload (struct tls_multi *multi, struct buffer *buf); -/* - * Update remote address of a tls_multi structure +/** + * Updates remote address in TLS sessions. + * + * @param multi - Tunnel to update + * @param addr - new address */ void tls_update_remote_addr (struct tls_multi *multi, - const struct link_socket_actual *from); + const struct link_socket_actual *addr); #ifdef MANAGEMENT_DEF_AUTH static inline char * diff --git a/app/openvpn/src/openvpn/ssl_backend.h b/app/openvpn/src/openvpn/ssl_backend.h index bfd15496..b0777bf5 100644 --- a/app/openvpn/src/openvpn/ssl_backend.h +++ b/app/openvpn/src/openvpn/ssl_backend.h @@ -109,11 +109,12 @@ void tls_clear_error(); * @return One of the TLS_VER_x constants or TLS_VER_BAD * if a parse error should be flagged. */ -#define TLS_VER_BAD -1 -#define TLS_VER_1_0 0 /* default */ -#define TLS_VER_1_1 1 -#define TLS_VER_1_2 2 -int tls_version_min_parse(const char *vstr, const char *extra); +#define TLS_VER_BAD -1 +#define TLS_VER_UNSPEC 0 /* default */ +#define TLS_VER_1_0 1 +#define TLS_VER_1_1 2 +#define TLS_VER_1_2 3 +int tls_version_parse(const char *vstr, const char *extra); /** * Return the maximum TLS version (as a TLS_VER_x constant) diff --git a/app/openvpn/src/openvpn/ssl_common.h b/app/openvpn/src/openvpn/ssl_common.h index cb0ba628..6222bd67 100644 --- a/app/openvpn/src/openvpn/ssl_common.h +++ b/app/openvpn/src/openvpn/ssl_common.h @@ -296,8 +296,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 diff --git a/app/openvpn/src/openvpn/ssl_openssl.c b/app/openvpn/src/openvpn/ssl_openssl.c index adf3ae6f..6782a953 100644 --- a/app/openvpn/src/openvpn/ssl_openssl.c +++ b/app/openvpn/src/openvpn/ssl_openssl.c @@ -184,15 +184,23 @@ 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) + int tls_ver_max = TLS_VER_UNSPEC; + const int tls_ver_min = + (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK; + + 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 SSL_CTX_set_options (ctx->ctx, sslopt); diff --git a/app/openvpn/src/openvpn/ssl_polarssl.c b/app/openvpn/src/openvpn/ssl_polarssl.c index 387e6369..20368857 100644 --- a/app/openvpn/src/openvpn/ssl_polarssl.c +++ b/app/openvpn/src/openvpn/ssl_polarssl.c @@ -685,6 +685,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) { @@ -743,30 +777,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 */ @@ -810,8 +846,8 @@ key_state_write_plaintext (struct key_state_ssl *ks, struct buffer *buf) if (0 == buf->len) { - return 0; perf_pop (); + return 0; } retval = ssl_write(ks->ctx, BPTR(buf), buf->len); diff --git a/app/openvpn/src/plugins/down-root/down-root.c b/app/openvpn/src/plugins/down-root/down-root.c index d51d0e55..6931becf 100644 --- a/app/openvpn/src/plugins/down-root/down-root.c +++ b/app/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: +*/ |