user1071840
user1071840

Reputation: 3592

Create a SMB share in C# with a security descriptor

I am saving my SMB Share information to disk so I can restore it later as needed.

For this I'm using the MSFT_SMB class. I get all SMB shares available on my local machine and save the data to disk. Security descriptor fetched like this is in string format.

This is the code that gets all the shares:

      private const string SmbNamespace = "\\\\.\\ROOT\\Microsoft\\Windows\\SMB";
      private const string SmbShareClass = "MSFT_SmbShare";
      private const string CreateShareMethod = "CreateShare";
      private const string GetShareMethod = "GetShare";

          public virtual List<SmbShareInfo> GetAllSmbShares()
      {
          ManagementClass SmbShares = new ManagementClass(SmbNamespace + ":" + SmbShareClass);
          List<SmbShareInfo> sharesPresent = new List<SmbShareInfo>();

          try
          {
              foreach (ManagementObject smbObject in SmbShares.GetInstances())
              {
                  sharesPresent.Add(constructSmbShareInfo(smbObject));
              }
              return sharesPresent;
          }
          catch (ManagementException e)
          {
              throw new Exception(e.ToString());
          }
      }

      private SmbShareInfo constructSmbShareInfo(ManagementObject smbObject)
      {
          return new SmbShareInfo(
              Convert.ToString(smbObject["Name"]),
              Convert.ToString(smbObject["Path"]),
              Convert.ToString(smbObject["Description"]),
              Convert.ToBoolean(smbObject["Special"]),
              Convert.ToUInt32(smbObject["ConcurrentUserLimit"]),
              Convert.ToString(smbObject["SecurityDescriptor"]),
              Convert.ToUInt32(smbObject["ShareType"]),
              Convert.ToUInt32(smbObject["FolderEnumerationMode"]),
              Convert.ToBoolean(smbObject["EncryptData"]),
              Convert.ToUInt32(smbObject["CachingMode"]));
      }

Where SmbShareInfo is a class I've defined to store SMB share information locally.

Now when I'm trying to create the share again (on a new windows machine), the CreateShare method expects "The name of the security descriptor of the share".

How do I get the "name" of the security descriptor and create the share? I just have string based security descriptors like this:

O:SYG:SYD:(A;;GA;;;BA)(A;;GA;;;BO)(A;;GA;;;IU)

This is my code to create share:

          ManagementObject managementObject = new ManagementClass(SmbNamespace + ":" + SmbShareClass);
          managementObject.Get();

          string[] accountNames = { "Everyone" };
          ManagementBaseObject createShareInParameters = managementObject.GetMethodParameters(CreateShareMethod);
          createShareInParameters["Name"] = fileShare.Name;
          createShareInParameters["Path"] = fileShare.Path;
          createShareInParameters["Description"] = fileShare.Description;
          createShareInParameters["ConcurrentUserLimit"] = fileShare.ConcurrentUserLimit;
          // Enable this
          //createShareInParameters["SecurityDescriptor"] = fileShare.SecurityDescriptor;
          createShareInParameters["FolderEnumerationMode"] = fileShare.FolderEnumerationMode;
          createShareInParameters["EncryptData"] = fileShare.EncryptData;
          createShareInParameters["CachingMode"] = fileShare.CachingMode;
          createShareInParameters["FullAccess"] = accountNames;

          managementObject.InvokeMethod(CreateShareMethod, createShareInParameters, null);

Upvotes: 2

Views: 1405

Answers (2)

user1071840
user1071840

Reputation: 3592

My code had a bug, along with the security descriptor I was also using the attribute FullAccess, which was given precedence over the security descriptors. This code worked finally:

ManagementObject managementObject = new ManagementClass(SmbNamespace + ":" + SmbShareClass); managementObject.Get();

      ManagementBaseObject createShareInParameters = managementObject.GetMethodParameters(CreateShareMethod);
      createShareInParameters["Name"] = fileShare.Name;
      createShareInParameters["Path"] = fileShare.Path;
      createShareInParameters["Description"] = fileShare.Description;
      createShareInParameters["ConcurrentUserLimit"] = fileShare.ConcurrentUserLimit;
      createShareInParameters["SecurityDescriptor"] = fileShare.SecurityDescriptor;
      createShareInParameters["FolderEnumerationMode"] = fileShare.FolderEnumerationMode;
      createShareInParameters["EncryptData"] = fileShare.EncryptData;
      createShareInParameters["CachingMode"] = fileShare.CachingMode;

      managementObject.InvokeMethod(CreateShareMethod, createShareInParameters, null);

Upvotes: 0

Alex KeySmith
Alex KeySmith

Reputation: 17091

I realised you are asking for c#, but let me offer you an interesting alternative.

You could use PowerShell's Get-SmbShare along with New-SmbShare, persisting settings if needed to a config file perhaps in the middle. Which would be trivial compared to c#.

EDIT: And Get-SmbShareAccess / Grant-SmbShareAccess for the security description (ACL) bit.

Of course I'm not aware of the full scope of project you are making, but things like system automation is where PowerShell shines. PowerShell does in fact mostly wrap c# libraries and presents an easy to consume API.

I say this as a die hard c# developer who has recently been enlightened to the power of powershell (needed for our DevOps landscape).

I realise this isn't the answer, but perhaps an interesting alternative to suit some scenarios.

Upvotes: 1

Related Questions