aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
authorSimon Rozman <simon@rozman.si>2020-10-14 12:51:15 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2020-10-30 16:51:00 +0100
commite157d421ecc4fda50ce00e3ae906bb02a4c6a306 (patch)
tree87cf414cf79d8ef69cd46b713b73af8181cdc20d /api
parentapi: disable driver management for wintun.dll that don't have a driver (diff)
downloadwintun-e157d421ecc4fda50ce00e3ae906bb02a4c6a306.tar.xz
wintun-e157d421ecc4fda50ce00e3ae906bb02a4c6a306.zip
api: implement driver version extraction from .inf file
With installer.dll the installer did the decision whether to install or upgrade the driver according to installer.dll version and hash stored in registry by MSM. With wintun.dll we need to know, which version of Wintun driver we are packing in the resources to decide about driver upgrade. The most accurate source of the driver version is the DriverVer directive in [Version] section of the driver's .inf file. Signed-off-by: Simon Rozman <simon@rozman.si>
Diffstat (limited to 'api')
-rw-r--r--api/driver.c196
-rw-r--r--api/driver.h2
2 files changed, 175 insertions, 23 deletions
diff --git a/api/driver.c b/api/driver.c
index d46f641..8884acc 100644
--- a/api/driver.c
+++ b/api/driver.c
@@ -158,6 +158,176 @@ cleanupBuf:
#if defined(HAVE_EV) || defined(HAVE_WHQL)
+/* We can't use RtlGetVersion, because appcompat's aclayers.dll shims it to report Vista
+ * when run from legacy contexts. So, we instead use the undocumented RtlGetNtVersionNumbers.
+ *
+ * Another way would be reading from the PEB directly:
+ * ((DWORD *)NtCurrentTeb()->ProcessEnvironmentBlock)[sizeof(void *) == 8 ? 70 : 41]
+ * Or just read from KUSER_SHARED_DATA the same way on 32-bit and 64-bit:
+ * *(DWORD *)0x7FFE026C
+ */
+extern VOID NTAPI
+RtlGetNtVersionNumbers(_Out_opt_ DWORD *MajorVersion, _Out_opt_ DWORD *MinorVersion, _Out_opt_ DWORD *BuildNumber);
+
+/**
+ * Queries driver availability and Windows requirement about driver signing model.
+ *
+ * @return non-zero when WHQL/Attestation-signed drivers are available and required; zero otherwise.
+ */
+static BOOL
+HaveWHQL()
+{
+# if defined(HAVE_EV) && defined(HAVE_WHQL)
+ DWORD MajorVersion;
+ RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL);
+ return MajorVersion >= 10;
+# elif defined(HAVE_EV)
+ return FALSE;
+# elif defined(HAVE_WHQL)
+ return TRUE;
+# endif
+}
+
+/**
+ * Locates the white-space string span.
+ *
+ * \param Beg String start
+ *
+ * \param End String end (non-inclusive)
+ *
+ * \return First non-white-space character or string end.
+ */
+static const CHAR *
+SkipWSpace(_In_ const CHAR *Beg, _In_ const CHAR *End)
+{
+ for (; Beg < End && iswspace(*Beg); ++Beg)
+ ;
+ return Beg;
+}
+
+/**
+ * Locates the non-LF string span.
+ *
+ * \param Beg String start
+ *
+ * \param End String end (non-inclusive)
+ *
+ * \return First LF character or string end.
+ */
+static const CHAR *
+SkipNonLF(_In_ const CHAR *Beg, _In_ const CHAR *End)
+{
+ for (; Beg < End && *Beg != '\n'; ++Beg)
+ ;
+ return Beg;
+}
+
+/**
+ * Queries the version of the driver this wintun.dll is packing.
+ *
+ * DriverDate Pointer to a variable to receive the driver date.
+ *
+ * DriverVersion Pointer to a variable to receive the driver version.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+WINTUN_STATUS
+DriverGetVersion(_Out_ FILETIME *DriverDate, _Out_ DWORDLONG *DriverVersion)
+{
+ const VOID *LockedResource;
+ DWORD SizeResource;
+ DWORD Result = ResourceGetAddress(HaveWHQL() ? L"wintun-whql.inf" : L"wintun.inf", &LockedResource, &SizeResource);
+ if (Result != ERROR_SUCCESS)
+ return WINTUN_LOGGER_ERROR(L"Failed to locate resource", Result);
+ enum
+ {
+ SectNone,
+ SectUnknown,
+ SectVersion
+ } Section = SectNone;
+ for (const CHAR *Inf = (const CHAR *)LockedResource, *InfEnd = Inf + SizeResource; Inf < InfEnd; ++Inf)
+ {
+ if (*Inf == ';')
+ {
+ Inf = SkipNonLF(Inf + 1, InfEnd);
+ continue;
+ }
+ Inf = SkipWSpace(Inf, InfEnd);
+ if (*Inf == '[')
+ {
+ Section = Inf + 9 <= InfEnd && !_strnicmp(Inf, "[Version]", 9) ? SectVersion : SectUnknown;
+ }
+ else if (Section == SectVersion)
+ {
+ if (Inf + 9 <= InfEnd && !_strnicmp(Inf, "DriverVer", 9))
+ {
+ Inf = SkipWSpace(Inf + 9, InfEnd);
+ if (Inf < InfEnd && *Inf == '=')
+ {
+ Inf = SkipWSpace(Inf + 1, InfEnd);
+ /* Duplicate buffer, as RT_RCDATA resource is not guaranteed to be zero-terminated. */
+ CHAR buf[0x100];
+ size_t n = InfEnd - Inf;
+ if (n >= _countof(buf))
+ n = _countof(buf) - 1;
+ strncpy_s(buf, _countof(buf), Inf, n);
+ buf[n] = 0;
+ const CHAR *p = buf;
+ CHAR *p_next;
+ unsigned long date[3] = { 0, 0, 0 };
+ for (size_t i = 0;; ++i, ++p)
+ {
+ date[i] = strtoul(p, &p_next, 10);
+ p = p_next;
+ if (i >= _countof(date) - 1)
+ break;
+ if (*p != '/' && *p != '-')
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Unexpected date delimiter");
+ return ERROR_INVALID_DATA;
+ }
+ }
+ if (date[0] < 1 || date[0] > 12 || date[1] < 1 || date[1] > 31 || date[2] < 1601 || date[2] > 30827)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Invalid date");
+ return ERROR_INVALID_DATA;
+ }
+ const SYSTEMTIME st = { .wYear = (WORD)date[2], .wMonth = (WORD)date[0], .wDay = (WORD)date[1] };
+ SystemTimeToFileTime(&st, DriverDate);
+ p = SkipWSpace(p, buf + n);
+ ULONGLONG version[4] = { 0, 0, 0, 0 };
+ if (*p == ',')
+ {
+ p = SkipWSpace(p + 1, buf + n);
+ for (size_t i = 0;; ++i, ++p)
+ {
+ version[i] = strtoul(p, &p_next, 10);
+ if (version[i] > 0xffff)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Version field may not exceed 65535");
+ return ERROR_INVALID_DATA;
+ }
+ p = p_next;
+ if (i >= _countof(version) - 1 || !*p || *p == ';' || iswspace(*p))
+ break;
+ if (*p != '.')
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Unexpected version delimiter");
+ return ERROR_INVALID_DATA;
+ }
+ }
+ }
+ *DriverVersion = (version[0] << 48) | (version[1] << 32) | (version[2] << 16) | version[3];
+ return ERROR_SUCCESS;
+ }
+ }
+ }
+ Inf = SkipNonLF(Inf, InfEnd);
+ }
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"DriverVer not found in INF resource");
+ return ERROR_FILE_NOT_FOUND;
+}
+
/**
* Checks if the Wintun driver is loaded.
*
@@ -295,17 +465,6 @@ cleanupQueriedStore:
return Result;
}
-/* We can't use RtlGetVersion, because appcompat's aclayers.dll shims it to report Vista
- * when run from legacy contexts. So, we instead use the undocumented RtlGetNtVersionNumbers.
- *
- * Another way would be reading from the PEB directly:
- * ((DWORD *)NtCurrentTeb()->ProcessEnvironmentBlock)[sizeof(void *) == 8 ? 70 : 41]
- * Or just read from KUSER_SHARED_DATA the same way on 32-bit and 64-bit:
- * *(DWORD *)0x7FFE026C
- */
-extern VOID NTAPI
-RtlGetNtVersionNumbers(_Out_opt_ DWORD *MajorVersion, _Out_opt_ DWORD *MinorVersion, _Out_opt_ DWORD *BuildNumber);
-
/**
* Installs Wintun driver to the Windows driver store and updates existing adapters to use it.
*
@@ -323,7 +482,7 @@ InstallDriver(_In_ BOOL UpdateExisting)
if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
return ERROR_BUFFER_OVERFLOW;
UCHAR RandomBytes[32] = { 0 };
-#pragma warning(suppress : 6387)
+# pragma warning(suppress : 6387)
if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
return WINTUN_LOGGER_LAST_ERROR(L"Failed to generate random");
WCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1];
@@ -354,16 +513,7 @@ InstallDriver(_In_ BOOL UpdateExisting)
goto cleanupFree;
}
- BOOL UseWHQL = FALSE;
-#if defined(HAVE_EV) && defined(HAVE_WHQL)
- DWORD MajorVersion;
- RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL);
- UseWHQL = MajorVersion >= 10;
-#elif defined(HAVE_EV)
- UseWHQL = FALSE;
-#elif defined(HAVE_WHQL)
- UseWHQL = TRUE;
-#endif
+ BOOL UseWHQL = HaveWHQL();
if (!UseWHQL && (Result = InstallCertificate(L"wintun.sys")) != ERROR_SUCCESS)
WINTUN_LOGGER_ERROR(L"Unable to install code signing certificate", Result);
@@ -447,7 +597,7 @@ cleanupDeviceInfoSet:
return Result;
}
-#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
+# define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
/**
* Closes all client handles to the Wintun adapter.
diff --git a/api/driver.h b/api/driver.h
index c17787c..3f91025 100644
--- a/api/driver.h
+++ b/api/driver.h
@@ -24,6 +24,8 @@ _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE
#if defined(HAVE_EV) || defined(HAVE_WHQL)
+WINTUN_STATUS DriverGetVersion(_Out_ FILETIME *DriverDate, _Out_ DWORDLONG *DriverVersion);
+
WINTUN_STATUS DriverInstallOrUpdate(VOID);
WINTUN_STATUS DriverUninstall(VOID);