From: Eric Biggers Date: Sat, 1 Sep 2012 05:45:14 +0000 (-0500) Subject: Get rid of code clone from libntfs-3g X-Git-Tag: v1.0.0~16 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=ae01a7b97e80bc86a53d9c4a377116fb293c3655 Get rid of code clone from libntfs-3g --- diff --git a/Makefile.am b/Makefile.am index 9c36d5a1..f05e58dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,13 +53,6 @@ libwim_la_SOURCES = \ src/xpress-decomp.c \ src/xpress.h -if WITH_NTFS_3G -if !WITH_NEW_NTFS_3G -libwim_la_SOURCES += src/ntfs-3g_security.c -endif -endif - - EXTRA_libwim_la_SOURCES = src/sha1-ssse3.asm libwim_la_DEPENDENCIES = $(SSSE3_SHA1_OBJ) STRIP_FPIC = sh $(top_srcdir)/build-aux/strip_fPIC.sh diff --git a/README b/README index 91deb1c0..364cb77f 100644 --- a/README +++ b/README @@ -94,11 +94,7 @@ server for PXE booting. See the main page `doc/mkwinpeiso.1' for more details. * libntfs-3g Unless configured with --without-ntfs-3g, wimlib requires the library - and headers for libntfs-3g to be installed. Currently, the libntfs-3g - version dated 2012-1-15 is required because I've cloned some of the code - from the library, and it needs to compiled against the same version. - I'm hoping to be able to use future version of libntfs-3g without code - cloning after submitting some patches, however. + and headers for libntfs-3g to be installed. * cdrkit (optional) * mtools (optional) diff --git a/config.h.in b/config.h.in index ecd767d3..db9f0e2d 100644 --- a/config.h.in +++ b/config.h.in @@ -106,9 +106,6 @@ /* Define to 1 if using libcrypto SHA1 */ #undef WITH_LIBCRYPTO -/* Define to 1 to use patched upstream NTFS-3g instead of version 2012-1-15 */ -#undef WITH_NEW_NTFS_3G - /* Define to 1 to enable support for NTFS-specific information */ #undef WITH_NTFS_3G diff --git a/configure.ac b/configure.ac index 5dcc2ff1..c2156614 100644 --- a/configure.ac +++ b/configure.ac @@ -163,25 +163,6 @@ AM_CONDITIONAL([WITH_NTFS_3G], [test "x$WITH_NTFS_3G" = "xyes"]) AC_SUBST([LIBNTFS_3G_LDADD], [$LIBNTFS_3G_LDADD]) AC_SUBST([LIBNTFS_3G_CFLAGS], [$LIBNTFS_3G_CFLAGS]) -AC_MSG_CHECKING([whether to use patched upstream libntfs-3g]) -AC_ARG_WITH([new-ntfs-3g], - [AS_HELP_STRING([--with-new-ntfs-3g], [build using patched upstream - NTFS-3g instead of version 2012-1-15])], - [WITH_NEW_NTFS_3G=$withval], - [WITH_NEW_NTFS_3G=no] - ) - -AC_MSG_RESULT([$WITH_NEW_NTFS_3G]) -if test "x$WITH_NEW_NTFS_3G" = "xyes"; then - if test "x$WITH_NTFS_3G" != "xyes"; then - AC_MSG_ERROR([Cannot use new NTFS-3g if configuring - --without-ntfs-3g]) - fi - AC_DEFINE([WITH_NEW_NTFS_3G], [1], [Define to 1 to use patched upstream - NTFS-3g instead of version 2012-1-15]) -fi -AM_CONDITIONAL([WITH_NEW_NTFS_3G], [test "x$WITH_NEW_NTFS_3G" = "xyes"]) - AC_MSG_CHECKING([whether to include support for mounting WIMs]) AC_ARG_WITH([fuse], AS_HELP_STRING([--without-fuse], [build without libfuse. diff --git a/src/ntfs-3g_security.c b/src/ntfs-3g_security.c deleted file mode 100644 index 7c8dec3c..00000000 --- a/src/ntfs-3g_security.c +++ /dev/null @@ -1,5163 +0,0 @@ -/** - * 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 - -#include -#include -#include -#include -#include -#ifdef HAVE_SETXATTR -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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); -} -