Permissions required to retrieve local group members with ADSI - c#

Using ADSI I can query the members of the local Administrator group on a given computer by doing (for example in PowerShell) :
([ADSI]"WinNT://computer-name/Administrators,Group").Invoke("members")
To do this, as far as I can tell, the user running the PowerShell script requires Administrator privileges on the target machine - that is, the user needs to be directly on indirectly in the local administrator group of computer-name (eg. by being a member of "Domain Admins").
This surprised me because a non-administrator account who can login to computer-name (eg. a user that's part of "Domain Users" and nothing else) can open the local users & groups application, and view the members of the local administrator group. No specific rights are required when doing it manually, yet ADSI seems to require it.
So my questions are:
Is it correct that using ADSI you need Administrator rights to access this information, or am I doing something wrong?
Is there a different approach to programmatically obtain this information, which requires less privileges than an Administrator account ? (If there are solutions that are not available in PowerShell that's fine, my targets are C#/.NET Core )
Please note I want to run this remotely on other workstations - not just on the local workstation.

ADSI is built on top of WMI. By default, only the local Administrators group is allowed to make remote WMI calls and read a computers local directory data.
You can change the permissions on the OS by going into Computer Management (local) -> Services and Applications -> WMI Control. Right click on WMI Control and choose Properties.
I've only experimented with allowing all reads, which you can set on the root folder. I did some research and you may be able to restrict this to just LDAP. On the Security tab drill down to Root -> directory -> LDAP. You'll want to adjust permissions on the LDAP entry (or maybe more?). The key permission is Remote Enable.
Update
To query WMI directly from PowerShell.
Remote WMI over PowerShell: https://learn.microsoft.com/en-us/windows/win32/wmisdk/connecting-to-wmi-on-a-remote-computer.
Custom PowerShell method for listing remote group membership through WMI: https://gallery.technet.microsoft.com/scriptcenter/List-local-group-members-c25dbcc4

I think your ADSI approach should work, at least when executed locally.
I used a c# snippet I grabbed from this SO answer: https://stackoverflow.com/a/8192062/3374994.
To test whether it could run from regular user permissions, I used
Runas /user:regularuser GetLocalUsers.exe.
I believe this shows that an ADSI approach would not necessarily require elevated privileges.
However, was your intention to run the code remotely?
var path = string.Format("WinNT://{0},computer", Environment.MachineName);
using (var computerEntry = new DirectoryEntry(path))
{
var userNames = from DirectoryEntry childEntry in computerEntry.Children
where childEntry.SchemaClassName == "User"
select childEntry.Name;
foreach (var name in userNames)
Console.WriteLine(name);
}

Related

Accessing Network Path in Windows Service C#

I have developed a windows service using local system as Account. I have used
network path of file
FileInfo fi = new FileInfo(#"\\epm-server\penDocuments_LabMeds\" + Convert.ToString(dr["mrn"]) + "\\SyncedXML\\" + Convert.ToString(dr["xmlfile"]));
if (!fi.Exists)
boolFileNotFound = true;
A dynamic path of a file that is built from database.
It works fine when I run Windows Service in Debug Mode, but when I install it then fileNotExists returns TRUE always like the file doesnt exist but infact it does exist.
This is bugging me a lot now. Kindly help me why its not working. Its a server path. Its getting opened in my PC.
Thanks
Did you notice the double backslashes in front and after SyncedXML (\\SyncedXML\\)?
This is probably the cause of your error.
Additionally I'd use string.Format in such cases to reduce the inadvertently addition of unwanted characters:
var path = string.Format(#"\\epm-server\penDocuments_LabMeds\{0}\SyncedXML\{1}", dr[mrn], dr[xmlfile]);
var fi = new FileInfo(path);
Edit:
If it's permission-related, then it's very likely that your local system account (in whose context the service is running) isn't allowed to access the epm-server.
The path is accessible if you're opening it directly or if you're running the service in debug mode as this is happening in your user context (e.g. YOURDOMAIN\vickyshazad), and you're allowed to access the ressource, whereas NT AUTHORITY\SYSTEM is not.
It's usually a good practise to have a special service account for your developed windows service and grant this user only and exactly the required permissions (least privilege). Maybe ask your system administrator for a service user.
Local System (NT AUTHORITY\SYSTEM) is a highly privileged account that's not recommended to use in general (see MSDN).
Most services do not need such a high privilege level. If your service does not need these privileges, and it is not an interactive service, consider using the LocalService account or the NetworkService account.

Retrieve LDAP information offline

I have the simple code below
using (DirectoryEntry deLocal = new DirectoryEntry("WinNT://" + domainName+"/"+userName))
{
foreach (string propName in deLocal.Properties.PropertyNames)
{
Console.WriteLine(propName+":"+Convert.ToString(deLocal.Properties[propName].Value));
}
}
And similarly using LDAP://.
I want to be able to access the same, or most of, the same data when the computer is not inside the domain, like a laptop a user takes home from the office. When the laptop is in the domain/network/vpn it has access to all of the information from the LDAP server.
Is there a similar way to access the while the laptop is on another network, logged in as the domain/user?
You might want to check out the Active Directory Lightweight Directory Services (AD LDS).
This is a subset of the full-blown AD - but basically works much the same way. It's installable on local machines, down to Windows XP, and can be run as a service and thus it can also be turned off when not needed.
Microsoft also has tools to synchronize between a central AD instance, and a local AD LDS setup.

Get windows users with C#

How can I get a list of all windows users of the local machine with the usage of .NET (C#) ?
Here is a blog post (with code) that explains how to do it:
http://csharptuning.blogspot.com/2007/09/how-to-get-list-of-windows-user-in-c.html
The author lists the following code (quoted from the above site):
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName);
DirectoryEntry admGroup = localMachine.Children.Find("users","group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
lstUsers.Items.Add(member.Name);
}
You need to add using System.DirectoryServices at the top of your code. To change machines, you would change the Environment.MachineName to be whatever machine you want to access (as long as you have permission to do so and the firewall isn't blocking you from doing so). I also modified the author's code to look at the users group instead of the administrators group.
It depends on what you are really 'after'... if you are on a windows domain (using active directory) then you can query Active Directory IF active directory is being used to limit the users who are "authorized" to use the local machine.
If your requirements are not as stringent then you can inspect the folders in the system UserProfiles where each folder except Default User and All Users represent a user profile that has logged into the local machine. caution this may include system and/or service accounts...

How to access HKCU registry of currently logged on user(s), from a service?

From within a windows service I want to check some user preferences that are stored within each users' HKCU registry area. How can I do this?
I see that HKEY_USERS has subkeys of each user that has logged in to the machine (or something like that?), and within these are the HKCU areas for each user. However, these subkeys are the SIDs of the users, so somehow I'd need to work out the SID of the currently logged in user(s).
I would then query HKEY_USERS\<the users SID>\whichever\key\i\need in place of querying HKEY_CURRENT_USER\whichever\key\i\need.
From this question I can get a list of the current users on the machine in DOMAIN\USER format. Is there a way to get the SID of a user from their windows login? Or is there a more direct way to get the registry path that is HKCU for the currently logged in user(s)?
In HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList you will find the SID's of the existing profiles. The ProfileImagePath will give the path of the profile.
Most of the time this path is the username. But it could be another path if a similar path already existed when the profile was created.
The short SID's like S-1-5-18 (=> Local System) are default local accounts (https://support.microsoft.com/en-us/kb/243330)
In order to do this you will need to do one of the following
Impersonate the users credentials and access HKCU from that impersonation context
Read the registry file directly off of disk (this has threading and data integrity implications).
I'm not 100% sure that #1 will work but I believe it will.
For either solution though you will need either the users credentials or access token in your process. This is not easily available because it's a security issue.
You can connect to their remote registry, then search the entire HKU key for their username (i.e. jsmith). Various entries reference their user profile; these will pop up then you can just look under which SID those entries are located. Bit of a roundabout way of doing it, but seems to work.
Using PowerShell you can match them up:
Get-ItemProperty -path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" | select ProfileImagePath, PSChildName
You can even search by username (eg john):
Get-ItemProperty -path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" | ? {$_.ProfileImagePath -match "john"} | select ProfileImagePath, PSChildName
Bonus: reverse SID lookup using PowerShell (will return DOMAIN\USERNAME)
$objSID = New-Object System.Security.Principal.SecurityIdentifier ("S-1-5-21-2139915555-1840087203-3974481593-26737")
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$objUser.Value

How can I grant permissions for a FTP Folder for users of the active directory

I have a asp.net web application that creates a Folder on a FTP site, but I need to grant permissions to that folder to some users of the active directory.
How can I do that programatically, inside my app when I create the folder?
Standard FTP does not allow for changing of permissions or file ownership, but it is reasonably common for FTP servers to allow this sort of thing through custom SITE commands. At least, this is common for UNIX hosted FTP servers.
Try connecting to the server with a standard FTP client and typing site help. That should give you a list of the custom SITE commands available. You can then get the usage for a specific command by typing site help <cmd>.
I'm not sure what your options will be against a Windows-based server, as the permissions model is more granular than with standard UNIX permissions. On a UNIX server, you can often change the group ownership and the permissions, but changing the file owner is not allowed unless you're root. I shouldn't have to say that logging into an FTP server as root is a really bad idea.
I hope that's some help.
R
You don't say what kind of FTP server you are running, or in what manner you want the AD user to interact with the new folder. I'm assuming that you are using IIS's FTP Service, and that you need your AD users to be able to use FTP to access the new folder.
IIS uses the same access rights as other file access methods in Windows (accessed from the Security tab on the folder's Properties view).
I created a WinForm that would do the following: create a new local user account (and add them to a group), create a new directory under our FTP Server's base directory, create a new Virtual FTP Folder (so that a user could map a connection to the folder), and finally give the new user full control of the new directory (and because the directory was a Virtual Folder, the new user has full control via FTP).
It sounds like you don't need to create the new user, you just need to give them permission to make changes to the new directory. In my app I shell out to invoke a batch file and pass it a few parameter.
The command for modifying a directory (or file's) access rights is:
cacls [The path to your new directory] /E /P [The AD user name]:C
I don't recall what those parameters mean any more; I suggest you research the command to make sure it does exactly what you want.
To execute this from VB.Net:
Shell(System.Configuration.ConfigurationSettings.AppSettings.Item("CACLS_batPath") & " " & strFolderPath & " " & _strUserName, AppWinStyle.NormalFocus, True, -1)
That will open a visible command window and execute the batch file, passing it the parameters. (It just occured to me that I'm doing this in WinForms and it might not be that easy to call from ASP.Net, check here for information about executing a batch file from ASP.Net: http://codebetter.com/blogs/brendan.tompkins/archive/2004/05/13/13484.aspx)
The contents of the BAT file are:
echo "update file permissions"
cacls %1 /E /P %2:C
REM PAUSE
Good Luck!
I'm assuming that you are creating the folder and want to set the permissions in a c# application.
You want to look at the DirectorySecurity class (msdn)
You create a directorySecurity class for the newly created directory and then you add FileSystemAccessRules to define the appropriate access based on AD users and groups.

Categories

Resources