Message ID | 1559746770-21285-1-git-send-email-lstipakov@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series | [Openvpn-devel] Wintun - experimental support | expand |
If someone would like to test it - you can get a Windows binary from here: https://ci.appveyor.com/api/buildjobs/w20knnbut89uoeuw/artifacts/msvc%2Fimage%2Fbin.zip Note that interactive service doesn't (yet) work with Wintun, you need to run openvpn as a privileged process. For example, from Administrator command prompt: > c:\Temp\ovpn2_wintun>openvpn.exe --config lev.ovpn --windows-driver wintun ke 5. kesäk. 2019 klo 17.59 Lev Stipakov (lstipakov@gmail.com) kirjoitti: > From: Lev Stipakov <lev@openvpn.net> > > This adds experimental support for Wintun, > an alternative to tap-windows6 on Windows. > > To use wintun, specify > > --windows-driver wintun > > as config option. Default value is "tap-windows6". > > Unlike tap-windows6, Wintun returns up to 256 packets, > which have to be decapsulated and processed one by one. > We do it by reading data into a new buffer, processing > single packet and returning control to event loop > to give it a chance to write processed packet to link. > > If there are no pending writes and there is data in wintun > buffer, event loop triggers tun read. There we read single > packet from wintun buffer or, if it is empty, read from > tun driver. > > Signed-off-by: Lev Stipakov <lev@openvpn.net> > --- > src/openvpn/forward.c | 76 +++++++++++++++++- > src/openvpn/forward.h | 15 ++++ > src/openvpn/init.c | 7 ++ > src/openvpn/openvpn.h | 4 + > src/openvpn/options.c | 36 ++++++++- > src/openvpn/options.h | 1 + > src/openvpn/socket.c | 4 +- > src/openvpn/syshead.h | 1 + > src/openvpn/tun.c | 212 > +++++++++++++++++++++++++++++++++----------------- > src/openvpn/tun.h | 34 +++++++- > src/openvpn/win32.c | 119 ++++++++++++++++++++++++++-- > src/openvpn/win32.h | 19 ++++- > 12 files changed, 441 insertions(+), 87 deletions(-) > > diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c > index ad4c1f5..8077835 100644 > --- a/src/openvpn/forward.c > +++ b/src/openvpn/forward.c > @@ -2182,6 +2182,52 @@ io_wait_dowork(struct context *c, const unsigned > int flags) > dmsg(D_EVENT_WAIT, "I/O WAIT status=0x%04x", c->c2.event_set_status); > } > > +#ifdef _WIN32 > +/** > + * This processes single packet from wintun buffer, > + * which can contain up to 256 packets. > + */ > +static void > +process_incoming_wintun(struct context *c) > +{ > + struct buffer *buf = &c->c2.wintun_buf; > + uint32_t *size = NULL; > + int end_padding_len = 0; > + > + /* get packet size */ > + size = (uint32_t *)buf_read_alloc(buf, 4); > + if (size == NULL) > + { > + return; > + } > + > + /* skip start padding */ > + if (!buf_advance(buf, 12)) > + { > + return; > + } > + > + /* copy packet */ > + c->c2.buf = c->c2.buffers->read_tun_buf; > + buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame)); > + buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame)); > + if (!buf_copy_n(&c->c2.buf, buf, (int)*size)) > + { > + return; > + } > + > + /* this encrypts packet and puts it to c->c2.to_link buffer */ > + process_incoming_tun(c); > + > + /* skip end padding */ > + end_padding_len = (16 - (*size & 15)) % 16; > + if (!buf_advance(buf, end_padding_len)) > + { > + return; > + } > +} > +#endif > + > void > process_io(struct context *c) > { > @@ -2217,10 +2263,34 @@ process_io(struct context *c) > /* Incoming data on TUN device */ > else if (status & TUN_READ) > { > - read_incoming_tun(c); > - if (!IS_SIG(c)) > +#ifdef _WIN32 > + if (c->options.wintun) > + { > + /* only read from driver if there are no packets in buffer */ > + if (BLEN(&c->c2.wintun_buf) == 0) > + { > + read_incoming_tun(c); > + c->c2.wintun_buf = c->c2.buf; > + } > + if (!IS_SIG(c)) > + { > + process_incoming_wintun(c); > + } > + > + /* > + * We need to write processed wintun packet to link, > + * hence we give control back to event loop, which will > + * call us again if wintun buffer is not empty > + */ > + } > + else > +#endif > { > - process_incoming_tun(c); > + read_incoming_tun(c); > + if (!IS_SIG(c)) > + { > + process_incoming_tun(c); > + } > } > } > } > diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h > index 48202c0..ac3e932 100644 > --- a/src/openvpn/forward.h > +++ b/src/openvpn/forward.h > @@ -61,6 +61,7 @@ > #define IOW_MBUF (1<<7) > #define IOW_READ_TUN_FORCE (1<<8) > #define IOW_WAIT_SIGNAL (1<<9) > +#define IOW_READ_WINTUN_BUF (1<<10) > > #define IOW_READ (IOW_READ_TUN|IOW_READ_LINK) > > @@ -375,6 +376,12 @@ p2p_iow_flags(const struct context *c) > { > flags |= IOW_TO_TUN; > } > +#ifdef _WIN32 > + if (c->c2.wintun_buf.len > 0) > + { > + flags |= IOW_READ_WINTUN_BUF; > + } > +#endif > return flags; > } > > @@ -401,6 +408,14 @@ io_wait(struct context *c, const unsigned int flags) > } > c->c2.event_set_status = ret; > } > +#ifdef _WIN32 > + /* make sure we write to link before reading from wintun buffer, > + * otherwise we'll overwrite c2->to_link buffer and lose a packet */ > + else if (!(flags & IOW_TO_LINK) && (flags & IOW_READ_WINTUN_BUF)) > + { > + c->c2.event_set_status = TUN_READ; > + } > +#endif > else > { > /* slow path */ > diff --git a/src/openvpn/init.c b/src/openvpn/init.c > index ef26503..1625272 100644 > --- a/src/openvpn/init.c > +++ b/src/openvpn/init.c > @@ -1696,6 +1696,10 @@ do_init_tun(struct context *c) > !c->options.ifconfig_nowarn, > c->c2.es); > > +#ifdef _WIN32 > + c->c1.tuntap->wintun = c->options.wintun; > +#endif > + > init_tun_post(c->c1.tuntap, > &c->c2.frame, > &c->options.tuntap_options); > @@ -1738,6 +1742,9 @@ do_open_tun(struct context *c) > /* store (hide) interactive service handle in tuntap_options */ > c->c1.tuntap->options.msg_channel = c->options.msg_channel; > msg(D_ROUTE, "interactive service msg_channel=%u", (unsigned int) > c->options.msg_channel); > + > + c->c1.tuntap->wintun = c->options.wintun; > + > #endif > > /* allocate route list structure */ > diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h > index d11f61d..d694666 100644 > --- a/src/openvpn/openvpn.h > +++ b/src/openvpn/openvpn.h > @@ -382,6 +382,10 @@ struct context_2 > struct buffer buf; > struct buffer to_tun; > struct buffer to_link; > +#ifdef _WIN32 > + /* contains up to 256 packets */ > + struct buffer wintun_buf; > +#endif > > /* should we print R|W|r|w to console on packet transfers? */ > bool log_rw; > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index e34b65b..534a6be 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -845,6 +845,7 @@ init_options(struct options *o, const bool init_gc) > 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; > + o->wintun = false; > #endif > #if P2MP_SERVER > o->real_hash_size = 256; > @@ -2937,6 +2938,12 @@ options_postprocess_mutate_invariant(struct options > *options) > options->ifconfig_noexec = false; > } > > + /* for wintun kernel doesn't send DHCP requests, so use ipapi to set > IP address and netmask */ > + if (options->wintun) > + { > + options->tuntap_options.ip_win32_type = IPW32_SET_IPAPI; > + } > + > remap_redirect_gateway_flags(options); > #endif > > @@ -3980,6 +3987,26 @@ foreign_option(struct options *o, char *argv[], int > len, struct env_set *es) > } > } > > +#ifdef _WIN32 > +bool > +parse_windows_driver(const char *str, const int msglevel) > +{ > + if (streq(str, "tap")) > + { > + return false; > + } > + else if (streq(str, "wintun")) > + { > + return true; > + } > + else > + { > + msg(msglevel, "--windows-driver must be tap-windows6 or wintun"); > + return false; > + } > +} > +#endif > + > /* > * parse/print topology coding > */ > @@ -5220,6 +5247,13 @@ add_option(struct options *options, > VERIFY_PERMISSION(OPT_P_GENERAL); > options->dev_type = p[1]; > } > +#ifdef _WIN32 > + else if (streq(p[0], "windows-driver") && p[1] && !p[2]) > + { > + VERIFY_PERMISSION(OPT_P_GENERAL); > + options->wintun = parse_windows_driver(p[1], M_FATAL); > + } > +#endif > else if (streq(p[0], "dev-node") && p[1] && !p[2]) > { > VERIFY_PERMISSION(OPT_P_GENERAL); > @@ -7217,7 +7251,7 @@ add_option(struct options *options, > else if (streq(p[0], "show-adapters") && !p[1]) > { > VERIFY_PERMISSION(OPT_P_GENERAL); > - show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX); > + show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX, > options->wintun); > openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); /* exit point */ > } > else if (streq(p[0], "show-net") && !p[1]) > diff --git a/src/openvpn/options.h b/src/openvpn/options.h > index e2b3893..670b70a 100644 > --- a/src/openvpn/options.h > +++ b/src/openvpn/options.h > @@ -614,6 +614,7 @@ struct options > bool show_net_up; > int route_method; > bool block_outside_dns; > + bool wintun; > #endif > > bool use_peer_id; > diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c > index c472cf1..4352869 100644 > --- a/src/openvpn/socket.c > +++ b/src/openvpn/socket.c > @@ -1635,8 +1635,8 @@ static void > socket_frame_init(const struct frame *frame, struct link_socket *sock) > { > #ifdef _WIN32 > - overlapped_io_init(&sock->reads, frame, FALSE, false); > - overlapped_io_init(&sock->writes, frame, TRUE, false); > + overlapped_io_init(&sock->reads, frame, true, false, false); > + overlapped_io_init(&sock->writes, frame, false, false, false); > sock->rw_handle.read = sock->reads.overlapped.hEvent; > sock->rw_handle.write = sock->writes.overlapped.hEvent; > #endif > diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h > index 2b4c49f..e7cbf25 100644 > --- a/src/openvpn/syshead.h > +++ b/src/openvpn/syshead.h > @@ -39,6 +39,7 @@ > #ifdef _WIN32 > #include <windows.h> > #include <winsock2.h> > +#include <tlhelp32.h> > #define sleep(x) Sleep((x)*1000) > #define random rand > #define srandom srand > diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c > index e929b50..5fd8339 100644 > --- a/src/openvpn/tun.c > +++ b/src/openvpn/tun.c > @@ -788,8 +788,8 @@ init_tun_post(struct tuntap *tt, > { > tt->options = *options; > #ifdef _WIN32 > - overlapped_io_init(&tt->reads, frame, FALSE, true); > - overlapped_io_init(&tt->writes, frame, TRUE, true); > + overlapped_io_init(&tt->reads, frame, true, true, tt->wintun); > + overlapped_io_init(&tt->writes, frame, false, true, tt->wintun); > tt->rw_handle.read = tt->reads.overlapped.hEvent; > tt->rw_handle.write = tt->writes.overlapped.hEvent; > tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; > @@ -3430,7 +3430,7 @@ tun_finalize( > } > > const struct tap_reg * > -get_tap_reg(struct gc_arena *gc) > +get_tap_reg(struct gc_arena *gc, bool wintun) > { > HKEY adapter_key; > LONG status; > @@ -3461,6 +3461,20 @@ get_tap_reg(struct gc_arena *gc) > char net_cfg_instance_id_string[] = "NetCfgInstanceId"; > char net_cfg_instance_id[256]; > DWORD data_type; > + char expected_component_id[256]; > + char expected_root_component_id[256]; > + char net_luid_index_string[] = "NetLuidIndex"; > + DWORD luid_index; > + > + if (wintun) > + { > + strcpy(expected_component_id, WINTUN_COMPONENT_ID); > + } > + else > + { > + strcpy(expected_component_id, TAP_WIN_COMPONENT_ID); > + } > + openvpn_snprintf(expected_root_component_id, > sizeof(expected_root_component_id), "root\\%s", expected_component_id); > > len = sizeof(enum_name); > status = RegEnumKeyEx( > @@ -3525,13 +3539,27 @@ get_tap_reg(struct gc_arena *gc) > > if (status == ERROR_SUCCESS && data_type == REG_SZ) > { > - if (!strcmp(component_id, TAP_WIN_COMPONENT_ID) || > - !strcmp(component_id, "root\\" > TAP_WIN_COMPONENT_ID)) > + if (!strcmp(component_id, expected_component_id) || > + !strcmp(component_id, expected_root_component_id)) > { > struct tap_reg *reg; > ALLOC_OBJ_CLEAR_GC(reg, struct tap_reg, gc); > reg->guid = string_alloc(net_cfg_instance_id, gc); > > + len = sizeof(luid_index); > + status = RegQueryValueEx( > + unit_key, > + net_luid_index_string, > + NULL, > + &data_type, > + (LPBYTE)&luid_index, > + &len); > + > + if (status == ERROR_SUCCESS && data_type == > REG_DWORD) > + { > + reg->luid_index = luid_index; > + } > + > /* link into return list */ > if (!first) > { > @@ -3744,7 +3772,7 @@ show_valid_win32_tun_subnets(void) > } > > void > -show_tap_win_adapters(int msglev, int warnlev) > +show_tap_win_adapters(int msglev, int warnlev, bool wintun) > { > struct gc_arena gc = gc_new(); > > @@ -3758,10 +3786,10 @@ show_tap_win_adapters(int msglev, int warnlev) > const struct tap_reg *tr1; > const struct panel_reg *pr; > > - const struct tap_reg *tap_reg = get_tap_reg(&gc); > + const struct tap_reg *tap_reg = get_tap_reg(&gc, wintun); > const struct panel_reg *panel_reg = get_panel_reg(&gc); > > - msg(msglev, "Available TAP-WIN32 adapters [name, GUID]:"); > + msg(msglev, "Available %s adapters [name, GUID]:", wintun ? "WinTun" > : "TAP-WIN32"); > > /* loop through each TAP-Windows adapter registry entry */ > for (tr = tap_reg; tr != NULL; tr = tr->next) > @@ -3883,13 +3911,14 @@ at_least_one_tap_win(const struct tap_reg *tap_reg) > } > > /* > - * Get an adapter GUID and optional actual_name from the > + * Get an adapter GUID, LUID index and optional actual_name from the > * registry for the TAP device # = device_number. > */ > static const char * > get_unspecified_device_guid(const int device_number, > char *actual_name, > int actual_name_size, > + int *luid_index, > const struct tap_reg *tap_reg_src, > const struct panel_reg *panel_reg_src, > struct gc_arena *gc) > @@ -3938,6 +3967,11 @@ get_unspecified_device_guid(const int device_number, > } > } > > + if (luid_index) > + { > + *luid_index = tap_reg->luid_index; > + } > + > /* Save GUID for return value */ > ret = alloc_buf_gc(256, gc); > buf_printf(&ret, "%s", tap_reg->guid); > @@ -4536,7 +4570,7 @@ get_adapter_index_flexible(const char *name) /* > actual name or GUID */ > } > if (index == TUN_ADAPTER_INDEX_INVALID) > { > - const struct tap_reg *tap_reg = get_tap_reg(&gc); > + const struct tap_reg *tap_reg = get_tap_reg(&gc, false); > const struct panel_reg *panel_reg = get_panel_reg(&gc); > const char *guid = name_to_guid(name, tap_reg, panel_reg); > index = get_adapter_index_method_1(guid); > @@ -4663,7 +4697,7 @@ void > tap_allow_nonadmin_access(const char *dev_node) > { > struct gc_arena gc = gc_new(); > - const struct tap_reg *tap_reg = get_tap_reg(&gc); > + const struct tap_reg *tap_reg = get_tap_reg(&gc, false); > const struct panel_reg *panel_reg = get_panel_reg(&gc); > const char *device_guid = NULL; > HANDLE hand; > @@ -4717,6 +4751,7 @@ tap_allow_nonadmin_access(const char *dev_node) > actual_buffer, > > sizeof(actual_buffer), > tap_reg, > + NULL, > panel_reg, > &gc); > > @@ -5239,7 +5274,7 @@ out: > static const char * > netsh_get_id(const char *dev_node, struct gc_arena *gc) > { > - const struct tap_reg *tap_reg = get_tap_reg(gc); > + const struct tap_reg *tap_reg = get_tap_reg(gc, false); > const struct panel_reg *panel_reg = get_panel_reg(gc); > struct buffer actual = alloc_buf_gc(256, gc); > const char *guid; > @@ -5252,9 +5287,9 @@ netsh_get_id(const char *dev_node, struct gc_arena > *gc) > } > else > { > - guid = get_unspecified_device_guid(0, BPTR(&actual), > BCAP(&actual), tap_reg, panel_reg, gc); > + guid = get_unspecified_device_guid(0, BPTR(&actual), > BCAP(&actual), NULL, tap_reg, panel_reg, gc); > > - if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, > gc)) /* ambiguous if more than one TAP-Windows adapter */ > + if (get_unspecified_device_guid(1, NULL, 0, NULL, tap_reg, > panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */ > { > guid = NULL; > } > @@ -5528,6 +5563,7 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > struct gc_arena gc = gc_new(); > char device_path[256]; > const char *device_guid = NULL; > + DWORD device_luid_index = 0; > DWORD len; > bool dhcp_masq = false; > bool dhcp_masq_post = false; > @@ -5554,7 +5590,7 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > * Lookup the device name in the registry, using the --dev-node high > level name. > */ > { > - const struct tap_reg *tap_reg = get_tap_reg(&gc); > + const struct tap_reg *tap_reg = get_tap_reg(&gc, tt->wintun); > const struct panel_reg *panel_reg = get_panel_reg(&gc); > char actual_buffer[256]; > > @@ -5601,6 +5637,7 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > device_guid = get_unspecified_device_guid(device_number, > actual_buffer, > > sizeof(actual_buffer), > + > &device_luid_index, > tap_reg, > panel_reg, > &gc); > @@ -5610,11 +5647,28 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > msg(M_FATAL, "All TAP-Windows adapters on this system > are currently in use."); > } > > - /* Open Windows TAP-Windows adapter */ > - openvpn_snprintf(device_path, sizeof(device_path), > "%s%s%s", > - USERMODEDEVICEDIR, > - device_guid, > - TAP_WIN_SUFFIX); > + /* Open wintun / tap-windows6 adapter */ > + if (tt->wintun) > + { > + openvpn_snprintf(device_path, sizeof(device_path), > "%sWINTUN%lu", > + USERMODEDEVICEDIR, > + device_luid_index); > + } > + else > + { > + openvpn_snprintf(device_path, sizeof(device_path), > "%s%s%s", > + USERMODEDEVICEDIR, > + device_guid, > + TAP_WIN_SUFFIX); > + } > + > + if (tt->wintun) > + { > + if (!impersonate_as_system()) > + { > + msg(M_FATAL, "ERROR: Failed to impersonate as > SYSTEM, make sure process is running under privileged account"); > + } > + } > > tt->hand = CreateFile( > device_path, > @@ -5626,6 +5680,14 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > 0 > ); > > + if (tt->wintun) > + { > + if (!RevertToSelf()) > + { > + msg(M_FATAL, "ERROR: RevertToSelf error: %lu", > GetLastError()); > + } > + } > + > if (tt->hand == INVALID_HANDLE_VALUE) > { > msg(D_TUNTAP_INFO, "CreateFile failed on TAP device: > %s", device_path); > @@ -5644,12 +5706,14 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > tt->actual_name = string_alloc(actual_buffer, NULL); > } > > - msg(M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, > device_path); > + msg(M_INFO, "%s device device [%s] opened: %s", tt->wintun ? "Wintun" > : "TAP - WIN32", tt->actual_name, device_path); > tt->adapter_index = get_adapter_index(device_guid); > > - /* get driver version info */ > + if (!tt->wintun) > { > + /* get driver version info */ > ULONG info[3]; > + ULONG mtu; > CLEAR(info); > if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, > &info, sizeof(info), > @@ -5684,11 +5748,8 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > { > msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is > buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32 > driver.", (int) info[0], (int) info[1] ); > } > - } > > - /* get driver MTU */ > - { > - ULONG mtu; > + /* get driver MTU */ > if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, > &mtu, sizeof(mtu), > &mtu, sizeof(mtu), &len, NULL)) > @@ -5746,7 +5807,7 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > > /* set point-to-point mode if TUN device */ > > - if (tt->type == DEV_TYPE_TUN) > + if (tt->type == DEV_TYPE_TUN && !tt->wintun) > { > if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup) > { > @@ -5834,71 +5895,80 @@ open_tun(const char *dev, const char *dev_type, > const char *dev_node, struct tun > ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, > tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); > } > > - /* lease time in seconds */ > - ep[3] = (uint32_t) tt->options.dhcp_lease_time; > + if (!tt->wintun) > + { > + /* lease time in seconds */ > + ep[3] = (uint32_t)tt->options.dhcp_lease_time; > > - ASSERT(ep[3] > 0); > + ASSERT(ep[3] > 0); > > #ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad > DHCP negotiation */ > - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, > - ep, sizeof(ep), > - ep, sizeof(ep), &len, NULL)) > - { > - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a > DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); > - } > + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, > + ep, sizeof(ep), > + ep, sizeof(ep), &len, NULL)) > + { > + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a > DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); > + } > > - msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask > of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", > - print_in_addr_t(tt->local, 0, &gc), > - print_in_addr_t(tt->adapter_netmask, 0, &gc), > - device_guid, > - print_in_addr_t(ep[2], IA_NET_ORDER, &gc), > - ep[3] > + msg(M_INFO, "Notified TAP-Windows driver to set a DHCP > IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", > + print_in_addr_t(tt->local, 0, &gc), > + print_in_addr_t(tt->adapter_netmask, 0, &gc), > + device_guid, > + print_in_addr_t(ep[2], IA_NET_ORDER, &gc), > + ep[3] > ); > > - /* user-supplied DHCP options capability */ > - if (tt->options.dhcp_options) > - { > - struct buffer buf = alloc_buf(256); > - if (build_dhcp_options_string(&buf, &tt->options)) > + /* user-supplied DHCP options capability */ > + if (tt->options.dhcp_options) > { > - msg(D_DHCP_OPT, "DHCP option string: %s", > format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); > - if (!DeviceIoControl(tt->hand, > TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, > - BPTR(&buf), BLEN(&buf), > - BPTR(&buf), BLEN(&buf), &len, NULL)) > + struct buffer buf = alloc_buf(256); > + if (build_dhcp_options_string(&buf, &tt->options)) > { > - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected > a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); > + msg(D_DHCP_OPT, "DHCP option string: %s", > format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); > + if (!DeviceIoControl(tt->hand, > TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, > + BPTR(&buf), BLEN(&buf), > + BPTR(&buf), BLEN(&buf), &len, NULL)) > + { > + msg(M_FATAL, "ERROR: The TAP-Windows driver > rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); > + } > } > + else > + { > + msg(M_WARN, "DHCP option string not set due to > error"); > + } > + free_buf(&buf); > } > - else > - { > - msg(M_WARN, "DHCP option string not set due to error"); > - } > - free_buf(&buf); > - } > #endif /* ifndef SIMULATE_DHCP_FAILED */ > + } > + > + > } > > - /* set driver media status to 'connected' */ > + if (!tt->wintun) > { > - ULONG status = TRUE; > - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, > - &status, sizeof(status), > - &status, sizeof(status), &len, NULL)) > + /* set driver media status to 'connected' */ > { > - msg(M_WARN, "WARNING: The TAP-Windows driver rejected a > TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); > + ULONG status = TRUE; > + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, > + &status, sizeof(status), > + &status, sizeof(status), &len, NULL)) > + { > + msg(M_WARN, "WARNING: The TAP-Windows driver rejected a > TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); > + } > } > - } > > - /* possible wait for adapter to come up */ > - { > - int s = tt->options.tap_sleep; > - if (s > 0) > + /* possible wait for adapter to come up */ > { > - msg(M_INFO, "Sleeping for %d seconds...", s); > - management_sleep(s); > + int s = tt->options.tap_sleep; > + if (s > 0) > + { > + msg(M_INFO, "Sleeping for %d seconds...", s); > + management_sleep(s); > + } > } > } > > + > /* possibly use IP Helper API to set IP address on adapter */ > { > const DWORD index = tt->adapter_index; > diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h > index 9ed8ef0..6591d59 100644 > --- a/src/openvpn/tun.h > +++ b/src/openvpn/tun.h > @@ -37,6 +37,10 @@ > #include "proto.h" > #include "misc.h" > > +#ifdef _WIN32 > +#define WINTUN_COMPONENT_ID "wintun" > +#endif > + > #if defined(_WIN32) || defined(TARGET_ANDROID) > > #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1) > @@ -174,6 +178,9 @@ struct tuntap > * ~0 if undefined */ > DWORD adapter_index; > > + bool wintun; /* true if wintun is used instead of tap-windows6 */ > + char wintun_padding[16]; > + > int standby_iter; > #else /* ifdef _WIN32 */ > int fd; /* file descriptor for TUN/TAP dev */ > @@ -337,6 +344,7 @@ route_order(void) > struct tap_reg > { > const char *guid; > + DWORD luid_index; > struct tap_reg *next; > }; > > @@ -374,7 +382,7 @@ DWORD adapter_index_of_ip(const IP_ADAPTER_INFO *list, > int *count, > in_addr_t *netmask); > > -void show_tap_win_adapters(int msglev, int warnlev); > +void show_tap_win_adapters(int msglev, int warnlev, bool wintun); > > void show_adapters(int msglev); > > @@ -465,6 +473,30 @@ read_tun_buffered(struct tuntap *tt, struct buffer > *buf) > static inline int > write_tun_buffered(struct tuntap *tt, struct buffer *buf) > { > + if (tt->wintun) > + { > + int len = BLEN(buf); > + > + /* variable len end padding */ > + int end_padding_len = 16 - (len & 15); > + if (!buf_write(buf, tt->wintun_padding, end_padding_len)) > + { > + return -1; > + } > + > + /* 12 bytes start padding */ > + if (!buf_write_prepend(buf, tt->wintun_padding, 12)) > + { > + return -1; > + } > + > + /* 4 bytes size */ > + if (!buf_write_prepend(buf, &len, 4)) > + { > + return -1; > + } > + } > + > return tun_write_win32(tt, buf); > } > > diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c > index eb4c030..4b4a2b1 100644 > --- a/src/openvpn/win32.c > +++ b/src/openvpn/win32.c > @@ -164,20 +164,34 @@ init_security_attributes_allow_all(struct > security_attributes *obj) > void > overlapped_io_init(struct overlapped_io *o, > const struct frame *frame, > - BOOL event_state, > - bool tuntap_buffer) /* if true: tuntap buffer, if > false: socket buffer */ > + bool reads, /* if true: reads buffer, if false: writes > buffer */ > + bool tuntap_buffer, /* if true: tuntap buffer, if > false: socket buffer */ > + bool wintun) > { > CLEAR(*o); > > /* manual reset event, initially set according to event_state */ > - o->overlapped.hEvent = CreateEvent(NULL, TRUE, event_state, NULL); > + o->overlapped.hEvent = CreateEvent(NULL, TRUE, reads ? FALSE : TRUE, > NULL); > if (o->overlapped.hEvent == NULL) > { > msg(M_ERR, "Error: overlapped_io_init: CreateEvent failed"); > } > > - /* allocate buffer for overlapped I/O */ > - alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0); > + if (wintun && tuntap_buffer && reads) > + { > + /* > + * wintun could return up to 256 packets, > + * each packet is accompanied with size, start and end padding > + */ > + int buf_size = (MAX_RW_SIZE_TUN(frame) + 4 + 12 + 15) * 256; > + o->buf_init = alloc_buf(buf_size); > + o->buf_init.len = buf_size; > + } > + else > + { > + /* allocate buffer for overlapped I/O */ > + alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0); > + } > } > > void > @@ -1493,4 +1507,99 @@ send_msg_iservice(HANDLE pipe, const void *data, > size_t size, > return ret; > } > > +bool > +impersonate_as_system() > +{ > + HANDLE thread_token, process_snapshot, winlogon_process, > winlogon_token, duplicated_token, file_handle; > + PROCESSENTRY32 entry; > + BOOL ret; > + DWORD pid = 0; > + TOKEN_PRIVILEGES privileges; > + > + CLEAR(entry); > + CLEAR(privileges); > + > + entry.dwSize = sizeof(PROCESSENTRY32); > + > + privileges.PrivilegeCount = 1; > + privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED; > + > + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, > &privileges.Privileges[0].Luid)) > + { > + return false; > + } > + > + if (!ImpersonateSelf(SecurityImpersonation)) > + { > + return false; > + } > + > + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, > FALSE, &thread_token)) > + { > + RevertToSelf(); > + return false; > + } > + if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, > sizeof(privileges), NULL, NULL)) > + { > + CloseHandle(thread_token); > + RevertToSelf(); > + return false; > + } > + CloseHandle(thread_token); > + > + process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); > + if (process_snapshot == INVALID_HANDLE_VALUE) > + { > + RevertToSelf(); > + return false; > + } > + for (ret = Process32First(process_snapshot, &entry); ret; ret = > Process32Next(process_snapshot, &entry)) > + { > + if (!_stricmp(entry.szExeFile, "winlogon.exe")) > + { > + pid = entry.th32ProcessID; > + break; > + } > + } > + CloseHandle(process_snapshot); > + if (!pid) > + { > + RevertToSelf(); > + return false; > + } > + > + winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); > + if (!winlogon_process) > + { > + RevertToSelf(); > + return false; > + } > + > + if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | > TOKEN_DUPLICATE, &winlogon_token)) > + { > + CloseHandle(winlogon_process); > + RevertToSelf(); > + return false; > + } > + CloseHandle(winlogon_process); > + > + if (!DuplicateToken(winlogon_token, SecurityImpersonation, > &duplicated_token)) > + { > + CloseHandle(winlogon_token); > + RevertToSelf(); > + return false; > + } > + CloseHandle(winlogon_token); > + > + if (!SetThreadToken(NULL, duplicated_token)) > + { > + CloseHandle(duplicated_token); > + RevertToSelf(); > + return false; > + } > + CloseHandle(duplicated_token); > + > + return true; > +} > + > #endif /* ifdef _WIN32 */ > diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h > index 4814bbc..e511b56 100644 > --- a/src/openvpn/win32.h > +++ b/src/openvpn/win32.h > @@ -212,10 +212,12 @@ struct overlapped_io { > struct buffer buf; > }; > > -void overlapped_io_init(struct overlapped_io *o, > - const struct frame *frame, > - BOOL event_state, > - bool tuntap_buffer); > +void > +overlapped_io_init(struct overlapped_io *o, > + const struct frame *frame, > + bool reads, /* if true: reads buffer, if false: writes > buffer */ > + bool tuntap_buffer, /* if true: tuntap buffer, if > false: socket buffer */ > + bool wintun); > > void overlapped_io_close(struct overlapped_io *o); > > @@ -323,5 +325,14 @@ bool send_msg_iservice(HANDLE pipe, const void *data, > size_t size, > int > openvpn_execve(const struct argv *a, const struct env_set *es, const > unsigned int flags); > > +/** > + * Impersonates current thread as SYSTEM, required > + * to open Wintun device. > + * > + * @returns True if it succeeds, false if it fails. > + */ > +bool > +impersonate_as_system(); > + > #endif /* ifndef OPENVPN_WIN32_H */ > #endif /* ifdef _WIN32 */ > -- > 2.7.4 > >
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index ad4c1f5..8077835 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -2182,6 +2182,52 @@ io_wait_dowork(struct context *c, const unsigned int flags) dmsg(D_EVENT_WAIT, "I/O WAIT status=0x%04x", c->c2.event_set_status); } +#ifdef _WIN32 +/** + * This processes single packet from wintun buffer, + * which can contain up to 256 packets. + */ +static void +process_incoming_wintun(struct context *c) +{ + struct buffer *buf = &c->c2.wintun_buf; + uint32_t *size = NULL; + int end_padding_len = 0; + + /* get packet size */ + size = (uint32_t *)buf_read_alloc(buf, 4); + if (size == NULL) + { + return; + } + + /* skip start padding */ + if (!buf_advance(buf, 12)) + { + return; + } + + /* copy packet */ + c->c2.buf = c->c2.buffers->read_tun_buf; + buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame)); + buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame)); + if (!buf_copy_n(&c->c2.buf, buf, (int)*size)) + { + return; + } + + /* this encrypts packet and puts it to c->c2.to_link buffer */ + process_incoming_tun(c); + + /* skip end padding */ + end_padding_len = (16 - (*size & 15)) % 16; + if (!buf_advance(buf, end_padding_len)) + { + return; + } +} +#endif + void process_io(struct context *c) { @@ -2217,10 +2263,34 @@ process_io(struct context *c) /* Incoming data on TUN device */ else if (status & TUN_READ) { - read_incoming_tun(c); - if (!IS_SIG(c)) +#ifdef _WIN32 + if (c->options.wintun) + { + /* only read from driver if there are no packets in buffer */ + if (BLEN(&c->c2.wintun_buf) == 0) + { + read_incoming_tun(c); + c->c2.wintun_buf = c->c2.buf; + } + if (!IS_SIG(c)) + { + process_incoming_wintun(c); + } + + /* + * We need to write processed wintun packet to link, + * hence we give control back to event loop, which will + * call us again if wintun buffer is not empty + */ + } + else +#endif { - process_incoming_tun(c); + read_incoming_tun(c); + if (!IS_SIG(c)) + { + process_incoming_tun(c); + } } } } diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 48202c0..ac3e932 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -61,6 +61,7 @@ #define IOW_MBUF (1<<7) #define IOW_READ_TUN_FORCE (1<<8) #define IOW_WAIT_SIGNAL (1<<9) +#define IOW_READ_WINTUN_BUF (1<<10) #define IOW_READ (IOW_READ_TUN|IOW_READ_LINK) @@ -375,6 +376,12 @@ p2p_iow_flags(const struct context *c) { flags |= IOW_TO_TUN; } +#ifdef _WIN32 + if (c->c2.wintun_buf.len > 0) + { + flags |= IOW_READ_WINTUN_BUF; + } +#endif return flags; } @@ -401,6 +408,14 @@ io_wait(struct context *c, const unsigned int flags) } c->c2.event_set_status = ret; } +#ifdef _WIN32 + /* make sure we write to link before reading from wintun buffer, + * otherwise we'll overwrite c2->to_link buffer and lose a packet */ + else if (!(flags & IOW_TO_LINK) && (flags & IOW_READ_WINTUN_BUF)) + { + c->c2.event_set_status = TUN_READ; + } +#endif else { /* slow path */ diff --git a/src/openvpn/init.c b/src/openvpn/init.c index ef26503..1625272 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1696,6 +1696,10 @@ do_init_tun(struct context *c) !c->options.ifconfig_nowarn, c->c2.es); +#ifdef _WIN32 + c->c1.tuntap->wintun = c->options.wintun; +#endif + init_tun_post(c->c1.tuntap, &c->c2.frame, &c->options.tuntap_options); @@ -1738,6 +1742,9 @@ do_open_tun(struct context *c) /* store (hide) interactive service handle in tuntap_options */ c->c1.tuntap->options.msg_channel = c->options.msg_channel; msg(D_ROUTE, "interactive service msg_channel=%u", (unsigned int) c->options.msg_channel); + + c->c1.tuntap->wintun = c->options.wintun; + #endif /* allocate route list structure */ diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index d11f61d..d694666 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -382,6 +382,10 @@ struct context_2 struct buffer buf; struct buffer to_tun; struct buffer to_link; +#ifdef _WIN32 + /* contains up to 256 packets */ + struct buffer wintun_buf; +#endif /* should we print R|W|r|w to console on packet transfers? */ bool log_rw; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index e34b65b..534a6be 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -845,6 +845,7 @@ init_options(struct options *o, const bool init_gc) 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; + o->wintun = false; #endif #if P2MP_SERVER o->real_hash_size = 256; @@ -2937,6 +2938,12 @@ options_postprocess_mutate_invariant(struct options *options) options->ifconfig_noexec = false; } + /* for wintun kernel doesn't send DHCP requests, so use ipapi to set IP address and netmask */ + if (options->wintun) + { + options->tuntap_options.ip_win32_type = IPW32_SET_IPAPI; + } + remap_redirect_gateway_flags(options); #endif @@ -3980,6 +3987,26 @@ foreign_option(struct options *o, char *argv[], int len, struct env_set *es) } } +#ifdef _WIN32 +bool +parse_windows_driver(const char *str, const int msglevel) +{ + if (streq(str, "tap")) + { + return false; + } + else if (streq(str, "wintun")) + { + return true; + } + else + { + msg(msglevel, "--windows-driver must be tap-windows6 or wintun"); + return false; + } +} +#endif + /* * parse/print topology coding */ @@ -5220,6 +5247,13 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->dev_type = p[1]; } +#ifdef _WIN32 + else if (streq(p[0], "windows-driver") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->wintun = parse_windows_driver(p[1], M_FATAL); + } +#endif else if (streq(p[0], "dev-node") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -7217,7 +7251,7 @@ add_option(struct options *options, else if (streq(p[0], "show-adapters") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); - show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX); + show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX, options->wintun); openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } else if (streq(p[0], "show-net") && !p[1]) diff --git a/src/openvpn/options.h b/src/openvpn/options.h index e2b3893..670b70a 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -614,6 +614,7 @@ struct options bool show_net_up; int route_method; bool block_outside_dns; + bool wintun; #endif bool use_peer_id; diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index c472cf1..4352869 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -1635,8 +1635,8 @@ static void socket_frame_init(const struct frame *frame, struct link_socket *sock) { #ifdef _WIN32 - overlapped_io_init(&sock->reads, frame, FALSE, false); - overlapped_io_init(&sock->writes, frame, TRUE, false); + overlapped_io_init(&sock->reads, frame, true, false, false); + overlapped_io_init(&sock->writes, frame, false, false, false); sock->rw_handle.read = sock->reads.overlapped.hEvent; sock->rw_handle.write = sock->writes.overlapped.hEvent; #endif diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index 2b4c49f..e7cbf25 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -39,6 +39,7 @@ #ifdef _WIN32 #include <windows.h> #include <winsock2.h> +#include <tlhelp32.h> #define sleep(x) Sleep((x)*1000) #define random rand #define srandom srand diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index e929b50..5fd8339 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -788,8 +788,8 @@ init_tun_post(struct tuntap *tt, { tt->options = *options; #ifdef _WIN32 - overlapped_io_init(&tt->reads, frame, FALSE, true); - overlapped_io_init(&tt->writes, frame, TRUE, true); + overlapped_io_init(&tt->reads, frame, true, true, tt->wintun); + overlapped_io_init(&tt->writes, frame, false, true, tt->wintun); tt->rw_handle.read = tt->reads.overlapped.hEvent; tt->rw_handle.write = tt->writes.overlapped.hEvent; tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; @@ -3430,7 +3430,7 @@ tun_finalize( } const struct tap_reg * -get_tap_reg(struct gc_arena *gc) +get_tap_reg(struct gc_arena *gc, bool wintun) { HKEY adapter_key; LONG status; @@ -3461,6 +3461,20 @@ get_tap_reg(struct gc_arena *gc) char net_cfg_instance_id_string[] = "NetCfgInstanceId"; char net_cfg_instance_id[256]; DWORD data_type; + char expected_component_id[256]; + char expected_root_component_id[256]; + char net_luid_index_string[] = "NetLuidIndex"; + DWORD luid_index; + + if (wintun) + { + strcpy(expected_component_id, WINTUN_COMPONENT_ID); + } + else + { + strcpy(expected_component_id, TAP_WIN_COMPONENT_ID); + } + openvpn_snprintf(expected_root_component_id, sizeof(expected_root_component_id), "root\\%s", expected_component_id); len = sizeof(enum_name); status = RegEnumKeyEx( @@ -3525,13 +3539,27 @@ get_tap_reg(struct gc_arena *gc) if (status == ERROR_SUCCESS && data_type == REG_SZ) { - if (!strcmp(component_id, TAP_WIN_COMPONENT_ID) || - !strcmp(component_id, "root\\" TAP_WIN_COMPONENT_ID)) + if (!strcmp(component_id, expected_component_id) || + !strcmp(component_id, expected_root_component_id)) { struct tap_reg *reg; ALLOC_OBJ_CLEAR_GC(reg, struct tap_reg, gc); reg->guid = string_alloc(net_cfg_instance_id, gc); + len = sizeof(luid_index); + status = RegQueryValueEx( + unit_key, + net_luid_index_string, + NULL, + &data_type, + (LPBYTE)&luid_index, + &len); + + if (status == ERROR_SUCCESS && data_type == REG_DWORD) + { + reg->luid_index = luid_index; + } + /* link into return list */ if (!first) { @@ -3744,7 +3772,7 @@ show_valid_win32_tun_subnets(void) } void -show_tap_win_adapters(int msglev, int warnlev) +show_tap_win_adapters(int msglev, int warnlev, bool wintun) { struct gc_arena gc = gc_new(); @@ -3758,10 +3786,10 @@ show_tap_win_adapters(int msglev, int warnlev) const struct tap_reg *tr1; const struct panel_reg *pr; - const struct tap_reg *tap_reg = get_tap_reg(&gc); + const struct tap_reg *tap_reg = get_tap_reg(&gc, wintun); const struct panel_reg *panel_reg = get_panel_reg(&gc); - msg(msglev, "Available TAP-WIN32 adapters [name, GUID]:"); + msg(msglev, "Available %s adapters [name, GUID]:", wintun ? "WinTun" : "TAP-WIN32"); /* loop through each TAP-Windows adapter registry entry */ for (tr = tap_reg; tr != NULL; tr = tr->next) @@ -3883,13 +3911,14 @@ at_least_one_tap_win(const struct tap_reg *tap_reg) } /* - * Get an adapter GUID and optional actual_name from the + * Get an adapter GUID, LUID index and optional actual_name from the * registry for the TAP device # = device_number. */ static const char * get_unspecified_device_guid(const int device_number, char *actual_name, int actual_name_size, + int *luid_index, const struct tap_reg *tap_reg_src, const struct panel_reg *panel_reg_src, struct gc_arena *gc) @@ -3938,6 +3967,11 @@ get_unspecified_device_guid(const int device_number, } } + if (luid_index) + { + *luid_index = tap_reg->luid_index; + } + /* Save GUID for return value */ ret = alloc_buf_gc(256, gc); buf_printf(&ret, "%s", tap_reg->guid); @@ -4536,7 +4570,7 @@ get_adapter_index_flexible(const char *name) /* actual name or GUID */ } if (index == TUN_ADAPTER_INDEX_INVALID) { - const struct tap_reg *tap_reg = get_tap_reg(&gc); + const struct tap_reg *tap_reg = get_tap_reg(&gc, false); const struct panel_reg *panel_reg = get_panel_reg(&gc); const char *guid = name_to_guid(name, tap_reg, panel_reg); index = get_adapter_index_method_1(guid); @@ -4663,7 +4697,7 @@ void tap_allow_nonadmin_access(const char *dev_node) { struct gc_arena gc = gc_new(); - const struct tap_reg *tap_reg = get_tap_reg(&gc); + const struct tap_reg *tap_reg = get_tap_reg(&gc, false); const struct panel_reg *panel_reg = get_panel_reg(&gc); const char *device_guid = NULL; HANDLE hand; @@ -4717,6 +4751,7 @@ tap_allow_nonadmin_access(const char *dev_node) actual_buffer, sizeof(actual_buffer), tap_reg, + NULL, panel_reg, &gc); @@ -5239,7 +5274,7 @@ out: static const char * netsh_get_id(const char *dev_node, struct gc_arena *gc) { - const struct tap_reg *tap_reg = get_tap_reg(gc); + const struct tap_reg *tap_reg = get_tap_reg(gc, false); const struct panel_reg *panel_reg = get_panel_reg(gc); struct buffer actual = alloc_buf_gc(256, gc); const char *guid; @@ -5252,9 +5287,9 @@ netsh_get_id(const char *dev_node, struct gc_arena *gc) } else { - guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, gc); + guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), NULL, tap_reg, panel_reg, gc); - if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */ + if (get_unspecified_device_guid(1, NULL, 0, NULL, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */ { guid = NULL; } @@ -5528,6 +5563,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun struct gc_arena gc = gc_new(); char device_path[256]; const char *device_guid = NULL; + DWORD device_luid_index = 0; DWORD len; bool dhcp_masq = false; bool dhcp_masq_post = false; @@ -5554,7 +5590,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun * Lookup the device name in the registry, using the --dev-node high level name. */ { - const struct tap_reg *tap_reg = get_tap_reg(&gc); + const struct tap_reg *tap_reg = get_tap_reg(&gc, tt->wintun); const struct panel_reg *panel_reg = get_panel_reg(&gc); char actual_buffer[256]; @@ -5601,6 +5637,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun device_guid = get_unspecified_device_guid(device_number, actual_buffer, sizeof(actual_buffer), + &device_luid_index, tap_reg, panel_reg, &gc); @@ -5610,11 +5647,28 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun msg(M_FATAL, "All TAP-Windows adapters on this system are currently in use."); } - /* Open Windows TAP-Windows adapter */ - openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); + /* Open wintun / tap-windows6 adapter */ + if (tt->wintun) + { + openvpn_snprintf(device_path, sizeof(device_path), "%sWINTUN%lu", + USERMODEDEVICEDIR, + device_luid_index); + } + else + { + openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAP_WIN_SUFFIX); + } + + if (tt->wintun) + { + if (!impersonate_as_system()) + { + msg(M_FATAL, "ERROR: Failed to impersonate as SYSTEM, make sure process is running under privileged account"); + } + } tt->hand = CreateFile( device_path, @@ -5626,6 +5680,14 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun 0 ); + if (tt->wintun) + { + if (!RevertToSelf()) + { + msg(M_FATAL, "ERROR: RevertToSelf error: %lu", GetLastError()); + } + } + if (tt->hand == INVALID_HANDLE_VALUE) { msg(D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path); @@ -5644,12 +5706,14 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun tt->actual_name = string_alloc(actual_buffer, NULL); } - msg(M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path); + msg(M_INFO, "%s device device [%s] opened: %s", tt->wintun ? "Wintun" : "TAP - WIN32", tt->actual_name, device_path); tt->adapter_index = get_adapter_index(device_guid); - /* get driver version info */ + if (!tt->wintun) { + /* get driver version info */ ULONG info[3]; + ULONG mtu; CLEAR(info); if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, &info, sizeof(info), @@ -5684,11 +5748,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun { msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32 driver.", (int) info[0], (int) info[1] ); } - } - /* get driver MTU */ - { - ULONG mtu; + /* get driver MTU */ if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, &mtu, sizeof(mtu), &mtu, sizeof(mtu), &len, NULL)) @@ -5746,7 +5807,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun /* set point-to-point mode if TUN device */ - if (tt->type == DEV_TYPE_TUN) + if (tt->type == DEV_TYPE_TUN && !tt->wintun) { if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup) { @@ -5834,71 +5895,80 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); } - /* lease time in seconds */ - ep[3] = (uint32_t) tt->options.dhcp_lease_time; + if (!tt->wintun) + { + /* lease time in seconds */ + ep[3] = (uint32_t)tt->options.dhcp_lease_time; - ASSERT(ep[3] > 0); + ASSERT(ep[3] > 0); #ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */ - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); - } + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); + } - msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid, - print_in_addr_t(ep[2], IA_NET_ORDER, &gc), - ep[3] + msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid, + print_in_addr_t(ep[2], IA_NET_ORDER, &gc), + ep[3] ); - /* user-supplied DHCP options capability */ - if (tt->options.dhcp_options) - { - struct buffer buf = alloc_buf(256); - if (build_dhcp_options_string(&buf, &tt->options)) + /* user-supplied DHCP options capability */ + if (tt->options.dhcp_options) { - msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, - BPTR(&buf), BLEN(&buf), - BPTR(&buf), BLEN(&buf), &len, NULL)) + struct buffer buf = alloc_buf(256); + if (build_dhcp_options_string(&buf, &tt->options)) { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); + msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, + BPTR(&buf), BLEN(&buf), + BPTR(&buf), BLEN(&buf), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); + } } + else + { + msg(M_WARN, "DHCP option string not set due to error"); + } + free_buf(&buf); } - else - { - msg(M_WARN, "DHCP option string not set due to error"); - } - free_buf(&buf); - } #endif /* ifndef SIMULATE_DHCP_FAILED */ + } + + } - /* set driver media status to 'connected' */ + if (!tt->wintun) { - ULONG status = TRUE; - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, - &status, sizeof(status), - &status, sizeof(status), &len, NULL)) + /* set driver media status to 'connected' */ { - msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + ULONG status = TRUE; + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, + &status, sizeof(status), + &status, sizeof(status), &len, NULL)) + { + msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + } } - } - /* possible wait for adapter to come up */ - { - int s = tt->options.tap_sleep; - if (s > 0) + /* possible wait for adapter to come up */ { - msg(M_INFO, "Sleeping for %d seconds...", s); - management_sleep(s); + int s = tt->options.tap_sleep; + if (s > 0) + { + msg(M_INFO, "Sleeping for %d seconds...", s); + management_sleep(s); + } } } + /* possibly use IP Helper API to set IP address on adapter */ { const DWORD index = tt->adapter_index; diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 9ed8ef0..6591d59 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -37,6 +37,10 @@ #include "proto.h" #include "misc.h" +#ifdef _WIN32 +#define WINTUN_COMPONENT_ID "wintun" +#endif + #if defined(_WIN32) || defined(TARGET_ANDROID) #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1) @@ -174,6 +178,9 @@ struct tuntap * ~0 if undefined */ DWORD adapter_index; + bool wintun; /* true if wintun is used instead of tap-windows6 */ + char wintun_padding[16]; + int standby_iter; #else /* ifdef _WIN32 */ int fd; /* file descriptor for TUN/TAP dev */ @@ -337,6 +344,7 @@ route_order(void) struct tap_reg { const char *guid; + DWORD luid_index; struct tap_reg *next; }; @@ -374,7 +382,7 @@ DWORD adapter_index_of_ip(const IP_ADAPTER_INFO *list, int *count, in_addr_t *netmask); -void show_tap_win_adapters(int msglev, int warnlev); +void show_tap_win_adapters(int msglev, int warnlev, bool wintun); void show_adapters(int msglev); @@ -465,6 +473,30 @@ read_tun_buffered(struct tuntap *tt, struct buffer *buf) static inline int write_tun_buffered(struct tuntap *tt, struct buffer *buf) { + if (tt->wintun) + { + int len = BLEN(buf); + + /* variable len end padding */ + int end_padding_len = 16 - (len & 15); + if (!buf_write(buf, tt->wintun_padding, end_padding_len)) + { + return -1; + } + + /* 12 bytes start padding */ + if (!buf_write_prepend(buf, tt->wintun_padding, 12)) + { + return -1; + } + + /* 4 bytes size */ + if (!buf_write_prepend(buf, &len, 4)) + { + return -1; + } + } + return tun_write_win32(tt, buf); } diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index eb4c030..4b4a2b1 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -164,20 +164,34 @@ init_security_attributes_allow_all(struct security_attributes *obj) void overlapped_io_init(struct overlapped_io *o, const struct frame *frame, - BOOL event_state, - bool tuntap_buffer) /* if true: tuntap buffer, if false: socket buffer */ + bool reads, /* if true: reads buffer, if false: writes buffer */ + bool tuntap_buffer, /* if true: tuntap buffer, if false: socket buffer */ + bool wintun) { CLEAR(*o); /* manual reset event, initially set according to event_state */ - o->overlapped.hEvent = CreateEvent(NULL, TRUE, event_state, NULL); + o->overlapped.hEvent = CreateEvent(NULL, TRUE, reads ? FALSE : TRUE, NULL); if (o->overlapped.hEvent == NULL) { msg(M_ERR, "Error: overlapped_io_init: CreateEvent failed"); } - /* allocate buffer for overlapped I/O */ - alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0); + if (wintun && tuntap_buffer && reads) + { + /* + * wintun could return up to 256 packets, + * each packet is accompanied with size, start and end padding + */ + int buf_size = (MAX_RW_SIZE_TUN(frame) + 4 + 12 + 15) * 256; + o->buf_init = alloc_buf(buf_size); + o->buf_init.len = buf_size; + } + else + { + /* allocate buffer for overlapped I/O */ + alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0); + } } void @@ -1493,4 +1507,99 @@ send_msg_iservice(HANDLE pipe, const void *data, size_t size, return ret; } +bool +impersonate_as_system() +{ + HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, file_handle; + PROCESSENTRY32 entry; + BOOL ret; + DWORD pid = 0; + TOKEN_PRIVILEGES privileges; + + CLEAR(entry); + CLEAR(privileges); + + entry.dwSize = sizeof(PROCESSENTRY32); + + privileges.PrivilegeCount = 1; + privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid)) + { + return false; + } + + if (!ImpersonateSelf(SecurityImpersonation)) + { + return false; + } + + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token)) + { + RevertToSelf(); + return false; + } + if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) + { + CloseHandle(thread_token); + RevertToSelf(); + return false; + } + CloseHandle(thread_token); + + process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (process_snapshot == INVALID_HANDLE_VALUE) + { + RevertToSelf(); + return false; + } + for (ret = Process32First(process_snapshot, &entry); ret; ret = Process32Next(process_snapshot, &entry)) + { + if (!_stricmp(entry.szExeFile, "winlogon.exe")) + { + pid = entry.th32ProcessID; + break; + } + } + CloseHandle(process_snapshot); + if (!pid) + { + RevertToSelf(); + return false; + } + + winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!winlogon_process) + { + RevertToSelf(); + return false; + } + + if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token)) + { + CloseHandle(winlogon_process); + RevertToSelf(); + return false; + } + CloseHandle(winlogon_process); + + if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) + { + CloseHandle(winlogon_token); + RevertToSelf(); + return false; + } + CloseHandle(winlogon_token); + + if (!SetThreadToken(NULL, duplicated_token)) + { + CloseHandle(duplicated_token); + RevertToSelf(); + return false; + } + CloseHandle(duplicated_token); + + return true; +} + #endif /* ifdef _WIN32 */ diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index 4814bbc..e511b56 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -212,10 +212,12 @@ struct overlapped_io { struct buffer buf; }; -void overlapped_io_init(struct overlapped_io *o, - const struct frame *frame, - BOOL event_state, - bool tuntap_buffer); +void +overlapped_io_init(struct overlapped_io *o, + const struct frame *frame, + bool reads, /* if true: reads buffer, if false: writes buffer */ + bool tuntap_buffer, /* if true: tuntap buffer, if false: socket buffer */ + bool wintun); void overlapped_io_close(struct overlapped_io *o); @@ -323,5 +325,14 @@ bool send_msg_iservice(HANDLE pipe, const void *data, size_t size, int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags); +/** + * Impersonates current thread as SYSTEM, required + * to open Wintun device. + * + * @returns True if it succeeds, false if it fails. + */ +bool +impersonate_as_system(); + #endif /* ifndef OPENVPN_WIN32_H */ #endif /* ifdef _WIN32 */