/*++ 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 LogonUserWrapper( IN PCWSTR pszUserName, IN PCWSTR pszDomainName, IN PCWSTR pszPassword, IN DWORD LogonType, IN DWORD dwLogonProvider, OUT HANDLE* phToken ) { THResult hResult = S_OK; PSID pLoginSid = NULL; VOID* pProfile = NULL; ULONG cbProfile = 0; QUOTA_LIMITS Quotas = {0}; DebugPrintf(SSPI_LOG, "LogonUserWrapper UserName %ws, DomainName %ws, Password %ws, LogonType %#x, Provider %#x\n", pszUserName, pszDomainName, pszPassword, LogonType, dwLogonProvider); hResult DBGCHK = LogonUserExW( (PWSTR) pszUserName, (PWSTR) pszDomainName, (PWSTR) pszPassword, LogonType, dwLogonProvider, phToken, &pLoginSid, &pProfile, &cbProfile, &Quotas ) ? S_OK : GetLastErrorAsHResult(); if (SUCCEEDED(hResult)) { DebugPrintSidFriendlyName(SSPI_LOG, "LogonSid:", pLoginSid); DebugPrintProfileAndQuotas(SSPI_LOG, pProfile, &Quotas); DebugPrintf(SSPI_LOG, "LogonUserWrapper TokenHandle %p\n", *phToken); } if (pLoginSid) { LsaFreeReturnBuffer(pLoginSid); } if (pProfile) { LsaFreeReturnBuffer(pProfile); } return SUCCEEDED(hResult) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } VOID DebugPrintProfileAndQuotas( IN ULONG Level, IN OPTIONAL VOID* pProfile, IN OPTIONAL QUOTA_LIMITS* pQuotas ) { if (pQuotas) { DebugPrintf(Level, "Quotas PagedPoolLimit %p, NonPagedPoolLimit %p, " "MinimumWorkingSetSize %p, MaximumWorkingSetSize %p, PagedPoolLimit %p\n", pQuotas->PagedPoolLimit, pQuotas->NonPagedPoolLimit, pQuotas->MinimumWorkingSetSize, pQuotas->MaximumWorkingSetSize, pQuotas->PagedPoolLimit); DebugPrintSysTimeAsLocalTime(Level, "TimeLimit", &pQuotas->TimeLimit); } if (pProfile) { if (MsV1_0InteractiveProfile == *((ULONG*) pProfile)) { MSV1_0_INTERACTIVE_PROFILE* pMsvInteractiveProfile = (MSV1_0_INTERACTIVE_PROFILE*) pProfile; DebugPrintf(Level, "MsV1_0InteractiveProfile: " "LogonCount %#x, BadPasswordCount %#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(Level, "LogonTime", &pMsvInteractiveProfile->LogonTime); DebugPrintSysTimeAsLocalTime(Level, "LogoffTime", &pMsvInteractiveProfile->LogoffTime); DebugPrintSysTimeAsLocalTime(Level, "KickOffTime", &pMsvInteractiveProfile->KickOffTime ); DebugPrintSysTimeAsLocalTime(Level, "PasswordLastSet", &pMsvInteractiveProfile->PasswordLastSet ); DebugPrintSysTimeAsLocalTime(Level, "PasswordCanChange", &pMsvInteractiveProfile->PasswordCanChange ); DebugPrintSysTimeAsLocalTime(Level, "PasswordMustChange", &pMsvInteractiveProfile->PasswordMustChange ); } else if (MsV1_0Lm20LogonProfile == *((ULONG*) pProfile)) { MSV1_0_LM20_LOGON_PROFILE* pMsvLm20LogonProfile = (MSV1_0_LM20_LOGON_PROFILE*) pProfile; DebugPrintf(Level, "MsV1_0Lm20LogonProfile: " "UserFlags %#x, LogonDomainName %wZ, LogonServer %wZ, UserParameters %#x\n", pMsvLm20LogonProfile->UserFlags, &pMsvLm20LogonProfile->LogonDomainName, &pMsvLm20LogonProfile->LogonServer, pMsvLm20LogonProfile->UserParameters); DebugPrintHex(Level, "UserSessionKey:", MSV1_0_USER_SESSION_KEY_LENGTH, pMsvLm20LogonProfile->UserSessionKey); DebugPrintHex(Level, "LanmanSessionKey:", MSV1_0_LANMAN_SESSION_KEY_LENGTH, pMsvLm20LogonProfile->LanmanSessionKey); DebugPrintSysTimeAsLocalTime(Level, "KickOffTime", &pMsvLm20LogonProfile->KickOffTime); DebugPrintSysTimeAsLocalTime(Level, "LogoffTime", &pMsvLm20LogonProfile->LogoffTime); } else if (KerbInteractiveProfile == *((ULONG*) pProfile)) { KERB_TICKET_PROFILE* pKerbTicketProfile = (KERB_TICKET_PROFILE*) pProfile; KERB_INTERACTIVE_PROFILE* pKrbInteractiveProfile = &pKerbTicketProfile->Profile; DebugPrintf(Level, "KerbInteractiveProfile: " "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(Level, "LogonTime", &pKrbInteractiveProfile->LogonTime); DebugPrintSysTimeAsLocalTime(Level, "LogoffTime", &pKrbInteractiveProfile->LogoffTime); DebugPrintSysTimeAsLocalTime(Level, "KickOffTime", &pKrbInteractiveProfile->KickOffTime); DebugPrintSysTimeAsLocalTime(Level, "PasswordLastSet", &pKrbInteractiveProfile->PasswordLastSet); DebugPrintSysTimeAsLocalTime(Level, "PasswordCanChange", &pKrbInteractiveProfile->PasswordCanChange); DebugPrintSysTimeAsLocalTime(Level, "PasswordMustChange", &pKrbInteractiveProfile->PasswordMustChange); DebugPrintHex(Level, "SessionKey:", sizeof(pKerbTicketProfile->SessionKey), &pKerbTicketProfile->SessionKey); } else { DebugPrintf(SSPI_ERROR, "Unsupported profile type %#x\n", *((ULONG*) pProfile)); } } } NTSTATUS GetLm20LogonInfoNtlmv1( IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation, IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH], OUT ULONG* pcbLogonInfo, OUT PMSV1_0_LM20_LOGON *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; DebugPrintf(SSPI_LOG, "GetLm20LogonInfoNtlmv1 UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n", pUserName, pDomainName, pPassword, pWorkstation); *ppLognInfo = NULL; *pcbLogonInfo = 0; cbMsvNetAuthInfo = ROUND_UP_COUNT(sizeof(MSV1_0_LM20_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 = 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)(pMsvNetAuthInfo + 1); // 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 ); RtlCopyMemory(pMsvNetAuthInfo->ChallengeToClient, ChallengeToClient, 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( (PNT_CHALLENGE) ChallengeToClient, &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( (PLM_CHALLENGE) ChallengeToClient, &LmPasswordHash, (PLM_RESPONSE) pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer ); *ppLognInfo = pMsvNetAuthInfo; pMsvNetAuthInfo = NULL; *pcbLogonInfo = cbMsvNetAuthInfo; } if (pMsvNetAuthInfo) { delete [] pMsvNetAuthInfo; } return Status; } VOID CalculateNtlmv2Owf( IN NT_OWF_PASSWORD* pNtOwfPassword, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, OUT UCHAR Ntlmv2Owf[MSV1_0_NTLM3_OWF_LENGTH] ) { HMACMD5_CTX HMACMD5Context; // // reserve a scratch buffer // WCHAR szUserName[(UNLEN + 4)] = {0}; UNICODE_STRING UserName = {0, sizeof(szUserName), szUserName}; // // first make a copy then upcase it // UserName.Length = min(UserName.MaximumLength, pUserName->Length); ASSERT(UserName.Length == pUserName->Length); RtlCopyMemory(UserName.Buffer, pUserName->Buffer, UserName.Length); RtlUpcaseUnicodeString(&UserName, &UserName, FALSE); // // Calculate Ntlmv2 OWF -- HMAC(MD4(P), (UserName, LogonDomainName)) // HMACMD5Init( &HMACMD5Context, (UCHAR *) pNtOwfPassword, sizeof(*pNtOwfPassword) ); HMACMD5Update( &HMACMD5Context, (UCHAR *) UserName.Buffer, UserName.Length ); HMACMD5Update( &HMACMD5Context, (UCHAR *) pLogonDomainName->Buffer, pLogonDomainName->Length ); HMACMD5Final( &HMACMD5Context, Ntlmv2Owf ); } VOID GetLmv2Response( IN NT_OWF_PASSWORD* pNtOwfPassword, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH], IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH], OUT UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH], OUT OPTIONAL USER_SESSION_KEY* pUserSessionKey, OUT OPTIONAL LM_SESSION_KEY* pLanmanSessionKey // [MSV1_0_LANMAN_SESSION_KEY_LENGTH] ) { HMACMD5_CTX HMACMD5Context; UCHAR Ntlmv2Owf[MSV1_0_NTLM3_OWF_LENGTH]; C_ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH); // // get Ntlmv2 OWF // CalculateNtlmv2Owf( pNtOwfPassword, pUserName, pLogonDomainName, Ntlmv2Owf ); // // Calculate Ntlmv2 Response // HMAC(Ntlmv2Owf, (ChallengeToClient, ChallengeFromClient)) // HMACMD5Init( &HMACMD5Context, Ntlmv2Owf, MSV1_0_NTLM3_OWF_LENGTH ); HMACMD5Update( &HMACMD5Context, ChallengeToClient, MSV1_0_CHALLENGE_LENGTH ); HMACMD5Update( &HMACMD5Context, ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH ); HMACMD5Final( &HMACMD5Context, Response ); if (pUserSessionKey && pLanmanSessionKey) { // now compute the session keys // HMAC(Kr, R) HMACMD5Init( &HMACMD5Context, Ntlmv2Owf, MSV1_0_NTLM3_OWF_LENGTH ); HMACMD5Update( &HMACMD5Context, Response, MSV1_0_NTLM3_RESPONSE_LENGTH ); ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH); HMACMD5Final( &HMACMD5Context, (PUCHAR)pUserSessionKey ); ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH <= MSV1_0_USER_SESSION_KEY_LENGTH); RtlCopyMemory( pLanmanSessionKey, pUserSessionKey, MSV1_0_LANMAN_SESSION_KEY_LENGTH ); } } VOID Lm20GetNtlmv2Response( IN NT_OWF_PASSWORD* pNtOwfPassword, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, IN STRING* pTargetInfo, IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH], OUT MSV1_0_NTLM3_RESPONSE* pNtlmv2Response, OUT MSV1_0_LM3_RESPONSE* pLmv2Response, OUT USER_SESSION_KEY* pNtUserSessionKey, OUT LM_SESSION_KEY* pLmSessionKey ) { // // fill in version numbers, timestamp, and client's challenge // pNtlmv2Response->RespType = 1; pNtlmv2Response->HiRespType = 1; pNtlmv2Response->Flags = 0; pNtlmv2Response->MsgWord = 0; GetSystemTimeAsFileTime((FILETIME*)(&pNtlmv2Response->TimeStamp)); RtlGenRandom(pNtlmv2Response->ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH); RtlCopyMemory(pNtlmv2Response->Buffer, pTargetInfo->Buffer, pTargetInfo->Length); // // Calculate Ntlmv2 response, filling in response field // GetNtlmv2Response( pNtOwfPassword, pUserName, pLogonDomainName, pTargetInfo->Length, ChallengeToClient, pNtlmv2Response, pNtUserSessionKey, pLmSessionKey ); // // Use same challenge to compute the LMV2 response // RtlCopyMemory(pLmv2Response->ChallengeFromClient, pNtlmv2Response->ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH); // // Calculate LMV2 response // GetLmv2Response( pNtOwfPassword, pUserName, pLogonDomainName, ChallengeToClient, pLmv2Response->ChallengeFromClient, pLmv2Response->Response, NULL, NULL ); } VOID GetNtlmv2Response( IN NT_OWF_PASSWORD* pNtOwfPassword, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, IN ULONG TargetInfoLength, IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH], IN OUT MSV1_0_NTLM3_RESPONSE* pNtlmv2Response, OUT USER_SESSION_KEY* pNtUserSessionKey, OUT LM_SESSION_KEY* pLmSessionKey ) { HMACMD5_CTX HMACMD5Context; UCHAR Ntlmv2Owf[MSV1_0_NTLM3_OWF_LENGTH]; C_ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH); C_ASSERT(MD5DIGESTLEN == sizeof(USER_SESSION_KEY)); C_ASSERT(sizeof(LM_SESSION_KEY) <= sizeof(USER_SESSION_KEY)); // // get Ntlmv2 OWF // CalculateNtlmv2Owf( pNtOwfPassword, pUserName, pLogonDomainName, Ntlmv2Owf ); HMACMD5Init( &HMACMD5Context, Ntlmv2Owf, MSV1_0_NTLM3_OWF_LENGTH ); HMACMD5Update( &HMACMD5Context, ChallengeToClient, MSV1_0_CHALLENGE_LENGTH ); HMACMD5Update( &HMACMD5Context, &pNtlmv2Response->RespType, (MSV1_0_NTLM3_INPUT_LENGTH + TargetInfoLength) ); HMACMD5Final( &HMACMD5Context, pNtlmv2Response->Response ); // // now compute the session keys // HMAC(Kr, R) // HMACMD5Init( &HMACMD5Context, Ntlmv2Owf, MSV1_0_NTLM3_OWF_LENGTH ); HMACMD5Update( &HMACMD5Context, pNtlmv2Response->Response, MSV1_0_NTLM3_RESPONSE_LENGTH ); HMACMD5Final( &HMACMD5Context, (UCHAR*) pNtUserSessionKey ); RtlCopyMemory(pLmSessionKey, pNtUserSessionKey, sizeof(LM_SESSION_KEY)); } NTSTATUS GetLm20LogonInfoNtlmv2( IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation, IN OPTIONAL UNICODE_STRING* pTargetName, IN OPTIONAL STRING* pTargetInfo, IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH], OUT ULONG* pcbLogonInfo, OUT PMSV1_0_LM20_LOGON *ppLognInfo ) { TNtStatus Status = STATUS_SUCCESS; STRING TargetInfo = {0}; PMSV1_0_LM20_LOGON pMsvNetAuthInfo = NULL; ULONG cbMsvNetAuthInfo = 0; NT_OWF_PASSWORD NtOwfPassword; USER_SESSION_KEY NtUserSessionKey; LM_SESSION_KEY LmSessionKey; MSV1_0_LM3_RESPONSE Lmv2Response; MSV1_0_NTLM3_RESPONSE* pNtlmv2Reponse = NULL; ULONG cbNtlmv2Response = 0; DebugPrintf(SSPI_LOG, "GetLm20LogonInfoNtlmv2 UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n", pUserName, pDomainName, pPassword, pWorkstation); *ppLognInfo = NULL; *pcbLogonInfo = 0; RtlCalculateNtOwfPassword( pPassword, &NtOwfPassword ); if (pTargetInfo) { TargetInfo = *pTargetInfo; } else if (pTargetName) { Status DBGCHK = CreateTargetInfo(pTargetName, &TargetInfo); } if (NT_SUCCESS(Status)) { cbNtlmv2Response = ROUND_UP_COUNT(sizeof(MSV1_0_NTLM3_RESPONSE), sizeof(ULONG_PTR)) + TargetInfo.Length; pNtlmv2Reponse = (MSV1_0_NTLM3_RESPONSE*) new CHAR[cbNtlmv2Response]; Status DBGCHK = pNtlmv2Reponse ? STATUS_SUCCESS : STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { Lm20GetNtlmv2Response( &NtOwfPassword, pUserName, pDomainName, &TargetInfo, ChallengeToClient, pNtlmv2Reponse, &Lmv2Response, &NtUserSessionKey, &LmSessionKey ); cbMsvNetAuthInfo = ROUND_UP_COUNT(sizeof(MSV1_0_LM20_LOGON), sizeof(ULONG_PTR)) + pUserName->Length + pDomainName->Length + pWorkstation->Length + cbNtlmv2Response + sizeof(Lmv2Response); 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 = MsV1_0NetworkLogon; // // Copy the user name into the authentication buffer // pMsvNetAuthInfo->UserName.Length = pUserName->Length; pMsvNetAuthInfo->UserName.MaximumLength = pMsvNetAuthInfo->UserName.Length; pMsvNetAuthInfo->UserName.Buffer = (PWSTR) (pMsvNetAuthInfo + 1); // 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 ); RtlCopyMemory(pMsvNetAuthInfo->ChallengeToClient, ChallengeToClient, MSV1_0_CHALLENGE_LENGTH); // // Set up space for response // pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer = ((PCHAR) (pMsvNetAuthInfo->Workstation.Buffer) + pMsvNetAuthInfo->Workstation.MaximumLength); pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Length = (USHORT) cbNtlmv2Response; pMsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength = (USHORT) cbNtlmv2Response; RtlCopyMemory( pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer, pNtlmv2Reponse, cbNtlmv2Response ); pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer = ((PCHAR) (pMsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer) + pMsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength ); pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Length = sizeof(Lmv2Response); pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.MaximumLength = sizeof(Lmv2Response); RtlCopyMemory( pMsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer, &Lmv2Response, sizeof(Lmv2Response) ); *ppLognInfo = pMsvNetAuthInfo; pMsvNetAuthInfo = NULL; *pcbLogonInfo = cbMsvNetAuthInfo; } if (pMsvNetAuthInfo) { delete [] pMsvNetAuthInfo; } if (pNtlmv2Reponse) { delete [] pNtlmv2Reponse; } return Status; } NTSTATUS GetMsvInteractiveLogonInfo( IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, IN UNICODE_STRING* pPassword, OUT ULONG* pcbLogonInfo, OUT MSV1_0_INTERACTIVE_LOGON** ppLogonInfo ) { TNtStatus Status; ULONG cbLogonInfo = 0; UCHAR* pWhere = NULL; MSV1_0_INTERACTIVE_LOGON* pLogonInfo = NULL; DebugPrintf(SSPI_LOG, "GetMsvInteractiveLogonInfo UserName %wZ, LogonDomainName %wZ, Password %wZ\n", pUserName, pLogonDomainName, pPassword); cbLogonInfo = ROUND_UP_COUNT(sizeof(MSV1_0_INTERACTIVE_LOGON), sizeof(ULONG_PTR)) + pUserName->Length + pLogonDomainName->Length + pPassword->Length; pLogonInfo = (MSV1_0_INTERACTIVE_LOGON*) new CHAR[cbLogonInfo]; Status DBGCHK = pLogonInfo ? STATUS_SUCCESS : STATUS_NO_MEMORY; if (NT_SUCCESS(Status)) { pLogonInfo->MessageType = MsV1_0InteractiveLogon; pWhere = (PUCHAR) (pLogonInfo + 1); pLogonInfo->UserName.Buffer = (PWSTR) pWhere; pLogonInfo->UserName.MaximumLength = pUserName->Length; pLogonInfo->UserName.Length = pUserName->Length; RtlCopyMemory(pLogonInfo->UserName.Buffer, pUserName->Buffer, pUserName->Length); pWhere += pLogonInfo->UserName.Length; pLogonInfo->LogonDomainName.Buffer = (PWSTR) pWhere; pLogonInfo->LogonDomainName.MaximumLength = pLogonDomainName->Length; pLogonInfo->LogonDomainName.Length = pLogonDomainName->Length; RtlCopyMemory(pLogonInfo->LogonDomainName.Buffer, pLogonDomainName->Buffer, pLogonDomainName->Length); pWhere += pLogonInfo->LogonDomainName.Length; pLogonInfo->Password.Buffer = (PWSTR) pWhere; pLogonInfo->Password.MaximumLength = pPassword->Length; pLogonInfo->Password.Length = pPassword->Length; RtlCopyMemory(pLogonInfo->Password.Buffer, pPassword->Buffer, pPassword->Length); pWhere += pLogonInfo->Password.Length; *ppLogonInfo = pLogonInfo; pLogonInfo = NULL; *pcbLogonInfo = cbLogonInfo; } if (pLogonInfo) { delete [] pLogonInfo; } return Status; } NTSTATUS GetKrbS4U2SelfLogonInfo( IN UNICODE_STRING* pClientUpn, IN OPTIONAL UNICODE_STRING* pClientRealm, IN ULONG Flags, OUT ULONG* pcbLogonInfo, OUT KERB_S4U_LOGON** ppLogonInfo ) { TNtStatus Status; ULONG cbLogonInfo = 0; WCHAR* pWhere = NULL; KERB_S4U_LOGON* pLogonInfo = NULL; DebugPrintf(SSPI_LOG, "GetKrbS4U2SelfLogonInfo ClientUpn %wZ, ClientRealm %wZ, Flags %#x\n", pClientUpn, pClientRealm, Flags); cbLogonInfo = ROUND_UP_COUNT(sizeof(KERB_S4U_LOGON), sizeof(ULONG_PTR)) + pClientUpn->Length + sizeof(WCHAR) + (pClientRealm ? pClientRealm->Length : 0) + sizeof(WCHAR); pLogonInfo = (KERB_S4U_LOGON*) new CHAR[cbLogonInfo]; Status DBGCHK = pLogonInfo ? STATUS_SUCCESS : STATUS_NO_MEMORY; if (NT_SUCCESS(Status)) { RtlZeroMemory(pLogonInfo, cbLogonInfo); pLogonInfo->MessageType = KerbS4ULogon; pWhere = (PWCHAR) (pLogonInfo + 1); PackUnicodeStringAsUnicodeStringZ(pClientUpn, &pWhere, &pLogonInfo->ClientUpn); if (pClientRealm) { PackUnicodeStringAsUnicodeStringZ(pClientRealm, &pWhere, &pLogonInfo->ClientRealm); } pLogonInfo->Flags = Flags; *ppLogonInfo = pLogonInfo; pLogonInfo = NULL; *pcbLogonInfo = cbLogonInfo; } if (pLogonInfo) { delete [] pLogonInfo; } return Status; } NTSTATUS GetKrbInteractiveLogonInfo( IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pLogonDomainName, IN UNICODE_STRING* pPassword, OUT ULONG* pcbLogonInfo, OUT KERB_INTERACTIVE_LOGON** ppLogonInfo ) { TNtStatus Status; ULONG cbLogonInfo = 0; UCHAR* pWhere = NULL; KERB_INTERACTIVE_LOGON* pLogonInfo = NULL; DebugPrintf(SSPI_LOG, "GetKrbInteractiveLogonInfo UserName %wZ, LogonDomainName %wZ, Password %wZ\n", pUserName, pLogonDomainName, pPassword); cbLogonInfo = ROUND_UP_COUNT(sizeof(KERB_INTERACTIVE_LOGON), sizeof(ULONG_PTR)) + pUserName->Length + pLogonDomainName->Length + pPassword->Length; pLogonInfo = (KERB_INTERACTIVE_LOGON*) new CHAR[cbLogonInfo]; Status DBGCHK = pLogonInfo ? STATUS_SUCCESS : STATUS_NO_MEMORY; if (NT_SUCCESS(Status)) { pLogonInfo->MessageType = KerbInteractiveLogon; pWhere = (PUCHAR) (pLogonInfo + 1); pLogonInfo->UserName.Buffer = (PWSTR) pWhere; pLogonInfo->UserName.MaximumLength = pUserName->Length; pLogonInfo->UserName.Length = pUserName->Length; RtlCopyMemory(pLogonInfo->UserName.Buffer, pUserName->Buffer, pUserName->Length); pWhere += pLogonInfo->UserName.Length; pLogonInfo->LogonDomainName.Buffer = (PWSTR) pWhere; pLogonInfo->LogonDomainName.MaximumLength = pLogonDomainName->Length; pLogonInfo->LogonDomainName.Length = pLogonDomainName->Length; RtlCopyMemory(pLogonInfo->LogonDomainName.Buffer, pLogonDomainName->Buffer, pLogonDomainName->Length); pWhere += pLogonInfo->LogonDomainName.Length; pLogonInfo->Password.Buffer = (PWSTR) pWhere; pLogonInfo->Password.MaximumLength = pPassword->Length; pLogonInfo->Password.Length = pPassword->Length; RtlCopyMemory(pLogonInfo->Password.Buffer, pPassword->Buffer, pPassword->Length); pWhere += pLogonInfo->Password.Length; *ppLogonInfo = pLogonInfo; pLogonInfo = NULL; *pcbLogonInfo = cbLogonInfo; } if (pLogonInfo) { delete [] pLogonInfo; } return Status; } NTSTATUS KrbLsaLogonUser( IN HANDLE hLsa, IN ULONG PackageId, IN SECURITY_LOGON_TYPE LogonType, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN ULONG Flags, OUT HANDLE* phToken ) { TNtStatus Status; NTSTATUS SubStatus = STATUS_UNSUCCESSFUL; PVOID pLogonInfo = NULL; ULONG cbLogonInfo = 0; LSA_STRING Name = {0}; TOKEN_SOURCE SourceContext = {0}; KERB_TICKET_PROFILE* pKerbTicketProfile = NULL; ULONG cbProfileSize = 0; LUID LogonId = {0}; QUOTA_LIMITS Quotas = {0}; DebugPrintf(SSPI_LOG, "KrbLsaLogonUser PackageId %#x, LogonType %#x, " "UserName %wZ, DomainName %wZ, Password %wZ\n", PackageId, LogonType, pUserName, pDomainName, pPassword); switch (LogonType) { case Network: Status DBGCHK = GetKrbS4U2SelfLogonInfo( pUserName, pDomainName, Flags, &cbLogonInfo, (KERB_S4U_LOGON**) &pLogonInfo ); break; case Interactive: case CachedInteractive: case RemoteInteractive: case Unlock: default: Status DBGCHK = GetKrbInteractiveLogonInfo( pUserName, pDomainName, pPassword, &cbLogonInfo, (KERB_INTERACTIVE_LOGON**) &pLogonInfo ); break; } if (NT_SUCCESS(Status)) { SspiPrintHex(SSPI_LOG, TEXT("KrbLsaLogonUser LogonInfo"), cbLogonInfo, pLogonInfo); strncpy( SourceContext.SourceName, "ssptest", sizeof(SourceContext.SourceName) ); NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier); // // Now call LsaLogonUser // RtlInitString( &Name, "ssptest" ); Status DBGCHK = LsaLogonUser( hLsa, &Name, LogonType, PackageId, pLogonInfo, cbLogonInfo, NULL, // no token groups &SourceContext, (VOID**) &pKerbTicketProfile, &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, "TokenHandle %p\n", *phToken); DebugPrintProfileAndQuotas(SSPI_LOG, pKerbTicketProfile, &Quotas); } if (pKerbTicketProfile) { LsaFreeReturnBuffer(pKerbTicketProfile); } return Status; } NTSTATUS MsvLsaLogonUser( IN HANDLE hLsa, IN ULONG PackageId, IN SECURITY_LOGON_TYPE LogonType, IN UNICODE_STRING* pUserName, IN UNICODE_STRING* pDomainName, IN UNICODE_STRING* pPassword, IN UNICODE_STRING* pWorkstation, IN ELogonTypeSubType SubType, OUT HANDLE* phToken ) { TNtStatus Status; NTSTATUS SubStatus = STATUS_UNSUCCESSFUL; PVOID pLogonInfo = NULL; ULONG cbLogonInfo = 0; LSA_STRING Name = {0}; TOKEN_SOURCE SourceContext = {0}; VOID* pProfile = NULL; ULONG cbProfileSize = 0; LUID LogonId = {0}; QUOTA_LIMITS Quotas = {0}; UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH] = {0}; DebugPrintf(SSPI_LOG, "MsvLsaLogonUser PackageId %#x, LogonType %#x, SubType %#x, " "UserName %wZ, DomainName %wZ, Password %wZ, Workstation %wZ\n", PackageId, LogonType, SubType, pUserName, pDomainName, pPassword, pWorkstation); RtlGenRandom(ChallengeToClient, sizeof(ChallengeToClient)); switch (LogonType) { case Network: switch (SubType) { case kNetworkLogonNtlmv1: Status DBGCHK = GetLm20LogonInfoNtlmv1( pUserName, pDomainName, pPassword, pWorkstation, ChallengeToClient, &cbLogonInfo, (MSV1_0_LM20_LOGON **) &pLogonInfo ); break; case kNetworkLogonNtlmv2: Status DBGCHK = GetLm20LogonInfoNtlmv2( pUserName, pDomainName, pPassword, pWorkstation, NULL, // no target name NULL, // no target info ChallengeToClient, &cbLogonInfo, (MSV1_0_LM20_LOGON **) &pLogonInfo ); break; case kSubAuthLogon: default: Status DBGCHK = STATUS_NOT_SUPPORTED; break; } break; case Interactive: case CachedInteractive: case RemoteInteractive: case Unlock: default: Status DBGCHK = GetMsvInteractiveLogonInfo( pUserName, pDomainName, pPassword, &cbLogonInfo, (MSV1_0_INTERACTIVE_LOGON **) &pLogonInfo ); break; } if (NT_SUCCESS(Status)) { strncpy( SourceContext.SourceName, "ssptest", sizeof(SourceContext.SourceName) ); NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier); // // Now call LsaLogonUser // RtlInitString( &Name, "ssptest" ); Status DBGCHK = LsaLogonUser( hLsa, &Name, LogonType, PackageId, pLogonInfo, cbLogonInfo, 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, "TokenHandle %p\n", *phToken); DebugPrintProfileAndQuotas(SSPI_LOG, pProfile, &Quotas); } if (pProfile) { LsaFreeReturnBuffer(pProfile); } return Status; }