Merge pull request #3528 from BrzVlad/fix-sgen-check-before-collections
[mono.git] / mcs / class / corlib / System.Security.AccessControl / NativeObjectSecurity.cs
1 //
2 // System.Security.AccessControl.NativeObjectSecurity implementation
3 //
4 // Authors:
5 //      Dick Porter  <dick@ximian.com>
6 //      James Bellinger  <jfb@zer7.com>
7 //
8 // Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
9 // Copyright (C) 2012       James Bellinger
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.IO;
33 using System.Runtime.InteropServices;
34 using System.Security.Principal;
35
36 namespace System.Security.AccessControl
37 {
38         public abstract class NativeObjectSecurity : CommonObjectSecurity
39         {
40                 ExceptionFromErrorCode exception_from_error_code;
41                 ResourceType resource_type;
42                 
43                 protected internal delegate Exception ExceptionFromErrorCode (int errorCode,
44                                                                               string name, SafeHandle handle,
45                                                                               object context);
46                 
47                 internal NativeObjectSecurity (CommonSecurityDescriptor securityDescriptor, ResourceType resourceType)
48                         : base (securityDescriptor)
49                 {
50                         resource_type = resourceType;
51                 }
52
53                 protected NativeObjectSecurity (bool isContainer,
54                                                 ResourceType resourceType)
55                         : this (isContainer, resourceType, null, null)
56                 {
57                 }
58
59                 protected NativeObjectSecurity (bool isContainer,
60                                                 ResourceType resourceType,
61                                                 ExceptionFromErrorCode exceptionFromErrorCode,
62                                                 object exceptionContext)
63                         : base (isContainer)
64                 {
65                         exception_from_error_code = exceptionFromErrorCode;
66                         resource_type = resourceType;
67                 }
68                 
69                 protected NativeObjectSecurity (bool isContainer,
70                                                 ResourceType resourceType,
71                                                 SafeHandle handle,
72                                                 AccessControlSections includeSections)
73                         : this (isContainer, resourceType, handle, includeSections, null, null)
74                 {
75                 }
76                 
77                 protected NativeObjectSecurity (bool isContainer,
78                                                 ResourceType resourceType,
79                                                 string name,
80                                                 AccessControlSections includeSections)
81                         : this (isContainer, resourceType, name, includeSections, null, null)
82                 {
83                 }
84                 
85                 protected NativeObjectSecurity (bool isContainer,
86                                                 ResourceType resourceType,
87                                                 SafeHandle handle,
88                                                 AccessControlSections includeSections,
89                                                 ExceptionFromErrorCode exceptionFromErrorCode,
90                                                 object exceptionContext)
91                         : this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
92                 {
93                         RaiseExceptionOnFailure (InternalGet (handle, includeSections),
94                                                  null, handle, exceptionContext);
95                          ClearAccessControlSectionsModified ();
96                 }
97                 
98                 protected NativeObjectSecurity (bool isContainer,
99                                                 ResourceType resourceType,
100                                                 string name,
101                                                 AccessControlSections includeSections,
102                                                 ExceptionFromErrorCode exceptionFromErrorCode,
103                                                 object exceptionContext)
104                         : this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
105                 {
106                         RaiseExceptionOnFailure (InternalGet (name, includeSections),
107                                                  name, null, exceptionContext);
108                         ClearAccessControlSectionsModified ();
109                 }
110
111                 void ClearAccessControlSectionsModified ()
112                 {
113                         WriteLock ();
114                         try {
115                                 AccessControlSectionsModified = AccessControlSections.None;
116                         } finally {
117                                 WriteUnlock ();
118                         }
119                 }
120                 
121                 protected override sealed void Persist (SafeHandle handle,
122                                                         AccessControlSections includeSections)
123                 {
124                         Persist (handle, includeSections, null);
125                 }
126                 
127                 protected override sealed void Persist (string name,
128                                                         AccessControlSections includeSections)
129                 {
130                         Persist (name, includeSections, null);
131                 }
132
133                 internal void Persist (SafeHandle handle)
134                 {
135                         PersistModifications (handle);
136                 }
137                 
138                 internal void PersistModifications (SafeHandle handle)
139                 {
140                         WriteLock();
141                         try {
142                                 Persist (handle, AccessControlSectionsModified, null);
143                         } finally {
144                                 WriteUnlock ();
145                         }
146                 }
147                 
148                 protected void Persist (SafeHandle handle,
149                                         AccessControlSections includeSections,
150                                         object exceptionContext)
151                 {
152                         WriteLock ();
153                         try {
154                                 RaiseExceptionOnFailure (InternalSet (handle, includeSections), null, handle, exceptionContext);
155                                 AccessControlSectionsModified &= ~includeSections;
156                         } finally {
157                                 WriteUnlock ();
158                         }
159                 }
160
161                 internal void PersistModifications (string name)
162                 {
163                         WriteLock();
164                         try {
165                                 Persist (name, AccessControlSectionsModified, null);
166                         } finally {
167                                 WriteUnlock ();
168                         }
169                 }
170                 
171                 protected void Persist (string name,
172                                         AccessControlSections includeSections,
173                                         object exceptionContext)
174                 {
175                         if (null == name)
176                                 throw new ArgumentNullException ("name");
177
178                         WriteLock ();
179                         try {
180                                 RaiseExceptionOnFailure (InternalSet (name, includeSections), name, null, exceptionContext);
181                                 AccessControlSectionsModified &= ~includeSections;
182                         } finally {
183                                 WriteUnlock ();
184                         }
185                 }
186                 
187                 internal static Exception DefaultExceptionFromErrorCode (int errorCode,
188                                                                          string name, SafeHandle handle,
189                                                                          object context)
190                 {
191                         switch (errorCode) {
192                                 case 2: return new FileNotFoundException ();
193                                 case 3: return new DirectoryNotFoundException ();
194                                 case 5: return new UnauthorizedAccessException ();
195                                 case 1314: return new PrivilegeNotHeldException (); // happens with audit rules
196                                 default: return new InvalidOperationException ("OS error code " + errorCode.ToString());
197                         }
198                 }
199                 
200                 void RaiseExceptionOnFailure (int errorCode, string name, SafeHandle handle, object context)
201                 {
202                         if (errorCode == 0) return;
203                         throw (exception_from_error_code ?? DefaultExceptionFromErrorCode)(errorCode, name, handle, context);
204                 }
205                 
206                 // InternalGet/InternalSet are virtual so that non-Windows platforms which do not share an
207                 // API between files, mutexes, etc. can override in the subclass and do their own thing.
208                 internal virtual int InternalGet (SafeHandle handle,
209                                                   AccessControlSections includeSections)
210                 {
211 #if MOBILE
212                         throw new PlatformNotSupportedException ();
213 #else
214                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
215                                 throw new PlatformNotSupportedException ();
216
217                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
218                                                          out IntPtr owner, out IntPtr group,
219                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
220                                 {
221                                         return GetSecurityInfo (handle, ResourceType, securityInfos,
222                                                                 out owner, out group,
223                                                                 out dacl, out sacl, out descriptor);
224                                 }, includeSections);
225 #endif
226                 }
227                 
228                 internal virtual int InternalGet (string name,
229                                                   AccessControlSections includeSections)
230                 {
231 #if MOBILE
232                         throw new PlatformNotSupportedException ();
233 #else
234                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
235                                 throw new PlatformNotSupportedException ();
236
237                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
238                                                          out IntPtr owner, out IntPtr group,
239                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
240                                 {
241                                         return GetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos,
242                                                                      out owner, out group,
243                                                                      out dacl, out sacl, out descriptor);
244                                 }, includeSections);
245 #endif
246                 }
247                 
248 #if MOBILE
249                 internal virtual int InternalSet (SafeHandle handle, AccessControlSections includeSections)
250                 {
251                         throw new PlatformNotSupportedException ();
252                 }
253
254                 internal virtual int InternalSet (string name, AccessControlSections includeSections)
255                 {
256                         throw new PlatformNotSupportedException ();
257                 }
258 #else
259                 internal virtual int InternalSet (SafeHandle handle,
260                                                   AccessControlSections includeSections)
261                 {
262                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
263                                 throw new PlatformNotSupportedException ();
264
265                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
266                                 SetSecurityInfo (handle, ResourceType, securityInfos, owner, group, dacl, sacl),
267                                 includeSections);
268                 }
269                 
270                 internal virtual int InternalSet (string name,
271                                                   AccessControlSections includeSections)
272                 {       
273                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
274                                 throw new PlatformNotSupportedException ();
275                 
276                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
277                                 SetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos, owner, group, dacl, sacl),
278                                 includeSections);
279                 }
280                 
281                 internal ResourceType ResourceType {
282                         get { return resource_type; }
283                 }
284                 
285                 #region Win32 Details           
286                 int Win32GetHelper (GetSecurityInfoNativeCall nativeCall,
287                                     AccessControlSections includeSections)
288                 {
289                         bool getOwner = 0 != (includeSections & AccessControlSections.Owner);
290                         bool getGroup = 0 != (includeSections & AccessControlSections.Group);
291                         bool getDacl = 0 != (includeSections & AccessControlSections.Access);
292                         bool getSacl = 0 != (includeSections & AccessControlSections.Audit);
293                         
294                         SecurityInfos securityInfos = 0;
295                         if (getOwner) securityInfos |= SecurityInfos.Owner;
296                         if (getGroup) securityInfos |= SecurityInfos.Group;
297                         if (getDacl ) securityInfos |= SecurityInfos.DiscretionaryAcl;
298                         if (getSacl ) securityInfos |= SecurityInfos.SystemAcl;
299                         
300                         IntPtr owner, group, dacl, sacl, descriptor;
301                         int result = nativeCall (securityInfos,
302                                                  out owner, out group, out dacl, out sacl, out descriptor);
303                         if (0 != result) return result;
304                         
305                         try {
306                                 int binaryLength = 0;
307                                 if (IsValidSecurityDescriptor (descriptor))
308                                         binaryLength = GetSecurityDescriptorLength (descriptor);
309                                         
310                                 byte[] binaryForm = new byte[binaryLength];
311                                 Marshal.Copy (descriptor, binaryForm, 0, binaryLength);
312                                 SetSecurityDescriptorBinaryForm (binaryForm, includeSections);
313                         } finally {
314                                 LocalFree (descriptor);
315                         }
316                         return 0;
317                 }
318                 
319                 int Win32SetHelper (SetSecurityInfoNativeCall nativeCall,
320                                     AccessControlSections includeSections)
321                 {
322                         // SE_REGISTRY_KEY will fail UnauthorizedAccessException without this check.
323                         if (AccessControlSections.None == includeSections) return 0;
324                         
325                         SecurityInfos securityInfos = 0;
326                         byte[] owner = null, group = null, dacl = null, sacl = null;
327                         
328                         if (0 != (includeSections & AccessControlSections.Owner)) {
329                                 securityInfos |= SecurityInfos.Owner;
330                                 SecurityIdentifier ownerSid = (SecurityIdentifier)GetOwner (typeof (SecurityIdentifier));
331                                 if (null != ownerSid) {
332                                         owner = new byte[ownerSid.BinaryLength];
333                                         ownerSid.GetBinaryForm (owner, 0);
334                                 }
335                         }
336                         
337                         if (0 != (includeSections & AccessControlSections.Group)) {
338                                 securityInfos |= SecurityInfos.Group;
339                                 SecurityIdentifier groupSid = (SecurityIdentifier)GetGroup (typeof (SecurityIdentifier));
340                                 if (null != groupSid) {
341                                         group = new byte[groupSid.BinaryLength];
342                                         groupSid.GetBinaryForm (group, 0);
343                                 }
344                         }
345                         
346                         if (0 != (includeSections & AccessControlSections.Access)) {
347                                 securityInfos |= SecurityInfos.DiscretionaryAcl;
348                                 if (AreAccessRulesProtected)
349                                         securityInfos |= unchecked((SecurityInfos)0x80000000);
350                                 else
351                                         securityInfos |= (SecurityInfos)0x20000000;
352                                 dacl = new byte[descriptor.DiscretionaryAcl.BinaryLength];
353                                 descriptor.DiscretionaryAcl.GetBinaryForm (dacl, 0);
354                         }
355                         
356                         if (0 != (includeSections & AccessControlSections.Audit)) {
357                                 if (null != descriptor.SystemAcl) {
358                                         securityInfos |= SecurityInfos.SystemAcl;
359                                         if (AreAuditRulesProtected)
360                                                 securityInfos |= (SecurityInfos)0x40000000;
361                                         else
362                                                 securityInfos |= (SecurityInfos)0x10000000;
363                                         sacl = new byte[descriptor.SystemAcl.BinaryLength];
364                                         descriptor.SystemAcl.GetBinaryForm (sacl, 0);
365                                 }
366                         }
367                         
368                         return nativeCall (securityInfos, owner, group, dacl, sacl);
369                 }
370                 
371                 string Win32FixName (string name)
372                 {
373                         if (ResourceType == ResourceType.RegistryKey) {
374                                 // For (Get|Set)NamedSecurityInfo, registry paths lack the HKEY_ prefix.
375                                 if (!name.StartsWith ("HKEY_")) throw new InvalidOperationException ();
376                                 name = name.Substring ("HKEY_".Length);
377                         }
378                         
379                         return name;
380                 }
381                 #endregion
382                 
383                 #region Win32 P/Invokes
384                 delegate int GetSecurityInfoNativeCall (SecurityInfos securityInfos,
385                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
386                                                         out IntPtr descriptor);
387
388                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityInfo")]
389                 static extern int GetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
390                                                    out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
391                                                    out IntPtr descriptor);
392
393                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetNamedSecurityInfo")]
394                 static extern int GetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
395                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
396                                                         out IntPtr descriptor);
397                                                         
398                 [DllImport ("kernel32.dll", EntryPoint="LocalFree")]
399                 static extern IntPtr LocalFree (IntPtr handle);
400
401                 delegate int SetSecurityInfoNativeCall (SecurityInfos securityInfos,
402                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
403
404                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetSecurityInfo")]
405                 static extern int SetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
406                                                    byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
407                                                 
408                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetNamedSecurityInfo")]
409                 static extern int SetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
410                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
411
412                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityDescriptorLength")]
413                 static extern int GetSecurityDescriptorLength (IntPtr descriptor);
414                 
415                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="IsValidSecurityDescriptor")]
416                 [return: MarshalAs (UnmanagedType.Bool)]
417                 static extern bool IsValidSecurityDescriptor (IntPtr descriptor);
418                 
419                 struct SecurityDescriptor
420                 {
421                         public byte Revision, Size;
422                         public ushort ControlFlags;
423                         public IntPtr Owner, Group, Sacl, Dacl;
424                 }
425                 #endregion
426 #endif
427         }
428 }
429