Resolving User SIDs
All security hinges on one central idea, that there is a user to whom you want to either grant or deny access. Generally, humans like to think of a user as a name such as "Dave" or "Administrator." This is fine for humans but inconvenient for computers. It is not efficient to always compare a string of characters as a name with a security permission. Likewise, human names can change. If a user account includes a last name as in "Jane_Doe" and she remarries, it might change to "Jane_Smith." This would require the Administrator to change all permissions that once pointed to "Jane_Doe"; this can be quite time-consuming and is error-prone.
To solve this, Win32 introduced the concept of a security identifier (SID). This is a unique binary value that Win32 maps to a username. When you associate security permissions with a user, you are really mapping the permissions to the user's SID. This enables the username to change as often as needed because it does not change the SID. This enables a scenario such as user "Joe" leaving the company and being replaced by user "Jane." All the Administrator needs to do is rename Joe's account to "Jane" and change the password. Jane now has the exact same security access as Joe did.
A SID can be represented as either a binary or a text string. Generally speaking, most coders refer to the text version of a SID because it is easier for humans to interpret. Here's an example of such a text SID:
S-1-5-21-7893360589-1606980848-1708537768-500
or
S-1-5-21-7890445662-1935453667-1060284298-1003
This value identifies not only a particular user but also the domain in which the user account resides. Therefore, this value will not be the same for two accounts that are named the same but from different domains.
Two functions built into Win32 Perl provide mapping between SIDs and user accounts. These are the Win32::LookupAccountSID() and Win32::LookupAccountName() functions.
The Win32::Perms extension also provides two convenient functions to map the SIDs to usernames. The first of these is the Win32::Perms::ResolveSid() function:
$TextSid = Win32::Perms::ResolveSid( $Account [, $BinarySid [, $Machine ] );
The first parameter ($Account) is the account you want to look up. This can be either a username (such as Joe) or a full account name including a domain (such as Accounting\Joe). You can specify a machine's local account by specifying the machine name in the account, as in \\MyMachine\Administrator. See Table 1 for details on the different formats the account name can be.
The second, optional parameter ($BinarySid) will be set with the binary SID if the function is successful. Therefore, this must be a variable (no constants). Its previous value will be overwritten.
The optional third parameter ($Machine) specifies a particular machine to perform the SID lookup. This is useful when looking up a local account name such as "Administrator." The machine name must be prefixed with double backslashes as in \\Machine.
If the function is successful, it returns a text-based SID. If the second parameter is passed in as a scalar variable, it will be set with the account's binary SID. If the function fails, nothing is returned.
Example 1 shows how the ResolveSid() function works. The first few lines (2 through 4) discover the current user's username and domain. This information is passed into the function to represent the user's user account. Line 9 attempts to resolve the account to a SID. If it is successful, $Sid{binary} is set to the binary SID, and the function returns a text-based SID.
Example 1 Looking up a SID from an account name
01. use Win32::Perms; 02. my $Domain = Win32::DomainName(); 03. my $User = Win32::LoginName(); 04. my $Account = sprintf( "%s%s", ( "" ne $Domain )? "$Domain\\":"", $User ); 05. my %Sid = ( 06. text => "", 07. binary => "" 08. ); 09. if( $Sid{text} = Win32::Perms::ResolveSid( $Account, $Sid{binary} ) ) 10. { 11. print "You are logged in as $Account.\n"; 12. print "Your SID is: $Sid{text}\n"; 13. print "Your binary SID is: '$Sid{binary}'\n"; 14. } 15. else 16. { 17. print "\tCouldn't lookup the SID for $Domain\\$User: $^E\n"; 18. }
Table 1 Different Win32::Perms Account Formats
Account Format |
Description |
UserName |
User account. The account is first searched for as a local account on the local machine. If it is not found, the default domain is examined. |
DomainName\UserName |
Domain account. The specified domain is searched for the account name. |
\\MachineName\UserName |
Local machine account. The specified machine is searched for a local account. If the specified machine does not have such an account, the search continues on the default domain. |
If you already have a SID but need to look up the user account associated with it, you can use the ResolveAccount() function:
$Account = Win32::Perms::ResolveAccount( $TextSid | $BinarySid [, $Machine ] );
The first parameter is the SID you are looking up. This can be either a text or binary SID. The function will determine which type of SID you are using and will handle it accordingly.
The optional second parameter indicates what machine on the network is to perform the lookup. If the SID represents a local account or group on a remote machine, you need to specify the remote machine in this parameter. The machine name must be prefixed with double backslashes as in \\Machine.
If the function is successful, it will return the account name in the form of "domain\user." Otherwise, the function returns nothing.
Example 2 shows how both the ResolveSid() and ResolveAccount() functions work. Notice that line 9 looks up the current user's SID, and then line 15 uses that SID to look up the user's name. Line 15 passes in a binary SID, but it could just as well pass in the text SID ($Sid{text}) instead.
Example 2 Resolving accounts and SIDs
01.use Win32::Perms; 02.my $Domain = Win32::DomainName(); 03.my $User = Win32::LoginName(); 04.my $Account = sprintf( "%s%s", ( "" ne $Domain )? "$Domain\\":"", $User ); 05.my %Sid = ( 06. text => "" 07. binary => "" 08.); 09.if( $Sid{text} = Win32::Perms::ResolveSid( $Account, $Sid{binary} ) ) 10.{ 11. print "You are logged in as $Account.\n"; 12. print "Your SID is: $Sid{text}\n"; 13. print "Your binary SID is: '$Sid{binary}'\n"; 14. print "Looking up your user account based on your SID:\n"; 15. if( my $LookupAccount = Win32::Perms::ResolveAccount( $Sid{binary} ) ) 16. { 17. print "\tBinary SID maps to: $LookupAccount\n"; 18. } 19. else 20. { 21. print "\tCouldn't lookup the account: $^E\n"; 22. } 23. if( my $LookupAccount = Win32::Perms::ResolveAccount( $Sid{text} ) ) 24. { 25. print "\tText SID maps to: $LookupAccount\n"; 26. } 27. else 28. { 29. print "\tCouldn't lookup the account: $^E\n"; 30. } 31.} 32.else 33.{ 34. print "\tCouldn't lookup the SID for $Domain\\$User: $^E\n"; 35.}
About This Article
If you want more in-depth information on security identifiers, check out Chapter 11, "Security," of Win32 Perl Programming, by David Roth (© 2002 New Riders Publishing, ISBN 1-57870-216-x).