/*++ Copyright (c) 2001 Microsoft Corporation Module Name: logon.cxx Abstract: logon Author: Larry Zhu (LZhu) December 1, 2001 Created Environment: User Mode Revision History: --*/ #include "precomp.hxx" #pragma hdrstop #include "logon.hxx" NTSTATUS GetSubAuthLogonInfo( IN ULONG SubAuthId, IN BOOLEAN bUseNewSubAuthStyle, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation, OUT ULONG* pcbLogonInfo, OUT VOID** ppLognInfo ) { TNtStatus Status; PMSV1_0_LM20_LOGON pMsvNetAuthInfo = NULL; ULONG cbMsvNetAuthInfo; NT_OWF_PASSWORD PasswordHash; OEM_STRING LmPassword; UCHAR LmPasswordBuf[ LM20_PWLEN + 1 ]; LM_OWF_PASSWORD LmPasswordHash; NT_CHALLENGE NtChallenge; DebugPrintf(SSPI_LOG, "GetSubAuthLogonInfo UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n", pUserName, pDomainName, pPassword, pWorkstation); *ppLognInfo = NULL; *pcbLogonInfo = 0; cbMsvNetAuthInfo = ROUND_UP_COUNT(sizeof(MSV1_0_SUBAUTH_LOGON), sizeof(ULONG_PTR)) + pUserName->Length + pDomainName->Length + pWorkstation->Length + NT_RESPONSE_LENGTH + LM_RESPONSE_LENGTH; pMsvNetAuthInfo = (PMSV1_0_LM20_LOGON) new CHAR[cbMsvNetAuthInfo]; Status DBGCHK = pMsvNetAuthInfo ? STATUS_SUCCESS : STATUS_NO_MEMORY; if (NT_SUCCESS(Status)) { // // Start packing in the string // RtlZeroMemory(pMsvNetAuthInfo, cbMsvNetAuthInfo); pMsvNetAuthInfo->MessageType = bUseNewSubAuthStyle ? MsV1_0SubAuthLogon : MsV1_0NetworkLogon; // if set MsV1_0Lm20Logon, ignore ParameterControl // // Copy the user name into the authentication buffer // pMsvNetAuthInfo->UserName.Length = pUserName->Length; pMsvNetAuthInfo->UserName.MaximumLength = pMsvNetAuthInfo->UserName.Length; pMsvNetAuthInfo->UserName.Buffer = (PWSTR)( ((CHAR*)pMsvNetAuthInfo) + sizeof(MSV1_0_SUBAUTH_LOGON) ); // could be aligned here RtlCopyMemory( pMsvNetAuthInfo->UserName.Buffer, pUserName->Buffer, pUserName->Length ); // // Copy the domain name into the authentication buffer // pMsvNetAuthInfo->LogonDomainName.Length = pDomainName->Length; pMsvNetAuthInfo->LogonDomainName.MaximumLength = pDomainName->Length ; pMsvNetAuthInfo->LogonDomainName.Buffer = (PWSTR) ((PBYTE)(pMsvNetAuthInfo->UserName.Buffer) + pMsvNetAuthInfo->UserName.MaximumLength); RtlCopyMemory( pMsvNetAuthInfo->LogonDomainName.Buffer, pDomainName->Buffer, pDomainName->Length ); // // Copy the workstation name into the buffer // pMsvNetAuthInfo->Workstation.Length = pWorkstation->Length; pMsvNetAuthInfo->Workstation.MaximumLength = pMsvNetAuthInfo->Workstation.Length; pMsvNetAuthInfo->Workstation.Buffer = (PWSTR) ((PBYTE) (pMsvNetAuthInfo->LogonDomainName.Buffer) + pMsvNetAuthInfo->LogonDomainName.MaximumLength); RtlCopyMemory( pMsvNetAuthInfo->Workstation.Buffer, pWorkstation->Buffer, pWorkstation->Length ); // // Now, generate the bits for the challenge // RtlGenRandom(&NtChallenge, sizeof(NtChallenge)); RtlCopyMemory(pMsvNetAuthInfo->ChallengeToClient, &NtChallenge, MSV1_0_CHALLENGE_LENGTH); // // Set up space for response // pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer = (((PCHAR) pMsvNetAuthInfo->Workstation.Buffer) + pMsvNetAuthInfo->Workstation.MaximumLength); pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Length = NT_RESPONSE_LENGTH; pMsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength = NT_RESPONSE_LENGTH; RtlCalculateNtOwfPassword( pPassword, &PasswordHash ); RtlCalculateNtResponse( &NtChallenge, &PasswordHash, (PNT_RESPONSE) pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer ); // // Now do the painful LM compatible hash, so anyone who is maintaining // their account from a WfW machine will still have a password. // LmPassword.Buffer = (PCHAR) LmPasswordBuf; LmPassword.Length = LmPassword.MaximumLength = LM20_PWLEN + 1; Status DBGCHK = RtlUpcaseUnicodeStringToOemString( &LmPassword, pPassword, FALSE ); } if (NT_SUCCESS(Status)) { pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer = ((PCHAR) (pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer) + pMsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength); pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Length = LM_RESPONSE_LENGTH; pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.MaximumLength = LM_RESPONSE_LENGTH; RtlCalculateLmOwfPassword( LmPassword.Buffer, &LmPasswordHash ); RtlZeroMemory(LmPassword.Buffer, LmPassword.Length); RtlCalculateLmResponse( &NtChallenge, &LmPasswordHash, (PLM_RESPONSE) pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer ); if (bUseNewSubAuthStyle) { ((MSV1_0_SUBAUTH_LOGON*) pMsvNetAuthInfo)->SubAuthPackageId = SubAuthId; } else { pMsvNetAuthInfo->ParameterControl |= SubAuthId << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT; } *ppLognInfo = pMsvNetAuthInfo; pMsvNetAuthInfo = NULL; *pcbLogonInfo = cbMsvNetAuthInfo; } if (pMsvNetAuthInfo) { delete [] pMsvNetAuthInfo; } return Status; } NTSTATUS MsvSubAuthLsaLogon( IN HANDLE hLsa, IN ULONG PackageId, IN SECURITY_LOGON_TYPE LogonType, IN ULONG SubAuthId, IN BOOLEAN bUseNewSubAuthStyle, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation, OUT HANDLE* phToken ) { TNtStatus Status; NTSTATUS SubStatus = STATUS_UNSUCCESSFUL; VOID* pLogonInfo = NULL; ULONG cbLogonInfoSize = 0; LSA_STRING Name = {0}; TOKEN_SOURCE SourceContext = {0}; VOID* pProfile = NULL; ULONG cbProfileSize = 0; LUID LogonId = {0}; QUOTA_LIMITS Quotas = {0}; DebugPrintf(SSPI_LOG, "MsvSubAuthLsaLogon PackageId %#x, LogonType %#x, SubAuthId %#x, " "UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n", PackageId, LogonType, SubAuthId, pUserName, pDomainName, pPassword, pWorkstation); strncpy( SourceContext.SourceName, "ssptest", sizeof(SourceContext.SourceName) ); NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier); // // Now call LsaLogonUser // RtlInitString( &Name, "ssptest" ); Status DBGCHK = GetSubAuthLogonInfo( SubAuthId, bUseNewSubAuthStyle, pUserName, pDomainName, pPassword, pWorkstation, &cbLogonInfoSize, &pLogonInfo ); if (NT_SUCCESS(Status)) { SspiPrintHex(SSPI_LOG, TEXT("SubAuthInfo"), cbLogonInfoSize, pLogonInfo); Status DBGCHK = LsaLogonUser( hLsa, &Name, LogonType, PackageId, pLogonInfo, cbLogonInfoSize, NULL, // no token groups &SourceContext, (VOID**) &pProfile, &cbProfileSize, &LogonId, phToken, &Quotas, &SubStatus ); } if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; } if (NT_SUCCESS(Status)) { DebugPrintf(SSPI_LOG, "LogonId %#x:%#x\n", LogonId.HighPart, LogonId.LowPart); DebugPrintf(SSPI_LOG, "Token handle %p\n", *phToken); 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); if (MsV1_0InteractiveProfile == *((ULONG*) pProfile)) { MSV1_0_INTERACTIVE_PROFILE* pMsvInteractiveProfile = (MSV1_0_INTERACTIVE_PROFILE*) pProfile; 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", pMsvInteractiveProfile->LogonCount, pMsvInteractiveProfile->BadPasswordCount, &pMsvInteractiveProfile->LogonScript, &pMsvInteractiveProfile->HomeDirectory, &pMsvInteractiveProfile->FullName, &pMsvInteractiveProfile->ProfilePath, &pMsvInteractiveProfile->HomeDirectoryDrive, &pMsvInteractiveProfile->LogonServer, pMsvInteractiveProfile->UserFlags); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "LogonTime ", &pMsvInteractiveProfile->LogonTime); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "KickOffTime ", &pMsvInteractiveProfile->KickOffTime ); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordLastSet ", &pMsvInteractiveProfile->PasswordLastSet ); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordCanChange ", &pMsvInteractiveProfile->PasswordCanChange ); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "PasswordMustChange ", &pMsvInteractiveProfile->PasswordMustChange ); } else if (MsV1_0Lm20LogonProfile == *((ULONG*) pProfile)) { MSV1_0_LM20_LOGON_PROFILE* pMsvLm20LogonProfile = (MSV1_0_LM20_LOGON_PROFILE*) pProfile; DebugPrintf(SSPI_LOG, "Lm20 logon profile: " "UserFlags %#x, LogonDomainName %wZ, LogonServer %wZ, UserParameters %#x\n", pMsvLm20LogonProfile->UserFlags, &pMsvLm20LogonProfile->LogonDomainName, &pMsvLm20LogonProfile->LogonServer, pMsvLm20LogonProfile->UserParameters); DebugPrintHex(SSPI_LOG, "UserSessionKey:", MSV1_0_USER_SESSION_KEY_LENGTH, pMsvLm20LogonProfile->UserSessionKey); DebugPrintHex(SSPI_LOG, "LanmanSessionKey:", MSV1_0_LANMAN_SESSION_KEY_LENGTH, pMsvLm20LogonProfile->LanmanSessionKey); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "KickOffTime", &pMsvLm20LogonProfile->KickOffTime); DebugPrintSysTimeAsLocalTime(SSPI_LOG, "LogoffTime", &pMsvLm20LogonProfile->LogoffTime); } else { DebugPrintf(SSPI_ERROR, "Unsupported profile type %#x\n", *((ULONG*) pProfile)); } } if (pProfile) { LsaFreeReturnBuffer(pProfile); } return Status; } NTSTATUS MsvSubAuthLogon( IN HANDLE hLsa, IN ULONG PackageId, IN ULONG SubAuthId, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation ) { TNtStatus Status; WCHAR LogonInfo[MAX_PATH] = {0}; PMSV1_0_PASSTHROUGH_REQUEST pPassthroughRequest = NULL; ULONG cbRequest = 0; ULONG cbResponse = 0; PMSV1_0_PASSTHROUGH_RESPONSE pPassthroughResponse = NULL; MSV1_0_SUBAUTH_REQUEST* pSubAuthRequest = NULL; UNICODE_STRING MsvPackageName = {0}; UCHAR* pWhere = NULL; NTSTATUS SubStatus = STATUS_UNSUCCESSFUL; SspiPrint(SSPI_LOG, TEXT("MsvSubAuthLsaLogon PackageId %#x, SubAuthId %#x(%d), ") TEXT("UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n"), PackageId, SubAuthId, SubAuthId, pUserName, pDomainName, pPassword, pWorkstation); _snwprintf(LogonInfo, COUNTOF(LogonInfo) - 1, L"%wZ%wZ%wZ%wZ", pUserName, pDomainName, pPassword, pWorkstation); RtlInitUnicodeString(&MsvPackageName, L"NTLM"); cbRequest = sizeof(MSV1_0_PASSTHROUGH_REQUEST) + ROUND_UP_COUNT(pDomainName->Length + sizeof(WCHAR), ALIGN_LPTSTR) + ROUND_UP_COUNT(MsvPackageName.Length + sizeof(WCHAR), ALIGN_LPTSTR) + ROUND_UP_COUNT(sizeof(MSV1_0_SUBAUTH_REQUEST), ALIGN_LPTSTR) + ROUND_UP_COUNT(wcslen(LogonInfo) * sizeof(WCHAR) + sizeof(WCHAR), ALIGN_LPTSTR); pPassthroughRequest = (PMSV1_0_PASSTHROUGH_REQUEST) new CHAR[cbRequest]; Status DBGCHK = pPassthroughRequest ? STATUS_SUCCESS : STATUS_NO_MEMORY; if (NT_SUCCESS(Status)) { RtlZeroMemory(pPassthroughRequest, cbRequest); pWhere = (PUCHAR) (pPassthroughRequest + 1); pPassthroughRequest->MessageType = MsV1_0GenericPassthrough; pPassthroughRequest->DomainName = *pDomainName; pPassthroughRequest->DomainName.Buffer = (PWSTR) pWhere; RtlCopyMemory( pWhere, pDomainName->Buffer, pDomainName->Length ); pWhere += ROUND_UP_COUNT(pDomainName->Length + sizeof(WCHAR), ALIGN_LPTSTR); pPassthroughRequest->PackageName = MsvPackageName; pPassthroughRequest->PackageName.Buffer = (PWSTR) pWhere; RtlCopyMemory( pWhere, MsvPackageName.Buffer, MsvPackageName.Length ); pWhere += ROUND_UP_COUNT(MsvPackageName.Length + sizeof(WCHAR), ALIGN_LPTSTR); pPassthroughRequest->LogonData = pWhere; pPassthroughRequest->DataLength = ROUND_UP_COUNT(sizeof(MSV1_0_SUBAUTH_REQUEST), ALIGN_LPTSTR) + ROUND_UP_COUNT(wcslen(LogonInfo) * sizeof(WCHAR), ALIGN_LPTSTR); pSubAuthRequest = (MSV1_0_SUBAUTH_REQUEST*) pPassthroughRequest->LogonData; pSubAuthRequest->MessageType = MsV1_0SubAuth; pWhere = (UCHAR*) (pSubAuthRequest + 1); pSubAuthRequest->SubAuthPackageId = SubAuthId; pSubAuthRequest->SubAuthInfoLength = wcslen(LogonInfo) * sizeof(WCHAR); pSubAuthRequest->SubAuthSubmitBuffer = pWhere; RtlCopyMemory( pSubAuthRequest->SubAuthSubmitBuffer, LogonInfo, pSubAuthRequest->SubAuthInfoLength ); pSubAuthRequest->SubAuthSubmitBuffer = (UCHAR*) (pWhere - (UCHAR*) pSubAuthRequest); SspiPrintHex(SSPI_LOG, TEXT("PassthroughRequest"), cbRequest, pPassthroughRequest); Status DBGCHK = LsaCallAuthenticationPackage( hLsa, PackageId, pPassthroughRequest, cbRequest, (PVOID *) &pPassthroughResponse, &cbResponse, &SubStatus ); } if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; } if (NT_SUCCESS(Status)) { Status DBGCHK = SubStatus; } if (NT_SUCCESS(Status)) { SspiPrintHex(SSPI_LOG, TEXT("ValidationData"), pPassthroughResponse->DataLength, pPassthroughResponse->ValidationData); } if (pPassthroughRequest) { delete [] pPassthroughRequest; } if (pPassthroughResponse) { LsaFreeReturnBuffer(pPassthroughResponse); } return Status; }