mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-12-03 23:13:39 +08:00
67feb2c286
Windows 10 brought security descriptors which contain ACE's related to new ways of accessing files or directories. These ACE are now accepted with minimal consistency checks. They are still ignored for translating permissions and for Windows-type inheritance.
4508 lines
116 KiB
C
4508 lines
116 KiB
C
/**
|
|
* acls.c - General function to process NTFS ACLs
|
|
*
|
|
* This module is part of ntfs-3g library, but may also be
|
|
* integrated in tools running over Linux or Windows
|
|
*
|
|
* Copyright (c) 2007-2017 Jean-Pierre Andre
|
|
*
|
|
* This program/include file is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program/include file is distributed in the hope that it will be
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (in the main directory of the NTFS-3G
|
|
* distribution in the file COPYING); if not, write to the Free Software
|
|
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_STDIO_H
|
|
#include <stdio.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_SYSLOG_H
|
|
#include <syslog.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
#include "types.h"
|
|
#include "layout.h"
|
|
#include "security.h"
|
|
#include "acls.h"
|
|
#include "misc.h"
|
|
|
|
/*
|
|
* A few useful constants
|
|
*/
|
|
|
|
/*
|
|
* null SID (S-1-0-0)
|
|
*/
|
|
|
|
static const char nullsidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 0, /* base */
|
|
0, 0, 0, 0 /* 1st level */
|
|
};
|
|
|
|
static const SID *nullsid = (const SID*)nullsidbytes;
|
|
|
|
/*
|
|
* SID for world (S-1-1-0)
|
|
*/
|
|
|
|
static const char worldsidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 1, /* base */
|
|
0, 0, 0, 0 /* 1st level */
|
|
} ;
|
|
|
|
const SID *worldsid = (const SID*)worldsidbytes;
|
|
|
|
/*
|
|
* SID for authenticated user (S-1-5-11)
|
|
*/
|
|
|
|
static const char authsidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 5, /* base */
|
|
11, 0, 0, 0 /* 1st level */
|
|
};
|
|
|
|
static const SID *authsid = (const SID*)authsidbytes;
|
|
|
|
/*
|
|
* SID for administrator
|
|
*/
|
|
|
|
static const char adminsidbytes[] = {
|
|
1, /* revision */
|
|
2, /* auth count */
|
|
0, 0, 0, 0, 0, 5, /* base */
|
|
32, 0, 0, 0, /* 1st level */
|
|
32, 2, 0, 0 /* 2nd level */
|
|
};
|
|
|
|
const SID *adminsid = (const SID*)adminsidbytes;
|
|
|
|
/*
|
|
* SID for system
|
|
*/
|
|
|
|
static const char systemsidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 5, /* base */
|
|
18, 0, 0, 0 /* 1st level */
|
|
};
|
|
|
|
static const SID *systemsid = (const SID*)systemsidbytes;
|
|
|
|
/*
|
|
* SID for generic creator-owner
|
|
* S-1-3-0
|
|
*/
|
|
|
|
static const char ownersidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 3, /* base */
|
|
0, 0, 0, 0 /* 1st level */
|
|
} ;
|
|
|
|
static const SID *ownersid = (const SID*)ownersidbytes;
|
|
|
|
/*
|
|
* SID for generic creator-group
|
|
* S-1-3-1
|
|
*/
|
|
|
|
static const char groupsidbytes[] = {
|
|
1, /* revision */
|
|
1, /* auth count */
|
|
0, 0, 0, 0, 0, 3, /* base */
|
|
1, 0, 0, 0 /* 1st level */
|
|
} ;
|
|
|
|
static const SID *groupsid = (const SID*)groupsidbytes;
|
|
|
|
/*
|
|
* Determine the size of a SID
|
|
*/
|
|
|
|
int ntfs_sid_size(const SID * sid)
|
|
{
|
|
return (sid->sub_authority_count * 4 + 8);
|
|
}
|
|
|
|
/*
|
|
* Test whether two SID are equal
|
|
*/
|
|
|
|
BOOL ntfs_same_sid(const SID *first, const SID *second)
|
|
{
|
|
int size;
|
|
|
|
size = ntfs_sid_size(first);
|
|
return ((ntfs_sid_size(second) == size)
|
|
&& !memcmp(first, second, size));
|
|
}
|
|
|
|
/*
|
|
* Test whether a SID means "world user"
|
|
* Local users group recognized as world
|
|
* Also interactive users so that /Users/Public is world accessible,
|
|
* but only if Posix ACLs are not enabled (if Posix ACLs are enabled,
|
|
* access to /Users/Public should be done by defining interactive users
|
|
* as a mapped group.)
|
|
*/
|
|
|
|
static int is_world_sid(const SID * usid)
|
|
{
|
|
return (
|
|
/* check whether S-1-1-0 : world */
|
|
((usid->sub_authority_count == 1)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(1))
|
|
&& (usid->sub_authority[0] == const_cpu_to_le32(0)))
|
|
|
|
/* check whether S-1-5-32-545 : local user */
|
|
|| ((usid->sub_authority_count == 2)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(5))
|
|
&& (usid->sub_authority[0] == const_cpu_to_le32(32))
|
|
&& (usid->sub_authority[1] == const_cpu_to_le32(545)))
|
|
|
|
/* check whether S-1-5-11 : authenticated user */
|
|
|| ((usid->sub_authority_count == 1)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(5))
|
|
&& (usid->sub_authority[0] == const_cpu_to_le32(11)))
|
|
|
|
#if !POSIXACLS
|
|
/* check whether S-1-5-4 : interactive user */
|
|
|| ((usid->sub_authority_count == 1)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(5))
|
|
&& (usid->sub_authority[0] == const_cpu_to_le32(4)))
|
|
#endif /* !POSIXACLS */
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Test whether a SID means "some user (or group)"
|
|
* Currently we only check for S-1-5-21... but we should
|
|
* probably test for other configurations
|
|
*/
|
|
|
|
BOOL ntfs_is_user_sid(const SID *usid)
|
|
{
|
|
return ((usid->sub_authority_count == 5)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(5))
|
|
&& (usid->sub_authority[0] == const_cpu_to_le32(21)));
|
|
}
|
|
|
|
/*
|
|
* Test whether a SID means "some special group"
|
|
* Currently we only check for a few S-1-5-n but we should
|
|
* probably test for other configurations.
|
|
*
|
|
* This is useful for granting access to /Users/Public for
|
|
* specific users when the Posix ACLs are enabled.
|
|
*/
|
|
|
|
static BOOL ntfs_known_group_sid(const SID *usid)
|
|
{
|
|
/* count == 1 excludes S-1-5-5-X-Y (logon) */
|
|
return ((usid->sub_authority_count == 1)
|
|
&& (usid->identifier_authority.high_part == const_cpu_to_be16(0))
|
|
&& (usid->identifier_authority.low_part == const_cpu_to_be32(5))
|
|
&& (le32_to_cpu(usid->sub_authority[0]) >= 1)
|
|
&& (le32_to_cpu(usid->sub_authority[0]) <= 6));
|
|
}
|
|
|
|
/*
|
|
* Determine the size of a security attribute
|
|
* whatever the order of fields
|
|
*/
|
|
|
|
unsigned int ntfs_attr_size(const char *attr)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pdacl;
|
|
const ACL *psacl;
|
|
const SID *psid;
|
|
unsigned int offdacl;
|
|
unsigned int offsacl;
|
|
unsigned int offowner;
|
|
unsigned int offgroup;
|
|
unsigned int endsid;
|
|
unsigned int endacl;
|
|
unsigned int attrsz;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
|
|
/*
|
|
* First check group, which is the last field in all descriptors
|
|
* we build, and in most descriptors built by Windows
|
|
*/
|
|
attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
|
|
offgroup = le32_to_cpu(phead->group);
|
|
if (offgroup >= attrsz) {
|
|
/* find end of GSID */
|
|
psid = (const SID*)&attr[offgroup];
|
|
endsid = offgroup + ntfs_sid_size(psid);
|
|
if (endsid > attrsz) attrsz = endsid;
|
|
}
|
|
offowner = le32_to_cpu(phead->owner);
|
|
if (offowner >= attrsz) {
|
|
/* find end of USID */
|
|
psid = (const SID*)&attr[offowner];
|
|
endsid = offowner + ntfs_sid_size(psid);
|
|
attrsz = endsid;
|
|
}
|
|
offsacl = le32_to_cpu(phead->sacl);
|
|
if (offsacl >= attrsz) {
|
|
/* find end of SACL */
|
|
psacl = (const ACL*)&attr[offsacl];
|
|
endacl = offsacl + le16_to_cpu(psacl->size);
|
|
if (endacl > attrsz)
|
|
attrsz = endacl;
|
|
}
|
|
|
|
|
|
/* find end of DACL */
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
if (offdacl >= attrsz) {
|
|
pdacl = (const ACL*)&attr[offdacl];
|
|
endacl = offdacl + le16_to_cpu(pdacl->size);
|
|
if (endacl > attrsz)
|
|
attrsz = endacl;
|
|
}
|
|
return (attrsz);
|
|
}
|
|
|
|
/**
|
|
* ntfs_valid_sid - determine if a SID is valid
|
|
* @sid: SID for which to determine if it is valid
|
|
*
|
|
* Determine if the SID pointed to by @sid is valid.
|
|
*
|
|
* Return TRUE if it is valid and FALSE otherwise.
|
|
*/
|
|
BOOL ntfs_valid_sid(const SID *sid)
|
|
{
|
|
return sid && sid->revision == SID_REVISION &&
|
|
sid->sub_authority_count <= SID_MAX_SUB_AUTHORITIES;
|
|
}
|
|
|
|
/*
|
|
* Check whether a SID is acceptable for an implicit
|
|
* mapping pattern.
|
|
* It should have been already checked it is a valid user SID.
|
|
*
|
|
* The last authority reference has to be >= 1000 (Windows usage)
|
|
* and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits
|
|
* from a gid an be inserted with no overflow.
|
|
*/
|
|
|
|
BOOL ntfs_valid_pattern(const SID *sid)
|
|
{
|
|
int cnt;
|
|
u32 auth;
|
|
le32 leauth;
|
|
|
|
cnt = sid->sub_authority_count;
|
|
leauth = sid->sub_authority[cnt-1];
|
|
auth = le32_to_cpu(leauth);
|
|
return ((auth >= 1000) && (auth <= 0x7fffffff));
|
|
}
|
|
|
|
/*
|
|
* Compute the uid or gid associated to a SID
|
|
* through an implicit mapping
|
|
*
|
|
* Returns 0 (root) if it does not match pattern
|
|
*/
|
|
|
|
static u32 findimplicit(const SID *xsid, const SID *pattern, int parity)
|
|
{
|
|
BIGSID defsid;
|
|
SID *psid;
|
|
u32 xid; /* uid or gid */
|
|
int cnt;
|
|
u32 carry;
|
|
le32 leauth;
|
|
u32 uauth;
|
|
u32 xlast;
|
|
u32 rlast;
|
|
|
|
memcpy(&defsid,pattern,ntfs_sid_size(pattern));
|
|
psid = (SID*)&defsid;
|
|
cnt = psid->sub_authority_count;
|
|
xid = 0;
|
|
if (xsid->sub_authority_count == cnt) {
|
|
psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1];
|
|
leauth = xsid->sub_authority[cnt-1];
|
|
xlast = le32_to_cpu(leauth);
|
|
leauth = pattern->sub_authority[cnt-1];
|
|
rlast = le32_to_cpu(leauth);
|
|
|
|
if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) {
|
|
/* direct check for basic situation */
|
|
if (ntfs_same_sid(psid,xsid))
|
|
xid = ((xlast - rlast) >> 1) & 0x3fffffff;
|
|
else {
|
|
/*
|
|
* check whether part of mapping had to be
|
|
* recorded in a higher level authority
|
|
*/
|
|
carry = 1;
|
|
do {
|
|
leauth = psid->sub_authority[cnt-2];
|
|
uauth = le32_to_cpu(leauth) + 1;
|
|
psid->sub_authority[cnt-2]
|
|
= cpu_to_le32(uauth);
|
|
} while (!ntfs_same_sid(psid,xsid)
|
|
&& (++carry < 4));
|
|
if (carry < 4)
|
|
xid = (((xlast - rlast) >> 1)
|
|
& 0x3fffffff) | (carry << 30);
|
|
}
|
|
}
|
|
}
|
|
return (xid);
|
|
}
|
|
|
|
/*
|
|
* Find usid mapped to a Linux user
|
|
* Returns NULL if not found
|
|
*/
|
|
|
|
const SID *ntfs_find_usid(const struct MAPPING* usermapping,
|
|
uid_t uid, SID *defusid)
|
|
{
|
|
const struct MAPPING *p;
|
|
const SID *sid;
|
|
le32 leauth;
|
|
u32 uauth;
|
|
int cnt;
|
|
|
|
if (!uid)
|
|
sid = adminsid;
|
|
else {
|
|
p = usermapping;
|
|
while (p && p->xid && ((uid_t)p->xid != uid))
|
|
p = p->next;
|
|
if (p && !p->xid) {
|
|
/*
|
|
* default pattern has been reached :
|
|
* build an implicit SID according to pattern
|
|
* (the pattern format was checked while reading
|
|
* the mapping file)
|
|
*/
|
|
memcpy(defusid, p->sid, ntfs_sid_size(p->sid));
|
|
cnt = defusid->sub_authority_count;
|
|
leauth = defusid->sub_authority[cnt-1];
|
|
uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff);
|
|
defusid->sub_authority[cnt-1] = cpu_to_le32(uauth);
|
|
if (uid & 0xc0000000) {
|
|
leauth = defusid->sub_authority[cnt-2];
|
|
uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3);
|
|
defusid->sub_authority[cnt-2] = cpu_to_le32(uauth);
|
|
}
|
|
sid = defusid;
|
|
} else
|
|
sid = (p ? p->sid : (const SID*)NULL);
|
|
}
|
|
return (sid);
|
|
}
|
|
|
|
/*
|
|
* Find Linux group mapped to a gsid
|
|
* Returns 0 (root) if not found
|
|
*/
|
|
|
|
const SID *ntfs_find_gsid(const struct MAPPING* groupmapping,
|
|
gid_t gid, SID *defgsid)
|
|
{
|
|
const struct MAPPING *p;
|
|
const SID *sid;
|
|
le32 leauth;
|
|
u32 uauth;
|
|
int cnt;
|
|
|
|
if (!gid)
|
|
sid = adminsid;
|
|
else {
|
|
p = groupmapping;
|
|
while (p && p->xid && ((gid_t)p->xid != gid))
|
|
p = p->next;
|
|
if (p && !p->xid) {
|
|
/*
|
|
* default pattern has been reached :
|
|
* build an implicit SID according to pattern
|
|
* (the pattern format was checked while reading
|
|
* the mapping file)
|
|
*/
|
|
memcpy(defgsid, p->sid, ntfs_sid_size(p->sid));
|
|
cnt = defgsid->sub_authority_count;
|
|
leauth = defgsid->sub_authority[cnt-1];
|
|
uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1;
|
|
defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth);
|
|
if (gid & 0xc0000000) {
|
|
leauth = defgsid->sub_authority[cnt-2];
|
|
uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3);
|
|
defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth);
|
|
}
|
|
sid = defgsid;
|
|
} else
|
|
sid = (p ? p->sid : (const SID*)NULL);
|
|
}
|
|
return (sid);
|
|
}
|
|
|
|
/*
|
|
* Find Linux owner mapped to a usid
|
|
* Returns 0 (root) if not found
|
|
*/
|
|
|
|
uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid)
|
|
{
|
|
uid_t uid;
|
|
const struct MAPPING *p;
|
|
|
|
p = usermapping;
|
|
while (p && p->xid && !ntfs_same_sid(usid, p->sid))
|
|
p = p->next;
|
|
if (p && !p->xid)
|
|
/*
|
|
* No explicit mapping found, try implicit mapping
|
|
*/
|
|
uid = findimplicit(usid,p->sid,0);
|
|
else
|
|
uid = (p ? p->xid : 0);
|
|
return (uid);
|
|
}
|
|
|
|
/*
|
|
* Find Linux group mapped to a gsid
|
|
* Returns 0 (root) if not found
|
|
*/
|
|
|
|
gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid)
|
|
{
|
|
gid_t gid;
|
|
const struct MAPPING *p;
|
|
|
|
p = groupmapping;
|
|
while (p && p->xid && !ntfs_same_sid(gsid, p->sid))
|
|
p = p->next;
|
|
if (p && !p->xid)
|
|
/*
|
|
* No explicit mapping found, try implicit mapping
|
|
*/
|
|
gid = findimplicit(gsid,p->sid,1);
|
|
else
|
|
gid = (p ? p->xid : 0);
|
|
return (gid);
|
|
}
|
|
|
|
/*
|
|
* Check the validity of the ACEs in a DACL or SACL
|
|
*/
|
|
|
|
static BOOL valid_acl(const ACL *pacl, unsigned int end)
|
|
{
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
unsigned int offace;
|
|
unsigned int acecnt;
|
|
unsigned int acesz;
|
|
unsigned int nace;
|
|
unsigned int wantsz;
|
|
BOOL ok;
|
|
|
|
ok = TRUE;
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = sizeof(ACL);
|
|
for (nace = 0; (nace < acecnt) && ok; nace++) {
|
|
/* be sure the beginning is within range */
|
|
if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end)
|
|
ok = FALSE;
|
|
else {
|
|
pace = (const ACCESS_ALLOWED_ACE*)
|
|
&((const char*)pacl)[offace];
|
|
acesz = le16_to_cpu(pace->size);
|
|
switch (pace->type) {
|
|
case ACCESS_ALLOWED_ACE_TYPE :
|
|
case ACCESS_DENIED_ACE_TYPE :
|
|
wantsz = ntfs_sid_size(&pace->sid) + 8;
|
|
if (((offace + acesz) > end)
|
|
|| !ntfs_valid_sid(&pace->sid)
|
|
|| (wantsz != acesz))
|
|
ok = FALSE;
|
|
break;
|
|
case SYSTEM_AUDIT_ACE_TYPE :
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE :
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE :
|
|
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE :
|
|
case SYSTEM_MANDATORY_LABEL_ACE_TYPE :
|
|
case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE :
|
|
case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE :
|
|
/* Extra data after the SID */
|
|
wantsz = ntfs_sid_size(&pace->sid) + 8;
|
|
if (((offace + acesz) > end)
|
|
|| !ntfs_valid_sid(&pace->sid)
|
|
|| (wantsz > acesz))
|
|
ok = FALSE;
|
|
break;
|
|
default :
|
|
/* SID at a different location */
|
|
if ((offace + acesz) > end)
|
|
ok = FALSE;
|
|
break;
|
|
}
|
|
offace += acesz;
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
/*
|
|
* Do sanity checks on security descriptors read from storage
|
|
* basically, we make sure that every field holds within
|
|
* allocated storage
|
|
* Should not be called with a NULL argument
|
|
* returns TRUE if considered safe
|
|
* if not, error should be logged by caller
|
|
*/
|
|
|
|
BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pdacl;
|
|
const ACL *psacl;
|
|
unsigned int offdacl;
|
|
unsigned int offsacl;
|
|
unsigned int offowner;
|
|
unsigned int offgroup;
|
|
BOOL ok;
|
|
|
|
ok = TRUE;
|
|
|
|
/*
|
|
* first check overall size if within allocation range
|
|
* and a DACL is present
|
|
* and owner and group SID are valid
|
|
*/
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
offsacl = le32_to_cpu(phead->sacl);
|
|
offowner = le32_to_cpu(phead->owner);
|
|
offgroup = le32_to_cpu(phead->group);
|
|
pdacl = (const ACL*)&securattr[offdacl];
|
|
psacl = (const ACL*)&securattr[offsacl];
|
|
|
|
/*
|
|
* size check occurs before the above pointers are used
|
|
*
|
|
* "DR Watson" standard directory on WinXP has an
|
|
* old revision and no DACL though SE_DACL_PRESENT is set
|
|
*/
|
|
if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
&& (phead->revision == SECURITY_DESCRIPTOR_REVISION)
|
|
&& (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
&& ((offowner + 2) < attrsz)
|
|
&& (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
&& ((offgroup + 2) < attrsz)
|
|
&& (!offdacl
|
|
|| ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
&& (offdacl+sizeof(ACL) <= attrsz)))
|
|
&& (!offsacl
|
|
|| ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
&& (offsacl+sizeof(ACL) <= attrsz)))
|
|
&& !(phead->owner & const_cpu_to_le32(3))
|
|
&& !(phead->group & const_cpu_to_le32(3))
|
|
&& !(phead->dacl & const_cpu_to_le32(3))
|
|
&& !(phead->sacl & const_cpu_to_le32(3))
|
|
&& (ntfs_attr_size(securattr) <= attrsz)
|
|
&& ntfs_valid_sid((const SID*)&securattr[offowner])
|
|
&& ntfs_valid_sid((const SID*)&securattr[offgroup])
|
|
/*
|
|
* if there is an ACL, as indicated by offdacl,
|
|
* require SE_DACL_PRESENT
|
|
* but "Dr Watson" has SE_DACL_PRESENT though no DACL
|
|
*/
|
|
&& (!offdacl
|
|
|| ((phead->control & SE_DACL_PRESENT)
|
|
&& ((pdacl->revision == ACL_REVISION)
|
|
|| (pdacl->revision == ACL_REVISION_DS))))
|
|
/* same for SACL */
|
|
&& (!offsacl
|
|
|| ((phead->control & SE_SACL_PRESENT)
|
|
&& ((psacl->revision == ACL_REVISION)
|
|
|| (psacl->revision == ACL_REVISION_DS))))) {
|
|
/*
|
|
* Check the DACL and SACL if present
|
|
*/
|
|
if ((offdacl && !valid_acl(pdacl,attrsz - offdacl))
|
|
|| (offsacl && !valid_acl(psacl,attrsz - offsacl)))
|
|
ok = FALSE;
|
|
} else
|
|
ok = FALSE;
|
|
return (ok);
|
|
}
|
|
|
|
/*
|
|
* Copy the inheritable parts of an ACL
|
|
*
|
|
* Returns the size of the new ACL
|
|
* or zero if nothing is inheritable
|
|
*/
|
|
|
|
int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
|
|
const SID *usid, const SID *gsid, BOOL fordir,
|
|
le16 inherited)
|
|
{
|
|
unsigned int src;
|
|
unsigned int dst;
|
|
int oldcnt;
|
|
int newcnt;
|
|
unsigned int selection;
|
|
int nace;
|
|
int acesz;
|
|
int usidsz;
|
|
int gsidsz;
|
|
BOOL acceptable;
|
|
const ACCESS_ALLOWED_ACE *poldace;
|
|
ACCESS_ALLOWED_ACE *pnewace;
|
|
ACCESS_ALLOWED_ACE *pauthace;
|
|
ACCESS_ALLOWED_ACE *pownerace;
|
|
|
|
pauthace = (ACCESS_ALLOWED_ACE*)NULL;
|
|
pownerace = (ACCESS_ALLOWED_ACE*)NULL;
|
|
usidsz = ntfs_sid_size(usid);
|
|
gsidsz = ntfs_sid_size(gsid);
|
|
|
|
/* ACL header */
|
|
|
|
newacl->revision = ACL_REVISION;
|
|
newacl->alignment1 = 0;
|
|
newacl->alignment2 = const_cpu_to_le16(0);
|
|
src = dst = sizeof(ACL);
|
|
|
|
selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE);
|
|
newcnt = 0;
|
|
oldcnt = le16_to_cpu(oldacl->ace_count);
|
|
for (nace = 0; nace < oldcnt; nace++) {
|
|
poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src);
|
|
acesz = le16_to_cpu(poldace->size);
|
|
src += acesz;
|
|
/*
|
|
* Currently only ACE for file or directory access are
|
|
* processed. More information needed about what to do
|
|
* for other types (whose SID may be at a different location)
|
|
*/
|
|
switch (poldace->type) {
|
|
case ACCESS_ALLOWED_ACE_TYPE :
|
|
case ACCESS_DENIED_ACE_TYPE :
|
|
acceptable = TRUE;
|
|
break;
|
|
default :
|
|
acceptable = FALSE;
|
|
break;
|
|
}
|
|
/*
|
|
* Extract inheritance for access, including inheritance for
|
|
* access from an ACE with is both applied and inheritable.
|
|
*
|
|
* must not output OBJECT_INHERIT_ACE or CONTAINER_INHERIT_ACE
|
|
*
|
|
* According to MSDN :
|
|
* "For a case in which a container object inherits an ACE
|
|
* "that is both effective on the container and inheritable
|
|
* "by its descendants, the container may inherit two ACEs.
|
|
* "This occurs if the inheritable ACE contains generic
|
|
* "information."
|
|
*/
|
|
if ((poldace->flags & selection)
|
|
&& acceptable
|
|
&& (!fordir
|
|
|| (poldace->flags & NO_PROPAGATE_INHERIT_ACE)
|
|
|| (poldace->mask & (GENERIC_ALL | GENERIC_READ
|
|
| GENERIC_WRITE | GENERIC_EXECUTE)))
|
|
&& !ntfs_same_sid(&poldace->sid, ownersid)
|
|
&& !ntfs_same_sid(&poldace->sid, groupsid)) {
|
|
pnewace = (ACCESS_ALLOWED_ACE*)
|
|
((char*)newacl + dst);
|
|
memcpy(pnewace,poldace,acesz);
|
|
/* reencode GENERIC_ALL */
|
|
if (pnewace->mask & GENERIC_ALL) {
|
|
pnewace->mask &= ~GENERIC_ALL;
|
|
if (fordir)
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| DIR_READ
|
|
| DIR_WRITE
|
|
| DIR_EXEC;
|
|
else
|
|
/*
|
|
* The last flag is not defined for a file,
|
|
* however Windows sets it, so do the same
|
|
*/
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| FILE_READ
|
|
| FILE_WRITE
|
|
| FILE_EXEC
|
|
| const_cpu_to_le32(0x40);
|
|
}
|
|
/* reencode GENERIC_READ (+ EXECUTE) */
|
|
if (pnewace->mask & GENERIC_READ) {
|
|
if (fordir)
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| DIR_READ
|
|
| DIR_EXEC;
|
|
else
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| FILE_READ
|
|
| FILE_EXEC;
|
|
pnewace->mask &= ~(GENERIC_READ
|
|
| GENERIC_EXECUTE
|
|
| WRITE_DAC
|
|
| WRITE_OWNER
|
|
| DELETE | FILE_WRITE_EA
|
|
| FILE_WRITE_ATTRIBUTES);
|
|
}
|
|
/* reencode GENERIC_WRITE */
|
|
if (pnewace->mask & GENERIC_WRITE) {
|
|
if (fordir)
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| DIR_WRITE;
|
|
else
|
|
pnewace->mask |= OWNER_RIGHTS
|
|
| FILE_WRITE;
|
|
pnewace->mask &= ~(GENERIC_WRITE
|
|
| WRITE_DAC
|
|
| WRITE_OWNER
|
|
| FILE_DELETE_CHILD);
|
|
}
|
|
/* remove inheritance flags */
|
|
pnewace->flags &= ~(OBJECT_INHERIT_ACE
|
|
| CONTAINER_INHERIT_ACE
|
|
| INHERIT_ONLY_ACE);
|
|
/*
|
|
* Group similar ACE for authenticated users
|
|
* (should probably be done for other SIDs)
|
|
*/
|
|
if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& ntfs_same_sid(&poldace->sid, authsid)) {
|
|
if (pauthace) {
|
|
pauthace->flags |= pnewace->flags;
|
|
pauthace->mask |= pnewace->mask;
|
|
} else {
|
|
pauthace = pnewace;
|
|
if (inherited)
|
|
pnewace->flags |= INHERITED_ACE;
|
|
dst += acesz;
|
|
newcnt++;
|
|
}
|
|
} else {
|
|
if (inherited)
|
|
pnewace->flags |= INHERITED_ACE;
|
|
dst += acesz;
|
|
newcnt++;
|
|
}
|
|
}
|
|
/*
|
|
* Inheritance for access, specific to
|
|
* creator-owner (and creator-group)
|
|
*/
|
|
if ((fordir || !inherited
|
|
|| (poldace->flags
|
|
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)))
|
|
&& acceptable) {
|
|
pnewace = (ACCESS_ALLOWED_ACE*)
|
|
((char*)newacl + dst);
|
|
memcpy(pnewace,poldace,acesz);
|
|
/*
|
|
* Replace generic creator-owner and
|
|
* creator-group by owner and group
|
|
* (but keep for further inheritance)
|
|
*/
|
|
if (ntfs_same_sid(&pnewace->sid, ownersid)) {
|
|
memcpy(&pnewace->sid, usid, usidsz);
|
|
pnewace->size = cpu_to_le16(usidsz + 8);
|
|
/* remove inheritance flags */
|
|
pnewace->flags &= ~(OBJECT_INHERIT_ACE
|
|
| CONTAINER_INHERIT_ACE
|
|
| INHERIT_ONLY_ACE);
|
|
if (inherited)
|
|
pnewace->flags |= INHERITED_ACE;
|
|
if ((pnewace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& pownerace
|
|
&& !(pnewace->flags & ~pownerace->flags)) {
|
|
pownerace->mask |= pnewace->mask;
|
|
} else {
|
|
dst += usidsz + 8;
|
|
newcnt++;
|
|
}
|
|
}
|
|
if (ntfs_same_sid(&pnewace->sid, groupsid)) {
|
|
memcpy(&pnewace->sid, gsid, gsidsz);
|
|
pnewace->size = cpu_to_le16(gsidsz + 8);
|
|
/* remove inheritance flags */
|
|
pnewace->flags &= ~(OBJECT_INHERIT_ACE
|
|
| CONTAINER_INHERIT_ACE
|
|
| INHERIT_ONLY_ACE);
|
|
if (inherited)
|
|
pnewace->flags |= INHERITED_ACE;
|
|
dst += gsidsz + 8;
|
|
newcnt++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* inheritance for further inheritance
|
|
*
|
|
* Situations leading to output CONTAINER_INHERIT_ACE
|
|
* or OBJECT_INHERIT_ACE
|
|
*/
|
|
if (fordir
|
|
&& (poldace->flags
|
|
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) {
|
|
pnewace = (ACCESS_ALLOWED_ACE*)
|
|
((char*)newacl + dst);
|
|
memcpy(pnewace,poldace,acesz);
|
|
if ((poldace->flags & OBJECT_INHERIT_ACE)
|
|
&& !(poldace->flags & (CONTAINER_INHERIT_ACE
|
|
| NO_PROPAGATE_INHERIT_ACE)))
|
|
pnewace->flags |= INHERIT_ONLY_ACE;
|
|
if (acceptable
|
|
&& (poldace->flags & CONTAINER_INHERIT_ACE)
|
|
&& !(poldace->flags & NO_PROPAGATE_INHERIT_ACE)
|
|
&& !ntfs_same_sid(&poldace->sid, ownersid)
|
|
&& !ntfs_same_sid(&poldace->sid, groupsid)) {
|
|
if ((poldace->mask & (GENERIC_ALL | GENERIC_READ
|
|
| GENERIC_WRITE | GENERIC_EXECUTE)))
|
|
pnewace->flags |= INHERIT_ONLY_ACE;
|
|
else
|
|
pnewace->flags &= ~INHERIT_ONLY_ACE;
|
|
}
|
|
if (inherited)
|
|
pnewace->flags |= INHERITED_ACE;
|
|
/*
|
|
* Prepare grouping similar ACE for authenticated users
|
|
*/
|
|
if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& !pauthace
|
|
&& !(pnewace->flags & INHERIT_ONLY_ACE)
|
|
&& ntfs_same_sid(&poldace->sid, authsid)) {
|
|
pauthace = pnewace;
|
|
}
|
|
/*
|
|
* Prepare grouping similar ACE for owner
|
|
*/
|
|
if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& !pownerace
|
|
&& !(pnewace->flags & INHERIT_ONLY_ACE)
|
|
&& ntfs_same_sid(&poldace->sid, usid)) {
|
|
pownerace = pnewace;
|
|
}
|
|
dst += acesz;
|
|
newcnt++;
|
|
}
|
|
}
|
|
/*
|
|
* Adjust header if something was inherited
|
|
*/
|
|
if (dst > sizeof(ACL)) {
|
|
newacl->ace_count = cpu_to_le16(newcnt);
|
|
newacl->size = cpu_to_le16(dst);
|
|
} else
|
|
dst = 0;
|
|
return (dst);
|
|
}
|
|
|
|
#if POSIXACLS
|
|
|
|
/*
|
|
* Do sanity checks on a Posix descriptor
|
|
* Should not be called with a NULL argument
|
|
* returns TRUE if considered safe
|
|
* if not, error should be logged by caller
|
|
*/
|
|
|
|
BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc)
|
|
{
|
|
const struct POSIX_ACL *pacl;
|
|
int i;
|
|
BOOL ok;
|
|
u16 tag;
|
|
u32 id;
|
|
int perms;
|
|
struct {
|
|
u16 previous;
|
|
u32 previousid;
|
|
u16 tagsset;
|
|
mode_t mode;
|
|
int owners;
|
|
int groups;
|
|
int others;
|
|
} checks[2], *pchk;
|
|
|
|
for (i=0; i<2; i++) {
|
|
checks[i].mode = 0;
|
|
checks[i].tagsset = 0;
|
|
checks[i].owners = 0;
|
|
checks[i].groups = 0;
|
|
checks[i].others = 0;
|
|
checks[i].previous = 0;
|
|
checks[i].previousid = 0;
|
|
}
|
|
ok = TRUE;
|
|
pacl = &pxdesc->acl;
|
|
/*
|
|
* header (strict for now)
|
|
*/
|
|
if ((pacl->version != POSIX_VERSION)
|
|
|| (pacl->flags != 0)
|
|
|| (pacl->filler != 0))
|
|
ok = FALSE;
|
|
/*
|
|
* Reject multiple owner, group or other
|
|
* but do not require them to be present
|
|
* Also check the ACEs are in correct order
|
|
* which implies there is no duplicates
|
|
*/
|
|
for (i=0; i<pxdesc->acccnt + pxdesc->defcnt; i++) {
|
|
if (i >= pxdesc->firstdef)
|
|
pchk = &checks[1];
|
|
else
|
|
pchk = &checks[0];
|
|
perms = pacl->ace[i].perms;
|
|
tag = pacl->ace[i].tag;
|
|
pchk->tagsset |= tag;
|
|
id = pacl->ace[i].id;
|
|
if (perms & ~7) ok = FALSE;
|
|
if ((tag < pchk->previous)
|
|
|| ((tag == pchk->previous)
|
|
&& (id <= pchk->previousid)))
|
|
ok = FALSE;
|
|
pchk->previous = tag;
|
|
pchk->previousid = id;
|
|
switch (tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
if (pchk->owners++)
|
|
ok = FALSE;
|
|
if (id != (u32)-1)
|
|
ok = FALSE;
|
|
pchk->mode |= perms << 6;
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
if (pchk->groups++)
|
|
ok = FALSE;
|
|
if (id != (u32)-1)
|
|
ok = FALSE;
|
|
pchk->mode = (pchk->mode & 07707) | (perms << 3);
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
if (pchk->others++)
|
|
ok = FALSE;
|
|
if (id != (u32)-1)
|
|
ok = FALSE;
|
|
pchk->mode |= perms;
|
|
break;
|
|
case POSIX_ACL_USER :
|
|
case POSIX_ACL_GROUP :
|
|
if (id == (u32)-1)
|
|
ok = FALSE;
|
|
break;
|
|
case POSIX_ACL_MASK :
|
|
if (id != (u32)-1)
|
|
ok = FALSE;
|
|
pchk->mode = (pchk->mode & 07707) | (perms << 3);
|
|
break;
|
|
default :
|
|
ok = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if ((pxdesc->acccnt > 0)
|
|
&& ((checks[0].owners != 1) || (checks[0].groups != 1)
|
|
|| (checks[0].others != 1)))
|
|
ok = FALSE;
|
|
/* do not check owner, group or other are present in */
|
|
/* the default ACL, Windows does not necessarily set them */
|
|
/* descriptor */
|
|
if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef))
|
|
ok = FALSE;
|
|
if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0))
|
|
ok = FALSE;
|
|
/* check mode, unless null or no tag set */
|
|
if (pxdesc->mode
|
|
&& checks[0].tagsset
|
|
&& (checks[0].mode != (pxdesc->mode & 0777)))
|
|
ok = FALSE;
|
|
/* check tagsset */
|
|
if (pxdesc->tagsset != checks[0].tagsset)
|
|
ok = FALSE;
|
|
return (ok);
|
|
}
|
|
|
|
/*
|
|
* Set standard header data into a Posix ACL
|
|
* The mode argument should provide the 3 upper bits of target mode
|
|
*/
|
|
|
|
static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode)
|
|
{
|
|
mode_t mode;
|
|
u16 tagsset;
|
|
struct POSIX_ACE *pace;
|
|
int i;
|
|
|
|
mode = basemode & 07000;
|
|
tagsset = 0;
|
|
for (i=0; i<pxdesc->acccnt; i++) {
|
|
pace = &pxdesc->acl.ace[i];
|
|
tagsset |= pace->tag;
|
|
switch(pace->tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
mode |= (pace->perms & 7) << 6;
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
case POSIX_ACL_MASK :
|
|
mode = (mode & 07707) | ((pace->perms & 7) << 3);
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
mode |= pace->perms & 7;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
pxdesc->tagsset = tagsset;
|
|
pxdesc->mode = mode;
|
|
pxdesc->acl.version = POSIX_VERSION;
|
|
pxdesc->acl.flags = 0;
|
|
pxdesc->acl.filler = 0;
|
|
return (mode);
|
|
}
|
|
|
|
/*
|
|
* Sort ACEs in a Posix ACL
|
|
* This is useful for always getting reusable converted ACLs,
|
|
* it also helps in merging ACEs.
|
|
* Repeated tag+id are allowed and not merged here.
|
|
*
|
|
* Tags should be in ascending sequence and for a repeatable tag
|
|
* ids should be in ascending sequence.
|
|
*/
|
|
|
|
void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc)
|
|
{
|
|
struct POSIX_ACL *pacl;
|
|
struct POSIX_ACE ace;
|
|
int i;
|
|
int offs;
|
|
BOOL done;
|
|
u16 tag;
|
|
u16 previous;
|
|
u32 id;
|
|
u32 previousid;
|
|
|
|
|
|
/*
|
|
* Check sequencing of tag+id in access ACE's
|
|
*/
|
|
pacl = &pxdesc->acl;
|
|
do {
|
|
done = TRUE;
|
|
previous = pacl->ace[0].tag;
|
|
previousid = pacl->ace[0].id;
|
|
for (i=1; i<pxdesc->acccnt; i++) {
|
|
tag = pacl->ace[i].tag;
|
|
id = pacl->ace[i].id;
|
|
|
|
if ((tag < previous)
|
|
|| ((tag == previous) && (id < previousid))) {
|
|
done = FALSE;
|
|
memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE));
|
|
memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE));
|
|
memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE));
|
|
} else {
|
|
previous = tag;
|
|
previousid = id;
|
|
}
|
|
}
|
|
} while (!done);
|
|
/*
|
|
* Same for default ACEs
|
|
*/
|
|
do {
|
|
done = TRUE;
|
|
if ((pxdesc->defcnt) > 1) {
|
|
offs = pxdesc->firstdef;
|
|
previous = pacl->ace[offs].tag;
|
|
previousid = pacl->ace[offs].id;
|
|
for (i=offs+1; i<offs+pxdesc->defcnt; i++) {
|
|
tag = pacl->ace[i].tag;
|
|
id = pacl->ace[i].id;
|
|
|
|
if ((tag < previous)
|
|
|| ((tag == previous) && (id < previousid))) {
|
|
done = FALSE;
|
|
memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE));
|
|
memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE));
|
|
memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE));
|
|
} else {
|
|
previous = tag;
|
|
previousid = id;
|
|
}
|
|
}
|
|
}
|
|
} while (!done);
|
|
}
|
|
|
|
/*
|
|
* Merge a new mode into a Posix descriptor
|
|
* The Posix descriptor is not reallocated, its size is unchanged
|
|
*
|
|
* returns 0 if ok
|
|
*/
|
|
|
|
int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode)
|
|
{
|
|
int i;
|
|
BOOL maskfound;
|
|
struct POSIX_ACE *pace;
|
|
int todo;
|
|
|
|
maskfound = FALSE;
|
|
todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER;
|
|
for (i=pxdesc->acccnt-1; i>=0; i--) {
|
|
pace = &pxdesc->acl.ace[i];
|
|
switch(pace->tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
pace->perms = (mode >> 6) & 7;
|
|
todo &= ~POSIX_ACL_USER_OBJ;
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
if (!maskfound)
|
|
pace->perms = (mode >> 3) & 7;
|
|
todo &= ~POSIX_ACL_GROUP_OBJ;
|
|
break;
|
|
case POSIX_ACL_MASK :
|
|
pace->perms = (mode >> 3) & 7;
|
|
maskfound = TRUE;
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
pace->perms = mode & 7;
|
|
todo &= ~POSIX_ACL_OTHER;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
pxdesc->mode = mode;
|
|
return (todo ? -1 : 0);
|
|
}
|
|
|
|
/*
|
|
* Replace an access or default Posix ACL
|
|
* The resulting ACL is checked for validity
|
|
*
|
|
* Returns a new ACL or NULL if there is a problem
|
|
*/
|
|
|
|
struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
|
|
const struct POSIX_ACL *newacl, int count, BOOL deflt)
|
|
{
|
|
struct POSIX_SECURITY *newpxdesc;
|
|
size_t newsize;
|
|
int offset;
|
|
int oldoffset;
|
|
int i;
|
|
|
|
if (deflt)
|
|
newsize = sizeof(struct POSIX_SECURITY)
|
|
+ (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE);
|
|
else
|
|
newsize = sizeof(struct POSIX_SECURITY)
|
|
+ (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE);
|
|
newpxdesc = (struct POSIX_SECURITY*)malloc(newsize);
|
|
if (newpxdesc) {
|
|
if (deflt) {
|
|
offset = oldpxdesc->acccnt;
|
|
newpxdesc->acccnt = oldpxdesc->acccnt;
|
|
newpxdesc->defcnt = count;
|
|
newpxdesc->firstdef = offset;
|
|
/* copy access ACEs */
|
|
for (i=0; i<newpxdesc->acccnt; i++)
|
|
newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i];
|
|
/* copy default ACEs */
|
|
for (i=0; i<count; i++)
|
|
newpxdesc->acl.ace[i + offset] = newacl->ace[i];
|
|
} else {
|
|
offset = count;
|
|
newpxdesc->acccnt = count;
|
|
newpxdesc->defcnt = oldpxdesc->defcnt;
|
|
newpxdesc->firstdef = count;
|
|
/* copy access ACEs */
|
|
for (i=0; i<count; i++)
|
|
newpxdesc->acl.ace[i] = newacl->ace[i];
|
|
/* copy default ACEs */
|
|
oldoffset = oldpxdesc->firstdef;
|
|
for (i=0; i<newpxdesc->defcnt; i++)
|
|
newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset];
|
|
}
|
|
/* assume special flags unchanged */
|
|
posix_header(newpxdesc, oldpxdesc->mode);
|
|
if (!ntfs_valid_posix(newpxdesc)) {
|
|
/* do not log, this is an application error */
|
|
free(newpxdesc);
|
|
newpxdesc = (struct POSIX_SECURITY*)NULL;
|
|
errno = EINVAL;
|
|
}
|
|
} else
|
|
errno = ENOMEM;
|
|
return (newpxdesc);
|
|
}
|
|
|
|
/*
|
|
* Build a basic Posix ACL from a mode and umask,
|
|
* ignoring inheritance from the parent directory
|
|
*/
|
|
|
|
struct POSIX_SECURITY *ntfs_build_basic_posix(
|
|
const struct POSIX_SECURITY *pxdesc __attribute__((unused)),
|
|
mode_t mode, mode_t mask, BOOL isdir __attribute__((unused)))
|
|
{
|
|
struct POSIX_SECURITY *pydesc;
|
|
struct POSIX_ACE *pyace;
|
|
|
|
pydesc = (struct POSIX_SECURITY*)malloc(
|
|
sizeof(struct POSIX_SECURITY) + 3*sizeof(struct POSIX_ACE));
|
|
if (pydesc) {
|
|
pyace = &pydesc->acl.ace[0];
|
|
pyace->tag = POSIX_ACL_USER_OBJ;
|
|
pyace->perms = ((mode & ~mask) >> 6) & 7;
|
|
pyace->id = -1;
|
|
pyace = &pydesc->acl.ace[1];
|
|
pyace->tag = POSIX_ACL_GROUP_OBJ;
|
|
pyace->perms = ((mode & ~mask) >> 3) & 7;
|
|
pyace->id = -1;
|
|
pyace = &pydesc->acl.ace[2];
|
|
pyace->tag = POSIX_ACL_OTHER;
|
|
pyace->perms = (mode & ~mask) & 7;
|
|
pyace->id = -1;
|
|
pydesc->mode = mode;
|
|
pydesc->tagsset = POSIX_ACL_USER_OBJ
|
|
| POSIX_ACL_GROUP_OBJ
|
|
| POSIX_ACL_OTHER;
|
|
pydesc->acccnt = 3;
|
|
pydesc->defcnt = 0;
|
|
pydesc->firstdef = 6;
|
|
} else
|
|
errno = ENOMEM;
|
|
return (pydesc);
|
|
}
|
|
|
|
/*
|
|
* Build an inherited Posix descriptor from parent
|
|
* descriptor (if any) restricted to creation mode
|
|
*
|
|
* Returns the inherited descriptor or NULL if there is a problem
|
|
*/
|
|
|
|
struct POSIX_SECURITY *ntfs_build_inherited_posix(
|
|
const struct POSIX_SECURITY *pxdesc, mode_t mode,
|
|
mode_t mask, BOOL isdir)
|
|
{
|
|
struct POSIX_SECURITY *pydesc;
|
|
struct POSIX_ACE *pyace;
|
|
int count;
|
|
int defcnt;
|
|
int size;
|
|
int i;
|
|
s16 tagsset;
|
|
|
|
if (pxdesc && pxdesc->defcnt) {
|
|
if (isdir)
|
|
count = 2*pxdesc->defcnt + 3;
|
|
else
|
|
count = pxdesc->defcnt + 3;
|
|
} else
|
|
count = 3;
|
|
pydesc = (struct POSIX_SECURITY*)malloc(
|
|
sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE));
|
|
if (pydesc) {
|
|
/*
|
|
* Copy inherited tags and adapt perms
|
|
* Use requested mode, ignoring umask
|
|
* (not possible with older versions of fuse)
|
|
*/
|
|
tagsset = 0;
|
|
defcnt = (pxdesc ? pxdesc->defcnt : 0);
|
|
for (i=defcnt-1; i>=0; i--) {
|
|
pyace = &pydesc->acl.ace[i];
|
|
*pyace = pxdesc->acl.ace[pxdesc->firstdef + i];
|
|
switch (pyace->tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
pyace->perms &= (mode >> 6) & 7;
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
if (!(tagsset & POSIX_ACL_MASK))
|
|
pyace->perms &= (mode >> 3) & 7;
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
pyace->perms &= mode & 7;
|
|
break;
|
|
case POSIX_ACL_MASK :
|
|
pyace->perms &= (mode >> 3) & 7;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
tagsset |= pyace->tag;
|
|
}
|
|
pydesc->acccnt = defcnt;
|
|
/*
|
|
* If some standard tags were missing, append them from mode
|
|
* and sort the list
|
|
* Here we have to use the umask'ed mode
|
|
*/
|
|
if (~tagsset & (POSIX_ACL_USER_OBJ
|
|
| POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) {
|
|
i = defcnt;
|
|
/* owner was missing */
|
|
if (!(tagsset & POSIX_ACL_USER_OBJ)) {
|
|
pyace = &pydesc->acl.ace[i];
|
|
pyace->tag = POSIX_ACL_USER_OBJ;
|
|
pyace->id = -1;
|
|
pyace->perms = ((mode & ~mask) >> 6) & 7;
|
|
tagsset |= POSIX_ACL_USER_OBJ;
|
|
i++;
|
|
}
|
|
/* owning group was missing */
|
|
if (!(tagsset & POSIX_ACL_GROUP_OBJ)) {
|
|
pyace = &pydesc->acl.ace[i];
|
|
pyace->tag = POSIX_ACL_GROUP_OBJ;
|
|
pyace->id = -1;
|
|
pyace->perms = ((mode & ~mask) >> 3) & 7;
|
|
tagsset |= POSIX_ACL_GROUP_OBJ;
|
|
i++;
|
|
}
|
|
/* other was missing */
|
|
if (!(tagsset & POSIX_ACL_OTHER)) {
|
|
pyace = &pydesc->acl.ace[i];
|
|
pyace->tag = POSIX_ACL_OTHER;
|
|
pyace->id = -1;
|
|
pyace->perms = mode & ~mask & 7;
|
|
tagsset |= POSIX_ACL_OTHER;
|
|
i++;
|
|
}
|
|
pydesc->acccnt = i;
|
|
pydesc->firstdef = i;
|
|
pydesc->defcnt = 0;
|
|
ntfs_sort_posix(pydesc);
|
|
}
|
|
|
|
/*
|
|
* append as a default ACL if a directory
|
|
*/
|
|
pydesc->firstdef = pydesc->acccnt;
|
|
if (defcnt && isdir) {
|
|
size = sizeof(struct POSIX_ACE)*defcnt;
|
|
memcpy(&pydesc->acl.ace[pydesc->firstdef],
|
|
&pxdesc->acl.ace[pxdesc->firstdef],size);
|
|
pydesc->defcnt = defcnt;
|
|
} else {
|
|
pydesc->defcnt = 0;
|
|
}
|
|
/* assume special bits are not inherited */
|
|
posix_header(pydesc, mode & 07000);
|
|
if (!ntfs_valid_posix(pydesc)) {
|
|
ntfs_log_error("Error building an inherited Posix desc\n");
|
|
errno = EIO;
|
|
free(pydesc);
|
|
pydesc = (struct POSIX_SECURITY*)NULL;
|
|
}
|
|
} else
|
|
errno = ENOMEM;
|
|
return (pydesc);
|
|
}
|
|
|
|
static int merge_lists_posix(struct POSIX_ACE *targetace,
|
|
const struct POSIX_ACE *firstace,
|
|
const struct POSIX_ACE *secondace,
|
|
int firstcnt, int secondcnt)
|
|
{
|
|
int k;
|
|
|
|
k = 0;
|
|
/*
|
|
* No list is exhausted :
|
|
* if same tag+id in both list :
|
|
* ignore ACE from second list
|
|
* else take the one with smaller tag+id
|
|
*/
|
|
while ((firstcnt > 0) && (secondcnt > 0))
|
|
if ((firstace->tag == secondace->tag)
|
|
&& (firstace->id == secondace->id)) {
|
|
secondace++;
|
|
secondcnt--;
|
|
} else
|
|
if ((firstace->tag < secondace->tag)
|
|
|| ((firstace->tag == secondace->tag)
|
|
&& (firstace->id < secondace->id))) {
|
|
targetace->tag = firstace->tag;
|
|
targetace->id = firstace->id;
|
|
targetace->perms = firstace->perms;
|
|
firstace++;
|
|
targetace++;
|
|
firstcnt--;
|
|
k++;
|
|
} else {
|
|
targetace->tag = secondace->tag;
|
|
targetace->id = secondace->id;
|
|
targetace->perms = secondace->perms;
|
|
secondace++;
|
|
targetace++;
|
|
secondcnt--;
|
|
k++;
|
|
}
|
|
/*
|
|
* One list is exhausted, copy the other one
|
|
*/
|
|
while (firstcnt > 0) {
|
|
targetace->tag = firstace->tag;
|
|
targetace->id = firstace->id;
|
|
targetace->perms = firstace->perms;
|
|
firstace++;
|
|
targetace++;
|
|
firstcnt--;
|
|
k++;
|
|
}
|
|
while (secondcnt > 0) {
|
|
targetace->tag = secondace->tag;
|
|
targetace->id = secondace->id;
|
|
targetace->perms = secondace->perms;
|
|
secondace++;
|
|
targetace++;
|
|
secondcnt--;
|
|
k++;
|
|
}
|
|
return (k);
|
|
}
|
|
|
|
/*
|
|
* Merge two Posix ACLs
|
|
* The input ACLs have to be adequately sorted
|
|
*
|
|
* Returns the merged ACL, which is allocated and has to be freed by caller,
|
|
* or NULL if failed
|
|
*/
|
|
|
|
struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
|
|
const struct POSIX_SECURITY *second)
|
|
{
|
|
struct POSIX_SECURITY *pxdesc;
|
|
struct POSIX_ACE *targetace;
|
|
const struct POSIX_ACE *firstace;
|
|
const struct POSIX_ACE *secondace;
|
|
size_t size;
|
|
int k;
|
|
|
|
size = sizeof(struct POSIX_SECURITY)
|
|
+ (first->acccnt + first->defcnt
|
|
+ second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE);
|
|
pxdesc = (struct POSIX_SECURITY*)malloc(size);
|
|
if (pxdesc) {
|
|
/*
|
|
* merge access ACEs
|
|
*/
|
|
firstace = first->acl.ace;
|
|
secondace = second->acl.ace;
|
|
targetace = pxdesc->acl.ace;
|
|
k = merge_lists_posix(targetace,firstace,secondace,
|
|
first->acccnt,second->acccnt);
|
|
pxdesc->acccnt = k;
|
|
/*
|
|
* merge default ACEs
|
|
*/
|
|
pxdesc->firstdef = k;
|
|
firstace = &first->acl.ace[first->firstdef];
|
|
secondace = &second->acl.ace[second->firstdef];
|
|
targetace = &pxdesc->acl.ace[k];
|
|
k = merge_lists_posix(targetace,firstace,secondace,
|
|
first->defcnt,second->defcnt);
|
|
pxdesc->defcnt = k;
|
|
/*
|
|
* build header
|
|
*/
|
|
pxdesc->acl.version = POSIX_VERSION;
|
|
pxdesc->acl.flags = 0;
|
|
pxdesc->acl.filler = 0;
|
|
pxdesc->mode = 0;
|
|
pxdesc->tagsset = 0;
|
|
} else
|
|
errno = ENOMEM;
|
|
return (pxdesc);
|
|
}
|
|
|
|
struct BUILD_CONTEXT {
|
|
BOOL isdir;
|
|
BOOL adminowns;
|
|
BOOL groupowns;
|
|
u16 selfuserperms;
|
|
u16 selfgrpperms;
|
|
u16 grpperms;
|
|
u16 othperms;
|
|
u16 mask;
|
|
u16 designates;
|
|
u16 withmask;
|
|
u16 rootspecial;
|
|
} ;
|
|
|
|
|
|
|
|
static BOOL build_user_denials(ACL *pacl,
|
|
const SID *usid, struct MAPPING* const mapping[],
|
|
ACE_FLAGS flags, const struct POSIX_ACE *pxace,
|
|
struct BUILD_CONTEXT *pset)
|
|
{
|
|
BIGSID defsid;
|
|
ACCESS_ALLOWED_ACE *pdace;
|
|
const SID *sid;
|
|
int sidsz;
|
|
int pos;
|
|
int acecnt;
|
|
le32 grants;
|
|
le32 denials;
|
|
u16 perms;
|
|
u16 mixperms;
|
|
u16 tag;
|
|
BOOL rejected;
|
|
BOOL rootuser;
|
|
BOOL avoidmask;
|
|
|
|
rejected = FALSE;
|
|
tag = pxace->tag;
|
|
perms = pxace->perms;
|
|
rootuser = FALSE;
|
|
pos = le16_to_cpu(pacl->size);
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X))
|
|
&& ((pset->designates && pset->withmask)
|
|
|| (!pset->designates && !pset->withmask));
|
|
if (tag == POSIX_ACL_USER_OBJ) {
|
|
sid = usid;
|
|
sidsz = ntfs_sid_size(sid);
|
|
grants = OWNER_RIGHTS;
|
|
} else {
|
|
if (pxace->id) {
|
|
sid = ntfs_find_usid(mapping[MAPUSERS],
|
|
pxace->id, (SID*)&defsid);
|
|
grants = WORLD_RIGHTS;
|
|
} else {
|
|
sid = adminsid;
|
|
rootuser = TRUE;
|
|
grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK;
|
|
}
|
|
if (sid) {
|
|
sidsz = ntfs_sid_size(sid);
|
|
/*
|
|
* Insert denial of complement of mask for
|
|
* each designated user (except root)
|
|
* WRITE_OWNER is inserted so that
|
|
* the mask can be identified
|
|
*/
|
|
if (!avoidmask && !rootuser) {
|
|
denials = WRITE_OWNER;
|
|
pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
if (pset->isdir) {
|
|
if (!(pset->mask & POSIX_PERM_X))
|
|
denials |= DIR_EXEC;
|
|
if (!(pset->mask & POSIX_PERM_W))
|
|
denials |= DIR_WRITE;
|
|
if (!(pset->mask & POSIX_PERM_R))
|
|
denials |= DIR_READ;
|
|
} else {
|
|
if (!(pset->mask & POSIX_PERM_X))
|
|
denials |= FILE_EXEC;
|
|
if (!(pset->mask & POSIX_PERM_W))
|
|
denials |= FILE_WRITE;
|
|
if (!(pset->mask & POSIX_PERM_R))
|
|
denials |= FILE_READ;
|
|
}
|
|
if (rootuser)
|
|
grants &= ~ROOT_OWNER_UNMARK;
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = flags;
|
|
pdace->size = cpu_to_le16(sidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt++;
|
|
}
|
|
} else
|
|
rejected = TRUE;
|
|
}
|
|
if (!rejected) {
|
|
if (pset->isdir) {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= DIR_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= DIR_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= FILE_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= FILE_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= FILE_READ;
|
|
}
|
|
|
|
/* a possible ACE to deny owner what he/she would */
|
|
/* induely get from administrator, group or world */
|
|
/* unless owner is administrator or group */
|
|
|
|
denials = const_cpu_to_le32(0);
|
|
pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
if (!pset->adminowns && !rootuser) {
|
|
if (!pset->groupowns) {
|
|
mixperms = pset->grpperms | pset->othperms;
|
|
if (tag == POSIX_ACL_USER_OBJ)
|
|
mixperms |= pset->selfuserperms;
|
|
if (pset->isdir) {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= DIR_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= DIR_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= DIR_READ;
|
|
} else {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= FILE_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= FILE_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= FILE_READ;
|
|
}
|
|
} else {
|
|
mixperms = ~pset->grpperms & pset->othperms;
|
|
if (tag == POSIX_ACL_USER_OBJ)
|
|
mixperms |= pset->selfuserperms;
|
|
if (pset->isdir) {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= DIR_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= DIR_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= DIR_READ;
|
|
} else {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= FILE_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= FILE_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= FILE_READ;
|
|
}
|
|
}
|
|
denials &= ~grants;
|
|
if (denials) {
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = flags;
|
|
pdace->size = cpu_to_le16(sidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
}
|
|
pacl->size = cpu_to_le16(pos);
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
return (!rejected);
|
|
}
|
|
|
|
static BOOL build_user_grants(ACL *pacl,
|
|
const SID *usid, struct MAPPING* const mapping[],
|
|
ACE_FLAGS flags, const struct POSIX_ACE *pxace,
|
|
struct BUILD_CONTEXT *pset)
|
|
{
|
|
BIGSID defsid;
|
|
ACCESS_ALLOWED_ACE *pgace;
|
|
const SID *sid;
|
|
int sidsz;
|
|
int pos;
|
|
int acecnt;
|
|
le32 grants;
|
|
u16 perms;
|
|
u16 tag;
|
|
BOOL rejected;
|
|
BOOL rootuser;
|
|
|
|
rejected = FALSE;
|
|
tag = pxace->tag;
|
|
perms = pxace->perms;
|
|
rootuser = FALSE;
|
|
pos = le16_to_cpu(pacl->size);
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
if (tag == POSIX_ACL_USER_OBJ) {
|
|
sid = usid;
|
|
sidsz = ntfs_sid_size(sid);
|
|
grants = OWNER_RIGHTS;
|
|
} else {
|
|
if (pxace->id) {
|
|
sid = ntfs_find_usid(mapping[MAPUSERS],
|
|
pxace->id, (SID*)&defsid);
|
|
if (sid)
|
|
sidsz = ntfs_sid_size(sid);
|
|
else
|
|
rejected = TRUE;
|
|
grants = WORLD_RIGHTS;
|
|
} else {
|
|
sid = adminsid;
|
|
sidsz = ntfs_sid_size(sid);
|
|
rootuser = TRUE;
|
|
grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK;
|
|
}
|
|
}
|
|
if (!rejected) {
|
|
if (pset->isdir) {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= DIR_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= DIR_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= FILE_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= FILE_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= FILE_READ;
|
|
}
|
|
if (rootuser)
|
|
grants &= ~ROOT_OWNER_UNMARK;
|
|
pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->size = cpu_to_le16(sidsz + 8);
|
|
pgace->flags = flags;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt = le16_to_cpu(pacl->ace_count) + 1;
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
pacl->size = cpu_to_le16(pos);
|
|
}
|
|
return (!rejected);
|
|
}
|
|
|
|
|
|
/* a grant ACE for group */
|
|
/* unless group-obj has the same rights as world */
|
|
/* but present if group is owner or owner is administrator */
|
|
/* this ACE will be inserted after denials for group */
|
|
|
|
static BOOL build_group_denials_grant(ACL *pacl,
|
|
const SID *gsid, struct MAPPING* const mapping[],
|
|
ACE_FLAGS flags, const struct POSIX_ACE *pxace,
|
|
struct BUILD_CONTEXT *pset)
|
|
{
|
|
BIGSID defsid;
|
|
ACCESS_ALLOWED_ACE *pdace;
|
|
ACCESS_ALLOWED_ACE *pgace;
|
|
const SID *sid;
|
|
int sidsz;
|
|
int pos;
|
|
int acecnt;
|
|
le32 grants;
|
|
le32 denials;
|
|
u16 perms;
|
|
u16 mixperms;
|
|
u16 tag;
|
|
BOOL avoidmask;
|
|
BOOL rootgroup;
|
|
BOOL rejected;
|
|
|
|
rejected = FALSE;
|
|
tag = pxace->tag;
|
|
perms = pxace->perms;
|
|
pos = le16_to_cpu(pacl->size);
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
rootgroup = FALSE;
|
|
avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X))
|
|
&& ((pset->designates && pset->withmask)
|
|
|| (!pset->designates && !pset->withmask));
|
|
if (tag == POSIX_ACL_GROUP_OBJ)
|
|
sid = gsid;
|
|
else
|
|
if (pxace->id)
|
|
sid = ntfs_find_gsid(mapping[MAPGROUPS],
|
|
pxace->id, (SID*)&defsid);
|
|
else {
|
|
sid = adminsid;
|
|
rootgroup = TRUE;
|
|
}
|
|
if (sid) {
|
|
sidsz = ntfs_sid_size(sid);
|
|
/*
|
|
* Insert denial of complement of mask for
|
|
* each group
|
|
* WRITE_OWNER is inserted so that
|
|
* the mask can be identified
|
|
* Note : this mask may lead on Windows to
|
|
* deny rights to administrators belonging
|
|
* to some user group
|
|
*/
|
|
if ((!avoidmask && !rootgroup)
|
|
|| (pset->rootspecial
|
|
&& (tag == POSIX_ACL_GROUP_OBJ))) {
|
|
denials = WRITE_OWNER;
|
|
pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
if (pset->isdir) {
|
|
if (!(pset->mask & POSIX_PERM_X))
|
|
denials |= DIR_EXEC;
|
|
if (!(pset->mask & POSIX_PERM_W))
|
|
denials |= DIR_WRITE;
|
|
if (!(pset->mask & POSIX_PERM_R))
|
|
denials |= DIR_READ;
|
|
} else {
|
|
if (!(pset->mask & POSIX_PERM_X))
|
|
denials |= FILE_EXEC;
|
|
if (!(pset->mask & POSIX_PERM_W))
|
|
denials |= FILE_WRITE;
|
|
if (!(pset->mask & POSIX_PERM_R))
|
|
denials |= FILE_READ;
|
|
}
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = flags;
|
|
pdace->size = cpu_to_le16(sidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt++;
|
|
}
|
|
} else
|
|
rejected = TRUE;
|
|
if (!rejected
|
|
&& (pset->adminowns
|
|
|| pset->groupowns
|
|
|| avoidmask
|
|
|| rootgroup
|
|
|| (perms != pset->othperms))) {
|
|
grants = WORLD_RIGHTS;
|
|
if (rootgroup)
|
|
grants &= ~ROOT_GROUP_UNMARK;
|
|
if (pset->isdir) {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= DIR_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= DIR_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= FILE_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= FILE_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= FILE_READ;
|
|
}
|
|
|
|
/* a possible ACE to deny group what it would get from world */
|
|
/* or administrator, unless owner is administrator or group */
|
|
|
|
denials = const_cpu_to_le32(0);
|
|
pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
if (!pset->adminowns
|
|
&& !pset->groupowns
|
|
&& !rootgroup) {
|
|
mixperms = pset->othperms;
|
|
if (tag == POSIX_ACL_GROUP_OBJ)
|
|
mixperms |= pset->selfgrpperms;
|
|
if (pset->isdir) {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= DIR_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= DIR_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= DIR_READ;
|
|
} else {
|
|
if (mixperms & POSIX_PERM_X)
|
|
denials |= FILE_EXEC;
|
|
if (mixperms & POSIX_PERM_W)
|
|
denials |= FILE_WRITE;
|
|
if (mixperms & POSIX_PERM_R)
|
|
denials |= FILE_READ;
|
|
}
|
|
denials &= ~(grants | OWNER_RIGHTS);
|
|
if (denials) {
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = flags;
|
|
pdace->size = cpu_to_le16(sidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
|
|
/* now insert grants to group if more than world */
|
|
if (pset->adminowns
|
|
|| pset->groupowns
|
|
|| (avoidmask && (pset->designates || pset->withmask))
|
|
|| (perms & ~pset->othperms)
|
|
|| (pset->rootspecial
|
|
&& (tag == POSIX_ACL_GROUP_OBJ))
|
|
|| (tag == POSIX_ACL_GROUP)) {
|
|
if (rootgroup)
|
|
grants &= ~ROOT_GROUP_UNMARK;
|
|
pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = flags;
|
|
pgace->size = cpu_to_le16(sidsz + 8);
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, sid, sidsz);
|
|
pos += sidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
pacl->size = cpu_to_le16(pos);
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
return (!rejected);
|
|
}
|
|
|
|
|
|
/*
|
|
* Build an ACL composed of several ACE's
|
|
* returns size of ACL or zero if failed
|
|
*
|
|
* Three schemes are defined :
|
|
*
|
|
* 1) if root is neither owner nor group up to 7 ACE's are set up :
|
|
* - denials to owner (preventing grants to world or group to apply)
|
|
* + mask denials to designated user (unless mask allows all)
|
|
* + denials to designated user
|
|
* - grants to owner (always present - first grant)
|
|
* + grants to designated user
|
|
* + mask denial to group (unless mask allows all)
|
|
* - denials to group (preventing grants to world to apply)
|
|
* - grants to group (unless group has no more than world rights)
|
|
* + mask denials to designated group (unless mask allows all)
|
|
* + grants to designated group
|
|
* + denials to designated group
|
|
* - grants to world (unless none)
|
|
* - full privileges to administrator, always present
|
|
* - full privileges to system, always present
|
|
*
|
|
* The same scheme is applied for Posix ACLs, with the mask represented
|
|
* as denials prepended to grants for designated users and groups
|
|
*
|
|
* This is inspired by an Internet Draft from Marius Aamodt Eriksen
|
|
* for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt)
|
|
* More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt)
|
|
* are not followed, as they ignore the Posix mask and lead to
|
|
* loss of compatibility with Linux implementations on other fs.
|
|
*
|
|
* Note that denials to group are located after grants to owner.
|
|
* This only occurs in the unfrequent situation where world
|
|
* has more rights than group and cannot be avoided if owner and other
|
|
* have some common right which is denied to group (eg for mode 745
|
|
* executing has to be denied to group, but not to owner or world).
|
|
* This rare situation is processed by Windows correctly, but
|
|
* Windows utilities may want to change the order, with a
|
|
* consequence of applying the group denials to the Windows owner.
|
|
* The interpretation on Linux is not affected by the order change.
|
|
*
|
|
* 2) if root is either owner or group, two problems arise :
|
|
* - granting full rights to administrator (as needed to transpose
|
|
* to Windows rights bypassing granting to root) would imply
|
|
* Linux permissions to always be seen as rwx, no matter the chmod
|
|
* - there is no different SID to separate an administrator owner
|
|
* from an administrator group. Hence Linux permissions for owner
|
|
* would always be similar to permissions to group.
|
|
*
|
|
* as a work-around, up to 5 ACE's are set up if owner or group :
|
|
* - grants to owner, always present at first position
|
|
* - grants to group, always present
|
|
* - grants to world, unless none
|
|
* - full privileges to administrator, always present
|
|
* - full privileges to system, always present
|
|
*
|
|
* On Windows, these ACE's are processed normally, though they
|
|
* are redundant (owner, group and administrator are the same,
|
|
* as a consequence any denials would damage administrator rights)
|
|
* but on Linux, privileges to administrator are ignored (they
|
|
* are not needed as root has always full privileges), and
|
|
* neither grants to group are applied to owner, nor grants to
|
|
* world are applied to owner or group.
|
|
*
|
|
* 3) finally a similar situation arises when group is owner (they
|
|
* have the same SID), but is not root.
|
|
* In this situation up to 6 ACE's are set up :
|
|
*
|
|
* - denials to owner (preventing grants to world to apply)
|
|
* - grants to owner (always present)
|
|
* - grants to group (unless groups has same rights as world)
|
|
* - grants to world (unless none)
|
|
* - full privileges to administrator, always present
|
|
* - full privileges to system, always present
|
|
*
|
|
* On Windows, these ACE's are processed normally, though they
|
|
* are redundant (as owner and group are the same), but this has
|
|
* no impact on administrator rights
|
|
*
|
|
* Special flags (S_ISVTX, S_ISGID, S_ISUID) :
|
|
* an extra null ACE is inserted to hold these flags, using
|
|
* the same conventions as cygwin.
|
|
*
|
|
*/
|
|
|
|
static int buildacls_posix(struct MAPPING* const mapping[],
|
|
char *secattr, int offs, const struct POSIX_SECURITY *pxdesc,
|
|
int isdir, const SID *usid, const SID *gsid)
|
|
{
|
|
struct BUILD_CONTEXT aceset[2], *pset;
|
|
BOOL adminowns;
|
|
BOOL groupowns;
|
|
ACL *pacl;
|
|
ACCESS_ALLOWED_ACE *pgace;
|
|
ACCESS_ALLOWED_ACE *pdace;
|
|
const struct POSIX_ACE *pxace;
|
|
BOOL ok;
|
|
mode_t mode;
|
|
u16 tag;
|
|
u16 perms;
|
|
ACE_FLAGS flags;
|
|
int pos;
|
|
int i;
|
|
int k;
|
|
BIGSID defsid;
|
|
const SID *sid;
|
|
int acecnt;
|
|
int usidsz;
|
|
int wsidsz;
|
|
int asidsz;
|
|
int ssidsz;
|
|
int nsidsz;
|
|
le32 grants;
|
|
|
|
usidsz = ntfs_sid_size(usid);
|
|
wsidsz = ntfs_sid_size(worldsid);
|
|
asidsz = ntfs_sid_size(adminsid);
|
|
ssidsz = ntfs_sid_size(systemsid);
|
|
mode = pxdesc->mode;
|
|
/* adminowns and groupowns are used for both lists */
|
|
adminowns = ntfs_same_sid(usid, adminsid)
|
|
|| ntfs_same_sid(gsid, adminsid);
|
|
groupowns = !adminowns && ntfs_same_sid(usid, gsid);
|
|
|
|
ok = TRUE;
|
|
|
|
/* ACL header */
|
|
pacl = (ACL*)&secattr[offs];
|
|
pacl->revision = ACL_REVISION;
|
|
pacl->alignment1 = 0;
|
|
pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8);
|
|
pacl->ace_count = const_cpu_to_le16(0);
|
|
pacl->alignment2 = const_cpu_to_le16(0);
|
|
|
|
/*
|
|
* Determine what is allowed to some group or world
|
|
* to prevent designated users or other groups to get
|
|
* rights from groups or world
|
|
* Do the same if owner and group appear as designated
|
|
* user or group
|
|
* Also get global mask
|
|
*/
|
|
for (k=0; k<2; k++) {
|
|
pset = &aceset[k];
|
|
pset->selfuserperms = 0;
|
|
pset->selfgrpperms = 0;
|
|
pset->grpperms = 0;
|
|
pset->othperms = 0;
|
|
pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
|
|
pset->designates = 0;
|
|
pset->withmask = 0;
|
|
pset->rootspecial = 0;
|
|
pset->adminowns = adminowns;
|
|
pset->groupowns = groupowns;
|
|
pset->isdir = isdir;
|
|
}
|
|
|
|
for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) {
|
|
if (i >= pxdesc->acccnt) {
|
|
pset = &aceset[1];
|
|
pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
|
|
} else {
|
|
pset = &aceset[0];
|
|
pxace = &pxdesc->acl.ace[i];
|
|
}
|
|
switch (pxace->tag) {
|
|
case POSIX_ACL_USER :
|
|
pset->designates++;
|
|
if (pxace->id) {
|
|
sid = ntfs_find_usid(mapping[MAPUSERS],
|
|
pxace->id, (SID*)&defsid);
|
|
if (sid && ntfs_same_sid(sid,usid))
|
|
pset->selfuserperms |= pxace->perms;
|
|
} else
|
|
/* root as designated user is processed apart */
|
|
pset->rootspecial = TRUE;
|
|
break;
|
|
case POSIX_ACL_GROUP :
|
|
pset->designates++;
|
|
if (pxace->id) {
|
|
sid = ntfs_find_gsid(mapping[MAPUSERS],
|
|
pxace->id, (SID*)&defsid);
|
|
if (sid && ntfs_same_sid(sid,gsid))
|
|
pset->selfgrpperms |= pxace->perms;
|
|
} else
|
|
/* root as designated group is processed apart */
|
|
pset->rootspecial = TRUE;
|
|
/* fall through */
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
pset->grpperms |= pxace->perms;
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
pset->othperms = pxace->perms;
|
|
break;
|
|
case POSIX_ACL_MASK :
|
|
pset->withmask++;
|
|
pset->mask = pxace->perms;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) {
|
|
ntfs_log_error("** error : access and default not consecutive\n");
|
|
return (0);
|
|
}
|
|
/*
|
|
* First insert all denials for owner and each
|
|
* designated user (with mask if needed)
|
|
*/
|
|
|
|
pacl->ace_count = const_cpu_to_le16(0);
|
|
pacl->size = const_cpu_to_le16(sizeof(ACL));
|
|
for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) {
|
|
if (i >= pxdesc->acccnt) {
|
|
flags = INHERIT_ONLY_ACE
|
|
| OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
|
|
pset = &aceset[1];
|
|
pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
|
|
} else {
|
|
if (pxdesc->defcnt)
|
|
flags = NO_PROPAGATE_INHERIT_ACE;
|
|
else
|
|
flags = (isdir ? DIR_INHERITANCE
|
|
: FILE_INHERITANCE);
|
|
pset = &aceset[0];
|
|
pxace = &pxdesc->acl.ace[i];
|
|
}
|
|
tag = pxace->tag;
|
|
perms = pxace->perms;
|
|
switch (tag) {
|
|
|
|
/* insert denial ACEs for each owner or allowed user */
|
|
|
|
case POSIX_ACL_USER :
|
|
case POSIX_ACL_USER_OBJ :
|
|
|
|
ok = build_user_denials(pacl,
|
|
usid, mapping, flags, pxace, pset);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* for directories, insert a world execution denial
|
|
* inherited to plain files.
|
|
* This is to prevent Windows from granting execution
|
|
* of files through inheritance from parent directory
|
|
*/
|
|
|
|
if (isdir && ok) {
|
|
pos = le16_to_cpu(pacl->size);
|
|
pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
|
|
pdace->size = cpu_to_le16(wsidsz + 8);
|
|
pdace->mask = FILE_EXEC;
|
|
memcpy((char*)&pdace->sid, worldsid, wsidsz);
|
|
pos += wsidsz + 8;
|
|
acecnt = le16_to_cpu(pacl->ace_count) + 1;
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
pacl->size = cpu_to_le16(pos);
|
|
}
|
|
|
|
/*
|
|
* now insert (if needed)
|
|
* - grants to owner and designated users
|
|
* - mask and denials for all groups
|
|
* - grants to other
|
|
*/
|
|
|
|
for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) {
|
|
if (i >= pxdesc->acccnt) {
|
|
flags = INHERIT_ONLY_ACE
|
|
| OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
|
|
pset = &aceset[1];
|
|
pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
|
|
} else {
|
|
if (pxdesc->defcnt)
|
|
flags = NO_PROPAGATE_INHERIT_ACE;
|
|
else
|
|
flags = (isdir ? DIR_INHERITANCE
|
|
: FILE_INHERITANCE);
|
|
pset = &aceset[0];
|
|
pxace = &pxdesc->acl.ace[i];
|
|
}
|
|
tag = pxace->tag;
|
|
perms = pxace->perms;
|
|
switch (tag) {
|
|
|
|
/* ACE for each owner or allowed user */
|
|
|
|
case POSIX_ACL_USER :
|
|
case POSIX_ACL_USER_OBJ :
|
|
ok = build_user_grants(pacl,usid,
|
|
mapping,flags,pxace,pset);
|
|
break;
|
|
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
/* denials and grants for group when needed */
|
|
if (pset->groupowns && !pset->adminowns
|
|
&& (pset->grpperms == pset->othperms)
|
|
&& !pset->designates && !pset->withmask) {
|
|
ok = TRUE;
|
|
} else {
|
|
ok = build_group_denials_grant(pacl,gsid,
|
|
mapping,flags,pxace,pset);
|
|
}
|
|
break;
|
|
|
|
case POSIX_ACL_GROUP :
|
|
|
|
/* denials and grants for designated groups */
|
|
|
|
ok = build_group_denials_grant(pacl,gsid,
|
|
mapping,flags,pxace,pset);
|
|
break;
|
|
|
|
case POSIX_ACL_OTHER :
|
|
|
|
/* grants for other users */
|
|
|
|
pos = le16_to_cpu(pacl->size);
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
grants = WORLD_RIGHTS;
|
|
if (isdir) {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= DIR_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= DIR_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
if (perms & POSIX_PERM_X)
|
|
grants |= FILE_EXEC;
|
|
if (perms & POSIX_PERM_W)
|
|
grants |= FILE_WRITE;
|
|
if (perms & POSIX_PERM_R)
|
|
grants |= FILE_READ;
|
|
}
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = flags;
|
|
pgace->size = cpu_to_le16(wsidsz + 8);
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, worldsid, wsidsz);
|
|
pos += wsidsz + 8;
|
|
acecnt = le16_to_cpu(pacl->ace_count) + 1;
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
pacl->size = cpu_to_le16(pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
errno = EINVAL;
|
|
pos = 0;
|
|
} else {
|
|
/* an ACE for administrators */
|
|
/* always full access */
|
|
|
|
pos = le16_to_cpu(pacl->size);
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
if (isdir)
|
|
flags = OBJECT_INHERIT_ACE
|
|
| CONTAINER_INHERIT_ACE;
|
|
else
|
|
flags = NO_PROPAGATE_INHERIT_ACE;
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = flags;
|
|
pgace->size = cpu_to_le16(asidsz + 8);
|
|
grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, adminsid, asidsz);
|
|
pos += asidsz + 8;
|
|
acecnt++;
|
|
|
|
/* an ACE for system (needed ?) */
|
|
/* always full access */
|
|
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = flags;
|
|
pgace->size = cpu_to_le16(ssidsz + 8);
|
|
grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, systemsid, ssidsz);
|
|
pos += ssidsz + 8;
|
|
acecnt++;
|
|
|
|
/* a null ACE to hold special flags */
|
|
/* using the same representation as cygwin */
|
|
|
|
if (mode & (S_ISVTX | S_ISGID | S_ISUID)) {
|
|
nsidsz = ntfs_sid_size(nullsid);
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = NO_PROPAGATE_INHERIT_ACE;
|
|
pgace->size = cpu_to_le16(nsidsz + 8);
|
|
grants = const_cpu_to_le32(0);
|
|
if (mode & S_ISUID)
|
|
grants |= FILE_APPEND_DATA;
|
|
if (mode & S_ISGID)
|
|
grants |= FILE_WRITE_DATA;
|
|
if (mode & S_ISVTX)
|
|
grants |= FILE_READ_DATA;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, nullsid, nsidsz);
|
|
pos += nsidsz + 8;
|
|
acecnt++;
|
|
}
|
|
|
|
/* fix ACL header */
|
|
pacl->size = cpu_to_le16(pos);
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
}
|
|
return (ok ? pos : 0);
|
|
}
|
|
|
|
#endif /* POSIXACLS */
|
|
|
|
static int buildacls(char *secattr, int offs, mode_t mode, int isdir,
|
|
const SID * usid, const SID * gsid)
|
|
{
|
|
ACL *pacl;
|
|
ACCESS_ALLOWED_ACE *pgace;
|
|
ACCESS_ALLOWED_ACE *pdace;
|
|
BOOL adminowns;
|
|
BOOL groupowns;
|
|
ACE_FLAGS gflags;
|
|
int pos;
|
|
int acecnt;
|
|
int usidsz;
|
|
int gsidsz;
|
|
int wsidsz;
|
|
int asidsz;
|
|
int ssidsz;
|
|
int nsidsz;
|
|
le32 grants;
|
|
le32 denials;
|
|
|
|
usidsz = ntfs_sid_size(usid);
|
|
gsidsz = ntfs_sid_size(gsid);
|
|
wsidsz = ntfs_sid_size(worldsid);
|
|
asidsz = ntfs_sid_size(adminsid);
|
|
ssidsz = ntfs_sid_size(systemsid);
|
|
adminowns = ntfs_same_sid(usid, adminsid)
|
|
|| ntfs_same_sid(gsid, adminsid);
|
|
groupowns = !adminowns && ntfs_same_sid(usid, gsid);
|
|
|
|
/* ACL header */
|
|
pacl = (ACL*)&secattr[offs];
|
|
pacl->revision = ACL_REVISION;
|
|
pacl->alignment1 = 0;
|
|
pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8);
|
|
pacl->ace_count = const_cpu_to_le16(1);
|
|
pacl->alignment2 = const_cpu_to_le16(0);
|
|
pos = sizeof(ACL);
|
|
acecnt = 0;
|
|
|
|
/* compute a grant ACE for owner */
|
|
/* this ACE will be inserted after denial for owner */
|
|
|
|
grants = OWNER_RIGHTS;
|
|
if (isdir) {
|
|
gflags = DIR_INHERITANCE;
|
|
if (mode & S_IXUSR)
|
|
grants |= DIR_EXEC;
|
|
if (mode & S_IWUSR)
|
|
grants |= DIR_WRITE;
|
|
if (mode & S_IRUSR)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
gflags = FILE_INHERITANCE;
|
|
if (mode & S_IXUSR)
|
|
grants |= FILE_EXEC;
|
|
if (mode & S_IWUSR)
|
|
grants |= FILE_WRITE;
|
|
if (mode & S_IRUSR)
|
|
grants |= FILE_READ;
|
|
}
|
|
|
|
/* a possible ACE to deny owner what he/she would */
|
|
/* induely get from administrator, group or world */
|
|
/* unless owner is administrator or group */
|
|
|
|
denials = const_cpu_to_le32(0);
|
|
pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
|
|
if (!adminowns) {
|
|
if (!groupowns) {
|
|
if (isdir) {
|
|
pdace->flags = DIR_INHERITANCE;
|
|
if (mode & (S_IXGRP | S_IXOTH))
|
|
denials |= DIR_EXEC;
|
|
if (mode & (S_IWGRP | S_IWOTH))
|
|
denials |= DIR_WRITE;
|
|
if (mode & (S_IRGRP | S_IROTH))
|
|
denials |= DIR_READ;
|
|
} else {
|
|
pdace->flags = FILE_INHERITANCE;
|
|
if (mode & (S_IXGRP | S_IXOTH))
|
|
denials |= FILE_EXEC;
|
|
if (mode & (S_IWGRP | S_IWOTH))
|
|
denials |= FILE_WRITE;
|
|
if (mode & (S_IRGRP | S_IROTH))
|
|
denials |= FILE_READ;
|
|
}
|
|
} else {
|
|
if (isdir) {
|
|
pdace->flags = DIR_INHERITANCE;
|
|
if ((mode & S_IXOTH) && !(mode & S_IXGRP))
|
|
denials |= DIR_EXEC;
|
|
if ((mode & S_IWOTH) && !(mode & S_IWGRP))
|
|
denials |= DIR_WRITE;
|
|
if ((mode & S_IROTH) && !(mode & S_IRGRP))
|
|
denials |= DIR_READ;
|
|
} else {
|
|
pdace->flags = FILE_INHERITANCE;
|
|
if ((mode & S_IXOTH) && !(mode & S_IXGRP))
|
|
denials |= FILE_EXEC;
|
|
if ((mode & S_IWOTH) && !(mode & S_IWGRP))
|
|
denials |= FILE_WRITE;
|
|
if ((mode & S_IROTH) && !(mode & S_IRGRP))
|
|
denials |= FILE_READ;
|
|
}
|
|
}
|
|
denials &= ~grants;
|
|
if (denials) {
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->size = cpu_to_le16(usidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, usid, usidsz);
|
|
pos += usidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
/*
|
|
* for directories, a world execution denial
|
|
* inherited to plain files
|
|
*/
|
|
|
|
if (isdir) {
|
|
pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
|
|
pdace->size = cpu_to_le16(wsidsz + 8);
|
|
pdace->mask = FILE_EXEC;
|
|
memcpy((char*)&pdace->sid, worldsid, wsidsz);
|
|
pos += wsidsz + 8;
|
|
acecnt++;
|
|
}
|
|
|
|
|
|
/* now insert grants to owner */
|
|
pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->size = cpu_to_le16(usidsz + 8);
|
|
pgace->flags = gflags;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, usid, usidsz);
|
|
pos += usidsz + 8;
|
|
acecnt++;
|
|
|
|
/* a grant ACE for group */
|
|
/* unless group has the same rights as world */
|
|
/* but present if group is owner or owner is administrator */
|
|
/* this ACE will be inserted after denials for group */
|
|
|
|
if (adminowns
|
|
|| (((mode >> 3) ^ mode) & 7)) {
|
|
grants = WORLD_RIGHTS;
|
|
if (isdir) {
|
|
gflags = DIR_INHERITANCE;
|
|
if (mode & S_IXGRP)
|
|
grants |= DIR_EXEC;
|
|
if (mode & S_IWGRP)
|
|
grants |= DIR_WRITE;
|
|
if (mode & S_IRGRP)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
gflags = FILE_INHERITANCE;
|
|
if (mode & S_IXGRP)
|
|
grants |= FILE_EXEC;
|
|
if (mode & S_IWGRP)
|
|
grants |= FILE_WRITE;
|
|
if (mode & S_IRGRP)
|
|
grants |= FILE_READ;
|
|
}
|
|
|
|
/* a possible ACE to deny group what it would get from world */
|
|
/* or administrator, unless owner is administrator or group */
|
|
|
|
denials = const_cpu_to_le32(0);
|
|
pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
if (!adminowns && !groupowns) {
|
|
if (isdir) {
|
|
pdace->flags = DIR_INHERITANCE;
|
|
if (mode & S_IXOTH)
|
|
denials |= DIR_EXEC;
|
|
if (mode & S_IWOTH)
|
|
denials |= DIR_WRITE;
|
|
if (mode & S_IROTH)
|
|
denials |= DIR_READ;
|
|
} else {
|
|
pdace->flags = FILE_INHERITANCE;
|
|
if (mode & S_IXOTH)
|
|
denials |= FILE_EXEC;
|
|
if (mode & S_IWOTH)
|
|
denials |= FILE_WRITE;
|
|
if (mode & S_IROTH)
|
|
denials |= FILE_READ;
|
|
}
|
|
denials &= ~(grants | OWNER_RIGHTS);
|
|
if (denials) {
|
|
pdace->type = ACCESS_DENIED_ACE_TYPE;
|
|
pdace->size = cpu_to_le16(gsidsz + 8);
|
|
pdace->mask = denials;
|
|
memcpy((char*)&pdace->sid, gsid, gsidsz);
|
|
pos += gsidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
|
|
if (adminowns
|
|
|| groupowns
|
|
|| ((mode >> 3) & ~mode & 7)) {
|
|
/* now insert grants to group */
|
|
/* if more rights than other */
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = gflags;
|
|
pgace->size = cpu_to_le16(gsidsz + 8);
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, gsid, gsidsz);
|
|
pos += gsidsz + 8;
|
|
acecnt++;
|
|
}
|
|
}
|
|
|
|
/* an ACE for world users */
|
|
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
grants = WORLD_RIGHTS;
|
|
if (isdir) {
|
|
pgace->flags = DIR_INHERITANCE;
|
|
if (mode & S_IXOTH)
|
|
grants |= DIR_EXEC;
|
|
if (mode & S_IWOTH)
|
|
grants |= DIR_WRITE;
|
|
if (mode & S_IROTH)
|
|
grants |= DIR_READ;
|
|
} else {
|
|
pgace->flags = FILE_INHERITANCE;
|
|
if (mode & S_IXOTH)
|
|
grants |= FILE_EXEC;
|
|
if (mode & S_IWOTH)
|
|
grants |= FILE_WRITE;
|
|
if (mode & S_IROTH)
|
|
grants |= FILE_READ;
|
|
}
|
|
pgace->size = cpu_to_le16(wsidsz + 8);
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, worldsid, wsidsz);
|
|
pos += wsidsz + 8;
|
|
acecnt++;
|
|
|
|
/* an ACE for administrators */
|
|
/* always full access */
|
|
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
if (isdir)
|
|
pgace->flags = DIR_INHERITANCE;
|
|
else
|
|
pgace->flags = FILE_INHERITANCE;
|
|
pgace->size = cpu_to_le16(asidsz + 8);
|
|
grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, adminsid, asidsz);
|
|
pos += asidsz + 8;
|
|
acecnt++;
|
|
|
|
/* an ACE for system (needed ?) */
|
|
/* always full access */
|
|
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
if (isdir)
|
|
pgace->flags = DIR_INHERITANCE;
|
|
else
|
|
pgace->flags = FILE_INHERITANCE;
|
|
pgace->size = cpu_to_le16(ssidsz + 8);
|
|
grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, systemsid, ssidsz);
|
|
pos += ssidsz + 8;
|
|
acecnt++;
|
|
|
|
/* a null ACE to hold special flags */
|
|
/* using the same representation as cygwin */
|
|
|
|
if (mode & (S_ISVTX | S_ISGID | S_ISUID)) {
|
|
nsidsz = ntfs_sid_size(nullsid);
|
|
pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
|
|
pgace->type = ACCESS_ALLOWED_ACE_TYPE;
|
|
pgace->flags = NO_PROPAGATE_INHERIT_ACE;
|
|
pgace->size = cpu_to_le16(nsidsz + 8);
|
|
grants = const_cpu_to_le32(0);
|
|
if (mode & S_ISUID)
|
|
grants |= FILE_APPEND_DATA;
|
|
if (mode & S_ISGID)
|
|
grants |= FILE_WRITE_DATA;
|
|
if (mode & S_ISVTX)
|
|
grants |= FILE_READ_DATA;
|
|
pgace->mask = grants;
|
|
memcpy((char*)&pgace->sid, nullsid, nsidsz);
|
|
pos += nsidsz + 8;
|
|
acecnt++;
|
|
}
|
|
|
|
/* fix ACL header */
|
|
pacl->size = cpu_to_le16(pos);
|
|
pacl->ace_count = cpu_to_le16(acecnt);
|
|
return (pos);
|
|
}
|
|
|
|
#if POSIXACLS
|
|
|
|
/*
|
|
* Build a full security descriptor from a Posix ACL
|
|
* returns descriptor in allocated memory, must free() after use
|
|
*/
|
|
|
|
char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
|
|
struct POSIX_SECURITY *pxdesc,
|
|
int isdir, const SID *usid, const SID *gsid)
|
|
{
|
|
int newattrsz;
|
|
SECURITY_DESCRIPTOR_RELATIVE *pnhead;
|
|
char *newattr;
|
|
int aclsz;
|
|
int usidsz;
|
|
int gsidsz;
|
|
int wsidsz;
|
|
int asidsz;
|
|
int ssidsz;
|
|
int k;
|
|
|
|
usidsz = ntfs_sid_size(usid);
|
|
gsidsz = ntfs_sid_size(gsid);
|
|
wsidsz = ntfs_sid_size(worldsid);
|
|
asidsz = ntfs_sid_size(adminsid);
|
|
ssidsz = ntfs_sid_size(systemsid);
|
|
|
|
/* allocate enough space for the new security attribute */
|
|
newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */
|
|
+ usidsz + gsidsz /* usid and gsid */
|
|
+ sizeof(ACL) /* acl header */
|
|
+ 2*(8 + usidsz) /* two possible ACE for user */
|
|
+ 3*(8 + gsidsz) /* three possible ACE for group and mask */
|
|
+ 8 + wsidsz /* one ACE for world */
|
|
+ 8 + asidsz /* one ACE for admin */
|
|
+ 8 + ssidsz; /* one ACE for system */
|
|
if (isdir) /* a world denial for directories */
|
|
newattrsz += 8 + wsidsz;
|
|
if (pxdesc->mode & 07000) /* a NULL ACE for special modes */
|
|
newattrsz += 8 + ntfs_sid_size(nullsid);
|
|
/* account for non-owning users and groups */
|
|
for (k=0; k<pxdesc->acccnt; k++) {
|
|
if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER)
|
|
|| (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP))
|
|
newattrsz += 3*MAX_SID_SIZE;
|
|
}
|
|
/* account for default ACE's */
|
|
newattrsz += 2*MAX_SID_SIZE*pxdesc->defcnt;
|
|
newattr = (char*)ntfs_malloc(newattrsz);
|
|
if (newattr) {
|
|
/* build the main header part */
|
|
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
|
|
pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
|
|
pnhead->alignment = 0;
|
|
/*
|
|
* The flag SE_DACL_PROTECTED prevents the ACL
|
|
* to be changed in an inheritance after creation
|
|
*/
|
|
pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
|
|
| SE_SELF_RELATIVE;
|
|
/*
|
|
* Windows prefers ACL first, do the same to
|
|
* get the same hash value and avoid duplication
|
|
*/
|
|
/* build permissions */
|
|
aclsz = buildacls_posix(mapping,newattr,
|
|
sizeof(SECURITY_DESCRIPTOR_RELATIVE),
|
|
pxdesc, isdir, usid, gsid);
|
|
if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz + gsidsz) <= newattrsz)) {
|
|
/* append usid and gsid */
|
|
memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz], usid, usidsz);
|
|
memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz], gsid, gsidsz);
|
|
/* positions of ACL, USID and GSID into header */
|
|
pnhead->owner =
|
|
cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz);
|
|
pnhead->group =
|
|
cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz);
|
|
pnhead->sacl = const_cpu_to_le32(0);
|
|
pnhead->dacl =
|
|
const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
|
|
} else {
|
|
/* ACL failure (errno set) or overflow */
|
|
free(newattr);
|
|
newattr = (char*)NULL;
|
|
if (aclsz) {
|
|
/* hope error was detected before overflowing */
|
|
ntfs_log_error("Security descriptor is longer than expected\n");
|
|
errno = EIO;
|
|
}
|
|
}
|
|
} else
|
|
errno = ENOMEM;
|
|
return (newattr);
|
|
}
|
|
|
|
#endif /* POSIXACLS */
|
|
|
|
/*
|
|
* Build a full security descriptor
|
|
* returns descriptor in allocated memory, must free() after use
|
|
*/
|
|
|
|
char *ntfs_build_descr(mode_t mode,
|
|
int isdir, const SID * usid, const SID * gsid)
|
|
{
|
|
int newattrsz;
|
|
SECURITY_DESCRIPTOR_RELATIVE *pnhead;
|
|
char *newattr;
|
|
int aclsz;
|
|
int usidsz;
|
|
int gsidsz;
|
|
int wsidsz;
|
|
int asidsz;
|
|
int ssidsz;
|
|
|
|
usidsz = ntfs_sid_size(usid);
|
|
gsidsz = ntfs_sid_size(gsid);
|
|
wsidsz = ntfs_sid_size(worldsid);
|
|
asidsz = ntfs_sid_size(adminsid);
|
|
ssidsz = ntfs_sid_size(systemsid);
|
|
|
|
/* allocate enough space for the new security attribute */
|
|
newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */
|
|
+ usidsz + gsidsz /* usid and gsid */
|
|
+ sizeof(ACL) /* acl header */
|
|
+ 2*(8 + usidsz) /* two possible ACE for user */
|
|
+ 2*(8 + gsidsz) /* two possible ACE for group */
|
|
+ 8 + wsidsz /* one ACE for world */
|
|
+ 8 + asidsz /* one ACE for admin */
|
|
+ 8 + ssidsz; /* one ACE for system */
|
|
if (isdir) /* a world denial for directories */
|
|
newattrsz += 8 + wsidsz;
|
|
if (mode & 07000) /* a NULL ACE for special modes */
|
|
newattrsz += 8 + ntfs_sid_size(nullsid);
|
|
newattr = (char*)ntfs_malloc(newattrsz);
|
|
if (newattr) {
|
|
/* build the main header part */
|
|
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr;
|
|
pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
|
|
pnhead->alignment = 0;
|
|
/*
|
|
* The flag SE_DACL_PROTECTED prevents the ACL
|
|
* to be changed in an inheritance after creation
|
|
*/
|
|
pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
|
|
| SE_SELF_RELATIVE;
|
|
/*
|
|
* Windows prefers ACL first, do the same to
|
|
* get the same hash value and avoid duplication
|
|
*/
|
|
/* build permissions */
|
|
aclsz = buildacls(newattr,
|
|
sizeof(SECURITY_DESCRIPTOR_RELATIVE),
|
|
mode, isdir, usid, gsid);
|
|
if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz + gsidsz) <= newattrsz) {
|
|
/* append usid and gsid */
|
|
memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz], usid, usidsz);
|
|
memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz], gsid, gsidsz);
|
|
/* positions of ACL, USID and GSID into header */
|
|
pnhead->owner =
|
|
cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz);
|
|
pnhead->group =
|
|
cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
|
|
+ aclsz + usidsz);
|
|
pnhead->sacl = const_cpu_to_le32(0);
|
|
pnhead->dacl =
|
|
const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
|
|
} else {
|
|
/* hope error was detected before overflowing */
|
|
free(newattr);
|
|
newattr = (char*)NULL;
|
|
ntfs_log_error("Security descriptor is longer than expected\n");
|
|
errno = EIO;
|
|
}
|
|
} else
|
|
errno = ENOMEM;
|
|
return (newattr);
|
|
}
|
|
|
|
/*
|
|
* Create a mode_t permission set
|
|
* from owner, group and world grants as represented in ACEs
|
|
*/
|
|
|
|
static int merge_permissions(BOOL isdir,
|
|
le32 owner, le32 group, le32 world, le32 special)
|
|
|
|
{
|
|
int perm;
|
|
|
|
perm = 0;
|
|
/* build owner permission */
|
|
if (owner) {
|
|
if (isdir) {
|
|
/* exec if any of list, traverse */
|
|
if (owner & DIR_GEXEC)
|
|
perm |= S_IXUSR;
|
|
/* write if any of addfile, adddir, delchild */
|
|
if (owner & DIR_GWRITE)
|
|
perm |= S_IWUSR;
|
|
/* read if any of list */
|
|
if (owner & DIR_GREAD)
|
|
perm |= S_IRUSR;
|
|
} else {
|
|
/* exec if execute or generic execute */
|
|
if (owner & FILE_GEXEC)
|
|
perm |= S_IXUSR;
|
|
/* write if any of writedata or generic write */
|
|
if (owner & FILE_GWRITE)
|
|
perm |= S_IWUSR;
|
|
/* read if any of readdata or generic read */
|
|
if (owner & FILE_GREAD)
|
|
perm |= S_IRUSR;
|
|
}
|
|
}
|
|
/* build group permission */
|
|
if (group) {
|
|
if (isdir) {
|
|
/* exec if any of list, traverse */
|
|
if (group & DIR_GEXEC)
|
|
perm |= S_IXGRP;
|
|
/* write if any of addfile, adddir, delchild */
|
|
if (group & DIR_GWRITE)
|
|
perm |= S_IWGRP;
|
|
/* read if any of list */
|
|
if (group & DIR_GREAD)
|
|
perm |= S_IRGRP;
|
|
} else {
|
|
/* exec if execute */
|
|
if (group & FILE_GEXEC)
|
|
perm |= S_IXGRP;
|
|
/* write if any of writedata, appenddata */
|
|
if (group & FILE_GWRITE)
|
|
perm |= S_IWGRP;
|
|
/* read if any of readdata */
|
|
if (group & FILE_GREAD)
|
|
perm |= S_IRGRP;
|
|
}
|
|
}
|
|
/* build world permission */
|
|
if (world) {
|
|
if (isdir) {
|
|
/* exec if any of list, traverse */
|
|
if (world & DIR_GEXEC)
|
|
perm |= S_IXOTH;
|
|
/* write if any of addfile, adddir, delchild */
|
|
if (world & DIR_GWRITE)
|
|
perm |= S_IWOTH;
|
|
/* read if any of list */
|
|
if (world & DIR_GREAD)
|
|
perm |= S_IROTH;
|
|
} else {
|
|
/* exec if execute */
|
|
if (world & FILE_GEXEC)
|
|
perm |= S_IXOTH;
|
|
/* write if any of writedata, appenddata */
|
|
if (world & FILE_GWRITE)
|
|
perm |= S_IWOTH;
|
|
/* read if any of readdata */
|
|
if (world & FILE_GREAD)
|
|
perm |= S_IROTH;
|
|
}
|
|
}
|
|
/* build special permission flags */
|
|
if (special) {
|
|
if (special & FILE_APPEND_DATA)
|
|
perm |= S_ISUID;
|
|
if (special & FILE_WRITE_DATA)
|
|
perm |= S_ISGID;
|
|
if (special & FILE_READ_DATA)
|
|
perm |= S_ISVTX;
|
|
}
|
|
return (perm);
|
|
}
|
|
|
|
#if POSIXACLS
|
|
|
|
/*
|
|
* Normalize a Posix ACL either from a sorted raw set of
|
|
* access ACEs or default ACEs
|
|
* (standard case : different owner, group and administrator)
|
|
*/
|
|
|
|
static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc,
|
|
BOOL groupowns, int start, int count, int target)
|
|
{
|
|
int j,k;
|
|
s32 id;
|
|
u16 tag;
|
|
u16 tagsset;
|
|
struct POSIX_ACE *pxace;
|
|
mode_t grantgrps;
|
|
mode_t grantwrld;
|
|
mode_t denywrld;
|
|
mode_t allow;
|
|
mode_t deny;
|
|
mode_t perms;
|
|
mode_t mode;
|
|
|
|
mode = 0;
|
|
tagsset = 0;
|
|
/*
|
|
* Determine what is granted to some group or world
|
|
* Also get denials to world which are meant to prevent
|
|
* execution flags to be inherited by plain files
|
|
*/
|
|
pxace = posix_desc->acl.ace;
|
|
grantgrps = 0;
|
|
grantwrld = 0;
|
|
denywrld = 0;
|
|
for (j=start; j<(start + count); j++) {
|
|
if (pxace[j].perms & POSIX_PERM_DENIAL) {
|
|
/* deny world exec unless for default */
|
|
if ((pxace[j].tag == POSIX_ACL_OTHER)
|
|
&& !start)
|
|
denywrld = pxace[j].perms;
|
|
} else {
|
|
switch (pxace[j].tag) {
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
grantgrps |= pxace[j].perms;
|
|
break;
|
|
case POSIX_ACL_GROUP :
|
|
if (pxace[j].id)
|
|
grantgrps |= pxace[j].perms;
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
grantwrld = pxace[j].perms;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Collect groups of ACEs related to the same id
|
|
* and determine what is granted and what is denied.
|
|
* It is important the ACEs have been sorted
|
|
*/
|
|
j = start;
|
|
k = target;
|
|
while (j < (start + count)) {
|
|
tag = pxace[j].tag;
|
|
id = pxace[j].id;
|
|
if (pxace[j].perms & POSIX_PERM_DENIAL) {
|
|
deny = pxace[j].perms | denywrld;
|
|
allow = 0;
|
|
} else {
|
|
deny = denywrld;
|
|
allow = pxace[j].perms;
|
|
}
|
|
j++;
|
|
while ((j < (start + count))
|
|
&& (pxace[j].tag == tag)
|
|
&& (pxace[j].id == id)) {
|
|
if (pxace[j].perms & POSIX_PERM_DENIAL)
|
|
deny |= pxace[j].perms;
|
|
else
|
|
allow |= pxace[j].perms;
|
|
j++;
|
|
}
|
|
/*
|
|
* Build the permissions equivalent to grants and denials
|
|
*/
|
|
if (groupowns) {
|
|
if (tag == POSIX_ACL_MASK)
|
|
perms = ~deny;
|
|
else
|
|
perms = allow & ~deny;
|
|
} else
|
|
switch (tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
perms = (allow | grantgrps | grantwrld) & ~deny;
|
|
break;
|
|
case POSIX_ACL_USER :
|
|
if (id)
|
|
perms = (allow | grantgrps | grantwrld)
|
|
& ~deny;
|
|
else
|
|
perms = allow;
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
perms = (allow | grantwrld) & ~deny;
|
|
break;
|
|
case POSIX_ACL_GROUP :
|
|
if (id)
|
|
perms = (allow | grantwrld) & ~deny;
|
|
else
|
|
perms = allow;
|
|
break;
|
|
case POSIX_ACL_MASK :
|
|
perms = ~deny;
|
|
break;
|
|
default :
|
|
perms = allow & ~deny;
|
|
break;
|
|
}
|
|
/*
|
|
* Store into a Posix ACE
|
|
*/
|
|
if (tag != POSIX_ACL_SPECIAL) {
|
|
pxace[k].tag = tag;
|
|
pxace[k].id = id;
|
|
pxace[k].perms = perms
|
|
& (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
|
|
tagsset |= tag;
|
|
k++;
|
|
}
|
|
switch (tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
mode |= ((perms & 7) << 6);
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
case POSIX_ACL_MASK :
|
|
mode = (mode & 07707) | ((perms & 7) << 3);
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
mode |= perms & 7;
|
|
break;
|
|
case POSIX_ACL_SPECIAL :
|
|
mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID));
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
if (!start) { /* not satisfactory */
|
|
posix_desc->mode = mode;
|
|
posix_desc->tagsset = tagsset;
|
|
}
|
|
return (k - target);
|
|
}
|
|
|
|
#endif /* POSIXACLS */
|
|
|
|
/*
|
|
* Interpret an ACL and extract meaningful grants
|
|
* (standard case : different owner, group and administrator)
|
|
*/
|
|
|
|
static int build_std_permissions(const char *securattr,
|
|
const SID *usid, const SID *gsid, BOOL isdir)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
int offdacl;
|
|
int offace;
|
|
int acecnt;
|
|
int nace;
|
|
BOOL noown;
|
|
le32 special;
|
|
le32 allowown, allowgrp, allowall;
|
|
le32 denyown, denygrp, denyall;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
special = const_cpu_to_le32(0);
|
|
allowown = allowgrp = allowall = const_cpu_to_le32(0);
|
|
denyown = denygrp = denyall = const_cpu_to_le32(0);
|
|
noown = TRUE;
|
|
if (offdacl) {
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
} else {
|
|
acecnt = 0;
|
|
offace = 0;
|
|
}
|
|
for (nace = 0; nace < acecnt; nace++) {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if (!(pace->flags & INHERIT_ONLY_ACE)) {
|
|
if (ntfs_same_sid(usid, &pace->sid)
|
|
|| ntfs_same_sid(ownersid, &pace->sid)) {
|
|
noown = FALSE;
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
allowown |= pace->mask;
|
|
else if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denyown |= pace->mask;
|
|
} else
|
|
if (ntfs_same_sid(gsid, &pace->sid)
|
|
&& !(pace->mask & WRITE_OWNER)) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
allowgrp |= pace->mask;
|
|
else if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denygrp |= pace->mask;
|
|
} else
|
|
if (is_world_sid((const SID*)&pace->sid)) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
allowall |= pace->mask;
|
|
else
|
|
if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denyall |= pace->mask;
|
|
} else
|
|
if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE))
|
|
special |= pace->mask;
|
|
}
|
|
offace += le16_to_cpu(pace->size);
|
|
}
|
|
/*
|
|
* No indication about owner's rights : grant basic rights
|
|
* This happens for files created by Windows in directories
|
|
* created by Linux and owned by root, because Windows
|
|
* merges the admin ACEs
|
|
*/
|
|
if (noown)
|
|
allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE);
|
|
/*
|
|
* Add to owner rights granted to group or world
|
|
* unless denied personaly, and add to group rights
|
|
* granted to world unless denied specifically
|
|
*/
|
|
allowown |= (allowgrp | allowall);
|
|
allowgrp |= allowall;
|
|
return (merge_permissions(isdir,
|
|
allowown & ~(denyown | denyall),
|
|
allowgrp & ~(denygrp | denyall),
|
|
allowall & ~denyall,
|
|
special));
|
|
}
|
|
|
|
/*
|
|
* Interpret an ACL and extract meaningful grants
|
|
* (special case : owner and group are the same,
|
|
* and not administrator)
|
|
*/
|
|
|
|
static int build_owngrp_permissions(const char *securattr,
|
|
const SID *usid, BOOL isdir)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
int offdacl;
|
|
int offace;
|
|
int acecnt;
|
|
int nace;
|
|
le32 special;
|
|
BOOL grppresent;
|
|
BOOL ownpresent;
|
|
le32 allowown, allowgrp, allowall;
|
|
le32 denyown, denygrp, denyall;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
special = const_cpu_to_le32(0);
|
|
allowown = allowgrp = allowall = const_cpu_to_le32(0);
|
|
denyown = denygrp = denyall = const_cpu_to_le32(0);
|
|
ownpresent = FALSE;
|
|
grppresent = FALSE;
|
|
if (offdacl) {
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
} else {
|
|
acecnt = 0;
|
|
offace = 0;
|
|
}
|
|
for (nace = 0; nace < acecnt; nace++) {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if (!(pace->flags & INHERIT_ONLY_ACE)) {
|
|
if ((ntfs_same_sid(usid, &pace->sid)
|
|
|| ntfs_same_sid(ownersid, &pace->sid))
|
|
&& (pace->mask & WRITE_OWNER)) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
|
|
allowown |= pace->mask;
|
|
ownpresent = TRUE;
|
|
}
|
|
} else
|
|
if (ntfs_same_sid(usid, &pace->sid)
|
|
&& (!(pace->mask & WRITE_OWNER))) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
|
|
allowgrp |= pace->mask;
|
|
grppresent = TRUE;
|
|
}
|
|
} else
|
|
if (is_world_sid((const SID*)&pace->sid)) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
allowall |= pace->mask;
|
|
else
|
|
if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denyall |= pace->mask;
|
|
} else
|
|
if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE))
|
|
special |= pace->mask;
|
|
}
|
|
offace += le16_to_cpu(pace->size);
|
|
}
|
|
if (!ownpresent)
|
|
allowown = allowall;
|
|
if (!grppresent)
|
|
allowgrp = allowall;
|
|
return (merge_permissions(isdir,
|
|
allowown & ~(denyown | denyall),
|
|
allowgrp & ~(denygrp | denyall),
|
|
allowall & ~denyall,
|
|
special));
|
|
}
|
|
|
|
#if POSIXACLS
|
|
|
|
/*
|
|
* Normalize a Posix ACL either from a sorted raw set of
|
|
* access ACEs or default ACEs
|
|
* (special case : owner or/and group is administrator)
|
|
*/
|
|
|
|
static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc,
|
|
int start, int count, int target)
|
|
{
|
|
int j,k;
|
|
s32 id;
|
|
u16 tag;
|
|
u16 tagsset;
|
|
struct POSIX_ACE *pxace;
|
|
mode_t denywrld;
|
|
mode_t allow;
|
|
mode_t deny;
|
|
mode_t perms;
|
|
mode_t mode;
|
|
|
|
mode = 0;
|
|
pxace = posix_desc->acl.ace;
|
|
tagsset = 0;
|
|
denywrld = 0;
|
|
/*
|
|
* Get denials to world which are meant to prevent
|
|
* execution flags to be inherited by plain files
|
|
*/
|
|
for (j=start; j<(start + count); j++) {
|
|
if (pxace[j].perms & POSIX_PERM_DENIAL) {
|
|
/* deny world exec not for default */
|
|
if ((pxace[j].tag == POSIX_ACL_OTHER)
|
|
&& !start)
|
|
denywrld = pxace[j].perms;
|
|
}
|
|
}
|
|
/*
|
|
* Collect groups of ACEs related to the same id
|
|
* and determine what is granted (denials are ignored)
|
|
* It is important the ACEs have been sorted
|
|
*/
|
|
j = start;
|
|
k = target;
|
|
deny = 0;
|
|
while (j < (start + count)) {
|
|
allow = 0;
|
|
tag = pxace[j].tag;
|
|
id = pxace[j].id;
|
|
if (tag == POSIX_ACL_MASK) {
|
|
deny = pxace[j].perms;
|
|
j++;
|
|
while ((j < (start + count))
|
|
&& (pxace[j].tag == POSIX_ACL_MASK))
|
|
j++;
|
|
} else {
|
|
if (!(pxace[j].perms & POSIX_PERM_DENIAL))
|
|
allow = pxace[j].perms;
|
|
j++;
|
|
while ((j < (start + count))
|
|
&& (pxace[j].tag == tag)
|
|
&& (pxace[j].id == id)) {
|
|
if (!(pxace[j].perms & POSIX_PERM_DENIAL))
|
|
allow |= pxace[j].perms;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store the grants into a Posix ACE
|
|
*/
|
|
if (tag == POSIX_ACL_MASK)
|
|
perms = ~deny;
|
|
else
|
|
perms = allow & ~denywrld;
|
|
if (tag != POSIX_ACL_SPECIAL) {
|
|
pxace[k].tag = tag;
|
|
pxace[k].id = id;
|
|
pxace[k].perms = perms
|
|
& (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
|
|
tagsset |= tag;
|
|
k++;
|
|
}
|
|
switch (tag) {
|
|
case POSIX_ACL_USER_OBJ :
|
|
mode |= ((perms & 7) << 6);
|
|
break;
|
|
case POSIX_ACL_GROUP_OBJ :
|
|
case POSIX_ACL_MASK :
|
|
mode = (mode & 07707) | ((perms & 7) << 3);
|
|
break;
|
|
case POSIX_ACL_OTHER :
|
|
mode |= perms & 7;
|
|
break;
|
|
case POSIX_ACL_SPECIAL :
|
|
mode |= perms & (S_ISVTX | S_ISUID | S_ISGID);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
if (!start) { /* not satisfactory */
|
|
posix_desc->mode = mode;
|
|
posix_desc->tagsset = tagsset;
|
|
}
|
|
return (k - target);
|
|
}
|
|
|
|
#endif /* POSIXACLS */
|
|
|
|
/*
|
|
* Interpret an ACL and extract meaningful grants
|
|
* (special case : owner or/and group is administrator)
|
|
*/
|
|
|
|
|
|
static int build_ownadmin_permissions(const char *securattr,
|
|
const SID *usid, const SID *gsid, BOOL isdir)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
int offdacl;
|
|
int offace;
|
|
int acecnt;
|
|
int nace;
|
|
BOOL firstapply;
|
|
int isforeign;
|
|
le32 special;
|
|
le32 allowown, allowgrp, allowall;
|
|
le32 denyown, denygrp, denyall;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
special = const_cpu_to_le32(0);
|
|
allowown = allowgrp = allowall = const_cpu_to_le32(0);
|
|
denyown = denygrp = denyall = const_cpu_to_le32(0);
|
|
if (offdacl) {
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
} else {
|
|
acecnt = 0;
|
|
offace = 0;
|
|
}
|
|
firstapply = TRUE;
|
|
isforeign = 3;
|
|
for (nace = 0; nace < acecnt; nace++) {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if (!(pace->flags & INHERIT_ONLY_ACE)
|
|
&& !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) {
|
|
if ((ntfs_same_sid(usid, &pace->sid)
|
|
|| ntfs_same_sid(ownersid, &pace->sid))
|
|
&& (((pace->mask & WRITE_OWNER) && firstapply))) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
|
|
allowown |= pace->mask;
|
|
isforeign &= ~1;
|
|
} else
|
|
if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denyown |= pace->mask;
|
|
} else
|
|
if (ntfs_same_sid(gsid, &pace->sid)
|
|
&& (!(pace->mask & WRITE_OWNER))) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
|
|
allowgrp |= pace->mask;
|
|
isforeign &= ~2;
|
|
} else
|
|
if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denygrp |= pace->mask;
|
|
} else if (is_world_sid((const SID*)&pace->sid)) {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
allowall |= pace->mask;
|
|
else
|
|
if (pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
denyall |= pace->mask;
|
|
}
|
|
firstapply = FALSE;
|
|
} else
|
|
if (!(pace->flags & INHERIT_ONLY_ACE))
|
|
if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE))
|
|
special |= pace->mask;
|
|
offace += le16_to_cpu(pace->size);
|
|
}
|
|
if (isforeign) {
|
|
allowown |= (allowgrp | allowall);
|
|
allowgrp |= allowall;
|
|
}
|
|
return (merge_permissions(isdir,
|
|
allowown & ~(denyown | denyall),
|
|
allowgrp & ~(denygrp | denyall),
|
|
allowall & ~denyall,
|
|
special));
|
|
}
|
|
|
|
#if OWNERFROMACL
|
|
|
|
/*
|
|
* Define the owner of a file as the first user allowed
|
|
* to change the owner, instead of the user defined as owner.
|
|
*
|
|
* This produces better approximations for files written by a
|
|
* Windows user in an inheritable directory owned by another user,
|
|
* as the access rights are inheritable but the ownership is not.
|
|
*
|
|
* An important case is the directories "Documents and Settings/user"
|
|
* which the users must have access to, though Windows considers them
|
|
* as owned by administrator.
|
|
*/
|
|
|
|
const SID *ntfs_acl_owner(const char *securattr)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const SID *usid;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
int offdacl;
|
|
int offace;
|
|
int acecnt;
|
|
int nace;
|
|
BOOL found;
|
|
|
|
found = FALSE;
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
if (offdacl) {
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
nace = 0;
|
|
do {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if ((pace->mask & WRITE_OWNER)
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& ntfs_is_user_sid(&pace->sid))
|
|
found = TRUE;
|
|
offace += le16_to_cpu(pace->size);
|
|
} while (!found && (++nace < acecnt));
|
|
}
|
|
if (found)
|
|
usid = &pace->sid;
|
|
else
|
|
usid = (const SID*)&securattr[le32_to_cpu(phead->owner)];
|
|
return (usid);
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* Special case for files owned by administrator with full
|
|
* access granted to a mapped user : consider this user as the tenant
|
|
* of the file.
|
|
*
|
|
* This situation cannot be represented with Linux concepts and can
|
|
* only be found for files or directories created by Windows.
|
|
* Typical situation : directory "Documents and Settings/user" which
|
|
* is on the path to user's files and must be given access to user
|
|
* only.
|
|
*
|
|
* Check file is owned by administrator and no user has rights before
|
|
* calling.
|
|
* Returns the uid of tenant or zero if none
|
|
*/
|
|
|
|
|
|
static uid_t find_tenant(struct MAPPING *const mapping[],
|
|
const char *securattr)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
int offdacl;
|
|
int offace;
|
|
int acecnt;
|
|
int nace;
|
|
uid_t tid;
|
|
uid_t xid;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
tid = 0;
|
|
if (offdacl) {
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
} else
|
|
acecnt = 0;
|
|
for (nace = 0; nace < acecnt; nace++) {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if ((pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& (pace->mask & DIR_WRITE)) {
|
|
xid = ntfs_find_user(mapping[MAPUSERS], &pace->sid);
|
|
if (xid) tid = xid;
|
|
}
|
|
offace += le16_to_cpu(pace->size);
|
|
}
|
|
return (tid);
|
|
}
|
|
|
|
#endif /* OWNERFROMACL */
|
|
|
|
#if POSIXACLS
|
|
|
|
/*
|
|
* Build Posix permissions from an ACL
|
|
* returns a pointer to the requested permissions
|
|
* or a null pointer (with errno set) if there is a problem
|
|
*
|
|
* If the NTFS ACL was created according to our rules, the retrieved
|
|
* Posix ACL should be the exact ACL which was set. However if
|
|
* the NTFS ACL was built by a different tool, the result could
|
|
* be a a poor approximation of what was expected
|
|
*/
|
|
|
|
struct POSIX_SECURITY *ntfs_build_permissions_posix(
|
|
struct MAPPING *const mapping[],
|
|
const char *securattr,
|
|
const SID *usid, const SID *gsid, BOOL isdir)
|
|
{
|
|
const SECURITY_DESCRIPTOR_RELATIVE *phead;
|
|
struct POSIX_SECURITY *pxdesc;
|
|
const ACL *pacl;
|
|
const ACCESS_ALLOWED_ACE *pace;
|
|
struct POSIX_ACE *pxace;
|
|
struct {
|
|
uid_t prevuid;
|
|
gid_t prevgid;
|
|
int groupmasks;
|
|
s16 tagsset;
|
|
BOOL gotowner;
|
|
BOOL gotownermask;
|
|
BOOL gotgroup;
|
|
mode_t permswrld;
|
|
} ctx[2], *pctx;
|
|
int offdacl;
|
|
int offace;
|
|
int alloccnt;
|
|
int acecnt;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
int i,j;
|
|
int k,l;
|
|
BOOL ignore;
|
|
BOOL adminowns;
|
|
BOOL groupowns;
|
|
BOOL firstinh;
|
|
BOOL genericinh;
|
|
|
|
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
|
|
offdacl = le32_to_cpu(phead->dacl);
|
|
if (offdacl) {
|
|
pacl = (const ACL*)&securattr[offdacl];
|
|
acecnt = le16_to_cpu(pacl->ace_count);
|
|
offace = offdacl + sizeof(ACL);
|
|
} else {
|
|
acecnt = 0;
|
|
offace = 0;
|
|
}
|
|
adminowns = FALSE;
|
|
groupowns = ntfs_same_sid(gsid,usid);
|
|
firstinh = FALSE;
|
|
genericinh = FALSE;
|
|
/*
|
|
* Build a raw posix security descriptor
|
|
* by just translating permissions and ids
|
|
* Add 2 to the count of ACE to be able to insert
|
|
* a group ACE later in access and default ACLs
|
|
* and add 2 more to be able to insert ACEs for owner
|
|
* and 2 more for other
|
|
*/
|
|
alloccnt = acecnt + 6;
|
|
pxdesc = (struct POSIX_SECURITY*)malloc(
|
|
sizeof(struct POSIX_SECURITY)
|
|
+ alloccnt*sizeof(struct POSIX_ACE));
|
|
k = 0;
|
|
l = alloccnt;
|
|
for (i=0; i<2; i++) {
|
|
pctx = &ctx[i];
|
|
pctx->permswrld = 0;
|
|
pctx->prevuid = -1;
|
|
pctx->prevgid = -1;
|
|
pctx->groupmasks = 0;
|
|
pctx->tagsset = 0;
|
|
pctx->gotowner = FALSE;
|
|
pctx->gotgroup = FALSE;
|
|
pctx->gotownermask = FALSE;
|
|
}
|
|
for (j=0; j<acecnt; j++) {
|
|
pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
|
|
if (pace->flags & INHERIT_ONLY_ACE) {
|
|
pxace = &pxdesc->acl.ace[l - 1];
|
|
pctx = &ctx[1];
|
|
} else {
|
|
pxace = &pxdesc->acl.ace[k];
|
|
pctx = &ctx[0];
|
|
}
|
|
ignore = FALSE;
|
|
/*
|
|
* grants for root as a designated user or group
|
|
*/
|
|
if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
&& ntfs_same_sid(&pace->sid, adminsid)) {
|
|
pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER);
|
|
pxace->id = 0;
|
|
if ((pace->mask & (GENERIC_ALL | WRITE_OWNER))
|
|
&& (pace->flags & INHERIT_ONLY_ACE))
|
|
ignore = genericinh = TRUE;
|
|
} else
|
|
if (ntfs_same_sid(usid, &pace->sid)) {
|
|
pxace->id = -1;
|
|
/*
|
|
* Owner has no write-owner right :
|
|
* a group was defined same as owner
|
|
* or admin was owner or group :
|
|
* denials are meant to owner
|
|
* and grants are meant to group
|
|
*/
|
|
if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL))
|
|
&& (pace->type == ACCESS_ALLOWED_ACE_TYPE)) {
|
|
if (ntfs_same_sid(gsid,usid)) {
|
|
pxace->tag = POSIX_ACL_GROUP_OBJ;
|
|
pxace->id = -1;
|
|
} else {
|
|
if (ntfs_same_sid(&pace->sid,usid))
|
|
groupowns = TRUE;
|
|
gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
|
|
if (gid) {
|
|
pxace->tag = POSIX_ACL_GROUP;
|
|
pxace->id = gid;
|
|
pctx->prevgid = gid;
|
|
} else {
|
|
uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
|
|
if (uid) {
|
|
pxace->tag = POSIX_ACL_USER;
|
|
pxace->id = uid;
|
|
} else
|
|
ignore = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* when group owns, late denials for owner
|
|
* mean group mask
|
|
*/
|
|
if ((pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
&& (pace->mask & WRITE_OWNER)) {
|
|
pxace->tag = POSIX_ACL_MASK;
|
|
pctx->gotownermask = TRUE;
|
|
if (pctx->gotowner)
|
|
pctx->groupmasks++;
|
|
} else {
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
pctx->gotowner = TRUE;
|
|
if (pctx->gotownermask && !pctx->gotowner) {
|
|
uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
|
|
pxace->id = uid;
|
|
pxace->tag = POSIX_ACL_USER;
|
|
} else
|
|
pxace->tag = POSIX_ACL_USER_OBJ;
|
|
/* system ignored, and admin */
|
|
/* ignored at first position */
|
|
if (pace->flags & INHERIT_ONLY_ACE) {
|
|
if ((firstinh && ntfs_same_sid(&pace->sid,adminsid))
|
|
|| ntfs_same_sid(&pace->sid,systemsid))
|
|
ignore = TRUE;
|
|
if (!firstinh) {
|
|
firstinh = TRUE;
|
|
}
|
|
} else {
|
|
if ((adminowns && ntfs_same_sid(&pace->sid,adminsid))
|
|
|| ntfs_same_sid(&pace->sid,systemsid))
|
|
ignore = TRUE;
|
|
if (ntfs_same_sid(usid,adminsid))
|
|
adminowns = TRUE;
|
|
}
|
|
}
|
|
}
|
|
} else if (ntfs_same_sid(gsid, &pace->sid)) {
|
|
if ((pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
&& (pace->mask & WRITE_OWNER)) {
|
|
pxace->tag = POSIX_ACL_MASK;
|
|
pxace->id = -1;
|
|
if (pctx->gotowner)
|
|
pctx->groupmasks++;
|
|
} else {
|
|
if (pctx->gotgroup || (pctx->groupmasks > 1)) {
|
|
gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
|
|
if (gid) {
|
|
pxace->id = gid;
|
|
pxace->tag = POSIX_ACL_GROUP;
|
|
pctx->prevgid = gid;
|
|
} else
|
|
ignore = TRUE;
|
|
} else {
|
|
pxace->id = -1;
|
|
pxace->tag = POSIX_ACL_GROUP_OBJ;
|
|
if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
pctx->gotgroup = TRUE;
|
|
}
|
|
|
|
if (ntfs_same_sid(gsid,adminsid)
|
|
|| ntfs_same_sid(gsid,systemsid)) {
|
|
if (pace->mask & (WRITE_OWNER | GENERIC_ALL))
|
|
ignore = TRUE;
|
|
if (ntfs_same_sid(gsid,adminsid))
|
|
adminowns = TRUE;
|
|
else
|
|
genericinh = ignore;
|
|
}
|
|
}
|
|
} else if (is_world_sid((const SID*)&pace->sid)) {
|
|
pxace->id = -1;
|
|
pxace->tag = POSIX_ACL_OTHER;
|
|
if ((pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
&& (pace->flags & INHERIT_ONLY_ACE))
|
|
ignore = TRUE;
|
|
} else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) {
|
|
pxace->id = -1;
|
|
pxace->tag = POSIX_ACL_SPECIAL;
|
|
} else {
|
|
uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
|
|
if (uid) {
|
|
if ((pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
&& (pace->mask & WRITE_OWNER)
|
|
&& (pctx->prevuid != uid)) {
|
|
pxace->id = -1;
|
|
pxace->tag = POSIX_ACL_MASK;
|
|
} else {
|
|
pxace->id = uid;
|
|
pxace->tag = POSIX_ACL_USER;
|
|
}
|
|
pctx->prevuid = uid;
|
|
} else {
|
|
gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
|
|
if (gid) {
|
|
if ((pace->type == ACCESS_DENIED_ACE_TYPE)
|
|
&& (pace->mask & WRITE_OWNER)
|
|
&& (pctx->prevgid != gid)) {
|
|
pxace->tag = POSIX_ACL_MASK;
|
|
pctx->groupmasks++;
|
|
} else {
|
|
pxace->tag = POSIX_ACL_GROUP;
|
|
}
|
|
pxace->id = gid;
|
|
pctx->prevgid = gid;
|
|
} else {
|
|
/*
|
|
* do not grant rights to unknown
|
|
* people and do not define root as a
|
|
* designated user or group
|
|
*/
|
|
ignore = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (((pace->type == ACCESS_ALLOWED_ACE_TYPE)
|
|
|| (pace->type == ACCESS_DENIED_ACE_TYPE))
|
|
&& !ignore) {
|
|
pxace->perms = 0;
|
|
/* specific decoding for vtx/uid/gid */
|
|
if (pxace->tag == POSIX_ACL_SPECIAL) {
|
|
if (pace->mask & FILE_APPEND_DATA)
|
|
pxace->perms |= S_ISUID;
|
|
if (pace->mask & FILE_WRITE_DATA)
|
|
pxace->perms |= S_ISGID;
|
|
if (pace->mask & FILE_READ_DATA)
|
|
pxace->perms |= S_ISVTX;
|
|
} else
|
|
if (isdir) {
|
|
if (pace->mask & DIR_GEXEC)
|
|
pxace->perms |= POSIX_PERM_X;
|
|
if (pace->mask & DIR_GWRITE)
|
|
pxace->perms |= POSIX_PERM_W;
|
|
if (pace->mask & DIR_GREAD)
|
|
pxace->perms |= POSIX_PERM_R;
|
|
if ((pace->mask & GENERIC_ALL)
|
|
&& (pace->flags & INHERIT_ONLY_ACE))
|
|
pxace->perms |= POSIX_PERM_X
|
|
| POSIX_PERM_W
|
|
| POSIX_PERM_R;
|
|
} else {
|
|
if (pace->mask & FILE_GEXEC)
|
|
pxace->perms |= POSIX_PERM_X;
|
|
if (pace->mask & FILE_GWRITE)
|
|
pxace->perms |= POSIX_PERM_W;
|
|
if (pace->mask & FILE_GREAD)
|
|
pxace->perms |= POSIX_PERM_R;
|
|
}
|
|
|
|
if (pace->type != ACCESS_ALLOWED_ACE_TYPE)
|
|
pxace->perms |= POSIX_PERM_DENIAL;
|
|
else
|
|
if (pxace->tag == POSIX_ACL_OTHER)
|
|
pctx->permswrld |= pxace->perms;
|
|
pctx->tagsset |= pxace->tag;
|
|
if (pace->flags & INHERIT_ONLY_ACE) {
|
|
l--;
|
|
} else {
|
|
k++;
|
|
}
|
|
}
|
|
offace += le16_to_cpu(pace->size);
|
|
}
|
|
/*
|
|
* Create world perms if none (both lists)
|
|
*/
|
|
for (i=0; i<2; i++)
|
|
if ((genericinh || !i)
|
|
&& !(ctx[i].tagsset & POSIX_ACL_OTHER)) {
|
|
if (i)
|
|
pxace = &pxdesc->acl.ace[--l];
|
|
else
|
|
pxace = &pxdesc->acl.ace[k++];
|
|
pxace->tag = POSIX_ACL_OTHER;
|
|
pxace->id = -1;
|
|
pxace->perms = 0;
|
|
ctx[i].tagsset |= POSIX_ACL_OTHER;
|
|
ctx[i].permswrld = 0;
|
|
}
|
|
/*
|
|
* Set basic owner perms if none (both lists)
|
|
* This happens for files created by Windows in directories
|
|
* created by Linux and owned by root, because Windows
|
|
* merges the admin ACEs
|
|
*/
|
|
for (i=0; i<2; i++)
|
|
if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ)
|
|
&& (ctx[i].tagsset & POSIX_ACL_OTHER)) {
|
|
if (i)
|
|
pxace = &pxdesc->acl.ace[--l];
|
|
else
|
|
pxace = &pxdesc->acl.ace[k++];
|
|
pxace->tag = POSIX_ACL_USER_OBJ;
|
|
pxace->id = -1;
|
|
pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X;
|
|
ctx[i].tagsset |= POSIX_ACL_USER_OBJ;
|
|
}
|
|
/*
|
|
* Duplicate world perms as group_obj perms if none
|
|
*/
|
|
for (i=0; i<2; i++)
|
|
if ((ctx[i].tagsset & POSIX_ACL_OTHER)
|
|
&& !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) {
|
|
if (i)
|
|
pxace = &pxdesc->acl.ace[--l];
|
|
else
|
|
pxace = &pxdesc->acl.ace[k++];
|
|
pxace->tag = POSIX_ACL_GROUP_OBJ;
|
|
pxace->id = -1;
|
|
pxace->perms = ctx[i].permswrld;
|
|
ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ;
|
|
}
|
|
/*
|
|
* Also duplicate world perms as group perms if they
|
|
* were converted to mask and not followed by a group entry
|
|
*/
|
|
if (ctx[0].groupmasks) {
|
|
for (j=k-2; j>=0; j--) {
|
|
if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
|
|
&& (pxdesc->acl.ace[j].id != -1)
|
|
&& ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP)
|
|
|| (pxdesc->acl.ace[j+1].id
|
|
!= pxdesc->acl.ace[j].id))) {
|
|
pxace = &pxdesc->acl.ace[k];
|
|
pxace->tag = POSIX_ACL_GROUP;
|
|
pxace->id = pxdesc->acl.ace[j].id;
|
|
pxace->perms = ctx[0].permswrld;
|
|
ctx[0].tagsset |= POSIX_ACL_GROUP;
|
|
k++;
|
|
}
|
|
if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
|
|
pxdesc->acl.ace[j].id = -1;
|
|
}
|
|
}
|
|
if (ctx[1].groupmasks) {
|
|
for (j=l; j<(alloccnt-1); j++) {
|
|
if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
|
|
&& (pxdesc->acl.ace[j].id != -1)
|
|
&& ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP)
|
|
|| (pxdesc->acl.ace[j+1].id
|
|
!= pxdesc->acl.ace[j].id))) {
|
|
pxace = &pxdesc->acl.ace[l - 1];
|
|
pxace->tag = POSIX_ACL_GROUP;
|
|
pxace->id = pxdesc->acl.ace[j].id;
|
|
pxace->perms = ctx[1].permswrld;
|
|
ctx[1].tagsset |= POSIX_ACL_GROUP;
|
|
l--;
|
|
}
|
|
if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
|
|
pxdesc->acl.ace[j].id = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert default mask if none present and
|
|
* there are designated users or groups
|
|
* (the space for it has not beed used)
|
|
*/
|
|
for (i=0; i<2; i++)
|
|
if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP))
|
|
&& !(ctx[i].tagsset & POSIX_ACL_MASK)) {
|
|
if (i)
|
|
pxace = &pxdesc->acl.ace[--l];
|
|
else
|
|
pxace = &pxdesc->acl.ace[k++];
|
|
pxace->tag = POSIX_ACL_MASK;
|
|
pxace->id = -1;
|
|
pxace->perms = POSIX_PERM_DENIAL;
|
|
ctx[i].tagsset |= POSIX_ACL_MASK;
|
|
}
|
|
|
|
if (k > l) {
|
|
ntfs_log_error("Posix descriptor is longer than expected\n");
|
|
errno = EIO;
|
|
free(pxdesc);
|
|
pxdesc = (struct POSIX_SECURITY*)NULL;
|
|
} else {
|
|
pxdesc->acccnt = k;
|
|
pxdesc->defcnt = alloccnt - l;
|
|
pxdesc->firstdef = l;
|
|
pxdesc->tagsset = ctx[0].tagsset;
|
|
pxdesc->acl.version = POSIX_VERSION;
|
|
pxdesc->acl.flags = 0;
|
|
pxdesc->acl.filler = 0;
|
|
ntfs_sort_posix(pxdesc);
|
|
if (adminowns) {
|
|
k = norm_ownadmin_permissions_posix(pxdesc,
|
|
0, pxdesc->acccnt, 0);
|
|
pxdesc->acccnt = k;
|
|
l = norm_ownadmin_permissions_posix(pxdesc,
|
|
pxdesc->firstdef, pxdesc->defcnt, k);
|
|
pxdesc->firstdef = k;
|
|
pxdesc->defcnt = l;
|
|
} else {
|
|
k = norm_std_permissions_posix(pxdesc,groupowns,
|
|
0, pxdesc->acccnt, 0);
|
|
pxdesc->acccnt = k;
|
|
l = norm_std_permissions_posix(pxdesc,groupowns,
|
|
pxdesc->firstdef, pxdesc->defcnt, k);
|
|
pxdesc->firstdef = k;
|
|
pxdesc->defcnt = l;
|
|
}
|
|
}
|
|
if (pxdesc && !ntfs_valid_posix(pxdesc)) {
|
|
ntfs_log_error("Invalid Posix descriptor built\n");
|
|
errno = EIO;
|
|
free(pxdesc);
|
|
pxdesc = (struct POSIX_SECURITY*)NULL;
|
|
}
|
|
return (pxdesc);
|
|
}
|
|
|
|
#endif /* POSIXACLS */
|
|
|
|
/*
|
|
* Build unix-style (mode_t) permissions from an ACL
|
|
* returns the requested permissions
|
|
* or a negative result (with errno set) if there is a problem
|
|
*/
|
|
|
|
int ntfs_build_permissions(const char *securattr,
|
|
const SID *usid, const SID *gsid, BOOL isdir)
|
|
{
|
|
int perm;
|
|
BOOL adminowns;
|
|
BOOL groupowns;
|
|
|
|
adminowns = ntfs_same_sid(usid,adminsid)
|
|
|| ntfs_same_sid(gsid,adminsid);
|
|
groupowns = !adminowns && ntfs_same_sid(gsid,usid);
|
|
if (adminowns)
|
|
perm = build_ownadmin_permissions(securattr, usid, gsid, isdir);
|
|
else
|
|
if (groupowns)
|
|
perm = build_owngrp_permissions(securattr, usid, isdir);
|
|
else
|
|
perm = build_std_permissions(securattr, usid, gsid, isdir);
|
|
return (perm);
|
|
}
|
|
|
|
/*
|
|
* The following must be in some library...
|
|
*/
|
|
|
|
static unsigned long atoul(const char *p)
|
|
{ /* must be somewhere ! */
|
|
unsigned long v;
|
|
|
|
v = 0;
|
|
while ((*p >= '0') && (*p <= '9'))
|
|
v = v * 10 + (*p++) - '0';
|
|
return (v);
|
|
}
|
|
|
|
/*
|
|
* Build an internal representation of a SID
|
|
* Returns a copy in allocated memory if it succeeds
|
|
* The SID is checked to be a valid user one.
|
|
*/
|
|
|
|
static SID *encodesid(const char *sidstr)
|
|
{
|
|
SID *sid;
|
|
int cnt;
|
|
BIGSID bigsid;
|
|
SID *bsid;
|
|
u32 auth;
|
|
const char *p;
|
|
|
|
sid = (SID*) NULL;
|
|
if (!strncmp(sidstr, "S-1-", 4)) {
|
|
bsid = (SID*)&bigsid;
|
|
bsid->revision = SID_REVISION;
|
|
p = &sidstr[4];
|
|
auth = atoul(p);
|
|
bsid->identifier_authority.high_part = const_cpu_to_be16(0);
|
|
bsid->identifier_authority.low_part = cpu_to_be32(auth);
|
|
cnt = 0;
|
|
p = strchr(p, '-');
|
|
while (p && (cnt < 8)) {
|
|
p++;
|
|
auth = atoul(p);
|
|
bsid->sub_authority[cnt] = cpu_to_le32(auth);
|
|
p = strchr(p, '-');
|
|
cnt++;
|
|
}
|
|
bsid->sub_authority_count = cnt;
|
|
if ((cnt > 0) && ntfs_valid_sid(bsid)
|
|
&& (ntfs_is_user_sid(bsid) || ntfs_known_group_sid(bsid))) {
|
|
sid = (SID*) ntfs_malloc(4 * cnt + 8);
|
|
if (sid)
|
|
memcpy(sid, bsid, 4 * cnt + 8);
|
|
}
|
|
}
|
|
return (sid);
|
|
}
|
|
|
|
/*
|
|
* Get a single mapping item from buffer
|
|
*
|
|
* Always reads a full line, truncating long lines
|
|
* Refills buffer when exhausted
|
|
* Returns pointer to item, or NULL when there is no more
|
|
*/
|
|
|
|
static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
|
|
off_t *poffs, char *buf, int *psrc, s64 *psize)
|
|
{
|
|
int src;
|
|
int dst;
|
|
char *q;
|
|
char *pu;
|
|
char *pg;
|
|
int gotend;
|
|
struct MAPLIST *item;
|
|
|
|
src = *psrc;
|
|
dst = 0;
|
|
/* allocate and get a full line */
|
|
item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST));
|
|
if (item) {
|
|
do {
|
|
gotend = 0;
|
|
while ((src < *psize)
|
|
&& (buf[src] != '\n')) {
|
|
if (dst < LINESZ)
|
|
item->maptext[dst++] = buf[src];
|
|
src++;
|
|
}
|
|
if (src >= *psize) {
|
|
*poffs += *psize;
|
|
*psize = reader(fileid, buf, (size_t)BUFSZ, *poffs);
|
|
src = 0;
|
|
} else {
|
|
gotend = 1;
|
|
src++;
|
|
item->maptext[dst] = '\0';
|
|
dst = 0;
|
|
}
|
|
} while (*psize && ((item->maptext[0] == '#') || !gotend));
|
|
if (gotend) {
|
|
pu = pg = (char*)NULL;
|
|
/* decompose into uid, gid and sid */
|
|
item->uidstr = item->maptext;
|
|
item->gidstr = strchr(item->uidstr, ':');
|
|
if (item->gidstr) {
|
|
pu = item->gidstr++;
|
|
item->sidstr = strchr(item->gidstr, ':');
|
|
if (item->sidstr) {
|
|
pg = item->sidstr++;
|
|
q = strchr(item->sidstr, ':');
|
|
if (q) *q = 0;
|
|
}
|
|
}
|
|
if (pu && pg)
|
|
*pu = *pg = '\0';
|
|
else {
|
|
ntfs_log_early_error("Bad mapping item \"%s\"\n",
|
|
item->maptext);
|
|
free(item);
|
|
item = (struct MAPLIST*)NULL;
|
|
}
|
|
} else {
|
|
free(item); /* free unused item */
|
|
item = (struct MAPLIST*)NULL;
|
|
}
|
|
}
|
|
*psrc = src;
|
|
return (item);
|
|
}
|
|
|
|
/*
|
|
* Read user mapping file and split into their attribute.
|
|
* Parameters are kept as text in a chained list until logins
|
|
* are converted to uid.
|
|
* Returns the head of list, if any
|
|
*
|
|
* If an absolute path is provided, the mapping file is assumed
|
|
* to be located in another mounted file system, and plain read()
|
|
* are used to get its contents.
|
|
* If a relative path is provided, the mapping file is assumed
|
|
* to be located on the current file system, and internal IO
|
|
* have to be used since we are still mounting and we have not
|
|
* entered the fuse loop yet.
|
|
*/
|
|
|
|
struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid)
|
|
{
|
|
char buf[BUFSZ];
|
|
struct MAPLIST *item;
|
|
struct MAPLIST *firstitem;
|
|
struct MAPLIST *lastitem;
|
|
int src;
|
|
off_t offs;
|
|
s64 size;
|
|
|
|
firstitem = (struct MAPLIST*)NULL;
|
|
lastitem = (struct MAPLIST*)NULL;
|
|
offs = 0;
|
|
size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0);
|
|
if (size > 0) {
|
|
src = 0;
|
|
do {
|
|
item = getmappingitem(reader, fileid, &offs,
|
|
buf, &src, &size);
|
|
if (item) {
|
|
item->next = (struct MAPLIST*)NULL;
|
|
if (lastitem)
|
|
lastitem->next = item;
|
|
else
|
|
firstitem = item;
|
|
lastitem = item;
|
|
}
|
|
} while (item);
|
|
}
|
|
return (firstitem);
|
|
}
|
|
|
|
/*
|
|
* Free memory used to store the user mapping
|
|
* The only purpose is to facilitate the detection of memory leaks
|
|
*/
|
|
|
|
void ntfs_free_mapping(struct MAPPING *mapping[])
|
|
{
|
|
struct MAPPING *user;
|
|
struct MAPPING *group;
|
|
|
|
/* free user mappings */
|
|
while (mapping[MAPUSERS]) {
|
|
user = mapping[MAPUSERS];
|
|
/* do not free SIDs used for group mappings */
|
|
group = mapping[MAPGROUPS];
|
|
while (group && (group->sid != user->sid))
|
|
group = group->next;
|
|
if (!group)
|
|
free(user->sid);
|
|
/* free group list if any */
|
|
if (user->grcnt)
|
|
free(user->groups);
|
|
/* unchain item and free */
|
|
mapping[MAPUSERS] = user->next;
|
|
free(user);
|
|
}
|
|
/* free group mappings */
|
|
while (mapping[MAPGROUPS]) {
|
|
group = mapping[MAPGROUPS];
|
|
free(group->sid);
|
|
/* unchain item and free */
|
|
mapping[MAPGROUPS] = group->next;
|
|
free(group);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Build the user mapping list
|
|
* user identification may be given in symbolic or numeric format
|
|
*
|
|
* ! Note ! : does getpwnam() read /etc/passwd or some other file ?
|
|
* if so there is a possible recursion into fuse if this
|
|
* file is on NTFS, and fuse is not recursion safe.
|
|
*/
|
|
|
|
struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem)
|
|
{
|
|
struct MAPLIST *item;
|
|
struct MAPPING *firstmapping;
|
|
struct MAPPING *lastmapping;
|
|
struct MAPPING *mapping;
|
|
struct passwd *pwd;
|
|
SID *sid;
|
|
int uid;
|
|
|
|
firstmapping = (struct MAPPING*)NULL;
|
|
lastmapping = (struct MAPPING*)NULL;
|
|
for (item = firstitem; item; item = item->next) {
|
|
if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9'))
|
|
uid = atoi(item->uidstr);
|
|
else {
|
|
uid = 0;
|
|
if (item->uidstr[0]) {
|
|
pwd = getpwnam(item->uidstr);
|
|
if (pwd)
|
|
uid = pwd->pw_uid;
|
|
else
|
|
ntfs_log_early_error("Invalid user \"%s\"\n",
|
|
item->uidstr);
|
|
}
|
|
}
|
|
/*
|
|
* Records with no uid and no gid are inserted
|
|
* to define the implicit mapping pattern
|
|
*/
|
|
if (uid
|
|
|| (!item->uidstr[0] && !item->gidstr[0])) {
|
|
sid = encodesid(item->sidstr);
|
|
if (sid && ntfs_known_group_sid(sid)) {
|
|
ntfs_log_error("Bad user SID %s\n",
|
|
item->sidstr);
|
|
free(sid);
|
|
sid = (SID*)NULL;
|
|
}
|
|
if (sid && !item->uidstr[0] && !item->gidstr[0]
|
|
&& !ntfs_valid_pattern(sid)) {
|
|
ntfs_log_error("Bad implicit SID pattern %s\n",
|
|
item->sidstr);
|
|
sid = (SID*)NULL;
|
|
}
|
|
if (sid) {
|
|
mapping =
|
|
(struct MAPPING*)
|
|
ntfs_malloc(sizeof(struct MAPPING));
|
|
if (mapping) {
|
|
mapping->sid = sid;
|
|
mapping->xid = uid;
|
|
mapping->grcnt = 0;
|
|
mapping->next = (struct MAPPING*)NULL;
|
|
if (lastmapping)
|
|
lastmapping->next = mapping;
|
|
else
|
|
firstmapping = mapping;
|
|
lastmapping = mapping;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (firstmapping);
|
|
}
|
|
|
|
/*
|
|
* Build the group mapping list
|
|
* group identification may be given in symbolic or numeric format
|
|
*
|
|
* gid not associated to a uid are processed first in order
|
|
* to favour real groups
|
|
*
|
|
* ! Note ! : does getgrnam() read /etc/group or some other file ?
|
|
* if so there is a possible recursion into fuse if this
|
|
* file is on NTFS, and fuse is not recursion safe.
|
|
*/
|
|
|
|
struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem)
|
|
{
|
|
struct MAPLIST *item;
|
|
struct MAPPING *firstmapping;
|
|
struct MAPPING *lastmapping;
|
|
struct MAPPING *mapping;
|
|
struct group *grp;
|
|
BOOL secondstep;
|
|
BOOL ok;
|
|
int step;
|
|
SID *sid;
|
|
int gid;
|
|
|
|
firstmapping = (struct MAPPING*)NULL;
|
|
lastmapping = (struct MAPPING*)NULL;
|
|
for (step=1; step<=2; step++) {
|
|
for (item = firstitem; item; item = item->next) {
|
|
secondstep = (item->uidstr[0] != '\0')
|
|
|| !item->gidstr[0];
|
|
ok = (step == 1 ? !secondstep : secondstep);
|
|
if ((item->gidstr[0] >= '0')
|
|
&& (item->gidstr[0] <= '9'))
|
|
gid = atoi(item->gidstr);
|
|
else {
|
|
gid = 0;
|
|
if (item->gidstr[0]) {
|
|
grp = getgrnam(item->gidstr);
|
|
if (grp)
|
|
gid = grp->gr_gid;
|
|
else
|
|
ntfs_log_early_error("Invalid group \"%s\"\n",
|
|
item->gidstr);
|
|
}
|
|
}
|
|
/*
|
|
* Records with no uid and no gid are inserted in the
|
|
* second step to define the implicit mapping pattern
|
|
*/
|
|
if (ok
|
|
&& (gid
|
|
|| (!item->uidstr[0] && !item->gidstr[0]))) {
|
|
sid = encodesid(item->sidstr);
|
|
if (sid && !item->uidstr[0] && !item->gidstr[0]
|
|
&& !ntfs_valid_pattern(sid)) {
|
|
/* error already logged */
|
|
sid = (SID*)NULL;
|
|
}
|
|
if (sid) {
|
|
mapping = (struct MAPPING*)
|
|
ntfs_malloc(sizeof(struct MAPPING));
|
|
if (mapping) {
|
|
mapping->sid = sid;
|
|
mapping->xid = gid;
|
|
/* special groups point to themselves */
|
|
if (ntfs_known_group_sid(sid)) {
|
|
mapping->groups =
|
|
(gid_t*)&mapping->xid;
|
|
mapping->grcnt = 1;
|
|
} else
|
|
mapping->grcnt = 0;
|
|
|
|
|
|
mapping->next = (struct MAPPING*)NULL;
|
|
if (lastmapping)
|
|
lastmapping->next = mapping;
|
|
else
|
|
firstmapping = mapping;
|
|
lastmapping = mapping;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (firstmapping);
|
|
}
|