Skip to content

Commit

Permalink
api: use SuggestedInstanceId instead of NetSetupAnticipatedInstanceId
Browse files Browse the repository at this point in the history
All was well with NetSetupAnticipatedInstanceId, until a bug crept into
recent Windows builds that caused old GUIDs not to be properly removed,
resulting in subsequent adapter creations to fail, because NetSetup
AnticipatedInstanceId considers it fatal when the target GUID
already exists, even if in diminished form.

The initial solution was to detect cruft, and then steal a
TrustedInstaller token and sleuth around the registry cleaning things
up. The horror!

Uncomfortable with this, I reopened IDA and had a look around with fresh
eyes, three years after the original discovery of NetSetupAnticipated
InstanceId. There, I found some interesting behavior in
NetSetupSvcDeviceManager::InstallNetworkInterfaces, which amounts to
something like:

    if (IsSet("RetiredNetCfgInstanceId") {
      if (IsSet("NetSetupAnticipatedInstanceId")
        DeleteAdapter(GetValue("RetiredNetCfgInstanceId"));
      else
        Set("NetSetupAnticipatedInstanceId", GetValue("RetiredNetCfgInstanceId"));
      Delete("RetiredNetCfgInstanceId");
    }
    CreateAdapter = TRUE;
    if (IsSet("NetSetupAnticipatedInstanceId")) {
      Guid = GetValue("NetSetupAnticipatedInstanceId");
      if (AdapterAlreadyExists(Guid))
        CreateAdapter = FALSE;
      else
        SetGuidOfNewAdapter(Guid);
      Delete("NetSetupAnticipatedInstanceId");
    } else if (IsSet("SuggestedInstanceId")) {
      Guid = GetValue("SuggestedInstanceId");
      if (!AdapterAlreadyExists(Guid))
        SetGuidOfNewAdapter(Guid);
      Delete("SuggestedInstanceId");
    }

Thus, one appealing strategy would be to set both NetSetupAnticipated
InstanceId and RetiredInstanceId to the same value, and let the service
handle deleting the old one for us before creating the new one.
However, the cleanup of the old adapter winds up being quasi-
asynchronous, and thus we still wind up in the CreateAdapter = FALSE
case.

So, the remaining strategy is to simply use SuggestedInstanceId instead.
This has the behavior that if there's an adapter already in use, it'll
use a new random GUID. The result is that adapter creation won't fail.

That's not great, but the docs have always made it clear that
"requested" is a best-effort sort of thing. Plus, hopefully the creation
of the new adapter will help nudge the bug a bit and cleanup the old
cruft. In some ways, transitioning from our old strategy of "cudgel the
registry until we get the GUID we want" to "ask politely and accept no
for an answer" is a disappointing regression in functionality. But it
also means we don't need to keep crazy token stealing code around, or
fish around in the registry dangerously. This probably also increases
the likelihood that an adapter will be created during edge cases, which
means fewer errors for users, which could be a good thing. On the
downside, we have the perpetual tensions caused by a system that now
"fails open" instead of "fails closed". But so it goes in Windows land.

Signed-off-by: Jason A. Donenfeld <[email protected]>
  • Loading branch information
zx2c4 committed Jul 8, 2021
1 parent cf6e441 commit 33e6c1b
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 467 deletions.
67 changes: 2 additions & 65 deletions api/adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <devpkey.h>

#include "adapter.h"
#include "elevate.h"
#include "entry.h"
#include "logger.h"
#include "namespace.h"
Expand Down Expand Up @@ -1421,62 +1420,6 @@ static _Return_type_success_(return != NULL) WINTUN_ADAPTER *CreateAdapter(
if (!IsWindows10())
RequestedGUID = NULL;

if (RequestedGUID)
{
WCHAR RegPath[MAX_REG_PATH];
WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN];
int GuidStrLen = StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr)) * sizeof(WCHAR);
if (_snwprintf_s(
RegPath,
MAX_REG_PATH,
_TRUNCATE,
L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%.*s\\Connection",
GuidStrLen,
RequestedGUIDStr) == -1)
goto guidIsFresh;
HKEY Key;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, RegPath, 0, KEY_QUERY_VALUE, &Key) != ERROR_SUCCESS)
goto guidIsFresh;
WCHAR *InstanceID = RegistryQueryString(Key, L"PnPInstanceId", FALSE);
RegCloseKey(Key);
if (!InstanceID)
goto guidIsFresh;
int Ret = _snwprintf_s(RegPath, MAX_REG_PATH, _TRUNCATE, L"SYSTEM\\CurrentControlSet\\Enum\\%s", InstanceID);
Free(InstanceID);
if (Ret == -1)
goto guidIsFresh;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, RegPath, 0, KEY_QUERY_VALUE, &Key) == ERROR_SUCCESS)
{
RegCloseKey(Key);
MIB_IF_ROW2 IfRow = { 0 };
if (ConvertInterfaceGuidToLuid(RequestedGUID, &IfRow.InterfaceLuid) == NO_ERROR &&
GetIfEntry2(&IfRow) == NO_ERROR && IfRow.OperStatus != IfOperStatusNotPresent)
{
SetLastError(
LOG_ERROR(ERROR_ALREADY_EXISTS, L"Requested GUID is already in use: %s", RequestedGUIDStr));
return NULL;
}
}
LOG(WINTUN_LOG_WARN, L"Requested GUID %s has leftover residue", RequestedGUIDStr);
HANDLE OriginalToken;
if (!ImpersonateService(L"NetSetupSvc", &OriginalToken))
{
LOG_LAST_ERROR(L"Unable to impersonate NetSetupSvc");
goto guidIsFresh; // non-fatal
}
if (_snwprintf_s(
RegPath,
MAX_REG_PATH,
_TRUNCATE,
L"SYSTEM\\CurrentControlSet\\Control\\NetworkSetup2\\Interfaces\\%.*s",
GuidStrLen,
RequestedGUIDStr) == -1 ||
!RegistryDeleteKeyRecursive(HKEY_LOCAL_MACHINE, RegPath))
LOG_LAST_ERROR(L"Unable to delete NetworkSetup2 registry key"); // non-fatal
RestoreToken(OriginalToken);
guidIsFresh:;
}

HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
{
Expand Down Expand Up @@ -1566,19 +1509,13 @@ static _Return_type_success_(return != NULL) WINTUN_ADAPTER *CreateAdapter(
}
if (RequestedGUID)
{
WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN];
LastError = RegSetValueExW(
NetDevRegKey,
L"NetSetupAnticipatedInstanceId",
0,
REG_SZ,
(const BYTE *)RequestedGUIDStr,
StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr)) * sizeof(WCHAR));
NetDevRegKey, L"SuggestedInstanceId", 0, REG_BINARY, (const BYTE *)RequestedGUID, sizeof(*RequestedGUID));
if (LastError != ERROR_SUCCESS)
{
WCHAR RegPath[MAX_REG_PATH];
LoggerGetRegistryKeyPath(NetDevRegKey, RegPath);
LOG_ERROR(LastError, L"Failed to set %.*s\\NetSetupAnticipatedInstanceId", MAX_REG_PATH, RegPath);
LOG_ERROR(LastError, L"Failed to set %.*s\\SuggestedInstanceId", MAX_REG_PATH, RegPath);
goto cleanupNetDevRegKey;
}
}
Expand Down
2 changes: 0 additions & 2 deletions api/api.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@
<ItemGroup>
<ClInclude Include="entry.h" />
<ClInclude Include="adapter.h" />
<ClInclude Include="elevate.h" />
<ClInclude Include="rundll32_i.c">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
Expand All @@ -171,7 +170,6 @@
<ItemGroup>
<ClCompile Include="entry.c" />
<ClCompile Include="adapter.c" />
<ClCompile Include="elevate.c" />
<ClCompile Include="logger.c" />
<ClCompile Include="namespace.c" />
<ClCompile Include="registry.c" />
Expand Down
8 changes: 1 addition & 7 deletions api/api.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@
<ClInclude Include="wintun.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="elevate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="entry.h">
<Filter>Header Files</Filter>
</ClInclude>
Expand Down Expand Up @@ -84,11 +81,8 @@
<ClCompile Include="session.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="elevate.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="entry.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
</Project>
248 changes: 0 additions & 248 deletions api/elevate.c

This file was deleted.

12 changes: 0 additions & 12 deletions api/elevate.h

This file was deleted.

Loading

0 comments on commit 33e6c1b

Please sign in to comment.