@@ -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_ */
@@ -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)
{
@@ -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",
@@ -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
*/
@@ -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);