api: close private namespace when unloading DLL

Prior, people making calls to LoadLibrary/FreeLibrary would experience
a failure to create or open adapters, because the private namespace was
already loaded into the process and not cleaned up on DLL detachment.
While this pattern is probably a misuse of the library, we should still
support cleaning that up. This commit makes the right calls to free the
boundary descriptor and close the private namespace. It does not,
however, destroy the private namespace using the flag on the second
parameter, in case of races with other processes.

Reported-by: Brad Spencer <bspencer@blackberry.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-12-17 19:00:25 +01:00
parent d440234d6e
commit 7710ff187b

View File

@ -12,7 +12,8 @@
#include <bcrypt.h> #include <bcrypt.h>
#include <wchar.h> #include <wchar.h>
static BOOL HasInitialized = FALSE; static HANDLE PrivateNamespace = NULL;
static HANDLE BoundaryDescriptor = NULL;
static CRITICAL_SECTION Initializing; static CRITICAL_SECTION Initializing;
static BCRYPT_ALG_HANDLE AlgProvider; static BCRYPT_ALG_HANDLE AlgProvider;
@ -43,7 +44,7 @@ static _Return_type_success_(return != FALSE) BOOL NamespaceRuntimeInit(void)
DWORD LastError; DWORD LastError;
EnterCriticalSection(&Initializing); EnterCriticalSection(&Initializing);
if (HasInitialized) if (PrivateNamespace)
{ {
LeaveCriticalSection(&Initializing); LeaveCriticalSection(&Initializing);
return TRUE; return TRUE;
@ -65,25 +66,25 @@ static _Return_type_success_(return != FALSE) BOOL NamespaceRuntimeInit(void)
goto cleanupBCryptCloseAlgorithmProvider; goto cleanupBCryptCloseAlgorithmProvider;
} }
HANDLE Boundary = CreateBoundaryDescriptorW(L"Wintun", 0); BoundaryDescriptor = CreateBoundaryDescriptorW(L"Wintun", 0);
if (!Boundary) if (!BoundaryDescriptor)
{ {
LastError = LOG_LAST_ERROR(L"Failed to create boundary descriptor"); LastError = LOG_LAST_ERROR(L"Failed to create boundary descriptor");
goto cleanupBCryptCloseAlgorithmProvider; goto cleanupBCryptCloseAlgorithmProvider;
} }
if (!AddSIDToBoundaryDescriptor(&Boundary, Sid)) if (!AddSIDToBoundaryDescriptor(&BoundaryDescriptor, Sid))
{ {
LastError = LOG_LAST_ERROR(L"Failed to add SID to boundary descriptor"); LastError = LOG_LAST_ERROR(L"Failed to add SID to boundary descriptor");
goto cleanupBCryptCloseAlgorithmProvider; goto cleanupBoundaryDescriptor;
} }
for (;;) for (;;)
{ {
if (CreatePrivateNamespaceW(&SecurityAttributes, Boundary, L"Wintun")) if ((PrivateNamespace = CreatePrivateNamespaceW(&SecurityAttributes, BoundaryDescriptor, L"Wintun")) != NULL)
break; break;
if ((LastError = GetLastError()) == ERROR_ALREADY_EXISTS) if ((LastError = GetLastError()) == ERROR_ALREADY_EXISTS)
{ {
if (OpenPrivateNamespaceW(Boundary, L"Wintun")) if ((PrivateNamespace = OpenPrivateNamespaceW(BoundaryDescriptor, L"Wintun")) != NULL)
break; break;
if ((LastError = GetLastError()) == ERROR_PATH_NOT_FOUND) if ((LastError = GetLastError()) == ERROR_PATH_NOT_FOUND)
continue; continue;
@ -91,13 +92,14 @@ static _Return_type_success_(return != FALSE) BOOL NamespaceRuntimeInit(void)
} }
else else
LOG_ERROR(L"Failed to create private namespace", LastError); LOG_ERROR(L"Failed to create private namespace", LastError);
goto cleanupBCryptCloseAlgorithmProvider; goto cleanupBoundaryDescriptor;
} }
HasInitialized = TRUE;
LeaveCriticalSection(&Initializing); LeaveCriticalSection(&Initializing);
return TRUE; return TRUE;
cleanupBoundaryDescriptor:
DeleteBoundaryDescriptor(BoundaryDescriptor);
cleanupBCryptCloseAlgorithmProvider: cleanupBCryptCloseAlgorithmProvider:
BCryptCloseAlgorithmProvider(AlgProvider, 0); BCryptCloseAlgorithmProvider(AlgProvider, 0);
cleanupLeaveCriticalSection: cleanupLeaveCriticalSection:
@ -219,10 +221,12 @@ void
NamespaceDone(void) NamespaceDone(void)
{ {
EnterCriticalSection(&Initializing); EnterCriticalSection(&Initializing);
if (HasInitialized) if (PrivateNamespace)
{ {
BCryptCloseAlgorithmProvider(AlgProvider, 0); BCryptCloseAlgorithmProvider(AlgProvider, 0);
HasInitialized = FALSE; ClosePrivateNamespace(PrivateNamespace, 0);
DeleteBoundaryDescriptor(BoundaryDescriptor);
PrivateNamespace = NULL;
} }
LeaveCriticalSection(&Initializing); LeaveCriticalSection(&Initializing);
DeleteCriticalSection(&Initializing); DeleteCriticalSection(&Initializing);