/*++ Copyright (c) 1987-1996 Microsoft Corporation Module Name: parse.c Abstract: Routine to parse the command line. Author: Ported from Lan Man 2.0 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 01-Aug-1991 (cliffv) Ported to NT. Converted to NT style. 09-May-1992 JohnRo Enable use of win32 registry. Use net config helpers for NetLogon. Fixed UNICODE bug handling debug file name. Use equates. --*/ // // Common include files. // #include "logonsrv.h" // Include files common to entire service #pragma hdrstop #include // USE_WIN32_CONFIG (if defined), etc. #include // PREFIX_ equates. // // Include files specific to this .c file // #include // strnicmp NET_API_STATUS NlParseOne( IN LPNET_CONFIG_HANDLE SectionHandle, IN BOOL GpSection, IN LPWSTR Keyword, IN ULONG DefaultValue, IN ULONG MinimumValue, IN ULONG MaximumValue, OUT PULONG Value ) /*++ Routine Description: Get a single numeric parameter from the netlogon section of the registry. Arguments: SectionHandle - Handle into the registry. GpSection - TRUE iff the section is the group policy section. Keyword - Name of the value to read. DefaultValue - Default value if parameter doesn't exist. MinimumValue - Minumin valid value. MaximumValue - Maximum valid value. Value - Returns the value parsed. Return Value: Status of the operation --*/ { NET_API_STATUS NetStatus; LPWSTR ValueT = NULL; // // Always return a reasonable value. // *Value = DefaultValue; // // Determine if the value is specified in the registry at all. // NetStatus = NetpGetConfigValue ( SectionHandle, Keyword, &ValueT ); if( ValueT != NULL ) { NetApiBufferFree( ValueT ); ValueT = NULL; } // // If the value wasn't specified, // use the default. // if ( NetStatus == NERR_CfgParamNotFound ) { *Value = DefaultValue; // // If the value was specifed, // get it from the registry. // } else { NetStatus = NetpGetConfigDword ( SectionHandle, Keyword, // keyword wanted DefaultValue, Value ); if (NetStatus == NO_ERROR) { if ( *Value > MaximumValue || *Value < MinimumValue ) { ULONG InvalidValue; LPWSTR MsgStrings[6]; // Each byte of the status code will transform into one character 0-F WCHAR InvalidValueString[sizeof(WCHAR) * (sizeof(InvalidValue) + 1)]; WCHAR MinimumValueString[sizeof(WCHAR) * (sizeof(MinimumValue) + 1)]; WCHAR MaximumValueString[sizeof(WCHAR) * (sizeof(MaximumValue) + 1)]; WCHAR AssignedValueString[sizeof(WCHAR) * (sizeof(*Value) + 1)]; InvalidValue = *Value; if ( *Value > MaximumValue ) { *Value = MaximumValue; } else if ( *Value < MinimumValue ) { *Value = MinimumValue; } swprintf( InvalidValueString, L"%lx", InvalidValue ); swprintf( MinimumValueString, L"%lx", MinimumValue ); swprintf( MaximumValueString, L"%lx", MaximumValue ); swprintf( AssignedValueString, L"%lx", *Value ); if ( GpSection ) { MsgStrings[0] = L"Group Policy"; } else { MsgStrings[0] = L"Parameters"; } MsgStrings[1] = InvalidValueString; MsgStrings[2] = Keyword; MsgStrings[3] = MinimumValueString; MsgStrings[4] = MaximumValueString; MsgStrings[5] = AssignedValueString; NlpWriteEventlog( NELOG_NetlogonInvalidDwordParameterValue, EVENTLOG_WARNING_TYPE, NULL, 0, MsgStrings, 6 ); } } else { return NetStatus; } } return NERR_Success; } NET_API_STATUS NlParseOnePath( IN LPNET_CONFIG_HANDLE SectionHandle, IN LPWSTR Keyword, IN LPWSTR DefaultValue1 OPTIONAL, OUT LPWSTR *Value ) /*++ Routine Description: Get a single path parameter from the netlogon section of the registry. Arguments: SectionHandle - Handle into the registry. Keyword - Name of the value to read. DefaultValue1 - Default value if parameter doesn't exist. If NULL, Value will be set to NULL to indicate there is no default. Value - Returns the value parsed. Must be freed using NetApiBufferFree. Return Value: Status of the operation --*/ { NET_API_STATUS NetStatus; WCHAR OutPathname[MAX_PATH+1]; WCHAR TempPathname[MAX_PATH*2+1]; LPWSTR ValueT = NULL; ULONG type; // // Get the configured parameter // *Value = NULL; NetStatus = NetpGetConfigValue ( SectionHandle, Keyword, // key wanted &ValueT ); // Must be freed by NetApiBufferFree(). if (NetStatus != NO_ERROR) { // // Handle the default // if (NetStatus == NERR_CfgParamNotFound) { // // If there is no default, // we're done. // if ( DefaultValue1 == NULL ) { *Value = NULL; NetStatus = NO_ERROR; goto Cleanup; } // // Build the default value. // ValueT = NetpAllocWStrFromWStr( DefaultValue1 ); if ( ValueT == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { goto Cleanup; } } NlAssert( ValueT != NULL ); // // Convert the configured sysvol path to a full pathname. // type = 0; // Let the API figure out the type. NetStatus = I_NetPathCanonicalize( NULL, ValueT, OutPathname, sizeof(OutPathname), NULL, &type, 0L ); if (NetStatus != NERR_Success ) { goto Cleanup; } if (type == ITYPE_PATH_ABSD) { NetpCopyTStrToWStr(TempPathname, OutPathname); } else if (type == ITYPE_PATH_RELND) { if ( !GetSystemWindowsDirectoryW( TempPathname, sizeof(TempPathname)/sizeof(WCHAR) ) ) { NetStatus = GetLastError(); goto Cleanup; } wcscat( TempPathname, L"\\" ); wcscat( TempPathname, OutPathname ); } else { NetStatus = NERR_BadComponent; goto Cleanup; } // // Return the pathname in an allocated buffer // *Value = NetpAllocWStrFromWStr( TempPathname ); if ( *Value == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } Cleanup: if ( ValueT != NULL ) { (VOID) NetApiBufferFree( ValueT ); } return NetStatus; } // // Table of numeric parameters to parse. // #define getoffset( _x ) offsetof( NETLOGON_PARAMETERS, _x ) struct { LPWSTR Keyword; ULONG DefaultValue; ULONG MinimumValue; ULONG MaximumValue; ULONG ValueOffset; BOOLEAN ChangesDnsRegistration; } ParseTable[] = { { NETLOGON_KEYWORD_PULSE, DEFAULT_PULSE, MIN_PULSE, MAX_PULSE, getoffset( Pulse ), FALSE }, { NETLOGON_KEYWORD_RANDOMIZE, DEFAULT_RANDOMIZE, MIN_RANDOMIZE, MAX_RANDOMIZE, getoffset( Randomize ), FALSE }, { NETLOGON_KEYWORD_PULSEMAXIMUM, DEFAULT_PULSEMAXIMUM, MIN_PULSEMAXIMUM, MAX_PULSEMAXIMUM, getoffset( PulseMaximum ), FALSE }, { NETLOGON_KEYWORD_PULSECONCURRENCY, DEFAULT_PULSECONCURRENCY, MIN_PULSECONCURRENCY, MAX_PULSECONCURRENCY, getoffset( PulseConcurrency ), FALSE }, { NETLOGON_KEYWORD_PULSETIMEOUT1, DEFAULT_PULSETIMEOUT1, MIN_PULSETIMEOUT1, MAX_PULSETIMEOUT1, getoffset( PulseTimeout1 ), FALSE }, { NETLOGON_KEYWORD_PULSETIMEOUT2, DEFAULT_PULSETIMEOUT2, MIN_PULSETIMEOUT2, MAX_PULSETIMEOUT2, getoffset( PulseTimeout2 ), FALSE }, { NETLOGON_KEYWORD_MAXIMUMMAILSLOTMESSAGES, DEFAULT_MAXIMUMMAILSLOTMESSAGES, MIN_MAXIMUMMAILSLOTMESSAGES, MAX_MAXIMUMMAILSLOTMESSAGES, getoffset( MaximumMailslotMessages ), FALSE }, { NETLOGON_KEYWORD_MAILSLOTMESSAGETIMEOUT, DEFAULT_MAILSLOTMESSAGETIMEOUT, MIN_MAILSLOTMESSAGETIMEOUT, MAX_MAILSLOTMESSAGETIMEOUT, getoffset( MailslotMessageTimeout ), FALSE }, { NETLOGON_KEYWORD_MAILSLOTDUPLICATETIMEOUT,DEFAULT_MAILSLOTDUPLICATETIMEOUT,MIN_MAILSLOTDUPLICATETIMEOUT,MAX_MAILSLOTDUPLICATETIMEOUT,getoffset( MailslotDuplicateTimeout ),FALSE }, { NETLOGON_KEYWORD_EXPECTEDDIALUPDELAY, DEFAULT_EXPECTEDDIALUPDELAY, MIN_EXPECTEDDIALUPDELAY, MAX_EXPECTEDDIALUPDELAY, getoffset( ExpectedDialupDelay ), FALSE }, { NETLOGON_KEYWORD_SCAVENGEINTERVAL, DEFAULT_SCAVENGEINTERVAL, MIN_SCAVENGEINTERVAL, MAX_SCAVENGEINTERVAL, getoffset( ScavengeInterval ), FALSE }, { NETLOGON_KEYWORD_MAXIMUMPASSWORDAGE, DEFAULT_MAXIMUMPASSWORDAGE, MIN_MAXIMUMPASSWORDAGE, MAX_MAXIMUMPASSWORDAGE, getoffset( MaximumPasswordAge ), FALSE }, { NETLOGON_KEYWORD_LDAPSRVPRIORITY, DEFAULT_LDAPSRVPRIORITY, MIN_LDAPSRVPRIORITY, MAX_LDAPSRVPRIORITY, getoffset( LdapSrvPriority ), TRUE }, { NETLOGON_KEYWORD_LDAPSRVWEIGHT, DEFAULT_LDAPSRVWEIGHT, MIN_LDAPSRVWEIGHT, MAX_LDAPSRVWEIGHT, getoffset( LdapSrvWeight ), TRUE }, { NETLOGON_KEYWORD_LDAPSRVPORT, DEFAULT_LDAPSRVPORT, MIN_LDAPSRVPORT, MAX_LDAPSRVPORT, getoffset( LdapSrvPort ), TRUE }, { NETLOGON_KEYWORD_LDAPGCSRVPORT, DEFAULT_LDAPGCSRVPORT, MIN_LDAPGCSRVPORT, MAX_LDAPGCSRVPORT, getoffset( LdapGcSrvPort ), TRUE }, { L"KdcSrvPort", DEFAULT_KDCSRVPORT, MIN_KDCSRVPORT, MAX_KDCSRVPORT, getoffset( KdcSrvPort ), TRUE }, { NETLOGON_KEYWORD_KERBISDDONEWITHJOIN, DEFAULT_KERBISDDONEWITHJOIN, MIN_KERBISDDONEWITHJOIN, MAX_KERBISDDONEWITHJOIN, getoffset( KerbIsDoneWithJoinDomainEntry),FALSE}, { NETLOGON_KEYWORD_DNSTTL, DEFAULT_DNSTTL, MIN_DNSTTL, MAX_DNSTTL, getoffset( DnsTtl ), TRUE }, { NETLOGON_KEYWORD_DNSREFRESHINTERVAL, DEFAULT_DNSREFRESHINTERVAL, MIN_DNSREFRESHINTERVAL, MAX_DNSREFRESHINTERVAL, getoffset( DnsRefreshInterval ), TRUE }, { L"CloseSiteTimeout", DEFAULT_CLOSESITETIMEOUT, MIN_CLOSESITETIMEOUT, MAX_CLOSESITETIMEOUT, getoffset( CloseSiteTimeout ), FALSE }, { L"SiteNameTimeout", DEFAULT_SITENAMETIMEOUT, MIN_SITENAMETIMEOUT, MAX_SITENAMETIMEOUT, getoffset( SiteNameTimeout ), FALSE }, { L"DuplicateEventlogTimeout", DEFAULT_DUPLICATEEVENTLOGTIMEOUT,MIN_DUPLICATEEVENTLOGTIMEOUT,MAX_DUPLICATEEVENTLOGTIMEOUT,getoffset( DuplicateEventlogTimeout ),FALSE }, { L"MaxConcurrentApi", DEFAULT_MAXCONCURRENTAPI, MIN_MAXCONCURRENTAPI, MAX_MAXCONCURRENTAPI, getoffset( MaxConcurrentApi ), FALSE }, { L"NegativeCachePeriod", DEFAULT_NEGATIVECACHEPERIOD, MIN_NEGATIVECACHEPERIOD, MAX_NEGATIVECACHEPERIOD, getoffset( NegativeCachePeriod ), FALSE }, { L"BackgroundRetryInitialPeriod", DEFAULT_BACKGROUNDRETRYINITIALPERIOD,MIN_BACKGROUNDRETRYINITIALPERIOD,MAX_BACKGROUNDRETRYINITIALPERIOD,getoffset( BackgroundRetryInitialPeriod ),FALSE }, { L"BackgroundRetryMaximumPeriod", DEFAULT_BACKGROUNDRETRYMAXIMUMPERIOD,MIN_BACKGROUNDRETRYMAXIMUMPERIOD,MAX_BACKGROUNDRETRYMAXIMUMPERIOD,getoffset( BackgroundRetryMaximumPeriod ),FALSE }, { L"BackgroundRetryQuitTime", DEFAULT_BACKGROUNDRETRYQUITTIME, MIN_BACKGROUNDRETRYQUITTIME, MAX_BACKGROUNDRETRYQUITTIME, getoffset( BackgroundRetryQuitTime ), FALSE }, { L"BackgroundSuccessfulRefreshPeriod", DEFAULT_BACKGROUNDREFRESHPERIOD, MIN_BACKGROUNDREFRESHPERIOD, MAX_BACKGROUNDREFRESHPERIOD, getoffset( BackgroundSuccessfulRefreshPeriod ), FALSE }, { L"NonBackgroundSuccessfulRefreshPeriod", DEFAULT_NONBACKGROUNDREFRESHPERIOD, MIN_NONBACKGROUNDREFRESHPERIOD, MAX_NONBACKGROUNDREFRESHPERIOD, getoffset( NonBackgroundSuccessfulRefreshPeriod ), FALSE }, { L"DnsFailedDeregisterTimeout", DEFAULT_DNSFAILEDDEREGTIMEOUT, MIN_DNSFAILEDDEREGTIMEOUT, MAX_DNSFAILEDDEREGTIMEOUT, getoffset( DnsFailedDeregisterTimeout ), FALSE }, { L"MaxLdapServersPinged", DEFAULT_MAXLDAPSERVERSPINGED, MIN_MAXLDAPSERVERSPINGED, MAX_MAXLDAPSERVERSPINGED, getoffset( MaxLdapServersPinged ), FALSE }, { L"SiteCoverageRefreshInterval", DEFAULT_SITECOVERAGEREFRESHINTERVAL, MIN_SITECOVERAGEREFRESHINTERVAL, MAX_SITECOVERAGEREFRESHINTERVAL, getoffset( SiteCoverageRefreshInterval ), TRUE }, { L"FtInfoUpdateInterval", DEFAULT_FTINFO_UPDATE_INTERVAL, MIN_FTINFO_UPDATE_INTERVAL, MAX_FTINFO_UPDATE_INTERVAL, getoffset( FtInfoUpdateInterval ), FALSE }, #if NETLOGONDBG { NETLOGON_KEYWORD_DBFLAG, 0, 0, 0xFFFFFFFF, getoffset( DbFlag ), FALSE }, { NETLOGON_KEYWORD_MAXIMUMLOGFILESIZE, DEFAULT_MAXIMUM_LOGFILE_SIZE, 0, 0xFFFFFFFF, getoffset( LogFileMaxSize ), FALSE }, #endif // NETLOGONDBG }; // // Table of boolean to parse. // struct { LPWSTR Keyword; BOOL DefaultValue; ULONG ValueOffset; BOOLEAN ChangesDnsRegistration; } BoolParseTable[] = { #ifdef _DC_NETLOGON { NETLOGON_KEYWORD_REFUSEPASSWORDCHANGE, DEFAULT_REFUSE_PASSWORD_CHANGE, getoffset( RefusePasswordChange ), FALSE }, { NETLOGON_KEYWORD_ALLOWREPLINNONMIXED, DEFAULT_ALLOWREPLINNONMIXED, getoffset( AllowReplInNonMixed ), FALSE }, { L"AvoidSamRepl", TRUE, getoffset( AvoidSamRepl ), FALSE }, { L"AvoidLsaRepl", TRUE, getoffset( AvoidLsaRepl ), FALSE }, { L"SignSecureChannel", TRUE, getoffset( SignSecureChannel ), FALSE }, { L"SealSecureChannel", TRUE, getoffset( SealSecureChannel ), FALSE }, { L"RequireSignOrSeal", FALSE, getoffset( RequireSignOrSeal ), FALSE }, { L"RequireStrongKey", FALSE, getoffset( RequireStrongKey ), FALSE }, { L"SysVolReady", TRUE, getoffset( SysVolReady ), FALSE }, { L"UseDynamicDns", TRUE, getoffset( UseDynamicDns ), TRUE }, { L"RegisterDnsARecords", TRUE, getoffset( RegisterDnsARecords ), TRUE }, { L"AvoidPdcOnWan", FALSE, getoffset( AvoidPdcOnWan ), FALSE }, { L"AutoSiteCoverage", TRUE, getoffset( AutoSiteCoverage ), TRUE }, { L"AvoidDnsDeregOnShutdown", TRUE, getoffset(AvoidDnsDeregOnShutdown), TRUE }, { L"DnsUpdateOnAllAdapters", FALSE, getoffset(DnsUpdateOnAllAdapters), TRUE }, { NETLOGON_KEYWORD_NT4EMULATOR, FALSE, getoffset(Nt4Emulator), FALSE }, #endif // _DC_NETLOGON { NETLOGON_KEYWORD_DISABLEPASSWORDCHANGE, DEFAULT_DISABLE_PASSWORD_CHANGE, getoffset( DisablePasswordChange ), FALSE }, { NETLOGON_KEYWORD_NEUTRALIZENT4EMULATOR, FALSE,/* default is set later */ getoffset( NeutralizeNt4Emulator ), FALSE }, { L"AllowSingleLabelDnsDomain", FALSE, getoffset(AllowSingleLabelDnsDomain), FALSE }, { L"AllowExclusiveSysvolShareAccess", FALSE, getoffset(AllowExclusiveSysvolShareAccess), FALSE }, { L"AllowExclusiveScriptsShareAccess", FALSE, getoffset(AllowExclusiveScriptsShareAccess), FALSE }, { L"AvoidLocatorAccountLookup", FALSE, getoffset(AvoidLocatorAccountLookup), FALSE }, }; VOID NlParseRecompute( IN PNETLOGON_PARAMETERS NlParameters ) /*++ Routine Description: This routine recomputes globals that are simple functions of registry parameters. Arguments: NlParameters - Structure describing all parameters Return Value: None. --*/ { ULONG RandomMinutes; // // Adjust values that are functions of each other. // if ( NlParameters->BackgroundRetryInitialPeriod < NlParameters->NegativeCachePeriod ) { NlParameters->BackgroundRetryInitialPeriod = NlParameters->NegativeCachePeriod; } if ( NlParameters->BackgroundRetryMaximumPeriod < NlParameters->BackgroundRetryInitialPeriod ) { NlParameters->BackgroundRetryMaximumPeriod = NlParameters->BackgroundRetryInitialPeriod; } if ( NlParameters->BackgroundRetryQuitTime != 0 && NlParameters->BackgroundRetryQuitTime < NlParameters->BackgroundRetryMaximumPeriod ) { NlParameters->BackgroundRetryQuitTime = NlParameters->BackgroundRetryMaximumPeriod; } // // Convert from seconds to 100ns // NlParameters->PulseMaximum_100ns.QuadPart = Int32x32To64( NlParameters->PulseMaximum, 10000000 ); NlParameters->PulseTimeout1_100ns.QuadPart = Int32x32To64( NlParameters->PulseTimeout1, 10000000 ); NlParameters->PulseTimeout2_100ns.QuadPart = Int32x32To64( NlParameters->PulseTimeout2, 10000000 ); NlParameters->MailslotMessageTimeout_100ns.QuadPart = Int32x32To64( NlParameters->MailslotMessageTimeout, 10000000 ); NlParameters->MailslotDuplicateTimeout_100ns.QuadPart = Int32x32To64( NlParameters->MailslotDuplicateTimeout, 10000000 ); NlParameters->BackgroundRetryQuitTime_100ns.QuadPart = Int32x32To64( NlParameters->BackgroundRetryQuitTime, 10000000 ); // // Convert from days to 100ns // NlParameters->MaximumPasswordAge_100ns.QuadPart = ((LONGLONG) NlParameters->MaximumPasswordAge) * ((LONGLONG) 10000000) * ((LONGLONG) 24*60*60); // // Add a fraction of a day to prevent all machines created at the same time // from changing their password at the same time. RandomMinutes = (DWORD) rand() % (24*60); NlParameters->MaximumPasswordAge_100ns.QuadPart += ((LONGLONG) RandomMinutes) * ((LONGLONG) 10000000) * ((LONGLONG) 60); #ifdef notdef NlPrint((NL_INIT," RandomMinutes = %lu (0x%lx)\n", RandomMinutes, RandomMinutes )); #endif // notdef NlParameters->ShortApiCallPeriod = SHORT_API_CALL_PERIOD + NlParameters->ExpectedDialupDelay * 1000; NlParameters->DnsRefreshIntervalPeriod = NlParameters->DnsRefreshInterval * 1000; if ( NlParameters->RequireSignOrSeal ) { NlParameters->SignSecureChannel = TRUE; } } NET_API_STATUS NlParseTStr( IN LPNET_CONFIG_HANDLE SectionHandle, IN LPWSTR Keyword, IN BOOL MultivaluedParameter, IN OUT LPWSTR *DefaultValue, OUT LPWSTR *Parameter ) /*++ Routine Description: This routine parses a null or doubly-null terminated string Arguments: SectionHandle - Handle to a section in registry Keyword - The name of the parameter to read MultivaluedParameter - If TRUE, the keyword is a multiple string where elements are separated by a single null character and the array is ended with two null characters. If FALSE, the keyword is a single string ended with one null terminator. DefaultValue - The default value of the parameter. If NULL, the section handle passed is that of the Netlogon Parameters section. If non-NULL, the section handle passed is that of the GP section. If specified and used by this routine, it is set to NULL to indicate that it has been consumed by this routine. Parameter - Returns the parameter read. Return Value: Status returned by NetpGetConfigTStrArray. --*/ { NET_API_STATUS NetStatus; // // Get the configured parameter // // GP doesn't support multivalued strings. Instead a single // string is used where individual strings are separated // by spaces. // if ( MultivaluedParameter && DefaultValue == NULL ) { NetStatus = NetpGetConfigTStrArray ( SectionHandle, Keyword, Parameter ); // Must be freed by NetApiBufferFree(). } else { NetStatus = NetpGetConfigValue ( SectionHandle, Keyword, Parameter ); // Must be freed by NetApiBufferFree(). } // // If the parameter is empty string, // set it to NULL // if ( NetStatus == NERR_Success && (*Parameter)[0] == UNICODE_NULL ) { NetApiBufferFree( *Parameter ); *Parameter = NULL; NetStatus = NERR_CfgParamNotFound; } // // Convert the single valued string into the multivalued form // if ( NetStatus == NERR_Success && // we successfully read the registry MultivaluedParameter && // this is multivalued parameter DefaultValue != NULL ) { // we are parsing the GP section ULONG ParameterLength = 0; LPWSTR LocalParameter = NULL; // // The multivalued string will have two NULL terminator // characters at the end, so allocate enough storage // ParameterLength = wcslen(*Parameter); NetStatus = NetApiBufferAllocate( (ParameterLength + 2) * sizeof(WCHAR), &LocalParameter ); if ( NetStatus == NO_ERROR ) { LPWSTR ParameterPtr = NULL; LPWSTR LocalParameterPtr = NULL; RtlZeroMemory( LocalParameter, (ParameterLength + 2) * sizeof(WCHAR) ); ParameterPtr = *Parameter; LocalParameterPtr = LocalParameter; while ( *ParameterPtr != UNICODE_NULL ) { // // Disregard spaces in the input string. Note that // the user may have used several spaces to separate // two adjacent strings. // while ( *ParameterPtr == L' ' && *ParameterPtr != UNICODE_NULL ) { ParameterPtr ++; } // // Copy non-space characters // while ( *ParameterPtr != L' ' && *ParameterPtr != UNICODE_NULL ) { *LocalParameterPtr++ = *ParameterPtr++; } // // Insert one NULL character between single values // *LocalParameterPtr++ = UNICODE_NULL; } // // Free the value read from registry // NetApiBufferFree( *Parameter ); *Parameter = NULL; // // If the resulting multivalued string is not empty, // use it. The resulting string may need smaller // storage that we have allocated, so allocate again // exactly what's needed to (potentially) save memory. // ParameterLength = NetpTStrArraySize( LocalParameter ); // this includes all storage if ( ParameterLength > 2*sizeof(WCHAR) ) { NetStatus = NetApiBufferAllocate( ParameterLength, Parameter ); if ( NetStatus == NO_ERROR ) { RtlCopyMemory( *Parameter, LocalParameter, ParameterLength ); } } else { NetStatus = ERROR_INVALID_PARAMETER; } if ( LocalParameter != NULL ) { NetApiBufferFree( LocalParameter ); LocalParameter = NULL; } } } // // Handle the default // if ( NetStatus != NERR_Success ) { if ( DefaultValue == NULL ) { *Parameter = NULL; } else { *Parameter = *DefaultValue; // // Indicate that we have consumed the // value from the default parameters // *DefaultValue = NULL; } } // // Write event log on error // if ( NetStatus != NERR_Success && NetStatus != NERR_CfgParamNotFound ) { LPWSTR MsgStrings[3]; if ( DefaultValue == NULL ) { MsgStrings[0] = L"Parameters"; } else { MsgStrings[0] = L"Group Policy"; } MsgStrings[1] = Keyword; MsgStrings[2] = (LPWSTR) ULongToPtr( NetStatus ); NlpWriteEventlog( NELOG_NetlogonInvalidGenericParameterValue, EVENTLOG_WARNING_TYPE, (LPBYTE)&NetStatus, sizeof(NetStatus), MsgStrings, 3 | NETP_LAST_MESSAGE_IS_NETSTATUS ); /* Not Fatal */ } return NetStatus; } BOOL Nlparse( IN PNETLOGON_PARAMETERS NlParameters, IN PNETLOGON_PARAMETERS DefaultParameters OPTIONAL, IN BOOLEAN IsChangeNotify ) /*++ Routine Description: Get parameters from the group policy or registry. All of the parameters are described in iniparm.h. Arguments: NlParameters - Structure describing all parameters DefaultParameters - Structure describing default values for all parameters If NULL, the values are read from the Netlogon Parameters section and the default values specified in the parse table are used. If non-NULL, the values are read from the Group Policy section and the specified defaults are used. IsChangeNotify - TRUE if this call is the result of a change notification Return Value: TRUE -- the registry was opened successfully and parameters were read. FALSE -- iff we couldn't open the appropriate registry section --*/ { BOOLEAN RetVal = TRUE; NET_API_STATUS NetStatus; NET_API_STATUS TempNetStatus; LPWSTR ValueT = NULL; LPWSTR Keyword = NULL; LPWSTR MsgStrings[3]; ULONG i; // // Variables for scanning the configuration data. // LPNET_CONFIG_HANDLE SectionHandle = NULL; LPNET_CONFIG_HANDLE WriteSectionHandle = NULL; RtlZeroMemory( NlParameters, sizeof(NlParameters) ); // // Open the appropriate configuration section // NetStatus = NetpOpenConfigDataWithPathEx( &SectionHandle, NULL, // no server name. (DefaultParameters == NULL) ? L"SYSTEM\\CurrentControlSet\\Services\\Netlogon" : TEXT(NL_GP_KEY), NULL, // default Parameters area TRUE ); // we only want readonly access if ( NetStatus != NO_ERROR ) { SectionHandle = NULL; // // The Netlogon Parameters section must always // exist. Write event log if we can't open it. // if ( DefaultParameters == NULL ) { MsgStrings[0] = L"Parameters"; MsgStrings[1] = L"Parameters"; MsgStrings[2] = (LPWSTR) ULongToPtr( NetStatus ); NlpWriteEventlog( NELOG_NetlogonInvalidGenericParameterValue, EVENTLOG_WARNING_TYPE, (LPBYTE)&NetStatus, sizeof(NetStatus), MsgStrings, 3 | NETP_LAST_MESSAGE_IS_NETSTATUS ); } RetVal = FALSE; goto Cleanup; } // // Loop parsing all the numeric parameters. // for ( i=0; iUnicodeSysvolPath, &NlParameters->UnicodeSysvolPath ); if ( NetStatus != NO_ERROR ) { NlParameters->UnicodeSysvolPath = NULL; if ( DefaultParameters == NULL ) { MsgStrings[0] = L"Parameters"; } else { MsgStrings[0] = L"Group Policy"; } MsgStrings[1] = NETLOGON_KEYWORD_SYSVOL; MsgStrings[2] = (LPWSTR) ULongToPtr( NetStatus ); NlpWriteEventlog( NELOG_NetlogonInvalidGenericParameterValue, EVENTLOG_WARNING_TYPE, (LPBYTE)&NetStatus, sizeof(NetStatus), MsgStrings, 3 | NETP_LAST_MESSAGE_IS_NETSTATUS ); /* Not Fatal */ } // // Get the "SCRIPTS" configured parameter // // Default Script path is relative to Sysvol // NetStatus = NlParseOnePath( SectionHandle, NETLOGON_KEYWORD_SCRIPTS, // key wanted (DefaultParameters == NULL) ? NULL : // No default (Default computed later) DefaultParameters->UnicodeScriptPath, &NlParameters->UnicodeScriptPath ); if ( NetStatus != NO_ERROR ) { NlParameters->UnicodeScriptPath = NULL; if ( DefaultParameters == NULL ) { MsgStrings[0] = L"Parameters"; } else { MsgStrings[0] = L"Group Policy"; } MsgStrings[1] = NETLOGON_KEYWORD_SCRIPTS; MsgStrings[2] = (LPWSTR) ULongToPtr( NetStatus ); NlpWriteEventlog( NELOG_NetlogonInvalidGenericParameterValue, EVENTLOG_WARNING_TYPE, (LPBYTE)&NetStatus, sizeof(NetStatus), MsgStrings, 3 | NETP_LAST_MESSAGE_IS_NETSTATUS ); /* Not Fatal */ } // // Get the "SiteName" configured parameter // NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_SITENAME, FALSE, // single valued parameter (DefaultParameters == NULL) ? NULL : &DefaultParameters->SiteName, &NlParameters->SiteName ); NlParameters->SiteNameConfigured = (NetStatus == NO_ERROR); // // If we are reading the Netlogon Parameters section ... // if ( DefaultParameters == NULL ) { // // If the site name is not configured, default it to the // dynamic site name determined by Netlogon // if ( NetStatus == NERR_CfgParamNotFound ) { NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_DYNAMICSITENAME, FALSE, // single valued parameter NULL, &NlParameters->SiteName ); } // // If we are reading the GP section ... // } else { // // If the site name is not configured in the GP section, // may be it was configured in the Netlogon parameters section // if ( !NlParameters->SiteNameConfigured ) { NlParameters->SiteNameConfigured = DefaultParameters->SiteNameConfigured; } } // // Get the "SiteCoverage" configured parameter // NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_SITECOVERAGE, TRUE, // multivalued parameter (DefaultParameters == NULL) ? NULL : &DefaultParameters->SiteCoverage, &NlParameters->SiteCoverage ); // // Get the "GcSiteCoverage" configured parameter // NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_GCSITECOVERAGE, TRUE, // multivalued parameter (DefaultParameters == NULL) ? NULL : &DefaultParameters->GcSiteCoverage, &NlParameters->GcSiteCoverage ); // // Get the "NdncSiteCoverage" configured parameter // NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_NDNCSITECOVERAGE, TRUE, // multivalued parameter (DefaultParameters == NULL) ? NULL : &DefaultParameters->NdncSiteCoverage, &NlParameters->NdncSiteCoverage ); // // Get the "DnsAvoidRegisterRecords" configured parameter // NetStatus = NlParseTStr( SectionHandle, NETLOGON_KEYWORD_DNSAVOIDNAME, TRUE, // multivalued parameter (DefaultParameters == NULL) ? NULL : &DefaultParameters->DnsAvoidRegisterRecords, &NlParameters->DnsAvoidRegisterRecords ); #endif // _DC_NETLOGON // // Convert parameters to a more convenient form. // NlParseRecompute( NlParameters ); // // If the KerbIsDoneWithJoinDomainEntry key value is 1, delete the // Netlogon\JoinDomain entry. Also delete this entry if this machine is // a DC in which case neither we nor Kerberos needs this entry. (As a // matter of fact, Kerberos won't even create KerbIsDoneWithJoinDomainEntry // on a DC.) // Always delete KerbIsDoneWithJoinDomainEntry // Ignore errors // // Do this only on the change notify since netlogon needs this info // to set the client session first time after a reboot. // if ( IsChangeNotify && DefaultParameters == NULL ) { // KerbIsDoneWithJoinDomainEntry is in netlogon params if ( NlParameters->KerbIsDoneWithJoinDomainEntry == 1 || !NlGlobalMemberWorkstation ) { ULONG WinError = ERROR_SUCCESS; HKEY hJoinKey = NULL; WinError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, NETSETUPP_NETLOGON_JD_PATH, 0, KEY_ALL_ACCESS, &hJoinKey); if ( WinError == ERROR_SUCCESS) { WinError = RegDeleteKey( hJoinKey, NETSETUPP_NETLOGON_JD ); if ( WinError == ERROR_SUCCESS ) { NlPrint(( NL_INIT, "NlParse: Deleted JoinDomain reg key\n" )); } if (hJoinKey) { WinError = RegCloseKey(hJoinKey); } } } TempNetStatus = NetpOpenConfigData( &WriteSectionHandle, NULL, // no server name. SERVICE_NETLOGON, FALSE); // writable, we are deleting it. if ( TempNetStatus == NO_ERROR ) { TempNetStatus = NetpDeleteConfigKeyword ( WriteSectionHandle, NETLOGON_KEYWORD_KERBISDDONEWITHJOIN ); } } NetStatus = NERR_Success; Cleanup: // // Free any locally used resources // if ( ValueT != NULL) { (VOID) NetApiBufferFree( ValueT ); } if ( SectionHandle != NULL ) { (VOID) NetpCloseConfigData( SectionHandle ); } if ( WriteSectionHandle != NULL ) { (VOID) NetpCloseConfigData( WriteSectionHandle ); } return RetVal; } BOOL NlparseAllSections( IN PNETLOGON_PARAMETERS NlParameters, IN BOOLEAN IsChangeNotify ) /*++ Routine Description: Get parameters from both the Group Policy and the Netlogon Parameters registry sections. Arguments: NlParameters - Structure describing all parameters IsChangeNotify - TRUE if this call is the result of a change notification Return Value: TRUE -- iff the parse was successful. --*/ { NETLOGON_PARAMETERS NlLocalParameters; NETLOGON_PARAMETERS GpParameters; RtlZeroMemory( &NlLocalParameters, sizeof(NlLocalParameters) ); RtlZeroMemory( &GpParameters, sizeof(GpParameters) ); // // Do the one time initialization here // if ( !IsChangeNotify ) { NT_PRODUCT_TYPE NtProductType; ULONG i; // // Flag if this is a workstation (or member server) // if ( !RtlGetNtProductType( &NtProductType ) ) { NtProductType = NtProductWinNt; } if ( NtProductType == NtProductLanManNt ) { NlGlobalMemberWorkstation = FALSE; } else { NlGlobalMemberWorkstation = TRUE; } // // Set the right default for NeutralizeNt4Emulator that depends // on whether we are a DC or not // for ( i=0; iUnicodeSysvolPath)); NlPrint((NL_INIT," Scripts = " FORMAT_LPWSTR "\n", NlParameters->UnicodeScriptPath)); NlPrint((NL_INIT," SiteName (%ld) = " FORMAT_LPWSTR "\n", NlParameters->SiteNameConfigured, NlParameters->SiteName )); { LPTSTR_ARRAY TStrArray; if ( NlParameters->SiteCoverage != NULL ) { NlPrint((NL_INIT," SiteCoverage = " )); TStrArray = NlParameters->SiteCoverage; while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } NlPrint((NL_INIT,"\n" )); } } { LPTSTR_ARRAY TStrArray; if ( NlParameters->GcSiteCoverage != NULL ) { NlPrint((NL_INIT," GcSiteCoverage = " )); TStrArray = NlParameters->GcSiteCoverage; while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } NlPrint((NL_INIT,"\n" )); } } { LPTSTR_ARRAY TStrArray; if ( NlParameters->NdncSiteCoverage != NULL ) { NlPrint((NL_INIT," NdncSiteCoverage = " )); TStrArray = NlParameters->NdncSiteCoverage; while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } NlPrint((NL_INIT,"\n" )); } } { LPTSTR_ARRAY TStrArray; if ( NlParameters->DnsAvoidRegisterRecords != NULL ) { NlPrint((NL_INIT," DnsAvoidRegisterRecords = " )); TStrArray = NlParameters->DnsAvoidRegisterRecords; while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } NlPrint((NL_INIT,"\n" )); } } for ( i=0; iSiteName != NULL) { (VOID) NetApiBufferFree( NlParameters->SiteName ); NlParameters->SiteName = NULL; } if ( NlParameters->SiteCoverage != NULL) { (VOID) NetApiBufferFree( NlParameters->SiteCoverage ); NlParameters->SiteCoverage = NULL; } if ( NlParameters->GcSiteCoverage != NULL) { (VOID) NetApiBufferFree( NlParameters->GcSiteCoverage ); NlParameters->GcSiteCoverage = NULL; } if ( NlParameters->NdncSiteCoverage != NULL) { (VOID) NetApiBufferFree( NlParameters->NdncSiteCoverage ); NlParameters->NdncSiteCoverage = NULL; } if ( NlParameters->DnsAvoidRegisterRecords != NULL) { (VOID) NetApiBufferFree( NlParameters->DnsAvoidRegisterRecords ); NlParameters->DnsAvoidRegisterRecords = NULL; } if ( NlParameters->UnicodeScriptPath != NULL) { (VOID) NetApiBufferFree( NlParameters->UnicodeScriptPath ); NlParameters->UnicodeScriptPath = NULL; } if ( NlParameters->UnicodeSysvolPath != NULL) { (VOID) NetApiBufferFree( NlParameters->UnicodeSysvolPath ); NlParameters->UnicodeSysvolPath = NULL; } } VOID NlReparse( VOID ) /*++ Routine Description: This routine handle a registry change notification. Arguments: None. Return Value: None --*/ { NETLOGON_PARAMETERS LocalParameters; ULONG i; LPWSTR TempString; BOOLEAN UpdateDns = FALSE; BOOLEAN UpdateShares = FALSE; BOOLEAN UpdateSiteName = FALSE; ULONG OldDnsTtl; BOOL OldSysVolReady; BOOL OldDisablePasswordChange; ULONG OldScavengeInterval; ULONG OldMaximumPasswordAge; // // Grab any old values that might be interesting. // OldDnsTtl = NlGlobalParameters.DnsTtl; OldSysVolReady = NlGlobalParameters.SysVolReady; OldDisablePasswordChange = NlGlobalParameters.DisablePasswordChange; OldScavengeInterval = NlGlobalParameters.ScavengeInterval; OldMaximumPasswordAge = NlGlobalParameters.MaximumPasswordAge; // // Parse both sections in registry relevant to us // if (! NlparseAllSections( &LocalParameters, TRUE ) ) { return; } // // Be Verbose // NlPrint((NL_INIT, "Following are the effective values after parsing\n")); if ( (LocalParameters.UnicodeSysvolPath == NULL && NlGlobalParameters.UnicodeSysvolPath != NULL ) || (LocalParameters.UnicodeSysvolPath != NULL && NlGlobalParameters.UnicodeSysvolPath == NULL ) || (LocalParameters.UnicodeSysvolPath != NULL && NlGlobalParameters.UnicodeSysvolPath != NULL ) && _wcsicmp( LocalParameters.UnicodeSysvolPath, NlGlobalParameters.UnicodeSysvolPath) != 0 ) { NlPrint((NL_INIT," Sysvol = " FORMAT_LPWSTR "\n", LocalParameters.UnicodeSysvolPath)); // We can get away with this since only Netlogon's main thread touches // this variable. TempString = LocalParameters.UnicodeSysvolPath; LocalParameters.UnicodeSysvolPath = NlGlobalParameters.UnicodeSysvolPath; NlGlobalParameters.UnicodeSysvolPath = TempString; UpdateShares = TRUE; } if ( (LocalParameters.UnicodeScriptPath == NULL && NlGlobalParameters.UnicodeScriptPath != NULL ) || (LocalParameters.UnicodeScriptPath != NULL && NlGlobalParameters.UnicodeScriptPath == NULL ) || (LocalParameters.UnicodeScriptPath != NULL && NlGlobalParameters.UnicodeScriptPath != NULL ) && _wcsicmp( LocalParameters.UnicodeScriptPath, NlGlobalParameters.UnicodeScriptPath) != 0 ) { NlPrint((NL_INIT," Scripts = " FORMAT_LPWSTR "\n", LocalParameters.UnicodeScriptPath)); // We can get away with this since only Netlogon's main thread touches // this variable. TempString = LocalParameters.UnicodeScriptPath; LocalParameters.UnicodeScriptPath = NlGlobalParameters.UnicodeScriptPath; NlGlobalParameters.UnicodeScriptPath = TempString; UpdateShares = TRUE; } // // Check whether the exclusive share access to Sysvol share needs update // if ( LocalParameters.AllowExclusiveSysvolShareAccess ) { if ( !NlGlobalParameters.AllowExclusiveSysvolShareAccess ) { UpdateShares = TRUE; } } else { if ( NlGlobalParameters.AllowExclusiveSysvolShareAccess ) { UpdateShares = TRUE; } } // // Check whether the exclusive share access to Scripts share needs update // if ( LocalParameters.AllowExclusiveScriptsShareAccess ) { if ( !NlGlobalParameters.AllowExclusiveScriptsShareAccess ) { UpdateShares = TRUE; } } else { if ( NlGlobalParameters.AllowExclusiveScriptsShareAccess ) { UpdateShares = TRUE; } } if ( (LocalParameters.SiteNameConfigured != NlGlobalParameters.SiteNameConfigured ) || (LocalParameters.SiteName == NULL && NlGlobalParameters.SiteName != NULL ) || (LocalParameters.SiteName != NULL && NlGlobalParameters.SiteName == NULL ) || (LocalParameters.SiteName != NULL && NlGlobalParameters.SiteName != NULL ) && _wcsicmp( LocalParameters.SiteName, NlGlobalParameters.SiteName) != 0 ) { NlPrint((NL_INIT," SiteName (%ld) = " FORMAT_LPWSTR "\n", LocalParameters.SiteNameConfigured, LocalParameters.SiteName )); // We can get away with this since only Netlogon's main thread touches // this variable. TempString = LocalParameters.SiteName; LocalParameters.SiteName = NlGlobalParameters.SiteName; NlGlobalParameters.SiteName = TempString; NlGlobalParameters.SiteNameConfigured = LocalParameters.SiteNameConfigured; UpdateSiteName = TRUE; } // // Handle SiteCoverage changing // if ( NlSitesSetSiteCoverageParam( DOM_REAL_DOMAIN, LocalParameters.SiteCoverage ) ) { LPTSTR_ARRAY TStrArray; NlPrint((NL_INIT," SiteCoverage = " )); TStrArray = LocalParameters.SiteCoverage; if ( TStrArray == NULL ) { NlPrint((NL_INIT,"" )); } else { while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } } NlPrint((NL_INIT,"\n" )); // NlSitesSetSiteCoverageParam used this allocated buffer LocalParameters.SiteCoverage = NULL; UpdateDns = TRUE; } // // Handle GcSiteCoverage changing // if ( NlSitesSetSiteCoverageParam( DOM_FOREST, LocalParameters.GcSiteCoverage ) ) { LPTSTR_ARRAY TStrArray; NlPrint((NL_INIT," GcSiteCoverage = " )); TStrArray = LocalParameters.GcSiteCoverage; if ( TStrArray == NULL ) { NlPrint((NL_INIT,"" )); } else { while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } } NlPrint((NL_INIT,"\n" )); // NlSitesSetSiteCoverageParam used this allocated buffer LocalParameters.GcSiteCoverage = NULL; UpdateDns = TRUE; } // // Handle NdncSiteCoverage changing // if ( NlSitesSetSiteCoverageParam( DOM_NON_DOMAIN_NC, LocalParameters.NdncSiteCoverage ) ) { LPTSTR_ARRAY TStrArray; NlPrint((NL_INIT," NdncSiteCoverage = " )); TStrArray = LocalParameters.NdncSiteCoverage; if ( TStrArray == NULL ) { NlPrint((NL_INIT,"" )); } else { while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } } NlPrint((NL_INIT,"\n" )); // NlSitesSetSiteCoverageParam used this allocated buffer LocalParameters.NdncSiteCoverage = NULL; UpdateDns = TRUE; } // // Handle DnsAvoidRegisterRecords changing // if ( NlDnsSetAvoidRegisterNameParam( LocalParameters.DnsAvoidRegisterRecords ) ) { LPTSTR_ARRAY TStrArray; NlPrint((NL_INIT," DnsAvoidRegisterRecords = " )); TStrArray = LocalParameters.DnsAvoidRegisterRecords; if ( TStrArray == NULL ) { NlPrint((NL_INIT,"" )); } else { while (!NetpIsTStrArrayEmpty(TStrArray)) { NlPrint((NL_INIT," '%ws'", TStrArray )); TStrArray = NetpNextTStrArrayEntry(TStrArray); } } NlPrint((NL_INIT,"\n" )); // NlSitesSetSiteCoverageParam used this allocated buffer LocalParameters.DnsAvoidRegisterRecords = NULL; UpdateDns = TRUE; } // // Install all the numeric parameters. // for ( i=0; i