#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/ucred.h>
#include <sys/vnode.h>
#include <string.h>
#include <libkern/OSMalloc.h>
#include <kern/debug.h>
#include <kern/locks.h>
#include "ntfs.h"
#include "ntfs_attr.h"
#include "ntfs_debug.h"
#include "ntfs_endian.h"
#include "ntfs_inode.h"
#include "ntfs_mft.h"
#include "ntfs_page.h"
#include "ntfs_types.h"
#include "ntfs_layout.h"
#include "ntfs_secure.h"
#include "ntfs_volume.h"
SDS_ENTRY *ntfs_file_sds_entry, *ntfs_dir_sds_entry;
SDS_ENTRY *ntfs_file_sds_entry_old, *ntfs_dir_sds_entry_old;
static void ntfs_default_sds_entry_init(SDS_ENTRY *sds, BOOL dir, BOOL old)
{
SECURITY_DESCRIPTOR_RELATIVE *sd;
ACL *dacl;
ACCESS_ALLOWED_ACE *ace;
SID *sid;
u32 sd_len;
sd = &sds->sd;
sd->revision = SECURITY_DESCRIPTOR_REVISION;
sd->control = SE_SELF_RELATIVE | SE_DACL_PROTECTED |
SE_DACL_AUTO_INHERITED | SE_DACL_PRESENT;
if (old)
sd->control &= ~(SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED);
sd->dacl = const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
dacl = (ACL*)((u8*)sd + le32_to_cpu(sd->dacl));
dacl->revision = ACL_REVISION;
dacl->ace_count = const_cpu_to_le16(1);
ace = (ACCESS_ALLOWED_ACE*)((u8*)dacl + sizeof(ACL));
ace->type = ACCESS_ALLOWED_ACE_TYPE;
ace->flags = dir ? (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) : 0;
ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES |
FILE_READ_ATTRIBUTES | FILE_DELETE_CHILD |
FILE_EXECUTE | FILE_WRITE_EA | FILE_READ_EA |
FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA;
sid = &ace->sid;
sid->revision = SID_REVISION;
sid->sub_authority_count = 1;
sid->identifier_authority.value[0] = 0;
sid->identifier_authority.value[1] = 0;
sid->identifier_authority.value[2] = 0;
sid->identifier_authority.value[3] = 0;
sid->identifier_authority.value[4] = 0;
sid->identifier_authority.value[5] = 1;
sid->sub_authority[0] = SECURITY_WORLD_RID;
dacl->size = const_cpu_to_le16(sizeof(ACL) + le16_to_cpu(ace->size));
sd->owner = cpu_to_le32(le32_to_cpu(sd->dacl) +
le16_to_cpu(dacl->size));
memcpy((u8*)sd + le32_to_cpu(sd->owner), sid, sizeof(SID));
sd->group = cpu_to_le32(le32_to_cpu(sd->owner) + sizeof(SID));
memcpy((u8*)sd + le32_to_cpu(sd->group), sid, sizeof(SID));
sd_len = le32_to_cpu(sd->group) + sizeof(SID);
sds->hash = ntfs_security_hash(sd, sd_len);
sds->length = cpu_to_le32(sizeof(SDS_ENTRY_HEADER) + sd_len);
}
errno_t ntfs_default_sds_entries_init(void)
{
SDS_ENTRY *sds;
ntfs_debug("Entering.");
sds = OSMalloc(0x60 * 4, ntfs_malloc_tag);
if (!sds) {
ntfs_error(NULL, "Failed to allocate memory for the default "
"$Secure/$DATA/$SDS entries.");
return ENOMEM;
}
ntfs_file_sds_entry = sds;
ntfs_default_sds_entry_init(sds, FALSE, FALSE);
sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15));
ntfs_dir_sds_entry = sds;
ntfs_default_sds_entry_init(sds, TRUE, FALSE);
sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15));
ntfs_file_sds_entry_old = sds;
ntfs_default_sds_entry_init(sds, FALSE, TRUE);
sds = (SDS_ENTRY*)((u8*)sds + ((le32_to_cpu(sds->length) + 15) & ~15));
ntfs_dir_sds_entry_old = sds;
ntfs_default_sds_entry_init(sds, TRUE, TRUE);
ntfs_debug("Done.");
return 0;
}
errno_t ntfs_next_security_id_init(ntfs_volume *vol, le32 *next_security_id)
{
VCN vcn, old_vcn;
ntfs_inode *base_ni, *ni;
MFT_RECORD *m;
INDEX_ROOT *ir;
INDEX_ENTRY *ie, *prev_ie;
INDEX_ALLOCATION *ia;
upl_t upl;
upl_page_info_array_t pl;
u8 *index_end, *kaddr;
ntfs_attr_search_ctx *actx;
errno_t err;
ntfs_debug("Entering.");
base_ni = vol->secure_ni;
if (!base_ni)
panic("%s(): !vol->secure_ni\n", __FUNCTION__);
ni = vol->secure_sii_ni;
if (!ni)
panic("%s(): !vol->secure_sii_ni\n", __FUNCTION__);
err = vnode_getwithref(ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for "
"$Secure/$INDEX_ALLOCATION/$SII.");
return err;
}
lck_rw_lock_shared(&ni->lock);
err = ntfs_mft_record_map(base_ni, &m);
if (err) {
ntfs_error(vol->mp, "Failed to map mft record of $Secure "
"(error %d).", err);
m = NULL;
actx = NULL;
goto err;
}
actx = ntfs_attr_search_ctx_get(base_ni, m);
if (!actx) {
err = ENOMEM;
goto err;
}
err = ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len, 0, NULL,
0, actx);
if (err) {
if (err == ENOENT) {
ntfs_error(vol->mp, "$SII index root attribute "
"missing in $Secure.");
err = EIO;
}
goto err;
}
ir = (INDEX_ROOT*)((u8*)actx->a + le16_to_cpu(actx->a->value_offset));
index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
ie = (INDEX_ENTRY*)((u8*)&ir->index +
le32_to_cpu(ir->index.entries_offset));
prev_ie = NULL;
for (;; prev_ie = ie, ie = (INDEX_ENTRY*)((u8*)ie +
le16_to_cpu(ie->length))) {
if ((u8*)ie < (u8*)&ir->index || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->length) > index_end ||
(u32)sizeof(INDEX_ENTRY_HEADER) +
le16_to_cpu(ie->key_length) >
le16_to_cpu(ie->length))
goto idx_err;
if (ie->flags & INDEX_ENTRY_END)
break;
if ((u32)sizeof(INDEX_ENTRY_HEADER) +
le16_to_cpu(ie->key_length) >
le16_to_cpu(ie->data_offset) ||
(u32)le16_to_cpu(ie->data_offset) +
le16_to_cpu(ie->data_length) >
le16_to_cpu(ie->length))
goto idx_err;
}
if (!(ie->flags & INDEX_ENTRY_NODE)) {
if (prev_ie)
*next_security_id = cpu_to_le32(le32_to_cpu(
prev_ie->key.sii.security_id) + 1);
else
*next_security_id = const_cpu_to_le32(0x100);
ntfs_attr_search_ctx_put(actx);
ntfs_mft_record_unmap(base_ni);
lck_rw_unlock_shared(&ni->lock);
(void)vnode_put(ni->vn);
ntfs_debug("Found next security_id 0x%x in index root.",
(unsigned)le32_to_cpu(*next_security_id));
return 0;
}
if (!NInoIndexAllocPresent(ni)) {
ntfs_error(vol->mp, "No index allocation attribute but index "
"entry requires one. $Secure is corrupt or "
"driver bug.");
goto err;
}
vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8));
ntfs_attr_search_ctx_put(actx);
ntfs_mft_record_unmap(base_ni);
m = NULL;
actx = NULL;
descend_into_child_node:
err = ntfs_page_map(ni, (vcn << ni->vcn_size_shift) &
~PAGE_MASK_64, &upl, &pl, &kaddr, FALSE);
if (err) {
ntfs_error(vol->mp, "Failed to map index page, error %d.",
err);
goto err;
}
fast_descend_into_child_node:
ia = (INDEX_ALLOCATION*)(kaddr + ((vcn << ni->vcn_size_shift) &
PAGE_MASK));
if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_SIZE) {
ntfs_error(vol->mp, "Out of bounds check failed. $Secure is "
"corrupt or driver bug.");
goto page_err;
}
if (!ntfs_is_indx_record(ia->magic)) {
ntfs_error(vol->mp, "Index record with vcn 0x%llx is "
"corrupt. $Secure is corrupt. Run chkdsk.",
(unsigned long long)vcn);
goto page_err;
}
if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
ntfs_error(vol->mp, "Actual VCN (0x%llx) of index buffer is "
"different from expected VCN (0x%llx). "
"$Secure is corrupt or driver bug.",
(unsigned long long)
sle64_to_cpu(ia->index_block_vcn),
(unsigned long long)vcn);
goto page_err;
}
if (offsetof(INDEX_BLOCK, index) +
le32_to_cpu(ia->index.allocated_size) !=
ni->block_size) {
ntfs_error(vol->mp, "Index buffer (VCN 0x%llx) of $Secure has "
"a size (%u) differing from the index "
"specified size (%u). $Secure is corrupt or "
"driver bug.", (unsigned long long)vcn,
(unsigned)(offsetof(INDEX_BLOCK, index) +
le32_to_cpu(ia->index.allocated_size)),
(unsigned)ni->block_size);
goto page_err;
}
index_end = (u8*)ia + ni->block_size;
if (index_end > kaddr + PAGE_SIZE) {
ntfs_error(vol->mp, "Index buffer (VCN 0x%llx) of $Secure "
"crosses page boundary. Impossible! Cannot "
"access! This is probably a bug in the "
"driver.", (unsigned long long)vcn);
goto page_err;
}
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (index_end > (u8*)ia + ni->block_size) {
ntfs_error(vol->mp, "Size of index buffer (VCN 0x%llx) of "
"$Secure exceeds maximum size.",
(unsigned long long)vcn);
goto page_err;
}
ie = (INDEX_ENTRY*)((u8*)&ia->index +
le32_to_cpu(ia->index.entries_offset));
prev_ie = NULL;
for (;; prev_ie = ie, ie = (INDEX_ENTRY*)((u8*)ie +
le16_to_cpu(ie->length))) {
if ((u8*)ie < (u8*)&ia->index || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->length) > index_end ||
(u32)sizeof(INDEX_ENTRY_HEADER) +
le16_to_cpu(ie->key_length) >
le16_to_cpu(ie->length)) {
ntfs_error(vol->mp, "Index entry out of bounds in "
"$Secure.");
goto page_err;
}
if (ie->flags & INDEX_ENTRY_END)
break;
if ((u32)sizeof(INDEX_ENTRY_HEADER) +
le16_to_cpu(ie->key_length) >
le16_to_cpu(ie->data_offset) ||
(u32)le16_to_cpu(ie->data_offset) +
le16_to_cpu(ie->data_length) >
le16_to_cpu(ie->length)) {
ntfs_error(vol->mp, "Index entry out of bounds in "
"$Secure.");
goto page_err;
}
}
if (!(ie->flags & INDEX_ENTRY_NODE)) {
if (prev_ie)
*next_security_id = cpu_to_le32(le32_to_cpu(
prev_ie->key.sii.security_id) + 1);
else
*next_security_id = const_cpu_to_le32(0x100);
ntfs_page_unmap(ni, upl, pl, FALSE);
lck_rw_unlock_shared(&ni->lock);
(void)vnode_put(ni->vn);
ntfs_debug("Found next security_id 0x%x in index allocation.",
le32_to_cpu(*next_security_id));
return 0;
}
if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
ntfs_error(vol->mp, "Index entry with child node found in a "
"leaf node in $Secure.");
goto page_err;
}
old_vcn = vcn;
vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8));
if (vcn >= 0) {
if (old_vcn << ni->vcn_size_shift >> PAGE_SHIFT ==
vcn << ni->vcn_size_shift >> PAGE_SHIFT)
goto fast_descend_into_child_node;
ntfs_page_unmap(ni, upl, pl, FALSE);
goto descend_into_child_node;
}
ntfs_error(vol->mp, "Negative child node vcn in $Secure.");
page_err:
ntfs_page_unmap(ni, upl, pl, FALSE);
err:
if (!err)
err = EIO;
if (actx)
ntfs_attr_search_ctx_put(actx);
if (m)
ntfs_mft_record_unmap(base_ni);
lck_rw_unlock_shared(&ni->lock);
(void)vnode_put(ni->vn);
return err;
idx_err:
ntfs_error(vol->mp, "Corrupt index. Aborting lookup.");
goto err;
}
errno_t ntfs_default_security_id_init(ntfs_volume *vol, struct vnode_attr *va)
{
le32 security_id;
ntfs_debug("Entering.");
lck_rw_lock_exclusive(&vol->secure_lock);
lck_spin_lock(&vol->security_id_lock);
if (va->va_type == VDIR)
security_id = vol->default_dir_security_id;
else
security_id = vol->default_file_security_id;
lck_spin_unlock(&vol->security_id_lock);
if (security_id) {
lck_rw_unlock_exclusive(&vol->secure_lock);
ntfs_debug("Done (lost race).");
return 0;
}
lck_rw_unlock_exclusive(&vol->secure_lock);
ntfs_debug("Failed (not implemented yet).");
return ENOTSUP;
}