Remove permissions from a windows folder - c#

I have a widows folder on a remote server from which I wish to remove permissions for a specific user. I've tried numerous methods and nothing seems to work.
I get no errors with the following code but the permission remains intact. Am I not using the correct objects or missing some step with these objects? Any assistance would be greatly appreciated.
The dirName is passed as a share, e.g. \myserver\myfolder
private void removePermissions(string dirName, string username)
{
string user = System.Environment.UserDomainName + "\\" + username;
DirectoryInfo dirinfo = new DirectoryInfo(dirName);
DirectorySecurity dsec = dirinfo.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = dsec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
foreach (AccessRule rule in rules)
{
if (rule.IdentityReference.Value == user)
{
bool value;
dsec.PurgeAccessRules(rule.IdentityReference);
dsec.ModifyAccessRule(AccessControlModification.RemoveAll, rule, out value);
MessageBox.Show("Removed permission from " + dirName + " for " + user);
}
}
}

Once you have created the new ACL you need to apply it to the folder.
Add
dirinfo.SetAccessControl(dsec);
after the loop.

Related

How to get a valid DirectoryEntry?

I am trying to read an LDAP directory entry in a C# console application and I somehow don't succeed. Setting up the connection works fine:
private static LdapConnection ldapConnection;
public static void Main()
{
NetworkCredential credentials = new NetworkCredential("uid=admin,ou=system", "secret");
ldapConnection = new LdapConnection("127.0.0.1:10389");
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.AuthType = AuthType.Basic;
try
{
ldapConnection.Bind(credentials);
Console.WriteLine("Ldap connection is created.");
}
catch (Exception e)
{
Console.WriteLine("\r\nUnexpected exception occurred:\r\n\t" + e.GetType() + ":" + e.Message);
return;
}
//searchUsers();
//getSchemaClassName();
}
I can even find me some users:
private static void searchUsers()
{
string[] attrs = new string[] { "cn", "sn" };
string[] followedBy = new string[] { " ", "\n" };
var request = new SearchRequest("ou=users,ou=system", "(objectClass=*)", System.DirectoryServices.Protocols.SearchScope.Subtree, attrs);
var response = (SearchResponse)ldapConnection.SendRequest(request);
foreach (SearchResultEntry entry in response.Entries)
{
for (int i = 0; i < attrs.Length; i++)
{
if (entry.Attributes.Contains(attrs[i]))
{
Console.Write(entry.Attributes[attrs[i]].GetValues(Type.GetType("System.String"))[0] + followedBy[i]);
}
}
}
}
But when I for example try to read a directory entry's schema classname, I find that the directory entry contains a bunch of errors. This is the code:
private static void getSchemaClassName()
{
string path = "ldap://localhost:10389/cn=David,ou=users,ou=system"; //path is ok, points to an existing user
DirectoryEntry de = new DirectoryEntry(path, "uid=admin,ou=system", "secret");
Console.WriteLine("Schema classname:" + de.SchemaClassName); //breakpoint here
}
When I place a breakpoint on the "breakpoint here"-line and subsequently examine de properties, I find that Guid, Name, Options, Parent, SchemaClassName and some others display the "The directory service is unavailable" error. Which even makes sense to me, since ldapConnection isn't in any way involved in this code, whereas it is in searchUsers(). But I don't know how to connect this code to ldapConnection; every DirectoryEntry example I come across on the internet apparently works without the connection. How can I get this fixed?
Thx, Cooz
Edit:
Changing getSchemaClassName() to the following helped somewhat...
private static void getSchemaClassName()
{
string path = "LDAP://localhost:10389/cn=David,ou=users,ou=system";
DirectoryEntry de = new DirectoryEntry(path, "uid=admin,ou=system", "secret", AuthenticationTypes.None);
Console.WriteLine("Schema classname:" + de.SchemaClassName); //breakpoint here
}
...but not enough. Now at first sight only the Guid and NativeGuid properties of de give a System.Runtime.InteropServices.COMException and the rest seems fine. de.SchemaClassName is "organizationalPerson". Perfect.
However, upon setting the breakpoint once again and expanding for example de.SchemaEntry.SchemaEntry, almost everything that becomes visible there throws said exception: Guid, Name, ObjectSecurity, Options, SchemaClassName, SchemaEntry... you name it. The code still is not much use to me. What might be the possible cause of all those errors and how can I get it fixed?

Error: File operation not allowed. Access to route denied

I am working on a project that uses Silverlight, where I want to show PDFS files of a server path, but when I start debugging my code I find the following exception:
where I generate the flow in the following code:
System.Windows.Browser.HtmlElement myFrame = System.Windows.Browser.HtmlPage.Document.GetElementById("_sl_historyFrame");
if (myFrame != null)
{
DirectoryInfo folderPath = new DirectoryInfo(#"\\192.168.1.216\UploadFileMobilePDF\" + transfer.IdTransfer);
foreach (var file in folderPath.EnumerateFiles("*.pdf", SearchOption.AllDirectories))
{
myFrame.SetStyleAttribute("width", "1024");
myFrame.SetStyleAttribute("height", "768");
Uri uri = new Uri(folderPath + file.FullName);
string path = uri.AbsoluteUri.ToString();
myFrame.SetAttribute("src", path);
myFrame.SetStyleAttribute("left", "0");
myFrame.SetStyleAttribute("top", "50");
myFrame.SetStyleAttribute("visibility", "visible");
}
}
The error marks me when instantiating the DirectoryInfo class folderPath = new DirectoryInfo ()
I don't know if silverlight can't have permissions to server addresses
Your application likely doesn't have permission to access the files on the server you're trying to access.
Look into WindowsImpersonationContext for the most likely way around this. https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8
You'll want a class (say, "MyImpersonator") that uses WindowsImpersonationContext to log onto the server using valid credentials. There are too many details to present an entire solution, but using the class (defined elsewhere) to get a single file might look something like this:
using (var impersonator = new MyImpersonator())
{
string name = ConfigurationManager.AppSettings["name"];
string password = ConfigurationManager.AppSettings["pass"];
if (impersonator.LogOnCrossDomain(account, pass))
{
if (File.Exists(filepath))
{
byte[] content = File.ReadAllBytes(filepath);
}
}
}

Adding group to local administrators

I am trying to add an existing group in Local Administrators. The group "ABC\Some Active Group" exists. I can add that through Windows GUI but I need to add it through code. Here is what I have tried so far:
public static bool AddGroup(string machineName, string groupName)
{
bool ifSuccessful = false;
try
{
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + machineName);
DirectoryEntry admGroup = localMachine.Children.Find("administrators", "group");
//admGroup.Children.Add(groupName, "Group");
admGroup.Invoke("Add", groupName);
admGroup.CommitChanges();
ifSuccessful = true;
}
catch (Exception ex)
{
ifSuccessful = false;
//logging
Console.WriteLine(machineName + " ----------" + ex.Message);
}
return ifSuccessful;
}
and I am calling it like:
AddGroup(Environment.MachineName, #"ABC\Some Active Group");
I get the exception, (its an inner exception)
An invalid directory pathname was passed
I also tried adding it like:
admGroup.Children.Add(groupName, "Group");
But then I got the exception:
The Active Directory object located at the path
WinNT://ABC/MachineName/Administrators is not a container
I have been able to successfully get all the users and groups with admGroup, I can't just add one. Can someone please tell me what am I doing wrong ?
You need to call AddGroup like this
AddGroup(Environment.MachineName, "WinNT://" + Environment.MachineName + "/Some Active Group");

folder permissions: Full control granted to all users

I'm working on an application which stores some files in the CommonApplicationData folder. My application has to modify these files. I managed to create a custom action to grant fullcontrol rights to my application folder in the CommonApplicationData folder. But this didn't solve the problem for non-admin users. When I log on as a user and try to modify one of these files, I get the "Access Denied" message.
How can I solve this problem? Thanks.
Here is the code which I used in the Custom Action:
public void GetUsers()
{
SelectQuery sQuery = new SelectQuery("Win32_UserAccount", "Domain='" + System.Environment.UserDomainName.ToString() + "'");
try
{
ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
foreach (ManagementObject mObject in mSearcher.Get())
{
Permission(mObject["Name"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void Permission(string user)
{
string directory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
string CompanyFolderPath = Path.Combine(directory, "naseelco\\lms2004");
DirectoryInfo myDirectoryInfo = new DirectoryInfo(CompanyFolderPath);
DirectorySecurity myDirectorySecurity = myDirectoryInfo.GetAccessControl();
string User = System.Environment.UserDomainName + "\\" + user;
myDirectorySecurity.AddAccessRule(new FileSystemAccessRule(User, FileSystemRights.FullControl, AccessControlType.Allow));
myDirectoryInfo.SetAccessControl(myDirectorySecurity);
}
EDIT:
For those who would like to know the solution for this problem:
Instead of granting Access Rights to the parent folder, the individual files int that folder are granted Access Rights for each user. The Permission method in the code above has been modified as follows:
private void Permission(string user)
{
string directory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
string filePath = Path.Combine(directory, "naseelco\\lms2004\\fms.txt");
FileSecurity fSecurity = File.GetAccessControl(filePath);
FileSystemAccessRule rule = new FileSystemAccessRule(user, FileSystemRights.FullControl, AccessControlType.Allow);
fSecurity.SetAccessRule(rule);
File.SetAccessControl(filePath, fSecurity);
}
A good solution is to grant full control to Everyone using xcacls.exe or any other ACL tool. This tool can be added as a custom action in your setup project.
Granting privileges to each user is not recommended because future accounts will not be covered. Also, doing this through custom code doesn't always work. Windows permissions are a bit tricky when it comes to controlling them through code.

How can I get DOMAIN\USER from an AD DirectoryEntry?

How can I get the Windows user and domain from an Active Directory DirectoryEntry (SchemaClassName="user") object?
The user name is in the sAMAccountName property but where can I look up the domain name?
(I can't assume a fixed domain name because the users are from various subdomains.)
This assumes that results is a SearchResultCollection obtained from a DirectorySearcher, but you should be able to get the objectsid from a DirectoryEntry directly.
SearchResult result = results[0];
var propertyValues = result.Properties["objectsid"];
var objectsid = (byte[])propertyValues[0];
var sid = new SecurityIdentifier(objectsid, 0);
var account = sid.Translate(typeof(NTAccount));
account.ToString(); // This give the DOMAIN\User format for the account
You won't find what you're looking for in the DirectoryEntry, unfortunately.
You have the sAMAccountName which typically is something like myuser (without the domain). You have the distinguishedName which is something like LDAP://cn=joe myuser,cn=Users,dc=yourCompany,dc=com. You also have a userPrincipalName but that's usually a name in the format of joeUser#mycompany.com.
But you won't find any attribute that has the domain\MyUser in it, unfortunately. You'll have to put that together from your information about the domain name, and the sAMAccountName of the DirectoryEntry.
For more information and some excellent Excel sheets on all the LDAP and WinNT properties in System.DirectoryServices, check out the Hilltop Lab website by ADSI MVP Richard Mueller.
Marc
To get the DirectoryEntry domain name you can use recursion on
directoryEntry.Parent.
And then if directoryEntry.SchemaClassName == "domainDNS"
you can get the domain name like this:
directoryEntry.Properties["Name"].Value
I found a partitions container in CN=Partitions,CN=Configuration that contains all domains.
When you match the user to the partion you can read the real domain name from the nETBIOSName+"\"+sAMAccountName property.
public static string GetDomainNameUserNameFromUPN(string strUPN)
{
try
{
WindowsIdentity wi = new WindowsIdentity(strUPN);
WindowsPrincipal wp = new WindowsPrincipal(wi);
return wp.Identity.Name;
}
catch (Exception ex)
{
}
return "";
}
If you are using the System.DirectoryServices libraries, you should have a SearchResultsCollection from a DirectorySearcher.
Within each SearchResult's Properties collection, there is a "distinguishedname" property. That will contain all the DC parts that make up the domain your directory entry belongs to.
I'm extending a previous answer by #laktak to provide the details of what he meant.
There is a partitions container in CN=Partitions,CN=Configuration that contains all domains which gives you the cn which is the Netbios domain name and the nCName property that contains the distinguishedName prefix a user will have if they are in this domain.
So start by searching ldap for (objectClass=*) in CN=Partitions,CN=Configuration and store the (cn, nCName) pairs of each result to a map.
Next you query ldap using (sAMAccountName=USERIDHERE) and get the distinguishedName from the user. Now go through the (cn, nCName) pairs and find the nCName that prefixes the distinguishedName from the user, and the corresponding cn is your desired Domain name.
I wrote this pieces of code for my own usage (in VB.net, easy translation) :
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainFQDN(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Dim DN As String = Entry.Properties("DistinguishedName").Value
Return DN.Replace("DC=", "").Replace(",", ".")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainNetbiosName(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Return Entry.Properties("Name").Value
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function
I feel obligated to add my answer which was inspired from Nicholas DiPiazza's answer here. Hope this PowerShell code helps someone!
$hash = #{} //this contains the map of CN and nCNAME
$Filter = '(nETBIOSName=*)'
$RootOU = "CN=Partitions,CN=Configuration,DC=DOMAIN,DC=LOCAL" //Change this to your org's domain
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchScope = "subtree"
$Searcher.Filter = $Filter
$Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($RootOU)")
$Searcher.FindAll()|sort | foreach { $hash[($_.Properties.ncname).Trim()] = ($_.Properties.cn).Trim() }
$hash.GetEnumerator() | sort -Property Value
If the user details are available in $userDetails, then the you can get the correct domain with this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value]
and the final username would look like this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value] + "\" + $userDetails.SamAccountName
1) You can get the userPrincipalName from the DirectoryEntry.
2) Then, split the UPN up between the Username and Domain Name.
3) Then call GetNetBIOSName() on it.
public static DirectoryEntry GetDirectoryObject(string strPath)
{
if (strPath == "")
{
strPath = ConfigurationManager.AppSettings["LDAPPath"]; //YOUR DEFAULT LDAP PATH ie. LDAP://YourDomainServer
}
string username = ConfigurationManager.AppSettings["LDAPAccount"];
string password = ConfigurationManager.AppSettings["LDAPPassword"];
//You can encrypt and decrypt your password settings in web.config, but for the sake of simplicity, I've excluded the encryption code from this listing.
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("user: " + username + ", LDAPAccount: "+ ConfigurationManager.AppSettings["LDAPAccount"] + ".<br /> "+ ex.Message +"<br />");
if (HttpContext.Current.User.Identity != null)
{
HttpContext.Current.Response.Write("HttpContext.Current.User.Identity: " + HttpContext.Current.User.Identity.Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString() + "<br />");
HttpContext.Current.Response.Write("Windows Identity: " + WindowsIdentity.GetCurrent().Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString());
}
else
{
HttpContext.Current.Response.Write("User.Identity is null.");
}
HttpContext.Current.Response.End();
}
DirectoryEntry oDE = new DirectoryEntry(strPath, username, password, AuthenticationTypes.Secure);
return oDE;
}
public static string GetNetBIOSName(string DomainName)
{
string netBIOSName = "";
DirectoryEntry rootDSE =GetDirectoryObject(
"LDAP://"+DomainName+"/rootDSE");
string domain = (string)rootDSE.Properties[
"defaultNamingContext"][0];
// netBIOSName += "Naming Context: " + domain + "<br />";
if (!String.IsNullOrEmpty(domain))
{
//This code assumes you have a directory entry at the /CN=Partitions, CN=Configuration
//It will not work if you do not have this entry.
DirectoryEntry parts = GetDirectoryObject(
"LDAP://"+DomainName+"/CN=Partitions, CN=Configuration," + domain);
foreach (DirectoryEntry part in parts.Children)
{
if ((string)part.Properties[
"nCName"][0] == domain)
{
netBIOSName += (string)part.Properties[
"NetBIOSName"][0];
break;
}
}
}
return netBIOSName;
}
public static string GetDomainUsernameFromUPN(string strUPN)
{
string DomainName;
string UserName;
if (strUPN.Contains("#"))
{
string[] ud = strUPN.Split('#');
strUPN= ud[0];
DomainName = ud[1];
DomainName=LDAPToolKit.GetNetBIOSName(DomainName);
UserName= DomainName + "\\" + strUPN;
}
else
{
UserName= strUPN;
}
return UserName;
}

Categories