ACLs now work on Windows. Tests all the way up to DirectorySecurityTest and FileSecur...
[mono.git] / mcs / class / corlib / System.Security.AccessControl / ObjectSecurity.cs
1 //
2 // System.Security.AccessControl.ObjectSecurity implementation
3 //
4 // Authors:
5 //      Dick Porter  <dick@ximian.com>
6 //      Atsushi Enomoto  <atsushi@ximian.com>
7 //      James Bellinger  <jfb@zer7.com>
8 //
9 // Copyright (C) 2005-2007 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 using System.Collections.Generic;
33 using System.Security.Principal;
34 using System.Runtime.InteropServices;
35 using System.Threading;
36
37 namespace System.Security.AccessControl
38 {
39         public abstract class ObjectSecurity
40         {
41                 internal ObjectSecurity (CommonSecurityDescriptor securityDescriptor)
42                 {
43                         if (securityDescriptor == null)
44                                 throw new ArgumentNullException ("securityDescriptor");
45                                 
46                         descriptor = securityDescriptor;
47                         rw_lock = new ReaderWriterLock ();
48                 }
49                 
50                 protected ObjectSecurity (bool isContainer, bool isDS)
51                         : this (new CommonSecurityDescriptor
52                                 (isContainer, isDS, ControlFlags.None, null, null, null,
53                                  new DiscretionaryAcl (isContainer, isDS, 0)))
54                 {
55                 }
56                 
57                 internal CommonSecurityDescriptor descriptor;
58                 AccessControlSections sections_modified;
59                 ReaderWriterLock rw_lock;
60
61                 public abstract Type AccessRightType { get; }
62                 
63                 public abstract Type AccessRuleType { get; }
64                 
65                 public abstract Type AuditRuleType { get; }
66                 
67                 public bool AreAccessRulesCanonical {
68                         get {
69                                 ReadLock ();
70                                 try {
71                                         return descriptor.IsDiscretionaryAclCanonical;
72                                 } finally {
73                                         ReadUnlock ();
74                                 }
75                         }
76                 }
77                 
78                 public bool AreAccessRulesProtected {
79                         get {
80                                 ReadLock ();
81                                 try {
82                                         return 0 != (descriptor.ControlFlags & ControlFlags.DiscretionaryAclProtected);
83                                 } finally {
84                                         ReadUnlock ();
85                                 }
86                         }
87                 }
88                 
89                 public bool AreAuditRulesCanonical {
90                         get {
91                                 ReadLock ();
92                                 try {
93                                         return descriptor.IsSystemAclCanonical;
94                                 } finally {
95                                         ReadUnlock ();
96                                 }
97                         }
98                 }
99                 
100                 public bool AreAuditRulesProtected {
101                         get {
102                                 ReadLock ();
103                                 try {
104                                         return 0 != (descriptor.ControlFlags & ControlFlags.SystemAclProtected);
105                                 } finally {
106                                         ReadUnlock ();
107                                 }
108                         }
109                 }
110                 
111                 internal AccessControlSections AccessControlSectionsModified {
112                         get { Reading (); return sections_modified; }
113                         set { Writing (); sections_modified = value; }
114                 }
115
116                 protected bool AccessRulesModified {
117                         get { return AreAccessControlSectionsModified (AccessControlSections.Access); }
118                         set { SetAccessControlSectionsModified (AccessControlSections.Access, value); }
119                 }
120                 
121                 protected bool AuditRulesModified {
122                         get { return AreAccessControlSectionsModified (AccessControlSections.Audit); }
123                         set { SetAccessControlSectionsModified (AccessControlSections.Audit, value); }
124                 }
125                 
126                 protected bool GroupModified {
127                         get { return AreAccessControlSectionsModified (AccessControlSections.Group); }
128                         set { SetAccessControlSectionsModified (AccessControlSections.Group, value); }
129                 }
130                 
131                 protected bool IsContainer {
132                         get { return descriptor.IsContainer; }
133                 }
134                 
135                 protected bool IsDS {
136                         get { return descriptor.IsDS; }
137                 }
138                 
139                 protected bool OwnerModified {
140                         get { return AreAccessControlSectionsModified (AccessControlSections.Owner); }
141                         set { SetAccessControlSectionsModified (AccessControlSections.Owner, value); }
142                 }
143                 
144                 public abstract AccessRule AccessRuleFactory (IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type);
145                 
146                 public abstract AuditRule AuditRuleFactory (IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags);             
147                                 
148                 public IdentityReference GetGroup (Type targetType)
149                 {
150                         ReadLock ();
151                         try {
152                                 if (descriptor.Group == null)
153                                         return null;
154                                 
155                                 return descriptor.Group.Translate (targetType);
156                         } finally {
157                                 ReadUnlock ();
158                         }
159                 }
160                 
161                 public IdentityReference GetOwner (Type targetType)
162                 {
163                         ReadLock ();
164                         try {
165                                 if (descriptor.Owner == null)
166                                         return null;
167                                 
168                                 return descriptor.Owner.Translate (targetType);
169                         } finally {
170                                 ReadUnlock ();
171                         }
172                 }
173                 
174                 public byte[] GetSecurityDescriptorBinaryForm ()
175                 {
176                         ReadLock ();
177                         try {
178                                 byte[] binaryForm = new byte[descriptor.BinaryLength];
179                                 descriptor.GetBinaryForm (binaryForm, 0);
180                                 return binaryForm;
181                         } finally {
182                                 ReadUnlock ();
183                         }
184                 }
185                 
186                 public string GetSecurityDescriptorSddlForm (AccessControlSections includeSections)
187                 {
188                         ReadLock ();
189                         try {
190                                 return descriptor.GetSddlForm (includeSections);
191                         } finally {
192                                 ReadUnlock ();
193                         }
194                 }
195
196                 public static bool IsSddlConversionSupported ()
197                 {
198                         return GenericSecurityDescriptor.IsSddlConversionSupported ();
199                 }
200                 
201                 public virtual bool ModifyAccessRule (AccessControlModification modification, AccessRule rule, out bool modified)
202                 {
203                         if (rule == null)
204                                 throw new ArgumentNullException ("rule");
205                                 
206                         if (!AccessRuleType.IsAssignableFrom (rule.GetType()))
207                                 throw new ArgumentException ("rule");
208
209                         return ModifyAccess (modification, rule, out modified);
210                 }
211                 
212                 public virtual bool ModifyAuditRule (AccessControlModification modification, AuditRule rule, out bool modified)
213                 {
214                         if (rule == null)
215                                 throw new ArgumentNullException ("rule");
216
217                         if (!AuditRuleType.IsAssignableFrom (rule.GetType()))
218                                 throw new ArgumentException ("rule");
219                                 
220                         return ModifyAudit (modification, rule, out modified);
221                 }
222                 
223                 public virtual void PurgeAccessRules (IdentityReference identity)
224                 {
225                         if (null == identity)
226                                 throw new ArgumentNullException ("identity");
227                                 
228                         WriteLock ();
229                         try {
230                                 descriptor.PurgeAccessControl (SidFromIR (identity));
231                         } finally {
232                                 WriteUnlock ();
233                         }
234                 }
235                 
236                 public virtual void PurgeAuditRules (IdentityReference identity)
237                 {
238                         if (null == identity)
239                                 throw new ArgumentNullException ("identity");
240                                 
241                         WriteLock ();
242                         try {
243                                 descriptor.PurgeAudit (SidFromIR (identity));
244                         } finally {
245                                 WriteUnlock ();
246                         }
247                 }
248                 
249                 public void SetAccessRuleProtection (bool isProtected,
250                                                      bool preserveInheritance)
251                 {
252                         WriteLock ();
253                         try {
254                                 descriptor.SetDiscretionaryAclProtection (isProtected, preserveInheritance);
255                         } finally {
256                                 WriteUnlock();
257                         }
258                 }
259                 
260                 public void SetAuditRuleProtection (bool isProtected,
261                                                     bool preserveInheritance)
262                 {
263                         WriteLock ();
264                         try {
265                                 descriptor.SetSystemAclProtection (isProtected, preserveInheritance);
266                         } finally {
267                                 WriteUnlock ();
268                         }
269                 }
270                 
271                 public void SetGroup (IdentityReference identity)
272                 {
273                         WriteLock ();
274                         try {
275                                 descriptor.Group = SidFromIR (identity);
276                                 GroupModified = true;
277                         } finally {
278                                 WriteUnlock ();
279                         }
280                 }
281                 
282                 public void SetOwner (IdentityReference identity)
283                 {
284                         WriteLock ();
285                         try {
286                                 descriptor.Owner = SidFromIR (identity);
287                                 OwnerModified = true;
288                         } finally {
289                                 WriteUnlock ();
290                         }
291                 }
292                 
293                 public void SetSecurityDescriptorBinaryForm (byte[] binaryForm)
294                 {
295                         SetSecurityDescriptorBinaryForm (binaryForm, AccessControlSections.All);
296                 }
297                 
298                 public void SetSecurityDescriptorBinaryForm (byte[] binaryForm, AccessControlSections includeSections)
299                 {
300                         CopySddlForm (new CommonSecurityDescriptor (IsContainer, IsDS, binaryForm, 0), includeSections);
301                 }
302                 
303                 public void SetSecurityDescriptorSddlForm (string sddlForm)
304                 {
305                         SetSecurityDescriptorSddlForm (sddlForm, AccessControlSections.All);
306                 }
307
308                 public void SetSecurityDescriptorSddlForm (string sddlForm, AccessControlSections includeSections)
309                 {
310                         CopySddlForm (new CommonSecurityDescriptor (IsContainer, IsDS, sddlForm), includeSections);
311                 }
312                 
313                 void CopySddlForm (CommonSecurityDescriptor sourceDescriptor, AccessControlSections includeSections)
314                 {
315                         WriteLock ();
316                         try {
317                                 AccessControlSectionsModified |= includeSections;
318                                 if (0 != (includeSections & AccessControlSections.Audit))
319                                         descriptor.SystemAcl = sourceDescriptor.SystemAcl;
320                                 if (0 != (includeSections & AccessControlSections.Access))
321                                         descriptor.DiscretionaryAcl = sourceDescriptor.DiscretionaryAcl;
322                                 if (0 != (includeSections & AccessControlSections.Owner))
323                                         descriptor.Owner = sourceDescriptor.Owner;
324                                 if (0 != (includeSections & AccessControlSections.Group))
325                                         descriptor.Group = sourceDescriptor.Group;
326                         } finally {
327                                 WriteUnlock ();
328                         }
329                 }
330                 
331                 protected abstract bool ModifyAccess (AccessControlModification modification, AccessRule rule, out bool modified);
332                 
333                 protected abstract bool ModifyAudit (AccessControlModification modification, AuditRule rule, out bool modified);
334                 
335                 // For MoMA. NotImplementedException is correct for this base class.
336                 Exception GetNotImplementedException ()
337                 {
338                         return new NotImplementedException ();
339                 }
340                 
341                 protected virtual void Persist (SafeHandle handle, AccessControlSections includeSections)
342                 {
343                         throw GetNotImplementedException ();
344                 }
345                 
346                 protected virtual void Persist (string name, AccessControlSections includeSections)
347                 {
348                         throw GetNotImplementedException ();
349                 }
350                 
351                 [MonoTODO]
352                 protected virtual void Persist (bool enableOwnershipPrivilege, string name, AccessControlSections includeSections)
353                 {
354                         throw new NotImplementedException ();
355                 }
356                 
357                 void Reading ()
358                 {
359                         if (!rw_lock.IsReaderLockHeld && !rw_lock.IsWriterLockHeld)
360                                 throw new InvalidOperationException ("Either a read or a write lock must be held.");
361                 }
362                 
363                 protected void ReadLock ()
364                 {
365                         rw_lock.AcquireReaderLock (Timeout.Infinite);
366                 }
367                 
368                 protected void ReadUnlock ()
369                 {
370                         rw_lock.ReleaseReaderLock ();
371                 }
372                 
373                 void Writing ()
374                 {
375                         if (!rw_lock.IsWriterLockHeld)
376                                 throw new InvalidOperationException ("Write lock must be held.");
377                 }
378                 
379                 protected void WriteLock ()
380                 {
381                         rw_lock.AcquireWriterLock (Timeout.Infinite);
382                 }
383                 
384                 protected void WriteUnlock ()
385                 {
386                         rw_lock.ReleaseWriterLock ();
387                 }
388                 
389                 internal AuthorizationRuleCollection InternalGetAccessRules (bool includeExplicit,
390                                                                              bool includeInherited,
391                                                                              Type targetType)
392                 {
393                         List<AuthorizationRule> rules = new List<AuthorizationRule> ();
394                         
395                         ReadLock ();
396                         try {
397                                 foreach (GenericAce genericAce in descriptor.DiscretionaryAcl) {
398                                         QualifiedAce ace = genericAce as QualifiedAce;
399                                         if (null == ace) continue;
400                                         if (ace.IsInherited && !includeInherited) continue;
401                                         if (!ace.IsInherited && !includeExplicit) continue;
402                                                         
403                                         AccessControlType type;
404                                         if (AceQualifier.AccessAllowed == ace.AceQualifier)
405                                                 type = AccessControlType.Allow;
406                                         else if (AceQualifier.AccessDenied == ace.AceQualifier)
407                                                 type = AccessControlType.Deny;
408                                         else
409                                                 continue;
410                                                 
411                                         AccessRule rule = InternalAccessRuleFactory (ace, targetType, type);
412                                         rules.Add (rule);
413                                 }
414                         } finally {
415                                 ReadUnlock ();
416                         }
417                         
418                         return new AuthorizationRuleCollection (rules.ToArray ());
419                 }
420                 
421                 internal virtual AccessRule InternalAccessRuleFactory (QualifiedAce ace, Type targetType,
422                                                                        AccessControlType type)
423                 {
424                         return AccessRuleFactory (ace.SecurityIdentifier.Translate (targetType),
425                                                   ace.AccessMask, ace.IsInherited,
426                                                   ace.InheritanceFlags, ace.PropagationFlags, type);
427                 }
428  
429                 internal AuthorizationRuleCollection InternalGetAuditRules (bool includeExplicit,
430                                                                             bool includeInherited,
431                                                                             Type targetType)
432                 {
433                         List<AuthorizationRule> rules = new List<AuthorizationRule> ();
434                         
435                         ReadLock ();
436                         try {
437                                 if (null != descriptor.SystemAcl) {
438                                         foreach (GenericAce genericAce in descriptor.SystemAcl) {
439                                                 QualifiedAce ace = genericAce as QualifiedAce;
440                                                 if (null == ace) continue;
441                                                 if (ace.IsInherited && !includeInherited) continue;
442                                                 if (!ace.IsInherited && !includeExplicit) continue;
443                                 
444                                                 if (AceQualifier.SystemAudit != ace.AceQualifier) continue;
445                                                 
446                                                 AuditRule rule = InternalAuditRuleFactory (ace, targetType);
447                                                 rules.Add (rule);
448                                         }
449                                 }
450                         } finally {
451                                 ReadUnlock ();
452                         }
453                         
454                         return new AuthorizationRuleCollection (rules.ToArray ());
455                 }
456                 
457                 internal virtual AuditRule InternalAuditRuleFactory (QualifiedAce ace, Type targetType)
458                 {
459                         return AuditRuleFactory (ace.SecurityIdentifier.Translate (targetType),
460                                                  ace.AccessMask, ace.IsInherited,
461                                                  ace.InheritanceFlags, ace.PropagationFlags, ace.AuditFlags);
462                 }
463                                 
464                 internal static SecurityIdentifier SidFromIR (IdentityReference identity)
465                 {
466                         if (null == identity)
467                                 throw new ArgumentNullException ("identity");
468                 
469                         return (SecurityIdentifier)identity.Translate (typeof (SecurityIdentifier));
470                 }
471                 
472                 bool AreAccessControlSectionsModified (AccessControlSections mask)
473                 {
474                         return 0 != (AccessControlSectionsModified & mask);
475                 }
476                 
477                 void SetAccessControlSectionsModified(AccessControlSections mask, bool modified)
478                 {
479                         if (modified)
480                                 AccessControlSectionsModified |= mask;
481                         else
482                                 AccessControlSectionsModified &= ~mask;
483                 }
484         }
485 }
486