From d4c0d345488d4d3751aed200e48cc043afe62889 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Fri, 8 Feb 2019 23:47:18 -0500 Subject: [PATCH] lib/krb5: krb5_get_instance does not work on Windows 7 krb5_get_instance() is meant to ensure that the shared library instance of heimdal loaded by a plugin matches the instance that loaded the plugin. It works by declaring a static C string whose memory address will be used as an instance identifier. If the instance returned from the plugin matches the instance obtain by the code that loads the plugin, then we can conclude the two instances are the same. This doesn't work on Windows 7. When heimdal.dll loads a plugin that is linked to heimdal.dll, the plugin's heimdal.dll is always a new instance. However, the requirement for plugin safety is not that the plugin be the same instance in memory but that they be the same instance on disk. This change loads the path name and version string for the module and generates a hash of those strings as an instance identifier. Change-Id: I1c0651969e9738c5feecb0b323969d13efd4704d --- lib/heimdal/NTMakefile | 4 +- lib/krb5/plugin.c | 28 ++++++++- lib/roken/NTMakefile | 1 + lib/roken/roken.h.in | 4 ++ lib/roken/win32_version.c | 128 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 lib/roken/win32_version.c diff --git a/lib/heimdal/NTMakefile b/lib/heimdal/NTMakefile index 42d51ef27..dc7d45960 100644 --- a/lib/heimdal/NTMakefile +++ b/lib/heimdal/NTMakefile @@ -53,7 +53,9 @@ DLLSDKDEPS= \ secur32.lib \ shell32.lib \ dnsapi.lib \ - shlwapi.lib + shlwapi.lib \ + version.lib + dlllflags=$(dlllflags) /DELAYLOAD:bcrypt.dll DLLSDKDEPS=$(DLLSDKDEPS)\ diff --git a/lib/krb5/plugin.c b/lib/krb5/plugin.c index d29716944..2dc22d18a 100644 --- a/lib/krb5/plugin.c +++ b/lib/krb5/plugin.c @@ -170,13 +170,39 @@ _krb5_plugin_run_f(krb5_context context, * @ingroup krb5_support */ +#ifdef WIN32 +static uintptr_t +djb2(uintptr_t hash, unsigned char *str) +{ + int c; + + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} +#endif + KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL krb5_get_instance(const char *libname) { +#ifdef WIN32 + char *version; + char *name; + uintptr_t instance; + + if (win32_getLibraryVersion("heimdal", &name, &version)) + return 0; + instance = djb2(5381, name); + instance = djb2(instance, version); + free(name); + free(version); + return instance; +#else static const char *instance = "libkrb5"; if (strcmp(libname, "krb5") == 0) return (uintptr_t)instance; - return 0; +#endif } diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile index ce8005ad3..6e33d1f06 100644 --- a/lib/roken/NTMakefile +++ b/lib/roken/NTMakefile @@ -130,6 +130,7 @@ libroken_la_OBJS = \ $(OBJ)\warnerr.obj \ $(OBJ)\warnx.obj \ $(OBJ)\win32_alloc.obj \ + $(OBJ)\win32_version.obj \ $(OBJ)\writev.obj \ $(OBJ)\xfree.obj diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index 4fed1c469..9cd1c0fc6 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -1321,6 +1321,10 @@ int ROKEN_LIB_FUNCTION rk_socket(int, int, int); #define EWOULDBLOCK 140 #endif +#ifdef WIN32 +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +win32_getLibraryVersion(const char *libname, char **outname, char **outversion); +#endif #ifdef SOCKET_WRAPPER_REPLACE #include diff --git a/lib/roken/win32_version.c b/lib/roken/win32_version.c new file mode 100644 index 000000000..faf05b8de --- /dev/null +++ b/lib/roken/win32_version.c @@ -0,0 +1,128 @@ +#include +#include "roken.h" +#include + +static DWORD +GetVersionInfo(CHAR *filename, CHAR *szOutput, DWORD dwOutput) +{ + DWORD dwVersionHandle; + LPVOID pVersionInfo = 0; + DWORD retval = 0; + LPDWORD pLangInfo = 0; + LPTSTR szVersion = 0; + UINT len = 0; + TCHAR szVerQ[] = TEXT("\\StringFileInfo\\12345678\\FileVersion"); + DWORD size = GetFileVersionInfoSize(filename, &dwVersionHandle); + + if (!size) + return GetLastError(); + + pVersionInfo = malloc(size); + if (!pVersionInfo) + return ERROR_NOT_ENOUGH_MEMORY; + + GetFileVersionInfo(filename, dwVersionHandle, size, pVersionInfo); + if (retval = GetLastError()) + goto cleanup; + + VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), + (LPVOID*)&pLangInfo, &len); + if (retval = GetLastError()) + goto cleanup; + + wsprintf(szVerQ, + TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), + LOWORD(*pLangInfo), HIWORD(*pLangInfo)); + + VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len); + if (retval = GetLastError()) { + /* try again with language 409 since the old binaries were tagged wrong */ + wsprintf(szVerQ, + TEXT("\\StringFileInfo\\0409%04x\\FileVersion"), + HIWORD(*pLangInfo)); + + VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len); + if (retval = GetLastError()) + goto cleanup; + } + snprintf(szOutput, dwOutput, TEXT("%s"), szVersion); + szOutput[dwOutput - 1] = 0; + + cleanup: + free(pVersionInfo); + + return retval; +} + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +win32_getLibraryVersion(const char *libname, char **outname, char **outversion) +{ + CHAR modVersion[128]; + HMODULE hMods[1024]; + HANDLE hProcess; + DWORD cbNeeded; + unsigned int i; + int success = -1; + HINSTANCE hPSAPI; + DWORD (WINAPI *pGetModuleFileNameExA)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize); + BOOL (WINAPI *pEnumProcessModules)(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded); + + if (outversion) + *outversion = NULL; + if (outname) + *outname = NULL; + + hPSAPI = LoadLibrary("psapi"); + if ( hPSAPI == NULL ) + return -1; + + if (((FARPROC) pGetModuleFileNameExA = + GetProcAddress( hPSAPI, "GetModuleFileNameExA" )) == NULL || + ((FARPROC) pEnumProcessModules = + GetProcAddress( hPSAPI, "EnumProcessModules" )) == NULL) + { + goto out; + } + + // Get a list of all the modules in this process. + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, GetCurrentProcessId()); + + if (pEnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) + { + for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) + { + char szModName[2048]; + + // Get the full path to the module's file. + if (pGetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName))) + { + CHAR checkName[1024]; + lstrcpy(checkName, szModName); + strlwr(checkName); + + if (strstr(checkName, libname)) { + if (GetVersionInfo(szModName, modVersion, sizeof(modVersion)) == 0) { + success = 0; + if (outversion) { + *outversion = strdup(modVersion); + if (*outversion == NULL) + success = -1; + } + if (outname) { + *outname = strdup(szModName); + if (*outname == NULL) + success = -1; + } + } + break; + } + } + } + } + CloseHandle(hProcess); + + out: + FreeLibrary(hPSAPI); + return success; +}