0

I need to pass a unique windows user account identifier between various processes (unique within the scope of a single computer running Windows 10 x64 1803 or higher).

The account's SID is the natural choice, but opaque IDs make debugging painful, so I'd prefer something human readable that maps 1:1 to a SID.

I originally tried just the logon name (e.g. "Rosalyn"). I could map that back to the correct SID, but the mapping method I used didn't work when running under a different user account for some reason (see edit below).

There seem to be at least two other forms of user account IDs that Windows uses:

  • username@somedomain.com
  • Domain\username

...but these make me a little nervous; the documentation mentions environments not all users have (e.g. LDAP, domains, Directory Services, etc). This has to work for all users, regardless of whether they're at home or in a corporate environment.

Anyone have experience with this sort of thing? Bonus points if there's documentation somewhere on how to map back and forth.

EDIT

... but the mapping method I used didn't work when running under a different user account for some reason.

Can you explain the reason? – Strive Sun - MSFT

// Get the SID for this user
try
{
    var ntAccount = new NTAccount(logonUser); // e.g. logonUser = "Scott"
    return (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier));
}
catch (Exception)
{
    Trace.WriteLine($"Unable to get SID for {logonUser}", "ERROR");
    throw;
}

When running as the logon user, this code returned the correct SID (S-1-5-21-4275680301-1639116052-1993100807-1001), but when running under the SYSTEM account, given the same logonUser, this code returned the SID for Local System (S-1-5-18).

I thought the logon user name might be under-qualified, so I tried "<user-domain-name>\<user-name>" and "<machine-name>\<user-name>", but those didn't work, regardless of the account I was running under.

Also, using things like UserDomainName made me a bit nervous, because whatever method I choose has to work for all users, regardless of their environment (home or corporate LAN). This is a case of being uneasy about what I don't know...

Scott Smith
  • 3,900
  • 2
  • 31
  • 63
  • use sid as is. you need unique but not human readable – RbMm Aug 13 '20 at 07:41
  • @RbMm - True, I don't _need_ human-readable. The desire for human-readable comes from past experience having to look up GUIDs and other opaque IDs over and over during debugging. – Scott Smith Aug 13 '20 at 16:44
  • 1
    `... but the mapping method I used didn't work when running under a different user account for some reason.` Can you explain the reason? – Strive Sun Aug 14 '20 at 10:05
  • 1
    When a User object moves from one domain to another, a new SID must be generated for the user account and stored in the ObjectSID property. When a user signs in and is successfully authenticated, the domain authentication service queries Active Directory for all the SIDs that are associated with the user, including the user's current SID, the user's old SIDs, and the SIDs for the user's groups. Refer: [Security identifiers](https://learn.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers#security-identifiers-and-globally-unique-identifiers) – Strive Sun Aug 14 '20 at 10:11
  • @StriveSun-MSFT - I've edited the question above. – Scott Smith Aug 14 '20 at 14:36
  • "Why not both?" Pass both the raw SID + a friendly name. Show the friendly name to the user; use the raw SID for business logic. You could pack them into a single string if you have to. – Raymond Chen Aug 21 '20 at 14:32

1 Answers1

1

You can use LookupAccountNameA to get the sid.

The LookupAccountName function accepts the name of a system and an account as input. It retrieves a security identifier (SID) for the account and the name of the domain on which the account was found.

And using this api does not need to run in the context of the LocalSystem account.

Code Sample: (C++)

#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;

}
Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • 1
    This is not guaranteed to round trip: If the named account is deleted, and a new account created with the same name, the name will now refer to the new account, not the old (deleted) one. – Raymond Chen Aug 17 '20 at 13:42
  • 1
    @RaymondChen Thanks for reminding. I think comparing the creation time of the account to determine whether it meets customer needs is a way. – Strive Sun Aug 21 '20 at 07:21
  • 1
    @Scott Smith Not sure if you have considered the problem Raymond said. Like I said, compare the account creation time. But this does not seem to be a good direction, see [this](https://social.technet.microsoft.com/Forums/en-US/617374de-a90e-498b-ba1e-f43b77cf0345/date-of-local-id-creation). I think the more important thing is to look at your needs. If it's just for the current account and not considering what ray said, then you can use my method. – Strive Sun Aug 21 '20 at 07:26
  • 1
    Comparing the account creation times (assuming you can even get the account creation times) will signal that the round trip failed, and you have now lost the original sid. So another question is "Is it okay if the round trip sometimes fails?" Another failure mode is if you lose network connectivity to the DC. – Raymond Chen Aug 21 '20 at 12:00
  • @StriveSun-MSFT - One side note with the use of `WTS...` APIs: The official Msft docs for some of these APIs state (incorrectly, in some cases) that Windows Terminal Service must be running or they will fail. Combine that with the fact that **Home** editions of Windows don't support Remote Desktop, and I was convinced that I couldn't rely on many/most of the `WTS...` APIs working for _all users of all editions_ of Windows 10... – Scott Smith Aug 21 '20 at 14:14
  • @ScottSmith `This has to work for all users, regardless of whether they're at home or in a corporate environment.` For users under the same domain, you can use `LookupAccountNameA` to check the sid. If for global users/different domains, you will have to rely on [Azure AD](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis). – Strive Sun Aug 24 '20 at 07:54