"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/libntfs-3g/security.c" (23 Mar 2017, 141553 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "security.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

    1 /**
    2  * security.c - Handling security/ACLs in NTFS.  Originated from the Linux-NTFS project.
    3  *
    4  * Copyright (c) 2004 Anton Altaparmakov
    5  * Copyright (c) 2005-2006 Szabolcs Szakacsits
    6  * Copyright (c) 2006 Yura Pakhuchiy
    7  * Copyright (c) 2007-2015 Jean-Pierre Andre
    8  *
    9  * This program/include file is free software; you can redistribute it and/or
   10  * modify it under the terms of the GNU General Public License as published
   11  * by the Free Software Foundation; either version 2 of the License, or
   12  * (at your option) any later version.
   13  *
   14  * This program/include file is distributed in the hope that it will be
   15  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
   16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17  * GNU General Public License for more details.
   18  *
   19  * You should have received a copy of the GNU General Public License
   20  * along with this program (in the main directory of the NTFS-3G
   21  * distribution in the file COPYING); if not, write to the Free Software
   22  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23  */
   24 
   25 #ifdef HAVE_CONFIG_H
   26 #include "config.h"
   27 #endif
   28 
   29 #ifdef HAVE_STDIO_H
   30 #include <stdio.h>
   31 #endif
   32 #ifdef HAVE_STDLIB_H
   33 #include <stdlib.h>
   34 #endif
   35 #ifdef HAVE_STRING_H
   36 #include <string.h>
   37 #endif
   38 #ifdef HAVE_ERRNO_H
   39 #include <errno.h>
   40 #endif
   41 #ifdef HAVE_FCNTL_H
   42 #include <fcntl.h>
   43 #endif
   44 #ifdef HAVE_SYS_STAT_H
   45 #include <sys/stat.h>
   46 #endif
   47 
   48 #include <unistd.h>
   49 #include <pwd.h>
   50 #include <grp.h>
   51 
   52 #include "compat.h"
   53 #include "param.h"
   54 #include "types.h"
   55 #include "layout.h"
   56 #include "attrib.h"
   57 #include "index.h"
   58 #include "dir.h"
   59 #include "bitmap.h"
   60 #include "security.h"
   61 #include "acls.h"
   62 #include "cache.h"
   63 #include "misc.h"
   64 #include "xattrs.h"
   65 
   66 /*
   67  *  JPA NTFS constants or structs
   68  *  should be moved to layout.h
   69  */
   70 
   71 #define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
   72 #define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
   73 #define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
   74 #define FIRST_SECURITY_ID 0x100 /* Lowest security id */
   75 
   76     /* Mask for attributes which can be forced */
   77 #define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY     \
   78                 | FILE_ATTR_HIDDEN  \
   79                 | FILE_ATTR_SYSTEM  \
   80                 | FILE_ATTR_ARCHIVE \
   81                 | FILE_ATTR_TEMPORARY   \
   82                 | FILE_ATTR_OFFLINE \
   83                 | FILE_ATTR_NOT_CONTENT_INDEXED )
   84 
   85 struct SII {        /* this is an image of an $SII index entry */
   86     le16 offs;
   87     le16 size;
   88     le32 fill1;
   89     le16 indexsz;
   90     le16 indexksz;
   91     le16 flags;
   92     le16 fill2;
   93     le32 keysecurid;
   94 
   95     /* did not find official description for the following */
   96     le32 hash;
   97     le32 securid;
   98     le32 dataoffsl; /* documented as badly aligned */
   99     le32 dataoffsh;
  100     le32 datasize;
  101 } ;
  102 
  103 struct SDH {        /* this is an image of an $SDH index entry */
  104     le16 offs;
  105     le16 size;
  106     le32 fill1;
  107     le16 indexsz;
  108     le16 indexksz;
  109     le16 flags;
  110     le16 fill2;
  111     le32 keyhash;
  112     le32 keysecurid;
  113 
  114     /* did not find official description for the following */
  115     le32 hash;
  116     le32 securid;
  117     le32 dataoffsl;
  118     le32 dataoffsh;
  119     le32 datasize;
  120     le32 fill3;
  121     } ;
  122 
  123 /*
  124  *  A few useful constants
  125  */
  126 
  127 static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
  128                  const_cpu_to_le16('S'),
  129                  const_cpu_to_le16('I'),   
  130                  const_cpu_to_le16('I'),   
  131                  const_cpu_to_le16(0) };
  132 static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
  133                  const_cpu_to_le16('S'),
  134                  const_cpu_to_le16('D'),
  135                  const_cpu_to_le16('H'),
  136                  const_cpu_to_le16(0) };
  137 
  138 /*
  139  *      null SID (S-1-0-0)
  140  */
  141 
  142 extern const SID *nullsid;
  143 
  144 /*
  145  * The zero GUID.
  146  */
  147 
  148 static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
  149         const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
  150 static const GUID *const zero_guid = &__zero_guid;
  151 
  152 /**
  153  * ntfs_guid_is_zero - check if a GUID is zero
  154  * @guid:   [IN] guid to check
  155  *
  156  * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
  157  * and FALSE otherwise.
  158  */
  159 BOOL ntfs_guid_is_zero(const GUID *guid)
  160 {
  161     return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
  162 }
  163 
  164 /**
  165  * ntfs_guid_to_mbs - convert a GUID to a multi byte string
  166  * @guid:   [IN]  guid to convert
  167  * @guid_str:   [OUT] string in which to return the GUID (optional)
  168  *
  169  * Convert the GUID pointed to by @guid to a multi byte string of the form
  170  * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".  Therefore, @guid_str (if not NULL)
  171  * needs to be able to store at least 37 bytes.
  172  *
  173  * If @guid_str is not NULL it will contain the converted GUID on return.  If
  174  * it is NULL a string will be allocated and this will be returned.  The caller
  175  * is responsible for free()ing the string in that case.
  176  *
  177  * On success return the converted string and on failure return NULL with errno
  178  * set to the error code.
  179  */
  180 char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
  181 {
  182     char *_guid_str;
  183     int res;
  184 
  185     if (!guid) {
  186         errno = EINVAL;
  187         return NULL;
  188     }
  189     _guid_str = guid_str;
  190     if (!_guid_str) {
  191         _guid_str = (char*)ntfs_malloc(37);
  192         if (!_guid_str)
  193             return _guid_str;
  194     }
  195     res = snprintf(_guid_str, 37,
  196             "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
  197             (unsigned int)le32_to_cpu(guid->data1),
  198             le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
  199             guid->data4[0], guid->data4[1],
  200             guid->data4[2], guid->data4[3], guid->data4[4],
  201             guid->data4[5], guid->data4[6], guid->data4[7]);
  202     if (res == 36)
  203         return _guid_str;
  204     if (!guid_str)
  205         free(_guid_str);
  206     errno = EINVAL;
  207     return NULL;
  208 }
  209 
  210 /**
  211  * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
  212  * @sid:    [IN]  SID for which to determine the maximum string size
  213  *
  214  * Determine the maximum multi byte string size in bytes which is needed to
  215  * store the standard textual representation of the SID pointed to by @sid.
  216  * See ntfs_sid_to_mbs(), below.
  217  *
  218  * On success return the maximum number of bytes needed to store the multi byte
  219  * string and on failure return -1 with errno set to the error code.
  220  */
  221 int ntfs_sid_to_mbs_size(const SID *sid)
  222 {
  223     int size, i;
  224 
  225     if (!ntfs_valid_sid(sid)) {
  226         errno = EINVAL;
  227         return -1;
  228     }
  229     /* Start with "S-". */
  230     size = 2;
  231     /*
  232      * Add the SID_REVISION.  Hopefully the compiler will optimize this
  233      * away as SID_REVISION is a constant.
  234      */
  235     for (i = SID_REVISION; i > 0; i /= 10)
  236         size++;
  237     /* Add the "-". */
  238     size++;
  239     /*
  240      * Add the identifier authority.  If it needs to be in decimal, the
  241      * maximum is 2^32-1 = 4294967295 = 10 characters.  If it needs to be
  242      * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
  243      */
  244     if (!sid->identifier_authority.high_part)
  245         size += 10;
  246     else
  247         size += 14;
  248     /*
  249      * Finally, add the sub authorities.  For each we have a "-" followed
  250      * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
  251      */
  252     size += (1 + 10) * sid->sub_authority_count;
  253     /* We need the zero byte at the end, too. */
  254     size++;
  255     return size * sizeof(char);
  256 }
  257 
  258 /**
  259  * ntfs_sid_to_mbs - convert a SID to a multi byte string
  260  * @sid:        [IN]  SID to convert
  261  * @sid_str:        [OUT] string in which to return the SID (optional)
  262  * @sid_str_size:   [IN]  size in bytes of @sid_str
  263  *
  264  * Convert the SID pointed to by @sid to its standard textual representation.
  265  * @sid_str (if not NULL) needs to be able to store at least
  266  * ntfs_sid_to_mbs_size() bytes.  @sid_str_size is the size in bytes of
  267  * @sid_str if @sid_str is not NULL.
  268  *
  269  * The standard textual representation of the SID is of the form:
  270  *  S-R-I-S-S...
  271  * Where:
  272  *    - The first "S" is the literal character 'S' identifying the following
  273  *  digits as a SID.
  274  *    - R is the revision level of the SID expressed as a sequence of digits
  275  *  in decimal.
  276  *    - I is the 48-bit identifier_authority, expressed as digits in decimal,
  277  *  if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
  278  *    - S... is one or more sub_authority values, expressed as digits in
  279  *  decimal.
  280  *
  281  * If @sid_str is not NULL it will contain the converted SUID on return.  If it
  282  * is NULL a string will be allocated and this will be returned.  The caller is
  283  * responsible for free()ing the string in that case.
  284  *
  285  * On success return the converted string and on failure return NULL with errno
  286  * set to the error code.
  287  */
  288 char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
  289 {
  290     u64 u;
  291     le32 leauth;
  292     char *s;
  293     int i, j, cnt;
  294 
  295     /*
  296      * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
  297      * check @sid, too.  8 is the minimum SID string size.
  298      */
  299     if (sid_str && (sid_str_size < 8 || !ntfs_valid_sid(sid))) {
  300         errno = EINVAL;
  301         return NULL;
  302     }
  303     /* Allocate string if not provided. */
  304     if (!sid_str) {
  305         cnt = ntfs_sid_to_mbs_size(sid);
  306         if (cnt < 0)
  307             return NULL;
  308         s = (char*)ntfs_malloc(cnt);
  309         if (!s)
  310             return s;
  311         sid_str = s;
  312         /* So we know we allocated it. */
  313         sid_str_size = 0;
  314     } else {
  315         s = sid_str;
  316         cnt = sid_str_size;
  317     }
  318     /* Start with "S-R-". */
  319     i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
  320     if (i < 0 || i >= cnt)
  321         goto err_out;
  322     s += i;
  323     cnt -= i;
  324     /* Add the identifier authority. */
  325     for (u = i = 0, j = 40; i < 6; i++, j -= 8)
  326         u += (u64)sid->identifier_authority.value[i] << j;
  327     if (!sid->identifier_authority.high_part)
  328         i = snprintf(s, cnt, "%lu", (unsigned long)u);
  329     else
  330         i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
  331     if (i < 0 || i >= cnt)
  332         goto err_out;
  333     s += i;
  334     cnt -= i;
  335     /* Finally, add the sub authorities. */
  336     for (j = 0; j < sid->sub_authority_count; j++) {
  337         leauth = sid->sub_authority[j];
  338         i = snprintf(s, cnt, "-%u", (unsigned int)
  339                 le32_to_cpu(leauth));
  340         if (i < 0 || i >= cnt)
  341             goto err_out;
  342         s += i;
  343         cnt -= i;
  344     }
  345     return sid_str;
  346 err_out:
  347     if (i >= cnt)
  348         i = EMSGSIZE;
  349     else
  350         i = errno;
  351     if (!sid_str_size)
  352         free(sid_str);
  353     errno = i;
  354     return NULL;
  355 }
  356 
  357 /**
  358  * ntfs_generate_guid - generatates a random current guid.
  359  * @guid:   [OUT]   pointer to a GUID struct to hold the generated guid.
  360  *
  361  * perhaps not a very good random number generator though...
  362  */
  363 void ntfs_generate_guid(GUID *guid)
  364 {
  365     unsigned int i;
  366     u8 *p = (u8 *)guid;
  367 
  368     /* this is called at most once from mkntfs */
  369     srandom(time((time_t*)NULL) ^ (getpid() << 16));
  370     for (i = 0; i < sizeof(GUID); i++) {
  371         p[i] = (u8)(random() & 0xFF);
  372         if (i == 7)
  373             p[7] = (p[7] & 0x0F) | 0x40;
  374         if (i == 8)
  375             p[8] = (p[8] & 0x3F) | 0x80;
  376     }
  377 }
  378 
  379 /**
  380  * ntfs_security_hash - calculate the hash of a security descriptor
  381  * @sd:         self-relative security descriptor whose hash to calculate
  382  * @length:     size in bytes of the security descritor @sd
  383  *
  384  * Calculate the hash of the self-relative security descriptor @sd of length
  385  * @length bytes.
  386  *
  387  * This hash is used in the $Secure system file as the primary key for the $SDH
  388  * index and is also stored in the header of each security descriptor in the
  389  * $SDS data stream as well as in the index data of both the $SII and $SDH
  390  * indexes.  In all three cases it forms part of the SDS_ENTRY_HEADER
  391  * structure.
  392  *
  393  * Return the calculated security hash in little endian.
  394  */
  395 le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
  396 {
  397     const le32 *pos = (const le32*)sd;
  398     const le32 *end = pos + (len >> 2);
  399     u32 hash = 0;
  400 
  401     while (pos < end) {
  402         hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
  403         pos++;
  404     }
  405     return cpu_to_le32(hash);
  406 }
  407 
  408 /*
  409  *  Get the first entry of current index block
  410  *  cut and pasted form ntfs_ie_get_first() in index.c
  411  */
  412 
  413 static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
  414 {
  415     return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
  416 }
  417 
  418 /*
  419  *      Stuff a 256KB block into $SDS before writing descriptors
  420  *  into the block.
  421  *
  422  *  This prevents $SDS from being automatically declared as sparse
  423  *  when the second copy of the first security descriptor is written
  424  *  256KB further ahead.
  425  *
  426  *  Having $SDS declared as a sparse file is not wrong by itself
  427  *  and chkdsk leaves it as a sparse file. It does however complain
  428  *  and add a sparse flag (0x0200) into field file_attributes of
  429  *  STANDARD_INFORMATION of $Secure. This probably means that a
  430  *  sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
  431  *  files (FILE_ATTR_SPARSE_FILE).
  432  *
  433  *  Windows normally does not convert to sparse attribute or sparse
  434  *  file. Stuffing is just a way to get to the same result.
  435  */
  436 
  437 static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
  438 {
  439     int res;
  440     int written;
  441     unsigned long total;
  442     char *stuff;
  443 
  444     res = 0;
  445     total = 0;
  446     stuff = (char*)ntfs_malloc(STUFFSZ);
  447     if (stuff) {
  448         memset(stuff, 0, STUFFSZ);
  449         do {
  450             written = ntfs_attr_data_write(vol->secure_ni,
  451                 STREAM_SDS, 4, stuff, STUFFSZ, offs);
  452             if (written == STUFFSZ) {
  453                 total += STUFFSZ;
  454                 offs += STUFFSZ;
  455             } else {
  456                 errno = ENOSPC;
  457                 res = -1;
  458             }
  459         } while (!res && (total < ALIGN_SDS_BLOCK));
  460         free(stuff);
  461     } else {
  462         errno = ENOMEM;
  463         res = -1;
  464     }
  465     return (res);
  466 }
  467 
  468 /*
  469  *      Enter a new security descriptor into $Secure (data only)
  470  *      it has to be written twice with an offset of 256KB
  471  *
  472  *  Should only be called by entersecurityattr() to ensure consistency
  473  *
  474  *  Returns zero if sucessful
  475  */
  476 
  477 static int entersecurity_data(ntfs_volume *vol,
  478             const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
  479             le32 hash, le32 keyid, off_t offs, int gap)
  480 {
  481     int res;
  482     int written1;
  483     int written2;
  484     char *fullattr;
  485     int fullsz;
  486     SECURITY_DESCRIPTOR_HEADER *phsds;
  487 
  488     res = -1;
  489     fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
  490     fullattr = (char*)ntfs_malloc(fullsz);
  491     if (fullattr) {
  492             /*
  493              * Clear the gap from previous descriptor
  494              * this could be useful for appending the second
  495              * copy to the end of file. When creating a new
  496              * 256K block, the gap is cleared while writing
  497              * the first copy
  498              */
  499         if (gap)
  500             memset(fullattr,0,gap);
  501         memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
  502                 attr,attrsz);
  503         phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
  504         phsds->hash = hash;
  505         phsds->security_id = keyid;
  506         phsds->offset = cpu_to_le64(offs);
  507         phsds->length = cpu_to_le32(fullsz - gap);
  508         written1 = ntfs_attr_data_write(vol->secure_ni,
  509             STREAM_SDS, 4, fullattr, fullsz,
  510             offs - gap);
  511         written2 = ntfs_attr_data_write(vol->secure_ni,
  512             STREAM_SDS, 4, fullattr, fullsz,
  513             offs - gap + ALIGN_SDS_BLOCK);
  514         if ((written1 == fullsz)
  515              && (written2 == written1)) {
  516             /*
  517              * Make sure the data size for $SDS marks the end
  518              * of the last security attribute. Windows uses
  519              * this to determine where the next attribute will
  520              * be written, which causes issues if chkdsk had
  521              * previously deleted the last entries without
  522              * adjusting the size.
  523              */
  524             res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS,
  525                 4, offs - gap + ALIGN_SDS_BLOCK + fullsz);
  526         } else
  527             errno = ENOSPC;
  528         free(fullattr);
  529     } else
  530         errno = ENOMEM;
  531     return (res);
  532 }
  533 
  534 /*
  535  *  Enter a new security descriptor in $Secure (indexes only)
  536  *
  537  *  Should only be called by entersecurityattr() to ensure consistency
  538  *
  539  *  Returns zero if sucessful
  540  */
  541 
  542 static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
  543             le32 hash, le32 keyid, off_t offs)
  544 {
  545     union {
  546         struct {
  547             le32 dataoffsl;
  548             le32 dataoffsh;
  549         } parts;
  550         le64 all;
  551     } realign;
  552     int res;
  553     ntfs_index_context *xsii;
  554     ntfs_index_context *xsdh;
  555     struct SII newsii;
  556     struct SDH newsdh;
  557 
  558     res = -1;
  559                 /* enter a new $SII record */
  560 
  561     xsii = vol->secure_xsii;
  562     ntfs_index_ctx_reinit(xsii);
  563     newsii.offs = const_cpu_to_le16(20);
  564     newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
  565     newsii.fill1 = const_cpu_to_le32(0);
  566     newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
  567     newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
  568     newsii.flags = const_cpu_to_le16(0);
  569     newsii.fill2 = const_cpu_to_le16(0);
  570     newsii.keysecurid = keyid;
  571     newsii.hash = hash;
  572     newsii.securid = keyid;
  573     realign.all = cpu_to_le64(offs);
  574     newsii.dataoffsh = realign.parts.dataoffsh;
  575     newsii.dataoffsl = realign.parts.dataoffsl;
  576     newsii.datasize = cpu_to_le32(attrsz
  577              + sizeof(SECURITY_DESCRIPTOR_HEADER));
  578     if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
  579 
  580         /* enter a new $SDH record */
  581 
  582         xsdh = vol->secure_xsdh;
  583         ntfs_index_ctx_reinit(xsdh);
  584         newsdh.offs = const_cpu_to_le16(24);
  585         newsdh.size = const_cpu_to_le16(
  586             sizeof(SECURITY_DESCRIPTOR_HEADER));
  587         newsdh.fill1 = const_cpu_to_le32(0);
  588         newsdh.indexsz = const_cpu_to_le16(
  589                 sizeof(struct SDH));
  590         newsdh.indexksz = const_cpu_to_le16(
  591                 sizeof(SDH_INDEX_KEY));
  592         newsdh.flags = const_cpu_to_le16(0);
  593         newsdh.fill2 = const_cpu_to_le16(0);
  594         newsdh.keyhash = hash;
  595         newsdh.keysecurid = keyid;
  596         newsdh.hash = hash;
  597         newsdh.securid = keyid;
  598         newsdh.dataoffsh = realign.parts.dataoffsh;
  599         newsdh.dataoffsl = realign.parts.dataoffsl;
  600         newsdh.datasize = cpu_to_le32(attrsz
  601              + sizeof(SECURITY_DESCRIPTOR_HEADER));
  602                            /* special filler value, Windows generally */
  603                            /* fills with 0x00490049, sometimes with zero */
  604         newsdh.fill3 = const_cpu_to_le32(0x00490049);
  605         if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
  606             res = 0;
  607     }
  608     return (res);
  609 }
  610 
  611 /*
  612  *  Enter a new security descriptor in $Secure (data and indexes)
  613  *  Returns id of entry, or zero if there is a problem.
  614  *  (should not be called for NTFS version < 3.0)
  615  *
  616  *  important : calls have to be serialized, however no locking is
  617  *  needed while fuse is not multithreaded
  618  */
  619 
  620 static le32 entersecurityattr(ntfs_volume *vol,
  621             const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
  622             le32 hash)
  623 {
  624     union {
  625         struct {
  626             le32 dataoffsl;
  627             le32 dataoffsh;
  628         } parts;
  629         le64 all;
  630     } realign;
  631     le32 securid;
  632     le32 keyid;
  633     u32 newkey;
  634     off_t offs;
  635     int gap;
  636     int size;
  637     BOOL found;
  638     struct SII *psii;
  639     INDEX_ENTRY *entry;
  640     INDEX_ENTRY *next;
  641     ntfs_index_context *xsii;
  642     int retries;
  643     ntfs_attr *na;
  644     int olderrno;
  645 
  646     /* find the first available securid beyond the last key */
  647     /* in $Secure:$SII. This also determines the first */
  648     /* available location in $Secure:$SDS, as this stream */
  649     /* is always appended to and the id's are allocated */
  650     /* in sequence */
  651 
  652     securid = const_cpu_to_le32(0);
  653     xsii = vol->secure_xsii;
  654     ntfs_index_ctx_reinit(xsii);
  655     offs = size = 0;
  656     keyid = const_cpu_to_le32(-1);
  657     olderrno = errno;
  658     found = !ntfs_index_lookup((char*)&keyid,
  659                    sizeof(SII_INDEX_KEY), xsii);
  660     if (!found && (errno != ENOENT)) {
  661         ntfs_log_perror("Inconsistency in index $SII");
  662         psii = (struct SII*)NULL;
  663     } else {
  664             /* restore errno to avoid misinterpretation */
  665         errno = olderrno;
  666         entry = xsii->entry;
  667         psii = (struct SII*)xsii->entry;
  668     }
  669     if (psii) {
  670         /*
  671          * Get last entry in block, but must get first one
  672          * one first, as we should already be beyond the
  673          * last one. For some reason the search for the last
  674          * entry sometimes does not return the last block...
  675          * we assume this can only happen in root block
  676          */
  677         if (xsii->is_in_root)
  678             entry = ntfs_ie_get_first
  679                 ((INDEX_HEADER*)&xsii->ir->index);
  680         else
  681             entry = ntfs_ie_get_first
  682                 ((INDEX_HEADER*)&xsii->ib->index);
  683         /*
  684          * All index blocks should be at least half full
  685          * so there always is a last entry but one,
  686          * except when creating the first entry in index root.
  687          * This was however found not to be true : chkdsk
  688          * sometimes deletes all the (unused) keys in the last
  689          * index block without rebalancing the tree.
  690          * When this happens, a new search is restarted from
  691          * the smallest key.
  692          */
  693         keyid = const_cpu_to_le32(0);
  694         retries = 0;
  695         while (entry) {
  696             next = ntfs_index_next(entry,xsii);
  697             if (next) { 
  698                 psii = (struct SII*)next;
  699                     /* save last key and */
  700                     /* available position */
  701                 keyid = psii->keysecurid;
  702                 realign.parts.dataoffsh
  703                          = psii->dataoffsh;
  704                 realign.parts.dataoffsl
  705                          = psii->dataoffsl;
  706                 offs = le64_to_cpu(realign.all);
  707                 size = le32_to_cpu(psii->datasize);
  708             }
  709             entry = next;
  710             if (!entry && !keyid && !retries) {
  711                 /* search failed, retry from smallest key */
  712                 ntfs_index_ctx_reinit(xsii);
  713                 found = !ntfs_index_lookup((char*)&keyid,
  714                            sizeof(SII_INDEX_KEY), xsii);
  715                 if (!found && (errno != ENOENT)) {
  716                     ntfs_log_perror("Index $SII is broken");
  717                     psii = (struct SII*)NULL;
  718                 } else {
  719                         /* restore errno */
  720                     errno = olderrno;
  721                     entry = xsii->entry;
  722                     psii = (struct SII*)entry;
  723                 }
  724                 if (psii
  725                     && !(psii->flags & INDEX_ENTRY_END)) {
  726                         /* save first key and */
  727                         /* available position */
  728                     keyid = psii->keysecurid;
  729                     realign.parts.dataoffsh
  730                              = psii->dataoffsh;
  731                     realign.parts.dataoffsl
  732                              = psii->dataoffsl;
  733                     offs = le64_to_cpu(realign.all);
  734                     size = le32_to_cpu(psii->datasize);
  735                 }
  736                 retries++;
  737             }
  738         }
  739     }
  740     if (!keyid) {
  741         /*
  742          * could not find any entry, before creating the first
  743          * entry, make a double check by making sure size of $SII
  744          * is less than needed for one entry
  745          */
  746         securid = const_cpu_to_le32(0);
  747         na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
  748         if (na) {
  749             if ((size_t)na->data_size < (sizeof(struct SII)
  750                     + sizeof(INDEX_ENTRY_HEADER))) {
  751                 ntfs_log_error("Creating the first security_id\n");
  752                 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
  753             }
  754             ntfs_attr_close(na);
  755         }
  756         if (!securid) {
  757             ntfs_log_error("Error creating a security_id\n");
  758             errno = EIO;
  759         }
  760     } else {
  761         newkey = le32_to_cpu(keyid) + 1;
  762         securid = cpu_to_le32(newkey);
  763     }
  764     /*
  765      * The security attr has to be written twice 256KB
  766      * apart. This implies that offsets like
  767      * 0x40000*odd_integer must be left available for
  768      * the second copy. So align to next block when
  769      * the last byte overflows on a wrong block.
  770      */
  771 
  772     if (securid) {
  773         gap = (-size) & (ALIGN_SDS_ENTRY - 1);
  774         offs += gap + size;
  775         if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
  776            & ALIGN_SDS_BLOCK) {
  777             offs = ((offs + attrsz
  778                  + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
  779                 | (ALIGN_SDS_BLOCK - 1)) + 1;
  780         }
  781         if (!(offs & (ALIGN_SDS_BLOCK - 1)))
  782             entersecurity_stuff(vol, offs);
  783         /*
  784          * now write the security attr to storage :
  785          * first data, then SII, then SDH
  786          * If failure occurs while writing SDS, data will never
  787          *    be accessed through indexes, and will be overwritten
  788          *    by the next allocated descriptor
  789          * If failure occurs while writing SII, the id has not
  790          *    recorded and will be reallocated later
  791          * If failure occurs while writing SDH, the space allocated
  792          *    in SDS or SII will not be reused, an inconsistency
  793          *    will persist with no significant consequence
  794          */
  795         if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
  796             || entersecurity_indexes(vol, attrsz, hash, securid, offs))
  797             securid = const_cpu_to_le32(0);
  798     }
  799         /* inode now is dirty, synchronize it all */
  800     ntfs_index_entry_mark_dirty(vol->secure_xsii);
  801     ntfs_index_ctx_reinit(vol->secure_xsii);
  802     ntfs_index_entry_mark_dirty(vol->secure_xsdh);
  803     ntfs_index_ctx_reinit(vol->secure_xsdh);
  804     NInoSetDirty(vol->secure_ni);
  805     if (ntfs_inode_sync(vol->secure_ni))
  806         ntfs_log_perror("Could not sync $Secure\n");
  807     return (securid);
  808 }
  809 
  810 /*
  811  *      Find a matching security descriptor in $Secure,
  812  *  if none, allocate a new id and write the descriptor to storage
  813  *  Returns id of entry, or zero if there is a problem.
  814  *
  815  *  important : calls have to be serialized, however no locking is
  816  *  needed while fuse is not multithreaded
  817  */
  818 
  819 static le32 setsecurityattr(ntfs_volume *vol,
  820             const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
  821 {
  822     struct SDH *psdh;   /* this is an image of index (le) */
  823     union {
  824         struct {
  825             le32 dataoffsl;
  826             le32 dataoffsh;
  827         } parts;
  828         le64 all;
  829     } realign;
  830     BOOL found;
  831     BOOL collision;
  832     size_t size;
  833     size_t rdsize;
  834     s64 offs;
  835     int res;
  836     ntfs_index_context *xsdh;
  837     char *oldattr;
  838     SDH_INDEX_KEY key;
  839     INDEX_ENTRY *entry;
  840     le32 securid;
  841     le32 hash;
  842     int olderrno;
  843 
  844     hash = ntfs_security_hash(attr,attrsz);
  845     oldattr = (char*)NULL;
  846     securid = const_cpu_to_le32(0);
  847     res = 0;
  848     xsdh = vol->secure_xsdh;
  849     if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
  850         ntfs_index_ctx_reinit(xsdh);
  851         /*
  852          * find the nearest key as (hash,0)
  853          * (do not search for partial key : in case of collision,
  854          * it could return a key which is not the first one which
  855          * collides)
  856          */
  857         key.hash = hash;
  858         key.security_id = const_cpu_to_le32(0);
  859         olderrno = errno;
  860         found = !ntfs_index_lookup((char*)&key,
  861                  sizeof(SDH_INDEX_KEY), xsdh);
  862         if (!found && (errno != ENOENT))
  863             ntfs_log_perror("Inconsistency in index $SDH");
  864         else {
  865                 /* restore errno to avoid misinterpretation */
  866             errno = olderrno;
  867             entry = xsdh->entry;
  868             found = FALSE;
  869             /*
  870              * lookup() may return a node with no data,
  871              * if so get next
  872              */
  873             if (entry->ie_flags & INDEX_ENTRY_END)
  874                 entry = ntfs_index_next(entry,xsdh);
  875             do {
  876                 collision = FALSE;
  877                 psdh = (struct SDH*)entry;
  878                 if (psdh)
  879                     size = (size_t) le32_to_cpu(psdh->datasize)
  880                          - sizeof(SECURITY_DESCRIPTOR_HEADER);
  881                 else size = 0;
  882                /* if hash is not the same, the key is not present */
  883                 if (psdh && (size > 0)
  884                    && (psdh->keyhash == hash)) {
  885                        /* if hash is the same */
  886                        /* check the whole record */
  887                     realign.parts.dataoffsh = psdh->dataoffsh;
  888                     realign.parts.dataoffsl = psdh->dataoffsl;
  889                     offs = le64_to_cpu(realign.all)
  890                         + sizeof(SECURITY_DESCRIPTOR_HEADER);
  891                     oldattr = (char*)ntfs_malloc(size);
  892                     if (oldattr) {
  893                         rdsize = ntfs_attr_data_read(
  894                             vol->secure_ni,
  895                             STREAM_SDS, 4,
  896                             oldattr, size, offs);
  897                         found = (rdsize == size)
  898                             && !memcmp(oldattr,attr,size);
  899                         free(oldattr);
  900                       /* if the records do not compare */
  901                       /* (hash collision), try next one */
  902                         if (!found) {
  903                             entry = ntfs_index_next(
  904                                 entry,xsdh);
  905                             collision = TRUE;
  906                         }
  907                     } else
  908                         res = ENOMEM;
  909                 }
  910             } while (collision && entry);
  911             if (found)
  912                 securid = psdh->keysecurid;
  913             else {
  914                 if (res) {
  915                     errno = res;
  916                     securid = const_cpu_to_le32(0);
  917                 } else {
  918                     /*
  919                      * no matching key :
  920                      * have to build a new one
  921                      */
  922                     securid = entersecurityattr(vol,
  923                         attr, attrsz, hash);
  924                 }
  925             }
  926         }
  927     }
  928     if (--vol->secure_reentry)
  929         ntfs_log_perror("Reentry error, check no multithreading\n");
  930     return (securid);
  931 }
  932 
  933 
  934 /*
  935  *      Update the security descriptor of a file
  936  *  Either as an attribute (complying with pre v3.x NTFS version)
  937  *  or, when possible, as an entry in $Secure (for NTFS v3.x)
  938  *
  939  *  returns 0 if success
  940  */
  941 
  942 static int update_secur_descr(ntfs_volume *vol,
  943                 char *newattr, ntfs_inode *ni)
  944 {
  945     int newattrsz;
  946     int written;
  947     int res;
  948     ntfs_attr *na;
  949 
  950     newattrsz = ntfs_attr_size(newattr);
  951 
  952 #if !FORCE_FORMAT_v1x
  953     if ((vol->major_ver < 3) || !vol->secure_ni) {
  954 #endif
  955 
  956         /* update for NTFS format v1.x */
  957 
  958         /* update the old security attribute */
  959         na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
  960         if (na) {
  961             /* resize attribute */
  962             res = ntfs_attr_truncate(na, (s64) newattrsz);
  963             /* overwrite value */
  964             if (!res) {
  965                 written = (int)ntfs_attr_pwrite(na, (s64) 0,
  966                      (s64) newattrsz, newattr);
  967                 if (written != newattrsz) {
  968                     ntfs_log_error("Failed to update "
  969                         "a v1.x security descriptor\n");
  970                     errno = EIO;
  971                     res = -1;
  972                 }
  973             }
  974 
  975             ntfs_attr_close(na);
  976             /* if old security attribute was found, also */
  977             /* truncate standard information attribute to v1.x */
  978             /* this is needed when security data is wanted */
  979             /* as v1.x though volume is formatted for v3.x */
  980             na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
  981                 AT_UNNAMED, 0);
  982             if (na) {
  983                 clear_nino_flag(ni, v3_Extensions);
  984             /*
  985              * Truncating the record does not sweep extensions
  986              * from copy in memory. Clear security_id to be safe
  987              */
  988                 ni->security_id = const_cpu_to_le32(0);
  989                 res = ntfs_attr_truncate(na, (s64)48);
  990                 ntfs_attr_close(na);
  991                 clear_nino_flag(ni, v3_Extensions);
  992             }
  993         } else {
  994             /*
  995              * insert the new security attribute if there
  996              * were none
  997              */
  998             res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
  999                         AT_UNNAMED, 0, (u8*)newattr,
 1000                         (s64) newattrsz);
 1001         }
 1002 #if !FORCE_FORMAT_v1x
 1003     } else {
 1004 
 1005         /* update for NTFS format v3.x */
 1006 
 1007         le32 securid;
 1008 
 1009         securid = setsecurityattr(vol,
 1010             (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
 1011             (s64)newattrsz);
 1012         if (securid) {
 1013             na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
 1014                 AT_UNNAMED, 0);
 1015             if (na) {
 1016                 res = 0;
 1017                 if (!test_nino_flag(ni, v3_Extensions)) {
 1018             /* expand standard information attribute to v3.x */
 1019                     res = ntfs_attr_truncate(na,
 1020                      (s64)sizeof(STANDARD_INFORMATION));
 1021                     ni->owner_id = const_cpu_to_le32(0);
 1022                     ni->quota_charged = const_cpu_to_le64(0);
 1023                     ni->usn = const_cpu_to_le64(0);
 1024                     ntfs_attr_remove(ni,
 1025                         AT_SECURITY_DESCRIPTOR,
 1026                         AT_UNNAMED, 0);
 1027             }
 1028                 set_nino_flag(ni, v3_Extensions);
 1029                 ni->security_id = securid;
 1030                 ntfs_attr_close(na);
 1031             } else {
 1032                 ntfs_log_error("Failed to update "
 1033                     "standard informations\n");
 1034                 errno = EIO;
 1035                 res = -1;
 1036             }
 1037         } else
 1038             res = -1;
 1039     }
 1040 #endif
 1041 
 1042     /* mark node as dirty */
 1043     NInoSetDirty(ni);
 1044     return (res);
 1045 }
 1046 
 1047 /*
 1048  *      Upgrade the security descriptor of a file
 1049  *  This is intended to allow graceful upgrades for files which
 1050  *  were created in previous versions, with a security attributes
 1051  *  and no security id.
 1052  *  
 1053  *      It will allocate a security id and replace the individual
 1054  *  security attribute by a reference to the global one
 1055  *
 1056  *  Special files are not upgraded (currently / and files in
 1057  *  directories /$*)
 1058  *
 1059  *  Though most code is similar to update_secur_desc() it has
 1060  *  been kept apart to facilitate the further processing of
 1061  *  special cases or even to remove it if found dangerous.
 1062  *
 1063  *  returns 0 if success,
 1064  *      1 if not upgradable. This is not an error.
 1065  *      -1 if there is a problem
 1066  */
 1067 
 1068 static int upgrade_secur_desc(ntfs_volume *vol,
 1069                 const char *attr, ntfs_inode *ni)
 1070 {
 1071     int attrsz;
 1072     int res;
 1073     le32 securid;
 1074     ntfs_attr *na;
 1075 
 1076         /*
 1077          * upgrade requires NTFS format v3.x
 1078          * also refuse upgrading for special files
 1079          * whose number is less than FILE_first_user
 1080          */
 1081 
 1082     if ((vol->major_ver >= 3)
 1083         && (ni->mft_no >= FILE_first_user)) {
 1084         attrsz = ntfs_attr_size(attr);
 1085         securid = setsecurityattr(vol,
 1086             (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
 1087             (s64)attrsz);
 1088         if (securid) {
 1089             na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
 1090                 AT_UNNAMED, 0);
 1091             if (na) {
 1092             /* expand standard information attribute to v3.x */
 1093                 res = ntfs_attr_truncate(na,
 1094                      (s64)sizeof(STANDARD_INFORMATION));
 1095                 ni->owner_id = const_cpu_to_le32(0);
 1096                 ni->quota_charged = const_cpu_to_le64(0);
 1097                 ni->usn = const_cpu_to_le64(0);
 1098                 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
 1099                         AT_UNNAMED, 0);
 1100                 set_nino_flag(ni, v3_Extensions);
 1101                 ni->security_id = securid;
 1102                 ntfs_attr_close(na);
 1103             } else {
 1104                 ntfs_log_error("Failed to upgrade "
 1105                     "standard informations\n");
 1106                 errno = EIO;
 1107                 res = -1;
 1108             }
 1109         } else
 1110             res = -1;
 1111             /* mark node as dirty */
 1112         NInoSetDirty(ni);
 1113     } else
 1114         res = 1;
 1115 
 1116     return (res);
 1117 }
 1118 
 1119 /*
 1120  *      Optional simplified checking of group membership
 1121  *
 1122  *  This only takes into account the groups defined in
 1123  *  /etc/group at initialization time.
 1124  *  It does not take into account the groups dynamically set by
 1125  *  setgroups() nor the changes in /etc/group since initialization
 1126  *
 1127  *  This optional method could be useful if standard checking
 1128  *  leads to a performance concern.
 1129  *
 1130  *  Should not be called for user root, however the group may be root
 1131  *
 1132  */
 1133 
 1134 static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
 1135 {
 1136     BOOL ingroup;
 1137     int grcnt;
 1138     gid_t *groups;
 1139     struct MAPPING *user;
 1140 
 1141     ingroup = FALSE;
 1142     if (uid) {
 1143         user = scx->mapping[MAPUSERS];
 1144         while (user && ((uid_t)user->xid != uid))
 1145             user = user->next;
 1146         if (user) {
 1147             groups = user->groups;
 1148             grcnt = user->grcnt;
 1149             while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
 1150             ingroup = (grcnt >= 0);
 1151         }
 1152     }
 1153     return (ingroup);
 1154 }
 1155 
 1156 #if defined(__sun) && defined (__SVR4)
 1157 
 1158 /*
 1159  *      Check whether current thread owner is member of file group
 1160  *              Solaris/OpenIndiana version
 1161  *  Should not be called for user root, however the group may be root
 1162  *
 1163  * The group list is available in "/proc/$PID/cred"
 1164  *
 1165  */
 1166 
 1167 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
 1168 {
 1169     typedef struct prcred {
 1170         uid_t pr_euid;      /* effective user id */
 1171         uid_t pr_ruid;      /* real user id */
 1172         uid_t pr_suid;      /* saved user id (from exec) */
 1173         gid_t pr_egid;      /* effective group id */
 1174         gid_t pr_rgid;      /* real group id */
 1175         gid_t pr_sgid;      /* saved group id (from exec) */
 1176         int pr_ngroups;     /* number of supplementary groups */
 1177         gid_t pr_groups[1]; /* array of supplementary groups */
 1178     } prcred_t;
 1179     enum { readset = 16 };
 1180 
 1181     prcred_t basecreds;
 1182     gid_t groups[readset];
 1183     char filename[64];
 1184     int fd;
 1185     int k;
 1186     int cnt;
 1187     gid_t *p;
 1188     BOOL ismember;
 1189     int got;
 1190     pid_t tid;
 1191 
 1192     if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
 1193         ismember = staticgroupmember(scx, uid, gid);
 1194     else {
 1195         ismember = FALSE; /* default return */
 1196         tid = scx->tid;
 1197         sprintf(filename,"/proc/%u/cred",tid);
 1198         fd = open(filename,O_RDONLY);
 1199         if (fd >= 0) {
 1200             got = read(fd, &basecreds, sizeof(prcred_t));
 1201             if (got == sizeof(prcred_t)) {
 1202                 if (basecreds.pr_egid == gid)
 1203                     ismember = TRUE;
 1204                 p = basecreds.pr_groups;
 1205                 cnt = 1;
 1206                 k = 0;
 1207                 while (!ismember
 1208                     && (k < basecreds.pr_ngroups)
 1209                     && (cnt > 0)
 1210                     && (*p != gid)) {
 1211                     k++;
 1212                     cnt--;
 1213                     p++;
 1214                     if (cnt <= 0) {
 1215                         got = read(fd, groups,
 1216                             readset*sizeof(gid_t));
 1217                         cnt = got/sizeof(gid_t);
 1218                         p = groups;
 1219                     }
 1220                 }
 1221                 if ((cnt > 0)
 1222                     && (k < basecreds.pr_ngroups))
 1223                     ismember = TRUE;
 1224             }
 1225         close(fd);
 1226         }
 1227     }
 1228     return (ismember);
 1229 }
 1230 
 1231 #else /* defined(__sun) && defined (__SVR4) */
 1232 
 1233 /*
 1234  *      Check whether current thread owner is member of file group
 1235  *              Linux version
 1236  *  Should not be called for user root, however the group may be root
 1237  *
 1238  * As indicated by Miklos Szeredi :
 1239  *
 1240  * The group list is available in
 1241  *
 1242  *   /proc/$PID/task/$TID/status
 1243  *
 1244  * and fuse supplies TID in get_fuse_context()->pid.  The only problem is
 1245  * finding out PID, for which I have no good solution, except to iterate
 1246  * through all processes.  This is rather slow, but may be speeded up
 1247  * with caching and heuristics (for single threaded programs PID = TID).
 1248  *
 1249  * The following implementation gets the group list from
 1250  *   /proc/$TID/task/$TID/status which apparently exists and
 1251  * contains the same data.
 1252  */
 1253 
 1254 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
 1255 {
 1256     static char key[] = "\nGroups:";
 1257     char buf[BUFSZ+1];
 1258     char filename[64];
 1259     enum { INKEY, INSEP, INNUM, INEND } state;
 1260     int fd;
 1261     char c;
 1262     int matched;
 1263     BOOL ismember;
 1264     int got;
 1265     char *p;
 1266     gid_t grp;
 1267     pid_t tid;
 1268 
 1269     if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
 1270         ismember = staticgroupmember(scx, uid, gid);
 1271     else {
 1272         ismember = FALSE; /* default return */
 1273         tid = scx->tid;
 1274         sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
 1275         fd = open(filename,O_RDONLY);
 1276         if (fd >= 0) {
 1277             got = read(fd, buf, BUFSZ);
 1278             buf[got] = 0;
 1279             state = INKEY;
 1280             matched = 0;
 1281             p = buf;
 1282             grp = 0;
 1283                 /*
 1284                  *  A simple automaton to process lines like
 1285                  *  Groups: 14 500 513
 1286                  */
 1287             do {
 1288                 c = *p++;
 1289                 if (!c) {
 1290                     /* refill buffer */
 1291                     got = read(fd, buf, BUFSZ);
 1292                     buf[got] = 0;
 1293                     p = buf;
 1294                     c = *p++; /* 0 at end of file */
 1295                 }
 1296                 switch (state) {
 1297                 case INKEY :
 1298                     if (key[matched] == c) {
 1299                         if (!key[++matched])
 1300                             state = INSEP;
 1301                     } else
 1302                         if (key[0] == c)
 1303                             matched = 1;
 1304                         else
 1305                             matched = 0;
 1306                     break;
 1307                 case INSEP :
 1308                     if ((c >= '0') && (c <= '9')) {
 1309                         grp = c - '0';
 1310                         state = INNUM;
 1311                     } else
 1312                         if ((c != ' ') && (c != '\t'))
 1313                             state = INEND;
 1314                     break;
 1315                 case INNUM :
 1316                     if ((c >= '0') && (c <= '9'))
 1317                         grp = grp*10 + c - '0';
 1318                     else {
 1319                         ismember = (grp == gid);
 1320                         if ((c != ' ') && (c != '\t'))
 1321                             state = INEND;
 1322                         else
 1323                             state = INSEP;
 1324                     }
 1325                 default :
 1326                     break;
 1327                 }
 1328             } while (!ismember && c && (state != INEND));
 1329         close(fd);
 1330         if (!c)
 1331             ntfs_log_error("No group record found in %s\n",filename);
 1332         } else
 1333             ntfs_log_error("Could not open %s\n",filename);
 1334     }
 1335     return (ismember);
 1336 }
 1337 
 1338 #endif /* defined(__sun) && defined (__SVR4) */
 1339 
 1340 #if POSIXACLS
 1341 
 1342 /*
 1343  *      Extract the basic permissions from a Posix ACL
 1344  *
 1345  *  This is only to be used when Posix ACLs are compiled in,
 1346  *  but not enabled in the mount options.
 1347  *
 1348  *  it replaces the permission mask by the group permissions.
 1349  *  If special groups are mapped, they are also considered as world.
 1350  */
 1351 
 1352 static int ntfs_basic_perms(const struct SECURITY_CONTEXT *scx,
 1353             const struct POSIX_SECURITY *pxdesc)
 1354 {
 1355     int k;
 1356     int perms;
 1357     const struct POSIX_ACE *pace;
 1358     const struct MAPPING* group;
 1359 
 1360     k = 0;
 1361     perms = pxdesc->mode;
 1362     for (k=0; k < pxdesc->acccnt; k++) {
 1363         pace = &pxdesc->acl.ace[k];
 1364         if (pace->tag == POSIX_ACL_GROUP_OBJ)
 1365             perms = (perms & 07707)
 1366                 | ((pace->perms & 7) << 3);
 1367         else
 1368             if (pace->tag == POSIX_ACL_GROUP) {
 1369                 group = scx->mapping[MAPGROUPS];
 1370                 while (group && (group->xid != pace->id))
 1371                     group = group->next;
 1372                 if (group && group->grcnt
 1373                     && (*(group->groups) == (gid_t)pace->id))
 1374                     perms |= pace->perms & 7;
 1375             }
 1376     }
 1377     return (perms);
 1378 }
 1379 
 1380 #endif /* POSIXACLS */
 1381 
 1382 /*
 1383  *  Cacheing is done two-way :
 1384  *  - from uid, gid and perm to securid (CACHED_SECURID)
 1385  *  - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
 1386  *
 1387  *  CACHED_SECURID data is kept in a most-recent-first list
 1388  *  which should not be too long to be efficient. Its optimal
 1389  *  size is depends on usage and is hard to determine.
 1390  *
 1391  *  CACHED_PERMISSIONS data is kept in a two-level indexed array. It
 1392  *  is optimal at the expense of storage. Use of a most-recent-first
 1393  *  list would save memory and provide similar performances for
 1394  *  standard usage, but not for file servers with too many file
 1395  *  owners
 1396  *
 1397  *  CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
 1398  *  for legacy directories which were not allocated a security_id
 1399  *  it is organized in a most-recent-first list.
 1400  *
 1401  *  In main caches, data is never invalidated, as the meaning of
 1402  *  a security_id only changes when user mapping is changed, which
 1403  *  current implies remounting. However returned entries may be
 1404  *  overwritten at next update, so data has to be copied elsewhere
 1405  *  before another cache update is made.
 1406  *  In legacy cache, data has to be invalidated when protection is
 1407  *  changed.
 1408  *
 1409  *  Though the same data may be found in both list, they
 1410  *  must be kept separately : the interpretation of ACL
 1411  *  in both direction are approximations which could be non
 1412  *  reciprocal for some configuration of the user mapping data
 1413  *
 1414  *  During the process of recompiling ntfs-3g from a tgz archive,
 1415  *  security processing added 7.6% to the cpu time used by ntfs-3g
 1416  *  and 30% if the cache is disabled.
 1417  */
 1418 
 1419 static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
 1420             u32 securindex)
 1421 {
 1422     struct PERMISSIONS_CACHE *cache;
 1423     unsigned int index1;
 1424     unsigned int i;
 1425 
 1426     cache = (struct PERMISSIONS_CACHE*)NULL;
 1427         /* create the first permissions blocks */
 1428     index1 = securindex >> CACHE_PERMISSIONS_BITS;
 1429     cache = (struct PERMISSIONS_CACHE*)
 1430         ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
 1431               + index1*sizeof(struct CACHED_PERMISSIONS*));
 1432     if (cache) {
 1433         cache->head.last = index1;
 1434         cache->head.p_reads = 0;
 1435         cache->head.p_hits = 0;
 1436         cache->head.p_writes = 0;
 1437         *scx->pseccache = cache;
 1438         for (i=0; i<=index1; i++)
 1439             cache->cachetable[i]
 1440                = (struct CACHED_PERMISSIONS*)NULL;
 1441     }
 1442     return (cache);
 1443 }
 1444 
 1445 /*
 1446  *      Free memory used by caches
 1447  *  The only purpose is to facilitate the detection of memory leaks
 1448  */
 1449 
 1450 static void free_caches(struct SECURITY_CONTEXT *scx)
 1451 {
 1452     unsigned int index1;
 1453     struct PERMISSIONS_CACHE *pseccache;
 1454 
 1455     pseccache = *scx->pseccache;
 1456     if (pseccache) {
 1457         for (index1=0; index1<=pseccache->head.last; index1++)
 1458             if (pseccache->cachetable[index1]) {
 1459 #if POSIXACLS
 1460                 struct CACHED_PERMISSIONS *cacheentry;
 1461                 unsigned int index2;
 1462 
 1463                 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
 1464                     cacheentry = &pseccache->cachetable[index1][index2];
 1465                     if (cacheentry->valid
 1466                         && cacheentry->pxdesc)
 1467                         free(cacheentry->pxdesc);
 1468                     }
 1469 #endif
 1470                 free(pseccache->cachetable[index1]);
 1471             }
 1472         free(pseccache);
 1473     }
 1474 }
 1475 
 1476 static int compare(const struct CACHED_SECURID *cached,
 1477             const struct CACHED_SECURID *item)
 1478 {
 1479 #if POSIXACLS
 1480     size_t csize;
 1481     size_t isize;
 1482 
 1483         /* only compare data and sizes */
 1484     csize = (cached->variable ?
 1485         sizeof(struct POSIX_ACL)
 1486         + (((struct POSIX_SECURITY*)cached->variable)->acccnt
 1487            + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
 1488             *sizeof(struct POSIX_ACE) :
 1489         0);
 1490     isize = (item->variable ?
 1491         sizeof(struct POSIX_ACL)
 1492         + (((struct POSIX_SECURITY*)item->variable)->acccnt
 1493            + ((struct POSIX_SECURITY*)item->variable)->defcnt)
 1494             *sizeof(struct POSIX_ACE) :
 1495         0);
 1496     return ((cached->uid != item->uid)
 1497          || (cached->gid != item->gid)
 1498          || (cached->dmode != item->dmode)
 1499          || (csize != isize)
 1500          || (csize
 1501             && isize
 1502             && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
 1503                &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
 1504 #else
 1505     return ((cached->uid != item->uid)
 1506          || (cached->gid != item->gid)
 1507          || (cached->dmode != item->dmode));
 1508 #endif
 1509 }
 1510 
 1511 static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
 1512             const struct CACHED_PERMISSIONS_LEGACY *item)
 1513 {
 1514     return (cached->mft_no != item->mft_no);
 1515 }
 1516 
 1517 /*
 1518  *  Resize permission cache table
 1519  *  do not call unless resizing is needed
 1520  *  
 1521  *  If allocation fails, the cache size is not updated
 1522  *  Lack of memory is not considered as an error, the cache is left
 1523  *  consistent and errno is not set.
 1524  */
 1525 
 1526 static void resize_cache(struct SECURITY_CONTEXT *scx,
 1527             u32 securindex)
 1528 {
 1529     struct PERMISSIONS_CACHE *oldcache;
 1530     struct PERMISSIONS_CACHE *newcache;
 1531     int newcnt;
 1532     int oldcnt;
 1533     unsigned int index1;
 1534     unsigned int i;
 1535 
 1536     oldcache = *scx->pseccache;
 1537     index1 = securindex >> CACHE_PERMISSIONS_BITS;
 1538     newcnt = index1 + 1;
 1539     if (newcnt <= ((CACHE_PERMISSIONS_SIZE
 1540             + (1 << CACHE_PERMISSIONS_BITS)
 1541             - 1) >> CACHE_PERMISSIONS_BITS)) {
 1542         /* expand cache beyond current end, do not use realloc() */
 1543         /* to avoid losing data when there is no more memory */
 1544         oldcnt = oldcache->head.last + 1;
 1545         newcache = (struct PERMISSIONS_CACHE*)
 1546             ntfs_malloc(
 1547                 sizeof(struct PERMISSIONS_CACHE)
 1548                   + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
 1549         if (newcache) {
 1550             memcpy(newcache,oldcache,
 1551                 sizeof(struct PERMISSIONS_CACHE)
 1552                   + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
 1553             free(oldcache);
 1554                  /* mark new entries as not valid */
 1555             for (i=newcache->head.last+1; i<=index1; i++)
 1556                 newcache->cachetable[i]
 1557                      = (struct CACHED_PERMISSIONS*)NULL;
 1558             newcache->head.last = index1;
 1559             *scx->pseccache = newcache;
 1560         }
 1561     }
 1562 }
 1563 
 1564 /*
 1565  *  Enter uid, gid and mode into cache, if possible
 1566  *
 1567  *  returns the updated or created cache entry,
 1568  *  or NULL if not possible (typically if there is no
 1569  *      security id associated)
 1570  */
 1571 
 1572 #if POSIXACLS
 1573 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
 1574         ntfs_inode *ni, uid_t uid, gid_t gid,
 1575         struct POSIX_SECURITY *pxdesc)
 1576 #else
 1577 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
 1578         ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
 1579 #endif
 1580 {
 1581     struct CACHED_PERMISSIONS *cacheentry;
 1582     struct CACHED_PERMISSIONS *cacheblock;
 1583     struct PERMISSIONS_CACHE *pcache;
 1584     u32 securindex;
 1585 #if POSIXACLS
 1586     int pxsize;
 1587     struct POSIX_SECURITY *pxcached;
 1588 #endif
 1589     unsigned int index1;
 1590     unsigned int index2;
 1591     int i;
 1592 
 1593     /* cacheing is only possible if a security_id has been defined */
 1594     if (test_nino_flag(ni, v3_Extensions)
 1595        && ni->security_id) {
 1596         /*
 1597          *  Immediately test the most frequent situation
 1598          *  where the entry exists
 1599          */
 1600         securindex = le32_to_cpu(ni->security_id);
 1601         index1 = securindex >> CACHE_PERMISSIONS_BITS;
 1602         index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
 1603         pcache = *scx->pseccache;
 1604         if (pcache
 1605              && (pcache->head.last >= index1)
 1606              && pcache->cachetable[index1]) {
 1607             cacheentry = &pcache->cachetable[index1][index2];
 1608             cacheentry->uid = uid;
 1609             cacheentry->gid = gid;
 1610 #if POSIXACLS
 1611             if (cacheentry->valid && cacheentry->pxdesc)
 1612                 free(cacheentry->pxdesc);
 1613             if (pxdesc) {
 1614                 pxsize = sizeof(struct POSIX_SECURITY)
 1615                     + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 1616                 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
 1617                 if (pxcached) {
 1618                     memcpy(pxcached, pxdesc, pxsize);
 1619                     cacheentry->pxdesc = pxcached;
 1620                 } else {
 1621                     cacheentry->valid = 0;
 1622                     cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1623                 }
 1624                 cacheentry->mode = pxdesc->mode & 07777;
 1625             } else
 1626                 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
 1627 #else
 1628             cacheentry->mode = mode & 07777;
 1629 #endif
 1630             cacheentry->inh_fileid = const_cpu_to_le32(0);
 1631             cacheentry->inh_dirid = const_cpu_to_le32(0);
 1632             cacheentry->valid = 1;
 1633             pcache->head.p_writes++;
 1634         } else {
 1635             if (!pcache) {
 1636                 /* create the first cache block */
 1637                 pcache = create_caches(scx, securindex);
 1638             } else {
 1639                 if (index1 > pcache->head.last) {
 1640                     resize_cache(scx, securindex);
 1641                     pcache = *scx->pseccache;
 1642                 }
 1643             }
 1644             /* allocate block, if cache table was allocated */
 1645             if (pcache && (index1 <= pcache->head.last)) {
 1646                 cacheblock = (struct CACHED_PERMISSIONS*)
 1647                     malloc(sizeof(struct CACHED_PERMISSIONS)
 1648                         << CACHE_PERMISSIONS_BITS);
 1649                 pcache->cachetable[index1] = cacheblock;
 1650                 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
 1651                     cacheblock[i].valid = 0;
 1652                 cacheentry = &cacheblock[index2];
 1653                 if (cacheentry) {
 1654                     cacheentry->uid = uid;
 1655                     cacheentry->gid = gid;
 1656 #if POSIXACLS
 1657                     if (pxdesc) {
 1658                         pxsize = sizeof(struct POSIX_SECURITY)
 1659                             + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 1660                         pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
 1661                         if (pxcached) {
 1662                             memcpy(pxcached, pxdesc, pxsize);
 1663                             cacheentry->pxdesc = pxcached;
 1664                         } else {
 1665                             cacheentry->valid = 0;
 1666                             cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1667                         }
 1668                         cacheentry->mode = pxdesc->mode & 07777;
 1669                     } else
 1670                         cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
 1671 #else
 1672                     cacheentry->mode = mode & 07777;
 1673 #endif
 1674                     cacheentry->inh_fileid = const_cpu_to_le32(0);
 1675                     cacheentry->inh_dirid = const_cpu_to_le32(0);
 1676                     cacheentry->valid = 1;
 1677                     pcache->head.p_writes++;
 1678                 }
 1679             } else
 1680                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1681         }
 1682     } else {
 1683         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1684 #if CACHE_LEGACY_SIZE
 1685         if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
 1686             struct CACHED_PERMISSIONS_LEGACY wanted;
 1687             struct CACHED_PERMISSIONS_LEGACY *legacy;
 1688 
 1689             wanted.perm.uid = uid;
 1690             wanted.perm.gid = gid;
 1691 #if POSIXACLS
 1692             wanted.perm.mode = pxdesc->mode & 07777;
 1693             wanted.perm.inh_fileid = const_cpu_to_le32(0);
 1694             wanted.perm.inh_dirid = const_cpu_to_le32(0);
 1695             wanted.mft_no = ni->mft_no;
 1696             wanted.variable = (void*)pxdesc;
 1697             wanted.varsize = sizeof(struct POSIX_SECURITY)
 1698                     + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 1699 #else
 1700             wanted.perm.mode = mode & 07777;
 1701             wanted.perm.inh_fileid = const_cpu_to_le32(0);
 1702             wanted.perm.inh_dirid = const_cpu_to_le32(0);
 1703             wanted.mft_no = ni->mft_no;
 1704             wanted.variable = (void*)NULL;
 1705             wanted.varsize = 0;
 1706 #endif
 1707             legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
 1708                 scx->vol->legacy_cache, GENERIC(&wanted),
 1709                 (cache_compare)leg_compare);
 1710             if (legacy) {
 1711                 cacheentry = &legacy->perm;
 1712 #if POSIXACLS
 1713                 /*
 1714                  * give direct access to the cached pxdesc
 1715                  * in the permissions structure
 1716                  */
 1717                 cacheentry->pxdesc = legacy->variable;
 1718 #endif
 1719             }
 1720         }
 1721 #endif
 1722     }
 1723     return (cacheentry);
 1724 }
 1725 
 1726 /*
 1727  *  Fetch owner, group and permission of a file, if cached
 1728  *
 1729  *  Beware : do not use the returned entry after a cache update :
 1730  *  the cache may be relocated making the returned entry meaningless
 1731  *
 1732  *  returns the cache entry, or NULL if not available
 1733  */
 1734 
 1735 static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
 1736         ntfs_inode *ni)
 1737 {
 1738     struct CACHED_PERMISSIONS *cacheentry;
 1739     struct PERMISSIONS_CACHE *pcache;
 1740     u32 securindex;
 1741     unsigned int index1;
 1742     unsigned int index2;
 1743 
 1744     /* cacheing is only possible if a security_id has been defined */
 1745     cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1746     if (test_nino_flag(ni, v3_Extensions)
 1747        && (ni->security_id)) {
 1748         securindex = le32_to_cpu(ni->security_id);
 1749         index1 = securindex >> CACHE_PERMISSIONS_BITS;
 1750         index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
 1751         pcache = *scx->pseccache;
 1752         if (pcache
 1753              && (pcache->head.last >= index1)
 1754              && pcache->cachetable[index1]) {
 1755             cacheentry = &pcache->cachetable[index1][index2];
 1756             /* reject if entry is not valid */
 1757             if (!cacheentry->valid)
 1758                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1759             else
 1760                 pcache->head.p_hits++;
 1761         if (pcache)
 1762             pcache->head.p_reads++;
 1763         }
 1764     }
 1765 #if CACHE_LEGACY_SIZE
 1766     else {
 1767         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1768         if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
 1769             struct CACHED_PERMISSIONS_LEGACY wanted;
 1770             struct CACHED_PERMISSIONS_LEGACY *legacy;
 1771 
 1772             wanted.mft_no = ni->mft_no;
 1773             wanted.variable = (void*)NULL;
 1774             wanted.varsize = 0;
 1775             legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
 1776                 scx->vol->legacy_cache, GENERIC(&wanted),
 1777                 (cache_compare)leg_compare);
 1778             if (legacy) cacheentry = &legacy->perm;
 1779         }
 1780     }
 1781 #endif
 1782 #if POSIXACLS
 1783     if (cacheentry && !cacheentry->pxdesc) {
 1784         ntfs_log_error("No Posix descriptor in cache\n");
 1785         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 1786     }
 1787 #endif
 1788     return (cacheentry);
 1789 }
 1790 
 1791 /*
 1792  *  Retrieve a security attribute from $Secure
 1793  */
 1794 
 1795 static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
 1796 {
 1797     struct SII *psii;
 1798     union {
 1799         struct {
 1800             le32 dataoffsl;
 1801             le32 dataoffsh;
 1802         } parts;
 1803         le64 all;
 1804     } realign;
 1805     int found;
 1806     size_t size;
 1807     size_t rdsize;
 1808     s64 offs;
 1809     ntfs_inode *ni;
 1810     ntfs_index_context *xsii;
 1811     char *securattr;
 1812 
 1813     securattr = (char*)NULL;
 1814     ni = vol->secure_ni;
 1815     xsii = vol->secure_xsii;
 1816     if (ni && xsii) {
 1817         ntfs_index_ctx_reinit(xsii);
 1818         found =
 1819             !ntfs_index_lookup((char*)&id,
 1820                        sizeof(SII_INDEX_KEY), xsii);
 1821         if (found) {
 1822             psii = (struct SII*)xsii->entry;
 1823             size =
 1824                 (size_t) le32_to_cpu(psii->datasize)
 1825                  - sizeof(SECURITY_DESCRIPTOR_HEADER);
 1826             /* work around bad alignment problem */
 1827             realign.parts.dataoffsh = psii->dataoffsh;
 1828             realign.parts.dataoffsl = psii->dataoffsl;
 1829             offs = le64_to_cpu(realign.all)
 1830                 + sizeof(SECURITY_DESCRIPTOR_HEADER);
 1831 
 1832             securattr = (char*)ntfs_malloc(size);
 1833             if (securattr) {
 1834                 rdsize = ntfs_attr_data_read(
 1835                     ni, STREAM_SDS, 4,
 1836                     securattr, size, offs);
 1837                 if ((rdsize != size)
 1838                     || !ntfs_valid_descr(securattr,
 1839                         rdsize)) {
 1840                     /* error to be logged by caller */
 1841                     free(securattr);
 1842                     securattr = (char*)NULL;
 1843                 }
 1844             }
 1845         } else
 1846             if (errno != ENOENT)
 1847                 ntfs_log_perror("Inconsistency in index $SII");
 1848     }
 1849     if (!securattr) {
 1850         ntfs_log_error("Failed to retrieve a security descriptor\n");
 1851         errno = EIO;
 1852     }
 1853     return (securattr);
 1854 }
 1855 
 1856 /*
 1857  *      Get the security descriptor associated to a file
 1858  *
 1859  *  Either :
 1860  *     - read the security descriptor attribute (v1.x format)
 1861  *     - or find the descriptor in $Secure:$SDS (v3.x format)
 1862  *
 1863  *  in both case, sanity checks are done on the attribute and
 1864  *  the descriptor can be assumed safe
 1865  *
 1866  *  The returned descriptor is dynamically allocated and has to be freed
 1867  */
 1868 
 1869 static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
 1870 {
 1871     SII_INDEX_KEY securid;
 1872     char *securattr;
 1873     s64 readallsz;
 1874 
 1875         /*
 1876          * Warning : in some situations, after fixing by chkdsk,
 1877          * v3_Extensions are marked present (long standard informations)
 1878          * with a default security descriptor inserted in an
 1879          * attribute
 1880          */
 1881     if (test_nino_flag(ni, v3_Extensions)
 1882         && vol->secure_ni && ni->security_id) {
 1883             /* get v3.x descriptor in $Secure */
 1884         securid.security_id = ni->security_id;
 1885         securattr = retrievesecurityattr(vol,securid);
 1886         if (!securattr)
 1887             ntfs_log_error("Bad security descriptor for 0x%lx\n",
 1888                     (long)le32_to_cpu(ni->security_id));
 1889     } else {
 1890             /* get v1.x security attribute */
 1891         readallsz = 0;
 1892         securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
 1893                 AT_UNNAMED, 0, &readallsz);
 1894         if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
 1895             ntfs_log_error("Bad security descriptor for inode %lld\n",
 1896                 (long long)ni->mft_no);
 1897             free(securattr);
 1898             securattr = (char*)NULL;
 1899         }
 1900     }
 1901     if (!securattr) {
 1902             /*
 1903              * in some situations, there is no security
 1904              * descriptor, and chkdsk does not detect or fix
 1905              * anything. This could be a normal situation.
 1906              * When this happens, simulate a descriptor with
 1907              * minimum rights, so that a real descriptor can
 1908              * be created by chown or chmod
 1909              */
 1910         ntfs_log_error("No security descriptor found for inode %lld\n",
 1911                 (long long)ni->mft_no);
 1912         securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
 1913     }
 1914     return (securattr);
 1915 }
 1916 
 1917 #if POSIXACLS
 1918 
 1919 /*
 1920  *      Determine which access types to a file are allowed
 1921  *  according to the relation of current process to the file
 1922  *
 1923  *  When Posix ACLs are compiled in but not enabled in the mount
 1924  *  options POSIX_ACL_USER, POSIX_ACL_GROUP and POSIX_ACL_MASK
 1925  *  are ignored.
 1926  */
 1927 
 1928 static int access_check_posix(struct SECURITY_CONTEXT *scx,
 1929             struct POSIX_SECURITY *pxdesc, mode_t request,
 1930             uid_t uid, gid_t gid)
 1931 {
 1932     struct POSIX_ACE *pxace;
 1933     int userperms;
 1934     int groupperms;
 1935     int mask;
 1936     BOOL somegroup;
 1937     BOOL needgroups;
 1938     BOOL noacl;
 1939     mode_t perms;
 1940     int i;
 1941 
 1942     noacl = !(scx->vol->secure_flags & (1 << SECURITY_ACL));
 1943     if (noacl)
 1944         perms = ntfs_basic_perms(scx, pxdesc);
 1945     else
 1946         perms = pxdesc->mode;
 1947                     /* owner and root access */
 1948     if (!scx->uid || (uid == scx->uid)) {
 1949         if (!scx->uid) {
 1950                     /* root access if owner or other execution */
 1951             if (perms & 0101)
 1952                 perms |= 01777;
 1953             else {
 1954                     /* root access if some group execution */
 1955                 groupperms = 0;
 1956                 mask = 7;
 1957                 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
 1958                     pxace = &pxdesc->acl.ace[i];
 1959                     switch (pxace->tag) {
 1960                     case POSIX_ACL_USER_OBJ :
 1961                     case POSIX_ACL_GROUP_OBJ :
 1962                         groupperms |= pxace->perms;
 1963                         break;
 1964                     case POSIX_ACL_GROUP :
 1965                         if (!noacl)
 1966                             groupperms
 1967                                 |= pxace->perms;
 1968                         break;
 1969                     case POSIX_ACL_MASK :
 1970                         if (!noacl)
 1971                             mask = pxace->perms & 7;
 1972                         break;
 1973                     default :
 1974                         break;
 1975                     }
 1976                 }
 1977                 perms = (groupperms & mask & 1) | 6;
 1978             }
 1979         } else
 1980             perms &= 07700;
 1981     } else {
 1982                 /*
 1983                  * analyze designated users, get mask
 1984                  * and identify whether we need to check
 1985                  * the group memberships. The groups are
 1986                  * not needed when all groups have the
 1987                  * same permissions as other for the
 1988                  * requested modes.
 1989                  */
 1990         userperms = -1;
 1991         groupperms = -1;
 1992         needgroups = FALSE;
 1993         mask = 7;
 1994         for (i=pxdesc->acccnt-1; i>=0 ; i--) {
 1995             pxace = &pxdesc->acl.ace[i];
 1996             switch (pxace->tag) {
 1997             case POSIX_ACL_USER :
 1998                 if (!noacl
 1999                     && ((uid_t)pxace->id == scx->uid))
 2000                     userperms = pxace->perms;
 2001                 break;
 2002             case POSIX_ACL_MASK :
 2003                 if (!noacl)
 2004                     mask = pxace->perms & 7;
 2005                 break;
 2006             case POSIX_ACL_GROUP_OBJ :
 2007                 if (((pxace->perms & mask) ^ perms)
 2008                     & (request >> 6) & 7)
 2009                     needgroups = TRUE;
 2010                 break;
 2011             case POSIX_ACL_GROUP :
 2012                 if (!noacl
 2013                     && (((pxace->perms & mask) ^ perms)
 2014                         & (request >> 6) & 7))
 2015                     needgroups = TRUE;
 2016                 break;
 2017             default :
 2018                 break;
 2019             }
 2020         }
 2021                     /* designated users */
 2022         if (userperms >= 0)
 2023             perms = (perms & 07000) + (userperms & mask);
 2024         else if (!needgroups)
 2025                 perms &= 07007;
 2026         else {
 2027                     /* owning group */
 2028             if (!(~(perms >> 3) & request & mask)
 2029                 && ((gid == scx->gid)
 2030                 || groupmember(scx, scx->uid, gid)))
 2031                 perms &= 07070;
 2032             else if (!noacl) {
 2033                     /* other groups */
 2034                 groupperms = -1;
 2035                 somegroup = FALSE;
 2036                 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
 2037                     pxace = &pxdesc->acl.ace[i];
 2038                     if ((pxace->tag == POSIX_ACL_GROUP)
 2039                         && groupmember(scx, scx->uid, pxace->id)) {
 2040                         if (!(~pxace->perms & request & mask))
 2041                             groupperms = pxace->perms;
 2042                         somegroup = TRUE;
 2043                     }
 2044                 }
 2045                 if (groupperms >= 0)
 2046                     perms = (perms & 07000) + (groupperms & mask);
 2047                 else
 2048                     if (somegroup)
 2049                         perms = 0;
 2050                     else
 2051                         perms &= 07007;
 2052             } else
 2053                 perms &= 07007;
 2054         }
 2055     }
 2056     return (perms);
 2057 }
 2058 
 2059 /*
 2060  *      Get permissions to access a file
 2061  *  Takes into account the relation of user to file (owner, group, ...)
 2062  *  Do no use as mode of the file
 2063  *  Do no call if default_permissions is set
 2064  *
 2065  *  returns -1 if there is a problem
 2066  */
 2067 
 2068 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
 2069          ntfs_inode * ni, mode_t request)
 2070 {
 2071     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 2072     const struct CACHED_PERMISSIONS *cached;
 2073     char *securattr;
 2074     const SID *usid;    /* owner of file/directory */
 2075     const SID *gsid;    /* group of file/directory */
 2076     uid_t uid;
 2077     gid_t gid;
 2078     int perm;
 2079     BOOL isdir;
 2080     struct POSIX_SECURITY *pxdesc;
 2081 
 2082     if (!scx->mapping[MAPUSERS])
 2083         perm = 07777;
 2084     else {
 2085         /* check whether available in cache */
 2086         cached = fetch_cache(scx,ni);
 2087         if (cached) {
 2088             uid = cached->uid;
 2089             gid = cached->gid;
 2090             perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
 2091         } else {
 2092             perm = 0;   /* default to no permission */
 2093             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 2094                 != const_cpu_to_le16(0);
 2095             securattr = getsecurityattr(scx->vol, ni);
 2096             if (securattr) {
 2097                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 2098                         securattr;
 2099                 gsid = (const SID*)&
 2100                        securattr[le32_to_cpu(phead->group)];
 2101                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 2102 #if OWNERFROMACL
 2103                 usid = ntfs_acl_owner(securattr);
 2104                 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
 2105                          usid, gsid, isdir);
 2106                 if (pxdesc)
 2107                     perm = pxdesc->mode & 07777;
 2108                 else
 2109                     perm = -1;
 2110                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2111 #else
 2112                 usid = (const SID*)&
 2113                         securattr[le32_to_cpu(phead->owner)];
 2114                 pxdesc = ntfs_build_permissions_posix(scx,securattr,
 2115                          usid, gsid, isdir);
 2116                 if (pxdesc)
 2117                     perm = pxdesc->mode & 07777;
 2118                 else
 2119                     perm = -1;
 2120                 if (!perm && ntfs_same_sid(usid, adminsid)) {
 2121                     uid = find_tenant(scx, securattr);
 2122                     if (uid)
 2123                         perm = 0700;
 2124                 } else
 2125                     uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2126 #endif
 2127                 /*
 2128                  *  Create a security id if there were none
 2129                  * and upgrade option is selected
 2130                  */
 2131                 if (!test_nino_flag(ni, v3_Extensions)
 2132                    && (perm >= 0)
 2133                    && (scx->vol->secure_flags
 2134                      & (1 << SECURITY_ADDSECURIDS))) {
 2135                     upgrade_secur_desc(scx->vol,
 2136                         securattr, ni);
 2137                     /*
 2138                      * fetch owner and group for cacheing
 2139                      * if there is a securid
 2140                      */
 2141                 }
 2142                 if (test_nino_flag(ni, v3_Extensions)
 2143                     && (perm >= 0)) {
 2144                     enter_cache(scx, ni, uid,
 2145                             gid, pxdesc);
 2146                 }
 2147                 if (pxdesc) {
 2148                     perm = access_check_posix(scx,pxdesc,request,uid,gid);
 2149                     free(pxdesc);
 2150                 }
 2151                 free(securattr);
 2152             } else {
 2153                 perm = -1;
 2154                 uid = gid = 0;
 2155             }
 2156         }
 2157     }
 2158     return (perm);
 2159 }
 2160 
 2161 /*
 2162  *      Get a Posix ACL
 2163  *
 2164  *  returns size or -errno if there is a problem
 2165  *  if size was too small, no copy is done and errno is not set,
 2166  *  the caller is expected to issue a new call
 2167  */
 2168 
 2169 int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 2170             const char *name, char *value, size_t size) 
 2171 {
 2172     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 2173     struct POSIX_SECURITY *pxdesc;
 2174     const struct CACHED_PERMISSIONS *cached;
 2175     char *securattr;
 2176     const SID *usid;    /* owner of file/directory */
 2177     const SID *gsid;    /* group of file/directory */
 2178     uid_t uid;
 2179     gid_t gid;
 2180     BOOL isdir;
 2181     size_t outsize;
 2182 
 2183     outsize = 0;    /* default to error */
 2184     if (!scx->mapping[MAPUSERS])
 2185         errno = ENOTSUP;
 2186     else {
 2187             /* check whether available in cache */
 2188         cached = fetch_cache(scx,ni);
 2189         if (cached)
 2190             pxdesc = cached->pxdesc;
 2191         else {
 2192             securattr = getsecurityattr(scx->vol, ni);
 2193             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 2194                 != const_cpu_to_le16(0);
 2195             if (securattr) {
 2196                 phead =
 2197                     (const SECURITY_DESCRIPTOR_RELATIVE*)
 2198                             securattr;
 2199                 gsid = (const SID*)&
 2200                       securattr[le32_to_cpu(phead->group)];
 2201 #if OWNERFROMACL
 2202                 usid = ntfs_acl_owner(securattr);
 2203 #else
 2204                 usid = (const SID*)&
 2205                       securattr[le32_to_cpu(phead->owner)];
 2206 #endif
 2207                 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
 2208                       usid, gsid, isdir);
 2209 
 2210                     /*
 2211                      * fetch owner and group for cacheing
 2212                      */
 2213                 if (pxdesc) {
 2214                 /*
 2215                  *  Create a security id if there were none
 2216                  * and upgrade option is selected
 2217                  */
 2218                     if (!test_nino_flag(ni, v3_Extensions)
 2219                        && (scx->vol->secure_flags
 2220                          & (1 << SECURITY_ADDSECURIDS))) {
 2221                         upgrade_secur_desc(scx->vol,
 2222                              securattr, ni);
 2223                     }
 2224 #if OWNERFROMACL
 2225                     uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2226 #else
 2227                     if (!(pxdesc->mode & 07777)
 2228                         && ntfs_same_sid(usid, adminsid)) {
 2229                         uid = find_tenant(scx,
 2230                                 securattr);
 2231                     } else
 2232                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2233 #endif
 2234                     gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 2235                     if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
 2236                     enter_cache(scx, ni, uid,
 2237                             gid, pxdesc);
 2238                 }
 2239                 free(securattr);
 2240             } else
 2241                 pxdesc = (struct POSIX_SECURITY*)NULL;
 2242         }
 2243 
 2244         if (pxdesc) {
 2245             if (ntfs_valid_posix(pxdesc)) {
 2246                 if (!strcmp(name,"system.posix_acl_default")) {
 2247                     if (ni->mrec->flags
 2248                             & MFT_RECORD_IS_DIRECTORY)
 2249                         outsize = sizeof(struct POSIX_ACL)
 2250                             + pxdesc->defcnt*sizeof(struct POSIX_ACE);
 2251                     else {
 2252                     /*
 2253                      * getting default ACL from plain file :
 2254                      * return EACCES if size > 0 as
 2255                      * indicated in the man, but return ok
 2256                      * if size == 0, so that ls does not
 2257                      * display an error
 2258                      */
 2259                         if (size > 0) {
 2260                             outsize = 0;
 2261                             errno = EACCES;
 2262                         } else
 2263                             outsize = sizeof(struct POSIX_ACL);
 2264                     }
 2265                     if (outsize && (outsize <= size)) {
 2266                         memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
 2267                         memcpy(&value[sizeof(struct POSIX_ACL)],
 2268                             &pxdesc->acl.ace[pxdesc->firstdef],
 2269                             outsize-sizeof(struct POSIX_ACL));
 2270                     }
 2271                 } else {
 2272                     outsize = sizeof(struct POSIX_ACL)
 2273                         + pxdesc->acccnt*sizeof(struct POSIX_ACE);
 2274                     if (outsize <= size)
 2275                         memcpy(value,&pxdesc->acl,outsize);
 2276                 }
 2277             } else {
 2278                 outsize = 0;
 2279                 errno = EIO;
 2280                 ntfs_log_error("Invalid Posix ACL built\n");
 2281             }
 2282             if (!cached)
 2283                 free(pxdesc);
 2284         } else
 2285             outsize = 0;
 2286     }
 2287     return (outsize ? (int)outsize : -errno);
 2288 }
 2289 
 2290 #else /* POSIXACLS */
 2291 
 2292 
 2293 /*
 2294  *      Get permissions to access a file
 2295  *  Takes into account the relation of user to file (owner, group, ...)
 2296  *  Do no use as mode of the file
 2297  *
 2298  *  returns -1 if there is a problem
 2299  */
 2300 
 2301 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
 2302         ntfs_inode *ni, mode_t request)
 2303 {
 2304     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 2305     const struct CACHED_PERMISSIONS *cached;
 2306     char *securattr;
 2307     const SID *usid;    /* owner of file/directory */
 2308     const SID *gsid;    /* group of file/directory */
 2309     BOOL isdir;
 2310     uid_t uid;
 2311     gid_t gid;
 2312     int perm;
 2313 
 2314     if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
 2315         perm = 07777;
 2316     else {
 2317         /* check whether available in cache */
 2318         cached = fetch_cache(scx,ni);
 2319         if (cached) {
 2320             perm = cached->mode;
 2321             uid = cached->uid;
 2322             gid = cached->gid;
 2323         } else {
 2324             perm = 0;   /* default to no permission */
 2325             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 2326                 != const_cpu_to_le16(0);
 2327             securattr = getsecurityattr(scx->vol, ni);
 2328             if (securattr) {
 2329                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 2330                         securattr;
 2331                 gsid = (const SID*)&
 2332                        securattr[le32_to_cpu(phead->group)];
 2333                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 2334 #if OWNERFROMACL
 2335                 usid = ntfs_acl_owner(securattr);
 2336                 perm = ntfs_build_permissions(securattr,
 2337                          usid, gsid, isdir);
 2338                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2339 #else
 2340                 usid = (const SID*)&
 2341                         securattr[le32_to_cpu(phead->owner)];
 2342                 perm = ntfs_build_permissions(securattr,
 2343                          usid, gsid, isdir);
 2344                 if (!perm && ntfs_same_sid(usid, adminsid)) {
 2345                     uid = find_tenant(scx, securattr);
 2346                     if (uid)
 2347                         perm = 0700;
 2348                 } else
 2349                     uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2350 #endif
 2351                 /*
 2352                  *  Create a security id if there were none
 2353                  * and upgrade option is selected
 2354                  */
 2355                 if (!test_nino_flag(ni, v3_Extensions)
 2356                    && (perm >= 0)
 2357                    && (scx->vol->secure_flags
 2358                      & (1 << SECURITY_ADDSECURIDS))) {
 2359                     upgrade_secur_desc(scx->vol,
 2360                         securattr, ni);
 2361                     /*
 2362                      * fetch owner and group for cacheing
 2363                      * if there is a securid
 2364                      */
 2365                 }
 2366                 if (test_nino_flag(ni, v3_Extensions)
 2367                     && (perm >= 0)) {
 2368                     enter_cache(scx, ni, uid,
 2369                             gid, perm);
 2370                 }
 2371                 free(securattr);
 2372             } else {
 2373                 perm = -1;
 2374                 uid = gid = 0;
 2375             }
 2376         }
 2377         if (perm >= 0) {
 2378             if (!scx->uid) {
 2379                 /* root access and execution */
 2380                 if (perm & 0111)
 2381                     perm |= 01777;
 2382                 else
 2383                     perm = 0;
 2384             } else
 2385                 if (uid == scx->uid)
 2386                     perm &= 07700;
 2387                 else
 2388                 /*
 2389                  * avoid checking group membership
 2390                  * when the requested perms for group
 2391                  * are the same as perms for other
 2392                  */
 2393                     if ((gid == scx->gid)
 2394                       || ((((perm >> 3) ^ perm)
 2395                         & (request >> 6) & 7)
 2396                         && groupmember(scx, scx->uid, gid)))
 2397                         perm &= 07070;
 2398                     else
 2399                         perm &= 07007;
 2400         }
 2401     }
 2402     return (perm);
 2403 }
 2404 
 2405 #endif /* POSIXACLS */
 2406 
 2407 /*
 2408  *      Get an NTFS ACL
 2409  *
 2410  *  Returns size or -errno if there is a problem
 2411  *  if size was too small, no copy is done and errno is not set,
 2412  *  the caller is expected to issue a new call
 2413  */
 2414 
 2415 int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 2416             char *value, size_t size)
 2417 {
 2418     char *securattr;
 2419     size_t outsize;
 2420 
 2421     outsize = 0;    /* default to no data and no error */
 2422     securattr = getsecurityattr(scx->vol, ni);
 2423     if (securattr) {
 2424         outsize = ntfs_attr_size(securattr);
 2425         if (outsize <= size) {
 2426             memcpy(value,securattr,outsize);
 2427         }
 2428         free(securattr);
 2429     }
 2430     return (outsize ? (int)outsize : -errno);
 2431 }
 2432 
 2433 /*
 2434  *      Get owner, group and permissions in an stat structure
 2435  *  returns permissions, or -1 if there is a problem
 2436  */
 2437 
 2438 int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
 2439         ntfs_inode * ni, struct stat *stbuf)
 2440 {
 2441     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 2442     char *securattr;
 2443     const SID *usid;    /* owner of file/directory */
 2444     const SID *gsid;    /* group of file/directory */
 2445     const struct CACHED_PERMISSIONS *cached;
 2446     int perm;
 2447     BOOL isdir;
 2448 #if POSIXACLS
 2449     struct POSIX_SECURITY *pxdesc;
 2450 #endif
 2451 
 2452     if (!scx->mapping[MAPUSERS])
 2453         perm = 07777;
 2454     else {
 2455             /* check whether available in cache */
 2456         cached = fetch_cache(scx,ni);
 2457         if (cached) {
 2458 #if POSIXACLS
 2459             if (!(scx->vol->secure_flags & (1 << SECURITY_ACL))
 2460                 && cached->pxdesc)
 2461                 perm = ntfs_basic_perms(scx,cached->pxdesc);
 2462             else
 2463 #endif
 2464                 perm = cached->mode;
 2465             stbuf->st_uid = cached->uid;
 2466             stbuf->st_gid = cached->gid;
 2467             stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
 2468         } else {
 2469             perm = -1;  /* default to error */
 2470             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 2471                 != const_cpu_to_le16(0);
 2472             securattr = getsecurityattr(scx->vol, ni);
 2473             if (securattr) {
 2474                 phead =
 2475                     (const SECURITY_DESCRIPTOR_RELATIVE*)
 2476                             securattr;
 2477                 gsid = (const SID*)&
 2478                       securattr[le32_to_cpu(phead->group)];
 2479 #if OWNERFROMACL
 2480                 usid = ntfs_acl_owner(securattr);
 2481 #else
 2482                 usid = (const SID*)&
 2483                       securattr[le32_to_cpu(phead->owner)];
 2484 #endif
 2485 #if POSIXACLS
 2486                 pxdesc = ntfs_build_permissions_posix(
 2487                         scx->mapping, securattr,
 2488                     usid, gsid, isdir);
 2489                 if (pxdesc) {
 2490                     if (!(scx->vol->secure_flags
 2491                         & (1 << SECURITY_ACL)))
 2492                         perm = ntfs_basic_perms(scx,
 2493                                 pxdesc);
 2494                     else
 2495                         perm = pxdesc->mode & 07777;
 2496                 } else
 2497                     perm = -1;
 2498 #else
 2499                 perm = ntfs_build_permissions(securattr,
 2500                       usid, gsid, isdir);
 2501 #endif
 2502                     /*
 2503                      * fetch owner and group for cacheing
 2504                      */
 2505                 if (perm >= 0) {
 2506                 /*
 2507                  *  Create a security id if there were none
 2508                  * and upgrade option is selected
 2509                  */
 2510                     if (!test_nino_flag(ni, v3_Extensions)
 2511                        && (scx->vol->secure_flags
 2512                          & (1 << SECURITY_ADDSECURIDS))) {
 2513                         upgrade_secur_desc(scx->vol,
 2514                              securattr, ni);
 2515                     }
 2516 #if OWNERFROMACL
 2517                     stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2518 #else
 2519                     if (!perm && ntfs_same_sid(usid, adminsid)) {
 2520                         stbuf->st_uid = 
 2521                             find_tenant(scx,
 2522                                 securattr);
 2523                         if (stbuf->st_uid)
 2524                             perm = 0700;
 2525                     } else
 2526                         stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2527 #endif
 2528                     stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 2529                     stbuf->st_mode =
 2530                         (stbuf->st_mode & ~07777) + perm;
 2531 #if POSIXACLS
 2532                     enter_cache(scx, ni, stbuf->st_uid,
 2533                         stbuf->st_gid, pxdesc);
 2534                     free(pxdesc);
 2535 #else
 2536                     enter_cache(scx, ni, stbuf->st_uid,
 2537                         stbuf->st_gid, perm);
 2538 #endif
 2539                 }
 2540                 free(securattr);
 2541             }
 2542         }
 2543     }
 2544     return (perm);
 2545 }
 2546 
 2547 #if POSIXACLS
 2548 
 2549 /*
 2550  *      Get the base for a Posix inheritance and
 2551  *  build an inherited Posix descriptor
 2552  */
 2553 
 2554 static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
 2555             ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
 2556 {
 2557     const struct CACHED_PERMISSIONS *cached;
 2558     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 2559     struct POSIX_SECURITY *pxdesc;
 2560     struct POSIX_SECURITY *pydesc;
 2561     char *securattr;
 2562     const SID *usid;
 2563     const SID *gsid;
 2564     uid_t uid;
 2565     gid_t gid;
 2566 
 2567     pydesc = (struct POSIX_SECURITY*)NULL;
 2568         /* check whether parent directory is available in cache */
 2569     cached = fetch_cache(scx,dir_ni);
 2570     if (cached) {
 2571         uid = cached->uid;
 2572         gid = cached->gid;
 2573         pxdesc = cached->pxdesc;
 2574         if (pxdesc) {
 2575             if (scx->vol->secure_flags & (1 << SECURITY_ACL))
 2576                 pydesc = ntfs_build_inherited_posix(pxdesc,
 2577                     mode, scx->umask, isdir);
 2578             else
 2579                 pydesc = ntfs_build_basic_posix(pxdesc,
 2580                     mode, scx->umask, isdir);
 2581         }
 2582     } else {
 2583         securattr = getsecurityattr(scx->vol, dir_ni);
 2584         if (securattr) {
 2585             phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 2586                     securattr;
 2587             gsid = (const SID*)&
 2588                    securattr[le32_to_cpu(phead->group)];
 2589             gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 2590 #if OWNERFROMACL
 2591             usid = ntfs_acl_owner(securattr);
 2592             pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
 2593                          usid, gsid, TRUE);
 2594             uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2595 #else
 2596             usid = (const SID*)&
 2597                     securattr[le32_to_cpu(phead->owner)];
 2598             pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
 2599                          usid, gsid, TRUE);
 2600             if (pxdesc && ntfs_same_sid(usid, adminsid)) {
 2601                 uid = find_tenant(scx, securattr);
 2602             } else
 2603                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 2604 #endif
 2605             if (pxdesc) {
 2606                 /*
 2607                  *  Create a security id if there were none
 2608                  * and upgrade option is selected
 2609                  */
 2610                 if (!test_nino_flag(dir_ni, v3_Extensions)
 2611                    && (scx->vol->secure_flags
 2612                      & (1 << SECURITY_ADDSECURIDS))) {
 2613                     upgrade_secur_desc(scx->vol,
 2614                         securattr, dir_ni);
 2615                     /*
 2616                      * fetch owner and group for cacheing
 2617                      * if there is a securid
 2618                      */
 2619                 }
 2620                 if (test_nino_flag(dir_ni, v3_Extensions)) {
 2621                     enter_cache(scx, dir_ni, uid,
 2622                             gid, pxdesc);
 2623                 }
 2624                 if (scx->vol->secure_flags
 2625                             & (1 << SECURITY_ACL))
 2626                     pydesc = ntfs_build_inherited_posix(
 2627                         pxdesc, mode,
 2628                         scx->umask, isdir);
 2629                 else
 2630                     pydesc = ntfs_build_basic_posix(
 2631                         pxdesc, mode,
 2632                         scx->umask, isdir);
 2633                 free(pxdesc);
 2634             }
 2635             free(securattr);
 2636         }
 2637     }
 2638     return (pydesc);
 2639 }
 2640 
 2641 /*
 2642  *      Allocate a security_id for a file being created
 2643  *  
 2644  *  Returns zero if not possible (NTFS v3.x required)
 2645  */
 2646 
 2647 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
 2648         uid_t uid, gid_t gid, ntfs_inode *dir_ni,
 2649         mode_t mode, BOOL isdir)
 2650 {
 2651 #if !FORCE_FORMAT_v1x
 2652     const struct CACHED_SECURID *cached;
 2653     struct CACHED_SECURID wanted;
 2654     struct POSIX_SECURITY *pxdesc;
 2655     char *newattr;
 2656     int newattrsz;
 2657     const SID *usid;
 2658     const SID *gsid;
 2659     BIGSID defusid;
 2660     BIGSID defgsid;
 2661     le32 securid;
 2662 #endif
 2663 
 2664     securid = const_cpu_to_le32(0);
 2665 
 2666 #if !FORCE_FORMAT_v1x
 2667 
 2668     pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
 2669     if (pxdesc) {
 2670         /* check whether target securid is known in cache */
 2671 
 2672         wanted.uid = uid;
 2673         wanted.gid = gid;
 2674         wanted.dmode = pxdesc->mode & mode & 07777;
 2675         if (isdir) wanted.dmode |= 0x10000;
 2676         wanted.variable = (void*)pxdesc;
 2677         wanted.varsize = sizeof(struct POSIX_SECURITY)
 2678                 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 2679         cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
 2680                 scx->vol->securid_cache, GENERIC(&wanted),
 2681                 (cache_compare)compare);
 2682             /* quite simple, if we are lucky */
 2683         if (cached)
 2684             securid = cached->securid;
 2685 
 2686             /* not in cache : make sure we can create ids */
 2687 
 2688         if (!cached && (scx->vol->major_ver >= 3)) {
 2689             usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
 2690             gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
 2691             if (!usid || !gsid) {
 2692                 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
 2693                         (int)uid, (int)gid);
 2694                 usid = gsid = adminsid;
 2695             }
 2696             newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
 2697                     isdir, usid, gsid);
 2698             if (newattr) {
 2699                 newattrsz = ntfs_attr_size(newattr);
 2700                 securid = setsecurityattr(scx->vol,
 2701                     (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
 2702                     newattrsz);
 2703                 if (securid) {
 2704                     /* update cache, for subsequent use */
 2705                     wanted.securid = securid;
 2706                     ntfs_enter_cache(scx->vol->securid_cache,
 2707                             GENERIC(&wanted),
 2708                             (cache_compare)compare);
 2709                 }
 2710                 free(newattr);
 2711             } else {
 2712                 /*
 2713                  * could not build new security attribute
 2714                  * errno set by ntfs_build_descr()
 2715                  */
 2716             }
 2717         }
 2718     free(pxdesc);
 2719     }
 2720 #endif
 2721     return (securid);
 2722 }
 2723 
 2724 /*
 2725  *      Apply Posix inheritance to a newly created file
 2726  *  (for NTFS 1.x only : no securid)
 2727  */
 2728 
 2729 int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
 2730         ntfs_inode *ni, uid_t uid, gid_t gid,
 2731         ntfs_inode *dir_ni, mode_t mode)
 2732 {
 2733     struct POSIX_SECURITY *pxdesc;
 2734     char *newattr;
 2735     const SID *usid;
 2736     const SID *gsid;
 2737     BIGSID defusid;
 2738     BIGSID defgsid;
 2739     BOOL isdir;
 2740     int res;
 2741 
 2742     res = -1;
 2743     isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
 2744     pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
 2745     if (pxdesc) {
 2746         usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
 2747         gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
 2748         if (!usid || !gsid) {
 2749             ntfs_log_error("File created by an unmapped user/group %d/%d\n",
 2750                     (int)uid, (int)gid);
 2751             usid = gsid = adminsid;
 2752         }
 2753         newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
 2754                     isdir, usid, gsid);
 2755         if (newattr) {
 2756                 /* Adjust Windows read-only flag */
 2757             res = update_secur_descr(scx->vol, newattr, ni);
 2758             if (!res && !isdir) {
 2759                 if (mode & S_IWUSR)
 2760                     ni->flags &= ~FILE_ATTR_READONLY;
 2761                 else
 2762                     ni->flags |= FILE_ATTR_READONLY;
 2763             }
 2764 #if CACHE_LEGACY_SIZE
 2765             /* also invalidate legacy cache */
 2766             if (isdir && !ni->security_id) {
 2767                 struct CACHED_PERMISSIONS_LEGACY legacy;
 2768 
 2769                 legacy.mft_no = ni->mft_no;
 2770                 legacy.variable = pxdesc;
 2771                 legacy.varsize = sizeof(struct POSIX_SECURITY)
 2772                     + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 2773                 ntfs_invalidate_cache(scx->vol->legacy_cache,
 2774                         GENERIC(&legacy),
 2775                         (cache_compare)leg_compare,0);
 2776             }
 2777 #endif
 2778             free(newattr);
 2779 
 2780         } else {
 2781             /*
 2782              * could not build new security attribute
 2783              * errno set by ntfs_build_descr()
 2784              */
 2785         }
 2786     }
 2787     return (res);
 2788 }
 2789 
 2790 #else
 2791 
 2792 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
 2793         uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
 2794 {
 2795 #if !FORCE_FORMAT_v1x
 2796     const struct CACHED_SECURID *cached;
 2797     struct CACHED_SECURID wanted;
 2798     char *newattr;
 2799     int newattrsz;
 2800     const SID *usid;
 2801     const SID *gsid;
 2802     BIGSID defusid;
 2803     BIGSID defgsid;
 2804     le32 securid;
 2805 #endif
 2806 
 2807     securid = const_cpu_to_le32(0);
 2808 
 2809 #if !FORCE_FORMAT_v1x
 2810         /* check whether target securid is known in cache */
 2811 
 2812     wanted.uid = uid;
 2813     wanted.gid = gid;
 2814     wanted.dmode = mode & 07777;
 2815     if (isdir) wanted.dmode |= 0x10000;
 2816     wanted.variable = (void*)NULL;
 2817     wanted.varsize = 0;
 2818     cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
 2819             scx->vol->securid_cache, GENERIC(&wanted),
 2820             (cache_compare)compare);
 2821         /* quite simple, if we are lucky */
 2822     if (cached)
 2823         securid = cached->securid;
 2824 
 2825         /* not in cache : make sure we can create ids */
 2826 
 2827     if (!cached && (scx->vol->major_ver >= 3)) {
 2828         usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
 2829         gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
 2830         if (!usid || !gsid) {
 2831             ntfs_log_error("File created by an unmapped user/group %d/%d\n",
 2832                     (int)uid, (int)gid);
 2833             usid = gsid = adminsid;
 2834         }
 2835         newattr = ntfs_build_descr(mode, isdir, usid, gsid);
 2836         if (newattr) {
 2837             newattrsz = ntfs_attr_size(newattr);
 2838             securid = setsecurityattr(scx->vol,
 2839                 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
 2840                 newattrsz);
 2841             if (securid) {
 2842                 /* update cache, for subsequent use */
 2843                 wanted.securid = securid;
 2844                 ntfs_enter_cache(scx->vol->securid_cache,
 2845                         GENERIC(&wanted),
 2846                         (cache_compare)compare);
 2847             }
 2848             free(newattr);
 2849         } else {
 2850             /*
 2851              * could not build new security attribute
 2852              * errno set by ntfs_build_descr()
 2853              */
 2854         }
 2855     }
 2856 #endif
 2857     return (securid);
 2858 }
 2859 
 2860 #endif
 2861 
 2862 /*
 2863  *      Update ownership and mode of a file, reusing an existing
 2864  *  security descriptor when possible
 2865  *  
 2866  *  Returns zero if successful
 2867  */
 2868 
 2869 #if POSIXACLS
 2870 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 2871         uid_t uid, gid_t gid, mode_t mode,
 2872         struct POSIX_SECURITY *pxdesc)
 2873 #else
 2874 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 2875         uid_t uid, gid_t gid, mode_t mode)
 2876 #endif
 2877 {
 2878     int res;
 2879     const struct CACHED_SECURID *cached;
 2880     struct CACHED_SECURID wanted;
 2881     char *newattr;
 2882     const SID *usid;
 2883     const SID *gsid;
 2884     BIGSID defusid;
 2885     BIGSID defgsid;
 2886     BOOL isdir;
 2887 
 2888     res = 0;
 2889 
 2890         /* check whether target securid is known in cache */
 2891 
 2892     isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
 2893     wanted.uid = uid;
 2894     wanted.gid = gid;
 2895     wanted.dmode = mode & 07777;
 2896     if (isdir) wanted.dmode |= 0x10000;
 2897 #if POSIXACLS
 2898     wanted.variable = (void*)pxdesc;
 2899     if (pxdesc)
 2900         wanted.varsize = sizeof(struct POSIX_SECURITY)
 2901             + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
 2902     else
 2903         wanted.varsize = 0;
 2904 #else
 2905     wanted.variable = (void*)NULL;
 2906     wanted.varsize = 0;
 2907 #endif
 2908     if (test_nino_flag(ni, v3_Extensions)) {
 2909         cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
 2910                 scx->vol->securid_cache, GENERIC(&wanted),
 2911                 (cache_compare)compare);
 2912             /* quite simple, if we are lucky */
 2913         if (cached) {
 2914             ni->security_id = cached->securid;
 2915             NInoSetDirty(ni);
 2916                 /* adjust Windows read-only flag */
 2917             if (!isdir) {
 2918                 if (mode & S_IWUSR)
 2919                     ni->flags &= ~FILE_ATTR_READONLY;
 2920                 else
 2921                     ni->flags |= FILE_ATTR_READONLY;
 2922                 NInoFileNameSetDirty(ni);
 2923             }
 2924         }
 2925     } else cached = (struct CACHED_SECURID*)NULL;
 2926 
 2927     if (!cached) {
 2928             /*
 2929              * Do not use usid and gsid from former attributes,
 2930              * but recompute them to get repeatable results
 2931              * which can be kept in cache.
 2932              */
 2933         usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
 2934         gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
 2935         if (!usid || !gsid) {
 2936             ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
 2937                 uid, gid);
 2938             usid = gsid = adminsid;
 2939         }
 2940 #if POSIXACLS
 2941         if (pxdesc)
 2942             newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
 2943                      isdir, usid, gsid);
 2944         else
 2945             newattr = ntfs_build_descr(mode,
 2946                      isdir, usid, gsid);
 2947 #else
 2948         newattr = ntfs_build_descr(mode,
 2949                      isdir, usid, gsid);
 2950 #endif
 2951         if (newattr) {
 2952             res = update_secur_descr(scx->vol, newattr, ni);
 2953             if (!res) {
 2954                 /* adjust Windows read-only flag */
 2955                 if (!isdir) {
 2956                     if (mode & S_IWUSR)
 2957                         ni->flags &= ~FILE_ATTR_READONLY;
 2958                     else
 2959                         ni->flags |= FILE_ATTR_READONLY;
 2960                     NInoFileNameSetDirty(ni);
 2961                 }
 2962                 /* update cache, for subsequent use */
 2963                 if (test_nino_flag(ni, v3_Extensions)) {
 2964                     wanted.securid = ni->security_id;
 2965                     ntfs_enter_cache(scx->vol->securid_cache,
 2966                             GENERIC(&wanted),
 2967                             (cache_compare)compare);
 2968                 }
 2969 #if CACHE_LEGACY_SIZE
 2970                 /* also invalidate legacy cache */
 2971                 if (isdir && !ni->security_id) {
 2972                     struct CACHED_PERMISSIONS_LEGACY legacy;
 2973 
 2974                     legacy.mft_no = ni->mft_no;
 2975 #if POSIXACLS
 2976                     legacy.variable = wanted.variable;
 2977                     legacy.varsize = wanted.varsize;
 2978 #else
 2979                     legacy.variable = (void*)NULL;
 2980                     legacy.varsize = 0;
 2981 #endif
 2982                     ntfs_invalidate_cache(scx->vol->legacy_cache,
 2983                         GENERIC(&legacy),
 2984                         (cache_compare)leg_compare,0);
 2985                 }
 2986 #endif
 2987             }
 2988             free(newattr);
 2989         } else {
 2990             /*
 2991              * could not build new security attribute
 2992              * errno set by ntfs_build_descr()
 2993              */
 2994             res = -1;
 2995         }
 2996     }
 2997     return (res);
 2998 }
 2999 
 3000 /*
 3001  *      Check whether user has ownership rights on a file
 3002  *
 3003  *  Returns TRUE if allowed
 3004  *      if not, errno tells why
 3005  */
 3006 
 3007 BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
 3008 {
 3009     const struct CACHED_PERMISSIONS *cached;
 3010     char *oldattr;
 3011     const SID *usid;
 3012     uid_t processuid;
 3013     uid_t uid;
 3014     BOOL gotowner;
 3015     int allowed;
 3016 
 3017     processuid = scx->uid;
 3018 /* TODO : use CAP_FOWNER process capability */
 3019     /*
 3020      * Always allow for root
 3021      * Also always allow if no mapping has been defined
 3022      */
 3023     if (!scx->mapping[MAPUSERS] || !processuid)
 3024         allowed = TRUE;
 3025     else {
 3026         gotowner = FALSE; /* default */
 3027         /* get the owner, either from cache or from old attribute  */
 3028         cached = fetch_cache(scx, ni);
 3029         if (cached) {
 3030             uid = cached->uid;
 3031             gotowner = TRUE;
 3032         } else {
 3033             oldattr = getsecurityattr(scx->vol, ni);
 3034             if (oldattr) {
 3035 #if OWNERFROMACL
 3036                 usid = ntfs_acl_owner(oldattr);
 3037 #else
 3038                 const SECURITY_DESCRIPTOR_RELATIVE *phead;
 3039 
 3040                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 3041                                 oldattr;
 3042                 usid = (const SID*)&oldattr
 3043                         [le32_to_cpu(phead->owner)];
 3044 #endif
 3045                 uid = ntfs_find_user(scx->mapping[MAPUSERS],
 3046                         usid);
 3047                 gotowner = TRUE;
 3048                 free(oldattr);
 3049             }
 3050         }
 3051         allowed = FALSE;
 3052         if (gotowner) {
 3053 /* TODO : use CAP_FOWNER process capability */
 3054             if (!processuid || (processuid == uid))
 3055                 allowed = TRUE;
 3056             else
 3057                 errno = EPERM;
 3058         }
 3059     }
 3060     return (allowed);
 3061 }
 3062 
 3063 
 3064 #if POSIXACLS
 3065 
 3066 /*
 3067  *      Set a new access or default Posix ACL to a file
 3068  *      (or remove ACL if no input data)
 3069  *  Validity of input data is checked after merging
 3070  *
 3071  *  Returns 0, or -1 if there is a problem which errno describes
 3072  */
 3073 
 3074 int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 3075             const char *name, const char *value, size_t size,
 3076             int flags)
 3077 {
 3078     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 3079     const struct CACHED_PERMISSIONS *cached;
 3080     char *oldattr;
 3081     uid_t processuid;
 3082     const SID *usid;
 3083     const SID *gsid;
 3084     uid_t uid;
 3085     uid_t gid;
 3086     int res;
 3087     BOOL isdir;
 3088     BOOL deflt;
 3089     BOOL exist;
 3090     int count;
 3091     struct POSIX_SECURITY *oldpxdesc;
 3092     struct POSIX_SECURITY *newpxdesc;
 3093 
 3094     /* get the current pxsec, either from cache or from old attribute  */
 3095     res = -1;
 3096     deflt = !strcmp(name,"system.posix_acl_default");
 3097     if (size)
 3098         count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
 3099     else
 3100         count = 0;
 3101     isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
 3102     newpxdesc = (struct POSIX_SECURITY*)NULL;
 3103     if ((!value
 3104         || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
 3105         && (!deflt || isdir || (!size && !value))) {
 3106         cached = fetch_cache(scx, ni);
 3107         if (cached) {
 3108             uid = cached->uid;
 3109             gid = cached->gid;
 3110             oldpxdesc = cached->pxdesc;
 3111             if (oldpxdesc) {
 3112                 newpxdesc = ntfs_replace_acl(oldpxdesc,
 3113                         (const struct POSIX_ACL*)value,count,deflt);
 3114                 }
 3115         } else {
 3116             oldattr = getsecurityattr(scx->vol, ni);
 3117             if (oldattr) {
 3118                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
 3119 #if OWNERFROMACL
 3120                 usid = ntfs_acl_owner(oldattr);
 3121 #else
 3122                 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
 3123 #endif
 3124                 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
 3125                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 3126                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 3127                 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
 3128                     oldattr, usid, gsid, isdir);
 3129                 if (oldpxdesc) {
 3130                     if (deflt)
 3131                         exist = oldpxdesc->defcnt > 0;
 3132                     else
 3133                         exist = oldpxdesc->acccnt > 3;
 3134                     if ((exist && (flags & XATTR_CREATE))
 3135                       || (!exist && (flags & XATTR_REPLACE))) {
 3136                         errno = (exist ? EEXIST : ENODATA);
 3137                     } else {
 3138                         newpxdesc = ntfs_replace_acl(oldpxdesc,
 3139                             (const struct POSIX_ACL*)value,count,deflt);
 3140                     }
 3141                     free(oldpxdesc);
 3142                 }
 3143                 free(oldattr);
 3144             }
 3145         }
 3146     } else
 3147         errno = EINVAL;
 3148 
 3149     if (newpxdesc) {
 3150         processuid = scx->uid;
 3151 /* TODO : use CAP_FOWNER process capability */
 3152         if (!processuid || (uid == processuid)) {
 3153                 /*
 3154                  * clear setgid if file group does
 3155                  * not match process group
 3156                  */
 3157             if (processuid && (gid != scx->gid)
 3158                 && !groupmember(scx, scx->uid, gid)) {
 3159                 newpxdesc->mode &= ~S_ISGID;
 3160             }
 3161             res = ntfs_set_owner_mode(scx, ni, uid, gid,
 3162                 newpxdesc->mode, newpxdesc);
 3163         } else
 3164             errno = EPERM;
 3165         free(newpxdesc);
 3166     }
 3167     return (res ? -1 : 0);
 3168 }
 3169 
 3170 /*
 3171  *      Remove a default Posix ACL from a file
 3172  *
 3173  *  Returns 0, or -1 if there is a problem which errno describes
 3174  */
 3175 
 3176 int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 3177             const char *name)
 3178 {
 3179     return (ntfs_set_posix_acl(scx, ni, name,
 3180             (const char*)NULL, 0, 0));
 3181 }
 3182 
 3183 #endif
 3184 
 3185 /*
 3186  *      Set a new NTFS ACL to a file
 3187  *
 3188  *  Returns 0, or -1 if there is a problem
 3189  */
 3190 
 3191 int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 3192             const char *value, size_t size, int flags)
 3193 {
 3194     char *attr;
 3195     int res;
 3196 
 3197     res = -1;
 3198     if ((size > 0)
 3199        && !(flags & XATTR_CREATE)
 3200        && ntfs_valid_descr(value,size)
 3201        && (ntfs_attr_size(value) == size)) {
 3202             /* need copying in order to write */
 3203         attr = (char*)ntfs_malloc(size);
 3204         if (attr) {
 3205             memcpy(attr,value,size);
 3206             res = update_secur_descr(scx->vol, attr, ni);
 3207             /*
 3208              * No need to invalidate standard caches :
 3209              * the relation between a securid and
 3210              * the associated protection is unchanged,
 3211              * only the relation between a file and
 3212              * its securid and protection is changed.
 3213              */
 3214 #if CACHE_LEGACY_SIZE
 3215             /*
 3216              * we must however invalidate the legacy
 3217              * cache, which is based on inode numbers.
 3218              * For safety, invalidate even if updating
 3219              * failed.
 3220              */
 3221             if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 3222                && !ni->security_id) {
 3223                 struct CACHED_PERMISSIONS_LEGACY legacy;
 3224 
 3225                 legacy.mft_no = ni->mft_no;
 3226                 legacy.variable = (char*)NULL;
 3227                 legacy.varsize = 0;
 3228                 ntfs_invalidate_cache(scx->vol->legacy_cache,
 3229                     GENERIC(&legacy),
 3230                     (cache_compare)leg_compare,0);
 3231             }
 3232 #endif
 3233             free(attr);
 3234         } else
 3235             errno = ENOMEM;
 3236     } else
 3237         errno = EINVAL;
 3238     return (res ? -1 : 0);
 3239 }
 3240 
 3241 
 3242 /*
 3243  *      Set new permissions to a file
 3244  *  Checks user mapping has been defined before request for setting
 3245  *
 3246  *  rejected if request is not originated by owner or root
 3247  *
 3248  *  returns 0 on success
 3249  *      -1 on failure, with errno = EIO
 3250  */
 3251 
 3252 int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
 3253 {
 3254     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 3255     const struct CACHED_PERMISSIONS *cached;
 3256     char *oldattr;
 3257     const SID *usid;
 3258     const SID *gsid;
 3259     uid_t processuid;
 3260     uid_t uid;
 3261     uid_t gid;
 3262     int res;
 3263 #if POSIXACLS
 3264     BOOL isdir;
 3265     int pxsize;
 3266     const struct POSIX_SECURITY *oldpxdesc;
 3267     struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
 3268 #endif
 3269 
 3270     /* get the current owner, either from cache or from old attribute  */
 3271     res = 0;
 3272     cached = fetch_cache(scx, ni);
 3273     if (cached) {
 3274         uid = cached->uid;
 3275         gid = cached->gid;
 3276 #if POSIXACLS
 3277         oldpxdesc = cached->pxdesc;
 3278         if (oldpxdesc) {
 3279                 /* must copy before merging */
 3280             pxsize = sizeof(struct POSIX_SECURITY)
 3281                 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
 3282             newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
 3283             if (newpxdesc) {
 3284                 memcpy(newpxdesc, oldpxdesc, pxsize);
 3285                 if (ntfs_merge_mode_posix(newpxdesc, mode))
 3286                     res = -1;
 3287             } else
 3288                 res = -1;
 3289         } else
 3290             newpxdesc = (struct POSIX_SECURITY*)NULL;
 3291 #endif
 3292     } else {
 3293         oldattr = getsecurityattr(scx->vol, ni);
 3294         if (oldattr) {
 3295             phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
 3296 #if OWNERFROMACL
 3297             usid = ntfs_acl_owner(oldattr);
 3298 #else
 3299             usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
 3300 #endif
 3301             gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
 3302             uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 3303             gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 3304 #if POSIXACLS
 3305             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
 3306             newpxdesc = ntfs_build_permissions_posix(scx->mapping,
 3307                 oldattr, usid, gsid, isdir);
 3308             if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
 3309                 res = -1;
 3310 #endif
 3311             free(oldattr);
 3312         } else
 3313             res = -1;
 3314     }
 3315 
 3316     if (!res) {
 3317         processuid = scx->uid;
 3318 /* TODO : use CAP_FOWNER process capability */
 3319         if (!processuid || (uid == processuid)) {
 3320                 /*
 3321                  * clear setgid if file group does
 3322                  * not match process group
 3323                  */
 3324             if (processuid && (gid != scx->gid)
 3325                 && !groupmember(scx, scx->uid, gid))
 3326                 mode &= ~S_ISGID;
 3327 #if POSIXACLS
 3328             if (newpxdesc) {
 3329                 newpxdesc->mode = mode;
 3330                 res = ntfs_set_owner_mode(scx, ni, uid, gid,
 3331                     mode, newpxdesc);
 3332             } else
 3333                 res = ntfs_set_owner_mode(scx, ni, uid, gid,
 3334                     mode, newpxdesc);
 3335 #else
 3336             res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
 3337 #endif
 3338         } else {
 3339             errno = EPERM;
 3340             res = -1;   /* neither owner nor root */
 3341         }
 3342     } else {
 3343         /*
 3344          * Should not happen : a default descriptor is generated
 3345          * by getsecurityattr() when there are none
 3346          */
 3347         ntfs_log_error("File has no security descriptor\n");
 3348         res = -1;
 3349         errno = EIO;
 3350     }
 3351 #if POSIXACLS
 3352     if (newpxdesc) free(newpxdesc);
 3353 #endif
 3354     return (res ? -1 : 0);
 3355 }
 3356 
 3357 /*
 3358  *  Create a default security descriptor for files whose descriptor
 3359  *  cannot be inherited
 3360  */
 3361 
 3362 int ntfs_sd_add_everyone(ntfs_inode *ni)
 3363 {
 3364     /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
 3365     SECURITY_DESCRIPTOR_RELATIVE *sd;
 3366     ACL *acl;
 3367     ACCESS_ALLOWED_ACE *ace;
 3368     SID *sid;
 3369     int ret, sd_len;
 3370     
 3371     /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
 3372     /*
 3373      * Calculate security descriptor length. We have 2 sub-authorities in
 3374      * owner and group SIDs, but structure SID contain only one, so add
 3375      * 4 bytes to every SID.
 3376      */
 3377     sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
 3378         sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); 
 3379     sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
 3380     if (!sd)
 3381         return -1;
 3382     
 3383     sd->revision = SECURITY_DESCRIPTOR_REVISION;
 3384     sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
 3385     
 3386     sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
 3387     sid->revision = SID_REVISION;
 3388     sid->sub_authority_count = 2;
 3389     sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
 3390     sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
 3391     sid->identifier_authority.value[5] = 5;
 3392     sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
 3393     
 3394     sid = (SID*)((u8*)sid + sizeof(SID) + 4); 
 3395     sid->revision = SID_REVISION;
 3396     sid->sub_authority_count = 2;
 3397     sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
 3398     sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
 3399     sid->identifier_authority.value[5] = 5;
 3400     sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
 3401     
 3402     acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
 3403     acl->revision = ACL_REVISION;
 3404     acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
 3405     acl->ace_count = const_cpu_to_le16(1);
 3406     sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
 3407     
 3408     ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
 3409     ace->type = ACCESS_ALLOWED_ACE_TYPE;
 3410     ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
 3411     ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
 3412     ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
 3413     ace->sid.revision = SID_REVISION;
 3414     ace->sid.sub_authority_count = 1;
 3415     ace->sid.sub_authority[0] = const_cpu_to_le32(0);
 3416     ace->sid.identifier_authority.value[5] = 1;
 3417 
 3418     ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
 3419                 sd_len);
 3420     if (ret)
 3421         ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
 3422     
 3423     free(sd);
 3424     return ret;
 3425 }
 3426 
 3427 /*
 3428  *      Check whether user can access a file in a specific way
 3429  *
 3430  *  Returns 1 if access is allowed, including user is root or no
 3431  *        user mapping defined
 3432  *      2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
 3433  *      0 and sets errno if there is a problem or if access
 3434  *        is not allowed
 3435  *
 3436  *  This is used for Posix ACL and checking creation of DOS file names
 3437  */
 3438 
 3439 int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
 3440         ntfs_inode *ni,
 3441         int accesstype) /* access type required (S_Ixxx values) */
 3442 {
 3443     int perm;
 3444     int res;
 3445     int allow;
 3446     struct stat stbuf;
 3447 
 3448     /*
 3449      * Always allow for root unless execution is requested.
 3450      * (was checked by fuse until kernel 2.6.29)
 3451      * Also always allow if no mapping has been defined
 3452      */
 3453     if (!scx->mapping[MAPUSERS]
 3454         || (!scx->uid
 3455         && (!(accesstype & S_IEXEC)
 3456             || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
 3457         allow = 1;
 3458     else {
 3459         perm = ntfs_get_perm(scx, ni, accesstype);
 3460         if (perm >= 0) {
 3461             res = EACCES;
 3462             switch (accesstype) {
 3463             case S_IEXEC:
 3464                 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
 3465                 break;
 3466             case S_IWRITE:
 3467                 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
 3468                 break;
 3469             case S_IWRITE + S_IEXEC:
 3470                 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
 3471                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
 3472                 break;
 3473             case S_IREAD:
 3474                 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
 3475                 break;
 3476             case S_IREAD + S_IEXEC:
 3477                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
 3478                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
 3479                 break;
 3480             case S_IREAD + S_IWRITE:
 3481                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
 3482                     && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
 3483                 break;
 3484             case S_IWRITE + S_IEXEC + S_ISVTX:
 3485                 if (perm & S_ISVTX) {
 3486                     if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
 3487                         && (stbuf.st_uid == scx->uid))
 3488                         allow = 1;
 3489                     else
 3490                         allow = 2;
 3491                 } else
 3492                     allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
 3493                         && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
 3494                 break;
 3495             case S_IREAD + S_IWRITE + S_IEXEC:
 3496                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
 3497                     && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
 3498                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
 3499                 break;
 3500             default :
 3501                 res = EINVAL;
 3502                 allow = 0;
 3503                 break;
 3504             }
 3505             if (!allow)
 3506                 errno = res;
 3507         } else
 3508             allow = 0;
 3509     }
 3510     return (allow);
 3511 }
 3512 
 3513 /*
 3514  *      Check whether user can create a file (or directory)
 3515  *
 3516  *  Returns TRUE if access is allowed,
 3517  *  Also returns the gid and dsetgid applicable to the created file
 3518  */
 3519 
 3520 int ntfs_allowed_create(struct SECURITY_CONTEXT *scx,
 3521         ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid)
 3522 {
 3523     int perm;
 3524     int res;
 3525     int allow;
 3526     struct stat stbuf;
 3527 
 3528     /*
 3529      * Always allow for root.
 3530      * Also always allow if no mapping has been defined
 3531      */
 3532     if (!scx->mapping[MAPUSERS])
 3533         perm = 0777;
 3534     else
 3535         perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
 3536     if (!scx->mapping[MAPUSERS]
 3537         || !scx->uid) {
 3538         allow = 1;
 3539     } else {
 3540         perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
 3541         if (perm >= 0) {
 3542             res = EACCES;
 3543             allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
 3544                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
 3545             if (!allow)
 3546                 errno = res;
 3547         } else
 3548             allow = 0;
 3549     }
 3550     *pgid = scx->gid;
 3551     *pdsetgid = 0;
 3552         /* return directory group if S_ISGID is set */
 3553     if (allow && (perm & S_ISGID)) {
 3554         if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) {
 3555             *pdsetgid = stbuf.st_mode & S_ISGID;
 3556             if (perm & S_ISGID)
 3557                 *pgid = stbuf.st_gid;
 3558         }
 3559     }
 3560     return (allow);
 3561 }
 3562 
 3563 #if 0 /* not needed any more */
 3564 
 3565 /*
 3566  *      Check whether user can access the parent directory
 3567  *  of a file in a specific way
 3568  *
 3569  *  Returns true if access is allowed, including user is root and
 3570  *      no user mapping defined
 3571  *  
 3572  *  Sets errno if there is a problem or if not allowed
 3573  *
 3574  *  This is used for Posix ACL and checking creation of DOS file names
 3575  */
 3576 
 3577 BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
 3578         const char *path, int accesstype)
 3579 {
 3580     int allow;
 3581     char *dirpath;
 3582     char *name;
 3583     ntfs_inode *ni;
 3584     ntfs_inode *dir_ni;
 3585     struct stat stbuf;
 3586 
 3587     allow = 0;
 3588     dirpath = strdup(path);
 3589     if (dirpath) {
 3590         /* the root of file system is seen as a parent of itself */
 3591         /* is that correct ? */
 3592         name = strrchr(dirpath, '/');
 3593         *name = 0;
 3594         dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
 3595         if (dir_ni) {
 3596             allow = ntfs_allowed_access(scx,
 3597                  dir_ni, accesstype);
 3598             ntfs_inode_close(dir_ni);
 3599                 /*
 3600                  * for an not-owned sticky directory, have to
 3601                  * check whether file itself is owned
 3602                  */
 3603             if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
 3604                && (allow == 2)) {
 3605                 ni = ntfs_pathname_to_inode(scx->vol, NULL,
 3606                      path);
 3607                 allow = FALSE;
 3608                 if (ni) {
 3609                     allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
 3610                         && (stbuf.st_uid == scx->uid);
 3611                 ntfs_inode_close(ni);
 3612                 }
 3613             }
 3614         }
 3615         free(dirpath);
 3616     }
 3617     return (allow);     /* errno is set if not allowed */
 3618 }
 3619 
 3620 #endif
 3621 
 3622 /*
 3623  *      Define a new owner/group to a file
 3624  *
 3625  *  returns zero if successful
 3626  */
 3627 
 3628 int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 3629             uid_t uid, gid_t gid)
 3630 {
 3631     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 3632     const struct CACHED_PERMISSIONS *cached;
 3633     char *oldattr;
 3634     const SID *usid;
 3635     const SID *gsid;
 3636     uid_t fileuid;
 3637     uid_t filegid;
 3638     mode_t mode;
 3639     int perm;
 3640     BOOL isdir;
 3641     int res;
 3642 #if POSIXACLS
 3643     struct POSIX_SECURITY *pxdesc;
 3644     BOOL pxdescbuilt = FALSE;
 3645 #endif
 3646 
 3647     res = 0;
 3648     /* get the current owner and mode from cache or security attributes */
 3649     oldattr = (char*)NULL;
 3650     cached = fetch_cache(scx,ni);
 3651     if (cached) {
 3652         fileuid = cached->uid;
 3653         filegid = cached->gid;
 3654         mode = cached->mode;
 3655 #if POSIXACLS
 3656         pxdesc = cached->pxdesc;
 3657         if (!pxdesc)
 3658             res = -1;
 3659 #endif
 3660     } else {
 3661         fileuid = 0;
 3662         filegid = 0;
 3663         mode = 0;
 3664         oldattr = getsecurityattr(scx->vol, ni);
 3665         if (oldattr) {
 3666             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 3667                 != const_cpu_to_le16(0);
 3668             phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 3669                 oldattr;
 3670             gsid = (const SID*)
 3671                 &oldattr[le32_to_cpu(phead->group)];
 3672 #if OWNERFROMACL
 3673             usid = ntfs_acl_owner(oldattr);
 3674 #else
 3675             usid = (const SID*)
 3676                 &oldattr[le32_to_cpu(phead->owner)];
 3677 #endif
 3678 #if POSIXACLS
 3679             pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
 3680                     usid, gsid, isdir);
 3681             if (pxdesc) {
 3682                 pxdescbuilt = TRUE;
 3683                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 3684                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 3685                 mode = perm = pxdesc->mode;
 3686             } else
 3687                 res = -1;
 3688 #else
 3689             mode = perm = ntfs_build_permissions(oldattr,
 3690                      usid, gsid, isdir);
 3691             if (perm >= 0) {
 3692                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 3693                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 3694             } else
 3695                 res = -1;
 3696 #endif
 3697             free(oldattr);
 3698         } else
 3699             res = -1;
 3700     }
 3701     if (!res) {
 3702         /* check requested by root */
 3703         /* or chgrp requested by owner to an owned group */
 3704         if (!scx->uid
 3705            || ((((int)uid < 0) || (uid == fileuid))
 3706               && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
 3707               && (fileuid == scx->uid))) {
 3708             /* replace by the new usid and gsid */
 3709             /* or reuse old gid and sid for cacheing */
 3710             if ((int)uid < 0)
 3711                 uid = fileuid;
 3712             if ((int)gid < 0)
 3713                 gid = filegid;
 3714 #if !defined(__sun) || !defined (__SVR4)
 3715             /* clear setuid and setgid if owner has changed */
 3716                         /* unless request originated by root */
 3717             if (uid && (fileuid != uid))
 3718                 mode &= 01777;
 3719 #endif
 3720 #if POSIXACLS
 3721             res = ntfs_set_owner_mode(scx, ni, uid, gid, 
 3722                 mode, pxdesc);
 3723 #else
 3724             res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
 3725 #endif
 3726         } else {
 3727             res = -1;   /* neither owner nor root */
 3728             errno = EPERM;
 3729         }
 3730 #if POSIXACLS
 3731         if (pxdescbuilt)
 3732             free(pxdesc);
 3733 #endif
 3734     } else {
 3735         /*
 3736          * Should not happen : a default descriptor is generated
 3737          * by getsecurityattr() when there are none
 3738          */
 3739         ntfs_log_error("File has no security descriptor\n");
 3740         res = -1;
 3741         errno = EIO;
 3742     }
 3743     return (res ? -1 : 0);
 3744 }
 3745 
 3746 /*
 3747  *      Define new owner/group and mode to a file
 3748  *
 3749  *  returns zero if successful
 3750  */
 3751 
 3752 int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
 3753             uid_t uid, gid_t gid, const mode_t mode)
 3754 {
 3755     const struct CACHED_PERMISSIONS *cached;
 3756     char *oldattr;
 3757     uid_t fileuid;
 3758     uid_t filegid;
 3759     int res;
 3760 #if POSIXACLS
 3761     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 3762     const SID *usid;
 3763     const SID *gsid;
 3764     BOOL isdir;
 3765     const struct POSIX_SECURITY *oldpxdesc;
 3766     struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
 3767     int pxsize;
 3768 #endif
 3769 
 3770     res = 0;
 3771     /* get the current owner and mode from cache or security attributes */
 3772     oldattr = (char*)NULL;
 3773     cached = fetch_cache(scx,ni);
 3774     if (cached) {
 3775         fileuid = cached->uid;
 3776         filegid = cached->gid;
 3777 #if POSIXACLS
 3778         oldpxdesc = cached->pxdesc;
 3779         if (oldpxdesc) {
 3780                 /* must copy before merging */
 3781             pxsize = sizeof(struct POSIX_SECURITY)
 3782                 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
 3783             newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
 3784             if (newpxdesc) {
 3785                 memcpy(newpxdesc, oldpxdesc, pxsize);
 3786                 if (ntfs_merge_mode_posix(newpxdesc, mode))
 3787                     res = -1;
 3788             } else
 3789                 res = -1;
 3790         }
 3791 #endif
 3792     } else {
 3793         fileuid = 0;
 3794         filegid = 0;
 3795         oldattr = getsecurityattr(scx->vol, ni);
 3796         if (oldattr) {
 3797 #if POSIXACLS
 3798             isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 3799                 != const_cpu_to_le16(0);
 3800             phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
 3801                 oldattr;
 3802             gsid = (const SID*)
 3803                 &oldattr[le32_to_cpu(phead->group)];
 3804 #if OWNERFROMACL
 3805             usid = ntfs_acl_owner(oldattr);
 3806 #else
 3807             usid = (const SID*)
 3808                 &oldattr[le32_to_cpu(phead->owner)];
 3809 #endif
 3810             newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
 3811                     usid, gsid, isdir);
 3812             if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
 3813                 res = -1;
 3814             else {
 3815                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
 3816                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
 3817             }
 3818 #endif
 3819             free(oldattr);
 3820         } else
 3821             res = -1;
 3822     }
 3823     if (!res) {
 3824         /* check requested by root */
 3825         /* or chgrp requested by owner to an owned group */
 3826         if (!scx->uid
 3827            || ((((int)uid < 0) || (uid == fileuid))
 3828               && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
 3829               && (fileuid == scx->uid))) {
 3830             /* replace by the new usid and gsid */
 3831             /* or reuse old gid and sid for cacheing */
 3832             if ((int)uid < 0)
 3833                 uid = fileuid;
 3834             if ((int)gid < 0)
 3835                 gid = filegid;
 3836 #if POSIXACLS
 3837             res = ntfs_set_owner_mode(scx, ni, uid, gid, 
 3838                 mode, newpxdesc);
 3839 #else
 3840             res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
 3841 #endif
 3842         } else {
 3843             res = -1;   /* neither owner nor root */
 3844             errno = EPERM;
 3845         }
 3846     } else {
 3847         /*
 3848          * Should not happen : a default descriptor is generated
 3849          * by getsecurityattr() when there are none
 3850          */
 3851         ntfs_log_error("File has no security descriptor\n");
 3852         res = -1;
 3853         errno = EIO;
 3854     }
 3855 #if POSIXACLS
 3856     free(newpxdesc);
 3857 #endif
 3858     return (res ? -1 : 0);
 3859 }
 3860 
 3861 /*
 3862  *      Build a security id for a descriptor inherited from
 3863  *  parent directory the Windows way
 3864  */
 3865 
 3866 static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
 3867             const char *parentattr, BOOL fordir)
 3868 {
 3869     const SECURITY_DESCRIPTOR_RELATIVE *pphead;
 3870     const ACL *ppacl;
 3871     const SID *usid;
 3872     const SID *gsid;
 3873     BIGSID defusid;
 3874     BIGSID defgsid;
 3875     int offpacl;
 3876     int offgroup;
 3877     SECURITY_DESCRIPTOR_RELATIVE *pnhead;
 3878     ACL *pnacl;
 3879     int parentattrsz;
 3880     char *newattr;
 3881     int newattrsz;
 3882     int aclsz;
 3883     int usidsz;
 3884     int gsidsz;
 3885     int pos;
 3886     le32 securid;
 3887 
 3888     parentattrsz = ntfs_attr_size(parentattr);
 3889     pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
 3890     if (scx->mapping[MAPUSERS]) {
 3891         usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
 3892         gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
 3893 #if OWNERFROMACL
 3894             /* Get approximation of parent owner when cannot map */
 3895         if (!gsid)
 3896             gsid = adminsid;
 3897         if (!usid) {
 3898             usid = ntfs_acl_owner(parentattr);
 3899             if (!ntfs_is_user_sid(gsid))
 3900                 gsid = usid;
 3901         }
 3902 #else
 3903             /* Define owner as root when cannot map */
 3904         if (!usid)
 3905             usid = adminsid;
 3906         if (!gsid)
 3907             gsid = adminsid;
 3908 #endif
 3909     } else {
 3910         /*
 3911          * If there is no user mapping and this is not a root
 3912          * user, we have to get owner and group from somewhere,
 3913          * and the parent directory has to contribute.
 3914          * Windows never has to do that, because it can always
 3915          * rely on a user mapping
 3916          */
 3917         if (!scx->uid)
 3918             usid = adminsid;
 3919         else {
 3920 #if OWNERFROMACL
 3921             usid = ntfs_acl_owner(parentattr);
 3922 #else
 3923             int offowner;
 3924 
 3925             offowner = le32_to_cpu(pphead->owner);
 3926             usid = (const SID*)&parentattr[offowner];
 3927 #endif
 3928         }
 3929         if (!scx->gid)
 3930             gsid = adminsid;
 3931         else {
 3932             offgroup = le32_to_cpu(pphead->group);
 3933             gsid = (const SID*)&parentattr[offgroup];
 3934         }
 3935     }
 3936         /*
 3937          * new attribute is smaller than parent's
 3938          * except for differences in SIDs which appear in
 3939          * owner, group and possible grants and denials in
 3940          * generic creator-owner and creator-group ACEs.
 3941          * For directories, an ACE may be duplicated for
 3942          * access and inheritance, so we double the count.
 3943          */
 3944     usidsz = ntfs_sid_size(usid);
 3945     gsidsz = ntfs_sid_size(gsid);
 3946     newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
 3947     if (fordir)
 3948         newattrsz *= 2;
 3949     newattr = (char*)ntfs_malloc(newattrsz);
 3950     if (newattr) {
 3951         pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
 3952         pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
 3953         pnhead->alignment = 0;
 3954         pnhead->control = (pphead->control
 3955             & (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED))
 3956                 | SE_SELF_RELATIVE;
 3957         pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
 3958             /*
 3959              * locate and inherit DACL
 3960              * do not test SE_DACL_PRESENT (wrong for "DR Watson")
 3961              */
 3962         pnhead->dacl = const_cpu_to_le32(0);
 3963         if (pphead->dacl) {
 3964             offpacl = le32_to_cpu(pphead->dacl);
 3965             ppacl = (const ACL*)&parentattr[offpacl];
 3966             pnacl = (ACL*)&newattr[pos];
 3967             aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
 3968                 fordir, pphead->control
 3969                     & SE_DACL_AUTO_INHERITED);
 3970             if (aclsz) {
 3971                 pnhead->dacl = cpu_to_le32(pos);
 3972                 pos += aclsz;
 3973                 pnhead->control |= SE_DACL_PRESENT;
 3974             }
 3975         }
 3976             /*
 3977              * locate and inherit SACL
 3978              */
 3979         pnhead->sacl = const_cpu_to_le32(0);
 3980         if (pphead->sacl) {
 3981             offpacl = le32_to_cpu(pphead->sacl);
 3982             ppacl = (const ACL*)&parentattr[offpacl];
 3983             pnacl = (ACL*)&newattr[pos];
 3984             aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
 3985                 fordir, pphead->control
 3986                     & SE_SACL_AUTO_INHERITED);
 3987             if (aclsz) {
 3988                 pnhead->sacl = cpu_to_le32(pos);
 3989                 pos += aclsz;
 3990                 pnhead->control |= SE_SACL_PRESENT;
 3991             }
 3992         }
 3993             /*
 3994              * inherit or redefine owner
 3995              */
 3996         memcpy(&newattr[pos],usid,usidsz);
 3997         pnhead->owner = cpu_to_le32(pos);
 3998         pos += usidsz;
 3999             /*
 4000              * inherit or redefine group
 4001              */
 4002         memcpy(&newattr[pos],gsid,gsidsz);
 4003         pnhead->group = cpu_to_le32(pos);
 4004         pos += gsidsz;
 4005         securid = setsecurityattr(scx->vol,
 4006             (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
 4007         free(newattr);
 4008     } else
 4009         securid = const_cpu_to_le32(0);
 4010     return (securid);
 4011 }
 4012 
 4013 /*
 4014  *      Get an inherited security id
 4015  *
 4016  *  For Windows compatibility, the normal initial permission setting
 4017  *  may be inherited from the parent directory instead of being
 4018  *  defined by the creation arguments.
 4019  *
 4020  *  The following creates an inherited id for that purpose.
 4021  *
 4022  *  Note : the owner and group of parent directory are also
 4023  *  inherited (which is not the case on Windows) if no user mapping
 4024  *  is defined.
 4025  *
 4026  *  Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
 4027  */
 4028 
 4029 le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
 4030             ntfs_inode *dir_ni, BOOL fordir)
 4031 {
 4032     struct CACHED_PERMISSIONS *cached;
 4033     char *parentattr;
 4034     le32 securid;
 4035 
 4036     securid = const_cpu_to_le32(0);
 4037     cached = (struct CACHED_PERMISSIONS*)NULL;
 4038         /*
 4039          * Try to get inherited id from cache, possible when
 4040          * the current process owns the parent directory
 4041          */
 4042     if (test_nino_flag(dir_ni, v3_Extensions)
 4043             && dir_ni->security_id) {
 4044         cached = fetch_cache(scx, dir_ni);
 4045         if (cached
 4046             && (cached->uid == scx->uid) && (cached->gid == scx->gid))
 4047             securid = (fordir ? cached->inh_dirid
 4048                     : cached->inh_fileid);
 4049     }
 4050         /*
 4051          * Not cached or not available in cache, compute it all
 4052          * Note : if parent directory has no id, it is not cacheable
 4053          */
 4054     if (!securid) {
 4055         parentattr = getsecurityattr(scx->vol, dir_ni);
 4056         if (parentattr) {
 4057             securid = build_inherited_id(scx,
 4058                         parentattr, fordir);
 4059             free(parentattr);
 4060             /*
 4061              * Store the result into cache for further use
 4062              * if the current process owns the parent directory
 4063              */
 4064             if (securid) {
 4065                 cached = fetch_cache(scx, dir_ni);
 4066                 if (cached
 4067                     && (cached->uid == scx->uid)
 4068                     && (cached->gid == scx->gid)) {
 4069                     if (fordir)
 4070                         cached->inh_dirid = securid;
 4071                     else
 4072                         cached->inh_fileid = securid;
 4073                 }
 4074             }
 4075         }
 4076     }
 4077     return (securid);
 4078 }
 4079 
 4080 /*
 4081  *      Link a group to a member of group
 4082  *
 4083  *  Returns 0 if OK, -1 (and errno set) if error
 4084  */
 4085 
 4086 static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
 4087             gid_t gid)
 4088 {
 4089     struct group *group;
 4090     char **grmem;
 4091     int grcnt;
 4092     gid_t *groups;
 4093     int res;
 4094 
 4095     res = 0;
 4096     group = getgrgid(gid);
 4097     if (group && group->gr_mem) {
 4098         grcnt = usermapping->grcnt;
 4099         groups = usermapping->groups;
 4100         grmem = group->gr_mem;
 4101         while (*grmem && strcmp(user->pw_name, *grmem))
 4102             grmem++;
 4103         if (*grmem) {
 4104             if (!grcnt)
 4105                 groups = (gid_t*)malloc(sizeof(gid_t));
 4106             else
 4107                 groups = (gid_t*)realloc(groups,
 4108                     (grcnt+1)*sizeof(gid_t));
 4109             if (groups)
 4110                 groups[grcnt++] = gid;
 4111             else {
 4112                 res = -1;
 4113                 errno = ENOMEM;
 4114             }
 4115         }
 4116         usermapping->grcnt = grcnt;
 4117         usermapping->groups = groups;
 4118     }
 4119     return (res);
 4120 }
 4121 
 4122 
 4123 /*
 4124  *      Statically link group to users
 4125  *  This is based on groups defined in /etc/group and does not take
 4126  *  the groups dynamically set by setgroups() nor any changes in
 4127  *  /etc/group into account
 4128  *
 4129  *  Only mapped groups and root group are linked to mapped users
 4130  *
 4131  *  Returns 0 if OK, -1 (and errno set) if error
 4132  *
 4133  */
 4134 
 4135 static int link_group_members(struct SECURITY_CONTEXT *scx)
 4136 {
 4137     struct MAPPING *usermapping;
 4138     struct MAPPING *groupmapping;
 4139     struct passwd *user;
 4140     int res;
 4141 
 4142     res = 0;
 4143     for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
 4144             usermapping=usermapping->next) {
 4145         usermapping->grcnt = 0;
 4146         usermapping->groups = (gid_t*)NULL;
 4147         user = getpwuid(usermapping->xid);
 4148         if (user && user->pw_name) {
 4149             for (groupmapping=scx->mapping[MAPGROUPS];
 4150                     groupmapping && !res;
 4151                     groupmapping=groupmapping->next) {
 4152                 if (link_single_group(usermapping, user,
 4153                     groupmapping->xid))
 4154                     res = -1;
 4155                 }
 4156             if (!res && link_single_group(usermapping,
 4157                      user, (gid_t)0))
 4158                 res = -1;
 4159         }
 4160     }
 4161     return (res);
 4162 }
 4163 
 4164 /*
 4165  *      Apply default single user mapping
 4166  *  returns zero if successful
 4167  */
 4168 
 4169 static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
 4170              uid_t uid, gid_t gid, const SID *usid)
 4171 {
 4172     struct MAPPING *usermapping;
 4173     struct MAPPING *groupmapping;
 4174     SID *sid;
 4175     int sidsz;
 4176     int res;
 4177 
 4178     res = -1;
 4179     sidsz = ntfs_sid_size(usid);
 4180     sid = (SID*)ntfs_malloc(sidsz);
 4181     if (sid) {
 4182         memcpy(sid,usid,sidsz);
 4183         usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
 4184         if (usermapping) {
 4185             groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
 4186             if (groupmapping) {
 4187                 usermapping->sid = sid;
 4188                 usermapping->xid = uid;
 4189                 usermapping->next = (struct MAPPING*)NULL;
 4190                 groupmapping->sid = sid;
 4191                 groupmapping->xid = gid;
 4192                 groupmapping->next = (struct MAPPING*)NULL;
 4193                 scx->mapping[MAPUSERS] = usermapping;
 4194                 scx->mapping[MAPGROUPS] = groupmapping;
 4195                 res = 0;
 4196             }
 4197         }
 4198     }
 4199     return (res);
 4200 }
 4201 
 4202 /*
 4203  *      Make sure there are no ambiguous mapping
 4204  *  Ambiguous mapping may lead to undesired configurations and
 4205  *  we had rather be safe until the consequences are understood
 4206  */
 4207 
 4208 #if 0 /* not activated for now */
 4209 
 4210 static BOOL check_mapping(const struct MAPPING *usermapping,
 4211         const struct MAPPING *groupmapping)
 4212 {
 4213     const struct MAPPING *mapping1;
 4214     const struct MAPPING *mapping2;
 4215     BOOL ambiguous;
 4216 
 4217     ambiguous = FALSE;
 4218     for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
 4219         for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
 4220             if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
 4221                 if (mapping1->xid != mapping2->xid)
 4222                     ambiguous = TRUE;
 4223             } else {
 4224                 if (mapping1->xid == mapping2->xid)
 4225                     ambiguous = TRUE;
 4226             }
 4227     for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
 4228         for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
 4229             if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
 4230                 if (mapping1->xid != mapping2->xid)
 4231                     ambiguous = TRUE;
 4232             } else {
 4233                 if (mapping1->xid == mapping2->xid)
 4234                     ambiguous = TRUE;
 4235             }
 4236     return (ambiguous);
 4237 }
 4238 
 4239 #endif
 4240 
 4241 #if 0 /* not used any more */
 4242 
 4243 /*
 4244  *      Try and apply default single user mapping
 4245  *  returns zero if successful
 4246  */
 4247 
 4248 static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
 4249 {
 4250     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 4251     ntfs_inode *ni;
 4252     char *securattr;
 4253     const SID *usid;
 4254     int res;
 4255 
 4256     res = -1;
 4257     ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
 4258     if (ni) {
 4259         securattr = getsecurityattr(scx->vol, ni);
 4260         if (securattr) {
 4261             phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
 4262             usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
 4263             if (ntfs_is_user_sid(usid))
 4264                 res = ntfs_do_default_mapping(scx,
 4265                         scx->uid, scx->gid, usid);
 4266             free(securattr);
 4267         }
 4268         ntfs_inode_close(ni);
 4269     }
 4270     return (res);
 4271 }
 4272 
 4273 #endif
 4274 
 4275 /*
 4276  *      Basic read from a user mapping file on another volume
 4277  */
 4278 
 4279 static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
 4280 {
 4281     return (read(*(int*)fileid, buf, size));
 4282 }
 4283 
 4284 
 4285 /*
 4286  *      Read from a user mapping file on current NTFS partition
 4287  */
 4288 
 4289 static int localread(void *fileid, char *buf, size_t size, off_t offs)
 4290 {
 4291     return (ntfs_attr_data_read((ntfs_inode*)fileid,
 4292             AT_UNNAMED, 0, buf, size, offs));
 4293 }
 4294 
 4295 /*
 4296  *      Build the user mapping
 4297  *  - according to a mapping file if defined (or default present),
 4298  *  - or try default single user mapping if possible
 4299  *
 4300  *  The mapping is specific to a mounted device
 4301  *  No locking done, mounting assumed non multithreaded
 4302  *
 4303  *  returns zero if mapping is successful
 4304  *  (failure should not be interpreted as an error)
 4305  */
 4306 
 4307 int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
 4308             BOOL allowdef)
 4309 {
 4310     struct MAPLIST *item;
 4311     struct MAPLIST *firstitem;
 4312     struct MAPPING *usermapping;
 4313     struct MAPPING *groupmapping;
 4314     ntfs_inode *ni;
 4315     int fd;
 4316     static struct {
 4317         u8 revision;
 4318         u8 levels;
 4319         be16 highbase;
 4320         be32 lowbase;
 4321         le32 level1;
 4322         le32 level2;
 4323         le32 level3;
 4324         le32 level4;
 4325         le32 level5;
 4326     } defmap = {
 4327         1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
 4328         const_cpu_to_le32(21),
 4329         const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
 4330         const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
 4331     } ;
 4332 
 4333     /* be sure not to map anything until done */
 4334     scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
 4335     scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
 4336 
 4337     if (!usermap_path) usermap_path = MAPPINGFILE;
 4338     if (usermap_path[0] == '/') {
 4339         fd = open(usermap_path,O_RDONLY);
 4340         if (fd > 0) {
 4341             firstitem = ntfs_read_mapping(basicread, (void*)&fd);
 4342             close(fd);
 4343         } else
 4344             firstitem = (struct MAPLIST*)NULL;
 4345     } else {
 4346         ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
 4347         if (ni) {
 4348             firstitem = ntfs_read_mapping(localread, ni);
 4349             ntfs_inode_close(ni);
 4350         } else
 4351             firstitem = (struct MAPLIST*)NULL;
 4352     }
 4353 
 4354 
 4355     if (firstitem) {
 4356         usermapping = ntfs_do_user_mapping(firstitem);
 4357         groupmapping = ntfs_do_group_mapping(firstitem);
 4358         if (usermapping && groupmapping) {
 4359             scx->mapping[MAPUSERS] = usermapping;
 4360             scx->mapping[MAPGROUPS] = groupmapping;
 4361         } else
 4362             ntfs_log_error("There were no valid user or no valid group\n");
 4363         /* now we can free the memory copy of input text */
 4364         /* and rely on internal representation */
 4365         while (firstitem) {
 4366             item = firstitem->next;
 4367             free(firstitem);
 4368             firstitem = item;
 4369         }
 4370     } else {
 4371             /* no mapping file, try a default mapping */
 4372         if (allowdef) {
 4373             if (!ntfs_do_default_mapping(scx,
 4374                     0, 0, (const SID*)&defmap))
 4375                 ntfs_log_info("Using default user mapping\n");
 4376         }
 4377     }
 4378     return (!scx->mapping[MAPUSERS] || link_group_members(scx));
 4379 }
 4380 
 4381 
 4382 /*
 4383  *      Get the ntfs attribute into an extended attribute
 4384  *  The attribute is returned according to cpu endianness
 4385  */
 4386 
 4387 int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
 4388 {
 4389     u32 attrib;
 4390     size_t outsize;
 4391 
 4392     outsize = 0;    /* default to no data and no error */
 4393     if (ni) {
 4394         attrib = le32_to_cpu(ni->flags);
 4395         if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 4396             attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
 4397         else
 4398             attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
 4399         if (!attrib)
 4400             attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
 4401         outsize = sizeof(FILE_ATTR_FLAGS);
 4402         if (size >= outsize) {
 4403             if (value)
 4404                 memcpy(value,&attrib,outsize);
 4405             else
 4406                 errno = EINVAL;
 4407         }
 4408     }
 4409     return (outsize ? (int)outsize : -errno);
 4410 }
 4411 
 4412 /*
 4413  *      Return the ntfs attribute into an extended attribute
 4414  *  The attribute is expected according to cpu endianness
 4415  *
 4416  *  Returns 0, or -1 if there is a problem
 4417  */
 4418 
 4419 int ntfs_set_ntfs_attrib(ntfs_inode *ni,
 4420             const char *value, size_t size, int flags)
 4421 {
 4422     u32 attrib;
 4423     le32 settable;
 4424     ATTR_FLAGS dirflags;
 4425     int res;
 4426 
 4427     res = -1;
 4428     if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
 4429         if (!(flags & XATTR_CREATE)) {
 4430             /* copy to avoid alignment problems */
 4431             memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
 4432             settable = FILE_ATTR_SETTABLE;
 4433             res = 0;
 4434             if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
 4435                 /*
 4436                  * Accept changing compression for a directory
 4437                  * and set index root accordingly
 4438                  */
 4439                 settable |= FILE_ATTR_COMPRESSED;
 4440                 if ((ni->flags ^ cpu_to_le32(attrib))
 4441                              & FILE_ATTR_COMPRESSED) {
 4442                     if (ni->flags & FILE_ATTR_COMPRESSED)
 4443                         dirflags = const_cpu_to_le16(0);
 4444                     else
 4445                         dirflags = ATTR_IS_COMPRESSED;
 4446                     res = ntfs_attr_set_flags(ni,
 4447                         AT_INDEX_ROOT,
 4448                             NTFS_INDEX_I30, 4,
 4449                         dirflags,
 4450                         ATTR_COMPRESSION_MASK);
 4451                 }
 4452             }
 4453             if (!res) {
 4454                 ni->flags = (ni->flags & ~settable)
 4455                      | (cpu_to_le32(attrib) & settable);
 4456                 NInoFileNameSetDirty(ni);
 4457                 NInoSetDirty(ni);
 4458             }
 4459         } else
 4460             errno = EEXIST;
 4461     } else
 4462         errno = EINVAL;
 4463     return (res ? -1 : 0);
 4464 }
 4465 
 4466 
 4467 /*
 4468  *  Open the volume's security descriptor index ($Secure)
 4469  *
 4470  *  returns  0 if it succeeds
 4471  *      -1 with errno set if it fails and the volume is NTFS v3.0+
 4472  */
 4473 int ntfs_open_secure(ntfs_volume *vol)
 4474 {
 4475     ntfs_inode *ni;
 4476     ntfs_index_context *sii;
 4477     ntfs_index_context *sdh;
 4478 
 4479     if (vol->secure_ni) /* Already open? */
 4480         return 0;
 4481 
 4482     ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
 4483     if (!ni)
 4484         goto err;
 4485 
 4486     if (ni->mft_no != FILE_Secure) {
 4487         ntfs_log_error("$Secure does not have expected inode number!");
 4488         errno = EINVAL;
 4489         goto err_close_ni;
 4490     }
 4491 
 4492     /* Allocate the needed index contexts. */
 4493     sii = ntfs_index_ctx_get(ni, sii_stream, 4);
 4494     if (!sii)
 4495         goto err_close_ni;
 4496 
 4497     sdh = ntfs_index_ctx_get(ni, sdh_stream, 4);
 4498     if (!sdh)
 4499         goto err_close_sii;
 4500 
 4501     vol->secure_xsdh = sdh;
 4502     vol->secure_xsii = sii;
 4503     vol->secure_ni = ni;
 4504     return 0;
 4505 
 4506 err_close_sii:
 4507     ntfs_index_ctx_put(sii);
 4508 err_close_ni:
 4509     ntfs_inode_close(ni);
 4510 err:
 4511     /* Failing on NTFS pre-v3.0 is expected. */
 4512     if (vol->major_ver < 3)
 4513         return 0;
 4514     ntfs_log_perror("Failed to open $Secure");
 4515     return -1;
 4516 }
 4517 
 4518 /*
 4519  *  Close the volume's security descriptor index ($Secure)
 4520  *
 4521  *  returns  0 if it succeeds
 4522  *      -1 with errno set if it fails
 4523  */
 4524 int ntfs_close_secure(ntfs_volume *vol)
 4525 {
 4526     int res = 0;
 4527 
 4528     if (vol->secure_ni) {
 4529         ntfs_index_ctx_put(vol->secure_xsdh);
 4530         ntfs_index_ctx_put(vol->secure_xsii);
 4531         res = ntfs_inode_close(vol->secure_ni);
 4532         vol->secure_ni = NULL;
 4533     }
 4534     return res;
 4535 }
 4536 
 4537 /*
 4538  *      Destroy a security context
 4539  *  Allocated memory is freed to facilitate the detection of memory leaks
 4540  */
 4541 void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx)
 4542 {
 4543     ntfs_free_mapping(scx->mapping);
 4544     free_caches(scx);
 4545 }
 4546 
 4547 /*
 4548  *      API for direct access to security descriptors
 4549  *  based on Win32 API
 4550  */
 4551 
 4552 
 4553 /*
 4554  *      Selective feeding of a security descriptor into user buffer
 4555  *
 4556  *  Returns TRUE if successful
 4557  */
 4558 
 4559 static BOOL feedsecurityattr(const char *attr, u32 selection,
 4560         char *buf, u32 buflen, u32 *psize)
 4561 {
 4562     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 4563     SECURITY_DESCRIPTOR_RELATIVE *pnhead;
 4564     const ACL *pdacl;
 4565     const ACL *psacl;
 4566     const SID *pusid;
 4567     const SID *pgsid;
 4568     unsigned int offdacl;
 4569     unsigned int offsacl;
 4570     unsigned int offowner;
 4571     unsigned int offgroup;
 4572     unsigned int daclsz;
 4573     unsigned int saclsz;
 4574     unsigned int usidsz;
 4575     unsigned int gsidsz;
 4576     unsigned int size; /* size of requested attributes */
 4577     BOOL ok;
 4578     unsigned int pos;
 4579     unsigned int avail;
 4580     le16 control;
 4581 
 4582     avail = 0;
 4583     control = SE_SELF_RELATIVE;
 4584     phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
 4585     size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
 4586 
 4587         /* locate DACL if requested and available */
 4588     if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
 4589         offdacl = le32_to_cpu(phead->dacl);
 4590         pdacl = (const ACL*)&attr[offdacl];
 4591         daclsz = le16_to_cpu(pdacl->size);
 4592         size += daclsz;
 4593         avail |= DACL_SECURITY_INFORMATION;
 4594     } else
 4595         offdacl = daclsz = 0;
 4596 
 4597         /* locate owner if requested and available */
 4598     offowner = le32_to_cpu(phead->owner);
 4599     if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
 4600             /* find end of USID */
 4601         pusid = (const SID*)&attr[offowner];
 4602         usidsz = ntfs_sid_size(pusid);
 4603         size += usidsz;
 4604         avail |= OWNER_SECURITY_INFORMATION;
 4605     } else
 4606         offowner = usidsz = 0;
 4607 
 4608         /* locate group if requested and available */
 4609     offgroup = le32_to_cpu(phead->group);
 4610     if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
 4611             /* find end of GSID */
 4612         pgsid = (const SID*)&attr[offgroup];
 4613         gsidsz = ntfs_sid_size(pgsid);
 4614         size += gsidsz;
 4615         avail |= GROUP_SECURITY_INFORMATION;
 4616     } else
 4617         offgroup = gsidsz = 0;
 4618 
 4619         /* locate SACL if requested and available */
 4620     if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
 4621             /* find end of SACL */
 4622         offsacl = le32_to_cpu(phead->sacl);
 4623         psacl = (const ACL*)&attr[offsacl];
 4624         saclsz = le16_to_cpu(psacl->size);
 4625         size += saclsz;
 4626         avail |= SACL_SECURITY_INFORMATION;
 4627     } else
 4628         offsacl = saclsz = 0;
 4629 
 4630         /*
 4631          * Check having enough size in destination buffer
 4632          * (required size is returned nevertheless so that
 4633          * the request can be reissued with adequate size)
 4634          */
 4635     if (size > buflen) {
 4636         *psize = size;
 4637         errno = EINVAL;
 4638         ok = FALSE;
 4639     } else {
 4640         if (selection & OWNER_SECURITY_INFORMATION)
 4641             control |= phead->control & SE_OWNER_DEFAULTED;
 4642         if (selection & GROUP_SECURITY_INFORMATION)
 4643             control |= phead->control & SE_GROUP_DEFAULTED;
 4644         if (selection & DACL_SECURITY_INFORMATION)
 4645             control |= phead->control
 4646                     & (SE_DACL_PRESENT
 4647                        | SE_DACL_DEFAULTED
 4648                        | SE_DACL_AUTO_INHERITED
 4649                        | SE_DACL_PROTECTED);
 4650         if (selection & SACL_SECURITY_INFORMATION)
 4651             control |= phead->control
 4652                     & (SE_SACL_PRESENT
 4653                        | SE_SACL_DEFAULTED
 4654                        | SE_SACL_AUTO_INHERITED
 4655                        | SE_SACL_PROTECTED);
 4656         /*
 4657          * copy header and feed new flags, even if no detailed data
 4658          */
 4659         memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
 4660         pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
 4661         pnhead->control = control;
 4662         pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
 4663 
 4664         /* copy DACL if requested and available */
 4665         if (selection & avail & DACL_SECURITY_INFORMATION) {
 4666             pnhead->dacl = cpu_to_le32(pos);
 4667             memcpy(&buf[pos],&attr[offdacl],daclsz);
 4668             pos += daclsz;
 4669         } else
 4670             pnhead->dacl = const_cpu_to_le32(0);
 4671 
 4672         /* copy SACL if requested and available */
 4673         if (selection & avail & SACL_SECURITY_INFORMATION) {
 4674             pnhead->sacl = cpu_to_le32(pos);
 4675             memcpy(&buf[pos],&attr[offsacl],saclsz);
 4676             pos += saclsz;
 4677         } else
 4678             pnhead->sacl = const_cpu_to_le32(0);
 4679 
 4680         /* copy owner if requested and available */
 4681         if (selection & avail & OWNER_SECURITY_INFORMATION) {
 4682             pnhead->owner = cpu_to_le32(pos);
 4683             memcpy(&buf[pos],&attr[offowner],usidsz);
 4684             pos += usidsz;
 4685         } else
 4686             pnhead->owner = const_cpu_to_le32(0);
 4687 
 4688         /* copy group if requested and available */
 4689         if (selection & avail & GROUP_SECURITY_INFORMATION) {
 4690             pnhead->group = cpu_to_le32(pos);
 4691             memcpy(&buf[pos],&attr[offgroup],gsidsz);
 4692             pos += gsidsz;
 4693         } else
 4694             pnhead->group = const_cpu_to_le32(0);
 4695         if (pos != size)
 4696             ntfs_log_error("Error in security descriptor size\n");
 4697         *psize = size;
 4698         ok = TRUE;
 4699     }
 4700 
 4701     return (ok);
 4702 }
 4703 
 4704 /*
 4705  *      Merge a new security descriptor into the old one
 4706  *  and assign to designated file
 4707  *
 4708  *  Returns TRUE if successful
 4709  */
 4710 
 4711 static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
 4712         const char *newattr, u32 selection, ntfs_inode *ni)
 4713 {
 4714     const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
 4715     const SECURITY_DESCRIPTOR_RELATIVE *newhead;
 4716     SECURITY_DESCRIPTOR_RELATIVE *targhead;
 4717     const ACL *pdacl;
 4718     const ACL *psacl;
 4719     const SID *powner;
 4720     const SID *pgroup;
 4721     int offdacl;
 4722     int offsacl;
 4723     int offowner;
 4724     int offgroup;
 4725     unsigned int size;
 4726     le16 control;
 4727     char *target;
 4728     int pos;
 4729     int oldattrsz;
 4730     int newattrsz;
 4731     BOOL ok;
 4732 
 4733     ok = FALSE; /* default return */
 4734     oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
 4735     newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
 4736     oldattrsz = ntfs_attr_size(oldattr);
 4737     newattrsz = ntfs_attr_size(newattr);
 4738     target = (char*)ntfs_malloc(oldattrsz + newattrsz);
 4739     if (target) {
 4740         targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
 4741         pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
 4742         control = SE_SELF_RELATIVE;
 4743             /*
 4744              * copy new DACL if selected
 4745              * or keep old DACL if any
 4746              */
 4747         if ((selection & DACL_SECURITY_INFORMATION) ?
 4748                 newhead->dacl : oldhead->dacl) {
 4749             if (selection & DACL_SECURITY_INFORMATION) {
 4750                 offdacl = le32_to_cpu(newhead->dacl);
 4751                 pdacl = (const ACL*)&newattr[offdacl];
 4752             } else {
 4753                 offdacl = le32_to_cpu(oldhead->dacl);
 4754                 pdacl = (const ACL*)&oldattr[offdacl];
 4755             }
 4756             size = le16_to_cpu(pdacl->size);
 4757             memcpy(&target[pos], pdacl, size);
 4758             targhead->dacl = cpu_to_le32(pos);
 4759             pos += size;
 4760         } else
 4761             targhead->dacl = const_cpu_to_le32(0);
 4762         if (selection & DACL_SECURITY_INFORMATION) {
 4763             control |= newhead->control
 4764                     & (SE_DACL_PRESENT
 4765                        | SE_DACL_DEFAULTED
 4766                        | SE_DACL_PROTECTED);
 4767             if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
 4768                 control |= SE_DACL_AUTO_INHERITED;
 4769         } else
 4770             control |= oldhead->control
 4771                     & (SE_DACL_PRESENT
 4772                        | SE_DACL_DEFAULTED
 4773                        | SE_DACL_AUTO_INHERITED
 4774                        | SE_DACL_PROTECTED);
 4775             /*
 4776              * copy new SACL if selected
 4777              * or keep old SACL if any
 4778              */
 4779         if ((selection & SACL_SECURITY_INFORMATION) ?
 4780                 newhead->sacl : oldhead->sacl) {
 4781             if (selection & SACL_SECURITY_INFORMATION) {
 4782                 offsacl = le32_to_cpu(newhead->sacl);
 4783                 psacl = (const ACL*)&newattr[offsacl];
 4784             } else {
 4785                 offsacl = le32_to_cpu(oldhead->sacl);
 4786                 psacl = (const ACL*)&oldattr[offsacl];
 4787             }
 4788             size = le16_to_cpu(psacl->size);
 4789             memcpy(&target[pos], psacl, size);
 4790             targhead->sacl = cpu_to_le32(pos);
 4791             pos += size;
 4792         } else
 4793             targhead->sacl = const_cpu_to_le32(0);
 4794         if (selection & SACL_SECURITY_INFORMATION) {
 4795             control |= newhead->control
 4796                     & (SE_SACL_PRESENT
 4797                        | SE_SACL_DEFAULTED
 4798                        | SE_SACL_PROTECTED);
 4799             if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
 4800                 control |= SE_SACL_AUTO_INHERITED;
 4801         } else
 4802             control |= oldhead->control
 4803                     & (SE_SACL_PRESENT
 4804                        | SE_SACL_DEFAULTED
 4805                        | SE_SACL_AUTO_INHERITED
 4806                        | SE_SACL_PROTECTED);
 4807             /*
 4808              * copy new OWNER if selected
 4809              * or keep old OWNER if any
 4810              */
 4811         if ((selection & OWNER_SECURITY_INFORMATION) ?
 4812                 newhead->owner : oldhead->owner) {
 4813             if (selection & OWNER_SECURITY_INFORMATION) {
 4814                 offowner = le32_to_cpu(newhead->owner);
 4815                 powner = (const SID*)&newattr[offowner];
 4816             } else {
 4817                 offowner = le32_to_cpu(oldhead->owner);
 4818                 powner = (const SID*)&oldattr[offowner];
 4819             }
 4820             size = ntfs_sid_size(powner);
 4821             memcpy(&target[pos], powner, size);
 4822             targhead->owner = cpu_to_le32(pos);
 4823             pos += size;
 4824         } else
 4825             targhead->owner = const_cpu_to_le32(0);
 4826         if (selection & OWNER_SECURITY_INFORMATION)
 4827             control |= newhead->control & SE_OWNER_DEFAULTED;
 4828         else
 4829             control |= oldhead->control & SE_OWNER_DEFAULTED;
 4830             /*
 4831              * copy new GROUP if selected
 4832              * or keep old GROUP if any
 4833              */
 4834         if ((selection & GROUP_SECURITY_INFORMATION) ?
 4835                 newhead->group : oldhead->group) {
 4836             if (selection & GROUP_SECURITY_INFORMATION) {
 4837                 offgroup = le32_to_cpu(newhead->group);
 4838                 pgroup = (const SID*)&newattr[offgroup];
 4839                 control |= newhead->control
 4840                          & SE_GROUP_DEFAULTED;
 4841             } else {
 4842                 offgroup = le32_to_cpu(oldhead->group);
 4843                 pgroup = (const SID*)&oldattr[offgroup];
 4844                 control |= oldhead->control
 4845                          & SE_GROUP_DEFAULTED;
 4846             }
 4847             size = ntfs_sid_size(pgroup);
 4848             memcpy(&target[pos], pgroup, size);
 4849             targhead->group = cpu_to_le32(pos);
 4850             pos += size;
 4851         } else
 4852             targhead->group = const_cpu_to_le32(0);
 4853         if (selection & GROUP_SECURITY_INFORMATION)
 4854             control |= newhead->control & SE_GROUP_DEFAULTED;
 4855         else
 4856             control |= oldhead->control & SE_GROUP_DEFAULTED;
 4857         targhead->revision = SECURITY_DESCRIPTOR_REVISION;
 4858         targhead->alignment = 0;
 4859         targhead->control = control;
 4860         ok = !update_secur_descr(vol, target, ni);
 4861         free(target);
 4862     }
 4863     return (ok);
 4864 }
 4865 
 4866 /*
 4867  *      Return the security descriptor of a file
 4868  *  This is intended to be similar to GetFileSecurity() from Win32
 4869  *  in order to facilitate the development of portable tools
 4870  *
 4871  *  returns zero if unsuccessful (following Win32 conventions)
 4872  *      -1 if no securid
 4873  *      the securid if any
 4874  *
 4875  *  The Win32 API is :
 4876  *
 4877  *  BOOL WINAPI GetFileSecurity(
 4878  *    __in          LPCTSTR lpFileName,
 4879  *    __in          SECURITY_INFORMATION RequestedInformation,
 4880  *    __out_opt     PSECURITY_DESCRIPTOR pSecurityDescriptor,
 4881  *    __in          DWORD nLength,
 4882  *    __out         LPDWORD lpnLengthNeeded
 4883  *  );
 4884  *
 4885  */
 4886 
 4887 int ntfs_get_file_security(struct SECURITY_API *scapi,
 4888         const char *path, u32 selection,
 4889         char *buf, u32 buflen, u32 *psize)
 4890 {
 4891     ntfs_inode *ni;
 4892     char *attr;
 4893     int res;
 4894 
 4895     res = 0; /* default return */
 4896     if (scapi && (scapi->magic == MAGIC_API)) {
 4897         ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
 4898         if (ni) {
 4899             attr = getsecurityattr(scapi->security.vol, ni);
 4900             if (attr) {
 4901                 if (feedsecurityattr(attr,selection,
 4902                         buf,buflen,psize)) {
 4903                     if (test_nino_flag(ni, v3_Extensions)
 4904                         && ni->security_id)
 4905                         res = le32_to_cpu(
 4906                             ni->security_id);
 4907                     else
 4908                         res = -1;
 4909                 }
 4910                 free(attr);
 4911             }
 4912             ntfs_inode_close(ni);
 4913         } else
 4914             errno = ENOENT;
 4915         if (!res) *psize = 0;
 4916     } else
 4917         errno = EINVAL; /* do not clear *psize */
 4918     return (res);
 4919 }
 4920 
 4921 
 4922 /*
 4923  *      Set the security descriptor of a file or directory
 4924  *  This is intended to be similar to SetFileSecurity() from Win32
 4925  *  in order to facilitate the development of portable tools
 4926  *
 4927  *  returns zero if unsuccessful (following Win32 conventions)
 4928  *      -1 if no securid
 4929  *      the securid if any
 4930  *
 4931  *  The Win32 API is :
 4932  *
 4933  *  BOOL WINAPI SetFileSecurity(
 4934  *    __in          LPCTSTR lpFileName,
 4935  *    __in          SECURITY_INFORMATION SecurityInformation,
 4936  *    __in          PSECURITY_DESCRIPTOR pSecurityDescriptor
 4937  *  );
 4938  */
 4939 
 4940 int ntfs_set_file_security(struct SECURITY_API *scapi,
 4941         const char *path, u32 selection, const char *attr)
 4942 {
 4943     const SECURITY_DESCRIPTOR_RELATIVE *phead;
 4944     ntfs_inode *ni;
 4945     int attrsz;
 4946     BOOL missing;
 4947     char *oldattr;
 4948     int res;
 4949 
 4950     res = 0; /* default return */
 4951     if (scapi && (scapi->magic == MAGIC_API) && attr) {
 4952         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
 4953         attrsz = ntfs_attr_size(attr);
 4954         /* if selected, owner and group must be present or defaulted */
 4955         missing = ((selection & OWNER_SECURITY_INFORMATION)
 4956                 && !phead->owner
 4957                 && !(phead->control & SE_OWNER_DEFAULTED))
 4958             || ((selection & GROUP_SECURITY_INFORMATION)
 4959                 && !phead->group
 4960                 && !(phead->control & SE_GROUP_DEFAULTED));
 4961         if (!missing
 4962             && (phead->control & SE_SELF_RELATIVE)
 4963             && ntfs_valid_descr(attr, attrsz)) {
 4964             ni = ntfs_pathname_to_inode(scapi->security.vol,
 4965                 NULL, path);
 4966             if (ni) {
 4967                 oldattr = getsecurityattr(scapi->security.vol,
 4968                         ni);
 4969                 if (oldattr) {
 4970                     if (mergesecurityattr(
 4971                         scapi->security.vol,
 4972                         oldattr, attr,
 4973                         selection, ni)) {
 4974                         if (test_nino_flag(ni,
 4975                                 v3_Extensions))
 4976                             res = le32_to_cpu(
 4977                                 ni->security_id);
 4978                         else
 4979                             res = -1;
 4980                     }
 4981                     free(oldattr);
 4982                 }
 4983                 ntfs_inode_close(ni);
 4984             }
 4985         } else
 4986             errno = EINVAL;
 4987     } else
 4988         errno = EINVAL;
 4989     return (res);
 4990 }
 4991 
 4992 
 4993 /*
 4994  *      Return the attributes of a file
 4995  *  This is intended to be similar to GetFileAttributes() from Win32
 4996  *  in order to facilitate the development of portable tools
 4997  *
 4998  *  returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
 4999  *
 5000  *  The Win32 API is :
 5001  *
 5002  *  DWORD WINAPI GetFileAttributes(
 5003  *   __in  LPCTSTR lpFileName
 5004  *  );
 5005  */
 5006 
 5007 int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
 5008 {
 5009     ntfs_inode *ni;
 5010     s32 attrib;
 5011 
 5012     attrib = -1; /* default return */
 5013     if (scapi && (scapi->magic == MAGIC_API) && path) {
 5014         ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
 5015         if (ni) {
 5016             attrib = le32_to_cpu(ni->flags);
 5017             if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
 5018                 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
 5019             else
 5020                 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
 5021             if (!attrib)
 5022                 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
 5023 
 5024             ntfs_inode_close(ni);
 5025         } else
 5026             errno = ENOENT;
 5027     } else
 5028         errno = EINVAL; /* do not clear *psize */
 5029     return (attrib);
 5030 }
 5031 
 5032 
 5033 /*
 5034  *      Set attributes to a file or directory
 5035  *  This is intended to be similar to SetFileAttributes() from Win32
 5036  *  in order to facilitate the development of portable tools
 5037  *
 5038  *  Only a few flags can be set (same list as Win32)
 5039  *
 5040  *  returns zero if unsuccessful (following Win32 conventions)
 5041  *      nonzero if successful
 5042  *
 5043  *  The Win32 API is :
 5044  *
 5045  *  BOOL WINAPI SetFileAttributes(
 5046  *    __in  LPCTSTR lpFileName,
 5047  *    __in  DWORD dwFileAttributes
 5048  *  );
 5049  */
 5050 
 5051 BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
 5052         const char *path, s32 attrib)
 5053 {
 5054     ntfs_inode *ni;
 5055     le32 settable;
 5056     ATTR_FLAGS dirflags;
 5057     int res;
 5058 
 5059     res = 0; /* default return */
 5060     if (scapi && (scapi->magic == MAGIC_API) && path) {
 5061         ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
 5062         if (ni) {
 5063             settable = FILE_ATTR_SETTABLE;
 5064             if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
 5065                 /*
 5066                  * Accept changing compression for a directory
 5067                  * and set index root accordingly
 5068                  */
 5069                 settable |= FILE_ATTR_COMPRESSED;
 5070                 if ((ni->flags ^ cpu_to_le32(attrib))
 5071                              & FILE_ATTR_COMPRESSED) {
 5072                     if (ni->flags & FILE_ATTR_COMPRESSED)
 5073                         dirflags = const_cpu_to_le16(0);
 5074                     else
 5075                         dirflags = ATTR_IS_COMPRESSED;
 5076                     res = ntfs_attr_set_flags(ni,
 5077                         AT_INDEX_ROOT,
 5078                             NTFS_INDEX_I30, 4,
 5079                         dirflags,
 5080                         ATTR_COMPRESSION_MASK);
 5081                 }
 5082             }
 5083             if (!res) {
 5084                 ni->flags = (ni->flags & ~settable)
 5085                      | (cpu_to_le32(attrib) & settable);
 5086                 NInoSetDirty(ni);
 5087                 NInoFileNameSetDirty(ni);
 5088             }
 5089             if (!ntfs_inode_close(ni))
 5090                 res = -1;
 5091         } else
 5092             errno = ENOENT;
 5093     }
 5094     return (res);
 5095 }
 5096 
 5097 
 5098 BOOL ntfs_read_directory(struct SECURITY_API *scapi,
 5099         const char *path, ntfs_filldir_t callback, void *context)
 5100 {
 5101     ntfs_inode *ni;
 5102     BOOL ok;
 5103     s64 pos;
 5104 
 5105     ok = FALSE; /* default return */
 5106     if (scapi && (scapi->magic == MAGIC_API) && callback) {
 5107         ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
 5108         if (ni) {
 5109             if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
 5110                 pos = 0;
 5111                 ntfs_readdir(ni,&pos,context,callback);
 5112                 ok = !ntfs_inode_close(ni);
 5113             } else {
 5114                 ntfs_inode_close(ni);
 5115                 errno = ENOTDIR;
 5116             }
 5117         } else
 5118             errno = ENOENT;
 5119     } else
 5120         errno = EINVAL; /* do not clear *psize */
 5121     return (ok);
 5122 }
 5123 
 5124 /*
 5125  *      read $SDS (for auditing security data)
 5126  *
 5127  *  Returns the number or read bytes, or -1 if there is an error
 5128  */
 5129 
 5130 int ntfs_read_sds(struct SECURITY_API *scapi,
 5131         char *buf, u32 size, u32 offset)
 5132 {
 5133     int got;
 5134 
 5135     got = -1; /* default return */
 5136     if (scapi && (scapi->magic == MAGIC_API)) {
 5137         if (scapi->security.vol->secure_ni)
 5138             got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
 5139                 STREAM_SDS, 4, buf, size, offset);
 5140         else
 5141             errno = EOPNOTSUPP;
 5142     } else
 5143         errno = EINVAL;
 5144     return (got);
 5145 }
 5146 
 5147 /*
 5148  *      read $SII (for auditing security data)
 5149  *
 5150  *  Returns next entry, or NULL if there is an error
 5151  */
 5152 
 5153 INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
 5154         INDEX_ENTRY *entry)
 5155 {
 5156     SII_INDEX_KEY key;
 5157     INDEX_ENTRY *ret;
 5158     BOOL found;
 5159     ntfs_index_context *xsii;
 5160 
 5161     ret = (INDEX_ENTRY*)NULL; /* default return */
 5162     if (scapi && (scapi->magic == MAGIC_API)) {
 5163         xsii = scapi->security.vol->secure_xsii;
 5164         if (xsii) {
 5165             if (!entry) {
 5166                 key.security_id = const_cpu_to_le32(0);
 5167                 found = !ntfs_index_lookup((char*)&key,
 5168                         sizeof(SII_INDEX_KEY), xsii);
 5169                 /* not supposed to find */
 5170                 if (!found && (errno == ENOENT))
 5171                     ret = xsii->entry;
 5172             } else
 5173                 ret = ntfs_index_next(entry,xsii);
 5174             if (!ret)
 5175                 errno = ENODATA;
 5176         } else
 5177             errno = EOPNOTSUPP;
 5178     } else
 5179         errno = EINVAL;
 5180     return (ret);
 5181 }
 5182 
 5183 /*
 5184  *      read $SDH (for auditing security data)
 5185  *
 5186  *  Returns next entry, or NULL if there is an error
 5187  */
 5188 
 5189 INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
 5190         INDEX_ENTRY *entry)
 5191 {
 5192     SDH_INDEX_KEY key;
 5193     INDEX_ENTRY *ret;
 5194     BOOL found;
 5195     ntfs_index_context *xsdh;
 5196 
 5197     ret = (INDEX_ENTRY*)NULL; /* default return */
 5198     if (scapi && (scapi->magic == MAGIC_API)) {
 5199         xsdh = scapi->security.vol->secure_xsdh;
 5200         if (xsdh) {
 5201             if (!entry) {
 5202                 key.hash = const_cpu_to_le32(0);
 5203                 key.security_id = const_cpu_to_le32(0);
 5204                 found = !ntfs_index_lookup((char*)&key,
 5205                         sizeof(SDH_INDEX_KEY), xsdh);
 5206                 /* not supposed to find */
 5207                 if (!found && (errno == ENOENT))
 5208                     ret = xsdh->entry;
 5209             } else
 5210                 ret = ntfs_index_next(entry,xsdh);
 5211             if (!ret)
 5212                 errno = ENODATA;
 5213         } else errno = ENOTSUP;
 5214     } else
 5215         errno = EINVAL;
 5216     return (ret);
 5217 }
 5218 
 5219 /*
 5220  *      Get the mapped user SID
 5221  *  A buffer of 40 bytes has to be supplied
 5222  *
 5223  *  returns the size of the SID, or zero and errno set if not found
 5224  */
 5225 
 5226 int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
 5227 {
 5228     const SID *usid;
 5229     BIGSID defusid;
 5230     int size;
 5231 
 5232     size = 0;
 5233     if (scapi && (scapi->magic == MAGIC_API)) {
 5234         usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
 5235         if (usid) {
 5236             size = ntfs_sid_size(usid);
 5237             memcpy(buf,usid,size);
 5238         } else
 5239             errno = ENODATA;
 5240     } else
 5241         errno = EINVAL;
 5242     return (size);
 5243 }
 5244 
 5245 /*
 5246  *      Get the mapped group SID
 5247  *  A buffer of 40 bytes has to be supplied
 5248  *
 5249  *  returns the size of the SID, or zero and errno set if not found
 5250  */
 5251 
 5252 int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
 5253 {
 5254     const SID *gsid;
 5255     BIGSID defgsid;
 5256     int size;
 5257 
 5258     size = 0;
 5259     if (scapi && (scapi->magic == MAGIC_API)) {
 5260         gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
 5261         if (gsid) {
 5262             size = ntfs_sid_size(gsid);
 5263             memcpy(buf,gsid,size);
 5264         } else
 5265             errno = ENODATA;
 5266     } else
 5267         errno = EINVAL;
 5268     return (size);
 5269 }
 5270 
 5271 /*
 5272  *      Get the user mapped to a SID
 5273  *
 5274  *  returns the uid, or -1 if not found
 5275  */
 5276 
 5277 int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
 5278 {
 5279     int uid;
 5280 
 5281     uid = -1;
 5282     if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
 5283         if (ntfs_same_sid(usid,adminsid))
 5284             uid = 0;
 5285         else {
 5286             uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
 5287             if (!uid) {
 5288                 uid = -1;
 5289                 errno = ENODATA;
 5290             }
 5291         }
 5292     } else
 5293         errno = EINVAL;
 5294     return (uid);
 5295 }
 5296 
 5297 /*
 5298  *      Get the group mapped to a SID
 5299  *
 5300  *  returns the uid, or -1 if not found
 5301  */
 5302 
 5303 int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
 5304 {
 5305     int gid;
 5306 
 5307     gid = -1;
 5308     if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
 5309         if (ntfs_same_sid(gsid,adminsid))
 5310             gid = 0;
 5311         else {
 5312             gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
 5313             if (!gid) {
 5314                 gid = -1;
 5315                 errno = ENODATA;
 5316             }
 5317         }
 5318     } else
 5319         errno = EINVAL;
 5320     return (gid);
 5321 }
 5322 
 5323 /*
 5324  *      Initializations before calling ntfs_get_file_security()
 5325  *  ntfs_set_file_security() and ntfs_read_directory()
 5326  *
 5327  *  Only allowed for root
 5328  *
 5329  *  Returns an (obscured) struct SECURITY_API* needed for further calls
 5330  *      NULL if not root (EPERM) or device is mounted (EBUSY)
 5331  */
 5332 
 5333 struct SECURITY_API *ntfs_initialize_file_security(const char *device,
 5334                 unsigned long flags)
 5335 {
 5336     ntfs_volume *vol;
 5337     unsigned long mntflag;
 5338     int mnt;
 5339     struct SECURITY_API *scapi;
 5340     struct SECURITY_CONTEXT *scx;
 5341 
 5342     scapi = (struct SECURITY_API*)NULL;
 5343     mnt = ntfs_check_if_mounted(device, &mntflag);
 5344     if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
 5345         vol = ntfs_mount(device, flags);
 5346         if (vol) {
 5347             scapi = (struct SECURITY_API*)
 5348                 ntfs_malloc(sizeof(struct SECURITY_API));
 5349             if (!ntfs_volume_get_free_space(vol)
 5350                 && scapi) {
 5351                 scapi->magic = MAGIC_API;
 5352                 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
 5353                 scx = &scapi->security;
 5354                 scx->vol = vol;
 5355                 scx->uid = getuid();
 5356                 scx->gid = getgid();
 5357                 scx->pseccache = &scapi->seccache;
 5358                 scx->vol->secure_flags = 0;
 5359                     /* accept no mapping and no $Secure */
 5360                 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
 5361             } else {
 5362                 if (scapi)
 5363                     free(scapi);
 5364                 else
 5365                     errno = ENOMEM;
 5366                 mnt = ntfs_umount(vol,FALSE);
 5367                 scapi = (struct SECURITY_API*)NULL;
 5368             }
 5369         }
 5370     } else
 5371         if (getuid())
 5372             errno = EPERM;
 5373         else
 5374             errno = EBUSY;
 5375     return (scapi);
 5376 }
 5377 
 5378 /*
 5379  *      Leaving after ntfs_initialize_file_security()
 5380  *
 5381  *  Returns FALSE if FAILED
 5382  */
 5383 
 5384 BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
 5385 {
 5386     int ok;
 5387     ntfs_volume *vol;
 5388 
 5389     ok = FALSE;
 5390     if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
 5391         vol = scapi->security.vol;
 5392         ntfs_destroy_security_context(&scapi->security);
 5393         free(scapi);
 5394         if (!ntfs_umount(vol, 0))
 5395             ok = TRUE;
 5396     }
 5397     return (ok);
 5398 }
 5399