I was reading through a bunch of documents I had lying around, and
found one that might be of interest to developers out there. A team in
a previous workplace encountered a strange issue: they were trying to
authenticate against a Windows domain (Active Directory-based domain
server) using ADSI via .NET's System.DirectoryServices, and in some
cases (particular users and particular machines) their login code would
bomb with a "Domain Not Found" error or somesuch.
Turns out they were trying to bind to ldap://domain_name, where domain name was programmatically derived from System.Environment.UserDomainName;
in some cases said property would return, instead of the logged on
user's domain name, the local computer name. Thing is, you would expect
a property in System.Environment to return the value of an evironment variable, presumably USERDOMAIN, which we verified contained the appropriate value.
Digging around in the documentation didn't help, so I turned to ye olde Reflector:
public static string get_UserDomainName()
{
byte[] array1;
int num1;
StringBuilder builder1;
int num2;
int num3;
bool flag1;
int num4;
new EnvironmentPermission(1, "UserDomainName").Demand();
array1 = new byte[1024];
num1 = array1.Length;
builder1 = new StringBuilder(1024);
num2 = builder1.Capacity;
flag1 =
Win32Native.LookupAccountName(null, Environment.UserName, array1, &(num1), builder1, &(num2), &(num3));
if (!flag1)
{
num4 = Marshal.GetLastWin32Error();
if (num4 == 120)
{
throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_Win9x"));
}
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_UserDomainName"));
}
return builder1.ToString();
}
Note the function call in bold; a quick look in MSDN revealed the following information:
BOOL LookupAccountName(
LPCTSTR lpSystemName,
LPCTSTR lpAccountName,
PSID Sid,
LPDWORD cbSid,
LPTSTR ReferencedDomainName,
LPDWORD cchReferencedDomainName,
PSID_NAME_USE peUse
);
Parameters
- lpSystemName
- [in] Pointer to a null-terminated character string that specifies the
name of the system. This string can be the name of a remote computer.
If this string is NULL, the account name translation
begins on the local system. If the name cannot be resolved on the local
system, this function will try to resolve the name using domain controllers
trusted by the local system. Generally, specify a value for
lpSystemName only when the account is in an untrusted domain and the
name of a computer in that domain is known.
- lpAccountName
- [in] Pointer to a null-terminated string that specifies the account
name.
Use a fully qualified string in the domain_name\user_name format to
ensure that LookupAccountName finds the account in the desired
domain.
Note the part marked in red: Environment.UserDomainName does indeed pass null for lpSystemName,
so if the machine contains a local user by the same name as the domain
user, the local machine name will be returned instead of the domain.
This behavior is apparently by design, although I can't figure out how that makes any sense what-so-ever.
There are two easy ways to avoid this issue:
string userDomain = Environment.GetEnvironmentVariable( "USERDOMAIN" );
string userDomain = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split( @'\' )[ 0 ];
Have fun.