Condividi tramite


Programmatically Configuring Permissions on a Share

I was asked this problem on how to set up permission for a share programmatically using .Net Framework. Well, I am not aware of any API that can do that. Searching does not return any good result. There are lot of resources on how to configure permission settings for local folder, but not so much for UNC path. At the end, I dug msdn and had my solution, using WMI.

To setup a share, you need these information, the share that you want to setup (securable or trustee), whom you will give the permissions to the share (principal), what kind of permissions you want to give.

Using this scenario, you have a share \\ContosoServer\JohnShare, and you want John Doe (contoso\johndoe) to have full access to this share. The steps to configure the share permissions are as follow:

  • Create a WMI instance of the principal (Win32_Trustee).
    //Getting the Sid value is not required for Vista.
    NTAccount account = newNTAccount(Domain, UserName);
    SecurityIdentifiersid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
    byte[] sidArray = new byte[sid.BinaryLength];
    sid.GetBinaryForm(sidArray, 0);

    ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null);
    Trustee["Domain"] = "contoso";
    Trustee["Name"] = "johndoe";
    Trustee["SID"] = sidArray;

  • Create a WMI instance of Win32_Ace, assign the Trustee to this Win32_Ace instance.
    ManagementObject AdminACE = new ManagementClass(new ManagementPath("Win32_Ace"), null);
    AdminACE["AccessMask"] = 2032127;
    AdminACE["AceFlags"] = 3;
    AdminACE["AceType"] = 0;
    AdminACE["Trustee"] = Trustee;

    To know what values you need to put there, check msdn (link). I actually encourage you to write an enum flag to encapsulate those values.
    In nut shell, 2032127 is for full access, Access Flags 3 is for non-container and container child objects to inherit the ACE, and Ace Type 0 is to allow the trustee to access it.

  • Create a WMI instance of the security descriptor (Win32_SecurityDescriptor)
    ManagementObject secDescriptor = new ManagementClass(new ManagementPath("Win32_SecurityDescriptor"), null);
    secDescriptor["ControlFlags"] = 4; //SE_DACL_PRESENT
    secDescriptor["DACL"] = new object[] { AdminACE};

  • Now, create a WMI instance of Win32_Share, and setup the security.
    ManagementObject share = new ManagementObject(@"\\ContosoServer\root\cimv2:Win32_Share.Name='JohnShare'");
    share.InvokeMethod("SetShareInfo", new object[] {Int32.MaxValue, "This is John's share", secDescriptor});
    Check the return value of the Invoke, the method returns an Object, convert it to Int32.

That code will overwrite the existing permission, so be careful. WMI stuff are available in System.Management assemblies.

For references, these are the links that you will be interested with, Win32_Trustee, Win32_ACE, Win32_SecurityDescriptor, and Win32_Share.

Update (6/9/2008)

I updated the first step with the code to assign Sid, thanks to David Smith for his email. With Windows Vista, you do not need to supply Sid. You can supply just the domain name and the user name, it will work. Using Server 2003, and most likely XP, you have to supply all three, user name, domain, and Sid.

Comments

  • Anonymous
    June 07, 2008
    The comment has been removed

  • Anonymous
    June 07, 2008
    Just to clarify, I realize the docs say you have to add them together, but coming up with the right combination (as you did here) requires a few more steps.

  • Anonymous
    June 08, 2008
    One last question, this works for individual users but does not seem to work for groups. Does anything need to be different to configure group permissions? Thanks.

  • Anonymous
    June 08, 2008
    My bad. It works. Just having a devil of a time picking out the right AccessMask. Again, great tip.

  • Anonymous
    June 08, 2008
    You are welcome. :) Thanks for your feedback about enum value 2032127, you are correct, that value is sum of several enum values. I am going to write another post on that next week.

  • Anonymous
    June 08, 2008
    The code you provided for configuring the trustee works for local accounts by not for domain accounts. To create both local and domain permissions, I set the SID as follows, where domain and accountName are strings for the obvious values. NTAccount ntAccount = new NTAccount(domain, accountName); SecurityIdentifier sid = (SecurityIdentifier) ntAccount.Translate(typeof(SecurityIdentifier)); byte[] sidArray = new byte[sid.BinaryLength]; sid.GetBinaryForm(sidArray, 0); ManagementObject trustee = new ManagementClass(new                      ManagementPath("Win32_Trustee"), null); trustee["SID"] = sidArray;

  • Anonymous
    June 09, 2008
    Sid is required when the code is executed on Windows Server 2003, Vista does not require Sid property to be assigned. On Server 2003, you have to supply all, only Sid won't assign the permission correctly.

  • Anonymous
    June 09, 2008
    In my previous post , I have shown you how to modify share permission using .Net framework. Access Mask

  • Anonymous
    June 25, 2008
    Hi I have been able to CHANGE the EVERYONE to any user or group that I want but I am not being able to ADD another user/group to my share list. example: Administrators Full controll and domainnameuser full control. (not talking NTFS) just shares permissions. I wonder if you could help me out.

  • Anonymous
    June 26, 2008
    Hello foxer, I can try to help you out. Please be aware that SetShareInfo will overwrite the security descriptor, so you cannot really add permissions, you define a new security permission. In order for you to define a full access for Admin and a domain user, what you have to do is to create two instances of Win32_Ace objects like what I have shown you in this blog, let us call it AdminAce and UserAce. When you create Win32_SecurityDescriptor, create it like this: ManagementObject secDescriptor = new ManagementClass(new ManagementPath("Win32_SecurityDescriptor"), null);
    secDescriptor["ControlFlags"] = 4; //SE_DACL_PRESENT
    secDescriptor["DACL"] = new object[] { AdminAce, UserAce};   Pass both Win32_Ace objects to the DACL property.

  • Anonymous
    June 26, 2008
    Hi Thanks It worked. At last! I should have thought of it.

  • Anonymous
    July 03, 2008
    Great article! I was interested in getting the list of User,Permission pairs, like in the Sharing->Permissions menu. I've tried using the 'GetAccessMask' in Win32_Share, but that only shows your current computer's access. The idea I had was to get the list of all domain-user/group pairs, then impersonate and use GetAccessmask, but that seems awful. If you had any ideas, I'd be grateful :)

  • Anonymous
    July 10, 2008
    Thanks for your comment, tomato. That is a great idea for the next article, I do not have time to work on it at this moment, but I will.

  • Anonymous
    July 21, 2008
    In my previous post, I have shown you how to set up permission on a share . The thing with Win32_Share,

  • Anonymous
    December 07, 2008
    Hey, if i have to give multiple users permission to the share.How should this be done

  • Anonymous
    December 07, 2008
    Hi Bhargavi, Each users must be represented by an ace.  Create Trustee for each users, and then assign each trustee to its own ace. Then when creating security descriptor, assign those aces to the DACL property.  For example: secDescriptor["DACL"] = new object[] { AdminACE, YourACE, MyAce, ThirdPersonAce };

  • Anonymous
    February 20, 2009
    The comment has been removed

  • Anonymous
    February 20, 2009
    @Subtile: What is the error? Is the share on a different machine or on the same machine?

  • Anonymous
    March 25, 2009
    Hi! Thanks for the post, this is really helpful.  However, I'm having some trouble in the following scenario: In a home network, user A and B are in the same workgroup, but on different machine.  A wants to allow user B (on a different machine) to share a folder residing on user A's machine. Is there a way to do this?  Both machines run windows XP, with only workgroups, no domains. --kim

  • Anonymous
    April 02, 2009
    @Kim. Give this a try. B must have an account on machine A, and B must be an admin. B's account on both machines must have identical password.

  • Anonymous
    July 07, 2009
    Hi Could you tell me how to share a folder to everyone instead of a specific user? Thanks!

  • Anonymous
    July 07, 2009
    @David B: If you need Everyone to have read-only access, then what you need to do is to set DACL to null. secDescriptor["DACL"] = null; This will give everyone a read access. If you need to give 'Everyone' read-write access, then you have to create the Win32_Trustee, using WellKnownSidType enum. I need to check my note again about this. I will put this for my next blog post. Give this a try, when creating Trustee object, try to use 'NT AuthorityEveryone' ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null); Trustee["Domain"] = "NT Authority"; Trustee["Name"]   = "Everyone";

  • Anonymous
    July 08, 2009
    @HelloWorld Thanks for the reply. This works on my Vista machine, but it doesn't work on Windows Server 2003. Any thoughts? Here is what I have for code. protected void CreateShare(string path, string shareName)
    {
       // Create a ManagementClass object
       ManagementClass managementClass = new ManagementClass("Win32_Share");
       // Create ManagementBaseObjects for in and out parameters
       ManagementBaseObject inParams = managementClass.GetMethodParameters("Create");
       ManagementBaseObject outParams;
       // Set the input parameters
       inParams["Description"] = "IdeaServer";
       inParams["Name"] = shareName;
       inParams["Path"] = path;
       inParams["Type"] = 0x0; // Disk Drive
       //Another Type:
       //        DISK_DRIVE = 0x0
       //        PRINT_QUEUE = 0x1
       //        DEVICE = 0x2
       //        IPC = 0x3
       //        DISK_DRIVE_ADMIN = 0x80000000
       //        PRINT_QUEUE_ADMIN = 0x80000001
       //        DEVICE_ADMIN = 0x80000002
       //        IPC_ADMIN = 0x8000003
       //inParams["MaximumAllowed"] = int maxConnectionsNum;
       // Invoke the method on the ManagementClass object
       inParams["Access"] = SecurityDescriptor();
       outParams = managementClass.InvokeMethod("Create", inParams, null);
       // Check to see if the method invocation was successful
       uint rVal = (uint)(outParams.Properties["ReturnValue"].Value);
       if (rVal != 0 && rVal != 22) // ok if it already exists
       {
           throw new Exception("Unable to share directory.");
       }
    } private static ManagementBaseObject SecurityDescriptor()
    {
       NTAccount account = new NTAccount("NT Authority", "Everyone");
       SecurityIdentifier sid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
       byte[] sidArray = new byte[sid.BinaryLength];
       sid.GetBinaryForm(sidArray, 0); 
       ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null);
       Trustee["Domain"] = "NT Authority";
       Trustee["Name"] = "Everyone";
       Trustee["SID"] = sidArray;
       ManagementObject AdminACE = new ManagementClass(new ManagementPath("Win32_Ace"), null);
       AdminACE["AccessMask"] = 2032127;
       AdminACE["AceFlags"] = 3;
       AdminACE["AceType"] = 0;
       AdminACE["Trustee"] = Trustee;
       ManagementObject SecurityDescriptor = new ManagementClass(new ManagementPath("Win32_SecurityDescriptor"), null);
       SecurityDescriptor["ControlFlags"] = 4; //SE_DACL_PRESENT
       SecurityDescriptor["DACL"] = new object[] { AdminACE };
       return SecurityDescriptor;
    } Thanks a lot! David

  • Anonymous
    July 09, 2009
    @David: Try this, update your code from ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null); Trustee["Domain"] = "NT Authority"; Trustee["Name"] = "Everyone"; Trustee["SID"] = sidArray; to SecurityIdentifier Sec = new SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null); byte[] sidArray = new byte[Sec.BinaryLength]; Sec.GetBinaryForm(sidArray, 0); ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null); Trustee["SID"] = sidArray;

  • Anonymous
    July 14, 2009
    @HelloWorld Thanks! That worked perfectly!

  • Anonymous
    August 29, 2009
    How do you update the share permissions on an already created share? For example add a new user to the share etc

  • Anonymous
    August 29, 2009
    @Aditya: Please check my other post. http://blogs.msdn.com/helloworld/archive/2008/07/22/editing-share-permission.aspx

  • Anonymous
    April 28, 2010
    Thank you SO much for posting this!  I couldn't find an example of how to do this for Everyone, but you covered that for another reading in your comments. Cheers! -RG!

  • Anonymous
    May 26, 2010
    Just wanted to point out that there is a Windows API for creating shared folders and specifying their permissions, for anyone who does not want to use WMI. I've spent the last 2 days trying to get it to work from a VB.NET app and have finally been successful - see this thread on vbforums for more details: www.vbforums.com/showthread.php Hope it is useful to someone Chris

  • Anonymous
    July 12, 2010
    Here is the breakdown for the access mask. Add them together as needed. FOLDER_LIST_DIRECTORY   = 1 FOLDER_ADD_FILE         = 2 FOLDER_ADD_SUBDIRECTORY = 4 FILE_READ_EA            = 8 FILE_WRITE_EA           = 16 FOLDER_TRAVERSE         = 32 FILE_DELETE_CHILD       = 64 FILE_READ_ATTRIBUTES    = 128 FILE_WRITE_ATTRIBUTES   = 256 FILE_DELETE             = 65536 FILE_READ_CONTROL       = 131072 FILE_WRITE_DAC          = 262144 FILE_WRITE_OWNER        = 524288 FILE_SYNCHRONIZE        = 1048576 FILE_ALL_ACCESS         = 2032127

  • Anonymous
    October 12, 2010
    Finding this post has been a godsend as I spent much time trying to figure this out.  I have a question though.  What do you do if there is already a shared folder with the same share name ('JohnShare' in your example) but different path that you are trying to create?  If you do this manually in windows, it asks if you want to go ahead and use this name with the new path and makes the old shared folder not a share anymore....but I can't figure out how to get it to go ahead and do that programmatically.  Any thoughts?

  • Anonymous
    January 19, 2011
    Awesome post! I have spent hours searching for this information and here it is.  Now I can relax.  Thank you so much.

  • Anonymous
    January 26, 2011
    @KG: Honestly, I am not really sure. My guess is that you should check for a share with identical name, prompt the user yourself, and based on user's input, delete and recreate the share with a different path. Probably it is a good idea to reassign the DACL from the old share to the new share. I am not sure if you can simply change the path of a share, Windows does not allow you to do that from the UI.

  • Anonymous
    June 29, 2011
    Can you help on how to configure it for workgroup machine ?

  • Anonymous
    June 29, 2011
    @Moh, I do not know, sorry. You may try this, though, assign the target computer name as the domain.