|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
tktlogon.cxx
Abstract:
ticket logon
Author:
Larry Zhu (LZhu) January 1, 2002 Created
Environment:
User Mode
Revision History:
--*/
#include "precomp.hxx"
#pragma hdrstop
#include "tktlogon.hxx"
VOID Usage( IN PCSTR pszApp ) { DebugPrintf(SSPI_ERROR, "\n\nUsage: %s <LogonId.HighPart> <LogonId.LowPart> " "<serviceprincipal(host/machine@domain)>\n", pszApp); exit(-1); }
NTSTATUS GetTGT( IN HANDLE hLsa, IN ULONG PackageId, IN LUID* pLogonId, OUT KERB_EXTERNAL_TICKET** ppCacheEntry ) { TNtStatus Status;
NTSTATUS SubStatus;
KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
ULONG ResponseSize;
CacheRequest.MessageType = KerbRetrieveTicketMessage; CacheRequest.LogonId = *pLogonId;
DebugPrintf(SSPI_LOG, "GetTgt PackageId %#x, LogonId %#x:%#x\n", PackageId, pLogonId->HighPart, pLogonId->LowPart);
Status DBGCHK = LsaCallAuthenticationPackage( hLsa, PackageId, &CacheRequest, sizeof(CacheRequest), (VOID **) ppCacheEntry, &ResponseSize, &SubStatus ); if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; }
return Status; }
NTSTATUS GetServiceTicket( IN HANDLE hLsa, IN ULONG ulPackageId, IN UNICODE_STRING* pServicePrincipal, IN LUID* pLogonId, IN BOOLEAN useCache, OUT KERB_RETRIEVE_TKT_RESPONSE** ppCacheResponse ) { TNtStatus Status; NTSTATUS SubStatus;
VOID* pResponse; ULONG ResponseSize;
KERB_RETRIEVE_TKT_REQUEST* pCacheRequest = NULL; ULONG cbCacheRequest = 0; UNICODE_STRING Target = {0};
HANDLE hLogon = hLsa; ULONG PackageId = ulPackageId;
cbCacheRequest = pServicePrincipal->Length + ROUND_UP_COUNT(sizeof(KERB_RETRIEVE_TKT_REQUEST), sizeof(ULONG_PTR)); pCacheRequest = (KERB_RETRIEVE_TKT_REQUEST*) new CHAR[cbCacheRequest];
Status DBGCHK = pCacheRequest ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (NT_SUCCESS(Status)) { RtlZeroMemory(pCacheRequest, cbCacheRequest);
pCacheRequest->MessageType = KerbRetrieveEncodedTicketMessage;
Target.Buffer = (PWSTR) (pCacheRequest + 1); Target.Length = pServicePrincipal->Length; Target.MaximumLength = pServicePrincipal->MaximumLength;
pCacheRequest->LogonId = *pLogonId;
RtlCopyMemory( Target.Buffer, pServicePrincipal->Buffer, pServicePrincipal->Length );
pCacheRequest->TargetName = Target;
if (!useCache) { pCacheRequest->CacheOptions = KERB_RETRIEVE_TICKET_DONT_USE_CACHE; } else { pCacheRequest->CacheOptions = 0; }
DebugPrintf(SSPI_LOG, "ServicePrincipal: %wZ\n", &Target);
Status DBGCHK = LsaCallAuthenticationPackage( hLsa, ulPackageId, pCacheRequest, pServicePrincipal->Length + sizeof(KERB_RETRIEVE_TKT_REQUEST), &pResponse, &ResponseSize, &SubStatus ); }
if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; }
if (NT_SUCCESS(Status)) { *ppCacheResponse = (KERB_RETRIEVE_TKT_RESPONSE*) pResponse; pResponse = NULL; }
if (pCacheRequest) { delete [] pCacheRequest; }
if (pResponse) { LsaFreeReturnBuffer(pResponse); }
return NT_SUCCESS(Status) ? SubStatus : Status; }
NTSTATUS BuildAllocTicketLogonInfo( IN UCHAR* pTGTData, IN ULONG cbTGTDataLength, IN UCHAR* pTicketData, IN ULONG cbTicketDataLength, IN ULONG DataOffset, IN OUT ULONG *pLogonInfoSize, OUT KERB_TICKET_LOGON** ppLogonInfo ) { TNtStatus Status = STATUS_SUCCESS;
UCHAR* pWhere = NULL; KERB_TICKET_LOGON* pLogonInfo = NULL; ULONG cbLogonInfoSize = *pLogonInfoSize;
//assemble LogonInfo
cbLogonInfoSize += cbTicketDataLength;
if (cbTGTDataLength && pTGTData) { cbLogonInfoSize += cbTGTDataLength; }
pLogonInfo = (KERB_TICKET_LOGON*) new CHAR[cbLogonInfoSize];
Status DBGCHK = pLogonInfo ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (NT_SUCCESS(Status)) { RtlZeroMemory(pLogonInfo, cbLogonInfoSize);
pWhere = ((UCHAR*) pLogonInfo) + DataOffset;
pLogonInfo->ServiceTicket = (UCHAR*) pWhere; pLogonInfo->ServiceTicketLength = cbTicketDataLength;
RtlCopyMemory(pLogonInfo->ServiceTicket, pTicketData, cbTicketDataLength);
pWhere += pLogonInfo->ServiceTicketLength;
pLogonInfo->TicketGrantingTicketLength = cbTGTDataLength;
if (pLogonInfo->TicketGrantingTicketLength) { pLogonInfo->TicketGrantingTicket = (UCHAR*) pWhere; RtlCopyMemory(pLogonInfo->TicketGrantingTicket, pTGTData, cbTGTDataLength); }
*ppLogonInfo = pLogonInfo; pLogonInfo = NULL; *pLogonInfoSize = cbLogonInfoSize; }
if (pLogonInfo) { delete [] pLogonInfo; }
return Status; }
NTSTATUS LsaTicketLogon( IN HANDLE hLsa, IN ULONG ulPackageId, IN UCHAR* pTGTData, IN ULONG cbTGTDataLength, IN UCHAR* pTicketData, IN ULONG cbTicketDataLength, OUT LUID* pLogonId, OUT HANDLE* phUserToken ) { TNtStatus Status;
SECURITY_LOGON_TYPE LogonType = Interactive; KERB_TICKET_LOGON* pLogonInfo = NULL; ULONG cbLogonInfoSize = sizeof(KERB_TICKET_LOGON);
TOKEN_SOURCE SourceContext = {0}; KERB_TICKET_PROFILE* pKerbTicketProfile = NULL; ULONG ProfileSize; STRING Name = {0}; QUOTA_LIMITS Quotas = {0}; NTSTATUS SubStatus;
Status DBGCHK = BuildAllocTicketLogonInfo( pTGTData, cbTGTDataLength, pTicketData, cbTicketDataLength, sizeof(KERB_TICKET_LOGON), //offset for data copy
&cbLogonInfoSize, //initialized to struct size
&pLogonInfo );
if (NT_SUCCESS(Status)) { pLogonInfo->MessageType = KerbTicketLogon; pLogonInfo->Flags = 0;
strncpy( SourceContext.SourceName, "krlogind", sizeof(SourceContext.SourceName) );
RtlInitString(&Name, "lzhu");
Status DBGCHK = NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier);
}
if (NT_SUCCESS(Status)) { Status DBGCHK = LsaLogonUser( hLsa, &Name, LogonType, ulPackageId, pLogonInfo, cbLogonInfoSize, NULL, // no token groups
&SourceContext, (PVOID *) &pKerbTicketProfile, &ProfileSize, pLogonId, phUserToken, &Quotas, &SubStatus ); }
if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; }
if (NT_SUCCESS(Status)) { DebugPrintf(SSPI_LOG, "LogonId %#x:%#x\n", pLogonId->HighPart, pLogonId->LowPart); DebugPrintf(SSPI_LOG, "TokenHandle %p\n", *phUserToken); DebugPrintf(SSPI_LOG, "Quotas PagedPoolLimit %p, NonPagedPoolLimit %p, " "MinimumWorkingSetSize %p, MaximumWorkingSetSize %p, PagedPoolLimit %p\n", Quotas.PagedPoolLimit, Quotas.NonPagedPoolLimit, Quotas.MinimumWorkingSetSize, Quotas.MaximumWorkingSetSize, Quotas.PagedPoolLimit); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "TimeLimit", &Quotas.TimeLimit);
KERB_INTERACTIVE_PROFILE* pKrbInteractiveProfile = &pKerbTicketProfile->Profile; DebugPrintf(SSPI_LOG, "interactive logon profile: " "LogCount %#x, BaddPasswordCount %#x, LogonScript %wZ, " "HomeDirectory %wZ, FullName %wZ, ProfilePath %wZ, " "HomeDriectoryDrive %wZ, LogonServer %wZ, UserFlags %#x\n", pKrbInteractiveProfile->LogonCount, pKrbInteractiveProfile->BadPasswordCount, &pKrbInteractiveProfile->LogonScript, &pKrbInteractiveProfile->HomeDirectory, &pKrbInteractiveProfile->FullName, &pKrbInteractiveProfile->ProfilePath, &pKrbInteractiveProfile->HomeDirectoryDrive, &pKrbInteractiveProfile->LogonServer, pKrbInteractiveProfile->UserFlags); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "LogonTime ", &pKrbInteractiveProfile->LogonTime); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "KickOffTime ", &pKrbInteractiveProfile->KickOffTime); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordLastSet ", &pKrbInteractiveProfile->PasswordLastSet); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordCanChange ", &pKrbInteractiveProfile->PasswordCanChange); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordMustChange ", &pKrbInteractiveProfile->PasswordMustChange); DebugPrintHex(SSPI_LOG, "SessionKey:", sizeof(pKerbTicketProfile->SessionKey), &pKerbTicketProfile->SessionKey); }
if (pKerbTicketProfile) { LsaFreeReturnBuffer(pKerbTicketProfile); }
if (pLogonInfo) { delete [] pLogonInfo; }
return Status; }
NTSTATUS KerbBuildKerbCredFromExternalTickets( IN PKERB_EXTERNAL_TICKET pTicket, IN PKERB_EXTERNAL_TICKET pDelegationTicket, OUT PUCHAR* pMarshalledKerbCred, OUT PULONG pcbKerbCredSize ) { TKerbErr KerbErr;
KERB_CRED KerbCred; KERB_CRED_INFO_LIST CredInfo; KERB_ENCRYPTED_CRED EncryptedCred; KERB_CRED_TICKET_LIST TicketList; ULONG EncryptionOverhead; ULONG BlockSize; PUCHAR pMarshalledEncryptPart = NULL; ULONG MarshalledEncryptSize; ULONG ConvertedFlags; PKERB_TICKET pDecodedTicket = NULL;
//
// Initialize the structures so they can be freed later.
//
*pMarshalledKerbCred = NULL; *pcbKerbCredSize = 0;
RtlZeroMemory( &KerbCred, sizeof(KERB_CRED) );
RtlZeroMemory( &EncryptedCred, sizeof(KERB_ENCRYPTED_CRED) ); RtlZeroMemory( &CredInfo, sizeof(KERB_CRED_INFO_LIST) ); RtlZeroMemory( &TicketList, sizeof(KERB_CRED_TICKET_LIST) );
KerbCred.version = KERBEROS_VERSION; KerbCred.message_type = KRB_CRED;
//
// Decode the ticket so we can put it in the structure (to re-encode it)
//
KerbErr DBGCHK = KerbUnpackData( pDelegationTicket->EncodedTicket, pDelegationTicket->EncodedTicketSize, KERB_TICKET_PDU, (PVOID *) &pDecodedTicket );
//
// First stick the ticket into the ticket list.
//
if (KERB_SUCCESS(KerbErr)) {
TicketList.next= NULL; TicketList.value = *pDecodedTicket; KerbCred.tickets = &TicketList;
//
// Now build the KERB_CRED_INFO for this ticket
//
CredInfo.value.key = * (PKERB_ENCRYPTION_KEY) &pDelegationTicket->SessionKey; KerbConvertLargeIntToGeneralizedTime( &CredInfo.value.endtime, NULL, &pDelegationTicket->EndTime ); CredInfo.value.bit_mask |= endtime_present;
KerbConvertLargeIntToGeneralizedTime( &CredInfo.value.starttime, NULL, &pDelegationTicket->StartTime ); CredInfo.value.bit_mask |= KERB_CRED_INFO_starttime_present;
KerbConvertLargeIntToGeneralizedTime( &CredInfo.value.KERB_CRED_INFO_renew_until, NULL, &pDelegationTicket->RenewUntil ); CredInfo.value.bit_mask |= KERB_CRED_INFO_renew_until_present; ConvertedFlags = KerbConvertUlongToFlagUlong(pDelegationTicket->TicketFlags); CredInfo.value.flags.value = (PUCHAR) &ConvertedFlags; CredInfo.value.flags.length = 8 * sizeof(ULONG); CredInfo.value.bit_mask |= flags_present;
KerbErr DBGCHK = KerbConvertKdcNameToPrincipalName( &CredInfo.value.principal_name, (PKERB_INTERNAL_NAME) pDelegationTicket->ClientName ); }
if (KERB_SUCCESS(KerbErr)) { CredInfo.value.bit_mask |= principal_name_present;
KerbErr DBGCHK = KerbConvertKdcNameToPrincipalName( &CredInfo.value.principal_name, (PKERB_INTERNAL_NAME) pDelegationTicket->ServiceName ); }
if (KERB_SUCCESS(KerbErr)) { CredInfo.value.bit_mask |= principal_name_present;
KerbErr DBGCHK = KerbConvertUnicodeStringToRealm( &CredInfo.value.principal_realm, &pDelegationTicket->DomainName ); }
//
// The realms are the same, so don't allocate both
//
if (KERB_SUCCESS(KerbErr)) { CredInfo.value.service_realm = CredInfo.value.service_realm; CredInfo.value.bit_mask |= principal_realm_present | service_realm_present;
EncryptedCred.ticket_info = &CredInfo;
//
// Now encrypted the encrypted cred into the cred
//
KerbErr DBGCHK = KerbPackEncryptedCred( &EncryptedCred, &MarshalledEncryptSize, &pMarshalledEncryptPart ); }
//
// If we are doing DES encryption, then we are talking with an non-NT
// server. Hence, don't encrypt the kerb-cred.
//
if (KERB_SUCCESS(KerbErr)) { if ((pTicket->SessionKey.KeyType == KERB_ETYPE_DES_CBC_CRC) || (pTicket->SessionKey.KeyType == KERB_ETYPE_DES_CBC_MD5)) { KerbCred.encrypted_part.cipher_text.length = MarshalledEncryptSize; KerbCred.encrypted_part.cipher_text.value = pMarshalledEncryptPart; KerbCred.encrypted_part.encryption_type = 0; pMarshalledEncryptPart = NULL; } else { //
// Now get the encryption overhead
//
KerbErr DBGCHK = KerbAllocateEncryptionBufferWrapper( pTicket->SessionKey.KeyType, MarshalledEncryptSize, &KerbCred.encrypted_part.cipher_text.length, &KerbCred.encrypted_part.cipher_text.value );
//
// Encrypt the data.
//
if (KERB_SUCCESS(KerbErr)) { KerbErr DBGCHK = KerbEncryptDataEx( &KerbCred.encrypted_part, MarshalledEncryptSize, pMarshalledEncryptPart, pTicket->SessionKey.KeyType, KERB_CRED_SALT, (PKERB_ENCRYPTION_KEY) &pTicket->SessionKey ); } } }
//
// Now we have to marshall the whole KERB_CRED
//
if (KERB_SUCCESS(KerbErr)) { KerbErr DBGCHK = KerbPackKerbCred( &KerbCred, pcbKerbCredSize, pMarshalledKerbCred ); }
if (pDecodedTicket != NULL) { KerbFreeData( KERB_TICKET_PDU, pDecodedTicket ); } KerbFreePrincipalName(&CredInfo.value.service_name);
KerbFreePrincipalName(&CredInfo.value.principal_name);
KerbFreeRealm(&CredInfo.value.principal_realm);
if (pMarshalledEncryptPart != NULL) { MIDL_user_free(pMarshalledEncryptPart); } if (KerbCred.encrypted_part.cipher_text.value != NULL) { MIDL_user_free(KerbCred.encrypted_part.cipher_text.value); }
return KerbMapKerbError(KerbErr); }
NTSTATUS TicketLogon( IN LUID* pLogonId, IN PCSTR pszServicePrincipal, OUT HANDLE* phToken ) { TNtStatus Status;
HANDLE hLogon = NULL; ULONG PackageId = -1;
KERB_EXTERNAL_TICKET* pTGTExternal = NULL; KERB_RETRIEVE_TKT_RESPONSE* pTicketCacheResponse = NULL;
UNICODE_STRING ServicePrincipal = {0};
UCHAR* pMarshalledTGT = NULL; ULONG ulTGTSize = 0;
LUID UserId;
Status DBGCHK = CreateUnicodeStringFromAsciiz(pszServicePrincipal, &ServicePrincipal);
if (NT_SUCCESS(Status)) { Status DBGCHK = GetLsaHandleAndPackageId( MICROSOFT_KERBEROS_NAME_A, &hLogon, &PackageId ); }
if (NT_SUCCESS(Status)) { Status DBGCHK = GetTGT( hLogon, PackageId, pLogonId, &pTGTExternal ); }
//
// get service pTicket
//
if (NT_SUCCESS(Status)) { Status DBGCHK = GetServiceTicket( hLogon, PackageId, &ServicePrincipal, pLogonId, FALSE, &pTicketCacheResponse ); }
if (NT_SUCCESS(Status)) { DebugPrintSysTimeAsLocalTime(SSPI_LOG, "StartTime: ", &pTGTExternal->StartTime);
Status DBGCHK = KerbBuildKerbCredFromExternalTickets( &pTicketCacheResponse->Ticket, pTGTExternal, &pMarshalledTGT, &ulTGTSize ); }
if (NT_SUCCESS(Status)) { Status DBGCHK = LsaTicketLogon( hLogon, PackageId, pMarshalledTGT, ulTGTSize, pTicketCacheResponse->Ticket.EncodedTicket, pTicketCacheResponse->Ticket.EncodedTicketSize, &UserId, phToken ); }
if (hLogon) { LsaDeregisterLogonProcess(hLogon); }
if (pTGTExternal) { LsaFreeReturnBuffer(pTGTExternal); }
if (pTicketCacheResponse) { LsaFreeReturnBuffer(pTicketCacheResponse); }
if (pMarshalledTGT) { MIDL_user_free(pMarshalledTGT); }
RtlFreeUnicodeString(&ServicePrincipal);
return Status; }
VOID __cdecl main( IN INT argc, IN PSTR argv[] ) { TNtStatus Status = STATUS_SUCCESS; HANDLE hToken = NULL;
LUID LogonId = {0};
if (argc != 4) { Usage(argv[0]); }
LogonId.HighPart = strtol(argv[1], NULL, 0); LogonId.LowPart = strtol(argv[2], NULL, 0);
DebugPrintf(SSPI_LOG, "LogonId %#x:%#x\n", LogonId.HighPart, LogonId.LowPart); DebugPrintf(SSPI_LOG, "service principal is %s\n", argv[3]);
Status DBGCHK = TicketLogon(&LogonId, argv[3], &hToken);
if (NT_SUCCESS(Status)) { UNICODE_STRING Application = {0};
Status DBGCHK = CreateUnicodeStringFromAsciiz("cmd.exe", &Application);
if (NT_SUCCESS(Status)) { Status DBGCHK = StartInteractiveClientProcessAsUser(hToken, Application.Buffer); }
RtlFreeUnicodeString(&Application); }
if (NT_SUCCESS(Status)) { DebugPrintf(SSPI_LOG, "tktlogon succeeded\n"); } else { DebugPrintf(SSPI_ERROR, "tktlogon failed\n"); }
if (hToken) { CloseHandle(hToken); } }
|