Search code examples
c#active-directoryuser-accountsiprincipal

Why is FindByIdentity with Domain Context ignoring local machine name in argument?


So I have this string:

testvm\Administrator

Where my machine hostname is 'testvm' and I call FindByIdentity using the domain context with that name. The problem is that FindByIdentity is returning the domain Administrator account (I know because it has a non-null UPN of that domain account and local accounts can't have a UPN). So why is it ignoring the hostname part of of the string? Shouldn't FindByIdentity return null because I'm specifically putting that machine hostname in there? Here is my code:

    private bool GetPrincipalFromName_IsDomain(string sName, out Principal userPrincipal)
    {
        bool bIsDomain = false;
        userPrincipal = null;
        PrincipalContext pcontextDomain = null;

        try
        {
            pcontextDomain = new PrincipalContext(ContextType.Domain);
        }
        catch (Exception ex)
        {
            throw new Exception("Unable to Initialize Domain Context", ex);
        }

        try
        {
            userPrincipal = Principal.FindByIdentity(pcontextDomain, sName);
            if(userPrincipal != null)
            {
                bIsDomain = true;
            }
        }
        catch(MultipleMatchesException mmEx)
        {
            throw new Exception(string.Format("Found more than one Principal on Domain using Name '{0}'. Use a more precise domain name.", sName), mmEx);
        }
        catch (Exception ex)
        {
            throw new Exception(string.Format("Unable to Find Principal on Domain using Name '{0}'.", sName), ex);
        }

        return bIsDomain;
    }

The only workaround I can find is to pass in:

testvm\\Administrator

Then FindByIdentity returns null in the domain context and non-null if I call it with the the local context. Why?


Solution

  • OK things I didn't fully grasp:

    1. The PrincipalContext constructor with the domain context assumes the local domain. If you do not want to assume a domain, you have to tell it explicitly which one in the next argument. In the case of the machine context, you have to let it assume the local machine OR specify which machine you want in the next argument.

    2. That FindByIdentity method cares nothing about anything before the slash. FindByIdentity looks for a type of identity (IdentityReference) and defaults to 'Name' which is a user or group name. It expects the name of the user or group unless you specify a different type of identity like SID, in which case it looks for the Principal in that context. So in my case of 'domain\jlaird' it was ignoring everything before the slash and looking up 'jlaird' in the current domain (I guess the domain the process was running under).

    So I needed some way to take a account string like 'DOMAIN_OR_MACHINE\USER_OR_GROUP' and get a Principal object out of it. Solution...

    1. Construct an NTAccount object with the raw account like above the string above. Being able to construct the object unfortunately just means the input string wasn't null. So to get anywhere, we have to translate the object to something else. Like a SID.
    2. Call: NTAccount.Translate(typeof(SecurityIdentifier)).Value And store the SID it returns.

    If the SID is not NULL, I can assume the account string is OK (it exists). So then...

    1. Split out the domain name from the user or group name using slash as a delimiter. Construct the domain context with what is before the slash.
    2. Call FindByIdentity with that domain context object, using the SID from #2.
    3. If FindByIdentity fails with the domain context, make a local machine context and call FindByIdentity with the same SID.

    Then I have a user or group: a) Account string in the format of 'DOMAIN_OR_MACHINE\USER_OR_GROUP' that is valid. b) I know whether it is domain (and part of the specified domain) or that it belongs to the machine c) I know whether it is a user or group by looking at the Principal object.

    So that solved the problem for me. Messy, and I think Microsoft made this process a PITA but it works.

    If this is useful to someone else, please mark as an answer.