summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in2
-rw-r--r--src/compat/Makefile.am1
-rw-r--r--src/compat/Makefile.in7
-rw-r--r--src/compat/compat-stdbool.h12
-rw-r--r--src/openvpn/Makefile.in6
-rw-r--r--src/openvpn/basic.h2
-rw-r--r--src/openvpn/buffer.c23
-rw-r--r--src/openvpn/buffer.h43
-rw-r--r--src/openvpn/console_builtin.c2
-rw-r--r--src/openvpn/crypto.c17
-rw-r--r--src/openvpn/crypto_openssl.c1
-rw-r--r--src/openvpn/init.c31
-rw-r--r--src/openvpn/manage.c2
-rw-r--r--src/openvpn/misc.c4
-rw-r--r--src/openvpn/multi.c8
-rw-r--r--src/openvpn/options.c94
-rw-r--r--src/openvpn/options.h14
-rw-r--r--src/openvpn/push.c55
-rw-r--r--src/openvpn/push.h3
-rw-r--r--src/openvpn/sig.c21
-rw-r--r--src/openvpn/ssl.c246
-rw-r--r--src/openvpn/ssl.h25
-rw-r--r--src/openvpn/ssl_backend.h2
-rw-r--r--src/openvpn/ssl_common.h2
-rw-r--r--src/openvpn/ssl_mbedtls.c2
-rw-r--r--src/openvpn/ssl_mbedtls.h2
-rw-r--r--src/openvpn/ssl_openssl.c2
-rw-r--r--src/openvpn/ssl_openssl.h2
-rw-r--r--src/openvpn/ssl_verify.c4
-rw-r--r--src/openvpn/ssl_verify_mbedtls.c4
-rw-r--r--src/openvpn/syshead.h2
-rw-r--r--src/openvpn/tun.c163
-rw-r--r--src/openvpn/tun.h3
-rw-r--r--src/openvpnserv/Makefile.am2
-rw-r--r--src/openvpnserv/Makefile.in8
-rw-r--r--src/openvpnserv/common.c12
-rw-r--r--src/openvpnserv/interactive.c180
-rw-r--r--src/openvpnserv/service.h3
-rw-r--r--src/plugins/Makefile.in2
-rw-r--r--src/plugins/auth-pam/Makefile.in6
-rw-r--r--src/plugins/down-root/Makefile.in6
41 files changed, 785 insertions, 241 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 8aeb7f5..c9d3d06 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
index 06bab5c..0a9d1c2 100644
--- a/src/compat/Makefile.am
+++ b/src/compat/Makefile.am
@@ -20,7 +20,6 @@ noinst_LTLIBRARIES = libcompat.la
libcompat_la_SOURCES = \
compat.h \
- compat-stdbool.h \
compat-dirname.c \
compat-basename.c \
compat-gettimeofday.c \
diff --git a/src/compat/Makefile.in b/src/compat/Makefile.in
index c7236ed..9232d31 100644
--- a/src/compat/Makefile.in
+++ b/src/compat/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -361,7 +361,6 @@ EXTRA_DIST = \
noinst_LTLIBRARIES = libcompat.la
libcompat_la_SOURCES = \
compat.h \
- compat-stdbool.h \
compat-dirname.c \
compat-basename.c \
compat-gettimeofday.c \
@@ -439,14 +438,14 @@ distclean-compile:
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/src/compat/compat-stdbool.h b/src/compat/compat-stdbool.h
deleted file mode 100644
index 9941218..0000000
--- a/src/compat/compat-stdbool.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __COMPAT_STDBOOL_H
-#define __COMPAT_STDBOOL_H
-
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#else
-typedef int bool;
-#define false 0
-#define true 1
-#endif
-
-#endif
diff --git a/src/openvpn/Makefile.in b/src/openvpn/Makefile.in
index ff15d6d..9ef9b28 100644
--- a/src/openvpn/Makefile.in
+++ b/src/openvpn/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -655,14 +655,14 @@ distclean-compile:
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/src/openvpn/basic.h b/src/openvpn/basic.h
index 298cf10..48d4d9b 100644
--- a/src/openvpn/basic.h
+++ b/src/openvpn/basic.h
@@ -30,7 +30,7 @@
/* size of an array */
#define SIZE(x) (sizeof(x)/sizeof(x[0]))
-/* clear an object */
+/* clear an object (may be optimized away, use secure_memzero() to erase secrets) */
#define CLEAR(x) memset(&(x), 0, sizeof(x))
#define IPV4_NETMASK_HOST 0xffffffffU
diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
index 52c6ab9..6af8dbb 100644
--- a/src/openvpn/buffer.c
+++ b/src/openvpn/buffer.c
@@ -155,7 +155,9 @@ void
buf_clear (struct buffer *buf)
{
if (buf->capacity > 0)
- memset (buf->data, 0, buf->capacity);
+ {
+ secure_memzero (buf->data, buf->capacity);
+ }
buf->len = 0;
buf->offset = 0;
}
@@ -438,13 +440,16 @@ format_hex_ex (const uint8_t *data, int size, int maxoutput,
unsigned int space_break_flags, const char* separator,
struct gc_arena *gc)
{
- struct buffer out = alloc_buf_gc (maxoutput ? maxoutput :
- ((size * 2) + (size / (space_break_flags & FHE_SPACE_BREAK_MASK)) * (int) strlen (separator) + 2),
- gc);
- int i;
- for (i = 0; i < size; ++i)
+ const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
+ const size_t separator_len = separator ? strlen (separator) : 0;
+ static_assert (INT_MAX <= SIZE_MAX, "Code assumes INT_MAX <= SIZE_MAX");
+ const size_t out_len = maxoutput > 0 ? maxoutput :
+ ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
+
+ struct buffer out = alloc_buf_gc (out_len, gc);
+ for (int i = 0; i < size; ++i)
{
- if (separator && i && !(i % (space_break_flags & FHE_SPACE_BREAK_MASK)))
+ if (separator && i && !(i % bytes_per_hexblock))
buf_printf (&out, "%s", separator);
if (space_break_flags & FHE_CAPS)
buf_printf (&out, "%02X", data[i]);
@@ -616,9 +621,7 @@ string_clear (char *str)
{
if (str)
{
- const int len = strlen (str);
- if (len > 0)
- memset (str, 0, len);
+ secure_memzero (str, strlen (str));
}
}
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
index 8070439..7747003 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -328,6 +328,49 @@ has_digit (const unsigned char* src)
return false;
}
+/**
+ * Securely zeroise memory.
+ *
+ * This code and description are based on code supplied by Zhaomo Yang, of the
+ * University of California, San Diego (which was released into the public
+ * domain).
+ *
+ * The secure_memzero function attempts to ensure that an optimizing compiler
+ * does not remove the intended operation if cleared memory is not accessed
+ * again by the program. This code has been tested under Clang 3.9.0 and GCC
+ * 6.2 with optimization flags -O, -Os, -O0, -O1, -O2, and -O3 on
+ * Ubuntu 16.04.1 LTS; under Clang 3.9.0 with optimization flags -O, -Os,
+ * -O0, -O1, -O2, and -O3 on FreeBSD 10.2-RELEASE; under Microsoft Visual Studio
+ * 2015 with optimization flags /O1, /O2 and /Ox on Windows 10.
+ *
+ * Theory of operation:
+ *
+ * 1. On Windows, use the SecureZeroMemory which ensures that data is
+ * overwritten.
+ * 2. Under GCC or Clang, use a memory barrier, which forces the preceding
+ * memset to be carried out. The overhead of a memory barrier is usually
+ * negligible.
+ * 3. If none of the above are available, use the volatile pointer
+ * technique to zero memory one byte at a time.
+ *
+ * @param data Pointer to data to zeroise.
+ * @param len Length of data, in bytes.
+ */
+static inline void
+secure_memzero (void *data, size_t len)
+{
+#if defined(_WIN32)
+ SecureZeroMemory (data, len);
+#elif defined(__GNUC__) || defined(__clang__)
+ memset(data, 0, len);
+ __asm__ __volatile__("" : : "r"(data) : "memory");
+#else
+ volatile char *p = (volatile char *) data;
+ while (len--)
+ *p++ = 0;
+#endif
+}
+
/*
* printf append to a buffer with overflow check,
* due to usage of vsnprintf, it will leave space for
diff --git a/src/openvpn/console_builtin.c b/src/openvpn/console_builtin.c
index 6b0211d..06994fd 100644
--- a/src/openvpn/console_builtin.c
+++ b/src/openvpn/console_builtin.c
@@ -218,7 +218,7 @@ static bool get_console_input (const char *prompt, const bool echo, char *input,
if (gp)
{
strncpynt (input, gp, capacity);
- memset (gp, 0, strlen (gp));
+ secure_memzero (gp, strlen (gp));
ret = true;
}
}
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 05622ce..708cc92 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -86,12 +86,11 @@ openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
{
struct buffer iv_buffer;
struct packet_id_net pin;
- uint8_t iv[OPENVPN_MAX_IV_LENGTH];
+ uint8_t iv[OPENVPN_MAX_IV_LENGTH] = {0};
const int iv_len = cipher_ctx_iv_length (ctx->cipher);
ASSERT (iv_len >= OPENVPN_AEAD_MIN_IV_LEN && iv_len <= OPENVPN_MAX_IV_LENGTH);
- memset(iv, 0, sizeof(iv));
buf_set_write (&iv_buffer, iv, iv_len);
/* IV starts with packet id to make the IV unique for packet */
@@ -175,7 +174,7 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,
/* Do Encrypt from buf -> work */
if (ctx->cipher)
{
- uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH];
+ uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH] = {0};
const int iv_size = cipher_ctx_iv_length (ctx->cipher);
const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
int outlen;
@@ -190,8 +189,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,
if (cipher_kt_mode_cbc(cipher_kt))
{
- CLEAR (iv_buf);
-
/* generate pseudo-random IV */
if (opt->flags & CO_USE_IV)
prng_bytes (iv_buf, iv_size);
@@ -214,7 +211,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,
ASSERT (packet_id_initialized(&opt->packet_id));
packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true);
- memset (iv_buf, 0, iv_size);
buf_set_write (&b, iv_buf, iv_size);
ASSERT (packet_id_write (&pin, &b, true, false));
}
@@ -550,14 +546,13 @@ openvpn_decrypt_v1 (struct buffer *buf, struct buffer work,
{
const int iv_size = cipher_ctx_iv_length (ctx->cipher);
const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
- uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH];
+ uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH] = { 0 };
int outlen;
/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT)));
/* use IV if user requested it */
- CLEAR (iv_buf);
if (opt->flags & CO_USE_IV)
{
if (buf->len < iv_size)
@@ -1128,7 +1123,7 @@ crypto_read_openvpn_key (const struct key_type *key_type,
init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type,
OPENVPN_OP_DECRYPT, log_prefix);
- CLEAR (key2);
+ secure_memzero (&key2, sizeof (key2));
}
/* header and footer for static key file */
@@ -1380,8 +1375,8 @@ write_key_file (const int nkeys, const char *filename)
buf_printf (&out, "%s\n", fmt);
/* zero memory which held key component (will be freed by GC) */
- memset (fmt, 0, strlen(fmt));
- CLEAR (key);
+ secure_memzero (fmt, strlen (fmt));
+ secure_memzero (&key, sizeof (key));
}
buf_printf (&out, "%s\n", static_key_foot);
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c
index 1ea06bb..306b6c6 100644
--- a/src/openvpn/crypto_openssl.c
+++ b/src/openvpn/crypto_openssl.c
@@ -47,6 +47,7 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
+#include <openssl/rand.h>
#include <openssl/ssl.h>
/*
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 470dc89..74f1139 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -30,6 +30,10 @@
#include "syshead.h"
+#ifdef ENABLE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
#include "win32.h"
#include "init.h"
#include "sig.h"
@@ -926,6 +930,13 @@ bool
possibly_become_daemon (const struct options *options)
{
bool ret = false;
+
+#ifdef ENABLE_SYSTEMD
+ /* return without forking if we are running from systemd */
+ if (sd_notify(0, "READY=0") > 0)
+ return ret;
+#endif
+
if (options->daemon)
{
ASSERT (!options->inetd);
@@ -1251,11 +1262,19 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
show_adapters (M_INFO|M_NOPREFIX);
msg (M_INFO, "%s With Errors ( see http://openvpn.net/faq.html#dhcpclientserv )", message);
#else
+#ifdef ENABLE_SYSTEMD
+ sd_notifyf(0, "STATUS=Failed to start up: %s With Errors\nERRNO=1", message);
+#endif /* HAVE_SYSTEMD_SD_DAEMON_H */
msg (M_INFO, "%s With Errors", message);
#endif
}
else
- msg (M_INFO, "%s", message);
+ {
+#ifdef ENABLE_SYSTEMD
+ sd_notifyf(0, "READY=1\nSTATUS=%s\nMAINPID=%lu", message, (unsigned long) getpid());
+#endif
+ msg (M_INFO, "%s", message);
+ }
/* Flag that we initialized */
if ((flags & (ISC_ERRORS|ISC_SERVER)) == 0)
@@ -1932,8 +1951,14 @@ do_deferred_options (struct context *c, const unsigned int found)
{
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
if (found & OPT_P_NCP)
- msg (D_PUSH, "OPTIONS IMPORT: data channel crypto options modified");
- /* Do not regenerate keys if server sends an extra push request */
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: data channel crypto options modified");
+ }
+ else if (c->options.ncp_enabled)
+ {
+ tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername);
+ }
+ /* Do not regenerate keys if server sends an extra push reply */
if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized &&
!tls_session_update_crypto_params(session, &c->options, &c->c2.frame))
{
diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c
index 77a8006..4918ed2 100644
--- a/src/openvpn/manage.c
+++ b/src/openvpn/manage.c
@@ -3154,7 +3154,7 @@ management_query_user_pass (struct management *man,
man->connection.up_query.nocache = up->nocache; /* preserve caller's nocache setting */
*up = man->connection.up_query;
}
- CLEAR (man->connection.up_query);
+ secure_memzero (&man->connection.up_query, sizeof (man->connection.up_query));
}
gc_free (&gc);
diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c
index 56d43e0..4e06c91 100644
--- a/src/openvpn/misc.c
+++ b/src/openvpn/misc.c
@@ -501,7 +501,7 @@ remove_env_item (const char *str, const bool do_free, struct env_item **list)
*list = current->next;
if (do_free)
{
- memset (current->string, 0, strlen (current->string));
+ secure_memzero (current->string, strlen (current->string));
free (current->string);
free (current);
}
@@ -1342,7 +1342,7 @@ purge_user_pass (struct user_pass *up, const bool force)
static bool warn_shown = false;
if (nocache || force)
{
- CLEAR (*up);
+ secure_memzero (up, sizeof(*up));
up->nocache = nocache;
}
else if (!warn_shown)
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 8f3d34e..4fc8b02 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2317,6 +2317,10 @@ void multi_process_float (struct multi_context* m, struct multi_instance* mi)
mroute_addr_print (&mi->real, &gc),
print_link_socket_actual (&m->top.c2.from, &gc));
+ /* remove old address from hash table before changing address */
+ 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);
@@ -2330,8 +2334,8 @@ void multi_process_float (struct multi_context* m, struct multi_instance* mi)
tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from);
- ASSERT (hash_add (m->hash, &mi->real, mi, true));
- ASSERT (hash_add (m->iter, &mi->real, mi, true));
+ ASSERT (hash_add (m->hash, &mi->real, mi, false));
+ ASSERT (hash_add (m->iter, &mi->real, mi, false));
#ifdef MANAGEMENT_DEF_AUTH
ASSERT (hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, true));
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 7f128c3..47acd97 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -704,7 +704,8 @@ static const char usage_message[] =
" which allow multiple addresses,\n"
" --dhcp-option must be repeated.\n"
" DOMAIN name : Set DNS suffix\n"
- " DNS addr : Set domain name server address(es)\n"
+ " DNS addr : Set domain name server address(es) (IPv4)\n"
+ " DNS6 addr : Set domain name server address(es) (IPv6)\n"
" NTP : Set NTP server address(es)\n"
" NBDD : Set NBDD server address(es)\n"
" WINS addr : Set WINS server address(es)\n"
@@ -716,8 +717,8 @@ static const char usage_message[] =
"--dhcp-pre-release : Ask Windows to release the previous TAP adapter lease on\n"
" startup.\n"
"--dhcp-release : Ask Windows to release the TAP adapter lease on shutdown.\n"
- "--register-dns : Run net stop dnscache, net start dnscache, ipconfig /flushdns\n"
- " and ipconfig /registerdns on connection initiation.\n"
+ "--register-dns : Run ipconfig /flushdns and ipconfig /registerdns\n"
+ " on connection initiation.\n"
"--tap-sleep n : Sleep for n seconds after TAP adapter open before\n"
" attempting to set adapter properties.\n"
"--pause-exit : When run from a console window, pause before exiting.\n"
@@ -1642,6 +1643,8 @@ show_settings (const struct options *o)
SHOW_STR (shared_secret_file);
SHOW_INT (key_direction);
SHOW_STR (ciphername);
+ SHOW_BOOL (ncp_enabled);
+ SHOW_STR (ncp_ciphers);
SHOW_STR (authname);
SHOW_STR (prng_hash);
SHOW_INT (prng_nonce_secret_len);
@@ -2523,6 +2526,22 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce)
}
+#ifdef _WIN32
+/* If iservice is in use, we need def1 method for redirect-gateway */
+static void
+remap_redirect_gateway_flags (struct options *opt)
+{
+ if (opt->routes
+ && opt->route_method == ROUTE_METHOD_SERVICE
+ && opt->routes->flags & RG_REROUTE_GW
+ && !(opt->routes->flags & RG_DEF1))
+ {
+ msg (M_INFO, "Flag 'def1' added to --redirect-gateway (iservice is in use)");
+ opt->routes->flags |= RG_DEF1;
+ }
+}
+#endif
+
static void
options_postprocess_mutate_invariant (struct options *options)
{
@@ -2552,6 +2571,8 @@ options_postprocess_mutate_invariant (struct options *options)
options->tuntap_options.ip_win32_type = IPW32_SET_MANUAL;
options->ifconfig_noexec = false;
}
+
+ remap_redirect_gateway_flags (options);
#endif
#if P2MP_SERVER
@@ -3444,6 +3465,36 @@ options_string_version (const char* s, struct gc_arena *gc)
#endif /* ENABLE_OCC */
+char *
+options_string_extract_option (const char *options_string,const char *opt_name,
+ struct gc_arena *gc)
+{
+ char *ret = NULL;
+ const size_t opt_name_len = strlen(opt_name);
+
+ const char *p = options_string;
+ while (p)
+ {
+ if (0 == strncmp(p, opt_name, opt_name_len) &&
+ strlen(p) > (opt_name_len+1) && p[opt_name_len] == ' ')
+ {
+ /* option found, extract value */
+ const char *start = &p[opt_name_len+1];
+ const char *end = strchr (p, ',');
+ size_t val_len = end ? end - start : strlen (start);
+ ret = gc_malloc (val_len+1, true, gc);
+ memcpy (ret, start, val_len);
+ break;
+ }
+ p = strchr (p, ',');
+ if (p)
+ {
+ p++; /* skip delimiter */
+ }
+ }
+ return ret;
+}
+
static void
foreign_option (struct options *o, char *argv[], int len, struct env_set *es)
{
@@ -3652,7 +3703,7 @@ usage_version (void)
show_windows_version( M_INFO|M_NOPREFIX );
#endif
msg (M_INFO|M_NOPREFIX, "Originally developed by James Yonan");
- msg (M_INFO|M_NOPREFIX, "Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>");
+ msg (M_INFO|M_NOPREFIX, "Copyright (C) 2002-2016 OpenVPN Technologies, Inc. <sales@openvpn.net>");
#ifndef ENABLE_SMALL
#ifdef CONFIGURE_DEFINES
msg (M_INFO|M_NOPREFIX, "Compile time defines: %s", CONFIGURE_DEFINES);
@@ -3935,7 +3986,7 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc)
ret = string_alloc (BSTR (&buf), gc);
buf_clear (&buf);
free_buf (&buf);
- CLEAR (line);
+ secure_memzero (line, sizeof (line));
return ret;
}
@@ -4050,7 +4101,7 @@ read_config_file (struct options *options,
{
msg (msglevel, "In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself.", top_file, top_line, file);
}
- CLEAR (line);
+ secure_memzero (line, sizeof (line));
CLEAR (p);
}
@@ -4082,7 +4133,7 @@ read_config_string (const char *prefix,
}
CLEAR (p);
}
- CLEAR (line);
+ secure_memzero (line, sizeof (line));
}
void
@@ -4374,6 +4425,8 @@ add_option (struct options *options,
*/
if (streq (p[0], "setenv") && p[1] && streq (p[1], "opt") && !(permission_mask & OPT_P_PULL_MODE))
{
+ if (!p[2])
+ p[2] = "setenv opt"; /* will trigger an error that includes setenv opt */
p += 2;
msglevel_fc = M_WARN;
}
@@ -5672,6 +5725,10 @@ add_option (struct options *options,
goto err;
}
}
+#ifdef _WIN32
+ /* we need this here to handle pushed --redirect-gateway */
+ remap_redirect_gateway_flags (options);
+#endif
options->routes->flags |= RG_ENABLE;
}
else if (streq (p[0], "remote-random-hostname") && !p[1])
@@ -6406,6 +6463,20 @@ add_option (struct options *options,
{
dhcp_option_address_parse ("DNS", p[2], o->dns, &o->dns_len, msglevel);
}
+ else if (streq (p[1], "DNS6") && p[2] && ipv6_addr_safe(p[2]))
+ {
+ struct in6_addr addr;
+ foreign_option (options, p, 3, es);
+ if (o->dns6_len >= N_DHCP_ADDR)
+ {
+ msg (msglevel, "--dhcp-option DNS6: maximum of %d dns servers can be specified",
+ N_DHCP_ADDR);
+ }
+ else if (get_ipv6_addr (p[2], &addr, NULL, msglevel))
+ {
+ o->dns6[o->dns6_len++] = addr;
+ }
+ }
else if (streq (p[1], "WINS") && p[2])
{
dhcp_option_address_parse ("WINS", p[2], o->wins, &o->wins_len, msglevel);
@@ -6427,7 +6498,14 @@ add_option (struct options *options,
msg (msglevel, "--dhcp-option: unknown option type '%s' or missing or unknown parameter", p[1]);
goto err;
}
- o->dhcp_options = true;
+
+ /* flag that we have options to give to the TAP driver's DHCPv4 server
+ * - skipped for "DNS6", as that's not a DHCPv4 option
+ */
+ if (!streq (p[1], "DNS6"))
+ {
+ o->dhcp_options = true;
+ }
}
#endif
#ifdef _WIN32
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index a028556..067728a 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -726,6 +726,20 @@ void options_warning (char *actual, const char *expected);
#endif
+/**
+ * Given an OpenVPN options string, extract the value of an option.
+ *
+ * @param options_string Zero-terminated, comma-separated options string
+ * @param opt_name The name of the option to extract
+ * @param gc The gc to allocate the return value
+ *
+ * @return gc-allocated value of option with name opt_name if option was found,
+ * or NULL otherwise.
+ */
+char *options_string_extract_option (const char *options_string,
+ const char *opt_name, struct gc_arena *gc);
+
+
void options_postprocess (struct options *options);
void pre_pull_save (struct options *o);
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index f86bdd3..9953079 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -42,31 +42,6 @@
static char push_reply_cmd[] = "PUSH_REPLY";
-/**
- * Add an option to the given push list by providing a format string.
- *
- * The string added to the push options is allocated in o->gc, so the caller
- * does not have to preserve anything.
- *
- * @param gc GC arena where options are allocated
- * @param push_list Push list containing options
- * @param msglevel The message level to use when printing errors
- * @param fmt Format string for the option
- * @param ... Format string arguments
- *
- * @return true on success, false on failure.
- */
-static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
- int msglevel, const char *fmt, ...)
-#ifdef __GNUC__
-#if __USE_MINGW_ANSI_STDIO
- __attribute__ ((format (gnu_printf, 4, 5)))
-#else
- __attribute__ ((format (__printf__, 4, 5)))
-#endif
-#endif
- ;
-
/*
* Auth username/password
*
@@ -177,6 +152,30 @@ server_pushed_signal (struct context *c, const struct buffer *buffer, const bool
}
#if P2MP_SERVER
+/**
+ * Add an option to the given push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so the caller
+ * does not have to preserve anything.
+ *
+ * @param gc GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel The message level to use when printing errors
+ * @param fmt Format string for the option
+ * @param ... Format string arguments
+ *
+ * @return true on success, false on failure.
+ */
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
+ int msglevel, const char *fmt, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((format (gnu_printf, 4, 5)))
+#else
+ __attribute__ ((format (__printf__, 4, 5)))
+#endif
+#endif
+ ;
/*
* Send auth failed message from server to client.
@@ -263,7 +262,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
!tls_session_update_crypto_params (session, &c->options,
&c->c2.frame))
{
- msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
+ msg (D_TLS_ERRORS, "TLS Error: initializing data channel failed");
goto error;
}
}
@@ -371,6 +370,10 @@ prepare_push_reply (struct context *c, struct gc_arena *gc,
push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
}
}
+ else if (o->ncp_enabled)
+ {
+ tls_poor_mans_ncp (o, tls_multi->remote_ciphername);
+ }
/* If server uses --auth-gen-token and we have an auth token
* to send to the client
diff --git a/src/openvpn/push.h b/src/openvpn/push.h
index d6cb4b1..1dfd80e 100644
--- a/src/openvpn/push.h
+++ b/src/openvpn/push.h
@@ -51,10 +51,9 @@ void receive_auth_failed (struct context *c, const struct buffer *buffer);
void server_pushed_signal (struct context *c, const struct buffer *buffer, const bool restart, const int adv);
-#if P2MP_SERVER
-
void incoming_push_message (struct context *c, const struct buffer *buffer);
+#if P2MP_SERVER
void clone_push_list (struct options *o);
void push_option (struct options *o, const char *opt, int msglevel);
diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c
index 0ff1437..b3ae645 100644
--- a/src/openvpn/sig.c
+++ b/src/openvpn/sig.c
@@ -378,7 +378,8 @@ process_sigterm (struct context *c)
/**
* If a restart signal is received during exit-notification, reset the
- * signal and return true.
+ * signal and return true. If its a soft restart signal from the event loop
+ * which implies the loop cannot continue, remap to SIGTERM to exit promptly.
*/
static bool
ignore_restart_signals (struct context *c)
@@ -388,10 +389,20 @@ ignore_restart_signals (struct context *c)
if ( (c->sig->signal_received == SIGUSR1 || c->sig->signal_received == SIGHUP) &&
event_timeout_defined(&c->c2.explicit_exit_notification_interval) )
{
- msg (M_INFO, "Ignoring %s received during exit notification",
- signal_name(c->sig->signal_received, true));
- signal_reset (c->sig);
- ret = true;
+ if (c->sig->source == SIG_SOURCE_HARD)
+ {
+ msg (M_INFO, "Ignoring %s received during exit notification",
+ signal_name(c->sig->signal_received, true));
+ signal_reset (c->sig);
+ ret = true;
+ }
+ else
+ {
+ msg (M_INFO, "Converting soft %s received during exit notification to SIGTERM",
+ signal_name(c->sig->signal_received, true));
+ register_signal(c, SIGTERM, "exit-with-notification");
+ ret = false;
+ }
}
#endif
return ret;
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index dc06350..34d163f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -512,6 +512,54 @@ tls_version_parse(const char *vstr, const char *extra)
return TLS_VER_BAD;
}
+/**
+ * Load (or possibly reload) the CRL file into the SSL context.
+ * No reload is performed under the following conditions:
+ * - the CRL file was passed inline
+ * - the CRL file was not modified since the last (re)load
+ *
+ * @param ssl_ctx The TLS context to use when reloading the CRL
+ * @param crl_file The file name to load the CRL from, or
+ * "[[INLINE]]" in the case of inline files.
+ * @param crl_inline A string containing the CRL
+ */
+static void
+tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
+ const char *crl_file_inline)
+{
+ /* if something goes wrong with stat(), we'll store 0 as mtime */
+ platform_stat_t crl_stat = {0};
+
+ /*
+ * an inline CRL can't change at runtime, therefore there is no need to
+ * reload it. It will be reloaded upon config change + SIGHUP.
+ * Use always '1' as dummy timestamp in this case: it will trigger the
+ * first load, but will prevent any future reload.
+ */
+ if (crl_file_inline)
+ {
+ crl_stat.st_mtime = 1;
+ }
+ else if (platform_stat(crl_file, &crl_stat) < 0)
+ {
+ msg(M_WARN, "WARNING: Failed to stat CRL file, not (re)loading CRL.");
+ return;
+ }
+
+ /*
+ * Store the CRL if this is the first time or if the file was changed since
+ * the last load.
+ * Note: Windows does not support tv_nsec.
+ */
+ if ((ssl_ctx->crl_last_size == crl_stat.st_size) &&
+ (ssl_ctx->crl_last_mtime.tv_sec == crl_stat.st_mtime))
+ return;
+
+ ssl_ctx->crl_last_mtime.tv_sec = crl_stat.st_mtime;
+ ssl_ctx->crl_last_size = crl_stat.st_size;
+ backend_tls_ctx_reload_crl(ssl_ctx, crl_file, crl_file_inline);
+}
+
/*
* Initialize SSL context.
* All files are in PEM format.
@@ -894,7 +942,7 @@ key_state_free (struct key_state *ks, bool clear)
#endif
if (clear)
- CLEAR (*ks);
+ secure_memzero (ks, sizeof (*ks));
}
/** @} name Functions for initialization and cleanup of key_state structures */
@@ -973,7 +1021,7 @@ tls_session_init (struct tls_multi *multi, struct tls_session *session)
/* Initialize control channel authentication parameters */
session->tls_wrap = session->opt->tls_wrap;
- session->tls_wrap.work = alloc_buf (TLS_CHANNEL_BUF_SIZE);
+ session->tls_wrap.work = alloc_buf (BUF_SIZE (&session->opt->frame));
/* initialize packet ID replay window for --tls-auth */
packet_id_init (&session->tls_wrap.opt.packet_id,
@@ -1024,7 +1072,7 @@ tls_session_free (struct tls_session *session, bool clear)
cert_hash_free (session->cert_hash_set);
if (clear)
- CLEAR (*session);
+ secure_memzero (session, sizeof (*session));
}
/** @} name Functions for initialization and cleanup of tls_session structures */
@@ -1048,7 +1096,7 @@ move_session (struct tls_multi* multi, int dest, int src, bool reinit_src)
if (reinit_src)
tls_session_init (multi, &multi->session[src]);
else
- CLEAR (multi->session[src]);
+ secure_memzero (&multi->session[src], sizeof (multi->session[src]));
dmsg (D_TLS_DEBUG, "TLS: move_session: exit");
}
@@ -1212,15 +1260,17 @@ tls_multi_free (struct tls_multi *multi, bool clear)
if (multi->auth_token)
{
- memset (multi->auth_token, 0, AUTH_TOKEN_SIZE);
+ secure_memzero (multi->auth_token, AUTH_TOKEN_SIZE);
free (multi->auth_token);
}
+ free (multi->remote_ciphername);
+
for (i = 0; i < TM_SIZE; ++i)
tls_session_free (&multi->session[i], false);
if (clear)
- CLEAR (*multi);
+ secure_memzero (multi, sizeof (*multi));
free(multi);
}
@@ -1320,13 +1370,20 @@ write_control_auth (struct tls_session *session,
}
else if (session->tls_wrap.mode == TLS_WRAP_CRYPT)
{
- buf_init (&session->tls_wrap.work, buf->offset);
+ ASSERT (buf_init (&session->tls_wrap.work, buf->offset));
ASSERT (buf_write (&session->tls_wrap.work, &header, sizeof(header)));
ASSERT (session_id_write (&session->session_id, &session->tls_wrap.work));
- ASSERT (tls_crypt_wrap (buf, &session->tls_wrap.work, &session->tls_wrap.opt));
- /* Don't change the original data in buf, it's used by the reliability
- * layer to resend on failure. */
- *buf = session->tls_wrap.work;
+ if (tls_crypt_wrap (buf, &session->tls_wrap.work, &session->tls_wrap.opt))
+ {
+ /* Don't change the original data in buf, it's used by the reliability
+ * layer to resend on failure. */
+ *buf = session->tls_wrap.work;
+ }
+ else
+ {
+ buf->len = 0;
+ return;
+ }
}
*to_link_addr = &ks->remote_addr;
}
@@ -1503,7 +1560,7 @@ tls1_P_hash(const md_kt_t *md_kt,
}
hmac_ctx_cleanup(&ctx);
hmac_ctx_cleanup(&ctx_tmp);
- CLEAR (A1);
+ secure_memzero (A1, sizeof (A1));
dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex (out_orig, olen_orig, 0, &gc));
gc_free (&gc);
@@ -1556,7 +1613,7 @@ tls1_PRF(const uint8_t *label,
for (i=0; i<olen; i++)
out1[i]^=out2[i];
- memset (out2, 0, olen);
+ secure_memzero (out2, olen);
dmsg (D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex (out1, olen, 0, &gc));
@@ -1613,13 +1670,15 @@ generate_key_expansion (struct key_ctx_bi *key,
const struct session_id *server_sid,
bool server)
{
- uint8_t master[48];
- struct key2 key2;
+ uint8_t master[48] = { 0 };
+ struct key2 key2 = { 0 };
bool ret = false;
- int i;
- CLEAR (master);
- CLEAR (key2);
+ if (key->initialized)
+ {
+ msg (D_TLS_ERRORS, "TLS Error: key already initialized");
+ goto exit;
+ }
/* debugging print of source key material */
key_source2_print (key_src);
@@ -1655,7 +1714,7 @@ generate_key_expansion (struct key_ctx_bi *key,
key2_print (&key2, key_type, "Master Encrypt", "Master Decrypt");
/* check for weak keys */
- for (i = 0; i < 2; ++i)
+ for (int i = 0; i < 2; ++i)
{
fixup_key (&key2.keys[i], key_type);
if (!check_key (&key2.keys[i], key_type))
@@ -1691,8 +1750,8 @@ generate_key_expansion (struct key_ctx_bi *key,
ret = true;
exit:
- CLEAR (master);
- CLEAR (key2);
+ secure_memzero (&master, sizeof (master));
+ secure_memzero (&key2, sizeof (key2));
return ret;
}
@@ -1714,8 +1773,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) {
}
}
-static bool
-item_in_list(const char *item, const char *list)
+bool
+tls_item_in_cipher_list(const char *item, const char *list)
{
char *tmp_ciphers = string_alloc (list, NULL);
char *tmp_ciphers_orig = tmp_ciphers;
@@ -1732,18 +1791,61 @@ item_in_list(const char *item, const char *list)
return token != NULL;
}
-bool
-tls_session_update_crypto_params(struct tls_session *session,
- const struct options *options, struct frame *frame)
+void
+tls_poor_mans_ncp(struct options *o, const char *remote_ciphername)
+{
+ if (o->ncp_enabled && remote_ciphername &&
+ 0 != strcmp(o->ciphername, remote_ciphername))
+ {
+ if (tls_item_in_cipher_list(remote_ciphername, o->ncp_ciphers))
+ {
+ o->ciphername = string_alloc(remote_ciphername, &o->gc);
+ msg (D_TLS_DEBUG_LOW, "Using peer cipher '%s'", o->ciphername);
+ }
+ }
+}
+
+/**
+ * Generate data channel keys for the supplied TLS session.
+ *
+ * This erases the source material used to generate the data channel keys, and
+ * can thus be called only once per session.
+ */
+static bool
+tls_session_generate_data_channel_keys(struct tls_session *session)
{
bool ret = false;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
+ const struct session_id *client_sid = session->opt->server ?
+ &ks->session_id_remote : &session->session_id;
+ const struct session_id *server_sid = !session->opt->server ?
+ &ks->session_id_remote : &session->session_id;
ASSERT (ks->authenticated);
+ if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
+ &session->opt->key_type, ks->key_src, client_sid, server_sid,
+ session->opt->server))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
+ goto cleanup;
+ }
+ tls_limit_reneg_bytes (session->opt->key_type.cipher,
+ &session->opt->renegotiate_bytes);
+
+ ret = true;
+cleanup:
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
+ return ret;
+}
+
+bool
+tls_session_update_crypto_params(struct tls_session *session,
+ const struct options *options, struct frame *frame)
+{
if (!session->opt->server &&
0 != strcmp(options->ciphername, session->opt->config_ciphername) &&
- !item_in_list(options->ciphername, options->ncp_ciphers))
+ !tls_item_in_cipher_list(options->ciphername, options->ncp_ciphers))
{
msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s",
options->ciphername, session->opt->config_ciphername,
@@ -1768,23 +1870,7 @@ tls_session_update_crypto_params(struct tls_session *session,
frame_init_mssfix(frame, options);
frame_print (frame, D_MTU_INFO, "Data Channel MTU parms");
- const struct session_id *client_sid = session->opt->server ?
- &ks->session_id_remote : &session->session_id;
- const struct session_id *server_sid = !session->opt->server ?
- &ks->session_id_remote : &session->session_id;
- if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
- &session->opt->key_type, ks->key_src, client_sid, server_sid,
- session->opt->server))
- {
- msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
- goto cleanup;
- }
- tls_limit_reneg_bytes (session->opt->key_type.cipher,
- &session->opt->renegotiate_bytes);
- ret = true;
-cleanup:
- CLEAR (*ks->key_src);
- return ret;
+ return tls_session_generate_data_channel_keys (session);
}
static bool
@@ -1979,7 +2065,7 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)
init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key,
&session->opt->key_type, OPENVPN_OP_ENCRYPT,
"Data Channel Encrypt");
- CLEAR (key);
+ secure_memzero (&key, sizeof (key));
/* send local options string */
{
@@ -2152,28 +2238,19 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
{
if (ks->authenticated)
{
- if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
- &session->opt->key_type,
- ks->key_src,
- &ks->session_id_remote,
- &session->session_id,
- true))
+ if (!tls_session_generate_data_channel_keys (session))
{
msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
goto error;
}
}
-
- CLEAR (*ks->key_src);
- tls_limit_reneg_bytes (session->opt->key_type.cipher,
- &session->opt->renegotiate_bytes);
}
return true;
error:
msg (D_TLS_ERRORS, "TLS Error: Key Method #2 write failed");
- CLEAR (*ks->key_src);
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
return false;
}
@@ -2228,13 +2305,13 @@ key_method_1_read (struct buffer *buf, struct tls_session *session)
init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key,
&session->opt->key_type, OPENVPN_OP_DECRYPT,
"Data Channel Decrypt");
- CLEAR (key);
+ secure_memzero (&key, sizeof (key));
ks->authenticated = true;
return true;
error:
buf_clear (buf);
- CLEAR (key);
+ secure_memzero (&key, sizeof (key));
return false;
}
@@ -2303,10 +2380,20 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
if ( multi->peer_info )
output_peer_info_env (session->opt->es, multi->peer_info);
+ free (multi->remote_ciphername);
+ multi->remote_ciphername =
+ options_string_extract_option (options, "cipher", NULL);
+
if (tls_peer_info_ncp_ver (multi->peer_info) < 2)
{
- /* Peer does not support NCP */
- session->opt->ncp_enabled = false;
+ /* Peer does not support NCP, but leave NCP enabled if the local and
+ * remote cipher do not match to attempt 'poor-man's NCP'.
+ */
+ if (multi->remote_ciphername == NULL ||
+ 0 == strcmp(multi->remote_ciphername, multi->opt.config_ciphername))
+ {
+ session->opt->ncp_enabled = false;
+ }
}
#endif
@@ -2338,7 +2425,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
}
/* clear username and password from memory */
- CLEAR (*up);
+ secure_memzero (up, sizeof (*up));
/* Perform final authentication checks */
if (ks->authenticated)
@@ -2383,27 +2470,18 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
*/
if (!session->opt->server && (!session->opt->pull || ks->key_id > 0))
{
- if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
- &session->opt->key_type,
- ks->key_src,
- &session->session_id,
- &ks->session_id_remote,
- false))
+ if (!tls_session_generate_data_channel_keys (session))
{
msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed");
goto error;
}
-
- CLEAR (*ks->key_src);
- tls_limit_reneg_bytes (session->opt->key_type.cipher,
- &session->opt->renegotiate_bytes);
}
gc_free (&gc);
return true;
error:
- CLEAR (*ks->key_src);
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
buf_clear (buf);
gc_free (&gc);
return false;
@@ -2551,7 +2629,10 @@ tls_process (struct tls_multi *multi,
ks->state = S_START;
state_change = true;
- /* Reload the CRL before TLS negotiation */
+ /*
+ * Attempt CRL reload before TLS negotiation. Won't be performed if
+ * the file was not modified since the last reload
+ */
if (session->opt->crl_file &&
!(session->opt->ssl_flags & SSLF_CRL_VERIFY_DIR))
{
@@ -2609,22 +2690,6 @@ tls_process (struct tls_multi *multi,
break;
}
-#ifndef TLS_AGGREGATE_ACK
- /* Send 1 or more ACKs (each received control packet gets one ACK) */
- if (!to_link->len && !reliable_ack_empty (ks->rec_ack))
- {
- buf = &ks->ack_write_buf;
- ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
- write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
- RELIABLE_ACK_SIZE, false);
- *to_link = *buf;
- active = true;
- state_change = true;
- dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP");
- break;
- }
-#endif
-
/* Write incoming ciphertext to TLS object */
buf = reliable_get_buf_sequenced (ks->rec_reliable);
if (buf)
@@ -2769,7 +2834,6 @@ tls_process (struct tls_multi *multi,
update_time ();
-#ifdef TLS_AGGREGATE_ACK
/* Send 1 or more ACKs (each received control packet gets one ACK) */
if (!to_link->len && !reliable_ack_empty (ks->rec_ack))
{
@@ -2779,10 +2843,8 @@ tls_process (struct tls_multi *multi,
RELIABLE_ACK_SIZE, false);
*to_link = buf;
active = true;
- state_change = true;
dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP");
}
-#endif
/* When should we wake up again? */
{
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 777b621..c971b75 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -70,15 +70,8 @@
#define P_FIRST_OPCODE 1
#define P_LAST_OPCODE 9
-/* Should we aggregate TLS
- * acknowledgements, and tack them onto
- * control packets? */
-#define TLS_AGGREGATE_ACK
-
/*
- * If TLS_AGGREGATE_ACK, set the
- * max number of acknowledgments that
- * can "hitch a ride" on an outgoing
+ * Set the max number of acknowledgments that can "hitch a ride" on an outgoing
* non-P_ACK_V1 control packet.
*/
#define CONTROL_SEND_ACK_MAX 4
@@ -489,6 +482,15 @@ void tls_update_remote_addr (struct tls_multi *multi,
bool tls_session_update_crypto_params(struct tls_session *session,
const struct options *options, struct frame *frame);
+/**
+ * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher.
+ * Allows non-NCP peers to upgrade their cipher individually.
+ *
+ * Make sure to call tls_session_update_crypto_params() after calling this
+ * function.
+ */
+void tls_poor_mans_ncp(struct options *o, const char *remote_ciphername);
+
#ifdef MANAGEMENT_DEF_AUTH
static inline char *
tls_get_peer_info(const struct tls_multi *multi)
@@ -512,6 +514,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);
*/
bool tls_check_ncp_cipher_list(const char *list);
+/**
+ * Return true iff item is present in the colon-separated zero-terminated
+ * cipher list.
+ */
+bool tls_item_in_cipher_list(const char *item, const char *list);
+
+
/*
* inline functions
*/
diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
index 0777c61..3fbd2b4 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -353,7 +353,7 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl);
* "[[INLINE]]" in the case of inline files.
* @param crl_inline A string containing the CRL
*/
-void tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
+void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
const char *crl_file, const char *crl_inline);
/**
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 28702af..7938f41 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -540,6 +540,8 @@ struct tls_multi
uint32_t peer_id;
bool use_peer_id;
+ char *remote_ciphername; /**< cipher specified in peer's config file */
+
char *auth_token; /**< If server sends a generated auth-token,
* this is the token to use for future
* user/pass authentications in this session.
diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c
index 7fa35a7..11ee65b 100644
--- a/src/openvpn/ssl_mbedtls.c
+++ b/src/openvpn/ssl_mbedtls.c
@@ -771,7 +771,7 @@ static void tls_version_to_major_minor(int tls_ver, int *major, int *minor) {
}
void
-tls_ctx_reload_crl(struct tls_root_ctx *ctx, const char *crl_file,
+backend_tls_ctx_reload_crl(struct tls_root_ctx *ctx, const char *crl_file,
const char *crl_inline)
{
ASSERT (crl_file);
diff --git a/src/openvpn/ssl_mbedtls.h b/src/openvpn/ssl_mbedtls.h
index 3edeedc..a4a7f05 100644
--- a/src/openvpn/ssl_mbedtls.h
+++ b/src/openvpn/ssl_mbedtls.h
@@ -74,6 +74,8 @@ struct tls_root_ctx {
mbedtls_x509_crt *ca_chain; /**< CA chain for remote verification */
mbedtls_pk_context *priv_key; /**< Local private key */
mbedtls_x509_crl *crl; /**< Certificate Revocation List */
+ struct timespec crl_last_mtime; /**< CRL last modification time */
+ off_t crl_last_size; /**< size of last loaded CRL */
#if defined(ENABLE_PKCS11)
mbedtls_pkcs11_context *priv_key_pkcs11; /**< PKCS11 private key */
#endif
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index 51669fc..4f472ff 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -772,7 +772,7 @@ end:
}
void
-tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
+backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
const char *crl_inline)
{
X509_CRL *crl = NULL;
diff --git a/src/openvpn/ssl_openssl.h b/src/openvpn/ssl_openssl.h
index 97dc742..115ac43 100644
--- a/src/openvpn/ssl_openssl.h
+++ b/src/openvpn/ssl_openssl.h
@@ -49,6 +49,8 @@
*/
struct tls_root_ctx {
SSL_CTX *ctx;
+ struct timespec crl_last_mtime;
+ off_t crl_last_size;
};
struct key_state_ssl {
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index a099776..4328828 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -1176,7 +1176,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
if (memcmp_constant_time(multi->auth_token, up->password,
strlen(multi->auth_token)) != 0)
{
- memset (multi->auth_token, 0, AUTH_TOKEN_SIZE);
+ secure_memzero (multi->auth_token, AUTH_TOKEN_SIZE);
free (multi->auth_token);
multi->auth_token = NULL;
multi->auth_token_sent = false;
@@ -1262,7 +1262,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
"No auth-token will be activated now");
if (multi->auth_token)
{
- memset (multi->auth_token, 0, AUTH_TOKEN_SIZE);
+ secure_memzero (multi->auth_token, AUTH_TOKEN_SIZE);
free (multi->auth_token);
multi->auth_token = NULL;
}
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c
index 332f04b..4260823 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -348,12 +348,10 @@ x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)
int i;
unsigned char c;
const mbedtls_x509_name *name;
- char s[128];
+ char s[128] = { 0 };
name = &cert->subject;
- memset( s, 0, sizeof( s ) );
-
while( name != NULL )
{
char name_expand[64+8];
diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h
index 8de7d87..f5008b7 100644
--- a/src/openvpn/syshead.h
+++ b/src/openvpn/syshead.h
@@ -26,7 +26,7 @@
#define SYSHEAD_H
#include "compat.h"
-#include "compat-stdbool.h"
+#include <stdbool.h>
/* branch prediction hints */
#if defined(__GNUC__)
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 77ae72f..572e168 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -68,6 +68,9 @@ static void netsh_ifconfig (const struct tuntap_options *to,
const in_addr_t ip,
const in_addr_t netmask,
const unsigned int flags);
+static void netsh_set_dns6_servers (const struct in6_addr *addr_list,
+ const int addr_len,
+ const char *flex_name);
static void netsh_command (const struct argv *a, int n, int msglevel);
static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
@@ -132,6 +135,74 @@ out:
return ret;
}
+static bool
+do_dns6_service (bool add, const struct tuntap *tt)
+{
+ DWORD len;
+ bool ret = false;
+ ack_message_t ack;
+ struct gc_arena gc = gc_new ();
+ HANDLE pipe = tt->options.msg_channel;
+ int addr_len = add ? tt->options.dns6_len : 0;
+
+ if (addr_len == 0 && add) /* no addresses to add */
+ return true;
+
+ dns_cfg_message_t dns = {
+ .header = {
+ (add ? msg_add_dns_cfg : msg_del_dns_cfg),
+ sizeof (dns_cfg_message_t),
+ 0 },
+ .iface = { .index = tt->adapter_index, .name = "" },
+ .domains = "",
+ .family = AF_INET6,
+ .addr_len = addr_len
+ };
+
+ /* interface name is required */
+ strncpy (dns.iface.name, tt->actual_name, sizeof (dns.iface.name));
+ dns.iface.name[sizeof (dns.iface.name) - 1] = '\0';
+
+ if (addr_len > _countof(dns.addr))
+ {
+ addr_len = _countof(dns.addr);
+ dns.addr_len = addr_len;
+ msg(M_WARN, "Number of IPv6 DNS addresses sent to service truncated to %d",
+ addr_len);
+ }
+
+ for (int i = 0; i < addr_len; ++i)
+ {
+ dns.addr[i].ipv6 = tt->options.dns6[i];
+ }
+
+ msg (D_LOW, "%s IPv6 dns servers on '%s' (if_index = %d) using service",
+ (add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index);
+
+ if (!WriteFile (pipe, &dns, sizeof (dns), &len, NULL) ||
+ !ReadFile (pipe, &ack, sizeof (ack), &len, NULL))
+ {
+ msg (M_WARN, "TUN: could not talk to service: %s [%lu]",
+ strerror_win32 (GetLastError (), &gc), GetLastError ());
+ goto out;
+ }
+
+ if (ack.error_number != NO_ERROR)
+ {
+ msg (M_WARN, "TUN: %s IPv6 dns failed using service: %s [status=%u if_name=%s]",
+ (add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc),
+ ack.error_number, dns.iface.name);
+ goto out;
+ }
+
+ msg (M_INFO, "IPv6 dns servers %s using service", (add ? "set" : "deleted"));
+ ret = true;
+
+out:
+ gc_free (&gc);
+ return ret;
+}
+
#endif
#ifdef TARGET_SOLARIS
@@ -1372,9 +1443,16 @@ do_ifconfig (struct tuntap *tt,
if ( do_ipv6 )
{
- if (tt->options.msg_channel)
+ if (tt->options.ip_win32_type == IPW32_SET_MANUAL)
+ {
+ msg (M_INFO, "******** NOTE: Please manually set the v6 IP of '%s' to %s (if it is not already set)",
+ actual,
+ ifconfig_ipv6_local);
+ }
+ else if (tt->options.msg_channel)
{
do_address_service (true, AF_INET6, tt);
+ do_dns6_service (true, tt);
}
else
{
@@ -1388,10 +1466,15 @@ do_ifconfig (struct tuntap *tt,
iface,
ifconfig_ipv6_local );
netsh_command (&argv, 4, M_FATAL);
+ /* set ipv6 dns servers if any are specified */
+ netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, actual);
}
/* explicit route needed */
- add_route_connected_v6_net(tt, es);
+ if (tt->options.ip_win32_type != IPW32_SET_MANUAL)
+ {
+ add_route_connected_v6_net(tt, es);
+ }
}
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
@@ -1623,14 +1706,20 @@ void
open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
{
#define ANDROID_TUNNAME "vpnservice-tun"
- int i;
struct user_pass up;
struct gc_arena gc = gc_new ();
bool opentun;
int oldtunfd = tt->fd;
- for (i = 0; i < tt->options.dns_len; ++i) {
+ /* Prefer IPv6 DNS servers,
+ * Android will use the DNS server in the order we specify*/
+ for (int i = 0; i < tt->options.dns6_len; i++) {
+ management_android_control (management, "DNS6SERVER",
+ print_in6_addr (tt->options.dns6[i], 0, &gc));
+ }
+
+ for (int i = 0; i < tt->options.dns_len; i++) {
management_android_control (management, "DNSSERVER",
print_in_addr_t(tt->options.dns[i], 0, &gc));
}
@@ -4508,23 +4597,9 @@ ipconfig_register_dns (const struct env_set *es)
bool status;
const char err[] = "ERROR: Windows ipconfig command failed";
- msg (D_TUNTAP_INFO, "Start net commands...");
+ msg (D_TUNTAP_INFO, "Start ipconfig commands for register-dns...");
netcmd_semaphore_lock ();
- argv_printf (&argv, "%s%sc stop dnscache",
- get_win_sys_path(),
- WIN_NET_PATH_SUFFIX);
- argv_msg (D_TUNTAP_INFO, &argv);
- status = openvpn_execve_check (&argv, es, 0, err);
- argv_reset(&argv);
-
- argv_printf (&argv, "%s%sc start dnscache",
- get_win_sys_path(),
- WIN_NET_PATH_SUFFIX);
- argv_msg (D_TUNTAP_INFO, &argv);
- status = openvpn_execve_check (&argv, es, 0, err);
- argv_reset(&argv);
-
argv_printf (&argv, "%s%sc /flushdns",
get_win_sys_path(),
WIN_IPCONFIG_PATH_SUFFIX);
@@ -4540,7 +4615,7 @@ ipconfig_register_dns (const struct env_set *es)
argv_reset(&argv);
netcmd_semaphore_release ();
- msg (D_TUNTAP_INFO, "End net commands...");
+ msg (D_TUNTAP_INFO, "End ipconfig commands for register-dns...");
}
void
@@ -4617,6 +4692,41 @@ ip_addr_member_of (const in_addr_t addr, const IP_ADDR_STRING *ias)
return false;
}
+/**
+ * Set the ipv6 dns servers on the specified interface.
+ * The list of dns servers currently set on the interface
+ * are cleared first.
+ * No action is taken if number of addresses (addr_len) < 1.
+ */
+static void
+netsh_set_dns6_servers (const struct in6_addr *addr_list,
+ const int addr_len,
+ const char *flex_name)
+{
+ struct gc_arena gc = gc_new ();
+ struct argv argv = argv_new ();
+
+ for (int i = 0; i < addr_len; ++i)
+ {
+ const char *fmt = (i == 0) ?
+ "%s%sc interface ipv6 set dns %s static %s"
+ : "%s%sc interface ipv6 add dns %s %s";
+ argv_printf (&argv, fmt, get_win_sys_path(),
+ NETSH_PATH_SUFFIX, flex_name,
+ print_in6_addr (addr_list[i], 0, &gc));
+
+ /* disable slow address validation on Windows 7 and higher */
+ if (win32_version_info() >= WIN_7)
+ argv_printf_cat (&argv, "%s", "validate=no");
+
+ /* Treat errors while adding as non-fatal as we do not check for duplicates */
+ netsh_command (&argv, 1, (i==0)? M_FATAL : M_NONFATAL);
+ }
+
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
static void
netsh_ifconfig_options (const char *type,
const in_addr_t *addr_list,
@@ -5540,6 +5650,8 @@ close_tun (struct tuntap *tt)
if (tt->options.msg_channel)
{
do_address_service (false, AF_INET6, tt);
+ if (tt->options.dns6_len > 0)
+ do_dns6_service (false, tt);
}
else
{
@@ -5563,6 +5675,17 @@ close_tun (struct tuntap *tt)
ifconfig_ipv6_local);
netsh_command (&argv, 1, M_WARN);
+
+ /* delete ipv6 dns servers if any were set */
+ if (tt->options.dns6_len > 0)
+ {
+ argv_printf (&argv,
+ "%s%sc interface ipv6 delete dns %s all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ tt->actual_name);
+ netsh_command (&argv, 1, M_WARN);
+ }
argv_reset (&argv);
}
}
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index dedd915..9b5a1b7 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -107,6 +107,9 @@ struct tuntap_options {
bool dhcp_release;
bool register_dns;
+
+ struct in6_addr dns6[N_DHCP_ADDR];
+ int dns6_len;
};
#elif TARGET_LINUX
diff --git a/src/openvpnserv/Makefile.am b/src/openvpnserv/Makefile.am
index 3521a34..58ecd91 100644
--- a/src/openvpnserv/Makefile.am
+++ b/src/openvpnserv/Makefile.am
@@ -26,7 +26,7 @@ openvpnserv_CFLAGS = \
-municode -D_UNICODE \
-UNTDDI_VERSION -U_WIN32_WINNT \
-D_WIN32_WINNT=_WIN32_WINNT_VISTA
-openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32
+openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 -lntdll
endif
openvpnserv_SOURCES = \
diff --git a/src/openvpnserv/Makefile.in b/src/openvpnserv/Makefile.in
index 74a802b..b38a76a 100644
--- a/src/openvpnserv/Makefile.in
+++ b/src/openvpnserv/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -387,7 +387,7 @@ AM_CPPFLAGS = \
@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \
@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA
-@WIN32_TRUE@openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32
+@WIN32_TRUE@openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 -lntdll
openvpnserv_SOURCES = \
common.c \
automatic.c \
@@ -504,14 +504,14 @@ distclean-compile:
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c
index dba4724..eafee20 100644
--- a/src/openvpnserv/common.c
+++ b/src/openvpnserv/common.c
@@ -216,3 +216,15 @@ MsgToEventLog (DWORD flags, LPCTSTR format, ...)
return error;
}
+
+/* Convert a utf8 string to utf16. Caller should free the result */
+wchar_t *
+utf8to16 (const char *utf8)
+{
+ int n = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0);
+ wchar_t *utf16 = malloc (n * sizeof (wchar_t));
+ if (!utf16)
+ return NULL;
+ MultiByteToWideChar (CP_UTF8, 0, utf8, -1, utf16, n);
+ return utf16;
+}
diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c
index ffaa171..ec54216 100644
--- a/src/openvpnserv/interactive.c
+++ b/src/openvpnserv/interactive.c
@@ -35,6 +35,12 @@
#include <sddl.h>
#include <shellapi.h>
+#ifdef HAVE_VERSIONHELPERS_H
+#include <versionhelpers.h>
+#else
+#include "compat-versionhelpers.h"
+#endif
+
#include "openvpn-msg.h"
#include "validate.h"
#include "block_dns.h"
@@ -82,6 +88,8 @@ typedef enum {
address,
route,
block_dns,
+ undo_dns4,
+ undo_dns6,
_undo_type_max
} undo_type_t;
typedef list_item_t* undo_lists_t[_undo_type_max];
@@ -891,8 +899,7 @@ RegisterDNS (LPVOID unused)
WCHAR sys_path[MAX_PATH];
DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
- /* default paths of net and ipconfig commands */
- WCHAR net[MAX_PATH] = L"C:\\Windows\\system32\\net.exe";
+ /* default path of ipconfig command */
WCHAR ipcfg[MAX_PATH] = L"C:\\Windows\\system32\\ipconfig.exe";
struct
@@ -901,8 +908,6 @@ RegisterDNS (LPVOID unused)
WCHAR *cmdline;
DWORD timeout;
} cmds [] = {
- { net, L"net stop dnscache", timeout },
- { net, L"net start dnscache", timeout },
{ ipcfg, L"ipconfig /flushdns", timeout },
{ ipcfg, L"ipconfig /registerdns", timeout },
};
@@ -912,9 +917,6 @@ RegisterDNS (LPVOID unused)
if(GetSystemDirectory(sys_path, MAX_PATH))
{
- _snwprintf (net, MAX_PATH, L"%s\\%s", sys_path, L"net.exe");
- net[MAX_PATH-1] = L'\0';
-
_snwprintf (ipcfg, MAX_PATH, L"%s\\%s", sys_path, L"ipconfig.exe");
ipcfg[MAX_PATH-1] = L'\0';
}
@@ -962,6 +964,156 @@ HandleRegisterDNSMessage (void)
return err;
}
+/**
+ * Run the command: netsh interface $proto $action dns $if_name $addr [validate=no]
+ * @param action "delete" or "add"
+ * @param proto "ipv6" or "ip"
+ * @param if_name "name_of_interface"
+ * @param addr IPv4 (for proto = ip) or IPv6 address as a string
+ *
+ * If addr is null and action = "delete" all addresses are deleted.
+ */
+static DWORD
+netsh_dns_cmd (const wchar_t *action, const wchar_t *proto, const wchar_t *if_name, const wchar_t *addr)
+{
+ DWORD err = 0;
+ int timeout = 30000; /* in msec */
+ wchar_t argv0[MAX_PATH];
+
+ if (!addr)
+ {
+ if (wcscmp(action, L"delete") == 0)
+ addr = L"all";
+ else /* nothing to do -- return success*/
+ goto out;
+ }
+
+ /* Path of netsh */
+ int n = GetSystemDirectory (argv0, MAX_PATH);
+ if (n > 0 && n < MAX_PATH) /* got system directory */
+ {
+ wcsncat(argv0, L"\\netsh.exe", MAX_PATH - n - 1);
+ }
+ else
+ {
+ wcsncpy(argv0, L"C:\\Windows\\system32\\netsh.exe", MAX_PATH);
+ }
+
+ /* cmd template:
+ * netsh interface $proto $action dns $if_name $addr [validate=no]
+ */
+ const wchar_t *fmt = L"netsh interface %s %s dns \"%s\" %s";
+
+ /* max cmdline length in wchars -- include room for worst case and some */
+ int ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1;
+ wchar_t *cmdline = malloc(ncmdline*sizeof(wchar_t));
+ if (!cmdline)
+ {
+ err = ERROR_OUTOFMEMORY;
+ goto out;
+ }
+
+ openvpn_sntprintf (cmdline, ncmdline, fmt, proto, action, if_name, addr);
+
+ if (IsWindows7OrGreater())
+ {
+ wcsncat(cmdline, L" validate=no", ncmdline - wcslen(cmdline) - 1);
+ }
+ err = ExecCommand (argv0, cmdline, timeout);
+
+out:
+ free (cmdline);
+ return err;
+}
+
+/* Delete all IPv4 or IPv6 dns servers for an interface */
+static DWORD
+DeleteDNS(short family, wchar_t *if_name)
+{
+ wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
+ return netsh_dns_cmd (L"delete", proto, if_name, NULL);
+}
+
+/* Add an IPv4 or IPv6 dns server to an interface */
+static DWORD
+AddDNS(short family, wchar_t *if_name, wchar_t *addr)
+{
+ wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
+ return netsh_dns_cmd (L"add", proto, if_name, addr);
+}
+
+static BOOL
+CmpWString (LPVOID item, LPVOID str)
+{
+ return (wcscmp (item, str) == 0) ? TRUE : FALSE;
+}
+
+static DWORD
+HandleDNSConfigMessage (const dns_cfg_message_t *msg, undo_lists_t *lists)
+{
+ DWORD err = 0;
+ wchar_t addr[46]; /* large enough to hold string representation of an ipv4 / ipv6 address */
+ undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns4 : undo_dns6;
+ int addr_len = msg->addr_len;
+
+ /* sanity check */
+ if (addr_len > _countof(msg->addr))
+ addr_len = _countof(msg->addr);
+
+ if (!msg->iface.name[0]) /* interface name is required */
+ return ERROR_MESSAGE_DATA;
+
+ wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
+ if (!wide_name)
+ return ERROR_OUTOFMEMORY;
+
+ /* We delete all current addresses before adding any
+ * OR if the message type is del_dns_cfg
+ */
+ if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
+ {
+ err = DeleteDNS(msg->family, wide_name);
+ if (err)
+ goto out;
+ free (RemoveListItem (&(*lists)[undo_type], CmpWString, wide_name));
+ }
+
+ if (msg->header.type == msg_del_dns_cfg) /* job done */
+ goto out;
+
+ for (int i = 0; i < addr_len; ++i)
+ {
+ if (msg->family == AF_INET6)
+ RtlIpv6AddressToStringW (&msg->addr[i].ipv6, addr);
+ else
+ RtlIpv4AddressToStringW (&msg->addr[i].ipv4, addr);
+ err = AddDNS(msg->family, wide_name, addr);
+ if (i == 0 && err)
+ goto out;
+ /* We do not check for duplicate addresses, so any error in adding
+ * additional addresses is ignored.
+ */
+ }
+
+ if (msg->addr_len > 0)
+ {
+ wchar_t *tmp_name = wcsdup(wide_name);
+ if (!tmp_name || AddListItem(&(*lists)[undo_type], tmp_name))
+ {
+ free(tmp_name);
+ DeleteDNS(msg->family, wide_name);
+ err = ERROR_OUTOFMEMORY;
+ goto out;
+ }
+ }
+
+ err = 0;
+
+out:
+ free(wide_name);
+ return err;
+}
+
static VOID
HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
{
@@ -972,6 +1124,7 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list
route_message_t route;
flush_neighbors_message_t flush_neighbors;
block_dns_message_t block_dns;
+ dns_cfg_message_t dns;
} msg;
ack_message_t ack = {
.header = {
@@ -1017,6 +1170,11 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list
ack.error_number = HandleRegisterDNSMessage ();
break;
+ case msg_add_dns_cfg:
+ case msg_del_dns_cfg:
+ ack.error_number = HandleDNSConfigMessage (&msg.dns, lists);
+ break;
+
default:
ack.error_number = ERROR_MESSAGE_TYPE;
MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
@@ -1048,6 +1206,14 @@ Undo (undo_lists_t *lists)
DeleteRoute (item->data);
break;
+ case undo_dns4:
+ DeleteDNS(AF_INET, item->data);
+ break;
+
+ case undo_dns6:
+ DeleteDNS(AF_INET6, item->data);
+ break;
+
case block_dns:
delete_block_dns_filters (item->data);
item->data = NULL;
diff --git a/src/openvpnserv/service.h b/src/openvpnserv/service.h
index 94bfb07..c5d745f 100644
--- a/src/openvpnserv/service.h
+++ b/src/openvpnserv/service.h
@@ -89,4 +89,7 @@ BOOL ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
LPCTSTR GetLastErrorText ();
DWORD MsgToEventLog (DWORD flags, LPCTSTR lpszMsg, ...);
+/* Convert a utf8 string to utf16. Caller should free the result */
+wchar_t *utf8to16 (const char *utf8);
+
#endif
diff --git a/src/plugins/Makefile.in b/src/plugins/Makefile.in
index 0d1d59b..7f80a5c 100644
--- a/src/plugins/Makefile.in
+++ b/src/plugins/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
diff --git a/src/plugins/auth-pam/Makefile.in b/src/plugins/auth-pam/Makefile.in
index 90d5058..3a3c656 100644
--- a/src/plugins/auth-pam/Makefile.in
+++ b/src/plugins/auth-pam/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -494,14 +494,14 @@ distclean-compile:
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/src/plugins/down-root/Makefile.in b/src/plugins/down-root/Makefile.in
index e5c0ad5..043dbb4 100644
--- a/src/plugins/down-root/Makefile.in
+++ b/src/plugins/down-root/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -485,14 +485,14 @@ distclean-compile:
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<