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