How to find global catalog of Active Directory? - c#

I want to search for users in an Active Directory environment with GC://DC=xxx,DC=yyy,DC=zzz format. But how can I programmatically find the global catalogs in an arbitary Active Directory environment? Does each domain name correspond to a global catalog always? Any alternative means I can try?
Note: The Forest.FindAllGlobalCatalogs() returns a list of server names but I'm actually not able to search using them.
Edit1: Here's what I want to do : Suppose my activedirectory has a domain called domain1.root.com, then I will use GC://DC=domain1,DC=root,DC=com to search for a user. But is this always a Global catalog? Must every domain have a global catalog?
Edit2: I am now able to search for users using the following code:
var currentForest = Forest.GetCurrentForest();
var globalCatalog = currentForest.FindGlobalCatalog();
Console.WriteLine(globalCatalog.Name);
//DirectorySearcher searcher = new DirectorySearcher("GC://"+y.Name);
DirectorySearcher searcher = globalCatalog.GetDirectorySearcher();
searcher.Filter = #"samaccountname=skaranth";
Console.WriteLine(searcher.SearchRoot.Path);
var result = searcher.FindOne();
if(result!=null)
Console.WriteLine(result.Properties["distinguishedname"][0]);
searcher.Dispose();
globalCatalog.Dispose();
currentForest.Dispose();

What exactly do you want to achieve with this??
The Global Catalog is a special subset of attributes that are stored on certain domain controllers. While each domain controller has a full set of attributes and object for that one domain, the Global Catalog contains data from all of the domains in the AD forest.
So the GC really only comes into play when you need to find things across multiple domains. If you have just a single domain, the GC won't really help you at all.
Forest.FindAllGlobalCatalogs() will indeed give you the list of all domain controller servers that contain a global catalog data set. So why can't you use those to search?? Can you show us what you've tried so far??
The Global Catalog is just that - global - e.g. you shouldn't have any reason at all to want to specify a specific server..... the servers should all have the same set of data anyway.
So again: why do you feel the need to find a server with a global catalog, and what do you want to do with that information once you have it? Why do you feel the need to specify a server when doing a global catalog search??

Usingng that search string format Active Directory will handle finding a GC server for you when you submit your query. It'll do lookups based on that AD site structure, find the closest GC server and use that server to query against.
Edit:
In answer to your edit, using the GC:// prefix indicates that you are interested in doing a Global Catalog search so it will always use a global catalog server, so yes to your question about it always being a Global Catalog. It's when you prefix your search string with LDAP:// that you'll hit a domain controller and will have to deal with non-global attributes. No need to figure out a specific server, AD will do that for you.
Here's a search string that will get you a user by their user principal name, return the userPrincipalName, cn, and distinguisedName attribute values (if any), and do a subtree search starting at the root of the domain:
GC://domain1.root.com;(&(objectClass=user)(objectCategory=Person)(userPrincipalName=myuser));userPrincipalName,cn,distinguishedName;subtree
Keep in mind that you'll then have to do an LDAP:// search to get the attributes that are not stored in the Global Catalog, binding to the path value of the distinguishedName returned by the GC search.

Related

NT AUTHORITY\Local Service is not listed in the Access Control List of a directory

I'm having this issue where I'm trying to check if NT\Authority Local Service has read\execute permissions on a directory (folder). The product that I work on REQUIRES that the folder the user is installing to has read\execute permissions set for Local Service.
The problem is that when I get the Access Control List (ACL) recursively (groups-within-groups), Local Service is not listed so I can't check if he has permissions to that folder or not.
By default, Local Service does not have read/execute permissions to user profiles (My Documents, Desktop, etc...) but I won't know if Local Service has access to other directories the user chooses to install to.
NOTE: Local Service DOES have access to Program Files, even though it is NOT listed in the ACL. Is it hidden somewhere else?
This is a short snippet on how I'm pulling the ACL:
GroupPrincipal groupPrincipal =
GroupPrincipal.FindByIdentity(principalContext, identityReferenceValue);
// GetMembers(true) is recursive (groups-within-groups)
foreach (var member in groupPrincipal.GetMembers(true)) {
if (member.SamAccountName.Equals("LOCAL SERVICE")) {
foundLocalService = true;
break;
}
}
Is there any other way I should be doing this? (Other than adding an access rule for Local Service on that directory)
Is Local Service just not listed in Directories ACL's?
Any help would be greatly appreciated.
It's notoriously difficult to calculate "effective permissions" for an account. But the simple answer to your question is that you will likely want to look for either on of:
The local Users group, sometimes shown as BUILTIN\Users or COMPUTERNAME\Users, or
Authenticated Users, sometimes shown as NT AUTHORITY\Authenticated Users.
Authenticated Users is one of the well-know SIDs. It is "a group that includes all users whose identities were authenticated when they logged on.". As long as you can prove who you are, you are included in Authenticated Users. The SID for this is always S-1-5-11 on every Windows computer.
However, it's not really considered a real group. To find it when adding permissions to a folder, you have to have "Built-in security principals" selected under "Select this object type":
The local Users group contains Authenticated Users by default. On my computer, I actually see both Users and Authenticated Users in the default permissions on the file system.
That's what you will most likely see, and that's likely all that matters.
But that's not the only way. You could see Everyone (S-1-1-0), which includes every user, authenticated or not.
Or, it could be a file or folder that has the LOCAL SERVICE account as the owner.
Or, there could be a local group that was created manually and LOCAL SERVICE was added to.
One way to get a more authoritative list of what you can look for is to run this under the LOCAL SERVICE account:
whoami /groups
That will tell you every group in the authentication token, which is every group that you are considered a member of for authentication purposes.
But you can't just open a command prompt as LOCAL SERVICE. So one way to do this is to open the Task Scheduler and create a task that runs under LOCAL SERVICE, with the action of:
Program: cmd
Arguments: /c "whoami /groups > C:\temp\localservice.txt"
Then run the task and, when it's done, look at C:\temp\localservice.txt. It will have a table of group names and their SIDs that you can look for.

How to use %username% variable in Active Directory user paths using C#?

I developed an Active Directory user management tool in C# that does some things automatically. This is better for my particular case than the "Active Directory Users and Computers" tool from Microsoft. I have to use roaming profiles and home directories which contain the username of a user.
Now I want to change the username of a user with my program in C#. Everything works fine, but I want to use the "%username%" variable instead of putting the new username directly into these paths (home directory and roaming profile), because, by using the variable, I ensure that the new username will be placed into these paths if I copy the user object using Microsoft's AD management tool (right click --> "copy").
If I enter "%username%" when creating or editing a user with Microsoft's AD tool, this variable gets replaced by the username, so it works. But if I put this variable into a path using C#, it just puts the string "%username%" at the end of the path (e.g. "\fileserver1\UserHomes\%username%"). It doesn't replace it with the actual username and "stores" that placeholder.
How can I use this variable within my C# code properly, so that it get's replaced with the actual username?
I'm using this code (reduced, it's just an example) to change the users username (SamAccountName) and, for example, the home directory. "user" is my UserPrincipal object. Of course, I'm renaming the actual folder after this:
[...]
string newUsername = "NewUsername"; // New username
user.SamAccountName = newUsername;
user.UserPrincipalName = $"{ newUsername }#{ domain }";
user.HomeDirectory = "\\fileserver1\UserHomes\%username%";
user.Save(); // Save changes
The %username% environment variable and the one used AD Users and Computers is not the same. AD Users and Computers replaces it with the actual username value immediately when you click OK. It is not something that AD understands, just a token used by the AD Users and Computers application.
Considering you already constructed a string using the username, you shouldn't find it hard to fix this.
user.HomeDirectory = $"\\fileserver1\UserHomes\{newUsername}";
Also powershell would probably be more appropriate for something like this.
see Renaming a User Account Does Not Automatically Change the Profile Path

How to get the computer name of a person

Is it possible to get the computer name for a person that is found through Active Directory?
I am currently getting the person via DirectorySearcher object, and from there can get the user's SID, but i don't know where to look next in order to find the actual computer name that is assigned to that SID or person?
Or maybe there is an easier way to get this information from Active Directory?
There is no association of computers to users in default AD - so there is no way to get such information.
You may find last/currently logged on set of machines for a user (also I don't think this is available in AD, see How to find what user last logged onto a given computer through Active Directory in C#? for some starting points) or maybe you add custom information that gives that association in AD (Retrieving custom Active Directory properties of users).

Can Directory Searcher Query Operational Attributes?

I'm using the .net directory searcher to query data from OpenLDAP. It connects OK and I can query the user data, but operational attributes don't seem to be returned. This is a problem, as I need the entryUUID. I've tried adding "entryUUID" to the propertiesToLoad parameter when constructing the DirectorySearcher, and have also tried "+" (which according to the OpenLDAP documentation should return all operational attributes), but no joy.
Does anyone have any suggestions please?
I couldn't find a way of doing this, so in the end I used the LDAPConnection class in the DirectoryServices.Protocols namespace instead. The DirectorySearcher class seems to be primarily designed for querying Active Directory - while it can query other LDAP Directories its functionality is limited.

Getting the display name from a domain \ alias combo

Apologies for not knowing the right way to phrase this question.
Given a domain name and an alias, for example CONTOSO\steveh how can I get the friendly display name for that alias? For example, in Outlook email sent to CONTOSO\steveh appears as 'Steve Holt'?
If you are using .net 3.5, add references to System.DirectoryServices and System.DirectoryServices.AccountManagement and try this:
PrincipalContext c = new PrincipalContext(ContextType.Domain,"CONTOSO");
UserPrincipal principal = UserPrincipal.FindByIdentity(c,"steveh");
Console.WriteLine(principal.DisplayName);
I can't verify if it works for a domain since I'm running on a standalone machine but it should help you get started.
You can query ActiveDirectory through LDAP I recommend taking a look at this question which has some basic information. You should be able to get a general direction from there.

Categories