How to edit the registry keys of a specific user programatically? - c#

I want to change a few settings of a Windows user that I created in my application. If I understand correctly, his "HKEY_CURRENT_USER" values will be under HKEY_USERS/<sid>/.... Is this correct? How can I get the sid of the user, if I know the user name and the domain?
Edit: How can I correctly edit the HKCU keys of that user, if I have the sid already?

I have a program that does exactly that. Here is the relevant part of the code:
NTAccount ntuser = new NTAccount(strUser);
SecurityIdentifier sID = (SecurityIdentifier) ntuser.Translate(typeof(SecurityIdentifier));
strSID = sID.ToString();
You will need to import two namespaces:
using System.DirectoryServices;
using System.Security.Principal;
Hope this helps.
Then use Registry.Users.SetValue with SID string\path to set the registry value.
This might not work as intended if you are editing a logged-off profile, especially a roaming profile.

There are two steps to this. First you must get the users sid. Second you must load the users registry hive. Other users hives are not loaded by default so you must load it explicitly.
The answer in Daniel White's comment is the best way to get the sid.
To load the user's registry hive, use the LoadUserProfile windows API via pinvoke. There is a complementary UnloadUserProfile to unload the hive when you are done with it.

You can use Query by example and search using PrincipalSearcher for appropriate UserPrincipal
// Since you know the domain and user
PrincipalContext context = new PrincipalContext(ContextType.Domain);
// Create the principal user object from the context
UserPrincipal usr = new UserPrincipal(context);
usr .GivenName = "Jim";
usr .Surname = "Daly";
// Create a PrincipalSearcher object.
PrincipalSearcher ps = new PrincipalSearcher(usr);
PrincipalSearchResult<Principal> results = ps.FindAll();
foreach (UserPrincipal user in results) {
if(user.DisplayName == userName) {
var usersSid = user.Sid.ToString();
}
}

Related

UserPrincipal doesn't let me add new user

I'm having a bit of a problem when trying to add a new user or trying to access an already existing user in the Active Directory through my C# program.
var principalContext = new PrincipalContext(ContextType.Domain, "domain", "OU=Users,OU=SI");
UserPrincipal usr = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountStr);
It's throwing the "An operations error ocurred" exception. I found out that it's supposed to that if the user doesn't have the right permission for the Active Directory but I am running the program as administrator with the account of someone who can add users to the AD. So I really don't know what could be wrong. When I try to add a new user I try it like this:
var principalContext = new PrincipalContext(ContextType.Domain, "domain", "OU=Users,OU=SI");
UserPrincipal usr = new UserPrincipal(principalContext);
The code for the existing user already breaks when I call FindByIdentity. The code for the new user however breaks after I try to set some values for the new user principle. For example:
usr.Surname = sn;
The extended error says it's:
SvcErr: DSID-031007DF, problem 5012 (DIR_ERROR)
So any idea as to what might be causing it if it's not a permission problem?
You have to provide a full Distinguished Name for the OU, including the domain. This is not valid:
"OU=Users,OU=SI"
Something like this would be (if your domain was "domain.com"):
"OU=Users,OU=SI,DC=domain,DC=com"

Is the GUID Property of a UserPrinciple Object in Active Directory Unique and Non-spoofable?

We are using Windows active directory to log users in without a password. The way we are currently doing it like this:
using System.DirectoryServices.AccountManagement;
var context = new PrincipalContext(ContextType.Domain, System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName);
var result = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, Environment.UserName);
Then we have a stored SamAccountName in our database which we match against the returned result.SamAccountName
This is definitely not secure as users could have the same SamAccountName and log in using that.
We are exploring the use of the GUID which exists on the UserPrinciple (result.GUID). My question is, is this variable non-spoofable on the windows side? Can we match the GUID that exists on the UserPrincple object with a variable we store on our database? Is this secure? Does this property always exist on an AD UserPrinciple? If not, how would we securely authenticate a user through this Windows Active Directory Login?
"sAMAccountName" is unique in a domain.
But you can also use both "objectSID" and "objectGIUD" for this purpose,this fields remain unchanged.
Note That If an object is moved to another domain, the objectSID changes, but not the objectGUID.
Overall, the best choice is "objectGIUD"
according to https://social.technet.microsoft.com/Forums/windowsserver/en-US/a5c0a863-cad1-4df8-a194-cb58f24ab1e6/is-objectguid-unique-in-the-domainforest?forum=winserverDS

Get WindowsPrincipal from UserPrincipal

The goal
I'm writing a class that abstracts various Windows user mechanics. My class knows about the user's account name and domain, if any. I am trying to hydrate a property that indicates whether the user has administrative privilege on either the domain or the local environment that it belongs to.
The problem
The WindowsPrincipal class provides that information via IsInRole, but it's constructor requires a WindowsIdentity, which I can't find a way to establish without a user principal name (UPN). The UserPrincipal.UserPrincipalName property is available for domain users, but null for local users. Is there another way to get a WindowsPrincipal from a UserPrincipal? Alternatively, is there another way to accomplish the goal without it?
The source
using (PrincipalContext principalContext = new PrincipalContext(PrincipalContextType, principalContextName))
{
using (UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.Name, Name))
{
// Capture additional information from the user principal.
Certificates = userPrincipal.Certificates;
DisplayName = userPrincipal.DisplayName;
UserPrincipalName = userPrincipal.UserPrincipalName;
// This constructor blows up because UserPrincipalName is null for local users.
using (WindowsIdentity windowsIdentity = new WindowsIdentity(UserPrincipalName))
{
// Capture group membership information about the specified user.
WindowsPrincipal windowsPrincipal = new WindowsPrincipal(windowsIdentity);
// Determine if the user has administrative privilege on the domain or local machine.
HasAdministrativePrivilege = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
}
Thanks in advance.

Using UserPrincipal.FindByIdentity and PrincipalContext with nested OU

Here is what I am trying to achieve:
I have a nested OU structure that is about 5 levels deep.
OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com
I am trying to find out if the user has permissions/exists at OU=Portal.
Here's a snippet of what I currently have:
PrincipalContext domain = new PrincipalContext(
ContextType.Domain,
"test.com",
"OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, myusername);
PrincipalSearchResult<Principal> group = user.GetAuthorizationGroups();
For some unknown reason, the value user generated from the above code is always null. However, if I were to drop all the OU as follows:
PrincipalContext domain = new PrincipalContext(
ContextType.Domain,
"test.com",
"DC=test,DC=com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, myusername);
PrincipalSearchResult<Principal> group = user.GetAuthorizationGroups();
this would work just fine and return me the correct user. I am simply trying to reduce the number of results as opposed to getting everything from AD.
Is there anything that I am doing wrong? I've Googled for hours and tested various combinations without much luck.
Well, if
UserPrincipal.FindByIdentity(context, identityType, username) == null
then the user has not been found, which in your case probably is, because the user isn't defined in the OU= you are setting as container in your Context.
After much exploring, experimentation, googling and searching through stack overflow; it appears that .NET does not have a built in method to 'read' a particular OU that has a reference to an external Group that contains users as its members. Unfortunately, the suggested and recommended solution is to retrieve at domain level and perform some form of custom filtering.
Is the user you're looking for inside OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com ?
What does your user object look like after your second search? What is it's DistinguishedName property?
The search you have in your first example will only search for objects inside that sub-sub-sub-sub-OU (the OU=Portal, .... that you have).
If your user exists in some other OU, then you have to search from the top of the domain - or inside the OU where the user actually exists (or any of its parents).
The user does not exist there, or you would not get null returned.
What is your end game? What do you mean by:
I am trying to find out if the user has permissions at
OU=Portal.
What type of permissions are you looking for? Admin delegation?
Hope this is of some help, I was having the same problem trying to retrieve groups from a nested OU. The structure of the ou was Groups > WebGroups. So I was writing the following...
var ctx = new PrincipalContext(ContextType.Domain, "domain", "OU=Groups,OU=WebGroups,DC=domain,DC=ie", "username", "password")
Turns out the order matters, WebGroups has to come first. When I changed it to the following my code worked...
var ctx = new PrincipalContext(ContextType.Domain, "domain", "OU=WebGroups,OU=Groups,DC=domain,DC=ie", "username", "password")
So I'm assuming you'd have to write "OU=Admin,OU=Groups... OU=Portal" to get yours working.

ASP.NET Active Directory C# field specification

We've got an active directory here. provided the unique user id of the user, I need to access the organization->manager->name attribute related to that userid. Basically this will be used to send an approval form to the manager of the person submitting request.
Any idea how this could be done?
You can use the following code :
/* Retreiving object from SID
*/
string SidLDAPURLForm = "LDAP://WM2008R2ENT:389/<SID={0}>";
System.Security.Principal.SecurityIdentifier sidToFind = new System.Security.Principal.SecurityIdentifier("S-1-5-21-3115856885-816991240-3296679909-1106");
/*
System.Security.Principal.NTAccount user = new System.Security.Principal.NTAccount("SomeUsername");
System.Security.Principal.SecurityIdentifier sidToFind = user.Translate(System.Security.Principal.SecurityIdentifier)
*/
DirectoryEntry userEntry = new DirectoryEntry(string.Format(SidLDAPURLForm, sidToFind.Value));
string managerDn = userEntry.Properties["manager"].Value.ToString();
But you can also find in this post other ways to seach bind to Active-directory.
Since you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
}
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
// if found....
if (group != null)
{
// iterate over members
foreach (Principal p in group.GetMembers())
{
Console.WriteLine("{0}: {1}", p.StructuralObjectClass, p.DisplayName);
// do whatever you need to do to those members
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
I'm not 100% sure what you want to do in your concrete case... the UserPrincipal has an EmployeeId property - is that what you want to search for?
Use the System.DirectoryServices.DirectoryEntry class to read out the appropriate property of the user object. The constructor of DirectoryEntry requires that you have an LDAP path to the user. Getting the LDAP path can often be tricky though as IIS prefers handing over the SAM account name only. If you provide more details of what the user id you have looks like it is easier to point you in the right direction.
To do this the account which runs the ASP.NET application needs read access to the AD, which probably doesn't have by default. Changing the application pool to run under "NetworkService" is the easiest way if the web server belongs to the AD. The ASP.NET app will then use the MACHINE$ account of the server to access the AD.

Categories