summaryrefslogtreecommitdiff
path: root/filesys/fastfat/acchksup.c
blob: af6c2f1429340afeadc4985ce9769e8846d06eb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    AcChkSup.c

Abstract:

    This module implements the FAT access checking routine


--*/

#include "FatProcs.h"

//
//  Our debug trace level
//

#define Dbg                              (DEBUG_TRACE_ACCHKSUP)

NTSTATUS
FatCreateRestrictEveryoneToken(
    IN PACCESS_TOKEN Token,
    OUT PACCESS_TOKEN *RestrictedToken
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCheckFileAccess)
#pragma alloc_text(PAGE, FatCheckManageVolumeAccess)
#pragma alloc_text(PAGE, FatCreateRestrictEveryoneToken)
#pragma alloc_text(PAGE, FatExplicitDeviceAccessGranted)
#endif


BOOLEAN
FatCheckFileAccess (
    PIRP_CONTEXT IrpContext,
    IN UCHAR DirentAttributes,
    IN PACCESS_MASK DesiredAccess
    )

/*++

Routine Description:

    This routine checks if a desired access is allowed to a file represented
    by the specified DirentAttriubutes.

Arguments:

    DirentAttributes - Supplies the Dirent attributes to check access for

    DesiredAccess - Supplies the desired access mask that we are checking for

Return Value:

    BOOLEAN - TRUE if access is allowed and FALSE otherwise

--*/

{
    BOOLEAN Result;

    DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0);
    DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes);
    DebugTrace( 0, Dbg, "DesiredAccess    = %8lx\n", *DesiredAccess);

    PAGED_CODE();

    //
    //  This procedures is programmed like a string of filters each
    //  filter checks to see if some access is allowed,  if it is not allowed
    //  the filter return FALSE to the user without further checks otherwise
    //  it moves on to the next filter.  The filter check is to check for
    //  desired access flags that are not allowed for a particular dirent
    //

    Result = TRUE;

    try {

        //
        //  Check for Volume ID or Device Dirents, these are not allowed user
        //  access at all
        //

        if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) ||
            FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) {

            DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0);

            try_return( Result = FALSE );
        }

        //
        //  Check the desired access for the object - we only blackball that
        //  we do not understand.  The model of filesystems using ACLs is that
        //  they do not type the ACL to the object the ACL is on.  Permissions
        //  are not checked for consistency vs. the object type - dir/file.
        //

        if (FlagOn(*DesiredAccess, ~(DELETE |
                                     READ_CONTROL |
                                     WRITE_OWNER |
                                     WRITE_DAC |
                                     SYNCHRONIZE |
                                     ACCESS_SYSTEM_SECURITY |
                                     FILE_WRITE_DATA |
                                     FILE_READ_EA |
                                     FILE_WRITE_EA |
                                     FILE_READ_ATTRIBUTES |
                                     FILE_WRITE_ATTRIBUTES |
                                     FILE_LIST_DIRECTORY |
                                     FILE_TRAVERSE |
                                     FILE_DELETE_CHILD |
                                     FILE_APPEND_DATA |
                                     MAXIMUM_ALLOWED))) {

            DebugTrace(0, Dbg, "Cannot open object\n", 0);

            try_return( Result = FALSE );
        }

        //
        //  Check for a read-only Dirent
        //

        if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {

            //
            //  Check the desired access for a read-only dirent.  AccessMask will contain
            //  the flags we're going to allow.
            //

            ACCESS_MASK AccessMask = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC |
                                    SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
                                    FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES |
                                    FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
                                    FILE_TRAVERSE;

            //
            //  If this is a subdirectory also allow add file/directory and delete.
            //
            
            if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) {

                AccessMask |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
            }
            
            if (FlagOn(*DesiredAccess, ~AccessMask)) {

                DebugTrace(0, Dbg, "Cannot open readonly\n", 0);

                try_return( Result = FALSE );
            }
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCheckFileAccess );

        DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result);
    }

    UNREFERENCED_PARAMETER( IrpContext );

    return Result;
}


BOOLEAN
FatCheckManageVolumeAccess (
    _In_ PIRP_CONTEXT IrpContext,
    _In_ PACCESS_STATE AccessState,
    _In_ KPROCESSOR_MODE ProcessorMode
    )

/*++

Routine Description:

    This function checks whether the SID described in the input access state has
    manage volume privilege.

Arguments:

    AccessState - the access state describing the security context to be checked
    
    ProcessorMode - the mode this check should occur against

Return Value:

    BOOLEAN - TRUE if privilege is held and FALSE otherwise

--*/

{
    PRIVILEGE_SET PrivilegeSet;

    PAGED_CODE();

    PrivilegeSet.PrivilegeCount = 1;
    PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
    PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid( SE_MANAGE_VOLUME_PRIVILEGE );
    PrivilegeSet.Privilege[0].Attributes = 0;

    if (SePrivilegeCheck( &PrivilegeSet,
                          &AccessState->SubjectSecurityContext,
                          ProcessorMode )) {

        return TRUE;
    }

    UNREFERENCED_PARAMETER( IrpContext );

    return FALSE;
}


NTSTATUS
FatExplicitDeviceAccessGranted (
    IN PIRP_CONTEXT IrpContext,
    IN PDEVICE_OBJECT DeviceObject,
    IN PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE ProcessorMode
    )

/*++

Routine Description:

    This function asks whether the SID described in the input access state has
    been granted any explicit access to the given device object.  It does this
    by acquiring a token stripped of its ability to acquire access via the
    Everyone SID and re-doing the access check.

Arguments:

    DeviceObject - the device whose ACL will be checked
    
    AccessState - the access state describing the security context to be checked
    
    ProcessorMode - the mode this check should occur against

Return Value:

    NTSTATUS - Indicating whether explicit access was granted.

--*/

{
    NTSTATUS Status;

    PACCESS_TOKEN OriginalAccessToken;
    PACCESS_TOKEN RestrictedAccessToken;
    
    PACCESS_TOKEN *EffectiveToken;
    
    ACCESS_MASK GrantedAccess;

    PAGED_CODE();

    UNREFERENCED_PARAMETER( IrpContext );

    //
    //  If the access state indicates that specific access other
    //  than traverse was acquired, either Everyone does have such
    //  access or explicit access was granted.  In both cases, we're
    //  happy to let this proceed.
    //

    if (AccessState->PreviouslyGrantedAccess & (SPECIFIC_RIGHTS_ALL ^
                                                FILE_TRAVERSE)) {

        return STATUS_SUCCESS;
    }

    //
    //  If the manage volume privilege is held, this also permits access.
    //

    if (FatCheckManageVolumeAccess( IrpContext,
                                    AccessState,
                                    ProcessorMode )) {

        return STATUS_SUCCESS;
    }

    //
    //  Capture the subject context as a prelude to everything below.
    //

    SeLockSubjectContext( &AccessState->SubjectSecurityContext );
    
    //
    //  Convert the token in the subject context into one which does not
    //  acquire access through the Everyone SID.
    //
    //  The logic for deciding which token is effective comes from
    //  SeQuerySubjectContextToken; since there is no natural way
    //  of getting a pointer to it, do it by hand.
    //
    
    if (ARGUMENT_PRESENT( AccessState->SubjectSecurityContext.ClientToken )) {
        EffectiveToken = &AccessState->SubjectSecurityContext.ClientToken;
    } else {
        EffectiveToken = &AccessState->SubjectSecurityContext.PrimaryToken;
    }

    OriginalAccessToken = *EffectiveToken;
    Status = FatCreateRestrictEveryoneToken( OriginalAccessToken, &RestrictedAccessToken );

    if (!NT_SUCCESS(Status)) {
        
        SeReleaseSubjectContext( &AccessState->SubjectSecurityContext );
        return Status;
    }

    //
    //  Now see if the resulting context has access to the device through
    //  its explicitly granted access.  We swap in our restricted token
    //  for this check as the effective client token.
    //

    *EffectiveToken = RestrictedAccessToken;
    
#pragma prefast( suppress: 28175, "we're a file system, this is ok to touch" )
    SeAccessCheck( DeviceObject->SecurityDescriptor,
                   &AccessState->SubjectSecurityContext,
                   FALSE,
                   AccessState->OriginalDesiredAccess,
                   0,
                   NULL,
                   IoGetFileObjectGenericMapping(),
                   ProcessorMode,
                   &GrantedAccess,
                   &Status );
    
    *EffectiveToken = OriginalAccessToken;
    
    //
    //  Cleanup and return.
    //

    SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
    ObDereferenceObject( RestrictedAccessToken );

    return Status;
}


NTSTATUS
FatCreateRestrictEveryoneToken (
    IN PACCESS_TOKEN Token,
    OUT PACCESS_TOKEN *RestrictedToken
    )

/*++

Routine Description:

    This function takes a token as the input and returns a new restricted token
    from which Everyone sid has been disabled.  The resulting token may be used
    to find out if access is available to a user-sid by explicit means.

Arguments:

    Token - Input token from which Everyone sid needs to be deactivated.

    RestrictedToken - Receives the the new restricted token.
        This must be released using ObDereferenceObject(*RestrictedToken);

Return Value:

    NTSTATUS - Returned by SeFilterToken.

--*/

{
    //
    // Array of sids to disable.
    //

    TOKEN_GROUPS SidsToDisable;

    NTSTATUS Status = STATUS_SUCCESS;

    PAGED_CODE();

    //
    //  Restricted token will contain the original sids with one change:
    //  If Everyone sid is present in the token, it will be marked for DenyOnly.
    //

    *RestrictedToken = NULL;

    //
    //  Put Everyone sid in the array of sids to disable. This will mark it
    //  for SE_GROUP_USE_FOR_DENY_ONLY and it'll only be applicable for Deny aces.
    //

    SidsToDisable.GroupCount = 1;
    SidsToDisable.Groups[0].Attributes = 0;
    SidsToDisable.Groups[0].Sid = SeExports->SeWorldSid;

    Status = SeFilterToken(
                 Token,            // Token that needs to be restricted.
                 0,                // No flags
                 &SidsToDisable,   // Disable everyone sid
                 NULL,             // Do not create any restricted sids
                 NULL,             // Do not delete any privileges
                 RestrictedToken   // Restricted token
                 );

    return Status;
}