//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 2001 // // File: krbaudit.cxx // // Contents: Auditing routines // // // History: 27-April-2001 Created kumarp // //------------------------------------------------------------------------ #include #include #include //+------------------------------------------------------------------------- // // Function: KerbGetLogonGuid // // Synopsis: Gets a unique ID based on certain fields in the ticket // // Arguments: pPrimaryCredentials -- primary credentials // pKdcReplyBody -- kdc reply structure // pLogonGuid -- returned GUID // // Returns: NTSTATUS code // // Notes: The generated GUID is MD5 hash of 3 fields: // -- client name // -- client realm // -- ticket start time // // All of these fields are available at client/kdc/server machine. // this allows us to generate the same unique ID on these machines // without having to introduce a new field in the ticket. // This GUID is used in audit events allowing us to correlate // events on three different machines. // //-------------------------------------------------------------------------- NTSTATUS KerbGetLogonGuid( IN PKERB_PRIMARY_CREDENTIAL pPrimaryCredentials, IN PKERB_ENCRYPTED_KDC_REPLY pKdcReplyBody, OUT LPGUID pLogonGuid ) { NTSTATUS Status = STATUS_INVALID_PARAMETER; // ISSUE-2001/04/28-kumarp : use KERB_ENCRYPTED_KDC_REPLY_starttime_present //if ( KdcReplyBody->flags & KERB_ENCRYPTED_KDC_REPLY_starttime_present ) if ( pKdcReplyBody && pPrimaryCredentials ) { Status = LsaIGetLogonGuid( &pPrimaryCredentials->UserName, &pPrimaryCredentials->DomainName, (PBYTE) &pKdcReplyBody->starttime, sizeof(KERB_TIME), pLogonGuid ); if (!NT_SUCCESS(Status)) { RtlZeroMemory( pLogonGuid, sizeof(GUID) ); } } return Status; } //+------------------------------------------------------------------------- // // Function: KerbGenerateAuditForLogonUsingExplicitCreds // // Synopsis: Generate an audit event to indicate that somebody logged on // by supplying explicit credentials. // // Arguments: pCurrentUserLogonSession -- logon session of the user // who supplied credentials of // another user // pNewUserPrimaryCredentials -- supplied primary credentials // pNewUserLogonGuid -- logon GUID for the new logon // pTargetName -- name of the target server // // Returns: NTSTATUS code // // Notes: This event covers credentials obtained from credman // //-------------------------------------------------------------------------- NTSTATUS KerbGenerateAuditForLogonUsingExplicitCreds( IN PKERB_LOGON_SESSION pCurrentUserLogonSession, IN PKERB_PRIMARY_CREDENTIAL pNewUserPrimaryCredentials, IN LPGUID pNewUserLogonGuid, IN PKERB_INTERNAL_NAME pTargetName ) { NTSTATUS Status = STATUS_SUCCESS; GUID CurrentUserLogonGuid; LPGUID pCurrentUserLogonGuid = NULL; PKERB_TICKET_CACHE_ENTRY pTicketCacheEntry = NULL; UNICODE_STRING OurDomainName = { 0 }; KERB_TIME CurrentUserStartTime = { 0 }; KERBERR KerbErr = KDC_ERR_NONE; PUNICODE_STRING pTargetServerName = NULL; PUNICODE_STRING pTargetFullName = NULL; UNICODE_STRING TargetFullName = { 0 }; // // calculate LogonGuid for current logged on user // we need the following 3 parameters for this: // -- client name // -- client realm // -- ticket start time // if ( !(pCurrentUserLogonSession->LogonSessionFlags & (KERB_LOGON_LOCAL_ONLY | KERB_LOGON_DEFERRED)) ) { Status = KerbGetOurDomainName( &OurDomainName ); ASSERT( NT_SUCCESS(Status) && L"KerbGenerateAuditForLogonUsingExplicitCreds: KerbGetOurDomainName failed" ); if (NT_SUCCESS(Status)) { // // find the cached ticket for the local machine from // the ticket cache of the current logon session. // pTicketCacheEntry = KerbLocateTicketCacheEntry( &pCurrentUserLogonSession->PrimaryCredentials.ServerTicketCache, KerbGlobalMitMachineServiceName, &OurDomainName ); if ( pTicketCacheEntry ) { // // Convert start time to the format we want. // KerbConvertLargeIntToGeneralizedTime( &CurrentUserStartTime, NULL, &pTicketCacheEntry->StartTime ); // // Generate the logon GUID // Status = LsaIGetLogonGuid( &pCurrentUserLogonSession->PrimaryCredentials.UserName, &pCurrentUserLogonSession->PrimaryCredentials.DomainName, (PBYTE) &CurrentUserStartTime, sizeof(KERB_TIME), &CurrentUserLogonGuid ); if (NT_SUCCESS(Status)) { pCurrentUserLogonGuid = &CurrentUserLogonGuid; } } else { D_DebugLog((DEB_TRACE, "KerbGenerateAuditForLogonUsingExplicitCreds: could not locate ticket")); } } // // ISSUE-2002/03/28-kumarp : this should be inside the if stmt // KerbFreeString(&OurDomainName); } #if DBG else { // // KERB_LOGON_LOCAL_ONLY indicates a logon using non Kerberos package. // Logon GUID is supported only by Kerberos therefore its generation // is skipped. // if (pCurrentUserLogonSession->LogonSessionFlags & KERB_LOGON_LOCAL_ONLY) { D_DebugLog((DEB_TRACE,"KerbGenerateAuditForLogonUsingExplicitCreds: LogonSession %p has KERB_LOGON_LOCAL_ONLY\n", pCurrentUserLogonSession)); } // // KERB_LOGON_DEFERRED indicates that a logon occurred using // cached credentials because kdc was not available. In this case, // we will not have a ticket for local machine therefore generation of // the logon GUID is skipped. // if (pCurrentUserLogonSession->LogonSessionFlags & KERB_LOGON_DEFERRED) { D_DebugLog((DEB_TRACE,"KerbGenerateAuditForLogonUsingExplicitCreds: LogonSession %p has KERB_LOGON_DEFERRED\n", pCurrentUserLogonSession)); } } #endif // // convert pTargetName which is in kdc format to a UNICODE_STRING // KerbErr = KerbConvertKdcNameToString( &TargetFullName, pTargetName, NULL ); if ( KerbErr == KDC_ERR_NONE ) { pTargetFullName = &TargetFullName; } else { pTargetFullName = NULL; } // // extract the target computer name for known cases // if ( pTargetName->NameCount == 1 ) { pTargetServerName = &pTargetName->Names[0]; } else if ( pTargetName->NameCount >= 2 ) { pTargetServerName = &pTargetName->Names[1]; } else { pTargetServerName = NULL; } // // now generate the audit // Status = I_LsaIAuditLogonUsingExplicitCreds( EVENTLOG_AUDIT_SUCCESS, &pCurrentUserLogonSession->LogonId, pCurrentUserLogonGuid, (HANDLE) (ULONG_PTR) GetCurrentProcessId(), &pNewUserPrimaryCredentials->UserName, &pNewUserPrimaryCredentials->DomainName, pNewUserLogonGuid, pTargetServerName, pTargetFullName ); if ( TargetFullName.Buffer != NULL ) { MIDL_user_free( TargetFullName.Buffer ); } return Status; } //+------------------------------------------------------------------------- // // Function: KerbAuditLogon // // Synopsis: Generate a successful logon audit event // // Arguments: LogonStatus -- overall logon status // LogonSubStatus -- sub-category within a failed logon status // pEncryptedTicket -- ticket used // pUserSid -- user's SID // pWorkstationName -- logon workstation // pLogonId -- logon LUID // pTransittedServices -- list of transitted services // // Returns: NTSTATUS code // // Notes: A new field (logon GUID) was added to this audit event. // In order to send this new field to LSA, we had two options: // 1) add new function (AuditLogonEx) to LSA dispatch table // 2) define a private (LsaI) function to do the job // // option#2 was chosen because the logon GUID is a Kerberos only // feature. // //-------------------------------------------------------------------------- NTSTATUS KerbAuditLogon( IN NTSTATUS LogonStatus, IN NTSTATUS LogonSubStatus, IN PKERB_CONTEXT Context, IN PUNICODE_STRING pWorkstationName, IN PLUID pLogonId, IN PLSA_ADT_STRING_LIST pTransittedServices ) { GUID LogonGuid = { 0 }; NTSTATUS Status = STATUS_SUCCESS; KERB_TIME StartTime; // // Pull this eventually. I think we're OK, though. // if (Context == NULL) { DsysAssert(FALSE); goto Cleanup; } KerbConvertLargeIntToGeneralizedTime( &StartTime, NULL, &Context->StartTime ); // // generate the logon GUID // Status = I_LsaIGetLogonGuid( &Context->ClientName, &Context->ClientRealm, (PBYTE) &StartTime, sizeof(KERB_TIME), &LogonGuid ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // generate successful logon audit. use the special // LsaIAuditKerberosLogon function so that we can pass in // the generated unique logon guid // I_LsaIAuditKerberosLogon( LogonStatus, LogonSubStatus, &Context->ClientName, &Context->ClientRealm, pWorkstationName, Context->UserSid, Network, &KerberosSource, pLogonId, &LogonGuid, pTransittedServices ); Cleanup: if ( !NT_SUCCESS( Status ) ) { D_DebugLog((DEB_ERROR,"KerbAuditLogon: failed: %x\n", Status )); } return Status; }