//+----------------------------------------------------------------------- // // File: secdata.cxx // // Contents: Global data and methods on it. // // // History: // //------------------------------------------------------------------------ #include "kdcsvr.hxx" #include /////////////////////////////////////////////////////////////// // // // Global data // // This is all the security information that gets cached. CSecurityData SecData; CAuthenticatorList * Authenticators; CAuthenticatorList * ReplayDetect; /////////////////////////////////////////////////////////////// // // // Prototypes // fLsaPolicyChangeNotificationCallback KdcPolicyChangeCallback; //+------------------------------------------------------------------------- // // Function: KdcPolicyChangeCallBack // // Synopsis: Function that gets called when policy changes // // Effects: Changes policy variables // // Arguments: MonitorInfoClass - class of data that changed // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcPolicyChangeCallBack( IN POLICY_NOTIFICATION_INFORMATION_CLASS MonitorInfoClass ) { NTSTATUS Status; WCHAR Class[10]; TRACE(KDC, KdcPolicyChangeCallBack, DEB_FUNCTION); Status = SecData.ReloadPolicy(MonitorInfoClass); if (!NT_SUCCESS(Status)) { _itow(MonitorInfoClass, Class, 10 ); ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_POLICY_UPDATE_FAILED, sizeof(NTSTATUS), &Status, 1, // number of strings Class ); } } //+------------------------------------------------------------------------- // // Function: CSecurityData::ReloadPolicy // // Synopsis: Reloads a particular piece of policy // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS CSecurityData::ReloadPolicy( IN POLICY_NOTIFICATION_INFORMATION_CLASS MonitorInfoClass ) { NTSTATUS Status = STATUS_SUCCESS; PLSAPR_POLICY_DOMAIN_INFORMATION DomainPolicy = NULL; PLSAPR_POLICY_INFORMATION LocalPolicy = NULL; WCHAR Class[10]; TRACE(KDC, CSecurityData::ReloadPolicy, DEB_FUNCTION); // // Ignore changes to non-kerberos ticket information // switch(MonitorInfoClass) { case PolicyNotifyDomainKerberosTicketInformation: Status = LsarQueryDomainInformationPolicy( GlobalPolicyHandle, PolicyDomainKerberosTicketInformation, &DomainPolicy ); if (!NT_SUCCESS(Status)) { goto Cleanup; } break; case PolicyNotifyAuditEventsInformation: Status = LsarQueryInformationPolicy( GlobalPolicyHandle, PolicyAuditEventsInformation, &LocalPolicy ); if (!NT_SUCCESS(Status)) { goto Cleanup; } break; default: return(STATUS_SUCCESS); } // // Update the changed information in the KDCs global data structures. // // // Current policy defaults, see KirkSol/JBrezak // [Kerberos Policy] // MaxTicketAge=10 ;Maximum User Ticket Lifetime (hours) // MaxRenewAge=7 ;Maximum lifetime that a user tickeet can be renewed (days) // MaxServiceAge=60 ;Maximum Service Ticket Lifetime (minutes) // MaxClockSkew=5 ;Maximum tolerance for synchronization of computer clocks (minutes) // TicketValidateClient=1 ;Enforce user logon restrictions WriteLock(); switch(MonitorInfoClass) { case PolicyNotifyDomainKerberosTicketInformation: DebugLog((DEB_TRACE, "MaxServiceTicketAge : %x\n", DomainPolicy->PolicyDomainKerbTicketInfo.MaxServiceTicketAge.QuadPart)); DebugLog((DEB_TRACE, "MaxTicketAge : %x\n", DomainPolicy->PolicyDomainKerbTicketInfo.MaxTicketAge.QuadPart)); DebugLog((DEB_TRACE, "MaxRenewAge : %x\n", DomainPolicy->PolicyDomainKerbTicketInfo.MaxRenewAge.QuadPart)); DebugLog((DEB_TRACE, "MaxClockSkew : %x\n", DomainPolicy->PolicyDomainKerbTicketInfo.MaxClockSkew.QuadPart)); // Validate parameters if ((DomainPolicy->PolicyDomainKerbTicketInfo.MaxServiceTicketAge.QuadPart <= (LONGLONG) -1) || (DomainPolicy->PolicyDomainKerbTicketInfo.MaxTicketAge.QuadPart <= (LONGLONG) -1) || (DomainPolicy->PolicyDomainKerbTicketInfo.MaxRenewAge.QuadPart <= (LONGLONG) -1) || (DomainPolicy->PolicyDomainKerbTicketInfo.MaxClockSkew.QuadPart <= (LONGLONG) -1) || (DomainPolicy->PolicyDomainKerbTicketInfo.MaxServiceTicketAge.QuadPart == (LONGLONG) 0) || (DomainPolicy->PolicyDomainKerbTicketInfo.MaxTicketAge.QuadPart == (LONGLONG) 0) ) { _itow(MonitorInfoClass, Class, 10 ); DebugLog((DEB_ERROR, "Policy update failed!\n")); ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_POLICY_UPDATE_FAILED, sizeof(NTSTATUS), &Status, 1, // number of strings Class ); } else { _KDC_TgsTicketLifespan = DomainPolicy->PolicyDomainKerbTicketInfo.MaxServiceTicketAge; _KDC_TgtTicketLifespan = DomainPolicy->PolicyDomainKerbTicketInfo.MaxTicketAge; _KDC_TicketRenewSpan = DomainPolicy->PolicyDomainKerbTicketInfo.MaxRenewAge; // // never allow the skew window to drop down to zero, which prevents // logon locally to the DC. The allowed skew window is at least // 10 seconds for now // SkewTime.QuadPart = max(DomainPolicy->PolicyDomainKerbTicketInfo.MaxClockSkew.QuadPart, 10 * 10000000); } // Update domain policy flags. Don't depend on the flags keeping in sync // with the kerberos internal flags if ( DomainPolicy->PolicyDomainKerbTicketInfo.AuthenticationOptions & POLICY_KERBEROS_VALIDATE_CLIENT) { _KDC_Flags |= AUTH_REQ_VALIDATE_CLIENT; } else { _KDC_Flags &= ~AUTH_REQ_VALIDATE_CLIENT; } break; case PolicyNotifyAuditEventsInformation: if ((LocalPolicy->PolicyAuditEventsInfo.AuditingMode) && (LocalPolicy->PolicyAuditEventsInfo.MaximumAuditEventCount > AuditCategoryAccountLogon)) { if (LocalPolicy->PolicyAuditEventsInfo.EventAuditingOptions[AuditCategoryAccountLogon] & POLICY_AUDIT_EVENT_SUCCESS ) { _KDC_AuditEvents |= KDC_AUDIT_AS_SUCCESS | KDC_AUDIT_TGS_SUCCESS | KDC_AUDIT_MAP_SUCCESS; } else { _KDC_AuditEvents &= ~(KDC_AUDIT_AS_SUCCESS | KDC_AUDIT_TGS_SUCCESS | KDC_AUDIT_MAP_SUCCESS); } if (LocalPolicy->PolicyAuditEventsInfo.EventAuditingOptions[AuditCategoryAccountLogon] & POLICY_AUDIT_EVENT_FAILURE ) { _KDC_AuditEvents |= KDC_AUDIT_AS_FAILURE | KDC_AUDIT_TGS_FAILURE | KDC_AUDIT_MAP_FAILURE; } else { _KDC_AuditEvents &= ~(KDC_AUDIT_AS_FAILURE | KDC_AUDIT_TGS_FAILURE | KDC_AUDIT_MAP_FAILURE); } } break; } Unlock(); Cleanup: if (DomainPolicy != NULL) { LsaIFree_LSAPR_POLICY_DOMAIN_INFORMATION ( PolicyDomainKerberosTicketInformation, DomainPolicy); } if (LocalPolicy != NULL) { LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAuditEventsInformation, LocalPolicy); } return(Status); } //+------------------------------------------------------------------------- // // Function: CSecurityData::SetForestRoot // // Synopsis: Sets the forest root // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS CSecurityData::SetForestRoot( IN PUNICODE_STRING NewForestRoot ) { NTSTATUS Status; UNICODE_STRING Temp; WriteLock(); RtlCopyMemory( &Temp, &_ForestRoot, sizeof(UNICODE_STRING) ); Status = KerbDuplicateString( &_ForestRoot, NewForestRoot ); // on alloc failure, just keep old version as it will never change if (!NT_SUCCESS(Status)) { RtlCopyMemory( &_ForestRoot, &Temp, sizeof(UNICODE_STRING) ); } else { KerbFreeString(&Temp); } _KDC_IsForestRoot = IsOurRealm(&_ForestRoot); Unlock(); return Status; } //////////////////////////////////////////////////////////////////// // // Name: CSecurityData::CSecurityData // // Synopsis: Constructor. // // Arguments: // // Notes: . // CSecurityData::CSecurityData() { TRACE(KDC, CSecurityData::CSecurityData, DEB_FUNCTION); RtlInitUnicodeString( &_MachineName, NULL ); RtlInitUnicodeString( &_SamMachineName, NULL ); RtlInitUnicodeString( &_MachineUpn, NULL ); RtlInitUnicodeString( &_RealmName, NULL ); RtlInitUnicodeString( &_KDC_Name, NULL ); RtlInitUnicodeString( &_KDC_FullName, NULL ); RtlInitUnicodeString( &_KDC_FullDnsName, NULL ); RtlInitUnicodeString( &_KDC_FullKdcName, NULL ); RtlInitUnicodeString( &_ForestRoot, NULL ); _KerbRealmName = NULL; _KerbDnsRealmName = NULL; _KrbtgtServiceName = NULL; _KpasswdServiceName = NULL; RtlZeroMemory( &_KrbtgtTicketInfo, sizeof(KDC_TICKET_INFO) ); _KrbtgtTicketInfoValid = FALSE; _KDC_CrossForestEnabled = FALSE; _KDC_IsForestRoot = FALSE; _fMonitorInitialized = FALSE; } //+--------------------------------------------------------------------------- // // Member: CSecurityData::InitLock // // Synopsis: Initializes the lock in the CSecurityData class. // // Effects: // // Arguments: (none) // // Returns: STATUS_SUCCESS or error code // // History: 2001-07-02 JSchwart created // // Notes: This must be called before any other method of CSecurityData. // It needs to be separate from Init since SAM can call KDC routines // that use the global SecData before Init has been called in the // KDC ServiceMain. // //---------------------------------------------------------------------------- NTSTATUS CSecurityData::InitLock( VOID ) { NTSTATUS Status = STATUS_SUCCESS; ASSERT(!_fMonitorInitialized); __try { RtlInitializeResource(&_Monitor); } __except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if (NT_SUCCESS(Status)) { _fMonitorInitialized = TRUE; } return Status; } //+--------------------------------------------------------------------------- // // Member: CSecurityData::Init // // Synopsis: Initializes the global data. // // Effects: Allocates memory // // Arguments: (none) // // Returns: STATUS_SUCCESS or error code // // Signals: May raise exception on out of memory. // // History: 4-02-93 WadeR Created // // Notes: This must be called after InitLock and before any other // method of CSecurityData. It gets data from the registry, the // domain object, and the kdc.ini file. // //---------------------------------------------------------------------------- NTSTATUS CSecurityData::Init() { TRACE(KDC, CSecurityData::Init, DEB_FUNCTION); NTSTATUS Status; UNICODE_STRING TempString; WCHAR TempMachineName[CNLEN+1]; ULONG MachineNameLength = CNLEN+1; LARGE_INTEGER MaxAuthenticatorAge; PLSAPR_POLICY_INFORMATION PolicyInfo = NULL; UNICODE_STRING KadminName; UNICODE_STRING ChangePwName; D_DebugLog(( DEB_TRACE, "Entered CSecurityData::Init()\n" )); DsysAssert(_fMonitorInitialized); // // Get the domain name and ID from the registry // Status = KerbDuplicateString( &_RealmName, &GlobalDomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (!KERB_SUCCESS(KerbConvertUnicodeStringToRealm( &_KerbRealmName, &GlobalDomainName))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } // // Construct the KDC Name from the realm and the suffix. // RtlInitUnicodeString( &TempString, KDC_PRINCIPAL_NAME ); Status = KerbDuplicateString( &_KDC_Name, &TempString ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (!GetComputerName( TempMachineName, &MachineNameLength )) { Status = STATUS_BUFFER_TOO_SMALL; goto Cleanup; } RtlInitUnicodeString( &TempString, TempMachineName ); Status = KerbDuplicateString( &_MachineName, &TempString ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaIQueryInformationPolicyTrusted( PolicyDnsDomainInformation, &PolicyInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // WAS BUG: this DNS name may have a trailing '.' - if so, strip it off // if (PolicyInfo->PolicyDnsDomainInfo.DnsDomainName.Length >= sizeof(WCHAR)) { if (PolicyInfo->PolicyDnsDomainInfo.DnsDomainName.Buffer[ -1 + PolicyInfo->PolicyDnsDomainInfo.DnsDomainName.Length / sizeof(WCHAR) ] == L'.') { PolicyInfo->PolicyDnsDomainInfo.DnsDomainName.Length -= sizeof(WCHAR); } } Status = KerbDuplicateString( &_DnsRealmName, (PUNICODE_STRING) &PolicyInfo->PolicyDnsDomainInfo.DnsDomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = RtlUpcaseUnicodeString( &_DnsRealmName, &_DnsRealmName, FALSE ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (!KERB_SUCCESS(KerbConvertUnicodeStringToRealm( &_KerbDnsRealmName, &_DnsRealmName))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } // // Build a sam style machine name // _SamMachineName.Length = _MachineName.Length + sizeof(WCHAR); _SamMachineName.MaximumLength = _SamMachineName.Length + sizeof(WCHAR); _SamMachineName.Buffer = (LPWSTR) MIDL_user_allocate(_SamMachineName.MaximumLength); if (_SamMachineName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( _SamMachineName.Buffer, _MachineName.Buffer, _MachineName.Length ); _SamMachineName.Buffer[_MachineName.Length / sizeof(WCHAR)] = L'$'; _SamMachineName.Buffer[_SamMachineName.Length / sizeof(WCHAR)] = L'\0'; // // Build the machine UPN: machinename$@dns.domain.name // _MachineUpn.Length = _MachineName.Length + 2 * sizeof(WCHAR) + _DnsRealmName.Length; _MachineUpn.MaximumLength = _MachineUpn.Length + sizeof(WCHAR); _MachineUpn.Buffer = (LPWSTR) MIDL_user_allocate(_MachineUpn.MaximumLength); if (_MachineUpn.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( _MachineUpn.Buffer, _MachineName.Buffer, _MachineName.Length ); _MachineUpn.Buffer[_MachineName.Length / sizeof(WCHAR)] = L'$'; _MachineUpn.Buffer[1+_MachineName.Length / sizeof(WCHAR)] = L'@'; RtlCopyMemory( _MachineUpn.Buffer + _MachineName.Length / sizeof(WCHAR) + 2 , _DnsRealmName.Buffer, _DnsRealmName.Length ); _MachineUpn.Buffer[_MachineUpn.Length / sizeof(WCHAR)] = L'\0'; if (!KERB_SUCCESS(KerbBuildFullServiceName( &_RealmName, &_KDC_Name, &_KDC_FullName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } if (!KERB_SUCCESS(KerbBuildFullServiceName( &_DnsRealmName, &_KDC_Name, &_KDC_FullDnsName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } // // Build the full kdc name - a kerberos style name // _KDC_FullKdcName.Length = _KDC_Name.Length + _DnsRealmName.Length + sizeof(WCHAR); _KDC_FullKdcName.MaximumLength = _KDC_FullKdcName.Length + sizeof(WCHAR); _KDC_FullKdcName.Buffer = (LPWSTR) MIDL_user_allocate(_KDC_FullDnsName.MaximumLength); if (_KDC_FullKdcName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( _KDC_FullKdcName.Buffer, _KDC_Name.Buffer, _KDC_Name.Length ); _KDC_FullKdcName.Buffer[_KDC_Name.Length / sizeof(WCHAR)] = L'/'; RtlCopyMemory( _KDC_FullKdcName.Buffer + 1 + _KDC_Name.Length / sizeof(WCHAR), _DnsRealmName.Buffer, _DnsRealmName.Length ); _KDC_FullKdcName.Buffer[_KDC_FullKdcName.Length / sizeof(WCHAR)] = L'\0'; D_DebugLog((DEB_TRACE, "_KDC_Name='%wZ', MachineName='%wZ'\n", &_KDC_Name, &_MachineName )); if (!KERB_SUCCESS(KerbBuildFullServiceKdcName( &_DnsRealmName, &_KDC_Name, KRB_NT_SRV_INST, &_KrbtgtServiceName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } // // Build the kdc name for kadmin/changepw. // RtlInitUnicodeString( &KadminName, KERB_KPASSWD_NAME ); RtlInitUnicodeString( &ChangePwName, L"changepw" ); if (!KERB_SUCCESS(KerbBuildFullServiceKdcName( &ChangePwName, &KadminName, KRB_NT_SRV_INST, &_KpasswdServiceName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Status = LoadParameters(GlobalAccountDomainHandle); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load parameters: 0x%x\n",Status)); goto Cleanup; } // // Create the authenticators. // // // In reality, set skew time to 5 minutes and same for authenticators. // SkewTime.QuadPart = (LONGLONG) 10000000 * 60 * 5; MaxAuthenticatorAge = SkewTime; // // Create the authenticator list // Authenticators = new CAuthenticatorList( MaxAuthenticatorAge, 1 ); if (Authenticators == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Status = Authenticators->Init(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Setup a list to track failed requests - we don't fail the // same request twice for the timeout time // ReplayDetect = new CAuthenticatorList( MaxAuthenticatorAge, 1 ); if (ReplayDetect == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Status = ReplayDetect->Init(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Setup a list to track potential canidates for fwd'ing to PDC // Status = AsNegCacheInit(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Register for policy callbacks // Status = LsaIRegisterPolicyChangeNotificationCallback( KdcPolicyChangeCallBack, PolicyNotifyDomainKerberosTicketInformation ); if (NT_SUCCESS(Status)) { Status = LsaIRegisterPolicyChangeNotificationCallback( KdcPolicyChangeCallBack, PolicyNotifyAuditEventsInformation ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to register for policy changes: 0x%x\n",Status)); goto Cleanup; } Status = UpdateKrbtgtTicketInfo(); if (!NT_SUCCESS(Status)) { goto Cleanup; } Cleanup: if (PolicyInfo != NULL) { LsaIFree_LSAPR_POLICY_INFORMATION( PolicyDnsDomainInformation, PolicyInfo ); } return(Status); } //+--------------------------------------------------------------------------- // // Member: CSecurityData::Cleanup // // Synopsis: Cleans up the object // // Effects: Frees memory // // Arguments: (none) // // History: 4-02-93 WadeR Created // // Notes: // //---------------------------------------------------------------------------- VOID CSecurityData::Cleanup() { TRACE(KDC, CSecurityData::Cleanup, DEB_FUNCTION); _KrbtgtTicketInfoValid = FALSE; KerbFreeString(&_RealmName); KerbFreeString(&_KDC_Name); KerbFreeString(&_KDC_FullName); KerbFreeString(&_KDC_FullDnsName); KerbFreeString(&_KDC_FullKdcName); KerbFreeString(&_MachineName); KerbFreeString(&_SamMachineName); KerbFreeString(&_MachineUpn); KerbFreeString(&_DnsRealmName); KerbFreeRealm(&_KerbRealmName); KerbFreeRealm(&_KerbDnsRealmName); KerbFreeKdcName(&_KrbtgtServiceName); KerbFreeKdcName(&_KpasswdServiceName); if (Authenticators != NULL) { delete Authenticators; Authenticators = NULL; } if (ReplayDetect != NULL) { delete ReplayDetect; ReplayDetect = NULL; } LsaIUnregisterAllPolicyChangeNotificationCallback( KdcPolicyChangeCallBack ); } //+--------------------------------------------------------------------------- // // Member: CSecurityData::~CSecurityData // // Synopsis: Destructor // // Effects: Frees memory // // Arguments: (none) // // History: 4-02-93 WadeR Created // // Notes: // //---------------------------------------------------------------------------- CSecurityData::~CSecurityData() { TRACE(KDC, CSecurityData::~CSecurityData, DEB_FUNCTION); Cleanup(); // // This doesn't happen during Cleanup() because we want to // make sure it only happens once. // if (_fMonitorInitialized) { RtlDeleteResource(&_Monitor); } } NTSTATUS CSecurityData::LoadParameters(SAMPR_HANDLE DomainHandle) { NTSTATUS Status = STATUS_SUCCESS; LARGE_INTEGER OneHour, EightHours, TenHours; TRACE(KDC, CSecurityData::LoadParameters, DEB_FUNCTION); OneHour.QuadPart = (LONGLONG) 10000000 * 60 * 60 * 1; EightHours.QuadPart = (LONGLONG) 10000000 * 60 * 60 * 8; TenHours.QuadPart = (LONGLONG) 10000000 * 60 * 60 * 10; // New internal defaults according to JBrezak. 7/28/99 // // Initialize Tgt lifetime to 10 hours. // _KDC_TgtTicketLifespan = TenHours; // // Initialize ticket max renew time to one hour. // _KDC_TicketRenewSpan = OneHour; // // Initialize Tgs lifetime to one hour. // _KDC_TgsTicketLifespan = OneHour; // // Initialize domain password replication skew tolerance to 60 minutes. // _KDC_DomainPasswordReplSkew.QuadPart = (LONGLONG) 60*60*10000000; // // Initialize restriciton lifetime to 20 minutes // _KDC_RestrictionLifetime.QuadPart = (LONGLONG) 20*60*10000000; // // Default authentication flags // _KDC_Flags = AUTH_REQ_ALLOW_FORWARDABLE | AUTH_REQ_ALLOW_PROXIABLE | AUTH_REQ_ALLOW_RENEWABLE | AUTH_REQ_ALLOW_NOADDRESS | AUTH_REQ_ALLOW_ENC_TKT_IN_SKEY | AUTH_REQ_ALLOW_VALIDATE | AUTH_REQ_VALIDATE_CLIENT | AUTH_REQ_OK_AS_DELEGATE | AUTH_REQ_ALLOW_S4U_DELEGATE; _KDC_AuditEvents = 0; // // Get kerberos policy information // Status = ReloadPolicy( PolicyNotifyDomainKerberosTicketInformation ); if (!NT_SUCCESS(Status)) { if ((Status != STATUS_NOT_FOUND) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { DebugLog((DEB_ERROR,"Failed to reload kerberos ticket policy: 0x%x\n",Status)); goto Cleanup; } Status = STATUS_SUCCESS; } // // Get audit information // Status = ReloadPolicy( PolicyNotifyAuditEventsInformation ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_NOT_FOUND) { DebugLog((DEB_ERROR,"Failed to query audit event info: 0x%x\n",Status)); goto Cleanup; } Status = STATUS_SUCCESS; } Cleanup: #if DBG DebugShowState(); #endif return(Status); } //+------------------------------------------------------------------------- // // Function: CSecurityData::GetKrbtgtTicketInfo // // Synopsis: Duplicates ticket info for krbtgt account // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- KERBERR CSecurityData::GetKrbtgtTicketInfo( OUT PKDC_TICKET_INFO TicketInfo ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG CredentialSize; RtlZeroMemory( TicketInfo, sizeof(KDC_TICKET_INFO) ); ReadLock(); if (!_KrbtgtTicketInfoValid) { KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; goto Cleanup; } // // Duplicate the cached copy of the KRBTGT information // *TicketInfo = _KrbtgtTicketInfo; TicketInfo->Passwords = NULL; TicketInfo->OldPasswords = NULL; TicketInfo->TrustSid = NULL; TicketInfo->AccountName.Buffer = NULL; if (!NT_SUCCESS(KerbDuplicateString( &TicketInfo->AccountName, &_KrbtgtTicketInfo.AccountName ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } KerbErr = KdcDuplicateCredentials( &TicketInfo->Passwords, &CredentialSize, _KrbtgtTicketInfo.Passwords, FALSE // don't marshall ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KdcDuplicateCredentials( &TicketInfo->OldPasswords, &CredentialSize, _KrbtgtTicketInfo.OldPasswords, FALSE // don't marshall ); Cleanup: Unlock(); if (!KERB_SUCCESS(KerbErr)) { FreeTicketInfo(TicketInfo); } return(KerbErr); } //+------------------------------------------------------------------------- // // Function: CSecurityData::UpdateKrbtgtTicketInfo // // Synopsis: Triggers an update of the krbtgt ticket info // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS CSecurityData::UpdateKrbtgtTicketInfo( VOID ) { NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr; KDC_TICKET_INFO NewTicketInfo = {0}; PUSER_INTERNAL6_INFORMATION UserInfo = NULL; KERB_EXT_ERROR ExtendedError; // dummy var WriteLock(); _KrbtgtTicketInfoValid = FALSE; KerbErr = KdcGetTicketInfo( SecData.KdcServiceName(), 0, // no lookup flags FALSE, // do not restrict user accounts (user2user) NULL, // no principal name NULL, // no realm &NewTicketInfo, &ExtendedError, // dummy NULL, // no user handle USER_ALL_PASSWORDLASTSET, 0L, // no extended fields &UserInfo, NULL // no group membership ); if (KERB_SUCCESS(KerbErr)) { FreeTicketInfo( &_KrbtgtTicketInfo ); _KrbtgtTicketInfo = NewTicketInfo; _KrbtgtTicketInfoValid = TRUE; _KrbtgtPasswordLastSet = UserInfo->I1.PasswordLastSet; SamIFree_UserInternal6Information( UserInfo ); } else { Status = KerbMapKerbError(KerbErr); } Unlock(); return(Status); } //+------------------------------------------------------------------------- // // Function: KdcAccountChangeNotificationRoutine // // Synopsis: Receives notification of changes to interesting accounts // // Effects: updatees cached krbtgt information // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KdcAccountChangeNotification ( IN PSID DomainSid, IN SECURITY_DB_DELTA_TYPE DeltaType, IN SECURITY_DB_OBJECT_TYPE ObjectType, IN ULONG ObjectRid, IN OPTIONAL PUNICODE_STRING ObjectName, IN PLARGE_INTEGER ModifiedCount, IN PSAM_DELTA_DATA DeltaData OPTIONAL ) { NTSTATUS Status; // // We are only interested in the krbtgt account // if (ObjectRid != DOMAIN_USER_RID_KRBTGT) { return(STATUS_SUCCESS); } Status = SecData.UpdateKrbtgtTicketInfo(); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to update krbtgt ticket info: 0x%x\n",Status)); } return(Status); } #if DBG //////////////////////////////////////////////////////////////////////////// // // Some debugging functions. // void CSecurityData::DebugShowState(void) { TRACE(KDC, CSecurityData::DebugShowState, DEB_FUNCTION); PrintTime(DEB_TRACE, " TGT Ticket lifespan\t", &_KDC_TgtTicketLifespan ); PrintTime(DEB_TRACE, " Ticket Renew Span\t",&_KDC_TicketRenewSpan ); D_DebugLog((DEB_TRACE, " Blank Addresses?\t%s\n",(_KDC_Flags & AUTH_REQ_ALLOW_NOADDRESS ? "Yes" : "No"))); D_DebugLog((DEB_TRACE, " Proxies? \t%s\n", (_KDC_Flags & AUTH_REQ_ALLOW_PROXIABLE ? "Yes" : "No"))); D_DebugLog((DEB_TRACE, " Renewable? \t%s\n", (_KDC_Flags & AUTH_REQ_ALLOW_RENEWABLE ? "Yes" : "No"))); D_DebugLog((DEB_TRACE, " Postdated? \t%s\n", (_KDC_Flags & AUTH_REQ_ALLOW_POSTDATE ? "Yes" : "No"))); D_DebugLog((DEB_TRACE, " Forwardable? \t%s\n", (_KDC_Flags & AUTH_REQ_ALLOW_FORWARDABLE ? "Yes" : "No"))); } NTSTATUS CSecurityData::DebugGetState( DWORD * KDCFlags, TimeStamp * MaxLifespan, TimeStamp * MaxRenewSpan) { TRACE(KDC, CSecurityData::DebugGetState, DEB_FUNCTION); *KDCFlags = _KDC_Flags; *MaxLifespan = _KDC_TgtTicketLifespan; *MaxRenewSpan = _KDC_TicketRenewSpan; return(STATUS_SUCCESS); } NTSTATUS CSecurityData::DebugSetState( DWORD KDCFlags, TimeStamp MaxLifespan, TimeStamp MaxRenewSpan) { TRACE(KDC, CSecurityData::DebugSetState, DEB_FUNCTION); _KDC_Flags = KDC_AUTH_STATE(KDCFlags); _KDC_AuditEvents = KDC_AUDIT_STATE(KDCFlags); _KDC_TgtTicketLifespan = MaxLifespan; _KDC_TicketRenewSpan = MaxRenewSpan; return(STATUS_SUCCESS); } #endif // DBG