Monday, July 3, 2017

How to resolve a System.Security.Principal.IdentityNotMappedException?

Sometimes when working with a non-English version of Windows, we get kinda problem
"Some or all identity references could not be translated"
This indicates that we are getting a System.Security.Principal.IdentityNotMappedException
What does it mean?


The first thing to understand is that in Windows, User Accounts and Groups have a unique and immutable identifier known as a Security Identifier (SID). When working with User Accounts and Groups in Windows, you can work with them via their SID or by their name.

In Windows, a user is represented by an Access Token (You can find more information on Access Tokens at http://msdn.microsoft.com/en-us/library/windows/desktop/aa374909(v=vs.85).aspx) which can be associated with a process or to a thread (via impersonation).
The token contains the user’s SID and all the Groups the user is a member of. The token contains the Group SIDs. (The groups are NOT stored by their names)

There are Win32 APIs that allows an application to translate between the name and the SID:



These Win32 APIs actually calls the LSA APIs. (One difference with these APIs is that they allow to translate multiple names/SIDs instead just one.)


When converting between a Name and a SID and vice versa, you can encounter an error if the translation can’t be done. The common Win32 Error code that you will see is 1332 also known as ERROR_NONE_MAPPED which translates to “No mapping between account names and security IDs was done.”

At the LSA API level where status errors are returned, the error is: STATUS_NONE_MAPPED or STATUS_SOME_NOT_MAPPED. In .NET, the error code is mapped as the following exception:System.Security.Principal.IdentityNotMappedException

Why would the translation APIs fail? Well, the API remarks provides some details on how the translation is done. It uses a lookup order to translate (there is also a local cache as well)

In addition to looking up SIDs for local accounts, local domain accounts, and explicitly trusted domain accounts, LsaLookupSids can look up SIDs for any account in any domain in the Windows forest, including SIDs that appear only in the SIDhistory field of an account in the forest. The SIDhistory field stores the former SIDs of an account that has been moved from another domain. To perform these searches, the function queries the global catalog of the forest.

So a translation failure could happen if you have an issue with your Domain(s) or an issue with SID History. So the first step in determining the issue is identifying which user or SID is the issue. Once you have identified the User or SID, you can then review its relationship with the caller (who is this and what domain do they belong to and what domain does the system belong to where the caller is running)

Finally, a word of advice, internally most APIs are going to be working with SIDs so you can avoid the translation error and improve performance by always trying to work with SIDs.
You’ll notice that most classes in .NET such as IsInRole() allows you to specify a SID instead of the name (via the SecurityIdentifier class). WindowsIdentity.Groups allows you access to the SID as well.


Example:
I want to set permission for a user

static void SetPermission(string directory)
{
   var directoryInfo = new DirectoryInfo(directory);
   var directorySecurity = directoryInfo.GetAccessControl();
   CanonicalizeDacl(directorySecurity);
   directorySecurity.AddAccessRule(new FileSystemAccessRule("BUILTIN\\IIS_IUSRS", FileSystemRights.FullControl, InheritanceFlags.ContainerInherit, PropagationFlags.InheritOnly, AccessControlType.Allow));
   directoryInfo.SetAccessControl(directorySecurity);
}


This code would be failed if it run in a non-English version of Windows because the "BUILTIN\\IIS_IUSRS" could not be translated. Exactly the "BUILTIN" could not be translated. To fix this problem, I could change to remove "BUILTIN" or use an SID instead.

static void SetPermission(string directory)
{
   var directoryInfo = new DirectoryInfo(directory);
   var directorySecurity = directoryInfo.GetAccessControl();
   var sid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
   directorySecurity.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit, PropagationFlags.InheritOnly, AccessControlType.Allow));
   directoryInfo.SetAccessControl(directorySecurity);
}


Refer these links for SID
https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
https://msdn.microsoft.com/en-us/library/cc980032.aspx

No comments:

Post a Comment