[FASTFAT_NEW] Import again FastFAT from MS. This time from GitHub for license reasons.
[reactos.git] / drivers / filesystems / fastfat_new / acchksup.c
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 \f
38 BOOLEAN
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
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 \f
224 NTSTATUS
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 \f
358 NTSTATUS
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