123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2021 David Woodhouse.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- */
- #include <config.h>
- #include "openconnect-internal.h"
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <winioctl.h>
- #include <winsock2.h>
- #include <ws2ipdef.h>
- #include <iphlpapi.h>
- #include <netioapi.h>
- #include <errno.h>
- #include <stdio.h>
- static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
- static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter;
- static WINTUN_DELETE_POOL_DRIVER_FUNC WintunDeletePoolDriver;
- static WINTUN_ENUM_ADAPTERS_FUNC WintunEnumAdapters;
- static WINTUN_FREE_ADAPTER_FUNC WintunFreeAdapter;
- static WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
- static WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
- static WINTUN_GET_ADAPTER_NAME_FUNC WintunGetAdapterName;
- static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName;
- static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
- static WINTUN_SET_LOGGER_FUNC WintunSetLogger;
- static WINTUN_START_SESSION_FUNC WintunStartSession;
- static WINTUN_END_SESSION_FUNC WintunEndSession;
- static WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
- static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
- static WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
- static WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
- static WINTUN_SEND_PACKET_FUNC WintunSendPacket;
- static struct openconnect_info *logger_vpninfo;
- #define WINTUN_POOL_NAME L"OpenConnect"
- #define WINTUN_RING_CAPACITY 0x400000 /* 4 MiB */
- static void CALLBACK wintun_log_fn(WINTUN_LOGGER_LEVEL wlvl, const WCHAR *wmsg)
- {
- int lvl = (wlvl == WINTUN_LOG_INFO) ? PRG_INFO : PRG_ERR;
- /* Sadly, Wintun doesn't provide any context information in the callback */
- if (!logger_vpninfo)
- return;
- vpn_progress(logger_vpninfo, lvl, "%d: %S\n", wlvl, wmsg);
- }
- static int init_wintun(struct openconnect_info *vpninfo)
- {
- if (!vpninfo->wintun) {
- vpninfo->wintun = LoadLibraryExW(L"wintun.dll", NULL,
- LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
- if (!vpninfo->wintun) {
- vpn_progress(vpninfo, PRG_DEBUG, _("Could not load wintun.dll\n"));
- return -ENOENT;
- }
- #define Resolve(Name) ((Name = (void *)GetProcAddress(vpninfo->wintun, #Name)) == NULL)
- if (Resolve(WintunCreateAdapter) || Resolve(WintunDeleteAdapter) ||
- Resolve(WintunDeletePoolDriver) || Resolve(WintunEnumAdapters) ||
- Resolve(WintunFreeAdapter) || Resolve(WintunOpenAdapter) ||
- Resolve(WintunGetAdapterLUID) || Resolve(WintunGetAdapterName) ||
- Resolve(WintunSetAdapterName) || Resolve(WintunGetRunningDriverVersion) ||
- Resolve(WintunSetLogger) || Resolve(WintunStartSession) ||
- Resolve(WintunEndSession) || Resolve(WintunGetReadWaitEvent) ||
- Resolve(WintunReceivePacket) || Resolve(WintunReleaseReceivePacket) ||
- Resolve(WintunAllocateSendPacket) || Resolve(WintunSendPacket)) {
- #undef Resolve
- vpn_progress(vpninfo, PRG_ERR, _("Could not resolve functions from wintun.dll\n"));
- FreeLibrary(vpninfo->wintun);
- vpninfo->wintun = NULL;
- return -EIO;
- }
- logger_vpninfo = vpninfo;
- WintunSetLogger(wintun_log_fn);
- }
- return 0;
- }
- int create_wintun(struct openconnect_info *vpninfo)
- {
- int ret = init_wintun(vpninfo);
- if (ret < 0)
- return ret;
- vpninfo->wintun_adapter = WintunCreateAdapter(WINTUN_POOL_NAME,
- vpninfo->ifname_w, NULL, NULL);
- if (vpninfo->wintun_adapter)
- return 0;
- ret = GetLastError();
- char *errstr = openconnect__win32_strerror(ret);
- vpn_progress(vpninfo, PRG_ERR, "Could not create Wintun adapter '%S': %s\n",
- vpninfo->ifname_w, errstr);
- free(errstr);
- return (ret == ERROR_ACCESS_DENIED ? -EPERM : -EIO);
- }
- intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
- {
- intptr_t ret;
- if (init_wintun(vpninfo))
- return 0;
- if (!vpninfo->wintun_adapter) {
- vpninfo->wintun_adapter = WintunOpenAdapter(WINTUN_POOL_NAME,
- wname);
- if (!vpninfo->wintun_adapter) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR, "Could not open Wintun adapter '%S': %s\n",
- wname, errstr);
- free(errstr);
- ret = OPEN_TUN_SOFTFAIL;
- goto out;
- }
- }
- DWORD ver = WintunGetRunningDriverVersion();
- vpn_progress(vpninfo, PRG_DEBUG, _("Loaded Wintun v%lu.%lu\n"),
- (ver >> 16) & 0xff, ver & 0xff);
- vpninfo->wintun_session = WintunStartSession(vpninfo->wintun_adapter,
- WINTUN_RING_CAPACITY);
- if (!vpninfo->wintun_session) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR, _("Failed to create Wintun session: %s\n"),
- errstr);
- free(errstr);
- ret = OPEN_TUN_HARDFAIL;
- goto out;
- }
- return 1;
- out:
- os_shutdown_wintun(vpninfo);
- return ret;
- }
- int os_read_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
- {
- DWORD tun_len;
- BYTE *tun_pkt = WintunReceivePacket(vpninfo->wintun_session,
- &tun_len);
- if (!tun_pkt) {
- DWORD err = GetLastError();
- if (err != ERROR_NO_MORE_ITEMS) {
- char *errstr = openconnect__win32_strerror(err);
- vpn_progress(vpninfo, PRG_ERR, _("Could not retrieve packet from Wintun adapter '%S': %s\n"),
- vpninfo->ifname_w, errstr);
- free(errstr);
- }
- return -1;
- }
- int ret = 0;
- if (tun_len <= pkt->len) {
- memcpy(pkt->data, tun_pkt, tun_len);
- pkt->len = tun_len;
- } else {
- vpn_progress(vpninfo, PRG_ERR, _("Drop oversized packet retrieved from Wintun adapter '%S' (%ld > %d)\n"),
- vpninfo->ifname_w, tun_len, pkt->len);
- ret = -1;
- }
- WintunReleaseReceivePacket(vpninfo->wintun_session, tun_pkt);
- return ret;
- }
- int os_write_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
- {
- BYTE *tun_pkt = WintunAllocateSendPacket(vpninfo->wintun_session,
- pkt->len);
- if (!tun_pkt) {
- DWORD err = GetLastError();
- char *errstr = openconnect__win32_strerror(err);
- vpn_progress(vpninfo, PRG_ERR, _("Could not send packet through Wintun adapter '%S': %s\n"),
- vpninfo->ifname_w, errstr);
- free(errstr);
- return -1;
- }
- memcpy(tun_pkt, pkt->data, pkt->len);
- WintunSendPacket(vpninfo->wintun_session, tun_pkt);
- return 0;
- }
- void os_shutdown_wintun(struct openconnect_info *vpninfo)
- {
- if (vpninfo->wintun_session) {
- WintunEndSession(vpninfo->wintun_session);
- vpninfo->wintun_session = NULL;
- }
- if (vpninfo->wintun_adapter) {
- WintunDeleteAdapter(vpninfo->wintun_adapter, FALSE, NULL);
- WintunFreeAdapter(vpninfo->wintun_adapter);
- vpninfo->wintun_adapter = NULL;
- }
- logger_vpninfo = NULL;
- FreeLibrary(vpninfo->wintun);
- vpninfo->wintun = NULL;
- }
- int setup_wintun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd)
- {
- vpninfo->tun_rd_overlap.hEvent = WintunGetReadWaitEvent(vpninfo->wintun_session);
- monitor_read_fd(vpninfo, tun);
- vpninfo->tun_fh = (HANDLE)tun_fd;
- return 0;
- }
|