I want to write a function in a windows service application to remove a given computer name from Active Directory.
The Windows service is running on a machine which is domain-joined to the DC. Currently I have logged in to this machine with domain admin account.
The Windows service is running under the security context of "NT AUTHORITY/SYSTEM" and this should not be changed, as there shouldn't be any user interaction after installing the application, meaning that admin shouldn't enter their credentials in services.
When I run the application with my newly added code to delete the computer account, it doesn't work. However, when I change the logon info on the Windows Service and update that with domain admin credentials, it's able to successfully remove the computer account from AD.
Below is [a shortened version of] the function used to delete computer accounts.
Is there any way I can modify the code to be able to remove Computer Accounts using the same security context (NT AUTHORITY/SYSTEM)?
private void DeleteComputerAccount(string CompName, DirectoryEntry DirEntry)
{
try
{
//Delete computer account
DirectorySearcher search = new DirectorySearcher(DirEntry, "(name=" + CompName + ")");
SearchResult res = search.FindOne();
res.GetDirectoryEntry().DeleteTree();
}
catch (Exception)
{
Throw();
}
}
Where DeleteComputerAccount is called:
DirectoryEntry dirEntry = new DirectoryEntry("LDAP://domain.contoso.com");
string compName = "MyWorkstation01";
DeleteComputerAccount(compName, dirEntry);
When a service runs as local system, it will access the network (and thus AD) in the security context of the host's computer account. You can delegate the computer account (or better, a group which the computer is a member of) the ability to delete objects from AD. This link has accurate advice on how to complete that task - http://sigkillit.com/2013/06/12/delegate-adddelete-computer-objects-in-ad/
While not what you asked, a couple other things stand out to me:
You're not filtering your search very well. You might get something other than what you want back. I'd suggest a filter like this instead: (&(objectClass=computer)(sAMAccountName=<PCName>$))
Local system is a lot of access. Could you run this as network service instead?
Related
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.
I have an intranet application that authenticates users against active directory. In general it works fine when testing locally(i.e. from my dev machine using VS 2017) or running it off of IIS in app server ("Browse *:80") until I try to access it using its URL from my local machine. Then whatever user id I use to get user's detail info, nothing is displayed.
Also, this app is to be accessible to users in certain groups so application checks logged in user's group membership.
Here is how I have it set up in IIS and different scenarios I tested with:
I set authentication to "Windows Authentication", disabled anonymous
authentication and enabled AS.NET Impersonation. This works fine when
running from app server using localhost but when trying to access
from my local machine and supplying user's user id to get his/her
detail info, no details are displayed (fails to get any info from
AD).
If I enable anonymous authentication and set it to "Application Pool Identity" (i.e. Network Services), it displays my custom
"Access Denied" page, presumably because this user is not part of
allowed group.
If I enable anonymous authentication and select "Specific User", supplying my credentials, then everything works fine, from app server or from my local machine, with one caveat: no matter who is accessing the site, it shows my name as logged in user.
I am stumped ad would appreciate some hints.
Update - Added code to get user info
Code to get user's identity:
WindowsIdentity wiUser = WindowsIdentity.GetCurrent();
string sID = wiUser.Name.ToUpper();
Code to get user's AD info:
static string adDomain = "www.xxx.yyy.zzz";
static string adContainer = "DC=www,DC=xxx,DC=yyy,DC=zzz";
public static DataTable getUserADInfoDT(string sSearchStr)
{
DataTable dtResults = new DataTable();
dtResults.Columns.Add("ID");
dtResults.Columns.Add("FirstName");
...
dtResults.Columns.Add("Zip");
string adDomain = string.Empty;
string adContainer = string.Empty;
// create domain context
PrincipalContext adPrincipalContext = new PrincipalContext(ContextType.Domain, adDomain, adContainer);
using (adPrincipalContext)
{
// define a "query-by-example" principal
UserPrincipal qbeUser = new UserPrincipal(adPrincipalContext);
qbeUser.SamAccountName = sSearchStr.Trim().ToUpper();
// create principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
PrincipalSearchResult<Principal> psr = srch.FindAll();
// find all matches
foreach (var found in psr)
{
DataRow dr = dtResults.NewRow();
UserPrincipal up = (UserPrincipal)found;
DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject();
dr["ID"] = de.Properties["SAMAccountName"].Value.ToString().ToUpper();
if (de.Properties["givenName"].Value != null)
dr["FirstName"] = de.Properties["givenName"].Value.ToString();
...
if (de.Properties["postalCode"].Value != null)
dr["Zip"] = de.Properties["postalCode"].Value.ToString();
dtResults.Rows.Add(dr);
//de.Close();
}
return dtResults;
}
}
WindowsIdentity.GetCurrent() will get the user account that the current process is running under. If the application is running in IIS, that will usually be the user account that the app pool is running under - not the user that logged in to your application.
If you use IIS Express for debugging locally, then IIS Express is running under your user account, so you wouldn't see a difference. But on the server you will.
That said, if you have impersonation turned on and working properly, then WindowsIdentity.GetCurrent() should return the user that logged in - since impersonation means that your app is now pretending to be that person. So this might mean that impersonation isn't setup right. But maybe you don't even need it. I've never personally found a need to use impersonation.
To get the user that logged into your app, you should use HttpRequest.Current.User or possibly just HttpContext.User if your app is ASP.NET MVC.
And there is actually an easier way to get a DirectoryEntry object for the user, without having to search for the username. You can bind directly to the SID of the user, which is information you already have:
var user = new DirectoryEntry(
$"LDAP://<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");
If your server is not joined to the same domain as your users, then you might need to include the domain name:
var user = new DirectoryEntry(
$"LDAP://www.xxx.yyy.zzz/<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");
Something else to keep in mind: DirectoryEntry keeps a cache of all the Properties. When you access a property that it doesn't have in the cache already, it will go out to AD and ask for every attribute with a value. That's a lot of useless data going over the network if you only need 3 attributes. So you can tell it to specifically ask for only those 4 attributes before you use them:
user.RefreshCache(new[] {"SAMAccountName", "givenName", "postalCode"});
I talked about this, and other points, in an article I wrote: Active Directory: Better Performance
Update: To verify if IIS is actually enforcing authentication for a page, you can do this in PowerShell (update the URL to what you need - and this requires at least PowerShell 3):
try { (Invoke-WebRequest "http://example.com/the/page").StatusCode }
catch { $_.Exception.Response.StatusCode.Value__}
That will output the status code of the reply. You should see 401, since that is how IIS challenges the browser to send credentials. If you see 200, then Windows Authentication is not working for that page.
I do this in PowerShell, since Chrome's dev tools won't even show you the 401 challenge.
I hope somebody could help me to solve my problem.
There is some domain, and some shared directory in that domain. User has domain username and password, to get access to that shared directory, but his computer is not in domain most of time. I mean, when he need to connect to that directory he is typing network path (for example "\\fileserver"), than windows asks for username and password, and after writing it he will get the access until restart windows or logout.
So my question is, how can i get domain user name that user uses to connect to network, by using C# code?
I tryed to use CredentialCache.GetCredential, but i think it works only if user connect to shared directory by using NetworkCredentials in C# program. If connection already was before C# program started, it will be empty.
I also tryed Environment.UserName, but since computer is not in domain, it returns only local user name, that different from domain username.
Sorry for my bad english, i hope you could understand my explanation.
Have you took a look at the nugget package CredentialManagement ?
I used it already, but couldn't roam credential saved with enterprise persistence
using CredentialManagement;
using (CredentialSet credentials = new CredentialSet())
{
Debug.Print(credentials.Count.ToString() + " credential(s) found.");
foreach (Credential credential in credentials)
{
Debug.Print(credential.Target); //This is domain/IP of the saved credential.
Debug.Print(credential.Type.ToString());
Debug.Print(credential.Username);
Debug.Print(credential.PersistanceType.ToString());
Debug.Print(credential.Description);
Debug.Print(credential.LastWriteTime.ToString());
}
Debug.Print("End");
}
I've created a small application which attempts to authenticate a user based on their username and password. This application works correctly when run on the same domain which Active Directory resides on.
I must now extend the application to also work on domains which are "closed" in terms of security and permissions. In other words, is there a way to run the application based on an administrator account, or an account which has the necessary permissions to access the Active Directory?
This is the code I have used to authenticate a user:
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, server + ":" + port))
{
if (pc.ValidateCredentials(username, password))
{
valid = true;
}
else
{
valid = false;
}
}
The above code works perfectly, however I would like to modify it so that it can communicate with the Active Directory database in an authenticated manner.
I have read numerous documentation and resources, but have not found anything. The closes I found was an article mentioning that IIS has to be set up and configured in a specific manner. However, my application is a simple C# application, and IIS is not being used.
If I understand the question properly, you want to execute ValidateCredentials using a different user than the current process' user.
I may be missing something, but have you tried modifying your code this way?
using (PrincipalContext pc =
new PrincipalContext(ContextType.Domain,
server + ":" + port,
specialAccountUsername,
specialAccountPassword))
{
return pc.ValidateCredentials(username, password);
}
It simply uses a constructor that takes the special account you are using for accessing the domain.
I am creating the asp.net web application. In that I used active directory for login. I have one group named Validusers, when user logged in it checks the user to that validusers group. If the user exists in the group then login is successful, if not login failed.
I did all the things, it works good in my local machine, but it is not working when I publishing the website. I got the following error
Logon failure: unknown user name or bad password.
On my machine it works good,while publishing i got an error.i used below code for your reference
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "DomainName");
System.Security.Principal.WindowsIdentity MyIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
string LogUser = MyIdentity.Name.ToString();
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, LogUser);
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "Validusers");Validusers---->Groupname
if (user.IsMemberOf(group))
{
Login success
}
else
{
Login Failed
}
it will check logged user with Validusers group.if the user exists in the group then login is success other wise failed login. I got an error when i published this website.please give some solution
Although you had supplied so little information about the domain and network relationship with your development machine and web server, I assume the web server has no physical connection to the Active Directory server that you depend your code on. Then it should be impossible for web server to query the AD directory.
If web and AD servers are on same network than you may need to work on the firewall settings of both web server and AD server to make sure that they can communicate.
If web and AD server have no communication problems you should check the availability of that "Validusers" to a code running at web server.
As IIS applications run with the user account that is defined for the application pool that hosts the application, you should make sure that the app pool identity has enough rights to access those delicate information.