//+----------------------------------------------------------------------- // // 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 // // Returns: NTSTATUS code // // Notes: This event covers credentials obtain from credman // //-------------------------------------------------------------------------- NTSTATUS KerbGenerateAuditForLogonUsingExplicitCreds( IN PKERB_LOGON_SESSION pCurrentUserLogonSession, IN PKERB_PRIMARY_CREDENTIAL pNewUserPrimaryCredentials, IN LPGUID pNewUserLogonGuid ) { NTSTATUS Status = STATUS_SUCCESS; GUID CurrentUserLogonGuid; LPGUID pCurrentUserLogonGuid = NULL; PKERB_TICKET_CACHE_ENTRY pTicketCacheEntry = NULL; UNICODE_STRING OurDomainName = { 0 }; KERB_TIME CurrentUserStartTime = { 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")); } } 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 // // now generate the audit // Status = I_LsaIAuditLogonUsingExplicitCreds( EVENTLOG_AUDIT_SUCCESS, NULL, // no user sid &pCurrentUserLogonSession->PrimaryCredentials.UserName, &pCurrentUserLogonSession->PrimaryCredentials.DomainName, &pCurrentUserLogonSession->LogonId, pCurrentUserLogonGuid, &pNewUserPrimaryCredentials->UserName, &pNewUserPrimaryCredentials->DomainName, pNewUserLogonGuid ); 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 // // 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_ENCRYPTED_TICKET pEncryptedTicket, IN PSID pUserSid, IN PUNICODE_STRING pWorkstationName, IN PLUID pLogonId ) { GUID LogonGuid = { 0 }; NTSTATUS Status = STATUS_SUCCESS; //PKERB_TIME pStartTime; UNICODE_STRING ClientRealm = { 0 }; UNICODE_STRING ClientName = { 0 }; KERBERR KerbErr = KDC_ERR_NONE; ULONG NameType; // // convert kerb style names to UNICODE_STRINGs // KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &pEncryptedTicket->client_realm ); if ( KerbErr == KDC_ERR_NONE ) { KerbErr = KerbConvertPrincipalNameToString( &ClientName, &NameType, &pEncryptedTicket->client_name ); } if ( KerbErr != KDC_ERR_NONE ) { Status = KerbMapKerbError( KerbErr ); goto Cleanup; } // // generate the logon GUID // Status = I_LsaIGetLogonGuid( &ClientName, &ClientRealm, (PBYTE)&pEncryptedTicket->KERB_ENCRYPTED_TICKET_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, &ClientName, &ClientRealm, pWorkstationName, pUserSid, Network, &KerberosSource, pLogonId, &LogonGuid ); Cleanup: if (ClientRealm.Buffer != NULL) { KerbFreeString( &ClientRealm ); } if (ClientName.Buffer != NULL) { KerbFreeString( &ClientName ); } if ( !NT_SUCCESS( Status ) ) { D_DebugLog((DEB_ERROR,"KerbAuditLogon: failed: %x\n", Status )); } return Status; }