#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#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_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#ifdef __HAIKU__
#define getgrgid(a) NULL
#define getpwuid(a) NULL
#define getgrnam(x) NULL
#define getpwnam(x) NULL
#endif
#include "compat.h"
#include "param.h"
#include "types.h"
#include "layout.h"
#include "attrib.h"
#include "index.h"
#include "dir.h"
#include "bitmap.h"
#include "security.h"
#include "acls.h"
#include "cache.h"
#include "misc.h"
#include "xattrs.h"
#define ALIGN_SDS_BLOCK 0x40000
#define ALIGN_SDS_ENTRY 16
#define STUFFSZ 0x4000
#define FIRST_SECURITY_ID 0x100
#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \
| FILE_ATTR_HIDDEN \
| FILE_ATTR_SYSTEM \
| FILE_ATTR_ARCHIVE \
| FILE_ATTR_TEMPORARY \
| FILE_ATTR_OFFLINE \
| FILE_ATTR_NOT_CONTENT_INDEXED )
struct SII {
le16 offs;
le16 size;
le32 fill1;
le16 indexsz;
le16 indexksz;
le16 flags;
le16 fill2;
le32 keysecurid;
le32 hash;
le32 securid;
le32 dataoffsl;
le32 dataoffsh;
le32 datasize;
} ;
struct SDH {
le16 offs;
le16 size;
le32 fill1;
le16 indexsz;
le16 indexksz;
le16 flags;
le16 fill2;
le32 keyhash;
le32 keysecurid;
le32 hash;
le32 securid;
le32 dataoffsl;
le32 dataoffsh;
le32 datasize;
le32 fill3;
} ;
static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
const_cpu_to_le16('S'),
const_cpu_to_le16('I'),
const_cpu_to_le16('I'),
const_cpu_to_le16(0) };
static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
const_cpu_to_le16('S'),
const_cpu_to_le16('D'),
const_cpu_to_le16('H'),
const_cpu_to_le16(0) };
extern const SID *nullsid;
static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
static const GUID *const zero_guid = &__zero_guid;
BOOL ntfs_guid_is_zero(const GUID *guid)
{
return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
}
char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
{
char *_guid_str;
int res;
if (!guid) {
errno = EINVAL;
return NULL;
}
_guid_str = guid_str;
if (!_guid_str) {
_guid_str = (char*)ntfs_malloc(37);
if (!_guid_str)
return _guid_str;
}
res = snprintf(_guid_str, 37,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
(unsigned int)le32_to_cpu(guid->data1),
le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
guid->data4[0], guid->data4[1],
guid->data4[2], guid->data4[3], guid->data4[4],
guid->data4[5], guid->data4[6], guid->data4[7]);
if (res == 36)
return _guid_str;
if (!guid_str)
free(_guid_str);
errno = EINVAL;
return NULL;
}
int ntfs_sid_to_mbs_size(const SID *sid)
{
int size, i;
if (!ntfs_valid_sid(sid)) {
errno = EINVAL;
return -1;
}
size = 2;
for (i = SID_REVISION; i > 0; i /= 10)
size++;
size++;
if (!sid->identifier_authority.high_part)
size += 10;
else
size += 14;
size += (1 + 10) * sid->sub_authority_count;
size++;
return size * sizeof(char);
}
char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
{
u64 u;
le32 leauth;
char *s;
int i, j, cnt;
if (sid_str && (sid_str_size < 8 || !ntfs_valid_sid(sid))) {
errno = EINVAL;
return NULL;
}
if (!sid_str) {
cnt = ntfs_sid_to_mbs_size(sid);
if (cnt < 0)
return NULL;
s = (char*)ntfs_malloc(cnt);
if (!s)
return s;
sid_str = s;
sid_str_size = 0;
} else {
s = sid_str;
cnt = sid_str_size;
}
i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
if (i < 0 || i >= cnt)
goto err_out;
s += i;
cnt -= i;
for (u = i = 0, j = 40; i < 6; i++, j -= 8)
u += (u64)sid->identifier_authority.value[i] << j;
if (!sid->identifier_authority.high_part)
i = snprintf(s, cnt, "%lu", (unsigned long)u);
else
i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
if (i < 0 || i >= cnt)
goto err_out;
s += i;
cnt -= i;
for (j = 0; j < sid->sub_authority_count; j++) {
leauth = sid->sub_authority[j];
i = snprintf(s, cnt, "-%u", (unsigned int)
le32_to_cpu(leauth));
if (i < 0 || i >= cnt)
goto err_out;
s += i;
cnt -= i;
}
return sid_str;
err_out:
if (i >= cnt)
i = EMSGSIZE;
else
i = errno;
if (!sid_str_size)
free(sid_str);
errno = i;
return NULL;
}
void ntfs_generate_guid(GUID *guid)
{
unsigned int i;
u8 *p = (u8 *)guid;
srandom(time((time_t*)NULL) ^ (getpid() << 16));
for (i = 0; i < sizeof(GUID); i++) {
p[i] = (u8)(random() & 0xFF);
if (i == 7)
p[7] = (p[7] & 0x0F) | 0x40;
if (i == 8)
p[8] = (p[8] & 0x3F) | 0x80;
}
}
le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
{
const le32 *pos = (const le32*)sd;
const le32 *end = pos + (len >> 2);
u32 hash = 0;
while (pos < end) {
hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
pos++;
}
return cpu_to_le32(hash);
}
static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
{
return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
}
static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
{
int res;
int written;
unsigned long total;
char *stuff;
res = 0;
total = 0;
stuff = (char*)ntfs_malloc(STUFFSZ);
if (stuff) {
memset(stuff, 0, STUFFSZ);
do {
written = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, stuff, STUFFSZ, offs);
if (written == STUFFSZ) {
total += STUFFSZ;
offs += STUFFSZ;
} else {
errno = ENOSPC;
res = -1;
}
} while (!res && (total < ALIGN_SDS_BLOCK));
free(stuff);
} else {
errno = ENOMEM;
res = -1;
}
return (res);
}
static int entersecurity_data(ntfs_volume *vol,
const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
le32 hash, le32 keyid, off_t offs, int gap)
{
int res;
int written1;
int written2;
char *fullattr;
int fullsz;
SECURITY_DESCRIPTOR_HEADER *phsds;
res = -1;
fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
fullattr = (char*)ntfs_malloc(fullsz);
if (fullattr) {
if (gap)
memset(fullattr,0,gap);
memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
attr,attrsz);
phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
phsds->hash = hash;
phsds->security_id = keyid;
phsds->offset = cpu_to_le64(offs);
phsds->length = cpu_to_le32(fullsz - gap);
written1 = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, fullattr, fullsz,
offs - gap);
written2 = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, fullattr, fullsz,
offs - gap + ALIGN_SDS_BLOCK);
if ((written1 == fullsz)
&& (written2 == written1)) {
res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS,
4, offs - gap + ALIGN_SDS_BLOCK + fullsz);
} else
errno = ENOSPC;
free(fullattr);
} else
errno = ENOMEM;
return (res);
}
static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
le32 hash, le32 keyid, off_t offs)
{
union {
struct {
le32 dataoffsl;
le32 dataoffsh;
} parts;
le64 all;
} realign;
int res;
ntfs_index_context *xsii;
ntfs_index_context *xsdh;
struct SII newsii;
struct SDH newsdh;
res = -1;
xsii = vol->secure_xsii;
ntfs_index_ctx_reinit(xsii);
newsii.offs = const_cpu_to_le16(20);
newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
newsii.fill1 = const_cpu_to_le32(0);
newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
newsii.flags = const_cpu_to_le16(0);
newsii.fill2 = const_cpu_to_le16(0);
newsii.keysecurid = keyid;
newsii.hash = hash;
newsii.securid = keyid;
realign.all = cpu_to_le64(offs);
newsii.dataoffsh = realign.parts.dataoffsh;
newsii.dataoffsl = realign.parts.dataoffsl;
newsii.datasize = cpu_to_le32(attrsz
+ sizeof(SECURITY_DESCRIPTOR_HEADER));
if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
xsdh = vol->secure_xsdh;
ntfs_index_ctx_reinit(xsdh);
newsdh.offs = const_cpu_to_le16(24);
newsdh.size = const_cpu_to_le16(
sizeof(SECURITY_DESCRIPTOR_HEADER));
newsdh.fill1 = const_cpu_to_le32(0);
newsdh.indexsz = const_cpu_to_le16(
sizeof(struct SDH));
newsdh.indexksz = const_cpu_to_le16(
sizeof(SDH_INDEX_KEY));
newsdh.flags = const_cpu_to_le16(0);
newsdh.fill2 = const_cpu_to_le16(0);
newsdh.keyhash = hash;
newsdh.keysecurid = keyid;
newsdh.hash = hash;
newsdh.securid = keyid;
newsdh.dataoffsh = realign.parts.dataoffsh;
newsdh.dataoffsl = realign.parts.dataoffsl;
newsdh.datasize = cpu_to_le32(attrsz
+ sizeof(SECURITY_DESCRIPTOR_HEADER));
newsdh.fill3 = const_cpu_to_le32(0x00490049);
if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
res = 0;
}
return (res);
}
static le32 entersecurityattr(ntfs_volume *vol,
const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
le32 hash)
{
union {
struct {
le32 dataoffsl;
le32 dataoffsh;
} parts;
le64 all;
} realign;
le32 securid;
le32 keyid;
u32 newkey;
off_t offs;
int gap;
int size;
BOOL found;
struct SII *psii;
INDEX_ENTRY *entry;
INDEX_ENTRY *next;
ntfs_index_context *xsii;
int retries;
ntfs_attr *na;
int olderrno;
securid = const_cpu_to_le32(0);
xsii = vol->secure_xsii;
ntfs_index_ctx_reinit(xsii);
offs = size = 0;
keyid = const_cpu_to_le32(-1);
olderrno = errno;
found = !ntfs_index_lookup((char*)&keyid,
sizeof(SII_INDEX_KEY), xsii);
if (!found && (errno != ENOENT)) {
ntfs_log_perror("Inconsistency in index $SII");
psii = (struct SII*)NULL;
} else {
errno = olderrno;
entry = xsii->entry;
psii = (struct SII*)xsii->entry;
}
if (psii) {
if (xsii->is_in_root)
entry = ntfs_ie_get_first
((INDEX_HEADER*)&xsii->ir->index);
else
entry = ntfs_ie_get_first
((INDEX_HEADER*)&xsii->ib->index);
keyid = const_cpu_to_le32(0);
retries = 0;
while (entry) {
next = ntfs_index_next(entry,xsii);
if (next) {
psii = (struct SII*)next;
keyid = psii->keysecurid;
realign.parts.dataoffsh
= psii->dataoffsh;
realign.parts.dataoffsl
= psii->dataoffsl;
offs = le64_to_cpu(realign.all);
size = le32_to_cpu(psii->datasize);
}
entry = next;
if (!entry && !keyid && !retries) {
ntfs_index_ctx_reinit(xsii);
found = !ntfs_index_lookup((char*)&keyid,
sizeof(SII_INDEX_KEY), xsii);
if (!found && (errno != ENOENT)) {
ntfs_log_perror("Index $SII is broken");
psii = (struct SII*)NULL;
} else {
errno = olderrno;
entry = xsii->entry;
psii = (struct SII*)entry;
}
if (psii
&& !(psii->flags & INDEX_ENTRY_END)) {
keyid = psii->keysecurid;
realign.parts.dataoffsh
= psii->dataoffsh;
realign.parts.dataoffsl
= psii->dataoffsl;
offs = le64_to_cpu(realign.all);
size = le32_to_cpu(psii->datasize);
}
retries++;
}
}
}
if (!keyid) {
securid = const_cpu_to_le32(0);
na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
if (na) {
if ((size_t)na->data_size < (sizeof(struct SII)
+ sizeof(INDEX_ENTRY_HEADER))) {
ntfs_log_error("Creating the first security_id\n");
securid = const_cpu_to_le32(FIRST_SECURITY_ID);
}
ntfs_attr_close(na);
}
if (!securid) {
ntfs_log_error("Error creating a security_id\n");
errno = EIO;
}
} else {
newkey = le32_to_cpu(keyid) + 1;
securid = cpu_to_le32(newkey);
}
if (securid) {
gap = (-size) & (ALIGN_SDS_ENTRY - 1);
offs += gap + size;
if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
& ALIGN_SDS_BLOCK) {
offs = ((offs + attrsz
+ sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
| (ALIGN_SDS_BLOCK - 1)) + 1;
}
if (!(offs & (ALIGN_SDS_BLOCK - 1)))
entersecurity_stuff(vol, offs);
if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
|| entersecurity_indexes(vol, attrsz, hash, securid, offs))
securid = const_cpu_to_le32(0);
}
ntfs_index_entry_mark_dirty(vol->secure_xsii);
ntfs_index_ctx_reinit(vol->secure_xsii);
ntfs_index_entry_mark_dirty(vol->secure_xsdh);
ntfs_index_ctx_reinit(vol->secure_xsdh);
NInoSetDirty(vol->secure_ni);
if (ntfs_inode_sync(vol->secure_ni))
ntfs_log_perror("Could not sync $Secure\n");
return (securid);
}
static le32 setsecurityattr(ntfs_volume *vol,
const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
{
struct SDH *psdh;
union {
struct {
le32 dataoffsl;
le32 dataoffsh;
} parts;
le64 all;
} realign;
BOOL found;
BOOL collision;
size_t size;
size_t rdsize;
s64 offs;
int res;
ntfs_index_context *xsdh;
char *oldattr;
SDH_INDEX_KEY key;
INDEX_ENTRY *entry;
le32 securid;
le32 hash;
int olderrno;
hash = ntfs_security_hash(attr,attrsz);
oldattr = (char*)NULL;
securid = const_cpu_to_le32(0);
res = 0;
xsdh = vol->secure_xsdh;
if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
ntfs_index_ctx_reinit(xsdh);
key.hash = hash;
key.security_id = const_cpu_to_le32(0);
olderrno = errno;
found = !ntfs_index_lookup((char*)&key,
sizeof(SDH_INDEX_KEY), xsdh);
if (!found && (errno != ENOENT))
ntfs_log_perror("Inconsistency in index $SDH");
else {
errno = olderrno;
entry = xsdh->entry;
found = FALSE;
if (entry->ie_flags & INDEX_ENTRY_END)
entry = ntfs_index_next(entry,xsdh);
do {
collision = FALSE;
psdh = (struct SDH*)entry;
if (psdh)
size = (size_t) le32_to_cpu(psdh->datasize)
- sizeof(SECURITY_DESCRIPTOR_HEADER);
else size = 0;
if (psdh && (size > 0)
&& (psdh->keyhash == hash)) {
realign.parts.dataoffsh = psdh->dataoffsh;
realign.parts.dataoffsl = psdh->dataoffsl;
offs = le64_to_cpu(realign.all)
+ sizeof(SECURITY_DESCRIPTOR_HEADER);
oldattr = (char*)ntfs_malloc(size);
if (oldattr) {
rdsize = ntfs_attr_data_read(
vol->secure_ni,
STREAM_SDS, 4,
oldattr, size, offs);
found = (rdsize == size)
&& !memcmp(oldattr,attr,size);
free(oldattr);
if (!found) {
entry = ntfs_index_next(
entry,xsdh);
collision = TRUE;
}
} else
res = ENOMEM;
}
} while (collision && entry);
if (found)
securid = psdh->keysecurid;
else {
if (res) {
errno = res;
securid = const_cpu_to_le32(0);
} else {
securid = entersecurityattr(vol,
attr, attrsz, hash);
}
}
}
}
if (--vol->secure_reentry)
ntfs_log_perror("Reentry error, check no multithreading\n");
return (securid);
}
static int update_secur_descr(ntfs_volume *vol,
char *newattr, ntfs_inode *ni)
{
int newattrsz;
int written;
int res;
ntfs_attr *na;
newattrsz = ntfs_attr_size(newattr);
#if !FORCE_FORMAT_v1x
if ((vol->major_ver < 3) || !vol->secure_ni) {
#endif
na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
if (na) {
res = ntfs_attr_truncate(na, (s64) newattrsz);
if (!res) {
written = (int)ntfs_attr_pwrite(na, (s64) 0,
(s64) newattrsz, newattr);
if (written != newattrsz) {
ntfs_log_error("Failed to update "
"a v1.x security descriptor\n");
errno = EIO;
res = -1;
}
}
ntfs_attr_close(na);
na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
AT_UNNAMED, 0);
if (na) {
clear_nino_flag(ni, v3_Extensions);
ni->security_id = const_cpu_to_le32(0);
res = ntfs_attr_truncate(na, (s64)48);
ntfs_attr_close(na);
clear_nino_flag(ni, v3_Extensions);
}
} else {
res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
AT_UNNAMED, 0, (u8*)newattr,
(s64) newattrsz);
}
#if !FORCE_FORMAT_v1x
} else {
le32 securid;
securid = setsecurityattr(vol,
(const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
(s64)newattrsz);
if (securid) {
na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
AT_UNNAMED, 0);
if (na) {
res = 0;
if (!test_nino_flag(ni, v3_Extensions)) {
res = ntfs_attr_truncate(na,
(s64)sizeof(STANDARD_INFORMATION));
ni->owner_id = const_cpu_to_le32(0);
ni->quota_charged = const_cpu_to_le64(0);
ni->usn = const_cpu_to_le64(0);
ntfs_attr_remove(ni,
AT_SECURITY_DESCRIPTOR,
AT_UNNAMED, 0);
}
set_nino_flag(ni, v3_Extensions);
ni->security_id = securid;
ntfs_attr_close(na);
} else {
ntfs_log_error("Failed to update "
"standard informations\n");
errno = EIO;
res = -1;
}
} else
res = -1;
}
#endif
NInoSetDirty(ni);
return (res);
}
static int upgrade_secur_desc(ntfs_volume *vol,
const char *attr, ntfs_inode *ni)
{
int attrsz;
int res;
le32 securid;
ntfs_attr *na;
if ((vol->major_ver >= 3)
&& (ni->mft_no >= FILE_first_user)) {
attrsz = ntfs_attr_size(attr);
securid = setsecurityattr(vol,
(const SECURITY_DESCRIPTOR_RELATIVE*)attr,
(s64)attrsz);
if (securid) {
na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
AT_UNNAMED, 0);
if (na) {
res = ntfs_attr_truncate(na,
(s64)sizeof(STANDARD_INFORMATION));
ni->owner_id = const_cpu_to_le32(0);
ni->quota_charged = const_cpu_to_le64(0);
ni->usn = const_cpu_to_le64(0);
ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
AT_UNNAMED, 0);
set_nino_flag(ni, v3_Extensions);
ni->security_id = securid;
ntfs_attr_close(na);
} else {
ntfs_log_error("Failed to upgrade "
"standard informations\n");
errno = EIO;
res = -1;
}
} else
res = -1;
NInoSetDirty(ni);
} else
res = 1;
return (res);
}
static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
{
BOOL ingroup;
int grcnt;
gid_t *groups;
struct MAPPING *user;
ingroup = FALSE;
if (uid) {
user = scx->mapping[MAPUSERS];
while (user && ((uid_t)user->xid != uid))
user = user->next;
if (user) {
groups = user->groups;
grcnt = user->grcnt;
while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
ingroup = (grcnt >= 0);
}
}
return (ingroup);
}
#if defined(__sun) && defined (__SVR4)
static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
{
typedef struct prcred {
uid_t pr_euid;
uid_t pr_ruid;
uid_t pr_suid;
gid_t pr_egid;
gid_t pr_rgid;
gid_t pr_sgid;
int pr_ngroups;
gid_t pr_groups[1];
} prcred_t;
enum { readset = 16 };
prcred_t basecreds;
gid_t groups[readset];
char filename[64];
int fd;
int k;
int cnt;
gid_t *p;
BOOL ismember;
int got;
pid_t tid;
if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
ismember = staticgroupmember(scx, uid, gid);
else {
ismember = FALSE;
tid = scx->tid;
sprintf(filename,"/proc/%u/cred",tid);
fd = open(filename,O_RDONLY);
if (fd >= 0) {
got = read(fd, &basecreds, sizeof(prcred_t));
if (got == sizeof(prcred_t)) {
if (basecreds.pr_egid == gid)
ismember = TRUE;
p = basecreds.pr_groups;
cnt = 1;
k = 0;
while (!ismember
&& (k < basecreds.pr_ngroups)
&& (cnt > 0)
&& (*p != gid)) {
k++;
cnt--;
p++;
if (cnt <= 0) {
got = read(fd, groups,
readset*sizeof(gid_t));
cnt = got/sizeof(gid_t);
p = groups;
}
}
if ((cnt > 0)
&& (k < basecreds.pr_ngroups))
ismember = TRUE;
}
close(fd);
}
}
return (ismember);
}
#elif defined(__HAIKU__)
static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
{
return TRUE;
}
#else
static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
{
static char key[] = "\nGroups:";
char buf[BUFSZ+1];
char filename[64];
enum { INKEY, INSEP, INNUM, INEND } state;
int fd;
char c;
int matched;
BOOL ismember;
int got;
char *p;
gid_t grp;
pid_t tid;
if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
ismember = staticgroupmember(scx, uid, gid);
else {
ismember = FALSE;
tid = scx->tid;
sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
fd = open(filename,O_RDONLY);
if (fd >= 0) {
got = read(fd, buf, BUFSZ);
buf[got] = 0;
state = INKEY;
matched = 0;
p = buf;
grp = 0;
do {
c = *p++;
if (!c) {
got = read(fd, buf, BUFSZ);
buf[got] = 0;
p = buf;
c = *p++;
}
switch (state) {
case INKEY :
if (key[matched] == c) {
if (!key[++matched])
state = INSEP;
} else
if (key[0] == c)
matched = 1;
else
matched = 0;
break;
case INSEP :
if ((c >= '0') && (c <= '9')) {
grp = c - '0';
state = INNUM;
} else
if ((c != ' ') && (c != '\t'))
state = INEND;
break;
case INNUM :
if ((c >= '0') && (c <= '9'))
grp = grp*10 + c - '0';
else {
ismember = (grp == gid);
if ((c != ' ') && (c != '\t'))
state = INEND;
else
state = INSEP;
}
default :
break;
}
} while (!ismember && c && (state != INEND));
close(fd);
if (!c)
ntfs_log_error("No group record found in %s\n",filename);
} else
ntfs_log_error("Could not open %s\n",filename);
}
return (ismember);
}
#endif
#if POSIXACLS
static int ntfs_basic_perms(const struct SECURITY_CONTEXT *scx,
const struct POSIX_SECURITY *pxdesc)
{
int k;
int perms;
const struct POSIX_ACE *pace;
const struct MAPPING* group;
k = 0;
perms = pxdesc->mode;
for (k=0; k < pxdesc->acccnt; k++) {
pace = &pxdesc->acl.ace[k];
if (pace->tag == POSIX_ACL_GROUP_OBJ)
perms = (perms & 07707)
| ((pace->perms & 7) << 3);
else
if (pace->tag == POSIX_ACL_GROUP) {
group = scx->mapping[MAPGROUPS];
while (group && (group->xid != pace->id))
group = group->next;
if (group && group->grcnt
&& (*(group->groups) == (gid_t)pace->id))
perms |= pace->perms & 7;
}
}
return (perms);
}
#endif
static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
u32 securindex)
{
struct PERMISSIONS_CACHE *cache;
unsigned int index1;
unsigned int i;
cache = (struct PERMISSIONS_CACHE*)NULL;
index1 = securindex >> CACHE_PERMISSIONS_BITS;
cache = (struct PERMISSIONS_CACHE*)
ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
+ index1*sizeof(struct CACHED_PERMISSIONS*));
if (cache) {
cache->head.last = index1;
cache->head.p_reads = 0;
cache->head.p_hits = 0;
cache->head.p_writes = 0;
*scx->pseccache = cache;
for (i=0; i<=index1; i++)
cache->cachetable[i]
= (struct CACHED_PERMISSIONS*)NULL;
}
return (cache);
}
static void free_caches(struct SECURITY_CONTEXT *scx)
{
unsigned int index1;
struct PERMISSIONS_CACHE *pseccache;
pseccache = *scx->pseccache;
if (pseccache) {
for (index1=0; index1<=pseccache->head.last; index1++)
if (pseccache->cachetable[index1]) {
#if POSIXACLS
struct CACHED_PERMISSIONS *cacheentry;
unsigned int index2;
for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
cacheentry = &pseccache->cachetable[index1][index2];
if (cacheentry->valid
&& cacheentry->pxdesc)
free(cacheentry->pxdesc);
}
#endif
free(pseccache->cachetable[index1]);
}
free(pseccache);
}
}
static int compare(const struct CACHED_SECURID *cached,
const struct CACHED_SECURID *item)
{
#if POSIXACLS
size_t csize;
size_t isize;
csize = (cached->variable ?
sizeof(struct POSIX_ACL)
+ (((struct POSIX_SECURITY*)cached->variable)->acccnt
+ ((struct POSIX_SECURITY*)cached->variable)->defcnt)
*sizeof(struct POSIX_ACE) :
0);
isize = (item->variable ?
sizeof(struct POSIX_ACL)
+ (((struct POSIX_SECURITY*)item->variable)->acccnt
+ ((struct POSIX_SECURITY*)item->variable)->defcnt)
*sizeof(struct POSIX_ACE) :
0);
return ((cached->uid != item->uid)
|| (cached->gid != item->gid)
|| (cached->dmode != item->dmode)
|| (csize != isize)
|| (csize
&& isize
&& memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
&((struct POSIX_SECURITY*)item->variable)->acl, csize)));
#else
return ((cached->uid != item->uid)
|| (cached->gid != item->gid)
|| (cached->dmode != item->dmode));
#endif
}
static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
const struct CACHED_PERMISSIONS_LEGACY *item)
{
return (cached->mft_no != item->mft_no);
}
static void resize_cache(struct SECURITY_CONTEXT *scx,
u32 securindex)
{
struct PERMISSIONS_CACHE *oldcache;
struct PERMISSIONS_CACHE *newcache;
int newcnt;
int oldcnt;
unsigned int index1;
unsigned int i;
oldcache = *scx->pseccache;
index1 = securindex >> CACHE_PERMISSIONS_BITS;
newcnt = index1 + 1;
if (newcnt <= ((CACHE_PERMISSIONS_SIZE
+ (1 << CACHE_PERMISSIONS_BITS)
- 1) >> CACHE_PERMISSIONS_BITS)) {
oldcnt = oldcache->head.last + 1;
newcache = (struct PERMISSIONS_CACHE*)
ntfs_malloc(
sizeof(struct PERMISSIONS_CACHE)
+ (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
if (newcache) {
memcpy(newcache,oldcache,
sizeof(struct PERMISSIONS_CACHE)
+ (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
free(oldcache);
for (i=newcache->head.last+1; i<=index1; i++)
newcache->cachetable[i]
= (struct CACHED_PERMISSIONS*)NULL;
newcache->head.last = index1;
*scx->pseccache = newcache;
}
}
}
#if POSIXACLS
static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, uid_t uid, gid_t gid,
struct POSIX_SECURITY *pxdesc)
#else
static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
#endif
{
struct CACHED_PERMISSIONS *cacheentry;
struct CACHED_PERMISSIONS *cacheblock;
struct PERMISSIONS_CACHE *pcache;
u32 securindex;
#if POSIXACLS
int pxsize;
struct POSIX_SECURITY *pxcached;
#endif
unsigned int index1;
unsigned int index2;
int i;
if (test_nino_flag(ni, v3_Extensions)
&& ni->security_id) {
securindex = le32_to_cpu(ni->security_id);
index1 = securindex >> CACHE_PERMISSIONS_BITS;
index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
pcache = *scx->pseccache;
if (pcache
&& (pcache->head.last >= index1)
&& pcache->cachetable[index1]) {
cacheentry = &pcache->cachetable[index1][index2];
cacheentry->uid = uid;
cacheentry->gid = gid;
#if POSIXACLS
if (cacheentry->valid && cacheentry->pxdesc)
free(cacheentry->pxdesc);
if (pxdesc) {
pxsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
if (pxcached) {
memcpy(pxcached, pxdesc, pxsize);
cacheentry->pxdesc = pxcached;
} else {
cacheentry->valid = 0;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
}
cacheentry->mode = pxdesc->mode & 07777;
} else
cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
#else
cacheentry->mode = mode & 07777;
#endif
cacheentry->inh_fileid = const_cpu_to_le32(0);
cacheentry->inh_dirid = const_cpu_to_le32(0);
cacheentry->valid = 1;
pcache->head.p_writes++;
} else {
if (!pcache) {
pcache = create_caches(scx, securindex);
} else {
if (index1 > pcache->head.last) {
resize_cache(scx, securindex);
pcache = *scx->pseccache;
}
}
if (pcache && (index1 <= pcache->head.last)) {
cacheblock = (struct CACHED_PERMISSIONS*)
malloc(sizeof(struct CACHED_PERMISSIONS)
<< CACHE_PERMISSIONS_BITS);
pcache->cachetable[index1] = cacheblock;
for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
cacheblock[i].valid = 0;
cacheentry = &cacheblock[index2];
if (cacheentry) {
cacheentry->uid = uid;
cacheentry->gid = gid;
#if POSIXACLS
if (pxdesc) {
pxsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
if (pxcached) {
memcpy(pxcached, pxdesc, pxsize);
cacheentry->pxdesc = pxcached;
} else {
cacheentry->valid = 0;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
}
cacheentry->mode = pxdesc->mode & 07777;
} else
cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
#else
cacheentry->mode = mode & 07777;
#endif
cacheentry->inh_fileid = const_cpu_to_le32(0);
cacheentry->inh_dirid = const_cpu_to_le32(0);
cacheentry->valid = 1;
pcache->head.p_writes++;
}
} else
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
}
} else {
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
#if CACHE_LEGACY_SIZE
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
struct CACHED_PERMISSIONS_LEGACY wanted;
struct CACHED_PERMISSIONS_LEGACY *legacy;
wanted.perm.uid = uid;
wanted.perm.gid = gid;
#if POSIXACLS
wanted.perm.mode = pxdesc->mode & 07777;
wanted.perm.inh_fileid = const_cpu_to_le32(0);
wanted.perm.inh_dirid = const_cpu_to_le32(0);
wanted.mft_no = ni->mft_no;
wanted.variable = (void*)pxdesc;
wanted.varsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
#else
wanted.perm.mode = mode & 07777;
wanted.perm.inh_fileid = const_cpu_to_le32(0);
wanted.perm.inh_dirid = const_cpu_to_le32(0);
wanted.mft_no = ni->mft_no;
wanted.variable = (void*)NULL;
wanted.varsize = 0;
#endif
legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
scx->vol->legacy_cache, GENERIC(&wanted),
(cache_compare)leg_compare);
if (legacy) {
cacheentry = &legacy->perm;
#if POSIXACLS
cacheentry->pxdesc = legacy->variable;
#endif
}
}
#endif
}
return (cacheentry);
}
static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct CACHED_PERMISSIONS *cacheentry;
struct PERMISSIONS_CACHE *pcache;
u32 securindex;
unsigned int index1;
unsigned int index2;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
if (test_nino_flag(ni, v3_Extensions)
&& (ni->security_id)) {
securindex = le32_to_cpu(ni->security_id);
index1 = securindex >> CACHE_PERMISSIONS_BITS;
index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
pcache = *scx->pseccache;
if (pcache
&& (pcache->head.last >= index1)
&& pcache->cachetable[index1]) {
cacheentry = &pcache->cachetable[index1][index2];
if (!cacheentry->valid)
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
else
pcache->head.p_hits++;
if (pcache)
pcache->head.p_reads++;
}
}
#if CACHE_LEGACY_SIZE
else {
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
struct CACHED_PERMISSIONS_LEGACY wanted;
struct CACHED_PERMISSIONS_LEGACY *legacy;
wanted.mft_no = ni->mft_no;
wanted.variable = (void*)NULL;
wanted.varsize = 0;
legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
scx->vol->legacy_cache, GENERIC(&wanted),
(cache_compare)leg_compare);
if (legacy) cacheentry = &legacy->perm;
}
}
#endif
#if POSIXACLS
if (cacheentry && !cacheentry->pxdesc) {
ntfs_log_error("No Posix descriptor in cache\n");
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
}
#endif
return (cacheentry);
}
static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
{
struct SII *psii;
union {
struct {
le32 dataoffsl;
le32 dataoffsh;
} parts;
le64 all;
} realign;
int found;
size_t size;
size_t rdsize;
s64 offs;
ntfs_inode *ni;
ntfs_index_context *xsii;
char *securattr;
securattr = (char*)NULL;
ni = vol->secure_ni;
xsii = vol->secure_xsii;
if (ni && xsii) {
ntfs_index_ctx_reinit(xsii);
found =
!ntfs_index_lookup((char*)&id,
sizeof(SII_INDEX_KEY), xsii);
if (found) {
psii = (struct SII*)xsii->entry;
size =
(size_t) le32_to_cpu(psii->datasize)
- sizeof(SECURITY_DESCRIPTOR_HEADER);
realign.parts.dataoffsh = psii->dataoffsh;
realign.parts.dataoffsl = psii->dataoffsl;
offs = le64_to_cpu(realign.all)
+ sizeof(SECURITY_DESCRIPTOR_HEADER);
securattr = (char*)ntfs_malloc(size);
if (securattr) {
rdsize = ntfs_attr_data_read(
ni, STREAM_SDS, 4,
securattr, size, offs);
if ((rdsize != size)
|| !ntfs_valid_descr(securattr,
rdsize)) {
free(securattr);
securattr = (char*)NULL;
}
}
} else
if (errno != ENOENT)
ntfs_log_perror("Inconsistency in index $SII");
}
if (!securattr) {
ntfs_log_error("Failed to retrieve a security descriptor\n");
errno = EIO;
}
return (securattr);
}
static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
{
SII_INDEX_KEY securid;
char *securattr;
s64 readallsz;
if (test_nino_flag(ni, v3_Extensions)
&& vol->secure_ni && ni->security_id) {
securid.security_id = ni->security_id;
securattr = retrievesecurityattr(vol,securid);
if (!securattr)
ntfs_log_error("Bad security descriptor for 0x%lx\n",
(long)le32_to_cpu(ni->security_id));
} else {
readallsz = 0;
securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
AT_UNNAMED, 0, &readallsz);
if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
ntfs_log_error("Bad security descriptor for inode %lld\n",
(long long)ni->mft_no);
free(securattr);
securattr = (char*)NULL;
}
}
if (!securattr) {
ntfs_log_error("No security descriptor found for inode %lld\n",
(long long)ni->mft_no);
securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
}
return (securattr);
}
#if POSIXACLS
static int access_check_posix(struct SECURITY_CONTEXT *scx,
struct POSIX_SECURITY *pxdesc, mode_t request,
uid_t uid, gid_t gid)
{
struct POSIX_ACE *pxace;
int userperms;
int groupperms;
int mask;
BOOL somegroup;
BOOL needgroups;
BOOL noacl;
mode_t perms;
int i;
noacl = !(scx->vol->secure_flags & (1 << SECURITY_ACL));
if (noacl)
perms = ntfs_basic_perms(scx, pxdesc);
else
perms = pxdesc->mode;
if (!scx->uid || (uid == scx->uid)) {
if (!scx->uid) {
if (perms & 0101)
perms |= 01777;
else {
groupperms = 0;
mask = 7;
for (i=pxdesc->acccnt-1; i>=0 ; i--) {
pxace = &pxdesc->acl.ace[i];
switch (pxace->tag) {
case POSIX_ACL_USER_OBJ :
case POSIX_ACL_GROUP_OBJ :
groupperms |= pxace->perms;
break;
case POSIX_ACL_GROUP :
if (!noacl)
groupperms
|= pxace->perms;
break;
case POSIX_ACL_MASK :
if (!noacl)
mask = pxace->perms & 7;
break;
default :
break;
}
}
perms = (groupperms & mask & 1) | 6;
}
} else
perms &= 07700;
} else {
userperms = -1;
groupperms = -1;
needgroups = FALSE;
mask = 7;
for (i=pxdesc->acccnt-1; i>=0 ; i--) {
pxace = &pxdesc->acl.ace[i];
switch (pxace->tag) {
case POSIX_ACL_USER :
if (!noacl
&& ((uid_t)pxace->id == scx->uid))
userperms = pxace->perms;
break;
case POSIX_ACL_MASK :
if (!noacl)
mask = pxace->perms & 7;
break;
case POSIX_ACL_GROUP_OBJ :
if (((pxace->perms & mask) ^ perms)
& (request >> 6) & 7)
needgroups = TRUE;
break;
case POSIX_ACL_GROUP :
if (!noacl
&& (((pxace->perms & mask) ^ perms)
& (request >> 6) & 7))
needgroups = TRUE;
break;
default :
break;
}
}
if (userperms >= 0)
perms = (perms & 07000) + (userperms & mask);
else if (!needgroups)
perms &= 07007;
else {
if (!(~(perms >> 3) & request & mask)
&& ((gid == scx->gid)
|| groupmember(scx, scx->uid, gid)))
perms &= 07070;
else if (!noacl) {
groupperms = -1;
somegroup = FALSE;
for (i=pxdesc->acccnt-1; i>=0 ; i--) {
pxace = &pxdesc->acl.ace[i];
if ((pxace->tag == POSIX_ACL_GROUP)
&& groupmember(scx, scx->uid, pxace->id)) {
if (!(~pxace->perms & request & mask))
groupperms = pxace->perms;
somegroup = TRUE;
}
}
if (groupperms >= 0)
perms = (perms & 07000) + (groupperms & mask);
else
if (somegroup)
perms = 0;
else
perms &= 07007;
} else
perms &= 07007;
}
}
return (perms);
}
static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
ntfs_inode * ni, mode_t request)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *securattr;
const SID *usid;
const SID *gsid;
uid_t uid;
gid_t gid;
int perm;
BOOL isdir;
struct POSIX_SECURITY *pxdesc;
if (!scx->mapping[MAPUSERS])
perm = 07777;
else {
cached = fetch_cache(scx,ni);
if (cached) {
uid = cached->uid;
gid = cached->gid;
perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
} else {
perm = 0;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
securattr = getsecurityattr(scx->vol, ni);
if (securattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
securattr;
gsid = (const SID*)&
securattr[le32_to_cpu(phead->group)];
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
#if OWNERFROMACL
usid = ntfs_acl_owner(securattr);
pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
usid, gsid, isdir);
if (pxdesc)
perm = pxdesc->mode & 07777;
else
perm = -1;
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
usid = (const SID*)&
securattr[le32_to_cpu(phead->owner)];
pxdesc = ntfs_build_permissions_posix(scx,securattr,
usid, gsid, isdir);
if (pxdesc)
perm = pxdesc->mode & 07777;
else
perm = -1;
if (!perm && ntfs_same_sid(usid, adminsid)) {
uid = find_tenant(scx, securattr);
if (uid)
perm = 0700;
} else
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
if (!test_nino_flag(ni, v3_Extensions)
&& (perm >= 0)
&& (scx->vol->secure_flags
& (1 << SECURITY_ADDSECURIDS))) {
upgrade_secur_desc(scx->vol,
securattr, ni);
}
if (test_nino_flag(ni, v3_Extensions)
&& (perm >= 0)) {
enter_cache(scx, ni, uid,
gid, pxdesc);
}
if (pxdesc) {
perm = access_check_posix(scx,pxdesc,request,uid,gid);
free(pxdesc);
}
free(securattr);
} else {
perm = -1;
uid = gid = 0;
}
}
}
return (perm);
}
int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
const char *name, char *value, size_t size)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
struct POSIX_SECURITY *pxdesc;
const struct CACHED_PERMISSIONS *cached;
char *securattr;
const SID *usid;
const SID *gsid;
uid_t uid;
gid_t gid;
BOOL isdir;
size_t outsize;
outsize = 0;
if (!scx->mapping[MAPUSERS])
errno = ENOTSUP;
else {
cached = fetch_cache(scx,ni);
if (cached)
pxdesc = cached->pxdesc;
else {
securattr = getsecurityattr(scx->vol, ni);
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
if (securattr) {
phead =
(const SECURITY_DESCRIPTOR_RELATIVE*)
securattr;
gsid = (const SID*)&
securattr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
usid = ntfs_acl_owner(securattr);
#else
usid = (const SID*)&
securattr[le32_to_cpu(phead->owner)];
#endif
pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
usid, gsid, isdir);
if (pxdesc) {
if (!test_nino_flag(ni, v3_Extensions)
&& (scx->vol->secure_flags
& (1 << SECURITY_ADDSECURIDS))) {
upgrade_secur_desc(scx->vol,
securattr, ni);
}
#if OWNERFROMACL
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
if (!(pxdesc->mode & 07777)
&& ntfs_same_sid(usid, adminsid)) {
uid = find_tenant(scx,
securattr);
} else
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
enter_cache(scx, ni, uid,
gid, pxdesc);
}
free(securattr);
} else
pxdesc = (struct POSIX_SECURITY*)NULL;
}
if (pxdesc) {
if (ntfs_valid_posix(pxdesc)) {
if (!strcmp(name,"system.posix_acl_default")) {
if (ni->mrec->flags
& MFT_RECORD_IS_DIRECTORY)
outsize = sizeof(struct POSIX_ACL)
+ pxdesc->defcnt*sizeof(struct POSIX_ACE);
else {
if (size > 0) {
outsize = 0;
errno = EACCES;
} else
outsize = sizeof(struct POSIX_ACL);
}
if (outsize && (outsize <= size)) {
memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
memcpy(&value[sizeof(struct POSIX_ACL)],
&pxdesc->acl.ace[pxdesc->firstdef],
outsize-sizeof(struct POSIX_ACL));
}
} else {
outsize = sizeof(struct POSIX_ACL)
+ pxdesc->acccnt*sizeof(struct POSIX_ACE);
if (outsize <= size)
memcpy(value,&pxdesc->acl,outsize);
}
} else {
outsize = 0;
errno = EIO;
ntfs_log_error("Invalid Posix ACL built\n");
}
if (!cached)
free(pxdesc);
} else
outsize = 0;
}
return (outsize ? (int)outsize : -errno);
}
#else
static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, mode_t request)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *securattr;
const SID *usid;
const SID *gsid;
BOOL isdir;
uid_t uid;
gid_t gid;
int perm;
if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
perm = 07777;
else {
cached = fetch_cache(scx,ni);
if (cached) {
perm = cached->mode;
uid = cached->uid;
gid = cached->gid;
} else {
perm = 0;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
securattr = getsecurityattr(scx->vol, ni);
if (securattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
securattr;
gsid = (const SID*)&
securattr[le32_to_cpu(phead->group)];
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
#if OWNERFROMACL
usid = ntfs_acl_owner(securattr);
perm = ntfs_build_permissions(securattr,
usid, gsid, isdir);
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
usid = (const SID*)&
securattr[le32_to_cpu(phead->owner)];
perm = ntfs_build_permissions(securattr,
usid, gsid, isdir);
if (!perm && ntfs_same_sid(usid, adminsid)) {
uid = find_tenant(scx, securattr);
if (uid)
perm = 0700;
} else
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
if (!test_nino_flag(ni, v3_Extensions)
&& (perm >= 0)
&& (scx->vol->secure_flags
& (1 << SECURITY_ADDSECURIDS))) {
upgrade_secur_desc(scx->vol,
securattr, ni);
}
if (test_nino_flag(ni, v3_Extensions)
&& (perm >= 0)) {
enter_cache(scx, ni, uid,
gid, perm);
}
free(securattr);
} else {
perm = -1;
uid = gid = 0;
}
}
if (perm >= 0) {
if (!scx->uid) {
if (perm & 0111)
perm |= 01777;
else
perm = 0;
} else
if (uid == scx->uid)
perm &= 07700;
else
if ((gid == scx->gid)
|| ((((perm >> 3) ^ perm)
& (request >> 6) & 7)
&& groupmember(scx, scx->uid, gid)))
perm &= 07070;
else
perm &= 07007;
}
}
return (perm);
}
#endif
int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
char *value, size_t size)
{
char *securattr;
size_t outsize;
outsize = 0;
securattr = getsecurityattr(scx->vol, ni);
if (securattr) {
outsize = ntfs_attr_size(securattr);
if (outsize <= size) {
memcpy(value,securattr,outsize);
}
free(securattr);
}
return (outsize ? (int)outsize : -errno);
}
int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
ntfs_inode * ni, struct stat *stbuf)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
char *securattr;
const SID *usid;
const SID *gsid;
const struct CACHED_PERMISSIONS *cached;
int perm;
BOOL isdir;
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
#endif
if (!scx->mapping[MAPUSERS])
perm = 07777;
else {
cached = fetch_cache(scx,ni);
if (cached) {
#if POSIXACLS
if (!(scx->vol->secure_flags & (1 << SECURITY_ACL))
&& cached->pxdesc)
perm = ntfs_basic_perms(scx,cached->pxdesc);
else
#endif
perm = cached->mode;
stbuf->st_uid = cached->uid;
stbuf->st_gid = cached->gid;
stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
} else {
perm = -1;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
securattr = getsecurityattr(scx->vol, ni);
if (securattr) {
phead =
(const SECURITY_DESCRIPTOR_RELATIVE*)
securattr;
gsid = (const SID*)&
securattr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
usid = ntfs_acl_owner(securattr);
#else
usid = (const SID*)&
securattr[le32_to_cpu(phead->owner)];
#endif
#if POSIXACLS
pxdesc = ntfs_build_permissions_posix(
scx->mapping, securattr,
usid, gsid, isdir);
if (pxdesc) {
if (!(scx->vol->secure_flags
& (1 << SECURITY_ACL)))
perm = ntfs_basic_perms(scx,
pxdesc);
else
perm = pxdesc->mode & 07777;
} else
perm = -1;
#else
perm = ntfs_build_permissions(securattr,
usid, gsid, isdir);
#endif
if (perm >= 0) {
if (!test_nino_flag(ni, v3_Extensions)
&& (scx->vol->secure_flags
& (1 << SECURITY_ADDSECURIDS))) {
upgrade_secur_desc(scx->vol,
securattr, ni);
}
#if OWNERFROMACL
stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
if (!perm && ntfs_same_sid(usid, adminsid)) {
stbuf->st_uid =
find_tenant(scx,
securattr);
if (stbuf->st_uid)
perm = 0700;
} else
stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
stbuf->st_mode =
(stbuf->st_mode & ~07777) + perm;
#if POSIXACLS
enter_cache(scx, ni, stbuf->st_uid,
stbuf->st_gid, pxdesc);
free(pxdesc);
#else
enter_cache(scx, ni, stbuf->st_uid,
stbuf->st_gid, perm);
#endif
}
free(securattr);
}
}
}
return (perm);
}
#if POSIXACLS
static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
{
const struct CACHED_PERMISSIONS *cached;
const SECURITY_DESCRIPTOR_RELATIVE *phead;
struct POSIX_SECURITY *pxdesc;
struct POSIX_SECURITY *pydesc;
char *securattr;
const SID *usid;
const SID *gsid;
uid_t uid;
gid_t gid;
pydesc = (struct POSIX_SECURITY*)NULL;
cached = fetch_cache(scx,dir_ni);
if (cached) {
uid = cached->uid;
gid = cached->gid;
pxdesc = cached->pxdesc;
if (pxdesc) {
if (scx->vol->secure_flags & (1 << SECURITY_ACL))
pydesc = ntfs_build_inherited_posix(pxdesc,
mode, scx->umask, isdir);
else
pydesc = ntfs_build_basic_posix(pxdesc,
mode, scx->umask, isdir);
}
} else {
securattr = getsecurityattr(scx->vol, dir_ni);
if (securattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
securattr;
gsid = (const SID*)&
securattr[le32_to_cpu(phead->group)];
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
#if OWNERFROMACL
usid = ntfs_acl_owner(securattr);
pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
usid, gsid, TRUE);
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
usid = (const SID*)&
securattr[le32_to_cpu(phead->owner)];
pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
usid, gsid, TRUE);
if (pxdesc && ntfs_same_sid(usid, adminsid)) {
uid = find_tenant(scx, securattr);
} else
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
if (pxdesc) {
if (!test_nino_flag(dir_ni, v3_Extensions)
&& (scx->vol->secure_flags
& (1 << SECURITY_ADDSECURIDS))) {
upgrade_secur_desc(scx->vol,
securattr, dir_ni);
}
if (test_nino_flag(dir_ni, v3_Extensions)) {
enter_cache(scx, dir_ni, uid,
gid, pxdesc);
}
if (scx->vol->secure_flags
& (1 << SECURITY_ACL))
pydesc = ntfs_build_inherited_posix(
pxdesc, mode,
scx->umask, isdir);
else
pydesc = ntfs_build_basic_posix(
pxdesc, mode,
scx->umask, isdir);
free(pxdesc);
}
free(securattr);
}
}
return (pydesc);
}
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, ntfs_inode *dir_ni,
mode_t mode, BOOL isdir)
{
#if !FORCE_FORMAT_v1x
const struct CACHED_SECURID *cached;
struct CACHED_SECURID wanted;
struct POSIX_SECURITY *pxdesc;
char *newattr;
int newattrsz;
const SID *usid;
const SID *gsid;
BIGSID defusid;
BIGSID defgsid;
le32 securid;
#endif
securid = const_cpu_to_le32(0);
#if !FORCE_FORMAT_v1x
pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
if (pxdesc) {
wanted.uid = uid;
wanted.gid = gid;
wanted.dmode = pxdesc->mode & mode & 07777;
if (isdir) wanted.dmode |= 0x10000;
wanted.variable = (void*)pxdesc;
wanted.varsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
scx->vol->securid_cache, GENERIC(&wanted),
(cache_compare)compare);
if (cached)
securid = cached->securid;
if (!cached && (scx->vol->major_ver >= 3)) {
usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
if (!usid || !gsid) {
ntfs_log_error("File created by an unmapped user/group %d/%d\n",
(int)uid, (int)gid);
usid = gsid = adminsid;
}
newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
isdir, usid, gsid);
if (newattr) {
newattrsz = ntfs_attr_size(newattr);
securid = setsecurityattr(scx->vol,
(const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
newattrsz);
if (securid) {
wanted.securid = securid;
ntfs_enter_cache(scx->vol->securid_cache,
GENERIC(&wanted),
(cache_compare)compare);
}
free(newattr);
} else {
}
}
free(pxdesc);
}
#endif
return (securid);
}
int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, uid_t uid, gid_t gid,
ntfs_inode *dir_ni, mode_t mode)
{
struct POSIX_SECURITY *pxdesc;
char *newattr;
const SID *usid;
const SID *gsid;
BIGSID defusid;
BIGSID defgsid;
BOOL isdir;
int res;
res = -1;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
if (pxdesc) {
usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
if (!usid || !gsid) {
ntfs_log_error("File created by an unmapped user/group %d/%d\n",
(int)uid, (int)gid);
usid = gsid = adminsid;
}
newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
isdir, usid, gsid);
if (newattr) {
res = update_secur_descr(scx->vol, newattr, ni);
if (!res && !isdir) {
if (mode & S_IWUSR)
ni->flags &= ~FILE_ATTR_READONLY;
else
ni->flags |= FILE_ATTR_READONLY;
}
#if CACHE_LEGACY_SIZE
if (isdir && !ni->security_id) {
struct CACHED_PERMISSIONS_LEGACY legacy;
legacy.mft_no = ni->mft_no;
legacy.variable = pxdesc;
legacy.varsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare,0);
}
#endif
free(newattr);
} else {
}
}
return (res);
}
#else
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
{
#if !FORCE_FORMAT_v1x
const struct CACHED_SECURID *cached;
struct CACHED_SECURID wanted;
char *newattr;
int newattrsz;
const SID *usid;
const SID *gsid;
BIGSID defusid;
BIGSID defgsid;
le32 securid;
#endif
securid = const_cpu_to_le32(0);
#if !FORCE_FORMAT_v1x
wanted.uid = uid;
wanted.gid = gid;
wanted.dmode = mode & 07777;
if (isdir) wanted.dmode |= 0x10000;
wanted.variable = (void*)NULL;
wanted.varsize = 0;
cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
scx->vol->securid_cache, GENERIC(&wanted),
(cache_compare)compare);
if (cached)
securid = cached->securid;
if (!cached && (scx->vol->major_ver >= 3)) {
usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
if (!usid || !gsid) {
ntfs_log_error("File created by an unmapped user/group %d/%d\n",
(int)uid, (int)gid);
usid = gsid = adminsid;
}
newattr = ntfs_build_descr(mode, isdir, usid, gsid);
if (newattr) {
newattrsz = ntfs_attr_size(newattr);
securid = setsecurityattr(scx->vol,
(const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
newattrsz);
if (securid) {
wanted.securid = securid;
ntfs_enter_cache(scx->vol->securid_cache,
GENERIC(&wanted),
(cache_compare)compare);
}
free(newattr);
} else {
}
}
#endif
return (securid);
}
#endif
#if POSIXACLS
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid, gid_t gid, mode_t mode,
struct POSIX_SECURITY *pxdesc)
#else
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid, gid_t gid, mode_t mode)
#endif
{
int res;
const struct CACHED_SECURID *cached;
struct CACHED_SECURID wanted;
char *newattr;
const SID *usid;
const SID *gsid;
BIGSID defusid;
BIGSID defgsid;
BOOL isdir;
res = 0;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
wanted.uid = uid;
wanted.gid = gid;
wanted.dmode = mode & 07777;
if (isdir) wanted.dmode |= 0x10000;
#if POSIXACLS
wanted.variable = (void*)pxdesc;
if (pxdesc)
wanted.varsize = sizeof(struct POSIX_SECURITY)
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
else
wanted.varsize = 0;
#else
wanted.variable = (void*)NULL;
wanted.varsize = 0;
#endif
if (test_nino_flag(ni, v3_Extensions)) {
cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
scx->vol->securid_cache, GENERIC(&wanted),
(cache_compare)compare);
if (cached) {
ni->security_id = cached->securid;
NInoSetDirty(ni);
if (!isdir) {
if (mode & S_IWUSR)
ni->flags &= ~FILE_ATTR_READONLY;
else
ni->flags |= FILE_ATTR_READONLY;
NInoFileNameSetDirty(ni);
}
}
} else cached = (struct CACHED_SECURID*)NULL;
if (!cached) {
usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
if (!usid || !gsid) {
ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
uid, gid);
usid = gsid = adminsid;
}
#if POSIXACLS
if (pxdesc)
newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
isdir, usid, gsid);
else
newattr = ntfs_build_descr(mode,
isdir, usid, gsid);
#else
newattr = ntfs_build_descr(mode,
isdir, usid, gsid);
#endif
if (newattr) {
res = update_secur_descr(scx->vol, newattr, ni);
if (!res) {
if (!isdir) {
if (mode & S_IWUSR)
ni->flags &= ~FILE_ATTR_READONLY;
else
ni->flags |= FILE_ATTR_READONLY;
NInoFileNameSetDirty(ni);
}
if (test_nino_flag(ni, v3_Extensions)) {
wanted.securid = ni->security_id;
ntfs_enter_cache(scx->vol->securid_cache,
GENERIC(&wanted),
(cache_compare)compare);
}
#if CACHE_LEGACY_SIZE
if (isdir && !ni->security_id) {
struct CACHED_PERMISSIONS_LEGACY legacy;
legacy.mft_no = ni->mft_no;
#if POSIXACLS
legacy.variable = wanted.variable;
legacy.varsize = wanted.varsize;
#else
legacy.variable = (void*)NULL;
legacy.varsize = 0;
#endif
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare,0);
}
#endif
}
free(newattr);
} else {
res = -1;
}
}
return (res);
}
BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
{
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
const SID *usid;
uid_t processuid;
uid_t uid;
BOOL gotowner;
int allowed;
processuid = scx->uid;
if (!scx->mapping[MAPUSERS] || !processuid)
allowed = TRUE;
else {
gotowner = FALSE;
cached = fetch_cache(scx, ni);
if (cached) {
uid = cached->uid;
gotowner = TRUE;
} else {
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
#if OWNERFROMACL
usid = ntfs_acl_owner(oldattr);
#else
const SECURITY_DESCRIPTOR_RELATIVE *phead;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
oldattr;
usid = (const SID*)&oldattr
[le32_to_cpu(phead->owner)];
#endif
uid = ntfs_find_user(scx->mapping[MAPUSERS],
usid);
gotowner = TRUE;
free(oldattr);
}
}
if (gotowner
&& (!processuid || (processuid == uid)))
allowed = TRUE;
else {
allowed = FALSE;
errno = EPERM;
}
}
return (allowed);
}
#if POSIXACLS
int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
const char *name, const char *value, size_t size,
int flags)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
uid_t processuid;
const SID *usid;
const SID *gsid;
uid_t uid;
uid_t gid;
int res;
BOOL isdir;
BOOL deflt;
BOOL exist;
int count;
struct POSIX_SECURITY *oldpxdesc;
struct POSIX_SECURITY *newpxdesc;
res = -1;
deflt = !strcmp(name,"system.posix_acl_default");
if (size)
count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
else
count = 0;
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
newpxdesc = (struct POSIX_SECURITY*)NULL;
if ((!value
|| (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
&& (!deflt || isdir || (!size && !value))) {
cached = fetch_cache(scx, ni);
if (cached) {
uid = cached->uid;
gid = cached->gid;
oldpxdesc = cached->pxdesc;
if (oldpxdesc) {
newpxdesc = ntfs_replace_acl(oldpxdesc,
(const struct POSIX_ACL*)value,count,deflt);
}
} else {
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
#if OWNERFROMACL
usid = ntfs_acl_owner(oldattr);
#else
usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
#endif
gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
oldattr, usid, gsid, isdir);
if (oldpxdesc) {
if (deflt)
exist = oldpxdesc->defcnt > 0;
else
exist = oldpxdesc->acccnt > 3;
if ((exist && (flags & XATTR_CREATE))
|| (!exist && (flags & XATTR_REPLACE))) {
errno = (exist ? EEXIST : ENODATA);
} else {
newpxdesc = ntfs_replace_acl(oldpxdesc,
(const struct POSIX_ACL*)value,count,deflt);
}
free(oldpxdesc);
}
free(oldattr);
}
}
} else
errno = EINVAL;
if (newpxdesc) {
processuid = scx->uid;
if (!processuid || (uid == processuid)) {
if (processuid && (gid != scx->gid)
&& !groupmember(scx, scx->uid, gid)) {
newpxdesc->mode &= ~S_ISGID;
}
res = ntfs_set_owner_mode(scx, ni, uid, gid,
newpxdesc->mode, newpxdesc);
} else
errno = EPERM;
free(newpxdesc);
}
return (res ? -1 : 0);
}
int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
const char *name)
{
return (ntfs_set_posix_acl(scx, ni, name,
(const char*)NULL, 0, 0));
}
#endif
int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
const char *value, size_t size, int flags)
{
char *attr;
int res;
res = -1;
if ((size > 0)
&& !(flags & XATTR_CREATE)
&& ntfs_valid_descr(value,size)
&& (ntfs_attr_size(value) == size)) {
attr = (char*)ntfs_malloc(size);
if (attr) {
memcpy(attr,value,size);
res = update_secur_descr(scx->vol, attr, ni);
#if CACHE_LEGACY_SIZE
if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
&& !ni->security_id) {
struct CACHED_PERMISSIONS_LEGACY legacy;
legacy.mft_no = ni->mft_no;
legacy.variable = (char*)NULL;
legacy.varsize = 0;
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare,0);
}
#endif
free(attr);
} else
errno = ENOMEM;
} else
errno = EINVAL;
return (res ? -1 : 0);
}
int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
const SID *usid;
const SID *gsid;
uid_t processuid;
uid_t uid;
uid_t gid;
int res;
#if POSIXACLS
BOOL isdir;
int pxsize;
const struct POSIX_SECURITY *oldpxdesc;
struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
#endif
res = 0;
cached = fetch_cache(scx, ni);
if (cached) {
uid = cached->uid;
gid = cached->gid;
#if POSIXACLS
oldpxdesc = cached->pxdesc;
if (oldpxdesc) {
pxsize = sizeof(struct POSIX_SECURITY)
+ (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
if (newpxdesc) {
memcpy(newpxdesc, oldpxdesc, pxsize);
if (ntfs_merge_mode_posix(newpxdesc, mode))
res = -1;
} else
res = -1;
} else
newpxdesc = (struct POSIX_SECURITY*)NULL;
#endif
} else {
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
#if OWNERFROMACL
usid = ntfs_acl_owner(oldattr);
#else
usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
#endif
gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
#if POSIXACLS
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
newpxdesc = ntfs_build_permissions_posix(scx->mapping,
oldattr, usid, gsid, isdir);
if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
res = -1;
#endif
free(oldattr);
} else
res = -1;
}
if (!res) {
processuid = scx->uid;
if (!processuid || (uid == processuid)) {
if (processuid && (gid != scx->gid)
&& !groupmember(scx, scx->uid, gid))
mode &= ~S_ISGID;
#if POSIXACLS
if (newpxdesc) {
newpxdesc->mode = mode;
res = ntfs_set_owner_mode(scx, ni, uid, gid,
mode, newpxdesc);
} else
res = ntfs_set_owner_mode(scx, ni, uid, gid,
mode, newpxdesc);
#else
res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
#endif
} else {
errno = EPERM;
res = -1;
}
} else {
ntfs_log_error("File has no security descriptor\n");
res = -1;
errno = EIO;
}
#if POSIXACLS
if (newpxdesc) free(newpxdesc);
#endif
return (res ? -1 : 0);
}
int ntfs_sd_add_everyone(ntfs_inode *ni)
{
SECURITY_DESCRIPTOR_RELATIVE *sd;
ACL *acl;
ACCESS_ALLOWED_ACE *ace;
SID *sid;
int ret, sd_len;
sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
if (!sd)
return -1;
sd->revision = SECURITY_DESCRIPTOR_REVISION;
sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
sid->revision = SID_REVISION;
sid->sub_authority_count = 2;
sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
sid->identifier_authority.value[5] = 5;
sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
sid = (SID*)((u8*)sid + sizeof(SID) + 4);
sid->revision = SID_REVISION;
sid->sub_authority_count = 2;
sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
sid->identifier_authority.value[5] = 5;
sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
acl->revision = ACL_REVISION;
acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
acl->ace_count = const_cpu_to_le16(1);
sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
ace->type = ACCESS_ALLOWED_ACE_TYPE;
ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
ace->mask = const_cpu_to_le32(0x1f01ff);
ace->sid.revision = SID_REVISION;
ace->sid.sub_authority_count = 1;
ace->sid.sub_authority[0] = const_cpu_to_le32(0);
ace->sid.identifier_authority.value[5] = 1;
ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
sd_len);
if (ret)
ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
free(sd);
return ret;
}
int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni,
int accesstype)
{
int perm;
int res;
int allow;
struct stat stbuf;
if (!scx->mapping[MAPUSERS]
|| (!scx->uid
&& (!(accesstype & S_IEXEC)
|| (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
allow = 1;
else {
perm = ntfs_get_perm(scx, ni, accesstype);
if (perm >= 0) {
res = EACCES;
switch (accesstype) {
case S_IEXEC:
allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
break;
case S_IWRITE:
allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
break;
case S_IWRITE + S_IEXEC:
allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
&& ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
break;
case S_IREAD:
allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
break;
case S_IREAD + S_IEXEC:
allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
&& ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
break;
case S_IREAD + S_IWRITE:
allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
&& ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
break;
case S_IWRITE + S_IEXEC + S_ISVTX:
if (perm & S_ISVTX) {
if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
&& (stbuf.st_uid == scx->uid))
allow = 1;
else
allow = 2;
} else
allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
&& ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
break;
case S_IREAD + S_IWRITE + S_IEXEC:
allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
&& ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
&& ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
break;
default :
res = EINVAL;
allow = 0;
break;
}
if (!allow)
errno = res;
} else
allow = 0;
}
return (allow);
}
int ntfs_allowed_create(struct SECURITY_CONTEXT *scx,
ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid)
{
int perm;
int res;
int allow;
struct stat stbuf;
if (!scx->mapping[MAPUSERS])
perm = 0777;
else
perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
if (!scx->mapping[MAPUSERS]
|| !scx->uid) {
allow = 1;
} else {
perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
if (perm >= 0) {
res = EACCES;
allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
&& ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
if (!allow)
errno = res;
} else
allow = 0;
}
*pgid = scx->gid;
*pdsetgid = 0;
if (allow && (perm & S_ISGID)) {
if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) {
*pdsetgid = stbuf.st_mode & S_ISGID;
if (perm & S_ISGID)
*pgid = stbuf.st_gid;
}
}
return (allow);
}
#if 0
BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
const char *path, int accesstype)
{
int allow;
char *dirpath;
char *name;
ntfs_inode *ni;
ntfs_inode *dir_ni;
struct stat stbuf;
allow = 0;
dirpath = strdup(path);
if (dirpath) {
name = strrchr(dirpath, '/');
*name = 0;
dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
if (dir_ni) {
allow = ntfs_allowed_access(scx,
dir_ni, accesstype);
ntfs_inode_close(dir_ni);
if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
&& (allow == 2)) {
ni = ntfs_pathname_to_inode(scx->vol, NULL,
path);
allow = FALSE;
if (ni) {
allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
&& (stbuf.st_uid == scx->uid);
ntfs_inode_close(ni);
}
}
}
free(dirpath);
}
return (allow);
}
#endif
int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid, gid_t gid)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
const SID *usid;
const SID *gsid;
uid_t fileuid;
uid_t filegid;
mode_t mode;
int perm;
BOOL isdir;
int res;
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
BOOL pxdescbuilt = FALSE;
#endif
res = 0;
oldattr = (char*)NULL;
cached = fetch_cache(scx,ni);
if (cached) {
fileuid = cached->uid;
filegid = cached->gid;
mode = cached->mode;
#if POSIXACLS
pxdesc = cached->pxdesc;
if (!pxdesc)
res = -1;
#endif
} else {
fileuid = 0;
filegid = 0;
mode = 0;
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
oldattr;
gsid = (const SID*)
&oldattr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
usid = ntfs_acl_owner(oldattr);
#else
usid = (const SID*)
&oldattr[le32_to_cpu(phead->owner)];
#endif
#if POSIXACLS
pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
usid, gsid, isdir);
if (pxdesc) {
pxdescbuilt = TRUE;
fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
mode = perm = pxdesc->mode;
} else
res = -1;
#else
mode = perm = ntfs_build_permissions(oldattr,
usid, gsid, isdir);
if (perm >= 0) {
fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
} else
res = -1;
#endif
free(oldattr);
} else
res = -1;
}
if (!res) {
if (!scx->uid
|| ((((int)uid < 0) || (uid == fileuid))
&& ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
&& (fileuid == scx->uid))) {
if ((int)uid < 0)
uid = fileuid;
if ((int)gid < 0)
gid = filegid;
#if !defined(__sun) || !defined (__SVR4)
if (uid && (fileuid != uid))
mode &= 01777;
#endif
#if POSIXACLS
res = ntfs_set_owner_mode(scx, ni, uid, gid,
mode, pxdesc);
#else
res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
#endif
} else {
res = -1;
errno = EPERM;
}
#if POSIXACLS
if (pxdescbuilt)
free(pxdesc);
#endif
} else {
ntfs_log_error("File has no security descriptor\n");
res = -1;
errno = EIO;
}
return (res ? -1 : 0);
}
int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid, gid_t gid, const mode_t mode)
{
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
uid_t fileuid;
uid_t filegid;
int res;
#if POSIXACLS
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const SID *usid;
const SID *gsid;
BOOL isdir;
const struct POSIX_SECURITY *oldpxdesc;
struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
int pxsize;
#endif
res = 0;
oldattr = (char*)NULL;
cached = fetch_cache(scx,ni);
if (cached) {
fileuid = cached->uid;
filegid = cached->gid;
#if POSIXACLS
oldpxdesc = cached->pxdesc;
if (oldpxdesc) {
pxsize = sizeof(struct POSIX_SECURITY)
+ (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
if (newpxdesc) {
memcpy(newpxdesc, oldpxdesc, pxsize);
if (ntfs_merge_mode_posix(newpxdesc, mode))
res = -1;
} else
res = -1;
}
#endif
} else {
fileuid = 0;
filegid = 0;
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
#if POSIXACLS
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
oldattr;
gsid = (const SID*)
&oldattr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
usid = ntfs_acl_owner(oldattr);
#else
usid = (const SID*)
&oldattr[le32_to_cpu(phead->owner)];
#endif
newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
usid, gsid, isdir);
if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
res = -1;
else {
fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
}
#endif
free(oldattr);
} else
res = -1;
}
if (!res) {
if (!scx->uid
|| ((((int)uid < 0) || (uid == fileuid))
&& ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
&& (fileuid == scx->uid))) {
if ((int)uid < 0)
uid = fileuid;
if ((int)gid < 0)
gid = filegid;
#if POSIXACLS
res = ntfs_set_owner_mode(scx, ni, uid, gid,
mode, newpxdesc);
#else
res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
#endif
} else {
res = -1;
errno = EPERM;
}
} else {
ntfs_log_error("File has no security descriptor\n");
res = -1;
errno = EIO;
}
#if POSIXACLS
free(newpxdesc);
#endif
return (res ? -1 : 0);
}
static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
const char *parentattr, BOOL fordir)
{
const SECURITY_DESCRIPTOR_RELATIVE *pphead;
const ACL *ppacl;
const SID *usid;
const SID *gsid;
BIGSID defusid;
BIGSID defgsid;
int offpacl;
int offgroup;
SECURITY_DESCRIPTOR_RELATIVE *pnhead;
ACL *pnacl;
int parentattrsz;
char *newattr;
int newattrsz;
int aclsz;
int usidsz;
int gsidsz;
int pos;
le32 securid;
parentattrsz = ntfs_attr_size(parentattr);
pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
if (scx->mapping[MAPUSERS]) {
usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
#if OWNERFROMACL
if (!gsid)
gsid = adminsid;
if (!usid) {
usid = ntfs_acl_owner(parentattr);
if (!ntfs_is_user_sid(gsid))
gsid = usid;
}
#else
if (!usid)
usid = adminsid;
if (!gsid)
gsid = adminsid;
#endif
} else {
if (!scx->uid)
usid = adminsid;
else {
#if OWNERFROMACL
usid = ntfs_acl_owner(parentattr);
#else
int offowner;
offowner = le32_to_cpu(pphead->owner);
usid = (const SID*)&parentattr[offowner];
#endif
}
if (!scx->gid)
gsid = adminsid;
else {
offgroup = le32_to_cpu(pphead->group);
gsid = (const SID*)&parentattr[offgroup];
}
}
usidsz = ntfs_sid_size(usid);
gsidsz = ntfs_sid_size(gsid);
newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
if (fordir)
newattrsz *= 2;
newattr = (char*)ntfs_malloc(newattrsz);
if (newattr) {
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
pnhead->alignment = 0;
pnhead->control = (pphead->control
& (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED))
| SE_SELF_RELATIVE;
pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
pnhead->dacl = const_cpu_to_le32(0);
if (pphead->dacl) {
offpacl = le32_to_cpu(pphead->dacl);
ppacl = (const ACL*)&parentattr[offpacl];
pnacl = (ACL*)&newattr[pos];
aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
fordir, pphead->control
& SE_DACL_AUTO_INHERITED);
if (aclsz) {
pnhead->dacl = cpu_to_le32(pos);
pos += aclsz;
pnhead->control |= SE_DACL_PRESENT;
}
}
pnhead->sacl = const_cpu_to_le32(0);
if (pphead->sacl) {
offpacl = le32_to_cpu(pphead->sacl);
ppacl = (const ACL*)&parentattr[offpacl];
pnacl = (ACL*)&newattr[pos];
aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
fordir, pphead->control
& SE_SACL_AUTO_INHERITED);
if (aclsz) {
pnhead->sacl = cpu_to_le32(pos);
pos += aclsz;
pnhead->control |= SE_SACL_PRESENT;
}
}
memcpy(&newattr[pos],usid,usidsz);
pnhead->owner = cpu_to_le32(pos);
pos += usidsz;
memcpy(&newattr[pos],gsid,gsidsz);
pnhead->group = cpu_to_le32(pos);
pos += gsidsz;
securid = setsecurityattr(scx->vol,
(SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
free(newattr);
} else
securid = const_cpu_to_le32(0);
return (securid);
}
le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
ntfs_inode *dir_ni, BOOL fordir)
{
struct CACHED_PERMISSIONS *cached;
char *parentattr;
le32 securid;
securid = const_cpu_to_le32(0);
cached = (struct CACHED_PERMISSIONS*)NULL;
if (test_nino_flag(dir_ni, v3_Extensions)
&& dir_ni->security_id) {
cached = fetch_cache(scx, dir_ni);
if (cached
&& (cached->uid == scx->uid) && (cached->gid == scx->gid))
securid = (fordir ? cached->inh_dirid
: cached->inh_fileid);
}
if (!securid) {
parentattr = getsecurityattr(scx->vol, dir_ni);
if (parentattr) {
securid = build_inherited_id(scx,
parentattr, fordir);
free(parentattr);
if (securid) {
cached = fetch_cache(scx, dir_ni);
if (cached
&& (cached->uid == scx->uid)
&& (cached->gid == scx->gid)) {
if (fordir)
cached->inh_dirid = securid;
else
cached->inh_fileid = securid;
}
}
}
}
return (securid);
}
static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
gid_t gid)
{
struct group *group;
char **grmem;
int grcnt;
gid_t *groups;
int res;
res = 0;
group = getgrgid(gid);
if (group && group->gr_mem) {
grcnt = usermapping->grcnt;
groups = usermapping->groups;
grmem = group->gr_mem;
while (*grmem && strcmp(user->pw_name, *grmem))
grmem++;
if (*grmem) {
if (!grcnt)
groups = (gid_t*)malloc(sizeof(gid_t));
else
groups = (gid_t*)realloc(groups,
(grcnt+1)*sizeof(gid_t));
if (groups)
groups[grcnt++] = gid;
else {
res = -1;
errno = ENOMEM;
}
}
usermapping->grcnt = grcnt;
usermapping->groups = groups;
}
return (res);
}
static int link_group_members(struct SECURITY_CONTEXT *scx)
{
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
struct passwd *user;
int res;
res = 0;
for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
usermapping=usermapping->next) {
usermapping->grcnt = 0;
usermapping->groups = (gid_t*)NULL;
user = getpwuid(usermapping->xid);
if (user && user->pw_name) {
for (groupmapping=scx->mapping[MAPGROUPS];
groupmapping && !res;
groupmapping=groupmapping->next) {
if (link_single_group(usermapping, user,
groupmapping->xid))
res = -1;
}
if (!res && link_single_group(usermapping,
user, (gid_t)0))
res = -1;
}
}
return (res);
}
static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, const SID *usid)
{
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
SID *sid;
int sidsz;
int res;
res = -1;
sidsz = ntfs_sid_size(usid);
sid = (SID*)ntfs_malloc(sidsz);
if (sid) {
memcpy(sid,usid,sidsz);
usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
if (usermapping) {
groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
if (groupmapping) {
usermapping->sid = sid;
usermapping->xid = uid;
usermapping->next = (struct MAPPING*)NULL;
groupmapping->sid = sid;
groupmapping->xid = gid;
groupmapping->next = (struct MAPPING*)NULL;
scx->mapping[MAPUSERS] = usermapping;
scx->mapping[MAPGROUPS] = groupmapping;
res = 0;
}
}
}
return (res);
}
#if 0
static BOOL check_mapping(const struct MAPPING *usermapping,
const struct MAPPING *groupmapping)
{
const struct MAPPING *mapping1;
const struct MAPPING *mapping2;
BOOL ambiguous;
ambiguous = FALSE;
for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
if (mapping1->xid != mapping2->xid)
ambiguous = TRUE;
} else {
if (mapping1->xid == mapping2->xid)
ambiguous = TRUE;
}
for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
if (mapping1->xid != mapping2->xid)
ambiguous = TRUE;
} else {
if (mapping1->xid == mapping2->xid)
ambiguous = TRUE;
}
return (ambiguous);
}
#endif
#if 0
static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
ntfs_inode *ni;
char *securattr;
const SID *usid;
int res;
res = -1;
ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
if (ni) {
securattr = getsecurityattr(scx->vol, ni);
if (securattr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
if (ntfs_is_user_sid(usid))
res = ntfs_do_default_mapping(scx,
scx->uid, scx->gid, usid);
free(securattr);
}
ntfs_inode_close(ni);
}
return (res);
}
#endif
static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
{
return (read(*(int*)fileid, buf, size));
}
static int localread(void *fileid, char *buf, size_t size, off_t offs)
{
return (ntfs_attr_data_read((ntfs_inode*)fileid,
AT_UNNAMED, 0, buf, size, offs));
}
int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
BOOL allowdef)
{
struct MAPLIST *item;
struct MAPLIST *firstitem;
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
ntfs_inode *ni;
int fd;
static struct {
u8 revision;
u8 levels;
be16 highbase;
be32 lowbase;
le32 level1;
le32 level2;
le32 level3;
le32 level4;
le32 level5;
} defmap = {
1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
const_cpu_to_le32(21),
const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
} ;
scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
if (!usermap_path) usermap_path = MAPPINGFILE;
if (usermap_path[0] == '/') {
fd = open(usermap_path,O_RDONLY);
if (fd > 0) {
firstitem = ntfs_read_mapping(basicread, (void*)&fd);
close(fd);
} else
firstitem = (struct MAPLIST*)NULL;
} else {
ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
if (ni) {
firstitem = ntfs_read_mapping(localread, ni);
ntfs_inode_close(ni);
} else
firstitem = (struct MAPLIST*)NULL;
}
if (firstitem) {
usermapping = ntfs_do_user_mapping(firstitem);
groupmapping = ntfs_do_group_mapping(firstitem);
if (usermapping && groupmapping) {
scx->mapping[MAPUSERS] = usermapping;
scx->mapping[MAPGROUPS] = groupmapping;
} else
ntfs_log_error("There were no valid user or no valid group\n");
while (firstitem) {
item = firstitem->next;
free(firstitem);
firstitem = item;
}
} else {
if (allowdef) {
if (!ntfs_do_default_mapping(scx,
0, 0, (const SID*)&defmap))
ntfs_log_info("Using default user mapping\n");
}
}
return (!scx->mapping[MAPUSERS] || link_group_members(scx));
}
int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
{
u32 attrib;
size_t outsize;
outsize = 0;
if (ni) {
attrib = le32_to_cpu(ni->flags);
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
else
attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
if (!attrib)
attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
outsize = sizeof(FILE_ATTR_FLAGS);
if (size >= outsize) {
if (value)
memcpy(value,&attrib,outsize);
else
errno = EINVAL;
}
}
return (outsize ? (int)outsize : -errno);
}
int ntfs_set_ntfs_attrib(ntfs_inode *ni,
const char *value, size_t size, int flags)
{
u32 attrib;
le32 settable;
ATTR_FLAGS dirflags;
int res;
res = -1;
if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
if (!(flags & XATTR_CREATE)) {
memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
settable = FILE_ATTR_SETTABLE;
res = 0;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
settable |= FILE_ATTR_COMPRESSED;
if ((ni->flags ^ cpu_to_le32(attrib))
& FILE_ATTR_COMPRESSED) {
if (ni->flags & FILE_ATTR_COMPRESSED)
dirflags = const_cpu_to_le16(0);
else
dirflags = ATTR_IS_COMPRESSED;
res = ntfs_attr_set_flags(ni,
AT_INDEX_ROOT,
NTFS_INDEX_I30, 4,
dirflags,
ATTR_COMPRESSION_MASK);
}
}
if (!res) {
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoFileNameSetDirty(ni);
NInoSetDirty(ni);
}
} else
errno = EEXIST;
} else
errno = EINVAL;
return (res ? -1 : 0);
}
int ntfs_open_secure(ntfs_volume *vol)
{
ntfs_inode *ni;
ntfs_index_context *sii;
ntfs_index_context *sdh;
if (vol->secure_ni)
return 0;
ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
if (!ni)
goto err;
if (ni->mft_no != FILE_Secure) {
ntfs_log_error("$Secure does not have expected inode number!");
errno = EINVAL;
goto err_close_ni;
}
sii = ntfs_index_ctx_get(ni, sii_stream, 4);
if (!sii)
goto err_close_ni;
sdh = ntfs_index_ctx_get(ni, sdh_stream, 4);
if (!sdh)
goto err_close_sii;
vol->secure_xsdh = sdh;
vol->secure_xsii = sii;
vol->secure_ni = ni;
return 0;
err_close_sii:
ntfs_index_ctx_put(sii);
err_close_ni:
ntfs_inode_close(ni);
err:
if (vol->major_ver < 3)
return 0;
ntfs_log_perror("Failed to open $Secure");
return -1;
}
int ntfs_close_secure(ntfs_volume *vol)
{
int res = 0;
if (vol->secure_ni) {
ntfs_index_ctx_put(vol->secure_xsdh);
ntfs_index_ctx_put(vol->secure_xsii);
res = ntfs_inode_close(vol->secure_ni);
vol->secure_ni = NULL;
}
return res;
}
void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx)
{
ntfs_free_mapping(scx->mapping);
free_caches(scx);
}
static BOOL feedsecurityattr(const char *attr, u32 selection,
char *buf, u32 buflen, u32 *psize)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
SECURITY_DESCRIPTOR_RELATIVE *pnhead;
const ACL *pdacl;
const ACL *psacl;
const SID *pusid;
const SID *pgsid;
unsigned int offdacl;
unsigned int offsacl;
unsigned int offowner;
unsigned int offgroup;
unsigned int daclsz;
unsigned int saclsz;
unsigned int usidsz;
unsigned int gsidsz;
unsigned int size;
BOOL ok;
unsigned int pos;
unsigned int avail;
le16 control;
avail = 0;
control = SE_SELF_RELATIVE;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
offdacl = le32_to_cpu(phead->dacl);
pdacl = (const ACL*)&attr[offdacl];
daclsz = le16_to_cpu(pdacl->size);
size += daclsz;
avail |= DACL_SECURITY_INFORMATION;
} else
offdacl = daclsz = 0;
offowner = le32_to_cpu(phead->owner);
if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
pusid = (const SID*)&attr[offowner];
usidsz = ntfs_sid_size(pusid);
size += usidsz;
avail |= OWNER_SECURITY_INFORMATION;
} else
offowner = usidsz = 0;
offgroup = le32_to_cpu(phead->group);
if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
pgsid = (const SID*)&attr[offgroup];
gsidsz = ntfs_sid_size(pgsid);
size += gsidsz;
avail |= GROUP_SECURITY_INFORMATION;
} else
offgroup = gsidsz = 0;
if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
offsacl = le32_to_cpu(phead->sacl);
psacl = (const ACL*)&attr[offsacl];
saclsz = le16_to_cpu(psacl->size);
size += saclsz;
avail |= SACL_SECURITY_INFORMATION;
} else
offsacl = saclsz = 0;
if (size > buflen) {
*psize = size;
errno = EINVAL;
ok = FALSE;
} else {
if (selection & OWNER_SECURITY_INFORMATION)
control |= phead->control & SE_OWNER_DEFAULTED;
if (selection & GROUP_SECURITY_INFORMATION)
control |= phead->control & SE_GROUP_DEFAULTED;
if (selection & DACL_SECURITY_INFORMATION)
control |= phead->control
& (SE_DACL_PRESENT
| SE_DACL_DEFAULTED
| SE_DACL_AUTO_INHERITED
| SE_DACL_PROTECTED);
if (selection & SACL_SECURITY_INFORMATION)
control |= phead->control
& (SE_SACL_PRESENT
| SE_SACL_DEFAULTED
| SE_SACL_AUTO_INHERITED
| SE_SACL_PROTECTED);
memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
pnhead->control = control;
pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
if (selection & avail & DACL_SECURITY_INFORMATION) {
pnhead->dacl = cpu_to_le32(pos);
memcpy(&buf[pos],&attr[offdacl],daclsz);
pos += daclsz;
} else
pnhead->dacl = const_cpu_to_le32(0);
if (selection & avail & SACL_SECURITY_INFORMATION) {
pnhead->sacl = cpu_to_le32(pos);
memcpy(&buf[pos],&attr[offsacl],saclsz);
pos += saclsz;
} else
pnhead->sacl = const_cpu_to_le32(0);
if (selection & avail & OWNER_SECURITY_INFORMATION) {
pnhead->owner = cpu_to_le32(pos);
memcpy(&buf[pos],&attr[offowner],usidsz);
pos += usidsz;
} else
pnhead->owner = const_cpu_to_le32(0);
if (selection & avail & GROUP_SECURITY_INFORMATION) {
pnhead->group = cpu_to_le32(pos);
memcpy(&buf[pos],&attr[offgroup],gsidsz);
pos += gsidsz;
} else
pnhead->group = const_cpu_to_le32(0);
if (pos != size)
ntfs_log_error("Error in security descriptor size\n");
*psize = size;
ok = TRUE;
}
return (ok);
}
static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
const char *newattr, u32 selection, ntfs_inode *ni)
{
const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
const SECURITY_DESCRIPTOR_RELATIVE *newhead;
SECURITY_DESCRIPTOR_RELATIVE *targhead;
const ACL *pdacl;
const ACL *psacl;
const SID *powner;
const SID *pgroup;
int offdacl;
int offsacl;
int offowner;
int offgroup;
unsigned int size;
le16 control;
char *target;
int pos;
int oldattrsz;
int newattrsz;
BOOL ok;
ok = FALSE;
oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
oldattrsz = ntfs_attr_size(oldattr);
newattrsz = ntfs_attr_size(newattr);
target = (char*)ntfs_malloc(oldattrsz + newattrsz);
if (target) {
targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
control = SE_SELF_RELATIVE;
if ((selection & DACL_SECURITY_INFORMATION) ?
newhead->dacl : oldhead->dacl) {
if (selection & DACL_SECURITY_INFORMATION) {
offdacl = le32_to_cpu(newhead->dacl);
pdacl = (const ACL*)&newattr[offdacl];
} else {
offdacl = le32_to_cpu(oldhead->dacl);
pdacl = (const ACL*)&oldattr[offdacl];
}
size = le16_to_cpu(pdacl->size);
memcpy(&target[pos], pdacl, size);
targhead->dacl = cpu_to_le32(pos);
pos += size;
} else
targhead->dacl = const_cpu_to_le32(0);
if (selection & DACL_SECURITY_INFORMATION) {
control |= newhead->control
& (SE_DACL_PRESENT
| SE_DACL_DEFAULTED
| SE_DACL_PROTECTED);
if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
control |= SE_DACL_AUTO_INHERITED;
} else
control |= oldhead->control
& (SE_DACL_PRESENT
| SE_DACL_DEFAULTED
| SE_DACL_AUTO_INHERITED
| SE_DACL_PROTECTED);
if ((selection & SACL_SECURITY_INFORMATION) ?
newhead->sacl : oldhead->sacl) {
if (selection & SACL_SECURITY_INFORMATION) {
offsacl = le32_to_cpu(newhead->sacl);
psacl = (const ACL*)&newattr[offsacl];
} else {
offsacl = le32_to_cpu(oldhead->sacl);
psacl = (const ACL*)&oldattr[offsacl];
}
size = le16_to_cpu(psacl->size);
memcpy(&target[pos], psacl, size);
targhead->sacl = cpu_to_le32(pos);
pos += size;
} else
targhead->sacl = const_cpu_to_le32(0);
if (selection & SACL_SECURITY_INFORMATION) {
control |= newhead->control
& (SE_SACL_PRESENT
| SE_SACL_DEFAULTED
| SE_SACL_PROTECTED);
if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
control |= SE_SACL_AUTO_INHERITED;
} else
control |= oldhead->control
& (SE_SACL_PRESENT
| SE_SACL_DEFAULTED
| SE_SACL_AUTO_INHERITED
| SE_SACL_PROTECTED);
if ((selection & OWNER_SECURITY_INFORMATION) ?
newhead->owner : oldhead->owner) {
if (selection & OWNER_SECURITY_INFORMATION) {
offowner = le32_to_cpu(newhead->owner);
powner = (const SID*)&newattr[offowner];
} else {
offowner = le32_to_cpu(oldhead->owner);
powner = (const SID*)&oldattr[offowner];
}
size = ntfs_sid_size(powner);
memcpy(&target[pos], powner, size);
targhead->owner = cpu_to_le32(pos);
pos += size;
} else
targhead->owner = const_cpu_to_le32(0);
if (selection & OWNER_SECURITY_INFORMATION)
control |= newhead->control & SE_OWNER_DEFAULTED;
else
control |= oldhead->control & SE_OWNER_DEFAULTED;
if ((selection & GROUP_SECURITY_INFORMATION) ?
newhead->group : oldhead->group) {
if (selection & GROUP_SECURITY_INFORMATION) {
offgroup = le32_to_cpu(newhead->group);
pgroup = (const SID*)&newattr[offgroup];
control |= newhead->control
& SE_GROUP_DEFAULTED;
} else {
offgroup = le32_to_cpu(oldhead->group);
pgroup = (const SID*)&oldattr[offgroup];
control |= oldhead->control
& SE_GROUP_DEFAULTED;
}
size = ntfs_sid_size(pgroup);
memcpy(&target[pos], pgroup, size);
targhead->group = cpu_to_le32(pos);
pos += size;
} else
targhead->group = const_cpu_to_le32(0);
if (selection & GROUP_SECURITY_INFORMATION)
control |= newhead->control & SE_GROUP_DEFAULTED;
else
control |= oldhead->control & SE_GROUP_DEFAULTED;
targhead->revision = SECURITY_DESCRIPTOR_REVISION;
targhead->alignment = 0;
targhead->control = control;
ok = !update_secur_descr(vol, target, ni);
free(target);
}
return (ok);
}
int ntfs_get_file_security(struct SECURITY_API *scapi,
const char *path, u32 selection,
char *buf, u32 buflen, u32 *psize)
{
ntfs_inode *ni;
char *attr;
int res;
res = 0;
if (scapi && (scapi->magic == MAGIC_API)) {
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
if (ni) {
attr = getsecurityattr(scapi->security.vol, ni);
if (attr) {
if (feedsecurityattr(attr,selection,
buf,buflen,psize)) {
if (test_nino_flag(ni, v3_Extensions)
&& ni->security_id)
res = le32_to_cpu(
ni->security_id);
else
res = -1;
}
free(attr);
}
ntfs_inode_close(ni);
} else
errno = ENOENT;
if (!res) *psize = 0;
} else
errno = EINVAL;
return (res);
}
int ntfs_set_file_security(struct SECURITY_API *scapi,
const char *path, u32 selection, const char *attr)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
ntfs_inode *ni;
int attrsz;
BOOL missing;
char *oldattr;
int res;
res = 0;
if (scapi && (scapi->magic == MAGIC_API) && attr) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
attrsz = ntfs_attr_size(attr);
missing = ((selection & OWNER_SECURITY_INFORMATION)
&& !phead->owner
&& !(phead->control & SE_OWNER_DEFAULTED))
|| ((selection & GROUP_SECURITY_INFORMATION)
&& !phead->group
&& !(phead->control & SE_GROUP_DEFAULTED));
if (!missing
&& (phead->control & SE_SELF_RELATIVE)
&& ntfs_valid_descr(attr, attrsz)) {
ni = ntfs_pathname_to_inode(scapi->security.vol,
NULL, path);
if (ni) {
oldattr = getsecurityattr(scapi->security.vol,
ni);
if (oldattr) {
if (mergesecurityattr(
scapi->security.vol,
oldattr, attr,
selection, ni)) {
if (test_nino_flag(ni,
v3_Extensions))
res = le32_to_cpu(
ni->security_id);
else
res = -1;
}
free(oldattr);
}
ntfs_inode_close(ni);
}
} else
errno = EINVAL;
} else
errno = EINVAL;
return (res);
}
int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
{
ntfs_inode *ni;
s32 attrib;
attrib = -1;
if (scapi && (scapi->magic == MAGIC_API) && path) {
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
if (ni) {
attrib = le32_to_cpu(ni->flags);
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
else
attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
if (!attrib)
attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
ntfs_inode_close(ni);
} else
errno = ENOENT;
} else
errno = EINVAL;
return (attrib);
}
BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
const char *path, s32 attrib)
{
ntfs_inode *ni;
le32 settable;
ATTR_FLAGS dirflags;
int res;
res = 0;
if (scapi && (scapi->magic == MAGIC_API) && path) {
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
if (ni) {
settable = FILE_ATTR_SETTABLE;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
settable |= FILE_ATTR_COMPRESSED;
if ((ni->flags ^ cpu_to_le32(attrib))
& FILE_ATTR_COMPRESSED) {
if (ni->flags & FILE_ATTR_COMPRESSED)
dirflags = const_cpu_to_le16(0);
else
dirflags = ATTR_IS_COMPRESSED;
res = ntfs_attr_set_flags(ni,
AT_INDEX_ROOT,
NTFS_INDEX_I30, 4,
dirflags,
ATTR_COMPRESSION_MASK);
}
}
if (!res) {
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
NInoFileNameSetDirty(ni);
}
if (!ntfs_inode_close(ni))
res = -1;
} else
errno = ENOENT;
}
return (res);
}
BOOL ntfs_read_directory(struct SECURITY_API *scapi,
const char *path, ntfs_filldir_t callback, void *context)
{
ntfs_inode *ni;
BOOL ok;
s64 pos;
ok = FALSE;
if (scapi && (scapi->magic == MAGIC_API) && callback) {
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
if (ni) {
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
pos = 0;
ntfs_readdir(ni,&pos,context,callback);
ok = !ntfs_inode_close(ni);
} else {
ntfs_inode_close(ni);
errno = ENOTDIR;
}
} else
errno = ENOENT;
} else
errno = EINVAL;
return (ok);
}
int ntfs_read_sds(struct SECURITY_API *scapi,
char *buf, u32 size, u32 offset)
{
int got;
got = -1;
if (scapi && (scapi->magic == MAGIC_API)) {
if (scapi->security.vol->secure_ni)
got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
STREAM_SDS, 4, buf, size, offset);
else
errno = EOPNOTSUPP;
} else
errno = EINVAL;
return (got);
}
INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
INDEX_ENTRY *entry)
{
SII_INDEX_KEY key;
INDEX_ENTRY *ret;
BOOL found;
ntfs_index_context *xsii;
ret = (INDEX_ENTRY*)NULL;
if (scapi && (scapi->magic == MAGIC_API)) {
xsii = scapi->security.vol->secure_xsii;
if (xsii) {
if (!entry) {
key.security_id = const_cpu_to_le32(0);
found = !ntfs_index_lookup((char*)&key,
sizeof(SII_INDEX_KEY), xsii);
if (!found && (errno == ENOENT))
ret = xsii->entry;
} else
ret = ntfs_index_next(entry,xsii);
if (!ret)
errno = ENODATA;
} else
errno = EOPNOTSUPP;
} else
errno = EINVAL;
return (ret);
}
INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
INDEX_ENTRY *entry)
{
SDH_INDEX_KEY key;
INDEX_ENTRY *ret;
BOOL found;
ntfs_index_context *xsdh;
ret = (INDEX_ENTRY*)NULL;
if (scapi && (scapi->magic == MAGIC_API)) {
xsdh = scapi->security.vol->secure_xsdh;
if (xsdh) {
if (!entry) {
key.hash = const_cpu_to_le32(0);
key.security_id = const_cpu_to_le32(0);
found = !ntfs_index_lookup((char*)&key,
sizeof(SDH_INDEX_KEY), xsdh);
if (!found && (errno == ENOENT))
ret = xsdh->entry;
} else
ret = ntfs_index_next(entry,xsdh);
if (!ret)
errno = ENODATA;
} else errno = ENOTSUP;
} else
errno = EINVAL;
return (ret);
}
int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
{
const SID *usid;
BIGSID defusid;
int size;
size = 0;
if (scapi && (scapi->magic == MAGIC_API)) {
usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
if (usid) {
size = ntfs_sid_size(usid);
memcpy(buf,usid,size);
} else
errno = ENODATA;
} else
errno = EINVAL;
return (size);
}
int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
{
const SID *gsid;
BIGSID defgsid;
int size;
size = 0;
if (scapi && (scapi->magic == MAGIC_API)) {
gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
if (gsid) {
size = ntfs_sid_size(gsid);
memcpy(buf,gsid,size);
} else
errno = ENODATA;
} else
errno = EINVAL;
return (size);
}
int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
{
int uid;
uid = -1;
if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
if (ntfs_same_sid(usid,adminsid))
uid = 0;
else {
uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
if (!uid) {
uid = -1;
errno = ENODATA;
}
}
} else
errno = EINVAL;
return (uid);
}
int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
{
int gid;
gid = -1;
if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
if (ntfs_same_sid(gsid,adminsid))
gid = 0;
else {
gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
if (!gid) {
gid = -1;
errno = ENODATA;
}
}
} else
errno = EINVAL;
return (gid);
}
struct SECURITY_API *ntfs_initialize_file_security(const char *device,
unsigned long flags)
{
ntfs_volume *vol;
unsigned long mntflag;
int mnt;
struct SECURITY_API *scapi;
struct SECURITY_CONTEXT *scx;
scapi = (struct SECURITY_API*)NULL;
mnt = ntfs_check_if_mounted(device, &mntflag);
if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
vol = ntfs_mount(device, flags);
if (vol) {
scapi = (struct SECURITY_API*)
ntfs_malloc(sizeof(struct SECURITY_API));
if (!ntfs_volume_get_free_space(vol)
&& scapi) {
scapi->magic = MAGIC_API;
scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
scx = &scapi->security;
scx->vol = vol;
scx->uid = getuid();
scx->gid = getgid();
scx->pseccache = &scapi->seccache;
scx->vol->secure_flags = 0;
ntfs_build_mapping(scx,(const char*)NULL,TRUE);
} else {
if (scapi)
free(scapi);
else
errno = ENOMEM;
mnt = ntfs_umount(vol,FALSE);
scapi = (struct SECURITY_API*)NULL;
}
}
} else
if (getuid())
errno = EPERM;
else
errno = EBUSY;
return (scapi);
}
BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
{
int ok;
ntfs_volume *vol;
ok = FALSE;
if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
vol = scapi->security.vol;
ntfs_destroy_security_context(&scapi->security);
free(scapi);
if (!ntfs_umount(vol, 0))
ok = TRUE;
}
return (ok);
}