1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     AcChkSup.c
8 
9 Abstract:
10 
11     This module implements the FAT access checking routine
12 
13 
14 --*/
15 
16 #include "fatprocs.h"
17 
18 //
19 //  Our debug trace level
20 //
21 
22 #define Dbg                              (DEBUG_TRACE_ACCHKSUP)
23 
24 NTSTATUS
25 FatCreateRestrictEveryoneToken(
26     IN PACCESS_TOKEN Token,
27     OUT PACCESS_TOKEN *RestrictedToken
28     );
29 
30 #ifdef ALLOC_PRAGMA
31 #pragma alloc_text(PAGE, FatCheckFileAccess)
32 #pragma alloc_text(PAGE, FatCheckManageVolumeAccess)
33 #pragma alloc_text(PAGE, FatCreateRestrictEveryoneToken)
34 #pragma alloc_text(PAGE, FatExplicitDeviceAccessGranted)
35 #endif
36 
37 
38 BOOLEAN
FatCheckFileAccess(PIRP_CONTEXT IrpContext,IN UCHAR DirentAttributes,IN PACCESS_MASK DesiredAccess)39 FatCheckFileAccess (
40     PIRP_CONTEXT IrpContext,
41     IN UCHAR DirentAttributes,
42     IN PACCESS_MASK DesiredAccess
43     )
44 
45 /*++
46 
47 Routine Description:
48 
49     This routine checks if a desired access is allowed to a file represented
50     by the specified DirentAttriubutes.
51 
52 Arguments:
53 
54     DirentAttributes - Supplies the Dirent attributes to check access for
55 
56     DesiredAccess - Supplies the desired access mask that we are checking for
57 
58 Return Value:
59 
60     BOOLEAN - TRUE if access is allowed and FALSE otherwise
61 
62 --*/
63 
64 {
65     BOOLEAN Result;
66 
67     DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0);
68     DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes);
69     DebugTrace( 0, Dbg, "DesiredAccess    = %8lx\n", *DesiredAccess);
70 
71     PAGED_CODE();
72 
73     //
74     //  This procedures is programmed like a string of filters each
75     //  filter checks to see if some access is allowed,  if it is not allowed
76     //  the filter return FALSE to the user without further checks otherwise
77     //  it moves on to the next filter.  The filter check is to check for
78     //  desired access flags that are not allowed for a particular dirent
79     //
80 
81     Result = TRUE;
82 
83     _SEH2_TRY {
84 
85         //
86         //  Check for Volume ID or Device Dirents, these are not allowed user
87         //  access at all
88         //
89 
90         if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) ||
91             FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) {
92 
93             DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0);
94 
95             try_return( Result = FALSE );
96         }
97 
98         //
99         //  Check the desired access for the object - we only blackball that
100         //  we do not understand.  The model of filesystems using ACLs is that
101         //  they do not type the ACL to the object the ACL is on.  Permissions
102         //  are not checked for consistency vs. the object type - dir/file.
103         //
104 
105         if (FlagOn(*DesiredAccess, ~(DELETE |
106                                      READ_CONTROL |
107                                      WRITE_OWNER |
108                                      WRITE_DAC |
109                                      SYNCHRONIZE |
110                                      ACCESS_SYSTEM_SECURITY |
111                                      FILE_WRITE_DATA |
112                                      FILE_READ_EA |
113                                      FILE_WRITE_EA |
114                                      FILE_READ_ATTRIBUTES |
115                                      FILE_WRITE_ATTRIBUTES |
116                                      FILE_LIST_DIRECTORY |
117                                      FILE_TRAVERSE |
118                                      FILE_DELETE_CHILD |
119                                      FILE_APPEND_DATA |
120                                      MAXIMUM_ALLOWED))) {
121 
122             DebugTrace(0, Dbg, "Cannot open object\n", 0);
123 
124             try_return( Result = FALSE );
125         }
126 
127         //
128         //  Check for a read-only Dirent
129         //
130 
131         if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
132 
133             //
134             //  Check the desired access for a read-only dirent.  AccessMask will contain
135             //  the flags we're going to allow.
136             //
137 
138             ACCESS_MASK AccessMask = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC |
139                                     SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
140                                     FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES |
141                                     FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
142                                     FILE_TRAVERSE;
143 
144             //
145             //  If this is a subdirectory also allow add file/directory and delete.
146             //
147 
148             if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) {
149 
150                 AccessMask |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
151             }
152 
153             if (FlagOn(*DesiredAccess, ~AccessMask)) {
154 
155                 DebugTrace(0, Dbg, "Cannot open readonly\n", 0);
156 
157                 try_return( Result = FALSE );
158             }
159         }
160 
161     try_exit: NOTHING;
162     } _SEH2_FINALLY {
163 
164         DebugUnwind( FatCheckFileAccess );
165 
166         DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result);
167     } _SEH2_END;
168 
169     UNREFERENCED_PARAMETER( IrpContext );
170 
171     return Result;
172 }
173 
174 
175 BOOLEAN
FatCheckManageVolumeAccess(_In_ PIRP_CONTEXT IrpContext,_In_ PACCESS_STATE AccessState,_In_ KPROCESSOR_MODE ProcessorMode)176 FatCheckManageVolumeAccess (
177     _In_ PIRP_CONTEXT IrpContext,
178     _In_ PACCESS_STATE AccessState,
179     _In_ KPROCESSOR_MODE ProcessorMode
180     )
181 
182 /*++
183 
184 Routine Description:
185 
186     This function checks whether the SID described in the input access state has
187     manage volume privilege.
188 
189 Arguments:
190 
191     AccessState - the access state describing the security context to be checked
192 
193     ProcessorMode - the mode this check should occur against
194 
195 Return Value:
196 
197     BOOLEAN - TRUE if privilege is held and FALSE otherwise
198 
199 --*/
200 
201 {
202     PRIVILEGE_SET PrivilegeSet;
203 
204     PAGED_CODE();
205 
206     PrivilegeSet.PrivilegeCount = 1;
207     PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
208     PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid( SE_MANAGE_VOLUME_PRIVILEGE );
209     PrivilegeSet.Privilege[0].Attributes = 0;
210 
211     if (SePrivilegeCheck( &PrivilegeSet,
212                           &AccessState->SubjectSecurityContext,
213                           ProcessorMode )) {
214 
215         return TRUE;
216     }
217 
218     UNREFERENCED_PARAMETER( IrpContext );
219 
220     return FALSE;
221 }
222 
223 
224 NTSTATUS
FatExplicitDeviceAccessGranted(IN PIRP_CONTEXT IrpContext,IN PDEVICE_OBJECT DeviceObject,IN PACCESS_STATE AccessState,IN KPROCESSOR_MODE ProcessorMode)225 FatExplicitDeviceAccessGranted (
226     IN PIRP_CONTEXT IrpContext,
227     IN PDEVICE_OBJECT DeviceObject,
228     IN PACCESS_STATE AccessState,
229     IN KPROCESSOR_MODE ProcessorMode
230     )
231 
232 /*++
233 
234 Routine Description:
235 
236     This function asks whether the SID described in the input access state has
237     been granted any explicit access to the given device object.  It does this
238     by acquiring a token stripped of its ability to acquire access via the
239     Everyone SID and re-doing the access check.
240 
241 Arguments:
242 
243     DeviceObject - the device whose ACL will be checked
244 
245     AccessState - the access state describing the security context to be checked
246 
247     ProcessorMode - the mode this check should occur against
248 
249 Return Value:
250 
251     NTSTATUS - Indicating whether explicit access was granted.
252 
253 --*/
254 
255 {
256     NTSTATUS Status;
257 
258     PACCESS_TOKEN OriginalAccessToken;
259     PACCESS_TOKEN RestrictedAccessToken;
260 
261     PACCESS_TOKEN *EffectiveToken;
262 
263     ACCESS_MASK GrantedAccess;
264 
265     PAGED_CODE();
266 
267     UNREFERENCED_PARAMETER( IrpContext );
268 
269     //
270     //  If the access state indicates that specific access other
271     //  than traverse was acquired, either Everyone does have such
272     //  access or explicit access was granted.  In both cases, we're
273     //  happy to let this proceed.
274     //
275 
276     if (AccessState->PreviouslyGrantedAccess & (SPECIFIC_RIGHTS_ALL ^
277                                                 FILE_TRAVERSE)) {
278 
279         return STATUS_SUCCESS;
280     }
281 
282     //
283     //  If the manage volume privilege is held, this also permits access.
284     //
285 
286     if (FatCheckManageVolumeAccess( IrpContext,
287                                     AccessState,
288                                     ProcessorMode )) {
289 
290         return STATUS_SUCCESS;
291     }
292 
293     //
294     //  Capture the subject context as a prelude to everything below.
295     //
296 
297     SeLockSubjectContext( &AccessState->SubjectSecurityContext );
298 
299     //
300     //  Convert the token in the subject context into one which does not
301     //  acquire access through the Everyone SID.
302     //
303     //  The logic for deciding which token is effective comes from
304     //  SeQuerySubjectContextToken; since there is no natural way
305     //  of getting a pointer to it, do it by hand.
306     //
307 
308     if (ARGUMENT_PRESENT( AccessState->SubjectSecurityContext.ClientToken )) {
309         EffectiveToken = &AccessState->SubjectSecurityContext.ClientToken;
310     } else {
311         EffectiveToken = &AccessState->SubjectSecurityContext.PrimaryToken;
312     }
313 
314     OriginalAccessToken = *EffectiveToken;
315     Status = FatCreateRestrictEveryoneToken( OriginalAccessToken, &RestrictedAccessToken );
316 
317     if (!NT_SUCCESS(Status)) {
318 
319         SeReleaseSubjectContext( &AccessState->SubjectSecurityContext );
320         return Status;
321     }
322 
323     //
324     //  Now see if the resulting context has access to the device through
325     //  its explicitly granted access.  We swap in our restricted token
326     //  for this check as the effective client token.
327     //
328 
329     *EffectiveToken = RestrictedAccessToken;
330 
331 #ifdef _MSC_VER
332 #pragma prefast( suppress: 28175, "we're a file system, this is ok to touch" )
333 #endif
334     SeAccessCheck( DeviceObject->SecurityDescriptor,
335                    &AccessState->SubjectSecurityContext,
336                    FALSE,
337                    AccessState->OriginalDesiredAccess,
338                    0,
339                    NULL,
340                    IoGetFileObjectGenericMapping(),
341                    ProcessorMode,
342                    &GrantedAccess,
343                    &Status );
344 
345     *EffectiveToken = OriginalAccessToken;
346 
347     //
348     //  Cleanup and return.
349     //
350 
351     SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
352     ObDereferenceObject( RestrictedAccessToken );
353 
354     return Status;
355 }
356 
357 
358 NTSTATUS
FatCreateRestrictEveryoneToken(IN PACCESS_TOKEN Token,OUT PACCESS_TOKEN * RestrictedToken)359 FatCreateRestrictEveryoneToken (
360     IN PACCESS_TOKEN Token,
361     OUT PACCESS_TOKEN *RestrictedToken
362     )
363 
364 /*++
365 
366 Routine Description:
367 
368     This function takes a token as the input and returns a new restricted token
369     from which Everyone sid has been disabled.  The resulting token may be used
370     to find out if access is available to a user-sid by explicit means.
371 
372 Arguments:
373 
374     Token - Input token from which Everyone sid needs to be deactivated.
375 
376     RestrictedToken - Receives the the new restricted token.
377         This must be released using ObDereferenceObject(*RestrictedToken);
378 
379 Return Value:
380 
381     NTSTATUS - Returned by SeFilterToken.
382 
383 --*/
384 
385 {
386     //
387     // Array of sids to disable.
388     //
389 
390     TOKEN_GROUPS SidsToDisable;
391 
392     NTSTATUS Status = STATUS_SUCCESS;
393 
394     PAGED_CODE();
395 
396     //
397     //  Restricted token will contain the original sids with one change:
398     //  If Everyone sid is present in the token, it will be marked for DenyOnly.
399     //
400 
401     *RestrictedToken = NULL;
402 
403     //
404     //  Put Everyone sid in the array of sids to disable. This will mark it
405     //  for SE_GROUP_USE_FOR_DENY_ONLY and it'll only be applicable for Deny aces.
406     //
407 
408     SidsToDisable.GroupCount = 1;
409     SidsToDisable.Groups[0].Attributes = 0;
410     SidsToDisable.Groups[0].Sid = SeExports->SeWorldSid;
411 
412     Status = SeFilterToken(
413                  Token,            // Token that needs to be restricted.
414                  0,                // No flags
415                  &SidsToDisable,   // Disable everyone sid
416                  NULL,             // Do not create any restricted sids
417                  NULL,             // Do not delete any privileges
418                  RestrictedToken   // Restricted token
419                  );
420 
421     return Status;
422 }
423 
424