+++ /dev/null
-/**
- * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project.
- *
- * Copyright (c) 2004 Anton Altaparmakov
- * Copyright (c) 2005-2006 Szabolcs Szakacsits
- * Copyright (c) 2006 Yura Pakhuchiy
- * Copyright (c) 2007-2010 Jean-Pierre Andre
- *
- * This program/include file is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program/include file is distributed in the hope that it will be
- * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program (in the main directory of the NTFS-3G
- * distribution in the file COPYING); if not, write to the Free Software
- * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef ENABLE_XATTR
-#define HAVE_SETXATTR
-#endif
-
-#include <stdarg.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#include <unistd.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include <ntfs-3g/param.h>
-#include <ntfs-3g/types.h>
-#include <ntfs-3g/layout.h>
-#include <ntfs-3g/attrib.h>
-#include <ntfs-3g/index.h>
-#include <ntfs-3g/dir.h>
-#include <ntfs-3g/bitmap.h>
-#include <ntfs-3g/security.h>
-#include <ntfs-3g/acls.h>
-#include <ntfs-3g/cache.h>
-#include <ntfs-3g/misc.h>
-
-/*
- * JPA NTFS constants or structs
- * should be moved to layout.h
- */
-
-#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
-#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
-#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
-#define FIRST_SECURITY_ID 0x100 /* Lowest security id */
-
- /* Mask for attributes which can be forced */
-#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \
- | FILE_ATTR_HIDDEN \
- | FILE_ATTR_SYSTEM \
- | FILE_ATTR_ARCHIVE \
- | FILE_ATTR_TEMPORARY \
- | FILE_ATTR_OFFLINE \
- | FILE_ATTR_NOT_CONTENT_INDEXED )
-
-struct SII { /* this is an image of an $SII index entry */
- le16 offs;
- le16 size;
- le32 fill1;
- le16 indexsz;
- le16 indexksz;
- le16 flags;
- le16 fill2;
- le32 keysecurid;
-
- /* did not find official description for the following */
- le32 hash;
- le32 securid;
- le32 dataoffsl; /* documented as badly aligned */
- le32 dataoffsh;
- le32 datasize;
-} ;
-
-struct SDH { /* this is an image of an $SDH index entry */
- le16 offs;
- le16 size;
- le32 fill1;
- le16 indexsz;
- le16 indexksz;
- le16 flags;
- le16 fill2;
- le32 keyhash;
- le32 keysecurid;
-
- /* did not find official description for the following */
- le32 hash;
- le32 securid;
- le32 dataoffsl;
- le32 dataoffsh;
- le32 datasize;
- le32 fill3;
- } ;
-
-/*
- * A few useful constants
- */
-
-static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
- const_cpu_to_le16('S'),
- const_cpu_to_le16('I'),
- const_cpu_to_le16('I'),
- const_cpu_to_le16(0) };
-static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
- const_cpu_to_le16('S'),
- const_cpu_to_le16('D'),
- const_cpu_to_le16('H'),
- const_cpu_to_le16(0) };
-
-/*
- * null SID (S-1-0-0)
- */
-
-extern const SID *nullsid;
-
-/*
- * The zero GUID.
- */
-
-static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
- const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
-static const GUID *const zero_guid = &__zero_guid;
-
-/**
- * ntfs_guid_is_zero - check if a GUID is zero
- * @guid: [IN] guid to check
- *
- * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
- * and FALSE otherwise.
- */
-BOOL ntfs_guid_is_zero(const GUID *guid)
-{
- return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
-}
-
-/**
- * ntfs_guid_to_mbs - convert a GUID to a multi byte string
- * @guid: [IN] guid to convert
- * @guid_str: [OUT] string in which to return the GUID (optional)
- *
- * Convert the GUID pointed to by @guid to a multi byte string of the form
- * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL)
- * needs to be able to store at least 37 bytes.
- *
- * If @guid_str is not NULL it will contain the converted GUID on return. If
- * it is NULL a string will be allocated and this will be returned. The caller
- * is responsible for free()ing the string in that case.
- *
- * On success return the converted string and on failure return NULL with errno
- * set to the error code.
- */
-char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
-{
- char *_guid_str;
- int res;
-
- if (!guid) {
- errno = EINVAL;
- return NULL;
- }
- _guid_str = guid_str;
- if (!_guid_str) {
- _guid_str = (char*)ntfs_malloc(37);
- if (!_guid_str)
- return _guid_str;
- }
- res = snprintf(_guid_str, 37,
- "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- (unsigned int)le32_to_cpu(guid->data1),
- le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
- guid->data4[0], guid->data4[1],
- guid->data4[2], guid->data4[3], guid->data4[4],
- guid->data4[5], guid->data4[6], guid->data4[7]);
- if (res == 36)
- return _guid_str;
- if (!guid_str)
- free(_guid_str);
- errno = EINVAL;
- return NULL;
-}
-
-/**
- * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
- * @sid: [IN] SID for which to determine the maximum string size
- *
- * Determine the maximum multi byte string size in bytes which is needed to
- * store the standard textual representation of the SID pointed to by @sid.
- * See ntfs_sid_to_mbs(), below.
- *
- * On success return the maximum number of bytes needed to store the multi byte
- * string and on failure return -1 with errno set to the error code.
- */
-int ntfs_sid_to_mbs_size(const SID *sid)
-{
- int size, i;
-
- if (!ntfs_sid_is_valid(sid)) {
- errno = EINVAL;
- return -1;
- }
- /* Start with "S-". */
- size = 2;
- /*
- * Add the SID_REVISION. Hopefully the compiler will optimize this
- * away as SID_REVISION is a constant.
- */
- for (i = SID_REVISION; i > 0; i /= 10)
- size++;
- /* Add the "-". */
- size++;
- /*
- * Add the identifier authority. If it needs to be in decimal, the
- * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be
- * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
- */
- if (!sid->identifier_authority.high_part)
- size += 10;
- else
- size += 14;
- /*
- * Finally, add the sub authorities. For each we have a "-" followed
- * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
- */
- size += (1 + 10) * sid->sub_authority_count;
- /* We need the zero byte at the end, too. */
- size++;
- return size * sizeof(char);
-}
-
-/**
- * ntfs_sid_to_mbs - convert a SID to a multi byte string
- * @sid: [IN] SID to convert
- * @sid_str: [OUT] string in which to return the SID (optional)
- * @sid_str_size: [IN] size in bytes of @sid_str
- *
- * Convert the SID pointed to by @sid to its standard textual representation.
- * @sid_str (if not NULL) needs to be able to store at least
- * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of
- * @sid_str if @sid_str is not NULL.
- *
- * The standard textual representation of the SID is of the form:
- * S-R-I-S-S...
- * Where:
- * - The first "S" is the literal character 'S' identifying the following
- * digits as a SID.
- * - R is the revision level of the SID expressed as a sequence of digits
- * in decimal.
- * - I is the 48-bit identifier_authority, expressed as digits in decimal,
- * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
- * - S... is one or more sub_authority values, expressed as digits in
- * decimal.
- *
- * If @sid_str is not NULL it will contain the converted SUID on return. If it
- * is NULL a string will be allocated and this will be returned. The caller is
- * responsible for free()ing the string in that case.
- *
- * On success return the converted string and on failure return NULL with errno
- * set to the error code.
- */
-char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
-{
- u64 u;
- le32 leauth;
- char *s;
- int i, j, cnt;
-
- /*
- * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
- * check @sid, too. 8 is the minimum SID string size.
- */
- if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
- errno = EINVAL;
- return NULL;
- }
- /* Allocate string if not provided. */
- if (!sid_str) {
- cnt = ntfs_sid_to_mbs_size(sid);
- if (cnt < 0)
- return NULL;
- s = (char*)ntfs_malloc(cnt);
- if (!s)
- return s;
- sid_str = s;
- /* So we know we allocated it. */
- sid_str_size = 0;
- } else {
- s = sid_str;
- cnt = sid_str_size;
- }
- /* Start with "S-R-". */
- i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
- if (i < 0 || i >= cnt)
- goto err_out;
- s += i;
- cnt -= i;
- /* Add the identifier authority. */
- for (u = i = 0, j = 40; i < 6; i++, j -= 8)
- u += (u64)sid->identifier_authority.value[i] << j;
- if (!sid->identifier_authority.high_part)
- i = snprintf(s, cnt, "%lu", (unsigned long)u);
- else
- i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
- if (i < 0 || i >= cnt)
- goto err_out;
- s += i;
- cnt -= i;
- /* Finally, add the sub authorities. */
- for (j = 0; j < sid->sub_authority_count; j++) {
- leauth = sid->sub_authority[j];
- i = snprintf(s, cnt, "-%u", (unsigned int)
- le32_to_cpu(leauth));
- if (i < 0 || i >= cnt)
- goto err_out;
- s += i;
- cnt -= i;
- }
- return sid_str;
-err_out:
- if (i >= cnt)
- i = EMSGSIZE;
- else
- i = errno;
- if (!sid_str_size)
- free(sid_str);
- errno = i;
- return NULL;
-}
-
-/**
- * ntfs_generate_guid - generatates a random current guid.
- * @guid: [OUT] pointer to a GUID struct to hold the generated guid.
- *
- * perhaps not a very good random number generator though...
- */
-void ntfs_generate_guid(GUID *guid)
-{
- unsigned int i;
- u8 *p = (u8 *)guid;
-
- for (i = 0; i < sizeof(GUID); i++) {
- p[i] = (u8)(random() & 0xFF);
- if (i == 7)
- p[7] = (p[7] & 0x0F) | 0x40;
- if (i == 8)
- p[8] = (p[8] & 0x3F) | 0x80;
- }
-}
-
-/**
- * ntfs_security_hash - calculate the hash of a security descriptor
- * @sd: self-relative security descriptor whose hash to calculate
- * @length: size in bytes of the security descritor @sd
- *
- * Calculate the hash of the self-relative security descriptor @sd of length
- * @length bytes.
- *
- * This hash is used in the $Secure system file as the primary key for the $SDH
- * index and is also stored in the header of each security descriptor in the
- * $SDS data stream as well as in the index data of both the $SII and $SDH
- * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER
- * structure.
- *
- * Return the calculated security hash in little endian.
- */
-le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
-{
- const le32 *pos = (const le32*)sd;
- const le32 *end = pos + (len >> 2);
- u32 hash = 0;
-
- while (pos < end) {
- hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
- pos++;
- }
- return cpu_to_le32(hash);
-}
-
-/*
- * Get the first entry of current index block
- * cut and pasted form ntfs_ie_get_first() in index.c
- */
-
-static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
-{
- return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
-}
-
-/*
- * Stuff a 256KB block into $SDS before writing descriptors
- * into the block.
- *
- * This prevents $SDS from being automatically declared as sparse
- * when the second copy of the first security descriptor is written
- * 256KB further ahead.
- *
- * Having $SDS declared as a sparse file is not wrong by itself
- * and chkdsk leaves it as a sparse file. It does however complain
- * and add a sparse flag (0x0200) into field file_attributes of
- * STANDARD_INFORMATION of $Secure. This probably means that a
- * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
- * files (FILE_ATTR_SPARSE_FILE).
- *
- * Windows normally does not convert to sparse attribute or sparse
- * file. Stuffing is just a way to get to the same result.
- */
-
-static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
-{
- int res;
- int written;
- unsigned long total;
- char *stuff;
-
- res = 0;
- total = 0;
- stuff = (char*)ntfs_malloc(STUFFSZ);
- if (stuff) {
- memset(stuff, 0, STUFFSZ);
- do {
- written = ntfs_attr_data_write(vol->secure_ni,
- STREAM_SDS, 4, stuff, STUFFSZ, offs);
- if (written == STUFFSZ) {
- total += STUFFSZ;
- offs += STUFFSZ;
- } else {
- errno = ENOSPC;
- res = -1;
- }
- } while (!res && (total < ALIGN_SDS_BLOCK));
- free(stuff);
- } else {
- errno = ENOMEM;
- res = -1;
- }
- return (res);
-}
-
-/*
- * Enter a new security descriptor into $Secure (data only)
- * it has to be written twice with an offset of 256KB
- *
- * Should only be called by entersecurityattr() to ensure consistency
- *
- * Returns zero if sucessful
- */
-
-static int entersecurity_data(ntfs_volume *vol,
- const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
- le32 hash, le32 keyid, off_t offs, int gap)
-{
- int res;
- int written1;
- int written2;
- char *fullattr;
- int fullsz;
- SECURITY_DESCRIPTOR_HEADER *phsds;
-
- res = -1;
- fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
- fullattr = (char*)ntfs_malloc(fullsz);
- if (fullattr) {
- /*
- * Clear the gap from previous descriptor
- * this could be useful for appending the second
- * copy to the end of file. When creating a new
- * 256K block, the gap is cleared while writing
- * the first copy
- */
- if (gap)
- memset(fullattr,0,gap);
- memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
- attr,attrsz);
- phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
- phsds->hash = hash;
- phsds->security_id = keyid;
- phsds->offset = cpu_to_le64(offs);
- phsds->length = cpu_to_le32(fullsz - gap);
- written1 = ntfs_attr_data_write(vol->secure_ni,
- STREAM_SDS, 4, fullattr, fullsz,
- offs - gap);
- written2 = ntfs_attr_data_write(vol->secure_ni,
- STREAM_SDS, 4, fullattr, fullsz,
- offs - gap + ALIGN_SDS_BLOCK);
- if ((written1 == fullsz)
- && (written2 == written1))
- res = 0;
- else
- errno = ENOSPC;
- free(fullattr);
- } else
- errno = ENOMEM;
- return (res);
-}
-
-/*
- * Enter a new security descriptor in $Secure (indexes only)
- *
- * Should only be called by entersecurityattr() to ensure consistency
- *
- * Returns zero if sucessful
- */
-
-static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
- le32 hash, le32 keyid, off_t offs)
-{
- union {
- struct {
- le32 dataoffsl;
- le32 dataoffsh;
- } parts;
- le64 all;
- } realign;
- int res;
- ntfs_index_context *xsii;
- ntfs_index_context *xsdh;
- struct SII newsii;
- struct SDH newsdh;
-
- res = -1;
- /* enter a new $SII record */
-
- xsii = vol->secure_xsii;
- ntfs_index_ctx_reinit(xsii);
- newsii.offs = const_cpu_to_le16(20);
- newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
- newsii.fill1 = const_cpu_to_le32(0);
- newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
- newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
- newsii.flags = const_cpu_to_le16(0);
- newsii.fill2 = const_cpu_to_le16(0);
- newsii.keysecurid = keyid;
- newsii.hash = hash;
- newsii.securid = keyid;
- realign.all = cpu_to_le64(offs);
- newsii.dataoffsh = realign.parts.dataoffsh;
- newsii.dataoffsl = realign.parts.dataoffsl;
- newsii.datasize = cpu_to_le32(attrsz
- + sizeof(SECURITY_DESCRIPTOR_HEADER));
- if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
-
- /* enter a new $SDH record */
-
- xsdh = vol->secure_xsdh;
- ntfs_index_ctx_reinit(xsdh);
- newsdh.offs = const_cpu_to_le16(24);
- newsdh.size = const_cpu_to_le16(
- sizeof(SECURITY_DESCRIPTOR_HEADER));
- newsdh.fill1 = const_cpu_to_le32(0);
- newsdh.indexsz = const_cpu_to_le16(
- sizeof(struct SDH));
- newsdh.indexksz = const_cpu_to_le16(
- sizeof(SDH_INDEX_KEY));
- newsdh.flags = const_cpu_to_le16(0);
- newsdh.fill2 = const_cpu_to_le16(0);
- newsdh.keyhash = hash;
- newsdh.keysecurid = keyid;
- newsdh.hash = hash;
- newsdh.securid = keyid;
- newsdh.dataoffsh = realign.parts.dataoffsh;
- newsdh.dataoffsl = realign.parts.dataoffsl;
- newsdh.datasize = cpu_to_le32(attrsz
- + sizeof(SECURITY_DESCRIPTOR_HEADER));
- /* special filler value, Windows generally */
- /* fills with 0x00490049, sometimes with zero */
- newsdh.fill3 = const_cpu_to_le32(0x00490049);
- if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
- res = 0;
- }
- return (res);
-}
-
-/*
- * Enter a new security descriptor in $Secure (data and indexes)
- * Returns id of entry, or zero if there is a problem.
- * (should not be called for NTFS version < 3.0)
- *
- * important : calls have to be serialized, however no locking is
- * needed while fuse is not multithreaded
- */
-
-static le32 entersecurityattr(ntfs_volume *vol,
- const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
- le32 hash)
-{
- union {
- struct {
- le32 dataoffsl;
- le32 dataoffsh;
- } parts;
- le64 all;
- } realign;
- le32 securid;
- le32 keyid;
- u32 newkey;
- off_t offs;
- int gap;
- int size;
- BOOL found;
- struct SII *psii;
- INDEX_ENTRY *entry;
- INDEX_ENTRY *next;
- ntfs_index_context *xsii;
- int retries;
- ntfs_attr *na;
- int olderrno;
-
- /* find the first available securid beyond the last key */
- /* in $Secure:$SII. This also determines the first */
- /* available location in $Secure:$SDS, as this stream */
- /* is always appended to and the id's are allocated */
- /* in sequence */
-
- securid = const_cpu_to_le32(0);
- xsii = vol->secure_xsii;
- ntfs_index_ctx_reinit(xsii);
- offs = size = 0;
- keyid = const_cpu_to_le32(-1);
- olderrno = errno;
- found = !ntfs_index_lookup((char*)&keyid,
- sizeof(SII_INDEX_KEY), xsii);
- if (!found && (errno != ENOENT)) {
- ntfs_log_perror("Inconsistency in index $SII");
- psii = (struct SII*)NULL;
- } else {
- /* restore errno to avoid misinterpretation */
- errno = olderrno;
- entry = xsii->entry;
- psii = (struct SII*)xsii->entry;
- }
- if (psii) {
- /*
- * Get last entry in block, but must get first one
- * one first, as we should already be beyond the
- * last one. For some reason the search for the last
- * entry sometimes does not return the last block...
- * we assume this can only happen in root block
- */
- if (xsii->is_in_root)
- entry = ntfs_ie_get_first
- ((INDEX_HEADER*)&xsii->ir->index);
- else
- entry = ntfs_ie_get_first
- ((INDEX_HEADER*)&xsii->ib->index);
- /*
- * All index blocks should be at least half full
- * so there always is a last entry but one,
- * except when creating the first entry in index root.
- * This was however found not to be true : chkdsk
- * sometimes deletes all the (unused) keys in the last
- * index block without rebalancing the tree.
- * When this happens, a new search is restarted from
- * the smallest key.
- */
- keyid = const_cpu_to_le32(0);
- retries = 0;
- while (entry) {
- next = ntfs_index_next(entry,xsii);
- if (next) {
- psii = (struct SII*)next;
- /* save last key and */
- /* available position */
- keyid = psii->keysecurid;
- realign.parts.dataoffsh
- = psii->dataoffsh;
- realign.parts.dataoffsl
- = psii->dataoffsl;
- offs = le64_to_cpu(realign.all);
- size = le32_to_cpu(psii->datasize);
- }
- entry = next;
- if (!entry && !keyid && !retries) {
- /* search failed, retry from smallest key */
- ntfs_index_ctx_reinit(xsii);
- found = !ntfs_index_lookup((char*)&keyid,
- sizeof(SII_INDEX_KEY), xsii);
- if (!found && (errno != ENOENT)) {
- ntfs_log_perror("Index $SII is broken");
- } else {
- /* restore errno */
- errno = olderrno;
- entry = xsii->entry;
- }
- retries++;
- }
- }
- }
- if (!keyid) {
- /*
- * could not find any entry, before creating the first
- * entry, make a double check by making sure size of $SII
- * is less than needed for one entry
- */
- securid = const_cpu_to_le32(0);
- na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
- if (na) {
- if ((size_t)na->data_size < sizeof(struct SII)) {
- ntfs_log_error("Creating the first security_id\n");
- securid = const_cpu_to_le32(FIRST_SECURITY_ID);
- }
- ntfs_attr_close(na);
- }
- if (!securid) {
- ntfs_log_error("Error creating a security_id\n");
- errno = EIO;
- }
- } else {
- newkey = le32_to_cpu(keyid) + 1;
- securid = cpu_to_le32(newkey);
- }
- /*
- * The security attr has to be written twice 256KB
- * apart. This implies that offsets like
- * 0x40000*odd_integer must be left available for
- * the second copy. So align to next block when
- * the last byte overflows on a wrong block.
- */
-
- if (securid) {
- gap = (-size) & (ALIGN_SDS_ENTRY - 1);
- offs += gap + size;
- if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
- & ALIGN_SDS_BLOCK) {
- offs = ((offs + attrsz
- + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
- | (ALIGN_SDS_BLOCK - 1)) + 1;
- }
- if (!(offs & (ALIGN_SDS_BLOCK - 1)))
- entersecurity_stuff(vol, offs);
- /*
- * now write the security attr to storage :
- * first data, then SII, then SDH
- * If failure occurs while writing SDS, data will never
- * be accessed through indexes, and will be overwritten
- * by the next allocated descriptor
- * If failure occurs while writing SII, the id has not
- * recorded and will be reallocated later
- * If failure occurs while writing SDH, the space allocated
- * in SDS or SII will not be reused, an inconsistency
- * will persist with no significant consequence
- */
- if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
- || entersecurity_indexes(vol, attrsz, hash, securid, offs))
- securid = const_cpu_to_le32(0);
- }
- /* inode now is dirty, synchronize it all */
- ntfs_index_entry_mark_dirty(vol->secure_xsii);
- ntfs_index_ctx_reinit(vol->secure_xsii);
- ntfs_index_entry_mark_dirty(vol->secure_xsdh);
- ntfs_index_ctx_reinit(vol->secure_xsdh);
- NInoSetDirty(vol->secure_ni);
- if (ntfs_inode_sync(vol->secure_ni))
- ntfs_log_perror("Could not sync $Secure\n");
- return (securid);
-}
-
-/*
- * Find a matching security descriptor in $Secure,
- * if none, allocate a new id and write the descriptor to storage
- * Returns id of entry, or zero if there is a problem.
- *
- * important : calls have to be serialized, however no locking is
- * needed while fuse is not multithreaded
- */
-
-static le32 setsecurityattr(ntfs_volume *vol,
- const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
-{
- struct SDH *psdh; /* this is an image of index (le) */
- union {
- struct {
- le32 dataoffsl;
- le32 dataoffsh;
- } parts;
- le64 all;
- } realign;
- BOOL found;
- BOOL collision;
- size_t size;
- size_t rdsize;
- s64 offs;
- int res;
- ntfs_index_context *xsdh;
- char *oldattr;
- SDH_INDEX_KEY key;
- INDEX_ENTRY *entry;
- le32 securid;
- le32 hash;
- int olderrno;
-
- hash = ntfs_security_hash(attr,attrsz);
- oldattr = (char*)NULL;
- securid = const_cpu_to_le32(0);
- res = 0;
- xsdh = vol->secure_xsdh;
- if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
- ntfs_index_ctx_reinit(xsdh);
- /*
- * find the nearest key as (hash,0)
- * (do not search for partial key : in case of collision,
- * it could return a key which is not the first one which
- * collides)
- */
- key.hash = hash;
- key.security_id = const_cpu_to_le32(0);
- olderrno = errno;
- found = !ntfs_index_lookup((char*)&key,
- sizeof(SDH_INDEX_KEY), xsdh);
- if (!found && (errno != ENOENT))
- ntfs_log_perror("Inconsistency in index $SDH");
- else {
- /* restore errno to avoid misinterpretation */
- errno = olderrno;
- entry = xsdh->entry;
- found = FALSE;
- /*
- * lookup() may return a node with no data,
- * if so get next
- */
- if (entry->ie_flags & INDEX_ENTRY_END)
- entry = ntfs_index_next(entry,xsdh);
- do {
- collision = FALSE;
- psdh = (struct SDH*)entry;
- if (psdh)
- size = (size_t) le32_to_cpu(psdh->datasize)
- - sizeof(SECURITY_DESCRIPTOR_HEADER);
- else size = 0;
- /* if hash is not the same, the key is not present */
- if (psdh && (size > 0)
- && (psdh->keyhash == hash)) {
- /* if hash is the same */
- /* check the whole record */
- realign.parts.dataoffsh = psdh->dataoffsh;
- realign.parts.dataoffsl = psdh->dataoffsl;
- offs = le64_to_cpu(realign.all)
- + sizeof(SECURITY_DESCRIPTOR_HEADER);
- oldattr = (char*)ntfs_malloc(size);
- if (oldattr) {
- rdsize = ntfs_attr_data_read(
- vol->secure_ni,
- STREAM_SDS, 4,
- oldattr, size, offs);
- found = (rdsize == size)
- && !memcmp(oldattr,attr,size);
- free(oldattr);
- /* if the records do not compare */
- /* (hash collision), try next one */
- if (!found) {
- entry = ntfs_index_next(
- entry,xsdh);
- collision = TRUE;
- }
- } else
- res = ENOMEM;
- }
- } while (collision && entry);
- if (found)
- securid = psdh->keysecurid;
- else {
- if (res) {
- errno = res;
- securid = const_cpu_to_le32(0);
- } else {
- /*
- * no matching key :
- * have to build a new one
- */
- securid = entersecurityattr(vol,
- attr, attrsz, hash);
- }
- }
- }
- }
- if (--vol->secure_reentry)
- ntfs_log_perror("Reentry error, check no multithreading\n");
- return (securid);
-}
-
-
-/*
- * Update the security descriptor of a file
- * Either as an attribute (complying with pre v3.x NTFS version)
- * or, when possible, as an entry in $Secure (for NTFS v3.x)
- *
- * returns 0 if success
- */
-
-static int update_secur_descr(ntfs_volume *vol,
- char *newattr, ntfs_inode *ni)
-{
- int newattrsz;
- int written;
- int res;
- ntfs_attr *na;
-
- newattrsz = ntfs_attr_size(newattr);
-
-#if !FORCE_FORMAT_v1x
- if ((vol->major_ver < 3) || !vol->secure_ni) {
-#endif
-
- /* update for NTFS format v1.x */
-
- /* update the old security attribute */
- na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
- if (na) {
- /* resize attribute */
- res = ntfs_attr_truncate(na, (s64) newattrsz);
- /* overwrite value */
- if (!res) {
- written = (int)ntfs_attr_pwrite(na, (s64) 0,
- (s64) newattrsz, newattr);
- if (written != newattrsz) {
- ntfs_log_error("Failed to update "
- "a v1.x security descriptor\n");
- errno = EIO;
- res = -1;
- }
- }
-
- ntfs_attr_close(na);
- /* if old security attribute was found, also */
- /* truncate standard information attribute to v1.x */
- /* this is needed when security data is wanted */
- /* as v1.x though volume is formatted for v3.x */
- na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
- AT_UNNAMED, 0);
- if (na) {
- clear_nino_flag(ni, v3_Extensions);
- /*
- * Truncating the record does not sweep extensions
- * from copy in memory. Clear security_id to be safe
- */
- ni->security_id = const_cpu_to_le32(0);
- res = ntfs_attr_truncate(na, (s64)48);
- ntfs_attr_close(na);
- clear_nino_flag(ni, v3_Extensions);
- }
- } else {
- /*
- * insert the new security attribute if there
- * were none
- */
- res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
- AT_UNNAMED, 0, (u8*)newattr,
- (s64) newattrsz);
- }
-#if !FORCE_FORMAT_v1x
- } else {
-
- /* update for NTFS format v3.x */
-
- le32 securid;
-
- securid = setsecurityattr(vol,
- (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
- (s64)newattrsz);
- if (securid) {
- na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
- AT_UNNAMED, 0);
- if (na) {
- res = 0;
- if (!test_nino_flag(ni, v3_Extensions)) {
- /* expand standard information attribute to v3.x */
- res = ntfs_attr_truncate(na,
- (s64)sizeof(STANDARD_INFORMATION));
- ni->owner_id = const_cpu_to_le32(0);
- ni->quota_charged = const_cpu_to_le64(0);
- ni->usn = const_cpu_to_le64(0);
- ntfs_attr_remove(ni,
- AT_SECURITY_DESCRIPTOR,
- AT_UNNAMED, 0);
- }
- set_nino_flag(ni, v3_Extensions);
- ni->security_id = securid;
- ntfs_attr_close(na);
- } else {
- ntfs_log_error("Failed to update "
- "standard informations\n");
- errno = EIO;
- res = -1;
- }
- } else
- res = -1;
- }
-#endif
-
- /* mark node as dirty */
- NInoSetDirty(ni);
- return (res);
-}
-
-/*
- * Upgrade the security descriptor of a file
- * This is intended to allow graceful upgrades for files which
- * were created in previous versions, with a security attributes
- * and no security id.
- *
- * It will allocate a security id and replace the individual
- * security attribute by a reference to the global one
- *
- * Special files are not upgraded (currently / and files in
- * directories /$*)
- *
- * Though most code is similar to update_secur_desc() it has
- * been kept apart to facilitate the further processing of
- * special cases or even to remove it if found dangerous.
- *
- * returns 0 if success,
- * 1 if not upgradable. This is not an error.
- * -1 if there is a problem
- */
-
-static int upgrade_secur_desc(ntfs_volume *vol,
- const char *attr, ntfs_inode *ni)
-{
- int attrsz;
- int res;
- le32 securid;
- ntfs_attr *na;
-
- /*
- * upgrade requires NTFS format v3.x
- * also refuse upgrading for special files
- * whose number is less than FILE_first_user
- */
-
- if ((vol->major_ver >= 3)
- && (ni->mft_no >= FILE_first_user)) {
- attrsz = ntfs_attr_size(attr);
- securid = setsecurityattr(vol,
- (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
- (s64)attrsz);
- if (securid) {
- na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
- AT_UNNAMED, 0);
- if (na) {
- /* expand standard information attribute to v3.x */
- res = ntfs_attr_truncate(na,
- (s64)sizeof(STANDARD_INFORMATION));
- ni->owner_id = const_cpu_to_le32(0);
- ni->quota_charged = const_cpu_to_le64(0);
- ni->usn = const_cpu_to_le64(0);
- ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
- AT_UNNAMED, 0);
- set_nino_flag(ni, v3_Extensions);
- ni->security_id = securid;
- ntfs_attr_close(na);
- } else {
- ntfs_log_error("Failed to upgrade "
- "standard informations\n");
- errno = EIO;
- res = -1;
- }
- } else
- res = -1;
- /* mark node as dirty */
- NInoSetDirty(ni);
- } else
- res = 1;
-
- return (res);
-}
-
-/*
- * Optional simplified checking of group membership
- *
- * This only takes into account the groups defined in
- * /etc/group at initialization time.
- * It does not take into account the groups dynamically set by
- * setgroups() nor the changes in /etc/group since initialization
- *
- * This optional method could be useful if standard checking
- * leads to a performance concern.
- *
- * Should not be called for user root, however the group may be root
- *
- */
-
-static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
-{
- BOOL ingroup;
- int grcnt;
- gid_t *groups;
- struct MAPPING *user;
-
- ingroup = FALSE;
- if (uid) {
- user = scx->mapping[MAPUSERS];
- while (user && ((uid_t)user->xid != uid))
- user = user->next;
- if (user) {
- groups = user->groups;
- grcnt = user->grcnt;
- while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
- ingroup = (grcnt >= 0);
- }
- }
- return (ingroup);
-}
-
-
-/*
- * Check whether current thread owner is member of file group
- *
- * Should not be called for user root, however the group may be root
- *
- * As indicated by Miklos Szeredi :
- *
- * The group list is available in
- *
- * /proc/$PID/task/$TID/status
- *
- * and fuse supplies TID in get_fuse_context()->pid. The only problem is
- * finding out PID, for which I have no good solution, except to iterate
- * through all processes. This is rather slow, but may be speeded up
- * with caching and heuristics (for single threaded programs PID = TID).
- *
- * The following implementation gets the group list from
- * /proc/$TID/task/$TID/status which apparently exists and
- * contains the same data.
- */
-
-static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
-{
- static char key[] = "\nGroups:";
- char buf[BUFSZ+1];
- char filename[64];
- enum { INKEY, INSEP, INNUM, INEND } state;
- int fd;
- char c;
- int matched;
- BOOL ismember;
- int got;
- char *p;
- gid_t grp;
- pid_t tid;
-
- if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
- ismember = staticgroupmember(scx, uid, gid);
- else {
- ismember = FALSE; /* default return */
- tid = scx->tid;
- sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
- fd = open(filename,O_RDONLY);
- if (fd >= 0) {
- got = read(fd, buf, BUFSZ);
- buf[got] = 0;
- state = INKEY;
- matched = 0;
- p = buf;
- grp = 0;
- /*
- * A simple automaton to process lines like
- * Groups: 14 500 513
- */
- do {
- c = *p++;
- if (!c) {
- /* refill buffer */
- got = read(fd, buf, BUFSZ);
- buf[got] = 0;
- p = buf;
- c = *p++; /* 0 at end of file */
- }
- switch (state) {
- case INKEY :
- if (key[matched] == c) {
- if (!key[++matched])
- state = INSEP;
- } else
- if (key[0] == c)
- matched = 1;
- else
- matched = 0;
- break;
- case INSEP :
- if ((c >= '0') && (c <= '9')) {
- grp = c - '0';
- state = INNUM;
- } else
- if ((c != ' ') && (c != '\t'))
- state = INEND;
- break;
- case INNUM :
- if ((c >= '0') && (c <= '9'))
- grp = grp*10 + c - '0';
- else {
- ismember = (grp == gid);
- if ((c != ' ') && (c != '\t'))
- state = INEND;
- else
- state = INSEP;
- }
- default :
- break;
- }
- } while (!ismember && c && (state != INEND));
- close(fd);
- if (!c)
- ntfs_log_error("No group record found in %s\n",filename);
- } else
- ntfs_log_error("Could not open %s\n",filename);
- }
- return (ismember);
-}
-
-/*
- * Cacheing is done two-way :
- * - from uid, gid and perm to securid (CACHED_SECURID)
- * - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
- *
- * CACHED_SECURID data is kept in a most-recent-first list
- * which should not be too long to be efficient. Its optimal
- * size is depends on usage and is hard to determine.
- *
- * CACHED_PERMISSIONS data is kept in a two-level indexed array. It
- * is optimal at the expense of storage. Use of a most-recent-first
- * list would save memory and provide similar performances for
- * standard usage, but not for file servers with too many file
- * owners
- *
- * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
- * for legacy directories which were not allocated a security_id
- * it is organized in a most-recent-first list.
- *
- * In main caches, data is never invalidated, as the meaning of
- * a security_id only changes when user mapping is changed, which
- * current implies remounting. However returned entries may be
- * overwritten at next update, so data has to be copied elsewhere
- * before another cache update is made.
- * In legacy cache, data has to be invalidated when protection is
- * changed.
- *
- * Though the same data may be found in both list, they
- * must be kept separately : the interpretation of ACL
- * in both direction are approximations which could be non
- * reciprocal for some configuration of the user mapping data
- *
- * During the process of recompiling ntfs-3g from a tgz archive,
- * security processing added 7.6% to the cpu time used by ntfs-3g
- * and 30% if the cache is disabled.
- */
-
-static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
- u32 securindex)
-{
- struct PERMISSIONS_CACHE *cache;
- unsigned int index1;
- unsigned int i;
-
- cache = (struct PERMISSIONS_CACHE*)NULL;
- /* create the first permissions blocks */
- index1 = securindex >> CACHE_PERMISSIONS_BITS;
- cache = (struct PERMISSIONS_CACHE*)
- ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
- + index1*sizeof(struct CACHED_PERMISSIONS*));
- if (cache) {
- cache->head.last = index1;
- cache->head.p_reads = 0;
- cache->head.p_hits = 0;
- cache->head.p_writes = 0;
- *scx->pseccache = cache;
- for (i=0; i<=index1; i++)
- cache->cachetable[i]
- = (struct CACHED_PERMISSIONS*)NULL;
- }
- return (cache);
-}
-
-/*
- * Free memory used by caches
- * The only purpose is to facilitate the detection of memory leaks
- */
-
-static void free_caches(struct SECURITY_CONTEXT *scx)
-{
- unsigned int index1;
- struct PERMISSIONS_CACHE *pseccache;
-
- pseccache = *scx->pseccache;
- if (pseccache) {
- for (index1=0; index1<=pseccache->head.last; index1++)
- if (pseccache->cachetable[index1]) {
-#if POSIXACLS
- struct CACHED_PERMISSIONS *cacheentry;
- unsigned int index2;
-
- for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
- cacheentry = &pseccache->cachetable[index1][index2];
- if (cacheentry->valid
- && cacheentry->pxdesc)
- free(cacheentry->pxdesc);
- }
-#endif
- free(pseccache->cachetable[index1]);
- }
- free(pseccache);
- }
-}
-
-static int compare(const struct CACHED_SECURID *cached,
- const struct CACHED_SECURID *item)
-{
-#if POSIXACLS
- size_t csize;
- size_t isize;
-
- /* only compare data and sizes */
- csize = (cached->variable ?
- sizeof(struct POSIX_ACL)
- + (((struct POSIX_SECURITY*)cached->variable)->acccnt
- + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
- *sizeof(struct POSIX_ACE) :
- 0);
- isize = (item->variable ?
- sizeof(struct POSIX_ACL)
- + (((struct POSIX_SECURITY*)item->variable)->acccnt
- + ((struct POSIX_SECURITY*)item->variable)->defcnt)
- *sizeof(struct POSIX_ACE) :
- 0);
- return ((cached->uid != item->uid)
- || (cached->gid != item->gid)
- || (cached->dmode != item->dmode)
- || (csize != isize)
- || (csize
- && isize
- && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
- &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
-#else
- return ((cached->uid != item->uid)
- || (cached->gid != item->gid)
- || (cached->dmode != item->dmode));
-#endif
-}
-
-static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
- const struct CACHED_PERMISSIONS_LEGACY *item)
-{
- return (cached->mft_no != item->mft_no);
-}
-
-/*
- * Resize permission cache table
- * do not call unless resizing is needed
- *
- * If allocation fails, the cache size is not updated
- * Lack of memory is not considered as an error, the cache is left
- * consistent and errno is not set.
- */
-
-static void resize_cache(struct SECURITY_CONTEXT *scx,
- u32 securindex)
-{
- struct PERMISSIONS_CACHE *oldcache;
- struct PERMISSIONS_CACHE *newcache;
- int newcnt;
- int oldcnt;
- unsigned int index1;
- unsigned int i;
-
- oldcache = *scx->pseccache;
- index1 = securindex >> CACHE_PERMISSIONS_BITS;
- newcnt = index1 + 1;
- if (newcnt <= ((CACHE_PERMISSIONS_SIZE
- + (1 << CACHE_PERMISSIONS_BITS)
- - 1) >> CACHE_PERMISSIONS_BITS)) {
- /* expand cache beyond current end, do not use realloc() */
- /* to avoid losing data when there is no more memory */
- oldcnt = oldcache->head.last + 1;
- newcache = (struct PERMISSIONS_CACHE*)
- ntfs_malloc(
- sizeof(struct PERMISSIONS_CACHE)
- + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
- if (newcache) {
- memcpy(newcache,oldcache,
- sizeof(struct PERMISSIONS_CACHE)
- + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
- free(oldcache);
- /* mark new entries as not valid */
- for (i=newcache->head.last+1; i<=index1; i++)
- newcache->cachetable[i]
- = (struct CACHED_PERMISSIONS*)NULL;
- newcache->head.last = index1;
- *scx->pseccache = newcache;
- }
- }
-}
-
-/*
- * Enter uid, gid and mode into cache, if possible
- *
- * returns the updated or created cache entry,
- * or NULL if not possible (typically if there is no
- * security id associated)
- */
-
-#if POSIXACLS
-static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni, uid_t uid, gid_t gid,
- struct POSIX_SECURITY *pxdesc)
-#else
-static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
-#endif
-{
- struct CACHED_PERMISSIONS *cacheentry;
- struct CACHED_PERMISSIONS *cacheblock;
- struct PERMISSIONS_CACHE *pcache;
- u32 securindex;
-#if POSIXACLS
- int pxsize;
- struct POSIX_SECURITY *pxcached;
-#endif
- unsigned int index1;
- unsigned int index2;
- int i;
-
- /* cacheing is only possible if a security_id has been defined */
- if (test_nino_flag(ni, v3_Extensions)
- && ni->security_id) {
- /*
- * Immediately test the most frequent situation
- * where the entry exists
- */
- securindex = le32_to_cpu(ni->security_id);
- index1 = securindex >> CACHE_PERMISSIONS_BITS;
- index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
- pcache = *scx->pseccache;
- if (pcache
- && (pcache->head.last >= index1)
- && pcache->cachetable[index1]) {
- cacheentry = &pcache->cachetable[index1][index2];
- cacheentry->uid = uid;
- cacheentry->gid = gid;
-#if POSIXACLS
- if (cacheentry->valid && cacheentry->pxdesc)
- free(cacheentry->pxdesc);
- if (pxdesc) {
- pxsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
- pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
- if (pxcached) {
- memcpy(pxcached, pxdesc, pxsize);
- cacheentry->pxdesc = pxcached;
- } else {
- cacheentry->valid = 0;
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- }
- cacheentry->mode = pxdesc->mode & 07777;
- } else
- cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
-#else
- cacheentry->mode = mode & 07777;
-#endif
- cacheentry->inh_fileid = const_cpu_to_le32(0);
- cacheentry->inh_dirid = const_cpu_to_le32(0);
- cacheentry->valid = 1;
- pcache->head.p_writes++;
- } else {
- if (!pcache) {
- /* create the first cache block */
- pcache = create_caches(scx, securindex);
- } else {
- if (index1 > pcache->head.last) {
- resize_cache(scx, securindex);
- pcache = *scx->pseccache;
- }
- }
- /* allocate block, if cache table was allocated */
- if (pcache && (index1 <= pcache->head.last)) {
- cacheblock = (struct CACHED_PERMISSIONS*)
- malloc(sizeof(struct CACHED_PERMISSIONS)
- << CACHE_PERMISSIONS_BITS);
- pcache->cachetable[index1] = cacheblock;
- for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
- cacheblock[i].valid = 0;
- cacheentry = &cacheblock[index2];
- if (cacheentry) {
- cacheentry->uid = uid;
- cacheentry->gid = gid;
-#if POSIXACLS
- if (pxdesc) {
- pxsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
- pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
- if (pxcached) {
- memcpy(pxcached, pxdesc, pxsize);
- cacheentry->pxdesc = pxcached;
- } else {
- cacheentry->valid = 0;
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- }
- cacheentry->mode = pxdesc->mode & 07777;
- } else
- cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
-#else
- cacheentry->mode = mode & 07777;
-#endif
- cacheentry->inh_fileid = const_cpu_to_le32(0);
- cacheentry->inh_dirid = const_cpu_to_le32(0);
- cacheentry->valid = 1;
- pcache->head.p_writes++;
- }
- } else
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- }
- } else {
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
-#if CACHE_LEGACY_SIZE
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
- struct CACHED_PERMISSIONS_LEGACY wanted;
- struct CACHED_PERMISSIONS_LEGACY *legacy;
-
- wanted.perm.uid = uid;
- wanted.perm.gid = gid;
-#if POSIXACLS
- wanted.perm.mode = pxdesc->mode & 07777;
- wanted.perm.inh_fileid = const_cpu_to_le32(0);
- wanted.perm.inh_dirid = const_cpu_to_le32(0);
- wanted.mft_no = ni->mft_no;
- wanted.variable = (void*)pxdesc;
- wanted.varsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
-#else
- wanted.perm.mode = mode & 07777;
- wanted.perm.inh_fileid = const_cpu_to_le32(0);
- wanted.perm.inh_dirid = const_cpu_to_le32(0);
- wanted.mft_no = ni->mft_no;
- wanted.variable = (void*)NULL;
- wanted.varsize = 0;
-#endif
- legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
- scx->vol->legacy_cache, GENERIC(&wanted),
- (cache_compare)leg_compare);
- if (legacy) {
- cacheentry = &legacy->perm;
-#if POSIXACLS
- /*
- * give direct access to the cached pxdesc
- * in the permissions structure
- */
- cacheentry->pxdesc = legacy->variable;
-#endif
- }
- }
-#endif
- }
- return (cacheentry);
-}
-
-/*
- * Fetch owner, group and permission of a file, if cached
- *
- * Beware : do not use the returned entry after a cache update :
- * the cache may be relocated making the returned entry meaningless
- *
- * returns the cache entry, or NULL if not available
- */
-
-static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni)
-{
- struct CACHED_PERMISSIONS *cacheentry;
- struct PERMISSIONS_CACHE *pcache;
- u32 securindex;
- unsigned int index1;
- unsigned int index2;
-
- /* cacheing is only possible if a security_id has been defined */
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- if (test_nino_flag(ni, v3_Extensions)
- && (ni->security_id)) {
- securindex = le32_to_cpu(ni->security_id);
- index1 = securindex >> CACHE_PERMISSIONS_BITS;
- index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
- pcache = *scx->pseccache;
- if (pcache
- && (pcache->head.last >= index1)
- && pcache->cachetable[index1]) {
- cacheentry = &pcache->cachetable[index1][index2];
- /* reject if entry is not valid */
- if (!cacheentry->valid)
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- else
- pcache->head.p_hits++;
- if (pcache)
- pcache->head.p_reads++;
- }
- }
-#if CACHE_LEGACY_SIZE
- else {
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
- struct CACHED_PERMISSIONS_LEGACY wanted;
- struct CACHED_PERMISSIONS_LEGACY *legacy;
-
- wanted.mft_no = ni->mft_no;
- wanted.variable = (void*)NULL;
- wanted.varsize = 0;
- legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
- scx->vol->legacy_cache, GENERIC(&wanted),
- (cache_compare)leg_compare);
- if (legacy) cacheentry = &legacy->perm;
- }
- }
-#endif
-#if POSIXACLS
- if (cacheentry && !cacheentry->pxdesc) {
- ntfs_log_error("No Posix descriptor in cache\n");
- cacheentry = (struct CACHED_PERMISSIONS*)NULL;
- }
-#endif
- return (cacheentry);
-}
-
-/*
- * Retrieve a security attribute from $Secure
- */
-
-static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
-{
- struct SII *psii;
- union {
- struct {
- le32 dataoffsl;
- le32 dataoffsh;
- } parts;
- le64 all;
- } realign;
- int found;
- size_t size;
- size_t rdsize;
- s64 offs;
- ntfs_inode *ni;
- ntfs_index_context *xsii;
- char *securattr;
-
- securattr = (char*)NULL;
- ni = vol->secure_ni;
- xsii = vol->secure_xsii;
- if (ni && xsii) {
- ntfs_index_ctx_reinit(xsii);
- found =
- !ntfs_index_lookup((char*)&id,
- sizeof(SII_INDEX_KEY), xsii);
- if (found) {
- psii = (struct SII*)xsii->entry;
- size =
- (size_t) le32_to_cpu(psii->datasize)
- - sizeof(SECURITY_DESCRIPTOR_HEADER);
- /* work around bad alignment problem */
- realign.parts.dataoffsh = psii->dataoffsh;
- realign.parts.dataoffsl = psii->dataoffsl;
- offs = le64_to_cpu(realign.all)
- + sizeof(SECURITY_DESCRIPTOR_HEADER);
-
- securattr = (char*)ntfs_malloc(size);
- if (securattr) {
- rdsize = ntfs_attr_data_read(
- ni, STREAM_SDS, 4,
- securattr, size, offs);
- if ((rdsize != size)
- || !ntfs_valid_descr(securattr,
- rdsize)) {
- /* error to be logged by caller */
- free(securattr);
- securattr = (char*)NULL;
- }
- }
- } else
- if (errno != ENOENT)
- ntfs_log_perror("Inconsistency in index $SII");
- }
- if (!securattr) {
- ntfs_log_error("Failed to retrieve a security descriptor\n");
- errno = EIO;
- }
- return (securattr);
-}
-
-/*
- * Get the security descriptor associated to a file
- *
- * Either :
- * - read the security descriptor attribute (v1.x format)
- * - or find the descriptor in $Secure:$SDS (v3.x format)
- *
- * in both case, sanity checks are done on the attribute and
- * the descriptor can be assumed safe
- *
- * The returned descriptor is dynamically allocated and has to be freed
- */
-
-static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
-{
- SII_INDEX_KEY securid;
- char *securattr;
- s64 readallsz;
-
- /*
- * Warning : in some situations, after fixing by chkdsk,
- * v3_Extensions are marked present (long standard informations)
- * with a default security descriptor inserted in an
- * attribute
- */
- if (test_nino_flag(ni, v3_Extensions)
- && vol->secure_ni && ni->security_id) {
- /* get v3.x descriptor in $Secure */
- securid.security_id = ni->security_id;
- securattr = retrievesecurityattr(vol,securid);
- if (!securattr)
- ntfs_log_error("Bad security descriptor for 0x%lx\n",
- (long)le32_to_cpu(ni->security_id));
- } else {
- /* get v1.x security attribute */
- readallsz = 0;
- securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
- AT_UNNAMED, 0, &readallsz);
- if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
- ntfs_log_error("Bad security descriptor for inode %lld\n",
- (long long)ni->mft_no);
- free(securattr);
- securattr = (char*)NULL;
- }
- }
- if (!securattr) {
- /*
- * in some situations, there is no security
- * descriptor, and chkdsk does not detect or fix
- * anything. This could be a normal situation.
- * When this happens, simulate a descriptor with
- * minimum rights, so that a real descriptor can
- * be created by chown or chmod
- */
- ntfs_log_error("No security descriptor found for inode %lld\n",
- (long long)ni->mft_no);
- securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
- }
- return (securattr);
-}
-
-#if POSIXACLS
-
-/*
- * Determine which access types to a file are allowed
- * according to the relation of current process to the file
- *
- * Do not call if default_permissions is set
- */
-
-static int access_check_posix(struct SECURITY_CONTEXT *scx,
- struct POSIX_SECURITY *pxdesc, mode_t request,
- uid_t uid, gid_t gid)
-{
- struct POSIX_ACE *pxace;
- int userperms;
- int groupperms;
- int mask;
- BOOL somegroup;
- BOOL needgroups;
- mode_t perms;
- int i;
-
- perms = pxdesc->mode;
- /* owner and root access */
- if (!scx->uid || (uid == scx->uid)) {
- if (!scx->uid) {
- /* root access if owner or other execution */
- if (perms & 0101)
- perms = 07777;
- else {
- /* root access if some group execution */
- groupperms = 0;
- mask = 7;
- for (i=pxdesc->acccnt-1; i>=0 ; i--) {
- pxace = &pxdesc->acl.ace[i];
- switch (pxace->tag) {
- case POSIX_ACL_USER_OBJ :
- case POSIX_ACL_GROUP_OBJ :
- case POSIX_ACL_GROUP :
- groupperms |= pxace->perms;
- break;
- case POSIX_ACL_MASK :
- mask = pxace->perms & 7;
- break;
- default :
- break;
- }
- }
- perms = (groupperms & mask & 1) | 6;
- }
- } else
- perms &= 07700;
- } else {
- /*
- * analyze designated users, get mask
- * and identify whether we need to check
- * the group memberships. The groups are
- * not needed when all groups have the
- * same permissions as other for the
- * requested modes.
- */
- userperms = -1;
- groupperms = -1;
- needgroups = FALSE;
- mask = 7;
- for (i=pxdesc->acccnt-1; i>=0 ; i--) {
- pxace = &pxdesc->acl.ace[i];
- switch (pxace->tag) {
- case POSIX_ACL_USER :
- if ((uid_t)pxace->id == scx->uid)
- userperms = pxace->perms;
- break;
- case POSIX_ACL_MASK :
- mask = pxace->perms & 7;
- break;
- case POSIX_ACL_GROUP_OBJ :
- case POSIX_ACL_GROUP :
- if (((pxace->perms & mask) ^ perms)
- & (request >> 6) & 7)
- needgroups = TRUE;
- break;
- default :
- break;
- }
- }
- /* designated users */
- if (userperms >= 0)
- perms = (perms & 07000) + (userperms & mask);
- else if (!needgroups)
- perms &= 07007;
- else {
- /* owning group */
- if (!(~(perms >> 3) & request & mask)
- && ((gid == scx->gid)
- || groupmember(scx, scx->uid, gid)))
- perms &= 07070;
- else {
- /* other groups */
- groupperms = -1;
- somegroup = FALSE;
- for (i=pxdesc->acccnt-1; i>=0 ; i--) {
- pxace = &pxdesc->acl.ace[i];
- if ((pxace->tag == POSIX_ACL_GROUP)
- && groupmember(scx, uid, pxace->id)) {
- if (!(~pxace->perms & request & mask))
- groupperms = pxace->perms;
- somegroup = TRUE;
- }
- }
- if (groupperms >= 0)
- perms = (perms & 07000) + (groupperms & mask);
- else
- if (somegroup)
- perms = 0;
- else
- perms &= 07007;
- }
- }
- }
- return (perms);
-}
-
-/*
- * Get permissions to access a file
- * Takes into account the relation of user to file (owner, group, ...)
- * Do no use as mode of the file
- * Do no call if default_permissions is set
- *
- * returns -1 if there is a problem
- */
-
-static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
- ntfs_inode * ni, mode_t request)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const struct CACHED_PERMISSIONS *cached;
- char *securattr;
- const SID *usid; /* owner of file/directory */
- const SID *gsid; /* group of file/directory */
- uid_t uid;
- gid_t gid;
- int perm;
- BOOL isdir;
- struct POSIX_SECURITY *pxdesc;
-
- if (!scx->mapping[MAPUSERS])
- perm = 07777;
- else {
- /* check whether available in cache */
- cached = fetch_cache(scx,ni);
- if (cached) {
- uid = cached->uid;
- gid = cached->gid;
- perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
- } else {
- perm = 0; /* default to no permission */
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- securattr = getsecurityattr(scx->vol, ni);
- if (securattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- securattr;
- gsid = (const SID*)&
- securattr[le32_to_cpu(phead->group)];
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
-#if OWNERFROMACL
- usid = ntfs_acl_owner(securattr);
- pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
- usid, gsid, isdir);
- if (pxdesc)
- perm = pxdesc->mode & 07777;
- else
- perm = -1;
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#else
- usid = (const SID*)&
- securattr[le32_to_cpu(phead->owner)];
- pxdesc = ntfs_build_permissions_posix(scx,securattr,
- usid, gsid, isdir);
- if (pxdesc)
- perm = pxdesc->mode & 07777;
- else
- perm = -1;
- if (!perm && ntfs_same_sid(usid, adminsid)) {
- uid = find_tenant(scx, securattr);
- if (uid)
- perm = 0700;
- } else
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#endif
- /*
- * Create a security id if there were none
- * and upgrade option is selected
- */
- if (!test_nino_flag(ni, v3_Extensions)
- && (perm >= 0)
- && (scx->vol->secure_flags
- & (1 << SECURITY_ADDSECURIDS))) {
- upgrade_secur_desc(scx->vol,
- securattr, ni);
- /*
- * fetch owner and group for cacheing
- * if there is a securid
- */
- }
- if (test_nino_flag(ni, v3_Extensions)
- && (perm >= 0)) {
- enter_cache(scx, ni, uid,
- gid, pxdesc);
- }
- if (pxdesc) {
- perm = access_check_posix(scx,pxdesc,request,uid,gid);
- free(pxdesc);
- }
- free(securattr);
- } else {
- perm = -1;
- uid = gid = 0;
- }
- }
- }
- return (perm);
-}
-
-/*
- * Get a Posix ACL
- *
- * returns size or -errno if there is a problem
- * if size was too small, no copy is done and errno is not set,
- * the caller is expected to issue a new call
- */
-
-int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- const char *name, char *value, size_t size)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- struct POSIX_SECURITY *pxdesc;
- const struct CACHED_PERMISSIONS *cached;
- char *securattr;
- const SID *usid; /* owner of file/directory */
- const SID *gsid; /* group of file/directory */
- uid_t uid;
- gid_t gid;
- BOOL isdir;
- size_t outsize;
-
- outsize = 0; /* default to error */
- if (!scx->mapping[MAPUSERS])
- errno = ENOTSUP;
- else {
- /* check whether available in cache */
- cached = fetch_cache(scx,ni);
- if (cached)
- pxdesc = cached->pxdesc;
- else {
- securattr = getsecurityattr(scx->vol, ni);
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- if (securattr) {
- phead =
- (const SECURITY_DESCRIPTOR_RELATIVE*)
- securattr;
- gsid = (const SID*)&
- securattr[le32_to_cpu(phead->group)];
-#if OWNERFROMACL
- usid = ntfs_acl_owner(securattr);
-#else
- usid = (const SID*)&
- securattr[le32_to_cpu(phead->owner)];
-#endif
- pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
- usid, gsid, isdir);
-
- /*
- * fetch owner and group for cacheing
- */
- if (pxdesc) {
- /*
- * Create a security id if there were none
- * and upgrade option is selected
- */
- if (!test_nino_flag(ni, v3_Extensions)
- && (scx->vol->secure_flags
- & (1 << SECURITY_ADDSECURIDS))) {
- upgrade_secur_desc(scx->vol,
- securattr, ni);
- }
-#if OWNERFROMACL
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#else
- if (!(pxdesc->mode & 07777)
- && ntfs_same_sid(usid, adminsid)) {
- uid = find_tenant(scx,
- securattr);
- } else
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#endif
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
- enter_cache(scx, ni, uid,
- gid, pxdesc);
- }
- free(securattr);
- } else
- pxdesc = (struct POSIX_SECURITY*)NULL;
- }
-
- if (pxdesc) {
- if (ntfs_valid_posix(pxdesc)) {
- if (!strcmp(name,"system.posix_acl_default")) {
- if (ni->mrec->flags
- & MFT_RECORD_IS_DIRECTORY)
- outsize = sizeof(struct POSIX_ACL)
- + pxdesc->defcnt*sizeof(struct POSIX_ACE);
- else {
- /*
- * getting default ACL from plain file :
- * return EACCES if size > 0 as
- * indicated in the man, but return ok
- * if size == 0, so that ls does not
- * display an error
- */
- if (size > 0) {
- outsize = 0;
- errno = EACCES;
- } else
- outsize = sizeof(struct POSIX_ACL);
- }
- if (outsize && (outsize <= size)) {
- memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
- memcpy(&value[sizeof(struct POSIX_ACL)],
- &pxdesc->acl.ace[pxdesc->firstdef],
- outsize-sizeof(struct POSIX_ACL));
- }
- } else {
- outsize = sizeof(struct POSIX_ACL)
- + pxdesc->acccnt*sizeof(struct POSIX_ACE);
- if (outsize <= size)
- memcpy(value,&pxdesc->acl,outsize);
- }
- } else {
- outsize = 0;
- errno = EIO;
- ntfs_log_error("Invalid Posix ACL built\n");
- }
- if (!cached)
- free(pxdesc);
- } else
- outsize = 0;
- }
- return (outsize ? (int)outsize : -errno);
-}
-
-#else /* POSIXACLS */
-
-
-/*
- * Get permissions to access a file
- * Takes into account the relation of user to file (owner, group, ...)
- * Do no use as mode of the file
- *
- * returns -1 if there is a problem
- */
-
-static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni, mode_t request)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const struct CACHED_PERMISSIONS *cached;
- char *securattr;
- const SID *usid; /* owner of file/directory */
- const SID *gsid; /* group of file/directory */
- BOOL isdir;
- uid_t uid;
- gid_t gid;
- int perm;
-
- if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
- perm = 07777;
- else {
- /* check whether available in cache */
- cached = fetch_cache(scx,ni);
- if (cached) {
- perm = cached->mode;
- uid = cached->uid;
- gid = cached->gid;
- } else {
- perm = 0; /* default to no permission */
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- securattr = getsecurityattr(scx->vol, ni);
- if (securattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- securattr;
- gsid = (const SID*)&
- securattr[le32_to_cpu(phead->group)];
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
-#if OWNERFROMACL
- usid = ntfs_acl_owner(securattr);
- perm = ntfs_build_permissions(securattr,
- usid, gsid, isdir);
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#else
- usid = (const SID*)&
- securattr[le32_to_cpu(phead->owner)];
- perm = ntfs_build_permissions(securattr,
- usid, gsid, isdir);
- if (!perm && ntfs_same_sid(usid, adminsid)) {
- uid = find_tenant(scx, securattr);
- if (uid)
- perm = 0700;
- } else
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#endif
- /*
- * Create a security id if there were none
- * and upgrade option is selected
- */
- if (!test_nino_flag(ni, v3_Extensions)
- && (perm >= 0)
- && (scx->vol->secure_flags
- & (1 << SECURITY_ADDSECURIDS))) {
- upgrade_secur_desc(scx->vol,
- securattr, ni);
- /*
- * fetch owner and group for cacheing
- * if there is a securid
- */
- }
- if (test_nino_flag(ni, v3_Extensions)
- && (perm >= 0)) {
- enter_cache(scx, ni, uid,
- gid, perm);
- }
- free(securattr);
- } else {
- perm = -1;
- uid = gid = 0;
- }
- }
- if (perm >= 0) {
- if (!scx->uid) {
- /* root access and execution */
- if (perm & 0111)
- perm = 07777;
- else
- perm = 0;
- } else
- if (uid == scx->uid)
- perm &= 07700;
- else
- /*
- * avoid checking group membership
- * when the requested perms for group
- * are the same as perms for other
- */
- if ((gid == scx->gid)
- || ((((perm >> 3) ^ perm)
- & (request >> 6) & 7)
- && groupmember(scx, scx->uid, gid)))
- perm &= 07070;
- else
- perm &= 07007;
- }
- }
- return (perm);
-}
-
-#endif /* POSIXACLS */
-
-/*
- * Get an NTFS ACL
- *
- * Returns size or -errno if there is a problem
- * if size was too small, no copy is done and errno is not set,
- * the caller is expected to issue a new call
- */
-
-int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- char *value, size_t size)
-{
- char *securattr;
- size_t outsize;
-
- outsize = 0; /* default to no data and no error */
- securattr = getsecurityattr(scx->vol, ni);
- if (securattr) {
- outsize = ntfs_attr_size(securattr);
- if (outsize <= size) {
- memcpy(value,securattr,outsize);
- }
- free(securattr);
- }
- return (outsize ? (int)outsize : -errno);
-}
-
-/*
- * Get owner, group and permissions in an stat structure
- * returns permissions, or -1 if there is a problem
- */
-
-int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
- ntfs_inode * ni, struct stat *stbuf)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- char *securattr;
- const SID *usid; /* owner of file/directory */
- const SID *gsid; /* group of file/directory */
- const struct CACHED_PERMISSIONS *cached;
- int perm;
- BOOL isdir;
-#if POSIXACLS
- struct POSIX_SECURITY *pxdesc;
-#endif
-
- if (!scx->mapping[MAPUSERS])
- perm = 07777;
- else {
- /* check whether available in cache */
- cached = fetch_cache(scx,ni);
- if (cached) {
- perm = cached->mode;
- stbuf->st_uid = cached->uid;
- stbuf->st_gid = cached->gid;
- stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
- } else {
- perm = -1; /* default to error */
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- securattr = getsecurityattr(scx->vol, ni);
- if (securattr) {
- phead =
- (const SECURITY_DESCRIPTOR_RELATIVE*)
- securattr;
- gsid = (const SID*)&
- securattr[le32_to_cpu(phead->group)];
-#if OWNERFROMACL
- usid = ntfs_acl_owner(securattr);
-#else
- usid = (const SID*)&
- securattr[le32_to_cpu(phead->owner)];
-#endif
-#if POSIXACLS
- pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr,
- usid, gsid, isdir);
- if (pxdesc)
- perm = pxdesc->mode & 07777;
- else
- perm = -1;
-#else
- perm = ntfs_build_permissions(securattr,
- usid, gsid, isdir);
-#endif
- /*
- * fetch owner and group for cacheing
- */
- if (perm >= 0) {
- /*
- * Create a security id if there were none
- * and upgrade option is selected
- */
- if (!test_nino_flag(ni, v3_Extensions)
- && (scx->vol->secure_flags
- & (1 << SECURITY_ADDSECURIDS))) {
- upgrade_secur_desc(scx->vol,
- securattr, ni);
- }
-#if OWNERFROMACL
- stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#else
- if (!perm && ntfs_same_sid(usid, adminsid)) {
- stbuf->st_uid =
- find_tenant(scx,
- securattr);
- if (stbuf->st_uid)
- perm = 0700;
- } else
- stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#endif
- stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- stbuf->st_mode =
- (stbuf->st_mode & ~07777) + perm;
-#if POSIXACLS
- enter_cache(scx, ni, stbuf->st_uid,
- stbuf->st_gid, pxdesc);
- free(pxdesc);
-#else
- enter_cache(scx, ni, stbuf->st_uid,
- stbuf->st_gid, perm);
-#endif
- }
- free(securattr);
- }
- }
- }
- return (perm);
-}
-
-#if POSIXACLS
-
-/*
- * Get the base for a Posix inheritance and
- * build an inherited Posix descriptor
- */
-
-static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
- ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
-{
- const struct CACHED_PERMISSIONS *cached;
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- struct POSIX_SECURITY *pxdesc;
- struct POSIX_SECURITY *pydesc;
- char *securattr;
- const SID *usid;
- const SID *gsid;
- uid_t uid;
- gid_t gid;
-
- pydesc = (struct POSIX_SECURITY*)NULL;
- /* check whether parent directory is available in cache */
- cached = fetch_cache(scx,dir_ni);
- if (cached) {
- uid = cached->uid;
- gid = cached->gid;
- pxdesc = cached->pxdesc;
- if (pxdesc) {
- pydesc = ntfs_build_inherited_posix(pxdesc,mode,
- scx->umask,isdir);
- }
- } else {
- securattr = getsecurityattr(scx->vol, dir_ni);
- if (securattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- securattr;
- gsid = (const SID*)&
- securattr[le32_to_cpu(phead->group)];
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
-#if OWNERFROMACL
- usid = ntfs_acl_owner(securattr);
- pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
- usid, gsid, TRUE);
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#else
- usid = (const SID*)&
- securattr[le32_to_cpu(phead->owner)];
- pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
- usid, gsid, TRUE);
- if (pxdesc && ntfs_same_sid(usid, adminsid)) {
- uid = find_tenant(scx, securattr);
- } else
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
-#endif
- if (pxdesc) {
- /*
- * Create a security id if there were none
- * and upgrade option is selected
- */
- if (!test_nino_flag(dir_ni, v3_Extensions)
- && (scx->vol->secure_flags
- & (1 << SECURITY_ADDSECURIDS))) {
- upgrade_secur_desc(scx->vol,
- securattr, dir_ni);
- /*
- * fetch owner and group for cacheing
- * if there is a securid
- */
- }
- if (test_nino_flag(dir_ni, v3_Extensions)) {
- enter_cache(scx, dir_ni, uid,
- gid, pxdesc);
- }
- pydesc = ntfs_build_inherited_posix(pxdesc,
- mode, scx->umask, isdir);
- free(pxdesc);
- }
- free(securattr);
- }
- }
- return (pydesc);
-}
-
-/*
- * Allocate a security_id for a file being created
- *
- * Returns zero if not possible (NTFS v3.x required)
- */
-
-le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
- uid_t uid, gid_t gid, ntfs_inode *dir_ni,
- mode_t mode, BOOL isdir)
-{
-#if !FORCE_FORMAT_v1x
- const struct CACHED_SECURID *cached;
- struct CACHED_SECURID wanted;
- struct POSIX_SECURITY *pxdesc;
- char *newattr;
- int newattrsz;
- const SID *usid;
- const SID *gsid;
- BIGSID defusid;
- BIGSID defgsid;
- le32 securid;
-#endif
-
- securid = const_cpu_to_le32(0);
-
-#if !FORCE_FORMAT_v1x
-
- pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
- if (pxdesc) {
- /* check whether target securid is known in cache */
-
- wanted.uid = uid;
- wanted.gid = gid;
- wanted.dmode = pxdesc->mode & mode & 07777;
- if (isdir) wanted.dmode |= 0x10000;
- wanted.variable = (void*)pxdesc;
- wanted.varsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
- cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
- scx->vol->securid_cache, GENERIC(&wanted),
- (cache_compare)compare);
- /* quite simple, if we are lucky */
- if (cached)
- securid = cached->securid;
-
- /* not in cache : make sure we can create ids */
-
- if (!cached && (scx->vol->major_ver >= 3)) {
- usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
- gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
- if (!usid || !gsid) {
- ntfs_log_error("File created by an unmapped user/group %d/%d\n",
- (int)uid, (int)gid);
- usid = gsid = adminsid;
- }
- newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
- isdir, usid, gsid);
- if (newattr) {
- newattrsz = ntfs_attr_size(newattr);
- securid = setsecurityattr(scx->vol,
- (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
- newattrsz);
- if (securid) {
- /* update cache, for subsequent use */
- wanted.securid = securid;
- ntfs_enter_cache(scx->vol->securid_cache,
- GENERIC(&wanted),
- (cache_compare)compare);
- }
- free(newattr);
- } else {
- /*
- * could not build new security attribute
- * errno set by ntfs_build_descr()
- */
- }
- }
- free(pxdesc);
- }
-#endif
- return (securid);
-}
-
-/*
- * Apply Posix inheritance to a newly created file
- * (for NTFS 1.x only : no securid)
- */
-
-int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni, uid_t uid, gid_t gid,
- ntfs_inode *dir_ni, mode_t mode)
-{
- struct POSIX_SECURITY *pxdesc;
- char *newattr;
- const SID *usid;
- const SID *gsid;
- BIGSID defusid;
- BIGSID defgsid;
- BOOL isdir;
- int res;
-
- res = -1;
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
- pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
- if (pxdesc) {
- usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
- gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
- if (!usid || !gsid) {
- ntfs_log_error("File created by an unmapped user/group %d/%d\n",
- (int)uid, (int)gid);
- usid = gsid = adminsid;
- }
- newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
- isdir, usid, gsid);
- if (newattr) {
- /* Adjust Windows read-only flag */
- res = update_secur_descr(scx->vol, newattr, ni);
- if (!res && !isdir) {
- if (mode & S_IWUSR)
- ni->flags &= ~FILE_ATTR_READONLY;
- else
- ni->flags |= FILE_ATTR_READONLY;
- }
-#if CACHE_LEGACY_SIZE
- /* also invalidate legacy cache */
- if (isdir && !ni->security_id) {
- struct CACHED_PERMISSIONS_LEGACY legacy;
-
- legacy.mft_no = ni->mft_no;
- legacy.variable = pxdesc;
- legacy.varsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
- ntfs_invalidate_cache(scx->vol->legacy_cache,
- GENERIC(&legacy),
- (cache_compare)leg_compare,0);
- }
-#endif
- free(newattr);
-
- } else {
- /*
- * could not build new security attribute
- * errno set by ntfs_build_descr()
- */
- }
- }
- return (res);
-}
-
-#else
-
-le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
- uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
-{
-#if !FORCE_FORMAT_v1x
- const struct CACHED_SECURID *cached;
- struct CACHED_SECURID wanted;
- char *newattr;
- int newattrsz;
- const SID *usid;
- const SID *gsid;
- BIGSID defusid;
- BIGSID defgsid;
- le32 securid;
-#endif
-
- securid = const_cpu_to_le32(0);
-
-#if !FORCE_FORMAT_v1x
- /* check whether target securid is known in cache */
-
- wanted.uid = uid;
- wanted.gid = gid;
- wanted.dmode = mode & 07777;
- if (isdir) wanted.dmode |= 0x10000;
- wanted.variable = (void*)NULL;
- wanted.varsize = 0;
- cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
- scx->vol->securid_cache, GENERIC(&wanted),
- (cache_compare)compare);
- /* quite simple, if we are lucky */
- if (cached)
- securid = cached->securid;
-
- /* not in cache : make sure we can create ids */
-
- if (!cached && (scx->vol->major_ver >= 3)) {
- usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
- gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
- if (!usid || !gsid) {
- ntfs_log_error("File created by an unmapped user/group %d/%d\n",
- (int)uid, (int)gid);
- usid = gsid = adminsid;
- }
- newattr = ntfs_build_descr(mode, isdir, usid, gsid);
- if (newattr) {
- newattrsz = ntfs_attr_size(newattr);
- securid = setsecurityattr(scx->vol,
- (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
- newattrsz);
- if (securid) {
- /* update cache, for subsequent use */
- wanted.securid = securid;
- ntfs_enter_cache(scx->vol->securid_cache,
- GENERIC(&wanted),
- (cache_compare)compare);
- }
- free(newattr);
- } else {
- /*
- * could not build new security attribute
- * errno set by ntfs_build_descr()
- */
- }
- }
-#endif
- return (securid);
-}
-
-#endif
-
-/*
- * Update ownership and mode of a file, reusing an existing
- * security descriptor when possible
- *
- * Returns zero if successful
- */
-
-#if POSIXACLS
-int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- uid_t uid, gid_t gid, mode_t mode,
- struct POSIX_SECURITY *pxdesc)
-#else
-int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- uid_t uid, gid_t gid, mode_t mode)
-#endif
-{
- int res;
- const struct CACHED_SECURID *cached;
- struct CACHED_SECURID wanted;
- char *newattr;
- const SID *usid;
- const SID *gsid;
- BIGSID defusid;
- BIGSID defgsid;
- BOOL isdir;
-
- res = 0;
-
- /* check whether target securid is known in cache */
-
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
- wanted.uid = uid;
- wanted.gid = gid;
- wanted.dmode = mode & 07777;
- if (isdir) wanted.dmode |= 0x10000;
-#if POSIXACLS
- wanted.variable = (void*)pxdesc;
- if (pxdesc)
- wanted.varsize = sizeof(struct POSIX_SECURITY)
- + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
- else
- wanted.varsize = 0;
-#else
- wanted.variable = (void*)NULL;
- wanted.varsize = 0;
-#endif
- if (test_nino_flag(ni, v3_Extensions)) {
- cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
- scx->vol->securid_cache, GENERIC(&wanted),
- (cache_compare)compare);
- /* quite simple, if we are lucky */
- if (cached) {
- ni->security_id = cached->securid;
- NInoSetDirty(ni);
- }
- } else cached = (struct CACHED_SECURID*)NULL;
-
- if (!cached) {
- /*
- * Do not use usid and gsid from former attributes,
- * but recompute them to get repeatable results
- * which can be kept in cache.
- */
- usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
- gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
- if (!usid || !gsid) {
- ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
- uid, gid);
- usid = gsid = adminsid;
- }
-#if POSIXACLS
- if (pxdesc)
- newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
- isdir, usid, gsid);
- else
- newattr = ntfs_build_descr(mode,
- isdir, usid, gsid);
-#else
- newattr = ntfs_build_descr(mode,
- isdir, usid, gsid);
-#endif
- if (newattr) {
- res = update_secur_descr(scx->vol, newattr, ni);
- if (!res) {
- /* adjust Windows read-only flag */
- if (!isdir) {
- if (mode & S_IWUSR)
- ni->flags &= ~FILE_ATTR_READONLY;
- else
- ni->flags |= FILE_ATTR_READONLY;
- NInoFileNameSetDirty(ni);
- }
- /* update cache, for subsequent use */
- if (test_nino_flag(ni, v3_Extensions)) {
- wanted.securid = ni->security_id;
- ntfs_enter_cache(scx->vol->securid_cache,
- GENERIC(&wanted),
- (cache_compare)compare);
- }
-#if CACHE_LEGACY_SIZE
- /* also invalidate legacy cache */
- if (isdir && !ni->security_id) {
- struct CACHED_PERMISSIONS_LEGACY legacy;
-
- legacy.mft_no = ni->mft_no;
-#if POSIXACLS
- legacy.variable = wanted.variable;
- legacy.varsize = wanted.varsize;
-#else
- legacy.variable = (void*)NULL;
- legacy.varsize = 0;
-#endif
- ntfs_invalidate_cache(scx->vol->legacy_cache,
- GENERIC(&legacy),
- (cache_compare)leg_compare,0);
- }
-#endif
- }
- free(newattr);
- } else {
- /*
- * could not build new security attribute
- * errno set by ntfs_build_descr()
- */
- res = -1;
- }
- }
- return (res);
-}
-
-/*
- * Check whether user has ownership rights on a file
- *
- * Returns TRUE if allowed
- * if not, errno tells why
- */
-
-BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
-{
- const struct CACHED_PERMISSIONS *cached;
- char *oldattr;
- const SID *usid;
- uid_t processuid;
- uid_t uid;
- BOOL gotowner;
- int allowed;
-
- processuid = scx->uid;
-/* TODO : use CAP_FOWNER process capability */
- /*
- * Always allow for root
- * Also always allow if no mapping has been defined
- */
- if (!scx->mapping[MAPUSERS] || !processuid)
- allowed = TRUE;
- else {
- gotowner = FALSE; /* default */
- /* get the owner, either from cache or from old attribute */
- cached = fetch_cache(scx, ni);
- if (cached) {
- uid = cached->uid;
- gotowner = TRUE;
- } else {
- oldattr = getsecurityattr(scx->vol, ni);
- if (oldattr) {
-#if OWNERFROMACL
- usid = ntfs_acl_owner(oldattr);
-#else
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
-
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- oldattr;
- usid = (const SID*)&oldattr
- [le32_to_cpu(phead->owner)];
-#endif
- uid = ntfs_find_user(scx->mapping[MAPUSERS],
- usid);
- gotowner = TRUE;
- free(oldattr);
- }
- }
- allowed = FALSE;
- if (gotowner) {
-/* TODO : use CAP_FOWNER process capability */
- if (!processuid || (processuid == uid))
- allowed = TRUE;
- else
- errno = EPERM;
- }
- }
- return (allowed);
-}
-
-#ifdef HAVE_SETXATTR /* extended attributes interface required */
-
-#if POSIXACLS
-
-/*
- * Set a new access or default Posix ACL to a file
- * (or remove ACL if no input data)
- * Validity of input data is checked after merging
- *
- * Returns 0, or -1 if there is a problem which errno describes
- */
-
-int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- const char *name, const char *value, size_t size,
- int flags)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const struct CACHED_PERMISSIONS *cached;
- char *oldattr;
- uid_t processuid;
- const SID *usid;
- const SID *gsid;
- uid_t uid;
- uid_t gid;
- int res;
- BOOL isdir;
- BOOL deflt;
- BOOL exist;
- int count;
- struct POSIX_SECURITY *oldpxdesc;
- struct POSIX_SECURITY *newpxdesc;
-
- /* get the current pxsec, either from cache or from old attribute */
- res = -1;
- deflt = !strcmp(name,"system.posix_acl_default");
- if (size)
- count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
- else
- count = 0;
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
- newpxdesc = (struct POSIX_SECURITY*)NULL;
- if ((!value
- || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
- && (!deflt || isdir || (!size && !value))) {
- cached = fetch_cache(scx, ni);
- if (cached) {
- uid = cached->uid;
- gid = cached->gid;
- oldpxdesc = cached->pxdesc;
- if (oldpxdesc) {
- newpxdesc = ntfs_replace_acl(oldpxdesc,
- (const struct POSIX_ACL*)value,count,deflt);
- }
- } else {
- oldattr = getsecurityattr(scx->vol, ni);
- if (oldattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
-#if OWNERFROMACL
- usid = ntfs_acl_owner(oldattr);
-#else
- usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
-#endif
- gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
- oldattr, usid, gsid, isdir);
- if (oldpxdesc) {
- if (deflt)
- exist = oldpxdesc->defcnt > 0;
- else
- exist = oldpxdesc->acccnt > 3;
- if ((exist && (flags & XATTR_CREATE))
- || (!exist && (flags & XATTR_REPLACE))) {
- errno = (exist ? EEXIST : ENODATA);
- } else {
- newpxdesc = ntfs_replace_acl(oldpxdesc,
- (const struct POSIX_ACL*)value,count,deflt);
- }
- free(oldpxdesc);
- }
- free(oldattr);
- }
- }
- } else
- errno = EINVAL;
-
- if (newpxdesc) {
- processuid = scx->uid;
-/* TODO : use CAP_FOWNER process capability */
- if (!processuid || (uid == processuid)) {
- /*
- * clear setgid if file group does
- * not match process group
- */
- if (processuid && (gid != scx->gid)
- && !groupmember(scx, scx->uid, gid)) {
- newpxdesc->mode &= ~S_ISGID;
- }
- res = ntfs_set_owner_mode(scx, ni, uid, gid,
- newpxdesc->mode, newpxdesc);
- } else
- errno = EPERM;
- free(newpxdesc);
- }
- return (res ? -1 : 0);
-}
-
-/*
- * Remove a default Posix ACL from a file
- *
- * Returns 0, or -1 if there is a problem which errno describes
- */
-
-int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- const char *name)
-{
- return (ntfs_set_posix_acl(scx, ni, name,
- (const char*)NULL, 0, 0));
-}
-
-#endif
-
-/*
- * Set a new NTFS ACL to a file
- *
- * Returns 0, or -1 if there is a problem
- */
-
-int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- const char *value, size_t size, int flags)
-{
- char *attr;
- int res;
-
- res = -1;
- if ((size > 0)
- && !(flags & XATTR_CREATE)
- && ntfs_valid_descr(value,size)
- && (ntfs_attr_size(value) == size)) {
- /* need copying in order to write */
- attr = (char*)ntfs_malloc(size);
- if (attr) {
- memcpy(attr,value,size);
- res = update_secur_descr(scx->vol, attr, ni);
- /*
- * No need to invalidate standard caches :
- * the relation between a securid and
- * the associated protection is unchanged,
- * only the relation between a file and
- * its securid and protection is changed.
- */
-#if CACHE_LEGACY_SIZE
- /*
- * we must however invalidate the legacy
- * cache, which is based on inode numbers.
- * For safety, invalidate even if updating
- * failed.
- */
- if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- && !ni->security_id) {
- struct CACHED_PERMISSIONS_LEGACY legacy;
-
- legacy.mft_no = ni->mft_no;
- legacy.variable = (char*)NULL;
- legacy.varsize = 0;
- ntfs_invalidate_cache(scx->vol->legacy_cache,
- GENERIC(&legacy),
- (cache_compare)leg_compare,0);
- }
-#endif
- free(attr);
- } else
- errno = ENOMEM;
- } else
- errno = EINVAL;
- return (res ? -1 : 0);
-}
-
-#endif /* HAVE_SETXATTR */
-
-/*
- * Set new permissions to a file
- * Checks user mapping has been defined before request for setting
- *
- * rejected if request is not originated by owner or root
- *
- * returns 0 on success
- * -1 on failure, with errno = EIO
- */
-
-int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const struct CACHED_PERMISSIONS *cached;
- char *oldattr;
- const SID *usid;
- const SID *gsid;
- uid_t processuid;
- uid_t uid;
- uid_t gid;
- int res;
-#if POSIXACLS
- BOOL isdir;
- int pxsize;
- const struct POSIX_SECURITY *oldpxdesc;
- struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
-#endif
-
- /* get the current owner, either from cache or from old attribute */
- res = 0;
- cached = fetch_cache(scx, ni);
- if (cached) {
- uid = cached->uid;
- gid = cached->gid;
-#if POSIXACLS
- oldpxdesc = cached->pxdesc;
- if (oldpxdesc) {
- /* must copy before merging */
- pxsize = sizeof(struct POSIX_SECURITY)
- + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
- newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
- if (newpxdesc) {
- memcpy(newpxdesc, oldpxdesc, pxsize);
- if (ntfs_merge_mode_posix(newpxdesc, mode))
- res = -1;
- } else
- res = -1;
- } else
- newpxdesc = (struct POSIX_SECURITY*)NULL;
-#endif
- } else {
- oldattr = getsecurityattr(scx->vol, ni);
- if (oldattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
-#if OWNERFROMACL
- usid = ntfs_acl_owner(oldattr);
-#else
- usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
-#endif
- gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
- uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
- gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
-#if POSIXACLS
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
- newpxdesc = ntfs_build_permissions_posix(scx->mapping,
- oldattr, usid, gsid, isdir);
- if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
- res = -1;
-#endif
- free(oldattr);
- } else
- res = -1;
- }
-
- if (!res) {
- processuid = scx->uid;
-/* TODO : use CAP_FOWNER process capability */
- if (!processuid || (uid == processuid)) {
- /*
- * clear setgid if file group does
- * not match process group
- */
- if (processuid && (gid != scx->gid)
- && !groupmember(scx, scx->uid, gid))
- mode &= ~S_ISGID;
-#if POSIXACLS
- if (newpxdesc) {
- newpxdesc->mode = mode;
- res = ntfs_set_owner_mode(scx, ni, uid, gid,
- mode, newpxdesc);
- } else
- res = ntfs_set_owner_mode(scx, ni, uid, gid,
- mode, newpxdesc);
-#else
- res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
-#endif
- } else {
- errno = EPERM;
- res = -1; /* neither owner nor root */
- }
- } else {
- /*
- * Should not happen : a default descriptor is generated
- * by getsecurityattr() when there are none
- */
- ntfs_log_error("File has no security descriptor\n");
- res = -1;
- errno = EIO;
- }
-#if POSIXACLS
- if (newpxdesc) free(newpxdesc);
-#endif
- return (res ? -1 : 0);
-}
-
-/*
- * Create a default security descriptor for files whose descriptor
- * cannot be inherited
- */
-
-int ntfs_sd_add_everyone(ntfs_inode *ni)
-{
- /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
- SECURITY_DESCRIPTOR_RELATIVE *sd;
- ACL *acl;
- ACCESS_ALLOWED_ACE *ace;
- SID *sid;
- int ret, sd_len;
-
- /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
- /*
- * Calculate security descriptor length. We have 2 sub-authorities in
- * owner and group SIDs, but structure SID contain only one, so add
- * 4 bytes to every SID.
- */
- sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
- sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
- sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
- if (!sd)
- return -1;
-
- sd->revision = SECURITY_DESCRIPTOR_REVISION;
- sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
-
- sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
- sid->revision = SID_REVISION;
- sid->sub_authority_count = 2;
- sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
- sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
- sid->identifier_authority.value[5] = 5;
- sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
-
- sid = (SID*)((u8*)sid + sizeof(SID) + 4);
- sid->revision = SID_REVISION;
- sid->sub_authority_count = 2;
- sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
- sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
- sid->identifier_authority.value[5] = 5;
- sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
-
- acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
- acl->revision = ACL_REVISION;
- acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
- acl->ace_count = const_cpu_to_le16(1);
- sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
-
- ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
- ace->type = ACCESS_ALLOWED_ACE_TYPE;
- ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
- ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
- ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
- ace->sid.revision = SID_REVISION;
- ace->sid.sub_authority_count = 1;
- ace->sid.sub_authority[0] = const_cpu_to_le32(0);
- ace->sid.identifier_authority.value[5] = 1;
-
- ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
- sd_len);
- if (ret)
- ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
-
- free(sd);
- return ret;
-}
-
-/*
- * Check whether user can access a file in a specific way
- *
- * Returns 1 if access is allowed, including user is root or no
- * user mapping defined
- * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
- * 0 and sets errno if there is a problem or if access
- * is not allowed
- *
- * This is used for Posix ACL and checking creation of DOS file names
- */
-
-int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
- ntfs_inode *ni,
- int accesstype) /* access type required (S_Ixxx values) */
-{
- int perm;
- int res;
- int allow;
- struct stat stbuf;
-
- /*
- * Always allow for root unless execution is requested.
- * (was checked by fuse until kernel 2.6.29)
- * Also always allow if no mapping has been defined
- */
- if (!scx->mapping[MAPUSERS]
- || (!scx->uid
- && (!(accesstype & S_IEXEC)
- || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
- allow = 1;
- else {
- perm = ntfs_get_perm(scx, ni, accesstype);
- if (perm >= 0) {
- res = EACCES;
- switch (accesstype) {
- case S_IEXEC:
- allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
- break;
- case S_IWRITE:
- allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
- break;
- case S_IWRITE + S_IEXEC:
- allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
- && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
- break;
- case S_IREAD:
- allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
- break;
- case S_IREAD + S_IEXEC:
- allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
- && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
- break;
- case S_IREAD + S_IWRITE:
- allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
- && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
- break;
- case S_IWRITE + S_IEXEC + S_ISVTX:
- if (perm & S_ISVTX) {
- if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
- && (stbuf.st_uid == scx->uid))
- allow = 1;
- else
- allow = 2;
- } else
- allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
- && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
- break;
- case S_IREAD + S_IWRITE + S_IEXEC:
- allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
- && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
- && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
- break;
- default :
- res = EINVAL;
- allow = 0;
- break;
- }
- if (!allow)
- errno = res;
- } else
- allow = 0;
- }
- return (allow);
-}
-
-#if 0 /* not needed any more */
-
-/*
- * Check whether user can access the parent directory
- * of a file in a specific way
- *
- * Returns true if access is allowed, including user is root and
- * no user mapping defined
- *
- * Sets errno if there is a problem or if not allowed
- *
- * This is used for Posix ACL and checking creation of DOS file names
- */
-
-BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
- const char *path, int accesstype)
-{
- int allow;
- char *dirpath;
- char *name;
- ntfs_inode *ni;
- ntfs_inode *dir_ni;
- struct stat stbuf;
-
- allow = 0;
- dirpath = strdup(path);
- if (dirpath) {
- /* the root of file system is seen as a parent of itself */
- /* is that correct ? */
- name = strrchr(dirpath, '/');
- *name = 0;
- dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
- if (dir_ni) {
- allow = ntfs_allowed_access(scx,
- dir_ni, accesstype);
- ntfs_inode_close(dir_ni);
- /*
- * for an not-owned sticky directory, have to
- * check whether file itself is owned
- */
- if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
- && (allow == 2)) {
- ni = ntfs_pathname_to_inode(scx->vol, NULL,
- path);
- allow = FALSE;
- if (ni) {
- allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
- && (stbuf.st_uid == scx->uid);
- ntfs_inode_close(ni);
- }
- }
- }
- free(dirpath);
- }
- return (allow); /* errno is set if not allowed */
-}
-
-#endif
-
-/*
- * Define a new owner/group to a file
- *
- * returns zero if successful
- */
-
-int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- uid_t uid, gid_t gid)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const struct CACHED_PERMISSIONS *cached;
- char *oldattr;
- const SID *usid;
- const SID *gsid;
- uid_t fileuid;
- uid_t filegid;
- mode_t mode;
- int perm;
- BOOL isdir;
- int res;
-#if POSIXACLS
- struct POSIX_SECURITY *pxdesc;
- BOOL pxdescbuilt = FALSE;
-#endif
-
- res = 0;
- /* get the current owner and mode from cache or security attributes */
- oldattr = (char*)NULL;
- cached = fetch_cache(scx,ni);
- if (cached) {
- fileuid = cached->uid;
- filegid = cached->gid;
- mode = cached->mode;
-#if POSIXACLS
- pxdesc = cached->pxdesc;
- if (!pxdesc)
- res = -1;
-#endif
- } else {
- fileuid = 0;
- filegid = 0;
- mode = 0;
- oldattr = getsecurityattr(scx->vol, ni);
- if (oldattr) {
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- oldattr;
- gsid = (const SID*)
- &oldattr[le32_to_cpu(phead->group)];
-#if OWNERFROMACL
- usid = ntfs_acl_owner(oldattr);
-#else
- usid = (const SID*)
- &oldattr[le32_to_cpu(phead->owner)];
-#endif
-#if POSIXACLS
- pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
- usid, gsid, isdir);
- if (pxdesc) {
- pxdescbuilt = TRUE;
- fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
- filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- mode = perm = pxdesc->mode;
- } else
- res = -1;
-#else
- mode = perm = ntfs_build_permissions(oldattr,
- usid, gsid, isdir);
- if (perm >= 0) {
- fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
- filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- } else
- res = -1;
-#endif
- free(oldattr);
- } else
- res = -1;
- }
- if (!res) {
- /* check requested by root */
- /* or chgrp requested by owner to an owned group */
- if (!scx->uid
- || ((((int)uid < 0) || (uid == fileuid))
- && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
- && (fileuid == scx->uid))) {
- /* replace by the new usid and gsid */
- /* or reuse old gid and sid for cacheing */
- if ((int)uid < 0)
- uid = fileuid;
- if ((int)gid < 0)
- gid = filegid;
- /* clear setuid and setgid if owner has changed */
- /* unless request originated by root */
- if (uid && (fileuid != uid))
- mode &= 01777;
-#if POSIXACLS
- res = ntfs_set_owner_mode(scx, ni, uid, gid,
- mode, pxdesc);
-#else
- res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
-#endif
- } else {
- res = -1; /* neither owner nor root */
- errno = EPERM;
- }
-#if POSIXACLS
- if (pxdescbuilt)
- free(pxdesc);
-#endif
- } else {
- /*
- * Should not happen : a default descriptor is generated
- * by getsecurityattr() when there are none
- */
- ntfs_log_error("File has no security descriptor\n");
- res = -1;
- errno = EIO;
- }
- return (res ? -1 : 0);
-}
-
-/*
- * Define new owner/group and mode to a file
- *
- * returns zero if successful
- */
-
-int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
- uid_t uid, gid_t gid, const mode_t mode)
-{
- const struct CACHED_PERMISSIONS *cached;
- char *oldattr;
- uid_t fileuid;
- uid_t filegid;
- int res;
-#if POSIXACLS
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- const SID *usid;
- const SID *gsid;
- BOOL isdir;
- const struct POSIX_SECURITY *oldpxdesc;
- struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
- int pxsize;
-#endif
-
- res = 0;
- /* get the current owner and mode from cache or security attributes */
- oldattr = (char*)NULL;
- cached = fetch_cache(scx,ni);
- if (cached) {
- fileuid = cached->uid;
- filegid = cached->gid;
-#if POSIXACLS
- oldpxdesc = cached->pxdesc;
- if (oldpxdesc) {
- /* must copy before merging */
- pxsize = sizeof(struct POSIX_SECURITY)
- + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
- newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
- if (newpxdesc) {
- memcpy(newpxdesc, oldpxdesc, pxsize);
- if (ntfs_merge_mode_posix(newpxdesc, mode))
- res = -1;
- } else
- res = -1;
- }
-#endif
- } else {
- fileuid = 0;
- filegid = 0;
- oldattr = getsecurityattr(scx->vol, ni);
- if (oldattr) {
-#if POSIXACLS
- isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- != const_cpu_to_le16(0);
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
- oldattr;
- gsid = (const SID*)
- &oldattr[le32_to_cpu(phead->group)];
-#if OWNERFROMACL
- usid = ntfs_acl_owner(oldattr);
-#else
- usid = (const SID*)
- &oldattr[le32_to_cpu(phead->owner)];
-#endif
- newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
- usid, gsid, isdir);
- if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
- res = -1;
- else {
- fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
- filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
- }
-#endif
- free(oldattr);
- } else
- res = -1;
- }
- if (!res) {
- /* check requested by root */
- /* or chgrp requested by owner to an owned group */
- if (!scx->uid
- || ((((int)uid < 0) || (uid == fileuid))
- && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
- && (fileuid == scx->uid))) {
- /* replace by the new usid and gsid */
- /* or reuse old gid and sid for cacheing */
- if ((int)uid < 0)
- uid = fileuid;
- if ((int)gid < 0)
- gid = filegid;
-#if POSIXACLS
- res = ntfs_set_owner_mode(scx, ni, uid, gid,
- mode, newpxdesc);
-#else
- res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
-#endif
- } else {
- res = -1; /* neither owner nor root */
- errno = EPERM;
- }
- } else {
- /*
- * Should not happen : a default descriptor is generated
- * by getsecurityattr() when there are none
- */
- ntfs_log_error("File has no security descriptor\n");
- res = -1;
- errno = EIO;
- }
-#if POSIXACLS
- free(newpxdesc);
-#endif
- return (res ? -1 : 0);
-}
-
-/*
- * Build a security id for a descriptor inherited from
- * parent directory the Windows way
- */
-
-static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
- const char *parentattr, BOOL fordir)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *pphead;
- const ACL *ppacl;
- const SID *usid;
- const SID *gsid;
- BIGSID defusid;
- BIGSID defgsid;
- int offpacl;
- int offowner;
- int offgroup;
- SECURITY_DESCRIPTOR_RELATIVE *pnhead;
- ACL *pnacl;
- int parentattrsz;
- char *newattr;
- int newattrsz;
- int aclsz;
- int usidsz;
- int gsidsz;
- int pos;
- le32 securid;
-
- parentattrsz = ntfs_attr_size(parentattr);
- pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
- if (scx->mapping[MAPUSERS]) {
- usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
- gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
- if (!usid)
- usid = adminsid;
- if (!gsid)
- gsid = adminsid;
- } else {
- /*
- * If there is no user mapping, we have to copy owner
- * and group from parent directory.
- * Windows never has to do that, because it can always
- * rely on a user mapping
- */
- offowner = le32_to_cpu(pphead->owner);
- usid = (const SID*)&parentattr[offowner];
- offgroup = le32_to_cpu(pphead->group);
- gsid = (const SID*)&parentattr[offgroup];
- }
- /*
- * new attribute is smaller than parent's
- * except for differences in SIDs which appear in
- * owner, group and possible grants and denials in
- * generic creator-owner and creator-group ACEs.
- * For directories, an ACE may be duplicated for
- * access and inheritance, so we double the count.
- */
- usidsz = ntfs_sid_size(usid);
- gsidsz = ntfs_sid_size(gsid);
- newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
- if (fordir)
- newattrsz *= 2;
- newattr = (char*)ntfs_malloc(newattrsz);
- if (newattr) {
- pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
- pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
- pnhead->alignment = 0;
- pnhead->control = SE_SELF_RELATIVE;
- pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
- /*
- * locate and inherit DACL
- * do not test SE_DACL_PRESENT (wrong for "DR Watson")
- */
- pnhead->dacl = const_cpu_to_le32(0);
- if (pphead->dacl) {
- offpacl = le32_to_cpu(pphead->dacl);
- ppacl = (const ACL*)&parentattr[offpacl];
- pnacl = (ACL*)&newattr[pos];
- aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
- if (aclsz) {
- pnhead->dacl = cpu_to_le32(pos);
- pos += aclsz;
- pnhead->control |= SE_DACL_PRESENT;
- }
- }
- /*
- * locate and inherit SACL
- */
- pnhead->sacl = const_cpu_to_le32(0);
- if (pphead->sacl) {
- offpacl = le32_to_cpu(pphead->sacl);
- ppacl = (const ACL*)&parentattr[offpacl];
- pnacl = (ACL*)&newattr[pos];
- aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
- if (aclsz) {
- pnhead->sacl = cpu_to_le32(pos);
- pos += aclsz;
- pnhead->control |= SE_SACL_PRESENT;
- }
- }
- /*
- * inherit or redefine owner
- */
- memcpy(&newattr[pos],usid,usidsz);
- pnhead->owner = cpu_to_le32(pos);
- pos += usidsz;
- /*
- * inherit or redefine group
- */
- memcpy(&newattr[pos],gsid,gsidsz);
- pnhead->group = cpu_to_le32(pos);
- pos += usidsz;
- securid = setsecurityattr(scx->vol,
- (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
- free(newattr);
- } else
- securid = const_cpu_to_le32(0);
- return (securid);
-}
-
-/*
- * Get an inherited security id
- *
- * For Windows compatibility, the normal initial permission setting
- * may be inherited from the parent directory instead of being
- * defined by the creation arguments.
- *
- * The following creates an inherited id for that purpose.
- *
- * Note : the owner and group of parent directory are also
- * inherited (which is not the case on Windows) if no user mapping
- * is defined.
- *
- * Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
- */
-
-le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
- ntfs_inode *dir_ni, BOOL fordir)
-{
- struct CACHED_PERMISSIONS *cached;
- char *parentattr;
- le32 securid;
-
- securid = const_cpu_to_le32(0);
- cached = (struct CACHED_PERMISSIONS*)NULL;
- /*
- * Try to get inherited id from cache
- */
- if (test_nino_flag(dir_ni, v3_Extensions)
- && dir_ni->security_id) {
- cached = fetch_cache(scx, dir_ni);
- if (cached)
- securid = (fordir ? cached->inh_dirid
- : cached->inh_fileid);
- }
- /*
- * Not cached or not available in cache, compute it all
- * Note : if parent directory has no id, it is not cacheable
- */
- if (!securid) {
- parentattr = getsecurityattr(scx->vol, dir_ni);
- if (parentattr) {
- securid = build_inherited_id(scx,
- parentattr, fordir);
- free(parentattr);
- /*
- * Store the result into cache for further use
- */
- if (securid) {
- cached = fetch_cache(scx, dir_ni);
- if (cached) {
- if (fordir)
- cached->inh_dirid = securid;
- else
- cached->inh_fileid = securid;
- }
- }
- }
- }
- return (securid);
-}
-
-/*
- * Link a group to a member of group
- *
- * Returns 0 if OK, -1 (and errno set) if error
- */
-
-static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
- gid_t gid)
-{
- struct group *group;
- char **grmem;
- int grcnt;
- gid_t *groups;
- int res;
-
- res = 0;
- group = getgrgid(gid);
- if (group && group->gr_mem) {
- grcnt = usermapping->grcnt;
- groups = usermapping->groups;
- grmem = group->gr_mem;
- while (*grmem && strcmp(user->pw_name, *grmem))
- grmem++;
- if (*grmem) {
- if (!grcnt)
- groups = (gid_t*)malloc(sizeof(gid_t));
- else
- groups = (gid_t*)realloc(groups,
- (grcnt+1)*sizeof(gid_t));
- if (groups)
- groups[grcnt++] = gid;
- else {
- res = -1;
- errno = ENOMEM;
- }
- }
- usermapping->grcnt = grcnt;
- usermapping->groups = groups;
- }
- return (res);
-}
-
-
-/*
- * Statically link group to users
- * This is based on groups defined in /etc/group and does not take
- * the groups dynamically set by setgroups() nor any changes in
- * /etc/group into account
- *
- * Only mapped groups and root group are linked to mapped users
- *
- * Returns 0 if OK, -1 (and errno set) if error
- *
- */
-
-static int link_group_members(struct SECURITY_CONTEXT *scx)
-{
- struct MAPPING *usermapping;
- struct MAPPING *groupmapping;
- struct passwd *user;
- int res;
-
- res = 0;
- for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
- usermapping=usermapping->next) {
- usermapping->grcnt = 0;
- usermapping->groups = (gid_t*)NULL;
- user = getpwuid(usermapping->xid);
- if (user && user->pw_name) {
- for (groupmapping=scx->mapping[MAPGROUPS];
- groupmapping && !res;
- groupmapping=groupmapping->next) {
- if (link_single_group(usermapping, user,
- groupmapping->xid))
- res = -1;
- }
- if (!res && link_single_group(usermapping,
- user, (gid_t)0))
- res = -1;
- }
- }
- return (res);
-}
-
-/*
- * Apply default single user mapping
- * returns zero if successful
- */
-
-static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
- uid_t uid, gid_t gid, const SID *usid)
-{
- struct MAPPING *usermapping;
- struct MAPPING *groupmapping;
- SID *sid;
- int sidsz;
- int res;
-
- res = -1;
- sidsz = ntfs_sid_size(usid);
- sid = (SID*)ntfs_malloc(sidsz);
- if (sid) {
- memcpy(sid,usid,sidsz);
- usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
- if (usermapping) {
- groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
- if (groupmapping) {
- usermapping->sid = sid;
- usermapping->xid = uid;
- usermapping->next = (struct MAPPING*)NULL;
- groupmapping->sid = sid;
- groupmapping->xid = gid;
- groupmapping->next = (struct MAPPING*)NULL;
- scx->mapping[MAPUSERS] = usermapping;
- scx->mapping[MAPGROUPS] = groupmapping;
- res = 0;
- }
- }
- }
- return (res);
-}
-
-/*
- * Make sure there are no ambiguous mapping
- * Ambiguous mapping may lead to undesired configurations and
- * we had rather be safe until the consequences are understood
- */
-
-#if 0 /* not activated for now */
-
-static BOOL check_mapping(const struct MAPPING *usermapping,
- const struct MAPPING *groupmapping)
-{
- const struct MAPPING *mapping1;
- const struct MAPPING *mapping2;
- BOOL ambiguous;
-
- ambiguous = FALSE;
- for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
- for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
- if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
- if (mapping1->xid != mapping2->xid)
- ambiguous = TRUE;
- } else {
- if (mapping1->xid == mapping2->xid)
- ambiguous = TRUE;
- }
- for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
- for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
- if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
- if (mapping1->xid != mapping2->xid)
- ambiguous = TRUE;
- } else {
- if (mapping1->xid == mapping2->xid)
- ambiguous = TRUE;
- }
- return (ambiguous);
-}
-
-#endif
-
-#if 0 /* not used any more */
-
-/*
- * Try and apply default single user mapping
- * returns zero if successful
- */
-
-static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- ntfs_inode *ni;
- char *securattr;
- const SID *usid;
- int res;
-
- res = -1;
- ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
- if (ni) {
- securattr = getsecurityattr(scx->vol, ni);
- if (securattr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
- usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
- if (ntfs_is_user_sid(usid))
- res = ntfs_do_default_mapping(scx,
- scx->uid, scx->gid, usid);
- free(securattr);
- }
- ntfs_inode_close(ni);
- }
- return (res);
-}
-
-#endif
-
-/*
- * Basic read from a user mapping file on another volume
- */
-
-static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
-{
- return (read(*(int*)fileid, buf, size));
-}
-
-
-/*
- * Read from a user mapping file on current NTFS partition
- */
-
-static int localread(void *fileid, char *buf, size_t size, off_t offs)
-{
- return (ntfs_attr_data_read((ntfs_inode*)fileid,
- AT_UNNAMED, 0, buf, size, offs));
-}
-
-/*
- * Build the user mapping
- * - according to a mapping file if defined (or default present),
- * - or try default single user mapping if possible
- *
- * The mapping is specific to a mounted device
- * No locking done, mounting assumed non multithreaded
- *
- * returns zero if mapping is successful
- * (failure should not be interpreted as an error)
- */
-
-int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
- BOOL allowdef)
-{
- struct MAPLIST *item;
- struct MAPLIST *firstitem;
- struct MAPPING *usermapping;
- struct MAPPING *groupmapping;
- ntfs_inode *ni;
- int fd;
- static struct {
- u8 revision;
- u8 levels;
- be16 highbase;
- be32 lowbase;
- le32 level1;
- le32 level2;
- le32 level3;
- le32 level4;
- le32 level5;
- } defmap = {
- 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
- const_cpu_to_le32(21),
- const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
- const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
- } ;
-
- /* be sure not to map anything until done */
- scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
- scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
-
- if (!usermap_path) usermap_path = MAPPINGFILE;
- if (usermap_path[0] == '/') {
- fd = open(usermap_path,O_RDONLY);
- if (fd > 0) {
- firstitem = ntfs_read_mapping(basicread, (void*)&fd);
- close(fd);
- } else
- firstitem = (struct MAPLIST*)NULL;
- } else {
- ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
- if (ni) {
- firstitem = ntfs_read_mapping(localread, ni);
- ntfs_inode_close(ni);
- } else
- firstitem = (struct MAPLIST*)NULL;
- }
-
-
- if (firstitem) {
- usermapping = ntfs_do_user_mapping(firstitem);
- groupmapping = ntfs_do_group_mapping(firstitem);
- if (usermapping && groupmapping) {
- scx->mapping[MAPUSERS] = usermapping;
- scx->mapping[MAPGROUPS] = groupmapping;
- } else
- ntfs_log_error("There were no valid user or no valid group\n");
- /* now we can free the memory copy of input text */
- /* and rely on internal representation */
- while (firstitem) {
- item = firstitem->next;
- free(firstitem);
- firstitem = item;
- }
- } else {
- /* no mapping file, try a default mapping */
- if (allowdef) {
- if (!ntfs_do_default_mapping(scx,
- 0, 0, (const SID*)&defmap))
- ntfs_log_info("Using default user mapping\n");
- }
- }
- return (!scx->mapping[MAPUSERS] || link_group_members(scx));
-}
-
-#ifdef HAVE_SETXATTR /* extended attributes interface required */
-
-/*
- * Get the ntfs attribute into an extended attribute
- * The attribute is returned according to cpu endianness
- */
-
-int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
-{
- u32 attrib;
- size_t outsize;
-
- outsize = 0; /* default to no data and no error */
- if (ni) {
- attrib = le32_to_cpu(ni->flags);
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
- else
- attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
- if (!attrib)
- attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
- outsize = sizeof(FILE_ATTR_FLAGS);
- if (size >= outsize) {
- if (value)
- memcpy(value,&attrib,outsize);
- else
- errno = EINVAL;
- }
- }
- return (outsize ? (int)outsize : -errno);
-}
-
-/*
- * Get the ntfs attributes from an inode
- * The attributes are returned according to cpu endianness
- *
- * Returns the attributes if successful (cannot be zero)
- * 0 if failed (errno to tell why)
- */
-
-u32 ntfs_get_inode_attributes(ntfs_inode *ni)
-{
- u32 attrib = -1;
-
- if (ni) {
- attrib = le32_to_cpu(ni->flags);
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
- attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
- else
- attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
- if (!attrib)
- attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
- } else
- errno = EINVAL;
- return (attrib);
-}
-
-/*
- * Set the ntfs attributes on an inode
- * The attribute is expected according to cpu endianness
- *
- * Returns 0 if successful
- * -1 if failed (errno to tell why)
- */
-
-int ntfs_set_inode_attributes(ntfs_inode *ni, u32 attrib)
-{
- le32 settable;
- ATTR_FLAGS dirflags;
- int res;
-
- res = -1;
- if (ni) {
- settable = FILE_ATTR_SETTABLE;
- res = 0;
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
- /*
- * Accept changing compression for a directory
- * and set index root accordingly
- */
- settable |= FILE_ATTR_COMPRESSED;
- if ((ni->flags ^ cpu_to_le32(attrib))
- & FILE_ATTR_COMPRESSED) {
- if (ni->flags & FILE_ATTR_COMPRESSED)
- dirflags = const_cpu_to_le16(0);
- else
- dirflags = ATTR_IS_COMPRESSED;
- res = ntfs_attr_set_flags(ni, AT_INDEX_ROOT,
- NTFS_INDEX_I30, 4, dirflags,
- ATTR_COMPRESSION_MASK);
- }
- }
- if (!res) {
- ni->flags = (ni->flags & ~settable)
- | (cpu_to_le32(attrib) & settable);
- NInoFileNameSetDirty(ni);
- NInoSetDirty(ni);
- }
- } else
- errno = EINVAL;
- return (res);
-}
-
-/*
- * Return the ntfs attribute into an extended attribute
- * The attribute is expected according to cpu endianness
- *
- * Returns 0, or -1 if there is a problem
- */
-
-int ntfs_set_ntfs_attrib(ntfs_inode *ni,
- const char *value, size_t size, int flags)
-{
- u32 attrib;
- int res;
-
- res = -1;
- if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
- if (!(flags & XATTR_CREATE)) {
- /* copy to avoid alignment problems */
- memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
- res = ntfs_set_inode_attributes(ni, attrib);
- } else
- errno = EEXIST;
- } else
- errno = EINVAL;
- return (res ? -1 : 0);
-}
-
-#endif /* HAVE_SETXATTR */
-
-/*
- * Open $Secure once for all
- * returns zero if it succeeds
- * non-zero if it fails. This is not an error (on NTFS v1.x)
- */
-
-
-int ntfs_open_secure(ntfs_volume *vol)
-{
- ntfs_inode *ni;
- int res;
-
- res = -1;
- vol->secure_ni = (ntfs_inode*)NULL;
- vol->secure_xsii = (ntfs_index_context*)NULL;
- vol->secure_xsdh = (ntfs_index_context*)NULL;
- if (vol->major_ver >= 3) {
- /* make sure this is a genuine $Secure inode 9 */
- ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
- if (ni && (ni->mft_no == 9)) {
- vol->secure_reentry = 0;
- vol->secure_xsii = ntfs_index_ctx_get(ni,
- sii_stream, 4);
- vol->secure_xsdh = ntfs_index_ctx_get(ni,
- sdh_stream, 4);
- if (ni && vol->secure_xsii && vol->secure_xsdh) {
- vol->secure_ni = ni;
- res = 0;
- }
- }
- }
- return (res);
-}
-
-/*
- * Final cleaning
- * Allocated memory is freed to facilitate the detection of memory leaks
- */
-
-void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
-{
- ntfs_volume *vol;
-
- vol = scx->vol;
- if (vol->secure_ni) {
- ntfs_index_ctx_put(vol->secure_xsii);
- ntfs_index_ctx_put(vol->secure_xsdh);
- ntfs_inode_close(vol->secure_ni);
-
- }
- ntfs_free_mapping(scx->mapping);
- free_caches(scx);
-}
-
-/*
- * API for direct access to security descriptors
- * based on Win32 API
- */
-
-
-/*
- * Selective feeding of a security descriptor into user buffer
- *
- * Returns TRUE if successful
- */
-
-static BOOL feedsecurityattr(const char *attr, u32 selection,
- char *buf, u32 buflen, u32 *psize)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- SECURITY_DESCRIPTOR_RELATIVE *pnhead;
- const ACL *pdacl;
- const ACL *psacl;
- const SID *pusid;
- const SID *pgsid;
- unsigned int offdacl;
- unsigned int offsacl;
- unsigned int offowner;
- unsigned int offgroup;
- unsigned int daclsz;
- unsigned int saclsz;
- unsigned int usidsz;
- unsigned int gsidsz;
- unsigned int size; /* size of requested attributes */
- BOOL ok;
- unsigned int pos;
- unsigned int avail;
- le16 control;
-
- avail = 0;
- control = SE_SELF_RELATIVE;
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
- size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
-
- /* locate DACL if requested and available */
- if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
- offdacl = le32_to_cpu(phead->dacl);
- pdacl = (const ACL*)&attr[offdacl];
- daclsz = le16_to_cpu(pdacl->size);
- size += daclsz;
- avail |= DACL_SECURITY_INFORMATION;
- } else
- offdacl = daclsz = 0;
-
- /* locate owner if requested and available */
- offowner = le32_to_cpu(phead->owner);
- if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
- /* find end of USID */
- pusid = (const SID*)&attr[offowner];
- usidsz = ntfs_sid_size(pusid);
- size += usidsz;
- avail |= OWNER_SECURITY_INFORMATION;
- } else
- offowner = usidsz = 0;
-
- /* locate group if requested and available */
- offgroup = le32_to_cpu(phead->group);
- if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
- /* find end of GSID */
- pgsid = (const SID*)&attr[offgroup];
- gsidsz = ntfs_sid_size(pgsid);
- size += gsidsz;
- avail |= GROUP_SECURITY_INFORMATION;
- } else
- offgroup = gsidsz = 0;
-
- /* locate SACL if requested and available */
- if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
- /* find end of SACL */
- offsacl = le32_to_cpu(phead->sacl);
- psacl = (const ACL*)&attr[offsacl];
- saclsz = le16_to_cpu(psacl->size);
- size += saclsz;
- avail |= SACL_SECURITY_INFORMATION;
- } else
- offsacl = saclsz = 0;
-
- /*
- * Check having enough size in destination buffer
- * (required size is returned nevertheless so that
- * the request can be reissued with adequate size)
- */
- if (size > buflen) {
- *psize = size;
- errno = EINVAL;
- ok = FALSE;
- } else {
- if (selection & OWNER_SECURITY_INFORMATION)
- control |= phead->control & SE_OWNER_DEFAULTED;
- if (selection & GROUP_SECURITY_INFORMATION)
- control |= phead->control & SE_GROUP_DEFAULTED;
- if (selection & DACL_SECURITY_INFORMATION)
- control |= phead->control
- & (SE_DACL_PRESENT
- | SE_DACL_DEFAULTED
- | SE_DACL_AUTO_INHERITED
- | SE_DACL_PROTECTED);
- if (selection & SACL_SECURITY_INFORMATION)
- control |= phead->control
- & (SE_SACL_PRESENT
- | SE_SACL_DEFAULTED
- | SE_SACL_AUTO_INHERITED
- | SE_SACL_PROTECTED);
- /*
- * copy header and feed new flags, even if no detailed data
- */
- memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
- pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
- pnhead->control = control;
- pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
-
- /* copy DACL if requested and available */
- if (selection & avail & DACL_SECURITY_INFORMATION) {
- pnhead->dacl = cpu_to_le32(pos);
- memcpy(&buf[pos],&attr[offdacl],daclsz);
- pos += daclsz;
- } else
- pnhead->dacl = const_cpu_to_le32(0);
-
- /* copy SACL if requested and available */
- if (selection & avail & SACL_SECURITY_INFORMATION) {
- pnhead->sacl = cpu_to_le32(pos);
- memcpy(&buf[pos],&attr[offsacl],saclsz);
- pos += saclsz;
- } else
- pnhead->sacl = const_cpu_to_le32(0);
-
- /* copy owner if requested and available */
- if (selection & avail & OWNER_SECURITY_INFORMATION) {
- pnhead->owner = cpu_to_le32(pos);
- memcpy(&buf[pos],&attr[offowner],usidsz);
- pos += usidsz;
- } else
- pnhead->owner = const_cpu_to_le32(0);
-
- /* copy group if requested and available */
- if (selection & avail & GROUP_SECURITY_INFORMATION) {
- pnhead->group = cpu_to_le32(pos);
- memcpy(&buf[pos],&attr[offgroup],gsidsz);
- pos += gsidsz;
- } else
- pnhead->group = const_cpu_to_le32(0);
- if (pos != size)
- ntfs_log_error("Error in security descriptor size\n");
- *psize = size;
- ok = TRUE;
- }
-
- return (ok);
-}
-
-/*
- * Merge a new security descriptor into the old one
- * and assign to designated file
- *
- * Returns TRUE if successful
- */
-
-static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
- const char *newattr, u32 selection, ntfs_inode *ni)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
- const SECURITY_DESCRIPTOR_RELATIVE *newhead;
- SECURITY_DESCRIPTOR_RELATIVE *targhead;
- const ACL *pdacl;
- const ACL *psacl;
- const SID *powner;
- const SID *pgroup;
- int offdacl;
- int offsacl;
- int offowner;
- int offgroup;
- unsigned int size;
- le16 control;
- char *target;
- int pos;
- int oldattrsz;
- int newattrsz;
- BOOL ok;
-
- ok = FALSE; /* default return */
- oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
- newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
- oldattrsz = ntfs_attr_size(oldattr);
- newattrsz = ntfs_attr_size(newattr);
- target = (char*)ntfs_malloc(oldattrsz + newattrsz);
- if (target) {
- targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
- pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
- control = SE_SELF_RELATIVE;
- /*
- * copy new DACL if selected
- * or keep old DACL if any
- */
- if ((selection & DACL_SECURITY_INFORMATION) ?
- newhead->dacl : oldhead->dacl) {
- if (selection & DACL_SECURITY_INFORMATION) {
- offdacl = le32_to_cpu(newhead->dacl);
- pdacl = (const ACL*)&newattr[offdacl];
- } else {
- offdacl = le32_to_cpu(oldhead->dacl);
- pdacl = (const ACL*)&oldattr[offdacl];
- }
- size = le16_to_cpu(pdacl->size);
- memcpy(&target[pos], pdacl, size);
- targhead->dacl = cpu_to_le32(pos);
- pos += size;
- } else
- targhead->dacl = const_cpu_to_le32(0);
- if (selection & DACL_SECURITY_INFORMATION) {
- control |= newhead->control
- & (SE_DACL_PRESENT
- | SE_DACL_DEFAULTED
- | SE_DACL_PROTECTED);
- if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
- control |= SE_DACL_AUTO_INHERITED;
- } else
- control |= oldhead->control
- & (SE_DACL_PRESENT
- | SE_DACL_DEFAULTED
- | SE_DACL_AUTO_INHERITED
- | SE_DACL_PROTECTED);
- /*
- * copy new SACL if selected
- * or keep old SACL if any
- */
- if ((selection & SACL_SECURITY_INFORMATION) ?
- newhead->sacl : oldhead->sacl) {
- if (selection & SACL_SECURITY_INFORMATION) {
- offsacl = le32_to_cpu(newhead->sacl);
- psacl = (const ACL*)&newattr[offsacl];
- } else {
- offsacl = le32_to_cpu(oldhead->sacl);
- psacl = (const ACL*)&oldattr[offsacl];
- }
- size = le16_to_cpu(psacl->size);
- memcpy(&target[pos], psacl, size);
- targhead->sacl = cpu_to_le32(pos);
- pos += size;
- } else
- targhead->sacl = const_cpu_to_le32(0);
- if (selection & SACL_SECURITY_INFORMATION) {
- control |= newhead->control
- & (SE_SACL_PRESENT
- | SE_SACL_DEFAULTED
- | SE_SACL_PROTECTED);
- if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
- control |= SE_SACL_AUTO_INHERITED;
- } else
- control |= oldhead->control
- & (SE_SACL_PRESENT
- | SE_SACL_DEFAULTED
- | SE_SACL_AUTO_INHERITED
- | SE_SACL_PROTECTED);
- /*
- * copy new OWNER if selected
- * or keep old OWNER if any
- */
- if ((selection & OWNER_SECURITY_INFORMATION) ?
- newhead->owner : oldhead->owner) {
- if (selection & OWNER_SECURITY_INFORMATION) {
- offowner = le32_to_cpu(newhead->owner);
- powner = (const SID*)&newattr[offowner];
- } else {
- offowner = le32_to_cpu(oldhead->owner);
- powner = (const SID*)&oldattr[offowner];
- }
- size = ntfs_sid_size(powner);
- memcpy(&target[pos], powner, size);
- targhead->owner = cpu_to_le32(pos);
- pos += size;
- } else
- targhead->owner = const_cpu_to_le32(0);
- if (selection & OWNER_SECURITY_INFORMATION)
- control |= newhead->control & SE_OWNER_DEFAULTED;
- else
- control |= oldhead->control & SE_OWNER_DEFAULTED;
- /*
- * copy new GROUP if selected
- * or keep old GROUP if any
- */
- if ((selection & GROUP_SECURITY_INFORMATION) ?
- newhead->group : oldhead->group) {
- if (selection & GROUP_SECURITY_INFORMATION) {
- offgroup = le32_to_cpu(newhead->group);
- pgroup = (const SID*)&newattr[offgroup];
- control |= newhead->control
- & SE_GROUP_DEFAULTED;
- } else {
- offgroup = le32_to_cpu(oldhead->group);
- pgroup = (const SID*)&oldattr[offgroup];
- control |= oldhead->control
- & SE_GROUP_DEFAULTED;
- }
- size = ntfs_sid_size(pgroup);
- memcpy(&target[pos], pgroup, size);
- targhead->group = cpu_to_le32(pos);
- pos += size;
- } else
- targhead->group = const_cpu_to_le32(0);
- if (selection & GROUP_SECURITY_INFORMATION)
- control |= newhead->control & SE_GROUP_DEFAULTED;
- else
- control |= oldhead->control & SE_GROUP_DEFAULTED;
- targhead->revision = SECURITY_DESCRIPTOR_REVISION;
- targhead->alignment = 0;
- targhead->control = control;
- ok = !update_secur_descr(vol, target, ni);
- free(target);
- }
- return (ok);
-}
-
-int ntfs_get_inode_security(ntfs_inode *ni, u32 selection,
- char *buf, u32 buflen, u32 *psize)
-{
- int res;
- char *attr;
-
- res = 0;
- if (ni) {
- attr = getsecurityattr(ni->vol, ni);
- if (attr) {
- if (feedsecurityattr(attr, selection,
- buf, buflen, psize)) {
- if (test_nino_flag(ni, v3_Extensions)
- && ni->security_id)
- res = le32_to_cpu(
- ni->security_id);
- else
- res = -1;
- }
- free(attr);
- }
- } else
- errno = EINVAL;
- return (res);
-}
-
-int ntfs_set_inode_security(ntfs_inode *ni, u32 selection, const char *attr)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- int attrsz;
- BOOL missing;
- char *oldattr;
- int res;
-
- res = -1; /* default return */
- if (ni) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
- attrsz = ntfs_attr_size(attr);
- /* if selected, owner and group must be present or defaulted */
- missing = ((selection & OWNER_SECURITY_INFORMATION)
- && !phead->owner
- && !(phead->control & SE_OWNER_DEFAULTED))
- || ((selection & GROUP_SECURITY_INFORMATION)
- && !phead->group
- && !(phead->control & SE_GROUP_DEFAULTED));
- if (!missing
- && (phead->control & SE_SELF_RELATIVE)
- && ntfs_valid_descr(attr, attrsz)) {
- oldattr = getsecurityattr(ni->vol, ni);
- if (oldattr) {
- if (mergesecurityattr(ni->vol, oldattr, attr,
- selection, ni)) {
- res = 0;
- }
- free(oldattr);
- }
- } else
- errno = EINVAL;
- } else
- errno = EINVAL;
- return (res);
-}
-
-/*
- * Return the security descriptor of a file
- * This is intended to be similar to GetFileSecurity() from Win32
- * in order to facilitate the development of portable tools
- *
- * returns zero if unsuccessful (following Win32 conventions)
- * -1 if no securid
- * the securid if any
- *
- * The Win32 API is :
- *
- * BOOL WINAPI GetFileSecurity(
- * __in LPCTSTR lpFileName,
- * __in SECURITY_INFORMATION RequestedInformation,
- * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
- * __in DWORD nLength,
- * __out LPDWORD lpnLengthNeeded
- * );
- *
- */
-
-int ntfs_get_file_security(struct SECURITY_API *scapi,
- const char *path, u32 selection,
- char *buf, u32 buflen, u32 *psize)
-{
- ntfs_inode *ni;
- char *attr;
- int res;
-
- res = 0; /* default return */
- if (scapi && (scapi->magic == MAGIC_API)) {
- ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
- if (ni) {
- attr = getsecurityattr(scapi->security.vol, ni);
- if (attr) {
- if (feedsecurityattr(attr,selection,
- buf,buflen,psize)) {
- if (test_nino_flag(ni, v3_Extensions)
- && ni->security_id)
- res = le32_to_cpu(
- ni->security_id);
- else
- res = -1;
- }
- free(attr);
- }
- ntfs_inode_close(ni);
- } else
- errno = ENOENT;
- if (!res) *psize = 0;
- } else
- errno = EINVAL; /* do not clear *psize */
- return (res);
-}
-
-
-/*
- * Set the security descriptor of a file or directory
- * This is intended to be similar to SetFileSecurity() from Win32
- * in order to facilitate the development of portable tools
- *
- * returns zero if unsuccessful (following Win32 conventions)
- * -1 if no securid
- * the securid if any
- *
- * The Win32 API is :
- *
- * BOOL WINAPI SetFileSecurity(
- * __in LPCTSTR lpFileName,
- * __in SECURITY_INFORMATION SecurityInformation,
- * __in PSECURITY_DESCRIPTOR pSecurityDescriptor
- * );
- */
-
-int ntfs_set_file_security(struct SECURITY_API *scapi,
- const char *path, u32 selection, const char *attr)
-{
- const SECURITY_DESCRIPTOR_RELATIVE *phead;
- ntfs_inode *ni;
- int attrsz;
- BOOL missing;
- char *oldattr;
- int res;
-
- res = 0; /* default return */
- if (scapi && (scapi->magic == MAGIC_API) && attr) {
- phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
- attrsz = ntfs_attr_size(attr);
- /* if selected, owner and group must be present or defaulted */
- missing = ((selection & OWNER_SECURITY_INFORMATION)
- && !phead->owner
- && !(phead->control & SE_OWNER_DEFAULTED))
- || ((selection & GROUP_SECURITY_INFORMATION)
- && !phead->group
- && !(phead->control & SE_GROUP_DEFAULTED));
- if (!missing
- && (phead->control & SE_SELF_RELATIVE)
- && ntfs_valid_descr(attr, attrsz)) {
- ni = ntfs_pathname_to_inode(scapi->security.vol,
- NULL, path);
- if (ni) {
- oldattr = getsecurityattr(scapi->security.vol,
- ni);
- if (oldattr) {
- if (mergesecurityattr(
- scapi->security.vol,
- oldattr, attr,
- selection, ni)) {
- if (test_nino_flag(ni,
- v3_Extensions))
- res = le32_to_cpu(
- ni->security_id);
- else
- res = -1;
- }
- free(oldattr);
- }
- ntfs_inode_close(ni);
- }
- } else
- errno = EINVAL;
- } else
- errno = EINVAL;
- return (res);
-}
-
-
-/*
- * Return the attributes of a file
- * This is intended to be similar to GetFileAttributes() from Win32
- * in order to facilitate the development of portable tools
- *
- * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
- *
- * The Win32 API is :
- *
- * DWORD WINAPI GetFileAttributes(
- * __in LPCTSTR lpFileName
- * );
- */
-
-int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
-{
- ntfs_inode *ni;
- s32 attrib;
-
- attrib = -1; /* default return */
- if (scapi && (scapi->magic == MAGIC_API) && path) {
- ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
- if (ni) {
- attrib = ntfs_get_inode_attributes(ni);
- ntfs_inode_close(ni);
- } else
- errno = ENOENT;
- } else
- errno = EINVAL; /* do not clear *psize */
- return (attrib);
-}
-
-
-/*
- * Set attributes to a file or directory
- * This is intended to be similar to SetFileAttributes() from Win32
- * in order to facilitate the development of portable tools
- *
- * Only a few flags can be set (same list as Win32)
- *
- * returns zero if unsuccessful (following Win32 conventions)
- * nonzero if successful
- *
- * The Win32 API is :
- *
- * BOOL WINAPI SetFileAttributes(
- * __in LPCTSTR lpFileName,
- * __in DWORD dwFileAttributes
- * );
- */
-
-BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
- const char *path, s32 attrib)
-{
- ntfs_inode *ni;
- int res;
-
- res = 0; /* default return */
- if (scapi && (scapi->magic == MAGIC_API) && path) {
- ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
- if (ni) {
- /* Win32 convention here : -1 means successful */
- if (!ntfs_set_inode_attributes(ni, attrib))
- res = -1;
- if (ntfs_inode_close(ni))
- res = 0;
- } else
- errno = ENOENT;
- }
- return (res);
-}
-
-
-BOOL ntfs_read_directory(struct SECURITY_API *scapi,
- const char *path, ntfs_filldir_t callback, void *context)
-{
- ntfs_inode *ni;
- BOOL ok;
- s64 pos;
-
- ok = FALSE; /* default return */
- if (scapi && (scapi->magic == MAGIC_API) && callback) {
- ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
- if (ni) {
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
- pos = 0;
- ntfs_readdir(ni,&pos,context,callback);
- ok = !ntfs_inode_close(ni);
- } else {
- ntfs_inode_close(ni);
- errno = ENOTDIR;
- }
- } else
- errno = ENOENT;
- } else
- errno = EINVAL; /* do not clear *psize */
- return (ok);
-}
-
-/*
- * read $SDS (for auditing security data)
- *
- * Returns the number or read bytes, or -1 if there is an error
- */
-
-int ntfs_read_sds(struct SECURITY_API *scapi,
- char *buf, u32 size, u32 offset)
-{
- int got;
-
- got = -1; /* default return */
- if (scapi && (scapi->magic == MAGIC_API)) {
- if (scapi->security.vol->secure_ni)
- got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
- STREAM_SDS, 4, buf, size, offset);
- else
- errno = EOPNOTSUPP;
- } else
- errno = EINVAL;
- return (got);
-}
-
-/*
- * read $SII (for auditing security data)
- *
- * Returns next entry, or NULL if there is an error
- */
-
-INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
- INDEX_ENTRY *entry)
-{
- SII_INDEX_KEY key;
- INDEX_ENTRY *ret;
- BOOL found;
- ntfs_index_context *xsii;
-
- ret = (INDEX_ENTRY*)NULL; /* default return */
- if (scapi && (scapi->magic == MAGIC_API)) {
- xsii = scapi->security.vol->secure_xsii;
- if (xsii) {
- if (!entry) {
- key.security_id = const_cpu_to_le32(0);
- found = !ntfs_index_lookup((char*)&key,
- sizeof(SII_INDEX_KEY), xsii);
- /* not supposed to find */
- if (!found && (errno == ENOENT))
- ret = xsii->entry;
- } else
- ret = ntfs_index_next(entry,xsii);
- if (!ret)
- errno = ENODATA;
- } else
- errno = EOPNOTSUPP;
- } else
- errno = EINVAL;
- return (ret);
-}
-
-/*
- * read $SDH (for auditing security data)
- *
- * Returns next entry, or NULL if there is an error
- */
-
-INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
- INDEX_ENTRY *entry)
-{
- SDH_INDEX_KEY key;
- INDEX_ENTRY *ret;
- BOOL found;
- ntfs_index_context *xsdh;
-
- ret = (INDEX_ENTRY*)NULL; /* default return */
- if (scapi && (scapi->magic == MAGIC_API)) {
- xsdh = scapi->security.vol->secure_xsdh;
- if (xsdh) {
- if (!entry) {
- key.hash = const_cpu_to_le32(0);
- key.security_id = const_cpu_to_le32(0);
- found = !ntfs_index_lookup((char*)&key,
- sizeof(SDH_INDEX_KEY), xsdh);
- /* not supposed to find */
- if (!found && (errno == ENOENT))
- ret = xsdh->entry;
- } else
- ret = ntfs_index_next(entry,xsdh);
- if (!ret)
- errno = ENODATA;
- } else errno = ENOTSUP;
- } else
- errno = EINVAL;
- return (ret);
-}
-
-/*
- * Get the mapped user SID
- * A buffer of 40 bytes has to be supplied
- *
- * returns the size of the SID, or zero and errno set if not found
- */
-
-int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
-{
- const SID *usid;
- BIGSID defusid;
- int size;
-
- size = 0;
- if (scapi && (scapi->magic == MAGIC_API)) {
- usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
- if (usid) {
- size = ntfs_sid_size(usid);
- memcpy(buf,usid,size);
- } else
- errno = ENODATA;
- } else
- errno = EINVAL;
- return (size);
-}
-
-/*
- * Get the mapped group SID
- * A buffer of 40 bytes has to be supplied
- *
- * returns the size of the SID, or zero and errno set if not found
- */
-
-int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
-{
- const SID *gsid;
- BIGSID defgsid;
- int size;
-
- size = 0;
- if (scapi && (scapi->magic == MAGIC_API)) {
- gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
- if (gsid) {
- size = ntfs_sid_size(gsid);
- memcpy(buf,gsid,size);
- } else
- errno = ENODATA;
- } else
- errno = EINVAL;
- return (size);
-}
-
-/*
- * Get the user mapped to a SID
- *
- * returns the uid, or -1 if not found
- */
-
-int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
-{
- int uid;
-
- uid = -1;
- if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
- if (ntfs_same_sid(usid,adminsid))
- uid = 0;
- else {
- uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
- if (!uid) {
- uid = -1;
- errno = ENODATA;
- }
- }
- } else
- errno = EINVAL;
- return (uid);
-}
-
-/*
- * Get the group mapped to a SID
- *
- * returns the uid, or -1 if not found
- */
-
-int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
-{
- int gid;
-
- gid = -1;
- if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
- if (ntfs_same_sid(gsid,adminsid))
- gid = 0;
- else {
- gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
- if (!gid) {
- gid = -1;
- errno = ENODATA;
- }
- }
- } else
- errno = EINVAL;
- return (gid);
-}
-
-/*
- * Initializations before calling ntfs_get_file_security()
- * ntfs_set_file_security() and ntfs_read_directory()
- *
- * Only allowed for root
- *
- * Returns an (obscured) struct SECURITY_API* needed for further calls
- * NULL if not root (EPERM) or device is mounted (EBUSY)
- */
-
-struct SECURITY_API *ntfs_initialize_file_security(const char *device,
- unsigned long flags)
-{
- ntfs_volume *vol;
- unsigned long mntflag;
- int mnt;
- struct SECURITY_API *scapi;
- struct SECURITY_CONTEXT *scx;
-
- scapi = (struct SECURITY_API*)NULL;
- mnt = ntfs_check_if_mounted(device, &mntflag);
- if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
- vol = ntfs_mount(device, flags);
- if (vol) {
- scapi = (struct SECURITY_API*)
- ntfs_malloc(sizeof(struct SECURITY_API));
- if (!ntfs_volume_get_free_space(vol)
- && scapi) {
- scapi->magic = MAGIC_API;
- scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
- scx = &scapi->security;
- scx->vol = vol;
- scx->uid = getuid();
- scx->gid = getgid();
- scx->pseccache = &scapi->seccache;
- scx->vol->secure_flags = 0;
- /* accept no mapping and no $Secure */
- ntfs_build_mapping(scx,(const char*)NULL,TRUE);
- ntfs_open_secure(vol);
- } else {
- if (scapi)
- free(scapi);
- else
- errno = ENOMEM;
- mnt = ntfs_umount(vol,FALSE);
- scapi = (struct SECURITY_API*)NULL;
- }
- }
- } else
- if (getuid())
- errno = EPERM;
- else
- errno = EBUSY;
- return (scapi);
-}
-
-/*
- * Leaving after ntfs_initialize_file_security()
- *
- * Returns FALSE if FAILED
- */
-
-BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
-{
- int ok;
- ntfs_volume *vol;
-
- ok = FALSE;
- if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
- vol = scapi->security.vol;
- ntfs_close_secure(&scapi->security);
- free(scapi);
- if (!ntfs_umount(vol, 0))
- ok = TRUE;
- }
- return (ok);
-}
-