diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-01-20 12:01:07 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-01-20 12:01:07 +0100 |
commit | 19eab1fe2df20e38ea64d7a642d3e21c957082b8 (patch) | |
tree | fad602b603321d3e0e45807f8abb4f6c670a6cf2 /src/openvpn | |
parent | 35807e5652390e8b2fd66b9051f3b41c9488302e (diff) | |
parent | 9653b1bffea4e96c1eb7c1814e8bed21fea62321 (diff) |
Merge tag 'upstream/2.3.10'
Upstream version 2.3.10
Diffstat (limited to 'src/openvpn')
45 files changed, 1454 insertions, 469 deletions
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 2e602f1..6d02fea 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -110,7 +110,7 @@ openvpn_SOURCES = \ status.c status.h \ syshead.h \ tun.c tun.h \ - win32.h win32.c \ + win32.h win32_wfp.h win32.c \ cryptoapi.h cryptoapi.c openvpn_LDADD = \ $(top_builddir)/src/compat/libcompat.la \ @@ -123,5 +123,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lrpcrt4 -lwinmm endif diff --git a/src/openvpn/Makefile.in b/src/openvpn/Makefile.in index ea9547c..8519969 100644 --- a/src/openvpn/Makefile.in +++ b/src/openvpn/Makefile.in @@ -106,7 +106,7 @@ DIST_COMMON = $(top_srcdir)/build/ltrc.inc $(srcdir)/Makefile.in \ @WIN32_TRUE@am__append_1 = -municode -UUNICODE sbin_PROGRAMS = openvpn$(EXEEXT) @WIN32_TRUE@am__append_2 = openvpn_win32_resources.rc -@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm +@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lrpcrt4 -lwinmm subdir = src/openvpn ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ @@ -149,8 +149,8 @@ am__openvpn_SOURCES_DIST = base64.c base64.h basic.h buffer.c buffer.h \ ssl_verify.c ssl_verify.h ssl_verify_backend.h \ ssl_verify_openssl.c ssl_verify_openssl.h \ ssl_verify_polarssl.c ssl_verify_polarssl.h status.c status.h \ - syshead.h tun.c tun.h win32.h win32.c cryptoapi.h cryptoapi.c \ - openvpn_win32_resources.rc + syshead.h tun.c tun.h win32.h win32_wfp.h win32.c cryptoapi.h \ + cryptoapi.c openvpn_win32_resources.rc @WIN32_TRUE@am__objects_1 = openvpn_win32_resources.$(OBJEXT) am_openvpn_OBJECTS = base64.$(OBJEXT) buffer.$(OBJEXT) \ clinat.$(OBJEXT) crypto.$(OBJEXT) crypto_openssl.$(OBJEXT) \ @@ -452,8 +452,8 @@ openvpn_SOURCES = base64.c base64.h basic.h buffer.c buffer.h \ ssl_verify.c ssl_verify.h ssl_verify_backend.h \ ssl_verify_openssl.c ssl_verify_openssl.h \ ssl_verify_polarssl.c ssl_verify_polarssl.h status.c status.h \ - syshead.h tun.c tun.h win32.h win32.c cryptoapi.h cryptoapi.c \ - $(am__append_2) + syshead.h tun.c tun.h win32.h win32_wfp.h win32.c cryptoapi.h \ + cryptoapi.c $(am__append_2) openvpn_LDADD = $(top_builddir)/src/compat/libcompat.la \ $(SOCKETS_LIBS) $(OPTIONAL_LZO_LIBS) \ $(OPTIONAL_PKCS11_HELPER_LIBS) $(OPTIONAL_CRYPTO_LIBS) \ diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h index d306a04..58f0601 100644 --- a/src/openvpn/buffer.h +++ b/src/openvpn/buffer.h @@ -873,7 +873,7 @@ gc_reset (struct gc_arena *a) } static inline void -check_malloc_return (void *p) +check_malloc_return (const void *p) { if (!p) out_of_memory (); diff --git a/src/openvpn/console.c b/src/openvpn/console.c index d66d408..e1d46c4 100644 --- a/src/openvpn/console.c +++ b/src/openvpn/console.c @@ -208,6 +208,19 @@ get_console_input (const char *prompt, const bool echo, char *input, const int c #if defined(WIN32) return get_console_input_win32 (prompt, echo, input, capacity); #elif defined(HAVE_GETPASS) + + /* did we --daemon'ize before asking for passwords? + * (in which case neither stdin or stderr are connected to a tty and + * /dev/tty can not be open()ed anymore) + */ + if ( !isatty(0) && !isatty(2) ) + { + int fd = open( "/dev/tty", O_RDWR ); + if ( fd < 0 ) + { msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a controlling tty nor systemd - can't ask for '%s'. If you used --daemon, you need to use --askpass to make passphrase-protected keys work, and you can not use --auth-nocache.", prompt ); } + close(fd); + } + if (echo) { FILE *fp; diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index c2d5c27..400bf11 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -1383,7 +1383,7 @@ prng_bytes (uint8_t *output, int len) } } else - rand_bytes (output, len); + ASSERT (rand_bytes (output, len)); } /* an analogue to the random() function, but use prng_bytes */ diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index 348bdee..4e195ce 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -378,7 +378,12 @@ show_available_engines () int rand_bytes(uint8_t *output, int len) { - return RAND_bytes (output, len); + if (unlikely(1 != RAND_bytes (output, len))) + { + msg(D_CRYPT_ERRORS, "RAND_bytes() failed"); + return 0; + } + return 1; } /* diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c index af79029..24712ed 100644 --- a/src/openvpn/crypto_polarssl.c +++ b/src/openvpn/crypto_polarssl.c @@ -457,7 +457,7 @@ cipher_ctx_init (cipher_context_t *ctx, uint8_t *key, int key_len, void cipher_ctx_cleanup (cipher_context_t *ctx) { - cipher_free_ctx(ctx); + cipher_free(ctx); } int cipher_ctx_iv_length (const cipher_context_t *ctx) @@ -487,7 +487,12 @@ cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf) { - return 0 == cipher_reset(ctx, iv_buf); + int retval = cipher_reset(ctx); + + if (0 == retval) + retval = cipher_set_iv(ctx, iv_buf, ctx->cipher_info->iv_size); + + return 0 == retval; } int cipher_ctx_update (cipher_context_t *ctx, uint8_t *dst, int *dst_len, @@ -614,7 +619,7 @@ void md_ctx_final (md_context_t *ctx, uint8_t *dst) { ASSERT(0 == md_finish(ctx, dst)); - ASSERT(0 == md_free_ctx(ctx)); + md_free(ctx); } @@ -645,7 +650,7 @@ hmac_ctx_init (md_context_t *ctx, const uint8_t *key, int key_len, const md_info void hmac_ctx_cleanup(md_context_t *ctx) { - ASSERT(0 == md_free_ctx(ctx)); + md_free(ctx); } int diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index b7fc11e..853c07b 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -46,6 +46,8 @@ #include <ctype.h> #include <assert.h> +#include "buffer.h" + /* MinGW w32api 3.17 is still incomplete when it comes to CryptoAPI while * MinGW32-w64 defines all macros used. This is a hack around that problem. */ @@ -116,7 +118,7 @@ static char *ms_error_text(DWORD ms_err) (LPTSTR) &lpMsgBuf, 0, NULL); if (lpMsgBuf) { char *p; - rv = strdup(lpMsgBuf); + rv = string_alloc(lpMsgBuf, NULL); LocalFree(lpMsgBuf); /* trim to the left */ if (rv) @@ -162,6 +164,7 @@ static void err_put_ms_error(DWORD ms_err, int func, const char *file, int line) err_map[i].ms_err = ms_err; err_map[i].err = esd->error = i + 100; esd->string = ms_error_text(ms_err); + check_malloc_return(esd->string); ERR_load_strings(ERR_LIB_CRYPTOAPI, esd); ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line); break; diff --git a/src/openvpn/error.c b/src/openvpn/error.c index 6848425..f503cf4 100644 --- a/src/openvpn/error.c +++ b/src/openvpn/error.c @@ -397,9 +397,13 @@ dont_mute (unsigned int flags) } void -assert_failed (const char *filename, int line) +assert_failed (const char *filename, int line, const char *condition) { - msg (M_FATAL, "Assertion failed at %s:%d", filename, line); + if (condition) + msg (M_FATAL, "Assertion failed at %s:%d (%s)", filename, line, condition); + else + msg (M_FATAL, "Assertion failed at %s:%d", filename, line); + _exit(1); } /* diff --git a/src/openvpn/error.h b/src/openvpn/error.h index 27c48b6..42308e8 100644 --- a/src/openvpn/error.h +++ b/src/openvpn/error.h @@ -144,19 +144,22 @@ bool dont_mute (unsigned int flags); /* check muting filter */ #define MSG_TEST(flags) (unlikely((((unsigned int)flags) & M_DEBUG_LEVEL) <= x_debug_level) && dont_mute (flags)) +/* Macro to ensure (and teach static analysis tools) we exit on fatal errors */ +#define EXIT_FATAL(flags) do { if ((flags) & M_FATAL) _exit(1); } while (false) + #if defined(HAVE_CPP_VARARG_MACRO_ISO) && !defined(__LCLINT__) # define HAVE_VARARG_MACROS -# define msg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); } while (false) +# define msg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); EXIT_FATAL(flags); } while (false) # ifdef ENABLE_DEBUG -# define dmsg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); } while (false) +# define dmsg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); EXIT_FATAL(flags); } while (false) # else # define dmsg(flags, ...) # endif #elif defined(HAVE_CPP_VARARG_MACRO_GCC) && !defined(__LCLINT__) # define HAVE_VARARG_MACROS -# define msg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); } while (false) +# define msg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); EXIT_FATAL(flags); } while (false) # ifdef ENABLE_DEBUG -# define dmsg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); } while (false) +# define dmsg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); EXIT_FATAL(flags); } while (false) # else # define dmsg(flags, args...) # endif @@ -211,9 +214,14 @@ const char *msg_flags_string (const unsigned int flags, struct gc_arena *gc); FILE *msg_fp(const unsigned int flags); /* Fatal logic errors */ -#define ASSERT(x) do { if (!(x)) assert_failed(__FILE__, __LINE__); } while (false) +#ifndef ENABLE_SMALL +#define ASSERT(x) do { if (!(x)) assert_failed(__FILE__, __LINE__, #x); } while (false) +#else +#define ASSERT(x) do { if (!(x)) assert_failed(__FILE__, __LINE__, NULL); } while (false) +#endif -void assert_failed (const char *filename, int line); +void assert_failed (const char *filename, int line, const char *condition) + __attribute__((__noreturn__)); #ifdef ENABLE_DEBUG void crash (void); /* force a segfault (debugging only) */ diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 217fbb3..d55fa3b 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -956,8 +956,9 @@ read_incoming_tun (struct context *c) /* Was TUN/TAP I/O operation aborted? */ if (tuntap_abort(c->c2.buf.len)) { - register_signal(c, SIGTERM, "tun-abort"); - msg(M_FATAL, "TUN/TAP I/O operation aborted, exiting"); + register_signal(c, SIGHUP, "tun-abort"); + c->persist.restart_sleep_seconds = 10; + msg(M_INFO, "TUN/TAP I/O operation aborted, restarting"); perf_pop(); return; } diff --git a/src/openvpn/helper.c b/src/openvpn/helper.c index 339e2ae..62f88ec 100644 --- a/src/openvpn/helper.c +++ b/src/openvpn/helper.c @@ -167,7 +167,7 @@ helper_client_server (struct options *o) * push "tun-ipv6" * ifconfig-ipv6 2001:db8::1 2001:db8::2 * if !nopool: - * ifconfig-ipv6-pool 2001:db8::1:0/64 + * ifconfig-ipv6-pool 2001:db8::1000/64 * */ if ( o->server_ipv6_defined ) diff --git a/src/openvpn/httpdigest.c b/src/openvpn/httpdigest.c index 78b8344..99bbda4 100644 --- a/src/openvpn/httpdigest.c +++ b/src/openvpn/httpdigest.c @@ -76,20 +76,20 @@ DigestCalcHA1( const md_kt_t *md5_kt = md_kt_get("MD5"); md_ctx_init(&md5_ctx, md5_kt); - md_ctx_update(&md5_ctx, pszUserName, strlen(pszUserName)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszRealm, strlen(pszRealm)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszPassword, strlen(pszPassword)); + md_ctx_update(&md5_ctx, (const uint8_t *) pszUserName, strlen(pszUserName)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszRealm, strlen(pszRealm)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszPassword, strlen(pszPassword)); md_ctx_final(&md5_ctx, HA1); if (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0) { md_ctx_init(&md5_ctx, md5_kt); md_ctx_update(&md5_ctx, HA1, HASHLEN); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszNonce, strlen(pszNonce)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszCNonce, strlen(pszCNonce)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszNonce, strlen(pszNonce)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszCNonce, strlen(pszCNonce)); md_ctx_final(&md5_ctx, HA1); }; md_ctx_cleanup(&md5_ctx); @@ -119,12 +119,12 @@ DigestCalcResponse( /* calculate H(A2) */ md_ctx_init(&md5_ctx, md5_kt); - md_ctx_update(&md5_ctx, pszMethod, strlen(pszMethod)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszDigestUri, strlen(pszDigestUri)); + md_ctx_update(&md5_ctx, (const uint8_t *) pszMethod, strlen(pszMethod)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszDigestUri, strlen(pszDigestUri)); if (strcasecmp(pszQop, "auth-int") == 0) { - md_ctx_update(&md5_ctx, ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); md_ctx_update(&md5_ctx, HEntity, HASHHEXLEN); }; md_ctx_final(&md5_ctx, HA2); @@ -133,17 +133,17 @@ DigestCalcResponse( /* calculate response */ md_ctx_init(&md5_ctx, md5_kt); md_ctx_update(&md5_ctx, HA1, HASHHEXLEN); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszNonce, strlen(pszNonce)); - md_ctx_update(&md5_ctx, ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszNonce, strlen(pszNonce)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); if (*pszQop) { - md_ctx_update(&md5_ctx, pszNonceCount, strlen(pszNonceCount)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszCNonce, strlen(pszCNonce)); - md_ctx_update(&md5_ctx, ":", 1); - md_ctx_update(&md5_ctx, pszQop, strlen(pszQop)); - md_ctx_update(&md5_ctx, ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszNonceCount, strlen(pszNonceCount)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszCNonce, strlen(pszCNonce)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); + md_ctx_update(&md5_ctx, (const uint8_t *) pszQop, strlen(pszQop)); + md_ctx_update(&md5_ctx, (const uint8_t *) ":", 1); }; md_ctx_update(&md5_ctx, HA2Hex, HASHHEXLEN); md_ctx_final(&md5_ctx, RespHash); diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 71c91a2..2148777 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -840,7 +840,7 @@ void init_options_dev (struct options *options) { if (!options->dev && options->dev_node) { - char *dev_node = strdup(options->dev_node); /* POSIX basename() implementaions may modify its arguments */ + char *dev_node = string_alloc(options->dev_node, NULL); /* POSIX basename() implementaions may modify its arguments */ options->dev = basename (dev_node); } } @@ -973,31 +973,30 @@ do_uid_gid_chroot (struct context *c, bool no_delay) static const char why_not[] = "will be delayed because of --client, --pull, or --up-delay"; struct context_0 *c0 = c->c0; - if (c->first_time && c0 && !c0->uid_gid_set) + if (c0 && !c0->uid_gid_chroot_set) { /* chroot if requested */ if (c->options.chroot_dir) { if (no_delay) platform_chroot (c->options.chroot_dir); - else + else if (c->first_time) msg (M_INFO, "NOTE: chroot %s", why_not); } - /* set user and/or group that we want to setuid/setgid to */ - if (no_delay) - { - platform_group_set (&c0->platform_state_group); - platform_user_set (&c0->platform_state_user); - c0->uid_gid_set = true; - } - else if (c0->uid_gid_specified) + /* set user and/or group if we want to setuid/setgid */ + if (c0->uid_gid_specified) { - msg (M_INFO, "NOTE: UID/GID downgrade %s", why_not); + if (no_delay) { + platform_group_set (&c0->platform_state_group); + platform_user_set (&c0->platform_state_user); + } + else if (c->first_time) + msg (M_INFO, "NOTE: UID/GID downgrade %s", why_not); } #ifdef ENABLE_MEMSTATS - if (c->options.memstats_fn) + if (c->first_time && c->options.memstats_fn) mstats_open(c->options.memstats_fn); #endif @@ -1016,10 +1015,16 @@ do_uid_gid_chroot (struct context *c, bool no_delay) else msg (M_INFO, "setcon to '%s' succeeded", c->options.selinux_context); } - else + else if (c->first_time) msg (M_INFO, "NOTE: setcon %s", why_not); } #endif + + /* Privileges are going to be dropped by now (if requested), be sure + * to prevent any future privilege dropping attempts from now on. + */ + if (no_delay) + c0->uid_gid_chroot_set = true; } } @@ -1453,6 +1458,9 @@ do_open_tun (struct context *c) c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, +#ifdef WIN32 + c->c1.tuntap->adapter_index, +#endif dev_type_string (c->options.dev, c->options.dev_type), TUN_MTU_SIZE (&c->c2.frame), EXPANDED_SIZE (&c->c2.frame), @@ -1463,6 +1471,15 @@ do_open_tun (struct context *c) "up", c->c2.es); +#ifdef WIN32 + if (c->options.block_outside_dns) + { + dmsg (D_LOW, "Blocking outside DNS"); + if (!win_wfp_block_dns(c->c1.tuntap->adapter_index)) + msg (M_FATAL, "Blocking DNS failed!"); + } +#endif + /* possibly add routes */ if (!c->options.route_delay_defined) do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list, @@ -1493,6 +1510,9 @@ do_open_tun (struct context *c) c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, +#ifdef WIN32 + c->c1.tuntap->adapter_index, +#endif dev_type_string (c->options.dev, c->options.dev_type), TUN_MTU_SIZE (&c->c2.frame), EXPANDED_SIZE (&c->c2.frame), @@ -1530,6 +1550,9 @@ do_close_tun (struct context *c, bool force) if (c->c1.tuntap && c->c1.tuntap_owned) { const char *tuntap_actual = string_alloc (c->c1.tuntap->actual_name, &gc); +#ifdef WIN32 + DWORD adapter_index = c->c1.tuntap->adapter_index; +#endif const in_addr_t local = c->c1.tuntap->local; const in_addr_t remote_netmask = c->c1.tuntap->remote_netmask; @@ -1553,6 +1576,9 @@ do_close_tun (struct context *c, bool force) c->plugins, OPENVPN_PLUGIN_ROUTE_PREDOWN, tuntap_actual, +#ifdef WIN32 + adapter_index, +#endif NULL, TUN_MTU_SIZE (&c->c2.frame), EXPANDED_SIZE (&c->c2.frame), @@ -1578,6 +1604,9 @@ do_close_tun (struct context *c, bool force) c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, +#ifdef WIN32 + adapter_index, +#endif NULL, TUN_MTU_SIZE (&c->c2.frame), EXPANDED_SIZE (&c->c2.frame), @@ -1589,6 +1618,14 @@ do_close_tun (struct context *c, bool force) "down", c->c2.es); +#ifdef WIN32 + if (c->options.block_outside_dns) + { + if (!win_wfp_uninit()) + msg (M_FATAL, "Uninitialising WFP failed!"); + } +#endif + /* actually close tun/tap device based on --down-pre flag */ if (c->options.down_pre) do_close_tun_simple (c); @@ -1601,6 +1638,9 @@ do_close_tun (struct context *c, bool force) c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, +#ifdef WIN32 + adapter_index, +#endif NULL, TUN_MTU_SIZE (&c->c2.frame), EXPANDED_SIZE (&c->c2.frame), diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index f20d059..04a5b5f 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -62,6 +62,9 @@ run_up_down (const char *command, const struct plugin_list *plugins, int plugin_type, const char *arg, +#ifdef WIN32 + DWORD adapter_index, +#endif const char *dev_type, int tun_mtu, int link_mtu, @@ -82,6 +85,9 @@ run_up_down (const char *command, setenv_str (es, "dev", arg); if (dev_type) setenv_str (es, "dev_type", dev_type); +#ifdef WIN32 + setenv_int (es, "dev_idx", adapter_index); +#endif if (!ifconfig_local) ifconfig_local = ""; @@ -1035,7 +1041,9 @@ get_user_pass_cr (struct user_pass *up, if (!up->defined) { - const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin")); + bool from_authfile = (auth_file && !streq (auth_file, "stdin")); + bool username_from_stdin = false; + bool password_from_stdin = false; if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) msg (M_WARN, "Note: previous '%s' credentials failed", prefix); @@ -1045,7 +1053,7 @@ get_user_pass_cr (struct user_pass *up, * Get username/password from management interface? */ if (management - && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT))) + && ((auth_file && streq (auth_file, "management")) || (!from_authfile && (flags & GET_USER_PASS_MANAGEMENT))) && management_query_user_pass_enabled (management)) { const char *sc = NULL; @@ -1082,18 +1090,59 @@ get_user_pass_cr (struct user_pass *up, if (!strlen (up->password)) strcpy (up->password, "ok"); } - + else if (from_authfile) + { + /* + * Try to get username/password from a file. + */ + FILE *fp; + char password_buf[USER_PASS_LEN] = { '\0' }; + + warn_if_group_others_accessible (auth_file); + + fp = platform_fopen (auth_file, "r"); + if (!fp) + msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file); + + if ((flags & GET_USER_PASS_PASSWORD_ONLY) == 0) + { + /* Read username first */ + if (fgets (up->username, USER_PASS_LEN, fp) == NULL) + msg (M_FATAL, "Error reading username from %s authfile: %s", + prefix, + auth_file); + } + chomp (up->username); + + if (fgets (password_buf, USER_PASS_LEN, fp) != NULL) + { + chomp (password_buf); + } + + if (flags & GET_USER_PASS_PASSWORD_ONLY && !password_buf[0]) + msg (M_FATAL, "Error reading password from %s authfile: %s", prefix, auth_file); + + if (password_buf[0]) + strncpy(up->password, password_buf, USER_PASS_LEN); + else + password_from_stdin = 1; + + fclose (fp); + + if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen (up->username) == 0) + msg (M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file); + } + else + { + username_from_stdin = true; + password_from_stdin = true; + } + /* * Get username/password from standard input? */ - else if (from_stdin) + if (username_from_stdin || password_from_stdin) { -#ifndef WIN32 - /* did we --daemon'ize before asking for passwords? */ - if ( !isatty(0) && !isatty(2) ) - { msg(M_FATAL, "neither stdin nor stderr are a tty device, can't ask for %s password. If you used --daemon, you need to use --askpass to make passphrase-protected keys work, and you can not use --auth-nocache.", prefix ); } -#endif - #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE)) { @@ -1124,7 +1173,7 @@ get_user_pass_cr (struct user_pass *up, buf_printf (&user_prompt, "Enter %s Username:", prefix); buf_printf (&pass_prompt, "Enter %s Password:", prefix); - if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) + if (username_from_stdin && !(flags & GET_USER_PASS_PASSWORD_ONLY)) { if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); @@ -1132,7 +1181,7 @@ get_user_pass_cr (struct user_pass *up, msg (M_FATAL, "ERROR: %s username is empty", prefix); } - if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) + if (password_from_stdin && !get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); #ifdef ENABLE_CLIENT_CR @@ -1158,52 +1207,6 @@ get_user_pass_cr (struct user_pass *up, #endif } } - else - { - /* - * Get username/password from a file. - */ - FILE *fp; - -#ifndef ENABLE_PASSWORD_SAVE - /* - * Unless ENABLE_PASSWORD_SAVE is defined, don't allow sensitive passwords - * to be read from a file. - */ - if (flags & GET_USER_PASS_SENSITIVE) - msg (M_FATAL, "Sorry, '%s' password cannot be read from a file", prefix); -#endif - - warn_if_group_others_accessible (auth_file); - - fp = platform_fopen (auth_file, "r"); - if (!fp) - msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file); - - if (flags & GET_USER_PASS_PASSWORD_ONLY) - { - if (fgets (up->password, USER_PASS_LEN, fp) == NULL) - msg (M_FATAL, "Error reading password from %s authfile: %s", - prefix, - auth_file); - } - else - { - if (fgets (up->username, USER_PASS_LEN, fp) == NULL - || fgets (up->password, USER_PASS_LEN, fp) == NULL) - msg (M_FATAL, "Error reading username and password (must be on two consecutive lines) from %s authfile: %s", - prefix, - auth_file); - } - - fclose (fp); - - chomp (up->username); - chomp (up->password); - - if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen (up->username) == 0) - msg (M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file); - } string_mod (up->username, CC_PRINT, CC_CRLF, 0); string_mod (up->password, CC_PRINT, CC_CRLF, 0); @@ -1647,7 +1650,7 @@ argv_extract_cmd_name (const char *path) { if (path) { - char *path_cp = strdup(path); /* POSIX basename() implementaions may modify its arguments */ + char *path_cp = string_alloc(path, NULL); /* POSIX basename() implementaions may modify its arguments */ const char *bn = basename (path_cp); if (bn) { diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h index e67b5e4..c1942b6 100644 --- a/src/openvpn/misc.h +++ b/src/openvpn/misc.h @@ -63,6 +63,9 @@ void run_up_down (const char *command, const struct plugin_list *plugins, int plugin_type, const char *arg, +#ifdef WIN32 + DWORD adapter_index, +#endif const char *dev_type, int tun_mtu, int link_mtu, @@ -239,7 +242,7 @@ struct static_challenge_info {}; * Flags for get_user_pass and management_query_user_pass */ #define GET_USER_PASS_MANAGEMENT (1<<0) -#define GET_USER_PASS_SENSITIVE (1<<1) +/* GET_USER_PASS_SENSITIVE (1<<1) not used anymore */ #define GET_USER_PASS_PASSWORD_ONLY (1<<2) #define GET_USER_PASS_NEED_OK (1<<3) #define GET_USER_PASS_NOFATAL (1<<4) diff --git a/src/openvpn/mss.c b/src/openvpn/mss.c index 64fd722..7298c7b 100644 --- a/src/openvpn/mss.c +++ b/src/openvpn/mss.c @@ -129,7 +129,7 @@ mss_fixup_dowork (struct buffer *buf, uint16_t maxmss) { int hlen, olen, optlen; uint8_t *opt; - uint16_t *mss; + uint16_t mssval; int accumulate; struct openvpn_tcphdr *tc; @@ -159,14 +159,13 @@ mss_fixup_dowork (struct buffer *buf, uint16_t maxmss) if (*opt == OPENVPN_TCPOPT_MAXSEG) { if (optlen != OPENVPN_TCPOLEN_MAXSEG) continue; - mss = (uint16_t *)(opt + 2); - if (ntohs (*mss) > maxmss) { - dmsg (D_MSS, "MSS: %d -> %d", - (int) ntohs (*mss), - (int) maxmss); - accumulate = *mss; - *mss = htons (maxmss); - accumulate -= *mss; + mssval = (opt[2]<<8)+opt[3]; + if (mssval > maxmss) { + dmsg (D_MSS, "MSS: %d -> %d", (int) mssval, (int) maxmss); + accumulate = htons(mssval); + opt[2] = (maxmss>>8)&0xff; + opt[3] = maxmss&0xff; + accumulate -= htons(maxmss); ADJUST_CHECKSUM (accumulate, tc->check); } } diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 374950e..577c5d3 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -1426,10 +1426,24 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) } } - /* TODO: I'm not exactly sure what these environment variables are - * used for, but if we have them for IPv4, we should also have - * them for IPv6, no? - */ + setenv_del (mi->context.c2.es, "ifconfig_pool_local_ip6"); + setenv_del (mi->context.c2.es, "ifconfig_pool_remote_ip6"); + setenv_del (mi->context.c2.es, "ifconfig_pool_ip6_netbits"); + + if (mi->context.c1.tuntap->ipv6 && mi->context.c2.push_ifconfig_ipv6_defined) + { + setenv_in6_addr (mi->context.c2.es, + "ifconfig_pool_remote", + &mi->context.c2.push_ifconfig_ipv6_local, + SA_SET_IF_NONZERO); + setenv_in6_addr (mi->context.c2.es, + "ifconfig_pool_local", + &mi->context.c2.push_ifconfig_ipv6_remote, + SA_SET_IF_NONZERO); + setenv_int (mi->context.c2.es, + "ifconfig_pool_ip6_netbits", + mi->context.c2.push_ifconfig_ipv6_netbits); + } } /* @@ -1780,6 +1794,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi { msg (D_MULTI_ERRORS, "MULTI: client has been rejected due to 'disable' directive"); cc_succeeded = false; + cc_succeeded_count = 0; } if (cc_succeeded) diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index 32e326e..823c3dd 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -220,6 +220,9 @@ openvpn_main (int argc, char *argv[]) /* print version number */ msg (M_INFO, "%s", title_string); +#ifdef WIN32 + show_windows_version(M_INFO); +#endif show_library_versions(M_INFO); /* misc stuff */ diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 10ec859..36c3100 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -133,13 +133,15 @@ struct context_persist * * Level 0 state is initialized once at program startup, and then remains * throughout the lifetime of the OpenVPN process. This structure - * contains information related to the process's PID, user, and group. + * contains information related to the process's PID, user, group, and + * privileges. */ struct context_0 { /* workspace for --user/--group */ bool uid_gid_specified; - bool uid_gid_set; + /* helper which tells us whether we should keep trying to drop privileges */ + bool uid_gid_chroot_set; struct platform_state_user platform_state_user; struct platform_state_group platform_state_group; }; diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj index 3b2340e..d691500 100755..100644 --- a/src/openvpn/openvpn.vcxproj +++ b/src/openvpn/openvpn.vcxproj @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> @@ -20,10 +20,12 @@ <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> @@ -62,7 +64,7 @@ <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> @@ -87,7 +89,7 @@ <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters index 40336ba..dbed3cd 100644 --- a/src/openvpn/openvpn.vcxproj.filters +++ b/src/openvpn/openvpn.vcxproj.filters @@ -449,10 +449,13 @@ <ClInclude Include="win32.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="win32_wfp.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="openvpn_win32_resources.rc"> <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 007bd8c..a49a4fb 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -486,8 +486,10 @@ static const char usage_message[] = "Client options (when connecting to a multi-client server):\n" "--client : Helper option to easily configure client mode.\n" "--auth-user-pass [up] : Authenticate with server using username/password.\n" - " up is a file containing username/password on 2 lines,\n" - " or omit to prompt from console.\n" + " up is a file containing the username on the first line,\n" + " and a password on the second. If either the password or both\n" + " the username and the password are omitted OpenVPN will prompt\n" + " for them from console.\n" "--pull : Accept certain config file options from the peer as if they\n" " were part of the local config file. Must be specified\n" " when connecting to a '--mode server' remote host.\n" @@ -713,6 +715,7 @@ static const char usage_message[] = " optional parameter controls the initial state of ex.\n" "--show-net-up : Show " PACKAGE_NAME "'s view of routing table and net adapter list\n" " after TAP adapter is up and routes have been added.\n" + "--block-outside-dns : Block DNS on other network adapters to prevent DNS leaks\n" "Windows Standalone Options:\n" "\n" "--show-adapters : Show all TAP-Windows adapters.\n" @@ -800,10 +803,6 @@ init_options (struct options *o, const bool init_gc) #ifdef ENABLE_FEATURE_TUN_PERSIST o->persist_mode = 1; #endif -#ifndef WIN32 - o->rcvbuf = 65536; - o->sndbuf = 65536; -#endif #ifdef TARGET_LINUX o->tuntap_options.txqueuelen = 100; #endif @@ -816,6 +815,7 @@ init_options (struct options *o, const bool init_gc) o->tuntap_options.dhcp_lease_time = 31536000; /* one year */ o->tuntap_options.dhcp_masq_offset = 0; /* use network address as internal DHCP server address */ o->route_method = ROUTE_METHOD_ADAPTIVE; + o->block_outside_dns = false; #endif #if P2MP_SERVER o->real_hash_size = 256; @@ -965,13 +965,11 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error) * "/nn" is optional, default to /64 if missing * * return true if parsing succeeded, modify *network and *netbits - * return address part without "/nn" in *printable_ipv6 (if != NULL) */ bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network, - unsigned int * netbits, char ** printable_ipv6, int msglevel ) + unsigned int * netbits, int msglevel) { - int rc; char * sep, * endp; int bits; struct in6_addr t_network; @@ -998,21 +996,14 @@ get_ipv6_addr( const char * prefix_str, struct in6_addr *network, if ( sep != NULL ) *sep = '\0'; - rc = inet_pton( AF_INET6, prefix_str, &t_network ); - - if ( rc == 1 && printable_ipv6 != NULL ) - { - *printable_ipv6 = string_alloc( prefix_str, NULL ); - } - - if ( sep != NULL ) *sep = '/'; - - if ( rc != 1 ) + if ( inet_pton( AF_INET6, prefix_str, &t_network ) != 1 ) { msg (msglevel, "IPv6 prefix '%s': invalid IPv6 address", prefix_str); return false; } + if ( sep != NULL ) *sep = '/'; + if ( netbits != NULL ) { *netbits = bits; @@ -1024,12 +1015,35 @@ get_ipv6_addr( const char * prefix_str, struct in6_addr *network, return true; /* parsing OK, values set */ } +/** + * Returns newly allocated string containing address part without "/nn". + * + * If gc != NULL, the allocated memory is registered in the supplied gc. + */ +static char * +get_ipv6_addr_no_netbits (const char *addr, struct gc_arena *gc) +{ + const char *end = strchr (addr, '/'); + char *ret = NULL; + if (NULL == end) + { + ret = string_alloc (addr, gc); + } + else + { + size_t len = end - addr; + ret = gc_malloc (len + 1, true, gc); + memcpy (ret, addr, len); + } + return ret; +} + static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec ) { struct in6_addr t_addr; unsigned int t_bits; - return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, NULL, M_WARN ); + return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, M_WARN ); } static char * @@ -1271,7 +1285,7 @@ option_iroute_ipv6 (struct options *o, ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc); - if ( !get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel )) + if ( !get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, msglevel )) { msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification", prefix_str); @@ -1585,6 +1599,7 @@ show_settings (const struct options *o) SHOW_STR (ca_path); SHOW_STR (dh_file); SHOW_STR (cert_file); + SHOW_STR (extra_certs_file); #ifdef MANAGMENT_EXTERNAL_KEY if((o->management_flags & MF_EXTERNAL_KEY)) @@ -1665,6 +1680,7 @@ show_settings (const struct options *o) #ifdef WIN32 SHOW_BOOL (show_net_up); SHOW_INT (route_method); + SHOW_BOOL (block_outside_dns); show_tuntap_options (&o->tuntap_options); #endif #endif @@ -2616,7 +2632,7 @@ check_file_access(const int type, const char *file, const int mode, const char * /* Is the directory path leading to the given file accessible? */ if (type & CHKACC_DIRPATH) { - char *fullpath = strdup(file); /* POSIX dirname() implementaion may modify its arguments */ + char *fullpath = string_alloc (file, NULL); /* POSIX dirname() implementaion may modify its arguments */ char *dirpath = dirname(fullpath); if (platform_access (dirpath, mode|X_OK) != 0) @@ -3473,6 +3489,15 @@ usage_small (void) openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ } +#ifdef WIN32 +void show_windows_version(const unsigned int flags) +{ + struct gc_arena gc = gc_new (); + msg (flags, "Windows version %s", win32_version_string (&gc, true)); + gc_free (&gc); +} +#endif + void show_library_versions(const unsigned int flags) { @@ -3498,6 +3523,9 @@ usage_version (void) { msg (M_INFO|M_NOPREFIX, "%s", title_string); show_library_versions( M_INFO|M_NOPREFIX ); +#ifdef WIN32 + 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>"); #ifndef ENABLE_SMALL @@ -3761,7 +3789,10 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) while (in_src_get (is, line, sizeof (line))) { - if (!strncmp (line, close_tag, strlen (close_tag))) + char *line_ptr = line; + /* Remove leading spaces */ + while (isspace(*line_ptr)) line_ptr++; + if (!strncmp (line_ptr, close_tag, strlen (close_tag))) { endtagfound = true; break; @@ -3853,7 +3884,7 @@ read_config_file (struct options *options, const int max_recursive_levels = 10; FILE *fp; int line_num; - char line[OPTION_LINE_SIZE]; + char line[OPTION_LINE_SIZE+1]; char *p[MAX_PARMS]; ++level; @@ -3871,6 +3902,10 @@ read_config_file (struct options *options, int offset = 0; CLEAR (p); ++line_num; + if (strlen(line) == OPTION_LINE_SIZE) + msg (msglevel, "In %s:%d: Maximum optione line length (%d) exceeded, line starts with %s", + file, line_num, OPTION_LINE_SIZE, line); + /* Ignore UTF-8 BOM at start of stream */ if (line_num == 1 && strncmp (line, "\xEF\xBB\xBF", 3) == 0) offset = 3; @@ -4467,10 +4502,9 @@ add_option (struct options *options, else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] ) { unsigned int netbits; - char * ipv6_local; VERIFY_PERMISSION (OPT_P_UP); - if ( get_ipv6_addr( p[1], NULL, &netbits, &ipv6_local, msglevel ) && + if ( get_ipv6_addr( p[1], NULL, &netbits, msglevel ) && ipv6_addr_safe( p[2] ) ) { if ( netbits < 64 || netbits > 124 ) @@ -4479,11 +4513,7 @@ add_option (struct options *options, goto err; } - if (options->ifconfig_ipv6_local) - /* explicitly ignoring this is a const char */ - free ((char *) options->ifconfig_ipv6_local); - - options->ifconfig_ipv6_local = ipv6_local; + options->ifconfig_ipv6_local = get_ipv6_addr_no_netbits (p[1], &options->gc); options->ifconfig_ipv6_netbits = netbits; options->ifconfig_ipv6_remote = p[2]; } @@ -5558,7 +5588,7 @@ add_option (struct options *options, unsigned int netbits = 0; VERIFY_PERMISSION (OPT_P_GENERAL); - if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev) ) + if ( ! get_ipv6_addr (p[1], &network, &netbits, lev) ) { msg (msglevel, "error parsing --server-ipv6 parameter"); goto err; @@ -5669,7 +5699,7 @@ add_option (struct options *options, unsigned int netbits = 0; VERIFY_PERMISSION (OPT_P_GENERAL); - if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev ) ) + if ( ! get_ipv6_addr (p[1], &network, &netbits, lev ) ) { msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters"); goto err; @@ -5930,7 +5960,7 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_INSTANCE); - if ( ! get_ipv6_addr( p[1], &local, &netbits, NULL, msglevel ) ) + if ( ! get_ipv6_addr( p[1], &local, &netbits, msglevel ) ) { msg (msglevel, "cannot parse --ifconfig-ipv6-push addresses"); goto err; @@ -5938,7 +5968,7 @@ add_option (struct options *options, if ( p[2] ) { - if ( !get_ipv6_addr( p[2], &remote, NULL, NULL, msglevel ) ) + if ( !get_ipv6_addr( p[2], &remote, NULL, msglevel ) ) { msg( msglevel, "cannot parse --ifconfig-ipv6-push addresses"); goto err; @@ -5948,7 +5978,7 @@ add_option (struct options *options, { if ( ! options->ifconfig_ipv6_local || ! get_ipv6_addr( options->ifconfig_ipv6_local, &remote, - NULL, NULL, msglevel ) ) + NULL, msglevel ) ) { msg( msglevel, "second argument to --ifconfig-ipv6-push missing and no global --ifconfig-ipv6 address set"); goto err; @@ -6230,6 +6260,22 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.register_dns = true; } +#ifdef WIN32 + else if (streq (p[0], "block-outside-dns") && !p[1]) + { + VERIFY_PERMISSION (OPT_P_IPWIN32); + if (win_wfp_init_funcs()) + { + options->block_outside_dns = true; + } + else + { + msg (msglevel_fc, "Failed to enable --block-outside-dns. " + "Maybe WFP is not supported on your system?"); + goto err; + } + } +#endif else if (streq (p[0], "rdns-internal")) /* standalone method for internal use * diff --git a/src/openvpn/options.h b/src/openvpn/options.h index af9a47f..26b09ea 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -593,6 +593,7 @@ struct options bool exit_event_initial_state; bool show_net_up; int route_method; + bool block_outside_dns; #endif bool use_peer_id; @@ -689,6 +690,10 @@ void usage_small (void); void show_library_versions(const unsigned int flags); +#ifdef WIN32 +void show_windows_version(const unsigned int flags); +#endif + void init_options (struct options *o, const bool init_gc); void uninit_options (struct options *o); @@ -777,8 +782,7 @@ void options_string_import (struct options *options, struct env_set *es); bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network, - unsigned int * netbits, char ** printable_ipv6, - int msglevel ); + unsigned int * netbits, int msglevel ); /* * inline functions diff --git a/src/openvpn/pkcs11_polarssl.c b/src/openvpn/pkcs11_polarssl.c index 03b2bab..a58beef 100644 --- a/src/openvpn/pkcs11_polarssl.c +++ b/src/openvpn/pkcs11_polarssl.c @@ -40,6 +40,7 @@ #include "errlevel.h" #include "pkcs11_backend.h" #include <polarssl/pkcs11.h> +#include <polarssl/x509.h> int pkcs11_init_tls_session(pkcs11h_certificate_t certificate, @@ -49,20 +50,21 @@ pkcs11_init_tls_session(pkcs11h_certificate_t certificate, ASSERT (NULL != ssl_ctx); + ALLOC_OBJ_CLEAR (ssl_ctx->crt_chain, x509_crt); if (pkcs11_x509_cert_init(ssl_ctx->crt_chain, certificate)) { msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); goto cleanup; } - ssl_ctx->priv_key_pkcs11 = malloc(sizeof(pkcs11_context)); - - if (ssl_ctx->priv_key_pkcs11 == NULL) { - msg (M_FATAL, "PKCS#11: Cannot allocate PolarSSL private key object"); + ALLOC_OBJ_CLEAR (ssl_ctx->priv_key_pkcs11, pkcs11_context); + if (pkcs11_priv_key_init(ssl_ctx->priv_key_pkcs11, certificate)) { + msg (M_FATAL, "PKCS#11: Cannot initialize PolarSSL private key object"); goto cleanup; } - if (pkcs11_priv_key_init(ssl_ctx->priv_key_pkcs11, certificate)) { - msg (M_FATAL, "PKCS#11: Cannot initialize PolarSSL private key object"); + ALLOC_OBJ_CLEAR (ssl_ctx->priv_key, pk_context); + if (0 != pk_init_ctx_rsa_alt(ssl_ctx->priv_key, ssl_ctx->priv_key_pkcs11, + ssl_pkcs11_decrypt, ssl_pkcs11_sign, ssl_pkcs11_key_len)) { goto cleanup; } @@ -78,14 +80,14 @@ pkcs11_certificate_dn (pkcs11h_certificate_t cert, struct gc_arena *gc) char *ret = NULL; char dn[1024] = {0}; - x509_cert polar_cert = {0}; + x509_crt polar_cert = {0}; if (pkcs11_x509_cert_init(&polar_cert, cert)) { msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); goto cleanup; } - if (-1 == x509parse_dn_gets (dn, sizeof(dn), &polar_cert.subject)) { + if (-1 == x509_dn_gets (dn, sizeof(dn), &polar_cert.subject)) { msg (M_FATAL, "PKCS#11: PolarSSL cannot parse subject"); goto cleanup; } @@ -93,7 +95,7 @@ pkcs11_certificate_dn (pkcs11h_certificate_t cert, struct gc_arena *gc) ret = string_alloc(dn, gc); cleanup: - x509_free(&polar_cert); + x509_crt_free(&polar_cert); return ret; } @@ -104,14 +106,14 @@ pkcs11_certificate_serial (pkcs11h_certificate_t cert, char *serial, { int ret = 1; - x509_cert polar_cert = {0}; + x509_crt polar_cert = {0}; if (pkcs11_x509_cert_init(&polar_cert, cert)) { msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); goto cleanup; } - if (-1 == x509parse_serial_gets (serial, serial_len, &polar_cert.serial)) { + if (-1 == x509_serial_gets (serial, serial_len, &polar_cert.serial)) { msg (M_FATAL, "PKCS#11: PolarSSL cannot parse serial"); goto cleanup; } @@ -119,7 +121,7 @@ pkcs11_certificate_serial (pkcs11h_certificate_t cert, char *serial, ret = 0; cleanup: - x509_free(&polar_cert); + x509_crt_free(&polar_cert); return ret; } diff --git a/src/openvpn/plugin.c b/src/openvpn/plugin.c index 0948f23..54c5b52 100644 --- a/src/openvpn/plugin.c +++ b/src/openvpn/plugin.c @@ -291,7 +291,7 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o) static void plugin_vlog (openvpn_plugin_log_flags_t flags, const char *name, const char *format, va_list arglist) { - unsigned int msg_flags; + unsigned int msg_flags = 0; if (!format) return; diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 11505cb..e4f3984 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -103,6 +103,7 @@ server_pushed_signal (struct context *c, const struct buffer *buffer, const bool m = BSTR (&buf); /* preserve cached passwords? */ + /* advance to next server? */ { bool purge = true; @@ -113,6 +114,12 @@ server_pushed_signal (struct context *c, const struct buffer *buffer, const bool { if (m[i] == 'P') purge = false; + else if (m[i] == 'N') + { + /* next server? */ + if (c->options.connection_list) + c->options.connection_list->no_advance = false; + } } } if (purge) diff --git a/src/openvpn/route.c b/src/openvpn/route.c index 2acfbe8..f35bc85 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -42,6 +42,7 @@ #include "manage.h" #include "win32.h" #include "options.h" +#include "win32.h" #include "memdbg.h" @@ -390,7 +391,7 @@ init_route_ipv6 (struct route_ipv6 *r6, { r6->defined = false; - if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, NULL, M_WARN )) + if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, M_WARN )) goto fail; /* gateway */ @@ -648,7 +649,7 @@ init_route_list (struct route_list *rl, bool warned = false; for (i = 0; i < opt->n; ++i) { - struct addrinfo* netlist; + struct addrinfo* netlist = NULL; struct route_ipv4 r; if (!init_route (&r, @@ -675,8 +676,9 @@ init_route_list (struct route_list *rl, } } } - freeaddrinfo(netlist); } + if (netlist) + freeaddrinfo(netlist); } rl->n = j; } @@ -1622,6 +1624,13 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla #elif defined (WIN32) + if (win32_version_info() != WIN_XP) + { + struct buffer out = alloc_buf_gc (64, &gc); + buf_printf (&out, "interface=%d", tt->adapter_index ); + device = buf_bptr(&out); + } + /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */ argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s", get_win_sys_path(), @@ -1953,6 +1962,13 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne #elif defined (WIN32) + if (win32_version_info() != WIN_XP) + { + struct buffer out = alloc_buf_gc (64, &gc); + buf_printf (&out, "interface=%d", tt->adapter_index ); + device = buf_bptr(&out); + } + /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */ argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s", get_win_sys_path(), @@ -3195,7 +3211,7 @@ test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi) { struct gc_arena gc = gc_new (); const in_addr_t nonlocal_netmask = 0x80000000L; /* routes with netmask <= to this are considered non-local */ - bool ret = TLA_NONLOCAL; + int ret = TLA_NONLOCAL; /* get full routing table */ const MIB_IPFORWARDTABLE *rt = get_windows_routing_table (&gc); diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 0424c0b..3e30c75 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -494,12 +494,9 @@ static void socket_set_sndbuf (int sd, int size) { #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF) - if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX) + if (setsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof (size)) != 0) { - if (setsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof (size)) != 0) - { - msg (M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size); - } + msg (M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size); } #endif } @@ -523,13 +520,10 @@ static bool socket_set_rcvbuf (int sd, int size) { #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF) - if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX) + if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof (size)) != 0) { - if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof (size)) != 0) - { - msg (M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size); - return false; - } + msg (M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size); + return false; } return true; #endif @@ -1265,20 +1259,24 @@ resolve_remote (struct link_socket *sock, ASSERT (0); } - /* Temporary fix, this need to be changed for dual stack */ - status = openvpn_getaddrinfo(flags, sock->remote_host, retry, - signal_received, af, &ai); - if(status == 0) { - sock->info.lsa->remote.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); - freeaddrinfo(ai); + /* Temporary fix, this need to be changed for dual stack */ + status = openvpn_getaddrinfo(flags, sock->remote_host, retry, + signal_received, af, &ai); + if(status == 0) + { + if ( ai->ai_family == AF_INET6 ) + sock->info.lsa->remote.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); + else + sock->info.lsa->remote.addr.in4 = *((struct sockaddr_in*)(ai->ai_addr)); + freeaddrinfo(ai); - dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", + dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", flags, phase, retry, signal_received ? *signal_received : -1, status); - } + } if (signal_received) { if (*signal_received) @@ -2412,6 +2410,22 @@ setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, c } void +setenv_in6_addr (struct env_set *es, + const char *name_prefix, + const struct in6_addr *addr, + const unsigned int flags) +{ + if (!IN6_IS_ADDR_UNSPECIFIED (addr) || !(flags & SA_SET_IF_NONZERO)) + { + struct openvpn_sockaddr si; + CLEAR (si); + si.addr.in6.sin6_family = AF_INET6; + si.addr.in6.sin6_addr = *addr; + setenv_sockaddr (es, name_prefix, &si, flags); + } +} + +void setenv_link_socket_actual (struct env_set *es, const char *name_prefix, const struct link_socket_actual *act, diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index 793cd9f..b7a4e01 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -42,11 +42,6 @@ #define OPENVPN_PORT 1194 /* - * Maximum size passed passed to setsockopt SNDBUF/RCVBUF - */ -#define SOCKET_SND_RCV_BUF_MAX 1000000 - -/* * Number of seconds that "resolv-retry infinite" * represents. */ @@ -383,6 +378,11 @@ void setenv_in_addr_t (struct env_set *es, in_addr_t addr, const unsigned int flags); +void setenv_in6_addr (struct env_set *es, + const char *name_prefix, + const struct in6_addr *addr, + const unsigned int flags); + void setenv_link_socket_actual (struct env_set *es, const char *name_prefix, const struct link_socket_actual *act, diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index a17c738..0679890 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -43,6 +43,7 @@ #endif #include "syshead.h" +#include "win32.h" #if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) @@ -301,8 +302,9 @@ tls_init_control_channel_frame_parameters(const struct frame *data_channel_frame reliable_ack_adjust_frame_parameters (frame, CONTROL_SEND_ACK_MAX); frame_add_to_extra_frame (frame, SID_SIZE + sizeof (packet_id_type)); - /* set dynamic link MTU to minimum value */ - frame_set_mtu_dynamic (frame, 0, SET_MTU_TUN); + /* set dynamic link MTU to cap control channel packets at 1250 bytes */ + ASSERT (TUN_LINK_DELTA (frame) < min_int (frame->link_mtu, 1250)); + frame->link_mtu_dynamic = min_int (frame->link_mtu, 1250) - TUN_LINK_DELTA (frame); } void @@ -333,7 +335,7 @@ void pem_password_setup (const char *auth_file) { if (!strlen (passbuf.password)) - get_user_pass (&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_PASSWORD_ONLY); + get_user_pass (&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY); } int @@ -376,11 +378,11 @@ auth_user_pass_setup (const char *auth_file, const struct static_challenge_info get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, - GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_DYNAMIC_CHALLENGE, + GET_USER_PASS_MANAGEMENT|GET_USER_PASS_DYNAMIC_CHALLENGE, auth_challenge); else if (sci) /* static challenge response */ { - int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_STATIC_CHALLENGE; + int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_STATIC_CHALLENGE; if (sci->flags & SC_ECHO) flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO; get_user_pass_cr (&auth_user_pass, @@ -391,7 +393,7 @@ auth_user_pass_setup (const char *auth_file, const struct static_challenge_info } else # endif - get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); + get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT); #endif } } @@ -555,6 +557,9 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) tls_ctx_load_extra_certs(new_ctx, options->extra_certs_file, options->extra_certs_file_inline); } + /* Check certificate notBefore and notAfter */ + tls_ctx_check_cert_time(new_ctx); + /* Allowable ciphers */ if (options->cipher_list) { @@ -1844,6 +1849,9 @@ push_peer_info(struct buffer *buf, struct tls_session *session) if (rgi.flags & RGI_HWADDR_DEFINED) buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (rgi.hwaddr, 6, 0, 1, ":", &gc)); buf_printf (&out, "IV_SSL=%s\n", get_ssl_library_version() ); +#if defined(WIN32) + buf_printf (&out, "IV_PLAT_VER=%s\n", win32_version_string (&gc, false)); +#endif } /* push env vars that begin with UV_ and IV_GUI_VER */ diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index 6d47bd0..4b35e51 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -176,6 +176,15 @@ void tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags); void tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers); /** + * Check our certificate notBefore and notAfter fields, and warn if the cert is + * either not yet valid or has expired. Note that this is a non-fatal error, + * since we compare against the system time, which might be incorrect. + * + * @param ctx TLS context to get our certificate from. + */ +void tls_ctx_check_cert_time (const struct tls_root_ctx *ctx); + +/** * Load Diffie Hellman Parameters, and load them into the library-specific * TLS context. * diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index be33caa..e595e1b 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -335,6 +335,55 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) } void +tls_ctx_check_cert_time (const struct tls_root_ctx *ctx) +{ + int ret; + const X509 *cert; + + ASSERT (ctx); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + /* OpenSSL 1.0.2 and up */ + cert = SSL_CTX_get0_certificate (ctx->ctx); +#else + /* OpenSSL 1.0.1 and earlier need an SSL object to get at the certificate */ + SSL *ssl = SSL_new (ctx->ctx); + cert = SSL_get_certificate (ssl); +#endif + + if (cert == NULL) + { + goto cleanup; /* Nothing to check if there is no certificate */ + } + + ret = X509_cmp_time (X509_get_notBefore (cert), NULL); + if (ret == 0) + { + msg (D_TLS_DEBUG_MED, "Failed to read certificate notBefore field."); + } + if (ret > 0) + { + msg (M_WARN, "WARNING: Your certificate is not yet valid!"); + } + + ret = X509_cmp_time (X509_get_notAfter (cert), NULL); + if (ret == 0) + { + msg (D_TLS_DEBUG_MED, "Failed to read certificate notAfter field."); + } + if (ret < 0) + { + msg (M_WARN, "WARNING: Your certificate has expired!"); + } + +cleanup: +#if OPENSSL_VERSION_NUMBER < 0x10002000L + SSL_free (ssl); +#endif + return; +} + +void tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file, const char *dh_file_inline ) diff --git a/src/openvpn/ssl_polarssl.c b/src/openvpn/ssl_polarssl.c index 30c7395..cd8ee1a 100644 --- a/src/openvpn/ssl_polarssl.c +++ b/src/openvpn/ssl_polarssl.c @@ -46,12 +46,13 @@ #include "manage.h" #include "ssl_common.h" -#include <polarssl/sha2.h> #include <polarssl/havege.h> #include "ssl_verify_polarssl.h" #include <polarssl/error.h> +#include <polarssl/oid.h> #include <polarssl/pem.h> +#include <polarssl/sha256.h> #include <polarssl/version.h> void @@ -76,11 +77,8 @@ tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) CLEAR(*ctx); ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context); - ALLOC_OBJ_CLEAR(ctx->priv_key, rsa_context); - - ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_cert); - ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_cert); + ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_crt); ctx->endpoint = SSL_IS_SERVER; ctx->initialised = true; @@ -93,10 +91,7 @@ tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) CLEAR(*ctx); ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context); - ALLOC_OBJ_CLEAR(ctx->priv_key, rsa_context); - - ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_cert); - ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_cert); + ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_crt); ctx->endpoint = SSL_IS_CLIENT; ctx->initialised = true; @@ -107,17 +102,21 @@ tls_ctx_free(struct tls_root_ctx *ctx) { if (ctx) { - rsa_free(ctx->priv_key); - free(ctx->priv_key); + pk_free(ctx->priv_key); + if (ctx->priv_key) + free(ctx->priv_key); - x509_free(ctx->ca_chain); - free(ctx->ca_chain); + x509_crt_free(ctx->ca_chain); + if (ctx->ca_chain) + free(ctx->ca_chain); - x509_free(ctx->crt_chain); - free(ctx->crt_chain); + x509_crt_free(ctx->crt_chain); + if (ctx->crt_chain) + free(ctx->crt_chain); dhm_free(ctx->dhm_ctx); - free(ctx->dhm_ctx); + if (ctx->dhm_ctx) + free(ctx->dhm_ctx); #if defined(ENABLE_PKCS11) if (ctx->priv_key_pkcs11 != NULL) { @@ -191,7 +190,7 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) /* Parse allowed ciphers, getting IDs */ i = 0; - tmp_ciphers_orig = tmp_ciphers = strdup(ciphers); + tmp_ciphers_orig = tmp_ciphers = string_alloc (ciphers, NULL); token = strtok (tmp_ciphers, ":"); while(token) @@ -206,19 +205,39 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) } void +tls_ctx_check_cert_time (const struct tls_root_ctx *ctx) +{ + ASSERT (ctx); + if (ctx->crt_chain == NULL) + { + return; /* Nothing to check if there is no certificate */ + } + + if (x509_time_future (&ctx->crt_chain->valid_from)) + { + msg (M_WARN, "WARNING: Your certificate is not yet valid!"); + } + + if (x509_time_expired (&ctx->crt_chain->valid_to)) + { + msg (M_WARN, "WARNING: Your certificate has expired!"); + } +} + +void tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file, const char *dh_inline ) { if (!strcmp (dh_file, INLINE_FILE_TAG) && dh_inline) { - if (0 != x509parse_dhm(ctx->dhm_ctx, (const unsigned char *) dh_inline, + if (0 != dhm_parse_dhm(ctx->dhm_ctx, (const unsigned char *) dh_inline, strlen(dh_inline))) msg (M_FATAL, "Cannot read inline DH parameters"); } else { - if (0 != x509parse_dhmfile(ctx->dhm_ctx, dh_file)) + if (0 != dhm_parse_dhmfile(ctx->dhm_ctx, dh_file)) msg (M_FATAL, "Cannot read DH parameters from file %s", dh_file); } @@ -251,15 +270,20 @@ tls_ctx_load_cert_file (struct tls_root_ctx *ctx, const char *cert_file, { ASSERT(NULL != ctx); + if (!ctx->crt_chain) + { + ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_crt); + } + if (!strcmp (cert_file, INLINE_FILE_TAG) && cert_inline) { - if (0 != x509parse_crt(ctx->crt_chain, + if (0 != x509_crt_parse(ctx->crt_chain, (const unsigned char *) cert_inline, strlen(cert_inline))) msg (M_FATAL, "Cannot load inline certificate file"); } else { - if (0 != x509parse_crtfile(ctx->crt_chain, cert_file)) + if (0 != x509_crt_parse_file(ctx->crt_chain, cert_file)) msg (M_FATAL, "Cannot load certificate file %s", cert_file); } } @@ -272,34 +296,40 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, int status; ASSERT(NULL != ctx); + if (!ctx->priv_key) + { + ALLOC_OBJ_CLEAR(ctx->priv_key, pk_context); + } + if (!strcmp (priv_key_file, INLINE_FILE_TAG) && priv_key_inline) { - status = x509parse_key(ctx->priv_key, + status = pk_parse_key(ctx->priv_key, (const unsigned char *) priv_key_inline, strlen(priv_key_inline), NULL, 0); - if (POLARSSL_ERR_X509_PASSWORD_REQUIRED == status) + + if (POLARSSL_ERR_PK_PASSWORD_REQUIRED == status) { char passbuf[512] = {0}; pem_password_callback(passbuf, 512, 0, NULL); - status = x509parse_key(ctx->priv_key, + status = pk_parse_key(ctx->priv_key, (const unsigned char *) priv_key_inline, strlen(priv_key_inline), - (const unsigned char *) passbuf, strlen(passbuf)); + (unsigned char *) passbuf, strlen(passbuf)); } } else { - status = x509parse_keyfile(ctx->priv_key, priv_key_file, NULL); - if (POLARSSL_ERR_X509_PASSWORD_REQUIRED == status) + status = pk_parse_keyfile(ctx->priv_key, priv_key_file, NULL); + if (POLARSSL_ERR_PK_PASSWORD_REQUIRED == status) { char passbuf[512] = {0}; pem_password_callback(passbuf, 512, 0, NULL); - status = x509parse_keyfile(ctx->priv_key, priv_key_file, passbuf); + status = pk_parse_keyfile(ctx->priv_key, priv_key_file, passbuf); } } if (0 != status) { #ifdef ENABLE_MANAGEMENT - if (management && (POLARSSL_ERR_X509_PASSWORD_MISMATCH == status)) + if (management && (POLARSSL_ERR_PK_PASSWORD_MISMATCH == status)) management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); #endif msg (M_WARN, "Cannot load private key file %s", priv_key_file); @@ -323,43 +353,43 @@ struct external_context { size_t signature_length; }; -int -tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline) -{ - ASSERT(NULL != ctx); - - tls_ctx_load_cert_file(ctx, cert_file, cert_file_inline); - - if (ctx->crt_chain == NULL) - return 0; - - /* Most of the initialization happens in key_state_ssl_init() */ - ALLOC_OBJ_CLEAR (ctx->external_key, struct external_context); - ctx->external_key->signature_length = ctx->crt_chain->rsa.len; - - return 1; -} - +/** + * external_pkcs1_sign implements a PolarSSL rsa_sign_func callback, that uses + * the management interface to request an RSA signature for the supplied hash. + * + * @param ctx_voidptr Management external key context. + * @param f_rng (Unused) + * @param p_rng (Unused) + * @param mode RSA mode (should be RSA_PRIVATE). + * @param md_alg Message digest ('hash') algorithm type. + * @param hashlen Length of hash (overridden by length specified by md_alg + * if md_alg != POLARSSL_MD_NONE). + * @param hash The digest ('hash') to sign. Should have a size + * matching the length of md_alg (if != POLARSSL_MD_NONE), + * or hashlen otherwise. + * @param sig Buffer that returns the signature. Should be at least of + * size ctx->signature_length. + * + * @return 0 on success, non-zero polarssl error code on failure. + */ static inline int external_pkcs1_sign( void *ctx_voidptr, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int mode, - int hash_id, unsigned int hashlen, const unsigned char *hash, + md_type_t md_alg, unsigned int hashlen, const unsigned char *hash, unsigned char *sig ) { struct external_context * const ctx = ctx_voidptr; char *in_b64 = NULL; char *out_b64 = NULL; int rv; - unsigned char * const p = sig; - size_t asn_len; + unsigned char *p = sig; + size_t asn_len = 0, oid_size = 0, sig_len = 0; + const char *oid = NULL; - ASSERT(NULL != ctx); + if( NULL == ctx ) + return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - if (RSA_PRIVATE != mode) - { - rv = POLARSSL_ERR_RSA_BAD_INPUT_DATA; - goto done; - } + if( RSA_PRIVATE != mode ) + return POLARSSL_ERR_RSA_BAD_INPUT_DATA; /* * Support a wide range of hashes. TLSv1.1 and before only need SIG_RSA_RAW, @@ -367,67 +397,54 @@ static inline int external_pkcs1_sign( void *ctx_voidptr, * * This code has been taken from PolarSSL pkcs11_sign(), under the GPLv2.0+. */ - switch( hash_id ) - { - case SIG_RSA_RAW: - asn_len = 0; - memcpy( p, hash, hashlen ); - break; - - case SIG_RSA_MD2: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 2; break; - - case SIG_RSA_MD4: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 4; break; - - case SIG_RSA_MD5: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 5; break; - - case SIG_RSA_SHA1: - asn_len = OID_SIZE(ASN1_HASH_SHA1); - memcpy( p, ASN1_HASH_SHA1, asn_len ); - memcpy( p + 15, hash, hashlen ); - break; - - case SIG_RSA_SHA224: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 4; p[18] += hashlen; break; - - case SIG_RSA_SHA256: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 1; p[18] += hashlen; break; - - case SIG_RSA_SHA384: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 2; p[18] += hashlen; break; - - case SIG_RSA_SHA512: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 3; p[18] += hashlen; break; - - /* End of copy */ - default: - rv = POLARSSL_ERR_RSA_BAD_INPUT_DATA; - goto done; + if( md_alg != POLARSSL_MD_NONE ) + { + const md_info_t *md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( oid_get_oid_by_md( md_alg, &oid, &oid_size ) != 0 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hashlen = md_get_size( md_info ); + asn_len = 10 + oid_size; + } + + sig_len = ctx->signature_length; + if ( (SIZE_MAX - hashlen) < asn_len || (hashlen + asn_len) > sig_len ) + return POLARSSL_ERR_RSA_BAD_INPUT_DATA; + + if( md_alg != POLARSSL_MD_NONE ) + { + /* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x08 + oid_size + hashlen ); + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x04 + oid_size ); + *p++ = ASN1_OID; + *p++ = oid_size & 0xFF; + memcpy( p, oid, oid_size ); + p += oid_size; + *p++ = ASN1_NULL; + *p++ = 0x00; + *p++ = ASN1_OCTET_STRING; + *p++ = hashlen; + + /* Determine added ASN length */ + asn_len = p - sig; } + /* Copy the hash to be signed */ + memcpy( p, hash, hashlen ); + /* convert 'from' to base64 */ if (openvpn_base64_encode (sig, asn_len + hashlen, &in_b64) <= 0) { @@ -454,7 +471,7 @@ static inline int external_pkcs1_sign( void *ctx_voidptr, rv = 0; - done: +done: if (in_b64) free (in_b64); if (out_b64) @@ -468,6 +485,28 @@ static inline size_t external_key_len(void *vctx) return ctx->signature_length; } + +int +tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, + const char *cert_file, const char *cert_file_inline) +{ + ASSERT(NULL != ctx); + + tls_ctx_load_cert_file(ctx, cert_file, cert_file_inline); + + if (ctx->crt_chain == NULL) + return 0; + + ALLOC_OBJ_CLEAR (ctx->external_key, struct external_context); + ctx->external_key->signature_length = pk_get_len(&ctx->crt_chain->pk); + + ALLOC_OBJ_CLEAR (ctx->priv_key, pk_context); + if (0 != pk_init_ctx_rsa_alt(ctx->priv_key, ctx->external_key, + NULL, external_pkcs1_sign, external_key_len)) + return 0; + + return 1; +} #endif void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, @@ -480,14 +519,14 @@ void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_inline) { - if (0 != x509parse_crt(ctx->ca_chain, (const unsigned char *) ca_inline, + if (0 != x509_crt_parse(ctx->ca_chain, (unsigned char *) ca_inline, strlen(ca_inline))) msg (M_FATAL, "Cannot load inline CA certificates"); } else { /* Load CA file for verifying peer supplied certificate */ - if (0 != x509parse_crtfile(ctx->ca_chain, ca_file)) + if (0 != x509_crt_parse_file(ctx->ca_chain, ca_file)) msg (M_FATAL, "Cannot load CA certificate file %s", ca_file); } } @@ -499,16 +538,21 @@ tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file { ASSERT(NULL != ctx); + if (!ctx->crt_chain) + { + ALLOC_OBJ_CLEAR (ctx->crt_chain, x509_crt); + } + if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_inline) { - if (0 != x509parse_crt(ctx->crt_chain, - (const unsigned char *) extra_certs_inline, + if (0 != x509_crt_parse(ctx->crt_chain, + (const unsigned char *) extra_certs_inline, strlen(extra_certs_inline))) msg (M_FATAL, "Cannot load inline extra-certs file"); } else { - if (0 != x509parse_crtfile(ctx->crt_chain, extra_certs_file)) + if (0 != x509_crt_parse_file(ctx->crt_chain, extra_certs_file)) msg (M_FATAL, "Cannot load extra-certs file: %s", extra_certs_file); } } @@ -631,9 +675,9 @@ void tls_ctx_personalise_random(struct tls_root_ctx *ctx) if (NULL != ctx->crt_chain) { - x509_cert *cert = ctx->crt_chain; + x509_crt *cert = ctx->crt_chain; - sha2(cert->tbs.p, cert->tbs.len, sha256_hash, false); + sha256(cert->tbs.p, cert->tbs.len, sha256_hash, false); if ( 0 != memcmp(old_sha256_hash, sha256_hash, sizeof(sha256_hash))) { ctr_drbg_update(cd_ctx, sha256_hash, 32); @@ -707,24 +751,19 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl, if (ssl_ctx->allowed_ciphers) ssl_set_ciphersuites (ks_ssl->ctx, ssl_ctx->allowed_ciphers); + /* Disable record splitting (for now). OpenVPN assumes records are sent + * unfragmented, and changing that will require thorough review and + * testing. Since OpenVPN is not susceptible to BEAST, we can just + * disable record splitting as a quick fix. */ +#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING) + ssl_set_cbc_record_splitting (ks_ssl->ctx, SSL_CBC_RECORD_SPLITTING_DISABLED); +#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */ + /* Initialise authentication information */ if (is_server) - ssl_set_dh_param_ctx (ks_ssl->ctx, ssl_ctx->dhm_ctx ); -#if defined(ENABLE_PKCS11) - if (ssl_ctx->priv_key_pkcs11 != NULL) - ssl_set_own_cert_alt( ks_ssl->ctx, ssl_ctx->crt_chain, - ssl_ctx->priv_key_pkcs11, ssl_pkcs11_decrypt, ssl_pkcs11_sign, - ssl_pkcs11_key_len ); - else -#endif -#if defined(MANAGMENT_EXTERNAL_KEY) - if (ssl_ctx->external_key != NULL) - ssl_set_own_cert_alt( ks_ssl->ctx, ssl_ctx->crt_chain, - ssl_ctx->external_key, NULL, external_pkcs1_sign, - external_key_len ); - else -#endif - ssl_set_own_cert( ks_ssl->ctx, ssl_ctx->crt_chain, ssl_ctx->priv_key ); + ssl_set_dh_param_ctx (ks_ssl->ctx, ssl_ctx->dhm_ctx); + + ssl_set_own_cert (ks_ssl->ctx, ssl_ctx->crt_chain, ssl_ctx->priv_key); /* Initialise SSL verification */ #if P2MP_SERVER @@ -815,8 +854,8 @@ key_state_write_plaintext (struct key_state_ssl *ks, struct buffer *buf) if (0 == buf->len) { - return 0; perf_pop (); + return 0; } retval = ssl_write(ks->ctx, BPTR(buf), buf->len); @@ -1055,7 +1094,7 @@ key_state_read_plaintext (struct key_state_ssl *ks, struct buffer *buf, void print_details (struct key_state_ssl * ks_ssl, const char *prefix) { - const x509_cert *cert; + const x509_crt *cert; char s1[256]; char s2[256]; @@ -1068,7 +1107,7 @@ print_details (struct key_state_ssl * ks_ssl, const char *prefix) cert = ssl_get_peer_cert(ks_ssl->ctx); if (cert != NULL) { - openvpn_snprintf (s2, sizeof (s2), ", " counter_format " bit RSA", (counter_type) cert->rsa.len * 8); + openvpn_snprintf (s2, sizeof (s2), ", %zu bit key", pk_get_size(&cert->pk)); } msg (D_HANDSHAKE, "%s%s", s1, s2); diff --git a/src/openvpn/ssl_polarssl.h b/src/openvpn/ssl_polarssl.h index fc9aa78..b80a509 100644 --- a/src/openvpn/ssl_polarssl.h +++ b/src/openvpn/ssl_polarssl.h @@ -33,6 +33,7 @@ #include "syshead.h" #include <polarssl/ssl.h> +#include <polarssl/x509_crt.h> #if defined(ENABLE_PKCS11) #include <polarssl/pkcs11.h> @@ -64,9 +65,9 @@ struct tls_root_ctx { int endpoint; /**< Whether or not this is a server or a client */ dhm_context *dhm_ctx; /**< Diffie-Helmann-Merkle context */ - x509_cert *crt_chain; /**< Local Certificate chain */ - x509_cert *ca_chain; /**< CA chain for remote verification */ - rsa_context *priv_key; /**< Local private key */ + x509_crt *crt_chain; /**< Local Certificate chain */ + x509_crt *ca_chain; /**< CA chain for remote verification */ + pk_context *priv_key; /**< Local private key */ #if defined(ENABLE_PKCS11) pkcs11_context *priv_key_pkcs11; /**< PKCS11 private key */ #endif diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 9693b81..ca69b41 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -619,7 +619,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep string_replace_leading (subject, '-', '_'); /* extract the username (default is CN) */ - if (SUCCESS != x509_get_username (common_name, sizeof(common_name), + if (SUCCESS != backend_x509_get_username (common_name, sizeof(common_name), opt->x509_username_field, cert)) { if (!cert_depth) diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h index 6f118c9..01e453e 100644 --- a/src/openvpn/ssl_verify_backend.h +++ b/src/openvpn/ssl_verify_backend.h @@ -109,7 +109,7 @@ unsigned char *x509_get_sha1_hash (openvpn_x509_cert_t *cert, struct gc_arena *g * * @return \c FAILURE, \c or SUCCESS */ -result_t x509_get_username (char *common_name, int cn_len, +result_t backend_x509_get_username (char *common_name, int cn_len, char * x509_username_field, openvpn_x509_cert_t *peer_cert); /* diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c index e1118d6..4750f02 100644 --- a/src/openvpn/ssl_verify_openssl.c +++ b/src/openvpn/ssl_verify_openssl.c @@ -200,7 +200,7 @@ extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, } result_t -x509_get_username (char *common_name, int cn_len, +backend_x509_get_username (char *common_name, int cn_len, char * x509_username_field, X509 *peer_cert) { #ifdef ENABLE_X509ALTUSERNAME @@ -585,6 +585,8 @@ x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) BIO *in=NULL; int n,i; result_t retval = FAILURE; + struct gc_arena gc = gc_new(); + char *serial; in = BIO_new_file (crl_file, "r"); @@ -609,7 +611,8 @@ x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) for (i = 0; i < n; i++) { revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(peer_cert)) == 0) { - msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject); + serial = backend_x509_get_serial_hex(peer_cert, &gc); + msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE")); goto end; } } @@ -618,6 +621,7 @@ x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); end: + gc_free(&gc); BIO_free(in); if (crl) X509_CRL_free (crl); diff --git a/src/openvpn/ssl_verify_polarssl.c b/src/openvpn/ssl_verify_polarssl.c index f8f9ab5..ac252a3 100644 --- a/src/openvpn/ssl_verify_polarssl.c +++ b/src/openvpn/ssl_verify_polarssl.c @@ -38,14 +38,15 @@ #if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) #include "ssl_verify.h" -#include <polarssl/error.h> #include <polarssl/bignum.h> +#include <polarssl/error.h> +#include <polarssl/oid.h> #include <polarssl/sha1.h> #define MAX_SUBJECT_LENGTH 256 int -verify_callback (void *session_obj, x509_cert *cert, int cert_depth, +verify_callback (void *session_obj, x509_crt *cert, int cert_depth, int *flags) { struct tls_session *session = (struct tls_session *) session_obj; @@ -90,8 +91,8 @@ verify_callback (void *session_obj, x509_cert *cert, int cert_depth, #endif result_t -x509_get_username (char *cn, int cn_len, - char *x509_username_field, x509_cert *cert) +backend_x509_get_username (char *cn, int cn_len, + char *x509_username_field, x509_crt *cert) { x509_name *name; @@ -102,7 +103,7 @@ x509_get_username (char *cn, int cn_len, /* Find common name */ while( name != NULL ) { - if( memcmp( name->oid.p, OID_CN, OID_SIZE(OID_CN) ) == 0) + if( memcmp( name->oid.p, OID_AT_CN, OID_SIZE(OID_AT_CN) ) == 0) break; name = name->next; @@ -127,8 +128,6 @@ x509_get_username (char *cn, int cn_len, char * backend_x509_get_serial (openvpn_x509_cert_t *cert, struct gc_arena *gc) { - int ret = 0; - int i = 0; char *buf = NULL; size_t buflen = 0; mpi serial_mpi = { 0 }; @@ -172,14 +171,14 @@ backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, struct gc_arena *gc) buf = gc_malloc(len, true, gc); - if(x509parse_serial_gets(buf, len-1, &cert->serial) < 0) + if(x509_serial_gets(buf, len-1, &cert->serial) < 0) buf = NULL; return buf; } unsigned char * -x509_get_sha1_hash (x509_cert *cert, struct gc_arena *gc) +x509_get_sha1_hash (x509_crt *cert, struct gc_arena *gc) { unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); sha1(cert->tbs.p, cert->tbs.len, sha1_hash); @@ -187,14 +186,14 @@ x509_get_sha1_hash (x509_cert *cert, struct gc_arena *gc) } char * -x509_get_subject(x509_cert *cert, struct gc_arena *gc) +x509_get_subject(x509_crt *cert, struct gc_arena *gc) { char tmp_subject[MAX_SUBJECT_LENGTH] = {0}; char *subject = NULL; int ret = 0; - ret = x509parse_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); + ret = x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); if (ret > 0) { /* Allocate the required space for the subject */ @@ -224,70 +223,28 @@ x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert) while( name != NULL ) { char name_expand[64+8]; + const char *shortname; - if( name->oid.len == 2 && memcmp( name->oid.p, OID_X520, 2 ) == 0 ) + if( 0 == oid_get_attr_short_name(&name->oid, &shortname) ) { - switch( name->oid.p[2] ) - { - case X520_COMMON_NAME: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_CN", - cert_depth); break; - - case X520_COUNTRY: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_C", - cert_depth); break; - - case X520_LOCALITY: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_L", - cert_depth); break; - - case X520_STATE: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_ST", - cert_depth); break; - - case X520_ORGANIZATION: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_O", - cert_depth); break; - - case X520_ORG_UNIT: - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_OU", - cert_depth); break; - - default: - openvpn_snprintf (name_expand, sizeof(name_expand), - "X509_%d_0x%02X", cert_depth, name->oid.p[2]); - break; - } + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_%s", + cert_depth, shortname); } - else if( name->oid.len == 8 && memcmp( name->oid.p, OID_PKCS9, 8 ) == 0 ) - { - switch( name->oid.p[8] ) - { - case PKCS9_EMAIL: - openvpn_snprintf (name_expand, sizeof(name_expand), - "X509_%d_emailAddress", cert_depth); break; - - default: - openvpn_snprintf (name_expand, sizeof(name_expand), - "X509_%d_0x%02X", cert_depth, name->oid.p[8]); - break; - } - } - else - { - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?", - cert_depth); - } - - for( i = 0; i < name->val.len; i++ ) + else + { + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?", + cert_depth); + } + + for( i = 0; i < name->val.len; i++ ) { - if( i >= (int) sizeof( s ) - 1 ) - break; + if( i >= (int) sizeof( s ) - 1 ) + break; - c = name->val.p[i]; - if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) - s[i] = '?'; - else s[i] = c; + c = name->val.p[i]; + if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) + s[i] = '?'; + else s[i] = c; } s[i] = '\0'; @@ -301,7 +258,7 @@ x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert) } result_t -x509_verify_ns_cert_type(const x509_cert *cert, const int usage) +x509_verify_ns_cert_type(const x509_crt *cert, const int usage) { if (usage == NS_CERT_CHECK_NONE) return SUCCESS; @@ -316,7 +273,7 @@ x509_verify_ns_cert_type(const x509_cert *cert, const int usage) } result_t -x509_verify_cert_ku (x509_cert *cert, const unsigned * const expected_ku, +x509_verify_cert_ku (x509_crt *cert, const unsigned * const expected_ku, int expected_len) { result_t fFound = FAILURE; @@ -349,7 +306,7 @@ x509_verify_cert_ku (x509_cert *cert, const unsigned * const expected_ku, } result_t -x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid) +x509_verify_cert_eku (x509_crt *cert, const char * const expected_oid) { result_t fFound = FAILURE; @@ -368,8 +325,7 @@ x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid) char oid_num_str[1024]; const char *oid_str; - oid_str = x509_oid_get_description(oid); - if (oid_str != NULL) + if (0 == oid_get_extended_key_usage( oid, &oid_str )) { msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", oid_str, expected_oid); @@ -380,7 +336,7 @@ x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid) } } - if (0 < x509_oid_get_numeric_string( oid_num_str, + if (0 < oid_get_numeric_string( oid_num_str, sizeof (oid_num_str), oid)) { msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", @@ -399,7 +355,7 @@ x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid) } result_t -x509_write_pem(FILE *peercert_file, x509_cert *peercert) +x509_write_pem(FILE *peercert_file, x509_crt *peercert) { msg (M_WARN, "PolarSSL does not support writing peer certificate in PEM format"); return FAILURE; @@ -409,16 +365,18 @@ x509_write_pem(FILE *peercert_file, x509_cert *peercert) * check peer cert against CRL */ result_t -x509_verify_crl(const char *crl_file, x509_cert *cert, const char *subject) +x509_verify_crl(const char *crl_file, x509_crt *cert, const char *subject) { result_t retval = FAILURE; x509_crl crl = {0}; + struct gc_arena gc = gc_new(); + char *serial; - int polar_retval = x509parse_crlfile(&crl, crl_file); + int polar_retval = x509_crl_parse_file(&crl, crl_file); if (polar_retval != 0) { char errstr[128]; - error_strerror(polar_retval, errstr, sizeof(errstr)); + polarssl_strerror(polar_retval, errstr, sizeof(errstr)); msg (M_WARN, "CRL: cannot read CRL from file %s (%s)", crl_file, errstr); goto end; } @@ -432,9 +390,10 @@ x509_verify_crl(const char *crl_file, x509_cert *cert, const char *subject) goto end; } - if (0 != x509parse_revoked(cert, &crl)) + if (0 != x509_crt_revoked(cert, &crl)) { - msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED", subject); + serial = backend_x509_get_serial_hex(cert, &gc); + msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE")); goto end; } @@ -442,6 +401,7 @@ x509_verify_crl(const char *crl_file, x509_cert *cert, const char *subject) msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); end: + gc_free(&gc); x509_crl_free(&crl); return retval; } diff --git a/src/openvpn/ssl_verify_polarssl.h b/src/openvpn/ssl_verify_polarssl.h index b259081..b5157ed 100644 --- a/src/openvpn/ssl_verify_polarssl.h +++ b/src/openvpn/ssl_verify_polarssl.h @@ -33,11 +33,11 @@ #include "syshead.h" #include "misc.h" #include "manage.h" -#include <polarssl/x509.h> +#include <polarssl/x509_crt.h> #ifndef __OPENVPN_X509_CERT_T_DECLARED #define __OPENVPN_X509_CERT_T_DECLARED -typedef x509_cert openvpn_x509_cert_t; +typedef x509_crt openvpn_x509_cert_t; #endif /** @name Function for authenticating a new connection from a remote OpenVPN peer @@ -72,7 +72,7 @@ typedef x509_cert openvpn_x509_cert_t; * * @return The return value is 0 unless a fatal error occurred. */ -int verify_callback (void *session_obj, x509_cert *cert, int cert_depth, +int verify_callback (void *session_obj, x509_crt *cert, int cert_depth, int *flags); /** @} name Function for authenticating a new connection from a remote OpenVPN peer */ diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index ffba4e8..24926ae 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -47,6 +47,7 @@ #ifdef _MSC_VER // Visual Studio #define __func__ __FUNCTION__ +#define __attribute__(x) #endif #if defined(__APPLE__) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 3e20215..bb019c0 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -66,6 +66,8 @@ static void netsh_command (const struct argv *a, int n); static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc); +static DWORD get_adapter_index_flexible (const char *name); + #endif #ifdef TARGET_SOLARIS @@ -633,7 +635,7 @@ void delete_route_connected_v6_net(struct tuntap * tt, * is still point to point and no layer 2 resolution is done... */ -char * +const char * create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) { in_addr_t remote; @@ -642,7 +644,7 @@ create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) if ( remote == tt->local ) remote ++; - return print_in_addr_t (remote, 0, &gc); + return print_in_addr_t (remote, 0, gc); } #endif @@ -1228,18 +1230,22 @@ do_ifconfig (struct tuntap *tt, if ( do_ipv6 ) { char * saved_actual; + char iface[64]; + DWORD idx; if (!strcmp (actual, "NULL")) msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Windows adapter, you must also specify --dev-node"); - /* example: netsh interface ipv6 set address MyTap 2001:608:8003::d store=active */ + idx = get_adapter_index_flexible(actual); + openvpn_snprintf(iface, sizeof(iface), "interface=%lu", idx); + + /* example: netsh interface ipv6 set address interface=42 2001:608:8003::d store=active */ argv_printf (&argv, - "%s%sc interface ipv6 set address %s %s store=active", + "%s%sc interface ipv6 set address %s %s store=active", get_win_sys_path(), NETSH_PATH_SUFFIX, - actual, - ifconfig_ipv6_local ); - + win32_version_info() == WIN_XP ? actual : iface, + ifconfig_ipv6_local); netsh_command (&argv, 4); /* explicit route needed */ @@ -1249,6 +1255,8 @@ do_ifconfig (struct tuntap *tt, */ saved_actual = tt->actual_name; tt->actual_name = (char*) actual; + /* we use adapter_index in add_route_ipv6 */ + tt->adapter_index = idx; add_route_connected_v6_net(tt, es); tt->actual_name = saved_actual; } @@ -2389,7 +2397,6 @@ close_tun (struct tuntap *tt) } else if (tt) /* close and destroy */ { - struct gc_arena gc = gc_new (); struct argv argv; /* setup command, close tun dev (clears tt->actual_name!), run command diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index f35c96b..6c6ac4c 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -46,6 +46,34 @@ #include "memdbg.h" +#include "win32_wfp.h" + +#ifdef HAVE_VERSIONHELPERS_H +#include <versionhelpers.h> +#else +#include "compat-versionhelpers.h" +#endif + +/* WFP function pointers. Initialized in win_wfp_init_funcs() */ +func_ConvertInterfaceIndexToLuid ConvertInterfaceIndexToLuid = NULL; +func_FwpmEngineOpen0 FwpmEngineOpen0 = NULL; +func_FwpmEngineClose0 FwpmEngineClose0 = NULL; +func_FwpmFilterAdd0 FwpmFilterAdd0 = NULL; +func_FwpmSubLayerAdd0 FwpmSubLayerAdd0 = NULL; +func_FwpmSubLayerDeleteByKey0 FwpmSubLayerDeleteByKey0 = NULL; +func_FwpmFreeMemory0 FwpmFreeMemory0 = NULL; +func_FwpmGetAppIdFromFileName0 FwpmGetAppIdFromFileName0 = NULL; + +/* + * WFP firewall name. + */ +WCHAR * FIREWALL_NAME = L"OpenVPN"; /* GLOBAL */ + +/* + * WFP handle and GUID. + */ +static HANDLE m_hEngineHandle = NULL; /* GLOBAL */ + /* * Windows internal socket API state (opaque). */ @@ -324,6 +352,53 @@ net_event_win32_close (struct net_event_win32 *ne) * (2) Service mode -- map Windows event object to SIGTERM */ +static void +win_trigger_event(struct win32_signal *ws) +{ + if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read)) + SetEvent (ws->in.read); + else /* generate a key-press event */ + { + DWORD tmp; + INPUT_RECORD ir; + HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + + CLEAR(ir); + ir.EventType = KEY_EVENT; + ir.Event.KeyEvent.bKeyDown = true; + if (!stdin_handle || !WriteConsoleInput(stdin_handle, &ir, 1, &tmp)) + msg(M_WARN|M_ERRNO, "WARN: win_trigger_event: WriteConsoleInput"); + } +} + +/* + * Callback to handle console ctrl events + */ +static bool WINAPI +win_ctrl_handler (DWORD signum) +{ + msg(D_LOW, "win_ctrl_handler: signal received (code=%lu)", (unsigned long) signum); + + if (siginfo_static.signal_received == SIGTERM) + return true; + + switch (signum) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + throw_signal(SIGTERM); + /* trigget the win32_signal to interrupt the event loop */ + win_trigger_event(&win32_signal); + return true; + break; + default: + msg(D_LOW, "win_ctrl_handler: signal (code=%lu) not handled", (unsigned long) signum); + break; + } + /* pass all other signals to the next handler */ + return false; +} + void win32_signal_clear (struct win32_signal *ws) { @@ -403,6 +478,9 @@ win32_signal_open (struct win32_signal *ws, ws->mode = WSO_MODE_SERVICE; } } + /* set the ctrl handler in both console and service modes */ + if (!SetConsoleCtrlHandler ((PHANDLER_ROUTINE) win_ctrl_handler, true)) + msg (M_WARN|M_ERRNO, "WARN: SetConsoleCtrlHandler failed"); } static bool @@ -512,6 +590,9 @@ win32_signal_get (struct win32_signal *ws) case 0x3E: /* F4 -> TERM */ ret = SIGTERM; break; + case 0x03: /* CTRL-C -> TERM */ + ret = SIGTERM; + break; } } if (ret) @@ -763,7 +844,12 @@ win_safe_filename (const char *fn) static char * env_block (const struct env_set *es) { - char * force_path = "PATH=C:\\Windows\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem"; + char force_path[256]; + char *sysroot = get_win_sys_path(); + + if (!openvpn_snprintf(force_path, sizeof(force_path), "PATH=%s\\System32;%s;%s\\System32\\Wbem", + sysroot, sysroot, sysroot)) + msg(M_WARN, "env_block: default path truncated to %s", force_path); if (es) { @@ -1019,4 +1105,251 @@ win_get_tempdir() WideCharToMultiByte (CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof (tmpdir), NULL, NULL); return tmpdir; } + +bool +win_wfp_init_funcs () +{ + /* Initialize all WFP-related function pointers */ + HMODULE iphlpapiHandle; + HMODULE fwpuclntHandle; + + iphlpapiHandle = LoadLibrary("iphlpapi.dll"); + if (iphlpapiHandle == NULL) + { + msg (M_NONFATAL, "Can't load iphlpapi.dll"); + return false; + } + + fwpuclntHandle = LoadLibrary("fwpuclnt.dll"); + if (fwpuclntHandle == NULL) + { + msg (M_NONFATAL, "Can't load fwpuclnt.dll"); + return false; + } + + ConvertInterfaceIndexToLuid = (func_ConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHandle, "ConvertInterfaceIndexToLuid"); + FwpmFilterAdd0 = (func_FwpmFilterAdd0)GetProcAddress(fwpuclntHandle, "FwpmFilterAdd0"); + FwpmEngineOpen0 = (func_FwpmEngineOpen0)GetProcAddress(fwpuclntHandle, "FwpmEngineOpen0"); + FwpmEngineClose0 = (func_FwpmEngineClose0)GetProcAddress(fwpuclntHandle, "FwpmEngineClose0"); + FwpmSubLayerAdd0 = (func_FwpmSubLayerAdd0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerAdd0"); + FwpmSubLayerDeleteByKey0 = (func_FwpmSubLayerDeleteByKey0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerDeleteByKey0"); + FwpmFreeMemory0 = (func_FwpmFreeMemory0)GetProcAddress(fwpuclntHandle, "FwpmFreeMemory0"); + FwpmGetAppIdFromFileName0 = (func_FwpmGetAppIdFromFileName0)GetProcAddress(fwpuclntHandle, "FwpmGetAppIdFromFileName0"); + + if (!ConvertInterfaceIndexToLuid || + !FwpmFilterAdd0 || + !FwpmEngineOpen0 || + !FwpmEngineClose0 || + !FwpmSubLayerAdd0 || + !FwpmSubLayerDeleteByKey0 || + !FwpmFreeMemory0 || + !FwpmGetAppIdFromFileName0) + { + msg (M_NONFATAL, "Can't get address for all WFP-related procedures."); + return false; + } + + return true; +} + +bool +win_wfp_add_filter (HANDLE engineHandle, + const FWPM_FILTER0 *filter, + PSECURITY_DESCRIPTOR sd, + UINT64 *id) +{ + if (FwpmFilterAdd0(engineHandle, filter, sd, id) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't add WFP filter"); + return false; + } + return true; +} + +bool +win_wfp_block_dns (const NET_IFINDEX index) +{ + FWPM_SESSION0 session = {0}; + FWPM_SUBLAYER0 SubLayer = {0}; + NET_LUID tapluid; + UINT64 filterid; + WCHAR openvpnpath[MAX_PATH]; + FWP_BYTE_BLOB *openvpnblob = NULL; + FWPM_FILTER0 Filter = {0}; + FWPM_FILTER_CONDITION0 Condition[2] = {0}; + + /* Add temporary filters which don't survive reboots or crashes. */ + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + dmsg (D_LOW, "Opening WFP engine"); + + if (FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &m_hEngineHandle) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't open WFP engine"); + return false; + } + + if (UuidCreate(&SubLayer.subLayerKey) != NO_ERROR) + return false; + + /* Populate packet filter layer information. */ + SubLayer.displayData.name = FIREWALL_NAME; + SubLayer.displayData.description = FIREWALL_NAME; + SubLayer.flags = 0; + SubLayer.weight = 0x100; + + /* Add packet filter to our interface. */ + dmsg (D_LOW, "Adding WFP sublayer"); + if (FwpmSubLayerAdd0(m_hEngineHandle, &SubLayer, NULL) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't add WFP sublayer"); + return false; + } + + dmsg (D_LOW, "Blocking DNS using WFP"); + if (ConvertInterfaceIndexToLuid(index, &tapluid) != NO_ERROR) + { + msg (M_NONFATAL, "Can't convert interface index to LUID"); + return false; + } + dmsg (D_LOW, "Tap Luid: %I64d", tapluid.Value); + + /* Get OpenVPN path. */ + GetModuleFileNameW(NULL, openvpnpath, MAX_PATH); + + if (FwpmGetAppIdFromFileName0(openvpnpath, &openvpnblob) != ERROR_SUCCESS) + return false; + + /* Prepare filter. */ + Filter.subLayerKey = SubLayer.subLayerKey; + Filter.displayData.name = FIREWALL_NAME; + Filter.weight.type = FWP_EMPTY; + Filter.filterCondition = Condition; + Filter.numFilterConditions = 2; + + /* First filter. Block IPv4 DNS queries except from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_BLOCK; + + Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + Condition[0].matchType = FWP_MATCH_EQUAL; + Condition[0].conditionValue.type = FWP_UINT16; + Condition[0].conditionValue.uint16 = 53; + + Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID; + Condition[1].matchType = FWP_MATCH_NOT_EQUAL; + Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE; + Condition[1].conditionValue.byteBlob = openvpnblob; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Block IPv4 DNS) added with ID=%I64d", filterid); + + /* Second filter. Block IPv6 DNS queries except from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Block IPv6 DNS) added with ID=%I64d", filterid); + + /* Third filter. Permit IPv4 DNS queries from TAP. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_PERMIT; + + Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE; + Condition[1].matchType = FWP_MATCH_EQUAL; + Condition[1].conditionValue.type = FWP_UINT64; + Condition[1].conditionValue.uint64 = &tapluid.Value; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Permit IPv4 DNS queries from TAP) added with ID=%I64d", filterid); + + /* Forth filter. Permit IPv6 DNS queries from TAP. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Permit IPv6 DNS queries from TAP) added with ID=%I64d", filterid); + + FwpmFreeMemory0((void **)&openvpnblob); + return true; + + err: + FwpmFreeMemory0((void **)&openvpnblob); + return false; +} + +bool +win_wfp_uninit() +{ + dmsg (D_LOW, "Uninitializing WFP"); + if (m_hEngineHandle) { + FwpmEngineClose0(m_hEngineHandle); + m_hEngineHandle = NULL; + } + return true; +} + +int +win32_version_info() +{ + if (!IsWindowsXPOrGreater()) + { + msg (M_FATAL, "Error: Windows version must be XP or greater."); + } + + if (!IsWindowsVistaOrGreater()) + { + return WIN_XP; + } + + if (!IsWindows7OrGreater()) + { + return WIN_VISTA; + } + + if (!IsWindows8OrGreater()) + { + return WIN_7; + } + else + { + return WIN_8; + } +} + +const char * +win32_version_string(struct gc_arena *gc, bool add_name) +{ + int version = win32_version_info(); + struct buffer out = alloc_buf_gc (256, gc); + + switch (version) + { + case WIN_XP: + buf_printf (&out, "5.1%s", add_name ? " (Windows XP)" : ""); + break; + case WIN_VISTA: + buf_printf (&out, "6.0%s", add_name ? " (Windows Vista)" : ""); + break; + case WIN_7: + buf_printf (&out, "6.1%s", add_name ? " (Windows 7)" : ""); + break; + case WIN_8: + buf_printf (&out, "6.2%s", add_name ? " (Windows 8 or greater)" : ""); + break; + default: + msg (M_NONFATAL, "Unknown Windows version: %d", version); + buf_printf (&out, "0.0%s", add_name ? " (unknown)" : ""); + break; + } + + return (const char *)out.data; +} + #endif diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index cc18f02..0990182 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -271,5 +271,22 @@ const char *win_get_tempdir(); /* Convert a string from UTF-8 to UCS-2 */ WCHAR *wide_string (const char* utf8, struct gc_arena *gc); +bool win_wfp_init_funcs(); +bool win_wfp_block_dns(const NET_IFINDEX index); +bool win_wfp_uninit(); + +#define WIN_XP 0 +#define WIN_VISTA 1 +#define WIN_7 2 +#define WIN_8 3 + +int win32_version_info(); + +/* +String representation of Windows version number and name, see +https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx +*/ +const char * win32_version_string(struct gc_arena *gc, bool add_name); + #endif #endif diff --git a/src/openvpn/win32_wfp.h b/src/openvpn/win32_wfp.h new file mode 100644 index 0000000..7559e5b --- /dev/null +++ b/src/openvpn/win32_wfp.h @@ -0,0 +1,359 @@ +/* + This Software is provided under the Zope Public License (ZPL) Version 2.1. + + Copyright (c) 2009, 2010 by the mingw-w64 project + + See the AUTHORS file for the list of contributors to the mingw-w64 project. + + This license has been certified as open source. It has also been designated + as GPL compatible by the Free Software Foundation (FSF). + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions in source code must retain the accompanying copyright + notice, this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + 3. Names of the copyright holders must not be used to endorse or promote + products derived from this software without prior written permission + from the copyright holders. + 4. The right to distribute this software or to use it for any purpose does + not give you the right to use Servicemarks (sm) or Trademarks (tm) of + the copyright holders. Use of them is covered by separate agreement + with the copyright holders. + 5. If any files are modified, you must cause the modified files to carry + prominent notices stating that you changed the files and the date of + any change. + + Disclaimer + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Windows Filtering Platform (WFP) related prototypes, mostly stripped out of + * mingw-w64. + */ + +/* + * WFP-related defines and GUIDs. + */ + +#ifndef WIN32_WFP_H +#define WIN32_WFP_H + +#include <initguid.h> +#include <iphlpapi.h> +#include <rpc.h> +#include <rpcdce.h> + +#ifndef FWPM_SESSION_FLAG_DYNAMIC +#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001 +#endif + +// c38d57d1-05a7-4c33-904f-7fbceee60e82 +DEFINE_GUID( + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + 0xc38d57d1, + 0x05a7, + 0x4c33, + 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82 +); + +// 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 +DEFINE_GUID( + FWPM_LAYER_ALE_AUTH_CONNECT_V6, + 0x4a72393b, + 0x319f, + 0x44bc, + 0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4 +); + +// d78e1e87-8644-4ea5-9437-d809ecefc971 +DEFINE_GUID( + FWPM_CONDITION_ALE_APP_ID, + 0xd78e1e87, + 0x8644, + 0x4ea5, + 0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71 +); + +// c35a604d-d22b-4e1a-91b4-68f674ee674b +DEFINE_GUID( + FWPM_CONDITION_IP_REMOTE_PORT, + 0xc35a604d, + 0xd22b, + 0x4e1a, + 0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b +); + +// 4cd62a49-59c3-4969-b7f3-bda5d32890a4 +DEFINE_GUID( + FWPM_CONDITION_IP_LOCAL_INTERFACE, + 0x4cd62a49, + 0x59c3, + 0x4969, + 0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4 +); + +/* From fwptypes.h */ + +#define FWP_ACTION_FLAG_TERMINATING (0x00001000) +#define FWP_ACTION_FLAG_NON_TERMINATING (0x00002000) + +#define FWP_ACTION_BLOCK (0x1 | FWP_ACTION_FLAG_TERMINATING) +#define FWP_ACTION_PERMIT (0x2 | FWP_ACTION_FLAG_TERMINATING) + +typedef UINT32 FWP_ACTION_TYPE; + +typedef enum FWP_DATA_TYPE_ { + FWP_EMPTY = 0, + FWP_UINT8 = 1, + FWP_UINT16 = 2, + FWP_UINT32 = 3, + FWP_UINT64 = 4, + FWP_INT8 = 5, + FWP_INT16 = 6, + FWP_INT32 = 7, + FWP_INT64 = 8, + FWP_FLOAT = 9, + FWP_DOUBLE = 10, + FWP_BYTE_ARRAY16_TYPE = 11, + FWP_BYTE_BLOB_TYPE = 12, + FWP_SID = 13, + FWP_SECURITY_DESCRIPTOR_TYPE = 14, + FWP_TOKEN_INFORMATION_TYPE = 15, + FWP_TOKEN_ACCESS_INFORMATION_TYPE = 16, + FWP_UNICODE_STRING_TYPE = 17, + FWP_BYTE_ARRAY6_TYPE = 18, + FWP_SINGLE_DATA_TYPE_MAX = 0xff, + FWP_V4_ADDR_MASK = 0x100, + FWP_V6_ADDR_MASK = 0x101, + FWP_RANGE_TYPE = 0x102, + FWP_DATA_TYPE_MAX = 0x103 +} FWP_DATA_TYPE; + +typedef enum FWP_MATCH_TYPE_ { + FWP_MATCH_EQUAL = 0, + FWP_MATCH_GREATER = 1, + FWP_MATCH_LESS = 2, + FWP_MATCH_GREATER_OR_EQUAL = 3, + FWP_MATCH_LESS_OR_EQUAL = 4, + FWP_MATCH_RANGE = 5, + FWP_MATCH_FLAGS_ALL_SET = 6, + FWP_MATCH_FLAGS_ANY_SET = 7, + FWP_MATCH_FLAGS_NONE_SET = 8, + FWP_MATCH_EQUAL_CASE_INSENSITIVE = 9, + FWP_MATCH_NOT_EQUAL = 10, + FWP_MATCH_TYPE_MAX = 11 +} FWP_MATCH_TYPE; + +typedef struct FWP_BYTE_ARRAY6_ { + UINT8 byteArray6[6]; +} FWP_BYTE_ARRAY6; + +typedef struct FWP_BYTE_ARRAY16_ { + UINT8 byteArray16[16]; +} FWP_BYTE_ARRAY16; + +typedef struct FWP_BYTE_BLOB_ { + UINT32 size; + UINT8 *data; +} FWP_BYTE_BLOB; + +typedef struct FWP_TOKEN_INFORMATION_ { + ULONG sidCount; + PSID_AND_ATTRIBUTES sids; + ULONG restrictedSidCount; + PSID_AND_ATTRIBUTES restrictedSids; +} FWP_TOKEN_INFORMATION; + +typedef struct FWP_VALUE0_ { + FWP_DATA_TYPE type; + union { + UINT8 uint8; + UINT16 uint16; + UINT32 uint32; + UINT64 *uint64; + INT8 int8; + INT16 int16; + INT32 int32; + INT64 *int64; + float float32; + double *double64; + FWP_BYTE_ARRAY16 *byteArray16; + FWP_BYTE_BLOB *byteBlob; + SID *sid; + FWP_BYTE_BLOB *sd; + FWP_TOKEN_INFORMATION *tokenInformation; + FWP_BYTE_BLOB *tokenAccessInformation; + LPWSTR unicodeString; + FWP_BYTE_ARRAY6 *byteArray6; + }; +} FWP_VALUE0; + +typedef struct FWP_V4_ADDR_AND_MASK_ { + UINT32 addr; + UINT32 mask; +} FWP_V4_ADDR_AND_MASK; + +typedef struct FWP_V6_ADDR_AND_MASK_ { + UINT8 addr[16]; + UINT8 prefixLength; +} FWP_V6_ADDR_AND_MASK; + +typedef struct FWP_RANGE0_ { + FWP_VALUE0 valueLow; + FWP_VALUE0 valueHigh; +} FWP_RANGE0; + +typedef struct FWP_CONDITION_VALUE0_ { + FWP_DATA_TYPE type; + union { + UINT8 uint8; + UINT16 uint16; + UINT32 uint32; + UINT64 *uint64; + INT8 int8; + INT16 int16; + INT32 int32; + INT64 *int64; + float float32; + double *double64; + FWP_BYTE_ARRAY16 *byteArray16; + FWP_BYTE_BLOB *byteBlob; + SID *sid; + FWP_BYTE_BLOB *sd; + FWP_TOKEN_INFORMATION *tokenInformation; + FWP_BYTE_BLOB *tokenAccessInformation; + LPWSTR unicodeString; + FWP_BYTE_ARRAY6 *byteArray6; + FWP_V4_ADDR_AND_MASK *v4AddrMask; + FWP_V6_ADDR_AND_MASK *v6AddrMask; + FWP_RANGE0 *rangeValue; + }; +} FWP_CONDITION_VALUE0; + +typedef struct FWPM_DISPLAY_DATA0_ { + wchar_t *name; + wchar_t *description; +} FWPM_DISPLAY_DATA0; + +/* From fwpmtypes.h */ + +typedef struct FWPM_ACTION0_ { + FWP_ACTION_TYPE type; + union { + GUID filterType; + GUID calloutKey; + }; +} FWPM_ACTION0; + +typedef struct FWPM_SESSION0_ { + GUID sessionKey; + FWPM_DISPLAY_DATA0 displayData; + UINT32 flags; + UINT32 txnWaitTimeoutInMSec; + DWORD processId; + SID *sid; + wchar_t *username; + BOOL kernelMode; +} FWPM_SESSION0; + +typedef struct FWPM_SUBLAYER0_ { + GUID subLayerKey; + FWPM_DISPLAY_DATA0 displayData; + UINT16 flags; + GUID *providerKey; + FWP_BYTE_BLOB providerData; + UINT16 weight; +} FWPM_SUBLAYER0; + +typedef struct FWPM_FILTER_CONDITION0_ { + GUID fieldKey; + FWP_MATCH_TYPE matchType; + FWP_CONDITION_VALUE0 conditionValue; +} FWPM_FILTER_CONDITION0; + +typedef struct FWPM_FILTER0_ { + GUID filterKey; + FWPM_DISPLAY_DATA0 displayData; + UINT32 flags; + GUID *providerKey; + FWP_BYTE_BLOB providerData; + GUID layerKey; + GUID subLayerKey; + FWP_VALUE0 weight; + UINT32 numFilterConditions; + FWPM_FILTER_CONDITION0 *filterCondition; + FWPM_ACTION0 action; + union { + UINT64 rawContext; + GUID providerContextKey; + }; + GUID *reserved; + UINT64 filterId; + FWP_VALUE0 effectiveWeight; +} FWPM_FILTER0; + +/* Typedefs of WFP functions */ + +#define NETIO_STATUS DWORD + +typedef NETIO_STATUS *(WINAPI *func_ConvertInterfaceIndexToLuid)( + NET_IFINDEX InterfaceIndex, + PNET_LUID InterfaceLuid +); + +typedef DWORD *(WINAPI *func_FwpmEngineOpen0)( + const wchar_t *serverName, + UINT32 authnService, + SEC_WINNT_AUTH_IDENTITY_W *authIdentity, + const FWPM_SESSION0 *session, + HANDLE *engineHandle +); + +typedef DWORD *(WINAPI *func_FwpmEngineClose0)( + HANDLE engineHandle +); + +typedef DWORD *(WINAPI *func_FwpmFilterAdd0)( + HANDLE engineHandle, + const FWPM_FILTER0 *filter, + PSECURITY_DESCRIPTOR sd, + UINT64 *id +); + +typedef DWORD *(WINAPI *func_FwpmSubLayerAdd0)( + HANDLE engineHandle, + const FWPM_SUBLAYER0 *subLayer, + PSECURITY_DESCRIPTOR sd +); + +typedef DWORD *(WINAPI *func_FwpmSubLayerDeleteByKey0)( + HANDLE engineHandle, + const GUID *key +); + +typedef void *(WINAPI *func_FwpmFreeMemory0)( + void **p +); + +typedef DWORD *(WINAPI *func_FwpmGetAppIdFromFileName0)( + const wchar_t *fileName, + FWP_BYTE_BLOB **appId +); + +#endif |