/*++ Copyright (c) 2000 Microsoft Corporation Module Name: lsa.c Abstract: Implements security network logon Author: Ahmed Mohamed (ahmedm) 1-Feb-2000 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include //int lstrlenW(char *); typedef int BOOL; typedef unsigned int UINT; typedef unsigned int *PUINT; typedef unsigned char *LPBYTE; #define LocalLSAInit() (LsaHandle != NULL) #ifdef LSA_AUDIT_FLAG int LsaAuditFlag = 0; #endif #define BUF_SIZ 512 void replstar( IN char * starred, OUT LPWSTR UnicodeOut ) /*++ replstar Routine Description: replaces the '*' in the string with either spaces or NULL if it's the only memeber of the string. Used by parse(). Converts the resultant string to unicode. Arguments: char * starred - Return Value: void - Warnings: --*/ { char *cp; STRING AnsiString; UNICODE_STRING UnicodeString; if ( !strcmp(starred,"*") ) { *starred = '\0'; } else { for ( cp = starred; *cp; ++cp ) if (*cp == '*') *cp = ' '; } // // Convert the result to unicode // AnsiString.Buffer = starred; AnsiString.Length = AnsiString.MaximumLength = (USHORT) strlen( starred ); UnicodeString.Buffer = UnicodeOut; UnicodeString.Length = 0; UnicodeString.MaximumLength = BUF_SIZ * sizeof(WCHAR); (VOID) RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE ); return; } VOID NlpPutString( IN PUNICODE_STRING OutString, IN PUNICODE_STRING InString, IN PUCHAR *Where ) /*++ NlpPutString Routine Description: This routine copies the InString string to the memory pointed to by the Where parameter, and fixes the OutString string to point to that new copy. Parameters: OutString - A pointer to a destination NT string InString - A pointer to an NT string to be copied Where - A pointer to space to put the actual string for the OutString. The pointer is adjusted to point to the first byte following the copied string. Return Values: None. --*/ { ASSERT( OutString != NULL ); ASSERT( InString != NULL ); ASSERT( Where != NULL && *Where != NULL); if ( InString->Length > 0 ) { OutString->Buffer = (PWCH) *Where; OutString->MaximumLength = InString->Length; *Where += OutString->MaximumLength; RtlCopyString( (PSTRING) OutString, (PSTRING) InString ); } else { RtlInitUnicodeString(OutString, NULL); } return; } BOOL LsapLogonNetwork( IN HANDLE LsaHandle, IN ULONG AuthenticationPackage, IN LPWSTR Username, IN PUCHAR ChallengeToClient, IN PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponse, IN UINT cbChallengeResponse, IN LPWSTR Domain, OUT PLUID LogonId, OUT PHANDLE TokenHandle ) /*++ LogonNetwork Routine Description: Logs a user onto the network Arguments: IN LPWSTR Username - self explanatory IN ChallengeToClient - The challenge sent to the client IN ChallengeResponse - The response sent from the client IN LPWSTR Domain - Logon Domain Name OUT PLUID LogonId - Unique generated logon id OUT PHANDLE TokenHandle - handle to the logon token Return Value: BOOL - Warnings: --*/ { NTSTATUS Status; UNICODE_STRING TempString; UNICODE_STRING TempString2; UNICODE_STRING OriginName; PMSV1_0_LM20_LOGON Auth; PCHAR Auth1[MSV1_0_CHALLENGE_LENGTH + BUF_SIZ*2]; PUCHAR Strings; PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer; ULONG ProfileBufferSize; NTSTATUS SubStatus; TOKEN_SOURCE SourceContext; QUOTA_LIMITS QuotaLimits; /* * Fill in the Authentication structure. */ Auth = (PMSV1_0_LM20_LOGON) Auth1; Strings = (PUCHAR)(Auth + 1); Auth->MessageType = MsV1_0Lm20Logon; RtlMoveMemory( Auth->ChallengeToClient, ChallengeToClient, MSV1_0_CHALLENGE_LENGTH ); /* Init Strings * username */ RtlInitUnicodeString( &TempString, Username ); NlpPutString( &Auth->UserName, &TempString, &Strings ); /* * workstation name */ RtlInitUnicodeString( &TempString, L"NetQFS" ); NlpPutString( &Auth->Workstation, &TempString, &Strings ); /* * Challenge Response */ Auth->CaseSensitiveChallengeResponse.Length = 0; Auth->CaseSensitiveChallengeResponse.MaximumLength = 0; Auth->CaseSensitiveChallengeResponse.Buffer = NULL; #ifdef OLD RtlInitUnicodeString( (PUNICODE_STRING)&TempString2, (PCWSTR)ChallengeResponse ); #else TempString2.Buffer = (PWSTR)ChallengeResponse; TempString2.Length = (USHORT)cbChallengeResponse; TempString2.MaximumLength = TempString2.Length; #endif if( TempString2.Length > 24 ) { TempString2.Length = 24; } NlpPutString( (PUNICODE_STRING)&Auth->CaseInsensitiveChallengeResponse, (PUNICODE_STRING)&TempString2, &Strings ); /* * domain */ RtlInitUnicodeString( &TempString, Domain ); NlpPutString( &Auth->LogonDomainName, &TempString, &Strings ); RtlInitUnicodeString( &OriginName, L"NetQFS" ); Status = LsaLogonUser( LsaHandle, (PSTRING)&OriginName, Network, AuthenticationPackage, // LATER? AuthenticationPackage | LSA_CALL_LICENSE_SERVER, Auth, (ULONG)(Strings - (PUCHAR)Auth), NULL, &SourceContext, (PVOID *)&ProfileBuffer, &ProfileBufferSize, LogonId, TokenHandle, &QuotaLimits, &SubStatus ); if ( !NT_SUCCESS( Status ) ) { extern void WINAPI debug_log(char *,...); debug_log("Logon failed %x\n",Status); // LSA Can't be trusted to not scrog our variables *TokenHandle = NULL; return( FALSE ); } LsaFreeReturnBuffer( ProfileBuffer ); return( TRUE ); } BOOL LsapChallenge( HANDLE LsaHandle, ULONG AuthenticationPackage, UCHAR *ChallengeToClient ) /*++ Challenge Routine Description: get a challenge Arguments: OUT ChallengeToClient - Returns the challenge to send to the client Return Value: NTSTATUS - Warnings: --*/ { NTSTATUS Status; NTSTATUS ProtocolStatus; ULONG ResponseSize; MSV1_0_LM20_CHALLENGE_REQUEST Request; PMSV1_0_LM20_CHALLENGE_RESPONSE Response; /* * Fill in the Authentication structure. */ Request.MessageType = MsV1_0Lm20ChallengeRequest; Status = LsaCallAuthenticationPackage ( LsaHandle, AuthenticationPackage, &Request, sizeof(Request), (PVOID *)&Response, &ResponseSize, &ProtocolStatus ); if ( !NT_SUCCESS( Status ) || !NT_SUCCESS( ProtocolStatus) ) { printf("ChallengeRequest failed %x\n",Status); return( FALSE ); } RtlMoveMemory( ChallengeToClient, Response->ChallengeToClient, MSV1_0_CHALLENGE_LENGTH ); LsaFreeReturnBuffer( Response ); return( TRUE ); } BOOL LsaGetChallenge( HANDLE LsaHandle, ULONG AuthenticationPackage, LPBYTE lpChallenge, UINT cbSize, PUINT lpcbChallengeSize ) { #ifdef LSA_AUDIT_FLAG if (LsaAuditFlag == TRUE) { memset(lpChallenge, 0, MSV1_0_CHALLENGE_LENGTH); *lpcbChallengeSize = MSV1_0_CHALLENGE_LENGTH; return TRUE; } #endif if( LocalLSAInit() ) { *lpcbChallengeSize = MSV1_0_CHALLENGE_LENGTH; return LsapChallenge( LsaHandle, AuthenticationPackage, lpChallenge ); } else { return( FALSE ); } } BOOL LsaValidateLogon( HANDLE LsaHandle, ULONG AuthenticationPackage, LPBYTE lpChallenge, UINT cbChallengeSize, LPBYTE lpResponse, UINT cbResponseSize, LPSTR lpszUserName, LPSTR lpszDomainName, LUID *pLogonId, PHANDLE phLogonToken ) { WCHAR wcUser[ BUF_SIZ ]; WCHAR wcDomain[ BUF_SIZ ]; BOOL nlRet; DWORD sz = BUF_SIZ; #ifdef LSA_AUDIT_FLAG if (LsaAuditFlag == 1) { pLogonId->LowPart = 10; pLogonId->HighPart = 0; *phLogonToken = INVALID_HANDLE_VALUE; return TRUE; } #endif if (GetUserName((LPSTR)wcUser, &sz)) { if (lpszUserName != NULL && *lpszUserName != '\0' && _stricmp(lpszUserName, (LPSTR)wcUser) && _stricmp(lpszUserName, "administrator")) { return FALSE; } } if( !LocalLSAInit() ) { return( FALSE ); } replstar( lpszUserName, wcUser ); replstar( lpszDomainName, wcDomain ); nlRet = LsapLogonNetwork( LsaHandle, AuthenticationPackage, wcUser, lpChallenge, (PMSV1_0_GETCHALLENRESP_RESPONSE)lpResponse, cbResponseSize, wcDomain, pLogonId, phLogonToken ); return( nlRet ); } BOOL LsaGetChallengeResponse( HANDLE LsaHandle, ULONG AuthenticationPackage, LUID LogonId, LPSTR lpszPasswordK1, int cbPasswordK1, LPSTR lpszChallenge, int cbChallenge, int *pcbPasswordK1, BOOL *pbHasPasswordK1 ) { NTSTATUS Status; NTSTATUS ProtocolStatus; ULONG ResponseSize; PMSV1_0_GETCHALLENRESP_RESPONSE Response; PMSV1_0_GETCHALLENRESP_REQUEST Request; PCHAR Auth1[BUF_SIZ]; PUCHAR Strings; if( !LocalLSAInit() ) { *pbHasPasswordK1 = FALSE; return( FALSE ); } Request = (PMSV1_0_GETCHALLENRESP_REQUEST) Auth1; Request->MessageType = MsV1_0Lm20GetChallengeResponse; Request->ParameterControl = 0; Request->ParameterControl |= USE_PRIMARY_PASSWORD; Request->LogonId = LogonId; Strings = (PUCHAR)(Request + 1); RtlMoveMemory( Request->ChallengeToClient, lpszChallenge, cbChallenge ); RtlInitUnicodeString( &Request->Password, NULL ); Status = LsaCallAuthenticationPackage ( LsaHandle, AuthenticationPackage, Request, sizeof(MSV1_0_GETCHALLENRESP_REQUEST), (PVOID *)&Response, &ResponseSize, &ProtocolStatus ); if ( !NT_SUCCESS( Status ) || !NT_SUCCESS( ProtocolStatus) ) { return( FALSE ); } *pcbPasswordK1 = (Response)->CaseInsensitiveChallengeResponse.Length; memcpy( lpszPasswordK1, (Response)->CaseInsensitiveChallengeResponse.Buffer, (Response)->CaseInsensitiveChallengeResponse.Length ); *pbHasPasswordK1 = TRUE; LsaFreeReturnBuffer( Response ); return( TRUE ); } #ifdef QON_TCB_REQUIRED // When we have support for untrusted on REQUEST we disable this. Till then we need TCB. // I am disabling this since the we don't need TCB anymore. Note if we // want to run in win2k then we must have TCB and this code path reenabled. NTSTATUS LsaInit(HANDLE *pLsaHandle, ULONG *pAuthenticationPackage) { char MyName[MAX_PATH]; char * ModuleName; STRING LogonProcessName; STRING PackageName; ULONG dummy; NTSTATUS Status; NTSTATUS TempStatus; BOOLEAN WasEnabled; HANDLE LsaHandle = NULL; ULONG AuthenticationPackage; BOOLEAN fThread = FALSE; // did we enable privilege in thread token? BOOL fReverted = FALSE; // did we RevertToSelf() during call? HANDLE hPreviousToken = NULL; #ifdef LSA_AUDIT_FLAG if (LsaAuditFlag == TRUE) { *pLsaHandle = 0; *pAuthenticationPackage = 0; return STATUS_SUCCESS; } #endif // // three SeTcbPrivilege scenarios: // 1. present in process token, thread not impersonating. // 2. present in process token, thread is impersonating. // 3. present in thread token. // // // try in this order: // process token (original method). // if thread impersonating, thread token // if thread impersonating, process token after reverting. // Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &WasEnabled); if (!NT_SUCCESS(Status)) { TempStatus = NtOpenThreadToken( NtCurrentThread(), TOKEN_IMPERSONATE, FALSE, &hPreviousToken ); if( !NT_SUCCESS(TempStatus) ) { // // retry with accesscheck against process. // if( TempStatus != STATUS_ACCESS_DENIED ) goto Cleanup; TempStatus = NtOpenThreadToken( NtCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hPreviousToken ); if( !NT_SUCCESS(TempStatus) ) goto Cleanup; } // // thread token is present. // first, try enabling the privilege in the thread token. // fThread = TRUE; Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, fThread, &WasEnabled); if( !NT_SUCCESS(Status) ) { HANDLE NewToken = NULL; // // if that fails, try reverting and enabling privilege in process token. // TempStatus = NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &NewToken, sizeof(NewToken) ); if( !NT_SUCCESS(TempStatus) ) goto Cleanup; fThread = FALSE; fReverted = TRUE; Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, fThread, &WasEnabled); if( !NT_SUCCESS(Status) ) goto Cleanup; } } GetModuleFileNameA(NULL, MyName, MAX_PATH); ModuleName = strrchr(MyName, '\\'); if (!ModuleName) { ModuleName = MyName; } // // Hookup to the LSA and locate our authentication package. // RtlInitString(&LogonProcessName, ModuleName); Status = LsaRegisterLogonProcess( &LogonProcessName, &LsaHandle, &dummy ); // // Turn off the privilege now. // if( !WasEnabled ) { (VOID) RtlAdjustPrivilege(SE_TCB_PRIVILEGE, FALSE, fThread, &WasEnabled); } if (!NT_SUCCESS(Status)) { LsaHandle = NULL; goto Cleanup; } // // Connect with the MSV1_0 authentication package // RtlInitString(&PackageName, MSV1_0_PACKAGE_NAME); //"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"); Status = LsaLookupAuthenticationPackage ( LsaHandle, &PackageName, &AuthenticationPackage ); if (!NT_SUCCESS(Status)) { goto Cleanup; } *pLsaHandle = LsaHandle; *pAuthenticationPackage = AuthenticationPackage; Cleanup: if( hPreviousToken ) { if( fReverted ) { // // put old token back... // (VOID) NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &hPreviousToken, sizeof(hPreviousToken) ); } NtClose( hPreviousToken ); } if( !NT_SUCCESS(Status) ) { if( LsaHandle ) { (VOID) LsaDeregisterLogonProcess( LsaHandle ); LsaHandle = NULL; } } return Status; } #else NTSTATUS LsaInit(HANDLE *pLsaHandle, ULONG *pAuthenticationPackage) { char MyName[MAX_PATH]; char * ModuleName; STRING LogonProcessName; STRING PackageName; ULONG dummy; NTSTATUS Status; NTSTATUS TempStatus; BOOLEAN WasEnabled; HANDLE LsaHandle; ULONG AuthenticationPackage; BOOLEAN fThread = FALSE; // did we enable privilege in thread token? BOOL fReverted = FALSE; // did we RevertToSelf() during call? HANDLE hPreviousToken = NULL; // // Connect with LSA process // Status = LsaConnectUntrusted(&LsaHandle); if (!NT_SUCCESS(Status)) return Status; // // Connect with the MSV1_0 authentication package // RtlInitString(&PackageName, MSV1_0_PACKAGE_NAME); //"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"); Status = LsaLookupAuthenticationPackage ( LsaHandle, &PackageName, &AuthenticationPackage ); if (NT_SUCCESS(Status)) { *pLsaHandle = LsaHandle; *pAuthenticationPackage = AuthenticationPackage; } else { (VOID) LsaDeregisterLogonProcess( LsaHandle ); } return Status; } #endif