1/* 2 * ntfs_secure.c - NTFS kernel security ($Secure) handling. 3 * 4 * Copyright (c) 2006-2011 Anton Altaparmakov. All Rights Reserved. 5 * Portions Copyright (c) 2006-2011 Apple Inc. All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from this 17 * software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ALTERNATIVELY, provided that this notice and licensing terms are retained in 31 * full, this file may be redistributed and/or modified under the terms of the 32 * GNU General Public License (GPL) Version 2, in which case the provisions of 33 * that version of the GPL will apply to you instead of the license terms 34 * above. You can obtain a copy of the GPL Version 2 at 35 * http://developer.apple.com/opensource/licenses/gpl-2.txt. 36 */ 37 38#include <sys/errno.h> 39#include <sys/stat.h> 40#include <sys/ucred.h> 41#include <sys/vnode.h> 42 43#include <string.h> 44 45#include <libkern/OSMalloc.h> 46 47#include <kern/debug.h> 48#include <kern/locks.h> 49 50#include "ntfs.h" 51#include "ntfs_attr.h" 52#include "ntfs_debug.h" 53#include "ntfs_endian.h" 54#include "ntfs_inode.h" 55#include "ntfs_mft.h" 56#include "ntfs_page.h" 57#include "ntfs_types.h" 58#include "ntfs_layout.h" 59#include "ntfs_secure.h" 60#include "ntfs_volume.h" 61 62/* 63 * Default $SDS entries containing the default security descriptors to apply to 64 * newly created files (ntfs_file_sds_entry) and directories 65 * (ntfs_dir_sds_entry). 66 * 67 * On ntfs 1.x volumes, use the ntfs_file_sds_entry_old as the default for 68 * files and ntfs_dir_sds_entry_old for directories. 69 */ 70SDS_ENTRY *ntfs_file_sds_entry, *ntfs_dir_sds_entry; 71SDS_ENTRY *ntfs_file_sds_entry_old, *ntfs_dir_sds_entry_old; 72 73/** 74 * ntfs_default_sds_entry_init - set up one of the default SDS entries 75 * @sds: destination buffer for SDS entry 76 * @dir: if true, create the default directory SDS entry 77 * @old: if true, create an NT4 style SDS entry 78 * 79 * Set up one of the default SDS entries in the destination buffer @sds. The 80 * generated security descriptor specifies the "Everyone" SID as both the owner 81 * and the group and grants full access to the "Everyone" SID. 82 * 83 * If @dir is true, create the default directory SDS entry. If @dir is false, 84 * create the default file SDS entry. The difference is that the directory SDS 85 * entry contains an ACE which specifies that directories and files created 86 * inside the directory to which the SDS entry applies will inherit the same 87 * ACE and that the ACE will be propagated indefinitely into files and 88 * sub-directories of directories, etc. 89 * 90 * If @old is true, create an NT4 style SDS entry. If @old is false, create a 91 * Win2k+ style SDS entry. The difference is that NT4 did not have the same 92 * ACE inheritance rules so we modify the security descriptor to suit. 93 */ 94static void ntfs_default_sds_entry_init(SDS_ENTRY *sds, BOOL dir, BOOL old) 95{ 96 SECURITY_DESCRIPTOR_RELATIVE *sd; 97 ACL *dacl; 98 ACCESS_ALLOWED_ACE *ace; 99 SID *sid; 100 u32 sd_len; 101 102 sd = &sds->sd; 103 sd->revision = SECURITY_DESCRIPTOR_REVISION; 104 sd->control = SE_SELF_RELATIVE | SE_DACL_PROTECTED | 105 SE_DACL_AUTO_INHERITED | SE_DACL_PRESENT; 106 if (old) 107 sd->control &= ~(SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED); 108 sd->dacl = const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); 109 /* 110 * Create a discretionary ACL containing a single ACE granting all 111 * access to the universally well-known SID WORLD_SID (S-1-1-0), i.e. 112 * the "Everyone" SID. 113 */ 114 dacl = (ACL*)((u8*)sd + le32_to_cpu(sd->dacl)); 115 dacl->revision = ACL_REVISION; 116 dacl->ace_count = const_cpu_to_le16(1); 117 ace = (ACCESS_ALLOWED_ACE*)((u8*)dacl + sizeof(ACL)); 118 ace->type = ACCESS_ALLOWED_ACE_TYPE; 119 /* 120 * If this is a directory, we want the ACE to be inherited both to the 121 * directory itself and to its sub-directories and files as well as to 122 * the sub-directories and files of the sub-directories, etc. 123 */ 124 ace->flags = dir ? (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) : 0; 125 ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); 126 ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | 127 FILE_READ_ATTRIBUTES | FILE_DELETE_CHILD | 128 FILE_EXECUTE | FILE_WRITE_EA | FILE_READ_EA | 129 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA; 130 sid = &ace->sid; 131 sid->revision = SID_REVISION; 132 sid->sub_authority_count = 1; 133 /* SECURITY_WORLD_SID_AUTHORITY */ 134 sid->identifier_authority.value[0] = 0; 135 sid->identifier_authority.value[1] = 0; 136 sid->identifier_authority.value[2] = 0; 137 sid->identifier_authority.value[3] = 0; 138 sid->identifier_authority.value[4] = 0; 139 sid->identifier_authority.value[5] = 1; 140 sid->sub_authority[0] = SECURITY_WORLD_RID; 141 dacl->size = const_cpu_to_le16(sizeof(ACL) + le16_to_cpu(ace->size)); 142 /* The owner of the file/directory is "Everyone". */ 143 sd->owner = cpu_to_le32(le32_to_cpu(sd->dacl) + 144 le16_to_cpu(dacl->size)); 145 memcpy((u8*)sd + le32_to_cpu(sd->owner), sid, sizeof(SID)); 146 /* The group of the file/directory is "Everyone", too. */ 147 sd->group = cpu_to_le32(le32_to_cpu(sd->owner) + sizeof(SID)); 148 memcpy((u8*)sd + le32_to_cpu(sd->group), sid, sizeof(SID)); 149 sd_len = le32_to_cpu(sd->group) + sizeof(SID); 150 sds->hash = ntfs_security_hash(sd, sd_len); 151 sds->length = cpu_to_le32(sizeof(SDS_ENTRY_HEADER) + sd_len); 152} 153 154/** 155 * ntfs_default_sds_entries_init - set up the default SDS entries 156 * 157 * Set up one of the default SDS entries for the ntfs driver. 158 * 159 * Return 0 on success and ENOMEM if not enough memory was available to 160 * allocate the buffer needed for storing the default SDS entries. 161 */ 162errno_t ntfs_default_sds_entries_init(void) 163{ 164 SDS_ENTRY *sds; 165 166 ntfs_debug("Entering."); 167 /* 168 * 0x60 is the size of each SDS entry we create below, aligned to the 169 * next 16-byte boundary. The actual size is 0x5c bytes. We hard code 170 * this here as we otherwise would not know the size until we have 171 * generated the SDS entry. 172 */ 173 sds = OSMalloc(0x60 * 4, ntfs_malloc_tag); 174 if (!sds) { 175 ntfs_error(NULL, "Failed to allocate memory for the default " 176 "$Secure/$DATA/$SDS entries."); 177 return ENOMEM; 178 } 179 ntfs_file_sds_entry = sds; 180 ntfs_default_sds_entry_init(sds, FALSE, FALSE); 181 sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15)); 182 ntfs_dir_sds_entry = sds; 183 ntfs_default_sds_entry_init(sds, TRUE, FALSE); 184 sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15)); 185 ntfs_file_sds_entry_old = sds; 186 ntfs_default_sds_entry_init(sds, FALSE, TRUE); 187 sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15)); 188 ntfs_dir_sds_entry_old = sds; 189 ntfs_default_sds_entry_init(sds, TRUE, TRUE); 190 ntfs_debug("Done."); 191 return 0; 192} 193 194/** 195 * ntfs_next_security_id_init - determine the next security_id to use 196 * @vol: volume for which to determine the next security_id 197 * @next_security_id: destination in which to return the next security_id 198 * 199 * Scan the $SII index of the $Secure system file and determine the security_id 200 * to use the next time a new security descriptor is added to $Secure. 201 * 202 * Return the next security_id in *@next_security_id. 203 * 204 * Return 0 on success and errno on error in which case *@next_security_id is 205 * undefined. 206 */ 207errno_t ntfs_next_security_id_init(ntfs_volume *vol, le32 *next_security_id) 208{ 209 VCN vcn, old_vcn; 210 ntfs_inode *base_ni, *ni; 211 MFT_RECORD *m; 212 INDEX_ROOT *ir; 213 INDEX_ENTRY *ie, *prev_ie; 214 INDEX_ALLOCATION *ia; 215 upl_t upl; 216 upl_page_info_array_t pl; 217 u8 *index_end, *kaddr; 218 ntfs_attr_search_ctx *actx; 219 errno_t err; 220 221 ntfs_debug("Entering."); 222 base_ni = vol->secure_ni; 223 if (!base_ni) 224 panic("%s(): !vol->secure_ni\n", __FUNCTION__); 225 ni = vol->secure_sii_ni; 226 if (!ni) 227 panic("%s(): !vol->secure_sii_ni\n", __FUNCTION__); 228 err = vnode_get(ni->vn); 229 if (err) { 230 ntfs_error(vol->mp, "Failed to get vnode for " 231 "$Secure/$INDEX_ALLOCATION/$SII."); 232 return err; 233 } 234 lck_rw_lock_shared(&ni->lock); 235 /* Get hold of the mft record for $Secure. */ 236 err = ntfs_mft_record_map(base_ni, &m); 237 if (err) { 238 ntfs_error(vol->mp, "Failed to map mft record of $Secure " 239 "(error %d).", err); 240 m = NULL; 241 actx = NULL; 242 goto err; 243 } 244 actx = ntfs_attr_search_ctx_get(base_ni, m); 245 if (!actx) { 246 err = ENOMEM; 247 goto err; 248 } 249 /* Find the index root attribute in the mft record. */ 250 err = ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len, 0, NULL, 251 0, actx); 252 if (err) { 253 if (err == ENOENT) { 254 ntfs_error(vol->mp, "$SII index root attribute " 255 "missing in $Secure."); 256 err = EIO; 257 } 258 goto err; 259 } 260 /* 261 * Get to the index root value (it has been verified when the inode was 262 * read in ntfs_{,index_}inode_read). 263 */ 264 ir = (INDEX_ROOT*)((u8*)actx->a + le16_to_cpu(actx->a->value_offset)); 265 index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); 266 /* The first index entry. */ 267 ie = (INDEX_ENTRY*)((u8*)&ir->index + 268 le32_to_cpu(ir->index.entries_offset)); 269 /* 270 * Loop until we exceed valid memory (corruption case) or until we 271 * reach the last entry, always storing the previous entry so that when 272 * we reach the last entry we can go back one to find the last 273 * security_id. 274 */ 275 prev_ie = NULL; 276 for (;; prev_ie = ie, ie = (INDEX_ENTRY*)((u8*)ie + 277 le16_to_cpu(ie->length))) { 278 /* Bounds checks. */ 279 if ((u8*)ie < (u8*)&ir->index || (u8*)ie + 280 sizeof(INDEX_ENTRY_HEADER) > index_end || 281 (u8*)ie + le16_to_cpu(ie->length) > index_end || 282 (u32)sizeof(INDEX_ENTRY_HEADER) + 283 le16_to_cpu(ie->key_length) > 284 le16_to_cpu(ie->length)) 285 goto idx_err; 286 /* 287 * The last entry cannot contain a key. It can however contain 288 * a pointer to a child node in the B+tree so we just break out. 289 */ 290 if (ie->flags & INDEX_ENTRY_END) 291 break; 292 /* Further bounds checks. */ 293 if ((u32)sizeof(INDEX_ENTRY_HEADER) + 294 le16_to_cpu(ie->key_length) > 295 le16_to_cpu(ie->data_offset) || 296 (u32)le16_to_cpu(ie->data_offset) + 297 le16_to_cpu(ie->data_length) > 298 le16_to_cpu(ie->length)) 299 goto idx_err; 300 } 301 /* 302 * We have finished with this index. Check for the presence of a child 303 * node and if not present, we have the last security_id in @prev_ie, 304 * or if that is NULL there are no security_ids on the volume so just 305 * start at 0x100. 306 */ 307 if (!(ie->flags & INDEX_ENTRY_NODE)) { 308 if (prev_ie) 309 *next_security_id = cpu_to_le32(le32_to_cpu( 310 prev_ie->key.sii.security_id) + 1); 311 else 312 *next_security_id = const_cpu_to_le32(0x100); 313 ntfs_attr_search_ctx_put(actx); 314 ntfs_mft_record_unmap(base_ni); 315 lck_rw_unlock_shared(&ni->lock); 316 (void)vnode_put(ni->vn); 317 ntfs_debug("Found next security_id 0x%x in index root.", 318 (unsigned)le32_to_cpu(*next_security_id)); 319 return 0; 320 } /* Child node present, descend into it. */ 321 /* Consistency check: Verify that an index allocation exists. */ 322 if (!NInoIndexAllocPresent(ni)) { 323 ntfs_error(vol->mp, "No index allocation attribute but index " 324 "entry requires one. $Secure is corrupt or " 325 "driver bug."); 326 goto err; 327 } 328 /* Get the starting vcn of the index_block holding the child node. */ 329 vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8)); 330 /* 331 * We are done with the index root and the mft record. Release them, 332 * otherwise we would deadlock with ntfs_page_map(). 333 */ 334 ntfs_attr_search_ctx_put(actx); 335 ntfs_mft_record_unmap(base_ni); 336 m = NULL; 337 actx = NULL; 338descend_into_child_node: 339 /* 340 * Convert vcn to byte offset in the index allocation attribute and map 341 * the corresponding page. 342 */ 343 err = ntfs_page_map(ni, (vcn << ni->vcn_size_shift) & 344 ~PAGE_MASK_64, &upl, &pl, &kaddr, FALSE); 345 if (err) { 346 ntfs_error(vol->mp, "Failed to map index page, error %d.", 347 err); 348 goto err; 349 } 350fast_descend_into_child_node: 351 /* Get to the index allocation block. */ 352 ia = (INDEX_ALLOCATION*)(kaddr + ((vcn << ni->vcn_size_shift) & 353 PAGE_MASK)); 354 /* Bounds checks. */ 355 if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_SIZE) { 356 ntfs_error(vol->mp, "Out of bounds check failed. $Secure is " 357 "corrupt or driver bug."); 358 goto page_err; 359 } 360 /* Catch multi sector transfer fixup errors. */ 361 if (!ntfs_is_indx_record(ia->magic)) { 362 ntfs_error(vol->mp, "Index record with vcn 0x%llx is " 363 "corrupt. $Secure is corrupt. Run chkdsk.", 364 (unsigned long long)vcn); 365 goto page_err; 366 } 367 if (sle64_to_cpu(ia->index_block_vcn) != vcn) { 368 ntfs_error(vol->mp, "Actual VCN (0x%llx) of index buffer is " 369 "different from expected VCN (0x%llx). " 370 "$Secure is corrupt or driver bug.", 371 (unsigned long long) 372 sle64_to_cpu(ia->index_block_vcn), 373 (unsigned long long)vcn); 374 goto page_err; 375 } 376 if (offsetof(INDEX_BLOCK, index) + 377 le32_to_cpu(ia->index.allocated_size) != 378 ni->block_size) { 379 ntfs_error(vol->mp, "Index buffer (VCN 0x%llx) of $Secure has " 380 "a size (%u) differing from the index " 381 "specified size (%u). $Secure is corrupt or " 382 "driver bug.", (unsigned long long)vcn, 383 (unsigned)(offsetof(INDEX_BLOCK, index) + 384 le32_to_cpu(ia->index.allocated_size)), 385 (unsigned)ni->block_size); 386 goto page_err; 387 } 388 index_end = (u8*)ia + ni->block_size; 389 if (index_end > kaddr + PAGE_SIZE) { 390 ntfs_error(vol->mp, "Index buffer (VCN 0x%llx) of $Secure " 391 "crosses page boundary. Impossible! Cannot " 392 "access! This is probably a bug in the " 393 "driver.", (unsigned long long)vcn); 394 goto page_err; 395 } 396 index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); 397 if (index_end > (u8*)ia + ni->block_size) { 398 ntfs_error(vol->mp, "Size of index buffer (VCN 0x%llx) of " 399 "$Secure exceeds maximum size.", 400 (unsigned long long)vcn); 401 goto page_err; 402 } 403 /* The first index entry. */ 404 ie = (INDEX_ENTRY*)((u8*)&ia->index + 405 le32_to_cpu(ia->index.entries_offset)); 406 /* 407 * Iterate similar to above big loop but applied to index buffer, thus 408 * loop until we exceed valid memory (corruption case) or until we 409 * reach the last entry, always storing the previous entry so that when 410 * we reach the last entry we can go back one to find the last 411 * security_id. 412 */ 413 prev_ie = NULL; 414 for (;; prev_ie = ie, ie = (INDEX_ENTRY*)((u8*)ie + 415 le16_to_cpu(ie->length))) { 416 /* Bounds checks. */ 417 if ((u8*)ie < (u8*)&ia->index || (u8*)ie + 418 sizeof(INDEX_ENTRY_HEADER) > index_end || 419 (u8*)ie + le16_to_cpu(ie->length) > index_end || 420 (u32)sizeof(INDEX_ENTRY_HEADER) + 421 le16_to_cpu(ie->key_length) > 422 le16_to_cpu(ie->length)) { 423 ntfs_error(vol->mp, "Index entry out of bounds in " 424 "$Secure."); 425 goto page_err; 426 } 427 /* 428 * The last entry cannot contain a key. It can however contain 429 * a pointer to a child node in the B+tree so we just break out. 430 */ 431 if (ie->flags & INDEX_ENTRY_END) 432 break; 433 /* Further bounds checks. */ 434 if ((u32)sizeof(INDEX_ENTRY_HEADER) + 435 le16_to_cpu(ie->key_length) > 436 le16_to_cpu(ie->data_offset) || 437 (u32)le16_to_cpu(ie->data_offset) + 438 le16_to_cpu(ie->data_length) > 439 le16_to_cpu(ie->length)) { 440 ntfs_error(vol->mp, "Index entry out of bounds in " 441 "$Secure."); 442 goto page_err; 443 } 444 } 445 /* 446 * We have finished with this index buffer. Check for the presence of 447 * a child node and if not present, we have the last security_id in 448 * @prev_ie, or if that is NULL there are no security_ids on the volume 449 * so just start at 0x100. 450 */ 451 if (!(ie->flags & INDEX_ENTRY_NODE)) { 452 if (prev_ie) 453 *next_security_id = cpu_to_le32(le32_to_cpu( 454 prev_ie->key.sii.security_id) + 1); 455 else 456 *next_security_id = const_cpu_to_le32(0x100); 457 ntfs_page_unmap(ni, upl, pl, FALSE); 458 lck_rw_unlock_shared(&ni->lock); 459 (void)vnode_put(ni->vn); 460 ntfs_debug("Found next security_id 0x%x in index allocation.", 461 le32_to_cpu(*next_security_id)); 462 return 0; 463 } 464 if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { 465 ntfs_error(vol->mp, "Index entry with child node found in a " 466 "leaf node in $Secure."); 467 goto page_err; 468 } 469 /* Child node present, descend into it. */ 470 old_vcn = vcn; 471 vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8)); 472 if (vcn >= 0) { 473 /* 474 * If @vcn is in the same page cache page as @old_vcn we 475 * recycle the mapped page. 476 */ 477 if (old_vcn << ni->vcn_size_shift >> PAGE_SHIFT == 478 vcn << ni->vcn_size_shift >> PAGE_SHIFT) 479 goto fast_descend_into_child_node; 480 ntfs_page_unmap(ni, upl, pl, FALSE); 481 goto descend_into_child_node; 482 } 483 ntfs_error(vol->mp, "Negative child node vcn in $Secure."); 484page_err: 485 ntfs_page_unmap(ni, upl, pl, FALSE); 486err: 487 if (!err) 488 err = EIO; 489 if (actx) 490 ntfs_attr_search_ctx_put(actx); 491 if (m) 492 ntfs_mft_record_unmap(base_ni); 493 lck_rw_unlock_shared(&ni->lock); 494 (void)vnode_put(ni->vn); 495 return err; 496idx_err: 497 ntfs_error(vol->mp, "Corrupt index. Aborting lookup."); 498 goto err; 499} 500 501errno_t ntfs_default_security_id_init(ntfs_volume *vol, struct vnode_attr *va) 502{ 503 le32 security_id; 504 505 ntfs_debug("Entering."); 506 lck_rw_lock_exclusive(&vol->secure_lock); 507 lck_spin_lock(&vol->security_id_lock); 508 if (va->va_type == VDIR) 509 security_id = vol->default_dir_security_id; 510 else 511 security_id = vol->default_file_security_id; 512 lck_spin_unlock(&vol->security_id_lock); 513 if (security_id) { 514 /* Someone else initialized the default security_id for us. */ 515 lck_rw_unlock_exclusive(&vol->secure_lock); 516 ntfs_debug("Done (lost race)."); 517 return 0; 518 } 519 // TODO: Look for our security descriptor appropriate to the volume 520 // version. If the security descriptor is not found, add it to $Secure 521 // and set the volume default security_id to point to it. 522 lck_rw_unlock_exclusive(&vol->secure_lock); 523 ntfs_debug("Failed (not implemented yet)."); 524 return ENOTSUP; 525} 526