/// <summary> /// The constructor /// </summary> /// <param name="binary">The input binary</param> /// <param name="offset">The offset in the binary</param> public _NonObjectAce(byte[] binary, int offset) : base(binary, offset) { //The AceSize field aceSize = BitConverter.ToUInt16(binary, offset + 2); if (offset > binary.Length - aceSize) { throw new ArgumentException(nameof(offset)); } if (aceSize < 8 + DtypUtility.MinLengthOfSecurityIdentifier) { throw new ArgumentException(nameof(aceSize)); } _AccessMask = BitConverter.ToInt32(binary, offset + DtypUtility.ACE_HEADER_LENGTH); _SecurityIdentifier = new _SID(binary, offset + DtypUtility.SHORT_FIXED_ACE_LENGTH); int dataLength = aceSize - (DtypUtility.SHORT_FIXED_ACE_LENGTH + _SecurityIdentifier.Size); if (dataLength > 0)// If there is still other application data exists { this.applicationData = new byte[dataLength]; Array.Copy(binary, offset + DtypUtility.SHORT_FIXED_ACE_LENGTH + _SecurityIdentifier.Size, this.applicationData, 0, dataLength); } }
/// <summary> /// Customized length calculator of ApplicationData, called by Channel. /// This method's name is written in ApplicationData's Size attribute. /// </summary> /// <param name="context">Channel's context.</param> /// <returns>ApplicationData's length.</returns> // this method is called by Channel. internal static int CalculateApplicationDataLength(IEvaluationContext context) { _ACE_HEADER header = (_ACE_HEADER)context.Variables["Header"]; _SID sid = (_SID)context.Variables["Sid"]; return(DtypUtility.CalculateApplicationDataLength(header, sid)); }
public string GetSddlForm() { StringBuilder result = new StringBuilder(); _SID sid = TypeMarshal.ToStruct <_SID>(buffer); string sddl = DtypUtility.ToSddlString(sid); result.AppendFormat(CultureInfo.InvariantCulture, "O:{0}", sddl); return(result.ToString()); }
/// <summary> /// The constructor /// </summary> /// <param name="binary">The input binary</param> /// <param name="offset">The offset in the binary</param> public _ObjectAce(byte[] binary, int offset) : base(binary, offset) { int length = BitConverter.ToUInt16(binary, offset + 2); if (offset > binary.Length - length) { throw new ArgumentException(nameof(offset)); } _AccessMask = BitConverter.ToInt32(binary, offset + DtypUtility.ACE_HEADER_LENGTH); ObjectFlags = (_ObjectAceFlags)BitConverter.ToInt32(binary, offset + DtypUtility.SHORT_FIXED_ACE_LENGTH); int pointer = DtypUtility.ACE_HEADER_LENGTH + DtypUtility.SHORT_FIXED_ACE_LENGTH; if (ObjectFlags.HasFlag(_ObjectAceFlags.ObjectAceTypePresent)) { ObjectType = DtypUtility.ReadGuid(binary, offset + pointer); pointer += 16; } if (ObjectFlags.HasFlag(_ObjectAceFlags.InheritedObjectAceTypePresent)) { InheritedObjectType = DtypUtility.ReadGuid(binary, offset + pointer); pointer += 16; } _SecurityIdentifier = new _SID(binary, offset + pointer); pointer += _SecurityIdentifier.Size; int appDataLength = length - pointer; if (appDataLength > 0) { this.applicationData = new byte[appDataLength]; Array.Copy(binary, offset + pointer, this.applicationData, 0, appDataLength); } }
/// <summary> /// A support function, SidInToken, takes the authorization context, /// a SID (referenced below as the SidToTest parameter), and an optional /// PrincipalSelfSubstitute parameter, and returns TRUE if the SidToTest /// is present in the authorization context; otherwise, it returns FALSE. /// The well-known SID PRINCIPAL_SELF, if passed as SidToTest, is replaced /// by the PrincipalSelfSubstitute SID prior to the examination of the /// authorization context. MS-DTYP section 2.5.4.1 /// </summary> /// <param name="token"> /// Token is an authorization context containing all SIDs /// that represent the security principal /// </param> /// <param name="sidToTest"> /// The SID for which to search in Token /// </param> /// <param name="principalSelfSubstitute"> /// a SID with which SidToTest may be replaced /// </param> /// <returns> /// Returns TRUE if the SidToTest is present in the authorization context; /// otherwise, it returns FALSE. /// </returns> /// <exception cref="ArgumentException"> /// Thrown when the well-known SID PRINCIPAL_SELF, if passed as SidToTest, but /// principalSelfSubstitute does't exist. /// </exception> public static bool SidInToken( Token token, _SID sidToTest, _SID? principalSelfSubstitute) { // //Pseudocode syntax // //-- //-- On entry //-- Token is an authorization context containing all SIDs //-- that represent the security principal //-- SidToTest, the SID for which to search in Token //-- PrincipalSelfSubstitute, a SID with which SidToTest may be //-- replaced //IF SidToTest is the Well Known SID PRINCIPAL_SELF THEN //set SidToTest to be PrincipalSelfSubstitute //END IF //FOR EACH SID s in Token DO //IF s equals SidToTest THEN //return TRUE //END IF //END FOR //Return FALSE //END-SUBROUTINE if (ObjectUtility.DeepCompare(GetWellKnownSid(WellKnownSid.PRINCIPAL_SELF, null), sidToTest)) { if (principalSelfSubstitute != null) { sidToTest = (_SID)principalSelfSubstitute; } else { throw new ArgumentException("The principalSelfSubstitute doesn't exist", "principalSelfSubstitute"); } } if (token.Sids != null) { foreach (_SID sid in token.Sids) { if (ObjectUtility.DeepCompare(sid, sidToTest)) { return true; } } } return false; }
/// <summary> /// A support function, SidDominates, compares the mandatory integrity levels expressed in two SIDs. /// This function can be used only on SIDs that encode integrity levels (the SID_IDENTIFIER_AUTHORITY /// field is SECURITY_MANDATORY_LABEL_AUTHORITY); any other use is unsupported. /// </summary> /// <param name="sid1">first sid in dominance calculation</param> /// <param name="sid2">second sid in dominance calculation</param> /// <returns> /// The function returns TRUE if the first SID dominates the second SID or is equal to the second SID, /// or FALSE if the first SID is subordinate to the second SID. /// </returns> public static bool SidDominates(_SID sid1, _SID sid2) { //Described in MS-DTYP Section 2.5.3.1.2 SidDominates: // //IF sid1 equals sid2 THEN // Return TRUE //END IF //-- If Sid2 has more SubAuthorities than Sid1, Sid1 cannot dominate. //IF sid2.SubAuthorityCount GREATER THAN sid1.SubAuthorityCount THEN // Return FALSE //END IF //--on entry, index is zero and is incremented for each iteration of the loop. //FOR each SubAuthority in sid1 // IF sid1.SubAuthority[ index ] GREATER THAN or EQUAL TO sid2.SubAuthority[ index ] THEN // Return TRUE // END IF //END FOR //Return FALSE if (ObjectUtility.DeepCompare(sid1, sid2) == true) { return true; } if (sid2.SubAuthorityCount > sid1.SubAuthorityCount) { return false; } for (int i = 0; i < sid1.SubAuthorityCount; i++) { if (i >= sid2.SubAuthorityCount) { break; } if (sid1.SubAuthority[i] >= sid2.SubAuthority[i]) { return true; } } return false; }
/// <summary> /// PostProcessACL. MS-DTYP section 2.5.4.9 /// </summary> /// <param name="acl">ACL on which to substitute SIDs.</param> /// <param name="copyFilter">The filter for post-processing the ACL.</param> /// <param name="owner">Owner to use in substituting the CreatorOwner SID.</param> /// <param name="group">Group to use in substituting the CreatorGroup SID.</param> /// <param name="genericMapping"> /// Mapping of generic permissions to resource manager-specific /// permissions supplied by the caller. /// </param> /// <returns> /// The computed ACL with the SID substitutions performed. /// </returns> public static _ACL PostProcessACL( _ACL acl, CopyFilter copyFilter, _SID owner, _SID group, GenericMapping genericMapping) { #region Pseudocode v20110329 //// Substitute CreatorOwner and CreatorGroup SIDs and do GenericMapping in ACL //Initialize NewACL to Empty ACL //FOR each ACE in ACL DO // // Determine if this ACE passes the filter to be copied to the new ACL // SET CopyThisAce = FALSE // CASE CopyFilter OF // CopyAllAces: // BEGIN // SET CopyThisAce = TRUE // END // CopyInheritedAces: // BEGIN // IF (ACE.AceFlags contains INHERITED_ACE) THEN // SET CopyThisAce = TRUE // ENDIF // END // CopyExplicitAces: // BEGIN // IF (ACE.AceFlags does not contain INHERITED_ACE) THEN // SET CopyThisAce = TRUE // ENDIF // END // ENDCASE // Set NewACE to ACE // IF (CopyThisAce) THEN // CASE ACE.Sid OF // CREATOR_OWNER: // NewACE.Sid = Owner // CREATOR_GROUP: // NewACE.Sid = Group // ENDCASE // IF (ACE.Mask contains GENERIC_READ) THEN // Add GenericMapping.GenericRead to NewACE.Mask // ENDIF // IF (ACE.Mask contains GENERIC_WRITE) THEN // Add GenericMapping.GenericWrite to NewACE.Mask // ENDIF // IF (ACE.Mask contains GENERIC_EXECUTE) THEN // Add GenericMapping.GenericExecute to NewACE.Mask // ENDIF // Append NewACE to NewACL // ENDIF //END FOR //RETURN NewACL //// END PostProcessACL #endregion _ACL newAcl = new _ACL(); newAcl.AclRevision = acl.AclRevision; newAcl.Aces = new object[] { }; List<object> newAces = new List<object>(); if (acl.Aces != null) { foreach (object ace in acl.Aces) { object newAce = ObjectUtility.DeepClone(ace); _ACE_HEADER header = (_ACE_HEADER)ObjectUtility.GetFieldValue(newAce, "Header"); bool copyThisAce = false; switch (copyFilter) { case CopyFilter.CopyAllAces: copyThisAce = true; break; case CopyFilter.CopyInheritedAces: if ((header.AceFlags & ACE_FLAGS.INHERITED_ACE) == ACE_FLAGS.INHERITED_ACE) { copyThisAce = true; } break; case CopyFilter.CopyExplicitAces: if ((header.AceFlags & ACE_FLAGS.INHERITED_ACE) == ACE_FLAGS.None) { copyThisAce = true; } break; } if (copyThisAce) { #region Substitute CreatorOwner and CreatorGroup SIDs in ACL _SID sid = (_SID)ObjectUtility.GetFieldValue(newAce, "Sid"); if (ObjectUtility.DeepCompare(sid, GetWellKnownSid(WellKnownSid.CREATOR_OWNER, null))) { ObjectUtility.SetFieldValue(newAce, "Sid", owner); header.AceSize = (ushort)(header.AceSize - (sid.SubAuthorityCount - owner.SubAuthorityCount) * sizeof(uint)); ObjectUtility.SetFieldValue(newAce, "Header", header); } else if (ObjectUtility.DeepCompare(sid, GetWellKnownSid(WellKnownSid.CREATOR_GROUP, null))) { ObjectUtility.SetFieldValue(newAce, "Sid", group); header.AceSize = (ushort)(header.AceSize - (sid.SubAuthorityCount - group.SubAuthorityCount) * sizeof(uint)); ObjectUtility.SetFieldValue(newAce, "Header", header); } #endregion #region Do GenericMapping in ACL uint mask = (uint)ObjectUtility.GetFieldValue(newAce, "Mask"); if ((mask & ACCESS_MASK_GENERIC_READ) == ACCESS_MASK_GENERIC_READ) { mask = mask | genericMapping.GenericRead; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } if ((mask & ACCESS_MASK_GENERIC_WRITE) == ACCESS_MASK_GENERIC_WRITE) { mask = mask | genericMapping.GenericWrite; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } if ((mask & ACCESS_MASK_GENERIC_EXECUTE) == ACCESS_MASK_GENERIC_EXECUTE) { mask = mask | genericMapping.GenericExecute; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } #endregion //Append NewACE to NewACL newAces.Add(newAce); } } } newAcl.Aces = newAces.ToArray(); newAcl.AceCount = (ushort)newAcl.Aces.Length; newAcl.AclSize = DtypUtility.CalculateAclSize(newAcl); return newAcl; }
/// <summary> /// MandatoryIntegrityCheck. MS-DTYP section 2.5.4.1 /// </summary> /// <param name="tokenIntegritySID"> /// Mandatory Integrity SID in the Token /// </param> /// <param name="aceIntegritySID"> /// Mandatory Integrity label SID in the Security descriptor of the object /// </param> /// <param name="mandatoryInformation"> /// Output of the function describing the allowable bits for the caller /// </param> /// <param name="token"> /// Authorization context /// </param> /// <param name="objectSecurityDescriptor"> /// SECURITY_DESCRIPTOR structure that is assigned to the object /// </param> /// <returns>True if check is done successfully; otherwise, false</returns> public static bool MandatoryIntegrityCheck( _SID tokenIntegritySID, _SID aceIntegritySID, out uint mandatoryInformation, Token token, _SECURITY_DESCRIPTOR objectSecurityDescriptor) { //Set TokenPolicy to Token MandatoryPolicy field //Set ObjectIntegrityACE to SecurityDescriptor ObjectIntegrity ACE field //Set ObjectIntegrityAceMask to SecurityDescriptor ObjectIntegrity Accessmask field // //IF TokenPolicy.Policy EQUAL TOKEN_MANDATORY_POLICY_OFF OR //TokenPolicy.Policy EQUAL TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN THEN //Set MandatoryInformation.AllowedAccess to GENERIC_ALL //Return success //END IF // //IF ObjectIntegrityACE.AceFlag NOT EQUAL INHERIT_ONLY_ACE THEN //Set AceMask to ObjectIintegrityACE.AccessMask //Set AceIntegritysid to ObjectIntegrityACE.TrusteeSid //ELSE //Set AceMask to SYSTEM_MANDATORY_LABEL_NO_WRITE_UP //--The DefaultMandatorySid is derived from policy managed in an //--implementation specific manner. The SID for ML_MEDIUM is used by //--Windows. //Set AceIntegritysid to DefaultMandatorySid //END IF TokenMandatoryPolicyValue tokenPolicy = token.MandatoryPolicy; _SYSTEM_MANDATORY_LABEL_ACE? objectIntegrityACE = null; mandatoryInformation = 0; bool tokenDominates; if (tokenPolicy == TokenMandatoryPolicyValue.TOKEN_MANDATORY_POLICY_OFF || tokenPolicy == TokenMandatoryPolicyValue.TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN) { mandatoryInformation = ACCESS_MASK_GENERIC_ALL; return true; } if ((objectSecurityDescriptor.Control & SECURITY_DESCRIPTOR_Control.SACLPresent) == SECURITY_DESCRIPTOR_Control.SACLPresent && objectSecurityDescriptor.Sacl != null) { if (objectSecurityDescriptor.Sacl.Value.Aces != null) { foreach (object ace in objectSecurityDescriptor.Sacl.Value.Aces) { _SID sid = (_SID)ObjectUtility.GetFieldValue(ace, "Sid"); if (ObjectUtility.DeepCompare(sid, aceIntegritySID)) { objectIntegrityACE = (_SYSTEM_MANDATORY_LABEL_ACE)ace; } } } } SYSTEM_MANDATORY_LABEL_ACE_Mask objectIntegrityAceMask; _SID aceIntegritySid; if (objectIntegrityACE != null && objectIntegrityACE.Value.Header.AceFlags != ACE_FLAGS.INHERIT_ONLY_ACE) { objectIntegrityAceMask = objectIntegrityACE.Value.Mask; aceIntegritySid = objectIntegrityACE.Value.Sid; } else { objectIntegrityAceMask = SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_WRITE_UP; //The DefaultMandatorySid is derived from policy managed in an //implementation specific manner. The SID for ML_MEDIUM is used by //Windows. aceIntegritySid = GetWellKnownSid(WellKnownSid.ML_MEDIUM, null); } // //Note:SidDominates is removed from the current function, because it's declared but not used. // //CALL sidEqual(AceIntegritysid, TokenIntegritysid) //IF AceIntegritySid EQUALS Token.MandatoryIntegritySid THEN //Set TokenDominates to TRUE //Set ObjectDominates to TRUE //ELSE //CALL SidDominates (Token.MandatoryIntegritySid, AceIntegritysid) //IF SidDominates returns TRUE THEN //Set TokenDominates to TRUE //ELSE //Set TokenDominates to FALSE //END IF //Set ObjectDominates to NOT(TokenDominates) //END IF tokenDominates = SidDominates((_SID)token.IntegrityLevelSid, aceIntegritySid); //IF TokenPolicy EQUAL TOKEN_MANDATORY_POLICY_NO_WRITE_UP THEN //Add READ to MandatoryInformation.AllowedAccess //Add EXECUTE to MandatoryInformation.AllowedAccess //IF TokenDominates is TRUE THEN //Add WRITE to MandatoryInformation.AllowedAccess //END IF //END IF // //IF TokenDominates is FALSE THEN //IF ObjectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_NO_READ_UP THEN //Remove READ from MandatoryInformation.AllowedAccess //END IF //IF ObjectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP THEN //Remove WRITE from MandatoryInformation.AllowedAccess //END IF //IF ObjectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP THEN //Remove EXECUTE from MandatoryInformation.AllowedAccess //END IF //END IF // //IF Token.Privileges contains SeRelabelPrivilegeTHEN //Add WRITE_OWNER to MandatoryInformation.AllowedAccess //END IF if (tokenPolicy == TokenMandatoryPolicyValue.TOKEN_MANDATORY_POLICY_NO_WRITE_UP) { mandatoryInformation |= ACCESS_MASK_GENERIC_READ; mandatoryInformation |= ACCESS_MASK_GENERIC_EXECUTE; if (tokenDominates) { mandatoryInformation |= ACCESS_MASK_GENERIC_WRITE; } } if (!tokenDominates) { if ((objectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_READ_UP) == SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_READ_UP) { mandatoryInformation &= ~ACCESS_MASK_GENERIC_READ; } if ((objectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_WRITE_UP) == SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_WRITE_UP) { mandatoryInformation &= ~ACCESS_MASK_GENERIC_WRITE; } if ((objectIntegrityAceMask & SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP) == SYSTEM_MANDATORY_LABEL_ACE_Mask.SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP) { mandatoryInformation &= ~ACCESS_MASK_GENERIC_EXECUTE; } } if (token.Privileges != null) { foreach (_LUID privilege in token.Privileges) { if (ObjectUtility.DeepCompare(privilege, GetPrivilegeLuid(PrivilegeName.SE_RELABEL_NAME))) { mandatoryInformation |= ACCESS_MASK_WRITE_OWNER; } } } return true; }
/// <summary> /// Create a SYSTEM_SCOPED_POLICY_ID_ACE with specified SID and optional ACE_FLAGS. /// </summary> /// <param name="sid">A SID that identifies a central access policy.</param> /// <param name="flags">An unsigned 8-bit integer that specifies a set of ACE type-specific control flags. </param> /// <returns>Return the ACE.</returns> public static _SYSTEM_SCOPED_POLICY_ID_ACE CreateSystemScopedPolicyIdAce(_SID sid, ACE_FLAGS flags = ACE_FLAGS.OBJECT_INHERIT_ACE | ACE_FLAGS.CONTAINER_INHERIT_ACE) { _ACE_HEADER aceHeader = new _ACE_HEADER { AceFlags = flags, AceType = ACE_TYPE.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE, // Header (4 bytes) + Mask (4 bytes) + SID length; // For details, please refer to MS-DTYP. AceSize = (ushort)(4 + 4 + DtypUtility.SidLength(sid)), }; _SYSTEM_SCOPED_POLICY_ID_ACE ace = new _SYSTEM_SCOPED_POLICY_ID_ACE { Header = aceHeader, Mask = 0, // An ACCESS_MASK that MUST be set to zero. Sid = sid, }; return ace; }
public _SecurityIdentifier(string sddl) { _SID sid = DtypUtility.ToSid(sddl); this.buffer = TypeMarshal.ToBytes <_SID>(sid); }
/// <summary> /// Calculate the length of the ApplicationData byte array according to the specified ace header. /// </summary> /// <param name="header">The specified ace header.</param> /// <param name="sid">The sid of the specified ace.</param> /// <returns>The calculated length.</returns> /// <exception cref="ArgumentOutOfRangeException">Throw when the ace type is invalid.</exception> internal static int CalculateApplicationDataLength(_ACE_HEADER header, _SID sid) { int applicationDataLength = 0; switch (header.AceType) { case ACE_TYPE.ACCESS_ALLOWED_CALLBACK_ACE_TYPE: case ACE_TYPE.ACCESS_DENIED_CALLBACK_ACE_TYPE: case ACE_TYPE.SYSTEM_AUDIT_CALLBACK_ACE_TYPE: //The AceSize contains the following parts: //_ACE_HEADER Header,(4 bytes) //uint Mask,(4 bytes) //_SID Sid;(variable length) //byte[] ApplicationData,(variable length) applicationDataLength = header.AceSize - SHORT_FIXED_ACE_LENGTH - TypeMarshal.ToBytes<_SID>(sid).GetLength(0); break; case ACE_TYPE.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE: case ACE_TYPE.ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE: case ACE_TYPE.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE: //The AceSize contains the following parts: //_ACE_HEADER Header,(4 bytes) //uint Mask,(4 bytes) //uint Flags,(4 bytes) //Guid ObjectType,(16 bytes) //Guid InheritedObjectType,(16 bytes) //_SID Sid;(variable length) //byte[] ApplicationData,(variable length) applicationDataLength = header.AceSize - LONG_FIXED_ACE_LENGTH - TypeMarshal.ToBytes<_SID>(sid).GetLength(0); break; default: throw new ArgumentOutOfRangeException("header", "The ace type is invalid."); } return applicationDataLength > 0 ? applicationDataLength : 0; }
/// <summary> /// Create an ACCESS_DENIED_ACE by using specific SID, access mask and optional ace flags. /// </summary> /// <param name="sid">The SID of the trustee.</param> /// <param name="mask">An ACCESS_MASK that specifies the user rights denied by this ACE.</param> /// <param name="flags">ACE type-specific control flags in the ACE header.</param> /// <returns>The constructed ACCESS_DENIED_ACE structure</returns> public static _ACCESS_DENIED_ACE CreateAccessDeniedAce(_SID sid, uint mask, ACE_FLAGS flags = ACE_FLAGS.None) { _ACE_HEADER aceHeader = new _ACE_HEADER { AceFlags = flags, AceType = ACE_TYPE.ACCESS_DENIED_ACE_TYPE, // Header (4 bytes) + Mask (4 bytes) + SID length; // For details, please refer to MS-DTYP. AceSize = (ushort)(4 + 4 + DtypUtility.SidLength(sid)), }; _ACCESS_DENIED_ACE ace = new _ACCESS_DENIED_ACE { Header = aceHeader, Mask = mask, Sid = sid, }; return ace; }
/// <summary> /// ComputeACL. MS-DTYP section 2.5.4.4 /// </summary> /// <param name="computeType">Enumeration of COMPUTE_DACL and COMPUTE_SACL.</param> /// <param name="parentAcl">ACL from the parent security descriptor.</param> /// <param name="parentControl">Control flags from the parent security descriptor.</param> /// <param name="creatorAcl">ACL supplied in the security descriptor by the creator.</param> /// <param name="creatorControl">Control flags supplied in the security descriptor by the creator.</param> /// <param name="isContainerObject">TRUE if the object is a container; otherwise, FALSE.</param> /// <param name="objectTypes">Array of GUIDs for the object type being created.</param> /// <param name="autoInheritFlags"> /// A set of bit flags that control how access control entries (ACEs) are /// inherited from ParentDescriptor. /// </param> /// <param name="genericMapping"> /// Mapping of generic permissions to resource manager-specific permissions supplied by the caller. /// </param> /// <param name="owner">Owner to use in substituting the CreatorOwner SID.</param> /// <param name="group">Group to use in substituting the CreatorGroup SID.</param> /// <param name="token">Token for default values.</param> /// <param name="computedControl">ComputedControl</param> /// <returns>Computed ACL</returns> public static _ACL? ComputeACL( AclComputeType computeType, _ACL? parentAcl, SECURITY_DESCRIPTOR_Control parentControl, _ACL? creatorAcl, SECURITY_DESCRIPTOR_Control creatorControl, bool isContainerObject, Guid[] objectTypes, SecurityDescriptorAutoInheritFlags autoInheritFlags, GenericMapping genericMapping, _SID owner, _SID group, Token token, out SECURITY_DESCRIPTOR_Control computedControl) { #region Pseudocode v20110329 //// The details of the algorithm to merge the parent ACL and the supplied ACL. //// The Control flags computed are slightly different based on whether it is the //// ACL in the DACL or the SACL field of the descriptor. //// The caller specifies whether it is a DACL or a SACL using the parameter, //// ComputeType. //Set ComputedACL to NULL //Set ComputedControl to NULL //CALL ContainsInheritableACEs WITH ParentACL RETURNING ParentHasInheritableACEs //IF ParentHasInheritableACEs = TRUE THEN // // The Parent ACL has inheritable ACEs. The Parent ACL should be used if no Creator // // ACL is supplied, or if the Creator ACL was supplied AND it is a default ACL based // // on object type information // IF(CreatorACL is not present) OR // ((CreatorACL is present) AND // (AutoInheritFlags contains DEFAULT_DESCRIPTOR_FOR_OBJECT)) // THEN // // Use only the inherited ACEs from the parent. First compute the ACL from the // // parent ACL, then clean it up by resolving the generic mappings etc. // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING NextACL // CALL PostProcessACL WITH // ACL set to NextACL, // CopyFilter set to CopyInheritedAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING FinalACL // Set ComputedACL to FinalACL // RETURN // ENDIF //IF ((CreatorACL is present) AND // (AutoInheritFlags does not contain DEFAULT_DESCRIPTOR_FOR_OBJECT)) //THEN // // Since a creator ACL is present, and we’re not defaulting the // // descriptor, determine which ACEs are inherited and compute the new ACL // CALL PreProcessACLFromCreator WITH // ACL set to CreatorACL // RETURNING PreACL // CALL ComputeInheritedACLFromCreator WITH // ACL set to PreACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING TmpACL // // Special handling for DACL types of ACLs // IF (ComputeType = DACL_COMPUTE) THEN // // DACL-specific operations // IF (CreatorControl does not have DACL_PROTECTED flag set) AND // (AutoInheritFlags contains DACL_AUTO_INHERIT) // THEN // // We’re not working from a protected DACL, and we’re supposed to // // allow automatic inheritance. Compute the inherited ACEs from // // Parent ACL this time, and append that to the ACL that we’re building // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING InheritedParentACL // Append InheritedParentACL.ACEs to TmpACL.ACE // Set DACL_AUTO_INHERITED flag in ComputedControl // ENDIF // ENDIF // DACL-Specific behavior // IF (ComputeType = SACL_COMPUTE) THEN // // Similar to the above, perform SACL-specific operations // IF (CreatorControl does not have SACL_PROTECTED flag set) AND // (AutoInheritFlags contains SACL_AUTO_INHERIT flag) // THEN // // We’re not working from a protected SACL, and we’re supposed to // // allow automatic inheritance. Compute the inherited ACEs from // // Parent ACL this time, and append that to the ACL that we’re building // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING InheritedParentACL // Append InheritedParentACL.ACEs to TmpACL.ACE // Set SACL_AUTO_INHERITED flag in ComputedControl // ENDIF // ENDIF // SACL-Specific behavior // CALL PostProcessACL WITH // ACL set to TmpACL, // CopyFilter set to CopyInheritedAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING ProcessedACL // Set ComputedACL to ProcessedACL // RETURN //ENDIF // CreatorACL is present //ELSE // ParentACL does not contain inheritable ACEs // IF CreatorACL = NULL THEN // // No ACL supplied for the object // IF (ComputeType = DACL_COMPUTE) THEN // Set TmpACL to Token.DefaultDACL // ELSE // // No default for SACL; left as NULL // ENDIF // ELSE // // Explicit ACL was supplied for the object - either default or not. // // In either case, use it for the object, since there are no inherited ACEs. // CALL PreProcessACLFromCreator WITH CreatorACL // RETURNING TmpACL // ENDIF // CALL PostProcessACL WITH // ACL set to TmpACL, // CopyFilter set to CopyAllAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING ProcessedACL // Set ComputedACL to ProcessedACL //ENDIF //// END ComputeACL #endregion _ACL? computedACL = null; computedControl = SECURITY_DESCRIPTOR_Control.None; _ACL tmpAcl = new _ACL(); _ACL preAcl = new _ACL(); if (parentAcl != null && ContainsInheritableACEs(parentAcl.Value)) { // ParentACL contains inheritable ACEs if ((creatorAcl == null) || ((creatorAcl != null) && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.DEFAULT_DESCRIPTOR_FOR_OBJECT) == SecurityDescriptorAutoInheritFlags.DEFAULT_DESCRIPTOR_FOR_OBJECT)) { // Use only the inherited ACEs from the parent _ACL nextAcl = ComputeInheritedACLfromParent(parentAcl.Value, isContainerObject, objectTypes); _ACL finalAcl = PostProcessACL(nextAcl, CopyFilter.CopyInheritedAces, owner, group, genericMapping); computedACL = finalAcl; } else { preAcl = PreProcessACLfromCreator(creatorAcl.Value); tmpAcl = ComputeInheritedACLfromCreator(preAcl, isContainerObject, objectTypes); _ACL? inheritedAcl = null; if ((computeType == AclComputeType.COMPUTE_DACL) && (creatorControl & SECURITY_DESCRIPTOR_Control.DACLProtected) == SECURITY_DESCRIPTOR_Control.None && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.DACL_AUTO_INHERIT) == SecurityDescriptorAutoInheritFlags.DACL_AUTO_INHERIT) { // Compute the inherited ACEs from the parent inheritedAcl = ComputeInheritedACLfromParent( parentAcl.Value, isContainerObject, objectTypes); computedControl |= SECURITY_DESCRIPTOR_Control.DACLAutoInherited; } else if ((computeType == AclComputeType.COMPUTE_SACL) && (creatorControl & SECURITY_DESCRIPTOR_Control.SACLProtected) == SECURITY_DESCRIPTOR_Control.None && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.SACL_AUTO_INHERIT) == SecurityDescriptorAutoInheritFlags.SACL_AUTO_INHERIT) { // Compute the inherited ACEs from the parent inheritedAcl = ComputeInheritedACLfromParent( parentAcl.Value, isContainerObject, objectTypes); computedControl |= SECURITY_DESCRIPTOR_Control.SACLAutoInherited; } if (inheritedAcl != null) { List<object> tmpAces = new List<object>(); tmpAces.AddRange(tmpAcl.Aces); tmpAces.AddRange(inheritedAcl.Value.Aces); tmpAcl.Aces = tmpAces.ToArray(); tmpAcl.AceCount = (ushort)tmpAcl.Aces.Length; tmpAcl.AclSize = DtypUtility.CalculateAclSize(tmpAcl); } computedACL = PostProcessACL(tmpAcl, CopyFilter.CopyInheritedAces, owner, group, genericMapping); } } else { // ParentACL does not contain inheritable ACEs if (creatorAcl == null) { if (computeType == AclComputeType.COMPUTE_DACL) { tmpAcl = token.DefaultDACL; } else { //No default for SACL;left as NULL return null; } } else { // Explicit ACL was supplied for the object - either default or not. // In either case, use it for the object, since there are no inherited ACEs. tmpAcl = PreProcessACLfromCreator(creatorAcl.Value); } computedACL = PostProcessACL(tmpAcl, CopyFilter.CopyAllAces, owner, group, genericMapping); } if (computedACL != null) { computedControl |= ((computeType == AclComputeType.COMPUTE_DACL) ? SECURITY_DESCRIPTOR_Control.DACLPresent : SECURITY_DESCRIPTOR_Control.SACLPresent); } return computedACL; }
private SystemResourceAttributeAce(string attributeName) { if (attributeName.Length < 2) { throw new ArgumentException("attributeName must be at least 4 bytes in length."); } AttributeName = attributeName; Sid = DtypUtility.GetWellKnownSid(WellKnownSid.EVERYONE, null); AttributeData.Flags = CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1.FlagEnum.CLAIM_SECURITY_ATTRIBUTE_MANDATORY | CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1.FlagEnum.FCI_CLAIM_SECURITY_ATTRIBUTE_MANUAL; Mask = 0; Header = new _ACE_HEADER { AceFlags = ACE_FLAGS.CONTAINER_INHERIT_ACE | ACE_FLAGS.OBJECT_INHERIT_ACE, AceType = ACE_TYPE.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE, // Size would be filled later }; }
/// <summary> /// This algorithm returns the size, in bytes, of SID. This is equal to the number of bytes occupied by the Revision, /// SubAuthorityCount, and IdentifierAuthorityCount fields of a SID. Added to this is the size of a SubAuthority field /// of a SID times SID.SubAuthorityCount. /// </summary> /// <param name="sid">The SID to be calculated.</param> /// <returns>The length of the specified SID.</returns> public static int SidLength(_SID sid) { return (8 + (4 * sid.SubAuthorityCount)); }
/// <summary> /// Convert SID to SDDL string. /// </summary> /// <param name="sid">SID to convert.</param> /// <returns>SDDL string.</returns> /// <exception cref="ArgumentException"> /// Thrown when the Revision field of sid is invalid. /// Thrown when the length of identifierAuthority is not 6 bytes. /// Thrown when the IdentifierAuthority field of sid is null. /// </exception> public static string ToSddlString(_SID sid) { // //SID String Format Syntax // //SID= "S-1-" IdentifierAuthority 1*SubAuthority //IdentifierAuthority= IdentifierAuthorityDec / IdentifierAuthorityHex //; If the identifier authority is < 2^32, the //; identifier authority is represented as a decimal //; number //; If the identifier authority is >= 2^32, //; the identifier authority is represented in //; hexadecimal //IdentifierAuthorityDec = 1*10DIGIT //; IdentifierAuthorityDec, top level authority of a //; security identifier is represented as a decimal number //IdentifierAuthorityHex = "0x" 12HEXDIG //; IdentifierAuthorityHex, the top-level authority of a //; security identifier is represented as a hexadecimal number //SubAuthority= "-" 1*10DIGIT //; Sub-Authority is always represented as a decimal number //; No leading "0" characters are allowed when IdentifierAuthority //; or SubAuthority is represented as a decimal number //; All hexadecimal digits must be output in string format, //; pre-pended by "0x" if (sid.Revision != 1) { throw new ArgumentException("The Revision field of sid is invalid.", "sid"); } //S-1: Indicates a revision or version 1 SID. string sddlString = "S-1-"; if (sid.IdentifierAuthority == null) { throw new ArgumentException("The IdentifierAuthority field of sid is null.", "sid"); } if (sid.IdentifierAuthority.Length != SID_AUTHORITY_LENGTH) { throw new ArgumentException("Incorrect size of identifierAuthority field.", "sid"); } ulong identifierAuthority = 0; for (int i = 0; i < sid.IdentifierAuthority.Length; i++) { identifierAuthority = (identifierAuthority << 8) + sid.IdentifierAuthority[i]; } if ((sid.IdentifierAuthority[0] == 0) && (sid.IdentifierAuthority[1] == 0)) { sddlString += string.Format(CultureInfo.InvariantCulture, "{0:D}", identifierAuthority); } else { sddlString += string.Format(CultureInfo.InvariantCulture, "0x{0:X12}", identifierAuthority); } for (int i = 0; i < sid.SubAuthorityCount; i++) { sddlString += string.Format(CultureInfo.InvariantCulture, "-{0:D}", sid.SubAuthority[i]); } return sddlString; }
/// <summary> /// Create a _SECURITY_DESCRIPTOR structure in self-relative format. /// </summary> /// <param name="control">An unsigned 16-bit field that specifies control access bit flags.</param> /// <param name="ownerSid">The SID of the owner of the object</param> /// <param name="groupSid">The SID of the group of the object.</param> /// <param name="sacl">The SACL of the object.</param> /// <param name="dacl">The DACL of the object.</param> /// <returns> Output security descriptor for the object.</returns> public static _SECURITY_DESCRIPTOR CreateSecurityDescriptor( SECURITY_DESCRIPTOR_Control control, _SID? ownerSid, _SID? groupSid, _ACL? sacl, _ACL? dacl) { RawAcl sRawAcl = null; RawAcl dRawAcl = null; SecurityIdentifier rawOwnerSid = null; SecurityIdentifier rawGroupSid = null; if (ownerSid != null) { rawOwnerSid = new SecurityIdentifier(TypeMarshal.ToBytes(ownerSid.Value), 0); } if (groupSid != null) { rawGroupSid = new SecurityIdentifier(TypeMarshal.ToBytes(groupSid.Value), 0); } if (sacl != null) { sRawAcl = new RawAcl(DtypUtility.EncodeAcl(sacl.Value), 0); } if (dacl != null) { dRawAcl = new RawAcl(DtypUtility.EncodeAcl(dacl.Value), 0); } RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor( (ControlFlags)control, rawOwnerSid, rawGroupSid, sRawAcl, dRawAcl); byte[] rawSecurityDescriptorBytes = new byte[rawSecurityDescriptor.BinaryLength]; rawSecurityDescriptor.GetBinaryForm(rawSecurityDescriptorBytes, 0); return DtypUtility.DecodeSecurityDescriptor(rawSecurityDescriptorBytes); }
/// <summary> /// Access Check Algorithm. MS-DTYP section 2.5.4.1 /// </summary> /// <param name="securityDescriptor"> /// SECURITY_DESCRIPTOR structure that is assigned to the object. /// </param> /// <param name="token"> /// Token is an authorization context containing all SIDs /// that represent the security principal /// </param> /// <param name="accessRequestMask"> /// Set of permissions requested on the object. /// </param> /// <param name="objectTree"> /// A tree representation of the hierarchy of objects for which /// to check access. Each node represents an object with two values. /// A GUID that represents the object itself and a value called /// Remaining, which indicates the user rights request for that /// node that have not yet been satisfied. It can be null. /// </param> /// <param name="principalSelfSubstitudeSid"> /// A SID that logically replaces the SID in any ACE that contains /// the well-known PRINCIPAL_SELF SID. It can be null. /// </param> /// <returns> /// Returns TRUE if access is allowed. Otherwise, it returns FALSE if access is denied. /// </returns> /// <exception cref="ArgumentException"> /// Thrown when the Dacl field of securityDescriptor doesn't exist, but the DACLPresent flag is set. /// Thrown when the Revision field of securityDescriptor is invalid /// </exception> public static bool AccessCheck( _SECURITY_DESCRIPTOR securityDescriptor, Token token, uint accessRequestMask, AccessCheckObjectTree objectTree, _SID? principalSelfSubstitudeSid) { //MS-ADTS Section 5.1.3.3.1 Null vs. Empty DACLs //The presence of a NULL DACL in the nTSecurityDescriptor attribute of an object grants full //access to the object to any principal that requests it; normal access checks are not performed //with respect to the object. if (securityDescriptor.Revision != 0x1) { throw new ArgumentException("The Revision field is invalid.", "securityDescriptor"); } if ((securityDescriptor.Control & SECURITY_DESCRIPTOR_Control.DACLPresent) == SECURITY_DESCRIPTOR_Control.None) { return true; } else if (securityDescriptor.Dacl == null) { throw new ArgumentException( "The Dacl field of securityDescriptor doesn't exist, but the DACLPresent flag is set.", "securityDescriptor"); } //Pscudocode // //Set DACL to SecurityDescriptor Dacl field //Set RemainingAccess to Access Request mask // //IF RemainingAccess contains ACCESS_SYSTEM_SECURITY access flag THEN //IF Token.Privileges contains SeSecurityPrivilege THEN //Remove ACCESS_SYSTEM_SECURITY access bit from RemainingAccess //END IF //END IF // //IF RemainingAccess contains WRITE_OWNER access bit THEN //IF Token.Privileges contains SeTakeOwnershipPrivilege THEN //Remove WRITE_OWNER access bit from RemainingAccess //END IF //END IF //-- the owner of an object is always granted READ_CONTROL and WRITE_DAC. //CALL SidInToken( Token, SecurityDescriptor.Owner, PrincipalSelfSubst) //IF SidInToken returns True THEN //Remove READ_CONTROL and WRITE_DAC from RemainingAccess //END IF _ACL? dacl = securityDescriptor.Dacl; uint remainingAccess = accessRequestMask; AccessCheckObjectTree localTree = new AccessCheckObjectTree(); Guid[] allNodes = new Guid[] { }; if ((remainingAccess & ACCESS_MASK_ACCESS_SYSTEM_SECURITY) != 0 && token.Privileges != null) { foreach (_LUID privilege in token.Privileges) { if (ObjectUtility.DeepCompare(privilege, GetPrivilegeLuid(PrivilegeName.SE_SECURITY_NAME))) { remainingAccess &= ~ACCESS_MASK_ACCESS_SYSTEM_SECURITY; } } } if ((remainingAccess & ACCESS_MASK_WRITE_OWNER) != 0 && token.Privileges != null) { foreach (_LUID privilege in token.Privileges) { if (ObjectUtility.DeepCompare(privilege, GetPrivilegeLuid(PrivilegeName.SE_TAKE_OWNERSHIP_NAME))) { remainingAccess &= ~ACCESS_MASK_WRITE_OWNER; } } } if (securityDescriptor.OwnerSid != null && SidInToken(token, securityDescriptor.OwnerSid.Value, principalSelfSubstitudeSid)) { remainingAccess &= ~(uint)(ACCESS_MASK_READ_CONTROL | ACCESS_MASK_WRITE_DAC); } //IF Object Tree is not NULL THEN //Set LocalTree to Object Tree //FOR each node in LocalTree DO //Set node.Remaining to RemainingAccess //END FOR //END IF if (objectTree != null && objectTree.Root != null) { localTree = (AccessCheckObjectTree)ObjectUtility.DeepClone(objectTree); List<Guid> tmpList = new List<Guid>(); tmpList.Add(localTree.Root.Value); tmpList.AddRange(localTree.GetDescendentNodes(localTree.Root.Value)); allNodes = tmpList.ToArray(); foreach (Guid node in allNodes) { localTree.SetTreeNodeData(node, remainingAccess); } } //FOR each ACE in DACL DO //IF ACE.flag does not contain INHERIT_ONLY_ACE THEN //CASE ACE.Type OF //CASE Allow Access: //CALL SidInToken( Token, ACE.Sid, and PrincipalSelfSubst ) //IF SidInToken returns True THEN // Remove ACE.AccessMask from RemainingAccess // FOR each node in LocalTree DO // Remove ACE.AccessMask from node.Remaining //END FOR //END IF //CASE Deny Access: //CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst ) //IF SidInToken returns True THEN // IF any bit of RemainingAccess is in ACE.AccessMask THEN // Return access_denied //END IF //END IF //CASE Object Allow Access: //CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst ) //IF SidInToken returns True THEN // IF ACE.Object is contained in LocalTree THEN // Locate node n in LocalTree such that // n.GUID is the same as ACE.Object // Remove ACE.AccessMask from n.Remaining // FOR each node ns such that ns is a descendent of n DO // Remove ACE.AccessMask from ns.Remaining // END FOR // FOR each node np such that np is an ancestor of n DO // Set np.Remaining = np.Remaining or np-1.Remaining //END FOR //END IF //END IF //CASE Object Deny Access: //CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst ) //IF SidInToken returns True THEN // Locate node n in LocalTree such that // n.GUID is the same as ACE.Object // IF n exists THEN // If any bit of n.Remaining is in ACE.AccessMask THEN // Return access_denied //END IF //END IF //END IF //END CASE //END IF //END FOR if (dacl.Value.Aces != null) { foreach (object ace in dacl.Value.Aces) { _ACE_HEADER header = (_ACE_HEADER)ObjectUtility.GetFieldValue(ace, "Header"); _SID sid = (_SID)ObjectUtility.GetFieldValue(ace, "Sid"); if ((header.AceFlags & ACE_FLAGS.INHERIT_ONLY_ACE) == ACE_FLAGS.INHERIT_ONLY_ACE || !SidInToken(token, sid, principalSelfSubstitudeSid)) { continue; } bool ret = DtypUtility.AceAccessCheck(ace, ref remainingAccess, ref localTree, allNodes); if (ret == false) { return false; } } } //IF RemainingAccess = 0 THEN //Return success //Else //Return access_denied //END IF bool status; if (objectTree != null && objectTree.Root != null) { status = (localTree.GetTreeNodeData(localTree.Root.Value) == 0); } else { status = (remainingAccess == 0); } return status; }
/// <summary> /// Create an instance of SID. /// </summary> /// <param name="identifierAuthority"> /// Six element arrays of 8-bit unsigned integers that specify /// the top-level authority of a RPC_SID. /// </param> /// <param name="subAuthorities"> /// A variable length array of unsigned 32-bit integers that /// uniquely identifies a principal relative to the IdentifierAuthority. /// </param> /// <returns>Created SID structure.</returns> /// <exception cref="ArgumentNullException"> /// Thrown when identifierAuthority or subAuthorities is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when length of identifierAuthority is not 6 bytes. /// </exception> public static _SID CreateSid( byte[] identifierAuthority, params uint[] subAuthorities) { if (identifierAuthority == null) { throw new ArgumentNullException("identifierAuthority"); } if (identifierAuthority.Length != SID_AUTHORITY_LENGTH) { throw new ArgumentException("Incorrect size of identifierAuthority.", "identifierAuthority"); } if (subAuthorities == null) { throw new ArgumentNullException("subAuthorities"); } const byte DEFAULT_REVISION = 1; _SID sid = new _SID(); sid.Revision = DEFAULT_REVISION; sid.SubAuthorityCount = (byte)subAuthorities.Length; sid.IdentifierAuthority = identifierAuthority; sid.SubAuthority = subAuthorities; return sid; }