Converting username to SID in C# - c#

I'm trying to use this code to convert a Windows username (in the classic .\username form) to a SID object:
NTAccount account = new NTAccount(".\\MyUser");
SecurityIdentifier sid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
However, I keep getting the following exception when executing the last instruction:
System.Security.Principal.IdentityNotMappedException: 'Some or all
identity references could not be translated.'
What am I doing wrong?

Answering my own question after some trial and error:
The code is correct, but the Translate function doesn't seem to support the shorthand . to indicate the account is local and not in a domain. So in case you have a username that starts with .\ you need to replace the dot with the machine name. The following code works correctly:
public static SecurityIdentifier usernameToSid(string user)
{
if (user.StartsWith(#".\"))
{
user = user.Replace(#".\", Environment.MachineName + #"\");
}
NTAccount account = new NTAccount(user);
return (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
}

Related

WindowsIdentity: Exception: System.Security.SecurityException: The user name or password is incorrect

On Windows Server 2012 R2 we want to get the Security Groups of an user from the Active Directory by C#-code. The application is an ASP.NET MVC5-project and is hosted by an IIS.
At first, we query the user account from the Active Directory by UserPrincipal.FindByIdentity(...). That works fine. At next, we use the WindowsIdentity class to query the Security Groups from the active directory. We use the class WindowsIdentity because the response is very fast.
Here is the code:
var lDomain = "MyDomain";
var lSamAccountName = "Simon";
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, lDomain))
{
// Get User Account from Active Directory
using (UserPrincipal lUserPrincipal = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, lSamAccountName))
{
var lUpn = lUserPrincipal.UserPrincipalName; // get UPN
// get Security Groups of the user
using (WindowsIdentity lWindowsIdentity = new WindowsIdentity(lUpn)) // Exception: System.Security.SecurityException: The user name or password is incorrect
{
var lGroups = lWindowsIdentity.Groups;
}
}
}
The problem: When instanciating the WindowsIdentity class (and passing the UPN) then an exception occurs:
“Upn: 'simon#MyDomain' Exception: System.Security.SecurityException: The user name or password is incorrect.
at System.Security.Principal.WindowsIdentity.KerbS4ULogon(String upn, SafeAccessTokenHandle& safeTokenHandle)
at System.Security.Principal.WindowsIdentity..ctor(String sUserPrincipalName, String type)
at System.Security.Principal.WindowsIdentity..ctor(String sUserPrincipalName)
at ...
This is curious because the query with UserPrincipal.FindByIdentity(...) was successful. Some accounts are working. Some accounts are not working. I cannot find a difference between the working und not working accounts.
Question: Who knows what I am doing wrong?
Additional Notes:
The Applicationpool Identity is: LocalSystem
In the Web.config, there is the following entry: <identity impersonate="false" />
When I query the groups by the following alternative, then no exception occurs and the result is satisfying. That´s curious:
var lResult = new List<SecurityIdentifier>();
DirectorySearcher lDirectorySearcher = new DirectorySearcher();
lDirectorySearcher.Filter = string.Format(CultureInfo.InvariantCulture, "(&(objectClass=user)(distinguishedName={0}))", lUserPrincipal.DistinguishedName); // for the object lUserPrincipal, look # code above
lDirectorySearcher.SearchScope = SearchScope.Subtree;
SearchResult lSearchResult = lDirectorySearcher.FindOne();
DirectoryEntry lDirectoryEntry = lSearchResult.GetDirectoryEntry();
lDirectoryEntry.RefreshCache(new string[] { "tokenGroups" });
// get groups
for (int i = 0; i < lDirectoryEntry.Properties["tokenGroups"].Count; i++)
{
SecurityIdentifier lSid = new SecurityIdentifier((byte[])lDirectoryEntry.Properties["tokenGroups"][i], 0);
lResult.Add(lSid);
// Translate into NTAccount to get Domain and SAMAccountname etc...
[...]
}
I really do not know if this helps, but somewhere I found this:
If IIS is set to allow anonymous access you can use the userPrincipleName format (username#domain).
If anonymouse access is NOT enabled in IIS you need to use the username in the form domain\username.
Seems weird though that for some accounts the UPN works and for others it does not..
Resolution 1:
Use ADSI Edit (via MMC.exe) to manually populate the userPrincipalName attribute of the desired Windows Service Account.
The format of a userPrincipalName is
<username>#<fully_qualified_domain_name>
For example, for the example log lines above, the expected UPN value should be
goodadmin#example.com
Allow a few minutes for Active Directory replication to complete before attempting to use the service account credentials.
Resolution 2:
Ensure the Active Directory Service Account information has the necessary permissions on the local BEMS host as Local Administrator and Logon As A Service.
Ensure the Active Directory Service Account information entered is actually valid. For example, ensure the password is entered correctly and that the account is not locked out in Active Directory.
Ensure the Active Directory Service Account information has the necessary Active Directory Group Permissions.
RTCUniversalReadOnlyAdmins
Here you can find how to get groups base on the UserPrincipal
How to create WindowsIdentity/WindowsPrincipal from username in DOMAIN\user format
// find all groups the user is member of (the check is recursive).
// Guid != null check is intended to remove all built-in objects that are not really AD gorups.
// the Sid.Translate method gets the DOMAIN\Group name format.
var userIsMemberOf = p.GetAuthorizationGroups().Where(o => o.Guid != null).Select(o => o.Sid.Translate(typeof(NTAccount)).ToString());
// use a HashSet to find the group the user is member of.
var groups = new HashSet<string>(userIsMemberOf), StringComparer.OrdinalIgnoreCase);
groups.IntersectWith(groupNames);

Cannot get LDAP connection working - always "Unknown error"

I'm researching what I need to build an Active Directory search tool which ultimately will be used in a C# ASP.NET web app.
I know very little about AD (and don't particularly want to know any more than necessary) so I've asked our tech ops to set up a dummy instance on a server. This they've done giving it the domain dummy.local
I now need to work out my LDAP connection string. Here I'm completely stuck. I'm using a user account which is a member of Domain Admins. After loads of hunting round the web I've tried all sorts of things to work out the various components of the LDAP connection string. For example, if I run the following in cmd.exe for that domain admin user on the server...
dsquery user -samid "username" | dsget user -memberof -expand
...this gives me the following information:
"CN=Domain Admins,CN=Users,DC=dummy,DC=local"
"CN=Remote Desktop Users,CN=Builtin,DC=dummy,DC=local"
"CN=Users,CN=Builtin,DC=dummy,DC=local"
"CN=Administrators,CN=Builtin,DC=dummy,DC=local"
"CN=Domain Users,CN=Users,DC=dummy,DC=local"
"CN=Denied RODC Password Replication Group,CN=Users,DC=dummy,DC=local"
I've also run the following in a C# console app...
using (var context = new PrincipalContext(ContextType.Domain))
using (var comp = ComputerPrincipal.FindByIdentity(context, Environment.MachineName))
{
Console.WriteLine(String.Join(",", comp.DistinguishedName.Split(',').SkipWhile(s => !s.StartsWith("OU=")).ToArray()));
}
...and this gives me the following:
OU=Domain Controllers,DC=dummy,DC=local
I thought I therefore had all the properties I needed to build my LDAP connection string. I ended up with this:
LDAP://COMSECWEBDEV.dummy.local/ou=Domain Controllers,/cn=Domain Admins,/cn=Users,/dc=dummy,/dc=local
I've tried using this connection string, with the username and password of the domain admin which I know is correct, but everything I try always gives me the same error:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
Since this error gives me no detail I have no idea what I'm doing wrong. I'm sure I'm just not getting the connection string right, but I've no idea how to work out the correct string.
For completeness, here is the console code which I'm testing with:
static void Main(string[] args)
{
var connString = ConfigurationSettings.AppSettings["lc"];
var username = ConfigurationSettings.AppSettings["lu"];
var password = ConfigurationSettings.AppSettings["lpw"];
using (DirectoryEntry de = new DirectoryEntry(connString, username, password))
{
DirectorySearcher search = new DirectorySearcher(de);
search.PageSize = 1001;// To Pull up more than 100 records.
DirectorySearcher directorySearcher = new DirectorySearcher(de);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
try
{
var result = directorySearcher.FindOne();
var found = false;
if (result != null)
{
if (result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
found = true;
Console.WriteLine("Found: " + result.Properties["msRTCSIP-PrimaryUserAddress"][0]);
}
}
if (!found)
{
Console.WriteLine("Sad face");
}
}
catch (Exception x)
{
Console.WriteLine(x.Message);
}
Console.WriteLine("------------");
}
}
I was trying to figure out how to properly format a LDAP connection string last week, and found this entry over on serverfault:
How can I figure out my LDAP connection string?
I noticed you had a "/" between each OU or DC entry - I didn't include those in mine, and I don't see them included in the above example either.
I'm far from an expert (obviously) but just figured I would throw it out there.

Check Password Reset on Active Directory Server

I need to reset windows password of any user through my .Net application. I am using the user's username to get its Directory Entry from AD server. I got these two different methods for changing password :
entry.Invoke("ChangePassword", oldPass, newPass);
&
entry.Invoke("SetPassword", "pass#123");
But I am getting the following error when am trying these methods on live AD server :
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
I have 2 AD servers. One of them is live and another is for testing purpose. I just want to check if my code is working or not. Since, access is denied on live server I can not change and check later my own password through code.
And if I am using the test AD server to change password, I don't know how to check whether the pasword is changed or not.
Kindly give any suggestions to check if my code is working properly or not.
Thanks in advance.
I think you're not getting a proper context setup before you call the invoke. Here's what I use for something similar. You'll need to set your own variables:
I'm using System.DirectoryServices.AccountManagement to get the functions.
//Domain related info
string _DCToUse = "myserver.domain.local";
string _ADConDomain = "DC=domain,DC=local";
string _AdDomain = "domain";
string _ADAdminUser = "administrator";
string _ADAdminPass = "password";
//User specific
string _UserName = "jsmith";
string _CurrentPass = "oldPass";
string _NewPass = "newPass";
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, _DCToUse,
_ADConDomain, _ADDomain+#"\"+_ADAdminUser, _ADAdminPass);
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, _UserName);
if (user == null)
{
string ADErrorMsg = "Couldn't find user, check your spelling.";
return Changed;
}
user.ChangePassword(oldPass, newPass);
user.Save();

Get fullname from Active Directory on C#

I have a problem about getting user's firstname and surname (fullname) from Active Directory. The code is below.
No problem with runing the project on my local. I can take data whatever I want. After publishing the project to remote server, it don't allow me to get firstname and surname (fullname), it just allows to get domain name and username. How can I fix it?
private string GetNameSurname()
{
string[] retVal;
//Pull the username out of the domain\user string.
retVal = Page.User.Identity.Name.Split(new string[] { #"\"},StringSplitOptions.RemoveEmptyEntries);
DirectoryEntry userEntry = new DirectoryEntry("WinNT://" + retVal[0] + "/" + retVal[1]);
string namesurname = (string)userEntry.Properties["fullname"].Value;
//return retVal[1];
//retVal[1] gives username.
//retVal[0] gives domain name
return namesurname;
}
Are you running the code to test for the same user?
Are you testing against AD in dev, Windows local accounts have a Full Name field (optional) but in AD the fields are named differently. Also, are you talking to the same version of AD.
According to the MSDN article there is no field called Full Name, only First Name and Surname and Display Name.
I think your problem is about credentials. Try to add an administrator username and password when conecting to active directory.

Active Directory Incorrect password attempts double counting

I am using the following C# code to connect to active directory and validate the login,
DirectoryEntry de = new DirectoryEntry();
string username = "myuser", path = "LDAP://addev2.dev.mycompany.com/CN=myuser,DC=dev,DC=mycompany,DC=com", password = "test";
for (int i = 0; i < 4;i++ )
{
try
{
de.AuthenticationType = AuthenticationTypes.Sealing | AuthenticationTypes.Secure | AuthenticationTypes.FastBind;
de.Username = username;
de.Password = password;
de.Path = path;
//de.RefreshCache();
Object obj = de.NativeObject;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
this works fine when the password is correct. However when the password is incorrect this shows as 2 invalid attempts in AD.
So what happens is when the AD admin allows 5 invalid attempts the user is locked out on the 3rd attempt.
when i look in the AD's event log 1 see 2 entries.
1)Pre-authentication failed:
2)Logon attempt by:
MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
Logon account: m0707b#dev.mycompany.com
Source Workstation: WKSXXXX
Error Code: 0xC000006A
Stepping thro the code i see 2 event entries on the line
de.RefreshCache()
I tried using de.NativeObject to see if that would solve the problem. No Dice
Anyone have any pointers?
Finally found the answer to this perplexing issue when you use the format username#domain the IIS app uses 2 calls once using Kerebros and when that fails using NTLM causing a double count The fix is to use the following format for authentication domain\username and that fixed the issue.
http://support.microsoft.com/kb/264678/EN-US/
You might check out the System.DirectoryServices.AccountManagement namespace. You can access an account and then cast one of the methods it has into a DirectoryEntry object. It might get around your double-authentication problem and it's easier to use.

Categories