From patchwork Wed Jul 17 03:20:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lev Stipakov X-Patchwork-Id: 781 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id iBpNLuQgL11XcAAAIUCqbw for ; Wed, 17 Jul 2019 09:21:40 -0400 Received: from proxy20.mail.ord1d.rsapps.net ([172.30.191.6]) by director11.mail.ord1d.rsapps.net with LMTP id MG0RLuQgL10NLAAAvGGmqA ; Wed, 17 Jul 2019 09:21:40 -0400 Received: from smtp27.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy20.mail.ord1d.rsapps.net with LMTP id 0FvYLeQgL12CWAAAsk8m8w ; Wed, 17 Jul 2019 09:21:40 -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: smtp27.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: cf39ab18-a895-11e9-bb2b-b8ca3a655ab8-1-1 Received: from [216.105.38.7] ([216.105.38.7:58492] helo=lists.sourceforge.net) by smtp27.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 9F/E8-47139-3E02F2D5; Wed, 17 Jul 2019 09:21:39 -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 1hnjrA-0006So-VQ; Wed, 17 Jul 2019 13:20:28 +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 1hnjrA-0006Sd-Ic for openvpn-devel@lists.sourceforge.net; Wed, 17 Jul 2019 13:20:28 +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=Mb+k4TpIVaVf9lRLX9qYppsUtXaFqNS9cmY3t2mvRgs=; b=CwrCMTGYpyVFUc24OdM1D55xBT Fid4EgAD1mlG2GGuO26d/5FLKHOTUdo2oAtx1aGNzorvKxC2fs3wSWD2SVg2TIsIKYv5JV1bzSTb4 PGnKeBR4nIyKsvuXfCSfXsj3F1l+tGDdL4IYDPhpwtCPuUVOd6W51QcgDNpenU4ON7XM=; 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=Mb+k4TpIVaVf9lRLX9qYppsUtXaFqNS9cmY3t2mvRgs=; b=PgA7CHYeE4UdvnsuZ1gcC07thr YNKrT3Nx/dlAHDrlgg4wTqxJeTgR5omuz8V3yYdXtX1O0bqlpZ5EixfKit4mitmoQ7F2GQ2Z0WWBa YTlkADvafBrTwTkSJTca3UrrbIs9uvpYBBKu7V1qBSZy0tundk0p41MizJhsT8HjEzZQ=; Received: from mail-ed1-f68.google.com ([209.85.208.68]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.90_1) id 1hnjr8-0058x0-Th for openvpn-devel@lists.sourceforge.net; Wed, 17 Jul 2019 13:20:28 +0000 Received: by mail-ed1-f68.google.com with SMTP id x19so19677240eda.12 for ; Wed, 17 Jul 2019 06:20:26 -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=Mb+k4TpIVaVf9lRLX9qYppsUtXaFqNS9cmY3t2mvRgs=; b=tNqHPUQBIccXY6WvyAdSy4tfPLGf/yHujzXEyN8TvqQqO706z78ZCDo1RjYk2ZRcCo r0NxveMCn2L90qLd8PrqFd6y9VoBSiTKBK6Eujl/4AB2AmuU7MTpQFrNlHHIUYbSd5qj 0ToYNauJySq6chL7ZRdSFFjsnix9tQ6lsPYQ807ybiuOMVT04kpOORG22+h1REeSAU2J r1chd6leIcVCHvLv42DzwpDVjqWwfYaZoFUC1RW0i1IkgHn06aW7L5JUsW6GqRD5475Z rAJyo+aw5GL4jylV7N5e5/KmH2AWmbt5v84+RbD8M61sEnkEpwuLix40ydUUeroBUmXO yKNg== 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=Mb+k4TpIVaVf9lRLX9qYppsUtXaFqNS9cmY3t2mvRgs=; b=aOM0WPs8PsPUOHLyXaCKOim6Jf2B5CXnePli3csj9iAcHGitpn5PCO2lQfRbHIrDAz SwItvIF6rB80INhVuyT2bxA63NpM2+GIzrdCwe5c5DXENovLcM9zH9BpZlm8myqIkR7o vJxIIp6hRxMmPTi7EPcfWP1I7d5o37JMHrAQGA8ShRFmXPuD7idOBJBnPIzYfIYY9YiY 7OpqvT9dOmSgEGMtVNRvsUn4e2g8ejphluB5wSJu7RpWZeqpWmiHzUsqs/eGOegCAgxF UQHIHyaK2oFZmAic89x5dwhL4ZRmmOZ2/60ZICj+kycE3PN9JFoHl44Tq2iM7ld+RbsH CwDw== X-Gm-Message-State: APjAAAXBF8xMyoPsMKEusfSJPbp33mOnOGiggmcrfcrH2W32xviONQoS LC9/vfFVxWtdDmIou1e3tND4qq6L0vCp9w== X-Google-Smtp-Source: APXvYqxFYyxci+E+ARWjd+sBU88jZZwI6BH6FPGx77c6BhHzmkwUXzQA0PQaEQHXXVvQ19icp9yicw== X-Received: by 2002:a50:976d:: with SMTP id d42mr34723547edb.77.1563369619884; Wed, 17 Jul 2019 06:20:19 -0700 (PDT) Received: from stipakov.fi (stipakov.fi. [128.199.52.117]) by smtp.gmail.com with ESMTPSA id q50sm7045295edd.91.2019.07.17.06.20.18 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 17 Jul 2019 06:20:18 -0700 (PDT) From: Lev Stipakov To: openvpn-devel@lists.sourceforge.net Date: Wed, 17 Jul 2019 16:20:08 +0300 Message-Id: <1563369608-10240-1-git-send-email-lstipakov@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: References: 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: 1hnjr8-0058x0-Th Subject: [Openvpn-devel] [PATCH v4] openvpnserv: enable interactive service to open tun 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 patch enables interactive service to open tun device. This is mostly needed by Wintun, which could be opened only by privileged process. When interactive service is used, instead of calling CreateFile() directly by openvpn process we pass tun device path into service process. There we open device, duplicate handle and pass it back to openvpn process. Signed-off-by: Lev Stipakov --- v4: - strengthen security by adding basic validation to device path - reorder fields in msg_open_tun_device_result for backward compatibility v3: - ensure that device path passed by client is null-terminated - support for multiple openvpn processes - return proper error code when device handle is invalid v2: - introduce send_msg_iservice_ex() instead of changing signature of existing send_msg_iservice() - use wchar_t strings in interactive service code include/openvpn-msg.h | 12 ++++++ src/openvpn/tun.c | 83 +++++++++++++++++++++++++++++++++-------- src/openvpn/win32.c | 9 ++++- src/openvpn/win32.h | 30 +++++++++++++-- src/openvpnserv/interactive.c | 86 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 196 insertions(+), 24 deletions(-) diff --git a/include/openvpn-msg.h b/include/openvpn-msg.h index 66177a2..86d82f2 100644 --- a/include/openvpn-msg.h +++ b/include/openvpn-msg.h @@ -39,6 +39,8 @@ typedef enum { msg_del_block_dns, msg_register_dns, msg_enable_dhcp, + msg_open_tun_device, + msg_open_tun_device_result, } message_type_t; typedef struct { @@ -117,4 +119,14 @@ typedef struct { interface_t iface; } enable_dhcp_message_t; +typedef struct { + message_header_t header; + char device_path[256]; +} open_tun_device_message_t; + +typedef struct { + message_header_t header; + int error_number; + HANDLE handle; +} open_tun_device_result_message_t; #endif /* ifndef OPENVPN_MSG_H_ */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 8f8f7c6..8c7e705 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -5248,6 +5248,43 @@ out: return ret; } +static HANDLE +service_open_tun_device(const HANDLE pipe, const char* device_path) +{ + open_tun_device_result_message_t result_msg; + struct gc_arena gc = gc_new(); + open_tun_device_message_t open_tun_device = { + .header = { + msg_open_tun_device, + sizeof(open_tun_device_message_t), + 0 + } + }; + result_msg.handle = INVALID_HANDLE_VALUE; + + strncpynt(open_tun_device.device_path, device_path, sizeof(open_tun_device.device_path)); + + if (!send_msg_iservice_ex(pipe, &open_tun_device, sizeof(open_tun_device), + &result_msg, sizeof(result_msg), "Open_tun_device")) + { + goto out; + } + + if (result_msg.error_number != NO_ERROR) + { + msg(D_TUNTAP_INFO, "TUN: opening tun handle using service failed: %s [status=%u device_path=%s]", + strerror_win32(result_msg.error_number, &gc), result_msg.error_number, device_path); + } + else + { + msg(M_INFO, "Opened tun device %s using service", device_path); + } + +out: + gc_free(&gc); + return result_msg.handle; +} + /* * Return a TAP name for netsh commands. */ @@ -5591,15 +5628,22 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun device_guid, TAP_WIN_SUFFIX); - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 + if (tt->options.msg_channel) + { + tt->hand = service_open_tun_device(tt->options.msg_channel, device_path); + } + else + { + tt->hand = CreateFile( + device_path, + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 ); + } if (tt->hand == INVALID_HANDLE_VALUE) { @@ -5631,15 +5675,22 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun device_guid, TAP_WIN_SUFFIX); - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 + if (tt->options.msg_channel) + { + tt->hand = service_open_tun_device(tt->options.msg_channel, device_path); + } + else + { + tt->hand = CreateFile( + device_path, + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 ); + } if (tt->hand == INVALID_HANDLE_VALUE) { diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index eb4c030..039c1a4 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -1476,12 +1476,19 @@ bool send_msg_iservice(HANDLE pipe, const void *data, size_t size, ack_message_t *ack, const char *context) { + return send_msg_iservice_ex(pipe, data, size, ack, sizeof(*ack), context); +} + +bool +send_msg_iservice_ex(HANDLE pipe, const void *data, size_t size, + void *response, size_t response_size, const char *context) +{ struct gc_arena gc = gc_new(); DWORD len; bool ret = true; if (!WriteFile(pipe, data, size, &len, NULL) - || !ReadFile(pipe, ack, sizeof(*ack), &len, NULL)) + || !ReadFile(pipe, response, response_size, &len, NULL)) { msg(M_WARN, "%s: could not talk to service: %s [%lu]", context ? context : "Unknown", diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index 4814bbc..8ccddc0 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -309,14 +309,36 @@ int win32_version_info(void); */ const char *win32_version_string(struct gc_arena *gc, bool add_name); -/* - * Send the |size| bytes in buffer |data| to the interactive service |pipe| - * and read the result in |ack|. Returns false on communication error. - * The string in |context| is used to prefix error messages. + +/** + * Send data to interactive service and receive response of type ack_message_t. + * + * @param pipe The handle of communication pipe + * @param data The data to send + * @param size The size of data to send + * @param response The pointer to ack_message_t structure to where response is written + * @param context The string used to prefix error messages + * + * @returns True on success, false on failure. */ bool send_msg_iservice(HANDLE pipe, const void *data, size_t size, ack_message_t *ack, const char *context); +/** + * Send data to interactive service and receive response. + * + * @param pipe The handle of communication pipe + * @param data The data to send + * @param size The size of data to send + * @param response The buffer to where response is written + * @param response_size The size of response buffer + * @param context The string used to prefix error messages + * + * @returns True on success, false on failure. + */ +bool send_msg_iservice_ex(HANDLE pipe, const void *data, size_t size, + void *response, size_t response_size, const char *context); + /* * Attempt to simulate fork/execve on Windows */ diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c index 623c3ff..c3f5754 100644 --- a/src/openvpnserv/interactive.c +++ b/src/openvpnserv/interactive.c @@ -58,7 +58,6 @@ static settings_t settings; static HANDLE rdns_semaphore = NULL; #define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */ - openvpn_service_t interactive_service = { interactive, TEXT(PACKAGE_NAME "ServiceInteractive"), @@ -1198,8 +1197,70 @@ HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp) return err; } +static DWORD +HandleOpenTunDeviceMessage(const open_tun_device_message_t *open_tun, HANDLE ovpn_proc, HANDLE *remote_handle) +{ + DWORD err = ERROR_SUCCESS; + HANDLE local_handle; + LPWSTR device_path; + const WCHAR prefix[] = L"\\\\.\\Global\\"; + const WCHAR wintun_prefix[] = L"\\\\.\\Global\\WINTUN"; + const WCHAR tap_suffix[] = L".tap"; + BOOL path_ok = FALSE; + + *remote_handle = INVALID_HANDLE_VALUE; + + device_path = utf8to16(open_tun->device_path); + if (!device_path) + { + err = ERROR_OUTOFMEMORY; + goto out; + } + + /* ensure we open device, not file */ + path_ok = (wcslen(device_path) > wcslen(prefix)) && + (wcsncmp(device_path, prefix, wcslen(prefix)) == 0); + + if (path_ok) + { + /* ensure we open path which looks like tap or wintun */ + path_ok = (wcsncmp(device_path + wcslen(device_path) - wcslen(tap_suffix), tap_suffix, wcslen(tap_suffix)) == 0) || + ((wcslen(device_path) > wcslen(wintun_prefix)) && (wcsncmp(device_path, wintun_prefix, wcslen(wintun_prefix)) == 0)); + } + + if (!path_ok) + { + err = ERROR_MESSAGE_DATA; + MsgToEventLog(MSG_FLAGS_ERROR, TEXT("Invalid device path (%s)"), device_path); + goto out; + } + + /* open tun device */ + local_handle = CreateFileW(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); + if (local_handle == INVALID_HANDLE_VALUE) + { + err = GetLastError(); + MsgToEventLog(M_SYSERR, TEXT("Error opening tun device (%s)"), device_path); + goto out; + } + + /* duplicate tun device handle to openvpn process */ + if (!DuplicateHandle(GetCurrentProcess(), local_handle, ovpn_proc, remote_handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + err = GetLastError(); + MsgToEventLog(M_SYSERR, TEXT("Error duplicating tun device handle")); + } + +out: + free(device_path); + + return err; +} + static VOID -HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists) +HandleMessage(HANDLE pipe, HANDLE ovpn_proc, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists) { DWORD read; union { @@ -1210,6 +1271,7 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists block_dns_message_t block_dns; dns_cfg_message_t dns; enable_dhcp_message_t dhcp; + open_tun_device_message_t open_tun; } msg; ack_message_t ack = { .header = { @@ -1277,6 +1339,24 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists } break; + case msg_open_tun_device: + if (msg.header.size == sizeof(msg.open_tun)) + { + open_tun_device_result_message_t res = { + .header = { + .type = msg_open_tun_device_result, + .size = sizeof(res), + .message_id = msg.header.message_id + } + }; + /* make sure that string passed from user process is null-terminated */ + msg.open_tun.device_path[sizeof(msg.open_tun.device_path) - 1] = '\0'; + res.error_number = HandleOpenTunDeviceMessage(&msg.open_tun, ovpn_proc, &res.handle); + WritePipeAsync(pipe, &res, sizeof(res), count, events); + return; + } + break; + default: ack.error_number = ERROR_MESSAGE_TYPE; MsgToEventLog(MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type); @@ -1611,7 +1691,7 @@ RunOpenvpn(LPVOID p) break; } - HandleMessage(ovpn_pipe, bytes, 1, &exit_event, &undo_lists); + HandleMessage(ovpn_pipe, proc_info.hProcess, bytes, 1, &exit_event, &undo_lists); } WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);