[Test] Avoid MethodInfoTest.GetMethodBody failure when executed on a release (IL...
[mono.git] / mcs / class / corlib / System.Security.Principal / SecurityIdentifier.cs
1 //
2 // System.Security.Policy.SecurityIdentifier class
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Kenneth Bell
7 //      James Bellinger  <jfb@zer7.com>
8 //
9 // Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2012       James Bellinger
11 //
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:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
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.
30 //
31
32
33 using System.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Text;
36
37 namespace System.Security.Principal
38 {
39         [ComVisible (false)]
40         public sealed class SecurityIdentifier : IdentityReference, IComparable<SecurityIdentifier>
41         {
42                 private byte[] buffer;
43
44                 public static readonly int MaxBinaryLength = 68;
45                 public static readonly int MinBinaryLength = 8;
46
47                 public SecurityIdentifier (string sddlForm)
48                 {
49                         if (sddlForm == null)
50                                 throw new ArgumentNullException ("sddlForm");
51                         
52                         buffer = ParseSddlForm (sddlForm);
53                 }
54
55                 unsafe public SecurityIdentifier (byte[] binaryForm, int offset)
56                 {
57                         if (binaryForm == null)
58                                 throw new ArgumentNullException ("binaryForm");
59                         if ((offset < 0) || (offset > binaryForm.Length - 2))
60                                 throw new ArgumentException ("offset");
61                         
62                         fixed (byte* binaryFormPtr = binaryForm)
63                                 CreateFromBinaryForm ((IntPtr)(binaryFormPtr + offset), binaryForm.Length - offset);
64                 }
65
66                 public SecurityIdentifier (IntPtr binaryForm)
67                 {                               
68                         CreateFromBinaryForm (binaryForm, int.MaxValue);
69                 }
70                 
71                 void CreateFromBinaryForm (IntPtr binaryForm, int length)
72                 {                       
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");
79                         
80                         buffer = new byte[8 + (numSubAuthorities * 4)];
81                         Marshal.Copy (binaryForm, buffer, 0, buffer.Length);
82                 }
83                 
84                 public SecurityIdentifier (WellKnownSidType sidType,
85                                            SecurityIdentifier domainSid)
86                 {
87                         WellKnownAccount acct = WellKnownAccount.LookupByType (sidType);
88                         if (acct == null)
89                                 throw new ArgumentException ("Unable to convert SID type: " + sidType);
90                         
91                         if (acct.IsAbsolute) {
92                                 buffer = ParseSddlForm (acct.Sid);
93                         } else {
94                                 if (domainSid == null)
95                                         throw new ArgumentNullException ("domainSid");
96                                 
97                                 buffer = ParseSddlForm (domainSid.Value + "-" + acct.Rid);
98                         }
99                 }
100
101                 public SecurityIdentifier AccountDomainSid {
102                         get {
103                                 string strForm = this.Value;
104                                 
105                                 // Check prefix, and ensure at least 4 sub authorities
106                                 if (!strForm.StartsWith ("S-1-5-21") || buffer[1] < 4)
107                                         return null;
108                                 
109                                 // Domain is first four sub-authorities
110                                 byte[] temp = new byte[8 + (4 * 4)];
111                                 Array.Copy (buffer, 0, temp, 0, temp.Length);
112                                 temp[1] = 4;
113                                 return new SecurityIdentifier (temp, 0);
114                         }
115                 }
116
117                 public int BinaryLength {
118                         get { return buffer.Length; }
119                 }
120
121                 public override string Value {
122                         get {
123                                 StringBuilder s = new StringBuilder ();
124                                 
125                                 ulong authority = GetSidAuthority ();
126                                 s.AppendFormat (CultureInfo.InvariantCulture, "S-1-{0}", authority);
127                                 
128                                 for (byte i = 0; i < GetSidSubAuthorityCount (); ++i)
129                                         s.AppendFormat (
130                                                 CultureInfo.InvariantCulture,
131                                                 "-{0}", GetSidSubAuthority (i));
132                                 
133                                 return s.ToString ();
134                         }
135                 }
136
137                 ulong GetSidAuthority ()
138                 {
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);
142                 }
143                 
144                 byte GetSidSubAuthorityCount ()
145                 {
146                         return buffer [1];
147                 }
148                 
149                 uint GetSidSubAuthority (byte index)
150                 {
151                         // Note sub authorities little-endian, authority (above) is big-endian!
152                         int offset = 8 + (index * 4);
153                         
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);
158                 }
159                 
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)
165                 {
166                         if (sid == null)
167                                 throw new ArgumentNullException ("sid");
168                                 
169                         int result;
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;
174                         return 0;
175                 }
176
177                 public override bool Equals (object o)
178                 {
179                         return Equals (o as SecurityIdentifier);
180                 }
181
182                 public bool Equals (SecurityIdentifier sid)
183                 {
184                         if (sid == null)
185                                 return false;
186                         return (sid.Value == Value);
187                 }
188
189                 public void GetBinaryForm (byte[] binaryForm, int offset)
190                 {
191                         if (binaryForm == null)
192                                 throw new ArgumentNullException ("binaryForm");
193                         if ((offset < 0) || (offset > binaryForm.Length - buffer.Length))
194                                 throw new ArgumentException ("offset");
195                         
196                         Array.Copy (buffer, 0, binaryForm, offset, buffer.Length);
197                 }
198
199                 public override int GetHashCode ()
200                 {
201                         return Value.GetHashCode ();
202                 }
203
204                 public bool IsAccountSid ()
205                 {
206                         return AccountDomainSid != null;
207                 }
208
209                 public bool IsEqualDomainSid (SecurityIdentifier sid)
210                 {
211                         SecurityIdentifier domSid = AccountDomainSid;
212                         if (domSid == null)
213                                 return false;
214                         
215                         return domSid.Equals (sid.AccountDomainSid);
216                 }
217
218                 public override bool IsValidTargetType (Type targetType)
219                 {
220                         if (targetType == typeof(SecurityIdentifier))
221                                 return true;
222                         if (targetType == typeof(NTAccount))
223                                 return true;
224                         return false;
225                 }
226
227                 public bool IsWellKnown (WellKnownSidType type)
228                 {
229                         WellKnownAccount acct = WellKnownAccount.LookupByType (type);
230                         if (acct == null)
231                                 return false;
232                         
233                         string sid = Value;
234                         
235                         if (acct.IsAbsolute)
236                                 return sid == acct.Sid;
237                         
238                         return sid.StartsWith ("S-1-5-21", StringComparison.OrdinalIgnoreCase)
239                                 && sid.EndsWith ("-" + acct.Rid, StringComparison.OrdinalIgnoreCase);
240                 }
241
242                 public override string ToString ()
243                 {
244                         return Value;
245                 }
246
247                 public override IdentityReference Translate (Type targetType)
248                 {
249                         if (targetType == typeof(SecurityIdentifier))
250                                 return this;
251                         
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);
256                                 
257                                 return new NTAccount (acct.Name);
258                         }
259                         
260                         throw new ArgumentException ("Unknown type.", "targetType");
261                 }
262
263                 public static bool operator == (SecurityIdentifier left, SecurityIdentifier right)
264                 {
265                         if (((object)left) == null)
266                                 return (((object)right) == null);
267                         if (((object)right) == null)
268                                 return false;
269                         return (left.Value == right.Value);
270                 }
271
272                 public static bool operator != (SecurityIdentifier left, SecurityIdentifier right)
273                 {
274                         if (((object)left) == null)
275                                 return (((object)right) != null);
276                         if (((object)right) == null)
277                                 return true;
278                         return (left.Value != right.Value);
279                 }
280
281                 internal string GetSddlForm()
282                 {
283                         string sidString = Value;
284                         
285                         WellKnownAccount acct = WellKnownAccount.LookupBySid(sidString);
286                         if(acct == null || acct.SddlForm == null)
287                                 return sidString;
288                         
289                         return acct.SddlForm;
290                 }
291
292                 internal static SecurityIdentifier ParseSddlForm(string sddlForm, ref int pos)
293                 {
294                         if (sddlForm.Length - pos < 2)
295                                 throw new ArgumentException("Invalid SDDL string.", "sddlForm");
296                         
297                         string sid;
298                         int len;
299                         
300                         string prefix = sddlForm.Substring(pos, 2).ToUpperInvariant();
301                         if (prefix == "S-")
302                         {
303                                 // Looks like a SID, try to parse it.
304                                 int endPos = pos;
305                                 
306                                 char ch = Char.ToUpperInvariant(sddlForm[endPos]);
307                                 while (ch == 'S' || ch == '-' || ch == 'X'
308                                        || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
309                                         ++endPos;
310                                         ch = Char.ToUpperInvariant(sddlForm[endPos]);
311                                 }
312                                 
313                                 sid = sddlForm.Substring(pos, endPos - pos);
314                                 len = endPos - pos;
315                         } else {
316                                 sid = prefix;
317                                 len = 2;
318                         }
319                         
320                         SecurityIdentifier ret = new SecurityIdentifier(sid);
321                         pos += len;
322                         return ret;
323                 }
324
325                 private static byte[] ParseSddlForm (string sddlForm)
326                 {
327                         string sid = sddlForm;
328                         
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);
333                                 if (acct == null)
334                                         throw new ArgumentException(
335                                                 "Invalid SDDL string - unrecognized account: " + sddlForm,
336                                                 "sddlForm");
337                                 if (!acct.IsAbsolute)
338                                         throw new NotImplementedException(
339                                                 "Mono unable to convert account to SID: "
340                                                 + (acct.Name != null ? acct.Name : sddlForm));
341
342                                 sid = acct.Sid;
343                         }
344                         
345                         string[] elements = sid.ToUpperInvariant ().Split ('-');
346                         int numSubAuthorities = elements.Length - 3;
347                         
348                         if (elements.Length < 3 || elements[0] != "S" || numSubAuthorities > 15)
349                                 throw new ArgumentException ("Value was invalid.");
350                         
351                         if (elements[1] != "1")
352                                 throw new ArgumentException ("Only SIDs with revision 1 are supported");
353                         
354                         byte[] buffer = new byte[8 + (numSubAuthorities * 4)];
355                         buffer[0] = 1;
356                         buffer[1] = (byte)numSubAuthorities;
357                         
358                         ulong authority;
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);
367                         
368                         for (int i = 0; i < numSubAuthorities; ++i) {
369                                 uint subAuthority;
370                                 
371                                 if (!TryParseSubAuthority (elements[i + 3],
372                                                            out subAuthority))
373                                         throw new ArgumentException ("Value was invalid.");
374                                 
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);
381                         }
382                         
383                         return buffer;
384                 }
385
386                 private static bool TryParseAuthority (string s, out ulong result)
387                 {
388                         if (s.StartsWith ("0X")) {
389                                 return ulong.TryParse (s.Substring (2),
390                                                        NumberStyles.HexNumber,
391                                                        CultureInfo.InvariantCulture,
392                                                        out result);
393                         } else {
394                                 return ulong.TryParse (s, NumberStyles.Integer,
395                                                        CultureInfo.InvariantCulture,
396                                                        out result);
397                         }
398                 }
399
400                 private static bool TryParseSubAuthority (string s, out uint result)
401                 {
402                         if (s.StartsWith ("0X")) {
403                                 return uint.TryParse (s.Substring (2),
404                                                       NumberStyles.HexNumber,
405                                                       CultureInfo.InvariantCulture,
406                                                       out result);
407                         } else {
408                                 return uint.TryParse (s, NumberStyles.Integer,
409                                                       CultureInfo.InvariantCulture,
410                                                       out result);
411                         }
412                 }
413         }
414 }
415