Click here to Skip to main content
15,892,927 members
Articles / Programming Languages / C++

Enumerating Logon Sessions

Rate me:
Please Sign up or sign in to vote.
4.92/5 (32 votes)
21 Jun 2004CPOL23 min read 217.5K   4.6K   80  
An article on enumerating logon sessions, specifically interactive logon sessions on NT based operating systems.
/*
Copyright 2004 by Stefan Kuhr
*/

#include <windows.h>
#include <tchar.h>
#include <ntsecapi.h>
#include <psapi.h>

#include "logonsessiondata.h"
#include "hlpfuncs.h"

/// local prototypes:
static DWORD SafeNo_ERROR_MORE_DATA_Error(DWORD dwLastErr);

#define GET_STRING_OR_BAIL_OUT(sz, szLSA)  {sz = GetStringFromLsaUnicodeString(szLSA); if (!sz) return FALSE;}

CLogonSessionData::CLogonSessionData(void) : m_szUserName(NULL), m_szLogonDomain(NULL), m_szAuthenticationPackage(NULL), 
                                    m_ulLogonType(0L), m_ulSession(0L), m_szUserSID(NULL), m_dwFlags(CLogonSessionData::VOID_STATE)


{
    m_liLogonTime.QuadPart = 0;
    m_Luid.HighPart = m_Luid.LowPart = 0;
}

CLogonSessionData::~CLogonSessionData(void)    
{
    if (m_szUserName)
        LocalFree(m_szUserName);
    if (m_szLogonDomain)
        LocalFree(m_szLogonDomain);
    if (m_szAuthenticationPackage)
        LocalFree(m_szAuthenticationPackage);
    if (m_szUserSID)
        LocalFree(m_szUserSID);
}

CLogonSessionData::CLogonSessionData(const CLogonSessionData &me)
{
    //copy ctor
    *this = me;
}




LPWSTR CopyString (LPCWSTR sz)
{
    LPWSTR szResult = NULL;    
    if (sz && (szResult = (LPWSTR)LocalAlloc(LPTR, (wcslen(sz)+1)*sizeof(wchar_t)))!=NULL)
        wcscpy(szResult, sz);

    return szResult;
}

CLogonSessionData & CLogonSessionData::operator = (const CLogonSessionData &me)
{
    if (this!=&me) // prevent self-assignment
    {
        m_szUserName = CopyString(me.m_szUserName);
        m_szLogonDomain = CopyString(me.m_szLogonDomain);
        m_szAuthenticationPackage = CopyString(me.m_szAuthenticationPackage);
        m_szUserSID = CopyString(me.m_szUserSID);
        m_ulLogonType = me.m_ulLogonType;
        m_ulSession = me.m_ulSession;
        m_liLogonTime = me.m_liLogonTime;
        m_dwFlags = me.m_dwFlags;
        m_Luid = me.m_Luid;
    }

    return *this;
}





class CCloseUserSid
{
    PSID m_pSid;    

public:
    CCloseUserSid(PSID psid): m_pSid(psid){}
    ~CCloseUserSid(void)
    {
        if (m_pSid)
            FreeUserSid(m_pSid);
    };
};





BOOL CLogonSessionData::Initialize(HANDLE hToken, PLUID pLogonId, PWTS_PROCESS_INFO pWtsInfo)
{
    PSID pUserSid = NULL;
    
    if (!hToken || !pLogonId)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (!ObtainUserSid(hToken, &pUserSid)|| !pUserSid)
        return FALSE;

    if (pWtsInfo && !EqualSid(pUserSid, pWtsInfo->pUserSid))
    {
        /*
        Duh!?! SIDs from WTSAPI32 and from opening the process with the 
        PID from PSAPI do not match? THis can only mean, that a PID has been
        reused for a new process with th same process name but under a 
        different user account. This is probably a very rare suituation, but
        anyway we have to deal with this and must not continue.
        We have to fail because the result from psapi's EnumProcesses
        and wtsapi32's EnumerateProcesses don't match. We fake a GLE of 
        ERROR_CAN_NOT_COMPLETE in the hope, this is meaningful to the caller.
        */
        SetLastError(ERROR_CAN_NOT_COMPLETE);
        return FALSE;
    }

    CCloseUserSid sdc(pUserSid);
    DWORD dwNameSize = 0L, dwDomainSize = 0L;
    SID_NAME_USE snu;


    if (!LookupAccountSid(NULL, pUserSid, NULL, &dwNameSize, NULL, &dwDomainSize, &snu)
        && ERROR_INSUFFICIENT_BUFFER==GetLastError())
    {
        m_szUserName = (LPWSTR)LocalAlloc(LPTR, dwNameSize*sizeof(TCHAR));
        if (!m_szUserName)
            return FALSE;

        m_szLogonDomain  = (LPWSTR)LocalAlloc(LPTR, dwDomainSize*sizeof(TCHAR));
        if (!m_szLogonDomain)
            return FALSE;

        if (!LookupAccountSid(NULL, pUserSid, m_szUserName, &dwNameSize, m_szLogonDomain, &dwDomainSize, &snu))
            return FALSE;
    }
    else
        return FALSE;


    DWORD dwSidBufSize = 0L;    
    if (!GetTextualSid(pUserSid, NULL, &dwSidBufSize) && GetLastError()==ERROR_INSUFFICIENT_BUFFER)
    {
        // okay, a valid SID:
        m_szUserSID = (LPWSTR) LocalAlloc(LPTR, dwSidBufSize);
        if (m_szUserSID)
            GetTextualSid(pUserSid, m_szUserSID, &dwSidBufSize);
        else
            return FALSE;
    }
    else
        return FALSE;

    m_Luid = *pLogonId;

    m_ulLogonType = Interactive;
    m_ulSession = pWtsInfo?pWtsInfo->SessionId:0L;

    SetFlags(INITIALIZED);
    return TRUE;
}


BOOL CLogonSessionData::Initialize(const PSECURITY_LOGON_SESSION_DATA pLSData)
{
    if (!pLSData)
    {
        SetLastError(ERROR_INVALID_PARAMETER);        
        return FALSE;
    }

    GET_STRING_OR_BAIL_OUT(m_szUserName, &pLSData->UserName);
    GET_STRING_OR_BAIL_OUT(m_szLogonDomain, &pLSData->LogonDomain);
    GET_STRING_OR_BAIL_OUT(m_szAuthenticationPackage, &pLSData->AuthenticationPackage);
    
    DWORD dwSidBufSize = 0L;    
    if (!GetTextualSid(pLSData->Sid, NULL, &dwSidBufSize) && GetLastError()==ERROR_INSUFFICIENT_BUFFER)
    {
        // okay, a valid SID:
        m_szUserSID = (LPWSTR) LocalAlloc(LPTR, dwSidBufSize);
        if (m_szUserSID)
            GetTextualSid(pLSData->Sid, m_szUserSID, &dwSidBufSize);
        else
            return FALSE;
    }

    m_ulLogonType = pLSData->LogonType;
    m_ulSession = pLSData->Session ;
    m_liLogonTime = pLSData->LogonTime;
    m_Luid = pLSData->LogonId;

    SetFlags(INITIALIZED);
    return TRUE;
}



static DWORD GetLUIDsFromProcesses(PLUID *ppLuid)
{
    DWORD          dwSize, dwSize2, dwIndex ;
    LPDWORD        lpdwPIDs ;
    DWORD dwLastError = ERROR_SUCCESS;

    if (!ppLuid)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0L;
    }

    // Call the PSAPI function EnumProcesses to get all of the
    // ProcID's currently in the system.
    // NOTE: In the documentation, the third parameter of
    // EnumProcesses is named cbNeeded, which implies that you
    // can call the function once to find out how much space to
    // allocate for a buffer and again to fill the buffer.
    // This is not the case. The cbNeeded parameter returns
    // the number of PIDs returned, so if your buffer size is
    // zero cbNeeded returns zero.
    // NOTE: The "HeapAlloc" loop here ensures that we
    // actually allocate a buffer large enough for all the
    // PIDs in the system.
    dwSize2 = 256 * sizeof( DWORD ) ;

    lpdwPIDs = NULL ;
    do
    {
        if( lpdwPIDs )
        {
            HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
            dwSize2 *= 2 ;
        }
        lpdwPIDs = (unsigned long *)HeapAlloc( GetProcessHeap(), 0, dwSize2 );
        if( lpdwPIDs == NULL )
            return 0L; // Last error will be that of HeapAlloc
        
        if( !EnumProcesses( lpdwPIDs, dwSize2, &dwSize ) )
        {
            DWORD dw = GetLastError();
            HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
            SetLastError(dw);
            return 0L;
        }
    }
    while( dwSize == dwSize2 ) ;

    /// 
    /// at this point we have an array of the PIDs at the 
    /// time of the last EnumProcesses invocation. We will
    /// allocate an array of LUIDs passed back via the out 
    /// param ppLuid of exactly the number of PIDs. We will
    /// only fill the first n values of this array, with n
    /// being the number of unique LUIDs found in these PIDs
    /// 

    // How many ProcIDs did we get?
    dwSize /= sizeof( DWORD ) ;
    dwSize2 = 0L; /// our return value of found luids
    
    *ppLuid = (LUID *)LocalAlloc(LPTR, dwSize*sizeof(LUID));
    if (!(*ppLuid))
    {
        dwLastError = GetLastError();
        goto CLEANUP;
    }
    for( dwIndex = 0 ; dwIndex < dwSize ; dwIndex++ )
    {
        (*ppLuid)[dwIndex].LowPart =0L;
        (*ppLuid)[dwIndex].HighPart=0;
        // Open the process (if we can... security does not
        // permit every process in the system).
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE, lpdwPIDs[ dwIndex ] ) ;
        if( hProcess != NULL )
        {
            HANDLE hAccessToken;
            if (OpenProcessToken( hProcess, TOKEN_QUERY, &hAccessToken))
            {
                TOKEN_STATISTICS ts;
                DWORD dwSize;
                if  (GetTokenInformation(hAccessToken, TokenStatistics, &ts, sizeof ts, &dwSize))
                {
                    DWORD dwTmp = 0L;
                    BOOL bFound = FALSE;
                    for (;dwTmp<dwSize2 && !bFound;dwTmp++)
                        bFound = (*ppLuid)[dwTmp].HighPart == ts.AuthenticationId.HighPart && 
                            (*ppLuid)[dwTmp].LowPart == ts.AuthenticationId.LowPart;
                        
                    if (!bFound)
                        (*ppLuid)[dwSize2++] = ts.AuthenticationId;
                }
                CloseHandle(hAccessToken) ;
            }
            else
            {
#ifdef _DEBUG
                TCHAR sz[100]; /// duh! magic # is just okay for Debug build...
                _stprintf(sz, _T("Could not open process token for PID %ld\n"), lpdwPIDs[dwIndex]);
                OutputDebugString(sz);
#endif
            }
                                
            CloseHandle( hProcess ) ;
        }
        else
        {
#ifdef _DEBUG
            TCHAR sz[100]; /// duh! magic # is just okay for Debug build...
            _stprintf(sz, _T("Could not open process with PID %ld\n"), lpdwPIDs[dwIndex]);
            OutputDebugString(sz);
#endif
        }
        /// we don't really care if OpenProcess or OpenProcessToken fail or succeed, because 
        /// there are quite a number of system processes we cannot open anyway, not even as SYSTEM.
    }

CLEANUP:

    if (lpdwPIDs)
        HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
    
    if (ERROR_SUCCESS !=dwLastError)
        SetLastError(dwLastError);
    
    return dwSize2;
}



#define INTERACTIVE_SID_FLAG 0x1
#define LOCAL_SID_FLAG       0x2
#define LOGON_SID_FLAG       0x4
#define IS_INTERACTIVE       (LOCAL_SID_FLAG|INTERACTIVE_SID_FLAG|LOGON_SID_FLAG)

/*
pLocalSid and pInterActiveSidhave been computed by the caller because this is much more efficient and leads 
to reduced complexity in this function and fewer points of failure:
*/


static BOOL InteractiveTokenExamination(HANDLE hToken, PBOOL pbInteractive, PSID pLocalSid, PSID pInterActiveSid)
{
    BOOL                    bSuccess = FALSE; // assume function will
                                                      // fail
    DWORD                   dwIndex;
    DWORD                   dwLength = 0;
    TOKEN_INFORMATION_CLASS tic      = TokenGroups;
    PTOKEN_GROUPS           ptg      = NULL;
    DWORD dwLastError = ERROR_SUCCESS;
    DWORD dwSIDFlags = 0L;

    if (!pbInteractive || !hToken)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    __try
    {
        // 
        // determine the size of the buffer
        // 
        if (!GetTokenInformation(hToken, tic, (LPVOID)ptg, 0, &dwLength))
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwLength);
                if (ptg == NULL)
                {
                    dwLastError = GetLastError();
                    __leave;
                }
            }
            else
            {
                dwLastError = GetLastError();
                __leave;
            }
        }

        // 
        // obtain the groups the access token belongs to
        // 
        if (!GetTokenInformation(hToken,tic,(LPVOID)ptg,dwLength,&dwLength))
        {
            dwLastError = GetLastError();
            __leave;
        }

        // 
        // iterate over the groups and collect the flags:
        // 
        for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
        {
            if (EqualSid (ptg->Groups[dwIndex].Sid, pInterActiveSid))
                dwSIDFlags |= INTERACTIVE_SID_FLAG;
            
            if (EqualSid (ptg->Groups[dwIndex].Sid, pLocalSid))
                dwSIDFlags |= LOCAL_SID_FLAG;
            
            if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) ==  SE_GROUP_LOGON_ID)
                dwSIDFlags |= LOGON_SID_FLAG;
        }

        // 
        // indicate success
        // 
        bSuccess = TRUE;
        *pbInteractive = (dwSIDFlags & IS_INTERACTIVE)==IS_INTERACTIVE;
    }
    __finally
    {
        // 
        // free the buffer for the token group
        // 
        if (ptg != NULL)
            HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
    }

    if (!bSuccess)
        SetLastError(dwLastError);

    return bSuccess;
}












static PWTS_PROCESS_INFO GetWtsProcessInfo(const PWTS_PROCESS_INFO pwtsPI, DWORD dwPID, DWORD dwCount, HANDLE hProcess)
{
    DWORD dwIndex = 0L;
    DWORD dwModCount;
    HMODULE hm;
    TCHAR szModname[_MAX_PATH];
    
    /// 
    /// Iterate over the pwtsPI array until dwPID is found
    /// 

    for (;dwIndex<dwCount;dwIndex++)
    {
        if (pwtsPI[dwIndex].ProcessId == dwPID)
        {
            if (EnumProcessModules(hProcess, &hm, sizeof(HMODULE), &dwModCount))
            {
                if(GetModuleBaseName(hProcess, hm, szModname, _MAX_PATH) && !_tcsicmp(szModname, pwtsPI[dwIndex].pProcessName))
                    return &pwtsPI[dwIndex];
            }
        }
    }

    return NULL;
}



BOOL EnumNT4StyleInteractiveSessions(CLogonSessionData *pLogonSessionData, PULONG lpulSessions)
{
    DWORD          dwSize, dwSize2, dwIndex ;
    LPDWORD        lpdwPIDs ;
    DWORD dwLastError = ERROR_SUCCESS;
    BOOL bResult = TRUE; // assume success

    if (!lpulSessions || (*lpulSessions)  && !pLogonSessionData)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    ULONG ulSessionsPassed = *lpulSessions;
    *lpulSessions = 0L;

    PSID pLocalSid = NULL;
    PSID pInterActiveSid = NULL;
	SID_IDENTIFIER_AUTHORITY sia = SECURITY_LOCAL_SID_AUTHORITY;
	SID_IDENTIFIER_AUTHORITY siant = SECURITY_NT_AUTHORITY; 
    PWTS_PROCESS_INFO pwtsPI = NULL;
    DWORD dwWTSPCount = 0L;
    HMODULE hWTS = NULL;

    dwSize2 = 256 * sizeof( DWORD ) ;

    lpdwPIDs = NULL ;
    do
    {
        if( lpdwPIDs )
        {
            HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
            dwSize2 *= 2 ;
        }
        lpdwPIDs = (unsigned long *)HeapAlloc( GetProcessHeap(), 0, dwSize2 );
        if( lpdwPIDs == NULL )
            return 0L; // Last error will be that of HeapAlloc
        
        if( !EnumProcesses( lpdwPIDs, dwSize2, &dwSize ) )
        {
            DWORD dw = GetLastError();
            HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
            SetLastError(dw);
            return 0L;
        }
    }
    while( dwSize == dwSize2 ) ;

    hWTS = LoadLibrary (_T("wtsapi32.dll"));
    if (hWTS)
    {
        if (GetProcAddress(hWTS, "WTSEnumerateProcessesW"))
        {
            if (!WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0L, 1L, &pwtsPI, &dwWTSPCount) && GetLastError()!=ERROR_APP_WRONG_OS)
            {
                pwtsPI =NULL;
                dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
                goto CLEANUP;
            }
        }
    }

    ///
    /// if hWTS is NULL we simply do nothing. After all, 
    /// NT4 Wksta and Server don't have this DLL.
    ///

	// well-known local SID: S-1-2-0
	if (!AllocateAndInitializeSid( &sia, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pLocalSid ))
    {
        dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
        goto CLEANUP;
    }

	// well-known interactive SID: S-1-5-4
	if (!AllocateAndInitializeSid( &siant, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInterActiveSid ))
    {
        dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
        goto CLEANUP;
    }

    /// 
    /// at this point we have an array of the PIDs at the 
    /// time of the last EnumProcesses invocation. 
    /// 

    // How many ProcIDs did we get?
    dwSize /= sizeof( DWORD ) ;
    
    for (dwIndex = 0L; 
        bResult && dwIndex<dwSize;
        dwIndex++)
    {

        ///
        /// enumerate each process and look at its SIDs:
        ///

        HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, lpdwPIDs[dwIndex]);
        if (hProcess)
        {
            HANDLE hToken;                    
            if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
            {
                BOOL bInteractive = FALSE;

                bResult &= InteractiveTokenExamination(hToken, &bInteractive, pLocalSid, pInterActiveSid);
                
                if (bResult)
                {
                    if (bInteractive)
                    {
                        TOKEN_STATISTICS ts;
    
                        DWORD dwSize;
                        if  (!GetTokenInformation(hToken, TokenStatistics, &ts, sizeof ts, &dwSize))
                        {
                            dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
                            bResult = FALSE;
                        }
                        else
                        {

                            ///
                            /// Test, if we already have recorded this logon session:
                            ///

                            BOOL bFound = FALSE;

                            for (DWORD dwLSIndex = 0L;pLogonSessionData && dwLSIndex<(*lpulSessions);dwLSIndex++)
                            {
                                LUID ld = pLogonSessionData[dwLSIndex].GetLuid();
                                
                                if (ld.HighPart == ts.AuthenticationId.HighPart && 
                                    ld.LowPart == ts.AuthenticationId.LowPart )
                                {
                                    bFound = TRUE;
                                    break;
                                }
        
                            }



                            if (!bFound)
                            {
                                (*lpulSessions)++;       

                                if ((*lpulSessions) <= ulSessionsPassed)
                                {
                                    PWTS_PROCESS_INFO pi = NULL;
                                    if (pwtsPI)
                                    {
                                        pi = GetWtsProcessInfo(pwtsPI, lpdwPIDs[dwIndex], dwWTSPCount, hProcess);
                                        if (!pi)
                                        {
                                            /*
                                            Duh!?! We are running on a TS capable machine but either the PID since taking the 
                                            process snapshot with WTSEnumerateProcesses has been reused or the PID did not
                                            exist at that time. We have to fail because the result from psapi's EnumProcesses
                                            and wtsapi32's EnumerateProcesses don't match. We fake a GLE of 
                                            ERROR_CAN_NOT_COMPLETE in the hope, this is meaningful to the caller.
                                            */
                                            dwLastError = ERROR_CAN_NOT_COMPLETE;
                                            bResult = FALSE;     
                                        }
                                    }

                                    if (bResult && pLogonSessionData && !pLogonSessionData[(*lpulSessions)-1].Initialize(hToken, &ts.AuthenticationId, pi))
                                    {
                                        dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
                                        bResult = FALSE; // initializazion of a CLogonSessionData object failed, 
                                                         // probably not 'nuff memory !?!    Bail out!
                                    }
                                }
                                else
                                    dwLastError = ERROR_MORE_DATA; // don't set bResult to FALSE *now* in order to continue the
                                                                   // enumeration so we get an accurate estimation of the # of
                                                                   // required CLogonSessionData objects to be passed.
                            }
                        }
                    }
                }

                CloseHandle(hToken);
            }
            CloseHandle(hProcess);
        }
    }

    if (bResult && ERROR_MORE_DATA == dwLastError)
        bResult = FALSE; // indicate to the caller that not enough objects have been passed.

CLEANUP:

    if (lpdwPIDs)
        HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
    
    if (pInterActiveSid)
        FreeSid(pInterActiveSid);

    if (pLocalSid)
        FreeSid(pLocalSid);

    if (pwtsPI)
        WTSFreeMemory(pwtsPI);

    if (hWTS)
        FreeLibrary(hWTS);

    if (!bResult)
        SetLastError(dwLastError);

    return bResult;
}









BOOL EnumLogonSessions(CLogonSessionData *pLogonSessionData, PULONG lpulSessions)
{
    BOOL bRet = FALSE;
    DWORD dwLastError = NO_ERROR;
    PLUID sessions = NULL;
    ULONG ulActualSessions = 0;
    
    NTSTATUS ntRet = 0;
    PLUID pLuid = NULL;
    DWORD dwNumOfProcLUIDs = 0L;

    /*
    We bail out if on NT4 or even older NT. Those don't even 
    necessarily have secur32.dll, which must be delayloaded
    if a binary using this module needs to run on those 
    versions.
    */

    OSVERSIONINFO osvi;
    memset(&osvi,0,sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
    GetVersionEx(&osvi);
    if (osvi.dwMajorVersion<5L)
    {
        dwLastError = ERROR_CALL_NOT_IMPLEMENTED;
        goto CLEANUP;
    }

    if (!lpulSessions)
    {
        dwLastError = ERROR_INVALID_PARAMETER;
        goto CLEANUP;
    }

    ntRet = LsaEnumerateLogonSessions(&ulActualSessions, &sessions);

    if (ntRet != STATUS_SUCCESS) 
    {
        dwLastError = SafeNo_ERROR_MORE_DATA_Error(LsaNtStatusToWinError(ntRet));
        sessions = NULL;
        goto CLEANUP;
    } 
    
    if (ulActualSessions> *lpulSessions)
    {
        dwLastError = ERROR_MORE_DATA;
        *lpulSessions = ulActualSessions;
        goto CLEANUP;
    }
    

    dwNumOfProcLUIDs = GetLUIDsFromProcesses(&pLuid);
    if (!dwNumOfProcLUIDs)
    {
        dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
        goto CLEANUP;
    }

    for (ulActualSessions = 0;ulActualSessions< *lpulSessions;ulActualSessions++)
    {
        PSECURITY_LOGON_SESSION_DATA sessionData = NULL;
        // Get the session information.
        ntRet = LsaGetLogonSessionData (&sessions[ulActualSessions], &sessionData);
        if (ntRet != STATUS_SUCCESS) 
        {
            // An error occurred. Tell the world.
            dwLastError = SafeNo_ERROR_MORE_DATA_Error(LsaNtStatusToWinError(ntRet));
            // If session information was returned, free it.
            if (sessionData) 
                LsaFreeReturnBuffer(sessionData);
            goto CLEANUP;
        }    
        
        BOOL bFoundInLUIDs = FALSE;

        for (DWORD dwIndex = 0L;dwIndex<dwNumOfProcLUIDs;dwIndex++)
        {
            if (pLuid[dwIndex].HighPart == sessionData->LogonId.HighPart && 
                pLuid[dwIndex].LowPart == sessionData->LogonId.LowPart )
            {
                bFoundInLUIDs  = TRUE;
                break;
            }
        }

        if (!pLogonSessionData[ulActualSessions].Initialize(sessionData))
        {
            dwLastError = SafeNo_ERROR_MORE_DATA_Error(GetLastError());
            goto CLEANUP;
        }
        
        if (!bFoundInLUIDs)
            pLogonSessionData[ulActualSessions].SetFlags(CLogonSessionData::STALE);

        LsaFreeReturnBuffer(sessionData);
    }

    // if we get here, everything is okay:
    bRet = TRUE;

CLEANUP:
    // Free the array of session LUIDs allocated by the LSA.
    if (sessions)
        LsaFreeReturnBuffer(sessions);

    if (pLuid)
        LocalFree (pLuid);

    SetLastError(dwLastError);
    return bRet;
}

/// With both EnumNT4StyleInteractiveSessions and EnumLogonSessions we want
/// to indicate to the caller a too small argument array by returning FALSE 
/// and setting the last error to ERROR_MORE_DATA. Conversely, this means:
/// We mustn't return ERROR_MORE_DATA in any other case of failure. So if 
/// there is any other reason for failure from one of the functions that 
/// are called from within these two functions, we have to convert a possibly
/// returned GetLastError into some other, more general error. One nice,
/// innocent looking and general last error is ERROR_CAN_NOT_COMPLETE. With 
/// the following function we convert ERROR_MORE_DATA into 
/// ERROR_CAN_NOT_COMPLETE, if necessary:

static DWORD SafeNo_ERROR_MORE_DATA_Error(DWORD dwLastErr)
{
    return dwLastErr==ERROR_MORE_DATA?ERROR_CAN_NOT_COMPLETE:dwLastErr; 
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
Stefan has been writing programs in C/C++ since 1991, and for Windows since 1993. He holds a German engineer's degree Dipl.-Ing. (FH) in "Microelectronics/Technical Computer Science" from the Aalen (Germany) University of Applied Sciences and an MSc in "Software Technology" from the Stuttgart (Germany) University of Applied Sciences. Currently, he is employed by a software company in the south-west of Germany that specializes in PC life-cycle products and software deployment technology. In his spare time, Stefan likes to go swimming and enjoys listening to jazz music from the fifties. And yes, he has a Weblog at http://mcblogs.craalse.de/sku (German only).

Comments and Discussions