diff options
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | buffer.c | 34 | ||||
-rw-r--r-- | buffer.h | 2 | ||||
-rw-r--r-- | common.h | 2 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | configure_h.awk | 39 | ||||
-rw-r--r-- | configure_log.awk | 33 | ||||
-rwxr-xr-x | easy-rsa/2.0/pkitool | 6 | ||||
-rw-r--r-- | error.c | 14 | ||||
-rw-r--r-- | init.c | 24 | ||||
-rw-r--r-- | misc.c | 7 | ||||
-rw-r--r-- | misc.h | 10 | ||||
-rw-r--r-- | multi.c | 6 | ||||
-rw-r--r-- | openvpn.8 | 42 | ||||
-rw-r--r-- | options.c | 78 | ||||
-rw-r--r-- | options.h | 4 | ||||
-rw-r--r-- | proxy.c | 7 | ||||
-rw-r--r-- | route.c | 53 | ||||
-rw-r--r-- | socket.c | 35 | ||||
-rw-r--r-- | socket.h | 12 | ||||
-rw-r--r-- | ssl.c | 146 | ||||
-rw-r--r-- | ssl.h | 10 | ||||
-rw-r--r-- | syshead.h | 4 | ||||
-rwxr-xr-x | tap-win32/proto.h | 60 | ||||
-rwxr-xr-x | tap-win32/tapdrvr.c | 197 | ||||
-rwxr-xr-x | tap-win32/types.h | 1 | ||||
-rw-r--r-- | tun.c | 59 | ||||
-rw-r--r-- | tun.h | 5 | ||||
-rw-r--r-- | version.m4 | 2 | ||||
-rw-r--r-- | win/build_exe.py | 15 | ||||
-rw-r--r-- | win/config.py | 1 | ||||
-rw-r--r-- | win/make_dist.py | 44 | ||||
-rw-r--r-- | win/msvc.mak.in | 19 | ||||
-rw-r--r-- | win/settings.in | 3 | ||||
-rw-r--r-- | win32.c | 13 | ||||
-rw-r--r-- | win32.h | 2 |
36 files changed, 871 insertions, 139 deletions
diff --git a/Makefile.am b/Makefile.am index 2980dac..75f9b1a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,7 @@ # packet compression. # # Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> +# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 @@ -37,7 +38,7 @@ MAINTAINERCLEANFILES = \ $(srcdir)/depcomp $(srcdir)/aclocal.m4 \ $(srcdir)/config.guess $(srcdir)/config.sub \ $(srcdir)/openvpn.spec -CLEANFILES = openvpn.8.html +CLEANFILES = openvpn.8.html configure.h EXTRA_DIST = \ easy-rsa \ @@ -64,7 +65,8 @@ dist_noinst_SCRIPTS = \ $(TESTS) \ doclean \ domake-win \ - t_cltsrv-down.sh + t_cltsrv-down.sh \ + configure_h.awk configure_log.awk dist_noinst_DATA = \ openvpn.spec \ @@ -140,6 +142,12 @@ openvpn_SOURCES = \ win32.h win32.c \ cryptoapi.h cryptoapi.c +nodist_openvpn_SOURCES = configure.h +options.$(OBJEXT): configure.h + +configure.h: Makefile + awk -f $(srcdir)/configure_h.awk config.h > $@ + awk -f $(srcdir)/configure_log.awk config.log >> $@ dist-hook: cd $(distdir) && for i in $(EXTRA_DIST) $(SUBDIRS) ; do find $$i -name .svn -type d -prune -exec rm -rf '{}' ';' ; rm -f `find $$i -type f | grep -E '(^|\/)\.?\#|\~$$|\.s?o$$'` ; done @@ -896,8 +896,11 @@ buffer_list_new (const int max_size) void buffer_list_free (struct buffer_list *ol) { - buffer_list_reset (ol); - free (ol); + if (ol) + { + buffer_list_reset (ol); + free (ol); + } } bool @@ -924,9 +927,21 @@ buffer_list_reset (struct buffer_list *ol) void buffer_list_push (struct buffer_list *ol, const unsigned char *str) { - if (!ol->max_size || ol->size < ol->max_size) + if (str) + { + const size_t len = strlen ((const char *)str); + struct buffer_entry *e = buffer_list_push_data (ol, str, len+1); + if (e) + e->buf.len = len; /* Don't count trailing '\0' as part of length */ + } +} + +struct buffer_entry * +buffer_list_push_data (struct buffer_list *ol, const uint8_t *data, size_t size) +{ + struct buffer_entry *e = NULL; + if (data && (!ol->max_size || ol->size < ol->max_size)) { - struct buffer_entry *e; ALLOC_OBJ_CLEAR (e, struct buffer_entry); ++ol->size; @@ -940,15 +955,18 @@ buffer_list_push (struct buffer_list *ol, const unsigned char *str) ASSERT (!ol->head); ol->head = e; } - e->buf = string_alloc_buf ((const char *) str, NULL); + e->buf = alloc_buf (size); + memcpy (e->buf.data, data, size); + e->buf.len = (int)size; ol->tail = e; } + return e; } struct buffer * buffer_list_peek (struct buffer_list *ol) { - if (ol->head) + if (ol && ol->head) return &ol->head->buf; else return NULL; @@ -993,10 +1011,10 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max) } } -static void +void buffer_list_pop (struct buffer_list *ol) { - if (ol->head) + if (ol && ol->head) { struct buffer_entry *e = ol->head->next; free_buf (&ol->head->buf); @@ -851,8 +851,10 @@ bool buffer_list_defined (const struct buffer_list *ol); void buffer_list_reset (struct buffer_list *ol); void buffer_list_push (struct buffer_list *ol, const unsigned char *str); +struct buffer_entry *buffer_list_push_data (struct buffer_list *ol, const uint8_t *data, size_t size); struct buffer *buffer_list_peek (struct buffer_list *ol); void buffer_list_advance (struct buffer_list *ol, int n); +void buffer_list_pop (struct buffer_list *ol); void buffer_list_aggregate (struct buffer_list *bl, const size_t max); @@ -97,6 +97,6 @@ typedef unsigned long ptr_type; /* * Script security warning */ -#define SCRIPT_SECURITY_WARNING "openvpn_execve: external program may not be called unless '--script-security 2' or higher is enabled. Use '--script-security 3 system' for backward compatibility with 2.1_rc8 and earlier. See --help text or man page for detailed info." +#define SCRIPT_SECURITY_WARNING "WARNING: External program may not be called unless '--script-security 2' or higher is enabled. Use '--script-security 3 system' for backward compatibility with 2.1_rc8 and earlier. See --help text or man page for detailed info." #endif diff --git a/configure.ac b/configure.ac index 4626e46..5b27b89 100644 --- a/configure.ac +++ b/configure.ac @@ -98,6 +98,12 @@ AC_ARG_ENABLE(plugins, [PLUGINS="yes"] ) +AC_ARG_ENABLE(eurephia, + [ --disable-eurephia Disable support for the eurephia plug-in], + [EUREPHIA="$enableval"], + [EUREPHIA="yes"] +) + AC_ARG_ENABLE(management, [ --disable-management Disable management server support], [MANAGEMENT="$enableval"], @@ -631,6 +637,9 @@ if test "${WIN32}" != "yes"; then )], [AC_MSG_RESULT([libdl headers not found.])] ) + if test "$EUREPHIA" = "yes"; then + AC_DEFINE(ENABLE_EUREPHIA, 1, [Enable support for the eurephia plug-in]) + fi fi fi diff --git a/configure_h.awk b/configure_h.awk new file mode 100644 index 0000000..672e745 --- /dev/null +++ b/configure_h.awk @@ -0,0 +1,39 @@ +# +# OpenVPN -- An application to securely tunnel IP networks +# over a single UDP port, with support for SSL/TLS-based +# session authentication and key exchange, +# packet encryption, packet authentication, and +# packet compression. +# +# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# This script will build up a line which can be included into a C program. +# The line will contain all interesting #define statements from f.ex. ./config.h +# + +BEGIN { + printf ("#define CONFIGURE_DEFINES \"") +} + +/^#define (ENABLE|DISABLE|DEPRECATED|USE)_/ { + printf (" %s", $2) +} + +END { + printf ("\"\n") +} diff --git a/configure_log.awk b/configure_log.awk new file mode 100644 index 0000000..099e5c4 --- /dev/null +++ b/configure_log.awk @@ -0,0 +1,33 @@ +# +# OpenVPN -- An application to securely tunnel IP networks +# over a single UDP port, with support for SSL/TLS-based +# session authentication and key exchange, +# packet encryption, packet authentication, and +# packet compression. +# +# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# This script will build up a line which can be included into a C program. +# The line will only contain the first entry of the ./configure line from +# ./config.log. +# + +/\$ (.*)\/configure/ { + printf ("#define CONFIGURE_CALL \"%s\"\n", $0) + exit 0 +} diff --git a/easy-rsa/2.0/pkitool b/easy-rsa/2.0/pkitool index 7266988..49588f5 100755 --- a/easy-rsa/2.0/pkitool +++ b/easy-rsa/2.0/pkitool @@ -192,6 +192,12 @@ while [ $# -gt 0 ]; do $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --list-objects --login --slot "$PKCS11_SLOT" exit 0;; + --help|--usage) + usage + exit ;; + --version) + echo "$PROGNAME $VERSION" + exit ;; # errors --* ) die "$PROGNAME: unknown option: $1" ;; * ) break ;; @@ -477,14 +477,16 @@ redirect_stdout_stderr (const char *file, bool append) { HANDLE log_handle; int log_fd; - struct security_attributes sa; - init_security_attributes_allow_all (&sa); + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; log_handle = CreateFile (file, GENERIC_WRITE, FILE_SHARE_READ, - &sa.sa, + &saAttr, append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); @@ -505,10 +507,12 @@ redirect_stdout_stderr (const char *file, bool append) /* save original stderr for password prompts */ orig_stderr = GetStdHandle (STD_ERROR_HANDLE); +#if 0 /* seems not be necessary with stdout/stderr redirection below*/ /* set up for redirection */ if (!SetStdHandle (STD_OUTPUT_HANDLE, log_handle) || !SetStdHandle (STD_ERROR_HANDLE, log_handle)) msg (M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file); +#endif /* direct stdout/stderr to point to log_handle */ log_fd = _open_osfhandle ((intptr_t)log_handle, _O_TEXT); @@ -521,6 +525,10 @@ redirect_stdout_stderr (const char *file, bool append) if (msgfp == NULL) msg (M_ERR, "Error: --log redirect failed due to _fdopen"); + /* redirect C-library stdout/stderr to log file */ + if (_dup2 (log_fd, 1) == -1 || _dup2 (log_fd, 2) == -1) + msg (M_WARN, "Error: --log redirect of stdout/stderr failed"); + std_redir = true; } #elif defined(HAVE_DUP2) @@ -122,7 +122,8 @@ ce_http_proxy_fallback_defined(const struct context *c) int i; for (i = 0; i < l->len; ++i) { - if (l->array[i]->flags & CE_HTTP_PROXY_FALLBACK) + const struct connection_entry *ce = l->array[i]; + if (ce->flags & CE_HTTP_PROXY_FALLBACK) return true; } } @@ -193,12 +194,9 @@ management_callback_http_proxy_fallback_cmd (void *arg, const char *server, cons struct connection_entry *ce = l->array[i]; if (ce->flags & CE_HTTP_PROXY_FALLBACK) { - if (ho) - { - ce->http_proxy_options = ho; - ret = true; - } + ce->http_proxy_options = ho; ce->ce_http_proxy_fallback_timestamp = now; + ret = true; } } } @@ -278,7 +276,7 @@ next_connection_entry (struct context *c) do { const char *remote_ip_hint = NULL; - bool advanced = false; + bool newcycle = false; ce_defined = true; if (l->no_advance && l->current >= 0) @@ -295,7 +293,8 @@ next_connection_entry (struct context *c) msg (M_FATAL, "No usable connection profiles are present"); } - advanced = true; + if (l->current == 0) + newcycle = true; show_connection_list(l); } @@ -305,7 +304,7 @@ next_connection_entry (struct context *c) remote_ip_hint = c->options.remote_ip_hint; #if HTTP_PROXY_FALLBACK - if (advanced && ce_http_proxy_fallback_defined(c)) + if (newcycle && ce_http_proxy_fallback_defined(c)) ce_http_proxy_fallback_start(c, remote_ip_hint); if (ce->flags & CE_HTTP_PROXY_FALLBACK) @@ -1139,6 +1138,10 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) if ((flags & (ISC_ERRORS|ISC_SERVER)) == 0 && connection_list_defined (&c->options)) connection_list_set_no_advance (&c->options); +#ifdef WIN32 + fork_register_dns_action (c->c1.tuntap); +#endif + #ifdef ENABLE_MANAGEMENT /* Tell management interface that we initialized */ if (management) @@ -1188,7 +1191,7 @@ do_route (const struct options *options, struct argv argv = argv_new (); setenv_str (es, "script_type", "route-up"); argv_printf (&argv, "%sc", options->route_script); - openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed"); + openvpn_run_script (&argv, es, 0, "--route-up"); argv_reset (&argv); } @@ -2021,6 +2024,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) #endif to.verify_command = options->tls_verify; + to.verify_export_cert = options->tls_export_cert; to.verify_x509name = options->tls_remote; to.crl_file = options->crl_file; to.ns_cert_type = options->ns_cert_type; @@ -230,7 +230,7 @@ run_up_down (const char *command, ifconfig_local, ifconfig_remote, context); argv_msg (M_INFO, &argv); - openvpn_execve_check (&argv, es, S_SCRIPT|S_FATAL, "script failed"); + openvpn_run_script (&argv, es, S_FATAL, "--up/--down"); argv_reset (&argv); } @@ -493,6 +493,7 @@ openvpn_execve_allowed (const unsigned int flags) return script_security >= SSEC_BUILT_IN; } + #ifndef WIN32 /* * Run execve() inside a fork(). Designed to replicate the semantics of system() but @@ -504,6 +505,7 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i { struct gc_arena gc = gc_new (); int ret = -1; + static bool warn_shown = false; if (a && a->argv[0]) { @@ -540,9 +542,10 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i ASSERT (0); } } - else + else if (!warn_shown && (script_security < SSEC_SCRIPTS)) { msg (M_WARN, SCRIPT_SECURITY_WARNING); + warn_shown = true; } #else msg (M_WARN, "openvpn_execve: execve function not available"); @@ -136,6 +136,15 @@ bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const bool openvpn_execve_allowed (const unsigned int flags); int openvpn_system (const char *command, const struct env_set *es, unsigned int flags); +static inline bool +openvpn_run_script (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *hook) +{ + char msg[256]; + + openvpn_snprintf(msg, sizeof(msg), "WARNING: Failed running command (%s)", hook); + return openvpn_execve_check(a, es, flags | S_SCRIPT, msg); +}; + #ifdef HAVE_STRERROR /* a thread-safe version of strerror */ const char* strerror_ts (int errnum, struct gc_arena *gc); @@ -308,6 +317,7 @@ void get_user_pass_auto_userid (struct user_pass *up, const char *tag); extern const char *iproute_path; #endif +/* Script security */ #define SSEC_NONE 0 /* strictly no calling of external programs */ #define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/ #define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */ @@ -109,7 +109,7 @@ learn_address_script (const struct multi_context *m, mroute_addr_print (addr, &gc)); if (mi) argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false)); - if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed")) + if (!openvpn_run_script (&argv, es, 0, "--learn-address")) ret = false; argv_reset (&argv); } @@ -480,7 +480,7 @@ multi_client_disconnect_script (struct multi_context *m, struct argv argv = argv_new (); setenv_str (mi->context.c2.es, "script_type", "client-disconnect"); argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script); - openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed"); + openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-disconnect"); argv_reset (&argv); } #ifdef MANAGEMENT_DEF_AUTH @@ -1594,7 +1594,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi mi->context.options.client_connect_script, dc_file); - if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed")) + if (openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-connect")) { multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found); ++cc_succeeded_count; @@ -4254,11 +4254,23 @@ test). .B cmd should return 0 to allow the TLS handshake to proceed, or 1 to fail. + +Note that +.B cmd +is a command line and as such may (if enclosed in quotes) contain +whitespace separated arguments. The first word of .B cmd -is executed as +is the shell command to execute and the remaining words are its +arguments. +When +.B cmd +is executed two arguments are appended, as follows: .B cmd certificate_depth X509_NAME_oneline +These arguments are, respectively, the current certificate depth and +the X509 common name (cn) of the peer. + This feature is useful if the peer you want to trust has a certificate which was signed by a certificate authority who also signed many other certificates, where you don't necessarily want to trust all of them, @@ -4272,14 +4284,14 @@ in the OpenVPN distribution. See the "Environmental Variables" section below for additional parameters passed as environmental variables. - -Note that -.B cmd -can be a shell command with multiple arguments, in which -case all OpenVPN-generated arguments will be appended -to -.B cmd -to build a command line which will be passed to the script. +.\"********************************************************* +.TP +.B --tls-export-cert directory +Store the certificates the clients uses upon connection to this +directory. This will be done before --tls-verify is called. The +certificates will use a temporary name and will be deleted when +the tls-verify script returns. The file name used for the certificate +is available via the peer_cert environment variable. .\"********************************************************* .TP .B \-\-tls-remote name @@ -4760,6 +4772,13 @@ This option has the same caveats as above. .\"********************************************************* .TP +.B \-\-register-dns +Run net stop dnscache, net start dnscache, ipconfig /flushdns +and ipconfig /registerdns on connection initiation. +This is known to kick Windows into +recognizing pushed DNS servers. +.\"********************************************************* +.TP .B \-\-pause-exit Put up a "press any key to continue" message on the console prior to OpenVPN program exit. This option is automatically used by the @@ -5266,6 +5285,11 @@ than their names as denoted on the command line or configuration file. .\"********************************************************* .TP +.B peer_cert +Temporary file name containing the client certificate upon +connection. Useful in conjunction with --tls-verify +.\"********************************************************* +.TP .B script_context Set to "init" or "restart" prior to up/down script execution. For more information, see @@ -7,6 +7,9 @@ * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> * + * Additions for eurephia plugin done by: + * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2009 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. @@ -45,6 +48,8 @@ #include "pool.h" #include "helper.h" #include "manage.h" +#include "configure.h" +#include <ctype.h> #include "memdbg.h" @@ -73,6 +78,9 @@ const char title_string[] = #ifdef ENABLE_PKCS11 " [PKCS11]" #endif +#ifdef ENABLE_EUREPHIA + " [eurephia]" +#endif " built on " __DATE__ ; @@ -503,6 +511,8 @@ static const char usage_message[] = "--key file : Local private key in .pem format.\n" "--pkcs12 file : PKCS#12 file containing local private key, local certificate\n" " and optionally the root CA certificate.\n" + "--x509-username-field : Field used in x509 certificat to be username.\n" + " Default is CN.\n" #ifdef WIN32 "--cryptoapicert select-string : Load the certificate and private key from the\n" " Windows Certificate System Store.\n" @@ -533,6 +543,9 @@ static const char usage_message[] = " tests of certification. cmd should return 0 to allow\n" " TLS handshake to proceed, or 1 to fail. (cmd is\n" " executed as 'cmd certificate_depth X509_NAME_oneline')\n" + "--tls-export-cert [directory] : Get peer cert in PEM format and store it \n" + " in an openvpn temporary file in [directory]. Peer cert is \n" + " stored before tls-verify script execution and deleted after.\n" "--tls-remote x509name: Accept connections only from a host with X509 name\n" " x509name. The remote host must also pass all other tests\n" " of verification.\n" @@ -615,6 +628,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" "--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" @@ -753,6 +768,7 @@ init_options (struct options *o, const bool init_gc) o->renegotiate_seconds = 3600; o->handshake_window = 60; o->transition_window = 3600; + o->x509_username_field = X509_USERNAME_FIELD_DEFAULT; #endif #endif #ifdef ENABLE_PKCS11 @@ -1331,6 +1347,7 @@ show_settings (const struct options *o) #endif SHOW_STR (cipher_list); SHOW_STR (tls_verify); + SHOW_STR (tls_export_cert); SHOW_STR (tls_remote); SHOW_STR (crl_file); SHOW_INT (ns_cert_type); @@ -1464,19 +1481,24 @@ parse_http_proxy_fallback (struct context *c, const char *flags, const int msglevel) { - struct gc_arena gc = gc_new (); + struct gc_arena gc = gc_new (); + struct http_proxy_options *ret = NULL; struct http_proxy_options *hp = parse_http_proxy_override(server, port, flags, msglevel, &gc); - struct hpo_store *hpos = c->options.hpo_store; - if (!hpos) + if (hp) { - ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc); - c->options.hpo_store = hpos; + struct hpo_store *hpos = c->options.hpo_store; + if (!hpos) + { + ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc); + c->options.hpo_store = hpos; + } + hpos->hpo = *hp; + hpos->hpo.server = hpos->server; + strncpynt(hpos->server, hp->server, sizeof(hpos->server)); + ret = &hpos->hpo; } - hpos->hpo = *hp; - hpos->hpo.server = hpos->server; - strncpynt(hpos->server, hp->server, sizeof(hpos->server)); gc_free (&gc); - return &hpos->hpo; + return ret; } static void @@ -2054,6 +2076,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (pkcs12_file); MUST_BE_UNDEF (cipher_list); MUST_BE_UNDEF (tls_verify); + MUST_BE_UNDEF (tls_export_cert); MUST_BE_UNDEF (tls_remote); MUST_BE_UNDEF (tls_timeout); MUST_BE_UNDEF (renegotiate_bytes); @@ -2896,6 +2919,12 @@ usage_version (void) msg (M_INFO|M_NOPREFIX, "%s", title_string); 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>"); +#ifndef ENABLE_SMALL +#ifdef CONFIGURE_CALL + msg (M_INFO|M_NOPREFIX, "\n%s\n", CONFIGURE_CALL); +#endif + msg (M_INFO|M_NOPREFIX, "Compile time defines: %s", CONFIGURE_DEFINES); +#endif openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ } @@ -5300,7 +5329,7 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.dhcp_release = true; } - else if (streq (p[0], "dhcp-rr") && p[1]) /* standalone method for internal use */ + else if (streq (p[0], "dhcp-internal") && p[1]) /* standalone method for internal use */ { unsigned int adapter_index; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -5311,13 +5340,26 @@ add_option (struct options *options, dhcp_release_by_adapter_index (adapter_index); if (options->tuntap_options.dhcp_renew) dhcp_renew_by_adapter_index (adapter_index); - openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ + openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ + } + else if (streq (p[0], "register-dns")) + { + VERIFY_PERMISSION (OPT_P_IPWIN32); + options->tuntap_options.register_dns = true; + } + else if (streq (p[0], "rdns-internal")) /* standalone method for internal use */ + { + VERIFY_PERMISSION (OPT_P_GENERAL); + set_debug_level (options->verbosity, SDL_CONSTRAIN); + if (options->tuntap_options.register_dns) + ipconfig_register_dns (NULL); + openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } else if (streq (p[0], "show-valid-subnets")) { VERIFY_PERMISSION (OPT_P_GENERAL); show_valid_win32_tun_subnets (); - openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ + openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } else if (streq (p[0], "pause-exit")) { @@ -5728,6 +5770,11 @@ add_option (struct options *options, warn_multiple_script (options->tls_verify, "tls-verify"); options->tls_verify = string_substitute (p[1], ',', ' ', &options->gc); } + else if (streq (p[0], "tls-export-cert") && p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->tls_export_cert = p[1]; + } else if (streq (p[0], "tls-remote") && p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); @@ -5853,6 +5900,13 @@ add_option (struct options *options, } options->key_method = key_method; } + else if (streq (p[0], "x509-username-field") && p[1]) + { + char *s = p[1]; + VERIFY_PERMISSION (OPT_P_GENERAL); + while ((*s = toupper(*s)) != '\0') s++; /* Uppercase if necessary */ + options->x509_username_field = p[1]; + } #endif /* USE_SSL */ #endif /* USE_CRYPTO */ #ifdef ENABLE_PKCS11 @@ -465,6 +465,7 @@ struct options const char *pkcs12_file; const char *cipher_list; const char *tls_verify; + const char *tls_export_cert; const char *tls_remote; const char *crl_file; @@ -508,6 +509,9 @@ struct options within n seconds of handshake initiation. */ int handshake_window; + /* Field used to be the username in X509 cert. */ + char *x509_username_field; + /* Old key allowed to live n seconds after new key goes active */ int transition_window; @@ -620,8 +620,11 @@ establish_http_proxy_passthru (struct http_proxy_info *p, { msg (D_PROXY, "Proxy requires authentication"); - /* check for NTLM */ - if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) + if (p->auth_method == HTTP_AUTH_BASIC && !processed) + { + processed = true; + } + else if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) /* check for NTLM */ { #if NTLM /* look for the phase 2 response */ @@ -219,6 +219,7 @@ is_special_addr (const char *addr_str) static bool init_route (struct route *r, + struct resolve_list *network_list, const struct route_option *ro, const struct route_special_addr *spec) { @@ -237,14 +238,15 @@ init_route (struct route *r, if (!get_special_addr (spec, ro->network, &r->network, &status)) { - r->network = getaddr ( - GETADDR_RESOLVE - | GETADDR_HOST_ORDER - | GETADDR_WARN_ON_SIGNAL, - ro->network, - 0, - &status, - NULL); + r->network = getaddr_multi ( + GETADDR_RESOLVE + | GETADDR_HOST_ORDER + | GETADDR_WARN_ON_SIGNAL, + ro->network, + 0, + &status, + NULL, + network_list); } if (!status) @@ -438,20 +440,45 @@ init_route_list (struct route_list *rl, else rl->spec.remote_endpoint_defined = false; - if (!(opt->n >= 0 && opt->n <= rl->capacity)) - msg (M_FATAL, PACKAGE_NAME " ROUTE: (init) number of route options (%d) is greater than route list capacity (%d)", opt->n, rl->capacity); - /* parse the routes from opt to rl */ { int i, j = 0; + bool warned = false; for (i = 0; i < opt->n; ++i) { - if (!init_route (&rl->routes[j], + struct resolve_list netlist; + struct route r; + int k; + + if (!init_route (&r, + &netlist, &opt->routes[i], &rl->spec)) ret = false; else - ++j; + { + if (!netlist.len) + { + netlist.data[0] = r.network; + netlist.len = 1; + } + for (k = 0; k < netlist.len; ++k) + { + if (j < rl->capacity) + { + r.network = netlist.data[k]; + rl->routes[j++] = r; + } + else + { + if (!warned) + { + msg (M_WARN, PACKAGE_NAME " ROUTE: routes dropped because number of expanded routes is greater than route list capacity (%d)", rl->capacity); + warned = true; + } + } + } + } } rl->n = j; } @@ -89,12 +89,26 @@ getaddr (unsigned int flags, bool *succeeded, volatile int *signal_received) { + return getaddr_multi (flags, hostname, resolve_retry_seconds, succeeded, signal_received, NULL); +} + +in_addr_t +getaddr_multi (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received, + struct resolve_list *reslist) +{ struct in_addr ia; int status; int sigrec = 0; int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; struct gc_arena gc = gc_new (); + if (reslist) + reslist->len = 0; + if (flags & GETADDR_RANDOMIZE) hostname = hostname_randomize(hostname, &gc); @@ -212,11 +226,28 @@ getaddr (unsigned int flags, ++n; ASSERT (n >= 2); - msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d addresses, choosing the first resolved IP address", + msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d addresses", hostname, n); + /* choose address randomly, for basic load-balancing capability */ + /*ia.s_addr = *(in_addr_t *) (h->h_addr_list[get_random () % n]);*/ + + /* choose first address */ ia.s_addr = *(in_addr_t *) (h->h_addr_list[0]); + + if (reslist) + { + int i; + for (i = 0; i < n && i < SIZE(reslist->data); ++i) + { + in_addr_t a = *(in_addr_t *) (h->h_addr_list[i]); + if (flags & GETADDR_HOST_ORDER) + a = ntohl(a); + reslist->data[i] = a; + } + reslist->len = i; + } } } @@ -1664,7 +1695,7 @@ link_socket_connection_initiated (const struct buffer *buf, struct argv argv = argv_new (); setenv_str (es, "script_type", "ipchange"); ipchange_fmt (true, &argv, info, &gc); - openvpn_execve_check (&argv, es, S_SCRIPT, "ip-change command failed"); + openvpn_run_script (&argv, es, 0, "--ipchange"); argv_reset (&argv); } @@ -439,6 +439,11 @@ bool unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int * * DNS resolution */ +struct resolve_list { + int len; + in_addr_t data[16]; +}; + #define GETADDR_RESOLVE (1<<0) #define GETADDR_FATAL (1<<1) #define GETADDR_HOST_ORDER (1<<2) @@ -456,6 +461,13 @@ in_addr_t getaddr (unsigned int flags, bool *succeeded, volatile int *signal_received); +in_addr_t getaddr_multi (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received, + struct resolve_list *reslist); + /* * Transport protocol naming and other details. */ @@ -7,6 +7,10 @@ * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> * + * Additions for eurephia plugin done by: + * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009 + * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. @@ -687,6 +691,51 @@ string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsig string_mod (str, restrictive_flags, 0, '_'); } +/* Get peer cert and store it in pem format in a temporary file + * in tmp_dir + */ + +const char * +get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc) +{ + X509 *peercert; + FILE *peercert_file; + const char *peercert_filename=""; + + if(!tmp_dir) + return NULL; + + /* get peer cert */ + peercert = X509_STORE_CTX_get_current_cert(ctx); + if(!peercert) + { + msg (M_ERR, "Unable to get peer certificate from current context"); + return NULL; + } + + /* create tmp file to store peer cert */ + peercert_filename = create_temp_file (tmp_dir, "pcf", gc); + + /* write peer-cert in tmp-file */ + peercert_file = fopen(peercert_filename, "w+"); + if(!peercert_file) + { + msg (M_ERR, "Failed to open temporary file : %s", peercert_filename); + return NULL; + } + if(PEM_write_X509(peercert_file,peercert)<0) + { + msg (M_ERR, "Failed to write peer certificate in PEM format"); + fclose(peercert_file); + return NULL; + } + + fclose(peercert_file); + return peercert_filename; +} + +char * x509_username_field; /* GLOBAL */ + /* * Our verify callback function -- check * that an incoming peer certificate is good. @@ -697,7 +746,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) { char *subject = NULL; char envname[64]; - char common_name[TLS_CN_LEN]; + char common_name[TLS_USERNAME_LEN]; SSL *ssl; struct tls_session *session; const struct tls_options *opt; @@ -729,18 +778,20 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags); string_replace_leading (subject, '-', '_'); - /* extract the common name */ - if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), "CN", common_name, TLS_CN_LEN)) + /* extract the username (default is CN) */ + if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), x509_username_field, common_name, sizeof(common_name))) { if (!ctx->error_depth) - { - msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract Common Name from X509 subject string ('%s') -- note that the Common Name length is limited to %d characters", - subject, - TLS_CN_LEN); - goto err; - } + { + msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 subject string ('%s') -- note that the username length is limited to %d characters", + x509_username_field, + subject, + TLS_USERNAME_LEN); + goto err; + } } + string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash); @@ -780,6 +831,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth); setenv_str (opt->es, envname, subject); +#ifdef ENABLE_EUREPHIA + /* export X509 cert SHA1 fingerprint */ + { + struct gc_arena gc = gc_new (); + openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth); + setenv_str (opt->es, envname, + format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); + gc_free(&gc); + } +#endif #if 0 /* export common name string as environmental variable */ openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", ctx->error_depth); @@ -906,32 +967,48 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) /* run --tls-verify script */ if (opt->verify_command) { + const char *tmp_file; + struct gc_arena gc; int ret; setenv_str (opt->es, "script_type", "tls-verify"); + if (opt->verify_export_cert) + { + gc = gc_new(); + if (tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc)) + { + setenv_str(opt->es, "peer_cert", tmp_file); + } + } + argv_printf (&argv, "%sc %d %s", opt->verify_command, ctx->error_depth, subject); argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command"); - ret = openvpn_execve (&argv, opt->es, S_SCRIPT); + ret = openvpn_run_script (&argv, opt->es, 0, "--tls-verify script"); + + if (opt->verify_export_cert) + { + if (tmp_file) + delete_file(tmp_file); + gc_free(&gc); + } - if (system_ok (ret)) + if (ret) { msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s", ctx->error_depth, subject); } else { - if (!system_executed (ret)) - argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute"); msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s", ctx->error_depth, subject); goto err; /* Reject connection */ } } - + /* check peer cert against CRL */ if (opt->crl_file) { @@ -1749,7 +1826,8 @@ init_ssl (const struct options *options) } else #endif - SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + x509_username_field = (char *) options->x509_username_field; + SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); /* Connection information callback */ @@ -2288,6 +2366,7 @@ key_state_free (struct key_state *ks, bool clear) free_buf (&ks->plaintext_read_buf); free_buf (&ks->plaintext_write_buf); free_buf (&ks->ack_write_buf); + buffer_list_free(ks->paybuf); if (ks->send_reliable) { @@ -3086,6 +3165,17 @@ key_source2_read (struct key_source2 *k2, return 1; } +static void +flush_payload_buffer (struct tls_multi *multi, struct key_state *ks) +{ + struct buffer *b; + while ((b = buffer_list_peek (ks->paybuf))) + { + key_state_write_plaintext_const (multi, ks, b->data, b->len); + buffer_list_pop (ks->paybuf); + } +} + /* * Macros for key_state_soft_reset & tls_process */ @@ -3190,7 +3280,6 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up struct gc_arena gc = gc_new (); struct argv argv = argv_new (); const char *tmp_file = ""; - int retval; bool ret = false; /* Is username defined? */ @@ -3233,16 +3322,11 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up /* format command line */ argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file); - + /* call command */ - retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT); + ret = openvpn_run_script (&argv, session->opt->es, 0, + "--auth-user-pass-verify"); - /* test return status of command */ - if (system_ok (retval)) - ret = true; - else if (!system_executed (retval)) - argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute"); - if (!session->opt->auth_user_pass_verify_script_via_file) setenv_del (session->opt->es, "password"); } @@ -3688,9 +3772,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi s2 = verify_user_pass_script (session, up); /* check sizing of username if it will become our common name */ - if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_CN_LEN) + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_USERNAME_LEN) { - msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_CN_LEN); + msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN); s1 = OPENVPN_PLUGIN_FUNC_ERROR; } @@ -4006,6 +4090,9 @@ tls_process (struct tls_multi *multi, /* Set outgoing address for data channel packets */ link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es); + /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */ + flush_payload_buffer (multi, ks); + #ifdef MEASURE_TLS_HANDSHAKE_STATS show_tls_performance_stats(); #endif @@ -5105,6 +5192,13 @@ tls_send_payload (struct tls_multi *multi, if (key_state_write_plaintext_const (multi, ks, data, size) == 1) ret = true; } + else + { + if (!ks->paybuf) + ks->paybuf = buffer_list_new (0); + buffer_list_push_data (ks->paybuf, data, (size_t)size); + ret = true; + } ERR_clear_error (); @@ -278,8 +278,8 @@ * Buffer sizes (also see mtu.h). */ -/* Maximum length of common name */ -#define TLS_CN_LEN 64 +/* Maximum length of the username in cert */ +#define TLS_USERNAME_LEN 64 /* Legal characters in an X509 or common name */ #define X509_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_COLON|CC_SLASH|CC_EQUAL) @@ -288,6 +288,9 @@ /* Maximum length of OCC options string passed as part of auth handshake */ #define TLS_OPTIONS_LEN 512 +/* Default field in X509 to be username */ +#define X509_USERNAME_FIELD_DEFAULT "CN" + /* * Range of key exchange methods */ @@ -376,6 +379,8 @@ struct key_state struct reliable *rec_reliable; /* order incoming ciphertext packets before we pass to TLS */ struct reliable_ack *rec_ack; /* buffers all packet IDs we want to ACK back to sender */ + struct buffer_list *paybuf; + counter_type n_bytes; /* how many bytes sent/recvd since last key exchange */ counter_type n_packets; /* how many packets sent/recvd since last key exchange */ @@ -444,6 +449,7 @@ struct tls_options /* cert verification parms */ const char *verify_command; + const char *verify_export_cert; const char *verify_x509name; const char *crl_file; int ns_cert_type; @@ -535,11 +535,9 @@ socket_defined (const socket_descriptor_t sd) #endif /* - * Don't compile the struct buffer_list code unless something needs it + * Compile the struct buffer_list code */ -#if defined(ENABLE_MANAGEMENT) || defined(ENABLE_PF) #define ENABLE_BUFFER_LIST -#endif /* * Do we have pthread capability? diff --git a/tap-win32/proto.h b/tap-win32/proto.h index 0390b08..894a37f 100755 --- a/tap-win32/proto.h +++ b/tap-win32/proto.h @@ -29,9 +29,11 @@ #pragma pack(1) #define IP_HEADER_SIZE 20 +#define IPV6_HEADER_SIZE 40 typedef unsigned char MACADDR [6]; typedef unsigned long IPADDR; +typedef unsigned char IPV6ADDR [16]; //----------------- // Ethernet address @@ -55,6 +57,7 @@ typedef struct MACADDR src; /* source ether addr */ # define ETH_P_IP 0x0800 /* IPv4 protocol */ +# define ETH_P_IPV6 0x86DD /* IPv6 protocol */ # define ETH_P_ARP 0x0806 /* ARP protocol */ USHORT proto; /* packet type ID field */ } ETH_HEADER, *PETH_HEADER; @@ -161,4 +164,61 @@ typedef struct { #define TCPOPT_MAXSEG 2 #define TCPOLEN_MAXSEG 4 +//------------ +// IPv6 Header +//------------ + +typedef struct { + UCHAR version_prio; + UCHAR flow_lbl[3]; + USHORT payload_len; +# define IPPROTO_ICMPV6 0x3a /* ICMP protocol v6 */ + UCHAR nexthdr; + UCHAR hop_limit; + IPV6ADDR saddr; + IPV6ADDR daddr; +} IPV6HDR; + +//-------------------------------------------- +// IPCMPv6 NS/NA Packets (RFC4443 and RFC4861) +//-------------------------------------------- + +// Neighbor Solictiation - RFC 4861, 4.3 +// (this is just the ICMPv6 part of the packet) +typedef struct { + UCHAR type; +# define ICMPV6_TYPE_NS 135 // neighbour solicitation + UCHAR code; +# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA + USHORT checksum; + ULONG reserved; + IPV6ADDR target_addr; +} ICMPV6_NS; + +// Neighbor Advertisement - RFC 4861, 4.4 + 4.6/4.6.1 +// (this is just the ICMPv6 payload) +typedef struct { + UCHAR type; +# define ICMPV6_TYPE_NA 136 // neighbour advertisement + UCHAR code; +# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA + USHORT checksum; + UCHAR rso_bits; // Router(0), Solicited(2), Ovrrd(4) + UCHAR reserved[3]; + IPV6ADDR target_addr; +// always include "Target Link-layer Address" option (RFC 4861 4.6.1) + UCHAR opt_type; +#define ICMPV6_OPTION_TLLA 2 + UCHAR opt_length; +#define ICMPV6_LENGTH_TLLA 1 // multiplied by 8 -> 1 = 8 bytes + MACADDR target_macaddr; +} ICMPV6_NA; + +// this is the complete packet with Ethernet and IPv6 headers +typedef struct { + ETH_HEADER eth; + IPV6HDR ipv6; + ICMPV6_NA icmpv6; +} ICMPV6_NA_PKT; + #pragma pack() diff --git a/tap-win32/tapdrvr.c b/tap-win32/tapdrvr.c index 506f1f6..7ab3916 100755 --- a/tap-win32/tapdrvr.c +++ b/tap-win32/tapdrvr.c @@ -1430,6 +1430,158 @@ NDIS_STATUS AdapterModify return l_Status; } +// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum +// see RFC 4443, 2.3, and RFC 2460, 8.1 +USHORT +icmpv6_checksum (const UCHAR *buf, + const int len_icmpv6, + const UCHAR *saddr6, + const UCHAR *daddr6) +{ + USHORT word16; + ULONG sum = 0; + int i; + + // make 16 bit words out of every two adjacent 8 bit words and + // calculate the sum of all 16 bit words + for (i = 0; i < len_icmpv6; i += 2){ + word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0); + sum += word16; + } + + // add the IPv6 pseudo header which contains the IP source and destination addresses + for (i = 0; i < 16; i += 2){ + word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF); + sum += word16; + } + for (i = 0; i < 16; i += 2){ + word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF); + sum += word16; + } + + // the next-header number and the length of the ICMPv6 packet + sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6; + + // keep only the last 16 bits of the 32 bit calculated sum and add the carries + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Take the one's complement of sum + return ((USHORT) ~sum); +} + +// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that +// the tap driver needs to answer?" +// see RFC 4861 4.3 for the different cases +static IPV6ADDR IPV6_NS_TARGET_MCAST = + { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 }; +static IPV6ADDR IPV6_NS_TARGET_UNICAST = + { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; + +BOOLEAN +HandleIPv6NeighborDiscovery( TapAdapterPointer p_Adapter, UCHAR * m_Data ) +{ + const ETH_HEADER * e = (ETH_HEADER *) m_Data; + const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER)); + const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR)); + ICMPV6_NA_PKT *na; + USHORT icmpv6_len, icmpv6_csum; + + // we don't really care about the destination MAC address here + // - it's either a multicast MAC, or the userland destination MAC + // but since the TAP driver is point-to-point, all packets are "for us" + + // IPv6 target address must be ff02::1::ff00:8 (multicast for + // initial NS) or fe80::1 (unicast for recurrent NUD) + if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST, + sizeof(IPV6ADDR) ) != 0 && + memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ) != 0 ) + { + return FALSE; // wrong target address + } + + // IPv6 Next-Header must be ICMPv6 + if ( ipv6->nexthdr != IPPROTO_ICMPV6 ) + { + return FALSE; // wrong next-header + } + + // ICMPv6 type+code must be 135/0 for NS + if ( icmpv6_ns->type != ICMPV6_TYPE_NS || + icmpv6_ns->code != ICMPV6_CODE_0 ) + { + return FALSE; // wrong ICMPv6 type + } + + // ICMPv6 target address must be fe80::8 (magic) + if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ) != 0 ) + { + return FALSE; // not for us + } + + // packet identified, build magic response packet + + na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE); + if ( !na ) return FALSE; + + //------------------------------------------------ + // Initialize Neighbour Advertisement reply packet + //------------------------------------------------ + + // ethernet header + na->eth.proto = htons(ETH_P_IPV6); + COPY_MAC(na->eth.dest, p_Adapter->m_MAC); + COPY_MAC(na->eth.src, p_Adapter->m_TapToUser.dest); + + // IPv6 header + na->ipv6.version_prio = ipv6->version_prio; + NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl, + sizeof(na->ipv6.flow_lbl) ); + icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR); + na->ipv6.payload_len = htons(icmpv6_len); + na->ipv6.nexthdr = IPPROTO_ICMPV6; + na->ipv6.hop_limit = 255; + NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ); + NdisMoveMemory( na->ipv6.daddr, ipv6->saddr, + sizeof(IPV6ADDR) ); + + // ICMPv6 + na->icmpv6.type = ICMPV6_TYPE_NA; + na->icmpv6.code = ICMPV6_CODE_0; + na->icmpv6.checksum = 0; + na->icmpv6.rso_bits = 0x60; // Solicited + Override + NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) ); + NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST, + sizeof(IPV6ADDR) ); + + // ICMPv6 option "Target Link Layer Address" + na->icmpv6.opt_type = ICMPV6_OPTION_TLLA; + na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA; + COPY_MAC( na->icmpv6.target_macaddr, p_Adapter->m_TapToUser.dest ); + + // calculate and set checksum + icmpv6_csum = icmpv6_checksum ( (UCHAR*) &(na->icmpv6), + icmpv6_len, + na->ipv6.saddr, + na->ipv6.daddr ); + na->icmpv6.checksum = htons( icmpv6_csum ); + + DUMP_PACKET ("HandleIPv6NeighborDiscovery", + (unsigned char *) na, + sizeof (ICMPV6_NA_PKT)); + + InjectPacketDeferred (p_Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT)); + + MemFree (na, sizeof (ICMPV6_NA_PKT)); + + return TRUE; // all fine +} + //==================================================================== // Adapter Transmission //==================================================================== @@ -1566,7 +1718,10 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, //=============================================== // In Point-To-Point mode, check to see whether - // packet is ARP or IPv4 (if neither, then drop). + // packet is ARP (handled) or IPv4 (sent to app). + // IPv6 packets are inspected for neighbour discovery + // (to be handled locally), and the rest is forwarded + // all other protocols are dropped //=============================================== if (l_Adapter->m_tun) { @@ -1611,6 +1766,27 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, // Packet looks like IPv4, queue it. l_PacketBuffer->m_SizeFlags |= TP_TUN; + + case ETH_P_IPV6: + // make sure that packet is large + // enough to be IPv6 + if (l_PacketLength + < ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE) + goto no_queue; + + // broadcasts and multicasts are handled specially + // (to be implemented) + + // neighbor discovery packets to fe80::8 are special + // OpenVPN sets this next-hop to signal "handled by tapdrv" + if ( HandleIPv6NeighborDiscovery( l_Adapter, + l_PacketBuffer->m_Data )) + { + goto no_queue; + } + + // Packet looks like IPv6, queue it :-) + l_PacketBuffer->m_SizeFlags |= TP_TUN; } } @@ -1902,6 +2078,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap; + l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6); l_Adapter->m_tun = TRUE; @@ -1939,6 +2117,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap; + l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6); l_Adapter->m_tun = TRUE; @@ -2236,10 +2416,18 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) { __try { + ETH_HEADER * p_UserToTap = &l_Adapter->m_UserToTap; + + // for IPv6, need to use ethernet header with IPv6 proto + if ( IPH_GET_VER( ((IPHDR*) p_IRP->AssociatedIrp.SystemBuffer)->version_len) == 6 ) + { + p_UserToTap = &l_Adapter->m_UserToTap_IPv6; + } + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; DUMP_PACKET2 ("IRP_MJ_WRITE P2P", - &l_Adapter->m_UserToTap, + p_UserToTap, (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length); @@ -2258,8 +2446,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) NdisMEthIndicateReceive (l_Adapter->m_MiniportAdapterHandle, (NDIS_HANDLE) l_Adapter, - (unsigned char *) &l_Adapter->m_UserToTap, - sizeof (l_Adapter->m_UserToTap), + (unsigned char *) p_UserToTap, + sizeof (ETH_HEADER), (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length, l_IrpSp->Parameters.Write.Length); @@ -2820,6 +3008,7 @@ VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) p_Adapter->m_remoteNetmask = 0; NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser)); NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap)); + NdisZeroMemory (&p_Adapter->m_UserToTap_IPv6, sizeof (p_Adapter->m_UserToTap_IPv6)); // DHCP Masq p_Adapter->m_dhcp_enabled = FALSE; diff --git a/tap-win32/types.h b/tap-win32/types.h index 9406252..bdc08e7 100755 --- a/tap-win32/types.h +++ b/tap-win32/types.h @@ -143,6 +143,7 @@ typedef struct _TapAdapter IPADDR m_remoteNetmask; ETH_HEADER m_TapToUser; ETH_HEADER m_UserToTap; + ETH_HEADER m_UserToTap_IPv6; // same as UserToTap but proto=ipv6 MACADDR m_MAC_Broadcast; // Used for DHCP server masquerade @@ -3372,6 +3372,48 @@ netsh_command (const struct argv *a, int n) } void +ipconfig_register_dns (const struct env_set *es) +{ + struct argv argv; + bool status; + const char err[] = "ERROR: Windows ipconfig command failed"; + + netcmd_semaphore_lock (); + + argv_init (&argv); + + 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); + argv_msg (D_TUNTAP_INFO, &argv); + status = openvpn_execve_check (&argv, es, 0, err); + argv_reset(&argv); + + argv_printf (&argv, "%s%sc /registerdns", + get_win_sys_path(), + WIN_IPCONFIG_PATH_SUFFIX); + argv_msg (D_TUNTAP_INFO, &argv); + status = openvpn_execve_check (&argv, es, 0, err); + argv_reset(&argv); + + netcmd_semaphore_release (); +} + +void ip_addr_string_to_array (in_addr_t *dest, int *dest_len, const IP_ADDR_STRING *src) { int i = 0; @@ -3806,8 +3848,23 @@ fork_dhcp_action (struct tuntap *tt) buf_printf (&cmd, " --dhcp-pre-release"); if (tt->options.dhcp_renew) buf_printf (&cmd, " --dhcp-renew"); - buf_printf (&cmd, " --dhcp-rr %u", (unsigned int)tt->adapter_index); + buf_printf (&cmd, " --dhcp-internal %u", (unsigned int)tt->adapter_index); + + fork_to_self (BSTR (&cmd)); + gc_free (&gc); + } +} +void +fork_register_dns_action (struct tuntap *tt) +{ + if (tt && tt->options.register_dns) + { + struct gc_arena gc = gc_new (); + struct buffer cmd = alloc_buf_gc (256, &gc); + const int verb = 3; + + buf_printf (&cmd, "openvpn --verb %d --register-dns --rdns-internal", verb); fork_to_self (BSTR (&cmd)); gc_free (&gc); } @@ -99,6 +99,8 @@ struct tuntap_options { bool dhcp_renew; bool dhcp_pre_release; bool dhcp_release; + + bool register_dns; }; #elif TARGET_LINUX @@ -335,6 +337,9 @@ void tun_show_debug (struct tuntap *tt); bool dhcp_release_by_adapter_index(const DWORD adapter_index); bool dhcp_renew_by_adapter_index (const DWORD adapter_index); +void fork_register_dns_action (struct tuntap *tt); +void ipconfig_register_dns (const struct env_set *es); + void tun_standby_init (struct tuntap *tt); bool tun_standby (struct tuntap *tt); @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1.1i]) +define(PRODUCT_VERSION,[2.1.1o]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) diff --git a/win/build_exe.py b/win/build_exe.py new file mode 100644 index 0000000..087f9a3 --- /dev/null +++ b/win/build_exe.py @@ -0,0 +1,15 @@ +from config import main as config_main
+from build import main as build_openvpn
+from build_ddk import main as build_ddk
+from sign import main as sign
+from make_dist import main as make_dist
+
+def main(config):
+ config_main(config)
+ build_openvpn()
+ make_dist(config, tap=False)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/config.py b/win/config.py index 67c1d82..cf38cac 100644 --- a/win/config.py +++ b/win/config.py @@ -9,6 +9,7 @@ def main(config): out_fn=home_fn('msvc.mak'),
quote_begin='@',
quote_end='@',
+ if_prefix='!',
head_comment='# %s\n\n' % autogen)
# if we are run directly, and not loaded as a module
diff --git a/win/make_dist.py b/win/make_dist.py index 3011279..a6a0563 100644 --- a/win/make_dist.py +++ b/win/make_dist.py @@ -1,7 +1,7 @@ import os
from wb import home_fn, rm_rf, mkdir, cp_a, cp
-def main(config):
+def main(config, tap=True):
dist = config['DIST']
assert dist
dist = home_fn(dist)
@@ -13,8 +13,9 @@ def main(config): rm_rf(dist)
mkdir(dist)
mkdir(bin)
- mkdir(i386)
- mkdir(amd64)
+ if tap:
+ mkdir(i386)
+ mkdir(amd64)
# copy openvpn.exe and manifest
cp(home_fn('openvpn.exe'), bin)
@@ -28,25 +29,26 @@ def main(config): # copy MSVC CRT
cp_a(home_fn(config['MSVC_CRT']), bin)
- # copy TAP drivers
- for dir_name, dest in (('amd64', amd64), ('i386', i386)):
- dir = home_fn(os.path.join('tap-win32', dir_name))
- for dirpath, dirnames, filenames in os.walk(dir):
+ if tap:
+ # copy TAP drivers
+ for dir_name, dest in (('amd64', amd64), ('i386', i386)):
+ dir = home_fn(os.path.join('tap-win32', dir_name))
+ for dirpath, dirnames, filenames in os.walk(dir):
+ for f in filenames:
+ root, ext = os.path.splitext(f)
+ if ext in ('.inf', '.cat', '.sys'):
+ cp(os.path.join(dir, f), dest)
+ break
+
+ # copy tapinstall
+ dest = {'amd64' : amd64, 'i386' : i386}
+ for dirpath, dirnames, filenames in os.walk(home_fn('tapinstall')):
for f in filenames:
- root, ext = os.path.splitext(f)
- if ext in ('.inf', '.cat', '.sys'):
- cp(os.path.join(dir, f), dest)
- break
-
- # copy tapinstall
- dest = {'amd64' : amd64, 'i386' : i386}
- for dirpath, dirnames, filenames in os.walk(home_fn('tapinstall')):
- for f in filenames:
- if f == 'tapinstall.exe':
- dir_name = os.path.basename(dirpath)
- src = os.path.join(dirpath, f)
- if dir_name in dest:
- cp(src, dest[dir_name])
+ if f == 'tapinstall.exe':
+ dir_name = os.path.basename(dirpath)
+ src = os.path.join(dirpath, f)
+ if dir_name in dest:
+ cp(src, dest[dir_name])
# if we are run directly, and not loaded as a module
if __name__ == "__main__":
diff --git a/win/msvc.mak.in b/win/msvc.mak.in index 3f8102a..5d94a6e 100644 --- a/win/msvc.mak.in +++ b/win/msvc.mak.in @@ -23,17 +23,22 @@ LIB_DIRS = -LIBPATH:$(OPENSSL)\lib -LIBPATH:$(LZO)\lib EXE = openvpn.exe
CPP=cl.exe
-CPP_ARG_COMMON=/nologo /W3 /O2 -DWIN32 -DWIN32_LEAN_AND_MEAN -D_CONSOLE -D_MBCS -D_CRT_SECURE_NO_DEPRECATE $(INCLUDE_DIRS) /FD /c
-# release:
-CPP_PROJ=$(CPP_ARG_COMMON) /MD -DNDEBUG
-# debug:
-#CPP_PROJ=$(CPP_ARG_COMMON) /MDd /Zi /Od -D_DEBUG
+CPP_ARG_COMMON=/nologo /W3 -DWIN32 -DWIN32_LEAN_AND_MEAN -D_CONSOLE -D_MBCS -D_CRT_SECURE_NO_DEPRECATE $(INCLUDE_DIRS) /FD /c
LINK32=link.exe
-# release:
-LINK32_FLAGS=/nologo /subsystem:console /incremental:no /out:"$(EXE)"
+
+!ifdef PRODUCT_OPENVPN_DEBUG
# debug:
+CPP_PROJ=$(CPP_ARG_COMMON) /MD /Z7
+LINK32_FLAGS=/nologo /subsystem:console /incremental:no /opt:ref /opt:icf /debug /out:"$(EXE)"
+# old debug:
+#CPP_PROJ=$(CPP_ARG_COMMON) /MDd /Zi /Od -D_DEBUG
#LINK32_FLAGS=/nologo /subsystem:console /incremental:no /debug /out:"$(EXE)"
+!else
+# release:
+CPP_PROJ=$(CPP_ARG_COMMON) /O2 /MD -DNDEBUG
+LINK32_FLAGS=/nologo /subsystem:console /incremental:no /out:"$(EXE)"
+!endif
# HEADERS and OBJS definitions, automatically generated
@HEADERS_OBJS@
diff --git a/win/settings.in b/win/settings.in index 044834a..0e1bb1c 100644 --- a/win/settings.in +++ b/win/settings.in @@ -40,6 +40,9 @@ # Build debugging version of TAP driver ;!define PRODUCT_TAP_DEBUG +# Build debugging version of openvpn.exe +;!define PRODUCT_OPENVPN_DEBUG + # DDK path -- currently Windows 7 WDK !define DDK_PATH "c:/winddk/7600.16385.1" ;!define DDK_PATH "c:/winddk/6001.18002" @@ -952,6 +952,8 @@ int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags) { int ret = -1; + static bool exec_warn = false; + if (a && a->argv[0]) { if (openvpn_execve_allowed (flags)) @@ -971,10 +973,8 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i /* fill in STARTUPINFO struct */ GetStartupInfo(&start_info); start_info.cb = sizeof(start_info); - start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + start_info.dwFlags = STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; - start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE); if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info)) { @@ -1004,9 +1004,10 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i ASSERT (0); } } - else + else if (!exec_warn && (script_security < SSEC_SCRIPTS)) { msg (M_WARN, SCRIPT_SECURITY_WARNING); + exec_warn = true; } } else @@ -1042,10 +1043,8 @@ fork_to_self (const char *cmdline) /* fill in STARTUPINFO struct */ GetStartupInfo(&start_info); start_info.cb = sizeof(start_info); - start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + start_info.dwFlags = STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; - start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE); if (CreateProcess (self_exe, cl, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info)) { @@ -33,6 +33,8 @@ #define DEFAULT_WIN_SYS_PATH "C:\\WINDOWS" /* --win-sys default value */ #define NETSH_PATH_SUFFIX "\\system32\\netsh.exe" #define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe" +#define WIN_IPCONFIG_PATH_SUFFIX "\\system32\\ipconfig.exe" +#define WIN_NET_PATH_SUFFIX "\\system32\\net.exe" /* * Win32-specific OpenVPN code, targetted at the mingw |