Obtaining the SecurityIdentifier (SID) from just a domain username (c#) - c#

I wish to obtain the domain SID from a user account that has been used to access my web page, but...
In some instances, you dont have access to the HttpContext object to call HttpContext.Current.Request.LogonUserIdentity.User.Value
All the static function is able to accept is a user-name as a string, and a domain-name as a string.
If the domain is left blank, it is amused that the user account name is within the same domain as the IIS server that the process is executing upon. :/
your help would be most appreciated as always.
Cheers.

To get the a user's domain, you can use LookupAccountName. In case you need a pinvoke sample, get it from pinvoke.net
You pass in DOMAIN\UserName to the function. The LookupAccountName function will give you back the user SID and the domain name.
I suspect that you don't really need to know the domain SID. I would think the returned domain name should be enough for you. If you really want to know the domain SID, you can extract it directly from a the returned user SID.
A user SID is always starting with a domain SID. For the details of how a SID structure looks like and how to extract the domain SID, please check here

Try using Directory Services? Need .Net 3.5 for this:
public UserPrincipal GetUserPrincipal(string userName)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, LDAPDomain, LDAPUser, LDAPPassword);
return UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, userName);
}
You can then use the UserPrincipal Class which has a .Sid method.
I set my Domain and AD account info from the web.config like so
private String LDAPDomain
{
get
{
return ConfigurationManager.AppSettings["LDAPDomain"];
}
}

Related

How to get down-level logon name from UserPrincipal in .Net

I have the following search query where I use a User Principal Name identification:
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, "user1#domain1.local"); //the searched UPN will be dynamic!
After I get the UserPrincipal object, I have to create a down-level logon format (domain1\user1).
Unfortunately I have the following distinguished name:
CN=User One,OU=Every employee,DC=domain1,DC=local
It looks like user.Context.Name is null which is claimed to be the name of the domain.
Also, if the selected user is a local one, I should get the machine1\localuser1 value.
The SamAccountName contains only the user name, without the context (user1).
What is the best way to get that format from any UserPrincipal object?
If you need to do this for machine accounts, then your answer is the way to go.
But another way, strictly for Active Directory (in case it comes in handy some other time), is to look at the msDS-PrincipalName attribute, which you can get via the underlying DirectoryEntry object:
var de = (DirectoryEntry) user.GetUnderlyingObject();
de.RefreshCache(new [] { "msDS-PrincipalName" }); //retrieve it from AD
var domainUsername = de.Properties["msDS-PrincipalName"].Value;
Since it's a constructed attribute, it's not returned unless you specifically ask for it, which is what you use RefreshCache for.
The down-level logon format can be obtained via an NTAccount object. However, it requires some translation to convert the Principal's SID to an NTAccount:
string downLevelLogonName =
userPrincipal.Sid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();
Using that code works in both case (domain or local computer users).

Unable to extract information

I'm trying to query a domain to determine if:
User is a valid user (and has the correct password)
User is enabled
User belongs to group x
My development machine does not belong to this domain.
I want to specify the username and password via my application
I'm using the System.DirectoryServices.AccountManagement namespace as this seems to be the most efficient way doing this, however I've struggling to get even the most basic of information out of my domain controller.
I can explore LDAP via another tool.
First test is to collect user information, the code below returns null on user.
The user however is valid.
What am I doing wrong?
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "server","CN=Users,DC=doom,DC=home", "ldapuser","password");
// get user contect
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.Name, username);
//is user locked?
var locked = user.Enabled;
Update:
Having defined the bind method as below, I now receive error
"Information about the domain could not be retrieved (1355)."
var ctx = new PrincipalContext(ContextType.Domain, "server", "DC=doom,DC=home", ContextOptions.SimpleBind, "ldapuser", "password");
Sorted.
This answer resolves the two issues I came across when attempting to connect to a domain controller that I am not a member of.
This article get me the final answer:
http://elegantcode.com/2009/03/21/one-scenario-where-the-systemdirectoryservices-accountmanagement-api-falls-down/
you need to define the Bind in the context (i.e. ContextOptions.SimpleBind)
You must set up the domain server in your Network adaptors DNS settings as the first DNS server to use.
I can now connect to my AD and collect data.

Logon failure error when attempting UserPrincipal.FindByIdentity

I've been searching for solutions on this for a while now, but each thread a dead-end unfortunately.
I'm working on a C#/ASP.net web app that will only be used internally by our company. Anonymous access has been switched off both in IIS and my web.config file forcing IIS to use the windows authenticated user (Active Dir user).
My problem is that the following code works perfectly to get the required (or any) AD user:
using System.DirectoryServices.AccountManagement;
... other code ...
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain", "someADuser", "someADuserpassword");
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "samaccountname");
"someADuser" used in the PrincipalContext above is the current logged in user through windows, thus authenticated and a valid AD user. Using the following code (with the exact same user still logged in) gives me a "Logon failure: unknown user name or bad password" error:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain");
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "samaccountname");
It seems UserPrincipal.FindByIdentity doesn't use the logged in user's validated credentials for some reason if it's not specified in the PrincipalContext object - something I don't want to do.
Is it possible that the ctx aren't picking up the logged in Windows users for some reason, even if the necessary settings (i hope) is added to web.config :
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
And Anonymous access is completely disabled in IIS?
It seems UserPrincipal.FindByIdentity doesn't use the logged in user's validated credentials for some reason if it's not specified in the PrincipalContext object - something I don't want to do.
UserPrincipal.FindByIdentity doesn't care about the user's credentials at all. You're just performing a look up to see if the account exists. The reason you're getting an error is because the default user credentials (i.e. the identity that your web application is running as) doesn't have access to the directory, so it can't perform the look up. When you pass in the client's credentials to the PrincipalContext then the problem goes away because your client has a valid AD account with access to the directory.
You should investigate which identity is being used to run the application pool and make sure it has access to the directory.
Quite annoying as I though if anonymous access was turned off, the current principal would default to the user logged in to windows. It turns out it's not as indicated by #RogerN.
Using the following statement as mentioned by #TheKingDave, it basically impersonates the user logged in to windows and makes the current thread run on it's principal rather than the "ASP" (in my case) account.
Because all the users on our domain has query/read access to Active Directory, this shouldn't be a problem to get more detail on them, which is what I wanted in the first place.
The end code (was testing):
System.Web.HttpContext.Current.Request.LogonUserIdentity.Impersonate();
ctx = new PrincipalContext(ContextType.Domain, System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName);
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "whicheveraccount");
Hope it helps some1 in future! thx! ;-)
I did a lot of search/research to resolve the issue, but nothing worked, at last, all what I did was to add # to the servername, container, username & password like below:
app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain, #"abc.net", #"OU=Customers,DC=abc,DC=net", ContextOptions.SimpleBind, #"abcnet\authuser", #"!$%MyPassword"));
And it worked. doh!
So I found out how to pass the impersonated identity. I had a situation where, as an admin, I wanted to unlock a user account automatically. I had to get the user names into variables and pass them in to the function like this:
string accountname = #"domain\username";
string admin = "adminusername";
string domain = Environment.UserDomainName;
string password = "password";
string dc = "WIN2K8DC1"; // example host name of domain controller, could use IP
// This determines the domain automatically, no need to specify
// Use the constructor that takes the domain controller name or IP,
// admin user name, and password
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, dc, admin, password);
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, accountname)
if (winuser != null)
{
WindowsImpersonationContext wic = Impersonation.doImpersonation(admin, domain, password); //class/function that does the logon of the user and returns the WIC
if (wic != null)
{
winuser.UnlockAccount();
}
}
Impersonation class functionality that can be used to return a WIC can be found on MSDN:
https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx

Create WindowsIdentity using just a domain and username

I am building a site that gets user information using the WindowsIdentity of the current user. The main info I get from this is the ssid.
I do this for the current users as follows
IntPtr logonToken = WindowsIdentity.GetCurrent().Token;
WindowsIdentity windowsId = new WindowsIdentity(logonToken);
string ssid = windowsId.User.ToString();
What I need to do now, and am failing at, is getting the ssid for any arbitrary username that exists on the domain.
I tried WindowsIdentity(string), but that gave me a SecurityException
The name provided is not a properly formed account name.
How are you formatting the principal? Generally they take the form of user#domain.ext, so if your AD provides principal resolution on say example.com, a user principal name (UPN) may look like: joe.bloggs#example.com. The WindowsIdentity(string) constructor accepts a UPN, not an older format username EXAMPLE\joe.bloggs

ASP.NET - Get the Principal / Relative Identifier (RID) for a DirectoryEntry / SID

I am using Active Directory in a custom MembershipProvider class to authenticate users in an ASP.NET 2.0 intranet application and associate their sid with a profile for the application.
When the ActiveDirectoryMembershipProvider is used, the ProviderUserKey object for the MembershipUser is as follows
SecurityIdentifier sid = (SecurityIdentifier)Membership.GetUser().ProviderUserKey;
string sidValue = sid.ToString();
/* sidValue = "S-1-5-21-XXXX-XXXX-XXXX-YY" */
As I understand it, YY is the principal within the namespace (also referred to as a group/domain).
When using the custom MembershipProvider, I can get the sid using the objectSid property of a DirectoryEntry object
DirectoryEntry entry = new DirectoryEntry(path, username, password);
SecurityIdentifier sid = new SecurityIdentifier((byte[])entry.Properties["objectSid"].Value, 0);
string sidValue = sid.ToString();
/* sidValue = "S-1-5-21-XXXX-XXXX-XXXX" */
The sidValue in this case is identical, except it does not contain the principal YY.
My question is two-fold
Is the principal required in order to uniquely identify an individual?
Is it possible to obtain the principal from the DirectoryEntry object (or through any other classes available in System.DirectoryServices)?
EDIT:
Having done some further reading ({1} {2}), I now know that the sid can change if the user is moved from one group/domain to another. In light of this, would using the GUID defined in the DirectoryEntry Properties["objectGUID"] be a better choice for uniquely identifying a user?
The objectGUID is the best choice for identifying a user account. I highlight this because the objectGUID is unique and fixed for an instance of an account. If you delete and recreate the account with the same distinguishedName you'll get a different objectGUID. So, objectGUID doesn't identify the user, it identifies the account.
So, if you want to identify the account, use objectGUID.
Sometimes, accounts can be deleted and recreated by admins to solve problems. If you need to identify the user even after this has happened, you need to pick something else on the account object. That will probably have to depend on your account definition policies. Maybe you have sAMAccountNames that are not based on the user's name? Maybe the admins populate employeeid or employeeNumber? Maybe they enforce uniqueness for displayNames?
Here's a link to AD attribute info.
Here's a link to DirectoryEntry Properties.

Categories

Resources