/*++ Copyright (c) 1991 Microsoft Corporation Module Name: ntinit.c Abstract: NT specific routines for loading and configuring the TCP/UDP driver. Author: Mike Massa (mikemas) Aug 13, 1993 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 08-13-93 created Notes: --*/ #include "precomp.h" #if !MILLEN #include #include #endif // !MILLEN #include "tdint.h" #include "addr.h" #include "tcp.h" #include "tcb.h" #include "udp.h" #include "raw.h" #include "tcpconn.h" #include "mdlpool.h" #include "pplasl.h" #include "tcprcv.h" #include "tcpsend.h" #include "tlcommon.h" #include "tcpcfg.h" #include "secfltr.h" #include "info.h" #if GPC #include #include #include "gpcifc.h" #include "ntddtc.h" GPC_HANDLE hGpcClient[GPC_CF_MAX] = {0}; ulong GpcCfCounts[GPC_CF_MAX] = {0}; GPC_EXPORTED_CALLS GpcEntries; GPC_CLIENT_FUNC_LIST GpcHandlers; ulong GPCcfInfo = 0; GPC_STATUS GPCcfInfoAddNotifyIpsec(GPC_CLIENT_HANDLE ClCtxt, GPC_HANDLE GpcHandle, PTC_INTERFACE_ID InterfaceInfo, ULONG CfInfoSize, PVOID CfInfo, PGPC_CLIENT_HANDLE pClInfoCxt); GPC_STATUS GPCcfInfoRemoveNotifyIpsec(GPC_CLIENT_HANDLE ClCtxt, GPC_CLIENT_HANDLE ClInfoCxt); GPC_STATUS GPCcfInfoAddNotifyQoS(GPC_CLIENT_HANDLE ClCtxt, GPC_HANDLE GpcHandle, PTC_INTERFACE_ID InterfaceInfo, ULONG CfInfoSize, PVOID CfInfo, PGPC_CLIENT_HANDLE pClInfoCxt); GPC_STATUS GPCcfInfoRemoveNotifyQoS(GPC_CLIENT_HANDLE ClCtxt, GPC_CLIENT_HANDLE ClInfoCxt); #endif ReservedPortListEntry *PortRangeList = NULL; VOID GetReservedPortList( NDIS_HANDLE ConfigHandle ); // // Global variables. // PDRIVER_OBJECT TCPDriverObject = NULL; PDEVICE_OBJECT TCPDeviceObject = NULL; PDEVICE_OBJECT UDPDeviceObject = NULL; PDEVICE_OBJECT RawIPDeviceObject = NULL; extern PDEVICE_OBJECT IPDeviceObject; TCPXSUM_ROUTINE tcpxsum_routine = tcpxsum; #if ACC PSECURITY_DESCRIPTOR TcpAdminSecurityDescriptor; extern uint AllowUserRawAccess; typedef ULONG SECURITY_INFORMATION; BOOLEAN IsRunningOnPersonal ( VOID ); #endif extern uint DisableLargeSendOffload; // //Place holder for Maximum duplicate acks we would like //to see before we do fast retransmit // extern uint MaxDupAcks; MM_SYSTEMSIZE systemSize; extern uint MaxHashTableSize; extern uint NumTcbTablePartitions; extern uint PerPartitionSize; extern uint LogPerPartitionSize; #define CACHE_LINE_SIZE 64 #define CACHE_ALIGN_MASK (~(CACHE_LINE_SIZE-1)) CTELock *pTWTCBTableLock; CTELock *pTCBTableLock; CTELock *pSynTCBTableLock; extern Queue *TWQueue; extern Queue *TWTCBTable; extern TCB **TCBTable; extern Queue *SYNTCBTable; extern PTIMER_WHEEL TimerWheel; PTIMER_WHEEL OrgTimerWheel; extern TCPConn **ConnTable; extern uint MaxConnBlocks; extern uint ConnPerBlock; extern uint GlobalMaxRcvWin; extern uint TcpHostOpts; extern uint TcpHostSendOpts; HANDLE TCPRegistrationHandle; HANDLE UDPRegistrationHandle; HANDLE IPRegistrationHandle; //SynAttackProtect=0 no syn flood attack protection //SynAttackProtect !0 syn flood attack protection //SynAttackProtect !0 syn flood attack protection+forced(non-dynamic) // delayed connect acceptance uint SynAttackProtect; // SYN-attack protection checks are made uint TCPMaxHalfOpen; //Max # of half-open connections allowed // before we dec. the syn-ack retries uint TCPMaxHalfOpenRetried; //Max # of half-open conn. that have // been retried at least 1 time uint TCPMaxHalfOpenRetriedLW; //Low-watermark of the above. When // go down to it, we revert to normal // # of retries for syn-acks uint TCPHalfOpen; //# of half-open connections uint TCPHalfOpenRetried; //# of half-open conn. that have been //retried at least once PDEVICE_OBJECT IPSECDeviceObject; PFILE_OBJECT IPSECFileObject; extern uint Time_Proc; extern HANDLE TcbPool; extern HANDLE TimewaitTcbPool; extern HANDLE SynTcbPool; extern void ArpUnload(PDRIVER_OBJECT); extern CTETimer TCBTimer[]; extern BOOLEAN fTCBTimerStopping; extern CTEBlockStruc TcpipUnloadBlock; HANDLE AddressChangeHandle; extern ulong DefaultTOSValue; extern ulong DisableUserTOSSetting; extern uint MaxSendSegments; // // External function prototypes // int tlinit( void ); NTSTATUS TCPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS TCPDispatchInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS IPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS IPDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS IPPostDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS GetRegMultiSZValue( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING ValueData ); PWCHAR EnumRegMultiSz( IN PWCHAR MszString, IN ULONG MszStringLength, IN ULONG StringIndex ); uint InitIsnGenerator(); #if !MILLEN extern ulong g_cRandIsnStore; #endif // !MILLEN #if MILLEN extern VOID InitializeWDebDebug(); #endif // MILLEN // // Local funcion prototypes // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); void * TLRegisterProtocol( uchar Protocol, void *RcvHandler, void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler, void *PnPHandler, void *ElistHandler ); IP_STATUS TLGetIPInfo( IPInfo * Buffer, int Size ); uchar TCPGetConfigInfo( void ); NTSTATUS TCPInitializeParameter( HANDLE KeyHandle, PWCHAR ValueName, PULONG Value ); #if !MILLEN NTSTATUS IpsecInitialize( void ); NTSTATUS IpsecDeinitialize( void ); #endif #if !MILLEN #ifdef i386 NTSTATUS TCPSetChecksumRoutine( VOID ); #endif #endif // !MILLEN uint EnumSecurityFilterValue( PNDIS_STRING FilterList, ulong Index, ulong * FilterValue ); VOID TCPAcdBind(); #ifdef ACC typedef ULONG SECURITY_INFORMATION; NTSTATUS TcpBuildDeviceAcl( OUT PACL * DeviceAcl ); NTSTATUS TcpCreateAdminSecurityDescriptor( VOID ); NTSTATUS AddNetConfigOpsAce(IN PACL Dacl, OUT PACL * DeviceAcl ); NTSTATUS CreateDeviceDriverSecurityDescriptor(PVOID DeviceOrDriverObject ); #endif // ACC #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(INIT, TLRegisterProtocol) #pragma alloc_text(INIT, TLGetIPInfo) #pragma alloc_text(INIT, TCPGetConfigInfo) #pragma alloc_text(INIT, TCPInitializeParameter) #if !MILLEN #pragma alloc_text(INIT, IpsecInitialize) #endif #if !MILLEN #ifdef i386 #pragma alloc_text(INIT, TCPSetChecksumRoutine) #endif #endif // !MILLEN #pragma alloc_text(PAGE, EnumSecurityFilterValue) #pragma alloc_text(INIT, TCPAcdBind) #ifdef ACC #pragma alloc_text(INIT, TcpBuildDeviceAcl) #pragma alloc_text(INIT, TcpCreateAdminSecurityDescriptor) #pragma alloc_text(INIT, AddNetConfigOpsAce) #pragma alloc_text(INIT, CreateDeviceDriverSecurityDescriptor) #endif // ACC #endif // // Function definitions // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Initialization routine for the TCP/UDP driver. Arguments: DriverObject - Pointer to the TCP driver object created by the system. DeviceDescription - The name of TCP's node in the registry. Return Value: The final status from the initialization operation. --*/ { NTSTATUS status; UNICODE_STRING deviceName; UNICODE_STRING SymbolicDeviceName; USHORT i; int initStatus; DEBUGMSGINIT(); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+DriverEntry(%x, %x)\n"), DriverObject, RegistryPath)); TdiInitialize(); // // IP calls the security filter code, so initialize it first. // InitializeSecurityFilters(); // // Initialize IP // status = IPDriverEntry(DriverObject, RegistryPath); if (!NT_SUCCESS(status)) { DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("TCPIP: IP Initialization failure %x\n"), status)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-DriverEntry [%x]\n"), status)); return (status); } #if !MILLEN // // Initialize IPSEC // status = IpsecInitialize(); if (!NT_SUCCESS(status)) { DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("TCPIP: IPSEC Initialization failure %x\n"), status)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-DriverEntry [%x]\n"), status)); goto init_failed; } #endif // // Initialize TCP, UDP, and RawIP // TCPDriverObject = DriverObject; // // Create the device objects. IoCreateDevice zeroes the memory // occupied by the object. // RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME); RtlInitUnicodeString(&SymbolicDeviceName, DD_TCP_SYMBOLIC_DEVICE_NAME); status = IoCreateDevice( DriverObject, 0, &deviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &TCPDeviceObject ); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("DriverEntry: failure %x to create TCP device object %ws\n"), status, DD_TCP_DEVICE_NAME)); goto init_failed; } status = IoCreateSymbolicLink(&SymbolicDeviceName, &deviceName); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("DriverEntry: failure %x to create TCP symbolic device link %ws\n"), status, DD_TCP_SYMBOLIC_DEVICE_NAME)); goto init_failed; } RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME); status = IoCreateDevice( DriverObject, 0, &deviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &UDPDeviceObject ); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); TCPTRACE(( "TCP: Failed to create UDP device object, status %lx\n", status )); goto init_failed; } RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME); status = IoCreateDevice( DriverObject, 0, &deviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &RawIPDeviceObject ); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); TCPTRACE(( "TCP: Failed to create Raw IP device object, status %lx\n", status )); goto init_failed; } // // Initialize the driver object // DriverObject->DriverUnload = ArpUnload; DriverObject->FastIoDispatch = NULL; for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = TCPDispatch; } // // We special case Internal Device Controls because they are the // hot path for kernel-mode clients. // DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = TCPDispatchInternalDeviceControl; // // Intialize the device objects. // TCPDeviceObject->Flags |= DO_DIRECT_IO; UDPDeviceObject->Flags |= DO_DIRECT_IO; RawIPDeviceObject->Flags |= DO_DIRECT_IO; #ifdef ACC // Change the different devices and Objects to allow access to Network Configuration Operators if (!IsRunningOnPersonal()) { status = CreateDeviceDriverSecurityDescriptor(IPDeviceObject); if (!NT_SUCCESS(status)) { goto init_failed; } status = CreateDeviceDriverSecurityDescriptor(TCPDeviceObject); if (!NT_SUCCESS(status)) { goto init_failed; } status = CreateDeviceDriverSecurityDescriptor(IPSECDeviceObject); if (!NT_SUCCESS(status)) { goto init_failed; } } // // Create the security descriptor used for raw socket access checks. // status = TcpCreateAdminSecurityDescriptor(); if (!NT_SUCCESS(status)) { goto init_failed; } #endif // ACC #if !MILLEN #ifdef i386 // // Set the checksum routine pointer according to the processor available // TCPSetChecksumRoutine(); #endif #endif // !MILLEN // // Finally, initialize the stack. // initStatus = tlinit(); if (initStatus == TRUE) { // // Get the automatic connection driver // entry points. // TCPAcdBind(); RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME); (void)TdiRegisterDeviceObject(&deviceName, &TCPRegistrationHandle); RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME); (void)TdiRegisterDeviceObject(&deviceName, &UDPRegistrationHandle); RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME); (void)TdiRegisterDeviceObject(&deviceName, &IPRegistrationHandle); #if GPC status = GpcInitialize(&GpcEntries); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"GpcInitialize Failed! Status: 0x%x\n", status)); //return status; } else { // // Need to register as GPC client. Try it now. // we register clients for QOS and IPSEC // memset(&GpcHandlers, 0, sizeof(GPC_CLIENT_FUNC_LIST)); GpcHandlers.ClAddCfInfoNotifyHandler = GPCcfInfoAddNotifyQoS; GpcHandlers.ClRemoveCfInfoNotifyHandler = GPCcfInfoRemoveNotifyQoS; status = GpcEntries.GpcRegisterClientHandler( GPC_CF_QOS, // classification family 0, // flags 1, // default max priority &GpcHandlers, // client notification vector - no notifications to TCPIP required 0, // client context, not needed &hGpcClient[GPC_CF_QOS]); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"GPC registerclient QOS status %x hGpcClient %p\n", status, hGpcClient[GPC_CF_QOS])); hGpcClient[GPC_CF_QOS] = NULL; } GpcHandlers.ClAddCfInfoNotifyHandler = GPCcfInfoAddNotifyIpsec; GpcHandlers.ClRemoveCfInfoNotifyHandler = GPCcfInfoRemoveNotifyIpsec; status = GpcEntries.GpcRegisterClientHandler( GPC_CF_IPSEC, // classification family 0, // flags GPC_PRIORITY_IPSEC, // default max priority &GpcHandlers, // client notification vector - no notifications to TCPIP required 0, // client context, not needed &hGpcClient[GPC_CF_IPSEC]); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"GPC registerclient IPSEC status %x hGpcClient %p\n", status, hGpcClient[GPC_CF_IPSEC])); hGpcClient[GPC_CF_IPSEC] = NULL; } } #endif if (InitIsnGenerator() == FALSE) { DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("InitIsnGenerator failure. TCP/IP failing to start.\n"))); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-DriverEntry [%x]\n"), status)); return (STATUS_UNSUCCESSFUL); } // do the ndis register protocol now after all the intialization // is complete, o/w we get bindadapter even before we r fully // initialized. status = IPPostDriverEntry(DriverObject, RegistryPath); if (!NT_SUCCESS(status)) { DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("TCPIP: IP post-init failure %x\n"), status)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-DriverEntry [%x]\n"), status)); return (status); } // Millennium TCPIP has debugger extensions built in! #if MILLEN InitializeWDebDebug(); #endif // MILLEN #if TRACE_EVENT // // Register with WMI for Enable/Disable Notification // of Trace Logging // TCPCPHandlerRoutine = NULL; IoWMIRegistrationControl( TCPDeviceObject, WMIREG_ACTION_REGISTER | WMIREG_FLAG_TRACE_PROVIDER | WMIREG_NOTIFY_TDI_IO ); #endif // // Initialize the connection callback object. We do not treat its // failure as fatal, and continue to function even in case of failure. // We may log it in the future. // TcpInitCcb(); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-DriverEntry [SUCCESS]\n"))); return (STATUS_SUCCESS); } DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("TCPIP: TCP initialization failed, IP still available.\n"))); CTELogEvent( DriverObject, EVENT_TCPIP_TCP_INIT_FAILED, 1, 0, NULL, 0, NULL ); status = STATUS_UNSUCCESSFUL; init_failed: DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("TCPIP DriverEntry initialization failure!\n"))); // // IP has successfully started, but TCP & UDP failed. Set the // Dispatch routine to point to IP only, since the TCP and UDP // devices don't exist. // if (TCPDeviceObject != NULL) { IoDeleteDevice(TCPDeviceObject); } if (UDPDeviceObject != NULL) { IoDeleteDevice(UDPDeviceObject); } if (RawIPDeviceObject != NULL) { IoDeleteDevice(RawIPDeviceObject); } for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = IPDispatch; } #if !MILLEN if (IPSECFileObject) { IpsecDeinitialize(); } #endif return (status); } #if !MILLEN #ifdef i386 NTSTATUS TCPSetChecksumRoutine( VOID ) /*++ Routine Description: This routine sets the checksum routine function pointer to the appropriate routine based on the processor features available Arguments: None Return Value: STATUS_SUCCESS - if successful --*/ { NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; if (ExIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE)) { tcpxsum_routine = tcpxsum_xmmi; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip: Loading XMMI Prefetch Checksum\n")); Status = STATUS_SUCCESS; } return Status; } #endif #endif // !MILLEN IP_STATUS TLGetIPInfo( IPInfo * Buffer, int Size ) /*++ Routine Description: Returns information necessary for TCP to call into IP. Arguments: Buffer - A pointer to the IP information structure. Size - The size of Buffer. Return Value: The IP status of the operation. --*/ { return (IPGetInfo(Buffer, Size)); } void * TLRegisterProtocol( uchar Protocol, void *RcvHandler, void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler, void *PnPHandler, void *ElistHandler ) /*++ Routine Description: Calls the IP driver's protocol registration function. Arguments: Protocol - The protocol number to register. RcvHandler - Transport's packet receive handler. XmitHandler - Transport's packet transmit complete handler. StatusHandler - Transport's status update handler. RcvCmpltHandler - Transport's receive complete handler Return Value: A context value for the protocol to pass to IP when transmitting. --*/ { return (IPRegisterProtocol( Protocol, RcvHandler, XmitHandler, StatusHandler, RcvCmpltHandler, PnPHandler, ElistHandler)); } // // Interval in milliseconds between keepalive transmissions until a // response is received. // #define DEFAULT_KEEPALIVE_INTERVAL 1000 // // time to first keepalive transmission. 2 hours == 7,200,000 milliseconds // #define DEFAULT_KEEPALIVE_TIME 7200000 #define MIN_THRESHOLD_MAX_HO 1 #define MIN_THRESHOLD_MAX_HO_RETRIED 80 uchar TCPGetConfigInfo( void ) /*++ Routine Description: Initializes TCP global configuration parameters. Arguments: None. Return Value: Zero on failure, nonzero on success. --*/ { HANDLE keyHandle; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING UKeyName; ULONG maxConnectRexmits = 0; ULONG maxConnectResponseRexmits = 0; ULONG maxDataRexmits = 0; ULONG pptpmaxDataRexmits = 0; ULONG useRFC1122UrgentPointer = 0; ULONG tcp1323opts = 3; //turning off 1323 options by default ULONG SackOpts; ULONG i, j; DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+TCPGetConfigInfo()\n"))); // // Initialize to the defaults in case an error occurs somewhere. // KAInterval = DEFAULT_KEEPALIVE_INTERVAL; KeepAliveTime = DEFAULT_KEEPALIVE_TIME; PMTUDiscovery = TRUE; PMTUBHDetect = FALSE; DeadGWDetect = TRUE; DefaultRcvWin = 0; // Automagically pick a reasonable one. MaxConnections = DEFAULT_MAX_CONNECTIONS; maxConnectRexmits = MAX_CONNECT_REXMIT_CNT; maxConnectResponseRexmits = MAX_CONNECT_RESPONSE_REXMIT_CNT; pptpmaxDataRexmits = maxDataRexmits = MAX_REXMIT_CNT; BSDUrgent = TRUE; FinWait2TO = FIN_WAIT2_TO; NTWMaxConnectCount = NTW_MAX_CONNECT_COUNT; NTWMaxConnectTime = NTW_MAX_CONNECT_TIME; MaxUserPort = DEFAULT_MAX_USER_PORT; // Default number of duplicate acks MaxDupAcks = 2; SynAttackProtect = 0; //by default it is always off #if MILLEN TCPMaxHalfOpen = 100; TCPMaxHalfOpenRetried = 80; #else // MILLEN if (!MmIsThisAnNtAsSystem()) { TCPMaxHalfOpen = 100; TCPMaxHalfOpenRetried = 80; } else { TCPMaxHalfOpen = 500; TCPMaxHalfOpenRetried = 400; } #endif // !MILLEN SecurityFilteringEnabled = FALSE; #ifdef ACC AllowUserRawAccess = FALSE; #endif // // Read the TCP optional (hidden) registry parameters. // #if !MILLEN RtlInitUnicodeString( &UKeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters" ); #else // !MILLEN RtlInitUnicodeString( &UKeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\VxD\\MSTCP" ); #endif // MILLEN DEBUGMSG(DBG_INFO && DBG_INIT, (DTEXT("TCPGetConfigInfo: Opening key %ws\n"), UKeyName.Buffer)); memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES)); InitializeObjectAttributes( &objectAttributes, &UKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); status = ZwOpenKey( &keyHandle, KEY_READ, &objectAttributes ); DEBUGMSG(!NT_SUCCESS(status) && DBG_ERROR, (DTEXT("TCPGetConfigInfo: failed to open TCP registry configuration: %ws\n"), UKeyName.Buffer)); if (NT_SUCCESS(status)) { DEBUGMSG(DBG_INFO && DBG_INIT, (DTEXT("TCPGetConfigInfo: successfully opened registry to read.\n"))); #if !MILLEN TCPInitializeParameter( keyHandle, L"IsnStoreSize", &g_cRandIsnStore ); #endif // !MILLEN TCPInitializeParameter( keyHandle, L"KeepAliveInterval", (PULONG)&KAInterval ); TCPInitializeParameter( keyHandle, L"KeepAliveTime", (PULONG)&KeepAliveTime ); status = TCPInitializeParameter( keyHandle, L"EnablePMTUBHDetect", (PULONG)&PMTUBHDetect ); #if MILLEN // // Backwards compatibility. If 'EnablePMTUBHDetect' value does not exist, // then attempt to read legacy 'PMTUBlackHoleDetect'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"PMTUBlackHoleDetect", &PMTUBHDetect ); } #endif // MILLEN status = TCPInitializeParameter( keyHandle, L"TcpWindowSize", (PULONG)&DefaultRcvWin ); #if MILLEN // // Backwards compatibility. If 'TcpWindowSize' value does not exist, // then attempt to read legacy 'DefaultRcvWindow'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"DefaultRcvWindow", &DefaultRcvWin ); } #endif // MILLEN // We can not have negative size windows if ( (LONG) DefaultRcvWin < 0 ) { DefaultRcvWin = TCP_MAX_SCALED_WIN; } status = TCPInitializeParameter( keyHandle, L"TcpNumConnections", (PULONG)&MaxConnections ); #if MILLEN // // Backwards compatibility. If 'TcpNumConnections' value does not exist, // then attempt to read legacy 'MaxConnections'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"MaxConnections", &MaxConnections ); } #endif // MILLEN status = TCPInitializeParameter( keyHandle, L"TcpMaxConnectRetransmissions", &maxConnectRexmits ); #if MILLEN // // Backwards compatibility. If 'TcpMaxConnectRetransmissions' value does not exist, // then attempt to read legacty 'MaxConnectRetries'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"MaxConnectRetries", &maxConnectRexmits ); } #endif // MILLEN if (maxConnectRexmits > 255) { maxConnectRexmits = 255; } TCPInitializeParameter( keyHandle, L"TcpMaxConnectResponseRetransmissions", &maxConnectResponseRexmits ); if (maxConnectResponseRexmits > 255) { maxConnectResponseRexmits = 255; } status = TCPInitializeParameter( keyHandle, L"TcpMaxDataRetransmissions", &maxDataRexmits ); #if MILLEN // // Backwards compatibility. If 'TcpMaxDataRetransmissions' value does not exist, // then attempt to read legacy 'MaxDataRetries'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"MaxDataRetries", &maxDataRexmits ); } #endif // MILLEN if (maxDataRexmits > 255) { maxDataRexmits = 255; } // Limit the MaxDupAcks to 3 status = TCPInitializeParameter( keyHandle, L"TcpMaxDupAcks", (PULONG)&MaxDupAcks ); #if MILLEN // // Backwards compatibility. If 'TcpMaxDupAcks' value does not exist, // then attempt to read legacy 'MaxDupAcks'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"MaxDupAcks", &MaxDupAcks ); } #endif // MILLEN if (MaxDupAcks > 3) { MaxDupAcks = 3; } if (MaxDupAcks == 0) { MaxDupAcks = 1; } #if MILLEN MaxConnBlocks = 16; #else // MILLEN systemSize = MmQuerySystemSize(); if (MmIsThisAnNtAsSystem()) { if (systemSize == MmSmallSystem) { MaxConnBlocks = 128; } else if (systemSize == MmMediumSystem) { MaxConnBlocks = 256; } else { #if defined(_WIN64) MaxConnBlocks = 4096; #else MaxConnBlocks = 1024; #endif } } else { //for workstation, small system limit default number of connections to 4K. // medium system 8k // Large system 32k connections if (systemSize == MmSmallSystem) { MaxConnBlocks = 16; } else if (systemSize == MmMediumSystem) { MaxConnBlocks = 32; } else { MaxConnBlocks = 128; } } #endif // !MILLEN #if MILLEN NumTcbTablePartitions = 1; #else NumTcbTablePartitions = (KeNumberProcessors * KeNumberProcessors); #endif TCPInitializeParameter( keyHandle, L"NumTcbTablePartitions", (PULONG)&NumTcbTablePartitions ); if (NumTcbTablePartitions > (MAXIMUM_PROCESSORS * MAXIMUM_PROCESSORS)) { NumTcbTablePartitions = (MAXIMUM_PROCESSORS * MAXIMUM_PROCESSORS); } NumTcbTablePartitions = ComputeLargerOrEqualPowerOfTwo(NumTcbTablePartitions); // Default to 128 buckets per partition MaxHashTableSize = 128 * NumTcbTablePartitions; TCPInitializeParameter( keyHandle, L"MaxHashTableSize", (PULONG)&MaxHashTableSize ); if (MaxHashTableSize < 64) { MaxHashTableSize = 64; } else if (MaxHashTableSize > 0xffff) { MaxHashTableSize = 0x10000; } MaxHashTableSize = ComputeLargerOrEqualPowerOfTwo(MaxHashTableSize); if (MaxHashTableSize < NumTcbTablePartitions) { MaxHashTableSize = 128 * NumTcbTablePartitions; } ASSERT(IsPowerOfTwo(MaxHashTableSize)); //since hash table size is power of 2 and cache line size //is power of 2 and number of partion is even, //entries per partions will be power of 2 and multiple of //cache line size. PerPartitionSize = MaxHashTableSize / NumTcbTablePartitions; ASSERT(IsPowerOfTwo(PerPartitionSize)); LogPerPartitionSize = ComputeShiftForLargerOrEqualPowerOfTwo(PerPartitionSize); status = TCPInitializeParameter( keyHandle, L"Tcp1323Opts", &tcp1323opts ); if (status == STATUS_SUCCESS) { // Check if TS and/or WS options // are enabled. TcpHostOpts = TCP_FLAG_WS | TCP_FLAG_TS; if (!(tcp1323opts & TCP_FLAG_TS)) { TcpHostOpts &= ~TCP_FLAG_TS; } if (!(tcp1323opts & TCP_FLAG_WS)) { TcpHostOpts &= ~TCP_FLAG_WS; } TcpHostSendOpts = TcpHostOpts; } else { TcpHostSendOpts = 0; } TcpHostOpts |= TCP_FLAG_SACK; status = TCPInitializeParameter( keyHandle, L"SackOpts", &SackOpts ); if (status == STATUS_SUCCESS) { //Check if Sack option is enabled //If so, set it in global options variable KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Sackopts %x\n", SackOpts)); if (!SackOpts) { TcpHostOpts &= ~TCP_FLAG_SACK; } } TCPInitializeParameter( keyHandle, L"GlobalMaxTcpWindowSize", (PULONG)&GlobalMaxRcvWin ); TCPInitializeParameter( keyHandle, L"SynAttackProtect", (unsigned long *)&SynAttackProtect ); if (SynAttackProtect) { TCPInitializeParameter( keyHandle, L"TCPMaxHalfOpen", (PULONG)&TCPMaxHalfOpen ); if (TCPMaxHalfOpen < MIN_THRESHOLD_MAX_HO) { TCPMaxHalfOpen = MIN_THRESHOLD_MAX_HO; } TCPInitializeParameter( keyHandle, L"TCPMaxHalfOpenRetried", (PULONG)&TCPMaxHalfOpenRetried ); if ((TCPMaxHalfOpenRetried > TCPMaxHalfOpen) || (TCPMaxHalfOpenRetried < MIN_THRESHOLD_MAX_HO_RETRIED)) { TCPMaxHalfOpenRetried = MIN_THRESHOLD_MAX_HO_RETRIED; } TCPMaxHalfOpenRetriedLW = (TCPMaxHalfOpenRetried >> 1) + (TCPMaxHalfOpenRetried >> 2); } // // If we fail, then set to same value as maxDataRexmit so that the // max(pptpmaxDataRexmit,maxDataRexmit) is a decent value // Need this since TCPInitializeParameter no longer "initializes" // to a default value // if (TCPInitializeParameter(keyHandle, L"PPTPTcpMaxDataRetransmissions", &pptpmaxDataRexmits) != STATUS_SUCCESS) { pptpmaxDataRexmits = maxDataRexmits; } if (pptpmaxDataRexmits > 255) { pptpmaxDataRexmits = 255; } status = TCPInitializeParameter( keyHandle, L"TcpUseRFC1122UrgentPointer", &useRFC1122UrgentPointer ); #if MILLEN // // Backwards compatibility. If TcpUseRFC1122UrgentPointer does not exist, // then check for BSDUrgent. These values are logical opposites. // if (!NT_SUCCESS(status)) { ULONG tmpBsdUrgent = TRUE; status = TCPInitializeParameter( keyHandle, L"BSDUrgent", &tmpBsdUrgent); if (NT_SUCCESS(status)) { useRFC1122UrgentPointer = !tmpBsdUrgent; } } #endif if (useRFC1122UrgentPointer) { BSDUrgent = FALSE; } TCPInitializeParameter( keyHandle, L"TcpTimedWaitDelay", (PULONG)&FinWait2TO ); if (FinWait2TO > 300) { FinWait2TO = 300; } FinWait2TO = MS_TO_TICKS(FinWait2TO * 1000); NTWMaxConnectTime = MS_TO_TICKS(NTWMaxConnectTime * 1000); TCPInitializeParameter( keyHandle, L"MaxUserPort", (PULONG)&MaxUserPort ); if (MaxUserPort < 5000) { MaxUserPort = 5000; } if (MaxUserPort > 65534) { MaxUserPort = 65534; } GetReservedPortList(keyHandle); //Reserve ports if // // Read a few IP optional (hidden) registry parameters that TCP // cares about. // status = TCPInitializeParameter( keyHandle, L"EnablePMTUDiscovery", (PULONG)&PMTUDiscovery ); #if MILLEN // // Backwards compatibility. If 'EnablePMTUDiscovery' value does not exist, // then attempt to read legacy 'PMTUDiscovery'. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"PMTUDiscovery", &PMTUDiscovery ); } #endif // MILLEN TCPInitializeParameter( keyHandle, L"EnableDeadGWDetect", (PULONG)&DeadGWDetect ); TCPInitializeParameter( keyHandle, L"EnableSecurityFilters", (PULONG)&SecurityFilteringEnabled ); #ifdef ACC TCPInitializeParameter( keyHandle, L"AllowUserRawAccess", (PULONG)&AllowUserRawAccess ); #endif // ACC status = TCPInitializeParameter( keyHandle, L"DefaultTOSValue", &DefaultTOSValue ); #if MILLEN // // Backwards compatibility. Read 'DefaultTOS' if 'DefaultTOSValue' is // not present. // if (!NT_SUCCESS(status)) { TCPInitializeParameter( keyHandle, L"DefaultTOS", &DefaultTOSValue ); } #endif // MILLEN TCPInitializeParameter( keyHandle, L"DisableUserTOSSetting", &DisableUserTOSSetting ); TCPInitializeParameter( keyHandle, L"MaxSendSegments", (PULONG)&MaxSendSegments ); TCPInitializeParameter( keyHandle, L"DisableLargeSendOffload", (PULONG)&DisableLargeSendOffload ); ZwClose(keyHandle); } MaxConnectRexmitCount = maxConnectRexmits; MaxConnectResponseRexmitCount = maxConnectResponseRexmits; MaxConnectResponseRexmitCountTmp = MaxConnectResponseRexmitCount; // // Use the greater of the two, hence both values should be valid // MaxDataRexmitCount = (maxDataRexmits > pptpmaxDataRexmits ? maxDataRexmits : pptpmaxDataRexmits); TWTCBTable = CTEAllocMemBoot(MaxHashTableSize * sizeof(*TWTCBTable)); if (TWTCBTable == NULL) { ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate tw tcb table of size %x\n", MaxHashTableSize)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } for (i = 0; i < MaxHashTableSize; i++) { INITQ(&TWTCBTable[i]); } TCBTable = CTEAllocMemBoot(MaxHashTableSize * sizeof(*TCBTable)); if (TCBTable == NULL) { ExFreePool(TWTCBTable); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate tcb table of size %x\n", MaxHashTableSize)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } NdisZeroMemory(TCBTable, MaxHashTableSize * sizeof(*TCBTable)); SYNTCBTable = CTEAllocMemBoot(MaxHashTableSize * sizeof(*SYNTCBTable)); if (SYNTCBTable == NULL) { ExFreePool(TWTCBTable); ExFreePool(TCBTable); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate syn tcb table of size %x\n", MaxHashTableSize)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } for (i = 0; i < MaxHashTableSize; i++) { INITQ(&SYNTCBTable[i]); } pSynTCBTableLock = CTEAllocMemBoot(NumTcbTablePartitions * sizeof(CTELock)); if (pSynTCBTableLock == NULL) { ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(SYNTCBTable); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate twtcb lock table \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } pTWTCBTableLock = CTEAllocMemBoot(NumTcbTablePartitions * sizeof(CTELock)); if (pTWTCBTableLock == NULL) { ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(SYNTCBTable); ExFreePool(pSynTCBTableLock); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate twtcb lock table \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } pTCBTableLock = CTEAllocMemBoot(NumTcbTablePartitions * sizeof(CTELock)); if (pTCBTableLock == NULL) { ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(pTWTCBTableLock); ExFreePool(SYNTCBTable); ExFreePool(pSynTCBTableLock); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate tcb lock table \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } TWQueue = CTEAllocMemBoot(NumTcbTablePartitions * sizeof(Queue)); if (TWQueue == NULL) { ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(pTWTCBTableLock); ExFreePool(SYNTCBTable); ExFreePool(pSynTCBTableLock); ExFreePool(pTCBTableLock); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate Twqueue \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } TimerWheel = CTEAllocMemBoot(NumTcbTablePartitions * sizeof(TIMER_WHEEL) + CACHE_LINE_SIZE); if (TimerWheel == NULL) { ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(pTWTCBTableLock); ExFreePool(SYNTCBTable); ExFreePool(pSynTCBTableLock); ExFreePool(pTCBTableLock); ExFreePool(TWQueue); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate Twqueue \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } OrgTimerWheel = TimerWheel; TimerWheel = (PTIMER_WHEEL)(((ULONG_PTR) TimerWheel + CACHE_LINE_SIZE) & CACHE_ALIGN_MASK); for (i = 0; i < NumTcbTablePartitions; i++) { CTEInitLock(&pTCBTableLock[i]); CTEInitLock(&pTWTCBTableLock[i]); CTEInitLock(&pSynTCBTableLock[i]); INITQ(&TWQueue[i]) // Init the timer wheel CTEInitLock(&TimerWheel[i].tw_lock); #ifdef TIMER_TEST TimerWheel[i].tw_starttick = 0xfffff000; #else TimerWheel[i].tw_starttick = 0; #endif for(j = 0; j < TIMER_WHEEL_SIZE; j++) { INITQ(&TimerWheel[i].tw_timerslot[j]) } } if (MaxConnections != DEFAULT_MAX_CONNECTIONS) { //make it even MaxConnBlocks = ((MaxConnections >> 1) << 1); //allow minimum of 1k level 1 conn blocks. //this gives minimum of 256K connections capability if (MaxConnBlocks < 1024) { MaxConnBlocks = 1024; } } ConnTable = CTEAllocMemBoot(MaxConnBlocks * sizeof(TCPConnBlock *)); if (ConnTable == NULL) { ExFreePool(OrgTimerWheel); ExFreePool(TWQueue); ExFreePool(TCBTable); ExFreePool(TWTCBTable); ExFreePool(pTWTCBTableLock); ExFreePool(pTCBTableLock); ExFreePool(SYNTCBTable); ExFreePool(pSynTCBTableLock); ZwClose(keyHandle); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not allocate ConnTable \n")); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [failure]\n"))); return (0); } DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-TCPGetConfigInfo [SUCCESS]\n"))); return (1); } #define WORK_BUFFER_SIZE 256 extern NTSTATUS GetRegDWORDValue( HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData ); NTSTATUS TCPInitializeParameter( HANDLE KeyHandle, PWCHAR ValueName, PULONG Value ) /*++ Routine Description: Initializes a ULONG parameter from the registry or to a default parameter if accessing the registry value fails. Arguments: KeyHandle - An open handle to the registry key for the parameter. ValueName - The UNICODE name of the registry value to read. Value - The ULONG into which to put the data. DefaultValue - The default to assign if reading the registry fails. Return Value: None. --*/ { return (GetRegDWORDValue(KeyHandle, ValueName, Value)); } VOID GetReservedPortList( NDIS_HANDLE ConfigHandle ) { UNICODE_STRING PortList; PWCHAR nextRange; TDI_STATUS status; PortList.Buffer = CTEAllocMemBoot(WORK_BUFFER_SIZE * sizeof(WCHAR)); if (!PortList.Buffer) { return; } PortList.Buffer[0] = UNICODE_NULL; PortList.Length = 0; PortList.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR); PortRangeList = NULL; if (PortList.Buffer) { NdisZeroMemory(PortList.Buffer, WORK_BUFFER_SIZE * sizeof(WCHAR)); status = GetRegMultiSZValue( ConfigHandle, L"ReservedPorts", &PortList ); if (NT_SUCCESS(status)) { for (nextRange = PortList.Buffer; *nextRange != L'\0'; nextRange += wcslen(nextRange) + 1) { PWCHAR tmps = nextRange; USHORT upval = 0, loval = 0, tmpval = 0; BOOLEAN error = FALSE; ReservedPortListEntry *ListEntry; while (*tmps != L'\0') { if (*tmps == L'-') { tmps++; loval = tmpval; tmpval = 0; } if (*tmps >= L'0' && *tmps <= L'9') { tmpval = tmpval * 10 + (*tmps - L'0'); } else { error = TRUE; break; } tmps++; } upval = tmpval; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"loval %d upval %d\n", loval, upval)); if (!error && (loval > 0) && (upval > 0) && (loval <= upval) && (upval <= MaxUserPort) && (loval <= MaxUserPort)) { ListEntry = CTEAllocMemBoot(sizeof(ReservedPortListEntry)); if (ListEntry) { //Insert this range. //No need to take locks //since we are at initialization. ListEntry->UpperRange = upval; ListEntry->LowerRange = loval; ListEntry->next = PortRangeList; PortRangeList = ListEntry; } } } } CTEFreeMem(PortList.Buffer); } } TDI_STATUS GetSecurityFilterList( NDIS_HANDLE ConfigHandle, ulong Protocol, PNDIS_STRING FilterList ) { PWCHAR parameterName; TDI_STATUS status; if (Protocol == PROTOCOL_TCP) { parameterName = L"TcpAllowedPorts"; } else if (Protocol == PROTOCOL_UDP) { parameterName = L"UdpAllowedPorts"; } else { parameterName = L"RawIpAllowedProtocols"; } status = GetRegMultiSZValue( ConfigHandle, parameterName, FilterList ); if (!NT_SUCCESS(status)) { FilterList->Length = 0; } return (status); } uint EnumSecurityFilterValue( PNDIS_STRING FilterList, ulong Index, ulong * FilterValue ) { PWCHAR valueString; UNICODE_STRING unicodeString; NTSTATUS status; PAGED_CODE(); valueString = EnumRegMultiSz( FilterList->Buffer, FilterList->Length, Index ); if ((valueString == NULL) || (valueString[0] == UNICODE_NULL)) { return (FALSE); } RtlInitUnicodeString(&unicodeString, valueString); status = RtlUnicodeStringToInteger(&unicodeString, 0, FilterValue); if (!(NT_SUCCESS(status))) { TCPTRACE(("TCP: Invalid filter value %ws\n", valueString)); return (FALSE); } return (TRUE); } VOID TCPFreeupMemory() /*++ Routine Description: This routine frees up the memory at the TCP layer Arguments: NULL Return Value: None. --*/ { // // Walk various lists and free assoc blocks // // DG header list KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Freeing DG headers....\n")); MdpDestroyPool(DgHeaderPool); if (AddrObjTable) { CTEFreeMem(AddrObjTable); } PplDestroyPool(TcbPool); PplDestroyPool(SynTcbPool); #ifdef ACC if (TcpAdminSecurityDescriptor) { ExFreePool(TcpAdminSecurityDescriptor); } #endif } VOID TCPUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine cleans up the TCP layer. Arguments: DriverObject - Pointer to driver object created by the system. Return Value: None. When the function returns, the driver is unloaded. --*/ { NTSTATUS status; uint i; TcpUnInitCcb(); #if !MILLEN // // Deinitialize IPSEC first // status = IpsecDeinitialize(); #endif // // Shut down all timers/events // CTEInitBlockStrucEx(&TcpipUnloadBlock); fTCBTimerStopping = TRUE; for (i = 0; i < Time_Proc; i++) { if (!CTEStopTimer(&TCBTimer[i])) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not stop TCB timer - waiting on unload event\n")); #if !MILLEN if (KeReadStateEvent(&(TcpipUnloadBlock.cbs_event))) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Event is signaled...\n")); } #endif // !MILLEN (VOID) CTEBlock(&TcpipUnloadBlock); KeClearEvent(&TcpipUnloadBlock.cbs_event); } } #if GPC // if (hGpcClient[GPC_CF_QOS]) { status = GpcEntries.GpcDeregisterClientHandler(hGpcClient[GPC_CF_QOS]); hGpcClient[GPC_CF_QOS] = NULL; } if (hGpcClient[GPC_CF_IPSEC]) { status = GpcEntries.GpcDeregisterClientHandler(hGpcClient[GPC_CF_IPSEC]); hGpcClient[GPC_CF_IPSEC] = NULL; } KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Deregistering GPC\n")); status = GpcDeinitialize(&GpcEntries); #endif // // Clean up all residual memory // TCPFreeupMemory(); // // Deregister address notifn handler with TDI // (void)TdiDeregisterPnPHandlers(AddressChangeHandle); // // Deregister our devices with TDI // (void)TdiDeregisterDeviceObject(TCPRegistrationHandle); (void)TdiDeregisterDeviceObject(UDPRegistrationHandle); (void)TdiDeregisterDeviceObject(IPRegistrationHandle); #if TRACE_EVENT // // Deregister with WMI // IoWMIRegistrationControl(TCPDeviceObject, WMIREG_ACTION_DEREGISTER); #endif // // Delete devices // IoDeleteDevice(TCPDeviceObject); IoDeleteDevice(UDPDeviceObject); IoDeleteDevice(RawIPDeviceObject); } #if GPC GPC_STATUS GPCcfInfoAddNotifyIpsec(GPC_CLIENT_HANDLE ClCtxt, GPC_HANDLE GpcHandle, PTC_INTERFACE_ID InterfaceInfo, ULONG CfInfoSize, PVOID CfInfo, PGPC_CLIENT_HANDLE pClInfoCxt) { InterlockedIncrement((PLONG)&GPCcfInfo); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification %x\n", GPCcfInfo)); InterlockedIncrement((PLONG)&GpcCfCounts[GPC_CF_IPSEC]); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification IPSEC:%x\n", GpcCfCounts[GPC_CF_IPSEC])); return (STATUS_SUCCESS); } GPC_STATUS GPCcfInfoRemoveNotifyIpsec(GPC_CLIENT_HANDLE ClCtxt, GPC_CLIENT_HANDLE ClInfoCxt) { InterlockedDecrement((PLONG)&GPCcfInfo); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo remove notification %x\n", GPCcfInfo)); InterlockedDecrement((PLONG)&GpcCfCounts[GPC_CF_IPSEC]); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification IPSEC: %x\n", GpcCfCounts[GPC_CF_IPSEC])); return (STATUS_SUCCESS); } GPC_STATUS GPCcfInfoAddNotifyQoS(GPC_CLIENT_HANDLE ClCtxt, GPC_HANDLE GpcHandle, PTC_INTERFACE_ID InterfaceInfo, ULONG CfInfoSize, PVOID CfInfo, PGPC_CLIENT_HANDLE pClInfoCxt) { InterlockedIncrement((PLONG)&GPCcfInfo); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification %x\n", GPCcfInfo)); InterlockedIncrement((PLONG)&GpcCfCounts[GPC_CF_QOS]); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification QOS: %x\n", GpcCfCounts[GPC_CF_QOS])); return (STATUS_SUCCESS); } GPC_STATUS GPCcfInfoRemoveNotifyQoS(GPC_CLIENT_HANDLE ClCtxt, GPC_CLIENT_HANDLE ClInfoCxt) { InterlockedDecrement((PLONG)&GPCcfInfo); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo remove notification %x\n", GPCcfInfo)); InterlockedDecrement((PLONG)&GpcCfCounts[GPC_CF_QOS]); IF_TCPDBG(TCP_DEBUG_GPC) KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcpip - Cfinfo Add notification %x\n", GpcCfCounts[GPC_CF_QOS])); return (STATUS_SUCCESS); } #endif #if ACC NTSTATUS TcpBuildDeviceAcl( OUT PACL * DeviceAcl ) /*++ Routine Description: (Lifted from AFD - AfdBuildDeviceAcl) This routine builds an ACL which gives Administrators and LocalSystem principals full access. All other principals have no access. Arguments: DeviceAcl - Output pointer to the new ACL. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { PGENERIC_MAPPING GenericMapping; PSID AdminsSid; PSID SystemSid; PSID NetworkSid; ULONG AclLength; NTSTATUS Status; ACCESS_MASK AccessMask = GENERIC_ALL; PACL NewAcl; // // Enable access to all the globally defined SIDs // GenericMapping = IoGetFileObjectGenericMapping(); RtlMapGenericMask(&AccessMask, GenericMapping); AdminsSid = SeExports->SeAliasAdminsSid; SystemSid = SeExports->SeLocalSystemSid; NetworkSid = SeExports->SeNetworkServiceSid; AclLength = sizeof(ACL) + 3 * FIELD_OFFSET(ACCESS_ALLOWED_ACE,SidStart) + RtlLengthSid(AdminsSid) + RtlLengthSid(SystemSid) + RtlLengthSid(NetworkSid); NewAcl = CTEAllocMemBoot(AclLength); if (NewAcl == NULL) { return (STATUS_INSUFFICIENT_RESOURCES); } Status = RtlCreateAcl(NewAcl, AclLength, ACL_REVISION); if (!NT_SUCCESS(Status)) { CTEFreeMem(NewAcl); return (Status); } Status = RtlAddAccessAllowedAce( NewAcl, ACL_REVISION, AccessMask, AdminsSid ); ASSERT(NT_SUCCESS(Status)); Status = RtlAddAccessAllowedAce( NewAcl, ACL_REVISION, AccessMask, SystemSid ); ASSERT(NT_SUCCESS(Status)); // Add acl for NetworkSid! Status = RtlAddAccessAllowedAce( NewAcl, ACL_REVISION, AccessMask, NetworkSid ); ASSERT(NT_SUCCESS(Status)); *DeviceAcl = NewAcl; return (STATUS_SUCCESS); } // TcpBuildDeviceAcl NTSTATUS TcpCreateAdminSecurityDescriptor( VOID ) /*++ Routine Description: (Lifted from AFD - AfdCreateAdminSecurityDescriptor) This routine creates a security descriptor which gives access only to Administrtors and LocalSystem. This descriptor is used to access check raw endpoint opens and exclisive access to transport addresses. Arguments: None. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { PACL rawAcl = NULL; NTSTATUS status; BOOLEAN memoryAllocated = FALSE; PSECURITY_DESCRIPTOR tcpSecurityDescriptor; ULONG tcpSecurityDescriptorLength; CHAR buffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; PSECURITY_DESCRIPTOR localSecurityDescriptor = (PSECURITY_DESCRIPTOR) & buffer; PSECURITY_DESCRIPTOR localTcpAdminSecurityDescriptor; SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION; // // Get a pointer to the security descriptor from the TCP device object. // status = ObGetObjectSecurity( TCPDeviceObject, &tcpSecurityDescriptor, &memoryAllocated ); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "TCP: Unable to get security descriptor, error: %x\n", status )); ASSERT(memoryAllocated == FALSE); return (status); } // // Build a local security descriptor with an ACL giving only // administrators and system access. // status = TcpBuildDeviceAcl(&rawAcl); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TCP: Unable to create Raw ACL, error: %x\n", status)); goto error_exit; } (VOID) RtlCreateSecurityDescriptor( localSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); (VOID) RtlSetDaclSecurityDescriptor( localSecurityDescriptor, TRUE, rawAcl, FALSE ); // // Make a copy of the TCP descriptor. This copy will be the raw descriptor. // tcpSecurityDescriptorLength = RtlLengthSecurityDescriptor( tcpSecurityDescriptor ); localTcpAdminSecurityDescriptor = ExAllocatePool( PagedPool, tcpSecurityDescriptorLength ); if (localTcpAdminSecurityDescriptor == NULL) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TCP: couldn't allocate security descriptor\n")); goto error_exit; } RtlMoveMemory( localTcpAdminSecurityDescriptor, tcpSecurityDescriptor, tcpSecurityDescriptorLength ); TcpAdminSecurityDescriptor = localTcpAdminSecurityDescriptor; // // Now apply the local descriptor to the raw descriptor. // status = SeSetSecurityDescriptorInfo( NULL, &securityInformation, localSecurityDescriptor, &TcpAdminSecurityDescriptor, PagedPool, IoGetFileObjectGenericMapping() ); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TCP: SeSetSecurity failed, %lx\n", status)); ASSERT(TcpAdminSecurityDescriptor == localTcpAdminSecurityDescriptor); ExFreePool(TcpAdminSecurityDescriptor); TcpAdminSecurityDescriptor = NULL; goto error_exit; } if (TcpAdminSecurityDescriptor != localTcpAdminSecurityDescriptor) { ExFreePool(localTcpAdminSecurityDescriptor); } status = STATUS_SUCCESS; error_exit: ObReleaseObjectSecurity( tcpSecurityDescriptor, memoryAllocated ); if (rawAcl != NULL) { CTEFreeMem(rawAcl); } return (status); } #endif // ACC #if !MILLEN NTSTATUS IpsecInitialize( void ) /*++ Routine Description: Initialize IPSEC.SYS. Arguments: None. Return Value: None. --*/ { UNICODE_STRING IPSECDeviceName; IPSEC_SET_TCPIP_STATUS SetTcpipStatus; PIRP Irp; IO_STATUS_BLOCK StatusBlock; KEVENT Event; NTSTATUS status; IPSECDeviceObject = NULL; IPSECFileObject = NULL; RtlInitUnicodeString(&IPSECDeviceName, DD_IPSEC_DEVICE_NAME); // // Keep a reference to the IPSec driver so it won't unload before us. // status = IoGetDeviceObjectPointer( &IPSECDeviceName, FILE_ALL_ACCESS, &IPSECFileObject, &IPSECDeviceObject); if (!NT_SUCCESS(status)) { IPSECFileObject = NULL; return (status); } SetTcpipStatus.TcpipStatus = TRUE; SetTcpipStatus.TcpipFreeBuff = FreeIprBuff; SetTcpipStatus.TcpipAllocBuff = IPAllocBuff; SetTcpipStatus.TcpipGetInfo = IPGetInfo; SetTcpipStatus.TcpipNdisRequest = IPProxyNdisRequest; SetTcpipStatus.TcpipSetIPSecStatus = IPSetIPSecStatus; SetTcpipStatus.TcpipSetIPSecPtr = SetIPSecPtr; SetTcpipStatus.TcpipUnSetIPSecPtr = UnSetIPSecPtr; SetTcpipStatus.TcpipUnSetIPSecSendPtr = UnSetIPSecSendPtr; SetTcpipStatus.TcpipTCPXsum = tcpxsum; SetTcpipStatus.TcpipSendICMPErr = SendICMPErr; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest( IOCTL_IPSEC_SET_TCPIP_STATUS, IPSECDeviceObject, &SetTcpipStatus, sizeof(IPSEC_SET_TCPIP_STATUS), NULL, 0, FALSE, &Event, &StatusBlock); if (Irp) { status = IoCallDriver(IPSECDeviceObject, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL); status = StatusBlock.Status; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } return (status); } NTSTATUS IpsecDeinitialize( void ) /*++ Routine Description: Deinitialize IPSEC.SYS. Arguments: None. Return Value: None. --*/ { IPSEC_SET_TCPIP_STATUS SetTcpipStatus; PIRP Irp; IO_STATUS_BLOCK StatusBlock; KEVENT Event; NTSTATUS status; if (!IPSECFileObject) { return (STATUS_SUCCESS); } RtlZeroMemory(&SetTcpipStatus, sizeof(IPSEC_SET_TCPIP_STATUS)); KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest( IOCTL_IPSEC_SET_TCPIP_STATUS, IPSECDeviceObject, &SetTcpipStatus, sizeof(IPSEC_SET_TCPIP_STATUS), NULL, 0, FALSE, &Event, &StatusBlock); if (Irp) { status = IoCallDriver(IPSECDeviceObject, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL); status = StatusBlock.Status; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } ObDereferenceObject(IPSECFileObject); IPSECFileObject = NULL; return (status); } NTSTATUS AddNetConfigOpsAce(IN PACL Dacl, OUT PACL * DeviceAcl ) /*++ Routine Description: This routine builds an ACL which gives adds the Network Configuration Operators group to the principals allowed to control the driver. Arguments: Dacl - Existing DACL. DeviceAcl - Output pointer to the new ACL. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { PGENERIC_MAPPING GenericMapping; PSID NetworkSid = NULL; PSID NetConfigOpsSid = NULL; ULONG AclLength; NTSTATUS Status; ACCESS_MASK AccessMask = GENERIC_ALL; PACL NewAcl = NULL; ULONG SidSize; SID_IDENTIFIER_AUTHORITY sidAuth = SECURITY_NT_AUTHORITY; PACE_HEADER AceTemp; int i; // // Enable access to all the globally defined SIDs // GenericMapping = IoGetFileObjectGenericMapping(); RtlMapGenericMask(&AccessMask, GenericMapping); NetworkSid = SeExports->SeNetworkServiceSid; SidSize = RtlLengthRequiredSid(3); NetConfigOpsSid = (PSID)(CTEAllocMemBoot(SidSize)); if (NULL == NetConfigOpsSid) { return STATUS_INSUFFICIENT_RESOURCES; } Status = RtlInitializeSid(NetConfigOpsSid, &sidAuth, 2); if (Status != STATUS_SUCCESS) { goto clean_up; } *RtlSubAuthoritySid(NetConfigOpsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID; *RtlSubAuthoritySid(NetConfigOpsSid, 1) = DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS; AclLength = Dacl->AclSize; AclLength = Dacl->AclSize + 2*FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(NetConfigOpsSid) + RtlLengthSid(NetworkSid); NewAcl = CTEAllocMemBoot(AclLength); if (NewAcl == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto clean_up; } Status = RtlCreateAcl(NewAcl, AclLength, ACL_REVISION); if (!NT_SUCCESS(Status)) { goto clean_up; } for (i = 0; i < Dacl->AceCount; i++) { Status = RtlGetAce(Dacl, i, &AceTemp); if (NT_SUCCESS(Status)) { Status = RtlAddAce(NewAcl, ACL_REVISION, MAXULONG, AceTemp, AceTemp->AceSize); } if (!NT_SUCCESS(Status)) { goto clean_up; } } // Add Net Config Operators Ace Status = RtlAddAccessAllowedAce(NewAcl, ACL_REVISION, AccessMask, NetConfigOpsSid); if (!NT_SUCCESS(Status)) { goto clean_up; } // Add Network Services Ace Status = RtlAddAccessAllowedAce(NewAcl, ACL_REVISION, AccessMask, NetworkSid); if (!NT_SUCCESS(Status)) { goto clean_up; } *DeviceAcl = NewAcl; clean_up: if (NetConfigOpsSid) { CTEFreeMem(NetConfigOpsSid); } if (!NT_SUCCESS(Status) && NewAcl) { CTEFreeMem(NewAcl); } return (Status); } NTSTATUS CreateDeviceDriverSecurityDescriptor(PVOID DeviceOrDriverObject) /*++ Routine Description: Creates the SD responsible for giving access to different users. Arguments: DeviceOrDriverObject - Object to which to assign the Access Rights. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { NTSTATUS status; BOOLEAN memoryAllocated = FALSE; PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; PACL Dacl = NULL; BOOLEAN HasDacl = FALSE; BOOLEAN DaclDefaulted = FALSE; PACL NewAcl = NULL; // // Get a pointer to the security descriptor from the driver/device object. // status = ObGetObjectSecurity( DeviceOrDriverObject, &SecurityDescriptor, &memoryAllocated ); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "TCP: Unable to get security descriptor, error: %x\n", status )); ASSERT(memoryAllocated == FALSE); return (status); } status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &HasDacl, &Dacl, &DaclDefaulted); if (NT_SUCCESS(status) && HasDacl && Dacl != NULL) { status = AddNetConfigOpsAce(Dacl, &NewAcl); if (NT_SUCCESS(status)) { PSECURITY_DESCRIPTOR SecDesc = NULL; ULONG SecDescSize = 0; PACL AbsDacl = NULL; ULONG DaclSize = 0; PACL AbsSacl = NULL; ULONG ulSacl = 0; PSID Owner = NULL; ULONG OwnerSize = 0; PSID PrimaryGroup = NULL; ULONG PrimaryGroupSize = 0; BOOLEAN OwnerDefault = FALSE; BOOLEAN GroupDefault = FALSE; BOOLEAN HasSacl = FALSE; BOOLEAN SaclDefaulted = FALSE; SECURITY_INFORMATION secInfo = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; SecDescSize = sizeof(SecDesc) + NewAcl->AclSize; SecDesc = CTEAllocMemBoot(SecDescSize); if (SecDesc) { DaclSize = NewAcl->AclSize; AbsDacl = CTEAllocMemBoot(DaclSize); if (AbsDacl) { status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &Owner, &OwnerDefault); if (NT_SUCCESS(status)) { OwnerSize = RtlLengthSid(Owner); status = RtlGetGroupSecurityDescriptor(SecurityDescriptor, &PrimaryGroup, &GroupDefault); if (NT_SUCCESS(status)) { PrimaryGroupSize = RtlLengthSid(PrimaryGroup); status = RtlGetSaclSecurityDescriptor(SecurityDescriptor, &HasSacl, &AbsSacl, &SaclDefaulted); if (NT_SUCCESS(status)) { if (HasSacl) { ulSacl = AbsSacl->AclSize; secInfo |= SACL_SECURITY_INFORMATION; } status = RtlSelfRelativeToAbsoluteSD(SecurityDescriptor, SecDesc, &SecDescSize, AbsDacl, &DaclSize, AbsSacl, &ulSacl, Owner, &OwnerSize, PrimaryGroup, &PrimaryGroupSize); if (NT_SUCCESS(status)) { status = RtlSetDaclSecurityDescriptor(SecDesc, TRUE, NewAcl, FALSE); if (NT_SUCCESS(status)) { status = ObSetSecurityObjectByPointer(DeviceOrDriverObject, secInfo, SecDesc); } } } } } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } if (SecDesc) { // Since this is a Self-Relative security descriptor, freeing it also frees // Owner and PrimaryGroup. CTEFreeMem(SecDesc); } if (AbsDacl) { CTEFreeMem(AbsDacl); } } if (NewAcl) { CTEFreeMem(NewAcl); } } } else { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TCP: No Dacl: %x\n", status)); } ObReleaseObjectSecurity( SecurityDescriptor, memoryAllocated ); return (status); } // // Function: IsRunningOnPersonal // // Purpose: Determines whether running on Whistler Personal // // Returns: Returns true if running on Personal - FALSE otherwise BOOLEAN IsRunningOnPersonal( VOID ) { OSVERSIONINFOEXW OsVer = {0}; ULONGLONG ConditionMask = 0; BOOLEAN IsPersonal = TRUE; OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); OsVer.wSuiteMask = VER_SUITE_PERSONAL; OsVer.wProductType = VER_NT_WORKSTATION; VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL); VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_AND); if (RtlVerifyVersionInfo(&OsVer, VER_PRODUCT_TYPE | VER_SUITENAME, ConditionMask) == STATUS_REVISION_MISMATCH) { IsPersonal = FALSE; } return IsPersonal; } #endif