From patchwork Wed Jun 5 04:59:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lev Stipakov X-Patchwork-Id: 740 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director9.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id GFYZIz3Z91zfPAAAIUCqbw for ; Wed, 05 Jun 2019 11:01:17 -0400 Received: from proxy7.mail.ord1d.rsapps.net ([172.30.191.6]) by director9.mail.ord1d.rsapps.net with LMTP id 0FHyIj3Z91xWPwAAalYnBA ; Wed, 05 Jun 2019 11:01:17 -0400 Received: from smtp7.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy7.mail.ord1d.rsapps.net with LMTP id iEi3Ij3Z91xhGwAAMe1Fpw ; Wed, 05 Jun 2019 11:01:17 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp7.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=gmail.com; dmarc=fail (p=none; dis=none) header.from=gmail.com X-Suspicious-Flag: YES X-Classification-ID: c456e9fe-87a2-11e9-9f64-525400d0c497-1-1 Received: from [216.105.38.7] ([216.105.38.7:38698] helo=lists.sourceforge.net) by smtp7.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 4B/93-28312-C39D7FC5; Wed, 05 Jun 2019 11:01:16 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1hYXOS-00088d-9B; Wed, 05 Jun 2019 15:00:00 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1hYXOQ-00088B-Os for openvpn-devel@lists.sourceforge.net; Wed, 05 Jun 2019 14:59:58 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=4n7F4PfJ3GoyKAjqVqWScOs+7ws71kGK/v3Yhs4JJRM=; b=PTvF3LHjgjJh+gw2zOWW6EPHM5 /dWCOFcLP8LinXYejkKrl6zwXDvtQniW9spnXhyd9zaGvv7u61qdJfkNk7RpxLf3wNVCkSWBcDU9c yiSpqOpO21/PuSJzESIPd0VTsnJB1zuKt2c0BeHBXpEubB/DYas0aTT03gojB97jYeoM=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:MIME-Version: Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=4n7F4PfJ3GoyKAjqVqWScOs+7ws71kGK/v3Yhs4JJRM=; b=jWbuYTF9rLCqFv+WtaOv405Ocp 0H621k+ScgwSJsZqe2rqcQ8XNc0obVoLIn4RsK6xHzVRlGVDEgMWlkqBLegU3oGu/xXnFFHq4dsy0 fLGbApEtYHqI9y9/pM7+lj/0lGkjc4qzuHY+5ngTOWF26yrlf1MAQhJRdzZWGFHFLbU0=; Received: from mail-lj1-f195.google.com ([209.85.208.195]) by sfi-mx-4.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.90_1) id 1hYXOO-009Owo-6K for openvpn-devel@lists.sourceforge.net; Wed, 05 Jun 2019 14:59:58 +0000 Received: by mail-lj1-f195.google.com with SMTP id a21so8221444ljh.7 for ; Wed, 05 Jun 2019 07:59:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=4n7F4PfJ3GoyKAjqVqWScOs+7ws71kGK/v3Yhs4JJRM=; b=jyoDMVAwnM6OemlYTVIa7SCweVd8Q3XyxE6ZWx97RWFFZoNCbhn6Qx3uGASot2b8zT LnAdGCQa1iJryV0XCNzW/Y71fii7mN78810mF3XyACjdF1LjVPo7TmN3jxHItUxIo2nn llX24iC3kgc+Z9ha5ALHE0Zgz0n6v/xlAH9rgztacEk7olV6ksIB5lbjaFfuRhFC8Ii8 9lj4+ooz/08CdEEoC8h8mg3n4/HfcEe8MhLvmkFwx2Z8qKmbf9Nw3e+gZ4pnFm6MVPWI EqMEWx3znV1orXDCJ7m5yS98R0p9unzXGYqJte07O6+khZ/zYjUny9Pgbre3z+3LutoJ jbQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=4n7F4PfJ3GoyKAjqVqWScOs+7ws71kGK/v3Yhs4JJRM=; b=RSREIe0mJiIntyNt/39/XvRAf6gwolnWlWcdYeOokxbxNVMGy2/deJRQ8Ok522eyaj 9pyVrhG6H8L1s5WVNy71PoMV8CYguOtAoUXQAJohRTh5RWTjZjQxV2rKzYDYjkBTb0i1 4WZFFro/M9ES3/wEYnNv5ndG+Z+Aw8X8rxPj4O/b9OV9WHK3+tb4PbyDDc1GE5t766MC 0oCU8jaP9cOtQdAD148dFkAoEfIZ6v6YRxdarp1S11x865pNvoIT1fHvRP9AOVQzutmZ nqKA5V/mR7i9WN1oTq3Naxgz4v36rMM1TITTj2K3iFRgW4KCpknxXv/P6fEA6rvzuSgO kDiQ== X-Gm-Message-State: APjAAAWAiOaAJ0lMCTkOxzkjRND/lmnW7dtvU0/QGWlknDVGwIsyqo+b YJP6fhdYoFYLpWGpUkrbImj7iM8X X-Google-Smtp-Source: APXvYqz2QhWGsIpXAX40z2CC+h85Tun5I396+pQGqcf01Yp021z1oVbEUQQh8DYKVsefxV1Ou5TO4g== X-Received: by 2002:a2e:28d:: with SMTP id y13mr21027534lje.177.1559746788648; Wed, 05 Jun 2019 07:59:48 -0700 (PDT) Received: from stipakov.fi (stipakov.fi. [128.199.52.117]) by smtp.gmail.com with ESMTPSA id z24sm4800609lfh.63.2019.06.05.07.59.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 05 Jun 2019 07:59:47 -0700 (PDT) From: Lev Stipakov To: openvpn-devel@lists.sourceforge.net Date: Wed, 5 Jun 2019 17:59:30 +0300 Message-Id: <1559746770-21285-1-git-send-email-lstipakov@gmail.com> X-Mailer: git-send-email 2.7.4 X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (lstipakov[at]gmail.com) -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [209.85.208.195 listed in list.dnswl.org] -0.6 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.208.195 listed in wl.mailspike.net] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-Headers-End: 1hYXOO-009Owo-6K Subject: [Openvpn-devel] [PATCH] Wintun - experimental support X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lev Stipakov MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Lev Stipakov 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 --- 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 #include +#include #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 */