Question

I have an application that is run by a service. The application is configured in a way that it only runs under a single Windows user account, i.e. the user who installed the software. The way this is handled is during installation, the application saves the username/domain combination of the user who initiated the installation and then the service, depending on whether or not the user session is active decides to run the application.

One of our customers reported that the application was not running on his account and after further investigation we discovered that for some reason when querying the session information, the domain name returned is different for the same session id.

This is the pInvoke for WTSQuerySessionInformation:

[DllImport("Wtsapi32.dll", SetLastError = true)]
        static extern bool WTSQuerySessionInformation(
            IntPtr hServer,
            uint sessionId,
            WTS_INFO_CLASS wtsInfoClass,
            out IntPtr ppBuffer,
            out uint pBytesReturned
        );

and this is how we get the service checks if the user has an active session :

WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref pSessionInfo, ref dwCount);

Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

Int32 current = (int)pSessionInfo;
uint bytes = 0;
for (int i = 0; i < dwCount; i++)
{
     WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
     if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
     {
         IntPtr userPtr = IntPtr.Zero;
         IntPtr domainPtr = IntPtr.Zero;

        //Check if the active session matches the saved username/domain
        WTSQuerySessionInformation(IntPtr.Zero, (uint)si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes);

        WTSQuerySessionInformation(IntPtr.Zero, (uint)si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes);

        string sessionUName = Marshal.PtrToStringAnsi(userPtr);
        string sessionDomain = Marshal.PtrToStringAnsi(domainPtr);
    }
}

The problem is when the service detects the user's session and tries to get the domain name, the name returned is different than the actual domain name of the user even though the session ID is correct for that particular user.

If it helps the domain name returned from WTSQuerySessionInformation is NTLAN_1 which is totally different from the actual domain of the user.

I would like to know if there is anything wrong with the pInvoke of WTSQuerySessionInformation or any of the calls in the above code. Also would like to know if the NTLAN_1 domain is associated with some service or RDP application of any sort.

EDIT:

After much negotiations I managed to get the result for LsaEnumerateLogonSessions and LsaGetLogonSessionData, sadly it's the same result and none of the listed sessions has the expected domain name. Here is the list of sessions with their respected info from the LsaEnumerateLogonSessions:

Session: 0 User: MARK *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 7:34:17 AM
Session: 0 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/4/2014 7:47:11 AM
Session: 0 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 1/27/2014 3:27:33 PM
Session: 0 User: MCMFILE2$ *** Domain: NTLAN_1 *** Login Type: (5) Service *** Login Time: 1/16/2014 3:52:46 PM
Session: 0 User: MCM-LR9-YE91K$ *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:52:40 AM
Session: 0 User: MARK *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:19:39 AM
Session: 0 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 1/20/2014 9:57:57 AM
Session: 0 User: ANONYMOUS LOGON *** Domain: NT AUTHORITY *** Login Type: (3) Network *** Login Time: 1/16/2014 3:53:19 PM
Session: 1 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (2) Interactive *** Login Time: 1/16/2014 3:53:11 PM
Session: 0 User: LOCAL SERVICE *** Domain: NT AUTHORITY *** Login Type: (5) Service *** Login Time: 1/16/2014 3:52:46 PM
Session: 0 User: MARK *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:52:40 AM
Session: 1 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:52:38 AM
Session: 0 User: MARK *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:19:39 AM
Session: 0 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 1/18/2014 6:20:25 PM
Session: 0 User: ANONYMOUS LOGON *** Domain: NT AUTHORITY *** Login Type: (3) Network *** Login Time: 1/16/2014 3:53:19 PM
Session: 0 User: CVSMANAGER_USER *** Domain: MCMFILE2 *** Login Type: (5) Service *** Login Time: 1/16/2014 3:53:09 PM
Session: 0 User: MCM-LR9-YE91K$ *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:52:40 AM
Session: 0 User: MARK *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/6/2014 10:19:39 AM
Session: 0 User: DefaultAppPool *** Domain: IIS APPPOOL *** Login Type: (5) Service *** Login Time: 2/6/2014 8:42:47 AM
Session: 0 User: ADMINISTRATOR *** Domain: NTLAN_1 *** Login Type: (3) Network *** Login Time: 2/4/2014 2:40:40 PM
Session: 0 User: IUSR *** Domain: NT AUTHORITY *** Login Type: (5) Service *** Login Time: 1/16/2014 3:53:09 PM
Session: 0 User: MCMFILE2$ *** Domain: NTLAN_1 *** Login Type: (0) 0 *** Login Time: 1/16/2014 3:52:44 PM

The session in question is have session id = 1. And from the list it shows the domain name to be NTLAN_1 instead of the actual domain.

Now, I caught something strange in my desktop application which I believe have a crucial part in finding the solution for this issue but I'm not sure what to make of it.

Now my desktop application is written in .NET, so I tried getting the username and domain by running the application directly. Now the funny thing is if I use the native GetUserNameEx function and passing NameUserPrincipal as the NameFormat, I am able to get the correct username/domain combination. However, and this is the funny part, when I try getting the domain name using the Environment class by calling Environment.UserDomainName I get the NTLAN_1 culprit. note that both managed and unmanaged calls were done by the same application in the same run.

I haven't had such an issue before and it seems now I have a couple more users who reported the same issue and it started to really bother me.

Was it helpful?

Solution

I finally found the issue, the domain in question was actually a UPN suffix, not a domain. I still believe that it is a bug from MS as both WTSQuerySessionInformation and LsaEnumerateLogonSessions should return the domain name and not the UPN suffix.

I was able to solve the issue by relying on the account SID rather than the username/domain combination and luckily the SID is the same whether querying using the actual domain name or the suffix.

Thanks to Harry Johnston for the amazing comment regarding SIDs.

OTHER TIPS

Too big to put as a comment... hope this is helpful...

So... is the service effectively querying a local or remote server?

The following is taken from this MSDN link which describes the WTSQuerySessionInformation function

"Only specify WTS_CURRENT_SESSION when obtaining session information on the local server. If WTS_CURRENT_SESSION is specified when querying session information on a remote server, the returned session information will be inconsistent. Do not use the returned data."

Thats the SessionId parameter you are passing in. Furthermore:

"To retrieve the session ID for the current session when Remote Desktop Services is running, call WTSQuerySessionInformation and specify WTS_CURRENT_SESSION for the SessionId parameter and WTSSessionId for the WTSInfoClass parameter. The session ID will be returned in the ppBuffer parameter."

Update: try using WTSEnumerateSessionsEx for more session info via the extended struct WTS_SESSION_INFO_1, which itself contains the domain name...

"pDomainName - A pointer to a null-terminated string that contains the domain name of the user who is logged on to the session. If no user is logged on to the session, the string contains NULL. "

Not sure what it holds if the user is shadowing another.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top