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