[HttpConnection] Bug fix: HttpListener's "IgnoreWriteExceptions" property value is...
[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 (Environment.OSVersion.Platform != PlatformID.Win32NT)
207                                 throw new PlatformNotSupportedException ();
208
209                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
210                                                          out IntPtr owner, out IntPtr group,
211                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
212                                 {
213                                         return GetSecurityInfo (handle, ResourceType, securityInfos,
214                                                                 out owner, out group,
215                                                                 out dacl, out sacl, out descriptor);
216                                 }, includeSections);
217                 }
218                 
219                 internal virtual int InternalGet (string name,
220                                                   AccessControlSections includeSections)
221                 {
222                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
223                                 throw new PlatformNotSupportedException ();
224
225                         return Win32GetHelper (delegate (SecurityInfos securityInfos,
226                                                          out IntPtr owner, out IntPtr group,
227                                                          out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
228                                 {
229                                         return GetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos,
230                                                                      out owner, out group,
231                                                                      out dacl, out sacl, out descriptor);
232                                 }, includeSections);
233                 }
234                 
235                 internal virtual int InternalSet (SafeHandle handle,
236                                                   AccessControlSections includeSections)
237                 {
238                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
239                                 throw new PlatformNotSupportedException ();
240
241                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
242                                 SetSecurityInfo (handle, ResourceType, securityInfos, owner, group, dacl, sacl),
243                                 includeSections);
244                 }
245                 
246                 internal virtual int InternalSet (string name,
247                                                   AccessControlSections includeSections)
248                 {       
249                         if (Environment.OSVersion.Platform != PlatformID.Win32NT)
250                                 throw new PlatformNotSupportedException ();
251                 
252                         return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
253                                 SetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos, owner, group, dacl, sacl),
254                                 includeSections);
255                 }
256                 
257                 internal ResourceType ResourceType {
258                         get { return resource_type; }
259                 }
260                 
261                 #region Win32 Details           
262                 int Win32GetHelper (GetSecurityInfoNativeCall nativeCall,
263                                     AccessControlSections includeSections)
264                 {
265                         bool getOwner = 0 != (includeSections & AccessControlSections.Owner);
266                         bool getGroup = 0 != (includeSections & AccessControlSections.Group);
267                         bool getDacl = 0 != (includeSections & AccessControlSections.Access);
268                         bool getSacl = 0 != (includeSections & AccessControlSections.Audit);
269                         
270                         SecurityInfos securityInfos = 0;
271                         if (getOwner) securityInfos |= SecurityInfos.Owner;
272                         if (getGroup) securityInfos |= SecurityInfos.Group;
273                         if (getDacl ) securityInfos |= SecurityInfos.DiscretionaryAcl;
274                         if (getSacl ) securityInfos |= SecurityInfos.SystemAcl;
275                         
276                         IntPtr owner, group, dacl, sacl, descriptor;
277                         int result = nativeCall (securityInfos,
278                                                  out owner, out group, out dacl, out sacl, out descriptor);
279                         if (0 != result) return result;
280                         
281                         try {
282                                 int binaryLength = 0;
283                                 if (IsValidSecurityDescriptor (descriptor))
284                                         binaryLength = GetSecurityDescriptorLength (descriptor);
285                                         
286                                 byte[] binaryForm = new byte[binaryLength];
287                                 Marshal.Copy (descriptor, binaryForm, 0, binaryLength);
288                                 SetSecurityDescriptorBinaryForm (binaryForm, includeSections);
289                         } finally {
290                                 LocalFree (descriptor);
291                         }
292                         return 0;
293                 }
294                 
295                 int Win32SetHelper (SetSecurityInfoNativeCall nativeCall,
296                                     AccessControlSections includeSections)
297                 {
298                         // SE_REGISTRY_KEY will fail UnauthorizedAccessException without this check.
299                         if (AccessControlSections.None == includeSections) return 0;
300                         
301                         SecurityInfos securityInfos = 0;
302                         byte[] owner = null, group = null, dacl = null, sacl = null;
303                         
304                         if (0 != (includeSections & AccessControlSections.Owner)) {
305                                 securityInfos |= SecurityInfos.Owner;
306                                 SecurityIdentifier ownerSid = (SecurityIdentifier)GetOwner (typeof (SecurityIdentifier));
307                                 if (null != ownerSid) {
308                                         owner = new byte[ownerSid.BinaryLength];
309                                         ownerSid.GetBinaryForm (owner, 0);
310                                 }
311                         }
312                         
313                         if (0 != (includeSections & AccessControlSections.Group)) {
314                                 securityInfos |= SecurityInfos.Group;
315                                 SecurityIdentifier groupSid = (SecurityIdentifier)GetGroup (typeof (SecurityIdentifier));
316                                 if (null != groupSid) {
317                                         group = new byte[groupSid.BinaryLength];
318                                         groupSid.GetBinaryForm (group, 0);
319                                 }
320                         }
321                         
322                         if (0 != (includeSections & AccessControlSections.Access)) {
323                                 securityInfos |= SecurityInfos.DiscretionaryAcl;
324                                 if (AreAccessRulesProtected)
325                                         securityInfos |= unchecked((SecurityInfos)0x80000000);
326                                 else
327                                         securityInfos |= (SecurityInfos)0x20000000;
328                                 dacl = new byte[descriptor.DiscretionaryAcl.BinaryLength];
329                                 descriptor.DiscretionaryAcl.GetBinaryForm (dacl, 0);
330                         }
331                         
332                         if (0 != (includeSections & AccessControlSections.Audit)) {
333                                 if (null != descriptor.SystemAcl) {
334                                         securityInfos |= SecurityInfos.SystemAcl;
335                                         if (AreAuditRulesProtected)
336                                                 securityInfos |= (SecurityInfos)0x40000000;
337                                         else
338                                                 securityInfos |= (SecurityInfos)0x10000000;
339                                         sacl = new byte[descriptor.SystemAcl.BinaryLength];
340                                         descriptor.SystemAcl.GetBinaryForm (sacl, 0);
341                                 }
342                         }
343                         
344                         return nativeCall (securityInfos, owner, group, dacl, sacl);
345                 }
346                 
347                 string Win32FixName (string name)
348                 {
349                         if (ResourceType == ResourceType.RegistryKey) {
350                                 // For (Get|Set)NamedSecurityInfo, registry paths lack the HKEY_ prefix.
351                                 if (!name.StartsWith ("HKEY_")) throw new InvalidOperationException ();
352                                 name = name.Substring ("HKEY_".Length);
353                         }
354                         
355                         return name;
356                 }
357                 #endregion
358                 
359                 #region Win32 P/Invokes
360                 delegate int GetSecurityInfoNativeCall (SecurityInfos securityInfos,
361                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
362                                                         out IntPtr descriptor);
363
364                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityInfo")]
365                 static extern int GetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
366                                                    out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
367                                                    out IntPtr descriptor);
368
369                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetNamedSecurityInfo")]
370                 static extern int GetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
371                                                         out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
372                                                         out IntPtr descriptor);
373                                                         
374                 [DllImport ("kernel32.dll", EntryPoint="LocalFree")]
375                 static extern IntPtr LocalFree (IntPtr handle);
376
377                 delegate int SetSecurityInfoNativeCall (SecurityInfos securityInfos,
378                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
379
380                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetSecurityInfo")]
381                 static extern int SetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
382                                                    byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
383                                                 
384                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetNamedSecurityInfo")]
385                 static extern int SetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
386                                                         byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
387
388                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityDescriptorLength")]
389                 static extern int GetSecurityDescriptorLength (IntPtr descriptor);
390                 
391                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="IsValidSecurityDescriptor")]
392                 [return: MarshalAs (UnmanagedType.Bool)]
393                 static extern bool IsValidSecurityDescriptor (IntPtr descriptor);
394                 
395                 struct SecurityDescriptor
396                 {
397                         public byte Revision, Size;
398                         public ushort ControlFlags;
399                         public IntPtr Owner, Group, Sacl, Dacl;
400                 }
401                 #endregion
402         }
403 }
404