#include "includes.h"
extern struct current_user current_user;
extern struct generic_mapping file_generic_mapping;
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_ACLS
enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
enum ace_attribute {ALLOW_ACE, DENY_ACE};
typedef union posix_id {
uid_t uid;
gid_t gid;
int world;
} posix_id;
typedef struct canon_ace {
struct canon_ace *next, *prev;
SMB_ACL_TAG_T type;
mode_t perms;
DOM_SID trustee;
enum ace_owner owner_type;
enum ace_attribute attr;
posix_id unix_ug;
BOOL inherited;
} canon_ace;
#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
#define PAI_VERSION_OFFSET 0
#define PAI_FLAG_OFFSET 1
#define PAI_NUM_ENTRIES_OFFSET 2
#define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
#define PAI_ENTRIES_BASE 6
#define PAI_VERSION 1
#define PAI_ACL_FLAG_PROTECTED 0x1
#define PAI_ENTRY_LENGTH 5
struct pai_entry {
struct pai_entry *next, *prev;
enum ace_owner owner_type;
posix_id unix_ug;
};
struct pai_val {
BOOL pai_protected;
unsigned int num_entries;
struct pai_entry *entry_list;
unsigned int num_def_entries;
struct pai_entry *def_entry_list;
};
static uint32 get_pai_entry_val(struct pai_entry *paie)
{
switch (paie->owner_type) {
case UID_ACE:
DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
return (uint32)paie->unix_ug.uid;
case GID_ACE:
DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
return (uint32)paie->unix_ug.gid;
case WORLD_ACE:
default:
DEBUG(10,("get_pai_entry_val: world ace\n"));
return (uint32)-1;
}
}
static uint32 get_entry_val(canon_ace *ace_entry)
{
switch (ace_entry->owner_type) {
case UID_ACE:
DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
return (uint32)ace_entry->unix_ug.uid;
case GID_ACE:
DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
return (uint32)ace_entry->unix_ug.gid;
case WORLD_ACE:
default:
DEBUG(10,("get_entry_val: world ace\n"));
return (uint32)-1;
}
}
static unsigned int num_inherited_entries(canon_ace *ace_list)
{
unsigned int num_entries = 0;
for (; ace_list; ace_list = ace_list->next)
if (ace_list->inherited)
num_entries++;
return num_entries;
}
static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL pai_protected, size_t *store_size)
{
char *pai_buf = NULL;
canon_ace *ace_list = NULL;
char *entry_offset = NULL;
unsigned int num_entries = 0;
unsigned int num_def_entries = 0;
for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
if (ace_list->inherited)
num_entries++;
for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
if (ace_list->inherited)
num_def_entries++;
DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
*store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
pai_buf = (char *)SMB_MALLOC(*store_size);
if (!pai_buf) {
return NULL;
}
memset(pai_buf, '\0', PAI_ENTRIES_BASE);
SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0));
SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
entry_offset = pai_buf + PAI_ENTRIES_BASE;
for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
if (ace_list->inherited) {
uint8 type_val = (unsigned char)ace_list->owner_type;
uint32 entry_val = get_entry_val(ace_list);
SCVAL(entry_offset,0,type_val);
SIVAL(entry_offset,1,entry_val);
entry_offset += PAI_ENTRY_LENGTH;
}
}
for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
if (ace_list->inherited) {
uint8 type_val = (unsigned char)ace_list->owner_type;
uint32 entry_val = get_entry_val(ace_list);
SCVAL(entry_offset,0,type_val);
SIVAL(entry_offset,1,entry_val);
entry_offset += PAI_ENTRY_LENGTH;
}
}
return pai_buf;
}
static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
canon_ace *dir_ace_list, BOOL pai_protected)
{
int ret;
size_t store_size;
char *pai_buf;
if (!lp_map_acl_inherit(SNUM(fsp->conn)))
return;
if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
if (fsp->fh->fd != -1)
SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
else
SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
return;
}
pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size);
if (fsp->fh->fd != -1)
ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, store_size, 0);
else
ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, store_size, 0);
SAFE_FREE(pai_buf);
DEBUG(10,("store_inheritance_attribute:%s for file %s\n", pai_protected ? " (protected)" : "", fsp->fsp_name));
if (ret == -1 && !no_acl_syscall_error(errno))
DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
}
static void free_inherited_info(struct pai_val *pal)
{
if (pal) {
struct pai_entry *paie, *paie_next;
for (paie = pal->entry_list; paie; paie = paie_next) {
paie_next = paie->next;
SAFE_FREE(paie);
}
for (paie = pal->def_entry_list; paie; paie = paie_next) {
paie_next = paie->next;
SAFE_FREE(paie);
}
SAFE_FREE(pal);
}
}
static BOOL get_protected_flag(struct pai_val *pal)
{
if (!pal)
return False;
return pal->pai_protected;
}
static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
{
struct pai_entry *paie;
if (!pal)
return False;
for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
if (ace_entry->owner_type == paie->owner_type &&
get_entry_val(ace_entry) == get_pai_entry_val(paie))
return True;
}
return False;
}
static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
{
uint16 num_entries;
uint16 num_def_entries;
if (pai_buf_data_size < PAI_ENTRIES_BASE) {
return False;
}
if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
return False;
num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
return False;
return True;
}
static struct pai_val *create_pai_val(char *buf, size_t size)
{
char *entry_offset;
struct pai_val *paiv = NULL;
int i;
if (!check_pai_ok(buf, size))
return NULL;
paiv = SMB_MALLOC_P(struct pai_val);
if (!paiv)
return NULL;
memset(paiv, '\0', sizeof(struct pai_val));
paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
entry_offset = buf + PAI_ENTRIES_BASE;
DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
paiv->pai_protected ? " (pai_protected)" : "", paiv->num_entries, paiv->num_def_entries ));
for (i = 0; i < paiv->num_entries; i++) {
struct pai_entry *paie;
paie = SMB_MALLOC_P(struct pai_entry);
if (!paie) {
free_inherited_info(paiv);
return NULL;
}
paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
switch( paie->owner_type) {
case UID_ACE:
paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
break;
case GID_ACE:
paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
break;
case WORLD_ACE:
paie->unix_ug.world = -1;
DEBUG(10,("create_pai_val: world ace\n"));
break;
default:
free_inherited_info(paiv);
return NULL;
}
entry_offset += PAI_ENTRY_LENGTH;
DLIST_ADD(paiv->entry_list, paie);
}
for (i = 0; i < paiv->num_def_entries; i++) {
struct pai_entry *paie;
paie = SMB_MALLOC_P(struct pai_entry);
if (!paie) {
free_inherited_info(paiv);
return NULL;
}
paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
switch( paie->owner_type) {
case UID_ACE:
paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
break;
case GID_ACE:
paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
break;
case WORLD_ACE:
paie->unix_ug.world = -1;
DEBUG(10,("create_pai_val: (def) world ace\n"));
break;
default:
free_inherited_info(paiv);
return NULL;
}
entry_offset += PAI_ENTRY_LENGTH;
DLIST_ADD(paiv->def_entry_list, paie);
}
return paiv;
}
static struct pai_val *load_inherited_info(files_struct *fsp)
{
char *pai_buf;
size_t pai_buf_size = 1024;
struct pai_val *paiv = NULL;
ssize_t ret;
if (!lp_map_acl_inherit(SNUM(fsp->conn)))
return NULL;
if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
return NULL;
do {
if (fsp->fh->fd != -1)
ret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, pai_buf_size);
else
ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, pai_buf_size);
if (ret == -1) {
if (errno != ERANGE) {
break;
}
pai_buf_size *= 2;
SAFE_FREE(pai_buf);
if (pai_buf_size > 1024*1024) {
return NULL;
}
if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
return NULL;
}
} while (ret == -1);
DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
if (ret == -1) {
#if defined(ENOATTR)
if (errno != ENOATTR)
DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
#else
if (errno != ENOSYS)
DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
#endif
SAFE_FREE(pai_buf);
return NULL;
}
paiv = create_pai_val(pai_buf, ret);
if (paiv && paiv->pai_protected)
DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
SAFE_FREE(pai_buf);
return paiv;
}
static size_t count_canon_ace_list( canon_ace *list_head )
{
size_t count = 0;
canon_ace *ace;
for (ace = list_head; ace; ace = ace->next)
count++;
return count;
}
static void free_canon_ace_list( canon_ace *list_head )
{
canon_ace *list, *next;
for (list = list_head; list; list = next) {
next = list->next;
DLIST_REMOVE(list_head, list);
SAFE_FREE(list);
}
}
static canon_ace *dup_canon_ace( canon_ace *src_ace)
{
canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
if (dst_ace == NULL)
return NULL;
*dst_ace = *src_ace;
dst_ace->prev = dst_ace->next = NULL;
return dst_ace;
}
static void print_canon_ace(canon_ace *pace, int num)
{
fstring str;
dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
if (pace->owner_type == UID_ACE) {
const char *u_name = uidtoname(pace->unix_ug.uid);
dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
} else if (pace->owner_type == GID_ACE) {
char *g_name = gidtoname(pace->unix_ug.gid);
dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
} else
dbgtext( "other ");
switch (pace->type) {
case SMB_ACL_USER:
dbgtext( "SMB_ACL_USER ");
break;
case SMB_ACL_USER_OBJ:
dbgtext( "SMB_ACL_USER_OBJ ");
break;
case SMB_ACL_GROUP:
dbgtext( "SMB_ACL_GROUP ");
break;
case SMB_ACL_GROUP_OBJ:
dbgtext( "SMB_ACL_GROUP_OBJ ");
break;
case SMB_ACL_OTHER:
dbgtext( "SMB_ACL_OTHER ");
break;
default:
dbgtext( "MASK " );
break;
}
if (pace->inherited)
dbgtext( "(inherited) ");
dbgtext( "perms ");
dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
}
static void print_canon_ace_list(const char *name, canon_ace *ace_list)
{
int count = 0;
if( DEBUGLVL( 10 )) {
dbgtext( "print_canon_ace_list: %s\n", name );
for (;ace_list; ace_list = ace_list->next, count++)
print_canon_ace(ace_list, count );
}
}
static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
{
mode_t ret = 0;
ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
return ret;
}
static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
{
mode_t ret = 0;
if (mode & r_mask)
ret |= S_IRUSR;
if (mode & w_mask)
ret |= S_IWUSR;
if (mode & x_mask)
ret |= S_IXUSR;
return ret;
}
static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
{
if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
return -1;
if (mode & S_IRUSR) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
return -1;
}
if (mode & S_IWUSR) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
return -1;
}
if (mode & S_IXUSR) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
return -1;
}
return 0;
}
static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
{
uid_to_sid( powner_sid, psbuf->st_uid );
gid_to_sid( pgroup_sid, psbuf->st_gid );
}
static BOOL identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
{
if (sid_equal(&ace1->trustee, &ace2->trustee)) {
return True;
}
if (ace1->owner_type == ace2->owner_type) {
if (ace1->owner_type == UID_ACE &&
ace1->unix_ug.uid == ace2->unix_ug.uid) {
return True;
} else if (ace1->owner_type == GID_ACE &&
ace1->unix_ug.gid == ace2->unix_ug.gid) {
return True;
}
}
return False;
}
static void merge_aces( canon_ace **pp_list_head )
{
canon_ace *list_head = *pp_list_head;
canon_ace *curr_ace_outer;
canon_ace *curr_ace_outer_next;
for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
canon_ace *curr_ace;
canon_ace *curr_ace_next;
curr_ace_outer_next = curr_ace_outer->next;
for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
curr_ace_next = curr_ace->next;
if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
(curr_ace->attr == curr_ace_outer->attr)) {
if( DEBUGLVL( 10 )) {
dbgtext("merge_aces: Merging ACE's\n");
print_canon_ace( curr_ace_outer, 0);
print_canon_ace( curr_ace, 0);
}
curr_ace_outer->perms |= curr_ace->perms;
DLIST_REMOVE(list_head, curr_ace);
SAFE_FREE(curr_ace);
curr_ace_outer_next = curr_ace_outer->next;
}
}
}
for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
canon_ace *curr_ace;
canon_ace *curr_ace_next;
curr_ace_outer_next = curr_ace_outer->next;
for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
curr_ace_next = curr_ace->next;
if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
(curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
if( DEBUGLVL( 10 )) {
dbgtext("merge_aces: Masking ACE's\n");
print_canon_ace( curr_ace_outer, 0);
print_canon_ace( curr_ace, 0);
}
curr_ace->perms &= ~curr_ace_outer->perms;
if (curr_ace->perms == 0) {
DLIST_REMOVE(list_head, curr_ace);
SAFE_FREE(curr_ace);
curr_ace_outer_next = curr_ace_outer->next;
} else {
DLIST_REMOVE(list_head, curr_ace_outer);
SAFE_FREE(curr_ace_outer);
break;
}
}
}
}
*pp_list_head = list_head;
}
static BOOL nt4_compatible_acls(void)
{
int compat = lp_acl_compatibility();
if (compat == ACL_COMPAT_AUTO) {
enum remote_arch_types ra_type = get_remote_arch();
return (ra_type <= RA_WINNT);
} else
return (compat == ACL_COMPAT_WINNT);
}
static SEC_ACCESS map_canon_ace_perms(int snum,
int *pacl_type,
mode_t perms,
BOOL directory_ace)
{
SEC_ACCESS sa;
uint32 nt_mask = 0;
*pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
if (directory_ace) {
nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
} else {
nt_mask = UNIX_ACCESS_RWX;
}
} else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
if (nt4_compatible_acls())
nt_mask = UNIX_ACCESS_NONE;
else
nt_mask = 0;
} else {
if (directory_ace) {
nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
} else {
nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
}
}
DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
(unsigned int)perms, (unsigned int)nt_mask ));
init_sec_access(&sa,nt_mask);
return sa;
}
#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
static mode_t map_nt_perms( uint32 *mask, int type)
{
mode_t mode = 0;
switch(type) {
case S_IRUSR:
if((*mask) & GENERIC_ALL_ACCESS)
mode = S_IRUSR|S_IWUSR|S_IXUSR;
else {
mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
}
break;
case S_IRGRP:
if((*mask) & GENERIC_ALL_ACCESS)
mode = S_IRGRP|S_IWGRP|S_IXGRP;
else {
mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
}
break;
case S_IROTH:
if((*mask) & GENERIC_ALL_ACCESS)
mode = S_IROTH|S_IWOTH|S_IXOTH;
else {
mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
}
break;
}
return mode;
}
BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
{
DOM_SID owner_sid;
DOM_SID grp_sid;
*puser = (uid_t)-1;
*pgrp = (gid_t)-1;
if(security_info_sent == 0) {
DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
return True;
}
memset(&owner_sid, '\0', sizeof(owner_sid));
memset(&grp_sid, '\0', sizeof(grp_sid));
DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
if (security_info_sent & OWNER_SECURITY_INFORMATION) {
sid_copy(&owner_sid, psd->owner_sid);
if (!sid_to_uid(&owner_sid, puser)) {
if (lp_force_unknown_acl_user(snum)) {
*puser = current_user.ut.uid;
} else {
DEBUG(3,("unpack_nt_owners: unable to validate"
" owner sid for %s\n",
sid_string_static(&owner_sid)));
return False;
}
}
}
if (security_info_sent & GROUP_SECURITY_INFORMATION) {
sid_copy(&grp_sid, psd->group_sid);
if (!sid_to_gid( &grp_sid, pgrp)) {
if (lp_force_unknown_acl_user(snum)) {
*pgrp = current_user.ut.gid;
} else {
DEBUG(3,("unpack_nt_owners: unable to validate"
" group sid.\n"));
return False;
}
}
}
DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
return True;
}
static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
{
int snum = SNUM(fsp->conn);
mode_t and_bits = (mode_t)0;
mode_t or_bits = (mode_t)0;
if (fsp->is_directory) {
and_bits = lp_dir_security_mask(snum);
or_bits = lp_force_dir_security_mode(snum);
} else {
and_bits = lp_security_mask(snum);
or_bits = lp_force_security_mode(snum);
}
switch(type) {
case S_IRUSR:
pace->perms |= S_IRUSR;
if (fsp->is_directory)
pace->perms |= (S_IWUSR|S_IXUSR);
and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
break;
case S_IRGRP:
and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
break;
case S_IROTH:
and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
break;
}
pace->perms = ((pace->perms & and_bits)|or_bits);
}
static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
{
fstring u_name;
if (sid_equal(&group_ace->trustee, &global_sid_World))
return True;
if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
return True;
fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
return user_in_group_sid(u_name, &group_ace->trustee);
}
static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
files_struct *fsp,
const DOM_SID *pfile_owner_sid,
const DOM_SID *pfile_grp_sid,
SMB_STRUCT_STAT *pst,
BOOL setting_acl)
{
canon_ace *pace;
BOOL got_user = False;
BOOL got_grp = False;
BOOL got_other = False;
canon_ace *pace_other = NULL;
for (pace = *pp_ace; pace; pace = pace->next) {
if (pace->type == SMB_ACL_USER_OBJ) {
if (setting_acl)
apply_default_perms(fsp, pace, S_IRUSR);
got_user = True;
} else if (pace->type == SMB_ACL_GROUP_OBJ) {
if (setting_acl)
apply_default_perms(fsp, pace, S_IRGRP);
got_grp = True;
} else if (pace->type == SMB_ACL_OTHER) {
if (setting_acl)
apply_default_perms(fsp, pace, S_IROTH);
got_other = True;
pace_other = pace;
}
}
if (!got_user) {
if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
ZERO_STRUCTP(pace);
pace->type = SMB_ACL_USER_OBJ;
pace->owner_type = UID_ACE;
pace->unix_ug.uid = pst->st_uid;
pace->trustee = *pfile_owner_sid;
pace->attr = ALLOW_ACE;
if (setting_acl) {
BOOL group_matched = False;
canon_ace *pace_iter;
for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
if (uid_entry_in_group(pace, pace_iter)) {
pace->perms |= pace_iter->perms;
group_matched = True;
}
}
}
if (!group_matched) {
if (got_other)
pace->perms = pace_other->perms;
else
pace->perms = 0;
}
apply_default_perms(fsp, pace, S_IRUSR);
} else {
pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
}
DLIST_ADD(*pp_ace, pace);
}
if (!got_grp) {
if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
ZERO_STRUCTP(pace);
pace->type = SMB_ACL_GROUP_OBJ;
pace->owner_type = GID_ACE;
pace->unix_ug.uid = pst->st_gid;
pace->trustee = *pfile_grp_sid;
pace->attr = ALLOW_ACE;
if (setting_acl) {
if (got_other)
pace->perms = pace_other->perms;
else
pace->perms = 0;
apply_default_perms(fsp, pace, S_IRGRP);
} else {
pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
}
DLIST_ADD(*pp_ace, pace);
}
if (!got_other) {
if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
ZERO_STRUCTP(pace);
pace->type = SMB_ACL_OTHER;
pace->owner_type = WORLD_ACE;
pace->unix_ug.world = -1;
pace->trustee = global_sid_World;
pace->attr = ALLOW_ACE;
if (setting_acl) {
pace->perms = 0;
apply_default_perms(fsp, pace, S_IROTH);
} else
pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
DLIST_ADD(*pp_ace, pace);
}
return True;
}
static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
{
BOOL got_user_obj, got_group_obj;
canon_ace *current_ace;
int i, entries;
entries = count_canon_ace_list(ace);
got_user_obj = False;
got_group_obj = False;
for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
if (current_ace->type == SMB_ACL_USER_OBJ)
got_user_obj = True;
else if (current_ace->type == SMB_ACL_GROUP_OBJ)
got_group_obj = True;
}
if (got_user_obj && got_group_obj) {
DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
return;
}
for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
if (!got_user_obj && current_ace->owner_type == UID_ACE &&
sid_equal(¤t_ace->trustee, pfile_owner_sid)) {
current_ace->type = SMB_ACL_USER_OBJ;
got_user_obj = True;
}
if (!got_group_obj && current_ace->owner_type == GID_ACE &&
sid_equal(¤t_ace->trustee, pfile_grp_sid)) {
current_ace->type = SMB_ACL_GROUP_OBJ;
got_group_obj = True;
}
}
if (!got_user_obj)
DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
if (!got_group_obj)
DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
}
static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
DOM_SID *pfile_owner_sid,
DOM_SID *pfile_grp_sid,
canon_ace **ppfile_ace, canon_ace **ppdir_ace,
SEC_ACL *dacl)
{
BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
canon_ace *current_ace = NULL;
BOOL got_dir_allow = False;
BOOL got_file_allow = False;
int i, j;
*ppfile_ace = NULL;
*ppdir_ace = NULL;
for(i = 0; i < dacl->num_aces; i++) {
SEC_ACE *psa = &dacl->aces[i];
if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
return False;
}
if (nt4_compatible_acls()) {
se_map_generic(&psa->access_mask, &file_generic_mapping);
psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
if(psa->access_mask != UNIX_ACCESS_NONE)
psa->access_mask &= ~UNIX_ACCESS_NONE;
}
}
for(i = 0; i < dacl->num_aces; i++) {
SEC_ACE *psa1 = &dacl->aces[i];
for (j = i + 1; j < dacl->num_aces; j++) {
SEC_ACE *psa2 = &dacl->aces[j];
if (psa1->access_mask != psa2->access_mask)
continue;
if (!sid_equal(&psa1->trustee, &psa2->trustee))
continue;
if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
} else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
}
}
}
for(i = 0; i < dacl->num_aces; i++) {
SEC_ACE *psa = &dacl->aces[i];
if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
return False;
}
ZERO_STRUCTP(current_ace);
sid_copy(¤t_ace->trustee, &psa->trustee);
if( sid_equal(¤t_ace->trustee, &global_sid_World)) {
current_ace->owner_type = WORLD_ACE;
current_ace->unix_ug.world = -1;
current_ace->type = SMB_ACL_OTHER;
} else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Owner)) {
current_ace->owner_type = UID_ACE;
current_ace->unix_ug.uid = pst->st_uid;
current_ace->type = SMB_ACL_USER_OBJ;
if (nt4_compatible_acls())
psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
} else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) {
current_ace->owner_type = GID_ACE;
current_ace->unix_ug.gid = pst->st_gid;
current_ace->type = SMB_ACL_GROUP_OBJ;
if (nt4_compatible_acls())
psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
} else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid)) {
current_ace->owner_type = UID_ACE;
if (current_ace->unix_ug.uid == pst->st_uid) {
current_ace->type = SMB_ACL_USER_OBJ;
} else {
current_ace->type = SMB_ACL_USER;
}
} else if (sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid)) {
current_ace->owner_type = GID_ACE;
if (current_ace->unix_ug.gid == pst->st_gid) {
current_ace->type = SMB_ACL_GROUP_OBJ;
} else {
current_ace->type = SMB_ACL_GROUP;
}
} else {
fstring str;
if (non_mappable_sid(&psa->trustee)) {
DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
sid_to_string(str, &psa->trustee) ));
SAFE_FREE(current_ace);
continue;
}
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
sid_to_string(str, ¤t_ace->trustee) ));
SAFE_FREE(current_ace);
return False;
}
current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
if (fsp->is_directory) {
if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
if (current_ace->attr == ALLOW_ACE)
got_dir_allow = True;
if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
}
if( DEBUGLVL( 10 )) {
dbgtext("create_canon_ace_lists: adding dir ACL:\n");
print_canon_ace( current_ace, 0);
}
if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
canon_ace *dup_ace = dup_canon_ace(current_ace);
if (!dup_ace) {
DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
}
current_ace = dup_ace;
} else {
current_ace = NULL;
}
}
}
if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
DLIST_ADD_END(file_ace, current_ace, canon_ace *);
if (current_ace->attr == ALLOW_ACE)
got_file_allow = True;
if ((current_ace->attr == DENY_ACE) && got_file_allow) {
DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
}
if( DEBUGLVL( 10 )) {
dbgtext("create_canon_ace_lists: adding file ACL:\n");
print_canon_ace( current_ace, 0);
}
all_aces_are_inherit_only = False;
current_ace = NULL;
}
SAFE_FREE(current_ace);
}
if (fsp->is_directory && all_aces_are_inherit_only) {
DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
file_ace = NULL;
dir_ace = NULL;
} else {
if (file_ace) {
check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
}
if (dir_ace) {
check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
}
}
*ppfile_ace = file_ace;
*ppdir_ace = dir_ace;
return True;
}
static void process_deny_list( canon_ace **pp_ace_list )
{
canon_ace *ace_list = *pp_ace_list;
canon_ace *curr_ace = NULL;
canon_ace *curr_ace_next = NULL;
for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
canon_ace *allow_ace_p;
curr_ace_next = curr_ace->next;
if (curr_ace->attr != DENY_ACE)
continue;
if (curr_ace->perms == (mode_t)0) {
DLIST_REMOVE(ace_list, curr_ace);
continue;
}
if (!sid_equal(&curr_ace->trustee, &global_sid_World))
continue;
SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
if (curr_ace->perms == ALL_ACE_PERMS) {
canon_ace *prev_entry = curr_ace->prev;
free_canon_ace_list( curr_ace );
if (prev_entry)
prev_entry->next = NULL;
else {
ace_list = NULL;
}
break;
}
for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
if (allow_ace_p->attr != ALLOW_ACE)
continue;
allow_ace_p->perms &= ~curr_ace->perms;
}
DLIST_REMOVE(ace_list, curr_ace);
}
for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
mode_t new_perms = (mode_t)0;
canon_ace *allow_ace_p;
curr_ace_next = curr_ace->next;
if (curr_ace->attr != DENY_ACE)
continue;
if (curr_ace->owner_type != UID_ACE)
continue;
if (curr_ace->perms == ALL_ACE_PERMS) {
curr_ace->attr = ALLOW_ACE;
curr_ace->perms = (mode_t)0;
DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
continue;
}
for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
if (allow_ace_p->attr != ALLOW_ACE)
continue;
if (allow_ace_p->owner_type == UID_ACE)
continue;
if (uid_entry_in_group( curr_ace, allow_ace_p))
new_perms |= allow_ace_p->perms;
}
curr_ace->attr = ALLOW_ACE;
curr_ace->perms = (new_perms & ~curr_ace->perms);
DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
}
for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
canon_ace *allow_ace_p;
canon_ace *allow_everyone_p = NULL;
curr_ace_next = curr_ace->next;
if (curr_ace->attr != DENY_ACE)
continue;
if (curr_ace->owner_type != GID_ACE)
continue;
for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
if (allow_ace_p->attr != ALLOW_ACE)
continue;
if (allow_ace_p->owner_type == WORLD_ACE)
allow_everyone_p = allow_ace_p;
if (allow_ace_p->owner_type != UID_ACE)
continue;
if (uid_entry_in_group( allow_ace_p, curr_ace))
allow_ace_p->perms &= ~curr_ace->perms;
}
curr_ace->attr = ALLOW_ACE;
if (allow_everyone_p)
curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
else
curr_ace->perms = (mode_t)0;
DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
}
#if 0
for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
canon_ace *allow_ace_p;
curr_ace_next = curr_ace->next;
if (curr_ace->attr != ALLOW_ACE)
continue;
if (curr_ace->owner_type != UID_ACE)
continue;
for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
if (allow_ace_p->attr != ALLOW_ACE)
continue;
if (allow_ace_p->owner_type != GID_ACE)
continue;
if (uid_entry_in_group( curr_ace, allow_ace_p))
curr_ace->perms |= allow_ace_p->perms;
}
}
#endif
*pp_ace_list = ace_list;
}
static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
{
int snum = SNUM(fsp->conn);
mode_t and_bits = (mode_t)0;
mode_t or_bits = (mode_t)0;
mode_t mode = interitable_mode
? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
NULL )
: S_IRUSR;
if (fsp->is_directory)
mode |= (S_IWUSR|S_IXUSR);
if (fsp->is_directory) {
and_bits = lp_dir_security_mask(snum);
or_bits = lp_force_dir_security_mode(snum);
} else {
and_bits = lp_security_mask(snum);
or_bits = lp_force_security_mode(snum);
}
return ((mode & and_bits)|or_bits);
}
static BOOL unpack_canon_ace(files_struct *fsp,
SMB_STRUCT_STAT *pst,
DOM_SID *pfile_owner_sid,
DOM_SID *pfile_grp_sid,
canon_ace **ppfile_ace, canon_ace **ppdir_ace,
uint32 security_info_sent, SEC_DESC *psd)
{
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
*ppfile_ace = NULL;
*ppdir_ace = NULL;
if(security_info_sent == 0) {
DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
return False;
}
if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
return True;
if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
&file_ace, &dir_ace, psd->dacl))
return False;
if ((file_ace == NULL) && (dir_ace == NULL)) {
return True;
}
print_canon_ace_list( "file ace - before merge", file_ace);
merge_aces( &file_ace );
print_canon_ace_list( "dir ace - before merge", dir_ace);
merge_aces( &dir_ace );
print_canon_ace_list( "file ace - before deny", file_ace);
process_deny_list( &file_ace);
print_canon_ace_list( "dir ace - before deny", dir_ace);
process_deny_list( &dir_ace);
print_canon_ace_list( "file ace - before valid", file_ace);
pst->st_mode = create_default_mode(fsp, False);
if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
}
print_canon_ace_list( "dir ace - before valid", dir_ace);
pst->st_mode = create_default_mode(fsp, True);
if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
}
print_canon_ace_list( "file ace - return", file_ace);
print_canon_ace_list( "dir ace - return", dir_ace);
*ppfile_ace = file_ace;
*ppdir_ace = dir_ace;
return True;
}
static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
{
canon_ace *list_head = *pp_list_head;
canon_ace *owner_ace = NULL;
canon_ace *other_ace = NULL;
canon_ace *ace = NULL;
for (ace = list_head; ace; ace = ace->next) {
if (ace->type == SMB_ACL_USER_OBJ)
owner_ace = ace;
else if (ace->type == SMB_ACL_OTHER) {
other_ace = ace;
}
}
if (!owner_ace || !other_ace) {
DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
filename ));
return;
}
if (owner_ace) {
DLIST_PROMOTE(list_head, owner_ace);
}
if (other_ace) {
DLIST_DEMOTE(list_head, other_ace, canon_ace *);
}
*pp_list_head = list_head;
}
static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
{
connection_struct *conn = fsp->conn;
mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
canon_ace *list_head = NULL;
canon_ace *ace = NULL;
canon_ace *next_ace = NULL;
int entry_id = SMB_ACL_FIRST_ENTRY;
SMB_ACL_ENTRY_T entry;
size_t ace_count;
while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
SMB_ACL_TAG_T tagtype;
SMB_ACL_PERMSET_T permset;
DOM_SID sid;
posix_id unix_ug;
enum ace_owner owner_type;
if (entry_id == SMB_ACL_FIRST_ENTRY)
entry_id = SMB_ACL_NEXT_ENTRY;
if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
continue;
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
continue;
switch(tagtype) {
case SMB_ACL_USER_OBJ:
sid_copy(&sid, powner);
unix_ug.uid = psbuf->st_uid;
owner_type = UID_ACE;
break;
case SMB_ACL_USER:
{
uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
if (puid == NULL) {
DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
continue;
}
if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
continue;
}
uid_to_sid( &sid, *puid);
unix_ug.uid = *puid;
owner_type = UID_ACE;
SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
break;
}
case SMB_ACL_GROUP_OBJ:
sid_copy(&sid, pgroup);
unix_ug.gid = psbuf->st_gid;
owner_type = GID_ACE;
break;
case SMB_ACL_GROUP:
{
gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
if (pgid == NULL) {
DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
continue;
}
gid_to_sid( &sid, *pgid);
unix_ug.gid = *pgid;
owner_type = GID_ACE;
SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
break;
}
case SMB_ACL_MASK:
acl_mask = convert_permset_to_mode_t(conn, permset);
continue;
case SMB_ACL_OTHER:
sid = global_sid_World;
unix_ug.world = -1;
owner_type = WORLD_ACE;
break;
default:
DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
continue;
}
if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
goto fail;
ZERO_STRUCTP(ace);
ace->type = tagtype;
ace->perms = convert_permset_to_mode_t(conn, permset);
ace->attr = ALLOW_ACE;
ace->trustee = sid;
ace->unix_ug = unix_ug;
ace->owner_type = owner_type;
ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
DLIST_ADD(list_head, ace);
}
if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
goto fail;
DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
next_ace = ace->next;
if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
ace->perms &= acl_mask;
if (ace->perms == 0) {
DLIST_PROMOTE(list_head, ace);
}
if( DEBUGLVL( 10 ) ) {
print_canon_ace(ace, ace_count);
}
}
arrange_posix_perms(fsp->fsp_name,&list_head );
print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
return list_head;
fail:
free_canon_ace_list(list_head);
return NULL;
}
static BOOL current_user_in_group(gid_t gid)
{
int i;
for (i = 0; i < current_user.ut.ngroups; i++) {
if (current_user.ut.groups[i] == gid) {
return True;
}
}
return False;
}
static BOOL acl_group_override(connection_struct *conn, gid_t prim_gid)
{
if ( (errno == EACCES || errno == EPERM)
&& (lp_acl_group_control(SNUM(conn)) || lp_dos_filemode(SNUM(conn)))
&& current_user_in_group(prim_gid))
{
return True;
}
return False;
}
static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, gid_t prim_gid, BOOL *pacl_set_support)
{
connection_struct *conn = fsp->conn;
BOOL ret = False;
SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
canon_ace *p_ace;
int i;
SMB_ACL_ENTRY_T mask_entry;
BOOL got_mask_entry = False;
SMB_ACL_PERMSET_T mask_permset;
SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
BOOL needs_mask = False;
mode_t mask_perms = 0;
#if defined(POSIX_ACL_NEEDS_MASK)
needs_mask = True;
#endif
if (the_acl == NULL) {
if (!no_acl_syscall_error(errno)) {
DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
default_ace ? "default" : "file", strerror(errno) ));
}
*pacl_set_support = False;
return False;
}
if( DEBUGLVL( 10 )) {
dbgtext("set_canon_ace_list: setting ACL:\n");
for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
print_canon_ace( p_ace, i);
}
}
for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
SMB_ACL_ENTRY_T the_entry;
SMB_ACL_PERMSET_T the_permset;
if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
needs_mask = True;
mask_perms |= p_ace->perms;
} else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
mask_perms |= p_ace->perms;
}
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (p_ace->type == SMB_ACL_MASK) {
mask_entry = the_entry;
got_mask_entry = True;
}
*pacl_set_support = True;
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
i, strerror(errno) ));
goto fail;
}
}
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
(unsigned int)p_ace->perms, i, strerror(errno) ));
goto fail;
}
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if( DEBUGLVL( 10 ))
print_canon_ace( p_ace, i);
}
if (needs_mask && !got_mask_entry) {
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
goto fail;
}
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
goto fail;
}
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
goto fail;
}
if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
goto fail;
}
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
goto fail;
}
}
if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
if (no_acl_syscall_error(errno)) {
*pacl_set_support = False;
}
if (acl_group_override(conn, prim_gid)) {
int sret;
DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
fsp->fsp_name ));
become_root();
sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
unbecome_root();
if (sret == 0) {
ret = True;
}
}
if (ret == False) {
DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
fsp->fsp_name, strerror(errno) ));
goto fail;
}
}
} else {
if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) {
if (no_acl_syscall_error(errno)) {
*pacl_set_support = False;
}
if (acl_group_override(conn, prim_gid)) {
int sret;
DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
fsp->fsp_name ));
become_root();
sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl);
unbecome_root();
if (sret == 0) {
ret = True;
}
}
if (ret == False) {
DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
fsp->fsp_name, strerror(errno) ));
goto fail;
}
}
}
ret = True;
fail:
if (the_acl != NULL) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
}
return ret;
}
static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
{
while (list) {
if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
(type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
(type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
break;
list = list->next;
}
return list;
}
SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
{
SMB_ACL_ENTRY_T entry;
if (!the_acl)
return NULL;
if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
return NULL;
}
return the_acl;
}
#define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
{
int snum = SNUM(fsp->conn);
size_t ace_count = count_canon_ace_list(file_ace_list);
canon_ace *ace_p;
canon_ace *owner_ace = NULL;
canon_ace *group_ace = NULL;
canon_ace *other_ace = NULL;
mode_t and_bits;
mode_t or_bits;
if (ace_count != 3) {
DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
posix perms.\n", fsp->fsp_name ));
return False;
}
for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
if (ace_p->owner_type == UID_ACE)
owner_ace = ace_p;
else if (ace_p->owner_type == GID_ACE)
group_ace = ace_p;
else if (ace_p->owner_type == WORLD_ACE)
other_ace = ace_p;
}
if (!owner_ace || !group_ace || !other_ace) {
DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
fsp->fsp_name ));
return False;
}
*posix_perms = (mode_t)0;
*posix_perms |= owner_ace->perms;
*posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
*posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
*posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
*posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
*posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
*posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
*posix_perms |= S_IRUSR;
if (fsp->is_directory)
*posix_perms |= (S_IWUSR|S_IXUSR);
if (fsp->is_directory) {
and_bits = lp_dir_security_mask(snum);
or_bits = lp_force_dir_security_mode(snum);
} else {
and_bits = lp_security_mask(snum);
or_bits = lp_force_security_mode(snum);
}
*posix_perms = (((*posix_perms) & and_bits)|or_bits);
DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
(int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
fsp->fsp_name ));
return True;
}
static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
{
size_t i, j;
for (i = 0; i < num_aces; i++) {
for (j = i+1; j < num_aces; j++) {
uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
(nt_ace_list[i].size == nt_ace_list[j].size) &&
(nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
(i_inh == j_inh) &&
(i_flags_ni == 0) &&
(j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
SEC_ACE_FLAG_CONTAINER_INHERIT|
SEC_ACE_FLAG_INHERIT_ONLY))) {
if (nt_ace_list[i].access_mask == 0) {
nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
(i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
if (num_aces - i - 1 > 0)
memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
sizeof(SEC_ACE));
DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
(unsigned int)i, (unsigned int)j ));
} else {
nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
(i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
if (num_aces - j - 1 > 0)
memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
sizeof(SEC_ACE));
DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
(unsigned int)j, (unsigned int)i ));
}
num_aces--;
break;
}
}
}
return num_aces;
}
size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
{
connection_struct *conn = fsp->conn;
SMB_STRUCT_STAT sbuf;
SEC_ACE *nt_ace_list = NULL;
DOM_SID owner_sid;
DOM_SID group_sid;
size_t sd_size = 0;
SEC_ACL *psa = NULL;
size_t num_acls = 0;
size_t num_def_acls = 0;
size_t num_aces = 0;
SMB_ACL_T posix_acl = NULL;
SMB_ACL_T def_acl = NULL;
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
size_t num_profile_acls = 0;
struct pai_val *pal = NULL;
SEC_DESC *psd = NULL;
*ppdesc = NULL;
DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
if(fsp->is_directory || fsp->fh->fd == -1) {
if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
return 0;
}
posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
if(fsp->is_directory) {
def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
def_acl = free_empty_sys_acl(conn, def_acl);
}
} else {
if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) {
return 0;
}
posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
}
DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
posix_acl ? "present" : "absent",
def_acl ? "present" : "absent" ));
pal = load_inherited_info(fsp);
if (lp_profile_acls(SNUM(conn))) {
sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
sid_copy(&group_sid, &global_sid_Builtin_Users);
num_profile_acls = 2;
} else {
create_file_sids(&sbuf, &owner_sid, &group_sid);
}
if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
if (count_canon_ace_list(file_ace) == 0) {
DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
goto done;
}
if (fsp->is_directory && def_acl) {
dir_ace = canonicalise_acl(fsp, def_acl, &sbuf,
&global_sid_Creator_Owner,
&global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
}
{
canon_ace *ace;
int nt_acl_type;
int i;
if (nt4_compatible_acls() && dir_ace) {
ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
if (ace && !ace->perms) {
DLIST_REMOVE(dir_ace, ace);
SAFE_FREE(ace);
ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
if (ace && !ace->perms) {
DLIST_REMOVE(file_ace, ace);
SAFE_FREE(ace);
}
}
#if 0
ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
if (ace && !ace->perms) {
DLIST_REMOVE(dir_ace, ace);
SAFE_FREE(ace);
}
#endif
ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
if (ace && !ace->perms) {
DLIST_REMOVE(file_ace, ace);
SAFE_FREE(ace);
}
}
num_acls = count_canon_ace_list(file_ace);
num_def_acls = count_canon_ace_list(dir_ace);
if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
goto done;
}
memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
ace = file_ace;
for (i = 0; i < num_acls; i++, ace = ace->next) {
SEC_ACCESS acc;
acc = map_canon_ace_perms(SNUM(conn),
&nt_acl_type,
ace->perms,
fsp->is_directory);
init_sec_ace(&nt_ace_list[num_aces++],
&ace->trustee,
nt_acl_type,
acc,
ace->inherited ?
SEC_ACE_FLAG_INHERITED_ACE : 0);
}
if (lp_profile_acls(SNUM(conn))) {
SEC_ACCESS acc;
init_sec_access(&acc,FILE_GENERIC_ALL);
init_sec_ace(&nt_ace_list[num_aces++],
&global_sid_Builtin_Users,
SEC_ACE_TYPE_ACCESS_ALLOWED,
acc, 0);
}
ace = dir_ace;
for (i = 0; i < num_def_acls; i++, ace = ace->next) {
SEC_ACCESS acc;
acc = map_canon_ace_perms(SNUM(conn),
&nt_acl_type,
ace->perms,
fsp->is_directory);
init_sec_ace(&nt_ace_list[num_aces++],
&ace->trustee,
nt_acl_type,
acc,
SEC_ACE_FLAG_OBJECT_INHERIT|
SEC_ACE_FLAG_CONTAINER_INHERIT|
SEC_ACE_FLAG_INHERIT_ONLY|
(ace->inherited ?
SEC_ACE_FLAG_INHERITED_ACE : 0));
}
if (lp_profile_acls(SNUM(conn))) {
SEC_ACCESS acc;
init_sec_access(&acc,FILE_GENERIC_ALL);
init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
SEC_ACE_FLAG_INHERIT_ONLY|0);
}
num_aces = merge_default_aces(nt_ace_list, num_aces);
}
if (num_aces) {
if((psa = make_sec_acl( main_loop_talloc_get(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
goto done;
}
}
}
psd = make_standard_sec_desc( main_loop_talloc_get(),
(security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
(security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
psa,
&sd_size);
if(!psd) {
DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
sd_size = 0;
goto done;
}
if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
psd->type |= SE_DESC_DACL_PROTECTED;
}
if (psd->dacl) {
dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
}
*ppdesc = psd;
done:
if (posix_acl) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
}
if (def_acl) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
}
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
free_inherited_info(pal);
SAFE_FREE(nt_ace_list);
return sd_size;
}
int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
{
int ret;
files_struct *fsp;
SMB_STRUCT_STAT st;
if(!CAN_WRITE(conn)) {
return -1;
}
ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
if (ret == 0)
return 0;
if (lp_enable_privileges()) {
BOOL has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
&se_take_ownership);
BOOL has_restore_priv = user_has_privileges(current_user.nt_user_token,
&se_restore);
if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
( has_restore_priv ) ) {
become_root();
ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
unbecome_root();
return ret;
}
}
if (!lp_dos_filemode(SNUM(conn))) {
return -1;
}
if (SMB_VFS_STAT(conn,fname,&st)) {
return -1;
}
if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,&st,&fsp))) {
return -1;
}
uid = current_user.ut.uid;
become_root();
ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1);
unbecome_root();
close_file_fchmod(fsp);
return ret;
}
static NTSTATUS append_parent_acl(files_struct *fsp,
SMB_STRUCT_STAT *psbuf,
SEC_DESC *psd,
SEC_DESC **pp_new_sd)
{
SEC_DESC *parent_sd = NULL;
files_struct *parent_fsp = NULL;
TALLOC_CTX *mem_ctx = talloc_parent(psd);
char *parent_name = NULL;
SEC_ACE *new_ace = NULL;
unsigned int num_aces = psd->dacl->num_aces;
SMB_STRUCT_STAT sbuf;
NTSTATUS status;
int info;
size_t sd_size;
unsigned int i, j;
BOOL is_dacl_protected = (psd->type & SE_DESC_DACL_PROTECTED);
ZERO_STRUCT(sbuf);
if (mem_ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
if (!parent_dirname_talloc(mem_ctx,
fsp->fsp_name,
&parent_name,
NULL)) {
return NT_STATUS_NO_MEMORY;
}
status = open_directory(fsp->conn,
parent_name,
&sbuf,
FILE_READ_ATTRIBUTES,
FILE_SHARE_NONE,
FILE_OPEN,
0,
0,
&info,
&parent_fsp);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
sd_size = SMB_VFS_GET_NT_ACL(parent_fsp, parent_fsp->fsp_name,
DACL_SECURITY_INFORMATION, &parent_sd );
close_file(parent_fsp, NORMAL_CLOSE);
if (!sd_size) {
return NT_STATUS_ACCESS_DENIED;
}
num_aces += parent_sd->dacl->num_aces;
if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
num_aces)) == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i = 0; i < psd->dacl->num_aces; i++) {
sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
}
for (j = 0; j < parent_sd->dacl->num_aces; j++) {
SEC_ACE *se = &parent_sd->dacl->aces[j];
if (fsp->is_directory) {
if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
DEBUG(10,("append_parent_acl: directory %s "
"ignoring non container "
"inherit flags %u on ACE with sid %s "
"from parent %s\n",
fsp->fsp_name,
(unsigned int)se->flags,
sid_string_static(&se->trustee),
parent_name));
continue;
}
} else {
if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
DEBUG(10,("append_parent_acl: file %s "
"ignoring non object "
"inherit flags %u on ACE with sid %s "
"from parent %s\n",
fsp->fsp_name,
(unsigned int)se->flags,
sid_string_static(&se->trustee),
parent_name));
continue;
}
}
if (is_dacl_protected) {
unsigned int k;
for (k = 0; k < psd->dacl->num_aces; k++) {
if (sid_equal(&psd->dacl->aces[k].trustee,
&se->trustee)) {
break;
}
}
if (k < psd->dacl->num_aces) {
DEBUG(10,("append_parent_acl: path %s "
"ignoring ACE with protected sid %s "
"from parent %s\n",
fsp->fsp_name,
sid_string_static(&se->trustee),
parent_name));
continue;
}
}
sec_ace_copy(&new_ace[i], se);
if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
}
new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
if (fsp->is_directory) {
new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
new_ace[i].flags &=
~(SEC_ACE_FLAG_CONTAINER_INHERIT|
SEC_ACE_FLAG_OBJECT_INHERIT);
}
} else {
new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
SEC_ACE_FLAG_INHERIT_ONLY|
SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
}
i++;
DEBUG(10,("append_parent_acl: path %s "
"inheriting ACE with sid %s "
"from parent %s\n",
fsp->fsp_name,
sid_string_static(&se->trustee),
parent_name));
}
parent_sd->dacl->aces = new_ace;
parent_sd->dacl->num_aces = i;
*pp_new_sd = parent_sd;
return status;
}
BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
{
connection_struct *conn = fsp->conn;
uid_t user = (uid_t)-1;
gid_t grp = (gid_t)-1;
SMB_STRUCT_STAT sbuf;
DOM_SID file_owner_sid;
DOM_SID file_grp_sid;
canon_ace *file_ace_list = NULL;
canon_ace *dir_ace_list = NULL;
BOOL acl_perms = False;
mode_t orig_mode = (mode_t)0;
uid_t orig_uid;
gid_t orig_gid;
BOOL need_chown = False;
DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
if (!CAN_WRITE(conn)) {
DEBUG(10,("set acl rejected on read-only share\n"));
return False;
}
if(fsp->is_directory || fsp->fh->fd == -1) {
if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
return False;
} else {
if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0)
return False;
}
orig_mode = sbuf.st_mode;
orig_uid = sbuf.st_uid;
orig_gid = sbuf.st_gid;
if (!unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd)) {
return False;
}
if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
need_chown = True;
}
if (need_chown && (user == (uid_t)-1 || user == current_user.ut.uid)) {
DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
return False;
}
if(fsp->is_directory) {
if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
return False;
}
} else {
int ret;
if(fsp->fh->fd == -1)
ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
else
ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf);
if(ret != 0)
return False;
}
orig_mode = sbuf.st_mode;
orig_uid = sbuf.st_uid;
orig_gid = sbuf.st_gid;
need_chown = False;
}
create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
if ((security_info_sent & DACL_SECURITY_INFORMATION) &&
psd->dacl != NULL &&
(psd->type & (SE_DESC_DACL_AUTO_INHERITED|
SE_DESC_DACL_AUTO_INHERIT_REQ))==
(SE_DESC_DACL_AUTO_INHERITED|
SE_DESC_DACL_AUTO_INHERIT_REQ) ) {
NTSTATUS status = append_parent_acl(fsp, &sbuf, psd, &psd);
if (!NT_STATUS_IS_OK(status)) {
return False;
}
}
acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
&file_ace_list, &dir_ace_list, security_info_sent, psd);
if (file_ace_list || dir_ace_list) {
if (!acl_perms) {
DEBUG(3,("set_nt_acl: cannot set permissions\n"));
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
return False;
}
if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
BOOL acl_set_support = False;
BOOL ret = False;
if (acl_perms && file_ace_list) {
ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
if (acl_set_support && ret == False) {
DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
return False;
}
}
if (acl_perms && acl_set_support && fsp->is_directory) {
if (dir_ace_list) {
if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support)) {
DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
return False;
}
} else {
if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
int sret = -1;
if (acl_group_override(conn, sbuf.st_gid)) {
DEBUG(5,("set_nt_acl: acl group control on and "
"current user in file %s primary group. Override delete_def_acl\n",
fsp->fsp_name ));
become_root();
sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
unbecome_root();
}
if (sret == -1) {
DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
return False;
}
}
}
}
if (acl_set_support) {
store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
(psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
}
if(!acl_set_support && acl_perms) {
mode_t posix_perms;
if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
fsp->fsp_name ));
return False;
}
if (orig_mode != posix_perms) {
DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
fsp->fsp_name, (unsigned int)posix_perms ));
if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
int sret = -1;
if (acl_group_override(conn, sbuf.st_gid)) {
DEBUG(5,("set_nt_acl: acl group control on and "
"current user in file %s primary group. Override chmod\n",
fsp->fsp_name ));
become_root();
sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
unbecome_root();
}
if (sret == -1) {
DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
return False;
}
}
}
}
}
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
}
if (need_chown) {
DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
return False;
}
}
return True;
}
int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
{
int entry_id = SMB_ACL_FIRST_ENTRY;
SMB_ACL_ENTRY_T entry;
SMB_ACL_T posix_acl;
int result = -1;
posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
if (posix_acl == (SMB_ACL_T)NULL)
return -1;
while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
SMB_ACL_TAG_T tagtype;
SMB_ACL_PERMSET_T permset;
if (entry_id == SMB_ACL_FIRST_ENTRY)
entry_id = SMB_ACL_NEXT_ENTRY;
if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
break;
if (tagtype == SMB_ACL_GROUP_OBJ) {
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
break;
} else {
*mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
*mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
*mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
*mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
result = 0;
break;
}
}
}
SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
return result;
}
static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
{
int entry_id = SMB_ACL_FIRST_ENTRY;
SMB_ACL_ENTRY_T entry;
int num_entries = 0;
while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
SMB_ACL_TAG_T tagtype;
SMB_ACL_PERMSET_T permset;
mode_t perms;
if (entry_id == SMB_ACL_FIRST_ENTRY)
entry_id = SMB_ACL_NEXT_ENTRY;
if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
return -1;
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
return -1;
num_entries++;
switch(tagtype) {
case SMB_ACL_USER_OBJ:
perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
break;
case SMB_ACL_GROUP_OBJ:
perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
break;
case SMB_ACL_MASK:
perms = S_IRUSR|S_IWUSR|S_IXUSR;
break;
case SMB_ACL_OTHER:
perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
break;
default:
continue;
}
if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
return -1;
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
return -1;
}
if ((num_entries == 3) || (num_entries == 0))
return -1;
return 0;
}
static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
{
SMB_ACL_T posix_acl = NULL;
int ret = -1;
if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
return -1;
if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
goto done;
ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
done:
SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
return ret;
}
int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
{
return copy_access_acl(conn, name, name, mode);
}
int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir,
const char *name, mode_t mode)
{
if (directory_has_default_acl(conn, inherit_from_dir))
return 0;
return copy_access_acl(conn, inherit_from_dir, name, mode);
}
int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
{
connection_struct *conn = fsp->conn;
SMB_ACL_T posix_acl = NULL;
int ret = -1;
if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
return -1;
if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
goto done;
ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
done:
SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
return ret;
}
BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
{
SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
BOOL has_acl = False;
SMB_ACL_ENTRY_T entry;
if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
has_acl = True;
}
if (def_acl) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
}
return has_acl;
}
static BOOL unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
{
if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
return False;
}
if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1) {
return False;
}
if (wire_perm & SMB_POSIX_ACL_READ) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
return False;
}
}
if (wire_perm & SMB_POSIX_ACL_WRITE) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
return False;
}
}
if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
return False;
}
}
return True;
}
static BOOL unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
{
switch (wire_tt) {
case SMB_POSIX_ACL_USER_OBJ:
*p_tt = SMB_ACL_USER_OBJ;
break;
case SMB_POSIX_ACL_USER:
*p_tt = SMB_ACL_USER;
break;
case SMB_POSIX_ACL_GROUP_OBJ:
*p_tt = SMB_ACL_GROUP_OBJ;
break;
case SMB_POSIX_ACL_GROUP:
*p_tt = SMB_ACL_GROUP;
break;
case SMB_POSIX_ACL_MASK:
*p_tt = SMB_ACL_MASK;
break;
case SMB_POSIX_ACL_OTHER:
*p_tt = SMB_ACL_OTHER;
break;
default:
return False;
}
return True;
}
static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
{
unsigned int i;
SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
if (the_acl == NULL) {
return NULL;
}
for (i = 0; i < num_acls; i++) {
SMB_ACL_ENTRY_T the_entry;
SMB_ACL_PERMSET_T the_permset;
SMB_ACL_TAG_T tag_type;
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
goto fail;
}
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
goto fail;
}
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
i, strerror(errno) ));
goto fail;
}
if (tag_type == SMB_ACL_USER) {
uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
uid_t uid = (uid_t)uidval;
if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
(unsigned int)uid, i, strerror(errno) ));
goto fail;
}
}
if (tag_type == SMB_ACL_GROUP) {
uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
gid_t gid = (uid_t)gidval;
if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
(unsigned int)gid, i, strerror(errno) ));
goto fail;
}
}
}
return the_acl;
fail:
if (the_acl != NULL) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
}
return NULL;
}
BOOL set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
uint16 num_def_acls, const char *pdata)
{
SMB_ACL_T def_acl = NULL;
if (num_def_acls && !S_ISDIR(psbuf->st_mode)) {
DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
errno = EISDIR;
return False;
}
if (!num_def_acls) {
if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
fname, strerror(errno) ));
return False;
}
return True;
}
if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
return False;
}
if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
fname, strerror(errno) ));
SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
return False;
}
DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
return True;
}
static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
{
SMB_ACL_T file_acl = NULL;
int entry_id = SMB_ACL_FIRST_ENTRY;
SMB_ACL_ENTRY_T entry;
BOOL ret = False;
SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
SMB_ACL_ENTRY_T user_ent = NULL;
SMB_ACL_ENTRY_T group_ent = NULL;
SMB_ACL_ENTRY_T other_ent = NULL;
if (new_file_acl == NULL) {
DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
return False;
}
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
fname, strerror(errno) ));
goto done;
}
if (fsp && fsp->fh->fd != -1) {
file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
} else {
file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
}
if (file_acl == NULL) {
DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
fname, strerror(errno) ));
goto done;
}
while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
SMB_ACL_TAG_T tagtype;
SMB_ACL_PERMSET_T permset;
if (entry_id == SMB_ACL_FIRST_ENTRY)
entry_id = SMB_ACL_NEXT_ENTRY;
if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
fname, strerror(errno) ));
goto done;
}
if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
fname, strerror(errno) ));
goto done;
}
if (tagtype == SMB_ACL_USER_OBJ) {
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
fname, strerror(errno) ));
}
} else if (tagtype == SMB_ACL_GROUP_OBJ) {
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
fname, strerror(errno) ));
}
} else if (tagtype == SMB_ACL_OTHER) {
if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
fname, strerror(errno) ));
}
}
}
if (fsp && fsp->fh->fd != -1) {
if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, new_file_acl) == -1) {
DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
fname, strerror(errno) ));
goto done;
}
} else {
if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
fname, strerror(errno) ));
goto done;
}
}
ret = True;
done:
if (file_acl) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
}
if (new_file_acl) {
SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
}
return ret;
}
BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
{
SMB_ACL_T file_acl = NULL;
if (!num_acls) {
return remove_posix_acl(conn, fsp, fname);
}
if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
return False;
}
if (fsp && fsp->fh->fd != -1) {
if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) {
DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
fname, strerror(errno) ));
SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
return False;
}
} else {
if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
fname, strerror(errno) ));
SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
return False;
}
}
DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
return True;
}
static NTSTATUS conn_get_nt_acl(TALLOC_CTX *mem_ctx,
struct connection_struct *conn,
const char *fname,
SMB_STRUCT_STAT *psbuf,
struct security_descriptor_info **psd)
{
NTSTATUS status;
struct files_struct *fsp = NULL;
struct security_descriptor_info *secdesc = NULL;
size_t secdesc_size;
if (!VALID_STAT(*psbuf)) {
if (SMB_VFS_STAT(conn, fname, psbuf) != 0) {
return map_nt_error_from_unix(errno);
}
}
if (S_ISDIR(psbuf->st_mode)) {
status = open_directory(conn, fname, psbuf,
READ_CONTROL_ACCESS,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
0,
FILE_ATTRIBUTE_DIRECTORY,
NULL, &fsp);
}
else {
status = open_file_stat(conn, fname, psbuf, &fsp);
}
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Unable to open file %s: %s\n", fname,
nt_errstr(status)));
return status;
}
secdesc_size = SMB_VFS_GET_NT_ACL(fsp, fname,
(OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION),
&secdesc);
if (secdesc_size == 0) {
DEBUG(5, ("Unable to get NT ACL for file %s\n", fname));
status = NT_STATUS_ACCESS_DENIED;
goto done;
}
*psd = talloc_move(mem_ctx, &secdesc);
status = NT_STATUS_OK;
done:
close_file(fsp, NORMAL_CLOSE);
return status;
}
static BOOL can_access_file_acl(struct connection_struct *conn,
const char * fname, SMB_STRUCT_STAT *psbuf,
uint32_t access_mask)
{
BOOL result;
NTSTATUS status;
uint32_t access_granted;
struct security_descriptor_info *secdesc = NULL;
status = conn_get_nt_acl(tmp_talloc_ctx(), conn, fname, psbuf, &secdesc);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("Could not get acl: %s\n", nt_errstr(status)));
return False;
}
result = se_access_check(secdesc, current_user.nt_user_token,
access_mask, &access_granted, &status);
TALLOC_FREE(secdesc);
return result;
}
#if HAVE_ACCESSX_NP
static const struct
{
int accessx;
int ntperm;
} nt_accessx_table[] =
{
{ _READ_OK, FILE_READ_DATA },
{ _WRITE_OK, FILE_WRITE_DATA },
{ _EXECUTE_OK, FILE_EXECUTE },
{ _DELETE_OK, STD_RIGHT_DELETE_ACCESS },
{ _APPEND_OK, FILE_APPEND_DATA },
{ _RMFILE_OK, FILE_DELETE_CHILD },
{ _RATTR_OK, FILE_READ_ATTRIBUTES },
{ _WATTR_OK, FILE_READ_EA },
{ _REXT_OK, FILE_WRITE_ATTRIBUTES },
{ _WEXT_OK, FILE_WRITE_EA },
{ _RPERM_OK, STD_RIGHT_READ_CONTROL_ACCESS },
{ _WPERM_OK, STD_RIGHT_WRITE_DAC_ACCESS },
{ _CHOWN_OK, STD_RIGHT_WRITE_OWNER_ACCESS },
};
static uint32_t accessx_convert_mask(uint32_t access_mode)
{
uint32_t accessx_mode = 0;
unsigned i;
for (i = 0; i < ARRAY_SIZE(nt_accessx_table); ++i) {
uint32_t p = nt_accessx_table[i].accessx;
if (access_mode & p) {
accessx_mode |= nt_accessx_table[i].ntperm;
}
}
return accessx_mode;
}
static BOOL accessx_check(const char * fname, uint32_t access_mode)
{
uint8_t * accessx_buffer;
size_t fname_size;
struct accessx_descriptor * desc0;
int err0 = 0;
int err;
if (is_ntfs_stream_name(fname)) {
const char * pivot = strchr_m(fname, ':');
fname_size = pivot - fname + 1;
} else {
fname_size = strlen(fname) + 1;
}
accessx_buffer =
alloca(sizeof(struct accessx_descriptor) + fname_size);
memcpy(accessx_buffer + sizeof(struct accessx_descriptor),
fname, fname_size);
accessx_buffer[sizeof(struct accessx_descriptor) + fname_size - 1] = '\0';
desc0 = (struct accessx_descriptor *)accessx_buffer;
ZERO_STRUCTP(desc0);
desc0->ad_flags = accessx_convert_mask(access_mode);
desc0->ad_name_offset = sizeof(struct accessx_descriptor);
err = accessx_np((struct accessx_descriptor *)accessx_buffer,
sizeof(struct accessx_descriptor) + fname_size,
&err0, (uid_t) -1 );
if (err == -1) {
DEBUG(0, ("accessx_np(%s, %#x) failed: %s\n",
fname, access_mode, strerror(errno)));
return False;
}
return (err0 == 0);
}
#endif
BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
{
SMB_STRUCT_STAT sbuf;
pstring dname;
if (!CAN_WRITE(conn)) {
return False;
}
pstrcpy(dname, parent_dirname(fname));
if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
return False;
}
if (!S_ISDIR(sbuf.st_mode)) {
return False;
}
#if HAVE_ACCESSX_NP
return accessx_check(dname, FILE_DELETE_CHILD);
#endif
if (current_user.ut.uid == 0 || conn->admin_user) {
return True;
}
if (current_user.ut.uid == sbuf.st_uid) {
return (sbuf.st_mode & S_IWUSR) ? True : False;
}
#ifdef S_ISVTX
if (sbuf.st_mode & S_ISVTX) {
SMB_STRUCT_STAT sbuf_file;
if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
if (errno == ENOENT) {
return True;
}
return False;
}
if (current_user.ut.uid != sbuf_file.st_uid) {
return False;
}
}
#endif
return can_access_file_acl(conn, dname, &sbuf, FILE_WRITE_DATA);
}
BOOL can_access_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
{
if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
return False;
}
access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);
DEBUG(10,("can_access_file: requesting 0x%x on file %s\n",
(unsigned int)access_mask, fname ));
#if HAVE_ACCESSX_NP
return accessx_check(fname, access_mask);
#endif
if (current_user.ut.uid == 0 || conn->admin_user) {
return True;
}
if (!VALID_STAT(*psbuf)) {
if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
return False;
}
}
#if defined(HAVE_STAT_ST_FLAGS)
#if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
if (access_mask & FILE_WRITE_DATA) {
if (psbuf->st_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
return False;
}
}
#endif
#endif
if (current_user.ut.uid == psbuf->st_uid) {
switch (access_mask) {
case FILE_READ_DATA:
return (psbuf->st_mode & S_IRUSR) ? True : False;
case FILE_WRITE_DATA:
return (psbuf->st_mode & S_IWUSR) ? True : False;
default:
if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) {
return True;
} else {
return False;
}
}
}
return can_access_file_acl(conn, fname, psbuf, access_mask);
}
BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
{
return can_access_file(conn, fname, psbuf, FILE_WRITE_DATA);
}
SEC_DESC* get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
{
SEC_DESC *psd, *ret_sd;
connection_struct conn;
files_struct finfo;
struct fd_handle fh;
pstring path;
pstring filename;
ZERO_STRUCT( conn );
if ( !(conn.mem_ctx = talloc_init( "novfs_get_nt_acl" )) ) {
DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
return NULL;
}
if (!(conn.params = TALLOC_P(conn.mem_ctx, struct share_params))) {
DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
TALLOC_FREE(conn.mem_ctx);
return NULL;
}
conn.params->service = -1;
pstrcpy( path, "/" );
set_conn_connectpath(&conn, path);
if (!smbd_vfs_init(&conn)) {
DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
conn_free_internal( &conn );
return NULL;
}
ZERO_STRUCT( finfo );
ZERO_STRUCT( fh );
finfo.fnum = -1;
finfo.conn = &conn;
finfo.fh = &fh;
finfo.fh->fd = -1;
pstrcpy( filename, fname );
finfo.fsp_name = filename;
if (get_nt_acl( &finfo, DACL_SECURITY_INFORMATION, &psd ) == 0) {
DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
conn_free_internal( &conn );
return NULL;
}
ret_sd = dup_sec_desc( ctx, psd );
conn_free_internal( &conn );
return ret_sd;
}