Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Did you ever need to set an ACE (Access Control Entry) on a directory or a file? I’ve had plenty of applications in the past where I needed to set permissions on a directory or file. Most of these were application configuration applications. Usually this is required for automatically installing Web applications that get redistributed in some way.
I recently was working on a generic Web Configuration utility, that performs setup tasks for Web applications in a semi-generic way so that you can ship a generic install utility that can configure your Web application. One of the important features of this tool is the ability to set permissions on a folder.
Assigning permissions to a file or directory is one of those black art topics when it comes to doing it via code. I remember looking for examples in C++ for this a few years back as part of the West Wind Web Connection installation. At the time I gave up on it because it took a ton of really ugly code that I couldn’t figure out
That was then, this is now and we’re still not a whole lot better off - .NET also doesn’t include ACE/L support (although .NET 2.0 is rumored to have a class to allow setting of directory permissions).
Regardless, it’s possible to fairly easily integrate this functionality by using the CACLS.EXE command line utility which is included in all versions of WinNT forward through XP and Win2003 Server. CACLS allows you to create individual ACE entries. With CACLS it's pretty easy to create ACE entries easily.
The following class encapsulates the basics of creating a single ACE entry.
///
/// This class sets an individual ACL value on a file or directory.
///
public class DirectoryPermission
{
public DirectoryPermission()
{
}
public DirectoryPermission(string Path, string Username, string UserRights)
{
this.Pathname = Path;
this.Username = Username;
this.UserRights = UserRights;
}
///
/// Directory Resource to assign ACL to. This is a physical disk path
/// like d:\temp\test.
///
public string Pathname = "";
///
/// Username or group name to assign to this resource
///
public string Username = "";
///
/// Rights to assign for the given user or group.
/// N - None
/// R - Read
/// C - Change
/// F - Full
///
public string UserRights = "R";
///
/// Determines whether the permissions walk down all child
/// directories.
///
public bool InheritSubDirectories = true;
///
/// When set overrides the existing ACLs and only attaches this
/// ACL effectively deleting all other ACLs. Generally you won't
/// want to do this and only change/add the current ACL.
///
public bool OverrideExistingRights = false;
///
/// Error Message set by SetAcl
///
[XmlIgnore]
public string ErrorMessage = "";
///
/// Sets the actual ACL based on the property settings of this class
///
///
public bool SetAce()
{
if ( this.Pathname == null || this.Pathname == "")
{
ErrorMessage += "Path cannot be empty.";
return false;
}
string CommandLine = '"' + System.IO.Path.GetFullPath(this.Pathname) + '"' +
" /C ";
if (this.InheritSubDirectories)
CommandLine += @" /T ";
if (!this.OverrideExistingRights)
CommandLine += @" /E ";
CommandLine += @" /P " + this.Username + ":" + this.UserRights;
Process p = new Process();
p.StartInfo.FileName = "cacls.exe";
p.StartInfo.Arguments = CommandLine;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized ;
p.StartInfo.CreateNoWindow = true;
p.Start();
string Response = p.StandardOutput.ReadToEnd();
// *** If no output gets generated there's an error
if (Response == null || Response == "")
{
this.ErrorMessage += "Unable to set permissions on " +
this.Pathname + " for " + this.Username ;
return false;
}
return true;
}
}
This class works by simply using the Process class to execute the CACLS utility. It maps the various property settings to command line options and then simply executes the command.
You need to be an administrator in order to be able to set permissions on directories and to be able to execute external programs out of the application.
The routine redirects StandardOutput to an internal stream that’s checked for a result. If the result is empty the CACLS call failed. Otherwise the call worked and the routine returns .t.
The command line looks something like this to create a single ACE entry:
CACLS.EXE "d:\MyApp\WebPath" /T /P IUSR_RASNOTEBOOK:R
The /T switch specifies that subdirectories will inherit the settings. /P plus the user or group name followed by colon and one of the following first letters: Read, Change, Write, Full.
This is sort of a brute force way of performing this task but it works well and the class encapsulates the process in such a way that it’s non-intrusive for the calling application. The Console window that CACLS runs in is minimized so it doesn’t affect the desktop other than a quick flash in the taskbar.
public bool AddDefaultWebPermissions()
{
if (this.Virtual.Virtual == null || this.Virtual.Virtual == "")
return true;
VirtualDirectory vd = new VirtualDirectory();
vd.IISPath = this.Virtual.IISPath;
vd.Virtual = this.Virtual.Virtual;
if (!vd.GetVirtual() )
{
if (! vd.GetVirtual("") )
{
this.SetError(vd.ErrorMessage);
return false;
}
}
// *** Add the Anonymous User
string AnonymousUser = vd.AnonymousUserName;
this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,AnonymousUser,"R") );
if (vd.ServerType == WebServerTypes.IIS6)
{
this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,"NETWORKSERVICE","F") );
}
else
{
this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,"ASPNET","F") );
}
return true;
}
public bool SetDirectoryPermissions()
{
bool Error = false;
// *** Create Directory Permissions
foreach( DirectoryPermission dm in this.DirectoryPermissions )
{
if (!dm.SetAce() )
{
this.SetError( dm.ErrorMessage );
Error = true;
}
}
return !Error;
}
************************************************************************
* wwUtils :: SetACL
****************************************
*** Function:
*** Assume:
*** Pass: lcPath - Path or Filename to assign ACE to
*** lcUser - The Username to assign
*** lcAccess - [N]one, [R]ead, [C]hange, [F]ull
*** llInherit - Pass rights down the directory tree
*** llReplace - Replace rights on the resource
*** (deletes all ACL entries except this one)
*** Return:
************************************************************************
FUNCTION SetACL(lcPath,lcUser,lcAccess,llInherit,llReplace)
LOCAL lcCommand, lcFile
*** Strip off any trailing backslashes
IF RIGHT(lcPath,1)="\" AND LEN(lcPath) > 1
lcPath = SUBSTR(lcPath,1,LEN(lcPath)-1)
ENDIF
lcCommand = "RUN Cacls.exe " + ShortPath( lcPath)
IF llInherit
lcCommand = lcCommand + " /T "
ENDIF
IF !llReplace
lcCommand = lcCommand + " /E "
ENDIF
lcCommand = lcCommand + " /P " + lcUser + ":" + lcAccess + " > cacls.txt"
&lcCommand
lcFile = ""
IF FILE("Cacls.txt")
lcFile = FILETOSTR("cacls.txt")
ERASE Cacls.txt
ENDIF
IF EMPTY(lcFile)
RETURN .F.
ENDIF
RETURN .T.
ENDFUNC
* wwUtils :: SetACL
The Voices of Reason
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=e6098575-dda0-48b8-9abf-e0705af065d9
-Mike
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
string Pathname = folder;
EventLog.WriteEntry(Pathname);
string Username = @"BUILTIN\Users";
string UserRights = "N";
if (Pathname == null || Pathname == "")
{
EventLog.WriteEntry("No File");
}
string CommandLine = '"' + System.IO.Path.GetFullPath(Pathname) + '"' + " /C ";
CommandLine += @" /T ";
CommandLine += @" /P " + Username + ":" + UserRights;
Process p = new Process();
p.StartInfo.FileName = "cacls.exe";
p.StartInfo.Arguments = CommandLine;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.Write("Y\r\n");
string Response = p.StandardOutput.ReadToEnd();
EventLog.WriteEntry(Response);
if (Response == null || Response == "")
{
EventLog.WriteEntry("Error");
}
tell me why this not working in windows service?????
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Imports System
Imports System.Diagnostics
Public Class FileSecurity
Public Shared Function SetSecurity() As String
Dim _cmdLine As String = "c:\dave /t /P ASPNET:F"
Try
Dim p As Process = New Process
p.StartInfo.FileName = "cacls.exe"
p.StartInfo.Arguments = _cmdLine
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.StartInfo.WindowStyle = Diagnostics.ProcessWindowStyle.Minimized
p.StartInfo.CreateNoWindow = True
p.Start()
'Return p.StandardOutput.ReadToEnd.ToString
Catch ex As Exception
Return ex.Message
End Try
End Function
End Class
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
This can normaly be done by piping y with 'echo y| cacls xx' but this is not something that I have managed to do with the process class.
I am now going to use a bit more time than the 5 minutes I have used so far to find a solution..
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = "/c echo y| cacls " & folderPath & " /T /G " & userName & ":F"
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
A couple of "hints":
/E Edit ACL instead of replacing it.
That's a very important argument! I am just pointing it out and bringing attention to it and your code ( !llReplace ) . I accidentally replaced mine instead of updating them, and had to go back and put back the ones I removed. :<
icacls.exe may be the Windows Server 2008 "update". I think 2003 SP2 includes icacls.exe as well.
Thanks for another great write up!
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
But it seems it doesn't work on european lang. like german on french.
# re: doesn't work when path contain space in CACLS.EXE
Do you knlw why cacls doesn't work when path contains a space? I gave the following command in DOS environment:
cacls.exe c:\program files\my company\my product /T /E /G users:C
thanks,
hhe
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
using EITHER xcacls OR cacls, I'm unable to assign rights to a group..if the group name is more then 14 characters...
In these examples...the process fails..until I rename the group with one less character..it is the ONLY change I make.
I can reproduce this consistently.
Any help is appreciated.
blom
C:\xcacls.exe c:\temp\Generally /G domain\Generally_SList:R /E /Y
No mapping between account names and security IDs was done.
C:\xcacls.exe c:\temp\Generally /G domain\Generally_List:R /E /Y
processed dir: c:\temp\Generally
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
i followed this process to create full control to a perticular user.
what i observed is
i have a folder path 'E:\testing', initially on rightclick-->properties-->security tab i have 5 user names.
i ran this command
"CACLS.EXE "'E:\testing" /T /P nreddy:F"
then again i opened security tab but there i hav only one user which i mentioned in command(nreddy) with full control
what happen to remaining 4 users?
those 4 users got deleted for this perticular path???
is there any way to give full control to a perticular user without deleting other users permissions?
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
I fixed my problem by altering my script to reference only the truncated version of the name.
Though ya might like to know..
Cheers!
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Yes. You forgot the /E switch.
E for Edit. If you don't use it, that means you want to replace everything.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
i.e.
long file path:
C:\Program Files\TestFolder
short file path:
C:\Progra~1\TestFo~1
vbcode to convert long file name to short:
Private Declare Function GetShortPathName Lib "kernel32.dll" Alias "GetShortPathNameA" _
(ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer _
As Integer) As Integer
Private Function ShortPath(ByVal LongPath As String) As String
Dim shrtPath As String = Space(255)
GetShortPathName(LongPath, shrtPath, shrtPath.Length)
Return shrtPath.Substring(0, InStr(shrtPath, Chr(0)) - 1)
End Function
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Public Function SetSecurity() As String
' Dim _cmdLine As String = "c:\dave /t /P ASPNET:F"
Try
Dim p As Process = New Process
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = "/c echo y| cacls " & "C:\Test" & " /T /G " & "DOMAIN\5541192" & ":F"
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.StartInfo.WindowStyle = Diagnostics.ProcessWindowStyle.Minimized
p.StartInfo.CreateNoWindow = True
p.Start()
'Return p.StandardOutput.ReadToEnd.ToString
Return "done"
lbxLogging.Items.Add("RP\5541192 added")
Catch ex As Exception
Return ex.Message
lbxLogging.Items.Add(ex.Message)
End Try
Console.ReadLine()
End Function
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
what about is_read_only
and is_execute or not
and is_write_only
and is_read_write
Thanks in advance
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Take a look here:
http://msdn.microsoft.com/msdnmag/issues/04/11/AccessControlinNET/default.aspx#S8
You can also use CACLS to read existing permissions and parse it out of the result in 1.1.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Thanks 4 ur help
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Thanks,
Agus
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Here's the updated code:
Process p = new Process();
p.StartInfo.FileName = "cacls.exe";
p.StartInfo.Arguments = CommandLine;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.Write("Y\r\n");
string Response = p.StandardOutput.ReadToEnd();
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
Please help :)
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
I am stuck with a problem where the application which we have depeloed require autoupgrade, but when a limited access user have logged in he is not able to do that as write permissions are not allocated to him on Program Files Foler.
Could this code also be used for the same.
I want some utility so that access to that folder is given for some amount of time till autoupgrade complete.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
This is vijay from Mumbai,INDIA.This is the nice things that you wrote.Sir ,same thing i was try to run,but its not woking for my web-site.But its working fine for offline.I have a some.dat file & some.EXE.By using the some.EXE,i have to use some.dat.At my local machine its working good,as we want.But when we are going to put the things at server,there is no error at runtime,but the the some.exe is not enable to call the some.dat , as i think.Kindly guide me.Waiting for your response.
Have a nice time & good day ahead.
Thanks with Regards,
Vijay Sharma
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE