From patchwork Fri Jun 14 05:35:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lev Stipakov X-Patchwork-Id: 754 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id WO91Mz6/A13oOAAAIUCqbw for ; Fri, 14 Jun 2019 11:37:34 -0400 Received: from proxy6.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id EJJIMz6/A11wcgAAfY0hYg ; Fri, 14 Jun 2019 11:37:34 -0400 Received: from smtp6.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy6.mail.ord1d.rsapps.net with LMTP id aDMJMz6/A12NOwAAQyIf0w ; Fri, 14 Jun 2019 11:37:34 -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: smtp6.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: 53e1f9b0-8eba-11e9-a2f2-52540050e3e0-1-1 Received: from [216.105.38.7] ([216.105.38.7:56560] helo=lists.sourceforge.net) by smtp6.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 8F/FB-03377-D3FB30D5; Fri, 14 Jun 2019 11:37:34 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1hboFi-0006ac-PQ; Fri, 14 Jun 2019 15:36:30 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1hboFh-0006aV-Cf for openvpn-devel@lists.sourceforge.net; Fri, 14 Jun 2019 15:36:29 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:To: From:Sender:Reply-To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=t8OQZzqvnxfLfegu4qJs0KZr6z0qYVANFJyIXXe0uwE=; b=mEa1KzLimjm7AWww+oBZWuWUoz k+jO8NbYelyqXo9ljvma/8T6ELNWRVju7S3hOBUm6BpFJOXLcWO32m0NNDIgfrIKX2zOXXyO5Dgf7 0clQKy3Uvq2nUqJ8KP25KjhglsT3+56GIRYBkOZtds3vGIctYix962pmtfqD9DBZnK+M=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=t8OQZzqvnxfLfegu4qJs0KZr6z0qYVANFJyIXXe0uwE=; b=Iy++oDJ7tscVd4enNJnHgMm9pZ g23LXx3QiSoCAAoTqGbcsXRCtHtunswlPh0lf+gbEZw0oKwarU2D4kaPnlpAB/lJ/xkv68nQftXQ6 DGMPIyLV4OXrxr184jeyUdc9DPkWtiJmXAmgY6hwv4GZOUViXX6fgS5VJLBUfan6uaK0=; Received: from mail-ed1-f68.google.com ([209.85.208.68]) by sfi-mx-4.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.90_1) id 1hboFf-00DV5v-I2 for openvpn-devel@lists.sourceforge.net; Fri, 14 Jun 2019 15:36:29 +0000 Received: by mail-ed1-f68.google.com with SMTP id e3so4037803edr.10 for ; Fri, 14 Jun 2019 08:36:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=t8OQZzqvnxfLfegu4qJs0KZr6z0qYVANFJyIXXe0uwE=; b=aU41SXIUwOJQ77LItklwQTJOzkUoTEvdq2dfPvN2bQZfow8fLtK5VwNEHMiCy9y48m 52HLTZx7IYzNxRMkP+okwokp9IZRohYKezWQivJK2MWcAG2ZAzJLS0MaoImVN+LNe27L RoCmJf9uUW1QyrRNQ+8oCQnfZZHkTIyXWc0rrrXHu+UicKBMsin0HKtzyqaHOnyCoCvd mytXxkJJPtm/HX30mVqgXnL0Fvo/FSQTAyI24DrmnrJ/J3t8RZ7tav7vV61/QnV+5Hqi lPfqlFdFG+h21oZJ1Mzspt9SY8Brm8yvI9/NT9nAUM3OO9aElS42f9rkBaVgqGOnQYyf ilKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=t8OQZzqvnxfLfegu4qJs0KZr6z0qYVANFJyIXXe0uwE=; b=qC87Qg69K0ZeDppfmlYb7yV29GgS5zJj7ehyuBBBRu9IbYCBLqpx1fx7j+I4wk9FbU X0sthgnCjKuEIFbQ5E5Oo7Wo3seOVLQH6CZbq9cVddpi3D8mqmtlIZ5CSqukBZXvW8W+ oMx5OsMNWZ8AibV2V2gF6vegD6xXr3qcAzYIpO5M3eHZUQ7vJFB/xkWLMoXWJC7ppCk7 qkDLH3NJPpSjkDdKWol2zz2OgyneokbLAZ1DWXlzqtzIt5BM2zsKjcJpbJoqdBConQwh nb++eqys5173OGuSvZSm1Htwx6O4p2I+mnK6yw3u9uwrNhWDtZiJGA04qe1wzTIr0Ewz 42vw== X-Gm-Message-State: APjAAAU6Weo+OPo/Qp32vj4Qpi1xzgxQZ5yrgp6Xpg71Y8LpTqly/mLb uLgCpehzFIscauAGCcGPIvvK8MPlwx4= X-Google-Smtp-Source: APXvYqx/gs/tdj4/Af5ElwKpnUCnJcIF2m/P2QyuMZkXy7kSZwOf27Yy5TQTZ8RQFNZefaLlK7iH8g== X-Received: by 2002:a17:906:c104:: with SMTP id h4mr59131650ejz.69.1560526580200; Fri, 14 Jun 2019 08:36:20 -0700 (PDT) Received: from stipakov.fi (stipakov.fi. [128.199.52.117]) by smtp.gmail.com with ESMTPSA id e43sm1012643ede.62.2019.06.14.08.36.18 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 14 Jun 2019 08:36:19 -0700 (PDT) From: Lev Stipakov To: openvpn-devel@lists.sourceforge.net Date: Fri, 14 Jun 2019 18:35:38 +0300 Message-Id: <1560526538-6355-1-git-send-email-lstipakov@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1559746770-21285-1-git-send-email-lstipakov@gmail.com> References: <1559746770-21285-1-git-send-email-lstipakov@gmail.com> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [209.85.208.68 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (lstipakov[at]gmail.com) 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: openvpn.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: 1hboFf-00DV5v-I2 Subject: [Openvpn-devel] [PATCH v2] 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: , 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 --- v2: - fix sending "icmp unreachable" - tweak buffer size - fix padding calculation on tun write src/openvpn/forward.c | 85 +++++++++++++++++--- 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 | 37 ++++++++- src/openvpn/win32.c | 118 ++++++++++++++++++++++++++-- src/openvpn/win32.h | 19 ++++- 12 files changed, 445 insertions(+), 94 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index ad4c1f5..2c0ce5c 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1505,12 +1505,6 @@ ipv6_send_icmp_unreachable(struct context *c, struct buffer *buf, bool client) int icmpheader_len = sizeof(struct openvpn_ipv6hdr) + sizeof(struct openvpn_icmp6hdr); - int totalheader_len = icmpheader_len; - - if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP) - { - totalheader_len += sizeof(struct openvpn_ethhdr); - } /* * Calculate size for payload, defined in the standard that the resulting @@ -1535,7 +1529,8 @@ ipv6_send_icmp_unreachable(struct context *c, struct buffer *buf, bool client) c->c2.to_link = c->c2.buffers->aux_buf; outbuf = &(c->c2.to_link); } - ASSERT(buf_init(outbuf, totalheader_len)); + /* reserve enough headroom to prepend with eth/ip/icmp header and Wintun encapsulation */ + ASSERT(buf_init(outbuf, FRAME_HEADROOM(&c->c2.frame))); /* Fill the end of the buffer with original packet */ ASSERT(buf_safe(outbuf, payload_len)); @@ -2182,6 +2177,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 +2258,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..871c5ff 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,33 @@ 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)) % 16; + if (end_padding_len > 0) + { + 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..44d5620 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -164,20 +164,33 @@ 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) + { + /* + * Lev: according to my tests, this buffer size gives the best performance + */ + int buf_size = 0x3C000; + 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 +1506,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 */