5
votes

I need to get the string format SID of the logged-on user. I already have the username and am trying to use LookupAccountName to get the SID. This works partly - I do get a SID but it is only a partial match to the user's actual SID.

I do not want the SID of the process owner as the process may be elevated (impersonating), but rather I want the SID of the user who is logged on where the process is running.

The code needs to work with non-elevated privileges.

This is my code so far

LPCTSTR wszAccName = TEXT("hardcoded username for testing");
LPTSTR wszDomainName = (LPTSTR)GlobalAlloc(GPTR, sizeof(TCHAR) * 1024);
DWORD cchDomainName = 1024;
SID_NAME_USE eSidType;
SID sid;
DWORD cbSid = 1024;
if (!LookupAccountName(NULL, wszAccName, &sid, &cbSid, wszDomainName, &cchDomainName, &eSidType)) {
    return GetLastError();
}

if (!ConvertSidToStringSid(&sid, &pszSidBuffer)) {
    return GetLastError();
}

This yields a SID like "S-1-5-21-1-1234567890-9-1000" But the user's actual SID is like "S-1-5-21-3214321539-1234567890-2233445522-1000" (as per the process owner and the registry key under HKEY_USERS). Note how the SIDs are the same except for the 5th and 7th components, which are only 1 digit each but should be longer.

How do I get the user's actual/complete SID?

Also, I'm a total C/C++ newbie (and the code above is not production quality at all). And I'm using /NODEFAULTLIB in order to avoid linking VC runtimes. Sorry about that.

1
It appears that the SID you're getting with this code has a different domain/local ID. How are you determining "the user's actual SID" to which you are comparing it? Is the machine attached to a domain?Cody Gray
@CodyGray Updated the question. By actual SID I mean the one I get through looking at the process owner (code not shown) or by looking at the user's registry key under HKEY_USERS.Blaze
It looks like WTSQueryUserToken would be quite helpful, as I could get the SessionID (not to be confused with SID) using WTS functions and then get the SID from the user token. However, WTSQueryUserToken requires special prvilieges (i.e. a service) whereas I need this to work in an ordinary, non-elevated process.Blaze
A colleague suggested getting the SID from the owner of explorer.exe. This would be somewhat effective but doesn't handle edge cases where explorer is relaunched by a different user or multiple users are logged on (and so multiple explorer processes exist) at the same time.Blaze
It might be possible to extract the SID you're after from the window station or the desktop object, e.g., the user might be the owner. But I can't really imagine any legitimate reason for non-elevated code to care about the logged-on user as opposed to the user the code is running as. Can you clarify what you are actually trying to achieve, i.e., what will you do with the SID once you've got it?Harry Johnston

1 Answers

4
votes

Your code doesn't provide a properly-sized buffer for the SID returned by LookupAccountName(). This results in stack corruption and undefined behaviour, which might conceivably explain why you're not getting the SID you were expecting. (Although I rather suspect that you're actually passing in the wrong username, or an improperly formatted username.)

Anyway, to fix the most obvious problem, the code should look more like this:

#include <Windows.h>
#include <Sddl.h>

#include <stdio.h>

int main(int argc, char ** argv)
{
    LPCTSTR wszAccName = TEXT("domainname\\username");
    LPTSTR wszDomainName = (LPTSTR)GlobalAlloc(GPTR, sizeof(TCHAR) * 1024);
    DWORD cchDomainName = 1024;
    SID_NAME_USE eSidType;
    LPTSTR sidstring;
    char sid_buffer[1024];
    DWORD cbSid = 1024;
    SID * sid = (SID *)sid_buffer;

    if (!LookupAccountName(NULL, wszAccName, sid_buffer, &cbSid, wszDomainName, &cchDomainName, &eSidType)) {
        return GetLastError();
    }

    if (!ConvertSidToStringSid(sid, &sidstring)) {
        return GetLastError();
    }

    printf("%ws\n", sidstring);
    return 0;

}

That's still not the correct way to do it, of course; you are supposed to call LookupAccountName() twice, once to determine the buffer length and then a second time to retrieve the actual information. But it demonstrates what you've done wrong, and is good enough for testing purposes.