/*++ Copyright (c) 1992 Microsoft Corporation Module Name: registry.c Abstract: This module contains registry _access routines for the NT server service. Author: Chuck Lenzmeier (chuckl) 19-Mar-1992 Revision History: --*/ #include "srvsvcp.h" #include "ssreg.h" #include "srvconfg.h" #include #include // // Simple MIN and MAX macros. Watch out for side effects! // #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) #define MAX(a,b) ( ((a) < (b)) ? (b) : (a) ) #define MAX_INTEGER_STRING 32 #define MB * 1024 * 1024 #define INF 0xffffffff // // ( u, n ) // u is units to allocate for every n megabytes on a medium server. // #define CONFIG_TUPLE_SIZE 2 typedef struct { DWORD initworkitems[CONFIG_TUPLE_SIZE]; DWORD maxworkitems[CONFIG_TUPLE_SIZE]; DWORD rawworkitems[CONFIG_TUPLE_SIZE]; DWORD maxrawworkitems[CONFIG_TUPLE_SIZE]; DWORD maxpagedmemoryusage[CONFIG_TUPLE_SIZE]; DWORD maxnonpagedmemoryusage[CONFIG_TUPLE_SIZE]; } CONFIG_SERVER_TABLE; CONFIG_SERVER_TABLE MedSrvCfgTbl = { // // ** NOTE ** : If the second column is greater than 4, then // you will need to add a check to make sure the statistic // did not drop to zero. // // Units / MB // Parameter // --------- // /* initworkitems */ { 1 , 4 }, /* maxworkitems */ { 4 , 1 }, /* rawworkitems */ { 1 , 4 }, /* maxrawworkitems */ { 4 , 1 }, /* maxpagedmemoryusage */ { 1 , 1 }, /* maxnonpagedmemoryusage */ { 1 , 8 }, }; // // Minimum configuration system size is 8MB. Anything lower treated // as if 8 MB. // #define MIN_SYSTEM_SIZE 8 // // A medium server reaches its max at 32M. A small server at 16M. // #define MAX_SMALL_SIZE 16 #define MAX_MEDIUM_SIZE 32 // // Note that the user limit is always -1 (unlimited). Autodisconnect // always defaults to 15 minutes. // // // Forward declarations // NTSTATUS EnumerateStickyShare ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NET_API_STATUS FillStickyShareInfo( IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo, IN PSHARE_INFO_502 Shi502 ); NTSTATUS GetSdFromRegistry( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); BOOLEAN GetStickyShareInfo ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, OUT PUNICODE_STRING RemarkString, OUT PUNICODE_STRING PathString, OUT PSHARE_INFO_502 shi502, OUT PDWORD CacheState ); LONG LoadParameters ( PWCH Path ); LONG LoadSizeParameter ( VOID ); NTSTATUS RecreateStickyShare ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SaveSdToRegistry( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN PWSTR ShareName ); NTSTATUS SetSizeParameters ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SetStickyParameter ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); #define IsPersonal() IsSuiteVersion(VER_SUITE_PERSONAL) #define IsWebBlade() IsSuiteVersion(VER_SUITE_BLADE) #define IsEmbedded() IsSuiteVersion(VER_SUITE_EMBEDDEDNT) BOOL IsSuiteVersion(USHORT SuiteMask) { OSVERSIONINFOEX Osvi; DWORD TypeMask; DWORDLONG ConditionMask; memset(&Osvi, 0, sizeof(OSVERSIONINFOEX)); Osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); Osvi.wSuiteMask = SuiteMask; TypeMask = VER_SUITENAME; ConditionMask = 0; VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_OR); return(VerifyVersionInfo(&Osvi, TypeMask, ConditionMask)); } ULONG SsRtlQueryEnvironmentLength ( IN PVOID Environment ) { PWCH p; ULONG length; p = Environment; ASSERT( p != NULL ); // // The environment variable block consists of zero or more null // terminated ASCII strings. Each string is of the form: // // name=value // // where the null termination is after the value. // while ( *p ) { while ( *p ) { p++; } p++; } p++; length = (ULONG)((PCHAR)p - (PCHAR)Environment); // // Return accumulated length. // return length; } VOID SsAddParameterToRegistry ( PFIELD_DESCRIPTOR Field, PVOID Value ) { NTSTATUS status; PWCH valueName; DWORD valueType; LPBYTE valuePtr; DWORD valueDataLength; // // The value name is the parameter name and the value data is the // parameter value. // valueName = Field->FieldName; switch ( Field->FieldType ) { case BOOLEAN_FIELD: case DWORD_FIELD: valueType = REG_DWORD; valuePtr = Value; valueDataLength = sizeof(DWORD); break; case LPSTR_FIELD: valueType = REG_SZ; valuePtr = *(LPBYTE *)Value; if ( valuePtr != NULL ) { valueDataLength = SIZE_WSTR( (PWCH)valuePtr ); } else { valueDataLength = 0; } break; } // // Set the value into the Parameters key. // status = RtlWriteRegistryValue( RTL_REGISTRY_SERVICES, PARAMETERS_REGISTRY_PATH, valueName, valueType, valuePtr, valueDataLength ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddParameterToRegistry: SetValue failed: %lx; " "parameter %ws won't stick\n", status, valueName )); } } return; } // SsAddParameterToRegistry VOID SsAddShareToRegistry ( IN PSHARE_INFO_2 ShareInfo2, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, IN DWORD CacheState ) { NTSTATUS status; PWCH valueName; PVOID environment; UNICODE_STRING nameString; UNICODE_STRING valueString; WCHAR integerString[MAX_INTEGER_STRING + 1]; ULONG environmentLength; // // Build the value name and data strings. The value name is the // share name (netname), while the value data is share information // in REG_MULTI_SZ format. To build the value data, we use the // RTL environment routines. // valueName = ShareInfo2->shi2_netname; status = RtlCreateEnvironment( FALSE, &environment ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: CreateEnvironment failed: %lx; " "share %ws won't stick\n", status, valueName )); } goto exit1; } RtlInitUnicodeString( &nameString, PATH_VARIABLE_NAME ); RtlInitUnicodeString( &valueString, ShareInfo2->shi2_path ); status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } if ( ShareInfo2->shi2_remark != NULL ) { RtlInitUnicodeString( &nameString, REMARK_VARIABLE_NAME ); RtlInitUnicodeString( &valueString, ShareInfo2->shi2_remark ); status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } } RtlInitUnicodeString( &nameString, TYPE_VARIABLE_NAME ); valueString.Buffer = integerString; valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR); status = RtlIntegerToUnicodeString( ShareInfo2->shi2_type, 10, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; " "share %ws won't stick\n", status, valueName )); } goto exit2; } status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } RtlInitUnicodeString( &nameString, PERMISSIONS_VARIABLE_NAME ); valueString.Buffer = integerString; valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR); status = RtlIntegerToUnicodeString( ShareInfo2->shi2_permissions, 10, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; " "share %ws won't stick\n", status, valueName )); } goto exit2; } status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } RtlInitUnicodeString( &nameString, MAXUSES_VARIABLE_NAME ); valueString.Buffer = integerString; valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR); status = RtlIntegerToUnicodeString( ShareInfo2->shi2_max_uses, 10, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; " "share %ws won't stick\n", status, valueName )); } goto exit2; } status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } // // Set the CacheState // RtlInitUnicodeString( &nameString, CSC_VARIABLE_NAME ); valueString.Buffer = integerString; valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR); status = RtlIntegerToUnicodeString( CacheState, 10, &valueString ); if( !NT_SUCCESS( status ) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; " "share %ws won't stick\n", status, valueName )); } goto exit2; } status = RtlSetEnvironmentVariable( &environment, &nameString, &valueString ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; " "share %s won't stick\n", status, valueName )); } goto exit2; } // // Set the value into the Shares key. // environmentLength = SsRtlQueryEnvironmentLength( environment ); status = RtlWriteRegistryValue( RTL_REGISTRY_SERVICES, SHARES_REGISTRY_PATH, valueName, REG_MULTI_SZ, (LPBYTE)environment, environmentLength ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SetValue failed: %lx; share %ws " "won't stick\n", status, valueName )); } } // // Save the file security descriptor // if ( ARGUMENT_PRESENT( SecurityDescriptor ) ) { status = SaveSdToRegistry( SecurityDescriptor, valueName ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsAddShareToRegistry: SaveSd failed: %lx; share %ws\n" , status, valueName )); } } } exit2: RtlDestroyEnvironment( environment ); exit1: return; } // SsAddShareToRegistry NET_API_STATUS SsCheckRegistry ( VOID ) /*++ Routine Description: This function verifies that the keys used by the server exist. Arguments: None. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { NTSTATUS status; LPWSTR subStrings[1]; // // Verify the existence of the main server service key. If this // fails, the server service fails to start. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, SERVER_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = SERVER_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_FOUND, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: main key doesn't exist\n" )); } return ERROR_INVALID_PARAMETER; // !!! Need better error } // // Verify the existence of the Linkage subkey. If this fails, the // server service fails to start. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, LINKAGE_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = LINKAGE_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_FOUND, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Linkage subkey doesn't exist\n" )); } return ERROR_INVALID_PARAMETER; // !!! Need better error } // // If the Parameters subkey doesn't exist, create it. If it can't // be created, fail to start the server. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, PARAMETERS_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { status = RtlCreateRegistryKey( RTL_REGISTRY_SERVICES, PARAMETERS_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = PARAMETERS_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_CREATED, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Can't create Parameters subkey: " "%lx\n", status )); } return RtlNtStatusToDosError( status ); } } // // Create the key holding the default security descriptors governing server APIs. // Since we have compiled-in versions for these APIs, it is a non-fatal error // if we cannot create this key. But we log it anyway. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, SHARES_DEFAULT_SECURITY_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { status = RtlCreateRegistryKey( RTL_REGISTRY_SERVICES, SHARES_DEFAULT_SECURITY_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = SHARES_DEFAULT_SECURITY_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_CREATED, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Can't create DefaultSecurityRegistry subkey: " "%lx\n", status )); } } } { LONG error; HKEY handle; GUID Guid; // // Make sure the GUID_VARIABLE_NAME value is there and contains a valid GUID. // error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, FULL_PARAMETERS_REGISTRY_PATH, 0, KEY_ALL_ACCESS, &handle ); if( error == ERROR_SUCCESS ) { DWORD type; DWORD size = sizeof( Guid ); error = RegQueryValueEx( handle, GUID_VARIABLE_NAME, NULL, &type, (LPBYTE)&Guid, &size ); if( error != ERROR_SUCCESS || type != REG_BINARY || size != sizeof( Guid ) ) { RPC_STATUS RpcStatus; // // We could not read it, or it's not a valid UUID. // Blow it away and reset // RegDeleteValue( handle, GUID_VARIABLE_NAME ); RpcStatus = UuidCreate( &Guid ); if( RpcStatus == RPC_S_OK || RpcStatus == RPC_S_UUID_LOCAL_ONLY ) { error = RegSetValueEx( handle, GUID_VARIABLE_NAME, 0, REG_BINARY, (LPBYTE)&Guid, sizeof( Guid ) ); } SsNotifyRdrOfGuid( &Guid ); } RegCloseKey( handle ); } else { RtlZeroMemory( &Guid, sizeof( Guid ) ); } SsData.ServerInfo598.sv598_serverguid = Guid; } // // If the AutotunedParameters subkey doesn't exist, create it. If // it can't be created, fail to start the server. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, AUTOTUNED_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { status = RtlCreateRegistryKey( RTL_REGISTRY_SERVICES, AUTOTUNED_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = AUTOTUNED_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_CREATED, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Can't create AutotunedParameters " "subkey: %lx\n", status )); } return RtlNtStatusToDosError( status ); } } // // If the Shares subkey doesn't exist, create it. If it can't be // created, fail to start the server. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, SHARES_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { status = RtlCreateRegistryKey( RTL_REGISTRY_SERVICES, SHARES_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_CREATED, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Can't create Shares subkey: " "%lx\n", status )); } return RtlNtStatusToDosError( status ); } } // // If the Shares Security subkey doesn't exist, create it. If it // can't be created, fail to start the server. // status = RtlCheckRegistryKey( RTL_REGISTRY_SERVICES, SHARES_SECURITY_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { status = RtlCreateRegistryKey( RTL_REGISTRY_SERVICES, SHARES_SECURITY_REGISTRY_PATH ); if ( !NT_SUCCESS(status) ) { subStrings[0] = SHARES_SECURITY_REGISTRY_PATH; SsLogEvent( EVENT_SRV_KEY_NOT_CREATED, 1, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsCheckRegistry: Can't create Shares Security subkey: " "%lx\n", status )); } return RtlNtStatusToDosError( status ); } } // // All keys successfully checked. // return NO_ERROR; } // SsCheckRegistry NET_API_STATUS SsEnumerateStickyShares ( IN OUT PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo ) /*++ Routine Description: Reads the registry to find and return sticky shares. Arguments: ShareEnumInfo - points to a structure that contains the parameters to the NetShareEnumSticky call. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { NTSTATUS status; PRTL_QUERY_REGISTRY_TABLE queryTable; ShareEnumInfo->TotalBytesNeeded = 0; ShareEnumInfo->TotalEntries = 0; ShareEnumInfo->EntriesRead = 0; // // Initialize the reserve fields. This tells the callback routine, // how many times it has been called. // ShareEnumInfo->ShareEnumIndex = 0; ShareEnumInfo->StartOfFixedData = (PCHAR)ShareEnumInfo->OutputBuffer; ShareEnumInfo->EndOfVariableData = (PCHAR)ShareEnumInfo->OutputBuffer + ShareEnumInfo->OutputBufferLength; // // We need to align it since we deal with unicode strings. // ShareEnumInfo->EndOfVariableData = (PCHAR)((ULONG_PTR)ShareEnumInfo->EndOfVariableData & ~1); // // Ask the RTL to call us back for each value in the Shares key. // queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if ( queryTable != NULL ) { queryTable[0].QueryRoutine = EnumerateStickyShare; queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; queryTable[0].Name = NULL; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, SHARES_REGISTRY_PATH, queryTable, ShareEnumInfo, NULL ); MIDL_user_free( queryTable ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } if ( !NT_SUCCESS(status) ) { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsEnumerateStickyShares: RtlQueryRegistryValues " "failed: %lx\n", status )); } return RtlNtStatusToDosError( status ); } return NO_ERROR; } // SsEnumerateStickyShares NET_API_STATUS SsLoadConfigurationParameters ( VOID ) /*++ Routine Description: Reads the registry to get server configuration parameters. These server parameters must be set before the server FSP has been started. Arguments: None. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { LONG error; // // Get the basic Size parameter, then load autotuned parameters, // then load manually set parameters. This ordering allows manual // settings to override autotuning. // error = LoadSizeParameter( ); if ( error == NO_ERROR ) { error = LoadParameters( AUTOTUNED_REGISTRY_PATH ); if ( error == NO_ERROR ) { error = LoadParameters( PARAMETERS_REGISTRY_PATH ); } } // // The copy read to MDL read switchover must occur at or below the // SMB buffer size. // SsData.ServerInfo598.sv598_mdlreadswitchover = MIN( SsData.ServerInfo598.sv598_mdlreadswitchover, SsData.ServerInfo599.sv599_sizreqbuf); // // If they want to require security signatures, it implies enabling them // if( SsData.ServerInfo598.sv598_requiresecuritysignature ) { SsData.ServerInfo598.sv598_enablesecuritysignature = TRUE; } // // Override parameters that cannot be set on WinNT (vs. NTAS). // // The server itself also performs most of these overrides, in case // somebody figures out the FSCTL that changes parameters. We also // override in the service in order to keep the service's view // consistent with the server's. If you make any changes here, also // make them in srv\svcsrv.c. // // Embedded does its own parameter validation, so skip it here if( !IsEmbedded() ) { if ( SsData.ServerInfo598.sv598_producttype == NtProductWinNt ) { // // On WinNT, the maximum value of certain parameters is fixed at // build time. These include: concurrent users, SMB buffers, // and threads. // #define MINIMIZE(_param,_max) _param = MIN( _param, _max ); MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_WKSTA ); MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_WKSTA ); MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_WKSTA ); SsData.ServerInfo599.sv599_maxmpxct = DEF_MAXMPXCT_WKSTA; if( IsPersonal() ) { MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_PERSONAL ); } // // On WinNT, we do not cache closed RFCBs. // SsData.ServerInfo598.sv598_cachedopenlimit = 0; // // Sharing of redirected drives is not allowed on WinNT. // SsData.ServerInfo599.sv599_enablesharednetdrives = FALSE; } if( IsWebBlade() ) { MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_WEB_BLADE ); MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_WKSTA ); MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_WKSTA ); } } else { // If this is a Class 1 basic embedded device, keep our memory consumption lower too if( SsData.ServerInfo102.sv102_users == MAX_USERS_EMBEDDED ) { MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_EMBEDDED ); MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_EMBEDDED ); SsData.ServerInfo599.sv599_maxmpxct = DEF_MAXMPXCT_EMBEDDED; } } return error; } // SsLoadConfigurationParameters NET_API_STATUS SsRecreateStickyShares ( VOID ) /*++ Routine Description: Reads the registry to find and create sticky shares. Arguments: None. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { NTSTATUS status; PRTL_QUERY_REGISTRY_TABLE queryTable; ULONG IterationCount = 0; // // Ask the RTL to call us back for each value in the Shares key. // queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if ( queryTable != NULL ) { queryTable[0].QueryRoutine = RecreateStickyShare; queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; queryTable[0].Name = NULL; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, SHARES_REGISTRY_PATH, queryTable, &IterationCount, NULL ); MIDL_user_free( queryTable ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } if ( !NT_SUCCESS(status) ) { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsRecreateStickyShares: RtlQueryRegistryValues " "failed: %lx\n", status )); } return RtlNtStatusToDosError( status ); } return NO_ERROR; } // SsRecreateStickyShares NET_API_STATUS SsRemoveShareFromRegistry ( LPWSTR NetName ) { NET_API_STATUS error = NO_ERROR; NTSTATUS status; PWCH valueName; // // The value name is the share name. Remove that value from the // Shares key. // valueName = NetName; // // Delete the share security // status = RtlDeleteRegistryValue( RTL_REGISTRY_SERVICES, SHARES_SECURITY_REGISTRY_PATH, valueName ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsRemoveShareFromRegistry: Delete Security value failed: %lx; " "share %ws will return\n", status, valueName )); } } // // Delete the share // status = RtlDeleteRegistryValue( RTL_REGISTRY_SERVICES, SHARES_REGISTRY_PATH, valueName ); if ( !NT_SUCCESS(status) ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "SsRemoveShareFromRegistry: DeleteValue failed: %lx; " "share %ws will return\n", status, valueName )); } error = RtlNtStatusToDosError( status ); } return error; } // SsRemoveShareFromRegistry VOID BindToTransport ( IN LPWSTR TransportName ) { NET_API_STATUS error; SERVER_TRANSPORT_INFO_0 svti0; RtlZeroMemory( &svti0, sizeof( svti0 ) ); svti0.svti0_transportname = TransportName; svti0.svti0_transportaddress = SsData.SsServerTransportAddress; svti0.svti0_transportaddresslength = ComputeTransportAddressClippedLength( SsData.SsServerTransportAddress, SsData.SsServerTransportAddressLength ); // // Bind to the transport. // IF_DEBUG(INITIALIZATION) { SS_PRINT(( "BindToTransport: binding to transport %ws\n", TransportName )); } error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&svti0 ); if ( error != NO_ERROR ) { DWORD eventId; LPWSTR subStrings[2]; IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SsBindToTransports: failed to bind to %ws: " "%ld\n", TransportName, error )); } eventId = (error == ERROR_DUP_NAME || error == ERROR_INVALID_NETNAME ) ? EVENT_SRV_CANT_BIND_DUP_NAME : EVENT_SRV_CANT_BIND_TO_TRANSPORT; subStrings[0] = TransportName; SsLogEvent( eventId, 1, subStrings, error ); } } // BindToTransport NTSTATUS BindOptionalNameToTransport ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { SERVER_TRANSPORT_INFO_0 sti; UCHAR serverName[ MAX_PATH ]; UNICODE_STRING UnicodeName; NET_API_STATUS error; LPWSTR subStrings[2]; ULONG namelen; subStrings[0] = (LPWSTR)ValueData; subStrings[1] = OPTIONAL_NAMES_VALUE_NAME; if( ValueType != REG_SZ ) { // // Not a string! // SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); return STATUS_SUCCESS; } UnicodeName.Length = wcslen( (LPWSTR)ValueData ) * sizeof( WCHAR ); UnicodeName.MaximumLength = UnicodeName.Length + sizeof( WCHAR ); UnicodeName.Buffer = (LPWSTR)ValueData; error = ConvertStringToTransportAddress( &UnicodeName, serverName, &namelen ); if( error != NO_ERROR ) { // // Invalid server name! // SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, error ); return STATUS_SUCCESS; } RtlZeroMemory( &sti, sizeof(sti) ); sti.svti0_transportname = (LPWSTR)Context; sti.svti0_transportaddress = serverName; sti.svti0_transportaddresslength = namelen; error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&sti ); if ( error != NO_ERROR ) { // // Could not register the name! // subStrings[0] = (LPWSTR)ValueData; subStrings[1] = OPTIONAL_NAMES_VALUE_NAME; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, error ); } return STATUS_SUCCESS; } VOID BindOptionalNames ( IN PWSTR TransportName ) { RTL_QUERY_REGISTRY_TABLE queryTable[2]; NTSTATUS status; // // We need to iterate over the optional names and bind them to this // transport. // // // Now see if there any optional bindings we should perform // queryTable[0].QueryRoutine = BindOptionalNameToTransport; queryTable[0].Flags = 0; queryTable[0].Name = OPTIONAL_NAMES_VALUE_NAME; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; (void)RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, PARAMETERS_REGISTRY_PATH, queryTable, TransportName, NULL ); } // BindOptionalNames NTSTATUS EnumerateStickyShare ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) /*++ Routine Description: Callback routine for SsEnumerateStickyShare. Routine will get information on share and fill in the output buffer. Arguments: ValueName - Name of the share ValueType - Value type of the share name. ValueData - Data associated with the ValueName. Context - Pointer to our enum information structure. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { NET_API_STATUS error; SHARE_INFO_502 shi502; UNICODE_STRING pathString; UNICODE_STRING remarkString; PSRVSVC_SHARE_ENUM_INFO enumInfo = (PSRVSVC_SHARE_ENUM_INFO) Context; DWORD cacheState; ValueLength, EntryContext; remarkString.Buffer = NULL; pathString.Buffer = NULL; if ( GetStickyShareInfo( ValueName, ValueType, ValueData, &remarkString, &pathString, &shi502, &cacheState ) ) { // // Do the actual add of the share. // IF_DEBUG(REGISTRY) { SS_PRINT(( "EnumerateStickyShares: adding share %ws\n", ValueName )); } shi502.shi502_remark = remarkString.Buffer; shi502.shi502_path = pathString.Buffer; // // Skip until we have the right share to resume from // if ( (enumInfo->TotalEntries == 0) && (enumInfo->ShareEnumIndex < enumInfo->ResumeHandle) ) { enumInfo->ShareEnumIndex++; } else { enumInfo->TotalEntries++; error = FillStickyShareInfo( enumInfo, &shi502 ); if ( error != NO_ERROR ) { IF_DEBUG(REGISTRY) { SS_PRINT(( "EnumerateStickyShares: failed to add share " "%ws = %wZ: %ld\n", ValueName, &pathString, error )); } } else { enumInfo->EntriesRead++; enumInfo->ResumeHandle++; } } // // free buffers allocated by GetStickyShareInfo // if ( remarkString.Buffer != NULL ) { RtlFreeUnicodeString( &remarkString ); } if ( pathString.Buffer != NULL ) { RtlFreeUnicodeString( &pathString ); } if ( shi502.shi502_security_descriptor != NULL ) { MIDL_user_free( shi502.shi502_security_descriptor ); } } return STATUS_SUCCESS; } // EnumerateStickyShare NTSTATUS GetSdFromRegistry( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NTSTATUS status = STATUS_SUCCESS; PSECURITY_DESCRIPTOR fileSD = NULL; PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) Context; LPWSTR subStrings[1]; EntryContext, ValueName, ValueType; if ( ValueLength > 0 ) { fileSD = MIDL_user_allocate( ValueLength ); if ( fileSD == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlCopyMemory( fileSD, ValueData, ValueLength ); if ( !RtlValidSecurityDescriptor( fileSD ) ) { subStrings[0] = ValueName; SsLogEvent( EVENT_SRV_INVALID_SD, 1, subStrings, RtlNtStatusToDosError( status ) ); MIDL_user_free( fileSD ); fileSD = NULL; status = STATUS_INVALID_SECURITY_DESCR; } } } shi502->shi502_security_descriptor = fileSD; return(status); } // GetSdFromRegistry BOOLEAN GetStickyShareInfo ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, OUT PUNICODE_STRING RemarkString, OUT PUNICODE_STRING PathString, OUT PSHARE_INFO_502 shi502, OUT PDWORD CacheState ) /*++ Routine Description: Gets share information from the registry. Arguments: ValueName - Name of the share ValueType - Value type of the share name. ValueData - Data associated with the ValueName. RemarkString - Upon return, points to a unicode string containing the user remark for this share. PathString - Upon return, points to a unicode string containing the path for this share. shi502 - Upon return, points to a unicode string containing a SHARE_INFO_502 structure. Return Value: TRUE, if share information successfully retrieved. FALSE, otherwise. --*/ { NTSTATUS status; UNICODE_STRING variableNameString; WCHAR integerStringBuffer[35]; UNICODE_STRING unicodeString; LPWSTR subStrings[2]; PathString->Buffer = NULL; RemarkString->Buffer = NULL; shi502->shi502_security_descriptor = NULL; shi502->shi502_path = NULL; shi502->shi502_remark = NULL; shi502->shi502_reserved = 0; // // Because the NT server doesn't support share-level security, the // password is always NULL. // shi502->shi502_passwd = NULL; // // The value type must be REG_MULTI_SZ, and the value name must not // be null. // if ( (ValueType != REG_MULTI_SZ) || (wcslen(ValueName) == 0) ) { subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: skipping invalid value %ws\n", ValueName )); } goto errorexit; } // // The share name is the value name. The value data describes the // rest of the information about the share. // shi502->shi502_netname = ValueName; // // The REG_MULTI_SZ format is the same as that used for storing // environment variables. Find known share parameters in the data. // // Get the share path. It must be present. // RtlInitUnicodeString( &variableNameString, PATH_VARIABLE_NAME ); PathString->MaximumLength = 0; status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, PathString ); if ( status != STATUS_BUFFER_TOO_SMALL ) { // // The path is not specified. Ignore this share. // subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: No path; ignoring share.\n" )); } goto errorexit; } PathString->MaximumLength = (USHORT)(PathString->Length + sizeof(WCHAR)); PathString->Buffer = MIDL_user_allocate( PathString->MaximumLength ); if ( PathString->Buffer == NULL ) { // // No space for path. Ignore this share. // subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, ERROR_NOT_ENOUGH_MEMORY ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring " "share.\n" )); } goto errorexit; } status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, PathString ); if ( !NT_SUCCESS(status) ) { // // Huh? The second attempt failed. Ignore this share. // subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: Second query failed! Ignoring " "share.\n" )); } goto errorexit; } // // Get the remark. It may be omitted. // RtlInitUnicodeString( &variableNameString, REMARK_VARIABLE_NAME ); RemarkString->MaximumLength = 0; status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, RemarkString ); if ( status == STATUS_BUFFER_TOO_SMALL ) { RemarkString->MaximumLength = (USHORT)(RemarkString->Length + sizeof(WCHAR)); RemarkString->Buffer = MIDL_user_allocate( RemarkString->MaximumLength ); if ( RemarkString->Buffer == NULL ) { // // No space for remark. Ignore this share. // subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, ERROR_NOT_ENOUGH_MEMORY ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring " "share.\n" )); } goto errorexit; } status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, RemarkString ); if ( !NT_SUCCESS(status) ) { // // Huh? The second attempt failed. Ignore this share. // subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: Second query failed! " "Ignoring share.\n" )); } goto errorexit; } } // // Get the share type. It may be omitted. // RtlInitUnicodeString( &variableNameString, TYPE_VARIABLE_NAME ); unicodeString.Buffer = integerStringBuffer; unicodeString.MaximumLength = 35; status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, &unicodeString ); if ( !NT_SUCCESS(status) ) { shi502->shi502_type = STYPE_DISKTREE; } else { status = RtlUnicodeStringToInteger( &unicodeString, 0, &shi502->shi502_type ); if ( !NT_SUCCESS(status) ) { subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: " "%lx\n", status )); } goto errorexit; } } // // Get the share permissions. It may be omitted. // RtlInitUnicodeString( &variableNameString, PERMISSIONS_VARIABLE_NAME ); status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, &unicodeString ); if ( !NT_SUCCESS(status) ) { shi502->shi502_permissions = 0; } else { DWORD permissions; status = RtlUnicodeStringToInteger( &unicodeString, 0, &permissions ); if ( !NT_SUCCESS(status) ) { subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: " "%lx\n", status )); } goto errorexit; } shi502->shi502_permissions = permissions; } // // Get the maximum number of uses allowed. It may be omitted. // RtlInitUnicodeString( &variableNameString, MAXUSES_VARIABLE_NAME ); status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, &unicodeString ); if ( !NT_SUCCESS(status) ) { shi502->shi502_max_uses = (DWORD)SHI_USES_UNLIMITED; } else { status = RtlUnicodeStringToInteger( &unicodeString, 0, &shi502->shi502_max_uses ); if ( !NT_SUCCESS(status) ) { subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); goto errorexit; } } // // Get the Cacheing flags. It may be omitted. // RtlInitUnicodeString( &variableNameString, CSC_VARIABLE_NAME ); *CacheState = 0; status = RtlQueryEnvironmentVariable_U( ValueData, &variableNameString, &unicodeString ); if( NT_SUCCESS( status ) ) { ULONG value; status = RtlUnicodeStringToInteger( &unicodeString, 0, &value ); if( NT_SUCCESS( status ) ) { *CacheState = (value & CSC_MASK); } else { subStrings[0] = ValueName; subStrings[1] = SHARES_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, RtlNtStatusToDosError( status ) ); } } { // // Get the Share file security descriptor // RTL_QUERY_REGISTRY_TABLE shareQueryTable[2]; // // Fill up the query table // shareQueryTable[0].QueryRoutine = GetSdFromRegistry; shareQueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; shareQueryTable[0].Name = shi502->shi502_netname; shareQueryTable[0].EntryContext = NULL; shareQueryTable[0].DefaultType = REG_NONE; shareQueryTable[1].QueryRoutine = NULL; shareQueryTable[1].Flags = 0; shareQueryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, SHARES_SECURITY_REGISTRY_PATH, shareQueryTable, shi502, NULL ); if ( !NT_SUCCESS( status) && ( status != STATUS_OBJECT_NAME_NOT_FOUND ) ) { ASSERT(0); IF_DEBUG(REGISTRY) { SS_PRINT(( "GetStickyShareInfo: Get file SD: " "%lx\n", status )); } goto errorexit; } } return TRUE; errorexit: if ( RemarkString->Buffer != NULL ) { RtlFreeUnicodeString( RemarkString ); } if ( PathString->Buffer != NULL ) { RtlFreeUnicodeString( PathString ); } if ( shi502->shi502_security_descriptor != NULL ) { MIDL_user_free( shi502->shi502_security_descriptor ); } return FALSE; } // GetStickyShareInfo BOOLEAN SsGetDefaultSdFromRegistry ( IN PWCH ValueName, OUT PSECURITY_DESCRIPTOR *FileSD ) /*++ Routine Description: Reads 'ValueName' from the registry and gets the security descriptor stored there. Arguments: ValueName - The name of the registry value in the Parameters section holding the descriptor FileSD - points to the allocated SD if one was obtained Return Value: NTSTATUS - success/failure of the operation. --*/ { RTL_QUERY_REGISTRY_TABLE queryTable[2]; SHARE_INFO_502 shi502 = {0}; NTSTATUS status; *FileSD = NULL; queryTable[0].QueryRoutine = GetSdFromRegistry; queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; queryTable[0].Name = ValueName; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, SHARES_DEFAULT_SECURITY_REGISTRY_PATH, queryTable, &shi502, NULL ); if ( NT_SUCCESS( status) ) { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SsGetDefaultSdFromRegistry: using %ws SD from registry.\n", ValueName )); } *FileSD = shi502.shi502_security_descriptor; return TRUE; } return FALSE; } VOID SsWriteDefaultSdToRegistry ( IN PWCH ValueName, IN PSECURITY_DESCRIPTOR FileSD ) /*++ Routine Description: Stores FileSD to 'ValueName' in the registry Arguments: ValueName - The name of the registry value in the Parameters section holding the descriptor FileSD - points to the SD to write --*/ { ULONG fileSDLength; if ( RtlValidSecurityDescriptor( FileSD ) ) { fileSDLength = RtlLengthSecurityDescriptor( FileSD ); RtlWriteRegistryValue( RTL_REGISTRY_SERVICES, SHARES_DEFAULT_SECURITY_REGISTRY_PATH, ValueName, REG_BINARY, (LPBYTE)FileSD, fileSDLength ); } } LONG LoadParameters ( PWCH Path ) /*++ Routine Description: Reads the registry to get server parameters. Arguments: Path - PARAMETERS_REGISTRY_PATH or AUTOTUNED_REGISTRY_PATH Return Value: LONG - success/failure of the operation. --*/ { NTSTATUS status; PRTL_QUERY_REGISTRY_TABLE queryTable; ULONG numberOfBindings = 0; // // Ask the RTL to call us back for each value in the appropriate // key. // queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if ( queryTable != NULL ) { queryTable[0].QueryRoutine = SetStickyParameter; queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; queryTable[0].Name = NULL; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, Path, queryTable, Path, // Context for SetStickyParameter NULL ); MIDL_user_free( queryTable ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } if ( !NT_SUCCESS(status) ) { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "LoadParameters: RtlQueryRegistryValues failed: " "%lx\n", status )); } return RtlNtStatusToDosError( status ); } return NO_ERROR; } // LoadParameters LONG LoadSizeParameter ( VOID ) /*++ Routine Description: Reads the registry to get the basic server Size parameter. Arguments: None. Return Value: LONG - success/failure of the operation. --*/ { NTSTATUS status; PRTL_QUERY_REGISTRY_TABLE queryTable; ULONG numberOfBindings = 0; // // Ask the RTL to call us back if the Size parameter exists. // queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if ( queryTable != NULL ) { queryTable[0].QueryRoutine = SetSizeParameters; queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; queryTable[0].Name = SIZE_VALUE_NAME; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, PARAMETERS_REGISTRY_PATH, queryTable, NULL, NULL ); MIDL_user_free( queryTable ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } if ( !NT_SUCCESS(status) ) { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "LoadSizeParameter: RtlQueryRegistryValues failed: " "%lx\n", status )); } return RtlNtStatusToDosError( status ); } return NO_ERROR; } // LoadSizeParameter VOID PrintShareAnnounce ( LPVOID event ) { ULONG i; // // Announce ourselves and then wait for awhile. // If the event gets signaled, terminate the loop and this thread. // But don't do this forever, since the print subsystem may actually // get stuck // // // Do it for 15 minutes // for( i=0; i < 60; i++ ) { AnnounceServiceStatus( 1 ); if( WaitForSingleObject( (HANDLE)event, 15*1000 ) != WAIT_TIMEOUT ) { break; } } } NTSTATUS RecreateStickyShare ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PULONG IterationCount, IN PVOID EntryContext ) { NET_API_STATUS error; SHARE_INFO_502 shi502; SHARE_INFO shareInfo; UNICODE_STRING pathString; UNICODE_STRING remarkString; HANDLE threadHandle = NULL; HANDLE event = NULL; DWORD CacheState; SHARE_INFO shareInfoBuffer; SHARE_INFO_1005 si1005; ValueLength, EntryContext; remarkString.Buffer = NULL; pathString.Buffer = NULL; if ( GetStickyShareInfo( ValueName, ValueType, ValueData, &remarkString, &pathString, &shi502, &CacheState ) ) { // // Do the actual add of the share. // IF_DEBUG(INITIALIZATION) { SS_PRINT(( "RecreateStickyShares: adding share %ws\n", ValueName )); } shi502.shi502_remark = remarkString.Buffer; shi502.shi502_path = pathString.Buffer; shareInfo.ShareInfo502 = (LPSHARE_INFO_502_I)&shi502; if( shi502.shi502_type == STYPE_PRINTQ ) { // // A really big problem is that FAX printers can take aribitrarily long to // complete the eventual OpenPrinter() call which the server will make back // up to srvsvc. And if we don't announce ourselves in the interval, the // service controller will presume that we got stuck on startup. Since // NetrShareAdd() is synchronous, we need to get a different thread to // announce our service status until NetrShareAdd returns. So, start it // now. This is most unfortunate. event = CreateEvent( NULL, TRUE, FALSE, NULL ); if( event != NULL ) { DWORD threadId; threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)PrintShareAnnounce, (LPVOID)event, 0, &threadId ); if( threadHandle == NULL ) { CloseHandle( event ); event = NULL; } } } // // RecreateStickyShare is called during server initialization. The service // controller will presume that we're stuck if we don't update our status // with it often enough. So every 64 recreated shares we call back to it. // There's nothing magic about the 64 -- easy to check for, and not too often. // if( (shi502.shi502_type == STYPE_PRINTQ && threadHandle == NULL) || (++(*IterationCount) & 63 ) == 0 ) { AnnounceServiceStatus( 1 ); } error = NetrShareAdd( NULL, 502, &shareInfo, NULL ); if( event != NULL ) { // // We created an announcement thread, set the event telling it to terminate // SetEvent( event ); // // Wait for the thread to terminate // if( WaitForSingleObject( threadHandle, INFINITE ) == WAIT_FAILED ) { error = GetLastError(); } // // Close the handles // CloseHandle( event ); CloseHandle( threadHandle ); } if ( error != NO_ERROR ) { IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "RecreateStickyShares: failed to add share " "%ws = %wZ: %ld\n", ValueName, &pathString, error )); } } // // If this is a share which can be cached, set the caching flag in the server // si1005.shi1005_flags = CacheState; if( si1005.shi1005_flags ) { shareInfoBuffer.ShareInfo1005 = &si1005; NetrShareSetInfo( NULL, ValueName, 1005, &shareInfoBuffer, NULL ); } // // free buffers allocated by GetStickyShareInfo // if ( remarkString.Buffer != NULL ) { RtlFreeUnicodeString( &remarkString ); } if ( pathString.Buffer != NULL ) { RtlFreeUnicodeString( &pathString ); } if ( shi502.shi502_security_descriptor != NULL ) { MIDL_user_free( shi502.shi502_security_descriptor ); } } return NO_ERROR; } // RecreateStickyShare NTSTATUS SaveSdToRegistry( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN PWSTR ShareName ) /*++ Routine Description: Stores the share file security descriptor in the registry. Arguments: SecurityDescriptor - Points to a self-relative security descriptor describing the access rights for files under this share. ShareName - Points to a string containing the share name under which the SD is to be stored. Return Value: Status of the operation. --*/ { NTSTATUS status; // // Store the security descriptor // ULONG fileSDLength; if ( !RtlValidSecurityDescriptor( SecurityDescriptor ) ) { status = STATUS_INVALID_SECURITY_DESCR; } else { fileSDLength = RtlLengthSecurityDescriptor( SecurityDescriptor ); status = RtlWriteRegistryValue( RTL_REGISTRY_SERVICES, SHARES_SECURITY_REGISTRY_PATH, ShareName, REG_BINARY, (LPBYTE)SecurityDescriptor, fileSDLength ); } return status; } // SaveSdToRegistry NTSTATUS SetSizeParameters ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NT_PRODUCT_TYPE productType; DWORD size; LPWSTR subStrings[2]; ValueLength, Context, EntryContext; // // Get the product type. // if ( !RtlGetNtProductType( &productType ) ) { productType = NtProductWinNt; } SsData.ServerInfo598.sv598_producttype = productType; // // Make sure that we got called for the right value. // ASSERT( _wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0 ); // // The Size value must be a DWORD, and must be in the following // range: // // 0 -> use defaults // 1 -> small server (minimize memory usage) // 2 -> medium server (balance) // 3 -> large server (maximize connections) // if ( ValueType == REG_DWORD ) { ASSERT( ValueLength == sizeof(DWORD) ); size = *(LPDWORD)ValueData; } if ( (ValueType != REG_DWORD) || (size > 3) ) { subStrings[0] = ValueName; subStrings[1] = PARAMETERS_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetSizeParameters: skipping invalid value " "%ws\n", ValueName )); } return STATUS_SUCCESS; } SsData.ServerInfo598.sv598_serversize = size; // // Set appropriate fields based on the product type (Windows NT or // Advanced Server) and the selected Size. Note that a Size of 0 // doesn't change any of the defaults. // // Note that the user limit is always -1 (unlimited). Autodisconnect // always defaults to 15 minutes. // if ( size != 0 ) { SYSTEM_BASIC_INFORMATION basicInfo; NTSTATUS status; ULONG noOfMb; ULONG factor; ULONG asFactor; // // Get system memory size. // status = NtQuerySystemInformation( SystemBasicInformation, &basicInfo, sizeof( SYSTEM_BASIC_INFORMATION ), NULL ); if ( status != STATUS_SUCCESS ) { subStrings[0] = ValueName; subStrings[1] = PARAMETERS_REGISTRY_PATH; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetSizeParameters: NtQuerySystemInfo failed %x\n", status )); } return STATUS_SUCCESS; } // // Note that we first divide the page size by 512 in order to // allow for physical memory sizes above 2^32-1. With this // calculation, we can handle up to two terabytes of physical // memory. The calculation assumes that the page size is at // least 512, and is not very accurate if the page size is not // a power of 2 (very unlikely). // ASSERT( basicInfo.PageSize >= 512 ); noOfMb = (((basicInfo.PageSize / 512) * basicInfo.NumberOfPhysicalPages) + (1 MB / 512 - 1)) / (1 MB / 512); // // Minimum is 8 MB // noOfMb = MAX( MIN_SYSTEM_SIZE, noOfMb ); // // If we have NTAS, and we're set to maximize performance or we have // lots of memory -- then set the default work item buffer size to // a larger value. This value has been chosen to work well with our // implementation of TCP/IP, and shows itself to advantage when doing // directory emumerations with directories having lots of entries in them. // if( productType != NtProductWinNt && ((noOfMb >= 512) && (size == 3)) ) { SsData.ServerInfo599.sv599_sizreqbuf = DEF_LARGE_SIZREQBUF; } // // Set the maximum for the different sizes // if ( size == 1 ) { noOfMb = MIN( noOfMb, MAX_SMALL_SIZE ); } else if ( size == 2 ) { noOfMb = MIN( noOfMb, MAX_MEDIUM_SIZE ); } // // If small, assume the system size is half of the real one. // This should give us half the paramater values of a medium server. // If large, double it. Also set the free connection count. // if ( size == 1 ) { // // Small // factor = (noOfMb + 1) / 2; SsData.ServerInfo599.sv599_minfreeconnections = 2; SsData.ServerInfo599.sv599_maxfreeconnections = 2; } else if ( size == 2 ) { // // Balanced // factor = noOfMb; SsData.ServerInfo599.sv599_minfreeconnections = 2; SsData.ServerInfo599.sv599_maxfreeconnections = 4; } else { // // Large // factor = noOfMb * 2; // Scale up our big servers, this uses the NEW version of small/med/large we picked // for the server service (< 1 GB, 1-16 GB, >16 GB) if( noOfMb < 1024 ) { SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_SMALL; SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_SMALL; } else if( noOfMb < 16*1024 ) { // >= 1 GB memory SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_MEDIUM; SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_MEDIUM; } else { // >= 16 GB memory SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_LARGE; SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_LARGE; } } // // If this is an Advanced Server with at least 24M, some // parameter will need to be even bigger. // asFactor = 1; if ( (productType != NtProductWinNt) && (noOfMb >= 24) ) asFactor = 2; // // Now set the values for a medium server with this much memory. // SsData.ServerInfo599.sv599_maxworkitems = MedSrvCfgTbl.maxworkitems[0] * factor * asFactor / MedSrvCfgTbl.maxworkitems[1]; SsData.ServerInfo599.sv599_initworkitems = MedSrvCfgTbl.initworkitems[0] * factor * asFactor / MedSrvCfgTbl.initworkitems[1]; SsData.ServerInfo599.sv599_rawworkitems = MedSrvCfgTbl.rawworkitems[0] * factor / MedSrvCfgTbl.rawworkitems[1]; SsData.ServerInfo598.sv598_maxrawworkitems = MedSrvCfgTbl.maxrawworkitems[0] * factor * asFactor / MedSrvCfgTbl.maxrawworkitems[1]; SsData.ServerInfo599.sv599_maxworkitems = MIN( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS ); SsData.ServerInfo599.sv599_initworkitems = MIN( SsData.ServerInfo599.sv599_initworkitems, MAX_INITWORKITEMS/4 ); SsData.ServerInfo599.sv599_rawworkitems = MIN( SsData.ServerInfo599.sv599_rawworkitems, MAX_RAWWORKITEMS/4 ); SsData.ServerInfo598.sv598_maxrawworkitems = MIN( SsData.ServerInfo598.sv598_maxrawworkitems, MAX_MAXRAWWORKITEMS ); if ( (productType != NtProductWinNt) || (size == 3) ) { SsData.ServerInfo599.sv599_maxpagedmemoryusage = INF; SsData.ServerInfo599.sv599_maxnonpagedmemoryusage = INF; } else { SsData.ServerInfo599.sv599_maxpagedmemoryusage = MedSrvCfgTbl.maxpagedmemoryusage[0] * factor / MedSrvCfgTbl.maxpagedmemoryusage[1] MB; SsData.ServerInfo599.sv599_maxpagedmemoryusage = MAX( SsData.ServerInfo599.sv599_maxpagedmemoryusage, MIN_MAXPAGEDMEMORYUSAGE); SsData.ServerInfo599.sv599_maxnonpagedmemoryusage = MedSrvCfgTbl.maxnonpagedmemoryusage[0] * factor / MedSrvCfgTbl.maxnonpagedmemoryusage[1] MB; SsData.ServerInfo599.sv599_maxnonpagedmemoryusage = MAX( SsData.ServerInfo599.sv599_maxnonpagedmemoryusage, MIN_MAXNONPAGEDMEMORYUSAGE); } } return STATUS_SUCCESS; } // SetSizeParameters NTSTATUS SetStickyParameter ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NET_API_STATUS error; DWORD_PTR i; PFIELD_DESCRIPTOR foundField = NULL; LPWSTR subStrings[2]; ValueLength, EntryContext; // // Ignore several parameters, since they are handled elsewhere // if( (_wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, NULL_SESSION_SHARES_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, NULL_SESSION_PIPES_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, PIPES_NEED_LICENSE_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, ERROR_LOG_IGNORE_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, GUID_VARIABLE_NAME ) == 0) || (_wcsicmp( ValueName, OPTIONAL_NAMES_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, NO_REMAP_PIPES_VALUE_NAME ) == 0) || (_wcsicmp( ValueName, SERVICE_DLL_VALUE_NAME ) == 0) ) { return STATUS_SUCCESS; } // // Determine which field we need to set, based on the value // name. // // NOTE: For Daytona, disc and comment are now invalid registry names. // We use their more famous aliases autodisconnect and srvcomment // instead. If we get more of these cases, we should consider adding // a field to the FIELD_DESCRIPTOR structure that indicates whether // the names are should appear on the registry or not. Any change // here should also be made to SsSetField(). // if ( (_wcsicmp( ValueName, DISC_VALUE_NAME ) != 0) && (_wcsicmp( ValueName, COMMENT_VALUE_NAME ) != 0) ) { for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) { if ( _wcsicmp( ValueName, SsServerInfoFields[i].FieldName ) == 0 ) { foundField = &SsServerInfoFields[i]; break; } } } if ( foundField == NULL || foundField->Settable == NOT_SETTABLE ) { #ifdef DBG subStrings[0] = ValueName; subStrings[1] = Context; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetStickyParameter: ignoring %s \"%ws\"\n", (foundField == NULL ? "unknown value name" : "unsettable value"), ValueName )); } #endif return STATUS_SUCCESS; } switch ( foundField->FieldType ) { case BOOLEAN_FIELD: case DWORD_FIELD: if ( ValueType != REG_DWORD ) { subStrings[0] = ValueName; subStrings[1] = Context; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetStickyParameter: skipping invalid value " "%ws\n", ValueName )); } return STATUS_SUCCESS; } i = *(LPDWORD)ValueData; break; case LPSTR_FIELD: if ( ValueType != REG_SZ ) { subStrings[0] = ValueName; subStrings[1] = Context; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, NO_ERROR ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetStickyParameter: skipping invalid value " "%ws\n", ValueName )); } return STATUS_SUCCESS; } if (ValueLength != 0) { i = (DWORD_PTR)ValueData; } else { i = (DWORD_PTR)NULL; } break; } // // Set the field. // error = SsSetField( foundField, &i, FALSE, NULL ); #ifdef DBG if ( error != NO_ERROR ) { subStrings[0] = ValueName; subStrings[1] = Context; SsLogEvent( EVENT_SRV_INVALID_REGISTRY_VALUE, 2, subStrings, error ); IF_DEBUG(REGISTRY) { SS_PRINT(( "SetStickyParameter: error %ld ignored in setting " "parameter \"%ws\"n", error, ValueName )); } } #endif return STATUS_SUCCESS; } // SetStickyParameter