Merge pull request #2282 from joelmartinez/docs-multiassembly-fix
[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 PersistModifications (SafeHandle handle)
134                 {
135                         WriteLock();
136                         try {
137                                 Persist (handle, AccessControlSectionsModified, null);
138                         } finally {
139                                 WriteUnlock ();
140                         }
141                 }
142                 
143                 protected void Persist (SafeHandle handle,
144                                         AccessControlSections includeSections,
145                                         object exceptionContext)
146                 {
147                         WriteLock ();
148                         try {
149                                 RaiseExceptionOnFailure (InternalSet (handle, includeSections), null, handle, exceptionContext);
150                                 AccessControlSectionsModified &= ~includeSections;
151                         } finally {
152                                 WriteUnlock ();
153                         }
154                 }
155
156                 internal void PersistModifications (string name)
157                 {
158                         WriteLock();
159                         try {
160                                 Persist (name, AccessControlSectionsModified, null);
161                         } finally {
162                                 WriteUnlock ();
163                         }
164                 }
165                 
166                 protected void Persist (string name,
167                                         AccessControlSections includeSections,
168                                         object exceptionContext)
169                 {
170                         if (null == name)
171                                 throw new ArgumentNullException ("name");
172
173                         WriteLock ();
174                         try {
175                                 RaiseExceptionOnFailure (InternalSet (name, includeSections), name, null, exceptionContext);
176                                 AccessControlSectionsModified &= ~includeSections;
177                         } finally {
178                                 WriteUnlock ();
179                         }
180                 }
181                 
182                 internal static Exception DefaultExceptionFromErrorCode (int errorCode,
183                                                                          string name, SafeHandle handle,
184                                                                          object context)
185                 {
186                         switch (errorCode) {
187                                 case 2: return new FileNotFoundException ();
188                                 case 3: return new DirectoryNotFoundException ();
189                                 case 5: return new UnauthorizedAccessException ();
190                                 case 1314: return new PrivilegeNotHeldException (); // happens with audit rules
191                                 default: return new InvalidOperationException ("OS error code " + errorCode.ToString());
192                         }
193                 }
194                 
195                 void RaiseExceptionOnFailure (int errorCode, string name, SafeHandle handle, object context)
196                 {
197                         if (errorCode == 0) return;
198                         throw (exception_from_error_code ?? DefaultExceptionFromErrorCode)(errorCode, name, handle, context);
199                 }
200                 
201                 // InternalGet/InternalSet are virtual so that non-Windows platforms which do not share an
202                 // API between files, mutexes, etc. can override in the subclass and do their own thing.
203                 internal virtual int InternalGet (SafeHandle handle,
204                                                   AccessControlSections includeSections)
205                 {
206 #if MOBILE
207                         throw new PlatformNotSupportedException ();
208 #else
209                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
210                                 throw new PlatformNotSupportedException ();
211
212                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
213                                                          out IntPtr owner, out IntPtr group,
214                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
215                                 {
216                                         return GetSecurityInfo (handle, ResourceType, securityInfos,
217                                                                 out owner, out group,
218                                                                 out dacl, out sacl, out descriptor);
219                                 }, includeSections);
220 #endif
221                 }
222                 
223                 internal virtual int InternalGet (string name,
224                                                   AccessControlSections includeSections)
225                 {
226 #if MOBILE
227                         throw new PlatformNotSupportedException ();
228 #else
229                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
230                                 throw new PlatformNotSupportedException ();
231
232                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
233                                                          out IntPtr owner, out IntPtr group,
234                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
235                                 {
236                                         return GetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos,
237                                                                      out owner, out group,
238                                                                      out dacl, out sacl, out descriptor);
239                                 }, includeSections);
240 #endif
241                 }
242                 
243 #if MOBILE
244                 internal virtual int InternalSet (SafeHandle handle, AccessControlSections includeSections)
245                 {
246                         throw new PlatformNotSupportedException ();
247                 }
248
249                 internal virtual int InternalSet (string name, AccessControlSections includeSections)
250                 {
251                         throw new PlatformNotSupportedException ();
252                 }
253 #else
254                 internal virtual int InternalSet (SafeHandle handle,
255                                                   AccessControlSections includeSections)
256                 {
257                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
258                                 throw new PlatformNotSupportedException ();
259
260                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
261                                 SetSecurityInfo (handle, ResourceType, securityInfos, owner, group, dacl, sacl),
262                                 includeSections);
263                 }
264                 
265                 internal virtual int InternalSet (string name,
266                                                   AccessControlSections includeSections)
267                 {       
268                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
269                                 throw new PlatformNotSupportedException ();
270                 
271                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
272                                 SetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos, owner, group, dacl, sacl),
273                                 includeSections);
274                 }
275                 
276                 internal ResourceType ResourceType {
277                         get { return resource_type; }
278                 }
279                 
280                 #region Win32 Details           
281                 int Win32GetHelper (GetSecurityInfoNativeCall nativeCall,
282                                     AccessControlSections includeSections)
283                 {
284                         bool getOwner = 0 != (includeSections & AccessControlSections.Owner);
285                         bool getGroup = 0 != (includeSections & AccessControlSections.Group);
286                         bool getDacl = 0 != (includeSections & AccessControlSections.Access);
287                         bool getSacl = 0 != (includeSections & AccessControlSections.Audit);
288                         
289                         SecurityInfos securityInfos = 0;
290                         if (getOwner) securityInfos |= SecurityInfos.Owner;
291                         if (getGroup) securityInfos |= SecurityInfos.Group;
292                         if (getDacl ) securityInfos |= SecurityInfos.DiscretionaryAcl;
293                         if (getSacl ) securityInfos |= SecurityInfos.SystemAcl;
294                         
295                         IntPtr owner, group, dacl, sacl, descriptor;
296                         int result = nativeCall (securityInfos,
297                                                  out owner, out group, out dacl, out sacl, out descriptor);
298                         if (0 != result) return result;
299                         
300                         try {
301                                 int binaryLength = 0;
302                                 if (IsValidSecurityDescriptor (descriptor))
303                                         binaryLength = GetSecurityDescriptorLength (descriptor);
304                                         
305                                 byte[] binaryForm = new byte[binaryLength];
306                                 Marshal.Copy (descriptor, binaryForm, 0, binaryLength);
307                                 SetSecurityDescriptorBinaryForm (binaryForm, includeSections);
308                         } finally {
309                                 LocalFree (descriptor);
310                         }
311                         return 0;
312                 }
313                 
314                 int Win32SetHelper (SetSecurityInfoNativeCall nativeCall,
315                                     AccessControlSections includeSections)
316                 {
317                         // SE_REGISTRY_KEY will fail UnauthorizedAccessException without this check.
318                         if (AccessControlSections.None == includeSections) return 0;
319                         
320                         SecurityInfos securityInfos = 0;
321                         byte[] owner = null, group = null, dacl = null, sacl = null;
322                         
323                         if (0 != (includeSections & AccessControlSections.Owner)) {
324                                 securityInfos |= SecurityInfos.Owner;
325                                 SecurityIdentifier ownerSid = (SecurityIdentifier)GetOwner (typeof (SecurityIdentifier));
326                                 if (null != ownerSid) {
327                                         owner = new byte[ownerSid.BinaryLength];
328                                         ownerSid.GetBinaryForm (owner, 0);
329                                 }
330                         }
331                         
332                         if (0 != (includeSections & AccessControlSections.Group)) {
333                                 securityInfos |= SecurityInfos.Group;
334                                 SecurityIdentifier groupSid = (SecurityIdentifier)GetGroup (typeof (SecurityIdentifier));
335                                 if (null != groupSid) {
336                                         group = new byte[groupSid.BinaryLength];
337                                         groupSid.GetBinaryForm (group, 0);
338                                 }
339                         }
340                         
341                         if (0 != (includeSections & AccessControlSections.Access)) {
342                                 securityInfos |= SecurityInfos.DiscretionaryAcl;
343                                 if (AreAccessRulesProtected)
344                                         securityInfos |= unchecked((SecurityInfos)0x80000000);
345                                 else
346                                         securityInfos |= (SecurityInfos)0x20000000;
347                                 dacl = new byte[descriptor.DiscretionaryAcl.BinaryLength];
348                                 descriptor.DiscretionaryAcl.GetBinaryForm (dacl, 0);
349                         }
350                         
351                         if (0 != (includeSections & AccessControlSections.Audit)) {
352                                 if (null != descriptor.SystemAcl) {
353                                         securityInfos |= SecurityInfos.SystemAcl;
354                                         if (AreAuditRulesProtected)
355                                                 securityInfos |= (SecurityInfos)0x40000000;
356                                         else
357                                                 securityInfos |= (SecurityInfos)0x10000000;
358                                         sacl = new byte[descriptor.SystemAcl.BinaryLength];
359                                         descriptor.SystemAcl.GetBinaryForm (sacl, 0);
360                                 }
361                         }
362                         
363                         return nativeCall (securityInfos, owner, group, dacl, sacl);
364                 }
365                 
366                 string Win32FixName (string name)
367                 {
368                         if (ResourceType == ResourceType.RegistryKey) {
369                                 // For (Get|Set)NamedSecurityInfo, registry paths lack the HKEY_ prefix.
370                                 if (!name.StartsWith ("HKEY_")) throw new InvalidOperationException ();
371                                 name = name.Substring ("HKEY_".Length);
372                         }
373                         
374                         return name;
375                 }
376                 #endregion
377                 
378                 #region Win32 P/Invokes
379                 delegate int GetSecurityInfoNativeCall (SecurityInfos securityInfos,
380                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
381                                                         out IntPtr descriptor);
382
383                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityInfo")]
384                 static extern int GetSecurityInfo (SafeHandle handle, ResourceType resourceType, 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="GetNamedSecurityInfo")]
389                 static extern int GetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
390                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
391                                                         out IntPtr descriptor);
392                                                         
393                 [DllImport ("kernel32.dll", EntryPoint="LocalFree")]
394                 static extern IntPtr LocalFree (IntPtr handle);
395
396                 delegate int SetSecurityInfoNativeCall (SecurityInfos securityInfos,
397                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
398
399                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetSecurityInfo")]
400                 static extern int SetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
401                                                    byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
402                                                 
403                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetNamedSecurityInfo")]
404                 static extern int SetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
405                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
406
407                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityDescriptorLength")]
408                 static extern int GetSecurityDescriptorLength (IntPtr descriptor);
409                 
410                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="IsValidSecurityDescriptor")]
411                 [return: MarshalAs (UnmanagedType.Bool)]
412                 static extern bool IsValidSecurityDescriptor (IntPtr descriptor);
413                 
414                 struct SecurityDescriptor
415                 {
416                         public byte Revision, Size;
417                         public ushort ControlFlags;
418                         public IntPtr Owner, Group, Sacl, Dacl;
419                 }
420                 #endregion
421 #endif
422         }
423 }
424