diff options
Diffstat (limited to 'src')
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 $@ $< |