Добавить пользователя в удаленный windows общий доступ с помощью WMI - PullRequest
0 голосов
/ 18 февраля 2020

У меня есть общий ресурс на root из C: \ на удаленном компьютере IMP C -1111, и я пытаюсь добавить пользовательские / w маски доступа:

FullControl = 2032127
Change = 1245631
[ReadOnly] = 1179817

Я понял, как их читать ... но не могу понять, как их написать / изменить. Это код, который я использую для их чтения.

Private Function GetSharedFolderAccessRule() As DataTable

    Dim DT As DataTable = New DataTable()

    Try

        DT.Columns.Add("ShareName")
        DT.Columns.Add("Caption")
        DT.Columns.Add("Path")
        DT.Columns.Add("Domain")
        DT.Columns.Add("User")
        DT.Columns.Add("AccessMask")
        DT.Columns.Add("AceType")

        Dim Con As ConnectionOptions = New ConnectionOptions
        Con.Username = "Username"
        Con.Password = "Password"

        'Dim Scope As ManagementScope = New ManagementScope("\\.\root\cimv2", Con)
        Dim Scope As ManagementScope = New ManagementScope("\\IMPC-1111\root\cimv2", Con)
        Scope.Connect()

        Dim Query As ObjectQuery = New ObjectQuery("SELECT * FROM Win32_LogicalShareSecuritySetting")
        Dim Searcher As ManagementObjectSearcher = New ManagementObjectSearcher(Scope, Query)
        Dim QueryCollection As ManagementObjectCollection = Searcher.[Get]()

        For Each SharedFolder As ManagementObject In QueryCollection

            If True Then
                Dim ShareName As String = CType(SharedFolder("Name"), String)
                Dim Caption As String = CType(SharedFolder("Caption"), String)
                Dim LocalPath As String = String.Empty
                Dim Win32Share As ManagementObjectSearcher = New ManagementObjectSearcher("SELECT Path FROM Win32_share WHERE Name = '" & ShareName & "'")

                For Each ShareData As ManagementObject In Win32Share.[Get]()
                    LocalPath = CType(ShareData("Path"), String)
                Next

                Dim Method As ManagementBaseObject = SharedFolder.InvokeMethod("GetSecurityDescriptor", Nothing, New InvokeMethodOptions())
                Dim Descriptor As ManagementBaseObject = CType(Method("Descriptor"), ManagementBaseObject)
                Dim DACL As ManagementBaseObject() = CType(Descriptor("DACL"), ManagementBaseObject())

                For Each ACE As ManagementBaseObject In DACL
                    Dim Trustee As ManagementBaseObject = CType(ACE("Trustee"), ManagementBaseObject)
                    Dim Row As DataRow = DT.NewRow()
                    Row("ShareName") = ShareName
                    Row("Caption") = Caption
                    Row("Path") = LocalPath
                    Row("Domain") = CType(Trustee("Domain"), String)
                    Row("User") = CType(Trustee("Name"), String)
                    Row("AccessMask") = CType(ACE("AccessMask"), UInt32)
                    Row("AceType") = CType(ACE("AceType"), UInt32)
                    DT.Rows.Add(Row)
                    DT.AcceptChanges()
                Next
            End If
        Next

    Catch ex As Exception
        MessageBox.Show(ex.StackTrace, ex.Message)
    End Try

    Return DT

End Function

Кто-нибудь может указать мне правильное направление? Можно ли вообще писать с помощью WMI?

Спасибо!

1 Ответ

1 голос
/ 21 февраля 2020

Вы можете использовать метод Win32_LogicalShareSecuritySetting. SetSecurityDescriptor() для изменения разрешений общего ресурса. Изменение существующих разрешений работает следующим образом ...

  1. Получить требуемый Win32_LogicalShareSecuritySetting экземпляр.
  2. Позвонить GetSecurityDescriptor() на получить дескриптор безопасности общего ресурса в виде Win32_SecurityDescriptor экземпляра.
  3. Получить список дискреционного контроля доступа дескриптора безопасности через его свойство DACL.
  4. Создать новый элемент управления доступом запись.
    1. Получить ссылку на Win32_ACE класс .
    2. Создать экземпляр Win32_ACE класса .
    3. Установите свойства AccessMask, AceFlags, AceType и Trustee записи управления доступом.
      1. Получить ссылку на Win32_Trustee класс .
      2. Создать экземпляр Win32_Trustee класса .
      3. Укажите принципала для записи, установив свойство SIDString или NameDomain) опекуна.
        • Запрос экземпляров класса Win32_Account или его производных будет одним из способов получения этих значений.
  5. Создайте новый список контроля доступа, объединяющий существующие записи и новую запись.
  6. Установите DACL свойство дескриптора безопасности для нового списка контроля доступа.
  7. Вызов SetSecurityDescriptor() для сохранения дескриптора безопасности общего ресурса.

Следующий класс содержит методы для получить свойства общего ресурса и, в частности, добавить новую запись управления доступом с помощью метода AddAccessControlEntry() ...

Imports System.Management
Imports System.Security.AccessControl

Friend Class ShareSecurity
    Private ReadOnly Property ShareName As String

    Private ReadOnly Property GetOptions As ObjectGetOptions

    Private ReadOnly Property Scope As ManagementScope

    Public Sub New(host As String, shareName As String, username As String, password As String)
        Me.ShareName = shareName
        GetOptions = New ObjectGetOptions()
        Scope = New ManagementScope(
            New ManagementPath() With {
                .NamespacePath = "root\cimv2",
                .Server = host
            },
            New ConnectionOptions() With {
 _ ' ***** For demonstration purposes only! *****
 _ 'TODO: Find a secure way to store the remote password
                .Password = password,
                .Username = username
            }
        )
    End Sub

    Public Function GetShareProperties() As IDictionary(Of String, Object)
        Dim sharePath As New ManagementPath($"Win32_Share.Name=""{ShareName}""")

        Using share As New ManagementObject(Scope, sharePath, GetOptions)
            Return GetPropertyDictionary(share)
        End Using
    End Function

    Public Function GetShareSecurityDescriptorProperties() As IDictionary(Of String, Object)
        Using shareSecurity As ManagementObject = GetShareSecurity()
            Using securityDescriptor As ManagementBaseObject = GetShareSecurityDescriptor(shareSecurity)
                Return GetPropertyDictionary(securityDescriptor)
            End Using
        End Using
    End Function

    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, securityIdentifier As String)
        AddAccessControlEntry(
            entryType,
            entryRights,
            Sub(trustee)
                trustee("SIDString") = securityIdentifier
            End Sub
        )
    End Sub

    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, domain As String, account As String)
        AddAccessControlEntry(
            entryType,
            entryRights,
            Sub(trustee)
                trustee("Domain") = domain
                trustee("Name") = account
            End Sub
        )
    End Sub

    ''' <param name="trusteeInitializer">Initializes the Win32_Trustee instance for the access control entry to be added.</param>
    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, trusteeInitializer As Action(Of ManagementObject))
        Using shareSecurity As ManagementObject = GetShareSecurity()
            Using securityDescriptor As ManagementBaseObject = GetShareSecurityDescriptor(shareSecurity)
                Dim accessControlEntries As ManagementBaseObject() = DirectCast(securityDescriptor("DACL"), ManagementBaseObject())

                ' The class must not be created in the remote scope otherwise CreateInstance()
                ' throws "System.UnauthorizedAccessException: 'Access is denied'."
                Using accessControlEntryClass As New ManagementClass("Win32_ACE")
                    Using accessControlEntry As ManagementObject = accessControlEntryClass.CreateInstance()
                        accessControlEntry("AccessMask") = CUInt(entryRights)
                        accessControlEntry("AceFlags") = CUInt(AceFlags.None)
                        accessControlEntry("AceType") = CUInt(entryType)

                        ' The class must not be created in the remote scope otherwise CreateInstance()
                        ' throws "System.UnauthorizedAccessException: 'Access is denied'."
                        Using trusteeClass As New ManagementClass("Win32_Trustee")
                            Using trustee As ManagementObject = trusteeClass.CreateInstance()
                                trusteeInitializer.Invoke(trustee)
                                accessControlEntry("Trustee") = trustee

                                ' Create a new access control list including the new access control
                                ' entry, sorted with Deny entries first (true sorts after false)
                                ' https://docs.microsoft.com/windows/win32/secauthz/order-of-aces-in-a-dacl
                                securityDescriptor("DACL") = accessControlEntries _
                                    .Append(accessControlEntry) _
                                    .OrderByDescending(Function(entry) CType(entry("AceType"), AceType)) _
                                    .ToArray()

                                SetShareSecurityDescriptor(shareSecurity, securityDescriptor)
                            End Using
                        End Using
                    End Using
                End Using
            End Using
        End Using
    End Sub

    Private Function GetShareSecurity() As ManagementObject
        Dim shareSecurityPath As New ManagementPath($"Win32_LogicalShareSecuritySetting.Name=""{ShareName}""")

        Return New ManagementObject(Scope, shareSecurityPath, GetOptions)
    End Function

    Private Function GetShareSecurityDescriptor(shareSecurity As ManagementObject) As ManagementBaseObject
        ' Create an array to store the output parameter
        Dim invokeParameters(0) As ManagementBaseObject
        Dim invokeResult As UInteger = shareSecurity.InvokeMethod("GetSecurityDescriptor", invokeParameters)

        If invokeResult = 0 Then
            Return invokeParameters(0)
        Else
            'TODO: Handle failure of GetSecurityDescriptor()...
            Return Nothing
        End If
    End Function

    Private Sub SetShareSecurityDescriptor(shareSecurity As ManagementObject, securityDescriptor As ManagementBaseObject)
        ' Create an array to store the input parameter
        Dim invokeParameters() As ManagementBaseObject = {securityDescriptor}
        Dim invokeResult As UInteger = shareSecurity.InvokeMethod("SetSecurityDescriptor", invokeParameters)

        If invokeResult <> 0 Then
            'TODO: Handle failure of SetSecurityDescriptor()...
        End If
    End Sub

    Private Shared Function GetPropertyDictionary(obj As ManagementBaseObject) As IDictionary(Of String, Object)
        Return obj.Properties _
            .Cast(Of PropertyData)() _
            .ToDictionary(
                Function([property]) [property].Name,
                Function([property])
                    ' Recursively create dictionaries in place of management objects
                    Dim baseObjectArray As ManagementBaseObject() = TryCast([property].Value, ManagementBaseObject())

                    If baseObjectArray IsNot Nothing Then
                        Return baseObjectArray.Select(AddressOf GetPropertyDictionary).ToArray()
                    Else
                        Dim baseObject As ManagementBaseObject = TryCast([property].Value, ManagementBaseObject)

                        If baseObject IsNot Nothing Then
                            Return GetPropertyDictionary(baseObject)
                        Else
                            Return [property].Value
                        End If
                    End If
                End Function
            )
    End Function
End Class

Следующая программа использует класс ShareSecurity выше для отображения свойств удаленный общий ресурс и, при необходимости, добавьте в него новую запись управления доступом ...

Imports System.Security.AccessControl

Public Class Program
    Public Shared Sub Main(args As String())
        If args Is Nothing OrElse args.Length = 0 Then
            DisplayUsage()
        ElseIf args.Length < 4 Then
            DisplayError("Too few arguments.")
        Else
            Dim host As String = args(0)
            Dim shareName As String = args(1)
            Dim username As String = args(2)
            ' ***** For demonstration purposes only! *****
            'TODO: Find a secure way to store the remote password
            Dim password As String = args(3)
            Dim shareSecurity As New ShareSecurity(host, shareName, username, password)

            DisplayShareProperties(shareSecurity)
            DisplaySecurityDescriptor(shareSecurity)

            If args.Length > 4 Then
                If args.Length <> 7 AndAlso args.Length <> 8 Then
                    DisplayError("Argument count mismatch.")
                Else
                    Dim entryType As AceType

                    If Not [Enum].TryParse(args(4), True, entryType) Then
                        DisplayError($"Invalid <type> value ""{args(4)}"".")
                    Else
                        Dim entryRights As FileSystemRights

                        If Not [Enum].TryParse(args(5), True, entryRights) Then
                            DisplayError($"Invalid <rights> value ""{args(5)}"".")
                        Else
                            DisplayTextWithSeparator("New access control entry")
                            Console.WriteLine($"   Type: {entryType}")
                            Console.WriteLine($" Rights: {entryRights}")

                            If args.Length = 7 Then
                                Dim securityIdentifier As String = args(6)

                                Console.WriteLine($"    SID: {securityIdentifier}")
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, securityIdentifier)
                            Else ' args.Length = 8
                                Dim domain As String = args(6)
                                Dim account As String = args(7)

                                Console.WriteLine($" Domain: {domain}")
                                Console.WriteLine($"Account: {account}")
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, domain, account)
                            End If
                            Console.WriteLine()
                            DisplaySecurityDescriptor(shareSecurity)
                        End If
                    End If
                End If
            End If
        End If
    End Sub

    Private Shared Sub DisplayUsage()
        Dim entryAssemblyPath As String = System.Reflection.Assembly.GetEntryAssembly().Location
        Dim entryAssemblyName As String = System.IO.Path.GetFileName(entryAssemblyPath)

        Console.WriteLine("Display share properties:")
        Console.WriteLine()
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password>")
        Console.WriteLine()
        Console.WriteLine("Add access control entry:")
        Console.WriteLine()
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <sid>")
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <domain> <account>")
        Console.WriteLine()
        Console.WriteLine(vbTab & $"  <type> - A {GetType(AceType).FullName} enumeration value.")
        Console.WriteLine(vbTab & $"<rights> - A {GetType(FileSystemRights).FullName} enumeration value.")
        Console.WriteLine(vbTab & "   <sid> - An account security identifier.")
        Console.WriteLine()
    End Sub

    Private Shared Sub DisplayError(message As String)
        Console.WriteLine($"ERROR: {message}")
        Console.WriteLine()
        DisplayUsage()
    End Sub

    Private Shared Sub DisplayTextWithSeparator(text As String)
        Console.WriteLine(text)
        Console.WriteLine(New String("-"c, text.Length))
    End Sub

    Private Shared Sub DisplayShareProperties(shareSecurity As ShareSecurity)
        Dim shareProperties As IDictionary(Of String, Object) = shareSecurity.GetShareProperties()

        DisplayTextWithSeparator("Share properties")
        For Each propertyName As String In New String() {"Description", "Name", "Path"}
            DisplayProperty(shareProperties, propertyName)
        Next
        Console.WriteLine()
    End Sub

    Private Shared Sub DisplaySecurityDescriptor(shareSecurity As ShareSecurity)
        Dim securityDescriptorProperties As IDictionary(Of String, Object) = shareSecurity.GetShareSecurityDescriptorProperties()

        DisplayTextWithSeparator("Share security descriptor")
        DisplayProperty(securityDescriptorProperties, "ControlFlags", Function(value) CType(value, ControlFlags))
        Console.WriteLine()

        Dim accessControlList As IDictionary(Of String, Object)() = securityDescriptorProperties("DACL")
        For i As Integer = 0 To accessControlList.Length - 1
            Dim accessControlEntryProperties As IDictionary(Of String, Object) = accessControlList(i)

            DisplayTextWithSeparator($"Access control entry #{i}")
            DisplayProperty(accessControlEntryProperties, "AccessMask", Function(value) CType(value, FileSystemRights))
            DisplayProperty(accessControlEntryProperties, "AceFlags", Function(value) CType(value, AceFlags))
            DisplayProperty(accessControlEntryProperties, "AceType", Function(value) CType(value, AceType))
            DisplayProperty(accessControlEntryProperties, "Trustee", Function(value) DirectCast(value, IDictionary(Of String, Object))("Name"))
            Console.WriteLine()
        Next
    End Sub

    Private Shared Sub DisplayProperty(properties As IDictionary(Of String, Object), propertyName As String)
        DisplayProperty(properties, propertyName, Nothing)
    End Sub

    Private Shared Sub DisplayProperty(properties As IDictionary(Of String, Object), propertyName As String, selector As Func(Of Object, Object))
        Dim propertyValue As Object = properties(propertyName)
        Dim displayValue As Object = If(
            selector IsNot Nothing,
            selector.Invoke(propertyValue),
            If(propertyValue, "<null>")
        )

        Console.WriteLine($"{propertyName}: {displayValue}")
    End Sub
End Class

Имеется такой общий ресурс ...

Share properties dialog for example My Share share

... с Full Control предоставленным Administrators и Read предоставленным Everyone, вызывая программу следующим образом ...

SO60271689.exe MyComputer "My Share" MyUser MyPassword AccessDenied "Modify, Synchronize" S-1-5-32-546

... или как this ...

SO60271689.exe MyComputer "My Share" MyUser MyPassword AccessDenied "Modify, Synchronize" BUILTIN Guests

... производит вывод, подобный этому ...

Share properties
----------------
Description: This is the share description.
Name: My Share
Path: C:\My Share

Share security descriptor
-------------------------
ControlFlags: DiscretionaryAclPresent, SelfRelative

Access control entry #0
-----------------------
AccessMask: ReadAndExecute, Synchronize
AceFlags: None
AceType: AccessAllowed
Trustee: Everyone

Access control entry #1
-----------------------
AccessMask: FullControl
AceFlags: None
AceType: AccessAllowed
Trustee: Administrators

New access control entry
------------------------
   Type: AccessDenied
 Rights: Modify, Synchronize
    SID: S-1-5-32-546

Share security descriptor
-------------------------
ControlFlags: DiscretionaryAclPresent, SelfRelative

Access control entry #0
-----------------------
AccessMask: Modify, Synchronize
AceFlags: None
AceType: AccessDenied
Trustee: Guests

Access control entry #1
-----------------------
AccessMask: ReadAndExecute, Synchronize
AceFlags: None
AceType: AccessAllowed
Trustee: Everyone

Access control entry #2
-----------------------
AccessMask: FullControl
AceFlags: None
AceType: AccessAllowed
Trustee: Administrators

Поскольку VB. NET не мой родной язык, вот эквивалент C# код я начал с того, чтобы все заработало ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Security.AccessControl;

namespace SO60271689.CSharp
{
    internal class ShareSecurity
    {
        private string ShareName
        {
            get;
        }

        private ObjectGetOptions GetOptions
        {
            get;
        }

        private ManagementScope Scope
        {
            get;
        }

        public ShareSecurity(string host, string shareName, string username, string password)
        {
            ShareName = shareName;
            GetOptions = new ObjectGetOptions();
            Scope = new ManagementScope(
                new ManagementPath() {
                    NamespacePath = @"root\cimv2",
                    Server = host
                },
                new ConnectionOptions() {
                    // ***** For demonstration purposes only! *****
                    //TODO: Find a secure way to store the remote password
                    Password = password,
                    Username = username
                }
            );
        }

        public IDictionary<string, object> GetShareProperties()
        {
            ManagementPath sharePath = new ManagementPath($"Win32_Share.Name=\"{ShareName}\"");

            using (ManagementObject share = new ManagementObject(Scope, sharePath, GetOptions))
                return GetPropertyDictionary(share);
        }

        public IDictionary<string, object> GetShareSecurityDescriptorProperties()
        {
            using (ManagementObject shareSecurity = GetShareSecurity())
            using (ManagementBaseObject securityDescriptor = GetShareSecurityDescriptor(shareSecurity))
                return GetPropertyDictionary(securityDescriptor);
        }

        public void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, string securityIdentifier)
        {
            AddAccessControlEntry(
                entryType,
                entryRights,
                trustee => trustee["SIDString"] = securityIdentifier
            );
        }

        public void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, string domain, string account)
        {
            AddAccessControlEntry(
                entryType,
                entryRights,
                trustee => {
                    trustee["Domain"] = domain;
                    trustee["Name"] = account;
                }
            );
        }

        /// <param name="trusteeInitializer">Initializes the Win32_Trustee instance for the access control entry to be added.</param>
        private void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, Action<ManagementObject> trusteeInitializer)
        {
            using (ManagementObject shareSecurity = GetShareSecurity())
            using (ManagementBaseObject securityDescriptor = GetShareSecurityDescriptor(shareSecurity))
            {
                ManagementBaseObject[] accessControlEntries = (ManagementBaseObject[]) securityDescriptor["DACL"];

                // The class must not be created in the remote scope otherwise CreateInstance()
                // throws "System.UnauthorizedAccessException: 'Access is denied'."
                using (ManagementClass accessControlEntryClass = new ManagementClass("Win32_ACE"))
                using (ManagementObject accessControlEntry = accessControlEntryClass.CreateInstance())
                {
                    accessControlEntry["AccessMask"] = (uint) entryRights;
                    accessControlEntry["AceFlags"] = (uint) AceFlags.None;
                    accessControlEntry["AceType"] = (uint) entryType;

                    // The class must not be created in the remote scope otherwise CreateInstance()
                    // throws "System.UnauthorizedAccessException: 'Access is denied'."
                    using (ManagementClass trusteeClass = new ManagementClass("Win32_Trustee"))
                    using (ManagementObject trustee = trusteeClass.CreateInstance())
                    {
                        trusteeInitializer.Invoke(trustee);
                        accessControlEntry["Trustee"] = trustee;

                        // Create a new access control list including the new access control
                        // entry, sorted with Deny entries first (true sorts after false)
                        // https://docs.microsoft.com/windows/win32/secauthz/order-of-aces-in-a-dacl
                        securityDescriptor["DACL"] = accessControlEntries
                            .Append(accessControlEntry)
                            .OrderByDescending(entry => (AceType) (uint) entry["AceType"] == AceType.AccessDenied)
                            .ToArray();

                        SetShareSecurityDescriptor(shareSecurity, securityDescriptor);
                    }
                }
            }
        }

        private ManagementObject GetShareSecurity()
        {
            ManagementPath shareSecurityPath = new ManagementPath($"Win32_LogicalShareSecuritySetting.Name=\"{ShareName}\"");

            return new ManagementObject(Scope, shareSecurityPath, GetOptions);
        }

        private ManagementBaseObject GetShareSecurityDescriptor(ManagementObject shareSecurity)
        {
            // Create an array to store the output parameter
            ManagementBaseObject[] invokeParameters = new ManagementBaseObject[1];
            uint invokeResult = (uint) shareSecurity.InvokeMethod("GetSecurityDescriptor", invokeParameters);

            if (invokeResult == 0)
                return invokeParameters[0];
            else
            {
                //TODO: Handle failure of GetSecurityDescriptor()...
                return null;
            }
        }

        private void SetShareSecurityDescriptor(ManagementObject shareSecurity, ManagementBaseObject securityDescriptor)
        {
            // Create an array to store the input parameter
            ManagementBaseObject[] invokeParameters = new ManagementBaseObject[1] { securityDescriptor };
            uint invokeResult = (uint) shareSecurity.InvokeMethod("SetSecurityDescriptor", invokeParameters);

            if (invokeResult != 0)
            {
                //TODO: Handle failure of SetSecurityDescriptor()...
            }
        }

        private static IDictionary<string, object> GetPropertyDictionary(ManagementBaseObject obj)
        {
            return obj.Properties
                .Cast<PropertyData>()
                .ToDictionary(
                    property => property.Name,
                    // Recursively create dictionaries in place of management objects
                    property => property.Value is ManagementBaseObject[] baseObjectArray
                        ? baseObjectArray.Select(GetPropertyDictionary).ToArray()
                        : property.Value is ManagementBaseObject baseObject
                        ? GetPropertyDictionary(baseObject)
                        : property.Value
                );
        }
    }
}
using System;
using System.Collections.Generic;
using System.Security.AccessControl;

namespace SO60271689.CSharp
{
    class Program
    {
        public static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
                DisplayUsage();
            else if (args.Length < 4)
                DisplayError("Too few arguments.");
            else
            {
                string host = args[0];
                string shareName = args[1];
                string username = args[2];
                // ***** For demonstration purposes only! *****
                //TODO: Find a secure way to store the remote password
                string password = args[3];
                ShareSecurity shareSecurity = new ShareSecurity(host, shareName, username, password);

                DisplayShareProperties(shareSecurity);
                DisplaySecurityDescriptor(shareSecurity);

                if (args.Length > 4)
                {
                    if (args.Length != 7 && args.Length != 8)
                        DisplayError("Argument count mismatch.");
                    else
                    {
                        if (!Enum.TryParse(args[4], true, out AceType entryType))
                            DisplayError($"Invalid <type> value \"{args[4]}\".");
                        else if (!Enum.TryParse<FileSystemRights>(args[5], true, out FileSystemRights entryRights))
                            DisplayError($"Invalid <rights> value \"{args[5]}\".");
                        else
                        {
                            DisplayTextWithSeparator("New access control entry");
                            Console.WriteLine($"   Type: {entryType}");
                            Console.WriteLine($" Rights: {entryRights}");

                            if (args.Length == 7)
                            {
                                string securityIdentifier = args[6];

                                Console.WriteLine($"    SID: {securityIdentifier}");
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, securityIdentifier);
                            }
                            else // args.Length == 8
                            {
                                string domain = args[6];
                                string account = args[7];

                                Console.WriteLine($" Domain: {domain}");
                                Console.WriteLine($"Account: {account}");
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, domain, account);
                            }
                            Console.WriteLine();
                            DisplaySecurityDescriptor(shareSecurity);
                        }
                    }
                }
            }
        }

        private static void DisplayUsage()
        {
            string entryAssemblyPath = System.Reflection.Assembly.GetEntryAssembly().Location;
            string entryAssemblyName = System.IO.Path.GetFileName(entryAssemblyPath);

            Console.WriteLine("Display share properties:");
            Console.WriteLine();
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password>");
            Console.WriteLine();
            Console.WriteLine("Add access control entry:");
            Console.WriteLine();
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <sid>");
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <domain> <account>");
            Console.WriteLine();
            Console.WriteLine($"\t  <type> - A {typeof(AceType).FullName} enumeration value.");
            Console.WriteLine($"\t<rights> - A {typeof(FileSystemRights).FullName} enumeration value.");
            Console.WriteLine("\t   <sid> - An account security identifier.");
            Console.WriteLine();
        }

        private static void DisplayError(string message)
        {
            Console.WriteLine($"ERROR: {message}");
            Console.WriteLine();
            DisplayUsage();
        }

        private static void DisplayTextWithSeparator(string text)
        {
            Console.WriteLine(text);
            Console.WriteLine(new string('=', text.Length));
        }

        private static void DisplayShareProperties(ShareSecurity shareSecurity)
        {
            IDictionary<string, object> shareProperties = shareSecurity.GetShareProperties();

            DisplayTextWithSeparator("Share properties");
            foreach (string propertyName in new string[] { "Description", "Name", "Path" })
                DisplayProperty(shareProperties, propertyName);
            Console.WriteLine();
        }

        private static void DisplaySecurityDescriptor(ShareSecurity shareSecurity)
        {
            IDictionary<string, object> securityDescriptorProperties = shareSecurity.GetShareSecurityDescriptorProperties();

            DisplayTextWithSeparator("Share security descriptor");
            DisplayProperty(securityDescriptorProperties, "ControlFlags", value => (ControlFlags) (uint) value);
            Console.WriteLine();

            IDictionary<string, object>[] accessControlList = (IDictionary<string, object>[]) securityDescriptorProperties["DACL"];
            for (int i = 0; i < accessControlList.Length; i++)
            {
                IDictionary<string, object> accessControlEntryProperties = accessControlList[i];

                DisplayTextWithSeparator($"Access control entry #{i}");
                DisplayProperty(accessControlEntryProperties, "AccessMask", value => (FileSystemRights) (uint) value);
                DisplayProperty(accessControlEntryProperties, "AceFlags", value => (AceFlags) (uint) value);
                DisplayProperty(accessControlEntryProperties, "AceType", value => (AceType) (uint) value);
                DisplayProperty(accessControlEntryProperties, "Trustee", value => ((IDictionary<string, object>) value)["Name"]);
                Console.WriteLine();
            }
        }

        private static void DisplayProperty(IDictionary<string, object> properties, string propertyName)
        {
            DisplayProperty(properties, propertyName, null);
        }

        private static void DisplayProperty(IDictionary<string, object> properties, string propertyName, Func<object, object> selector)
        {
            object propertyValue = properties[propertyName];
            object displayValue = selector != null
                ? selector.Invoke(propertyValue)
                : propertyValue ?? "<null>";

            Console.WriteLine($"{propertyName}: {displayValue}");
        }
    }
}
...