/*++

Copyright (c) 1991-1994  Microsoft Corporation

Module Name:

    logclear.c

Abstract:

    Contains functions used to log an event indicating who cleared the log.
    This is only called after the security log has been cleared.

Author:

    Dan Lafferty (danl)     01-July-1994

Environment:

    User Mode -Win32

Revision History:

    01-July-1994    danl & robertre
        Created - Rob supplied the code which I fitted into the eventlog.

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <msaudite.h>
#include <eventp.h>
#include <tstr.h>
#include <winsock2.h>

#define NUM_STRINGS     6


//
// LOCAL FUNCTION PROTOTYPES
//
BOOL
GetUserInfo(
    IN HANDLE Token,
    OUT LPWSTR *UserName,
    OUT LPWSTR *DomainName,
    OUT LPWSTR *AuthenticationId,
    OUT PSID *UserSid
    );

LPWSTR
ElfpGetComputerName(
    VOID
    );

LPWSTR 
GetNameFromIPAddress(
	LPWSTR pszComputerNameFromBinding);

BOOL
IsLocal(
    VOID
    );
    
VOID
ElfpGenerateLogClearedEvent(
    IELF_HANDLE    LogHandle
    )

/*++

Routine Description:

    This function generates an event indicating that the log was cleared.

Arguments:

    LogHandle -- This is a handle to the log that the event is to be placed in.
        It is intended that this function only be called when the SecurityLog
        has been cleared.  So it is expected the LogHandle will always be
        a handle to the security log.

Return Value:

    None -- Either it works or it doesn't.  If it doesn't, there isn't much
            we can do about it.

--*/
{
    LPWSTR  UserName               = NULL;
    LPWSTR  DomainName             = NULL;
    LPWSTR  AuthenticationId       = NULL;
    LPWSTR  ClientUserName         = NULL;
    LPWSTR  ClientDomainName       = NULL;
    LPWSTR  ClientAuthenticationId = NULL;
    PSID    UserSid                = NULL;
    PSID    ClientSid              = NULL;
    LPWSTR  ComputerName           = NULL;
    DWORD   i;
    BOOL    Result;
    HANDLE  Token;
    PUNICODE_STRING StringPtrArray[NUM_STRINGS];
    UNICODE_STRING  StringArray[NUM_STRINGS];
    NTSTATUS        Status;
    LARGE_INTEGER   Time;
    ULONG           EventTime;
    ULONG           LogHandleGrantedAccess;
    UNICODE_STRING  ComputerNameU;
    DWORD           dwStatus;

    //
    // Get information about the Eventlog service (i.e., LocalSystem)
    //
    Result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token);

    if (!Result)
    {
        ELF_LOG1(ERROR,
                 "ElfpGenerateLogClearedEvent: OpenProcessToken failed %d\n",
                 GetLastError());

        return;
    }

    Result = GetUserInfo(Token,
                         &UserName,
                         &DomainName,
                         &AuthenticationId,
                         &UserSid);

    CloseHandle(Token);

    if (!Result)
    {
        ELF_LOG1(ERROR,
                 "ElfpGenerateLogClearedEvent: GetUserInfo failed %d\n",
                 GetLastError());

        return;
    }

    ELF_LOG3(TRACE,
             "ElfpGenerateLogClearedEvent: GetUserInfo returned: \n"
                 "\tUserName         = %ws,\n"
                 "\tDomainName       = %ws,\n"
                 "\tAuthenticationId = %ws\n",
             UserName,
             DomainName,
             AuthenticationId);

    //
    // Now impersonate in order to get the client's information.
    // This call should never fail.
    //
    dwStatus = RpcImpersonateClient(NULL);

    if (dwStatus != RPC_S_OK)
    {
        ELF_LOG1(ERROR,
                 "ElfpGenerateLogClearedEvent: RpcImpersonateClient failed %d\n",
                 dwStatus);
        goto CleanExit;
                 
    }

    Result = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &Token);

    if (Result)
    {
        Result = GetUserInfo(Token,
                             &ClientUserName,
                             &ClientDomainName,
                             &ClientAuthenticationId,
                             &ClientSid);

        CloseHandle(Token);

        if (!Result)
        {
            ELF_LOG1(ERROR,
                     "ElfpGenerateLogClearedEvent: GetUserInfo (call 2) failed %d\n",
                     GetLastError());

            goto CleanExit;
        }
    }
    else
    {
        //
        // We're impersonating here, so this should never
        // happen but just in case, use dashes
        //
        ELF_LOG1(ERROR,
                 "ElfpGenerateLogClearedEvent: OpenThreadToken failed %d\n",
                 GetLastError());

        ASSERT(FALSE);

        ClientUserName = L"-";
        ClientDomainName = L"-";
        ClientAuthenticationId = L"-";
    }

    ELF_LOG3(TRACE,
             "ElfpGenerateLogClearedEvent: GetUserInfo (call 2) returned: \n"
                 "\tUserName         = %ws,\n"
                 "\tDomainName       = %ws,\n"
                 "\tAuthenticationId = %ws\n",
             ClientUserName,
             ClientDomainName,
             ClientAuthenticationId);

    RtlInitUnicodeString(&StringArray[0], UserName);
    RtlInitUnicodeString(&StringArray[1], DomainName);
    RtlInitUnicodeString(&StringArray[2], AuthenticationId);
    RtlInitUnicodeString(&StringArray[3], ClientUserName);
    RtlInitUnicodeString(&StringArray[4], ClientDomainName);
    RtlInitUnicodeString(&StringArray[5], ClientAuthenticationId);

    //
    // Create an array of pointers to UNICODE_STRINGs.
    //
    for (i = 0; i < NUM_STRINGS; i++)
    {
        StringPtrArray[i] = &StringArray[i];
    }

    //
    // Generate the time of the event. This is done on the client side
    // since that is where the event occurred.
    //
    NtQuerySystemTime(&Time);
    RtlTimeToSecondsSince1970(&Time, &EventTime);

    //
    // Generate the ComputerName of the client.
    // We have to do this in the client side since this call may be
    // remoted to another server and we would not necessarily have
    // the computer name there.
    //

    ComputerName = ElfpGetComputerName();
    if(ComputerName == NULL)
    	goto CleanExit;				// ElfpGetComputerName dumps error msg
    RtlInitUnicodeString(&ComputerNameU, ComputerName);

    //
    // Since all processes other than LSA are given read-only access
    // to the security log, we have to explicitly give the current
    // process the right to write the "Log cleared" event
    //
    LogHandleGrantedAccess    = LogHandle->GrantedAccess;
    LogHandle->GrantedAccess |= ELF_LOGFILE_WRITE;

    Status = ElfrReportEventW (
                 LogHandle,                         // Log Handle
                 EventTime,                         // Time
                 EVENTLOG_AUDIT_SUCCESS,            // Event Type
                 (USHORT)SE_CATEGID_SYSTEM,         // Event Category
                 SE_AUDITID_AUDIT_LOG_CLEARED,      // EventID
                 NUM_STRINGS,                       // NumStrings
                 0,                                 // DataSize
                 &ComputerNameU,                    // ComputerNameU
                 UserSid,                           // UserSid
                 StringPtrArray,                    // *Strings
                 NULL,                              // Data
                 0,                                 // Flags
                 NULL,                              // RecordNumber
                 NULL);                             // TimeWritten

    LogHandle->GrantedAccess = LogHandleGrantedAccess;

CleanExit:

    //
    // We only come down this path if we know for sure that these
    // first three have been allocated.
    //
    ElfpFreeBuffer(UserName);
    ElfpFreeBuffer(DomainName);
    ElfpFreeBuffer(AuthenticationId);

    ElfpFreeBuffer(UserSid);
    ElfpFreeBuffer(ClientUserName);
    ElfpFreeBuffer(ClientDomainName);
    ElfpFreeBuffer(ClientAuthenticationId);
    ElfpFreeBuffer(ClientSid);
    ElfpFreeBuffer(ComputerName);
    //
    // Stop impersonating
    //
    dwStatus = RpcRevertToSelf();
    
    if (dwStatus != RPC_S_OK)
    {
        ELF_LOG1(ERROR,
                 "ElfpGenerateLogClearedEvent: RpcRevertToSelf failed %d\n",
                 GetLastError());
    }
}


BOOL
GetUserInfo(
    IN HANDLE  Token,
    OUT LPWSTR *UserName,
    OUT LPWSTR *DomainName,
    OUT LPWSTR *AuthenticationId,
    OUT PSID   *UserSid
    )

/*++

Routine Description:

    This function gathers information about the user identified with the
    token.

Arguments:
    Token - This token identifies the entity for which we are gathering
        information.
    UserName - This is the entity's user name.
    DomainName -  This is the entity's domain name.
    AuthenticationId -  This is the entity's Authentication ID.
    UserSid - This is the entity's SID.

NOTE:
    Memory is allocated by this routine for UserName, DomainName,
    AuthenticationId, and UserSid.  It is the caller's responsibility to
    free this memory.

Return Value:
    TRUE - If the operation was successful.  It is possible that
        UserSid did not get allocated in the successful case.  Therefore,
        the caller should test prior to freeing.
    FALSE - If unsuccessful.  No memory is allocated in this case.


--*/
{
    PTOKEN_USER      Buffer      = NULL;
    LPWSTR           Domain      = NULL;
    LPWSTR           AccountName = NULL;
    SID_NAME_USE     Use;
    BOOL             Result;
    DWORD            RequiredLength;
    DWORD            AccountNameSize;
    DWORD            DomainNameSize;
    TOKEN_STATISTICS Statistics;
    WCHAR            LogonIdString[256];
    DWORD            Status = ERROR_SUCCESS;


    *UserSid = NULL;

    Result = GetTokenInformation(Token, TokenUser, NULL, 0, &RequiredLength);

    if (!Result)
    {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
            ELF_LOG1(TRACE,
                     "GetUserInfo: GetTokenInformation needs %d bytes\n",
                     RequiredLength);

            Buffer = ElfpAllocateBuffer(RequiredLength);

            if (Buffer == NULL)
            {
                ELF_LOG0(ERROR,
                         "GetUserInfo: Unable to allocate memory for token\n");

                return FALSE;
            }

            Result = GetTokenInformation(Token,
                                         TokenUser,
                                         Buffer,
                                         RequiredLength,
                                         &RequiredLength);

            if (!Result)
            {
                ELF_LOG1(ERROR,
                         "GetUserInfo: GetTokenInformation (call 2) failed %d\n",
                         GetLastError());

                return FALSE;
            }
        }
        else
        {
            ELF_LOG1(ERROR,
                     "GetUserInfo: GetTokenInformation (call 1) failed %d\n",
                     GetLastError());

            return FALSE;
        }
    }

    AccountNameSize = 0;
    DomainNameSize  = 0;

    Result = LookupAccountSidW(L"",
                               Buffer->User.Sid,
                               NULL,
                               &AccountNameSize,
                               NULL,
                               &DomainNameSize,
                               &Use);

    if (!Result)
    {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
            AccountName = ElfpAllocateBuffer((AccountNameSize + 1) * sizeof(WCHAR));
            Domain = ElfpAllocateBuffer((DomainNameSize + 1) * sizeof(WCHAR));

            if (AccountName == NULL)
            {
                ELF_LOG1(ERROR,
                         "GetUserInfo: Unable to allocate %d bytes for AccountName\n",
                         AccountNameSize);

                goto ErrorCleanup;
            }

            if (Domain == NULL)
            {
                ELF_LOG1(ERROR,
                         "GetUserInfo: Unable to allocate %d bytes for Domain\n",
                         DomainNameSize);

                goto ErrorCleanup;
            }

            Result = LookupAccountSidW(L"",
                                       Buffer->User.Sid,
                                       AccountName,
                                       &AccountNameSize,
                                       Domain,
                                       &DomainNameSize,
                                       &Use
                                       );
            if (!Result)
            {
                ELF_LOG1(ERROR,
                         "GetUserInfo: LookupAccountSid (call 2) failed %d\n",
                         GetLastError());

                goto ErrorCleanup;
            }
        }
        else
        {
            ELF_LOG1(ERROR,
                     "GetUserInfo: LookupAccountsid (call 1) failed %d\n",
                     GetLastError());

            goto ErrorCleanup;
        }
    }
    else
    {
        ELF_LOG0(ERROR,
                 "GetUserInfo: LookupAccountSid succeeded unexpectedly\n");

        goto ErrorCleanup;
    }

    ELF_LOG2(TRACE,
             "GetUserInfo: Name = %ws\\%ws\n",
             Domain,
             AccountName);

    Result = GetTokenInformation(Token,
                                 TokenStatistics,
                                 &Statistics,
                                 sizeof(Statistics),
                                 &RequiredLength);

    if (!Result)
    {
        ELF_LOG1(ERROR,
                 "GetUserInfo: GetTokenInformation (call 3) failed %d\n",
                 GetLastError());

        goto ErrorCleanup;
    }

    swprintf(LogonIdString,
             L"(0x%X,0x%X)",
             Statistics.AuthenticationId.HighPart,
             Statistics.AuthenticationId.LowPart);

    ELF_LOG1(TRACE,
             "GetUserInfo: LogonIdString = %ws\n",
             LogonIdString);

    *AuthenticationId = ElfpAllocateBuffer(WCSSIZE(LogonIdString));

    if (*AuthenticationId == NULL)
    {
        ELF_LOG0(ERROR,
                 "GetUserInfo: Unable to allocate memory for AuthenticationId\n");

        goto ErrorCleanup;
    }

    wcscpy(*AuthenticationId, LogonIdString);

    //
    // Return accumulated information
    //
    *UserSid = ElfpAllocateBuffer(GetLengthSid(Buffer->User.Sid));

    if (*UserSid == NULL)
    {
        ELF_LOG0(ERROR,
                 "GetUserInfo: Unable to allocate memory for UserSid\n");

        goto ErrorCleanup;
    }

    Result = CopySid(GetLengthSid(Buffer->User.Sid),
                     *UserSid,
                     Buffer->User.Sid);

    ElfpFreeBuffer(Buffer);

    *DomainName = Domain;
    *UserName   = AccountName;

    return TRUE;

ErrorCleanup:

    ElfpFreeBuffer(Buffer);
    ElfpFreeBuffer(Domain);
    ElfpFreeBuffer(AccountName);
    ElfpFreeBuffer(*UserSid);
    ElfpFreeBuffer(*AuthenticationId);

    return FALSE;
}



LPWSTR 
GetNameFromIPAddress(
	LPWSTR pszComputerNameFromBinding)
/*++

Routine Description:

    Examines a string and determines if it looks like an ip address.  If it
    does, then it converts it to a fqdn

Arguments:

    Machine name:  Could be xxx.xxx.xxx.xxx or anything else!

Return Value:

    If successful, returns a fully qualified domain name which the caller
    will free.  Returns NULL if there are any problems.


--*/
{

	LPWSTR pComputerName = NULL;
	DWORD dwAddr;
	char cName[17];  // should be plenty of room
	size_t NumConv;
	HOSTENT  FAR * pEnt;
	DWORD dwSize;
	WORD wVersionRequested;
	WSADATA wsaData;
	int error;
	
	NumConv = wcstombs(cName, pszComputerNameFromBinding, 16);  
	if(NumConv == -1 || NumConv == 0)
	    return NULL;

	// convert the string into a network order dword representation
	
	dwAddr = inet_addr(cName);
	if(dwAddr == INADDR_NONE)
		return NULL;

	//  initialize sockets.

	wVersionRequested = MAKEWORD( 2, 2 );
 
    error = WSAStartup( wVersionRequested, &wsaData );
	if(error != 0)
	{
	   	ELF_LOG1(TRACE,
           "GetNameFromIPAddress: failed to initialize sockets, error = 0x%x\n", error);
		return NULL;
	}

		
	pEnt = gethostbyaddr((char FAR *)&dwAddr, 4, PF_INET);
	if(pEnt == NULL || pEnt->h_name == NULL)
	{
	   	ELF_LOG1(TRACE,
                 "GetNameFromIPAddress: failed gethostbyaddr, error = 0x%x\n",
                 WSAGetLastError());
		WSACleanup();
		return NULL;
	}
	dwSize = strlen(pEnt->h_name) + 1 ;
	pComputerName = ElfpAllocateBuffer(2*dwSize);
	if(pComputerName == NULL)
	{
	   	ELF_LOG0(ERROR,
                 "GetNameFromIPAddress: failed trying to allocate memory\n");
	    WSACleanup();
		return NULL;
	}
	pComputerName[0] = 0;
	mbstowcs(pComputerName, pEnt->h_name, dwSize);
    WSACleanup();
	return pComputerName;
}

BOOL
IsLocal(
    VOID
    )
/*++

Routine Description:

    Determines if the caller is local. 

Arguments:

    NONE

Return Value:

    TRUE if definately local.


--*/
{
    UINT            LocalFlag;
    RPC_STATUS      RpcStatus;
    RpcStatus = I_RpcBindingIsClientLocal(0, &LocalFlag);

    if( RpcStatus != RPC_S_OK ) 
    {
        ELF_LOG1(ERROR,
                 "IsLocal: I_RpcBindingIsClientLocal failed %d\n",
                 RpcStatus);
        return FALSE;
    }
    if(LocalFlag == 0)
        return FALSE;
    else
    	return TRUE;
}

LPWSTR
ElfpGetComputerName(
    VOID
    )

/*++

Routine Description:

    This routine gets the LPWSTR name of the computer. 

Arguments:

    NONE

Return Value:

    Returns a pointer to the computer name, or a NULL.  Note that the caller
    is expected to free this via 


--*/
{
    RPC_STATUS      RpcStatus;
    handle_t hServerBinding = NULL;
    LPWSTR pszBinding = NULL;
    LPWSTR pszComputerNameFromBinding = NULL;
	LPWSTR pszComputerName = NULL;
	DWORD dwSize;
	BOOL bOK;

//      Check if the connection is local.  If it is, then just 
//      call GetComputerName

	if(IsLocal())
	{
		dwSize = MAX_COMPUTERNAME_LENGTH + 1 ;
		pszComputerName = ElfpAllocateBuffer(2*dwSize);
		if(pszComputerName == NULL)
		{
        	ELF_LOG0(ERROR,
                 "ElfpGetComputerName: failed trying to allocate memory\n");
			return NULL;
		}
		bOK = GetComputerNameW(pszComputerName, &dwSize);
		if(bOK == FALSE)
		{
			ElfpFreeBuffer(pszComputerName);
        	ELF_LOG1(ERROR,
                 "ElfpGetComputerName: failed calling GetComputerNameW, last error 0x%x\n",
                  GetLastError());
            return NULL;    
		}
		else
			return pszComputerName;
	}

    RpcStatus = RpcBindingServerFromClient( NULL, &hServerBinding );
    if( RpcStatus != RPC_S_OK ) 
    {
        ELF_LOG1(ERROR,
                 "ElfpGetComputerName: RpcBindingServerFromClient failed %d\n",
                 RpcStatus);
        return NULL;
    }

	// This gets us something like "ncacn_np:xxx.xxx.xxx.xxx" or 
	// "ncacn_np:localMachine"
	
    RpcStatus = RpcBindingToStringBinding( hServerBinding, &pszBinding );
    if( RpcStatus != ERROR_SUCCESS )
    {
        ELF_LOG1(ERROR,
                 "ElfpGetComputerName: RpcBindingToStringBinding failed %d\n",
                 RpcStatus);
        goto CleanExitGetCompName;
    } 

    // Acquire just the network address.  That will be something like
    // "xxx.xxx.xxx.xxx" or "mymachine"

    RpcStatus = RpcStringBindingParse( pszBinding,
                                                NULL,
                                                NULL,
                                                &pszComputerNameFromBinding,
                                                NULL,
                                                NULL );
    if( RpcStatus != ERROR_SUCCESS || pszComputerNameFromBinding == NULL)
    {
        ELF_LOG1(ERROR,
                 "ElfpGetComputerName: RpcStringBindingParse failed %d\n",
                 RpcStatus);
        goto CleanExitGetCompName;
    }

    // sometimes the name is an ip address.  If it is, then the following determines
    // that and returns the right string.

	pszComputerName = GetNameFromIPAddress(pszComputerNameFromBinding);                                                
	if(pszComputerName == NULL)
	{
		dwSize = wcslen(pszComputerNameFromBinding) + 1;
		pszComputerName = ElfpAllocateBuffer(2*dwSize);
		if(pszComputerName == NULL)
		{
        	ELF_LOG0(ERROR,
                 "ElfpGetComputerName: failed trying to allocate memory\n");
		}
		else
			wcscpy(pszComputerName, pszComputerNameFromBinding);
	}

CleanExitGetCompName:
	if(hServerBinding)
		RpcStatus = RpcBindingFree(&hServerBinding);
	if(pszBinding)
		RpcStatus = RpcStringFree(&pszBinding);
	if(pszComputerNameFromBinding)
		RpcStatus = RpcStringFree(&pszComputerNameFromBinding);
    return pszComputerName;
}