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 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 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 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 424