/// <summary> /// Adds or replaces groups in the specified Authz Client Context. /// </summary> /// <remarks>This method invokes AuthzModifySids, modifying the groups /// using AUTHZ_SID_OPERATION_REPLACE. This ensures that a group that /// already exists is retained and the ones not present are added</remarks> /// <param name="hAuthzClientContext">Handle to the Authz Client Context to be modified</param> /// <returns>Win32Error.ERROR_SUCCESS on success and Win32 error code otherwise.</returns> public int ApplyGroups(AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext) { var toBeFreed = new List <SafeHGlobalHandle>(); var sidAndAttributes = new List <NativeMethods.SID_AND_ATTRIBUTES>(this.Count); var sidOps = new NativeMethods.AuthzSIDOperation[this.Count]; foreach (var sid in this) { byte[] rawSid = new byte[sid.BinaryLength]; sid.GetBinaryForm(rawSid, 0); SafeHGlobalHandle safesid = SafeHGlobalHandle.AllocHGlobal(rawSid); toBeFreed.Add(safesid); sidAndAttributes.Add(new NativeMethods.SID_AND_ATTRIBUTES(safesid.ToIntPtr(), NativeMethods.GroupAddtibute.Enabled)); } SafeHGlobalHandle tokenGroups = SafeHGlobalHandle.AllocHGlobal(Marshal.SizeOf(typeof(ULONG)), sidAndAttributes, sidAndAttributes.Count); Marshal.WriteInt32(tokenGroups.ToIntPtr(), sidAndAttributes.Count); for (int Idx = 0; Idx < this.Count; ++Idx) { sidOps[Idx] = NativeMethods.AuthzSIDOperation.Replace; } if (!NativeMethods.AuthzModifySids(hAuthzClientContext, type == GroupType.User ? NativeMethods.AuthzContextInformationClass.AuthzContextInfoGroupsSids : NativeMethods.AuthzContextInformationClass.AuthzContextInfoDeviceSids, sidOps, tokenGroups.ToIntPtr())) { return(Marshal.GetLastWin32Error()); } return(Win32Error.ERROR_SUCCESS); }
/// <summary> /// Adds or replaces claims in the specified Authz Client Context. /// </summary> /// <remarks>This method invokes AuthzModifyClaims, modifying the claims /// using AUTHZ_SECURITY_ATTRIBUTE_OPERATION_REPLACE. This ensures that /// the values of a claims that already exists are replaces and the ones /// not present are added.</remarks> /// <param name="handleClientContext">Handle to the Authz Client Context to be modified</param> /// <returns>Win32Error.ERROR_SUCCESS on success and Win32 error code otherwise.</returns> public int ApplyClaims(AUTHZ_CLIENT_CONTEXT_HANDLE handleClientContext) { NativeMethods.AuthzSecurityAttributeOperation[] claimOps = null; var claims = new List <NativeMethods.AUTHZ_SECURITY_ATTRIBUTE_V1>(this.Count); foreach (var claim in this) { // // If all of the value specified turned out invalid, ignore the claim altogether. // if (claim.Value.ValueCount == 0) { continue; } var attribute = new NativeMethods.AUTHZ_SECURITY_ATTRIBUTE_V1(); attribute.Name = claim.Key; attribute.Flags = 0; attribute.Values = claim.Value.RawValues.ToIntPtr(); attribute.ValueCount = claim.Value.ValueCount; switch (claim.Value.ValueType) { case ClaimValueType.Integer: { Debug.Assert(attribute.ValueCount == 1); attribute.Type = NativeMethods.AuthzSecurityAttributeValueType.Int; break; } case ClaimValueType.Boolean: { Debug.Assert(attribute.ValueCount == 1); attribute.Type = NativeMethods.AuthzSecurityAttributeValueType.Boolean; break; } case ClaimValueType.String: { Debug.Assert(attribute.ValueCount == 1); goto case ClaimValueType.MultiValuedString; } case ClaimValueType.MultiValuedString: { attribute.Type = NativeMethods.AuthzSecurityAttributeValueType.String; break; } } claims.Add(attribute); } var claimInfo = new NativeMethods.AUTHZ_SECURITY_ATTRIBUTES_INFORMATION(); claimInfo.Version = 1; // AUTHZ_SECURITY_ATTRIBUTES_INFORMATION_VERSION_V1 claimInfo.Reserved = 0; claimInfo.AttributeCount = (ULONG)claims.Count; SafeHGlobalHandle v1Attributes = SafeHGlobalHandle.InvalidHandle; if (claimInfo.AttributeCount != 0) { v1Attributes = SafeHGlobalHandle.AllocHGlobal(claims); claimOps = new NativeMethods.AuthzSecurityAttributeOperation[claimInfo.AttributeCount]; for (ULONG Idx = 0; Idx < claimInfo.AttributeCount; ++Idx) { claimOps[Idx] = NativeMethods.AuthzSecurityAttributeOperation.Replace; } } claimInfo.pAttributeV1 = v1Attributes.ToIntPtr(); if (!NativeMethods.AuthzModifyClaims(handleClientContext, claimDefnType == ClaimDefinitionType.User ? NativeMethods.AuthzContextInformationClass.AuthzContextInfoUserClaims : NativeMethods.AuthzContextInformationClass.AuthzContextInfoDeviceClaims, claimOps, ref claimInfo)) { return(Marshal.GetLastWin32Error()); } return(Win32Error.ERROR_SUCCESS); }
/// <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; } } } }