Blaze Blaze - 2 months ago 10
C Question

How to get the logged-on user's SID in Windows

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.

Answer

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.

Comments