FileSystemRights Issues (C#) - c#

I'm fairly new to file systems and permissions/rights access (aka Access Control List, ACL). While coding with regards to ACL, I am not able to set properties I want to the files. I'm unsure if my understanding of FileSystemRights members are wrong, or I'm totally doing the wrong thing. (And I'm spending quite some time on this part already)
What I'd like to do is change the rights of a file, so that it can only be soelly readable AND cannot be edited, renamed, deleted and copied elsewhere.
Using the MSDN's example, here's what I have so far:
try
{
//Get current identity
WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
// Add the access control entry to the file.
AddFileSecurity(filename, self.Name, FileSystemRights.Modify, AccessControlType.Deny);
AddFileSecurity(filename, self.Name, FileSystemRights.Write, AccessControlType.Deny);
AddFileSecurity(filename, self.Name, FileSystemRights.ReadAndExecute, AccessControlType.Allow);
// Remove the access control entry from the file.
RemoveFileSecurity(filename, self.Name, FileSystemRights.ReadAndExecute, AccessControlType.Deny);
RemoveFileSecurity(filename, self.Name, FileSystemRights.Read, AccessControlType.Deny);
Console.WriteLine("Done.");
}
catch (Exception e)
{
Console.WriteLine(e);
}
My logic is that:
Add Deny Modify rights (Denying .Modify will cause the file to become unreadable)
Add Deny Write rights
Add Allow ReadAndExecute rights
Remove Deny entry on ReadAndExecute (As .Modify denies ReadAndExecute)
Remove Deny entry on Read (As .Modify denies Read)
Am I doing this part correctly? If not, please advise on what should I do to make the file only readable only and not editable, renamable, deletable and copiable. Many thanks in advance!

Please explain what it is doing wrong. The only thing I see that may be an issue is that you're setting the permissions for yourself, but not the other users, or groups. Perhaps you should iterate through all the groups (admins, users, etc) and disable all but read&execute. Although I think SYSTEM always has full control of all files.

Related

Remove Users group permission for folder inside ProgramData

When I create a folder inside ProgramData folder say Test then I'm seeing below permission for the folder by default for the Users group,
Question, can I remove all the permission for Users group?
I tried below code, but nothing no permission removed,
// This gets the "Authenticated Users" group, no matter what it's called
SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
// Create the rules
FileSystemAccessRule fullControlRule = new FileSystemAccessRule(sid, FileSystemRights.FullControl, AccessControlType.Allow);
if (Directory.Exists("C:\\ProgramData\\Test"))
{
// Get your file's ACL
DirectorySecurity fsecurity = Directory.GetAccessControl("C:\\ProgramData\\Test");
// remove the rule to the ACL
fsecurity.RemoveAccessRuleAll(fullControlRule);
// Set the ACL back to the file
Directory.SetAccessControl("C:\\ProgramData\\Test", fsecurity);
}
First, code which should work for your requirement (just tested it myself):
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
...
...
var directoryInfo = new DirectoryInfo(#"C:\ProgramData\Test");
// get the ACL of the directory
var dirSec = directoryInfo.GetAccessControl();
// remove inheritance, copying all entries so that they are direct ACEs
dirSec.SetAccessRuleProtection(true, true);
// do the operation on the directory
directoryInfo.SetAccessControl(dirSec);
// reread the ACL
dirSec = directoryInfo.GetAccessControl();
// get the well known SID for "Users"
var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
// loop through every ACE in the ACL
foreach (FileSystemAccessRule rule in dirSec.GetAccessRules(true, false, typeof(SecurityIdentifier)))
{
// if the current entry is one with the identity of "Users", remove it
if (rule.IdentityReference == sid)
dirSec.RemoveAccessRule(rule);
}
// do the operation on the directory
directoryInfo.SetAccessControl(dirSec);
And now to the details:
First, your idea was good to use a well-known SID and not directly the string. But the Users group is NOT Authenticated Users, so we have to use BuiltinUsersSid
Then, we have to remove the inheritance. In your above screen shot, the entries are grayed out, so not directly changeable. We first have to migrate the inherited entries to direct ones, preserving old entries (if not, the ACL would be empty afterwards).
Then, there can be more than one entry for Users (in fact, there are two). We have to loop through all entries and check if it is one which has the Users sid. Then we remove it.
Some final words:
The ACL / permissioning logic is very complex, and escpecially the inheritance can lead to many problems. But it's getting better now.
I remember the first years after the introduction of inheritance (NT4 => Windows 2000), when many tools (even MS own ones) did not handle it correctly, which produced all sort of problems with invalid / damaged ACLs.

Remove Read-Only Attribute On FOLDER On A Network Share

I am having an issue that is really killing me.
I have a directory that when I go to the properties window, shows Read-Only as partially checked (not a full check box, but the box is filled).
So I looked in the directory and I checked all the files, none of them have the read-only attribute. Only the folder has it, and only partially.
I tried the following code:
if (directoryInfo.Exists)
{
try
{
directoryInfo.Attributes &= ~FileAttributes.ReadOnly;
foreach (FileInfo f in directoryInfo.GetFiles())
{
f.IsReadOnly = false;
}
}
catch (Exception e)
{
throw e;
}
}
It still did not work. I can right click on the folder and manually remove the read-only permissions but I need to be able to do this in code. The code executes but does not error.
Anyone have any idea what the issue could be? My only guess is because the folder is on a network share (in the form of \\computer\folder\subfolder), that I might need special rights in order to change permissions on a folder?
Please someone help.
Thanks in advance
readonly on folders is used by Windows internally... if you really need to change it then is some work involved (Registry and changing alot of folders)... see http://support.microsoft.com/kb/256614/en-us
Why do you need to make that change ?
EDIT - some information on Powershell and TFS:
http://codesmartnothard.com/ExecutingPowerShellScriptsOnRemoteMachinesWithTFS2010AndTeamDeploy2010.aspx
http://blogs.msdn.com/b/yao/archive/2011/06/15/tfs-integration-pack-and-scripting-using-powershell.aspx
or try a normal "batch file" (.bat) with "attrib -r" on the folder

how to read all values from the registry

what i am trying to do is to have multiple
user names and folder paths in one key.
so i have this structure
HKEY_LOCAL_MACHINE
-- SOFTWARE
-- XYZ
-- userDB
now in userDB i have the info like this
> NAME TYpe Data
>
> Admin Reg_sz C:\Desktop
>
> Admin2 REG_SZ C:\xyz\logs
how can i read the values in userDB...
any suggestions.. thanks
i tried this code:
RegistryKey masterKey = Registry.LocalMachine.CreateSubKey("SOFTWARE\\xyz");
if (masterKey == null)
{
//Console.WriteLine("Null Masterkey!");
}
else
{
table.Rows.Add(false, masterKey.GetValue("userDB"), DateTime.Now);
dataGridView2.DataSource = table;
//Console.WriteLine("MyKey = {0}", masterKey.GetValue("userDB"));
}
masterKey.Close();
but i get the error
Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\xyz' is denied.
While you talk about reading values in your topic, your code actually writes to the registry.
You can't write to most of the HKLM part of the registry by default as a limited user. A limited users may not destroy/manipulate these keys because that's a security risk.
You could have your setup program(running with admin privs) change the permissions for your shared registry key. But that's bad style, and I wouldn't do it.
When using asp.net there might be additional restrictions related to the medium trust model.
You have:
RegistryKey masterKey = Registry.LocalMachine.CreateSubKey("SOFTWARE\\xyz");
RegistryKey.CreateSubKey is documented as:
Creates a new subkey or opens an existing subkey for write access.
Opening for write access most likely requires write privileges.
RegistryKey.OpenSubKey is used to open a key for read access. So it most likely requires no writing privileges.
At which point are you getting access denied? Are you running this code elevated or as administrator?
Chances are you are failing when calling CreateSubKey(), which when writing to HKEY_LOCAL_MACHINE requires elevated permissions.
I think Registry.LocalMachine.CreateSubKey("SOFTWARE\xyz") would try to open the key with write access if that key exists.
Try to open the key with read access instead.
I think that you can use CreateSubKey(String, RegistryKeyPermissionCheck) instead to specify permission access.
For more information, please refer to MSDN: http://msdn.microsoft.com/en-us/library/dd411617.aspx

Read Permissions to a directory in C#

I've noticed if you change the security settings for a particular directory, you can make that folder no longer "browsable" in windows. In particular, changing the "Read" permission for Administrators to "Deny" will make that folder inaccessible.
The question I now have, is how do I figure this out in code? I following gets me close, but it still ain't right:
/// <summary>
/// Takes in a directory and determines if the current user has read access to it (doesn't work for network drives)
/// THIS IS VERY HACKY
/// </summary>
/// <param name="dInfo">directoryInfo object to the directory to examine</param>
/// <returns>true if read access is available, false otherwise</returns>
public static bool IsDirectoryReadable(DirectoryInfo dInfo)
{
try
{
System.Security.AccessControl.DirectorySecurity dirSec = dInfo.GetAccessControl();
System.Security.Principal.WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
System.Security.Principal.WindowsPrincipal selfGroup = new System.Security.Principal.WindowsPrincipal(self);
// Go through each access rule found for the directory
foreach (System.Security.AccessControl.FileSystemAccessRule ar in dirSec.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)))
{
if (selfGroup.IsInRole((System.Security.Principal.SecurityIdentifier)ar.IdentityReference))
{
// See if the Read right is included
if ((ar.FileSystemRights & System.Security.AccessControl.FileSystemRights.Read) == System.Security.AccessControl.FileSystemRights.Read)
{
if (ar.AccessControlType == System.Security.AccessControl.AccessControlType.Allow)
{
// If all of the above are true, we do have read access to this directory
return true;
}
else
{
return false;
}
}
}
}
// If we didn't find anything
return false;
}
catch
{
// If anything goes wrong, assume false
return false;
}
}
I'm close with the above, but I'm still missing something huge. If I right click on a folder to set permissions, I see (in my example) 3 groups or user names: "Administrators, myUserName and SYSTEM". If I set the "Read" to Deny for either "Administrators" or "myUserName" I can no longer browse the directory. If I only set "System" to "Deny", I can still browse it.
There seems to be some sort of permission hierarchy implied, where either myUserName or Administrator supersedes the SYSTEM group/user.
The code above looks for the first Allow for "Read" it finds for my user identity and returns true. I could also write code that looks for the first "Deny" for Read and returns false.
I can set a folder to be Read - "Deny" for SYSTEM and Read - "Allow" for the other two accounts and still read the folder. If I change the code to look for Deny's and it encounters the SYSTEM user identity first, my function will return "false", which is... false. It might very well be Read - "Allow" for the other two accounts.
The problem that I still can't figure out is, how can I determine which user identity permission has priority over all of the others?
It gets very tricky because ACL's allow inheritance, but they also have a model of most restrictive access. In other words, if you have a DENY anywhere in your user chain to a resource, no matter how many other groups may give you an ALLOW, you are denied. There is a good article on the subect on MSDN.
The system group is related to the O/S process and not directly related to your user account. It's what the O/S would use to access the filesystem if there was no user context. Since your application is running as your "username" the permissions come from it and the groups it's in. Don't think you need to be checking System in this case unless I'm missing something.
Update: Keep in mind that Directory.Exists() will also check that you have permissions to read the directory.
The issue isn’t a permission hierarchy. The problem is that your code is returning true or false based on the first matching Role. You really need to evaluate all permissions.
I recently had to deal with this issue... I posted my code here.

Copy ACL information like XCopy

We recently were forced to move to a new domain server half-way around the world. This may not seem like much of a change, but one of the processes that we run frequently has suddenly gone form a 2-second command to a 5-minute command.
The reason? We are updating the permissions on many directories based on a "template" directory structure.
We've discovered that XCOPY can update the majority of these settings in the same-old-two-second window. The remaining settings, of course, get left off.
What I'm trying to figure out is, how can XCopy do faster what .NET security classes should do natively? Obviously I'm missing something.
What is the best method for copying a directory's ACL information without pinging (or minimal pinging) the domain/Active Directory server?
Here's what I have:
...
DirectorySecurity TemplateSecurity = new DirectorySecurity(TemplateDir, AccessControlSections.All);
...
public static void UpdateSecurity(string destination, DirectorySecurity TemplateSecurity)
{
DirectorySecurity dSecurity = Directory.GetAccessControl(destination);
// Remove previous security settings. (We have all we need in the other TemplateSecurity setting!!)
AuthorizationRuleCollection acl_old = dSecurity.GetAccessRules(true, true, typeof(NTAccount));
foreach (FileSystemAccessRule ace in acl_old)
{
// REMOVE IT IF YOU CAN... if you can't don't worry about it.
try
{
dSecurity.RemoveAccessRule(ace);
}
catch { }
}
// Remove the inheritance for the folders...
// Per the business unit, we must specify permissions per folder.
dSecurity.SetAccessRuleProtection(true, true);
// Copy the permissions from TemplateSecurity to Destination folder.
AuthorizationRuleCollection acl = TemplateSecurity.GetAccessRules(true, true, typeof(NTAccount));
foreach (FileSystemAccessRule ace in acl)
{
// Set the existing security.
dSecurity.AddAccessRule(ace);
try
{
// Remove folder inheritance...
dSecurity.AddAccessRule(new FileSystemAccessRule(
ace.IdentityReference, ace.FileSystemRights,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
ace.AccessControlType));
}
catch { }
}
// Apply the new changes.
Directory.SetAccessControl(destination, dSecurity);
}
Okay... I have a working prototype after A TON OF DIGGING on the internet. Needless to say, there's alot of mis-information about ACL online. I'm not exactly certain if this bit of info will be a godsend, or more mis-information. I'll have to leave that for you, the user, to decide.
What I ended up with is clean, slick, and very, very fast since it doesn't ever TOUCH the domain server. I'm copying the SDDL entries directly. Wait, you say... you can't do that on a directory because you get the dreaded SeSecurityPrivilege error!
Not if you restrict the copy to ONLY the Access Control Lists (ACL).
Here's the code:
public static void UpdateSecurity(string destination, DirectorySecurity templateSecurity)
{
DirectorySecurity dSecurity = Directory.GetAccessControl(destination);
string sddl = templateSecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access);
try
{
// TOTALLY REPLACE The existing access rights with the new ones.
dSecurity.SetSecurityDescriptorSddlForm(sddl, AccessControlSections.Access);
// Disable inheritance for this directory.
dSecurity.SetAccessRuleProtection(true, true);
// Apply these changes.
Directory.SetAccessControl(destination, dSecurity);
}
catch (Exception ex)
{
// Note the error on the console... we can formally log it later.
Console.WriteLine(pth1 + " : " + ex.Message);
}
// Do some other settings stuff here...
}
Note the AccessControlSections.Access flags on the SDDL methods. That was the magic key to make it all work.

Categories