|
|
//+-----------------------------------------------------------------------
//
// File: pac.cxx
//
// Contents: KDC Pac generation code.
//
//
// History: 16-Jan-93 WadeR Created.
//
//------------------------------------------------------------------------
#include "kdcsvr.hxx"
#include <pac.hxx>
#include "kdctrace.h"
#include "fileno.h"
#include <userall.h>
#include <sddl.h>
#include <utils.hxx>
#define FILENO FILENO_GETAS
SECURITY_DESCRIPTOR AuthenticationSD;
#ifndef DONT_SUPPORT_OLD_TYPES
#define KDC_PAC_KEYTYPE KERB_ETYPE_RC4_HMAC_OLD
#define KDC_PAC_CHECKSUM KERB_CHECKSUM_HMAC_MD5
#else
#define KDC_PAC_KEYTYPE KERB_ETYPE_RC4_HMAC
#define KDC_PAC_CHECKSUM KERB_CHECKSUM_HMAC_MD5
#endif
//+-------------------------------------------------------------------------
//
// Function: EnterApiCall
//
// Synopsis: Makes sure that the KDC service is initialized and running
// and won't terminate during the call.
//
// Effects: increments the CurrentApiCallers count.
//
// Arguments:
//
// Requires:
//
// Returns: STATUS_INVALID_SERVER_STATE - the KDC service is not
// running
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS EnterApiCall( VOID ) { NTSTATUS hrRet = STATUS_SUCCESS; EnterCriticalSection(&ApiCriticalSection); if (KdcState != Stopped) { CurrentApiCallers++; } else { hrRet = STATUS_INVALID_SERVER_STATE; } LeaveCriticalSection(&ApiCriticalSection); return(hrRet); }
//+-------------------------------------------------------------------------
//
// Function: LeaveApiCall
//
// Synopsis: Decrements the count of active calls and if the KDC is
// shutting down sets an event to let it continue.
//
// Effects: Deccrements the CurrentApiCallers count.
//
// Arguments:
//
// Requires:
//
// Returns: Nothing
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID LeaveApiCall( VOID ) { EnterCriticalSection(&ApiCriticalSection); CurrentApiCallers--;
if (KdcState == Stopped) { if (CurrentApiCallers == 0) { if (!SetEvent(hKdcShutdownEvent)) { D_DebugLog((DEB_ERROR,"Failed to set shutdown event from LeaveApiCall: 0x%d\n",GetLastError())); } else { UpdateStatus(SERVICE_STOP_PENDING); }
//
// Free any DS libraries in use
//
SecData.Cleanup();
if (KdcTraceRegistrationHandle != (TRACEHANDLE)0) { UnregisterTraceGuids( KdcTraceRegistrationHandle ); KdcTraceRegistrationHandle = (TRACEHANDLE)0; } } }
LeaveCriticalSection(&ApiCriticalSection); }
//+-------------------------------------------------------------------------
//
// Function: KdcInsertPacIntoAuthData
//
// Synopsis: Inserts the PAC into the auth data in the two places
// it lives - in the IF_RELEVANT portion & in the outer body
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcInsertPacIntoAuthData( IN PKERB_AUTHORIZATION_DATA AuthData, IN PKERB_IF_RELEVANT_AUTH_DATA IfRelevantData, IN PKERB_AUTHORIZATION_DATA PacAuthData, OUT PKERB_AUTHORIZATION_DATA * UpdatedAuthData ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_AUTHORIZATION_DATA LocalAuthData = NULL; PKERB_AUTHORIZATION_DATA LocalIfRelevantData = NULL; PKERB_AUTHORIZATION_DATA NewIfRelevantData = NULL; PKERB_AUTHORIZATION_DATA NewPacData = NULL; KERB_AUTHORIZATION_DATA TempPacData = {0}; PKERB_AUTHORIZATION_DATA NewAuthData = NULL; KERB_AUTHORIZATION_DATA TempOldPac = {0}; PKERB_AUTHORIZATION_DATA TempNextPointer,NextPointer;
NewPacData = (PKERB_AUTHORIZATION_DATA) MIDL_user_allocate(sizeof(KERB_AUTHORIZATION_DATA)); NewIfRelevantData = (PKERB_AUTHORIZATION_DATA) MIDL_user_allocate(sizeof(KERB_AUTHORIZATION_DATA));
if ((NewPacData == NULL) || (NewIfRelevantData == NULL)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlZeroMemory( NewPacData, sizeof(KERB_AUTHORIZATION_DATA) );
RtlZeroMemory( NewIfRelevantData, sizeof(KERB_AUTHORIZATION_DATA) );
//
// First build the IfRelevantData
//
// The general idea is to replace, in line, the relevant authorization
// data. This means (a) putting it into the IfRelevantData or making
// the IfRelevantData be PacAuthData, and (b) putting it into AuthData
// as well as changing the IfRelevant portions of that data
//
if (IfRelevantData != NULL) { LocalAuthData = KerbFindAuthDataEntry( KERB_AUTH_DATA_PAC, IfRelevantData );
if (LocalAuthData == NULL) { LocalIfRelevantData = PacAuthData; PacAuthData->next = IfRelevantData; } else { //
// Replace the pac in the if-relevant list with the
// new one.
//
TempOldPac = *LocalAuthData; LocalAuthData->value.auth_data.value = PacAuthData->value.auth_data.value; LocalAuthData->value.auth_data.length = PacAuthData->value.auth_data.length;
LocalIfRelevantData = IfRelevantData; } } else { //
// build a new if-relevant data
//
TempPacData = *PacAuthData; TempPacData.next = NULL; LocalIfRelevantData = &TempPacData; }
//
// Build a local if-relevant auth data
//
KerbErr = KerbPackData( &LocalIfRelevantData, PKERB_IF_RELEVANT_AUTH_DATA_PDU, (PULONG) &NewIfRelevantData->value.auth_data.length, &NewIfRelevantData->value.auth_data.value );
//
// fixup the old if-relevant list, if necessary
//
if (TempOldPac.value.auth_data.value != NULL) { *LocalAuthData = TempOldPac; }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
NewIfRelevantData->value.auth_data_type = KERB_AUTH_DATA_IF_RELEVANT;
*NewPacData = *PacAuthData;
//
// Zero this out so the old data doesn't get used
//
PacAuthData->value.auth_data.value = NULL; PacAuthData->value.auth_data.length = 0;
//
// Now we have a new if_relevant & a new pac for the outer auth-data list.
//
NewAuthData = NewIfRelevantData; NewIfRelevantData->next = NULL; NewIfRelevantData = NULL;
//
// Start building the list, first putting the non-pac entries at the end
//
NextPointer = AuthData; while (NextPointer != NULL) { if ((NextPointer->value.auth_data_type != KERB_AUTH_DATA_IF_RELEVANT) && (NextPointer->value.auth_data_type != KERB_AUTH_DATA_PAC)) { TempNextPointer = NextPointer->next; NextPointer->next = NULL;
KerbErr = KerbCopyAndAppendAuthData( &NewAuthData, NextPointer );
NextPointer->next = TempNextPointer;
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
NextPointer = NextPointer->next; }
*UpdatedAuthData = NewAuthData; NewAuthData = NULL;
Cleanup:
if (NewPacData != NULL) { KerbFreeAuthData(NewPacData); }
if (NewIfRelevantData != NULL) { KerbFreeAuthData(NewIfRelevantData); }
if (NewAuthData != NULL) { KerbFreeAuthData(NewAuthData); }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcBuildPacSidList
//
// Synopsis: Builds a list of SIDs in the PAC
//
// Effects:
//
// Arguments: UserInfo - validation information
// AddEveryone - add "Everyone" and "Authenticated User" SIDs?
// CrossOrganization - add "Other Org" SID?
// Sids - used to return the resulting SIDs
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcBuildPacSidList( IN PNETLOGON_VALIDATION_SAM_INFO3 UserInfo, IN BOOLEAN AddEveryone, IN BOOLEAN CrossOrganization, OUT PSAMPR_PSID_ARRAY Sids ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG Size = 0, i;
Sids->Count = 0; Sids->Sids = NULL;
if (UserInfo->UserId != 0) { Size += sizeof(SAMPR_SID_INFORMATION); }
if (AddEveryone) { Size += (sizeof(SAMPR_SID_INFORMATION) * 2); }
if (CrossOrganization) { Size += sizeof(SAMPR_SID_INFORMATION); }
Size += UserInfo->GroupCount * (ULONG)sizeof(SAMPR_SID_INFORMATION);
//
// If there are extra SIDs, add space for them
//
if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) { Size += UserInfo->SidCount * (ULONG)sizeof(SAMPR_SID_INFORMATION); }
Sids->Sids = (PSAMPR_SID_INFORMATION) MIDL_user_allocate( Size );
if ( Sids->Sids == NULL ) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlZeroMemory( Sids->Sids, Size );
//
// Start copying SIDs into the structure
//
i = 0;
//
// If the UserId is non-zero, then it contians the users RID.
//
if ( UserInfo->UserId ) {
Sids->Sids[0].SidPointer = (PRPC_SID) KerbMakeDomainRelativeSid( UserInfo->LogonDomainId, UserInfo->UserId );
if( Sids->Sids[0].SidPointer == NULL ) {
KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++; }
//
// Copy over all the groups passed as RIDs
//
for ( i=0; i < UserInfo->GroupCount; i++ ) {
Sids->Sids[Sids->Count].SidPointer = (PRPC_SID) KerbMakeDomainRelativeSid( UserInfo->LogonDomainId, UserInfo->GroupIds[i].RelativeId );
if( Sids->Sids[Sids->Count].SidPointer == NULL ) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++; }
//
// Add in the extra SIDs
//
//
// No need to allocate these, but...
//
if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) {
for ( i = 0; i < UserInfo->SidCount; i++ ) {
if (!NT_SUCCESS(KerbDuplicateSid( (PSID *) &Sids->Sids[Sids->Count].SidPointer, UserInfo->ExtraSids[i].Sid ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++; } }
//
// Add in everyone, and authenticated users.
//
if ( AddEveryone ) { if (!NT_SUCCESS(KerbDuplicateSid( (PSID*) &Sids->Sids[Sids->Count].SidPointer, GlobalEveryoneSid ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++;
if (!NT_SUCCESS(KerbDuplicateSid( (PSID*) &Sids->Sids[Sids->Count].SidPointer, GlobalAuthenticatedUserSid ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++; }
//
// Add in the "Other Organization" SID
//
if ( CrossOrganization ) { if (!NT_SUCCESS(KerbDuplicateSid( (PSID*) &Sids->Sids[Sids->Count].SidPointer, GlobalOtherOrganizationSid ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Sids->Count++; }
Cleanup:
if (!KERB_SUCCESS(KerbErr)) { if (Sids->Sids != NULL) { for (i = 0; i < Sids->Count ;i++ ) { if (Sids->Sids[i].SidPointer != NULL) { MIDL_user_free(Sids->Sids[i].SidPointer); } }
MIDL_user_free(Sids->Sids); Sids->Sids = NULL; Sids->Count = 0; } }
return KerbErr; }
//+-------------------------------------------------------------------------
//
// Function: KdcAddResourceGroupsToPac
//
// Synopsis: Queries SAM for resources groups and builds a new PAC with
// those groups
//
// Effects: Adds Domain Local and Universal groups **IN NATIVE MODE ONLY**
// Only called when you reach domain of target service.
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcAddResourceGroupsToPac( IN PPACTYPE OldPac, IN BOOLEAN DcTarget, IN ULONG ChecksumSize, OUT PPACTYPE * NewPac ) { NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; PPAC_INFO_BUFFER LogonInfo; ULONG Index; PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL; SAMPR_PSID_ARRAY SidList = {0}; PSAMPR_PSID_ARRAY ResourceGroups = NULL;
//
// First, find the logon information
//
LogonInfo = PAC_Find( OldPac, PAC_LOGON_INFO, NULL );
if (LogonInfo == NULL) { D_DebugLog((DEB_WARN,"No logon info for PAC - not adding resource groups\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Now unmarshall the validation information and build a list of sids
//
if (!NT_SUCCESS(PAC_UnmarshallValidationInfo( &ValidationInfo, LogonInfo->Data, LogonInfo->cbBufferSize))) { D_DebugLog((DEB_ERROR,"Failed to unmarshall validation info!\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
KerbErr = KdcBuildPacSidList( ValidationInfo, FALSE, FALSE, &SidList );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Call SAM to get the sids
//
Status = SamIGetResourceGroupMembershipsTransitive( GlobalAccountDomainHandle, &SidList, (DcTarget ? SAM_SERVICE_TARGET_IS_DC : 0), &ResourceGroups );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to get resource groups: 0x%x\n",Status)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Now build a new pac
//
Status = PAC_InitAndUpdateGroups( ValidationInfo, ResourceGroups, OldPac, NewPac );
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Cleanup:
if (ValidationInfo != NULL) { MIDL_user_free(ValidationInfo); }
if (SidList.Sids != NULL) { for (Index = 0; Index < SidList.Count ;Index++ ) { if (SidList.Sids[Index].SidPointer != NULL) { MIDL_user_free(SidList.Sids[Index].SidPointer); } }
MIDL_user_free(SidList.Sids); }
SamIFreeSidArray( ResourceGroups );
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcSignPac
//
// Synopsis: Signs a PAC by first checksumming it with the
// server's key and then signing that with the KDC key.
//
// Effects: Modifies the server sig & privsvr sig fields of the PAC
//
// Arguments: ServerInfo - Ticket info for the server, used
// for the initial signature
// PacData - An marshalled PAC.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcSignPac( IN PKERB_ENCRYPTION_KEY ServerKey, IN BOOLEAN AddResourceGroups, IN BOOLEAN DCTarget, IN OUT PUCHAR * PacData, IN PULONG PacSize ) { NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; PCHECKSUM_FUNCTION Check = NULL ; PCHECKSUM_BUFFER CheckBuffer = NULL; PPAC_INFO_BUFFER ServerBuffer; PPAC_INFO_BUFFER PrivSvrBuffer; PPAC_SIGNATURE_DATA ServerSignature; PPAC_SIGNATURE_DATA PrivSvrSignature; PKERB_ENCRYPTION_KEY EncryptionKey; PPACTYPE Pac = NULL, NewPac = NULL; ULONG LocalPacSize = 0; KDC_TICKET_INFO KdcTicketInfo = {0}; BOOL PacUnmarshalled = FALSE;
TRACE(KDC, KdcSignPac, DEB_FUNCTION);
KerbErr = SecData.GetKrbtgtTicketInfo(&KdcTicketInfo); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Locate the checksum used to sign the PAC.
//
Status = CDLocateCheckSum( (ULONG) KDC_PAC_CHECKSUM, &Check );
if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_SUMTYPE_NOSUPP; goto Cleanup; }
//
// Unmarshal the PAC in place so we can locate the signatuer buffers
//
Pac = (PPACTYPE) *PacData; LocalPacSize = *PacSize;
if (PAC_UnMarshal(Pac, LocalPacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PacUnmarshalled = TRUE; //
// If we are to add local groups, do so now
//
if (AddResourceGroups) { KerbErr = KdcAddResourceGroupsToPac( Pac, DCTarget, Check->CheckSumSize, &NewPac );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Pac = NewPac; LocalPacSize = PAC_GetSize(Pac); }
//
// Locate the signature buffers so the signature fields can be zeroed out
// before computing the checksum.
//
ServerBuffer = PAC_Find(Pac, PAC_SERVER_CHECKSUM, NULL ); DsysAssert(ServerBuffer != NULL); if (ServerBuffer == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
ServerSignature = (PPAC_SIGNATURE_DATA) ServerBuffer->Data; ServerSignature->SignatureType = (ULONG) KDC_PAC_CHECKSUM;
RtlZeroMemory( ServerSignature->Signature, PAC_CHECKSUM_SIZE(ServerBuffer->cbBufferSize) );
PrivSvrBuffer = PAC_Find(Pac, PAC_PRIVSVR_CHECKSUM, NULL ); DsysAssert(PrivSvrBuffer != NULL); if (PrivSvrBuffer == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PrivSvrSignature = (PPAC_SIGNATURE_DATA) PrivSvrBuffer->Data; PrivSvrSignature->SignatureType = (ULONG) KDC_PAC_CHECKSUM;
RtlZeroMemory( PrivSvrSignature->Signature, PAC_CHECKSUM_SIZE(PrivSvrBuffer->cbBufferSize) );
//
// Now remarshall the PAC to compute the checksum.
//
if (!PAC_ReMarshal(Pac, LocalPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PacUnmarshalled = FALSE; //
// Now compute the signatures on the PAC. First we compute the checksum
// of the whole PAC.
//
if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( ServerKey->keyvalue.value, ServerKey->keyvalue.length, NULL, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( ServerKey->keyvalue.value, ServerKey->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); }
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Check->Sum( CheckBuffer, LocalPacSize, (PUCHAR) Pac ); Check->Finalize( CheckBuffer, ServerSignature->Signature ); Check->Finish( &CheckBuffer );
//
// Now we've compute the server checksum - next compute the checksum
// of the server checksum using the KDC account.
//
//
// Get the key used to sign pacs.
//
EncryptionKey = KerbGetKeyFromList( KdcTicketInfo.Passwords, (ULONG) KDC_PAC_KEYTYPE );
if (EncryptionKey == NULL) { Status = SEC_E_ETYPE_NOT_SUPP; goto Cleanup; }
if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, NULL, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); }
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Check->Sum( CheckBuffer, Check->CheckSumSize, ServerSignature->Signature ); Check->Finalize( CheckBuffer, PrivSvrSignature->Signature ); Check->Finish( &CheckBuffer );
if (*PacData != (PBYTE) Pac) { MIDL_user_free(*PacData); *PacData = (PBYTE) Pac; *PacSize = LocalPacSize; }
Cleanup:
if ( PacUnmarshalled ) { if (!PAC_ReMarshal(Pac, LocalPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; } }
if ( ( CheckBuffer != NULL ) && ( Check != NULL ) ) { Check->Finish(&CheckBuffer); }
if (!KERB_SUCCESS(KerbErr) && (NewPac != NULL)) { MIDL_user_free(NewPac); }
FreeTicketInfo(&KdcTicketInfo);
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyPacSignature
//
// Synopsis: Verifies a PAC by checksumming it and comparing the result
// with the server checksum. In addition, if the pac wasn't
// created by another realm (server ticket info is not
// an interdomain account) verify the KDC signature on the
// pac.
//
// Effects:
//
// Arguments: ServerInfo - Ticket info for the server, used
// for the initial signature
// Pac - An unmarshalled PAC.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcVerifyPacSignature( IN PKERB_ENCRYPTION_KEY ServerKey, IN PKDC_TICKET_INFO ServerInfo, IN ULONG PacSize, IN PUCHAR PacData ) { NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; PCHECKSUM_FUNCTION Check = NULL ; PCHECKSUM_BUFFER CheckBuffer = NULL; PKERB_ENCRYPTION_KEY EncryptionKey = NULL; PPAC_INFO_BUFFER ServerBuffer; PPAC_INFO_BUFFER PrivSvrBuffer; PPAC_SIGNATURE_DATA ServerSignature; PPAC_SIGNATURE_DATA PrivSvrSignature; UCHAR LocalChecksum[20]; UCHAR LocalServerChecksum[20]; UCHAR LocalPrivSvrChecksum[20]; PPACTYPE Pac; KDC_TICKET_INFO KdcTicketInfo = {0}; BOOL PacUnmarshalled = FALSE;
TRACE(KDC, KdcVerifyPacSignature, DEB_FUNCTION);
Pac = (PPACTYPE) PacData;
if (PAC_UnMarshal(Pac, PacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PacUnmarshalled = TRUE; KerbErr = SecData.GetKrbtgtTicketInfo(&KdcTicketInfo); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Locate the two signatures, copy the checksum, and zero the value
// so the checksum won't include the old checksums.
//
ServerBuffer = PAC_Find(Pac, PAC_SERVER_CHECKSUM, NULL ); DsysAssert(ServerBuffer != NULL); if ((ServerBuffer == NULL) || (ServerBuffer->cbBufferSize < PAC_SIGNATURE_SIZE(0))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
ServerSignature = (PPAC_SIGNATURE_DATA) ServerBuffer->Data;
RtlCopyMemory( LocalServerChecksum, ServerSignature->Signature, PAC_CHECKSUM_SIZE(ServerBuffer->cbBufferSize) );
RtlZeroMemory( ServerSignature->Signature, PAC_CHECKSUM_SIZE(ServerBuffer->cbBufferSize) );
PrivSvrBuffer = PAC_Find(Pac, PAC_PRIVSVR_CHECKSUM, NULL ); DsysAssert(PrivSvrBuffer != NULL); if ((PrivSvrBuffer == NULL) || (PrivSvrBuffer->cbBufferSize < PAC_SIGNATURE_SIZE(0))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PrivSvrSignature = (PPAC_SIGNATURE_DATA) PrivSvrBuffer->Data;
RtlCopyMemory( LocalPrivSvrChecksum, PrivSvrSignature->Signature, PAC_CHECKSUM_SIZE(PrivSvrBuffer->cbBufferSize) );
RtlZeroMemory( PrivSvrSignature->Signature, PAC_CHECKSUM_SIZE(PrivSvrBuffer->cbBufferSize) );
//
// Remarshal the pac so we can checksum it.
//
if (!PAC_ReMarshal(Pac, PacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PacUnmarshalled = FALSE; //
// Now compute the signatures on the PAC. First we compute the checksum
// of the validation information using the server's key.
//
//
// Locate the checksum used to sign the PAC.
//
Status = CDLocateCheckSum( ServerSignature->SignatureType, &Check );
if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_SUMTYPE_NOSUPP; goto Cleanup; }
if (Check->CheckSumSize > sizeof(LocalChecksum)) { DsysAssert(Check->CheckSumSize <= sizeof(LocalChecksum)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// if available use the Ex2 version for keyed checksums where checksum
// must be passed in on verification
//
if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( ServerKey->keyvalue.value, ServerKey->keyvalue.length, LocalServerChecksum, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( ServerKey->keyvalue.value, ServerKey->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); }
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Check->Sum( CheckBuffer, PacSize, PacData ); Check->Finalize( CheckBuffer, LocalChecksum ); Check->Finish( &CheckBuffer );
if (Check->CheckSumSize != PAC_CHECKSUM_SIZE(ServerBuffer->cbBufferSize) || !RtlEqualMemory( LocalChecksum, LocalServerChecksum, Check->CheckSumSize)) { DebugLog((DEB_ERROR, "Pac was modified - server checksum doesn't match\n")); KerbErr = KRB_AP_ERR_MODIFIED; goto Cleanup; }
//
// If the service wasn't the KDC and it wasn't an interdomain account
// verify the KDC checksum.
//
if ((ServerInfo->UserId == DOMAIN_USER_RID_KRBTGT) || ((ServerInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) != 0)) { goto Cleanup; }
//
// Get the key used to sign pacs.
//
EncryptionKey = KerbGetKeyFromList( KdcTicketInfo.Passwords, (ULONG) KDC_PAC_KEYTYPE );
if (EncryptionKey == NULL) { Status = SEC_E_ETYPE_NOT_SUPP; goto Cleanup; }
//
// Locate the checksum used to sign the PAC.
//
Status = CDLocateCheckSum( PrivSvrSignature->SignatureType, &Check );
if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_SUMTYPE_NOSUPP; goto Cleanup; }
//
// if available use the Ex2 version for keyed checksums where checksum
// must be passed in on verification
//
if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, LocalPrivSvrChecksum, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); }
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Check->Sum( CheckBuffer, Check->CheckSumSize, LocalServerChecksum ); Check->Finalize( CheckBuffer, LocalChecksum ); Check->Finish( &CheckBuffer );
if ((Check->CheckSumSize != PAC_CHECKSUM_SIZE(PrivSvrBuffer->cbBufferSize)) || !RtlEqualMemory( LocalChecksum, LocalPrivSvrChecksum, Check->CheckSumSize)) { DebugLog((DEB_ERROR, "Pac was modified - privsvr checksum doesn't match\n")); KerbErr = KRB_AP_ERR_MODIFIED; goto Cleanup; }
Cleanup:
if ( PacUnmarshalled ) { if (!PAC_ReMarshal(Pac, PacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; } }
if (KerbErr == KRB_AP_ERR_MODIFIED) { LPWSTR AccountName = NULL; AccountName = (LPWSTR) MIDL_user_allocate(ServerInfo->AccountName.Length + sizeof(WCHAR)); //
// if the allocation fails don't log the name (leave it NULL)
//
if (NULL != AccountName) { RtlCopyMemory( AccountName, ServerInfo->AccountName.Buffer, ServerInfo->AccountName.Length ); }
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_PAC_VERIFICATION_FAILURE, sizeof(ULONG), &KerbErr, 1, AccountName );
if (NULL != AccountName) { MIDL_user_free(AccountName); } }
if ( ( CheckBuffer != NULL ) && ( Check != NULL ) ) { Check->Finish(&CheckBuffer); }
FreeTicketInfo(&KdcTicketInfo);
return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Name: KdcGetPacAuthData
//
// Synopsis: Creates a PAC for the specified client, encrypts it with the
// server's key, and packs it into a KERB_AUTHORIZATON_DATA
//
// Arguments: UserInfo - Information about user
// GroupMembership - Users group memberships
// ServerKey - Key of server, used for signing
// CredentialKey - if present & valid, used to encrypt supp. creds
// AddResourceGroups - if TRUE, resources groups will be included
// EncryptedTicket - Optional ticke to tie PAC to
// S4UTicketInfo - used only when inserting initial S4U2self auth
// data into the ticket. causes the S4U2self
// target to be stored inside the PAC
// PacAuthData - Receives a KERB_AUTHORIZATION_DATA of type
// KERB_AUTH_DATA_PAC, containing a PAC.
//
// Notes: PacAuthData should be freed with KerbFreeAuthorizationData.
//
//+---------------------------------------------------------------------------
KERBERR KdcGetPacAuthData( IN PUSER_INTERNAL6_INFORMATION UserInfo, IN PSID_AND_ATTRIBUTES_LIST GroupMembership, IN PKERB_ENCRYPTION_KEY ServerKey, IN PKERB_ENCRYPTION_KEY CredentialKey, IN BOOLEAN AddResourceGroups, IN PKERB_ENCRYPTED_TICKET EncryptedTicket, IN OPTIONAL PKDC_S4U_TICKET_INFO S4UClientInfo, OUT PKERB_AUTHORIZATION_DATA * PacAuthData, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; PACTYPE *pNewPac = NULL; KERB_AUTHORIZATION_DATA AuthorizationData = {0}; ULONG PacSize, NameType; PCHECKSUM_FUNCTION Check; NTSTATUS Status; UNICODE_STRING ClientName = {0}; PKERB_INTERNAL_NAME KdcName = NULL; TimeStamp ClientId;
TRACE(KDC, KdcGetPacAuthData, DEB_FUNCTION);
Status = CDLocateCheckSum( (ULONG) KDC_PAC_CHECKSUM, &Check );
if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_SUMTYPE_NOSUPP; FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__); goto Cleanup; }
KerbConvertGeneralizedTimeToLargeInt( &ClientId, &EncryptedTicket->authtime, 0 // no usec
);
//
// Put the S4U client in the pac verifier. For S4USelf, we use
// user@domain to keep W2K servers / kdcs from allowing xrealm tgts
// w/ s4u pacs
//
if (ARGUMENT_PRESENT(S4UClientInfo)) { KerbErr = KerbConvertKdcNameToString( &ClientName, S4UClientInfo->PACCName, (((S4UClientInfo->Flags & TI_REQUESTOR_THIS_REALM) != 0) ? NULL : &S4UClientInfo->PACCRealm ) ); } else // use the ticket
{ KerbErr = KerbConvertPrincipalNameToString( &ClientName, &NameType, &EncryptedTicket->client_name ); }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = GetPacAndSuppCred( UserInfo, GroupMembership, Check->CheckSumSize, // leave space for signature
CredentialKey, &ClientId, &ClientName, &pNewPac, pExtendedError );
if (!KERB_SUCCESS(KerbErr)) { D_DebugLog(( DEB_WARN, "GetPAC: Can't get PAC or supp creds: 0x%x \n", KerbErr )); goto Cleanup; }
//
// The PAC is going to be double-encrypted. This is done by having the
// PAC in an EncryptedData, and having that EncryptedData in a AuthData
// as part of an AuthDataList (along with the rest of the supp creds).
// Finally, the entire list is encrypted.
//
// KERB_AUTHORIZATION_DATA containing {
// PAC
//
// }
//
//
// First build inner encrypted data
//
PacSize = PAC_GetSize( pNewPac );
AuthorizationData.value.auth_data_type = KERB_AUTH_DATA_PAC; AuthorizationData.value.auth_data.length = PacSize; AuthorizationData.value.auth_data.value = (PUCHAR) MIDL_user_allocate(PacSize); if (AuthorizationData.value.auth_data.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
PAC_Marshal( pNewPac, PacSize, AuthorizationData.value.auth_data.value );
//
// Compute the signatures
//
KerbErr = KdcSignPac( ServerKey, AddResourceGroups, FALSE, // this is a TGT...
&AuthorizationData.value.auth_data.value, (PULONG) &AuthorizationData.value.auth_data.length );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Create the auth data to return
//
KerbErr = KdcInsertPacIntoAuthData( NULL, // no original auth data
NULL, // no if-relevant auth data
&AuthorizationData, PacAuthData );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to insert pac into new auth data: 0x%x\n", KerbErr)); goto Cleanup; }
Cleanup:
if (AuthorizationData.value.auth_data.value != NULL) { MIDL_user_free(AuthorizationData.value.auth_data.value); }
if (pNewPac != NULL) { MIDL_user_free(pNewPac); }
KerbFreeString(&ClientName); KerbFreeKdcName(&KdcName); return(KerbErr);
}
//+-------------------------------------------------------------------------
//
// Function: KdcGetUserPac
//
// Synopsis: Function for external users to get the PAC for a user
//
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
extern "C" NTSTATUS KdcGetUserPac( IN PUNICODE_STRING UserName, OUT PPACTYPE * Pac, OUT PUCHAR * SupplementalCredentials, OUT PULONG SupplementalCredSize, OUT PKERB_EXT_ERROR pExtendedError ) { KDC_TICKET_INFO TicketInfo; PUSER_INTERNAL6_INFORMATION UserInfo = NULL; SID_AND_ATTRIBUTES_LIST GroupMembership; NTSTATUS Status; KERBERR KerbErr;
TRACE(KDC, KdcGetUserPac, DEB_FUNCTION);
*SupplementalCredentials = NULL; *SupplementalCredSize = 0;
RtlZeroMemory( &TicketInfo, sizeof(KDC_TICKET_INFO) );
RtlZeroMemory( &GroupMembership, sizeof(SID_AND_ATTRIBUTES_LIST) );
Status = EnterApiCall();
if (!NT_SUCCESS(Status)) { return(Status); }
//
// Get the account information
//
KerbErr = KdcGetTicketInfo( UserName, 0, // no flags
FALSE, // do not restrict user accounts (user2user)
NULL, // no principal name
NULL, // no realm
&TicketInfo, pExtendedError, NULL, // no user handle
USER_ALL_GET_PAC_AND_SUPP_CRED, 0L, // no extended fields
&UserInfo, &GroupMembership );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN,"Failed to get ticket info for user %wZ: 0x%x\n", UserName->Buffer, KerbErr)); Status = KerbMapKerbError(KerbErr);
goto Cleanup; }
//
// Now get the PAC and supplemental credentials
//
KerbErr = GetPacAndSuppCred( UserInfo, &GroupMembership, 0, // no signature space
NULL, // no credential key
NULL, // no client ID
NULL, // no client name
Pac, pExtendedError );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to get PAC for user %wZ : 0x%x\n", UserName->Buffer,KerbErr));
Status = KerbMapKerbError(KerbErr); goto Cleanup; }
Cleanup:
SamIFree_UserInternal6Information( UserInfo ); SamIFreeSidAndAttributesList(&GroupMembership); FreeTicketInfo(&TicketInfo);
LeaveApiCall();
return Status; }
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyPac
//
// Synopsis: Function for kerberos to pass through a pac signature
// to be verified.
//
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
extern "C" NTSTATUS KdcVerifyPac( IN ULONG ChecksumSize, IN PUCHAR Checksum, IN ULONG SignatureType, IN ULONG SignatureSize, IN PUCHAR Signature ) { NTSTATUS Status; KERBERR KerbErr; PCHECKSUM_FUNCTION Check; PCHECKSUM_BUFFER CheckBuffer = NULL; UCHAR LocalChecksum[20]; PKERB_ENCRYPTION_KEY EncryptionKey = NULL; KDC_TICKET_INFO KdcTicketInfo = {0};
TRACE(KDC, KdcVerifyPac, DEB_FUNCTION);
Status = EnterApiCall();
if (!NT_SUCCESS(Status)) { return(Status); }
KerbErr = SecData.GetKrbtgtTicketInfo(&KdcTicketInfo);
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Get the key used to sign pacs.
//
EncryptionKey = KerbGetKeyFromList( KdcTicketInfo.Passwords, (ULONG) KDC_PAC_KEYTYPE );
if (EncryptionKey == NULL) { Status = SEC_E_ETYPE_NOT_SUPP; goto Cleanup; }
Status = CDLocateCheckSum( SignatureType, &Check );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
if (Check->CheckSumSize > sizeof(LocalChecksum)) { DsysAssert(Check->CheckSumSize <= sizeof(LocalChecksum)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// if available use the Ex2 version for keyed checksums where checksum
// must be passed in on verification
//
if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, Signature, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( EncryptionKey->keyvalue.value, EncryptionKey->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &CheckBuffer ); }
if (!NT_SUCCESS(Status)) { goto Cleanup;
}
Check->Sum( CheckBuffer, ChecksumSize, Checksum );
Check->Finalize( CheckBuffer, LocalChecksum );
Check->Finish(&CheckBuffer);
//
// Now compare the local checksum to the supplied checksum.
//
if (Check->CheckSumSize != SignatureSize) { Status = STATUS_LOGON_FAILURE; goto Cleanup; }
if (!RtlEqualMemory( LocalChecksum, Signature, Check->CheckSumSize )) { DebugLog((DEB_ERROR,"Checksum on the PAC does not match!\n")); Status = STATUS_LOGON_FAILURE; goto Cleanup; }
Cleanup:
if (Status == STATUS_LOGON_FAILURE) { PUNICODE_STRING OwnName = NULL; //
// since this call should only be made by pass through callback
// this signature should be our own
//
OwnName = SecData.KdcFullServiceDnsName();
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_PAC_VERIFICATION_FAILURE, 0, NULL, 1, // number of strings
OwnName->Buffer ); }
FreeTicketInfo(&KdcTicketInfo); LeaveApiCall();
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KdcUpdateAndValidateS4UProxyPAC
//
// Synopsis: Validates your target name from original pac, and updates
// existing info.
//
// Effects:
//
// Arguments:
// CLientId - Auth time of ticket.
// CName - Client name to put into verifier.
// Pac - **UNMARSHALLED** PAC, freed in this function, and
// rebuilt.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcReplacePacVerifier( PTimeStamp ClientId, PUNICODE_STRING CName, IN PPACTYPE OldPac, OUT PPACTYPE *NewPac ) {
ULONG cbBytes = 0; ULONG cPacBuffers = 0; PPAC_INFO_BUFFER Verifier = NULL; PPACTYPE pNewPac = NULL; PBYTE pDataStore; NTSTATUS Status = STATUS_SUCCESS; ULONG Index, iBuffer = 0;
*NewPac = NULL;
for (Index = 0; Index < OldPac->cBuffers ; Index++ ) { if (OldPac->Buffers[Index].ulType != PAC_CLIENT_INFO_TYPE) { cbBytes += ROUND_UP_COUNT(OldPac->Buffers[Index].cbBufferSize,ALIGN_QUAD); cPacBuffers++; } }
Status = KdcBuildPacVerifier( ClientId, CName, &Verifier );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "BuildPacVerifier failed\n")); goto Cleanup; }
cPacBuffers++; cbBytes += ROUND_UP_COUNT(Verifier->cbBufferSize, ALIGN_QUAD);
//
// We need space for the PAC structure itself. Because the PAC_INFO_BUFFER
// is defined to be an array, a sizeof(PAC) already includes the
// size of ANYSIZE_ARRAY PAC_INFO_BUFFERs so we can subtract some bytes off.
//
cbBytes += sizeof(PACTYPE) + (cPacBuffers - ANYSIZE_ARRAY) * sizeof(PAC_INFO_BUFFER);
cbBytes = ROUND_UP_COUNT( cbBytes, ALIGN_QUAD );
pNewPac = (PPACTYPE) MIDL_user_allocate( cbBytes ); if (pNewPac == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } pNewPac->cBuffers = cPacBuffers;
pDataStore = (PBYTE)&(pNewPac->Buffers[pNewPac->cBuffers]); pDataStore = (PBYTE) ROUND_UP_POINTER( pDataStore, ALIGN_QUAD );
//
// Copy the data over
//
for (Index = 0; Index < OldPac->cBuffers ; Index++ ) { if (OldPac->Buffers[Index].ulType != PAC_CLIENT_INFO_TYPE) { pNewPac->Buffers[iBuffer].ulType = OldPac->Buffers[Index].ulType; pNewPac->Buffers[iBuffer].cbBufferSize = OldPac->Buffers[Index].cbBufferSize; pNewPac->Buffers[iBuffer].Data = pDataStore;
RtlCopyMemory( pDataStore, OldPac->Buffers[Index].Data, OldPac->Buffers[Index].cbBufferSize );
pDataStore += pNewPac->Buffers[iBuffer].cbBufferSize; pDataStore = (PBYTE) ROUND_UP_POINTER( pDataStore, ALIGN_QUAD ); iBuffer ++; } }
//
// Finally copy over the pac verifier.
//
pNewPac->Buffers[iBuffer].ulType = PAC_CLIENT_INFO_TYPE; pNewPac->Buffers[iBuffer].cbBufferSize = Verifier->cbBufferSize; pNewPac->Buffers[iBuffer].Data = pDataStore;
RtlCopyMemory( pDataStore, Verifier->Data, Verifier->cbBufferSize );
*NewPac = pNewPac; pNewPac = NULL; Cleanup:
if ( pNewPac ) { MIDL_user_free( pNewPac); }
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: KdcUpdateAndValidateS4UProxyPAC
//
// Synopsis: Validates your target name from original pac, and updates
// existing info.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
#define DEB_TEST_CODE 0xff000000
#define DEB_TEST_CODE2 0x00ff0000
KERBERR KdcUpdateAndVerifyS4UPacVerifier( IN PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OUT PUCHAR *PacData, IN OUT PULONG PacSize ) {
PPAC_INFO_BUFFER Verifier = NULL; PPACTYPE OldPac; ULONG OldPacSize; PPACTYPE NewPac = NULL; NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE;
PPAC_CLIENT_INFO ClientInfo = NULL; TimeStamp ClientId; UNICODE_STRING VerifierNames = {0}; UNICODE_STRING VerifierCName = {0}; UNICODE_STRING PreauthCName = {0}; UNICODE_STRING VerifierCRealm = {0}; PWSTR Realm = NULL; PWSTR CName = NULL; PPACTYPE RemarshalPac = NULL; ULONG RemarshalPacSize = 0;
LONG i;
OldPac = (PPACTYPE) *PacData; OldPacSize = *PacSize; if (PAC_UnMarshal(OldPac, OldPacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KDC_ERR_POLICY; goto Cleanup; } //
// Must remember to remarshal the PAC prior to returning
//
RemarshalPac = OldPac; RemarshalPacSize = OldPacSize;
Verifier = PAC_Find( OldPac, PAC_CLIENT_INFO_TYPE, NULL );
if ( Verifier == NULL ) { DebugLog((DEB_ERROR, "Missing PAC verifier in S4U Tickets\n")); DsysAssert(FALSE); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
if ( Verifier->cbBufferSize < sizeof(PAC_CLIENT_INFO) ) { D_DebugLog((DEB_ERROR, "Clientinfo is too small: %d instead of %d\n", Verifier->cbBufferSize, sizeof(PAC_CLIENT_INFO))); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
ClientInfo = (PPAC_CLIENT_INFO) Verifier->Data; if ((ClientInfo->NameLength - ANYSIZE_ARRAY * sizeof(WCHAR) + sizeof(PPAC_CLIENT_INFO)) > Verifier->cbBufferSize) { KerbErr = KDC_ERR_POLICY; goto Cleanup; }
KerbConvertGeneralizedTimeToLargeInt( &ClientId, &S4UTicketInfo->EvidenceTicket->authtime, 0 // no usec
);
if (!RtlEqualMemory( &ClientId, &ClientInfo->ClientId, sizeof(TimeStamp) )) { D_DebugLog((DEB_ERROR, "Client IDs don't match.\n")); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
//
// Check the name now - for s4uself requests, the name is going
// to be cname@crealm. This was inserted by the KDC of crealm during
// the initial processing of the pa-for-user.
//
// Now, we have to substite in the "w2k" version of the pac verifier,
// as we're granting a service ticket to a s4uself request from our realm.
// So, there are a couple of checks to be done here:
//
//
// 1. Does the PA-FOR-USER cname match that in the verifier?
// 2. Does the PA-FOR-USER crealm match that in the verifier?
// 3. Does the validation info (PAC) cname match that in the verifier?
//
// If so, add in a W2K pac verfier.
//
VerifierNames.Length = ClientInfo->NameLength; VerifierNames.MaximumLength = ClientInfo->NameLength + sizeof(WCHAR);
SafeAllocaAllocate( VerifierNames.Buffer, VerifierNames.MaximumLength); if ( VerifierNames.Buffer == NULL ) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( VerifierNames.Buffer, ClientInfo->Name, VerifierNames.Length );
VerifierNames.Buffer[(VerifierNames.Length / sizeof(WCHAR))] = L'\0';
//
// Find the @ sign, and split. Search from the end of the string.
//
i = VerifierNames.Length / sizeof(WCHAR);
while (i > 0) { if (VerifierNames.Buffer[i] == L'@') { VerifierNames.Buffer[i] = L'\0'; if ( i < (LONG) (VerifierNames.Length / sizeof(WCHAR)) ) { Realm = &VerifierNames.Buffer[i + 1]; CName = VerifierNames.Buffer; break; } }
i--; } if ( Realm == NULL ) { DebugLog((DEB_ERROR, "S4U Pac verifier missing @ sign\n")); DsysAssert(FALSE); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
RtlInitUnicodeString( &VerifierCRealm, Realm );
RtlInitUnicodeString( &VerifierCName, CName );
if (!RtlEqualUnicodeString( &VerifierCRealm, &S4UTicketInfo->PACCRealm, TRUE )) { DebugLog((DEB_ERROR, "pa-for-user != pac verfier realm\n")); DsysAssert(FALSE); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
KerbErr = KerbConvertKdcNameToString( &PreauthCName, S4UTicketInfo->PACCName, NULL );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
if (!RtlEqualUnicodeString( &PreauthCName, &VerifierCName, TRUE )) { DebugLog((DEB_ERROR, "pa-for-user != pac verifier cname\n")); DsysAssert(FALSE); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
//
// Now check the validation information. Fester - netbios and dns names preclude this from
// working...
//
/*LogonInfo = PAC_Find(
OldPac, PAC_LOGON_INFO, NULL );
if ( LogonInfo == NULL ) { KerbErr = KRB_ERR_GENERIC; Status = STATUS_INVALID_PARAMETER; goto Cleanup; } Status = PAC_UnmarshallValidationInfo( &LocalValidationInfo, LogonInfo->Data, LogonInfo->cbBufferSize ); if ( !NT_SUCCESS( Status )) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if (!RtlEqualUnicodeString( &LocalValidationInfo->EffectiveName, &VerifierCName, TRUE )) { DebugLog((DEB_ERROR, "pa-for-user != logon info cname\n")); DsysAssert(FALSE); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
//
// Fester - how do we use the Netbios domain name in Validation info
// to validate S4U Pac verifier?
// */
//
// Cool - everything looks good. Now remove the S4U pac verifier, and
// add in the W2K style verfier.
//
Status = KdcReplacePacVerifier( &ClientId, &VerifierCName, OldPac, &NewPac );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "KdcAddPacVerifier failed\n")); DsysAssert(FALSE); goto Cleanup; }
MIDL_user_free( OldPac );
RemarshalPacSize = PAC_GetSize(NewPac); RemarshalPac = NewPac; NewPac = NULL;
Cleanup:
if ( RemarshalPac != NULL ) { if (!PAC_ReMarshal(RemarshalPac, RemarshalPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; }
*PacData = (PBYTE) RemarshalPac; *PacSize = RemarshalPacSize; }
SafeAllocaFree( VerifierNames.Buffer ); if (NewPac != NULL) { MIDL_user_free(NewPac); }
KerbFreeString(&PreauthCName);
return KerbErr;
}
//+-------------------------------------------------------------------------
//
// Function: KdcUpdateAndValidateS4UProxyPAC
//
// Synopsis: Validates your target name from original pac, and updates
// existing info.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcUpdateAndValidateS4UProxyPAC( IN PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OUT PUCHAR *PacData, IN OUT PULONG PacSize, OUT OPTIONAL PS4U_DELEGATION_INFO* S4UDelegationInfo ) { PPAC_INFO_BUFFER MarshalledDelegationInfo; PS4U_DELEGATION_INFO DelegationInfo = NULL; S4U_DELEGATION_INFO NewDelegInfo = {0}; BYTE* FinalDelegInfoMarshalled = NULL; ULONG FinalDelegInfoMarshalledSize = 0; PPACTYPE OldPac; ULONG OldPacSize; PPACTYPE NewPac = NULL; ULONG NewPacSize = NULL; PUNICODE_STRING TransittedService = NULL; UNICODE_STRING tmpstring = {0}; PUNICODE_STRING NewTargetName = NULL; NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE;
OldPac = (PPACTYPE) *PacData; OldPacSize = *PacSize;
if (PAC_UnMarshal(OldPac, OldPacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
MarshalledDelegationInfo = PAC_Find( OldPac, PAC_DELEGATION_INFO, NULL );
if ( MarshalledDelegationInfo == NULL ) { //
// If this is using S4U, and we don't have delegation info, bomb out
// here - someone's ripped out the delegation info while we were transiting.
//
if (( S4UTicketInfo->Flags & TI_PRXY_REQUESTOR_THIS_REALM) == 0 ) { DebugLog((DEB_ERROR, "Missing delegation info while transitting %p\n", S4UTicketInfo)); DsysAssert(FALSE); KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; goto Cleanup; } //
// Time to create one.
//
if (!NT_SUCCESS(KerbDuplicateString( &NewDelegInfo.S4U2proxyTarget, &S4UTicketInfo->TargetName ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
DelegationInfo = &NewDelegInfo;
D_DebugLog((DEB_T_PAC, "KdcUpdateAndValidateS4UProxyPAC create new S4uDelegateInfo: target %wZ\n", &NewDelegInfo.S4U2proxyTarget)); } else { if (!NT_SUCCESS( PAC_UnmarshallS4UDelegationInfo( &DelegationInfo, MarshalledDelegationInfo->Data, MarshalledDelegationInfo->cbBufferSize ))) { D_DebugLog((DEB_ERROR, "Failed to unmarshall S4U delgation info\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// If the target's in our realm, we need to verify that the target name
// in the PAC == the target name being requested.
//
// However, this only applies to validating the PAC in xrealm TGTs.
// If the requestor is in our realm, we need to create a targetname entry.
//
if (( S4UTicketInfo->Flags & TI_PRXY_REQUESTOR_THIS_REALM ) != 0 ) { NewTargetName = &S4UTicketInfo->TargetName; } else if (( S4UTicketInfo->Flags & TI_TARGET_OUR_REALM ) != 0 ) { if (!RtlEqualUnicodeString( &S4UTicketInfo->TargetName, &DelegationInfo->S4U2proxyTarget, TRUE )) { D_DebugLog((DEB_ERROR, "Wrong S4UProxytarget %wZ %wZ\n", &S4UTicketInfo->TargetName, &DelegationInfo->S4U2proxyTarget)); KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; goto Cleanup; } }
D_DebugLog((DEB_T_PAC, "KdcUpdateAndValidateS4UProxyPAC add S4uDelegateInfo: target %wZ, flags %#x\n", NewTargetName, S4UTicketInfo->Flags)); }
//
// We're in the S4U requestor's realm - add in requestor's name into
// PAC.
//
if (( S4UTicketInfo->Flags & TI_PRXY_REQUESTOR_THIS_REALM) != 0 ) { KerbErr = KerbConvertKdcNameToString( &tmpstring, S4UTicketInfo->RequestorServiceName, ((S4UTicketInfo->RequestorServiceName->NameType == KRB_NT_ENTERPRISE_PRINCIPAL) && (S4UTicketInfo->RequestorServiceName->NameCount == 1)) ? NULL : &S4UTicketInfo->RequestorServiceRealm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
TransittedService = &tmpstring;
D_DebugLog((DEB_T_PAC, "KdcUpdateAndValidateS4UProxyPAC add ts %wZ\n", TransittedService)); }
#if DBG
if (DelegationInfo) { D_DebugLog((DEB_T_PAC, "KdcUpdateAndValidateS4UProxyPAC target %wZ\n", &DelegationInfo->S4U2proxyTarget)); for ( ULONG i = 0; i < DelegationInfo->TransitedListSize; i++ ) { D_DebugLog((DEB_T_PAC, "KdcUpdateAndValidateS4UProxyPAC existing ts %#x: %wZ\n", i, &DelegationInfo->S4UTransitedServices[i])); } }
#endif // DBG
Status = PAC_InitAndUpdateTransitedService( DelegationInfo, TransittedService, NewTargetName, OldPac, &NewPac, &FinalDelegInfoMarshalledSize, &FinalDelegInfoMarshalled );
if (!NT_SUCCESS( Status )) { KerbErr = KRB_ERR_GENERIC; D_DebugLog((DEB_ERROR, "PacInit&UPdatedTransitedService fail - %x\n", Status)); goto Cleanup; }
NewPacSize = PAC_GetSize(NewPac);
if (!PAC_ReMarshal(NewPac, NewPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if (S4UDelegationInfo) { Status = UnmarshalS4UDelegationInformation( FinalDelegInfoMarshalledSize, FinalDelegInfoMarshalled, S4UDelegationInfo );
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; DebugLog((DEB_ERROR, "KdcUpdateAndValidateS4UProxyPAC failed to unmarshall S4U delgation info %#x\n", Status)); goto Cleanup; } }
if (*PacData != (PBYTE)NewPac) { MIDL_user_free(*PacData); *PacData = (PBYTE) NewPac; NewPac = NULL; *PacSize = NewPacSize; }
Cleanup:
KerbFreeString(&NewDelegInfo.S4U2proxyTarget);
if (( DelegationInfo != NULL ) && ( DelegationInfo != &NewDelegInfo )) { MIDL_user_free( DelegationInfo ); }
if (FinalDelegInfoMarshalled != NULL) { MIDL_user_free(FinalDelegInfoMarshalled); }
KerbFreeString( &tmpstring );
if (NewPac != NULL) { MIDL_user_free(NewPac); }
return (KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcFilterSids
//
// Synopsis: Function that just call LsaIFilterSids. Pulled into this function
// for more widespread use than KdcCheckPacForSidFiltering.
//
// Effects:
//
// Arguments: ServerInfo structure containing attributes of the trust
// ValidationInfo authorization information to filter
//
// Requires:
//
// Returns: See LsaIFilterSids
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcFilterSids( IN PKDC_TICKET_INFO ServerInfo, IN PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo ) { NTSTATUS Status = STATUS_SUCCESS; PUNICODE_STRING TrustedForest = NULL;
if ((ServerInfo->TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0) { TrustedForest = &(ServerInfo->TrustedForest); D_DebugLog((DEB_TRACE, "Filtering Sids for forest %wZ\n", TrustedForest)); }
if ( ServerInfo->TrustSid != NULL || ServerInfo->TrustType == TRUST_TYPE_MIT || ( ServerInfo->TrustAttributes & TRUST_ATTRIBUTE_CROSS_ORGANIZATION ) != 0 ) { Status = LsaIFilterSids( TrustedForest, // Pass domain name here
TRUST_DIRECTION_OUTBOUND, ServerInfo->TrustType, ServerInfo->TrustAttributes, ServerInfo->TrustSid, NetlogonValidationSamInfo2, ValidationInfo, NULL, NULL, NULL );
if (!NT_SUCCESS(Status)) { //
// Create an audit log if it looks like the SID has been tampered with
//
if ((STATUS_DOMAIN_TRUST_INCONSISTENT == Status) && SecData.AuditKdcEvent(KDC_AUDIT_TGS_FAILURE)) { DWORD Dummy = 0;
KdcLsaIAuditTgsEvent( SE_AUDITID_TGS_TICKET_REQUEST, &ValidationInfo->EffectiveName, &ValidationInfo->LogonDomainName, NULL, &ServerInfo->AccountName, NULL, &Dummy, (PULONG) &Status, NULL, NULL, // no preauth type
GET_CLIENT_ADDRESS(NULL), NULL, // no logon guid
NULL ); }
DebugLog((DEB_ERROR,"Failed to filter SIDS (LsaIFilterSids): 0x%x\n",Status)); } }
return Status; }
//+-------------------------------------------------------------------------
//
// Function: KdcFilterNamespace
//
// Synopsis: Function that just call lsaifiltersids. Pulled into this function
// for more widespread use than KdcCheckPacForSidFiltering.
//
// Effects:
//
// Arguments: ServerInfo structure containing attributes of the trust
// ClientRealm namespace to filter
//
// Requires:
//
// Returns: KDC_ERR_NONE namespace is good to go
// KDC_ERR_POLICY filtering policy rejects this namespace
// KDC_ERR_GENERIC unexpected error (out of memory, etc)
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcFilterNamespace( IN PKDC_TICKET_INFO ServerInfo, IN KERB_REALM ClientRealm, OUT PKERB_EXT_ERROR pExtendedError ) { NTSTATUS Status; KERBERR KerbErr; UNICODE_STRING ClientRealmU = {0};
if ( ServerInfo == NULL || ServerInfo->TrustType == 0 || ( ServerInfo->TrustSid == NULL && ServerInfo->TrustType != TRUST_TYPE_MIT )) { //
// Not going over a trust, simply succeed
//
return KDC_ERR_NONE; }
//
// We can only digest Unicode strings below
//
KerbErr = KerbConvertRealmToUnicodeString( &ClientRealmU, &ClientRealm );
if ( !KERB_SUCCESS( KerbErr )) { return KerbErr; }
//
// Let LSA policy logic decide what's kosher
//
Status = LsaIFilterNamespace( &ServerInfo->AccountName, // misnomer - contains DNS domain name
TRUST_DIRECTION_OUTBOUND, ServerInfo->TrustType, ServerInfo->TrustAttributes, &ClientRealmU );
KerbFreeString( &ClientRealmU );
switch ( Status ) { case STATUS_SUCCESS: return KDC_ERR_NONE;
case STATUS_DOMAIN_TRUST_INCONSISTENT: FILL_EXT_ERROR_EX2( pExtendedError, STATUS_DOMAIN_TRUST_INCONSISTENT, FILENO, __LINE__ ); return KDC_ERR_POLICY;
case STATUS_INSUFFICIENT_RESOURCES: default: FILL_EXT_ERROR( pExtendedError, Status, FILENO, __LINE__ ); return KRB_ERR_GENERIC; } }
//+-------------------------------------------------------------------------
//
// Function: KdcCheckPacForSidFiltering
//
// Synopsis: If the server ticket info has a TDOSid then the function
// makes a check to make sure the SID from the TDO matches
// the client's home domain SID. A call to LsaIFilterSids
// is made to do the check. If this function fails with
// STATUS_TRUST_FAILURE then an audit log is generated.
// Otherwise the function succeeds but SIDs are filtered
// from the PAC.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcCheckPacForSidFiltering( IN PKDC_TICKET_INFO ServerInfo, IN OUT PUCHAR *PacData, IN OUT PULONG PacSize ) { NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; PPAC_INFO_BUFFER LogonInfo; PPACTYPE OldPac; ULONG OldPacSize; PPACTYPE NewPac = NULL; PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL; SAMPR_PSID_ARRAY ZeroResourceGroups; ULONG OldExtraSidCount; PNETLOGON_SID_AND_ATTRIBUTES SavedExtraSids = NULL; PPACTYPE RemarshalPac = NULL; ULONG RemarshalPacSize = 0;
OldPac = (PPACTYPE) *PacData; OldPacSize = *PacSize; if (PAC_UnMarshal(OldPac, OldPacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Must remember to remarshal the PAC prior to returning
//
RemarshalPac = OldPac; RemarshalPacSize = OldPacSize;
RtlZeroMemory( &ZeroResourceGroups, sizeof(ZeroResourceGroups)); // allows us to use PAC_InitAndUpdateGroups to remarshal the PAC
//
// First, find the logon information
//
LogonInfo = PAC_Find( OldPac, PAC_LOGON_INFO, NULL );
if (LogonInfo == NULL) { D_DebugLog((DEB_WARN,"No logon info for PAC - not making SID filtering check\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Now unmarshall the validation information and build a list of sids
//
if (!NT_SUCCESS(PAC_UnmarshallValidationInfo( &ValidationInfo, LogonInfo->Data, LogonInfo->cbBufferSize))) { D_DebugLog((DEB_ERROR,"Failed to unmarshall validation info!\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Save the old extra SID count (so that if KdcFilterSids compresses
// the SID array, we can avoid allocating memory for the other-org SID later)
//
OldExtraSidCount = ValidationInfo->SidCount;
//
// Call lsaifiltersids().
//
Status = KdcFilterSids( ServerInfo, ValidationInfo );
if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_POLICY; goto Cleanup; }
//
// If we're crossing an organization boundary, add the "other organization"
// SID to the PAC.
//
// NOTE: for efficiency reasons, no check is made for whether the
// SID is already in the PAC. The hope is that adding a duplicate
// SID will not cause problems.
//
if ( ServerInfo->TrustAttributes & TRUST_ATTRIBUTE_CROSS_ORGANIZATION ) { if ( ValidationInfo->SidCount >= OldExtraSidCount ) { SavedExtraSids = ValidationInfo->ExtraSids;
SafeAllocaAllocate( ValidationInfo->ExtraSids, sizeof( SID_AND_ATTRIBUTES ) * ( ValidationInfo->SidCount + 1 ) );
if ( ValidationInfo->ExtraSids == NULL ) { ValidationInfo->ExtraSids = SavedExtraSids; SavedExtraSids = NULL; KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( ValidationInfo->ExtraSids, SavedExtraSids, sizeof( SID_AND_ATTRIBUTES ) * ValidationInfo->SidCount ); }
ValidationInfo->ExtraSids[ValidationInfo->SidCount].Sid = GlobalOtherOrganizationSid;
ValidationInfo->ExtraSids[ValidationInfo->SidCount].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
ValidationInfo->SidCount += 1; }
//
// Now build a new pac
//
Status = PAC_InitAndUpdateGroups( ValidationInfo, &ZeroResourceGroups, OldPac, &NewPac );
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RemarshalPacSize = PAC_GetSize(NewPac); RemarshalPac = NewPac;
Cleanup:
if ( RemarshalPac != NULL ) { if (!PAC_ReMarshal(RemarshalPac, RemarshalPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; } else if ( NewPac != NULL && *PacData != (PBYTE)NewPac ) { MIDL_user_free(*PacData); *PacData = (PBYTE) NewPac; NewPac = NULL; *PacSize = RemarshalPacSize; } }
if (NewPac != NULL) { MIDL_user_free(NewPac); }
if ( SavedExtraSids != NULL ) { SafeAllocaFree( ValidationInfo->ExtraSids ); ValidationInfo->ExtraSids = SavedExtraSids; ValidationInfo->SidCount -= 1; }
if (ValidationInfo != NULL) { MIDL_user_free(ValidationInfo); }
return(KerbErr); }
#ifdef ROGUE_DC
#pragma message( "COMPILING A ROGUE DC!!!" )
#pragma message( "MUST NOT SHIP THIS BUILD!!!" )
extern HKEY hKdcRogueKey;
KERBERR KdcInstrumentRoguePac( IN OUT PKERB_AUTHORIZATION_DATA PacAuthData ) { KERBERR KerbErr; NTSTATUS Status; PNETLOGON_VALIDATION_SAM_INFO3 OldValidationInfo = NULL; NETLOGON_VALIDATION_SAM_INFO3 NewValidationInfo = {0}; SAMPR_PSID_ARRAY ZeroResourceGroups = {0}; PPACTYPE NewPac = NULL; ULONG NewPacSize; PPAC_INFO_BUFFER LogonInfo;
PSID LogonDomainId = NULL; PSID ResourceGroupDomainSid = NULL; PGROUP_MEMBERSHIP GroupIds = NULL; PGROUP_MEMBERSHIP ResourceGroupIds = NULL; PNETLOGON_SID_AND_ATTRIBUTES ExtraSids = NULL; BYTE FullUserSidBuffer[MAX_SID_LEN]; SID * FullUserSid = ( SID * )FullUserSidBuffer; CHAR * FullUserSidText = NULL;
DWORD dwType; DWORD cbData = 0; PCHAR Buffer; PCHAR Value = NULL;
BOOLEAN PacChanged = FALSE;
//
// Optimization: no "rogue" key in registry - nothing for us to do
//
if ( hKdcRogueKey == NULL ) { return STATUS_SUCCESS; }
//
// Unmarshall the old PAC
//
if ( PAC_UnMarshal( (PPACTYPE)PacAuthData->value.auth_data.value, PacAuthData->value.auth_data.length) == 0 ) { DebugLog((DEB_ERROR, "ROGUE: Unable to unmarshal the PAC\n"));
KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// First, find the logon information
//
LogonInfo = PAC_Find( (PPACTYPE)PacAuthData->value.auth_data.value, PAC_LOGON_INFO, NULL );
if ( LogonInfo == NULL ) { DebugLog((DEB_ERROR, "ROGUE: No logon info on PAC - not performing substitution\n")); KerbErr = KDC_ERR_NONE; goto Error; }
//
// Now unmarshall the validation information and build a list of sids
//
if ( !NT_SUCCESS(PAC_UnmarshallValidationInfo( &OldValidationInfo, LogonInfo->Data, LogonInfo->cbBufferSize ))) { DebugLog((DEB_ERROR, "ROGUE: Unable to unmarshal validation info\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
//
// Construct the text form of the full user's SID (logon domain ID + user ID)
//
DsysAssert( sizeof( FullUserSidBuffer ) >= MAX_SID_LEN );
RtlCopySid( sizeof( FullUserSidBuffer ), FullUserSid, OldValidationInfo->LogonDomainId );
FullUserSid->SubAuthority[FullUserSid->SubAuthorityCount] = OldValidationInfo->UserId; FullUserSid->SubAuthorityCount += 1;
if ( FALSE == ConvertSidToStringSidA( FullUserSid, &FullUserSidText )) { DebugLog((DEB_ERROR, "ROGUE: Unable to convert user's SID\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
//
// Now look in the registry for the SID matching the validation info
//
if ( ERROR_SUCCESS != RegQueryValueExA( hKdcRogueKey, FullUserSidText, NULL, &dwType, NULL, &cbData ) || dwType != REG_MULTI_SZ || cbData <= 1 ) { DebugLog((DEB_ERROR, "ROGUE: No substitution info available for %s\n", FullUserSidText)); KerbErr = KDC_ERR_NONE; goto Error; }
SafeAllocaAllocate( Value, cbData );
if ( Value == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating substitution buffer\n", FullUserSidText)); KerbErr = KRB_ERR_GENERIC; goto Error; }
if ( ERROR_SUCCESS != RegQueryValueExA( hKdcRogueKey, FullUserSidText, NULL, &dwType, (PBYTE)Value, &cbData ) || dwType != REG_MULTI_SZ || cbData <= 1 ) { DebugLog((DEB_ERROR, "ROGUE: Error reading from registry\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
DebugLog((DEB_ERROR, "ROGUE: Substituting the PAC for %s\n", FullUserSidText));
if ( _stricmp( Value, "xPAC" ) == 0 ) { //
// This means that the logon info should be stripped from the PAC
//
Status = PAC_RemoveSection( (PPACTYPE)PacAuthData->value.auth_data.value, PAC_LOGON_INFO, &NewPac );
if ( NT_SUCCESS( Status )) { NewPacSize = PAC_GetSize( NewPac );
if (!PAC_ReMarshal(NewPac, NewPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; goto Error; }
MIDL_user_free( PacAuthData->value.auth_data.value ); PacAuthData->value.auth_data.value = (PBYTE) NewPac; NewPac = NULL; PacAuthData->value.auth_data.length = NewPacSize; } else { DebugLog((DEB_ERROR, "ROGUE: Unable to strip PAC_LOGON_INFO\n")); }
KerbErr = KDC_ERR_NONE;
goto Cleanup; }
Buffer = Value;
//
// New validation info will be overloaded with stuff from the file
//
NewValidationInfo = *OldValidationInfo;
//
// Read the input file one line at a time
//
while ( *Buffer != '\0' ) { switch( Buffer[0] ) { case 'l': case 'L': // logon domain ID
if ( LogonDomainId != NULL ) { DebugLog((DEB_ERROR, "ROGUE: Logon domain ID specified more than once - only first one kept\n")); break; }
DebugLog((DEB_ERROR, "ROGUE: Substituting logon domain ID by %s\n", &Buffer[1]));
if ( FALSE == ConvertStringSidToSidA( &Buffer[1], &LogonDomainId )) { DebugLog((DEB_ERROR, "ROGUE: Unable to convert SID\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
if ( LogonDomainId == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating LogonDomainId\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
NewValidationInfo.LogonDomainId = LogonDomainId; LogonDomainId = NULL; PacChanged = TRUE;
break;
case 'd': case 'D': // resource group domain SID
if ( ResourceGroupDomainSid != NULL ) { DebugLog((DEB_ERROR, "ROGUE: Resource group domain SID specified more than once - only first one kept\n")); break; }
DebugLog((DEB_ERROR, "ROGUE: Substituting resource group domain SID by %s\n", &Buffer[1]));
if ( FALSE == ConvertStringSidToSidA( &Buffer[1], &ResourceGroupDomainSid )) { DebugLog((DEB_ERROR, "ROGUE: Unable to convert SID\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
if ( ResourceGroupDomainSid == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating ResourceGroupDomainSid\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
NewValidationInfo.ResourceGroupDomainSid = ResourceGroupDomainSid; ResourceGroupDomainSid = NULL; PacChanged = TRUE;
break;
case 'p': case 'P': // primary group ID
DebugLog((DEB_ERROR, "ROGUE: Substituting primary group ID by %s\n", &Buffer[1]));
NewValidationInfo.PrimaryGroupId = atoi(&Buffer[1]); PacChanged = TRUE;
break;
case 'u': case 'U': // User ID
DebugLog((DEB_ERROR, "ROGUE: Substituting user ID by %s\n", &Buffer[1]));
NewValidationInfo.UserId = atoi(&Buffer[1]); PacChanged = TRUE;
break;
case 'e': case 'E': // Extra SID
DebugLog((DEB_ERROR, "ROGUE: Adding an ExtraSid: %s\n", &Buffer[1]));
if ( ExtraSids == NULL ) { NewValidationInfo.ExtraSids = NULL; NewValidationInfo.SidCount = 0;
ExtraSids = ( PNETLOGON_SID_AND_ATTRIBUTES )HeapAlloc( GetProcessHeap(), 0, sizeof( NETLOGON_SID_AND_ATTRIBUTES ) ); } else { ExtraSids = ( PNETLOGON_SID_AND_ATTRIBUTES )HeapReAlloc( GetProcessHeap(), 0, NewValidationInfo.ExtraSids, ( NewValidationInfo.SidCount + 1 ) * sizeof( NETLOGON_SID_AND_ATTRIBUTES ) ); }
if ( ExtraSids == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating ExtraSids\n")); ExtraSids = NewValidationInfo.ExtraSids; KerbErr = KRB_ERR_GENERIC; goto Error; }
//
// Read the actual SID
//
NewValidationInfo.ExtraSids = ExtraSids;
if ( FALSE == ConvertStringSidToSidA( &Buffer[1], &NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Sid )) { DebugLog((DEB_ERROR, "ROGUE: Unable to convert SID\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
if ( NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Sid == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating an extra SID\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
NewValidationInfo.SidCount += 1; PacChanged = TRUE;
break;
case 'g': case 'G': // Group ID
DebugLog((DEB_ERROR, "ROGUE: Adding a GroupId: %s\n", &Buffer[1]));
if ( GroupIds == NULL ) { NewValidationInfo.GroupIds = NULL; NewValidationInfo.GroupCount = 0;
GroupIds = ( PGROUP_MEMBERSHIP )HeapAlloc( GetProcessHeap(), 0, sizeof( GROUP_MEMBERSHIP ) ); } else { GroupIds = ( PGROUP_MEMBERSHIP )HeapReAlloc( GetProcessHeap(), 0, NewValidationInfo.GroupIds, ( NewValidationInfo.GroupCount + 1 ) * sizeof( GROUP_MEMBERSHIP ) ); }
if ( GroupIds == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating Group IDs\n")); GroupIds = NewValidationInfo.GroupIds; KerbErr = KRB_ERR_GENERIC; goto Error; }
//
// Read the actual ID
//
NewValidationInfo.GroupIds = GroupIds; NewValidationInfo.GroupIds[NewValidationInfo.GroupCount].RelativeId = atoi(&Buffer[1]); NewValidationInfo.GroupIds[NewValidationInfo.GroupCount].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; NewValidationInfo.GroupCount += 1; PacChanged = TRUE;
break;
case 'r': case 'R': // Resource groups
DebugLog((DEB_ERROR, "ROGUE: Adding a ResourceGroupId: %s\n", &Buffer[1]));
if ( ResourceGroupIds == NULL ) { NewValidationInfo.ResourceGroupIds = NULL; NewValidationInfo.ResourceGroupCount = 0;
ResourceGroupIds = ( PGROUP_MEMBERSHIP )HeapAlloc( GetProcessHeap(), 0, sizeof( GROUP_MEMBERSHIP ) ); } else { ResourceGroupIds = ( PGROUP_MEMBERSHIP )HeapReAlloc( GetProcessHeap(), 0, NewValidationInfo.ResourceGroupIds, ( NewValidationInfo.ResourceGroupCount + 1 ) * sizeof( GROUP_MEMBERSHIP ) ); }
if ( ResourceGroupIds == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating Resource Group IDs\n")); ResourceGroupIds = NewValidationInfo.ResourceGroupIds; KerbErr = KRB_ERR_GENERIC; goto Error; }
//
// Read the actual ID
//
NewValidationInfo.ResourceGroupIds = ResourceGroupIds; NewValidationInfo.ResourceGroupIds[NewValidationInfo.ResourceGroupCount].RelativeId = atoi(&Buffer[1]); NewValidationInfo.ResourceGroupIds[NewValidationInfo.ResourceGroupCount].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; NewValidationInfo.ResourceGroupCount += 1; PacChanged = TRUE;
break;
default: // unrecognized
DebugLog((DEB_ERROR, "ROGUE: Entry \'%c\' unrecognized\n", Buffer[0]));
break; }
//
// Move to the next line
//
while (*Buffer++ != '\0'); }
if ( !PacChanged ) { DebugLog((DEB_ERROR, "ROGUE: Nothing to substitute for %s\n", FullUserSidText)); KerbErr = KDC_ERR_NONE; goto Error; }
//
// If resource group IDs were added, indicate that by setting the corresponding flag
//
if ( ResourceGroupIds ) { NewValidationInfo.UserFlags |= LOGON_RESOURCE_GROUPS; }
//
// If extra SIDs were added, indicate that by setting the corresponding flag
//
if ( ExtraSids ) { NewValidationInfo.UserFlags |= LOGON_EXTRA_SIDS; }
//
// Now build a new pac
//
Status = PAC_InitAndUpdateGroups( &NewValidationInfo, &ZeroResourceGroups, (PPACTYPE)PacAuthData->value.auth_data.value, &NewPac );
if ( !NT_SUCCESS( Status )) { DebugLog((DEB_ERROR, "ROGUE: Error 0x%x from PAC_InitAndUpdateGroups\n")); KerbErr = KRB_ERR_GENERIC; goto Error; }
NewPacSize = PAC_GetSize( NewPac );
if (!PAC_ReMarshal(NewPac, NewPacSize)) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; goto Error; }
MIDL_user_free( PacAuthData->value.auth_data.value ); PacAuthData->value.auth_data.value = (PBYTE) NewPac; NewPac = NULL; PacAuthData->value.auth_data.length = NewPacSize;
KerbErr = KDC_ERR_NONE;
Cleanup:
MIDL_user_free( OldValidationInfo ); LocalFree( FullUserSidText ); LocalFree( ResourceGroupDomainSid ); LocalFree( LogonDomainId ); HeapFree( GetProcessHeap(), 0, ResourceGroupIds ); HeapFree( GetProcessHeap(), 0, GroupIds );
if ( ExtraSids ) { for ( ULONG i = 0; i < NewValidationInfo.SidCount; i++ ) { HeapFree( GetProcessHeap(), 0, ExtraSids[i].Sid ); }
HeapFree( GetProcessHeap(), 0, ExtraSids ); }
MIDL_user_free( NewPac );
SafeAllocaFree( Value );
return KerbErr;
Error:
if ( !KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "ROGUE: Substitution encountered an error, not performed\n")); }
if ( !PAC_ReMarshal( (PPACTYPE)PacAuthData->value.auth_data.value, PacAuthData->value.auth_data.length )) { DsysAssert(!"PAC_Remarshal Failed"); KerbErr = KRB_ERR_GENERIC; }
goto Cleanup; }
#endif
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyAndResignPac
//
// Synopsis: Verifies the signature on a PAC and re-signs it with the
// new servers & kdc's key
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcCheckForDelegationInfo( IN OUT PUCHAR PacData, IN OUT ULONG PacSize, IN OUT PBOOLEAN InfoPresent ) { KERBERR KerbErr = KDC_ERR_NONE; PPAC_INFO_BUFFER DelegationInfo = NULL; PPACTYPE Pac; ULONG Size;
*InfoPresent = FALSE; Pac = (PPACTYPE) PacData; Size = PacSize;
if (PAC_UnMarshal(Pac, Size) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); return KRB_ERR_GENERIC; }
DelegationInfo = PAC_Find( Pac, PAC_DELEGATION_INFO, NULL );
if (!PAC_ReMarshal(Pac, Size)) { DsysAssert(!"PAC_Remarshal Failed"); return KRB_ERR_GENERIC; }
*InfoPresent = (DelegationInfo != NULL);
return KerbErr; }
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyAndResignPac
//
// Synopsis: Verifies the signature on a PAC and re-signs it with the
// new servers & kdc's key
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcVerifyAndResignPac( IN PKERB_ENCRYPTION_KEY OldKey, IN PKERB_ENCRYPTION_KEY NewKey, IN PKDC_TICKET_INFO OldServerInfo, IN OPTIONAL PKDC_TICKET_INFO TargetServiceInfo, IN OPTIONAL PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OPTIONAL PKERB_ENCRYPTED_TICKET FinalTicket, IN BOOLEAN AddResourceGroups, IN PKERB_EXT_ERROR ExtendedError, IN OUT PKERB_AUTHORIZATION_DATA PacAuthData, OUT OPTIONAL PS4U_DELEGATION_INFO* S4UDelegationInfo ) { KERBERR KerbErr = KDC_ERR_NONE; BOOLEAN InfoPresent = FALSE; BOOLEAN DCTarget = FALSE; TRACE(KDC, KdcVerifyAndResignPac, DEB_FUNCTION);
PKDC_TICKET_INFO LocalServerInfo = OldServerInfo;
//
// If the TI_PRXY_REQUESTOR_THIS_REALM bit is set, then
// the evidence ticket is encrypted in the requestor's key.
//
if (( ARGUMENT_PRESENT( S4UTicketInfo ) ) && ( S4UTicketInfo->Flags & TI_S4UPROXY_INFO )) { LocalServerInfo = &S4UTicketInfo->RequestorTicketInfo; }
if (ARGUMENT_PRESENT( TargetServiceInfo )) { DCTarget = ((TargetServiceInfo->UserAccountControl & USER_SERVER_TRUST_ACCOUNT ) != 0); }
//
// Delegation info in PAC? Then it better not be interdomain, as
// constrained delegation (S4UProxy) only works in a single
// domain. Reject the request.
//
if ( OldServerInfo->UserId == DOMAIN_USER_RID_KRBTGT && ( OldServerInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT )) { KerbErr = KdcCheckForDelegationInfo( PacAuthData->value.auth_data.value, PacAuthData->value.auth_data.length, &InfoPresent );
if ( InfoPresent ) { DebugLog((DEB_ERROR, "Attempting XRealm S4UProxy Inbound\n")); KerbErr = KDC_ERR_POLICY; FILL_EXT_ERROR_EX2( ExtendedError, STATUS_CROSSREALM_DELEGATION_FAILURE, FILENO, __LINE__ ); goto Cleanup; } else if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
//
// Now verify the existing signature
//
KerbErr = KdcVerifyPacSignature( OldKey, LocalServerInfo, PacAuthData->value.auth_data.length, PacAuthData->value.auth_data.value );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Perform SID filtering if necessary
//
KerbErr = KdcCheckPacForSidFiltering( OldServerInfo, &PacAuthData->value.auth_data.value, (PULONG) &PacAuthData->value.auth_data.length );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// See if this is an S4U variant, and fill in the interesting pac sections.
//
if (ARGUMENT_PRESENT( S4UTicketInfo )) { if (( S4UTicketInfo->Flags & TI_S4UPROXY_INFO ) != 0) { //
// S4UProxy
// 2 courses of action here.
// 1. If this is for a server in our realm, then we need to insert the
// target into the S4U_DELEGATION_INFO.
// 2. If we're transitting, validate the target name vs. what's in the PAC,
// and continue on.
//
KerbErr = KdcUpdateAndValidateS4UProxyPAC( S4UTicketInfo, &PacAuthData->value.auth_data.value, (PULONG) &PacAuthData->value.auth_data.length, S4UDelegationInfo );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcUpdateAndValidateS4UProxyInfo failed - %x\n", KerbErr)); goto Cleanup; } } else if (( S4UTicketInfo->Flags & TI_REQUESTOR_THIS_REALM ) != 0) { //
// S4USelf - replace the pac verifier w/ one that is acceptable to the
// destination server.
//
KerbErr = KdcUpdateAndVerifyS4UPacVerifier( S4UTicketInfo, &PacAuthData->value.auth_data.value, (PULONG) &PacAuthData->value.auth_data.length );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcVerifyS4UPacVerifier failed - %x\n", KerbErr)); goto Cleanup; }
}
}
#ifdef ROGUE_DC
KerbErr = KdcInstrumentRoguePac( PacAuthData );
if ( !KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "KdcInstrumentRoguePac failed\n")); } #endif
//
// Now resign the PAC. If we add new sig algs, then we may need to
// address growing sigs, but for now, its all KDC_PAC_CHECKSUM
//
KerbErr = KdcSignPac( NewKey, AddResourceGroups, DCTarget, &PacAuthData->value.auth_data.value, (PULONG) &PacAuthData->value.auth_data.length );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Cleanup:
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcFreeAuthzInfo
//
// Synopsis: Used to free buffers / handles from KdcGetValidationInfoFromTgt.
// Allows us to use allocated buffers w/o copy overhead.
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KdcFreeAuthzInfo( IN PKDC_AUTHZ_GROUP_BUFFERS InfoToFree ) { if (InfoToFree->BuiltInSids) { MIDL_user_free(InfoToFree->BuiltInSids); }
if (InfoToFree->PacGroups.Sids != NULL) { for (ULONG Index = 0; Index < InfoToFree->PacGroups.Count ;Index++ ) { if (InfoToFree->PacGroups.Sids[Index].SidPointer != NULL) { MIDL_user_free(InfoToFree->PacGroups.Sids[Index].SidPointer); } }
MIDL_user_free(InfoToFree->PacGroups.Sids); }
SamIFree_SAMPR_ULONG_ARRAY( &InfoToFree->AliasGroups ); SamIFreeSidArray( InfoToFree->ResourceGroups );
if ( InfoToFree->SidAndAttributes ) { MIDL_user_free(InfoToFree->SidAndAttributes); }
if ( InfoToFree->ValidationInfo ) { MIDL_user_free( InfoToFree->ValidationInfo ); }
RtlZeroMemory( InfoToFree, sizeof(KDC_AUTHZ_GROUP_BUFFERS) ); }
//+-------------------------------------------------------------------------
//
// Function: KdcGetSidsFromTgt
//
// Synopsis: Takes a TGT, grabs the PAC from the authorization data, and
// extracts the validation info, builds groups up.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcGetSidsFromTgt( IN PKERB_ENCRYPTED_TICKET EncryptedTicket, IN OPTIONAL PKERB_ENCRYPTION_KEY EncryptedTicketKey, IN ULONG EncryptionType, IN PKDC_TICKET_INFO TgtAccountInfo, IN OUT PKDC_AUTHZ_INFO AuthzInfo, IN OUT PKDC_AUTHZ_GROUP_BUFFERS InfoToFree, OUT NTSTATUS * pStatus ) { KERBERR KerbErr; NTSTATUS Status = STATUS_SUCCESS;
PKERB_AUTHORIZATION_DATA SourceAuthData = NULL; PKERB_IF_RELEVANT_AUTH_DATA * IfRelevantData = NULL; PKERB_AUTHORIZATION_DATA PacAuthData = NULL; PKERB_ENCRYPTION_KEY KdcKey = EncryptedTicketKey; PPAC_INFO_BUFFER LogonInfo; PPACTYPE Pac; ULONG PacSize; PNETLOGON_VALIDATION_SAM_INFO3 LocalValidationInfo = NULL; SAMPR_PSID_ARRAY SidList = {0}; PSAMPR_PSID_ARRAY ResourceGroups = NULL; SAMPR_ULONG_ARRAY BuiltinGroups = {0, NULL}; PSID SidBuffer = NULL; PNETLOGON_SID_AND_ATTRIBUTES SidAndAttributes = NULL; ULONG Index, Index2, GroupCount = 0, SidSize = 0;
RtlZeroMemory( InfoToFree, sizeof(KDC_AUTHZ_GROUP_BUFFERS) );
if (EncryptedTicket->bit_mask & KERB_ENCRYPTED_TICKET_authorization_data_present) { DsysAssert(EncryptedTicket->KERB_ENCRYPTED_TICKET_authorization_data != NULL); SourceAuthData = EncryptedTicket->KERB_ENCRYPTED_TICKET_authorization_data; } else { DsysAssert(FALSE); *pStatus = STATUS_INVALID_PARAMETER; return KRB_ERR_GENERIC; }
KerbErr = KerbGetPacFromAuthData( SourceAuthData, &IfRelevantData, &PacAuthData );
if ( PacAuthData == NULL ) { KerbErr = KRB_ERR_GENERIC; }
if (!KERB_SUCCESS(KerbErr)) { Status = STATUS_NO_MEMORY; // difficult to figure out exactly what it was
goto Cleanup; }
//
// Verify the signature, using Krbtgt key.
//
if ( KdcKey == NULL ) { KdcKey = KerbGetKeyFromList( TgtAccountInfo->Passwords, EncryptionType ); if ( KdcKey == NULL ) { DebugLog((DEB_ERROR, "Can't find key for PAC (%x)\n", EncryptionType )); KerbErr = KRB_ERR_GENERIC; Status = STATUS_NO_KERB_KEY; goto Cleanup; } }
KerbErr = KdcVerifyPacSignature( KdcKey, TgtAccountInfo, PacAuthData->value.auth_data.length, PacAuthData->value.auth_data.value );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR,"PAC signature didn't verify\n")); Status = KerbMapKerbError( KerbErr ); goto Cleanup; }
Pac = (PPACTYPE) PacAuthData->value.auth_data.value; PacSize = PacAuthData->value.auth_data.length;
if (PAC_UnMarshal(Pac, PacSize) == 0) { D_DebugLog((DEB_ERROR,"Failed to unmarshal pac\n")); KerbErr = KRB_ERR_GENERIC; Status = STATUS_INVALID_PARAMETER; // better error code?
goto Cleanup; }
LogonInfo = PAC_Find( Pac, PAC_LOGON_INFO, NULL );
if ( LogonInfo == NULL ) { KerbErr = KRB_ERR_GENERIC; Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
Status = PAC_UnmarshallValidationInfo( &LocalValidationInfo, LogonInfo->Data, LogonInfo->cbBufferSize );
if ( !NT_SUCCESS( Status )) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Filter the sids. If trust sid is present, this is an inbound TGT. We
// don't do this for TGTs from our own domain.
//
if (NULL != TgtAccountInfo->TrustSid) { Status = KdcFilterSids( TgtAccountInfo, LocalValidationInfo );
if ( !NT_SUCCESS( Status )) { KerbErr = KDC_ERR_POLICY; goto Cleanup; } }
//
// Make a sid list from the validation info
//
KerbErr = KdcBuildPacSidList( LocalValidationInfo, TRUE, // Add everyone and authenticated user sids.
(( TgtAccountInfo->TrustAttributes & TRUST_ATTRIBUTE_CROSS_ORGANIZATION ) != 0 ), &SidList );
if (!KERB_SUCCESS(KerbErr)) { Status = STATUS_NO_MEMORY; goto Cleanup; }
RtlCopyMemory( &InfoToFree->PacGroups, &SidList, sizeof(SAMPR_PSID_ARRAY) );
//
// Call SAM to get the sids for resource groups and built-in groups
//
Status = SamIGetResourceGroupMembershipsTransitive( GlobalAccountDomainHandle, &SidList, 0, // no flags
&ResourceGroups );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to get resource groups: 0x%x\n",Status)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
InfoToFree->ResourceGroups = ResourceGroups;
Status = SamIGetAliasMembership( GlobalBuiltInDomainHandle, &SidList, &BuiltinGroups );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to get ALIAS MEMBERSHIP groups: 0x%x\n",Status)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( &InfoToFree->AliasGroups, &BuiltinGroups, sizeof(SAMPR_ULONG_ARRAY) );
GroupCount = BuiltinGroups.Count + ResourceGroups->Count + SidList.Count;
//
// Enumerate and allocate the groups sids...
//
if (GroupCount != 0) { SidAndAttributes = (PNETLOGON_SID_AND_ATTRIBUTES) MIDL_user_allocate(sizeof(NETLOGON_SID_AND_ATTRIBUTES) * GroupCount); if (SidAndAttributes == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
InfoToFree->SidAndAttributes = SidAndAttributes;
//
// Add in all the extra sids that are not resource groups
//
Index2 = 0; for (Index = 0; Index < SidList.Count; Index++ ) { SidAndAttributes[Index2].Sid = SidList.Sids[Index].SidPointer; SidAndAttributes[Index2].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; Index2++; }
//
// Copy all the resource group SIDs
//
for (Index = 0; Index < ResourceGroups->Count ; Index++ ) { SidAndAttributes[Index2].Sid = ResourceGroups->Sids[Index].SidPointer; SidAndAttributes[Index2].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_RESOURCE; Index2++; }
//
// Copy in the builtin group sids.
//
SidSize = RtlLengthSid(GlobalBuiltInSid) + sizeof(ULONG); SidBuffer = (PSID) MIDL_user_allocate(SidSize * BuiltinGroups.Count);
if (SidBuffer == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
InfoToFree->BuiltInSids = SidBuffer; PBYTE Current = (PBYTE) SidBuffer;
for (Index = 0; Index < BuiltinGroups.Count ; Index++ ) { RtlCopySid( RtlLengthSid(GlobalBuiltInSid), Current, GlobalBuiltInSid );
//
// The final value is just a ULONG - appended to the right place.
//
(*RtlSubAuthoritySid( Current, ((ULONG) (*RtlSubAuthorityCountSid(GlobalBuiltInSid)) - 1) )) = BuiltinGroups.Element[Index];
SidAndAttributes[Index2].Sid = Current; SidAndAttributes[Index2].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; Index2++; Current += SidSize; } }
InfoToFree->ValidationInfo = LocalValidationInfo; LocalValidationInfo = NULL;
AuthzInfo->SidCount = GroupCount; AuthzInfo->SidAndAttributes = SidAndAttributes; SidAndAttributes = NULL;
Cleanup:
if (LocalValidationInfo) { MIDL_user_free(LocalValidationInfo); }
if (!KERB_SUCCESS(KerbErr)) { KdcFreeAuthzInfo(InfoToFree); }
if (IfRelevantData) { KerbFreeData(PKERB_IF_RELEVANT_AUTH_DATA_PDU, IfRelevantData); }
*pStatus = Status;
return KerbErr; }
|