/// <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; } } } }