//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1996 // // File: ctxtapi.cxx // // Contents: Context APIs for Kerberos package // // // History: 16-April-1996 Created MikeSw // 26-Sep-1998 ChandanS // Added more debugging support etc. // //------------------------------------------------------------------------ #include #include #include "kerbevt.h" #include #include #ifdef RETAIL_LOG_SUPPORT static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif UNICODE_STRING KerbTargetPrefix = {sizeof(L"krb5://")-sizeof(WCHAR),sizeof(L"krb5://"),L"krb5://" }; #define FILENO FILENO_CTXTAPI //+------------------------------------------------------------------------- // // Function: SpDeleteContext // // Synopsis: Deletes a Kerberos context // // Effects: // // Arguments: ContextHandle - The context to delete // // Requires: // // Returns: STATUS_SUCCESS or STATUS_INVALID_HANDLE // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpDeleteContext( IN LSA_SEC_HANDLE ContextHandle ) { NTSTATUS Status; PKERB_CONTEXT Context; KERB_CONTEXT_STATE ContextState; ULONG ClientProcess = 0; D_DebugLog((DEB_TRACE_API,"SpDeleteContext 0x%x called\n",ContextHandle)); if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; Context = NULL; goto Cleanup; } Status = KerbReferenceContext( ContextHandle, TRUE, // unlink handle &Context ); if (Context == NULL) { D_DebugLog((DEB_ERROR,"SpDeleteContext: Context 0x%x not located. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__)); goto Cleanup; } #ifndef WIN32_CHICAGO KerbReadLockContexts(); // Need to copy out the context data else we'll end up holding the lock // for too long a time. // ContextState = Context->ContextState; ClientProcess = Context->ClientProcess; KerbUnlockContexts(); // If this was a context that was dropped in the middle, // audit it as a logon failure. // if (ContextState == ApReplySentState) { LsaFunctions->AuditLogon( STATUS_UNFINISHED_CONTEXT_DELETED, STATUS_SUCCESS, &Context->ClientName, &Context->ClientRealm, NULL, // no workstation Context->UserSid, Network, &KerberosSource, &Context->LogonId ); } #endif // WIN32_CHICAGO // // Now dereference the Context. If nobody else is using this Context // currently it will be freed. // KerbDereferenceContext(Context); Status = STATUS_SUCCESS; Cleanup: D_DebugLog((DEB_TRACE_LEAKS,"SpDeleteContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), Context, ClientProcess)); D_DebugLog((DEB_TRACE_API, "SpDeleteContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: KerbProcessTargetNames // // Synopsis: Takes the target names from both the negotiate hint and // supplied by the caller and creates the real Kerberos target // name. // // Effects: // // Arguments: TargetName - supplies the name passed in the TargetName // parameter of InitializeSecurityContext. // SuppTargetName - If present, an alternate name passed in // a security token. // Flags - flags to use when cracking a name. May be: // KERB_CRACK_NAME_USE_WKSTA_REALM - if no realm can be // determined, use the realm of the wksta // KERB_CRACK_NAME_REALM_SUPPLIED - the caller has the // realm name, so treat "@" in the name as a normal // character, not a spacer. // UseSpnRealmSupplied - If the target name was an spn and it // contained a realm, it's set to TRUE // FinalTarget - The processed name. Must be freed with KerbFreeKdcName. // TargetRealm - If the name contains a realm portions, this is // the realm. Should be freed using KerbFreeString. // // // Requires: // // Returns: // // Notes: If the name has an "@" in it, it is converted into a standard // Kerberos V5 name - everything after the "@" is put in the // realm field, and everything before the @ is separted at the // "/" characters into different pieces of the name. Depending // on the number of pieces, it is passed as a KRB_NT_PRINCIPAL (1) // or KRB_NT_SRV_INSTANCE (2), or KRB_NT_SRV_XHST (3+) // // If the name has an "\" in it, it is assumed to be an NT4 // name & put as is into a KRB_NT_MS_PRINCIPAL_NAME name // // If the name has neither a "\" or a "@" or a "/", it is // assumed to be a simple name & passed as KRB_NT_PRINCIPAL // in the caller's domain. // // // //-------------------------------------------------------------------------- #ifdef later #define KERB_LOCALHOST_NAME L"localhost" #endif NTSTATUS KerbProcessTargetNames( IN PUNICODE_STRING TargetName, IN OPTIONAL PUNICODE_STRING SuppTargetName, IN ULONG Flags, IN OUT PULONG ProcessFlags, OUT PKERB_INTERNAL_NAME * FinalTarget, OUT PUNICODE_STRING TargetRealm, OUT OPTIONAL PKERB_SPN_CACHE_ENTRY * SpnCacheEntry ) { NTSTATUS Status = STATUS_SUCCESS; USHORT NameType = 0; PKERB_INTERNAL_NAME OutputName = NULL; USHORT NameParts = 0, ExtraNameParts = 0; ULONG NameLength = 0; USHORT Index, NameIndex; PUNICODE_STRING RealTargetName; UNICODE_STRING SuppliedRealm = {0}; UNICODE_STRING EmptyString = {0}; UNICODE_STRING SidString = {0}; UNICODE_STRING FirstNamePart = {0}; PKERB_SPN_CACHE_ENTRY LocalCacheEntry = NULL; #ifdef later UNICODE_STRING LocalhostName = {0}; BOOLEAN ReplaceLocalhost = FALSE; #endif BOOLEAN DoneParsing = FALSE; BOOLEAN PurgedEntry = FALSE; PKERB_MIT_REALM MitRealm; BOOLEAN UsedAlternateName; PUCHAR Where; PKERB_SID_CACHE_ENTRY CacheEntry = NULL; *ProcessFlags = 0; // // If a supplemental target name was supplied, use it as it is more likely // to be correct. // if (ARGUMENT_PRESENT(SuppTargetName) && (SuppTargetName->Length > 0)) { RealTargetName = SuppTargetName; } else { RealTargetName = TargetName; } // // If this is an IP address, we don't handle it so bail now. // if (KerbIsIpAddress(RealTargetName)) { D_DebugLog((DEB_ERROR,"Ip address passed as target name: %wZ. Failing InitSecCtxt\n", RealTargetName )); Status = SEC_E_TARGET_UNKNOWN; goto Cleanup; } #ifdef later RtlInitUnicodeString( &LocalhostName, KERB_LOCALHOST_NAME ); #endif // // Initialize the first part of the name to the whole string // FirstNamePart.Buffer = RealTargetName->Buffer; FirstNamePart.Length = RealTargetName->Length; FirstNamePart.MaximumLength = FirstNamePart.Length; // // Check the characters in the name. Search backwards to front because // username may have "@" signs in them. // for ( Index = (RealTargetName->Length / sizeof(WCHAR)); Index-- > 0; ) { switch(RealTargetName->Buffer[Index]) { case L'@': // // If we have a realm name already, ignore this character. // if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) != 0) { break; } // // If we haven't hit any other separators, this is user@domain kind // of name // if (NameType == 0) { NameType = KRB_NT_PRINCIPAL; NameParts++; SuppliedRealm.Buffer = &RealTargetName->Buffer[Index] + 1; SuppliedRealm.Length = RealTargetName->Length - (Index + 1) * sizeof(WCHAR); SuppliedRealm.MaximumLength = SuppliedRealm.Length; if (SuppliedRealm.Length == 0) { Status = SEC_E_TARGET_UNKNOWN; goto Cleanup; } FirstNamePart.Buffer = RealTargetName->Buffer; FirstNamePart.Length = Index * sizeof(WCHAR); FirstNamePart.MaximumLength = FirstNamePart.Length; } // else // { // Status = SEC_E_TARGET_UNKNOWN; // goto Cleanup; // } break; case L'/': // // All names that have a '/' separator are KRB_NT_SRV_INST // If we saw an @before this, we need to do something special. // NameType = KRB_NT_SRV_INST; NameParts ++; break; case '\\': // // If we have a realm name already, ignore this character. // if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) != 0) { break; } // // If we hit a backslash, this is an NT4 style name, so treat it // as such. // Just for error checking purposes, make sure that the current // name type was 0 // if (NameType != 0) { Status = SEC_E_TARGET_UNKNOWN; goto Cleanup; } NameType = KRB_NT_MS_PRINCIPAL; NameParts = 1; SuppliedRealm.Buffer = RealTargetName->Buffer; SuppliedRealm.Length = Index * sizeof(WCHAR); SuppliedRealm.MaximumLength = SuppliedRealm.Length; FirstNamePart.Buffer = &RealTargetName->Buffer[Index] + 1; FirstNamePart.Length = RealTargetName->Length - (Index + 1) * sizeof(WCHAR); FirstNamePart.MaximumLength = FirstNamePart.Length; if (SuppliedRealm.Length == 0 || FirstNamePart.Length == 0) { Status = SEC_E_TARGET_UNKNOWN; goto Cleanup; } DoneParsing = TRUE; break; default: break; } if (DoneParsing) { break; } } // // If we didn't exit early, then we were sent a name with no "@" sign. // If there were no separators, then it is a flat principal name // if (!DoneParsing && (NameType == 0)) { if (NameParts == 0) { // // The name has no separators, so it is a flat principal name // NameType = KRB_NT_PRINCIPAL; NameParts = 1; } } // // For KRB_NT_SRV_INST, get the name parts correct and tell the caller // that a realm was supplied in the spn // if (NameType == KRB_NT_SRV_INST) { if (SuppliedRealm.Length == 0) { // We have an spn of the form a/b..../n and the name parts needs // to be upped by one // If we had an spn of the form a/b@c, then the name // parts would be right. NameParts++; } else { // We need to filter this back to the caller so that the // name canonicalization bit is not set. *ProcessFlags |= KERB_GET_TICKET_NO_CANONICALIZE; } } // // Check for an MIT realm with the supplied realm - if the name type is // KRB_NT_PRINCIPAL, send it as a UPN unless we can find an MIT realm. // If we are not a member of a domain, then we can't default so use // the domain name supplied. Also, if we are using supplied credentials // we don't want to use the wksta realm. // if ((NameType == KRB_NT_PRINCIPAL) && (KerbGetGlobalRole() != KerbRoleStandalone) && ((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) != 0)) { BOOLEAN Result; Result = KerbLookupMitRealm( &SuppliedRealm, &MitRealm, &UsedAlternateName ); // // If we didn't find a realm, use this as a UPN // if (!Result) { // // If the caller supplied the realm separately, then don't // send it as a UPN. // if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) == 0) { NameType = KRB_NT_ENTERPRISE_PRINCIPAL; NameParts = 1; FirstNamePart = *RealTargetName; RtlInitUnicodeString( &SuppliedRealm, NULL ); } } // // For logon, its interesting to know if we're doing an MIT realm lookup // else { D_DebugLog((DEB_TRACE, "Using MIT realm in Process TargetName\n")); *ProcessFlags |= KERB_MIT_REALM_USED; } } NameLength = FirstNamePart.Length + NameParts * sizeof(WCHAR); D_DebugLog((DEB_TRACE_CTXT, "Parsed name %wZ (%wZ) into:\n\t name type 0x%x, name count %d, \n\t realm %wZ, \n\t first part %wZ\n", TargetName, SuppTargetName, NameType, NameParts, &SuppliedRealm, &FirstNamePart )); #ifdef later // // If the name end in "localhost", replace it with our dns machine // name. // if ((NameType == KRB_NT_SRV_INST) && (NameParts == 2) && (RealTargetName->Length > LocalhostName.Length) && RtlEqualMemory( RealTargetName->Buffer + (RealTargetName->Length - LocalhostName.Length ) / sizeof(WCHAR), LocalhostName.Buffer, LocalhostName.Length )) { NameLength -= LocalhostName.Length; KerbGlobalReadLock(); NameLength += KerbGlobalMitMachineServiceName->Names[1].Length; // // Set the flag to indicate we need to replace the name, and that // the global lock is held. // ReplaceLocalhost = TRUE; } #endif // // Create the output names // OutputName = (PKERB_INTERNAL_NAME) KerbAllocate(KERB_INTERNAL_NAME_SIZE(NameParts + ExtraNameParts) + NameLength); if (OutputName == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } OutputName->NameCount = NameParts + ExtraNameParts; OutputName->NameType = NameType; Where = (PUCHAR) OutputName + KERB_INTERNAL_NAME_SIZE(NameParts+ExtraNameParts); NameIndex = 0; // // If there is only one part of the name, handle that first // if (NameParts == 1) { OutputName->Names[0].Length = FirstNamePart.Length; OutputName->Names[0].MaximumLength = FirstNamePart.Length + sizeof(WCHAR); OutputName->Names[0].Buffer = (LPWSTR) Where; RtlCopyMemory( Where, FirstNamePart.Buffer, FirstNamePart.Length ); OutputName->Names[0].Buffer[FirstNamePart.Length / sizeof(WCHAR)] = L'\0'; Where += FirstNamePart.Length + sizeof(WCHAR); NameIndex = 1; } else { UNICODE_STRING TempName; // // Build up the name, piece by piece // DoneParsing = FALSE; NameIndex = 0; TempName.Buffer = FirstNamePart.Buffer; for ( Index = 0; Index <= FirstNamePart.Length / sizeof(WCHAR) ; Index++ ) { // // If we hit the end or a separator, build a name part // if ((Index == FirstNamePart.Length / sizeof(WCHAR)) || (FirstNamePart.Buffer[Index] == L'/') ) { #ifdef later if ((NameIndex == 1) && (ReplaceLocalhost)) { OutputName->Names[NameIndex].Length = KerbGlobalMitMachineServiceName->Names[1].Length; OutputName->Names[NameIndex].MaximumLength = OutputName->Names[NameIndex].Length + sizeof(WCHAR); OutputName->Names[NameIndex].Buffer = (LPWSTR) Where; RtlCopyMemory( Where, KerbGlobalMitMachineServiceName->Names[1].Buffer, OutputName->Names[NameIndex].Length ); // // Release the lock now // KerbGlobalReleaseLock(); ReplaceLocalhost = FALSE; } else #endif { OutputName->Names[NameIndex].Length = (USHORT) (&FirstNamePart.Buffer[Index] - TempName.Buffer) * sizeof(WCHAR); OutputName->Names[NameIndex].MaximumLength = OutputName->Names[NameIndex].Length + sizeof(WCHAR); OutputName->Names[NameIndex].Buffer = (LPWSTR) Where; RtlCopyMemory( Where, TempName.Buffer, OutputName->Names[NameIndex].Length ); } Where += OutputName->Names[NameIndex].Length; *(LPWSTR)Where = L'\0'; Where += sizeof(WCHAR); NameIndex++; TempName.Buffer = &FirstNamePart.Buffer[Index+1]; } } DsysAssert(NameParts == NameIndex); } // // Now that we've built the output name, check SPN Cache. // if ( ARGUMENT_PRESENT(SpnCacheEntry) && NameType == KRB_NT_SRV_INST && SuppliedRealm.Length == 0 ) { LocalCacheEntry = KerbLocateSpnCacheEntry(OutputName); if (NULL != LocalCacheEntry) { DebugLog((DEB_TRACE_SPN_CACHE, "Found in SPN Cache %p ", LocalCacheEntry)); D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, LocalCacheEntry->Spn); *SpnCacheEntry = LocalCacheEntry; LocalCacheEntry = NULL; *ProcessFlags |= KERB_TARGET_USED_SPN_CACHE; } } D_DebugLog((DEB_TRACE_CTXT,"Cracked name %wZ into: ", RealTargetName)); D_KerbPrintKdcName(DEB_TRACE_CTXT,OutputName); if (((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) == 0) || SuppliedRealm.Length > 0) { Status = KerbDuplicateString( TargetRealm, &SuppliedRealm ); } else { if ((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) == 0) { DsysAssert(FALSE); // hey, this shouldn't ever be hit... RtlInitUnicodeString( TargetRealm, NULL ); } else { // // There was no realm name provided, so use the wksta domain // Status = KerbGetOurDomainName( TargetRealm ); } } if (!NT_SUCCESS(Status)) { goto Cleanup; } *FinalTarget = OutputName; OutputName = NULL; Cleanup: #ifdef later if (ReplaceLocalhost) { KerbGlobalReleaseLock(); } #endif if ( LocalCacheEntry ) { KerbDereferenceSpnCacheEntry( LocalCacheEntry ); } if (OutputName != NULL) { KerbFree(OutputName); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbValidateChannelBindings // // Synopsis: Validates the channel bindings copied from the client. // // Effects: // // Arguments: pBuffer -- Input buffer that contains channel bindings // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- NTSTATUS KerbValidateChannelBindings( IN PVOID pBuffer, IN ULONG ulBufferLength ) { PSEC_CHANNEL_BINDINGS pClientBindings = (PSEC_CHANNEL_BINDINGS) pBuffer; DWORD dwBindingLength; DWORD dwInitiatorEnd; DWORD dwAcceptorEnd; DWORD dwApplicationEnd; // // If channel bindings were specified, they had better be there // if (pBuffer == NULL || ulBufferLength < sizeof(SEC_CHANNEL_BINDINGS)) { return STATUS_INVALID_PARAMETER; } // // Make sure we got one contiguous buffer // dwBindingLength = sizeof(SEC_CHANNEL_BINDINGS) + pClientBindings->cbInitiatorLength + pClientBindings->cbAcceptorLength + pClientBindings->cbApplicationDataLength; // // Make sure the lengths are valid and check for overflow // if (dwBindingLength > ulBufferLength) { return STATUS_INVALID_PARAMETER; } dwInitiatorEnd = pClientBindings->dwInitiatorOffset + pClientBindings->cbInitiatorLength; dwAcceptorEnd = pClientBindings->dwAcceptorOffset + pClientBindings->cbAcceptorLength; dwApplicationEnd = pClientBindings->dwApplicationDataOffset + pClientBindings->cbApplicationDataLength; if ((dwInitiatorEnd > dwBindingLength || dwInitiatorEnd < pClientBindings->dwInitiatorOffset) || (dwAcceptorEnd > dwBindingLength || dwAcceptorEnd < pClientBindings->dwAcceptorOffset) || (dwApplicationEnd > dwBindingLength || dwApplicationEnd < pClientBindings->dwApplicationDataOffset)) { return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } //+------------------------------------------------------------------------- // // Function: SpInitLsaModeContext // // Synopsis: Kerberos implementation of InitializeSecurityContext. This // routine handles the client side of authentication by // acquiring a ticket to the specified target. If a context // handle is passed in, then the input buffer is used to // verify the authenticity of the server. // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpInitLsaModeContext( IN OPTIONAL LSA_SEC_HANDLE CredentialHandle, IN OPTIONAL LSA_SEC_HANDLE ContextHandle, IN OPTIONAL PUNICODE_STRING TargetName, IN ULONG ContextRequirements, IN ULONG TargetDataRep, IN PSecBufferDesc InputBuffers, OUT PLSA_SEC_HANDLE NewContextHandle, IN OUT PSecBufferDesc OutputBuffers, OUT PULONG ContextAttributes, OUT PTimeStamp ExpirationTime, OUT PBOOLEAN MappedContext, OUT PSecBuffer ContextData ) { PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDENTIAL Credential = NULL; PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL; ULONG TicketOptions = 0; NTSTATUS Status = STATUS_SUCCESS; LUID LogonId; PUCHAR Request = NULL; ULONG RequestSize = 0; PUCHAR Reply = NULL; ULONG ReplySize; PSecBuffer OutputToken = NULL; PSecBuffer InputToken = NULL; UNICODE_STRING LocalTargetName; UNICODE_STRING TargetDomainName; PKERB_INTERNAL_NAME TargetInternalName = NULL; ULONG Index; PKERB_CONTEXT Context = NULL; ULONG Nonce = 0; ULONG ProcessFlags = 0; ULONG ReceiveNonce = 0; ULONG ContextFlags = 0; ULONG ContextAttribs = 0; TimeStamp ContextLifetime; BOOLEAN DoThirdLeg = FALSE; BOOLEAN GetAuthTicket = FALSE; BOOLEAN UseNullSession = FALSE; BOOLEAN GetServerTgt = FALSE; PKERB_ERROR ErrorMessage = NULL; PKERB_ERROR_METHOD_DATA ErrorData = NULL; PKERB_EXT_ERROR pExtendedError = NULL; PKERB_TGT_REPLY TgtReply = NULL; PKERB_SPN_CACHE_ENTRY SpnCacheEntry = NULL; ULONG ContextRetries = 0; KERB_CONTEXT_STATE ContextState = InvalidState; KERB_ENCRYPTION_KEY SubSessionKey = {0}; BOOLEAN ClientAskedForDelegate = FALSE, ClientAskedForDelegateIfSafe = FALSE; ULONG ClientProcess = 0; PKERB_CREDMAN_CRED CredManCredentials = NULL; NTSTATUS InitialStatus = STATUS_SUCCESS; PSEC_CHANNEL_BINDINGS pChannelBindings = NULL; PBYTE pbMarshalledTargetInfo = NULL; ULONG cbMarshalledTargetInfo = 0; LUID NetworkServiceLuid = NETWORKSERVICE_LUID; KERB_INITSC_INFO InitSCTraceInfo; InitSCTraceInfo.EventTrace.Size = 0; D_DebugLog((DEB_TRACE_API,"SpInitLsaModeContext 0x%x called\n",ContextHandle)); if( KerbEventTraceFlag ) // Event Trace: KerbInitSecurityContextStart {No Data} { InitSCTraceInfo.EventTrace.Guid = KerbInitSCGuid; InitSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; InitSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; InitSCTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER); TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&InitSCTraceInfo ); } // // Initialize the outputs. // *ContextAttributes = 0; *NewContextHandle = 0; *ExpirationTime = KerbGlobalHasNeverTime; *MappedContext = FALSE; ContextData->pvBuffer = NULL; ContextData->cbBuffer = 0; LocalTargetName.Buffer = NULL; LocalTargetName.Length = 0; TargetDomainName.Buffer = NULL; TargetDomainName.Length = 0; if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; goto Cleanup; } // // Make sure we have at least one ip address // KerbGlobalReadLock(); if (KerbGlobalNoTcpUdp) { Status = STATUS_NETWORK_UNREACHABLE; } KerbGlobalReleaseLock(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Delegate will mean delegate_if_safe for this // release (NT5) // if ( ContextRequirements & ISC_REQ_DELEGATE ) { ClientAskedForDelegate = TRUE; ContextRequirements |= ISC_REQ_DELEGATE_IF_SAFE ; ContextRequirements &= ~(ISC_REQ_DELEGATE) ; } else if ( ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE ) { ClientAskedForDelegateIfSafe = TRUE; } ////////////////////////////////////////////////////////////////////// // // Process the input tokens // ///////////////////////////////////////////////////////////////////// // // First locate the output token. // for (Index = 0; Index < OutputBuffers->cBuffers ; Index++ ) { if (BUFFERTYPE(OutputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { OutputToken = &OutputBuffers->pBuffers[Index]; Status = LsaFunctions->MapBuffer(OutputToken,OutputToken); break; } } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to map output token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Now locate the Input token. // for (Index = 0; Index < InputBuffers->cBuffers ; Index++ ) { if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { InputToken = &InputBuffers->pBuffers[Index]; Status = LsaFunctions->MapBuffer(InputToken,InputToken); break; } } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to map Input token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Check to see if we were passed an additional name // for (Index = 0; Index < InputBuffers->cBuffers ; Index++ ) { if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_NEGOTIATION_INFO) { Status = LsaFunctions->MapBuffer( &InputBuffers->pBuffers[Index], &InputBuffers->pBuffers[Index] ); if (!NT_SUCCESS(Status)) { D_DebugLog( (DEB_ERROR, "Failed to map incoming SECBUFFER_NEGOTIATION_INFO. %x, %ws, %d", Status, THIS_FILE, __LINE__) ); goto Cleanup; } LocalTargetName.Buffer = (LPWSTR) InputBuffers->pBuffers[Index].pvBuffer; // // We can only stick 64k in a the length field, so make sure the // buffer is not too big. // if (InputBuffers->pBuffers[Index].cbBuffer > 0xffff) { Status = SEC_E_INVALID_TOKEN; goto Cleanup; } LocalTargetName.Length = LocalTargetName.MaximumLength = (USHORT) InputBuffers->pBuffers[Index].cbBuffer; break; } } // // Process the target names // Status = KerbProcessTargetNames( TargetName, &LocalTargetName, 0, // No flags &ProcessFlags, &TargetInternalName, &TargetDomainName, &SpnCacheEntry ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Check to see if we were passed channel bindings // for( Index = 0; Index < InputBuffers->cBuffers; Index++ ) { if( BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_CHANNEL_BINDINGS ) { PVOID temp = NULL; Status = LsaFunctions->MapBuffer( &InputBuffers->pBuffers[Index], &InputBuffers->pBuffers[Index] ); if( !NT_SUCCESS(Status) ) { D_DebugLog( (DEB_ERROR, "Failed to map incoming SECBUFFER_CHANNEL_BINDINGS. %x, %ws, %d\n", Status, THIS_FILE, __LINE__) ); goto Cleanup; } pChannelBindings = (PSEC_CHANNEL_BINDINGS) InputBuffers->pBuffers[Index].pvBuffer; Status = KerbValidateChannelBindings(pChannelBindings, InputBuffers->pBuffers[Index].cbBuffer); if (!NT_SUCCESS(Status)) { pChannelBindings = NULL; goto Cleanup; } break; } } ////////////////////////////////////////////////////////////////////// // // If the caller passed in a context handle, deal with updating an // existing context. // ///////////////////////////////////////////////////////////////////// // // If the input context handle is no NULL then we are actually // finalizing an already-existing context. So be it. // // // Use "while" so we can break out. // while (ContextHandle != 0) { D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: Second call to Initialize\n")); if (InputToken == NULL) { D_DebugLog((DEB_ERROR,"Trying to complete a context with no input token! %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // First reference the context. // Status = KerbReferenceContext( ContextHandle, FALSE, // don't unlink &Context ); if (Context == NULL) { D_DebugLog((DEB_ERROR,"Failed to reference context 0x%x. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__)); goto Cleanup; } // // Check the mode of the context to make sure we can finalize it. // KerbReadLockContexts(); ContextState = Context->ContextState; if ((ContextState != ApRequestSentState) && (ContextState != TgtRequestSentState)) { D_DebugLog((DEB_ERROR,"Invalid context state: %d. %ws, line %d\n", Context->ContextState, THIS_FILE, __LINE__)); Status = STATUS_INVALID_HANDLE; KerbUnlockContexts(); goto Cleanup; } ContextRetries = Context->Retries; ContextFlags = Context->ContextFlags; ContextAttribs = Context->ContextAttributes; Nonce = Context->Nonce; CredentialHandle = Context->CredentialHandle; ClientProcess = Context->ClientProcess; KerbUnlockContexts(); // // If we are not doing datagram, unpack the AP or TGT reply. // if ((ContextFlags & ISC_RET_DATAGRAM) == 0) { //////////////////////////////////////////////////// // // Handle a TGT reply - get out the TGT & the name of // the server // //////////////////////////////////////////////////// if (ContextState == TgtRequestSentState) { D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext calling KerbUnpackTgtReply\n")); KerbWriteLockContexts(); if (!(Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER)) { Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER; DebugLog((DEB_WARN, "SpInitLsaModeContext * use_sesion_key but USER2USER-OUTBOUND not set, added it now\n")); } KerbUnlockContexts(); Status = KerbUnpackTgtReply( Context, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, &TargetInternalName, &TargetDomainName, &TgtReply ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to unpack TGT reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); // // Check for an error message // Status = KerbReceiveErrorMessage( (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, Context, &ErrorMessage, &ErrorData ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbReportKerbError( NULL, NULL, NULL, NULL, KLIN(FILENO,__LINE__), ErrorMessage, ((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC), pExtendedError, FALSE ); // // Ahh. We have an error message. See if it is an // error we can handle, and if so, Now we need to // try to build a better AP request. Or, if we have // already retried once, fail. // DebugLog((DEB_WARN, "SpInitLsaModeContext received KERB_ERROR message with error 0x%x, can't handle\n", ErrorMessage->error_code )); if ((ErrorMessage->error_code == KRB_ERR_GENERIC) && (ErrorData != NULL) && (ErrorData->bit_mask & data_value_present) && (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS) && (ErrorData->data_value.length == sizeof(ULONG))) { Status = *((PULONG)ErrorData->data_value.value); } else { Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code); if (NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; } } goto Cleanup; } // // Break out so we generate a normal request now // break; } else // not user-to-user { //////////////////////////////////////////////////// // // This is the response to an AP request. It could be // an AP reply or an error. Handle both cases // //////////////////////////////////////////////////// // // Now unpack the AP reply // Status = KerbVerifyApReply( Context, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, &ReceiveNonce ); if (!NT_SUCCESS(Status)) { // // Check for an error message // Status = KerbReceiveErrorMessage( (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, Context, &ErrorMessage, &ErrorData ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbReportKerbError( NULL, NULL, NULL, NULL, KLIN(FILENO, __LINE__), ErrorMessage, ((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC), pExtendedError, FALSE ); DebugLog((DEB_WARN,"Failed to verify AP reply: 0x%x\n",ErrorMessage->error_code)); // // Ahh. We have an error message. See if it is an // error we can handle, and if so, Now we need to // try to build a better AP request. Or, if we have // already retried once, fail. We can't get a new ticket // with user-to-user. // if ((ContextRetries != 0) || (((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_SKEW) && ((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_TKT_NYV) && ((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_USER_TO_USER_REQUIRED) && ((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_MODIFIED)) || (((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED) && ((ContextAttribs & KERB_CONTEXT_USER_TO_USER) != 0))) { if ((ErrorMessage->error_code == KRB_ERR_GENERIC) && (ErrorData != NULL) && (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS) && ((ErrorData->bit_mask & data_value_present) != 0) && (ErrorData->data_value.value != NULL) && (ErrorData->data_value.length == sizeof(ULONG))) { Status = *((PULONG)ErrorMessage->error_data.value); if (NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; } } if (ErrorMessage->error_code == KRB_AP_ERR_MODIFIED) { // // If the server couldn't decrypt the ticket, // then the target name is wrong for the server. // DebugLog((DEB_ERROR, "App modified error (NO CONTINUE, bail)\n")); KerbReportApError(ErrorMessage); Status = SEC_E_WRONG_PRINCIPAL; } else if (ErrorMessage->error_code == KRB_AP_ERR_TKT_NYV) { DebugLog((DEB_ERROR, "Not yet valid error - Check Time Skew\n")); KerbReportApError(ErrorMessage); Status = STATUS_TIME_DIFFERENCE_AT_DC; } else { DebugLog((DEB_ERROR, "InitSecContext Received KERB_ERROR message with error 0x%x, can't handle. %ws, line %d\n", ErrorMessage->error_code, THIS_FILE, __LINE__ )); Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code); if (NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; } } goto Cleanup; } else { // // Check to see if the server supports skew // if ((ErrorMessage->error_code == KRB_AP_ERR_SKEW) && (ErrorData == NULL) || ((ErrorData != NULL) && (ErrorData->data_type != KERB_AP_ERR_TYPE_SKEW_RECOVERY))) { // // The server doesn't support skew recovery. // D_DebugLog((DEB_WARN,"Skew error but server doesn't handle skew recovery.\n")); Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code); goto Cleanup; } // // Here's where we'll punt "not yet valid tickets" and friends... // if (ErrorMessage->error_code == KRB_AP_ERR_TKT_NYV) { KerbPurgeServiceTicketAndTgt( Context, CredentialHandle, CredManCredentials ); DebugLog((DEB_ERROR, "Purged all tickets due to NYV error!\n")); } KerbWriteLockContexts(); Context->Retries++; KerbUnlockContexts(); } //////////////////////////////////////////////////// // // We got an error we can handle. For user-to-user // required, we received the TGT so we can get // the appropriate ticket. For modified, we want // to toss the old ticket & get a new one, hopefully // with a better key. // //////////////////////////////////////////////////// if ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_USER_TO_USER_REQUIRED) { D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext received KRB_AP_ERR_USER_TO_USER_REQUIRED\n")); if ((ErrorMessage->bit_mask & error_data_present) == 0) { DebugLog((DEB_ERROR,"Server requires user-to-user but didn't send TGT reply. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_NO_TGT_REPLY; goto Cleanup; } // // Check for TGT reply // Status = KerbUnpackTgtReply( Context, ErrorMessage->error_data.value, ErrorMessage->error_data.length, &TargetInternalName, &TargetDomainName, &TgtReply ); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Fall through into normal ticket handling // ContextFlags |= ISC_RET_USE_SESSION_KEY; // // Remove the old ticket cache entry // KerbWriteLockContexts(); TicketCacheEntry = Context->TicketCacheEntry; Context->TicketCacheEntry = NULL; KerbUnlockContexts(); if (TicketCacheEntry != NULL) { KerbFreeString( &TargetDomainName ); KerbFreeKdcName( &TargetInternalName ); // // Get the target name from the old ticket // KerbReadLockTicketCache(); Status = KerbDuplicateString( &TargetDomainName, &TicketCacheEntry->DomainName ); if (NT_SUCCESS(Status)) { Status = KerbDuplicateKdcName( &TargetInternalName, TicketCacheEntry->ServiceName ); } KerbUnlockTicketCache(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Free this ticket cache entry // KerbRemoveTicketCacheEntry(TicketCacheEntry); // // Remove the reference holding it to the context // KerbDereferenceTicketCacheEntry(TicketCacheEntry); TicketCacheEntry = NULL; } break; } else if ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED) { DebugLog((DEB_WARN, "App modified error (purge ticket!)\n")); KerbWriteLockContexts(); TicketCacheEntry = Context->TicketCacheEntry; Context->TicketCacheEntry = NULL; KerbUnlockContexts(); if (TicketCacheEntry != NULL) { // // Get rid of the old ticket in the context KerbFreeString( &TargetDomainName ); KerbFreeKdcName( &TargetInternalName ); // // Get the target name from the old ticket // KerbReadLockTicketCache(); Status = KerbDuplicateString( &TargetDomainName, &TicketCacheEntry->DomainName ); if (NT_SUCCESS(Status)) { Status = KerbDuplicateKdcName( &TargetInternalName, TicketCacheEntry->ServiceName ); } KerbUnlockTicketCache(); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Free this ticket cache entry // KerbRemoveTicketCacheEntry(TicketCacheEntry); // // Remove the reference holding it to the context // KerbDereferenceTicketCacheEntry(TicketCacheEntry); TicketCacheEntry = NULL; } } break; } } //////////////////////////////////////////////////// // // We successfully decrypted the AP reply. At this point // we want to finalize the context. For DCE style we send // a useless AP reply to the server. // //////////////////////////////////////////////////// // // If the caller wanted DCE style authentication, build another // AP reply // KerbWriteLockContexts(); if ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) { DoThirdLeg = TRUE; } else { DoThirdLeg = FALSE; } Context->ReceiveNonce = ReceiveNonce; KerbUnlockContexts(); if (DoThirdLeg) { // // Build an AP reply to send back to the server. // Status = KerbBuildThirdLegApReply( Context, ReceiveNonce, &Reply, &ReplySize ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to build AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } if (OutputToken == NULL) { D_DebugLog((DEB_ERROR,"Output token missing. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Return the AP reply in the output buffer. // if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0) { if (OutputToken->cbBuffer < ReplySize) { ULONG ErrorData[3]; ErrorData[0] = ReplySize; ErrorData[1] = OutputToken->cbBuffer; ErrorData[2] = ClientProcess; DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n", OutputToken->cbBuffer,ReplySize, THIS_FILE, __LINE__ )); OutputToken->cbBuffer = ReplySize; Status = STATUS_BUFFER_TOO_SMALL; KerbReportNtstatus( KERBEVT_INSUFFICIENT_TOKEN_SIZE, Status, NULL, 0, ErrorData, 3 ); goto Cleanup; } RtlCopyMemory( OutputToken->pvBuffer, Reply, ReplySize ); } else { OutputToken->pvBuffer = Reply; Reply = NULL; *ContextAttributes |= ISC_RET_ALLOCATED_MEMORY; } OutputToken->cbBuffer = ReplySize; } else { // // No return message, so set the return length to zero. // if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } } } else { ////////////////////////////////////////////////////////////////////// // // We are doing datagram, so we don't expect anything from the // server but perhaps an error. If we get an error, handle it. // Otherwise, build an AP request to send to the server // ///////////////////////////////////////////////////////////////////// // // We are doing datagram. Build the AP request for the // server side. // // // Check for an error message // if ((InputToken != NULL) && (InputToken->cbBuffer != 0)) { Status = KerbReceiveErrorMessage( (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, Context, &ErrorMessage, &ErrorData ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbReportKerbError( NULL, NULL, NULL, NULL, KLIN(FILENO, __LINE__), ErrorMessage, ((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC), pExtendedError, FALSE ); // // Check for a real error // if ((ErrorData != NULL) && (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS) && ((ErrorData->bit_mask & data_value_present) != 0) && (ErrorData->data_value.value != NULL) && (ErrorData->data_value.length == sizeof(ULONG))) { Status = *((PULONG)ErrorMessage->error_data.value); if (NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; } goto Cleanup; } } // // Get the associated credential // Status = KerbReferenceCredential( CredentialHandle, KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL, FALSE, &Credential); if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Failed to locate credential: 0x%x\n",Status)); goto Cleanup; } // // Get the logon id from the credentials so we can locate the // logon session. // LogonId = Credential->LogonId; // // Get the logon session // LogonSession = KerbReferenceLogonSession( &LogonId, FALSE ); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } KerbReadLockLogonSessions(LogonSession); if (Credential->SuppliedCredentials != NULL) { GetAuthTicket = TRUE; } else if (((Credential->CredentialFlags & KERB_CRED_NULL_SESSION) != 0) || ((ContextRequirements & ISC_REQ_NULL_SESSION) != 0)) { UseNullSession = TRUE; ContextFlags |= ISC_RET_NULL_SESSION; } KerbUnlockLogonSessions(LogonSession); KerbReadLockContexts(); TicketCacheEntry = Context->TicketCacheEntry; // // Get the session key to use from the context // if (Context->SessionKey.keyvalue.value != 0) { if (!KERB_SUCCESS(KerbDuplicateKey( &SubSessionKey, &Context->SessionKey ))) { Status = STATUS_INSUFFICIENT_RESOURCES; } } Context->TicketCacheEntry = NULL; KerbUnlockContexts(); if (!NT_SUCCESS(Status)) { goto Cleanup; } D_DebugLog((DEB_TRACE_CTXT2,"Building AP request for datagram oriented context\n")); // // If we are building a null session, build the special null // session AP request // if (UseNullSession) { Status = KerbBuildNullSessionApRequest( &Request, &RequestSize ); // // Turn off all unsupported flags // ContextFlags &= ( ISC_RET_ALLOCATED_MEMORY | ISC_RET_CONNECTION | ISC_RET_DATAGRAM | ISC_RET_NULL_SESSION ); } else { if (TicketCacheEntry == NULL) { DebugLog((DEB_ERROR, "SpInitLsaModeContext does have service ticket\n")); Status = STATUS_INTERNAL_ERROR; goto Cleanup; } Status = KerbBuildApRequest( LogonSession, Credential, CredManCredentials, TicketCacheEntry, ErrorMessage, ContextAttribs, &ContextFlags, &Request, &RequestSize, &Nonce, &SubSessionKey, pChannelBindings ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to build AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Return the AP request in the output buffer. // if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0) { if (OutputToken->cbBuffer < RequestSize) { ULONG ErrorData[3]; ErrorData[0] = RequestSize; ErrorData[1] = OutputToken->cbBuffer; ErrorData[2] = ClientProcess; D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n", OutputToken->cbBuffer,RequestSize, THIS_FILE, __LINE__ )); OutputToken->cbBuffer = RequestSize; Status = STATUS_BUFFER_TOO_SMALL; KerbReportNtstatus( KERBEVT_INSUFFICIENT_TOKEN_SIZE, Status, NULL, 0, ErrorData, 3 ); goto Cleanup; } RtlCopyMemory( OutputToken->pvBuffer, Request, RequestSize ); } else { OutputToken->pvBuffer = Request; *ContextAttributes |= ISC_RET_ALLOCATED_MEMORY; Request = NULL; } OutputToken->cbBuffer = RequestSize; } // // // We're done - we finalized. // Status = STATUS_SUCCESS; KerbReadLockContexts(); Context->ContextFlags = ContextFlags; *ContextAttributes |= Context->ContextFlags; KerbUtcTimeToLocalTime( ExpirationTime, &Context->Lifetime ); *NewContextHandle = ContextHandle; KerbUnlockContexts(); goto Cleanup; } ////////////////////////////////////////////////////////////////////// // // We need to create a request to the server, possibly a TGT request // or an AP request depending on what phase of the protocol we're in. // ///////////////////////////////////////////////////////////////////// DsysAssert(!((Context != NULL) ^ ((ErrorMessage != NULL) || (TgtReply != NULL)))); D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: First call to Initialize\n")); // // This is the case where we are constructing a new context. // // // Get the associated credential and its TGT, if needed // Status = KerbReferenceCredential( CredentialHandle, KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL, FALSE, &Credential ); if (!NT_SUCCESS(Status)) { InitialStatus = Status; Status = KerbReferenceCredential( CredentialHandle, KERB_CRED_OUTBOUND, FALSE, &Credential ); if( !NT_SUCCESS( Status ) || Credential->SuppliedCredentials != NULL) { Status = InitialStatus; D_DebugLog((DEB_WARN,"Failed to locate credential 0x%x\n",Status)); goto Cleanup; } // // if we got here, only explicit or credman creds are allowed. // If the explicit creds failed to get a TGT from above, its also // time to bail, as explicit creds never should fall back to credman. // } // // Get the logon id from the credentials so we can locate the // logon session. // LogonId = Credential->LogonId; // // Get the logon session // LogonSession = KerbReferenceLogonSession( &LogonId, FALSE ); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } KerbWriteLockLogonSessions(LogonSession); if (Credential->SuppliedCredentials != NULL) { GetAuthTicket = TRUE; // Ignore SPN cache for supplied credentials ProcessFlags &= ~KERB_TARGET_UNKNOWN_SPN; } else if (((Credential->CredentialFlags & KERB_CRED_NULL_SESSION) != 0) || ((ContextRequirements & ISC_REQ_NULL_SESSION) != 0)) { UseNullSession = TRUE; ContextFlags |= ISC_RET_NULL_SESSION; } else { // // go to the credential manager to try and find // credentials for this specific target // ULONG ExtraTargetFlags = 0; if ((ContextRequirements & ISC_REQ_USE_SUPPLIED_CREDS) != 0) { ExtraTargetFlags = CRED_TI_ONLY_PASSWORD_REQUIRED; } Status = KerbCheckCredMgrForGivenTarget( LogonSession, Credential, TargetName, // original targetname, may contain marshalled targetinfo TargetInternalName, ExtraTargetFlags, &TargetDomainName, NULL, &CredManCredentials, &pbMarshalledTargetInfo, &cbMarshalledTargetInfo ); if (!NT_SUCCESS(Status)) { KerbUnlockLogonSessions(LogonSession); D_DebugLog((DEB_ERROR,"Failed to get outbound ticket: 0x%x\n",Status)); goto Cleanup; } if (CredManCredentials != NULL) { GetAuthTicket = TRUE; ProcessFlags &= ~KERB_TARGET_UNKNOWN_SPN; } else { // // if this is a local account logon then we have to have a cred man // credential // if ((Credential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) != 0) { KerbUnlockLogonSessions(LogonSession); D_DebugLog((DEB_WARN, "Trying to use a local logon session with Kerberos\n")); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; } // // if no credman cred was found, we didn't use explicit creds, // and the initial credential reference for TGT_AVAIL failed, bail now. // if( !NT_SUCCESS( InitialStatus ) ) { KerbUnlockLogonSessions(LogonSession); Status = InitialStatus; D_DebugLog((DEB_WARN,"Failed to locate credential 0x%x\n",Status)); goto Cleanup; } } } #if DBG D_DebugLog((DEB_TRACE_CTXT, "SpInitLsaModeContext: Initailizing context for %wZ\\%wZ\n", &LogonSession->PrimaryCredentials.DomainName, &LogonSession->PrimaryCredentials.UserName )); #endif KerbUnlockLogonSessions(LogonSession); ////////////////////////////////////////////////////////////////////// // // Process all the context requirements. We don't support all of them // and some of them are mutually exclusive. In general, we don't fail // if we're asked to do something we can't do, unless it seems mandatory, // like allocating memory. // ///////////////////////////////////////////////////////////////////// // // Figure out the context flags // if ((ContextRequirements & ISC_REQ_MUTUAL_AUTH) != 0) { D_DebugLog((DEB_TRACE_CTXT,"Client wants mutual auth.\n")); ContextFlags |= ISC_RET_MUTUAL_AUTH; } if ((ContextRequirements & ISC_REQ_SEQUENCE_DETECT) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants sequence detect\n")); ContextFlags |= ISC_RET_SEQUENCE_DETECT | ISC_RET_INTEGRITY; } if ((ContextRequirements & ISC_REQ_REPLAY_DETECT) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants replay detect\n")); ContextFlags |= ISC_RET_REPLAY_DETECT | ISC_RET_INTEGRITY; } if ((ContextRequirements & ISC_REQ_INTEGRITY) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants integrity\n")); ContextFlags |= ISC_RET_INTEGRITY; } if ((ContextRequirements & ISC_REQ_CONFIDENTIALITY) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants privacy\n")); ContextFlags |= (ISC_RET_CONFIDENTIALITY | ISC_RET_INTEGRITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT ); } if ((ContextRequirements & ISC_REQ_USE_DCE_STYLE) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants DCE style\n")); ContextFlags |= ISC_RET_USED_DCE_STYLE; } if ((ContextRequirements & ISC_REQ_EXTENDED_ERROR) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants extended error\n")); ContextFlags |= ISC_RET_EXTENDED_ERROR; } if ((ContextRequirements & ISC_REQ_DATAGRAM) != 0) { if ((ContextRequirements & ISC_REQ_CONNECTION) != 0) { D_DebugLog((DEB_ERROR,"Client wanted both data gram and connection. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } D_DebugLog((DEB_TRACE_CTXT, "Client wants Datagram style\n")); ContextFlags |= ISC_RET_DATAGRAM; } if ((ContextRequirements & ISC_REQ_USE_SESSION_KEY) != 0) { D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext Client wants sub-session key\n")); // // Can't do this with datagram because we need to be able to // start sealing messages after the first call to Initialize. // // With a null session there is no real ticket so we don't ever // need the server TGT either. // // Can't do DCE style because they don't have this. // if (!UseNullSession && (ContextRequirements & (ISC_REQ_DATAGRAM | ISC_REQ_USE_DCE_STYLE)) == 0) { // // If we are in the first call, get a server TGT // if (ContextState == InvalidState) { GetServerTgt = TRUE; } ContextFlags |= ISC_RET_USE_SESSION_KEY; } else { D_DebugLog((DEB_WARN,"Client wanted both datagram and session key, dropping session key\n")); } } if ((ContextRequirements & ISC_REQ_DELEGATE) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants Delegation\n")); if ((ContextFlags & ISC_RET_MUTUAL_AUTH) == 0) { D_DebugLog((DEB_WARN,"Can't do delegate without mutual\n")); } else { ContextFlags |= ISC_RET_DELEGATE; } } else if ((ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants Delegation, if safe\n")); if ((ContextFlags & ISC_RET_MUTUAL_AUTH) == 0) { D_DebugLog((DEB_WARN,"Can't do delegate without mutual\n")); } else { ContextFlags |= ISC_RET_DELEGATE_IF_SAFE; } } if ((ContextRequirements & ISC_REQ_CONNECTION) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants Connection style\n")); ContextFlags |= ISC_RET_CONNECTION; } if ((ContextRequirements & ISC_REQ_IDENTIFY) != 0) { D_DebugLog((DEB_TRACE_CTXT, "Client wants Identify level\n")); ContextFlags |= ISC_RET_IDENTIFY; if (((ContextRequirements & ISC_REQ_DELEGATE) != 0) || ((ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE) != 0)) { D_DebugLog((DEB_WARN, "Client wants Delegation and Indentify, turning off delegation\n")); ContextFlags &= ~ISC_RET_DELEGATE; ContextFlags &= ~ISC_RET_DELEGATE_IF_SAFE; } } ////////////////////////////////////////////////////////////////////// // // Get the ticket necessary to process the request. At this point: // - TicketCacheEntry should contain the ticket to re-use // - ErrorMessage should contain the error message, if there was one // ///////////////////////////////////////////////////////////////////// // // Get the outbound ticket. If the credential has attached supplied // credentials, get an AS ticket instead. // if (GetServerTgt) { // // Nothing to do // } else if (!UseNullSession) { D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: Getting outbound ticket for %wZ (%wZ) or ", TargetName, &LocalTargetName )); D_KerbPrintKdcName(DEB_TRACE_CTXT, TargetInternalName ); // // If we got a skew error and we already have a cached ticket, don't // bother getting a new ticket. // KerbReadLockContexts(); if (ErrorMessage != NULL) { if (((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_SKEW) && (Context->TicketCacheEntry != NULL)) { KerbReferenceTicketCacheEntry(Context->TicketCacheEntry); TicketCacheEntry = Context->TicketCacheEntry; } else { // // use2user assumes ticketTicketCacheEntry to be non null at // this point // DsysAssert((Context->TicketCacheEntry != NULL) || ((ContextAttribs & KERB_CONTEXT_USER_TO_USER) == 0)); } } KerbUnlockContexts(); // // If we don't have a ticket in the context, go ahead and get // a new ticket // if (TicketCacheEntry == NULL) { D_DebugLog((DEB_TRACE, "Getting service ticket\n")); Status = KerbGetServiceTicket( LogonSession, Credential, CredManCredentials, TargetInternalName, &TargetDomainName, SpnCacheEntry, ProcessFlags, TicketOptions, 0, // no enc type ErrorMessage, NULL, // no authorization data TgtReply, // no tgt reply &TicketCacheEntry, NULL // don't return logon guid ); if (Status == STATUS_USER2USER_REQUIRED) { D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext failed to get serviceticket: STATUS_USER2USER_REQUIRED\n")); Status = STATUS_SUCCESS; ContextFlags |= ISC_RET_USE_SESSION_KEY; GetServerTgt = TRUE; } else if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Failed to get outbound ticket: 0x%x\n",Status)); goto Cleanup; } } // // fail user2user in data gram: not enough round trips to complete the protocol // if ((ContextFlags & ISC_RET_USE_SESSION_KEY) && (ContextFlags & ISC_RET_DATAGRAM)) { DebugLog((DEB_ERROR, "SpInitLsaModeContext Client needed session key in datagram, dropping session key\n")); ContextFlags |= ~ISC_RET_USE_SESSION_KEY; Status = STATUS_LOGON_FAILURE; goto Cleanup; } } else { // // Turn off all unsupported flags // ContextFlags &= ( ISC_RET_ALLOCATED_MEMORY | ISC_RET_CONNECTION | ISC_RET_DATAGRAM | ISC_RET_NULL_SESSION ); } ////////////////////////////////////////////////////////////////////// // // Build the request - an AP request for standard Kerberos, or a TGT // request. // ///////////////////////////////////////////////////////////////////// // // For datagram requests, there is no output. // if ((ContextFlags & ISC_RET_DATAGRAM) == 0) { // // This is the case where we are constructing a response. // if (OutputToken == NULL) { D_DebugLog((DEB_ERROR,"Trying to initialize a context with no output token! %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } if (UseNullSession) { Status = KerbBuildNullSessionApRequest( &Request, &RequestSize ); } else if (GetServerTgt) { D_DebugLog((DEB_TRACE,"Building TGT request for ")); KerbPrintKdcName(DEB_TRACE, TargetInternalName); if (((ContextRequirements & ISC_REQ_MUTUAL_AUTH) != 0) && (!ARGUMENT_PRESENT(TargetName) || TargetName->Length == 0)) { D_DebugLog((DEB_ERROR, "Client wanted mutual auth, but did not supply target name\n")); Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } Status = KerbBuildTgtRequest( TargetInternalName, &TargetDomainName, &ContextAttribs, &Request, &RequestSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } else { D_DebugLog((DEB_TRACE_CTXT2,"Building AP request for connection oriented context\n")); Status = KerbBuildApRequest( LogonSession, Credential, CredManCredentials, TicketCacheEntry, ErrorMessage, ContextAttribs, &ContextFlags, &Request, &RequestSize, &Nonce, &SubSessionKey, pChannelBindings ); // // Set the receive nonce to be the nonce, as the code below // expects it to be valid. // ReceiveNonce = Nonce; } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to build AP request: 0x%x\n. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } if (OutputToken == NULL) { Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Return the AP request in the output buffer. // if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0) { if (OutputToken->cbBuffer < RequestSize) { ULONG ErrorData[3]; ErrorData[0] = RequestSize; ErrorData[1] = OutputToken->cbBuffer; ErrorData[2] = ClientProcess; D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n", OutputToken->cbBuffer,RequestSize, THIS_FILE, __LINE__ )); OutputToken->cbBuffer = RequestSize; Status = STATUS_BUFFER_TOO_SMALL; KerbReportNtstatus( KERBEVT_INSUFFICIENT_TOKEN_SIZE, Status, NULL, 0, ErrorData, 3 ); goto Cleanup; } RtlCopyMemory( OutputToken->pvBuffer, Request, RequestSize ); } else { OutputToken->pvBuffer = Request; if (OutputToken->pvBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } *ContextAttributes |= ISC_RET_ALLOCATED_MEMORY; // // Set this to NULL so it won't be freed by us on cleanup. // Request = NULL; } OutputToken->cbBuffer = RequestSize; } else { // // All we do here is allocate a nonce for use in the context. // Nonce = KerbAllocateNonce(); if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } } ////////////////////////////////////////////////////////////////////// // // If we haven't yet created a context, created one now. If we have, // update the context with the latest status. // ///////////////////////////////////////////////////////////////////// // // Allocate a client context, if we don't already have one // if (Context == NULL) { Status = KerbCreateClientContext( LogonSession, Credential, CredManCredentials, TicketCacheEntry, TargetName, Nonce, ContextFlags, ContextAttribs, &SubSessionKey, &Context, &ContextLifetime ); //CredManCredentials = NULL; } else { Status = KerbUpdateClientContext( Context, TicketCacheEntry, Nonce, ReceiveNonce, ContextFlags, ContextAttribs, &SubSessionKey, &ContextLifetime ); } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to create client context: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Keep track of network service session keys to detect whether network // logon session is for local network service // if (RtlEqualLuid(&LogonId, &NetworkServiceLuid)) { FILETIME CurTime = {0}; GetSystemTimeAsFileTime(&CurTime); // // use 2 times KerbGlobalSkewTime as ticket life time // KerbGetTime(*((TimeStamp*) &CurTime)) += 2 * KerbGetTime(KerbGlobalSkewTime); Status = KerbCreateSKeyEntry(&SubSessionKey, &CurTime); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to create session key entry: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } } // // Hold on to the ticket for later use // KerbWriteLockContexts(); if ((Context->TicketCacheEntry == NULL) && (TicketCacheEntry != NULL)) { KerbReferenceTicketCacheEntry(TicketCacheEntry); Context->TicketCacheEntry = TicketCacheEntry; } ClientProcess = Context->ClientProcess; KerbUnlockContexts(); // // update the context with the marshalled target info. // if( NT_SUCCESS(Status) && pbMarshalledTargetInfo ) { if( Context->pbMarshalledTargetInfo == NULL ) { Context->pbMarshalledTargetInfo = pbMarshalledTargetInfo; Context->cbMarshalledTargetInfo = cbMarshalledTargetInfo; pbMarshalledTargetInfo = NULL; } } // // Return the correct flags // *NewContextHandle = KerbGetContextHandle(Context); *ContextAttributes |= ContextFlags; KerbUtcTimeToLocalTime( ExpirationTime, &ContextLifetime ); // // If mutual authentication was requested, ask for a continuation // if (((ContextFlags & ( ISC_RET_USED_DCE_STYLE | ISC_RET_DATAGRAM | ISC_RET_MUTUAL_AUTH )) != 0) || GetServerTgt ) { Status = SEC_I_CONTINUE_NEEDED; } Cleanup: // Adjust for the new meaning of delegate/delegate-if-safe if they got munged somehow. if (ClientAskedForDelegateIfSafe && (*ContextAttributes & ISC_RET_DELEGATE)) { (*ContextAttributes) &= ~ISC_RET_DELEGATE; (*ContextAttributes) |= ISC_RET_DELEGATE_IF_SAFE; } else if ((ClientAskedForDelegate) && (*ContextAttributes & ISC_RET_DELEGATE_IF_SAFE)) { (*ContextAttributes) &= ~ISC_RET_DELEGATE_IF_SAFE; (*ContextAttributes) |= ISC_RET_DELEGATE; } if ( Status == STATUS_WRONG_PASSWORD ) { // // don't leak WRONG_PASSWORD to the caller. // Status = STATUS_LOGON_FAILURE; } if ( KerbEventTraceFlag ) // Event Trace: KerbInitSecurityContextEnd {Status, CredSource, DomainName, UserName, Target, (ExtErr), (Klininfo)} { PCWSTR TraceStrings[] = { L"CredMan", L"Supplied", L"Context", L"LogonSession", L"None" }; enum { TSTR_CREDMAN = 0, TSTR_SUPPLIED, TSTR_CONTEXT, TSTR_LOGONSESSION, TSTR_NONE }; UNICODE_STRING UNICODE_NONE = { 4*sizeof(WCHAR), 4*sizeof(WCHAR), L"NONE" }; UNICODE_STRING CredSource; PUNICODE_STRING trace_DomainName, trace_UserName, trace_target; trace_target = (Context != NULL) ? &Context->ServerPrincipalName : &UNICODE_NONE; if( Context != NULL && Context->CredManCredentials != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CREDMAN] ); trace_DomainName = &Context->CredManCredentials->SuppliedCredentials->DomainName; trace_UserName = &Context->CredManCredentials->SuppliedCredentials->UserName; } else if( Credential != NULL && Credential->SuppliedCredentials != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_SUPPLIED] ); trace_DomainName = &Credential->SuppliedCredentials->DomainName; trace_UserName = &Credential->SuppliedCredentials->UserName; } else if( Context != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CONTEXT] ); trace_DomainName = &Context->ClientRealm; trace_UserName = &Context->ClientName; } else if( LogonSession != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_LOGONSESSION] ); trace_DomainName = &LogonSession->PrimaryCredentials.DomainName; trace_UserName = &LogonSession->PrimaryCredentials.UserName; } else { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_NONE] ); trace_DomainName = &UNICODE_NONE; trace_UserName = &UNICODE_NONE; } INSERT_ULONG_INTO_MOF( Status, InitSCTraceInfo.MofData, 0 ); INSERT_UNICODE_STRING_INTO_MOF( CredSource, InitSCTraceInfo.MofData, 1 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_DomainName, InitSCTraceInfo.MofData, 3 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_UserName, InitSCTraceInfo.MofData, 5 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_target, InitSCTraceInfo.MofData, 7 ); InitSCTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 9*sizeof(MOF_FIELD); //Check for extended error if( pExtendedError != NULL ) { INSERT_ULONG_INTO_MOF( pExtendedError->status, InitSCTraceInfo.MofData, 9 ); INSERT_ULONG_INTO_MOF( pExtendedError->klininfo, InitSCTraceInfo.MofData, 10 ); InitSCTraceInfo.EventTrace.Size += 2*sizeof(MOF_FIELD); } // Set trace parameters InitSCTraceInfo.EventTrace.Guid = KerbInitSCGuid; InitSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; InitSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR; TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&InitSCTraceInfo ); } // // If we allocated a context, unlink it now // if (Context != NULL) { if (!NT_SUCCESS(Status)) { // // Only unlink the context if we just created it // if (ContextHandle == 0) { KerbReferenceContextByPointer( Context, TRUE ); KerbDereferenceContext(Context); } else { // // Set the context to an invalid state. // KerbWriteLockContexts(); Context->ContextState = InvalidState; KerbUnlockContexts(); } } else { KerbWriteLockContexts(); if (Status == STATUS_SUCCESS) { Context->ContextState = AuthenticatedState; } else if (!GetServerTgt) { Context->ContextState = ApRequestSentState; } else { Context->ContextState = TgtRequestSentState; // // mark the context as user2user // Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER; DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext (TGT in TGT reply) USER2USER-OUTBOUND set\n")); } KerbUnlockContexts(); } KerbDereferenceContext(Context); } if ((Status == STATUS_SUCCESS) || ((Status == SEC_I_CONTINUE_NEEDED) && ((ContextFlags & ISC_RET_DATAGRAM) != 0))) { NTSTATUS TempStatus; // // On real success we map the context to the callers address // space. // TempStatus = KerbMapContext( Context, MappedContext, ContextData ); D_DebugLog((DEB_TRACE, "SpInitLsaModeContext called KerbMapContext ContextAttributes %#x, %#x\n", Context->ContextAttributes, TempStatus)); if (!NT_SUCCESS(TempStatus)) { Status = TempStatus; } // // Update the skew time with a success // KerbUpdateSkewTime(FALSE); } if (NULL != CredManCredentials) { KerbDereferenceCredmanCred( CredManCredentials, &LogonSession->CredmanCredentials ); } if( pbMarshalledTargetInfo ) { LocalFree( pbMarshalledTargetInfo ); } if (TgtReply != NULL) { KerbFreeData(KERB_TGT_REPLY_PDU, TgtReply); } if (LogonSession != NULL) { KerbDereferenceLogonSession( LogonSession ); } if (Credential != NULL) { KerbDereferenceCredential( Credential ); } if (TicketCacheEntry != NULL) { KerbDereferenceTicketCacheEntry( TicketCacheEntry ); } if ( SpnCacheEntry != NULL ) { KerbDereferenceSpnCacheEntry( SpnCacheEntry); } KerbFreeKerbError( ErrorMessage ); if (NULL != pExtendedError) { KerbFree(pExtendedError); } if (ErrorData != NULL) { MIDL_user_free(ErrorData); } if (Request != NULL) { KerbFree(Request); } if (Reply != NULL) { KerbFree(Reply); } KerbFreeKey(&SubSessionKey); KerbFreeString( &TargetDomainName ); KerbFreeKdcName( &TargetInternalName ); D_DebugLog((DEB_TRACE_LEAKS,"SpInitLsaModeContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), *NewContextHandle, ClientProcess)); D_DebugLog((DEB_TRACE_API, "SpInitLsaModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } NTSTATUS NTAPI SpApplyControlToken( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc ControlToken ) { NTSTATUS Status = STATUS_NOT_SUPPORTED; D_DebugLog((DEB_TRACE_API,"SpApplyControlToken Called\n")); D_DebugLog((DEB_TRACE_API,"SpApplyControlToken returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } #ifndef WIN32_CHICAGO //we don't do server side stuff //+------------------------------------------------------------------------- // // Function: SpAcceptLsaModeContext // // Synopsis: Kerberos support routine for AcceptSecurityContext call. // This routine accepts an AP request message from a client // and verifies that it is a valid ticket. If mutual // authentication is desired an AP reply is generated to // send back to the client. // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpAcceptLsaModeContext( IN OPTIONAL LSA_SEC_HANDLE CredentialHandle, IN OPTIONAL LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc InputBuffers, IN ULONG ContextRequirements, IN ULONG TargetDataRep, OUT PLSA_SEC_HANDLE NewContextHandle, OUT PSecBufferDesc OutputBuffers, OUT PULONG ContextAttributes, OUT PTimeStamp ExpirationTime, OUT PBOOLEAN MappedContext, OUT PSecBuffer ContextData ) { PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDENTIAL Credential = NULL; NTSTATUS Status = STATUS_SUCCESS; NTSTATUS LastStatus = STATUS_SUCCESS; PKERB_AP_REQUEST Request = NULL; PUCHAR Reply = NULL; PSecBuffer InputToken = NULL; PSecBuffer OutputToken = NULL; ULONG Index; ULONG ReplySize; LUID LogonId; PKERB_ENCRYPTED_TICKET InternalTicket = NULL; PKERB_AUTHENTICATOR InternalAuthenticator = NULL; KERB_ENCRYPTION_KEY SessionKey; KERB_ENCRYPTION_KEY TicketKey; KERB_ENCRYPTION_KEY ServerKey; PKERB_CONTEXT Context = NULL; TimeStamp ContextLifetime; HANDLE TokenHandle = 0; ULONG ContextFlags = 0; ULONG ContextAttribs = KERB_CONTEXT_INBOUND; ULONG Nonce = 0; ULONG ReceiveNonce = 0; BOOLEAN UseSuppliedCreds = FALSE; ULONG_PTR LocalCredentialHandle = 0; PSID UserSid = NULL; KERBERR KerbErr = KDC_ERR_NONE; KERB_CONTEXT_STATE ContextState = InvalidState; UNICODE_STRING ServiceDomain = {0}; UNICODE_STRING ClientName = {0}; UNICODE_STRING ClientDomain = {0}; BOOLEAN IsTgtRequest = FALSE; ULONG ClientProcess = 0; PSEC_CHANNEL_BINDINGS pChannelBindings = NULL; KERB_ACCEPTSC_INFO AcceptSCTraceInfo; D_DebugLog((DEB_TRACE_API,"SpAcceptLsaModeContext 0x%x called\n",ContextHandle)); if( KerbEventTraceFlag ) // Event Trace: KerbAcceptSecurityContextStart {No Data} { AcceptSCTraceInfo.EventTrace.Guid = KerbAcceptSCGuid; AcceptSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; AcceptSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; AcceptSCTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER); TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&AcceptSCTraceInfo ); } // // Initialize the outputs. // *ContextAttributes = 0; *NewContextHandle = 0; *ExpirationTime = KerbGlobalHasNeverTime; *MappedContext = FALSE; ContextData->pvBuffer = NULL; ContextData->cbBuffer = 0; RtlZeroMemory( &SessionKey, sizeof(KERB_ENCRYPTION_KEY) ); TicketKey = SessionKey; ServerKey = TicketKey; if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; goto Cleanup; } // // First locate the Input token. // for (Index = 0; Index < InputBuffers->cBuffers ; Index++ ) { if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { InputToken = &InputBuffers->pBuffers[Index]; Status = LsaFunctions->MapBuffer(InputToken,InputToken); break; } } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to map Input token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Check to see if we were passed channel bindings // for( Index = 0; Index < InputBuffers->cBuffers; Index++ ) { if( BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_CHANNEL_BINDINGS ) { PVOID temp = NULL; Status = LsaFunctions->MapBuffer( &InputBuffers->pBuffers[Index], &InputBuffers->pBuffers[Index] ); if( !NT_SUCCESS(Status) ) { goto Cleanup; } pChannelBindings = (PSEC_CHANNEL_BINDINGS) InputBuffers->pBuffers[Index].pvBuffer; Status = KerbValidateChannelBindings(pChannelBindings, InputBuffers->pBuffers[Index].cbBuffer); if (!NT_SUCCESS(Status)) { pChannelBindings = NULL; goto Cleanup; } break; } } // // Locate the output token // for (Index = 0; Index < OutputBuffers->cBuffers ; Index++ ) { if (BUFFERTYPE(OutputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { OutputToken = &OutputBuffers->pBuffers[Index]; Status = LsaFunctions->MapBuffer(OutputToken,OutputToken); break; } } if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to map output token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // If the context handle is no NULL we are finalizing a context // if (ContextHandle != 0) { if (InputToken == NULL) { D_DebugLog((DEB_ERROR,"Trying to complete a context with no input token! %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // First reference the context. // Status = KerbReferenceContext( ContextHandle, FALSE, // don't unlink &Context ); if (Context == NULL) { D_DebugLog((DEB_ERROR,"Failed to reference context 0x%x. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__)); goto Cleanup; } // // Check the mode of the context to make sure we can finalize it. // KerbReadLockContexts(); ContextState = Context->ContextState; if (((ContextState != ApReplySentState) && (ContextState != TgtReplySentState) && (ContextState != ErrorMessageSentState)) || ((Context->ContextAttributes & KERB_CONTEXT_INBOUND) == 0)) { D_DebugLog((DEB_ERROR,"Invalid context state: %d. %ws, line %d\n", Context->ContextState, THIS_FILE, __LINE__)); Status = STATUS_INVALID_HANDLE; KerbUnlockContexts(); goto Cleanup; } if ((Context->ContextAttributes & KERB_CONTEXT_USED_SUPPLIED_CREDS) != 0) { UseSuppliedCreds = TRUE; } ContextFlags = Context->ContextFlags; LogonId = Context->LogonId; LocalCredentialHandle = Context->CredentialHandle; ClientProcess = Context->ClientProcess; KerbUnlockContexts(); } if (CredentialHandle != 0) { if ((LocalCredentialHandle != 0) && (CredentialHandle != LocalCredentialHandle)) { D_DebugLog((DEB_ERROR,"Different cred handle passsed to subsequent call to AcceptSecurityContext: 0x%x instead of 0x%x. %ws, line %d\n", CredentialHandle, LocalCredentialHandle, THIS_FILE, __LINE__ )); Status = STATUS_WRONG_CREDENTIAL_HANDLE; goto Cleanup; } } else { CredentialHandle = LocalCredentialHandle; } // // If we are finalizing a context, do that here // if (ContextState == ApReplySentState) { // // If we are doing datgram, then the finalize is actually an AP request // if ((ContextFlags & ISC_RET_DATAGRAM) != 0) { // // Get the logon session // LogonSession = KerbReferenceLogonSession( &LogonId, FALSE ); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } // // If we are using supplied creds, get the credentials. Copy // out the domain name so we can use it to verify the PAC. // Status = KerbReferenceCredential( LocalCredentialHandle, KERB_CRED_INBOUND, FALSE, // don't dereference &Credential ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (UseSuppliedCreds) { KerbReadLockLogonSessions(LogonSession); Status = KerbDuplicateString( &ServiceDomain, &Credential->SuppliedCredentials->DomainName ); KerbUnlockLogonSessions(LogonSession); } else { KerbReadLockLogonSessions(LogonSession); Status = KerbDuplicateString( &ServiceDomain, &LogonSession->PrimaryCredentials.DomainName ); KerbUnlockLogonSessions(LogonSession); } if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Verify the AP request // Status = KerbVerifyApRequest( Context, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, LogonSession, Credential, UseSuppliedCreds, ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) == 0), &Request, &InternalTicket, &InternalAuthenticator, &SessionKey, &TicketKey, &ServerKey, &ContextFlags, &ContextAttribs, &KerbErr, pChannelBindings ); // // We don't allow user-to-user recovery with datagram // if ((Status == STATUS_REPARSE_OBJECT) // this is a TGT request || ((Status == SEC_E_NO_CREDENTIALS) && (KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED))) { DebugLog((DEB_ERROR, "Won't allow user2user with Datagram. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_INVALID_TOKEN; KerbErr = KRB_AP_ERR_MSG_TYPE; goto ErrorReturn; } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to verify AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); // // Let the skew tracker know about the failure // if (KerbErr == KRB_AP_ERR_SKEW) { KerbUpdateSkewTime(TRUE); } // // Go to ErrorReturn so we can return an error message // goto ErrorReturn; } // // Turn on the flag if it was called for // if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0) { ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY; } // // Record the success // KerbUpdateSkewTime(FALSE); // // Check if the caller wants to allow null sessions // if (((ContextFlags & ISC_RET_NULL_SESSION) != 0) && ((ContextRequirements & ASC_REQ_ALLOW_NULL_SESSION) == 0)) { D_DebugLog((DEB_ERROR,"Received null session but not allowed. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_LOGON_FAILURE; goto Cleanup; } // // Save away the ReceiveNonce if it was provided // if ((InternalAuthenticator!= NULL) && ((InternalAuthenticator->bit_mask & KERB_AUTHENTICATOR_sequence_number_present) != 0)) { // // If the number is unsigned, convert it as unsigned. Otherwise // convert as signed. // if (ASN1intxisuint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number)) { ReceiveNonce = ASN1intx2uint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number); } else { ReceiveNonce = (ULONG) ASN1intx2int32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number); } } else { ReceiveNonce = 0; } // // Authentication succeeded, so build a token // Status = KerbCreateTokenFromTicket( InternalTicket, InternalAuthenticator, ContextFlags, &ServerKey, &ServiceDomain, &SessionKey, &LogonId, &UserSid, &TokenHandle, &ClientName, &ClientDomain ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create token from ticket: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } Status = KerbUpdateServerContext( Context, InternalTicket, Request, &SessionKey, &LogonId, &UserSid, ContextFlags, ContextAttribs, Nonce, ReceiveNonce, &TokenHandle, &ClientName, &ClientDomain, &ContextLifetime ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } } else { // // Now unpack the AP reply // Status = KerbVerifyApReply( Context, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, &Nonce ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to verify AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // We're done - we finalized. // Status = STATUS_SUCCESS; if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } } KerbReadLockContexts(); *ContextAttributes = KerbMapContextFlags(Context->ContextFlags); KerbUtcTimeToLocalTime( ExpirationTime, &Context->Lifetime ); if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } *NewContextHandle = ContextHandle; KerbUnlockContexts(); goto Cleanup; // datagram and finalized contexts exit here. } // // Get the associated credential // Status = KerbReferenceCredential( CredentialHandle, KERB_CRED_INBOUND, FALSE, &Credential ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_WARN,"Failed to locate credential 0x%x\n",Status)); goto Cleanup; } // // Get the logon id from the credentials so we can locate the // logon session. // LogonId = Credential->LogonId; // // Get the logon session // LogonSession = KerbReferenceLogonSession( &LogonId, FALSE ); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } KerbReadLockLogonSessions(LogonSession); if ((Credential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) != 0) { D_DebugLog((DEB_WARN, "Trying to use a local logon session with Kerberos\n")); KerbUnlockLogonSessions(LogonSession); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; } if (Credential->SuppliedCredentials != NULL) { UseSuppliedCreds = TRUE; ContextAttribs |= KERB_CONTEXT_USED_SUPPLIED_CREDS; Status = KerbDuplicateString( &ServiceDomain, &Credential->SuppliedCredentials->DomainName ); } else { Status = KerbDuplicateString( &ServiceDomain, &LogonSession->PrimaryCredentials.DomainName ); } #if DBG D_DebugLog((DEB_TRACE_CTXT, "SpAcceptLsaModeContext: Accepting context for %wZ\\%wZ\n", &LogonSession->PrimaryCredentials.DomainName, &LogonSession->PrimaryCredentials.UserName )); #endif KerbUnlockLogonSessions(LogonSession); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // If datagram was requested, note it now. There is no input // buffer on the first call using datagram. // if ((ContextRequirements & ASC_REQ_DATAGRAM) != 0) { D_DebugLog((DEB_TRACE_CTXT2, "Accepting datagram first call\n")); // // Verify that there is no input token or it is small. RPC passes // in two bytes for the DEC package that we can ignore. // if ((InputToken != NULL) && (InputToken->cbBuffer > 4)) { D_DebugLog((DEB_WARN, "Non null input token passed to AcceptSecurityContext for datagram\n")); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } ContextFlags |= ISC_RET_DATAGRAM; ReceiveNonce = 0; // // Build a server context // Status = KerbCreateEmptyContext( Credential, ContextFlags, ContextAttribs, &LogonId, &Context, &ContextLifetime ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to create server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } } else { D_DebugLog((DEB_TRACE_CTXT2,"Accepting connection first call\n")); // // Unmarshall the AP request // if ((InputToken == NULL) || (InputToken->cbBuffer == 0)) { D_DebugLog((DEB_WARN, "Null input token passed to AcceptSecurityContext for datagram\n")); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Verify the AP request // Status = KerbVerifyApRequest( Context, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, LogonSession, Credential, UseSuppliedCreds, ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) == 0), &Request, &InternalTicket, &InternalAuthenticator, &SessionKey, &TicketKey, &ServerKey, &ContextFlags, &ContextAttribs, &KerbErr, pChannelBindings ); if (!NT_SUCCESS(Status)) { // // Track time skew errors // if ((KerbErr == KRB_AP_ERR_SKEW) || (KerbErr == KRB_AP_ERR_TKT_NYV)) { KerbUpdateSkewTime(TRUE); } DebugLog((DEB_ERROR,"Failed to verify AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto ErrorReturn; } ContextFlags |= ISC_RET_CONNECTION; // // Check if this was a user-to-user tgt request. If so, then // there was no AP request // if (Status == STATUS_REPARSE_OBJECT) { IsTgtRequest = TRUE; Status = KerbHandleTgtRequest( LogonSession, Credential, UseSuppliedCreds, (PUCHAR) InputToken->pvBuffer, InputToken->cbBuffer, ContextRequirements, OutputToken, &LogonId, ContextAttributes, &Context, &ContextLifetime, &KerbErr ); if (!NT_SUCCESS(Status)) { goto ErrorReturn; } ContextFlags |= ISC_RET_USE_SESSION_KEY; D_DebugLog((DEB_TRACE_U2U, "SpAcceptLsaModeContext handled TGT request and use_session_key set, ContextAttributes %#x\n", Context->ContextAttributes)); } else // not a user-to-user request { // // Track successful time if this wasn't an error recovery // if (ContextState != ErrorMessageSentState) { KerbUpdateSkewTime(FALSE); } if ((InternalAuthenticator != NULL) && (InternalAuthenticator->bit_mask & KERB_AUTHENTICATOR_sequence_number_present)) { // // If the number is unsigned, convert it as unsigned. Otherwise // convert as signed. // if (ASN1intxisuint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number)) { ReceiveNonce = ASN1intx2uint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number); } else { ReceiveNonce = (ULONG) ASN1intx2int32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number); } } else { ReceiveNonce = 0; } // // Initialize the opposite direction nonce to the same value // Nonce = ReceiveNonce; // // Turn on the flag if it was called for // if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0) { ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY; } // // Check if the caller wants to allow null sessions // if (((ContextFlags & ISC_RET_NULL_SESSION) != 0) && ((ContextRequirements & ASC_REQ_ALLOW_NULL_SESSION) == 0)) { D_DebugLog((DEB_ERROR,"Received null session but not allowed. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_LOGON_FAILURE; goto Cleanup; } // // Authentication succeeded, so build a token // D_DebugLog((DEB_TRACE_CTXT2, "AcceptLsaModeContext: Creating token from ticket\n")); Status = KerbCreateTokenFromTicket( InternalTicket, InternalAuthenticator, ContextFlags, &ServerKey, &ServiceDomain, &SessionKey, &LogonId, &UserSid, &TokenHandle, &ClientName, &ClientDomain ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create token from ticket: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } // // If the caller wants mutual authentication, build an AP reply // if (((ContextFlags & ISC_RET_MUTUAL_AUTH) != 0) || ((ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)) { // // We require an output token in this case. // if (OutputToken == NULL) { Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Build the reply message // D_DebugLog((DEB_TRACE_CTXT2,"SpAcceptLsaModeContext: Building AP reply\n")); Status = KerbBuildApReply( InternalAuthenticator, Request, ContextFlags, ContextAttribs, &TicketKey, &SessionKey, &Nonce, &Reply, &ReplySize ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to build AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } if (OutputToken == NULL) { D_DebugLog((DEB_ERROR,"Output token missing. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Return the AP reply in the output buffer. // if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0) { if (OutputToken->cbBuffer < ReplySize) { ULONG ErrorData[3]; ErrorData[0] = ReplySize; ErrorData[1] = OutputToken->cbBuffer; ErrorData[2] = ClientProcess; D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n", OutputToken->cbBuffer,ReplySize, THIS_FILE, __LINE__ )); OutputToken->cbBuffer = ReplySize; Status = STATUS_BUFFER_TOO_SMALL; KerbReportNtstatus( KERBEVT_INSUFFICIENT_TOKEN_SIZE, Status, NULL, 0, ErrorData, 3 ); goto Cleanup; } RtlCopyMemory( OutputToken->pvBuffer, Reply, ReplySize ); } else { OutputToken->pvBuffer = Reply; Reply = NULL; *ContextAttributes |= ASC_RET_ALLOCATED_MEMORY; } OutputToken->cbBuffer = ReplySize; } else { if (OutputToken != NULL) { OutputToken->cbBuffer = 0; } } // // Build a server context if we don't already have one. // // // Turn on the flag if it was called for // if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0) { ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY; } if (Context == NULL) { Status = KerbCreateServerContext( LogonSession, Credential, InternalTicket, Request, &SessionKey, &LogonId, &UserSid, ContextFlags, ContextAttribs, Nonce, ReceiveNonce, &TokenHandle, &ClientName, &ClientDomain, &Context, &ContextLifetime ); } else { // // Update an existing context // Status = KerbUpdateServerContext( Context, InternalTicket, Request, &SessionKey, &LogonId, &UserSid, ContextFlags, ContextAttribs, Nonce, ReceiveNonce, &TokenHandle, &ClientName, &ClientDomain, &ContextLifetime ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create or update server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } } // not a TGT request } // not datagram *NewContextHandle = KerbGetContextHandle(Context); KerbUtcTimeToLocalTime( ExpirationTime, &ContextLifetime ); #if DBG KerbReadLockContexts(); ClientProcess = Context->ClientProcess; KerbUnlockContexts(); #endif // DBG *ContextAttributes |= KerbMapContextFlags(ContextFlags); if (IsTgtRequest || (((ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) || ((ContextFlags & ISC_RET_DATAGRAM) != 0))) { Status = SEC_I_CONTINUE_NEEDED; } goto Cleanup; ErrorReturn: // // Generate a KERB_ERROR message if necessary, meaning that there was // an authentication failure. // if ((OutputToken != NULL ) && (!KERB_SUCCESS(KerbErr) || ((ContextRequirements & ASC_REQ_EXTENDED_ERROR) != 0) || ((ContextFlags & ISC_RET_EXTENDED_ERROR) != 0))) { NTSTATUS TempStatus; PBYTE ErrorMessage = NULL; ULONG ErrorMessageSize; PBYTE ErrorData = NULL; ULONG ErrorDataSize = 0; // // Check whether it is an error we want the client to retry on. // For datagram, we can't handle this. // if (ContextRequirements & ASC_REQ_DATAGRAM) { goto Cleanup; } if (!(((ContextRequirements & ASC_REQ_EXTENDED_ERROR) != 0) || (KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED) || (KerbErr == KRB_AP_ERR_SKEW) || (KerbErr == KRB_AP_ERR_TKT_NYV) || (KerbErr == KRB_AP_ERR_TKT_EXPIRED) || (KerbErr == KRB_AP_ERR_MODIFIED) )) { goto Cleanup; } // // Create an empty context that can be used later // if (Context == NULL) { TempStatus = KerbCreateEmptyContext( Credential, ContextFlags, ContextAttribs, &LogonId, &Context, &ContextLifetime ); if (!NT_SUCCESS(TempStatus)) { goto Cleanup; } } // // if the error code is one with error data, build the error data // switch ((UINT_PTR) KerbErr) { case (UINT_PTR) KRB_AP_ERR_USER_TO_USER_REQUIRED: NTSTATUS TempStatus; TempStatus = KerbBuildTgtErrorReply( LogonSession, Credential, UseSuppliedCreds, Context, &ErrorDataSize, &ErrorData ); D_DebugLog((DEB_TRACE_U2U, "SpAcceptLsaModeContext called KerbBuildTgtErrorReply %#x\n", TempStatus)); if (TempStatus == STATUS_USER2USER_REQUIRED) { KerbErr = KRB_AP_ERR_NO_TGT; } else if (!NT_SUCCESS(TempStatus)) { D_DebugLog((DEB_ERROR,"Failed to build tgt error reply: 0x%x. Ignoring. %ws, line %d\n",TempStatus, THIS_FILE, __LINE__)); } break; case (UINT_PTR) KDC_ERR_NONE: // // In this case, return the KRB_ERR_GENERIC and the NTSTATUS code // in the error data // KerbErr = KRB_ERR_GENERIC; ErrorData = (PUCHAR) &Status; ErrorDataSize = sizeof(ULONG); break; } TempStatus = KerbBuildGssErrorMessage( KerbErr, ErrorData, ErrorDataSize, Context, &ErrorMessageSize, &ErrorMessage ); if ((ErrorData != NULL) && (ErrorData != (PUCHAR) &Status)) { MIDL_user_free(ErrorData); } if (!NT_SUCCESS(TempStatus)) { goto Cleanup; } *ContextAttributes |= ASC_RET_EXTENDED_ERROR; if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0) { if (OutputToken->cbBuffer < ErrorMessageSize) { D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n", OutputToken->cbBuffer,ErrorMessageSize, THIS_FILE, __LINE__ )); MIDL_user_free(ErrorMessage); goto Cleanup; } else { DsysAssert(OutputToken->pvBuffer != NULL); RtlCopyMemory( OutputToken->pvBuffer, ErrorMessage, ErrorMessageSize ); OutputToken->cbBuffer = ErrorMessageSize; MIDL_user_free(ErrorMessage); } } else { DsysAssert(OutputToken->pvBuffer == NULL); OutputToken->cbBuffer = ErrorMessageSize; OutputToken->pvBuffer = ErrorMessage; ErrorMessage = NULL; *ContextAttributes |= ASC_RET_ALLOCATED_MEMORY; } *ContextAttributes |= ASC_RET_EXTENDED_ERROR; *NewContextHandle = KerbGetContextHandle(Context); KerbUtcTimeToLocalTime( ExpirationTime, &ContextLifetime ); *ContextAttributes |= KerbMapContextFlags(ContextFlags); LastStatus = Status; // // now it is time to mark the context as user2user // if (KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED) { DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext (TGT in error reply) USER2USER-INBOUND set\n")); KerbWriteLockContexts() Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER; KerbUnlockContexts(); } Status = SEC_I_CONTINUE_NEEDED; } Cleanup: if( KerbEventTraceFlag ) // Event Trace: KerbAcceptSecurityContextEnd {Status, CredSource, DomainName, UserName, Target, (ExtError), (klininfo)} { PCWSTR TraceStrings[] = { L"CredMan", L"Supplied", L"Context", L"LogonSession", L"None" }; enum { TSTR_CREDMAN = 0, TSTR_SUPPLIED, TSTR_CONTEXT, TSTR_LOGONSESSION, TSTR_NONE }; UNICODE_STRING UNICODE_NONE = { 4*sizeof(WCHAR), 4*sizeof(WCHAR), L"NONE" }; UNICODE_STRING CredSource; PUNICODE_STRING trace_DomainName, trace_UserName, trace_target; trace_target = (Context!=NULL) ? &Context->ServerPrincipalName : &UNICODE_NONE; if( Context != NULL && Context->CredManCredentials != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CREDMAN] ); trace_DomainName = &Context->CredManCredentials->SuppliedCredentials->DomainName; trace_UserName = &Context->CredManCredentials->SuppliedCredentials->UserName; } else if( Credential != NULL && Credential->SuppliedCredentials != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_SUPPLIED] ); trace_DomainName = &Credential->SuppliedCredentials->DomainName; trace_UserName = &Credential->SuppliedCredentials->UserName; } else if( Context != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CONTEXT] ); trace_DomainName = &Context->ClientRealm; trace_UserName = &Context->ClientName; } else if( LogonSession != NULL ) { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_LOGONSESSION] ); trace_DomainName = &LogonSession->PrimaryCredentials.DomainName; trace_UserName = &LogonSession->PrimaryCredentials.UserName; } else { RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_NONE] ); trace_DomainName = &UNICODE_NONE; trace_UserName = &UNICODE_NONE; } INSERT_ULONG_INTO_MOF( Status, AcceptSCTraceInfo.MofData, 0 ); INSERT_UNICODE_STRING_INTO_MOF( CredSource, AcceptSCTraceInfo.MofData, 1 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_DomainName, AcceptSCTraceInfo.MofData, 3 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_UserName, AcceptSCTraceInfo.MofData, 5 ); INSERT_UNICODE_STRING_INTO_MOF( *trace_target, AcceptSCTraceInfo.MofData, 7 ); AcceptSCTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 9*sizeof(MOF_FIELD); // Set trace parameters AcceptSCTraceInfo.EventTrace.Guid = KerbAcceptSCGuid; AcceptSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; AcceptSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR; TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&AcceptSCTraceInfo ); } // // First, handle auditing // if (Status == STATUS_SUCCESS) { // // Don't audit if we didn't create a token. // if (Context->UserSid != NULL) { UNICODE_STRING WorkstationName = {0}; // // note that UserSid will only be non-Null if the ClientName, ClientRealm were updated in the context. // and the context is currently referenced, so the fields won't vanish under us. // if ((InternalTicket != NULL) && ((InternalTicket->bit_mask & KERB_ENCRYPTED_TICKET_client_addresses_present) != 0)) { (VOID) KerbGetClientNetbiosAddress( &WorkstationName, InternalTicket->KERB_ENCRYPTED_TICKET_client_addresses ); // // The following generates a successful audit event. // 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. // (void) KerbAuditLogon( Status, Status, InternalTicket, Context->UserSid, &WorkstationName, &LogonId ); KerbFreeString(&WorkstationName); } } } else if (!NT_SUCCESS(Status) || (LastStatus != STATUS_SUCCESS)) { if (Context != NULL) { LsaFunctions->AuditLogon( STATUS_LOGON_FAILURE, (LastStatus != STATUS_SUCCESS) ? LastStatus : Status, &Context->ClientName, &Context->ClientRealm, NULL, // no workstation NULL, // no sid instead of a bogus one Network, &KerberosSource, &LogonId ); } else { UNICODE_STRING EmptyString = NULL_UNICODE_STRING; LsaFunctions->AuditLogon( (LastStatus != STATUS_SUCCESS) ? LastStatus : Status, STATUS_SUCCESS, &EmptyString, &EmptyString, NULL, // no workstation NULL, Network, &KerberosSource, &LogonId ); } } if (Context != NULL) { if (!NT_SUCCESS(Status)) { // // Only unlink the context if we just created it. // if (ContextHandle == 0) { KerbReferenceContextByPointer( Context, TRUE ); KerbDereferenceContext(Context); } else { // // Set the context to an invalid state. // KerbWriteLockContexts(); Context->ContextState = InvalidState; KerbUnlockContexts(); } } else { KerbWriteLockContexts(); if (Status == STATUS_SUCCESS) { Context->ContextState = AuthenticatedState; } else { if ((*ContextAttributes & ASC_RET_EXTENDED_ERROR) != 0) { Context->ContextState = ErrorMessageSentState; } else if (!IsTgtRequest) { Context->ContextState = ApReplySentState; } else { // // else the HandleTgtRequest set the state // DsysAssert(Context->ContextState == TgtReplySentState); } } KerbUnlockContexts(); } KerbDereferenceContext(Context); } if (Status == STATUS_SUCCESS) { // // On real success we map the context to the callers address // space. // Status = KerbMapContext( Context, MappedContext, ContextData ); DebugLog((DEB_TRACE, "SpAcceptLsaModeContext called KerbMapContext ContextAttributes %#x, %#x\n", Context->ContextAttributes, Status)); } if (LogonSession != NULL) { KerbDereferenceLogonSession( LogonSession ); } if (Credential != NULL) { KerbDereferenceCredential( Credential ); } if (InternalTicket != NULL) { KerbFreeTicket( InternalTicket ); } if (InternalAuthenticator != NULL) { KerbFreeAuthenticator(InternalAuthenticator); } if (Request != NULL) { KerbFreeApRequest(Request); } if (Reply != NULL) { KerbFree(Reply); } KerbFreeKey(&SessionKey); KerbFreeKey(&ServerKey); if (UserSid != NULL) { KerbFree(UserSid); } // // If there was a problem with the context or AP reply, the TokenHandle // will not be reset to NULL. If it is NULL, close it so we don't leak // if ( TokenHandle != NULL ) { D_DebugLog(( DEB_TRACE, "Closing token handle because context creation failed (%x)\n", Status )); NtClose( TokenHandle ); } // // Update performance counter // #ifndef WIN32_CHICAGO if (ContextHandle == 0) { I_SamIIncrementPerformanceCounter(KerbServerContextCounter); } #endif // WIN32_CHICAGO KerbFreeKey(&TicketKey); KerbFreeString(&ServiceDomain); KerbFreeString(&ClientDomain); KerbFreeString(&ClientName); D_DebugLog((DEB_TRACE_LEAKS,"SpAcceptLsaModeContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), *NewContextHandle, ClientProcess)); D_DebugLog((DEB_TRACE_API, "SpAcceptLsaModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } #endif WIN32_CHICAGO //we don't do server side stuff