2 // System.Security.Policy.SecurityIdentifier class
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // James Bellinger <jfb@zer7.com>
9 // Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2012 James Bellinger
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Globalization;
34 using System.Runtime.InteropServices;
37 namespace System.Security.Principal
40 public sealed class SecurityIdentifier : IdentityReference, IComparable<SecurityIdentifier>
42 private byte[] buffer;
44 public static readonly int MaxBinaryLength = 68;
45 public static readonly int MinBinaryLength = 8;
47 public SecurityIdentifier (string sddlForm)
50 throw new ArgumentNullException ("sddlForm");
52 buffer = ParseSddlForm (sddlForm);
55 unsafe public SecurityIdentifier (byte[] binaryForm, int offset)
57 if (binaryForm == null)
58 throw new ArgumentNullException ("binaryForm");
59 if ((offset < 0) || (offset > binaryForm.Length - 2))
60 throw new ArgumentException ("offset");
62 fixed (byte* binaryFormPtr = binaryForm)
63 CreateFromBinaryForm ((IntPtr)(binaryFormPtr + offset), binaryForm.Length - offset);
66 public SecurityIdentifier (IntPtr binaryForm)
68 CreateFromBinaryForm (binaryForm, int.MaxValue);
71 void CreateFromBinaryForm (IntPtr binaryForm, int length)
73 int revision = Marshal.ReadByte (binaryForm, 0);
74 int numSubAuthorities = Marshal.ReadByte (binaryForm, 1);
75 if (revision != 1 || numSubAuthorities > 15)
76 throw new ArgumentException ("Value was invalid.");
77 if (length < (8 + (numSubAuthorities * 4)))
78 throw new ArgumentException ("offset");
80 buffer = new byte[8 + (numSubAuthorities * 4)];
81 Marshal.Copy (binaryForm, buffer, 0, buffer.Length);
84 public SecurityIdentifier (WellKnownSidType sidType,
85 SecurityIdentifier domainSid)
87 WellKnownAccount acct = WellKnownAccount.LookupByType (sidType);
89 throw new ArgumentException ("Unable to convert SID type: " + sidType);
91 if (acct.IsAbsolute) {
92 buffer = ParseSddlForm (acct.Sid);
94 if (domainSid == null)
95 throw new ArgumentNullException ("domainSid");
97 buffer = ParseSddlForm (domainSid.Value + "-" + acct.Rid);
101 public SecurityIdentifier AccountDomainSid {
103 string strForm = this.Value;
105 // Check prefix, and ensure at least 4 sub authorities
106 if (!strForm.StartsWith ("S-1-5-21") || buffer[1] < 4)
109 // Domain is first four sub-authorities
110 byte[] temp = new byte[8 + (4 * 4)];
111 Array.Copy (buffer, 0, temp, 0, temp.Length);
113 return new SecurityIdentifier (temp, 0);
117 public int BinaryLength {
118 get { return buffer.Length; }
121 public override string Value {
123 StringBuilder s = new StringBuilder ();
125 ulong authority = GetSidAuthority ();
126 s.AppendFormat (CultureInfo.InvariantCulture, "S-1-{0}", authority);
128 for (byte i = 0; i < GetSidSubAuthorityCount (); ++i)
130 CultureInfo.InvariantCulture,
131 "-{0}", GetSidSubAuthority (i));
133 return s.ToString ();
137 ulong GetSidAuthority ()
139 return (((ulong)buffer [2]) << 40) | (((ulong)buffer [3]) << 32)
140 | (((ulong)buffer [4]) << 24) | (((ulong)buffer [5]) << 16)
141 | (((ulong)buffer [6]) << 8) | (((ulong)buffer [7]) << 0);
144 byte GetSidSubAuthorityCount ()
149 uint GetSidSubAuthority (byte index)
151 // Note sub authorities little-endian, authority (above) is big-endian!
152 int offset = 8 + (index * 4);
154 return (((uint)buffer [offset + 0]) << 0)
155 | (((uint)buffer [offset + 1]) << 8)
156 | (((uint)buffer [offset + 2]) << 16)
157 | (((uint)buffer [offset + 3]) << 24);
160 // The CompareTo ordering was determined by unit test applied to MS.NET implementation,
161 // necessary because the CompareTo has no details in its documentation.
162 // (See MonoTests.System.Security.AccessControl.DiscretionaryAclTest.)
163 // The comparison was determined to be: authority, then subauthority count, then subauthority.
164 public int CompareTo (SecurityIdentifier sid)
167 throw new ArgumentNullException ("sid");
170 if (0 != (result = GetSidAuthority ().CompareTo (sid.GetSidAuthority ()))) return result;
171 if (0 != (result = GetSidSubAuthorityCount ().CompareTo (sid.GetSidSubAuthorityCount ()))) return result;
172 for (byte i = 0; i < GetSidSubAuthorityCount (); ++i)
173 if (0 != (result = GetSidSubAuthority (i).CompareTo (sid.GetSidSubAuthority (i)))) return result;
177 public override bool Equals (object o)
179 return Equals (o as SecurityIdentifier);
182 public bool Equals (SecurityIdentifier sid)
186 return (sid.Value == Value);
189 public void GetBinaryForm (byte[] binaryForm, int offset)
191 if (binaryForm == null)
192 throw new ArgumentNullException ("binaryForm");
193 if ((offset < 0) || (offset > binaryForm.Length - buffer.Length))
194 throw new ArgumentException ("offset");
196 Array.Copy (buffer, 0, binaryForm, offset, buffer.Length);
199 public override int GetHashCode ()
201 return Value.GetHashCode ();
204 public bool IsAccountSid ()
206 return AccountDomainSid != null;
209 public bool IsEqualDomainSid (SecurityIdentifier sid)
211 SecurityIdentifier domSid = AccountDomainSid;
215 return domSid.Equals (sid.AccountDomainSid);
218 public override bool IsValidTargetType (Type targetType)
220 if (targetType == typeof(SecurityIdentifier))
222 if (targetType == typeof(NTAccount))
227 public bool IsWellKnown (WellKnownSidType type)
229 WellKnownAccount acct = WellKnownAccount.LookupByType (type);
236 return sid == acct.Sid;
238 return sid.StartsWith ("S-1-5-21", StringComparison.OrdinalIgnoreCase)
239 && sid.EndsWith ("-" + acct.Rid, StringComparison.OrdinalIgnoreCase);
242 public override string ToString ()
247 public override IdentityReference Translate (Type targetType)
249 if (targetType == typeof(SecurityIdentifier))
252 if (targetType == typeof(NTAccount)) {
253 WellKnownAccount acct = WellKnownAccount.LookupBySid (this.Value);
254 if (acct == null || acct.Name == null)
255 throw new IdentityNotMappedException ("Unable to map SID: " + this.Value);
257 return new NTAccount (acct.Name);
260 throw new ArgumentException ("Unknown type.", "targetType");
263 public static bool operator == (SecurityIdentifier left, SecurityIdentifier right)
265 if (((object)left) == null)
266 return (((object)right) == null);
267 if (((object)right) == null)
269 return (left.Value == right.Value);
272 public static bool operator != (SecurityIdentifier left, SecurityIdentifier right)
274 if (((object)left) == null)
275 return (((object)right) != null);
276 if (((object)right) == null)
278 return (left.Value != right.Value);
281 internal string GetSddlForm()
283 string sidString = Value;
285 WellKnownAccount acct = WellKnownAccount.LookupBySid(sidString);
286 if(acct == null || acct.SddlForm == null)
289 return acct.SddlForm;
292 internal static SecurityIdentifier ParseSddlForm(string sddlForm, ref int pos)
294 if (sddlForm.Length - pos < 2)
295 throw new ArgumentException("Invalid SDDL string.", "sddlForm");
300 string prefix = sddlForm.Substring(pos, 2).ToUpperInvariant();
303 // Looks like a SID, try to parse it.
306 char ch = Char.ToUpperInvariant(sddlForm[endPos]);
307 while (ch == 'S' || ch == '-' || ch == 'X'
308 || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
310 ch = Char.ToUpperInvariant(sddlForm[endPos]);
313 sid = sddlForm.Substring(pos, endPos - pos);
320 SecurityIdentifier ret = new SecurityIdentifier(sid);
325 private static byte[] ParseSddlForm (string sddlForm)
327 string sid = sddlForm;
329 // If only 2 characters long, can't be a full SID string - so assume
330 // it's an attempted alias. Do that conversion first.
331 if(sddlForm.Length == 2) {
332 WellKnownAccount acct = WellKnownAccount.LookupBySddlForm(sddlForm);
334 throw new ArgumentException(
335 "Invalid SDDL string - unrecognized account: " + sddlForm,
337 if (!acct.IsAbsolute)
338 throw new NotImplementedException(
339 "Mono unable to convert account to SID: "
340 + (acct.Name != null ? acct.Name : sddlForm));
345 string[] elements = sid.ToUpperInvariant ().Split ('-');
346 int numSubAuthorities = elements.Length - 3;
348 if (elements.Length < 3 || elements[0] != "S" || numSubAuthorities > 15)
349 throw new ArgumentException ("Value was invalid.");
351 if (elements[1] != "1")
352 throw new ArgumentException ("Only SIDs with revision 1 are supported");
354 byte[] buffer = new byte[8 + (numSubAuthorities * 4)];
356 buffer[1] = (byte)numSubAuthorities;
359 if (!TryParseAuthority (elements[2], out authority))
360 throw new ArgumentException ("Value was invalid.");
361 buffer[2] = (byte)((authority >> 40) & 0xFF);
362 buffer[3] = (byte)((authority >> 32) & 0xFF);
363 buffer[4] = (byte)((authority >> 24) & 0xFF);
364 buffer[5] = (byte)((authority >> 16) & 0xFF);
365 buffer[6] = (byte)((authority >> 8) & 0xFF);
366 buffer[7] = (byte)((authority >> 0) & 0xFF);
368 for (int i = 0; i < numSubAuthorities; ++i) {
371 if (!TryParseSubAuthority (elements[i + 3],
373 throw new ArgumentException ("Value was invalid.");
375 // Note sub authorities little-endian!
376 int offset = 8 + (i * 4);
377 buffer[offset + 0] = (byte)(subAuthority >> 0);
378 buffer[offset + 1] = (byte)(subAuthority >> 8);
379 buffer[offset + 2] = (byte)(subAuthority >> 16);
380 buffer[offset + 3] = (byte)(subAuthority >> 24);
386 private static bool TryParseAuthority (string s, out ulong result)
388 if (s.StartsWith ("0X")) {
389 return ulong.TryParse (s.Substring (2),
390 NumberStyles.HexNumber,
391 CultureInfo.InvariantCulture,
394 return ulong.TryParse (s, NumberStyles.Integer,
395 CultureInfo.InvariantCulture,
400 private static bool TryParseSubAuthority (string s, out uint result)
402 if (s.StartsWith ("0X")) {
403 return uint.TryParse (s.Substring (2),
404 NumberStyles.HexNumber,
405 CultureInfo.InvariantCulture,
408 return uint.TryParse (s, NumberStyles.Integer,
409 CultureInfo.InvariantCulture,