windows-nt/Source/XPSP1/NT/net/homenet/bridge/sys/brdggpo.c
2020-09-26 16:20:57 +08:00

4193 lines
122 KiB
C

/*++
Copyright(c) 1999-2002 Microsoft Corporation
Module Name:
brdggpo.c
Abstract:
Ethernet MAC level bridge.
Group Policy code for Network Bridge.
Author:
Salahuddin J. Khan (sjkhan)
Environment:
Kernel mode
Revision History:
April 2002 - Original version
--*/
#define NDIS_MINIPORT_DRIVER
#define NDIS50_MINIPORT 1
#define NDIS_WDM 1
#pragma warning( push, 3 )
#include <ndis.h>
#include <tdikrnl.h>
#include <ntstatus.h>
#include <wchar.h>
#pragma warning( pop )
#include "bridge.h"
#include "brdggpo.h"
#include "brdgsta.h"
#include "brdgmini.h"
#include "brdgprot.h"
#include "brdgbuf.h"
#include "brdgfwd.h"
#include "brdgtbl.h"
#include "brdgctl.h"
#include "brdgtdi.h"
// ===========================================================================
//
// GLOBALS
//
// ===========================================================================
BRDG_GPO_GLOBALS g_BrdgGpoGlobals;
// ===========================================================================
//
// CONSTANTS
//
// ===========================================================================
const WCHAR HiveListKey[] = {L"\\Registry\\Machine\\SYSTEM\\CURRENTCONTROLSET\\CONTROL\\HIVELIST"};
const WCHAR SoftwareHiveKey[] = {L"\\REGISTRY\\MACHINE\\SOFTWARE"};
const WCHAR PolicyBaseKey[] = {L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\Windows"};
const WCHAR NetworkPoliciesKey[] = {L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\Windows\\Network Connections"};
const WCHAR GroupPolicyKey[] = {L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy"};
const WCHAR BridgePolicyValue[] = {L"NC_AllowNetBridge_NLA"};
const WCHAR TcpipInterfacesKey[] = {L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"};
const WCHAR HistoryKey[] = {L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\History"};
// ===========================================================================
//
// PRIVATE PROTOTYPES
//
// ===========================================================================
VOID
static
BrdgGpoRegNotify(
IN PVOID Context
);
NTSTATUS
BrdgGpoBuildNotifyForRegKeyChange(
IN PBRDG_GPO_NOTIFY_KEY Notify,
IN LPWSTR Identifier,
IN LPWSTR RegKeyName,
IN LPWSTR RegValueName,
IN PWORKER_THREAD_ROUTINE ApcRoutine,
IN PVOID ApcContext,
IN ULONG CompletionFilter,
IN BOOLEAN WatchTree,
IN PBRDG_GPO_REG_CALLBACK FunctionCallback,
IN BOOLEAN Recurring,
IN PBOOLEAN SuccessfulRegistration,
IN PBRDG_GPO_REGISTER FunctionRegister);
NTSTATUS
BrdgGpoRegisterForRegKeyChange(
IN PBRDG_GPO_NOTIFY_KEY Notify);
NTSTATUS
BrdgGpoRequestNotification(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoProcessNotifications(
IN PVOID Context);
PLIST_ENTRY
BrdgGpoGetNotifyListHead();
PKEVENT
BrdgGpoGetNotifyEvent();
PKEVENT
BrdgGpoGetKillEvent();
PNDIS_RW_LOCK
BrdgGpoGetNotifyListLock();
NTSTATUS
BrdgGpoFindNotify(
IN PLIST_ENTRY ListHead,
IN PNDIS_RW_LOCK ListLock,
IN LPWSTR Identifier,
OUT PBRDG_GPO_NOTIFY_KEY* Notify
);
NTSTATUS
BrdgGpoInitializeNotifyList(
OUT PLIST_ENTRY* ListHead,
OUT PNDIS_RW_LOCK* ListLock,
OUT PKEVENT* WaitEvent,
OUT PKEVENT* KillEvent);
VOID
BrdgGpoFreeNotifyList();
BOOLEAN
BrdgGpoAllowedToBridge();
VOID
BrdgGpoUpdateBridgeMode(
BOOLEAN NetworkMatch);
VOID
BrdgGpoCheckForMatchAndUpdateMode();
NTSTATUS
BrdgGpoRegisterForGroupPolicyNetworkNameNotification();
NTSTATUS
BrdgGpoRegisterForWindowsGroupPolicyNotification();
NTSTATUS
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification();
NTSTATUS
BrdgGpoRegisterForGroupPolicyNotification();
VOID
BrdgGpoRegisterForHiveListNotification();
NTSTATUS
BrdgGpoAllocateAndInitializeNotifyStruct(
OUT PBRDG_GPO_NOTIFY_KEY* Notify);
NTSTATUS
BrdgGpoUpdateGroupPolicyNetworkName();
VOID
BrdgGpoFreeNotifyStructAndData(
IN PBRDG_GPO_NOTIFY_KEY Notify);
BOOLEAN
BrdgGpoWaitingOnSoftwareHive();
//
// We need this if the regkey for Network Connections Group Policy doesn't exist yet.
//
VOID
BrdgGpoWindowsGroupPolicyChangeCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoNetworkConnectionsGroupPolicyChangeCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoGroupPolicyChangeCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoTcpipInterfacesChangeCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoGroupPolicyNetworkNameChangeCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoHiveListCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoQueryNetworkConnectionsValue(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoQueryTcpipInterfacesValues(
IN PBRDG_GPO_NOTIFY_KEY Notify);
VOID
BrdgGpoQueryGroupPolicyNetworkName(
IN PBRDG_GPO_NOTIFY_KEY Notify);
// ===========================================================================
//
// BRIDGE GPO IMPLEMENTATION
//
// ===========================================================================
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK, BrdgGpoRegNotify)
#endif
NTSTATUS
BrdgGpoDriverInit()
/*++
Routine Description:
Driver load-time initialization
Return Value:
Status of initialization
Locking Constraints:
Top-level function. Assumes no locks are held by caller.
--*/
{
NTSTATUS status;
HANDLE ThreadHandle;
DBGPRINT(GPO, ("BrdgGpoDriverInit\r\n"));
g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer = NULL;
status = BrdgGpoInitializeNetworkList();
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to initialize Network List\r\n"));
goto cleanup;
}
status = BrdgGpoInitializeNotifyList( &g_BrdgGpoGlobals.QueueInfo.NotifyList,
&g_BrdgGpoGlobals.QueueInfo.NotifyListLock,
&g_BrdgGpoGlobals.QueueInfo.NotifyEvent,
&g_BrdgGpoGlobals.QueueInfo.KillEvent);
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to initialize Notify List\r\n"));
goto cleanup;
}
//
// Since the Software hive is not up at this point, we use this to make sure we only register for
// Group Policy changes once (we'll handle this in the Add Address notification, since the software
// hive is up by the time this gets called. Will check with reg guys to see if there's a way to know
// when the software hive is up. We'll use the timer to re-attempt the registration until it is up.
//
g_BrdgGpoGlobals.NotificationsThread = NULL;
g_BrdgGpoGlobals.RegisteredForGroupPolicyChanges = FALSE;
g_BrdgGpoGlobals.WaitingOnSoftwareHive = TRUE;
// Create a thread for handling the notifications.
status = PsCreateSystemThread( &ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
NULL,
BrdgGpoProcessNotifications,
&g_BrdgGpoGlobals.QueueInfo);
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to created Notification Processing thread\r\n"));
goto cleanup;
}
// Retrieve a pointer to the thread object and reference it so we can wait for
// its termination safely.
status = ObReferenceObjectByHandle( ThreadHandle, STANDARD_RIGHTS_ALL, NULL, KernelMode,
&g_BrdgGpoGlobals.NotificationsThread, NULL );
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to reference thread handle\r\n"));
goto cleanup;
}
cleanup:
if (!NT_SUCCESS(status))
{
BrdgGpoCleanup();
}
return status;
}
VOID
BrdgGpoCleanup()
/*++
Routine Description:
Driver shutdown cleanup
Return Value:
None
Locking Constraints:
Top-level function. Assumes no locks are held by caller.
--*/
{
NTSTATUS status;
PNPAGED_LOOKASIDE_LIST LookasideQueueList;
LOCK_STATE LockState;
PLIST_ENTRY pListEntry;
PNDIS_RW_LOCK ListLock;
PLIST_ENTRY ListHead;
PLIST_ENTRY QueuedList;
PBRDG_GPO_QUEUED_NOTIFY QueuedNotify;
DBGPRINT(GPO, ("BrdgGpoCleanup\r\n"));
g_BrdgGpoGlobals.ProcessingNotifications = FALSE;
LookasideQueueList = ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'gdrB');
if (NULL == LookasideQueueList)
{
return;
}
QueuedList = ExAllocatePoolWithTag(NonPagedPool, sizeof(LIST_ENTRY), 'gdrB');
if (NULL == QueuedList)
{
ExFreePool(LookasideQueueList);
return;
}
ListHead = BrdgGpoGetNotifyListHead();
ListLock = BrdgGpoGetNotifyListLock();
ExInitializeNPagedLookasideList(LookasideQueueList,
NULL,
NULL,
0,
sizeof(BRDG_GPO_QUEUED_NOTIFY),
'grbQ',
0);
InitializeListHead(QueuedList);
DBGPRINT(GPO, ("Acquiring Read-Write Lock and clearing list\r\n"));
//
// We use a temporary list to close each key, since we can't close them at
// DISPATCH_LEVEL
//
NdisAcquireReadWriteLock(ListLock, TRUE /* Write-access */, &LockState);
//
// Loop through the list of notifications that we have.
//
for (pListEntry = ListHead->Flink; pListEntry != ListHead; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NOTIFY_KEY Notify;
Notify = CONTAINING_RECORD(pListEntry, BRDG_GPO_NOTIFY_KEY, ListEntry);
//
// We're going to be shutting this down soon, so block it now
// so that no-one else can increment this.
//
BrdgBlockWaitRef(&Notify->RefCount);
//
// We don't want any notifications the fire to run either.
//
Notify->Recurring = FALSE;
QueuedNotify = ExAllocateFromNPagedLookasideList(LookasideQueueList);
QueuedNotify->Notify = Notify;
InsertTailList(QueuedList, &QueuedNotify->ListEntry);
}
while (!IsListEmpty(ListHead))
{
//
// We'll be freeing this from our sencondary list.
//
pListEntry = RemoveHeadList(ListHead);
}
NdisReleaseReadWriteLock(ListLock, &LockState);
DBGPRINT(GPO, ("Closing and Freeing Notifications\r\n"));
//
// We're back at PASSIVE_LEVEL so we can now do the registration for the changes.
//
for (pListEntry = QueuedList->Flink; pListEntry != QueuedList; pListEntry = pListEntry->Flink)
{
HANDLE hKey;
PBRDG_GPO_NOTIFY_KEY Notify;
QueuedNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_QUEUED_NOTIFY, ListEntry);
Notify = QueuedNotify->Notify;
DBGPRINT(GPO, ("Closing Regkey and Freeing Notification: %S\r\n", Notify->Identifier.Buffer));
hKey = Notify->RegKey;
Notify->RegKey = NULL;
if (hKey)
{
ZwClose(hKey);
}
DBGPRINT(GPO, ("Refcount for %S \t-\t %d\r\n", Notify->Identifier.Buffer, Notify->RefCount.Refcount));
//
// Since we're freeing this notification, we decrement the refcount
//
BrdgDecrementWaitRef(&Notify->RefCount);
//
// This will block until the ref count is zero. Any attempts to increment the waitref will
// fail.
//
BrdgShutdownWaitRef(&Notify->RefCount);
//
// We NULL these out so that the free routine below doesn't try to remove us from
// the notify list.
//
QueuedNotify->Notify->ListEntry.Blink = NULL;
QueuedNotify->Notify->ListEntry.Flink = NULL;
//
// Free the data associated with this struct, and the struct itself.
//
BrdgGpoFreeNotifyStructAndData(QueuedNotify->Notify);
}
//
// Free the temporary list.
//
while (!IsListEmpty(QueuedList))
{
pListEntry = RemoveHeadList(QueuedList);
QueuedNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_QUEUED_NOTIFY, ListEntry);
ExFreeToNPagedLookasideList(LookasideQueueList, QueuedNotify);
}
if (g_BrdgGpoGlobals.NotificationsThread)
{
//
// Set the Event to kill the thread so that the notifications are no longer waiting.
//
KeSetEvent(BrdgGpoGetKillEvent(), EVENT_INCREMENT, TRUE);
status = KeWaitForSingleObject(g_BrdgGpoGlobals.NotificationsThread, Executive, KernelMode, TRUE, NULL);
KeLowerIrql(0);
//
// De-reference the thread handle to allow the thread to be destroyed.
//
ObDereferenceObject(g_BrdgGpoGlobals.NotificationsThread);
SAFEASSERT(NT_SUCCESS(status));
}
DBGPRINT(GPO, ("Freeing List structures\r\n"));
ExDeleteNPagedLookasideList(LookasideQueueList);
ExFreePool(LookasideQueueList);
ExFreePool(QueuedList);
//
// Free any remaining data.
//
if (NULL != g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer)
{
ExFreePool(g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer);
}
status = BrdgGpoEmptyNetworkList(g_BrdgGpoGlobals.ListHeadNetworks, g_BrdgGpoGlobals.NetworkListLock);
SAFEASSERT(NT_SUCCESS(status));
if (g_BrdgGpoGlobals.ListHeadNetworks)
{
ExFreePool(g_BrdgGpoGlobals.ListHeadNetworks);
}
if (g_BrdgGpoGlobals.NetworkListLock)
{
ExFreePool(g_BrdgGpoGlobals.NetworkListLock);
}
BrdgGpoFreeNotifyList();
DBGPRINT(GPO, ("BrdgGpoCleanup complete\r\n"));
}
BOOLEAN
BrdgGpoWaitingOnSoftwareHive()
{
return g_BrdgGpoGlobals.WaitingOnSoftwareHive;
}
NTSTATUS
BrdgGpoRegisterForAdapterAddressChangeNotification(
IN LPWSTR NetworkIdentifier,
IN LPWSTR RegKeyName)
{
NTSTATUS status;
PBRDG_GPO_NOTIFY_KEY Notify;
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
NetworkIdentifier,
&Notify);
if (NT_SUCCESS(status) && (STATUS_OBJECT_NAME_EXISTS != status))
{
UNICODE_STRING RegKey;
RtlInitUnicodeString(&RegKey, RegKeyName);
if ((STATUS_OBJECT_NAME_EXISTS != status) && NT_SUCCESS(status))
{
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
Notify->Recurring = TRUE;
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
NetworkIdentifier,
RegKeyName,
L"DhcpDomain",
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE,
BrdgGpoTcpipInterfacesChangeCallback,
TRUE,
NULL,
NULL);
}
}
}
if (NT_SUCCESS(status))
{
SAFEASSERT(Notify);
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
}
return status;
}
NTSTATUS
BrdgGpoNewAddressNotification(
IN PWSTR DeviceId
)
/*++
Routine Description:
Called when a our TDI AddAddress handler receives a new IP Address.
Arguments:
DeviceID - GUID Identifying the adapter
Return Value:
NTSTATUS - Possible values include:
STATUS_INSUFFICIENT_RESOURCES (not enough memory)
STATUS_SUCCESS
Locking Constraints:
Top-level function. Assumes no locks are held by caller.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
DBGPRINT(GPO, ("BrdgGpoNewAddressNotification\r\n"));
if (FALSE == g_BrdgGpoGlobals.RegisteredForGroupPolicyChanges)
{
BrdgGpoRegisterForHiveListNotification();
BrdgGpoRegisterForGroupPolicyNotification();
BrdgGpoRegisterForGroupPolicyNetworkNameNotification();
BrdgGpoRegisterForWindowsGroupPolicyNotification();
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification();
g_BrdgGpoGlobals.RegisteredForGroupPolicyChanges = TRUE;
}
DBGPRINT(GPO, ("Device: %S\r\n", DeviceId));
if (NULL != DeviceId)
{
PBRDG_GPO_NETWORKS Network = NULL;
UNICODE_STRING RegKey;
PWCHAR NetworkIdentifier = NULL;
PWCHAR RegNetworkName = NULL;
PWCHAR NetworkName = NULL;
ULONG NetworkNameLen = 0;
PWCHAR RegString = NULL;
status = ( NdisAllocateMemoryWithTag( &RegString,
((UINT)wcslen(TcpipInterfacesKey) + 1 + (UINT)wcslen(DeviceId) + 1) * sizeof(WCHAR),
'gdrB'));
if (NT_SUCCESS(status))
{
wcscpy(RegString, TcpipInterfacesKey);
wcscat(RegString, L"\\");
wcsncat(RegString, DeviceId, MAX_GUID_LEN - 1);
NetworkIdentifier = DeviceId;
RtlInitUnicodeString(&RegKey, RegString);
status = BrdgReadRegUnicode(&RegKey,
L"DhcpDomain",
&RegNetworkName,
&NetworkNameLen);
if (!NT_SUCCESS(status) || (0 == NetworkNameLen))
{
//
// Either we didn't get a network name back, or the name is blank.
// in both cases we go to the ipaddress and subnetmask to determine
// the network that we're on.
// We AND the two together to get this.
// For example: Address: 10.251.1.3 Subnet: 255.0.0.0 gives us a
// network of: 10.0.0.0
//
status = BrdgGpoGetCurrentNetwork(&RegKey, &RegNetworkName);
}
if (NT_SUCCESS(status))
{
ULONG NetworkNameByteLen =(ULONG) ((wcslen(RegNetworkName) + 1) * sizeof(WCHAR));
//
// Copy the network name from the reg into a NonPagedPool string
// (since it will be accessed at DISPATCH_LEVEL)
//
NetworkName = ExAllocatePoolWithTag(NonPagedPool, NetworkNameByteLen, 'gdrB');
if(NetworkName)
{
RtlZeroMemory(NetworkName, NetworkNameByteLen);
RtlCopyMemory(NetworkName, RegNetworkName, NetworkNameByteLen);
}
//
// Check if we match the current GP network.
//
if ((0 != g_BrdgGpoGlobals.GroupPolicyNetworkName.Length) &&
(NULL != g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer))
{
if(_wcsicmp(g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer, NetworkName) == 0)
{
//
// We do match the network.
//
BrdgGpoUpdateBridgeMode(BRDG_ON_SAME_NETWORK);
}
else
{
//
// No, we're not, so look at other adapters
//
BrdgGpoCheckForMatchAndUpdateMode();
}
}
else
{
//
// We don't have a Group Policy network.
//
BrdgGpoUpdateBridgeMode(BRDG_ON_DIFFERENT_NETWORK);
}
status = BrdgGpoAllocateAndInitializeNetwork( &Network,
NetworkIdentifier,
RegNetworkName);
if (NT_SUCCESS(status))
{
//
// We first try to insert the Network into the list
//
status = BrdgGpoInsertNetwork( g_BrdgGpoGlobals.ListHeadNetworks,
&Network->ListEntry,
g_BrdgGpoGlobals.NetworkListLock);
if (STATUS_DUPLICATE_NAME == status)
{
UNICODE_STRING Identifier;
//
// This Network already exists in the list, so we free it update the
// NetworkName in the existing entry.
//
BrdgGpoFreeNetworkAndData(Network);
Network = NULL;
RtlInitUnicodeString(&Identifier, NetworkIdentifier);
status = BrdgGpoUpdateNetworkName( g_BrdgGpoGlobals.ListHeadNetworks,
&Identifier,
NetworkName,
g_BrdgGpoGlobals.NetworkListLock);
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNetworkAndData(Network);
Network = NULL;
}
}
//
// We've made a copy of this, so let's free it.
//
NdisFreeMemory(RegNetworkName, NetworkNameLen, 0);
}
if (NetworkName)
{
ExFreePool(NetworkName);
}
if (NT_SUCCESS(status))
{
status = BrdgGpoRegisterForAdapterAddressChangeNotification(NetworkIdentifier,
RegString);
}
#if DBG
if (Network)
{
SAFEASSERT(Network->ListEntry.Blink && Network->ListEntry.Flink);
}
#endif
NdisFreeMemory(RegString, (UINT)wcslen(RegString) + 1, 0);
}
}
return status;
}
// ===========================================================================
//
// REGISTRY CHANGE NOTIFICATION FUNCTIONS
//
// ===========================================================================
__forceinline
PLIST_ENTRY
BrdgGpoGetNotifyListHead()
/*++
Routine Description:
Arguments:
None.
Return Value:
Returns a pointer to the head of the Notifications List.
--*/
{
return g_BrdgGpoGlobals.QueueInfo.NotifyList;
}
__forceinline
PKEVENT
BrdgGpoGetNotifyEvent()
/*++
Routine Description:
Arguments:
None.
Return Value:
Returns a pointer to the Event used for signaling the Processing
Thread to start processing notification requests.
--*/
{
return g_BrdgGpoGlobals.QueueInfo.NotifyEvent;
}
__forceinline
PKEVENT
BrdgGpoGetKillEvent()
/*++
Routine Description:
Arguments:
Return Value:
Returns a pointer to the Event used for signaling the Processing
Thread to exit.
--*/
{
return g_BrdgGpoGlobals.QueueInfo.KillEvent;
}
__forceinline
PNDIS_RW_LOCK
BrdgGpoGetNotifyListLock()
/*++
Routine Description:
Arguments:
Return Value:
Returns a pointer to the Read-Write lock that protects the
notification request list.
--*/
{
return g_BrdgGpoGlobals.QueueInfo.NotifyListLock;
}
__forceinline
BOOLEAN
BrdgGpoProcessingNotifications()
/*++
Routine Description:
Arguments:
Return Value:
TRUE - We're still processing Notifications (ie. we're not shutting down).
FALSE - We're shutting down, don't add anything else to the list.
--*/
{
return g_BrdgGpoGlobals.ProcessingNotifications;
}
NTSTATUS
BrdgGpoFindNotify(
IN PLIST_ENTRY ListHead,
IN PNDIS_RW_LOCK ListLock,
IN LPWSTR Identifier,
OUT PBRDG_GPO_NOTIFY_KEY* Notify
)
/*++
Routine Description:
Since we don't want to have duplicate Notifications in the list,
this function is used to find an existing item if has already been added.
Arguments:
ListHead - Pointer to the head of a Notifications list.
ListLock - Read-Write lock for protecting the list.
Identifier - A unique identifier associated with the item. For NICs this is the
GUID assigned to the NIC. For other items like the Group Policies,
it is just a name we assign for example: "GroupPolicyNetworkName".
Notify - An out param the contains either a pointer to the Notify we found,
or NULL if we didn't find a matching entry.
Return Value:
STATUS_SUCCESS We didn't find a matching entry.
STATUS_OBJECT_NAME_EXISTS We found a match, so we'll use that instead
of allocating a new item.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
LOCK_STATE LockState;
PLIST_ENTRY pListEntry;
if (NULL != Notify)
{
*Notify = NULL;
}
if (NULL == ListHead ||
NULL == ListLock ||
NULL == Identifier ||
NULL == Notify
)
{
return STATUS_INVALID_PARAMETER;
}
BrdgGpoAcquireNetworkListLock(ListLock, FALSE /* Read */, &LockState);
for (pListEntry = ListHead->Flink; pListEntry != ListHead; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NOTIFY_KEY CurrentNotify;
CurrentNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_NOTIFY_KEY, ListEntry);
if ((CurrentNotify->Identifier.Buffer) &&
(0 == _wcsicmp(CurrentNotify->Identifier.Buffer, Identifier)))
{
*Notify = CurrentNotify;
status = STATUS_OBJECT_NAME_EXISTS;
break;
}
}
BrdgGpoReleaseNetworkListLock(ListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoInitializeNotifyList(
OUT PLIST_ENTRY* ListHead,
OUT PNDIS_RW_LOCK* ListLock,
OUT PKEVENT* WaitEvent,
OUT PKEVENT* KillEvent)
/*++
Routine Description:
Initializes the Notifications List and associated objects.
Arguments:
ListHead - [OUT] Pointer to the list head that we'll allocate.
ListLock - [OUT] Pointer to the Read-Write lock that we'll allocate.
WaitEvent - [OUT] Pointer to the WaitEvent we'll allocate
KillEvent - [OUT] Pointer to the KillEvent we'll allocate
Return Value:
STATUS_INSUFFICIENT_RESOURCES (unable to allocate everything).
STATUS_INVALID_PARAMETER (we were passed a NULL pointer to a pointer).
STATUS_SUCCESS (we were able to allocate everything successfully).
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PLIST_ENTRY pListHead;
PNDIS_RW_LOCK pListLock;
PKEVENT pWaitEvent;
PKEVENT pKillEvent;
if (NULL == ListHead ||
NULL == ListLock ||
NULL == WaitEvent ||
NULL == KillEvent)
{
return STATUS_INVALID_PARAMETER;
}
*ListHead = NULL;
*ListLock = NULL;
*WaitEvent = NULL;
*KillEvent = NULL;
pListHead = NULL;
pListLock = NULL;
pWaitEvent = NULL;
pKillEvent = NULL;
pListHead = ExAllocatePoolWithTag(NonPagedPool, sizeof(LIST_ENTRY), 'gdrB');
if (pListHead)
{
InitializeListHead(pListHead);
pListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(NDIS_RW_LOCK), 'gdrB');
if (pListLock)
{
NdisInitializeReadWriteLock(pListLock);
pWaitEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), 'gdrB');
if (pWaitEvent)
{
KeInitializeEvent(pWaitEvent, SynchronizationEvent, FALSE);
pKillEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), 'gdrB');
if (pKillEvent)
{
KeInitializeEvent(pKillEvent, SynchronizationEvent, FALSE);
*ListHead = pListHead;
*ListLock = pListLock;
*WaitEvent = pWaitEvent;
*KillEvent = pKillEvent;
g_BrdgGpoGlobals.ProcessingNotifications = TRUE;
status = STATUS_SUCCESS;
}
}
}
}
if (!NT_SUCCESS(status))
{
if (pListHead)
{
ExFreePool(pListHead);
}
if (pListLock)
{
ExFreePool(pListLock);
}
if (pWaitEvent)
{
ExFreePool(pWaitEvent);
}
if (pKillEvent)
{
ExFreePool(pKillEvent);
}
}
return status;
}
VOID
BrdgGpoFreeNotifyList()
/*++
Routine Description:
Frees the notify list and all it's associated entries.
Arguments:
None.
Return Value:
None.
--*/
{
if (g_BrdgGpoGlobals.QueueInfo.NotifyList)
{
ExFreePool(g_BrdgGpoGlobals.QueueInfo.NotifyList);
g_BrdgGpoGlobals.QueueInfo.NotifyList = NULL;
}
if (g_BrdgGpoGlobals.QueueInfo.NotifyListLock)
{
ExFreePool(g_BrdgGpoGlobals.QueueInfo.NotifyListLock);
g_BrdgGpoGlobals.QueueInfo.NotifyListLock = NULL;
}
if (g_BrdgGpoGlobals.QueueInfo.NotifyEvent)
{
ExFreePool(g_BrdgGpoGlobals.QueueInfo.NotifyEvent);
g_BrdgGpoGlobals.QueueInfo.NotifyEvent = NULL;
}
if (g_BrdgGpoGlobals.QueueInfo.KillEvent)
{
ExFreePool(g_BrdgGpoGlobals.QueueInfo.KillEvent);
g_BrdgGpoGlobals.QueueInfo.KillEvent = NULL;
}
}
NTSTATUS
BrdgGpoRequestNotification(
IN PBRDG_GPO_NOTIFY_KEY Notify)
/*++
Routine Description:
Adds the Notification request to the list and signals the processing thread
to re-check the list and register for any outstanding notifications.
Arguments:
Notify - Notify struct that contains all the information necessary to register
for registry key changes.
Return Value:
STATUS_SHUTDOWN_IN_PROGRESS - We're no longer processing notifications as we're
shutting down.
STATUS_UNSUCCESSFUL - We were unable to get a valid list or lock.
STATUS_SUCCESS - We successfully notified the processing thread to
request notification on this item.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PLIST_ENTRY ListHead;
PNDIS_RW_LOCK ListLock;
LOCK_STATE LockState;
PLIST_ENTRY pListEntry;
PKEVENT WaitEvent;
BOOLEAN NewEntry = TRUE;
BOOLEAN ShuttingDown = FALSE;
ListLock = BrdgGpoGetNotifyListLock();
ListHead = BrdgGpoGetNotifyListHead();
if (NULL == ListLock || NULL == ListHead)
{
return STATUS_UNSUCCESSFUL;
}
NdisAcquireReadWriteLock(ListLock, TRUE /* Write */, &LockState);
ShuttingDown = !BrdgGpoProcessingNotifications();
for (pListEntry = ListHead->Flink; pListEntry != ListHead; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NOTIFY_KEY CurrentNotify;
CurrentNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_NOTIFY_KEY, ListEntry);
if (0 == _wcsicmp(CurrentNotify->Identifier.Buffer, Notify->Identifier.Buffer))
{
NewEntry = FALSE;
break;
}
}
if (!ShuttingDown)
{
if (NewEntry)
{
InsertTailList(ListHead, &Notify->ListEntry);
}
Notify->Modified = TRUE;
}
NdisReleaseReadWriteLock(ListLock, &LockState);
if (ShuttingDown)
{
status = STATUS_SHUTDOWN_IN_PROGRESS;
}
else
{
WaitEvent = BrdgGpoGetNotifyEvent();
KeSetEvent(WaitEvent, 0, FALSE);
}
return status;
}
VOID
BrdgGpoProcessNotifications(
IN PVOID Context)
/*++
Routine Description:
This is the processing thread worker function that is responsible to doing
all notifications that we are interested in.
WARNING: Don't try to remove this thread or have it exit until you're
no longer interested in notifications. The registery
notifications mechanism stores the notifications information
in the _ETHREAD structure, so exiting the thread loses all
remaining notifications.
Arguments:
Context - PBRDG_GPO_THREAD_PARAMS structure that contains a pointer to the
notify list, it's lock and the notify and kill events.
Return Value:
None.
--*/
{
PBRDG_GPO_THREAD_PARAMS ThreadParms = (PBRDG_GPO_THREAD_PARAMS) Context;
BOOLEAN Exiting = FALSE;
PNDIS_RW_LOCK ListLock;
PLIST_ENTRY ListHead;
PVOID WaitObjects[2];
PLIST_ENTRY QueuedList;
PBRDG_GPO_QUEUED_NOTIFY QueuedNotify;
PNPAGED_LOOKASIDE_LIST LookasideQueueList;
DBGPRINT(GPO, ("Notification Processing Thread Routine Running\r\n"));
//
// The lookaside list and Queuedlist need to live in NonPaged Pool because we utilize them
// at DISPATCH_LEVEL
//
LookasideQueueList = ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'gdrB');
if (NULL == LookasideQueueList)
{
return;
}
QueuedList = ExAllocatePoolWithTag(NonPagedPool, sizeof(LIST_ENTRY), 'gdrB');
if (NULL == QueuedList)
{
ExFreePool(LookasideQueueList);
return;
}
ExInitializeNPagedLookasideList(LookasideQueueList,
NULL,
NULL,
0,
sizeof(BRDG_GPO_QUEUED_NOTIFY),
'grbQ',
0);
InitializeListHead(QueuedList);
//
// We passed in the list through the context of this thread.
//
ListHead = ThreadParms->NotifyList;
ListLock = ThreadParms->NotifyListLock;
WaitObjects[0]= (PVOID)ThreadParms->NotifyEvent;
WaitObjects[1]= (PVOID)ThreadParms->KillEvent;
while (!Exiting)
{
NTSTATUS status;
LOCK_STATE LockState;
PLIST_ENTRY pListEntry;
ULONG FiredEvent;
//
// We only do this if we're still processing notifications, otherwise we're waiting on
// the kill event.
//
if (BrdgGpoProcessingNotifications())
{
//
// We use a temporary list to fire off the notifications, since we can't
// register for RegKey notifications at DISPATCH_LEVEL.
//
NdisAcquireReadWriteLock(ListLock, FALSE /* Read-only */, &LockState);
//
// Loop through the list of notifications looking for any that have changed.
//
for (pListEntry = ListHead->Flink; pListEntry != ListHead; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NOTIFY_KEY Notify;
Notify = CONTAINING_RECORD(pListEntry, BRDG_GPO_NOTIFY_KEY, ListEntry);
if (TRUE == Notify->Modified)
{
//
// We've found an item that has changed, add it to the list that we'll
// use to do the actual work from (at PASSIVE_LEVEL).
//
if (FALSE == Notify->PendingNotification)
{
if (BrdgIncrementWaitRef(&Notify->RefCount))
{
QueuedNotify = ExAllocateFromNPagedLookasideList(LookasideQueueList);
QueuedNotify->Notify = Notify;
InsertTailList(QueuedList, &QueuedNotify->ListEntry);
}
}
//
// We're going to handle this request so set the Modified value to FALSE
// so that we don't do anything with it if we run through the list again
// due to another item being added.
//
Notify->Modified = FALSE;
}
}
NdisReleaseReadWriteLock(ListLock, &LockState);
//
// We're back at PASSIVE_LEVEL so we can now do the registration for the changes.
//
for (pListEntry = QueuedList->Flink; pListEntry != QueuedList; pListEntry = pListEntry->Flink)
{
QueuedNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_QUEUED_NOTIFY, ListEntry);
DBGPRINT(GPO, ("Processing Notification: %S\r\n", QueuedNotify->Notify->Identifier.Buffer));
//
// Do the actual registration for the key change notification. Since we can also be
// passed in a pointer to a BOOLEAN that is used elsewhere, we set that accordingly
// if we have one.
//
DBGPRINT(GPO, ("Refcount for %S \t-\t %d\r\n", QueuedNotify->Notify->Identifier.Buffer, QueuedNotify->Notify->RefCount));
status = BrdgGpoRegisterForRegKeyChange(QueuedNotify->Notify);
if (QueuedNotify->Notify->SuccessfulRegistration)
{
*(QueuedNotify->Notify->SuccessfulRegistration) = (BOOLEAN)NT_SUCCESS(status);
BrdgGpoCheckForMatchAndUpdateMode();
if (QueuedNotify->Notify->FunctionRegister)
{
NTSTATUS tmpStatus;
tmpStatus = QueuedNotify->Notify->FunctionRegister();
DBGPRINT(GPO, ("Function returned: 0x%x\r\n", tmpStatus));
}
}
if (NT_SUCCESS(status))
{
InterlockedExchange(&QueuedNotify->Notify->PendingNotification, TRUE);
}
else
{
InterlockedExchange(&QueuedNotify->Notify->PendingNotification, FALSE);
BrdgDecrementWaitRef(&QueuedNotify->Notify->RefCount);
}
}
//
// Free the temporary list.
//
while (!IsListEmpty(QueuedList))
{
pListEntry = RemoveHeadList(QueuedList);
QueuedNotify = CONTAINING_RECORD(pListEntry, BRDG_GPO_QUEUED_NOTIFY, ListEntry);
ExFreeToNPagedLookasideList(LookasideQueueList, QueuedNotify);
}
}
//
// We're done, we'll wait here until the event has fired, ie, one of the items needs to be re-registered,
// or a new item has been added to the list and we need to register for notifications.
//
status = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, Executive, KernelMode, FALSE, FALSE, NULL);
if (!NT_SUCCESS(status))
{
FiredEvent = 1L; // We're going to terminate the thread.
DBGPRINT(GPO, ("KeWaitForMultipleObjects returned an error"));
}
else
{
FiredEvent = (ULONG)status - (ULONG)STATUS_WAIT_0;
}
if (1L == FiredEvent)
{
Exiting = TRUE;
}
}
ExDeleteNPagedLookasideList(LookasideQueueList);
ExFreePool(LookasideQueueList);
ExFreePool(QueuedList);
DBGPRINT(GPO, ("Notification Processing Thread Routine Exiting\r\n"));
// We're done, kill this thread.
PsTerminateSystemThread( STATUS_SUCCESS );
}
NTSTATUS
BrdgGpoRegisterForRegKeyChange(
IN PBRDG_GPO_NOTIFY_KEY Notify)
{
NTSTATUS status;
if (!BrdgGpoProcessingNotifications())
{
return STATUS_SHUTDOWN_IN_PROGRESS;
}
//
// Call our notify worker function (this does the real request for notification).
//
status = BrdgGpoNotifyRegKeyChange( Notify,
(PIO_APC_ROUTINE)(ULONG_PTR)&Notify->RegChangeWorkItem,
Notify->WorkItemContext,
Notify->CompletionFilter,
Notify->WatchTree);
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to register for notification on %S. Status: 0x%x\r\n", Notify->RegKeyName.Buffer, status));
}
return status;
}
NTSTATUS
BrdgGpoBuildNotifyForRegKeyChange(
IN PBRDG_GPO_NOTIFY_KEY Notify,
IN LPWSTR Identifier,
IN LPWSTR RegKeyName,
IN LPWSTR RegValueName,
IN PWORKER_THREAD_ROUTINE ApcRoutine,
IN PVOID ApcContext,
IN ULONG CompletionFilter,
IN BOOLEAN WatchTree,
IN PBRDG_GPO_REG_CALLBACK FunctionCallback,
IN BOOLEAN Recurring,
IN PBOOLEAN SuccessfulRegistration,
IN PBRDG_GPO_REGISTER FunctionRegister
)
/*++
Routine Description:
Builds a Notify structure used for Registry Key and Value changes.
Arguments:
Notify - If ReRegister is FALSE, then this structure has simply been
initialized with some basic information. The rest will be
filled in here. If ReRegister is TRUE, then this structure
contains all the information necessary to redo the notification
request, this saves us having to pass all the data in each time.
Identifier - Identifies this Notify structure. Can be a name, or a GUID for an adapter.
RegKeyName - The Registry key that we're interesting in waiting on.
RegValueName - The Value that we need (or "Default" if we don't care about it")
ApcRoutine - The routine that we which to be notified on.
ApcContext - Information that we want to be passed back (we expect a valid Notify Struct).
CompletionFilter - What type of change we're interested in. ie. New Subkey added, or Value changed etc.
WatchTree - Do we want to what for changes on all subkeys as well.
FunctionCallback - Our own internal callback functions
Recurring - Do we want to re-do the notification once we're done handling it.
SuccessfulRegistration - A pointer to a BOOLEAN that we set if the registration is successful.
Return Value:
STATUS_SUCCESS or a specific error code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
LPWSTR lpszIdentifier = NULL;
LPWSTR lpszRegKeyName = NULL;
LPWSTR lpszRegValueName = NULL;
if (NULL == Notify ||
NULL == Identifier ||
NULL == RegKeyName ||
NULL == RegValueName ||
NULL == ApcRoutine ||
NULL == ApcContext ||
NULL == FunctionCallback
)
{
return STATUS_INVALID_PARAMETER;
}
//
// This buffer is not used by ZwNotifyChangeKey. So no need to really allocate anything for it.
//
Notify->Buffer = 0L;
Notify->BufferSize = sizeof(ULONG);
//
// We Allocate these from NonPagedPool because they're passed as part of a struct that can be used at
// DISPATCH_LEVEL
//
lpszIdentifier = ExAllocatePoolWithTag(NonPagedPool, (wcslen(Identifier) + 1) * sizeof(WCHAR), 'gdrB');
if (lpszIdentifier)
{
lpszRegKeyName = ExAllocatePoolWithTag(NonPagedPool, (wcslen(RegKeyName) + 1) * sizeof(WCHAR), 'gdrB');
if (lpszRegKeyName)
{
lpszRegValueName = ExAllocatePoolWithTag(NonPagedPool, (wcslen(RegValueName) + 1) * sizeof(WCHAR), 'gdrB');
if (lpszRegValueName)
{
BOOLEAN Success;
RtlZeroMemory(lpszIdentifier, (wcslen(Identifier) + 1) * sizeof(WCHAR));
RtlZeroMemory(lpszRegKeyName, (wcslen(RegKeyName) + 1) * sizeof(WCHAR));
RtlZeroMemory(lpszRegValueName, (wcslen(RegValueName) + 1) * sizeof(WCHAR));
//
// We need to allocate new strings because the RtlInitUnicodeString function just sets its buffer
// to the LPWSTR we pass it and these values need to be used outside the scope of these functions.
//
wcscpy(lpszIdentifier, Identifier);
wcscpy(lpszRegKeyName, RegKeyName);
wcscpy(lpszRegValueName, RegValueName);
//
// Set the strings inside our struct. This enables us to fully rebuild the information required
// for keeping track of the different keys that we need to be notified about.
//
RtlInitUnicodeString(&Notify->Identifier, lpszIdentifier);
RtlInitUnicodeString(&Notify->RegKeyName, lpszRegKeyName);
RtlInitUnicodeString(&Notify->RegValue, lpszRegValueName);
//
// Recurring will tell us if we need to re-register once a change is fired.
//
Notify->Recurring = Recurring;
//
// Rather than have the BrdgGpoRegNotify function do everything, we have seperate functions
// for each one. This also means that we don't have to keep all of them in the paged-locked
// section, since they will be called at PASSIVE_LEVEL.
//
Notify->FunctionCallback = FunctionCallback;
//
// We are using a Workitem to get called back on. We pass in the notify structure
// which has enough info to re-notify if necessary. The context is generally just
// the Deferred work queue.
//
ExInitializeWorkItem(&Notify->RegChangeWorkItem, ApcRoutine, Notify);
Notify->WorkItemContext = ApcContext;
//
// We store the WatchTree and CompletionFilter so that we can renotify needing any
// additional parameters, since we're probably ddoing this from a different thread.
//
Notify->WatchTree = WatchTree;
Notify->CompletionFilter = CompletionFilter;
//
// We set this once we have successfully registered for notification on the key of
// interest.
//
Notify->SuccessfulRegistration = SuccessfulRegistration;
//
// Increment this once so that we can decrement it in the cleanup code and have it only go to Zero then.
//
BrdgInitializeWaitRef(&Notify->RefCount, FALSE);
//
// Since we're initializing this object, there is no way that this should fail.
//
Success = BrdgIncrementWaitRef(&Notify->RefCount);
SAFEASSERT(Success);
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(status))
{
if (lpszIdentifier)
{
ExFreePool(lpszIdentifier);
}
if (lpszRegKeyName)
{
ExFreePool(lpszRegKeyName);
}
if (lpszRegValueName)
{
ExFreePool(lpszRegValueName);
}
}
return status;
}
NTSTATUS
BrdgGpoNotifyRegKeyChange(
IN PBRDG_GPO_NOTIFY_KEY Notify,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
IN ULONG CompletionFilter,
IN BOOLEAN WatchTree)
/*++
Routine Description:
This calls ZwNotifyChangeKey to register us for notifications on individual keys.
We close the key in the callback functions because you can only listen once per handle.
Arguments:
Notify - Structure containing relevant information about the notification. Allows us to
know what values to read to get the relevant data that we need.
ApcRoutine - The routine that we which to be notified on.
ApcContext - Information that we want to be passed back (we expect a valid Notify Struct).
CompletionFilter - What type of change we're interested in. ie. New Subkey added, or Value changed etc.
WatchTree - Do we want to what for changes on all subkeys as well.
Return Value:
STATUS_SUCCESS or a specific error code.
--*/
{
OBJECT_ATTRIBUTES ObAttr;
NTSTATUS status;
InitializeObjectAttributes(&ObAttr, &Notify->RegKeyName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ZwOpenKey( &Notify->RegKey,
KEY_READ | KEY_NOTIFY | KEY_WRITE,
&ObAttr);
if (NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Opened Regkey successfully\r\n"));
status = ZwNotifyChangeKey( Notify->RegKey,
NULL,
ApcRoutine,
ApcContext,
&Notify->IoStatus,
CompletionFilter,
WatchTree,
&Notify->Buffer,
Notify->BufferSize,
TRUE
);
}
else
{
//
// Set it to NULL so that we don't try to close it accidentally during shutdown.
//
Notify->RegKey = NULL;
}
return status;
}
VOID
static
BrdgGpoRegNotify(
IN PVOID Context)
/*++
Routine Description:
This is the central callback function that we are notified on.
This is called on an Executive worker thread at PASSIVE_LEVEL.
Arguments:
Context - Is just our Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
PBRDG_GPO_NOTIFY_KEY Notify = (PBRDG_GPO_NOTIFY_KEY)Context;
DBGPRINT(GPO, ("APC routine called\r\n"));
DBGPRINT(GPO, ("Current IRQL: %d\r\n", CURRENT_IRQL));
if (Notify)
{
LONG RefCount;
InterlockedExchange(&Notify->PendingNotification, FALSE);
Notify->FunctionCallback(Notify);
RefCount = Notify->RefCount.Refcount - 1;
DBGPRINT(GPO, ("Refcount for %S \t-\t %d\r\n", Notify->Identifier.Buffer, RefCount));
BrdgDecrementWaitRef(&Notify->RefCount);
}
}
NTSTATUS
BrdgGpoAllocateAndInitializeNotifyStruct(
OUT PBRDG_GPO_NOTIFY_KEY* Notify)
/*++
Routine Description:
Allocates and initializes the Notify struct to all zeros.
Arguments:
Notify - A pointer to pointer to a Notify struct that is allocated
from NonPagedPool.
Return Value:
STATUS_INSUFFICIENT_RESOURCES - Not enough memory to filfull the request.
STATUS_INVALID_PARAMETER - We were passed a NULL Pointer to pointer
to a Notify struct.
STATUS_SUCCESS - We successfully allocated space for the structure.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
if (NULL == Notify)
{
return STATUS_INVALID_PARAMETER;
}
//
// We allocate this from NonPagedPool because it will be accessed at DISPATCH_LEVEL
//
*Notify = ExAllocatePoolWithTag(NonPagedPool, sizeof(BRDG_GPO_NOTIFY_KEY), 'gdrB');
if (*Notify)
{
//
// Zero it out so that we don't try and free invalid strings when we free it.
//
RtlZeroMemory(*Notify, sizeof(BRDG_GPO_NOTIFY_KEY));
status = STATUS_SUCCESS;
}
return status;
}
VOID
BrdgGpoFreeNotifyStructAndData(
IN PBRDG_GPO_NOTIFY_KEY Notify)
/*++
Routine Description:
Frees all data associated with a Notify struct and then frees the struct
itself.
Note: This will not free a structure that is still in a list.
If you need to free something, use RemoveListEntry and then
set the Notify->ListEntry Blink and Flink = NULL and then call
this.
WARNING:
Since it's possible that this structure is still being used by
the a waiting registration, it's better to leave them alone until
shutdown as it's possible that a notification may be fired once
this has been freed and that will result in a system crash since
the struct will be invalid.
Arguments:
Notify - A pointer to the Notify struct to be freed.
Return Value:
None.
--*/
{
if (Notify)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
if (Notify->Identifier.Buffer)
{
ExFreePool(Notify->Identifier.Buffer);
}
if (Notify->RegKeyName.Buffer)
{
ExFreePool(Notify->RegKeyName.Buffer);
}
if (Notify->RegValue.Buffer)
{
ExFreePool(Notify->RegValue.Buffer);
}
ExFreePool(Notify);
}
else
{
if (BrdgGpoProcessingNotifications())
{
DBGPRINT(GPO, ("Attempt to free a Notify that is still in a list\r\nWhile we're still processing Notifications\r\n"));
}
}
}
}
// ===========================================================================
//
// NOTIFICATION REGISTRATION FUNCTIONS
//
// ===========================================================================
NTSTATUS
BrdgGpoRegisterForGroupPolicyNetworkNameNotification()
/*++
Routine Description:
Registers for the changes on the following registry key:
"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\History"
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status;
PBRDG_GPO_NOTIFY_KEY Notify = NULL;
UNICODE_STRING RegKeyName;
PWCHAR RegValue;
ULONG DataLen;
if (g_BrdgGpoGlobals.RegisteredForGroupPolicyHistoryChanges)
{
DBGPRINT(GPO, ("Already Registered for Group Policy Network Name Notification\r\n"));
return STATUS_SUCCESS;
}
DBGPRINT(GPO, ("BrdgGpoRegisterForGroupPolicyNetworkNameNotification\r\n"));
RtlInitUnicodeString(&RegKeyName, (LPWSTR) HistoryKey);
//
// Read the current value from the Registry.
//
status = BrdgReadRegUnicode(&RegKeyName,
L"NetworkName",
&RegValue,
&DataLen);
if (NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Group Policy Network Name: %S\r\n", RegValue));
if (NULL != g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer)
{
ExFreePool(g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer);
}
//
// Success. Now store the value for later use.
//
RtlInitUnicodeString(&g_BrdgGpoGlobals.GroupPolicyNetworkName, RegValue);
//
// Since something changed, we'll just re-verify that we're in
// the correct bridging mode.
//
BrdgGpoCheckForMatchAndUpdateMode();
}
else
{
//
// We failed to get a value for this. It probably isn't there yet - this can happen if this
// is the first boot after joining a domain. We'll be waiting on this key so if we get one later
// we'll update this value.
//
g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer = NULL;
g_BrdgGpoGlobals.GroupPolicyNetworkName.Length = 0;
g_BrdgGpoGlobals.GroupPolicyNetworkName.MaximumLength = 0;
}
//
// We don't want to allocate these twice, so we first try to find an existing notify struct.
//
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
L"GroupPolicyNetworkName",
&Notify);
if (NT_SUCCESS(status))
{
if (STATUS_OBJECT_NAME_EXISTS != status)
{
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
L"GroupPolicyNetworkName",
(LPWSTR)HistoryKey,
L"NetworkName",
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE,
BrdgGpoGroupPolicyNetworkNameChangeCallback,
TRUE,
&g_BrdgGpoGlobals.RegisteredForGroupPolicyHistoryChanges,
BrdgGpoUpdateGroupPolicyNetworkName);
}
}
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to Build notification on %S. Status: 0x%x\r\n", NetworkPoliciesKey, status));
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
else
{
SAFEASSERT(Notify);
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
return status;
}
NTSTATUS
BrdgGpoRegisterForGroupPolicyNotification()
/*++
Routine Description:
Registers for the changes on the following registry key:
"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy"
This is the parent to the History key and is always on a system.
If will be notified if the History Key is created in which case
we will register for notifications on that key.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PBRDG_GPO_NOTIFY_KEY Notify = NULL;
DBGPRINT(GPO, ("BrdgGpoRegisterForGroupPolicyNotification\r\n"));
//
// We don't want to allocate these twice, so we first try to find an existing notify struct.
//
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
L"GroupPolicyParent",
&Notify);
if (NT_SUCCESS(status))
{
if (STATUS_OBJECT_NAME_EXISTS != status)
{
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
L"GroupPolicyParent",
(LPWSTR)GroupPolicyKey,
L"Default",
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_NAME,
FALSE,
BrdgGpoGroupPolicyChangeCallback,
TRUE,
&g_BrdgGpoGlobals.RegisteredForGroupPolicyChanges,
BrdgGpoRegisterForGroupPolicyNetworkNameNotification);
}
}
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to register for notification on %S. Status: 0x%x\r\n", GroupPolicyKey, status));
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
else
{
SAFEASSERT(Notify);
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
return status;
}
NTSTATUS
BrdgGpoRegisterForWindowsGroupPolicyNotification()
/*++
Routine Description:
Registers for the changes on the following registry key:
HKLM\SOFTWARE\Policies\Microsoft\Windows
If this gets notified, then we'll attempt to wait on the
Network Connections key below this.
Arguments:
None.
Return Value:
STATUS_INSUFFICIENT_RESOURCES - Not enough memory to allocate the structure.
STATUS_SUCCESS - We were able to post the request successfully.
This doesn't mean we've successfully requested
notification though, it only means we've added it
to the Notifications list and have signaled the
processing thread to attempt a notification.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PBRDG_GPO_NOTIFY_KEY Notify = NULL;
DBGPRINT(GPO, ("BrdgGpoRegisterForWindowsGroupPolicyNotification\r\n"));
//
// We don't want to allocate these twice, so we first try to find an existing notify struct.
//
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
L"WindowsGroupPolicies",
&Notify);
if (NT_SUCCESS(status))
{
if (STATUS_OBJECT_NAME_EXISTS != status)
{
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
L"WindowsGroupPolicies",
(LPWSTR)PolicyBaseKey,
(LPWSTR)L"Default",
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_NAME,
FALSE,
BrdgGpoWindowsGroupPolicyChangeCallback,
TRUE,
NULL,
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification);
}
}
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to register for notification on %S. Status: 0x%x\r\n", PolicyBaseKey, status));
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
else
{
SAFEASSERT(Notify);
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
return status;
}
VOID
BrdgGpoRegisterForHiveListNotification()
/*++
Routine Description:
Registers for the changes on the following registry key:
"HKLM\System\CurrentControlSet\Control\HiveList"
Each time this is fired we attempt to open the Software hive, if this
open is successful then we request notification on all the keys that
we are interested in under the software hive.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PBRDG_GPO_NOTIFY_KEY Notify = NULL;
UNICODE_STRING Software;
OBJECT_ATTRIBUTES ObAttr;
HANDLE hKey;
DBGPRINT(GPO, ("BrdgGpoRegisterForHiveListNotification\r\n"));
//
// We attempt to open this key now in case the hives are already loaded.
//
RtlInitUnicodeString(&Software, SoftwareHiveKey);
InitializeObjectAttributes( &ObAttr,
&Software,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status = ZwOpenKey(&hKey, KEY_READ, &ObAttr);
if (NT_SUCCESS(status))
{
//
// The software hive is already loaded, no need to register for changes
// to this. Just attempt to register for all other changes.
//
BrdgGpoRegisterForGroupPolicyNetworkNameNotification();
BrdgGpoRegisterForWindowsGroupPolicyNotification();
BrdgGpoRegisterForGroupPolicyNotification();
//
// To avoid turning the bridge on and then off again, we set this just after
// verifying the Network Connections policy setting.
//
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification();
g_BrdgGpoGlobals.WaitingOnSoftwareHive = FALSE;
ZwClose(hKey);
}
else
{
//
// We don't want to allocate these twice, so we first try to find an existing notify struct.
//
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
L"HiveList",
&Notify);
if (NT_SUCCESS(status))
{
if (STATUS_OBJECT_NAME_EXISTS != status)
{
//
// The item doesn't exist yet, so allocate it from the NonPagedPool and
// attempt build a notification request.
//
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
L"HiveList",
(LPWSTR)HiveListKey,
(LPWSTR)L"Default",
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE,
BrdgGpoHiveListCallback,
TRUE,
NULL,
NULL);
}
}
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to register for notification on %S. Status: 0x%x\r\n", PolicyBaseKey, status));
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
else
{
SAFEASSERT(Notify);
//
// We have a valid Notify structure, post a notification request to the
// processing thread.
//
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
}
}
NTSTATUS
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification()
/*++
Routine Description:
Registers for the changes on the following registry key:
"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Network Connections"
We also read any value that may already be there and act upon it.
Arguments:
None.
Return Value:
STATUS_INSUFFICIENT_RESOURCES - Not enough memory to allocate the structure.
STATUS_SUCCESS - We were able to post the request successfully.
This doesn't mean we've successfully requested
notification though, it only means we've added it
to the Notifications list and have signaled the
processing thread to attempt a notification.
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PBRDG_GPO_NOTIFY_KEY Notify = NULL;
UNICODE_STRING RegKeyName;
ULONG RegValue;
if (g_BrdgGpoGlobals.RegisteredForNetworkConnectionsGroupPolicyChanges)
{
DBGPRINT(GPO, ("Already Registered for Network Connections Group Policy Notification\r\n"));
return STATUS_SUCCESS;
}
DBGPRINT(GPO, ("BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification\r\n"));
RtlInitUnicodeString(&RegKeyName, (LPWSTR)NetworkPoliciesKey);
//
// Read the current value from the Registry.
//
status = BrdgReadRegDWord( &RegKeyName,
(LPWSTR)BridgePolicyValue,
&RegValue);
if (NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Bridge Policy Setting: %d\r\n", RegValue));
//
// Since something changed, we'll just re-verify that we're in
// the correct bridging mode.
//
BrdgGpoCheckForMatchAndUpdateMode();
}
//
// We don't want to allocate these twice, so we first try to find an existing notify struct.
//
status = BrdgGpoFindNotify( BrdgGpoGetNotifyListHead(),
BrdgGpoGetNotifyListLock(),
L"NetworkConnectionsGroupPolicies",
&Notify);
if (NT_SUCCESS(status))
{
if (STATUS_OBJECT_NAME_EXISTS != status)
{
status = BrdgGpoAllocateAndInitializeNotifyStruct(&Notify);
if (NT_SUCCESS(status))
{
status = BrdgGpoBuildNotifyForRegKeyChange( Notify,
L"NetworkConnectionsGroupPolicies",
(LPWSTR)NetworkPoliciesKey,
(LPWSTR)BridgePolicyValue,
BrdgGpoRegNotify,
(PVOID)(UINT_PTR)(unsigned int)DelayedWorkQueue,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE,
BrdgGpoNetworkConnectionsGroupPolicyChangeCallback,
TRUE,
&g_BrdgGpoGlobals.RegisteredForNetworkConnectionsGroupPolicyChanges,
NULL);
}
}
if (!NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Unable to register for notification on %S. Status: 0x%x\r\n", NetworkPoliciesKey, status));
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
else
{
SAFEASSERT(Notify);
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
#if DBG
if (Notify)
{
SAFEASSERT(Notify->ListEntry.Blink && Notify->ListEntry.Flink);
}
#endif
return status;
}
// ===========================================================================
//
// REGISTRY CHANGE CALLBACK FUNCTIONS
//
// ===========================================================================
VOID
BrdgGpoTcpipInterfacesChangeCallback(
PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the an the TcpIp interfaces key changes for
and adapter that we're interested in (any Non-NdisWan adapter).
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
NTSTATUS status;
PWCHAR RegValue;
ULONG StringLen = 0;
DBGPRINT(GPO, ("BrdgGpoTcpipInterfacesChangeCallback\r\n"));
DBGPRINT(GPO, ("Called for Key: %S re-registering.\r\n", Notify->RegKeyName.Buffer));
//
// Read the current value from the Registry.
//
status = BrdgReadRegUnicode(&Notify->RegKeyName,
Notify->RegValue.Buffer,
&RegValue,
&StringLen);
if (!NT_SUCCESS(status))
{
status = BrdgGpoGetCurrentNetwork( &Notify->RegKeyName,
&RegValue);
if (NT_SUCCESS(status))
{
StringLen = (UINT)wcslen(RegValue);
}
}
if (NT_SUCCESS(status))
{
PBRDG_GPO_NETWORKS Network;
LPWSTR NetworkName;
DBGPRINT(GPO, ("Current Network: %S\r\n", RegValue));
NetworkName = ExAllocatePoolWithTag(NonPagedPool, (StringLen + 1) * sizeof(WCHAR), 'gdrB');
if (NULL != NetworkName)
{
RtlZeroMemory(NetworkName, (StringLen + 1) * sizeof(WCHAR));
wcscpy(NetworkName, RegValue);
//
// Try to find a match for the current network identifier (generally the adapter guid)
//
status = BrdgGpoFindNetwork(g_BrdgGpoGlobals.ListHeadNetworks,
&Notify->Identifier,
g_BrdgGpoGlobals.NetworkListLock,
&Network);
if (STATUS_NOT_FOUND == status)
{
//
// No match so this is a new key (very unlikely code path).
//
status = BrdgGpoAllocateAndInitializeNetwork( &Network,
Notify->Identifier.Buffer,
NetworkName);
if (NT_SUCCESS(status))
{
status = BrdgGpoInsertNetwork( g_BrdgGpoGlobals.ListHeadNetworks,
&Network->ListEntry,
g_BrdgGpoGlobals.NetworkListLock);
if (!NT_SUCCESS(status))
{
BrdgGpoFreeNetworkAndData(Network);
Network = NULL;
}
}
}
else
{
//
// This is expected to happen most times, if not always.
//
status = BrdgGpoUpdateNetworkName( g_BrdgGpoGlobals.ListHeadNetworks,
&Notify->Identifier,
NetworkName,
g_BrdgGpoGlobals.NetworkListLock);
}
#if DBG
if (Network)
{
SAFEASSERT(Network->ListEntry.Blink && Network->ListEntry.Flink);
}
#endif
if (NetworkName)
{
ExFreePool(NetworkName);
}
NdisFreeMemory(RegValue, StringLen, 0);
}
}
else
{
//
// We change the name to NULL since the key appears to have disappeared.
//
status = BrdgGpoUpdateNetworkName( g_BrdgGpoGlobals.ListHeadNetworks,
&Notify->Identifier,
NULL,
g_BrdgGpoGlobals.NetworkListLock);
}
//
// Since something changed, we'll just re-verify that we're in
// the correct bridging mode.
//
BrdgGpoCheckForMatchAndUpdateMode();
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twices/
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
//
// Re-register. The notify object contains enough info to do this.
//
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
VOID
BrdgGpoWindowsGroupPolicyChangeCallback(
PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the Windows Group Policy key changes.
We attempt to register for the Network Connections key changes
if we haven't already done so.
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
DBGPRINT(GPO, ("BrdgGpoWindowsGroupPolicyChangeCallback\r\n"));
if (!g_BrdgGpoGlobals.RegisteredForNetworkConnectionsGroupPolicyChanges)
{
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification();
}
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twices/
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
NTSTATUS status;
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
VOID
BrdgGpoHiveListCallback(
IN PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the HiveList key changes.
If it does, we attempt to open the software hive. If that succeeds then
we attempt to register for the keys that we're interested in under
the Software Hive.
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
NTSTATUS status;
UNICODE_STRING Software;
OBJECT_ATTRIBUTES ObAttr;
HANDLE hKey;
DBGPRINT(GPO, ("BrdgGpoHiveListCallback\r\n"));
RtlInitUnicodeString(&Software, SoftwareHiveKey);
InitializeObjectAttributes( &ObAttr,
&Software,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status = ZwOpenKey(&hKey, KEY_READ, &ObAttr);
if (NT_SUCCESS(status))
{
BrdgGpoRegisterForGroupPolicyNetworkNameNotification();
BrdgGpoRegisterForWindowsGroupPolicyNotification();
BrdgGpoRegisterForGroupPolicyNotification();
//
// To avoid turning the bridge on and then off again, we set this just after
// verifying the Network Connections policy setting.
//
BrdgGpoRegisterForNetworkConnectionsGroupPolicyNotification();
g_BrdgGpoGlobals.WaitingOnSoftwareHive = FALSE;
Notify->Recurring = FALSE;
ZwClose(hKey);
}
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twices/
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
VOID
BrdgGpoGroupPolicyChangeCallback(
PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the Group Policy key changes.
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
DBGPRINT(GPO, ("BrdgGpoGroupPolicyChangeCallback\r\n"));
if (!g_BrdgGpoGlobals.RegisteredForGroupPolicyHistoryChanges)
{
BrdgGpoRegisterForGroupPolicyNetworkNameNotification();
}
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twice.
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
NTSTATUS status;
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
VOID
BrdgGpoNetworkConnectionsGroupPolicyChangeCallback(
PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the Network Connection Policy key changes.
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
NTSTATUS status;
ULONG RegValue;
DBGPRINT(GPO, ("BrdgGpoNetworkConnectionsGroupPolicyChangeCallback\r\n"));
DBGPRINT(GPO, ("Called for Key: %S re-registering.\r\n", Notify->RegKeyName.Buffer));
status = BrdgReadRegDWord( &Notify->RegKeyName,
Notify->RegValue.Buffer,
&RegValue);
if (NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Bridge Policy Setting: %d\r\n", RegValue));
}
//
// Since something changed, we'll just re-verify that we're in
// the correct bridging mode.
//
BrdgGpoCheckForMatchAndUpdateMode();
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twice.
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
NTSTATUS
BrdgGpoUpdateGroupPolicyNetworkName()
{
NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
PWCHAR RegValue = NULL;
LPWSTR GroupPolicyNetwork = NULL;
ULONG DataLen = 0;
UNICODE_STRING RegKeyName;
RtlInitUnicodeString(&RegKeyName, HistoryKey);
//
// Read the current value from the registry
//
status = BrdgReadRegUnicode(&RegKeyName,
L"NetworkName",
&RegValue,
&DataLen);
if (NT_SUCCESS(status))
{
DBGPRINT(GPO, ("Group Policy Network Name: %S\r\n", RegValue));
//
// Almost always checked at DISPATCH_LEVEL, so we allocate from NonPagedPool
//
GroupPolicyNetwork = ExAllocatePoolWithTag(NonPagedPool, (DataLen + 1) * sizeof(WCHAR), 'gdrB');
if (GroupPolicyNetwork)
{
if (NULL != g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer)
{
ExFreePool( g_BrdgGpoGlobals.GroupPolicyNetworkName.Buffer);
}
RtlZeroMemory(GroupPolicyNetwork, (DataLen + 1) * sizeof(WCHAR));
wcsncpy(GroupPolicyNetwork, RegValue, DataLen);
RtlInitUnicodeString(&g_BrdgGpoGlobals.GroupPolicyNetworkName, GroupPolicyNetwork);
//
// Since something changed, we'll just re-verify that we're in
// the correct bridging mode.
//
BrdgGpoCheckForMatchAndUpdateMode();
}
NdisFreeMemory(RegValue, DataLen, 0);
}
return status;
}
VOID
BrdgGpoGroupPolicyNetworkNameChangeCallback(
PBRDG_GPO_NOTIFY_KEY Notify
)
/*++
Routine Description:
Called back if the Group Policy History key changes.
Arguments:
Notify - Notify structure that we passed in
to ZwNotifyChangeKey
Return Value:
None.
--*/
{
NTSTATUS status;
//
// Read the current value from the registry
//
status = BrdgGpoUpdateGroupPolicyNetworkName();
//
// We set this to NULL if we're closing it for shutdown, since we
// shouldn't close it twice.
//
if (Notify->RegKey)
{
ZwClose(Notify->RegKey);
Notify->RegKey = NULL;
}
if (TRUE == Notify->Recurring)
{
status = BrdgGpoRequestNotification(Notify);
if (STATUS_SHUTDOWN_IN_PROGRESS == status)
{
if (!Notify->ListEntry.Blink && !Notify->ListEntry.Flink)
{
BrdgDecrementWaitRef(&Notify->RefCount);
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
else if (!NT_SUCCESS(status))
{
BrdgGpoFreeNotifyStructAndData(Notify);
Notify = NULL;
}
}
}
// ===========================================================================
//
// GROUP POLICY NETWORK VERIFICATION FUNCTIONS
//
// ===========================================================================
BOOLEAN
BrdgGpoAllowedToBridge()
/*++
Routine Description:
Checks the Network Connections policy key for the Bridge Policy setting.
Arguments:
None.
Return Value:
TRUE if we couldn't find a Policy Value, or the Value is 1.
FALSE if the policy exists and contains a value of 0.
--*/
{
NTSTATUS status;
UNICODE_STRING RegKey;
ULONG RegValue;
BOOLEAN CanBridge = TRUE; // If there is no key then we're allowed to bridge.
RtlInitUnicodeString(&RegKey, NetworkPoliciesKey);
status = BrdgReadRegDWord(&RegKey, (LPWSTR) BridgePolicyValue, &RegValue);
if (NT_SUCCESS(status))
{
if (FALSE == RegValue)
{
CanBridge = FALSE;
}
}
return CanBridge;
}
VOID
BrdgGpoUpdateBridgeMode(
BOOLEAN NetworkMatch
)
/*++
Routine Description:
Checks for a Network Match and if we're not allowed to bridge then
turns bridging off, otherwise it turns it on.
Arguments:
NetworkMatch - Do we have a match for the group policy network?
Return Value:
None.
--*/
{
//
// If we're still waiting on the software hive then
// we shouldn't do any further processing for this.
//
if (BrdgGpoWaitingOnSoftwareHive())
{
return;
}
if (NetworkMatch && !BrdgGpoAllowedToBridge())
{
BrdgFwdChangeBridging(FALSE);
}
else
{
BrdgFwdChangeBridging(TRUE);
}
}
VOID
BrdgGpoCheckForMatchAndUpdateMode()
/*++
Routine Description:
This looks for a matching network and group policy network and
attempts to update the bridging status accordingly.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status;
if (NULL != g_BrdgGpoGlobals.ListHeadNetworks)
{
status = BrdgGpoMatchNetworkName( g_BrdgGpoGlobals.ListHeadNetworks,
&g_BrdgGpoGlobals.GroupPolicyNetworkName,
g_BrdgGpoGlobals.NetworkListLock);
if (BRDG_STATUS_EMPTY_LIST != status)
{
if (STATUS_SUCCESS == status)
{
//
// We found a match. Check if we're allowed to run.
//
BrdgGpoUpdateBridgeMode(BRDG_ON_SAME_NETWORK);
}
else if (STATUS_NO_MATCH == status)
{
//
// No match, but we may need to turn bridging back on.
//
BrdgGpoUpdateBridgeMode(BRDG_ON_DIFFERENT_NETWORK);
}
else
{
// We should never get here.
SAFEASSERT(FALSE);
}
}
else if (BrdgGpoAllowedToBridge())
{
BrdgFwdChangeBridging(TRUE);
}
}
}
NTSTATUS BrdgGpoGetCurrentNetwork(
IN PUNICODE_STRING RegKeyName,
OUT PWCHAR* NetworkName)
/*++
Routine Description:
Determines the current network that we are on. This either uses the DHCP Domain name,
or the IP Address ANDed with the Subnet mask.
For example: 10.251.1.3 AND 255.0.0.0 results in a network of 10.0.0.0
This routine MUST be called at IRQL = PASSIVE_LEVEL.
Arguments:
RegKeyName (IN) - The RegistryKey for the adapter we're interested in.
NetworkName (OUT) - The network we're currently one.
Return Value(s):
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_NO_IP_ADDRESSES - if we've released the only address we have.
Out of memory can also be returned.
--*/
{
NTSTATUS status;
PWCHAR lpszNetworkName = NULL;
BOOLEAN HaveNetwork = FALSE;
BOOLEAN HaveDhcpDomain = FALSE;
WCHAR BaseNetwork[MAX_IP4_STRING_LEN];
PWCHAR DhcpIPAddress = NULL;
ULONG DhcpIPAddrLen = 0;
if (!RegKeyName || !NetworkName)
{
return STATUS_INVALID_PARAMETER;
}
RtlZeroMemory(BaseNetwork, MAX_IP4_STRING_LEN * sizeof(WCHAR));
*NetworkName = NULL;
//
// We don't have a valid Network Name. Attempt to build one
// from the DhcpIPAddess and DhcpSubnetMask.
//
status = BrdgReadRegUnicode(RegKeyName,
L"DhcpIPAddress",
&DhcpIPAddress,
&DhcpIPAddrLen);
if (NT_SUCCESS(status))
{
PWCHAR DhcpSubnetMask = NULL;
ULONG DhcpSubnetMaskLen = 0;
status = BrdgReadRegUnicode(RegKeyName,
L"DhcpSubnetMask",
&DhcpSubnetMask,
&DhcpSubnetMaskLen);
if (NT_SUCCESS(status))
{
LPWSTR Terminator;
in_addr ipaddr;
in_addr subnet;
//
// We and the two values together to get the Network.
// For example: 10.251.1.3 AND 255.0.0.0 gives 10.0.0.0
//
status = BrdgTdiIpv4StringToAddress(DhcpIPAddress,
FALSE,
&Terminator,
&ipaddr);
if (NT_SUCCESS(status))
{
in_addr network;
status = BrdgTdiIpv4StringToAddress(DhcpSubnetMask,
FALSE,
&Terminator,
&subnet);
network.S_un.S_addr = ipaddr.S_un.S_addr & subnet.S_un.S_addr;
DBGPRINT(GPO,
("in_addr = %u.%u.%u.%u\r\n",
network.S_un.S_un_b.s_b1, network.S_un.S_un_b.s_b2,
network.S_un.S_un_b.s_b3, network.S_un.S_un_b.s_b4));
//
// Do we have a valid IPaddress
//
if (0 != ipaddr.S_un.S_addr)
{
_snwprintf( BaseNetwork,
MAX_IP4_STRING_LEN,
L"%u.%u.%u.%u",
network.S_un.S_un_b.s_b1,
network.S_un.S_un_b.s_b2,
network.S_un.S_un_b.s_b3,
network.S_un.S_un_b.s_b4);
HaveNetwork = TRUE;
}
}
}
}
if (!HaveNetwork)
{
PWCHAR IPAddress = NULL;
ULONG IPAddrLen = 0;
status = BrdgReadRegUnicode(RegKeyName,
L"IPAddress",
&IPAddress,
&IPAddrLen);
if (NT_SUCCESS(status))
{
PWCHAR SubnetMask = NULL;
ULONG SubnetMaskLen = 0;
status = BrdgReadRegUnicode(RegKeyName,
L"SubnetMask",
&SubnetMask,
&SubnetMaskLen);
if (NT_SUCCESS(status))
{
LPWSTR Terminator;
in_addr ipaddr;
in_addr subnet;
//
// We and the two values together to get the Network.
// For example: 10.251.1.3 AND 255.0.0.0 gives 10.0.0.0
//
status = BrdgTdiIpv4StringToAddress(IPAddress,
FALSE,
&Terminator,
&ipaddr);
if (NT_SUCCESS(status))
{
in_addr network;
status = BrdgTdiIpv4StringToAddress(SubnetMask,
FALSE,
&Terminator,
&subnet);
network.S_un.S_addr = ipaddr.S_un.S_addr & subnet.S_un.S_addr;
DBGPRINT(GPO,
("in_addr = %u.%u.%u.%u\r\n",
network.S_un.S_un_b.s_b1, network.S_un.S_un_b.s_b2,
network.S_un.S_un_b.s_b3, network.S_un.S_un_b.s_b4));
//
// Do we have a valid IPaddress
//
if (0 != ipaddr.S_un.S_addr)
{
_snwprintf( BaseNetwork,
MAX_IP4_STRING_LEN,
L"%u.%u.%u.%u",
network.S_un.S_un_b.s_b1,
network.S_un.S_un_b.s_b2,
network.S_un.S_un_b.s_b3,
network.S_un.S_un_b.s_b4);
HaveNetwork = TRUE;
}
}
}
}
}
if (!HaveNetwork)
{
//
// Returning this will cause us not to update the network name this
// card.
//
status = STATUS_NO_IP_ADDRESSES;
}
else if (HaveDhcpDomain)
{
*NetworkName = lpszNetworkName;
status = STATUS_SUCCESS;
}
else
{
status = NdisAllocateMemoryWithTag(NetworkName,
((UINT)wcslen(BaseNetwork) + 1) * sizeof(WCHAR),
'gdrB');
wcscpy(*NetworkName, BaseNetwork);
}
return status;
}
// ===========================================================================
//
// NETWORK LIST MANIPULATION FUNCTIONS
//
// ===========================================================================
NTSTATUS
BrdgGpoInitializeNetworkList()
/*++
Routine Description:
Initializes the Network List and Lock.
This can can be called at any IRQL (but since it's called from driver entry,
it will most likely be called at PASSIVE_LEVEL).
Arguments:
None.
Return Value(s):
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES
--*/
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
g_BrdgGpoGlobals.ListHeadNetworks = ExAllocatePoolWithTag(NonPagedPool, sizeof(LIST_ENTRY), 'gdrB');
if (NULL != g_BrdgGpoGlobals.ListHeadNetworks)
{
InitializeListHead(g_BrdgGpoGlobals.ListHeadNetworks);
g_BrdgGpoGlobals.NetworkListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(NDIS_RW_LOCK), 'gdrB');
if (g_BrdgGpoGlobals.NetworkListLock)
{
NdisInitializeReadWriteLock(g_BrdgGpoGlobals.NetworkListLock);
status = STATUS_SUCCESS;
}
else
{
ExFreePool(g_BrdgGpoGlobals.ListHeadNetworks);
}
}
return status;
}
VOID
BrdgGpoUninitializeNetworkList()
/*++
Routine Description:
Frees the memory associated with the Network List.
This can be called at IRQL <= DISPATCH_LEVEL but is likely
to be called at PASSIVE_LEVEL as it's during shutdown.
Arguments:
None.
Return Value:
None.
--*/
{
ExFreePool(g_BrdgGpoGlobals.ListHeadNetworks);
ExFreePool(g_BrdgGpoGlobals.NetworkListLock);
}
VOID
BrdgGpoAcquireNetworkListLock(
IN PNDIS_RW_LOCK NetworkListLock,
IN BOOLEAN fWrite,
IN OUT PLOCK_STATE LockState
)
/*++
Routine Description:
Acquires the NetworkList read-write lock. We support a NULL Lock
as it allows us to acquire for write and then call functions that
need the lock for read without locking up the system (by
supplying NULL for the lock).
This can be called at IRQL <= DISPATCH_LEVEL
Arguments:
NetworkListLock - Read-Write Lock to be acquired.
fWrite - TRUE == Write Access, FALSE == Read Access
LockState - Opaque value used by NDIS.
Return Value:
None.
--*/
{
if (NetworkListLock)
{
NdisAcquireReadWriteLock(NetworkListLock, fWrite, LockState);
}
}
VOID
BrdgGpoReleaseNetworkListLock(
IN PNDIS_RW_LOCK NetworkListLock,
IN OUT PLOCK_STATE LockState
)
/*++
Routine Description:
Releases the NetworkList read-write lock. We support a NULL Lock
as it allows us to acquire for write and then call functions that
need the lock for read without locking up the system (by
supplying NULL for the lock).
This can be called at IRQL <= DISPATCH_LEVEL
Arguments:
NetworkListLock - Read-Write Lock to be released.
LockState - Opaque value used by NDIS.
Return Value:
None.
--*/
{
if (NetworkListLock)
{
NdisReleaseReadWriteLock(NetworkListLock, LockState);
}
}
NTSTATUS
BrdgGpoAllocateAndInitializeNetwork(
IN OUT PBRDG_GPO_NETWORKS* Network,
IN PWCHAR Identifier,
IN PWCHAR NetworkName
)
/*++
Routine Description:
Allocates the memory needed for a Network structure from the NonPaged pool
and copies the data into the structure.
Must be called at IRQL <= APC_LEVEL.
Arguments:
Network - The structure to be allocated and initialized with the data.
Identifier - The AdapterID for this network structure.
NetworkName - The current network that we are on. This can be NULL if we
haven't determined a network yet.
Return Value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES
--*/
{
PBRDG_GPO_NETWORKS pNetwork;
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
*Network = NULL;
if (!BrdgGpoProcessingNotifications())
{
return STATUS_SHUTDOWN_IN_PROGRESS;
}
//
// Everything in this struct will be used at DISPATCH_LEVEL, so all of it is
// allocated from the NonPagedPool
//
pNetwork = ExAllocatePoolWithTag(NonPagedPool, sizeof(BRDG_GPO_NETWORKS), 'gdrB');
if (NULL != pNetwork)
{
PUNICODE_STRING pIdentifier = NULL;
PUNICODE_STRING pNetworkName = NULL;
LPWSTR lpszIdentifier = NULL;
LPWSTR lpszNetworkName = NULL;
pIdentifier = ExAllocatePoolWithTag(NonPagedPool, sizeof(UNICODE_STRING), 'gdrB');
if (pIdentifier)
{
pNetworkName = ExAllocatePoolWithTag(NonPagedPool, sizeof(UNICODE_STRING), 'gdrB');
if (pNetworkName)
{
lpszIdentifier = ExAllocatePoolWithTag(NonPagedPool, (wcslen(Identifier) + 1) * sizeof(WCHAR), 'gdrB');
if (lpszIdentifier)
{
RtlZeroMemory(lpszIdentifier, wcslen(Identifier) + 1);
wcscpy(lpszIdentifier, Identifier);
//
// A NULL Network name is valid, so we only allocate it if we are passed one.
//
if (NetworkName)
{
lpszNetworkName = ExAllocatePoolWithTag(NonPagedPool, (wcslen(NetworkName) + 1) * sizeof(WCHAR), 'gdrB');
if (lpszNetworkName)
{
RtlZeroMemory(lpszNetworkName, wcslen(NetworkName) + 1);
wcscpy(lpszNetworkName, NetworkName);
}
}
//
// This is a Logical AND operation:
// Either we have both or we have neither. We can't have one and not the other, if we do
// then we didn't succeed the last allocate.
//
if ((NetworkName && lpszNetworkName) || (!NetworkName && !lpszNetworkName))
{
RtlInitUnicodeString(pIdentifier, lpszIdentifier);
//
// This may be NULL, but that's fine, since it means we'll add it when it gets written.
//
RtlInitUnicodeString(pNetworkName, lpszNetworkName);
pNetwork->Identifier = pIdentifier;
pNetwork->NetworkName = pNetworkName;
pNetwork->ListEntry.Blink = NULL;
pNetwork->ListEntry.Flink = NULL;
*Network = pNetwork;
status = STATUS_SUCCESS;
}
}
}
}
if (!NT_SUCCESS(status))
{
if (lpszIdentifier)
{
ExFreePool(lpszIdentifier);
}
if (pIdentifier)
{
ExFreePool(pIdentifier);
}
if (pNetworkName)
{
ExFreePool(pNetworkName);
}
if (pNetwork)
{
ExFreePool(pNetwork);
}
}
}
return status;
}
VOID
BrdgGpoFreeNetworkAndData(
IN PBRDG_GPO_NETWORKS Network)
/*++
Routine Description:
This frees any data associated with a particular network.
This can be called IRQL <= DISPATCH_LEVEL.
Arguments:
Network - Structure containing an ID and Network name
for an adapter.
Return Value:
None.
--*/
{
//
// First free the data associated with this entry
//
if (Network->Identifier)
{
if (Network->Identifier->Buffer)
{
ExFreePool(Network->Identifier->Buffer);
}
ExFreePool(Network->Identifier);
}
if (Network->NetworkName)
{
if (Network->NetworkName->Buffer)
{
ExFreePool(Network->NetworkName->Buffer);
}
ExFreePool(Network->NetworkName);
}
//
// Now free the structure
//
ExFreePool(Network);
}
NTSTATUS
BrdgGpoEmptyNetworkList(
IN PLIST_ENTRY NetworkList,
IN PNDIS_RW_LOCK NetworkListLock)
/*++
Routine Description:
Empties the existing list and frees all the items.
Do not acquire the list lock before calling this function.
Arguments:
NetworkList - The list of current networks.
NetworkListLock - Ndis Read Write Lock for synchronizing changes to the list.
Return Value:
STATUS_SUCCESS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
LOCK_STATE LockState;
BrdgGpoAcquireNetworkListLock(NetworkListLock, TRUE /* Write-access */, &LockState);
//
// Loop through the list deleting the entries.
//
while (!IsListEmpty(NetworkList))
{
PBRDG_GPO_NETWORKS Network;
PLIST_ENTRY pListEntry;
pListEntry = RemoveHeadList(NetworkList);
Network = CONTAINING_RECORD(pListEntry, BRDG_GPO_NETWORKS, ListEntry);
BrdgGpoFreeNetworkAndData(Network);
}
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoInsertNetwork(
IN PLIST_ENTRY NetworkList,
IN PLIST_ENTRY Network,
IN PNDIS_RW_LOCK NetworkListLock)
/*++
Routine Description:
This routine is responsible for adding a new Network to the list.
If someone attempts to insert an existing item, an error is returned.
The caller is responsible for calling BrdgGpoUpdateNetworkName instead.
This routine can be called at IRQL <= DISPATCH_LEVEL.
Arguments:
NetworkList - The list of current networks.
Network - The new Network entry to add to the list.
NetworkListLock - Ndis Read Write Lock for synchronizing changes to the list.
Return Value:
STATUS_SHUTDOWN_IN_PROGRESS - We're busy shutting down, so the item was not added.
STATUS_INVALID_PARAMETER - One or more of the parameters was NULL.
STATUS_DUPLICATE_NAME - This entry already exists in the list.
STATUS_SUCCESS - We successfully added the entry to the list.
--*/
{
PBRDG_GPO_NETWORKS pNetwork = NULL;
PBRDG_GPO_NETWORKS NewNetwork = NULL;
LOCK_STATE LockState;
NTSTATUS status;
BOOLEAN ShuttingDown;
if (!NetworkList || !Network || !NetworkListLock)
{
return STATUS_INVALID_PARAMETER;
}
//
// Lock the list for update
//
BrdgGpoAcquireNetworkListLock(NetworkListLock, TRUE /* Write-access */, &LockState);
ShuttingDown = !BrdgGpoProcessingNotifications();
if (!ShuttingDown)
{
NewNetwork = CONTAINING_RECORD(Network, BRDG_GPO_NETWORKS, ListEntry);
//
// We do this to prevent us accidentally inserting a duplicate item. We grab the lock before so that
// we can't insert the same item twice.
//
status = BrdgGpoFindNetwork( NetworkList,
NewNetwork->Identifier,
NULL, // We have already grabbed the lock for Write access.
&pNetwork);
if (STATUS_NOT_FOUND == status)
{
InsertTailList(NetworkList, Network);
status = STATUS_SUCCESS;
}
else if (STATUS_SUCCESS == status)
{
status = STATUS_DUPLICATE_NAME;
}
}
else
{
status = STATUS_SHUTDOWN_IN_PROGRESS;
}
//
// Release the lock, we're done updating
//
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoDeleteNetwork(
IN PLIST_ENTRY NetworkList,
IN PUNICODE_STRING NetworkIdentifier,
IN PNDIS_RW_LOCK NetworkListLock)
/*++
Routine Description:
Deletes an existing network entry.
Arguments:
NetworkList - The list of current networks.
NetworkIdentifier - A unique identifier that identifies which Network entry to remove.
NetworkListLock - Ndis Read Write Lock for synchronizing changes to the list.
Return Value:
STATUS_NOT_FOUND - We couldn't find and entry matching the identifier.
STATUS_SUCCESS - We were able to remove the entry successfully.
*/
{
PBRDG_GPO_NETWORKS pNetwork = NULL;
LOCK_STATE LockState;
NTSTATUS status = STATUS_NOT_FOUND;
//
// Lock the list for update
//
BrdgGpoAcquireNetworkListLock(NetworkListLock, TRUE /* Write-access */, &LockState);
//
// Find the entry;
//
status = BrdgGpoFindNetwork( NetworkList,
NetworkIdentifier,
NULL, // We have already grabbed the lock for Write access.
&pNetwork);
if (NT_SUCCESS(status))
{
RemoveEntryList(&pNetwork->ListEntry);
BrdgGpoFreeNetworkAndData(pNetwork);
pNetwork = NULL;
status = STATUS_SUCCESS;
}
//
// Release the lock, we're done updating
//
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoFindNetwork(
IN PLIST_ENTRY NetworkList,
IN PUNICODE_STRING NetworkIdentifier,
IN PNDIS_RW_LOCK NetworkListLock,
OUT PBRDG_GPO_NETWORKS* Network
)
/*++
Routine Description:
Finds a particular Network in the list of networks.
Arguments:
NetworkList - The list of current networks.
NetworkIdentifier - A unique identifier that identifies which Network entry to remove.
NetworkListLock - Ndis Read Write Lock for synchronizing changes to the list.
Network - The item if found, NULL otherwise.
Return Value:
STATUS_NOT_FOUND - No entry matching the identifier could be found.
STATUS_SUCCESS - The entry was successfully found.
--*/
{
PLIST_ENTRY pListEntry;
LOCK_STATE LockState;
NTSTATUS status = STATUS_NOT_FOUND;
if (!NetworkIdentifier || !Network) // We can have a NULL list lock.
{
return STATUS_INVALID_PARAMETER;
}
//
// Set the value to NULL so it isn't accidentally used by someone who doesn't realise
// that they didn't really get a record back.
//
*Network = NULL;
if (IsListEmpty(NetworkList))
{
return STATUS_NOT_FOUND;
}
//
// Lock the list for read
//
BrdgGpoAcquireNetworkListLock(NetworkListLock, FALSE /* Read-only */, &LockState);
//
// Loop through the list looking for an entry with the same identifier
//
for (pListEntry = NetworkList->Flink; pListEntry != NetworkList; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NETWORKS CurrentNetwork;
CurrentNetwork = CONTAINING_RECORD(pListEntry, BRDG_GPO_NETWORKS, ListEntry);
//
// Compare this entries network to the network name that was passed in.
//
if ((NULL != CurrentNetwork->NetworkName->Buffer) &&
(0 == _wcsicmp(CurrentNetwork->Identifier->Buffer, NetworkIdentifier->Buffer)))
{
*Network = CurrentNetwork;
status = STATUS_SUCCESS;
break;
}
}
//
// Release the lock, we're done searching
//
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoMatchNetworkName(
IN PLIST_ENTRY NetworkList,
IN PUNICODE_STRING NetworkName,
IN PNDIS_RW_LOCK NetworkListLock
)
/*++
Routine Description:
Enumerates through the list looking for a match for the supplied Network Name.
This can be called on IRQL <= DISPATCH_LEVEL
Arguments:
NetworkList - The list through which to enumerate.
NetworkName - The name to look for.
NetworkListLock - The NDIS read-write lock for the list.
Return Value:
STATUS_NO_MATCH - No match could be found.
STATUS_SUCCESS - We found a matching Network Name.
--*/
{
PLIST_ENTRY pListEntry;
LOCK_STATE LockState;
NTSTATUS status = STATUS_NO_MATCH;
if (!NetworkList || !NetworkName || !NetworkListLock)
{
return STATUS_INVALID_PARAMETER;
}
//
// Lock the list for read
//
BrdgGpoAcquireNetworkListLock(NetworkListLock, FALSE /* Read */, &LockState);
if (!IsListEmpty(NetworkList))
{
//
// Loop through the list looking for an entry with the same NetworkName
//
for (pListEntry = NetworkList->Flink; pListEntry != NetworkList; pListEntry = pListEntry->Flink)
{
PBRDG_GPO_NETWORKS CurrentNetwork;
CurrentNetwork = CONTAINING_RECORD(pListEntry, BRDG_GPO_NETWORKS, ListEntry);
//
// The network name can be empty, so we don't want to compare if this is the case.
//
if ((NULL != CurrentNetwork->NetworkName->Buffer) &&
(NULL != NetworkName->Buffer) &&
(0 == _wcsicmp(CurrentNetwork->NetworkName->Buffer, NetworkName->Buffer)))
{
status = STATUS_SUCCESS;
break;
}
}
}
else
{
status = BRDG_STATUS_EMPTY_LIST;
}
//
// Release the lock, we're done searching
//
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}
NTSTATUS
BrdgGpoUpdateNetworkName(
IN PLIST_ENTRY NetworkList,
IN PUNICODE_STRING Identifier,
IN PWCHAR NetworkName,
IN PNDIS_RW_LOCK NetworkListLock
)
/*++
Routine Description:
Finds a particular Network in the list of networks.
Arguments:
NetworkList - The list of current networks.
Identifier - A unique identifier that identifies which Network entry to update.
NetworkName - The new network name for this identifier.
NetworkListLock - Ndis Read Write Lock for synchronizing changes to the list.
Return Value:
STATUS_NOT_FOUND - No entry matching the identifier could be found.
STATUS_SUCCESS - The entry was successfully found.
--*/
{
PBRDG_GPO_NETWORKS pNetwork;
NTSTATUS status = STATUS_SUCCESS;
LOCK_STATE LockState;
PUNICODE_STRING pNetworkName = NULL;
LPWSTR lpszNetworkName = NULL;
if (!NetworkList || !Identifier || !NetworkListLock)
{
return STATUS_INVALID_PARAMETER;
}
//
// Allocate space for the new network name from NonPagedPool
// (it will be accessed from DISPATCH_LEVEL
//
pNetworkName = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING));
if (pNetworkName)
{
RtlZeroMemory(pNetworkName, sizeof(UNICODE_STRING));
if (NetworkName)
{
lpszNetworkName = ExAllocatePoolWithTag(NonPagedPool, (wcslen(NetworkName) + 1) * sizeof(WCHAR), 'gdrB');
if (NULL == lpszNetworkName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
wcscpy(lpszNetworkName, NetworkName);
}
}
if (NT_SUCCESS(status))
{
RtlInitUnicodeString(pNetworkName, lpszNetworkName);
}
else
{
//
// We failed to allocate the actual string, so free the PUNICODE_STRING
// as well.
//
ExFreePool(pNetworkName);
return status;
}
}
else
{
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Lock the list for update (this will pend until all readers have released the lock).
//
BrdgGpoAcquireNetworkListLock(NetworkListLock, TRUE /* Write-access */, &LockState);
//
// We pass NULL as the RW-Lock here because we've already locked it and
// we don't want this entry going away while we're still busy with it
// (which could happen between a find and an update if we locked twice).
//
status = BrdgGpoFindNetwork(NetworkList, Identifier, NULL, &pNetwork);
if (NT_SUCCESS(status))
{
//
// We first free the current networkname associated with this networkid.
//
if (pNetwork->NetworkName->Buffer)
{
ExFreePool(pNetwork->NetworkName->Buffer);
}
ExFreePool(pNetwork->NetworkName);
//
// We do this even if we were passed a NULL network name (ie. the Value/Key has been deleted).
// Since this means we're probably not on the same network or we've gone to static address and,
// GPO wise, we're not on the same network.
//
pNetwork->NetworkName = pNetworkName;
}
else
{
ExFreePool(pNetworkName);
}
//
// We're done with the update, so we can release the lock.
//
BrdgGpoReleaseNetworkListLock(NetworkListLock, &LockState);
return status;
}