// // System.Security.AccessControl.GenericAce implementation // // Authors: // Dick Porter // Atsushi Enomoto // Kenneth Bell // // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Globalization; using System.Security.Principal; using System.Text; namespace System.Security.AccessControl { public abstract class GenericAce { private AceFlags ace_flags; private AceType ace_type; internal GenericAce (AceType type, AceFlags flags) { if (type > AceType.MaxDefinedAceType) { throw new ArgumentOutOfRangeException ("type"); } this.ace_type = type; this.ace_flags = flags; } internal GenericAce(byte[] binaryForm, int offset) { if (binaryForm == null) throw new ArgumentNullException("binaryForm"); if (offset < 0 || offset > binaryForm.Length - 2) throw new ArgumentOutOfRangeException("offset", offset, "Offset out of range"); ace_type = (AceType)binaryForm[offset]; ace_flags = (AceFlags)binaryForm[offset + 1]; } public AceFlags AceFlags { get { return ace_flags; } set { ace_flags = value; } } public AceType AceType { get { return ace_type; } } public AuditFlags AuditFlags { get { AuditFlags ret = AuditFlags.None; if ((ace_flags & AceFlags.SuccessfulAccess) != 0) ret |= AuditFlags.Success; if ((ace_flags & AceFlags.FailedAccess) != 0) ret |= AuditFlags.Failure; return ret; } } public abstract int BinaryLength { get; } public InheritanceFlags InheritanceFlags { get { InheritanceFlags ret = InheritanceFlags.None; if ((ace_flags & AceFlags.ObjectInherit) != 0) ret |= InheritanceFlags.ObjectInherit; if ((ace_flags & AceFlags.ContainerInherit) != 0) ret |= InheritanceFlags.ContainerInherit; return ret; } } public bool IsInherited { get { return (ace_flags & AceFlags.Inherited) != AceFlags.None; } } public PropagationFlags PropagationFlags { get { PropagationFlags ret = PropagationFlags.None; if ((ace_flags & AceFlags.InheritOnly) != 0) ret |= PropagationFlags.InheritOnly; if ((ace_flags & AceFlags.NoPropagateInherit) != 0) ret |= PropagationFlags.NoPropagateInherit; return ret; } } public GenericAce Copy () { byte[] buffer = new byte[BinaryLength]; GetBinaryForm(buffer, 0); return GenericAce.CreateFromBinaryForm(buffer, 0); } public static GenericAce CreateFromBinaryForm (byte[] binaryForm, int offset) { if (binaryForm == null) throw new ArgumentNullException("binaryForm"); if (offset < 0 || offset > binaryForm.Length - 1) throw new ArgumentOutOfRangeException("offset", offset, "Offset out of range"); AceType type = (AceType)binaryForm[offset]; if (IsObjectType(type)) return new ObjectAce(binaryForm, offset); else return new CommonAce(binaryForm, offset); } public override sealed bool Equals (object o) { return this == (o as GenericAce); } public abstract void GetBinaryForm (byte[] binaryForm, int offset); public override sealed int GetHashCode () { byte[] buffer = new byte[BinaryLength]; GetBinaryForm(buffer, 0); int code = 0; for(int i = 0; i < buffer.Length; ++i) { code = (code << 3) | ((code >> 29) & 0x7); code ^= ((int)buffer[i]) & 0xff; } return code; } public static bool operator== (GenericAce left, GenericAce right) { if(((object)left) == null) return((object)right) == null; if(((object)right) == null) return false; int leftLen = left.BinaryLength; int rightLen = right.BinaryLength; if( leftLen != rightLen) return false; byte[] leftBuffer = new byte[leftLen]; byte[] rightBuffer = new byte[rightLen]; left.GetBinaryForm(leftBuffer, 0); right.GetBinaryForm(rightBuffer, 0); for(int i = 0; i < leftLen; ++i) { if(leftBuffer[i] != rightBuffer[i]) return false; } return true; } public static bool operator!= (GenericAce left, GenericAce right) { if(((object)left) == null) return((object)right) != null; if(((object)right) == null) return true; int leftLen = left.BinaryLength; int rightLen = right.BinaryLength; if( leftLen != rightLen) return true; byte[] leftBuffer = new byte[leftLen]; byte[] rightBuffer = new byte[rightLen]; left.GetBinaryForm(leftBuffer, 0); right.GetBinaryForm(rightBuffer, 0); for(int i = 0; i < leftLen; ++i) { if(leftBuffer[i] != rightBuffer[i]) return true; } return false; } internal abstract string GetSddlForm(); static internal GenericAce CreateFromSddlForm (string sddlForm, ref int pos) { if (sddlForm[pos] != '(') throw new ArgumentException ("Invalid SDDL string.", "sddlForm"); int endPos = sddlForm.IndexOf (')', pos); if (endPos < 0) throw new ArgumentException ("Invalid SDDL string.", "sddlForm"); int count = endPos - (pos + 1); string elementsStr = sddlForm.Substring (pos + 1, count); elementsStr = elementsStr.ToUpperInvariant (); string[] elements = elementsStr.Split (';'); if (elements.Length != 6) throw new ArgumentException ("Invalid SDDL string.", "sddlForm"); ObjectAceFlags objFlags = ObjectAceFlags.None; AceType type = ParseSddlAceType (elements[0]); AceFlags flags = ParseSddlAceFlags (elements[1]); int accessMask = ParseSddlAccessRights (elements[2]); Guid objectType = Guid.Empty; if (!string.IsNullOrEmpty (elements[3])) { objectType = new Guid(elements[3]); objFlags |= ObjectAceFlags.ObjectAceTypePresent; } Guid inhObjectType = Guid.Empty; if (!string.IsNullOrEmpty (elements[4])) { inhObjectType = new Guid(elements[4]); objFlags |= ObjectAceFlags.InheritedObjectAceTypePresent; } SecurityIdentifier sid = new SecurityIdentifier (elements[5]); if (type == AceType.AccessAllowedCallback || type == AceType.AccessDeniedCallback) throw new NotImplementedException ("Conditional ACEs not supported"); pos = endPos + 1; if (IsObjectType(type)) return new ObjectAce(type, flags, accessMask, sid, objFlags, objectType, inhObjectType, null); else { if (objFlags != ObjectAceFlags.None) throw new ArgumentException( "Invalid SDDL string.", "sddlForm"); return new CommonAce (type, flags, accessMask, sid, null); } } private static bool IsObjectType(AceType type) { return type == AceType.AccessAllowedCallbackObject || type == AceType.AccessAllowedObject || type == AceType.AccessDeniedCallbackObject || type == AceType.AccessDeniedObject || type == AceType.SystemAlarmCallbackObject || type == AceType.SystemAlarmObject || type == AceType.SystemAuditCallbackObject || type == AceType.SystemAuditObject; } internal static string GetSddlAceType (AceType type) { switch (type) { case AceType.AccessAllowed: return "A"; case AceType.AccessDenied: return "D"; case AceType.AccessAllowedObject: return "OA"; case AceType.AccessDeniedObject: return "OD"; case AceType.SystemAudit: return "AU"; case AceType.SystemAlarm: return "AL"; case AceType.SystemAuditObject: return "OU"; case AceType.SystemAlarmObject: return "OL"; case AceType.AccessAllowedCallback: return "XA"; case AceType.AccessDeniedCallback: return "XD"; default: throw new ArgumentException ("Unable to convert to SDDL ACE type: " + type, "type"); } } private static AceType ParseSddlAceType (string type) { switch (type) { case "A": return AceType.AccessAllowed; case "D": return AceType.AccessDenied; case "OA": return AceType.AccessAllowedObject; case "OD": return AceType.AccessDeniedObject; case "AU": return AceType.SystemAudit; case "AL": return AceType.SystemAlarm; case "OU": return AceType.SystemAuditObject; case "OL": return AceType.SystemAlarmObject; case "XA": return AceType.AccessAllowedCallback; case "XD": return AceType.AccessDeniedCallback; default: throw new ArgumentException ("Unable to convert SDDL to ACE type: " + type, "type"); } } internal static string GetSddlAceFlags (AceFlags flags) { StringBuilder result = new StringBuilder (); if ((flags & AceFlags.ObjectInherit) != 0) result.Append ("OI"); if ((flags & AceFlags.ContainerInherit) != 0) result.Append ("CI"); if ((flags & AceFlags.NoPropagateInherit) != 0) result.Append ("NP"); if ((flags & AceFlags.InheritOnly) != 0) result.Append ("IO"); if ((flags & AceFlags.Inherited) != 0) result.Append ("ID"); if ((flags & AceFlags.SuccessfulAccess) != 0) result.Append ("SA"); if ((flags & AceFlags.FailedAccess) != 0) result.Append ("FA"); return result.ToString (); } private static AceFlags ParseSddlAceFlags (string flags) { AceFlags ret = AceFlags.None; int pos = 0; while (pos < flags.Length - 1) { string flag = flags.Substring (pos, 2); switch (flag) { case "CI": ret |= AceFlags.ContainerInherit; break; case "OI": ret |= AceFlags.ObjectInherit; break; case "NP": ret |= AceFlags.NoPropagateInherit; break; case "IO": ret |= AceFlags.InheritOnly; break; case "ID": ret |= AceFlags.Inherited; break; case "SA": ret |= AceFlags.SuccessfulAccess; break; case "FA": ret |= AceFlags.FailedAccess; break; default: throw new ArgumentException ("Invalid SDDL string.", "flags"); } pos += 2; } if (pos != flags.Length) throw new ArgumentException ("Invalid SDDL string.", "flags"); return ret; } private static int ParseSddlAccessRights (string accessMask) { if (accessMask.StartsWith ("0X")) { return int.Parse (accessMask.Substring (2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); } else if (Char.IsDigit (accessMask, 0)) { return int.Parse (accessMask, NumberStyles.Integer, CultureInfo.InvariantCulture); } else { return ParseSddlAliasRights (accessMask); } } private static int ParseSddlAliasRights(string accessMask) { int ret = 0; int pos = 0; while (pos < accessMask.Length - 1) { string flag = accessMask.Substring (pos, 2); SddlAccessRight right = SddlAccessRight.LookupByName(flag); if (right == null) throw new ArgumentException ("Invalid SDDL string.", "accessMask"); ret |= right.Value; pos += 2; } if (pos != accessMask.Length) throw new ArgumentException ("Invalid SDDL string.", "accessMask"); return ret; } internal static ushort ReadUShort (byte[] buffer, int offset) { return (ushort)((((int)buffer[offset + 0]) << 0) | (((int)buffer[offset + 1]) << 8)); } internal static int ReadInt (byte[] buffer, int offset) { return (((int)buffer[offset + 0]) << 0) | (((int)buffer[offset + 1]) << 8) | (((int)buffer[offset + 2]) << 16) | (((int)buffer[offset + 3]) << 24); } internal static void WriteInt (int val, byte[] buffer, int offset) { buffer[offset] = (byte)val; buffer[offset + 1] = (byte)(val >> 8); buffer[offset + 2] = (byte)(val >> 16); buffer[offset + 3] = (byte)(val >> 24); } internal static void WriteUShort (ushort val, byte[] buffer, int offset) { buffer[offset] = (byte)val; buffer[offset + 1] = (byte)(val >> 8); } } }