diff --git a/README-TAP-Windows.txt b/README-TAP-Windows.txt new file mode 100644 index 000000000000..eefb0b4928c3 --- /dev/null +++ b/README-TAP-Windows.txt @@ -0,0 +1,39 @@ + +Experimental tunnel management has been added on Windows. It relies on +TAP-Windows V9 driver, avilable here: + + https://build.openvpn.net/downloads/releases/tap-windows-9.23.3-I601-Win10.exe + +At the moment (2024/09/23) this patch has been tested only on Windows10-19045 +and TAP-Windows driver 9.23.3.601. Most of the tests used client mode +(ssh.exe). Server mode (sshd.exe) has only been tested superficially. + +Since TAP driver only supports level-2 tunnel, option "Tunnel=ethernet" must +be provided by "-o" command line option or by configuration file. + +A notable difference between Linux TAP and Windows TAP is that on Windows +there is no dynamic TAP device instance creation. This is why we need to +statically create a number of TAP adapter instances by installing TAP driver +for the desired number of times. Each adapter created this way must be +renamed manually, the names must begin with a common prefix ("SSH Tunnel" by +default). SSH will search a free adapter into the set of adapters whose name +begins with the prefix. The number of adapters created in this way is the number +of simultaneous sessions that can be opened. For clients (ssh.exe) one +single instance is usually enough, but for servers (sshd.exe) a largest number +of reserved adapters is required. The name prefix SSH looks for can be +configured by appendig a ":" caracter and the prefix it to "ethernet" string +in "Tunnel" (or "PermitTunnel" sor sshd) option. +(e.g: "Tunnel=ethernet:MY_PREFIX")) . At the moment, only +ASCII characters are allowed, although Windows uses WCHAR for adapter names +(property "FriendlyName"). + +It is advised to specify "-w any" in command line, and let the program +choose a free adapter. Although it is possible to specify a particular tunnel +device number (the "IfIndex" adapter's property), it isn't handy to get it, +since Windows puts al kind of adapters (TAP, ethernet...) togather, and +enumeration is not intuitive. A way to get it is the powershell command +"Get-NetAdapter". + +SSH only establishes the L2 tunnel, it is on the user (both on the client and the +server side) to configure the TAP device (IP addresses, gateway, routes..). + diff --git a/clientloop.c b/clientloop.c index 2d6e42b028f6..608cf20fa7ab 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1861,7 +1861,7 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode, debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); /* Open local tunnel device */ - if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { + if ((fd = tun_open(local_tun, tun_mode, options.tun_options, &ifname)) == -1) { error("Tunnel device open failed."); return NULL; } diff --git a/configure.ac b/configure.ac index 2eede34c3cf9..2cbbe22d03cc 100644 --- a/configure.ac +++ b/configure.ac @@ -770,6 +770,7 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], [Define if your resolver libs need this for getrrsetbyname]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) + AC_DEFINE([SSH_TUN_TAP_WINDOWS_V9], [1], [Use TAP-Windows Adapter V9 (>=W10 only)]) AC_DEFINE([SSH_TUN_COMPAT_AF], [1], [Use tunnel device compatibility to OpenBSD]) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs index 3b6af382a313..d70c9f3d2731 100644 --- a/contrib/win32/openssh/config.h.vs +++ b/contrib/win32/openssh/config.h.vs @@ -1474,6 +1474,9 @@ /* Open tunnel devices the FreeBSD way */ /* #undef SSH_TUN_FREEBSD */ +/* Use TAP-Windows Adapter V9 (>=W10 only) */ +#define SSH_TUN_TAP_WINDOWS_V9 1 + /* Open tunnel devices the Linux tun/tap way */ /* #undef SSH_TUN_LINUX */ diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index b7e4e6338274..4b14873e456f 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -317,7 +317,7 @@ createFile_flags_setup(int flags, mode_t mode, struct createFile_flags* cf_flags } /*only following create and status flags currently supported*/ - if (c_s_flags & ~(O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC | O_EXCL | O_BINARY)) { + if (c_s_flags & ~(O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC | O_EXCL | O_BINARY | O_SYSTEM)) { debug3("open - ERROR: Unsupported flags: %d", flags); errno = ENOTSUP; return -1; @@ -354,7 +354,14 @@ createFile_flags_setup(int flags, mode_t mode, struct createFile_flags* cf_flags if (c_s_flags & O_APPEND) cf_flags->dwDesiredAccess = FILE_APPEND_DATA; - cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS; + // Hack to deal with TAP driver requirements: FILE_ATTRIBUTE_SYSTEM, no FILE_FLAG_BACKUP_SEMANTICS + if (c_s_flags & O_SYSTEM) { + cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_SYSTEM; + mode = USHRT_MAX; + } + else { + cf_flags->dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS; + } // If the mode is USHRT_MAX then we will inherit the permissions from the parent folder. if (mode != USHRT_MAX) { @@ -427,12 +434,12 @@ file_in_chroot_jail(HANDLE handle) { if (!final_path) return 0; - const wchar_t* uncPrefix = L"UNC\\"; - int isUNCPath = memcmp(final_path, uncPrefix, 2 * wcslen(uncPrefix)); - - if (0 == isUNCPath) { - debug3("symlink points to UNCPath"); - return 1; + const wchar_t* uncPrefix = L"UNC\\"; + int isUNCPath = memcmp(final_path, uncPrefix, 2 * wcslen(uncPrefix)); + + if (0 == isUNCPath) { + debug3("symlink points to UNCPath"); + return 1; } to_wlower_case(final_path); @@ -682,35 +689,35 @@ WriteCompletionRoutine(_In_ DWORD dwErrorCode, *((__int64*)&lpOverlapped->Offset) += dwNumberOfBytesTransfered; } -int -fileio_write_wrapper(struct w32_io* pio, const void* buf, size_t bytes_to_copy) -{ - int bytes_written = 0; - if (bytes_to_copy <= WRITE_BUFFER_SIZE) { - bytes_written = fileio_write(pio, buf, bytes_to_copy); - return bytes_written; - } - - void* chunk_buf = NULL; - int chunk_count = 0; - int bytes_copied = -1; - size_t chunk_size = 0; - - for (int i = 0; i < bytes_to_copy; i += WRITE_BUFFER_SIZE, chunk_count++) { - chunk_buf = (BYTE*)buf + chunk_count * WRITE_BUFFER_SIZE; - chunk_size = ((bytes_to_copy - i) >= WRITE_BUFFER_SIZE) ? WRITE_BUFFER_SIZE : (bytes_to_copy - i); - bytes_written = fileio_write(pio, chunk_buf, chunk_size); - - if (bytes_written == -1) - return bytes_copied; - - if (bytes_copied == -1) - bytes_copied = 0; - - bytes_copied += bytes_written; - } - return bytes_copied; - +int +fileio_write_wrapper(struct w32_io* pio, const void* buf, size_t bytes_to_copy) +{ + int bytes_written = 0; + if (bytes_to_copy <= WRITE_BUFFER_SIZE) { + bytes_written = fileio_write(pio, buf, bytes_to_copy); + return bytes_written; + } + + void* chunk_buf = NULL; + int chunk_count = 0; + int bytes_copied = -1; + size_t chunk_size = 0; + + for (int i = 0; i < bytes_to_copy; i += WRITE_BUFFER_SIZE, chunk_count++) { + chunk_buf = (BYTE*)buf + chunk_count * WRITE_BUFFER_SIZE; + chunk_size = ((bytes_to_copy - i) >= WRITE_BUFFER_SIZE) ? WRITE_BUFFER_SIZE : (bytes_to_copy - i); + bytes_written = fileio_write(pio, chunk_buf, chunk_size); + + if (bytes_written == -1) + return bytes_copied; + + if (bytes_copied == -1) + bytes_copied = 0; + + bytes_copied += bytes_written; + } + return bytes_copied; + } /* write() implementation */ @@ -947,51 +954,51 @@ fileio_lseek(struct w32_io* pio, unsigned __int64 offset, int origin) return 0; } -/* fdopen() to be used on pipe handles */ -static FILE* -fileio_fdopen_pipe(struct w32_io* pio, const char *mode) -{ - int fd_flags = 0; - FILE* ret; - debug4("fdopen - io:%p", pio); - - if (mode[1] == '\0') { - switch (*mode) { - case 'r': - fd_flags = _O_RDONLY; - break; - case 'w': - break; - case 'a': - fd_flags = _O_APPEND; - break; - default: - errno = ENOTSUP; - debug3("fdopen - ERROR unsupported mode %s", mode); - return NULL; - } - } - else { - errno = ENOTSUP; - debug3("fdopen - ERROR unsupported mode %s", mode); - return NULL; - } - - int fd = _open_osfhandle((intptr_t)pio->handle, fd_flags); - - if (fd == -1 || (ret = _fdopen(fd, mode)) == NULL) { - errno = EOTHER; - debug3("fdopen - ERROR:%d _open_osfhandle()", errno); - return NULL; - } - - // overwrite underlying win32 handle - its expected to be closed via fclose - // and close pio - pio->handle = NULL; +/* fdopen() to be used on pipe handles */ +static FILE* +fileio_fdopen_pipe(struct w32_io* pio, const char *mode) +{ + int fd_flags = 0; + FILE* ret; + debug4("fdopen - io:%p", pio); + + if (mode[1] == '\0') { + switch (*mode) { + case 'r': + fd_flags = _O_RDONLY; + break; + case 'w': + break; + case 'a': + fd_flags = _O_APPEND; + break; + default: + errno = ENOTSUP; + debug3("fdopen - ERROR unsupported mode %s", mode); + return NULL; + } + } + else { + errno = ENOTSUP; + debug3("fdopen - ERROR unsupported mode %s", mode); + return NULL; + } + + int fd = _open_osfhandle((intptr_t)pio->handle, fd_flags); + + if (fd == -1 || (ret = _fdopen(fd, mode)) == NULL) { + errno = EOTHER; + debug3("fdopen - ERROR:%d _open_osfhandle()", errno); + return NULL; + } + + // overwrite underlying win32 handle - its expected to be closed via fclose + // and close pio + pio->handle = NULL; int w32_close(int); - w32_close(pio->table_index); - return ret; -} + w32_close(pio->table_index); + return ret; +} /* fdopen() to be used on file handles */ static FILE* diff --git a/contrib/win32/win32compat/inc/fcntl.h b/contrib/win32/win32compat/inc/fcntl.h index 94ed0fa3dfe3..c522f67d0c21 100644 --- a/contrib/win32/win32compat/inc/fcntl.h +++ b/contrib/win32/win32compat/inc/fcntl.h @@ -37,6 +37,8 @@ void* w32_fd_to_handle(int fd); #define O_SEQUENTIAL _O_SEQUENTIAL #define O_RANDOM _O_RANDOM #define O_U16TEXT _O_U16TEXT +// Hack to deal with TAP driver requirements: FILE_ATTRIBUTE_SYSTEM, no FILE_FLAG_BACKUP_SEMANTICS +#define O_SYSTEM 0x4000000 /* * open() POSIX specific modes and flags. diff --git a/misc.c b/misc.c index 0e09d23edee0..3eb27cf3d8b2 100644 --- a/misc.c +++ b/misc.c @@ -1492,10 +1492,10 @@ percent_dollar_expand(const char *string, ...) } int -tun_open(int tun, int mode, char **ifname) +tun_open(int tun, int mode, const char* tun_options, char **ifname) { #if defined(CUSTOM_SYS_TUN_OPEN) - return (sys_tun_open(tun, mode, ifname)); + return (sys_tun_open(tun, mode, tun_options, ifname)); #elif defined(SSH_TUN_OPENBSD) struct ifreq ifr; char name[100]; diff --git a/misc.h b/misc.h index 1b650866c96f..b7686c6ab300 100644 --- a/misc.h +++ b/misc.h @@ -138,7 +138,7 @@ void freeargs(arglist *); void duplicateargs(arglist *, const arglist *); #endif -int tun_open(int, int, char **); +int tun_open(int, int, const char *, char **); /* Common definitions for ssh tunnel device forwarding */ #define SSH_TUNMODE_NO 0x00 diff --git a/openbsd-compat/port-net.c b/openbsd-compat/port-net.c index 198e73f0de20..d80120f54ce2 100644 --- a/openbsd-compat/port-net.c +++ b/openbsd-compat/port-net.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include "includes.h" #include @@ -141,7 +142,7 @@ sys_set_process_rdomain(const char *name) #define TUN_CTRL_DEV "/dev/net/tun" int -sys_tun_open(int tun, int mode, char **ifname) +sys_tun_open(int tun, int mode, const char* tunnel_options, char **ifname) { struct ifreq ifr; int fd = -1; @@ -206,7 +207,7 @@ sys_tun_open(int tun, int mode, char **ifname) #endif int -sys_tun_open(int tun, int mode, char **ifname) +sys_tun_open(int tun, int mode, const char* tunnel_options, char **ifname) { struct ifreq ifr; char name[100]; @@ -376,3 +377,346 @@ sys_tun_outfilter(struct ssh *ssh, struct Channel *c, return (buf); } #endif /* SSH_TUN_FILTER */ + +#if defined(SSH_TUN_TAP_WINDOWS_V9) + +#if defined(WIN32) || defined(WIN64) + +//////////////////////////////////////////////////////////////////// +// +// Experimental support for Windows Tunnel (TAP-Windows V9 driver) +// +// This part adds a quite rude support to tunnel device on Windows, +// so you can use "-w" options on Windows. +// It uses TAP-Windows V9 driver. At the moment (2024/09/20) this +// code has been tested only on Windows10-19045 and TAP-Windows +// driver 9.23.3.601 (you can download it here: +// https://build.openvpn.net/downloads/releases/tap-windows-9.23.3-I601-Win10.exe). +// Only "ethernet" tunnel mode is supported, since TAP-Windows +// driver doesn't implement TUN (point-to-point). Because of this, +// "-o Tunnel=ethernet" command line option or equivalent configuration +// item must be set. Here's the typical command invocation: +// +// ssh -o Tunnel=ethernet user@host -w any +// +// One can also specify an adapter index (the ifIndex field +// as shown, for example, in powershell's "get-netadapter" +// output) to force the use of a particular TAP instance. +// +// Here's what the function "sys_tun_open" does: +// - It checks if the selected tunnel mode is "ethernet". +// - It explores the list of adapters returned by +// GetAdaptersAddresses Windows function (from IPHLPAPI subsystem). +// - If a index is specified, the function takes the adaper +// whose IfIndex match the given value. If the index is not +// specified ("any"), the function takes the adapters whose +// friendly name starts with "SSH Tunnel" (case insensitive) +// or whatever is configured. The name prefix SSH looks for can be +// configured by appendig a ":" caracter and the prefix it to "ethernet" string +// in "Tunnel" (or "PermitTunnel" sor sshd) option. +// - If a matching adapter if found, the function tries to open +// and activate it. In case of failure, it takes the next matching +// adapter. +// - The file descriptor of the first successful open is resturned, +// or -1 if there are no free adapters. +// +// The function relies on the presence of a certain number of +// instances of the TAP-Windows driver, all with friendly names +// starting with "SSH Tunnel". During installation, these instances +// must be generated by installing the driver as many times as +// necessary. Each instance must be renamed according to the +// convention (e.g. "SSH Tunnel 1", "SSH Tunnel 2" ...). +// You can change the default prefix via "TunnelOptions" +// configuration item. NOTE: Only ANSI characters are allowed. +// +// NOTE: TAP devices must be opened in a special way. In order to +// make the device interoperate with the existing SSH framework +// (based on UNIX' "file descriptor" paradigm), we added +// a special flag "O_SYSTEM" for "open". This is because the way +// "open" ("w32_open") calls "CreateFile" is not suitable for TAP +// devices. In particular, FILE_ATTRIBUTE_SYSTEM must be provided, +// and FILE_FLAG_BACKUP_SEMANTICS must not appear. The new flag +// instructs "open" to respect this rule. +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#pragma comment(lib, "IPHLPAPI.lib") + +#include "ssh.h" + +//#include "readconf.h" +//extern Options options; + +/* From OpenVPN tap driver, common.h */ +#define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) +#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE (10, METHOD_BUFFERED) + + +#define SSH_BASE_ADAPTER_FRIENDLY_NAME "SSH Tunnel" + +static int open_adapter(const char* name) +{ + int fd = -1; + char *netdev = NULL; + int ok = 0; + + debug(" Trying adapter %s", name); + + + netdev = (char*)malloc(strlen(name) + 30 /* 16 at least */); + if (netdev == NULL) { + error("Out of memory while creating adapter device name"); + goto FAIL; + } + + sprintf(netdev, "\\\\.\\Global\\%s.tap", name); + + /* + HANDLE h = CreateFile(netdev, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); + */ + + // Open device. Non-standard flag O_SYSTEM forces FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED + // in CreateFile. + // NOTE: "open" internally modifies paths (e.g. converts "\" in "/" + // and much more). Windows seems to understand anyway, fortunately. + + fd = open(netdev, O_RDWR | O_EXCL | O_SYSTEM); + + if (fd < 0) { + goto FAIL; + } + + HANDLE h = w32_fd_to_handle(fd); + DWORD len = 0; + + // For debugging purpose, we print the MAC. + // It is also a way to make sure that the device is a TAP. + + unsigned char mac[6]; + + if (DeviceIoControl(h, TAP_IOCTL_GET_MAC, &mac, sizeof(mac), &mac, sizeof(mac), &len, NULL)) { + debug(" Adapter's MAC: %02x-%02x-%02x-%02x-%02x-%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } + else { + // We could survive (we don't actually need to know MAC), + // but a failure here is a symptom of more general + // problems, or perhaps the device is not a real TAP, but something else. + error("Could not get adapter MAC, error 0x%08x", GetLastError()); + goto FAIL; + } + + // Turn adapter on. If we didn't do the device couldn't work (e.g. ReadFileEx would fail). + + ULONG flag = 1; + + if (DeviceIoControl(h, TAP_IOCTL_SET_MEDIA_STATUS, &flag, sizeof(flag), &flag, sizeof(flag), &len, NULL) == 0) { + error("Could not activate adapter, error 0x%08x", GetLastError()); + goto FAIL; + } + + ok = 1; + +FAIL: + if (!ok) { + if (fd >= 0) { + close(fd); + fd = -1; + } + } + if (netdev != NULL) { + free(netdev); + } + return fd; +} + +static int lookup_and_open_tap_instance(int tun, char** ifname, const char *prefix) +{ + DWORD rv; + ULONG sz = 0; + ULONG new_sz = 0x4000; + PIP_ADAPTER_ADDRESSES result = NULL; + int tun_fd = -1; + PWCHAR match = NULL; + size_t match_len = 0; + int ok = 0; + + if (prefix == NULL) { + prefix = SSH_BASE_ADAPTER_FRIENDLY_NAME; + } + + match_len = mbstowcs(NULL, prefix, 0); + if (match_len == (size_t)(-1)) { + error("Bad adapter name prefix: \"%s\"", prefix); + goto FAIL; + } + match = (PWCHAR)malloc((match_len + 1) * sizeof(*match)); + if (match == NULL) { + error("Out of memory while creating adapter name"); + goto FAIL; + } + match_len = mbstowcs(match, prefix, match_len + 1); + + // if tun == SSH_TUNID_ANY: + // Looking for a free TUN/TAP instance. + // We should create some TUN/TAP instances devoted to SSH, + // and choose a recognizable "frendly name" for them, + // otherwise whe could disturb (or be distubed by) + // other applications (e.g. OpenVPN). + // So, we'll look for the first free adapter whose frendly name + // starts with "SSH Tunnel" (or whatever is configured). + // TODO: Add a configuration item to change the string. + // if tun != SSH_TUNID_ANY: + // We got an adapter IfIndex directly from options, we have just + // to check if it's a TUN/TAP instance. + // + // Anyway, we explore the list of IP_ADAPTER_ADDRESSES objects + // and try to open the device instance of first one whose + // FiendlyName or IfIndex match. If the device is busy or not working, + // we try the next objects. + // + + + if (tun != SSH_TUNID_ANY) { + debug("Exploring network adapter list, looking for IfIndex %d", tun); + } + else { + debug("Exploring network adapter list, looking for a FriendlyName matching \"%ws.*\"", match); + } + + for (;;) { + if (sz < new_sz) { + if (result != NULL) { + free(result); + } + sz = new_sz; + result = (PIP_ADAPTER_ADDRESSES)malloc(sz); + if (result == NULL) { + error("Out of memory while reading adapter list"); + goto FAIL; + } + } + rv = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_ALL_INTERFACES | GAA_FLAG_INCLUDE_PREFIX, NULL, result, &new_sz); + if (rv == ERROR_SUCCESS) { + break; + } + if (rv != ERROR_BUFFER_OVERFLOW) { + error("Could not read adapter list, error 0x%08x", (unsigned int)rv); + goto FAIL; + } + } + + PIP_ADAPTER_ADDRESSES addr; + int no_match = 1; + + for (addr = result; addr != NULL; addr = addr->Next) { + + int match_found = 0; + + debug3(" Adapter %lu: Name=\"%s\", FriendlyName=\"%ws\", Description=\"%ws\"", addr->IfIndex, addr->AdapterName, addr->FriendlyName, addr->Description); + + if (tun != SSH_TUNID_ANY) { + if (addr->IfIndex == (IF_INDEX)tun) { + match_found = 1; + } + } + else { + if (_wcsnicmp(addr->FriendlyName, match, match_len) == 0) { + match_found = 1; + } + } + + if (match_found) { + + no_match = 0; + + tun_fd = open_adapter(addr->AdapterName); + + if (tun_fd >= 0 || tun != SSH_TUNID_ANY) { + break; + } + } + } + + if (no_match) { + debug("No matching adapter found"); + } + + if (tun_fd != -1 && ifname != NULL) { + *ifname = strdup(addr->AdapterName); + if (*ifname == NULL) { + error("Out of memory while storing adapter name"); + goto FAIL; + } + } + + ok = 1; + +FAIL: + + if (match != NULL) { + free(match); + } + + if (result != NULL) { + free(result); + } + + if (!ok) { + if (ifname != NULL && *ifname != NULL) { + free(*ifname); + } + if (tun_fd != -1) { + close(tun_fd); + tun_fd = -1; + } + } + + return tun_fd; +} + +int +sys_tun_open(int tun, int mode, const char *tun_options, char** ifname) +{ + int tun_fd = -1; + const char *prefix = NULL; + + prefix = tun_options; + + if (ifname != NULL) { + *ifname = NULL; + } + + if (mode != SSH_TUNMODE_ETHERNET) { + // Sorry, TUN/TAP for Windows is actually TAP only. + // TODO: Simulate Point-to-point (== TUN) + error("Only ethernet mode tunnel interfaces (a.k.a. TAP) are supported on this platform"); + goto FAIL; + } + + tun_fd = lookup_and_open_tap_instance(tun, ifname, prefix); + +FAIL: + return tun_fd; +} + +#else + +#error SSH_TUN_TAP_WINDOWS_V9 option is valid only on Windows + +#endif + +#endif // SSH_TUN_TAP_WINDOWS_V9 + diff --git a/openbsd-compat/port-net.h b/openbsd-compat/port-net.h index 3a0d1104bf6d..64f820e52a9f 100644 --- a/openbsd-compat/port-net.h +++ b/openbsd-compat/port-net.h @@ -20,9 +20,9 @@ struct Channel; struct ssh; -#if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD) +#if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD) || defined(SSH_TUN_TAP_WINDOWS_V9) # define CUSTOM_SYS_TUN_OPEN -int sys_tun_open(int, int, char **); +int sys_tun_open(int, int, const char *, char **); #endif #if defined(SSH_TUN_COMPAT_AF) || defined(SSH_TUN_PREPEND_AF) diff --git a/readconf.c b/readconf.c index 39e615b805f9..6738d9261f6f 100644 --- a/readconf.c +++ b/readconf.c @@ -1183,6 +1183,7 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host, multistate_ptr = multistate_flag; parse_multistate: arg = argv_next(&ac, &av); + parse_multistate_arg: if ((value = parse_multistate_value(arg, filename, linenum, multistate_ptr)) == -1) { error("%s line %d: unsupported option \"%s\".", @@ -1947,7 +1948,15 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host, case oTunnel: intptr = &options->tun_open; multistate_ptr = multistate_tunnel; - goto parse_multistate; + arg = argv_next(&ac, &av); + if (arg != NULL) { + char* opt = strchr(arg, ':'); + if (opt != NULL) { + options->tun_options = xstrdup(opt + 1); + *opt = '\0'; + } + } + goto parse_multistate_arg; case oTunnelDevice: arg = argv_next(&ac, &av); @@ -2663,6 +2672,7 @@ initialize_options(Options * options) options->required_rsa_size = -1; options->enable_escape_commandline = -1; options->obscure_keystroke_timing_interval = -1; + options->tun_options = NULL; options->tag = NULL; options->channel_timeouts = NULL; options->num_channel_timeouts = 0; @@ -3577,7 +3587,9 @@ dump_client_config(Options *o, const char *host) dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking); dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive); - dump_cfg_fmtint(oTunnel, o->tun_open); + printf("%s %s%s%s\n", lookup_opcode_name(oTunnel), fmt_intarg(oTunnel, o->tun_open), + ((o->tun_options == NULL) ? "" : ":"), + ((o->tun_options == NULL) ? "" : o->tun_options)); dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); @@ -3682,6 +3694,7 @@ dump_client_config(Options *o, const char *host) printf(":%d", o->tun_remote); printf("\n"); + /* oCanonicalizePermittedCNAMEs */ printf("canonicalizePermittedcnames"); if (o->num_permitted_cnames == 0) diff --git a/readconf.h b/readconf.h index 9447d5d6e53d..ca22d03cb31a 100644 --- a/readconf.h +++ b/readconf.h @@ -184,6 +184,7 @@ typedef struct { char **channel_timeouts; /* inactivity timeout by channel type */ u_int num_channel_timeouts; + char *tun_options; char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ } Options; diff --git a/servconf.c b/servconf.c index 5affc02fae60..e7329ed92609 100644 --- a/servconf.c +++ b/servconf.c @@ -194,6 +194,7 @@ initialize_server_options(ServerOptions *options) options->num_accept_env = 0; options->num_setenv = 0; options->permit_tun = -1; + options->tun_options = NULL; options->permitted_opens = NULL; options->permitted_listens = NULL; options->adm_forced_command = NULL; @@ -2289,9 +2290,17 @@ process_server_config_line_depth(ServerOptions *options, char *line, case sPermitTunnel: intptr = &options->permit_tun; arg = argv_next(&ac, &av); - if (!arg || *arg == '\0') + if (!arg || *arg == '\0') { fatal("%s line %d: %s missing argument.", - filename, linenum, keyword); + filename, linenum, keyword); + } + else { + char* opt = strchr(arg, ':'); + if (opt != NULL) { + options->tun_options = xstrdup(opt + 1); + *opt = '\0'; + } + } value = -1; for (i = 0; tunmode_desc[i].val != -1; i++) if (strcmp(tunmode_desc[i].text, arg) == 0) { @@ -3015,6 +3024,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) free(dst->chroot_directory); dst->chroot_directory = NULL; } + M_CP_STROPT(tun_options); /* Subsystems require merging. */ servconf_merge_subsystems(dst, src); diff --git a/servconf.h b/servconf.h index 22b158d1057c..aaf63a2efab0 100644 --- a/servconf.h +++ b/servconf.h @@ -214,6 +214,8 @@ typedef struct { int permit_tun; + char* tun_options; + char **permitted_opens; /* May also be one of PERMITOPEN_* */ u_int num_permitted_opens; char **permitted_listens; /* May also be one of PERMITOPEN_* */ diff --git a/serverloop.c b/serverloop.c index 757cc6f0244e..04176610d355 100644 --- a/serverloop.c +++ b/serverloop.c @@ -516,7 +516,7 @@ server_request_tun(struct ssh *ssh) goto done; tun = auth_opts->force_tun_device; } - sock = tun_open(tun, mode, &ifname); + sock = tun_open(tun, mode, options.tun_options, &ifname); if (sock < 0) goto done; debug("Tunnel forwarding using interface %s", ifname);