From 3cdb12b7fc8c5c1b8697697258dca0deb3124e05 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Sat, 10 Aug 2013 22:39:39 +0200 Subject: Update openvpn source code --- openvpn/INSTALL | 2 - openvpn/configure.ac | 12 +- openvpn/doc/openvpn.8 | 35 +++ openvpn/include/openvpn-plugin.h | 25 ++- openvpn/sample/sample-plugins/log/build | 2 +- openvpn/sample/sample-plugins/log/log_v3.c | 5 + openvpn/src/openvpn/options.c | 36 ++- openvpn/src/openvpn/plugin.c | 5 +- openvpn/src/openvpn/ssl.c | 26 ++- openvpn/src/openvpn/ssl_backend.h | 30 +++ openvpn/src/openvpn/ssl_common.h | 4 +- openvpn/src/openvpn/ssl_openssl.c | 39 +++- openvpn/src/openvpn/ssl_polarssl.c | 40 ++++ openvpn/src/openvpn/ssl_verify.c | 2 - openvpn/src/openvpn/tun.c | 338 +++++++++++++++++++++++------ openvpn/src/openvpn/tun.h | 3 + 16 files changed, 502 insertions(+), 102 deletions(-) (limited to 'openvpn') diff --git a/openvpn/INSTALL b/openvpn/INSTALL index ed696673..61dc9758 100644 --- a/openvpn/INSTALL +++ b/openvpn/INSTALL @@ -169,8 +169,6 @@ OPTIONS for ./configure: --disable-server disable server support only (but retain client support) [default=yes] --disable-plugins disable plug-in support [default=yes] - --disable-eurephia disable support for the eurephia plug-in - [default=yes] --disable-management disable management server support [default=yes] --enable-pkcs11 enable pkcs11 support [default=no] --disable-socks disable Socks support [default=yes] diff --git a/openvpn/configure.ac b/openvpn/configure.ac index 5da5772b..65c639c5 100644 --- a/openvpn/configure.ac +++ b/openvpn/configure.ac @@ -100,13 +100,6 @@ AC_ARG_ENABLE( [enable_plugins="yes"] ) -AC_ARG_ENABLE( - [eurephia], - [AS_HELP_STRING([--disable-eurephia], [disable support for the eurephia plug-in @<:@default=yes@:>@])], - , - [enable_eurephia="yes"] -) - AC_ARG_ENABLE( [management], [AS_HELP_STRING([--disable-management], [disable management server support @<:@default=yes@:>@])], @@ -459,7 +452,7 @@ SOCKET_INCLUDES=" " AC_CHECK_HEADERS( - [net/if.h netinet/ip.h netinet/if_ether.h resolv.h sys/un.h], + [net/if.h netinet/ip.h netinet/if_ether.h resolv.h sys/un.h net/if_utun.h sys/kern_control.h], , , [[${SOCKET_INCLUDES}]] @@ -1019,8 +1012,7 @@ fi if test "${enable_plugins}" = "yes"; then OPTIONAL_DL_LIBS="${DL_LIBS}" - AC_DEFINE([ENABLE_PLUGIN], [1], [Enable systemd support]) - test "${enable_eurephia}" = "yes" && AC_DEFINE([ENABLE_EUREPHIA], [1], [Enable support for the eurephia plug-in]) + AC_DEFINE([ENABLE_PLUGIN], [1], [Enable plug-in support]) else enable_plugin_auth_pam="no" enable_plugin_down_root="no" diff --git a/openvpn/doc/openvpn.8 b/openvpn/doc/openvpn.8 index 42c7bf6e..868fb841 100644 --- a/openvpn/doc/openvpn.8 +++ b/openvpn/doc/openvpn.8 @@ -804,6 +804,17 @@ also specify or .B \-\-dev-type tap. +Under Mac OS X this option can be used to specify the default tun +implementation. Using +.B \-\-dev\-node utun +forces usage of the native Darwin tun kernel support. Use +.B \-\-dev\-node utunN +to select a specific utun instance. To force using the tun.kext (/dev/tunX) use +.B \-\-dev\-node tun +. When not specifying a +.B \-\-dev\-node +option openvpn will first try to open utun, and fall back to tun.kext. + On Windows systems, select the TAP-Win32 adapter which is named .B node @@ -1879,6 +1890,11 @@ reasons for having OpenVPN fail if it detects problems in a config file. Having said that, there are valid reasons for wanting new software features to gracefully degrade when encountered by older software versions. + +It is also possible to tag a single directive so as not to trigger +a fatal error if the directive isn't recognized. To do this, +prepend the following before the directive: +.B setenv opt .\"********************************************************* .TP .B \-\-setenv-safe name value @@ -4234,6 +4250,15 @@ when you built your peer's certificate (see above). .\"********************************************************* .TP +.B \-\-tls-version-min version ['or-highest'] +Sets the minimum +TLS version we will accept from the peer (default is "1.0"). +Examples for version +include "1.0", "1.1", or "1.2". If 'or-highest' is specified +and version is not recognized, we will only accept the highest TLS +version supported by the local SSL implementation. +.\"********************************************************* +.TP .B \-\-pkcs12 file Specify a PKCS #12 file containing local private key, local certificate, and root CA certificate. @@ -5965,6 +5990,16 @@ Set prior to execution of the script. .\"********************************************************* .TP +.B tls_digest_{n} +Contains the certificate SHA1 fingerprint/digest hash value, +where +.B n +is the verification level. Only set for TLS connections. Set prior +to execution of +.B \-\-tls-verify +script. +.\"********************************************************* +.TP .B tls_id_{n} A series of certificate fields from the remote peer, where diff --git a/openvpn/include/openvpn-plugin.h b/openvpn/include/openvpn-plugin.h index 0879f490..03da92ab 100644 --- a/openvpn/include/openvpn-plugin.h +++ b/openvpn/include/openvpn-plugin.h @@ -201,10 +201,15 @@ struct openvpn_plugin_string_list * * Version Comment * 1 Initial plugin v3 structures providing the same API as - * the v2 plugin interface + X509 certificate information. + * the v2 plugin interface, X509 certificate information + + * a logging API for plug-ins. + * + * 2 Added ssl_api member in struct openvpn_plugin_args_open_in + * which identifies the SSL implementation OpenVPN is compiled + * against. * */ -#define OPENVPN_PLUGINv3_STRUCTVER 1 +#define OPENVPN_PLUGINv3_STRUCTVER 2 /** * Definitions needed for the plug-in callback functions. @@ -259,6 +264,18 @@ struct openvpn_plugin_callbacks plugin_vlog_t plugin_vlog; }; +/** + * Used by the openvpn_plugin_open_v3() function to indicate to the + * plug-in what kind of SSL implementation OpenVPN uses. This is + * to avoid SEGV issues when OpenVPN is complied against PolarSSL + * and the plug-in against OpenSSL. + */ +typedef enum { + SSLAPI_NONE, + SSLAPI_OPENSSL, + SSLAPI_POLARSSL +} ovpnSSLAPI; + /** * Arguments used to transport variables to the plug-in. * The struct openvpn_plugin_args_open_in is only used @@ -286,6 +303,7 @@ struct openvpn_plugin_args_open_in const char ** const argv; const char ** const envp; struct openvpn_plugin_callbacks *callbacks; + const ovpnSSLAPI ssl_api; }; @@ -557,7 +575,8 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2) * ARGUMENTS * * version : fixed value, defines the API version of the OpenVPN plug-in API. The plug-in - * should validate that this value is matching the OPENVPN_PLUGIN_VERSION value. + * should validate that this value is matching the OPENVPN_PLUGINv3_STRUCTVER + * value. * * arguments : Structure with all arguments available to the plug-in. * diff --git a/openvpn/sample/sample-plugins/log/build b/openvpn/sample/sample-plugins/log/build index bbb05f7c..c07ec408 100755 --- a/openvpn/sample/sample-plugins/log/build +++ b/openvpn/sample/sample-plugins/log/build @@ -6,7 +6,7 @@ # # This directory is where we will look for openvpn-plugin.h -CPPFLAGS="${CPPFLAGS:--I../../..}" +CPPFLAGS="${CPPFLAGS:--I../../../include}" CC="${CC:-gcc}" CFLAGS="${CFLAGS:--O2 -Wall -g}" diff --git a/openvpn/sample/sample-plugins/log/log_v3.c b/openvpn/sample/sample-plugins/log/log_v3.c index 742c7568..4d3af91a 100644 --- a/openvpn/sample/sample-plugins/log/log_v3.c +++ b/openvpn/sample/sample-plugins/log/log_v3.c @@ -85,6 +85,11 @@ openvpn_plugin_open_v3 (const int v3structver, return OPENVPN_PLUGIN_FUNC_ERROR; } + if( args->ssl_api != SSLAPI_OPENSSL ) { + printf("This plug-in can only be used against OpenVPN with OpenSSL\n"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + /* Which callbacks to intercept. */ ret->type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | diff --git a/openvpn/src/openvpn/options.c b/openvpn/src/openvpn/options.c index e31f918f..48065f99 100644 --- a/openvpn/src/openvpn/options.c +++ b/openvpn/src/openvpn/options.c @@ -6,9 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. - * - * Additions for eurephia plugin done by: - * David Sommerseth Copyright (C) 2009 + * Copyright (C) 2008-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 @@ -107,9 +105,6 @@ const char title_string[] = #ifdef ENABLE_PKCS11 " [PKCS11]" #endif -#ifdef ENABLE_EUREPHIA - " [eurephia]" -#endif #if ENABLE_IP_PKTINFO " [MH]" #endif @@ -582,6 +577,9 @@ static const char usage_message[] = " by a Certificate Authority in --ca file.\n" "--extra-certs file : one or more PEM certs that complete the cert chain.\n" "--key file : Local private key in .pem format.\n" + "--tls-version-min ['or-highest'] : sets the minimum TLS version we\n" + " will accept from the peer. If version is unrecognized and 'or-highest'\n" + " is specified, require max TLS version supported by SSL implementation.\n" #ifndef ENABLE_CRYPTO_POLARSSL "--pkcs12 file : PKCS#12 file containing local private key, local certificate\n" " and optionally the root CA certificate.\n" @@ -4013,7 +4011,18 @@ add_option (struct options *options, const bool pull_mode = BOOL_CAST (permission_mask & OPT_P_PULL_MODE); int msglevel_fc = msglevel_forward_compatible (options, msglevel); - ASSERT (MAX_PARMS >= 5); + ASSERT (MAX_PARMS >= 7); + + /* + * If directive begins with "setenv opt" prefix, don't raise an error if + * directive is unrecognized. + */ + if (streq (p[0], "setenv") && p[1] && streq (p[1], "opt") && !(permission_mask & OPT_P_PULL_MODE)) + { + p += 2; + msglevel_fc = M_WARN; + } + if (!file) { file = "[CMD-LINE]"; @@ -6408,6 +6417,19 @@ add_option (struct options *options, options->priv_key_file_inline = p[2]; } } + else if (streq (p[0], "tls-version-min") && p[1]) + { + int ver; + VERIFY_PERMISSION (OPT_P_GENERAL); + ver = tls_version_min_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); + } #ifndef ENABLE_CRYPTO_POLARSSL else if (streq (p[0], "pkcs12") && p[1]) { diff --git a/openvpn/src/openvpn/plugin.c b/openvpn/src/openvpn/plugin.c index c96c121f..0948f238 100644 --- a/openvpn/src/openvpn/plugin.c +++ b/openvpn/src/openvpn/plugin.c @@ -40,8 +40,8 @@ #include "error.h" #include "misc.h" #include "plugin.h" +#include "ssl_backend.h" #include "win32.h" - #include "memdbg.h" #define PLUGIN_SYMBOL_REQUIRED (1<<0) @@ -374,7 +374,8 @@ plugin_open_item (struct plugin *p, struct openvpn_plugin_args_open_in args = { p->plugin_type_mask, (const char ** const) o->argv, (const char ** const) envp, - &callbacks }; + &callbacks, + SSLAPI }; struct openvpn_plugin_args_open_return retargs; CLEAR(retargs); diff --git a/openvpn/src/openvpn/ssl.c b/openvpn/src/openvpn/ssl.c index 7cf3b2e4..f78cca88 100644 --- a/openvpn/src/openvpn/ssl.c +++ b/openvpn/src/openvpn/ssl.c @@ -7,10 +7,7 @@ * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. * Copyright (C) 2010 Fox Crypto B.V. - * - * Additions for eurephia plugin done by: - * David Sommerseth Copyright (C) 2008-2009 - * + * Copyright (C) 2008-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 @@ -447,6 +444,27 @@ ssl_put_auth_challenge (const char *cr_str) #endif +/* + * Parse a TLS version string, returning a TLS_VER_x constant. + * If version string is not recognized and extra == "or-highest", + * return tls_version_max(). + */ +int +tls_version_min_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) + return TLS_VER_1_0; + else if (!strcmp(vstr, "1.1") && TLS_VER_1_1 <= max_version) + return TLS_VER_1_1; + else if (!strcmp(vstr, "1.2") && TLS_VER_1_2 <= max_version) + return TLS_VER_1_2; + else if (extra && !strcmp(extra, "or-highest")) + return max_version; + else + return TLS_VER_BAD; +} + /* * Initialize SSL context. * All files are in PEM format. diff --git a/openvpn/src/openvpn/ssl_backend.h b/openvpn/src/openvpn/ssl_backend.h index 72235ae5..4d2958c7 100644 --- a/openvpn/src/openvpn/ssl_backend.h +++ b/openvpn/src/openvpn/ssl_backend.h @@ -36,10 +36,17 @@ #ifdef ENABLE_CRYPTO_OPENSSL #include "ssl_openssl.h" #include "ssl_verify_openssl.h" +#define SSLAPI SSLAPI_OPENSSL #endif #ifdef ENABLE_CRYPTO_POLARSSL #include "ssl_polarssl.h" #include "ssl_verify_polarssl.h" +#define SSLAPI SSLAPI_POLARSSL +#endif + +/* Ensure that SSLAPI got a sane value if SSL is disabled or unknown */ +#ifndef SSLAPI +#define SSLAPI SSLAPI_NONE #endif /** @@ -93,6 +100,29 @@ void tls_free_lib(); */ void tls_clear_error(); +/** + * Parse a TLS version specifier + * + * @param vstr The TLS version string + * @param extra An optional extra parameter, may be NULL + * + * @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); + +/** + * Return the maximum TLS version (as a TLS_VER_x constant) + * supported by current SSL implementation + * + * @return One of the TLS_VER_x constants (but not TLS_VER_BAD). + */ +int tls_version_max(void); + /** * Initialise a library-specific TLS context for a server. * diff --git a/openvpn/src/openvpn/ssl_common.h b/openvpn/src/openvpn/ssl_common.h index 7e52f9a2..04ba7892 100644 --- a/openvpn/src/openvpn/ssl_common.h +++ b/openvpn/src/openvpn/ssl_common.h @@ -290,12 +290,14 @@ struct tls_options struct compress_options comp_options; #endif - /* configuration file boolean options */ + /* configuration file SSL-related boolean and low-permutation options */ # define SSLF_CLIENT_CERT_NOT_REQUIRED (1<<0) # define SSLF_USERNAME_AS_COMMON_NAME (1<<1) # 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) */ unsigned int ssl_flags; #ifdef MANAGEMENT_DEF_AUTH diff --git a/openvpn/src/openvpn/ssl_openssl.c b/openvpn/src/openvpn/ssl_openssl.c index 5db717df..12c725d9 100644 --- a/openvpn/src/openvpn/ssl_openssl.c +++ b/openvpn/src/openvpn/ssl_openssl.c @@ -114,7 +114,7 @@ tls_ctx_server_new(struct tls_root_ctx *ctx) { ASSERT(NULL != ctx); - ctx->ctx = SSL_CTX_new (TLSv1_server_method ()); + ctx->ctx = SSL_CTX_new (SSLv23_server_method ()); if (ctx->ctx == NULL) msg (M_SSLERR, "SSL_CTX_new TLSv1_server_method"); @@ -127,7 +127,7 @@ tls_ctx_client_new(struct tls_root_ctx *ctx) { ASSERT(NULL != ctx); - ctx->ctx = SSL_CTX_new (TLSv1_client_method ()); + ctx->ctx = SSL_CTX_new (SSLv23_client_method ()); if (ctx->ctx == NULL) msg (M_SSLERR, "SSL_CTX_new TLSv1_client_method"); @@ -174,13 +174,46 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret) } } +/* + * Return maximum TLS version supported by local OpenSSL library. + * Assume that presence of SSL_OP_NO_TLSvX macro indicates that + * TLSvX is supported. + */ +int +tls_version_max(void) +{ +#if defined(SSL_OP_NO_TLSv1_2) + return TLS_VER_1_2; +#elif defined(SSL_OP_NO_TLSv1_1) + return TLS_VER_1_1; +#else + return TLS_VER_1_0; +#endif +} + void tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) { ASSERT(NULL != ctx); + /* process SSL options including minimum TLS version we will accept from peer */ + { + long sslopt = SSL_OP_SINGLE_DH_USE | 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) + sslopt |= SSL_OP_NO_TLSv1; +#ifdef SSL_OP_NO_TLSv1_1 + if (tls_version_min > 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) + sslopt |= SSL_OP_NO_TLSv1_2; +#endif + SSL_CTX_set_options (ctx->ctx, sslopt); + } + SSL_CTX_set_session_cache_mode (ctx->ctx, SSL_SESS_CACHE_OFF); - SSL_CTX_set_options (ctx->ctx, SSL_OP_SINGLE_DH_USE); SSL_CTX_set_default_passwd_cb (ctx->ctx, pem_password_callback); /* Require peer certificate verification */ diff --git a/openvpn/src/openvpn/ssl_polarssl.c b/openvpn/src/openvpn/ssl_polarssl.c index 8a917b34..fb732254 100644 --- a/openvpn/src/openvpn/ssl_polarssl.c +++ b/openvpn/src/openvpn/ssl_polarssl.c @@ -501,6 +501,18 @@ void tls_ctx_personalise_random(struct tls_root_ctx *ctx) } } +int +tls_version_max(void) +{ +#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_3) + return TLS_VER_1_2; +#elif defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_2) + return TLS_VER_1_1; +#else + return TLS_VER_1_0; +#endif +} + 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) { @@ -550,6 +562,34 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl, /* TODO: PolarSSL does not currently support sending the CA chain to the client */ ssl_set_ca_chain (ks_ssl->ctx, ssl_ctx->ca_chain, NULL, NULL ); + /* 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) + { + 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 + } + ssl_set_min_version(ks_ssl->ctx, polar_major, polar_minor); + } + /* Initialise BIOs */ ALLOC_OBJ_CLEAR (ks_ssl->ct_in, endless_buffer); ALLOC_OBJ_CLEAR (ks_ssl->ct_out, endless_buffer); diff --git a/openvpn/src/openvpn/ssl_verify.c b/openvpn/src/openvpn/ssl_verify.c index b1bbc96f..4dd3aa2d 100644 --- a/openvpn/src/openvpn/ssl_verify.c +++ b/openvpn/src/openvpn/ssl_verify.c @@ -425,7 +425,6 @@ verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert setenv_str (es, envname, common_name); #endif -#ifdef ENABLE_EUREPHIA /* export X509 cert SHA1 fingerprint */ { unsigned char *sha1_hash = x509_get_sha1_hash(peer_cert, &gc); @@ -434,7 +433,6 @@ verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert setenv_str (es, envname, format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); } -#endif /* export serial number as environmental variable */ serial = x509_get_serial(peer_cert, &gc); diff --git a/openvpn/src/openvpn/tun.c b/openvpn/src/openvpn/tun.c index 98cb16f3..bd62b392 100644 --- a/openvpn/src/openvpn/tun.c +++ b/openvpn/src/openvpn/tun.c @@ -74,6 +74,12 @@ static void solaris_error_close (struct tuntap *tt, const struct env_set *es, co #include #endif +#if defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H +#include +#include +#include +#endif + static void clear_tuntap (struct tuntap *tuntap); bool @@ -1284,6 +1290,87 @@ open_null (struct tuntap *tt) tt->actual_name = string_alloc ("null", NULL); } + +#if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) + +/* + * OpenBSD and Mac OS X when using utun + * have a slightly incompatible TUN device from + * the rest of the world, in that it prepends a + * uint32 to the beginning of the IP header + * to designate the protocol (why not just + * look at the version field in the IP header to + * determine v4 or v6?). + * + * We strip off this field on reads and + * put it back on writes. + * + * I have not tested TAP devices on OpenBSD, + * but I have conditionalized the special + * TUN handling code described above to + * go away for TAP devices. + */ + +#include +#include + +static inline int +header_modify_read_write_return (int len) +{ + if (len > 0) + return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0; + else + return len; +} + +int +write_tun_header (struct tuntap* tt, uint8_t *buf, int len) +{ + if (tt->type == DEV_TYPE_TUN) + { + u_int32_t type; + struct iovec iv[2]; + struct ip *iph; + + iph = (struct ip *) buf; + + if (tt->ipv6 && iph->ip_v == 6) + type = htonl (AF_INET6); + else + type = htonl (AF_INET); + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof (type); + iv[1].iov_base = buf; + iv[1].iov_len = len; + + return header_modify_read_write_return (writev (tt->fd, iv, 2)); + } + else + return write (tt->fd, buf, len); +} + +int +read_tun_header (struct tuntap* tt, uint8_t *buf, int len) +{ + if (tt->type == DEV_TYPE_TUN) + { + u_int32_t type; + struct iovec iv[2]; + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof (type); + iv[1].iov_base = buf; + iv[1].iov_len = len; + + return header_modify_read_write_return (readv (tt->fd, iv, 2)); + } + else + return read (tt->fd, buf, len); +} +#endif + + #ifndef WIN32 static void open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, @@ -2062,23 +2149,6 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) #elif defined(TARGET_OPENBSD) -/* - * OpenBSD has a slightly incompatible TUN device from - * the rest of the world, in that it prepends a - * uint32 to the beginning of the IP header - * to designate the protocol (why not just - * look at the version field in the IP header to - * determine v4 or v6?). - * - * We strip off this field on reads and - * put it back on writes. - * - * I have not tested TAP devices on OpenBSD, - * but I have conditionalized the special - * TUN handling code described above to - * go away for TAP devices. - */ - void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { @@ -2145,59 +2215,16 @@ close_tun (struct tuntap* tt) } } -static inline int -openbsd_modify_read_write_return (int len) -{ - if (len > 0) - return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0; - else - return len; -} - int -write_tun (struct tuntap* tt, uint8_t *buf, int len) +write_tun(struct tuntap *tt, uint8_t *buf, int len) { - if (tt->type == DEV_TYPE_TUN) - { - u_int32_t type; - struct iovec iv[2]; - struct ip *iph; - - iph = (struct ip *) buf; - - if (tt->ipv6 && iph->ip_v == 6) - type = htonl (AF_INET6); - else - type = htonl (AF_INET); - - iv[0].iov_base = &type; - iv[0].iov_len = sizeof (type); - iv[1].iov_base = buf; - iv[1].iov_len = len; - - return openbsd_modify_read_write_return (writev (tt->fd, iv, 2)); - } - else - return write (tt->fd, buf, len); + return write_tun_header (tt, buf, len); } int -read_tun (struct tuntap* tt, uint8_t *buf, int len) +read_tun (struct tuntap *tt, uint8_t *buf, int len) { - if (tt->type == DEV_TYPE_TUN) - { - u_int32_t type; - struct iovec iv[2]; - - iv[0].iov_base = &type; - iv[0].iov_len = sizeof (type); - iv[1].iov_base = buf; - iv[1].iov_len = len; - - return openbsd_modify_read_write_return (readv (tt->fd, iv, 2)); - } - else - return read (tt->fd, buf, len); + return read_tun_header (tt, buf, len); } #elif defined(TARGET_NETBSD) @@ -2557,10 +2584,177 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) * pointing to lo0. Need to unconfigure... (observed on 10.5) */ +/* + * utun is the native Darwin tun driver present since at least 10.7 + * Thanks goes to Jonathan Levin for providing an example how to utun + * (http://newosxbook.com/src.jl?tree=listings&file=17-15-utun.c) + */ + +#ifdef HAVE_NET_IF_UTUN_H + +/* Helper functions that tries to open utun device + return -2 on early initialization failures (utun not supported + at all (old OS X) and -1 on initlization failure of utun + device (utun works but utunX is already used */ +static +int utun_open_helper (struct ctl_info ctlInfo, int utunnum) +{ + struct sockaddr_ctl sc; + int fd; + + fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + + if (fd < 0) + { + msg (M_INFO, "Opening utun (%s): %s", "socket(SYSPROTO_CONTROL)", + strerror (errno)); + return -2; + } + + if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) + { + close (fd); + msg (M_INFO, "Opening utun (%s): %s", "ioctl(CTLIOCGINFO)", + strerror (errno)); + return -2; + } + + + sc.sc_id = ctlInfo.ctl_id; + sc.sc_len = sizeof(sc); + sc.sc_family = AF_SYSTEM; + sc.ss_sysaddr = AF_SYS_CONTROL; + + sc.sc_unit = utunnum+1; + + + /* If the connect is successful, a utun%d device will be created, where "%d" + * is (sc.sc_unit - 1) */ + + if (connect (fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) + { + msg (M_INFO, "Opening utun (%s): %s", "connect(AF_SYS_CONTROL)", + strerror (errno)); + close(fd); + return -1; + } + + set_nonblock (fd); + set_cloexec (fd); /* don't pass fd to scripts */ + + return fd; +} + +void +open_darwin_utun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + struct ctl_info ctlInfo; + int fd; + char utunname[20]; + int utunnum =-1; + socklen_t utunname_len = sizeof(utunname); + + /* dev_node is simply utun, do the normal dynamic utun + * otherwise try to parse the utun number */ + if (dev_node && !strcmp ("utun", dev_node)==0) + { + if (!sscanf (dev_node, "utun%d", &utunnum)==1) + msg (M_FATAL, "Cannot parse 'dev-node %s' please use 'dev-node utunX'" + "to use a utun device number X", dev_node); + } + + + + CLEAR (ctlInfo); + if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >= + sizeof(ctlInfo.ctl_name)) + { + msg (M_ERR, "Opening utun: UTUN_CONTROL_NAME too long"); + } + + /* try to open first available utun device if no specific utun is requested */ + if (utunnum == -1) + { + for (utunnum=0; utunnum<255; utunnum++) + { + fd = utun_open_helper (ctlInfo, utunnum); + /* Break if the fd is valid, + * or if early initalization failed (-2) */ + if (fd !=-1) + break; + } + } + else + { + fd = utun_open_helper (ctlInfo, utunnum); + } + + /* opening an utun device failed */ + tt->fd = fd; + + if (fd < 0) + return; + + /* Retrieve the assigned interface name. */ + if (getsockopt (fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len)) + msg (M_ERR | M_ERRNO, "Error retrieving utun interface name"); + + tt->actual_name = string_alloc (utunname, NULL); + + msg (M_INFO, "Opened utun device %s", utunname); + tt->is_utun = true; +} + +#endif + void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); +#ifdef HAVE_NET_IF_UTUN_H + /* If dev_node does not start start with utun assume regular tun/tap */ + if ((!dev_node && tt->type==DEV_TYPE_TUN) || + (dev_node && !strncmp (dev_node, "utun", 4))) + { + + /* Check if user has specific dev_type tap and forced utun with + dev-node utun */ + if (tt->type!=DEV_TYPE_TUN) + msg (M_FATAL, "Cannot use utun devices with --dev-type %s", + dev_type_string (dev, dev_type)); + + /* Try utun first and fall back to normal tun if utun fails + and dev_node is not specified */ + open_darwin_utun(dev, dev_type, dev_node, tt); + + if (!tt->is_utun) + { + if (!dev_node) + { + /* No explicit utun and utun failed, try the generic way) */ + msg (M_INFO, "Failed to open utun device. Falling back to /dev/tun device"); + open_tun_generic (dev, dev_type, NULL, true, true, tt); + } + else + { + /* Specific utun device or generic utun request with no tun + fall back failed, consider this a fatal failure */ + msg (M_FATAL, "Cannot open utun device"); + } + } + } + else +#endif + { + + /* Use plain dev-node tun to select /dev/tun style + * Unset dev_node variable prior to passing to open_tun_generic to + * let open_tun_generic pick the first available tun device */ + + if (dev_node && strcmp (dev_node, "tun")==0) + dev_node=NULL; + + open_tun_generic (dev, dev_type, dev_node, true, true, tt); + } } void @@ -2593,13 +2787,23 @@ close_tun (struct tuntap* tt) int write_tun (struct tuntap* tt, uint8_t *buf, int len) { - return write (tt->fd, buf, len); +#ifdef HAVE_NET_IF_UTUN_H + if (tt->is_utun) + return write_tun_header (tt, buf, len); + else +#endif + return write (tt->fd, buf, len); } int read_tun (struct tuntap* tt, uint8_t *buf, int len) { - return read (tt->fd, buf, len); +#ifdef HAVE_NET_IF_UTUN_H + if (tt->is_utun) + return read_tun_header (tt, buf, len); + else +#endif + return read (tt->fd, buf, len); } #elif defined(WIN32) diff --git a/openvpn/src/openvpn/tun.h b/openvpn/src/openvpn/tun.h index 2bbb8133..631b53c6 100644 --- a/openvpn/src/openvpn/tun.h +++ b/openvpn/src/openvpn/tun.h @@ -181,6 +181,9 @@ struct tuntap int ip_fd; #endif +#ifdef HAVE_NET_IF_UTUN_H + bool is_utun; +#endif /* used for printing status info only */ unsigned int rwflags_debug; -- cgit v1.2.3