You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1311 lines
33 KiB
1311 lines
33 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// File: secdata.cxx
|
|
//
|
|
// Contents: Global data and methods on it.
|
|
//
|
|
//
|
|
// History:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include "kdcsvr.hxx"
|
|
|
|
#include <kpasswd.h>
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// 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: <none>
|
|
//
|
|
// 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: [email protected]
|
|
//
|
|
|
|
_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
|