windows-nt/Source/XPSP1/NT/ds/security/protocols/kerberos/client2/tktlogon.cxx
2020-09-26 16:20:57 +08:00

360 lines
9.2 KiB
C++

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1999
//
// File: tktlogon.cxx
//
// Contents: Code for proxy logon for the Kerberos package
//
//
// History: 15-March Created MikeSw
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__);
#endif
//+-------------------------------------------------------------------------
//
// Function: KerbCreateTicketLogonSession
//
// Synopsis: Does all the work for a ticket logon, removing it from
// LsaApLogonUserEx2
//
// Effects:
//
// Arguments: WorkstationTicket - ticket to workstation. The ticket
// cache entry is pretty much empty except for
// the ticket, and is not linked
// ForwardedTgt - receives pointers into the Protocol
// SubmitBuffer to the forwarded TGT.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCreateTicketLogonSession(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
IN SECURITY_LOGON_TYPE LogonType,
OUT PKERB_LOGON_SESSION * NewLogonSession,
OUT PLUID LogonId,
OUT PKERB_TICKET_CACHE_ENTRY * WorkstationTicket,
OUT PKERB_MESSAGE_BUFFER ForwardedTgt
)
{
NTSTATUS Status = STATUS_SUCCESS;
PKERB_TICKET_LOGON LogonInfo = NULL;
PKERB_LOGON_SESSION LogonSession = NULL;
KERB_KDC_REPLY KdcReply = {0};
ULONG_PTR Offset;
UNICODE_STRING EmptyString = {0};
PKERB_TICKET Ticket = NULL;
KERBERR KerbErr;
*WorkstationTicket = NULL;
ForwardedTgt->Buffer = NULL;
ForwardedTgt->BufferSize = 0;
//
// Pull the interesting information out of the submit buffer
//
if (SubmitBufferSize < sizeof(KERB_TICKET_LOGON))
{
DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
LogonInfo = (PKERB_TICKET_LOGON) ProtocolSubmitBuffer;
DsysAssert((LogonInfo->MessageType == KerbTicketLogon) || (LogonInfo->MessageType == KerbTicketUnlockLogon));
//
// Relocate any pointers to be relative to 'LogonInfo'
//
Offset = ((PUCHAR) LogonInfo->ServiceTicket) - ((PUCHAR)ClientBufferBase);
if ( (Offset >= SubmitBufferSize) ||
(Offset + LogonInfo->ServiceTicketLength > SubmitBufferSize))
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
LogonInfo->ServiceTicket = (PUCHAR) ProtocolSubmitBuffer + Offset;
if (LogonInfo->TicketGrantingTicketLength != 0)
{
if (LogonInfo->TicketGrantingTicket == NULL)
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
Offset = ((PUCHAR) LogonInfo->TicketGrantingTicket) - ((PUCHAR)ClientBufferBase);
if ( (Offset >= SubmitBufferSize) ||
(Offset + LogonInfo->TicketGrantingTicketLength > SubmitBufferSize))
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
LogonInfo->TicketGrantingTicket = (PUCHAR) ProtocolSubmitBuffer + Offset;
ForwardedTgt->BufferSize = LogonInfo->TicketGrantingTicketLength;
ForwardedTgt->Buffer = LogonInfo->TicketGrantingTicket;
}
//
// Allocate a locally unique ID for this logon session. We will
// create it in the LSA just before returning.
//
Status = NtAllocateLocallyUniqueId( LogonId );
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Build a logon session to hold all this information
//
Status = KerbCreateLogonSession(
LogonId,
&EmptyString,
&EmptyString,
NULL, // no password
NULL, // no old password
0, // no password options
LogonType,
&LogonSession
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Unpack the supplied service ticket
//
KerbErr = KerbUnpackData(
LogonInfo->ServiceTicket,
LogonInfo->ServiceTicketLength,
KERB_TICKET_PDU,
(PVOID *) &Ticket
);
if (!KERB_SUCCESS(KerbErr))
{
DebugLog((DEB_ERROR,"Failed to unpack service ticket in ticket logon\n"));
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
//
// Build a ticket structure from the service ticket.
//
KdcReply.ticket = *Ticket;
Status = KerbCacheTicket(
NULL, // no ticket cache
&KdcReply,
NULL, // no kdc reply
NULL, // no target name
NULL, // no target realm
0, // no flags
FALSE, // don't link entry
WorkstationTicket
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Return the new logon session.
//
*NewLogonSession = LogonSession;
LogonSession = NULL;
Cleanup:
if (!NT_SUCCESS(Status))
{
if (LogonSession != NULL)
{
KerbReferenceLogonSessionByPointer(LogonSession, TRUE);
KerbDereferenceLogonSession(LogonSession);
}
}
if (Ticket != NULL)
{
KerbFreeData(KERB_TICKET_PDU, Ticket);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbExtractForwardedTgt
//
// Synopsis: Extracts a forwarded TGT from its encoded representation
// and sticks it in the logon session ticket cache. This
// also updates the user name & domain name in the
// logon session, if they aren't present.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbExtractForwardedTgt(
IN PKERB_LOGON_SESSION LogonSession,
IN PKERB_MESSAGE_BUFFER ForwardedTgt,
IN PKERB_ENCRYPTED_TICKET WorkstationTicket
)
{
PKERB_CRED KerbCred = NULL;
PKERB_ENCRYPTED_CRED EncryptedCred = NULL;
KERBERR KerbErr;
NTSTATUS Status = STATUS_SUCCESS;
D_DebugLog((DEB_TRACE, "Extracting a forwarded TGT\n"));
KerbErr = KerbUnpackKerbCred(
ForwardedTgt->Buffer,
ForwardedTgt->BufferSize,
&KerbCred
);
if (!KERB_SUCCESS(KerbErr))
{
DebugLog((DEB_WARN, "Failed to unpack kerb cred for forwaded tgt\n"));
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
//
// Now decrypt the encrypted part of the KerbCred.
//
KerbErr = KerbDecryptDataEx(
&KerbCred->encrypted_part,
&WorkstationTicket->key,
KERB_CRED_SALT,
(PULONG) &KerbCred->encrypted_part.cipher_text.length,
KerbCred->encrypted_part.cipher_text.value
);
if (!KERB_SUCCESS(KerbErr))
{
DebugLog((DEB_ERROR,"Failed to decrypt KERB_CRED: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
if (KerbErr == KRB_ERR_GENERIC)
{
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
else
{
Status = STATUS_LOGON_FAILURE;
//
// MIT clients don't encrypt the encrypted part, so drop through
//
}
}
//
// Now unpack the encrypted part.
//
KerbErr = KerbUnpackEncryptedCred(
KerbCred->encrypted_part.cipher_text.value,
KerbCred->encrypted_part.cipher_text.length,
&EncryptedCred
);
if (!KERB_SUCCESS(KerbErr))
{
//
// Use the old status if it is available.
//
if (NT_SUCCESS(Status))
{
Status = KerbMapKerbError(KerbErr);
}
DebugLog((DEB_WARN, "Failed to unpack encrypted cred\n"));
goto Cleanup;
}
//
// Now build a logon session.
//
Status = KerbCreateLogonSessionFromKerbCred(
NULL,
WorkstationTicket,
KerbCred,
EncryptedCred,
&LogonSession
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to create logon session from kerb cred: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
Cleanup:
if (EncryptedCred != NULL)
{
KerbFreeEncryptedCred(EncryptedCred);
}
if (KerbCred != NULL)
{
KerbFreeKerbCred(KerbCred);
}
return(Status);
}