/// <summary>
        /// Computes the effective access permissions
        /// </summary>
        /// <param name="userSid">User account for which effective access rights are to be determined</param>
        /// <param name="deviceSid">Account to simulate the device where from the resource is being accessed</param>
        /// <param name="serverName">Target machine on which the resource</param>
        /// <param name="securityObjects">Security objects that affect that result of the access check</param>
        /// <returns>Dictionary of rights from NTFSAccess and the security object name that limits access to this
        /// permission</returns>
        Dictionary <NativeMethods.FileAccess, string> ComputeEffectiveAccessResult(
            string serverName,
            Dictionary <string, FileSecurityObject> securityObjects)
        {
            AUTHZ_CLIENT_CONTEXT_HANDLE userClientCtxt   = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;
            AUTHZ_CLIENT_CONTEXT_HANDLE deviceClientCtxt = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;
            AUTHZ_CLIENT_CONTEXT_HANDLE compoundCtxt     = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;

            try
            {
                var rpcInitInfo = new NativeMethods.AUTHZ_RPC_INIT_INFO_CLIENT();

                rpcInitInfo.version    = NativeMethods.AuthzRpcClientVersion.V1;
                rpcInitInfo.objectUuid = NativeMethods.AUTHZ_OBJECTUUID_WITHCAP;
                rpcInitInfo.protocol   = NativeMethods.RCP_OVER_TCP_PROTOCOL;
                rpcInitInfo.server     = serverName;

                SafeAuthzRMHandle authzRM;

                SafeHGlobalHandle pRpcInitInfo = SafeHGlobalHandle.AllocHGlobalStruct(rpcInitInfo);
                if (!NativeMethods.AuthzInitializeRemoteResourceManager(pRpcInitInfo.ToIntPtr(), out authzRM))
                {
                    int error = Marshal.GetLastWin32Error();

                    if (error != Win32Error.EPT_S_NOT_REGISTERED)
                    {
                        throw new Win32Exception(error);
                    }

                    //
                    // As a fallback we do AuthzInitializeResourceManager. But the results can be inaccurate.
                    //
                    Helper.LogWarning("The effective rights can only be computed based on group membership on this" +
                                      " computer. For more accurate results, calculate effective access rights on " +
                                      "the target computer.", true);

                    if (!NativeMethods.AuthzInitializeResourceManager(
                            NativeMethods.AuthzResourceManagerFlags.NO_AUDIT,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            "EffectiveAccessCheck",
                            out authzRM))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }

                byte[] rawSid = new byte[userSid.BinaryLength];
                userSid.GetBinaryForm(rawSid, 0);

                //
                // Create an AuthZ context based on the user account
                //
                if (!NativeMethods.AuthzInitializeContextFromSid(NativeMethods.AuthzInitFlags.Default,
                                                                 rawSid,
                                                                 authzRM,
                                                                 PLARGE_INTEGER.Zero,
                                                                 Win32.LUID.NullLuid,
                                                                 LPVOID.Zero,
                                                                 out userClientCtxt))
                {
                    Win32Exception win32Expn = new Win32Exception(Marshal.GetLastWin32Error());

                    if (win32Expn.NativeErrorCode != Win32Error.RPC_S_SERVER_UNAVAILABLE)
                    {
                        throw win32Expn;
                    }

                    Helper.LogWarning(string.Format(CultureInfo.CurrentCulture,
                                                    "{0}. Please enable the inward firewall rule: Netlogon Service " +
                                                    "Authz(RPC), on the target machine and try again.", win32Expn.Message),
                                      true);
                    return(null);
                }

                //
                // Create an Authz Compound context based on the userClientCtxt and the device account
                //
                if (deviceSid != null)
                {
                    rawSid = new byte[deviceSid.BinaryLength];
                    deviceSid.GetBinaryForm(rawSid, 0);

                    if (!NativeMethods.AuthzInitializeContextFromSid(NativeMethods.AuthzInitFlags.Default,
                                                                     rawSid,
                                                                     authzRM,
                                                                     PLARGE_INTEGER.Zero,
                                                                     Win32.LUID.NullLuid,
                                                                     LPVOID.Zero,
                                                                     out deviceClientCtxt))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }

                    if (!NativeMethods.AuthzInitializeCompoundContext(userClientCtxt,
                                                                      deviceClientCtxt,
                                                                      out compoundCtxt))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    compoundCtxt = userClientCtxt;
                }

                //
                // Modify user claims in the Authz context
                //
                if (userClaims.Count != 0)
                {
                    int result = userClaims.ApplyClaims(compoundCtxt);

                    if (result != Win32Error.ERROR_SUCCESS)
                    {
                        throw new Win32Exception(result);
                    }
                }

                //
                // Modify device claims in the Authz context
                //
                if (deviceClaims.Count != 0)
                {
                    int result = deviceClaims.ApplyClaims(compoundCtxt);

                    if (result != Win32Error.ERROR_SUCCESS)
                    {
                        throw new Win32Exception(result);
                    }
                }

                //
                // Include additional group membership in the Authz context
                //
                if (userGroups.Count != 0)
                {
                    int result = userGroups.ApplyGroups(compoundCtxt);

                    if (result != Win32Error.ERROR_SUCCESS)
                    {
                        throw new Win32Exception(result);
                    }
                }

                //
                // Include additional device membership in the Authz context
                //
                if (deviceGroups.Count != 0)
                {
                    int result = deviceGroups.ApplyGroups(compoundCtxt);

                    if (result != Win32Error.ERROR_SUCCESS)
                    {
                        throw new Win32Exception(result);
                    }
                }

                PACCESS_MASK[] grantedAccess = new PACCESS_MASK[securityObjects.Count];
                PDWORD[]       errorSecObj   = new PACCESS_MASK[securityObjects.Count];

                try
                {
                    uint Index = 0;
                    foreach (var securityObject in securityObjects)
                    {
                        NativeMethods.AUTHZ_ACCESS_REQUEST request = new NativeMethods.AUTHZ_ACCESS_REQUEST();
                        request.DesiredAccess        = NativeMethods.StdAccess.MAXIMUM_ALLOWED;
                        request.PrincipalSelfSid     = null;
                        request.ObjectTypeList       = POBJECT_TYPE_LIST.Zero;
                        request.ObjectTypeListLength = 0;
                        request.OptionalArguments    = LPVOID.Zero;

                        var reply = new NativeMethods.AUTHZ_ACCESS_REPLY();
                        reply.ResultListLength      = 1;
                        reply.SaclEvaluationResults = PDWORD.Zero;
                        reply.GrantedAccessMask     = grantedAccess[Index] = Marshal.AllocHGlobal(sizeof(uint));
                        reply.Error = errorSecObj[Index] = Marshal.AllocHGlobal(sizeof(uint));

                        byte[] rawSD = new byte[securityObject.Value.securityDescriptor.BinaryLength];
                        securityObject.Value.securityDescriptor.GetBinaryForm(rawSD, 0);

                        //
                        // If a security object has the AppliesTo predicate, then we are processing a Central Access
                        // Rule. If the AppliesTo predicate is not satisfied, we do not have to bother performing an
                        // access check on the Central Access Rule's DACL.
                        //
                        if (securityObject.Value.appliesTo != null)
                        {
                            byte[] rawSD2 = new byte[securityObject.Value.appliesTo.BinaryLength];
                            securityObject.Value.appliesTo.GetBinaryForm(rawSD2, 0);

                            if (!NativeMethods.AuthzAccessCheck(NativeMethods.AuthzACFlags.None,
                                                                compoundCtxt,
                                                                ref request,
                                                                AUTHZ_AUDIT_EVENT_HANDLE.Zero,
                                                                rawSD2,
                                                                null,
                                                                0,
                                                                ref reply,
                                                                AUTHZ_ACCESS_CHECK_RESULTS_HANDLE.Zero))
                            {
                                throw new Win32Exception(Marshal.GetLastWin32Error());
                            }

                            //
                            // The applies to DACL is D:(XA;;FR;;;<user-sid>;<resource condition>).
                            // Check if we were successful in acquiring this particular access
                            //
                            var whatWasGranted = (NativeMethods.FileAccess)Marshal.ReadInt32(grantedAccess[Index]);
                            if ((NativeMethods.FileAccess.ReadData & whatWasGranted) == 0)
                            {
                                securityObject.Value.result.grantedAccess = NativeMethods.FileAccess.CategoricalAll;
                                continue;
                            }
                        }

                        if (!NativeMethods.AuthzAccessCheck(NativeMethods.AuthzACFlags.None,
                                                            compoundCtxt,
                                                            ref request,
                                                            AUTHZ_AUDIT_EVENT_HANDLE.Zero,
                                                            rawSD,
                                                            null,
                                                            0,
                                                            ref reply,
                                                            AUTHZ_ACCESS_CHECK_RESULTS_HANDLE.Zero))
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }

                        securityObject.Value.result.grantedAccess = (NativeMethods.FileAccess)
                                                                    Marshal.ReadInt32(grantedAccess[Index]);
                        ++Index;
                    }

                    //
                    // Build the effective permission results
                    //
                    var accessLimitedBy = new Dictionary <NativeMethods.FileAccess, string>();

                    foreach (var permission in NTFSAccess)
                    {
                        foreach (var securityObject in securityObjects)
                        {
                            if (!accessLimitedBy.ContainsKey(permission.Key))
                            {
                                accessLimitedBy.Add(permission.Key, "");
                            }

                            if ((permission.Key & securityObject.Value.result.grantedAccess) != permission.Key)
                            {
                                if (!string.IsNullOrEmpty(accessLimitedBy[permission.Key]))
                                {
                                    accessLimitedBy[permission.Key] += ", ";
                                }

                                accessLimitedBy[permission.Key] += securityObject.Key;
                            }
                        }
                    }

                    return(accessLimitedBy);
                }
                finally
                {
                    for (int Index = 0; Index < grantedAccess.Length; ++Index)
                    {
                        Marshal.FreeHGlobal(grantedAccess[Index]);
                    }

                    for (int Index = 0; Index < errorSecObj.Length; ++Index)
                    {
                        Marshal.FreeHGlobal(errorSecObj[Index]);
                    }
                }
            }
            finally
            {
                if (userClientCtxt != AUTHZ_CLIENT_CONTEXT_HANDLE.Zero)
                {
                    NativeMethods.AuthzFreeContext(userClientCtxt);
                    userClientCtxt = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;
                }

                if (deviceClientCtxt != AUTHZ_CLIENT_CONTEXT_HANDLE.Zero)
                {
                    NativeMethods.AuthzFreeContext(deviceClientCtxt);
                    deviceClientCtxt = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;

                    if (compoundCtxt != AUTHZ_CLIENT_CONTEXT_HANDLE.Zero)
                    {
                        NativeMethods.AuthzFreeContext(compoundCtxt);
                        compoundCtxt = AUTHZ_CLIENT_CONTEXT_HANDLE.Zero;
                    }
                }
            }
        }