From patchwork Wed Nov 20 03:44:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lev Stipakov X-Patchwork-Id: 930 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 SEVrIM1R1V3rHwAAIUCqbw for ; Wed, 20 Nov 2019 09:46:37 -0500 Received: from proxy2.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id 4BY+IM1R1V0cJgAAfY0hYg ; Wed, 20 Nov 2019 09:46:37 -0500 Received: from smtp12.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy2.mail.ord1d.rsapps.net with LMTP id EFe1H81R1V3MZQAAfawv4w ; Wed, 20 Nov 2019 09:46:37 -0500 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: smtp12.gate.ord1c.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: 8d89913e-0ba4-11ea-8986-bc305bf03e5c-1-1 Received: from [216.105.38.7] ([216.105.38.7:59034] helo=lists.sourceforge.net) by smtp12.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id C8/6D-10858-CC155DD5; Wed, 20 Nov 2019 09:46:37 -0500 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 1iXREl-0004Rs-DZ; Wed, 20 Nov 2019 14:45:43 +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 1iXREk-0004RU-0S for openvpn-devel@lists.sourceforge.net; Wed, 20 Nov 2019 14:45:42 +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: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:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=j7HPOP6JMHflyU1AghbCSDzgYCPRwjkxRKl9hpZAclo=; b=UBvlyn1VqHrSZ2gm2YtAE9EgCO TAJOPVECDNIaJIssA197VyupvqoAlZ71SU297NH5wPVNVQ6nseD8ZfPpLcDSNYNBbwPVZk9tXqNXi /Tty0bDoCBGmJ7KNiv7vnNMBARs/vE24Opb6ECgibRhOVOp3feG8w65KA00xMWPAdgnM=; 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: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:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=j7HPOP6JMHflyU1AghbCSDzgYCPRwjkxRKl9hpZAclo=; b=CKKycypTlYimT3TUCe/8tuHtPx 2iNf7Apr7YYOxo63yUyqrLFsfJJu+chGmiksR0g6QB5JcA3IK6yreIu3/Ib+P+yhRNmj8AypUJPOX eNd1WDlRCXIW9fYslrimdNm1cUEqFlOBmCJZYD5gywNsf6wyfJf8TPy6tO2ChvvkDlvw=; Received: from mail-lj1-f194.google.com ([209.85.208.194]) by sfi-mx-4.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.92.2) id 1iXREg-007QV8-U8 for openvpn-devel@lists.sourceforge.net; Wed, 20 Nov 2019 14:45:41 +0000 Received: by mail-lj1-f194.google.com with SMTP id e9so27773965ljp.13 for ; Wed, 20 Nov 2019 06:45:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=j7HPOP6JMHflyU1AghbCSDzgYCPRwjkxRKl9hpZAclo=; b=F02CuT2OB9mIvACsctlHZxM9x+VmFi0SJAFyMdbBe0Fv9/j6bzKem5VJ50BPMzALJR e2bVZgj2rh8cFT/qelpL9dwaRdPyu3eNPLop/hbau0hCa46lcIUboJ8LdPnlDNLW5TjS bX0HEQgII9Yy7IqJj77WpTICGZjkdD/vr8qLKC5p34MHR+2plOXwcO92r4j3GgTVhsq5 +i5PyPSJ/6ZCcKA26b972aM0UviKbZa3rR3mlrJNsg5nZUjKRffl9SDvI3N2Ain4ZUUq oHFBgvcR2lGw5ZQEWEupjZ9TVEnRrKxHEt0R4IDtafZ+N1CttPZATENd2LYWfiGLJTHO I3Cg== 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:in-reply-to :references; bh=j7HPOP6JMHflyU1AghbCSDzgYCPRwjkxRKl9hpZAclo=; b=cuq6XsY4i3YcqEyu5+YxzpQvh+ZgrxulNlj3GQD1pgO8HkR+0IoxdIxoclW8VpUK56 eo+p+KCBjkD4QIGKJuHnE6IiHffVUc0CPTYB8bNdgBmGJE9+8no9lg7qMM1Y52z1Qd89 irOtWtQG2dU7nAYOUwy0BFMlEciwOTX0dgmfmG+1uCOGTX9MtalC/EZt5YIB2Oo6IWQn zuLoefh8HWUza8ELS2Xuy7lTxUWeIns9+HXZjQUfmxtvxiox8BCezQP+7B6+HtuVaDsj ewvnplE8aWL3KKuqbN4qBY+ZVSEjbfrW5l7ydfez4puBb6pXJbKJ4sfugQTMhXp2S1UG G7Cg== X-Gm-Message-State: APjAAAWYeBamiCImm+h7eYYYjLbApVajXVWtYR9dU2hZ7B1Sg6MLWNzZ AIqJlKldy/qASaSX3x39fLlkdtdAdCg= X-Google-Smtp-Source: APXvYqxsHxz/QkE6ZrFFkc5iDoGepMP4E1hgHHrJUxKQh3KIV303ul18g8idgpJTSM353PhO6JAlOA== X-Received: by 2002:a2e:b014:: with SMTP id y20mr3110391ljk.223.1574261131280; Wed, 20 Nov 2019 06:45:31 -0800 (PST) Received: from LAPTOP-4L3N7KFS.localdomain (dmczdm3mgdrzqc9ydzt-4.rev.dnainternet.fi. [2001:14bb:50:a835:5d45:1ba8:d174:3a4]) by smtp.gmail.com with ESMTPSA id z12sm13069791lfi.84.2019.11.20.06.45.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Nov 2019 06:45:30 -0800 (PST) From: Lev Stipakov To: openvpn-devel@lists.sourceforge.net Date: Wed, 20 Nov 2019 16:44:04 +0200 Message-Id: <20191120144404.187-1-lstipakov@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <1573390828-779-1-git-send-email-lstipakov@gmail.com> References: <1573390828-779-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.194 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (lstipakov[at]gmail.com) -0.0 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.208.194 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: 1iXREg-007QV8-U8 Subject: [Openvpn-devel] [PATCH v5 4/7] wintun: ring buffers based I/O 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 Implemented according to Wintun documentation and reference client code. Wintun uses ring buffers to communicate between kernel driver and user process. Client allocates send and receive ring buffers, creates events and passes it to kernel driver under LocalSystem privileges. When data is available for read, wintun modifies "tail" pointer of send ring and signals via event. User process reads data from "head" to "tail" and updates "head" pointer. When user process is ready to write, it writes to receive ring, updates "tail" pointer and signals to kernel via event. In openvpn code we add send ring's event to event loop. Before performing io wait, we compare "head" and "tail" pointers of send ring and if they're different, we skip io wait and perform read. This also adds ring buffers support to tcp and udp server code. Signed-off-by: Lev Stipakov --- v5: - fix crash at ring buffer registration on Win7 (passing NULL to DeviceIOControl, reported by kitsune1) v4: - added helper function tuntap_ring_empty() - refactored event handling, got rid of separate event_ctl() call for wintun and send/receive_tail_moved members - added wintun_ prefix for ring buffer variables - added a comment explaining the size of wintun-specific buffers v3: - simplified convoluted #ifdefs - replaced "greater than" with "greater or equal than" v2; - rebased on top of master src/openvpn/forward.c | 25 +++++++- src/openvpn/forward.h | 38 +++++++++++- src/openvpn/mtcp.c | 19 +++++- src/openvpn/mudp.c | 7 ++- src/openvpn/options.c | 4 +- src/openvpn/syshead.h | 1 + src/openvpn/tun.c | 62 ++++++++++++++++++- src/openvpn/tun.h | 135 +++++++++++++++++++++++++++++++++++++++++- src/openvpn/win32.c | 122 ++++++++++++++++++++++++++++++++++++++ src/openvpn/win32.h | 51 ++++++++++++++++ 10 files changed, 453 insertions(+), 11 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 8451706b..3dc04a40 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1256,8 +1256,24 @@ read_incoming_tun(struct context *c) perf_push(PERF_READ_IN_TUN); c->c2.buf = c->c2.buffers->read_tun_buf; + #ifdef _WIN32 - read_tun_buffered(c->c1.tuntap, &c->c2.buf); + if (c->c1.tuntap->wintun) + { + read_wintun(c->c1.tuntap, &c->c2.buf); + if (c->c2.buf.len == -1) + { + register_signal(c, SIGHUP, "tun-abort"); + c->persist.restart_sleep_seconds = 1; + msg(M_INFO, "Wintun read error, restarting"); + perf_pop(); + return; + } + } + else + { + read_tun_buffered(c->c1.tuntap, &c->c2.buf); + } #else ASSERT(buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame))); ASSERT(buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame))); @@ -2099,6 +2115,13 @@ io_wait_dowork(struct context *c, const unsigned int flags) tuntap |= EVENT_READ; } +#ifdef _WIN32 + if (tuntap_is_wintun(c->c1.tuntap)) + { + tuntap = EVENT_READ; + } +#endif + /* * Configure event wait based on socket, tuntap flags. */ diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 48202c07..b711ff00 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -375,6 +375,12 @@ p2p_iow_flags(const struct context *c) { flags |= IOW_TO_TUN; } +#ifdef _WIN32 + if (tuntap_ring_empty(c->c1.tuntap)) + { + flags &= ~IOW_READ_TUN; + } +#endif return flags; } @@ -403,8 +409,36 @@ io_wait(struct context *c, const unsigned int flags) } else { - /* slow path */ - io_wait_dowork(c, flags); +#ifdef _WIN32 + bool skip_iowait = flags & IOW_TO_TUN; + if (flags & IOW_READ_TUN) + { + /* + * don't read from tun if we have pending write to link, + * since every tun read overwrites to_link buffer filled + * by previous tun read + */ + skip_iowait = !(flags & IOW_TO_LINK); + } + if (tuntap_is_wintun(c->c1.tuntap) && skip_iowait) + { + unsigned int ret = 0; + if (flags & IOW_TO_TUN) + { + ret |= TUN_WRITE; + } + if (flags & IOW_READ_TUN) + { + ret |= TUN_READ; + } + c->c2.event_set_status = ret; + } + else +#endif + { + /* slow path */ + io_wait_dowork(c, flags); + } } } diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c index abe20593..30a13f73 100644 --- a/src/openvpn/mtcp.c +++ b/src/openvpn/mtcp.c @@ -269,8 +269,25 @@ multi_tcp_wait(const struct context *c, struct multi_tcp *mtcp) { int status; + unsigned int *persistent = &mtcp->tun_rwflags; socket_set_listen_persistent(c->c2.link_socket, mtcp->es, MTCP_SOCKET); - tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, &mtcp->tun_rwflags); + +#ifdef _WIN32 + if (tuntap_is_wintun(c->c1.tuntap)) + { + if (!tuntap_ring_empty(c->c1.tuntap)) + { + /* there is a data in wintun ring buffer, read it immediately */ + mtcp->esr[0].arg = MTCP_TUN; + mtcp->esr[0].rwflags = EVENT_READ; + mtcp->n_esr = 1; + return 1; + } + persistent = NULL; + } +#endif + tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent); + #ifdef ENABLE_MANAGEMENT if (management) { diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c index b7f061a2..6a29ccc8 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -278,7 +278,12 @@ p2mp_iow_flags(const struct multi_context *m) { flags |= IOW_READ; } - +#ifdef _WIN32 + if (tuntap_ring_empty(m->top.c1.tuntap)) + { + flags &= ~IOW_READ_TUN; + } +#endif return flags; } diff --git a/src/openvpn/options.c b/src/openvpn/options.c index c282b582..98874431 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -3005,10 +3005,10 @@ 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 */ + /* for wintun kernel doesn't send DHCP requests, so use netsh to set IP address and netmask */ if (options->wintun) { - options->tuntap_options.ip_win32_type = IPW32_SET_IPAPI; + options->tuntap_options.ip_win32_type = IPW32_SET_NETSH; } remap_redirect_gateway_flags(options); diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index 899aa59e..e9accb52 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 37bf065b..6522c6b6 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -795,9 +795,27 @@ init_tun_post(struct tuntap *tt, #ifdef _WIN32 overlapped_io_init(&tt->reads, frame, FALSE, true); overlapped_io_init(&tt->writes, frame, TRUE, true); - tt->rw_handle.read = tt->reads.overlapped.hEvent; - tt->rw_handle.write = tt->writes.overlapped.hEvent; tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; + + if (tt->wintun) + { + tt->wintun_send_ring = malloc(sizeof(struct tun_ring)); + tt->wintun_receive_ring = malloc(sizeof(struct tun_ring)); + if ((tt->wintun_send_ring == NULL) || (tt->wintun_receive_ring == NULL)) + { + msg(M_FATAL, "Cannot allocate memory for ring buffer"); + } + ZeroMemory(tt->wintun_send_ring, sizeof(struct tun_ring)); + ZeroMemory(tt->wintun_receive_ring, sizeof(struct tun_ring)); + + tt->rw_handle.read = CreateEvent(NULL, FALSE, FALSE, NULL); + tt->rw_handle.write = CreateEvent(NULL, FALSE, FALSE, NULL); + } + else + { + tt->rw_handle.read = tt->reads.overlapped.hEvent; + tt->rw_handle.write = tt->writes.overlapped.hEvent; + } #endif } @@ -6207,6 +6225,34 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun tt->ipapi_context_defined = true; } } + + if (tt->wintun) + { + if (tt->options.msg_channel) + { + /* TODO */ + } + else + { + if (!impersonate_as_system()) + { + msg(M_FATAL, "ERROR: Failed to impersonate as SYSTEM, make sure process is running under privileged account"); + } + if (!register_ring_buffers(tt->hand, + tt->wintun_send_ring, + tt->wintun_receive_ring, + tt->rw_handle.read, + tt->rw_handle.write)) + { + msg(M_FATAL, "ERROR: Failed to register ring buffers: %lu", GetLastError()); + } + if (!RevertToSelf()) + { + msg(M_FATAL, "ERROR: RevertToSelf error: %lu", GetLastError()); + } + } + } + /*netcmd_semaphore_release ();*/ gc_free(&gc); } @@ -6345,6 +6391,18 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) free(tt->actual_name); } + if (tt->wintun) + { + CloseHandle(tt->rw_handle.read); + CloseHandle(tt->rw_handle.write); + } + + free(tt->wintun_receive_ring); + free(tt->wintun_send_ring); + + tt->wintun_receive_ring = NULL; + tt->wintun_send_ring = NULL; + clear_tuntap(tt); free(tt); gc_free(&gc); diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 19cab7ea..784bde1a 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -183,6 +183,9 @@ struct tuntap bool wintun; /* true if wintun is used instead of tap-windows6 */ int standby_iter; + + struct tun_ring *wintun_send_ring; + struct tun_ring *wintun_receive_ring; #else /* ifdef _WIN32 */ int fd; /* file descriptor for TUN/TAP dev */ #endif @@ -212,6 +215,20 @@ tuntap_defined(const struct tuntap *tt) #endif } +#ifdef _WIN32 +inline bool +tuntap_is_wintun(struct tuntap *tt) +{ + return tt && tt->wintun; +} + +inline bool +tuntap_ring_empty(struct tuntap *tt) +{ + return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail); +} +#endif + /* * Function prototypes */ @@ -479,10 +496,124 @@ read_tun_buffered(struct tuntap *tt, struct buffer *buf) return tun_finalize(tt->hand, &tt->reads, buf); } +static inline ULONG +wintun_ring_packet_align(ULONG size) +{ + return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1); +} + +static inline ULONG +wintun_ring_wrap(ULONG value) +{ + return value & (WINTUN_RING_CAPACITY - 1); +} + +static inline void +read_wintun(struct tuntap *tt, struct buffer* buf) +{ + struct tun_ring *ring = tt->wintun_send_ring; + ULONG head = ring->head; + ULONG tail = ring->tail; + ULONG content_len; + struct TUN_PACKET *packet; + ULONG aligned_packet_size; + + *buf = tt->reads.buf_init; + buf->len = 0; + + if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) + { + msg(M_INFO, "Wintun: ring capacity exceeded"); + buf->len = -1; + return; + } + + if (head == tail) + { + /* nothing to read */ + return; + } + + content_len = wintun_ring_wrap(tail - head); + if (content_len < sizeof(struct TUN_PACKET_HEADER)) + { + msg(M_INFO, "Wintun: incomplete packet header in send ring"); + buf->len = -1; + return; + } + + packet = (struct TUN_PACKET *) &ring->data[head]; + if (packet->size > WINTUN_MAX_PACKET_SIZE) + { + msg(M_INFO, "Wintun: packet too big in send ring"); + buf->len = -1; + return; + } + + aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + packet->size); + if (aligned_packet_size > content_len) + { + msg(M_INFO, "Wintun: incomplete packet in send ring"); + buf->len = -1; + return; + } + + buf_write(buf, packet->data, packet->size); + + head = wintun_ring_wrap(head + aligned_packet_size); + ring->head = head; +} + +static inline int +write_wintun(struct tuntap *tt, struct buffer *buf) +{ + struct tun_ring *ring = tt->wintun_receive_ring; + ULONG head = ring->head; + ULONG tail = ring->tail; + ULONG aligned_packet_size; + ULONG buf_space; + struct TUN_PACKET *packet; + + if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) + { + msg(M_INFO, "Wintun: head/tail value is over capacity"); + return -1; + } + + aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + BLEN(buf)); + buf_space = wintun_ring_wrap(head - tail - WINTUN_PACKET_ALIGN); + if (aligned_packet_size > buf_space) + { + msg(M_INFO, "Wintun: ring is full"); + return 0; + } + + /* copy packet size and data into ring */ + packet = (struct TUN_PACKET* )&ring->data[tail]; + packet->size = BLEN(buf); + memcpy(packet->data, BPTR(buf), BLEN(buf)); + + /* move ring tail */ + ring->tail = wintun_ring_wrap(tail + aligned_packet_size); + if (ring->alertable != 0) + { + SetEvent(tt->rw_handle.write); + } + + return BLEN(buf); +} + static inline int write_tun_buffered(struct tuntap *tt, struct buffer *buf) { - return tun_write_win32(tt, buf); + if (tt->wintun) + { + return write_wintun(tt, buf); + } + else + { + return tun_write_win32(tt, buf); + } } #else /* ifdef _WIN32 */ @@ -545,7 +676,7 @@ tun_set(struct tuntap *tt, } } #ifdef _WIN32 - if (rwflags & EVENT_READ) + if (!tt->wintun && (rwflags & EVENT_READ)) { tun_read_queue(tt, 0); } diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index eb4c0307..5c168b3b 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -1493,4 +1493,126 @@ 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; + 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; +} + +bool +register_ring_buffers(HANDLE device, + struct tun_ring* send_ring, + struct tun_ring* receive_ring, + HANDLE send_tail_moved, + HANDLE receive_tail_moved) +{ + struct tun_register_rings rr; + BOOL res; + DWORD bytes_returned; + + ZeroMemory(&rr, sizeof(rr)); + + rr.send.ring = send_ring; + rr.send.ring_size = sizeof(send_ring->data); + rr.send.tail_moved = send_tail_moved; + + rr.receive.ring = receive_ring; + rr.receive.ring_size = sizeof(receive_ring->data); + rr.receive.tail_moved = receive_tail_moved; + + res = DeviceIoControl(device, TUN_IOCTL_REGISTER_RINGS, &rr, sizeof(rr), + NULL, 0, &bytes_returned, NULL); + + return res == TRUE; +} + #endif /* ifdef _WIN32 */ diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index 4814bbc5..f6d45bdc 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -25,6 +25,8 @@ #ifndef OPENVPN_WIN32_H #define OPENVPN_WIN32_H +#include + #include "mtu.h" #include "openvpn-msg.h" #include "argv.h" @@ -323,5 +325,54 @@ 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); +/* + * Values below are taken from Wireguard Windows client + * https://github.com/WireGuard/wireguard-go/blob/master/tun/wintun/ring_windows.go#L14 + */ +#define WINTUN_RING_CAPACITY 0x800000 +#define WINTUN_RING_TRAILING_BYTES 0x10000 +#define WINTUN_RING_FRAMING_SIZE 12 +#define WINTUN_MAX_PACKET_SIZE 0xffff +#define WINTUN_PACKET_ALIGN 4 + +struct tun_ring +{ + volatile ULONG head; + volatile ULONG tail; + volatile LONG alertable; + UCHAR data[WINTUN_RING_CAPACITY + WINTUN_RING_TRAILING_BYTES + WINTUN_RING_FRAMING_SIZE]; +}; + +struct tun_register_rings +{ + struct + { + ULONG ring_size; + struct tun_ring *ring; + HANDLE tail_moved; + } send, receive; +}; + +struct TUN_PACKET_HEADER +{ + uint32_t size; +}; + +struct TUN_PACKET +{ + uint32_t size; + UCHAR data[WINTUN_MAX_PACKET_SIZE]; +}; + +#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) + +bool impersonate_as_system(); + +bool register_ring_buffers(HANDLE device, + struct tun_ring *send_ring, + struct tun_ring *receive_ring, + HANDLE send_tail_moved, + HANDLE receive_tail_moved); + #endif /* ifndef OPENVPN_WIN32_H */ #endif /* ifdef _WIN32 */