#include "includes.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ACLS
+
/****************************************************************************
Data structures representing the internal ACE format.
****************************************************************************/
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)
+/*
+ * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
+ * attribute on disk.
+ *
+ * | 1 | 1 | 2 | 2 | ....
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ */
+
+#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
+
+/*
+ * In memory format of user.SAMBA_PAI attribute.
+ */
+
+struct pai_entry {
+ struct pai_entry *next, *prev;
+ enum ace_owner owner_type;
+ posix_id unix_ug;
+};
+
+struct pai_val {
+ BOOL protected;
+ unsigned int num_entries;
+ struct pai_entry *entry_list;
+ unsigned int num_def_entries;
+ struct pai_entry *def_entry_list;
+};
+
+/************************************************************************
+ Return a uint32 of the pai_entry principal.
+************************************************************************/
+
+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;
+ }
+}
+
+/************************************************************************
+ Return a uint32 of the entry principal.
+************************************************************************/
+
+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;
+ }
+}
+
+/************************************************************************
+ Count the inherited entries.
+************************************************************************/
+
+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;
+}
+
+/************************************************************************
+ Create the on-disk format. Caller must free.
+************************************************************************/
+
+static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL 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 = SMB_MALLOC(*store_size);
+ if (!pai_buf) {
+ return NULL;
+ }
+
+ /* Set up the header. */
+ memset(pai_buf, '\0', PAI_ENTRIES_BASE);
+ SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
+ SCVAL(pai_buf,PAI_FLAG_OFFSET,(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;
+}
+
+/************************************************************************
+ Store the user.SAMBA_PAI attribute on disk.
+************************************************************************/
+
+static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
+ canon_ace *dir_ace_list, BOOL protected)
+{
+ int ret;
+ size_t store_size;
+ char *pai_buf;
+
+ if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ return;
+
+ /*
+ * Don't store if this ACL isn't protected and
+ * none of the entries in it are marked as inherited.
+ */
+
+ if (!protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
+ /* Instead just remove the attribute if it exists. */
+ if (fsp->fd != -1)
+ SMB_VFS_FREMOVEXATTR(fsp, fsp->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, protected, &store_size);
+
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FSETXATTR(fsp, fsp->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", protected ? " (protected)" : "", fsp->fsp_name));
+ if (ret == -1 && !no_acl_syscall_error(errno))
+ DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+}
+
+/************************************************************************
+ Delete the in memory inheritance info.
+************************************************************************/
+
+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);
+ }
+}
+
+/************************************************************************
+ Was this ACL protected ?
+************************************************************************/
+
+static BOOL get_protected_flag(struct pai_val *pal)
+{
+ if (!pal)
+ return False;
+ return pal->protected;
+}
+
+/************************************************************************
+ Was this ACE inherited ?
+************************************************************************/
+
+static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
+{
+ struct pai_entry *paie;
+
+ if (!pal)
+ return False;
+
+ /* If the entry exists it is inherited. */
+ 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;
+}
+
+/************************************************************************
+ Ensure an attribute just read is valid.
+************************************************************************/
+
+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) {
+ /* Corrupted - too small. */
+ 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);
+
+ /* Check the entry lists match. */
+ /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
+
+ if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
+ return False;
+
+ return True;
+}
+
+
+/************************************************************************
+ Convert to in-memory format.
+************************************************************************/
+
+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->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->protected ? " (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;
+}
+
+/************************************************************************
+ Load the user.SAMBA_PAI attribute.
+************************************************************************/
+
+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 = SMB_MALLOC(pai_buf_size)) == NULL)
+ return NULL;
+
+ do {
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FGETXATTR(fsp, fsp->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;
+ }
+ /* Buffer too small - enlarge it. */
+ pai_buf_size *= 2;
+ SAFE_FREE(pai_buf);
+ if (pai_buf_size > 1024*1024) {
+ return NULL; /* Limit malloc to 1mb. */
+ }
+ if ((pai_buf = 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) {
+ /* No attribute or not supported. */
+#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->protected)
+ DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
+
+ SAFE_FREE(pai_buf);
+ return paiv;
+}
+
/****************************************************************************
Functions to manipulate the internal ACE format.
****************************************************************************/
static canon_ace *dup_canon_ace( canon_ace *src_ace)
{
- canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+ canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
if (dst_ace == NULL)
return NULL;
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);
+ 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);
+ dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
} else
dbgtext( "other ");
switch (pace->type) {
dbgtext( "SMB_ACL_OTHER ");
break;
}
+ if (pace->inherited)
+ dbgtext( "(inherited) ");
dbgtext( "perms ");
dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
Unpack a SEC_DESC into a UNIX owner and group.
****************************************************************************/
-static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+static BOOL unpack_nt_owners(int snum, SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
{
DOM_SID owner_sid;
DOM_SID grp_sid;
if (security_info_sent & OWNER_SECURITY_INFORMATION) {
sid_copy(&owner_sid, psd->owner_sid);
- if (NT_STATUS_IS_ERR(sid_to_uid(&owner_sid, puser))) {
-#if ACL_FORCE_UNMAPPABLE
- /* this allows take ownership to work reasonably */
- extern struct current_user current_user;
- *puser = current_user.uid;
-#else
- DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
- sid_string_static(&owner_sid)));
- return False;
-#endif
+ if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) {
+ if (lp_force_unknown_acl_user(snum)) {
+ /* this allows take ownership to work
+ * reasonably */
+ extern struct current_user current_user;
+ *puser = current_user.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->grp_sid);
- if (NT_STATUS_IS_ERR(sid_to_gid( &grp_sid, pgrp))) {
-#if ACL_FORCE_UNMAPPABLE
- /* this allows take group ownership to work reasonably */
- extern struct current_user current_user;
- *pgrp = current_user.gid;
-#else
- DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
- return False;
-#endif
+ if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) {
+ if (lp_force_unknown_acl_user(snum)) {
+ /* this allows take group ownership to work
+ * reasonably */
+ extern struct current_user current_user;
+ *pgrp = current_user.gid;
+ } else {
+ DEBUG(3,("unpack_nt_owners: unable to validate"
+ " group sid.\n"));
+ return False;
+ }
}
}
}
if (!got_user) {
- if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
}
if (!got_grp) {
- if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
}
if (!got_other) {
- if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
return False;
}
Unpack a SEC_DESC into two canonical ace lists.
****************************************************************************/
-static BOOL create_canon_ace_lists(files_struct *fsp,
+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,
* Create a cannon_ace entry representing this NT DACL ACE.
*/
- if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ 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"));
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.world = -1;
+ current_ace->unix_ug.uid = pst->st_uid;
current_ace->type = SMB_ACL_USER_OBJ;
/*
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.world = -1;
+ 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 (NT_STATUS_IS_OK(sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid))) {
- current_ace->owner_type = GID_ACE;
- current_ace->type = SMB_ACL_GROUP;
} else if (NT_STATUS_IS_OK(sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid))) {
current_ace->owner_type = UID_ACE;
current_ace->type = SMB_ACL_USER;
+ } else if (NT_STATUS_IS_OK(sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid))) {
+ current_ace->owner_type = GID_ACE;
+ current_ace->type = SMB_ACL_GROUP;
} else {
fstring str;
current_ace->perms |= map_nt_perms( psa->info, 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);
/*
* Now add the created ace to either the file list, the directory
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) : S_IRUSR;
+ mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name, False) : S_IRUSR;
if (fsp->is_directory)
mode |= (S_IWUSR|S_IXUSR);
* Now go through the DACL and create the canon_ace lists.
*/
- if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
+ if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
&file_ace, &dir_ace, psd->dacl))
return False;
****************************************************************************/
static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
- DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T the_acl_type)
+ DOM_SID *powner, DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
{
extern DOM_SID global_sid_World;
connection_struct *conn = fsp->conn;
* entries out of the blue when setting ACLs, so a get/set
* cycle will drop them.
*/
- if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
+ 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;
* Add this entry to the list.
*/
- if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
+ if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
goto fail;
ZERO_STRUCTP(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;
- arrange_posix_perms(fsp->fsp_name,&list_head );
-
/*
* Now go through the list, masking the permissions with the
* acl_mask. Ensure all DENY Entries are at the start of the list.
*/
- DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
+ 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;
}
}
+ arrange_posix_perms(fsp->fsp_name,&list_head );
+
print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
return list_head;
if (the_acl == NULL) {
- if (errno != ENOSYS) {
+ if (!no_acl_syscall_error(errno)) {
/*
* Only print this error message if we have some kind of ACL
* support that's not working. Otherwise we would always get this.
if( DEBUGLVL( 10 ))
print_canon_ace( p_ace, i);
+
}
if (needs_mask && !got_mask_entry) {
* Some systems allow all the above calls and only fail with no ACL support
* when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
*/
- if (errno == ENOSYS)
- *pacl_set_support = False;
-
-#ifdef ENOTSUP
- if (errno == ENOTSUP)
+ if (no_acl_syscall_error(errno)) {
*pacl_set_support = False;
-#endif
+ }
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",
* Some systems allow all the above calls and only fail with no ACL support
* when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
*/
- if (errno == ENOSYS)
- *pacl_set_support = False;
-
-#ifdef ENOTSUP
- if (errno == ENOTSUP)
+ if (no_acl_syscall_error(errno)) {
*pacl_set_support = False;
-#endif
+ }
DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
fsp->fsp_name, strerror(errno) ));
return True;
}
-static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
-{
- if (a1->type == a2->type)
- return 0;
-
- if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
- return -1;
- return 1;
-}
-
/****************************************************************************
Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
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;
+
/* We know the lower number ACE's are file entries. */
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].info.mask == nt_ace_list[j].info.mask) &&
sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
- (nt_ace_list[i].flags == 0) &&
- (nt_ace_list[j].flags == (SEC_ACE_FLAG_OBJECT_INHERIT|
- SEC_ACE_FLAG_CONTAINER_INHERIT|
- SEC_ACE_FLAG_INHERIT_ONLY))) {
+ (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))) {
/*
- * These are identical except for the flags.
- * Merge the inherited ACE onto the non-inherited ACE.
+ * W2K wants to have access allowed zero access ACE's
+ * at the end of the list. If the mask is zero, merge
+ * the non-inherited ACE onto the inherited ACE.
*/
- nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT;
- if (num_aces - j - 1 > 0)
- memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
- sizeof(SEC_ACE));
+ if (nt_ace_list[i].info.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 {
+ /*
+ * These are identical except for the flags.
+ * Merge the inherited ACE onto the non-inherited ACE.
+ */
+
+ 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;
}
size_t sd_size = 0;
SEC_ACL *psa = NULL;
size_t num_acls = 0;
- size_t num_dir_acls = 0;
+ size_t num_def_acls = 0;
size_t num_aces = 0;
SMB_ACL_T posix_acl = NULL;
- SMB_ACL_T dir_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) {
- dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
- dir_acl = free_empty_sys_acl(conn, dir_acl);
+ 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 {
DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
posix_acl ? "present" : "absent",
- dir_acl ? "present" : "absent" ));
+ def_acl ? "present" : "absent" ));
+
+ pal = load_inherited_info(fsp);
/*
* Get the owner, group and world SIDs.
create_file_sids(&sbuf, &owner_sid, &group_sid);
}
- if (security_info & DACL_SECURITY_INFORMATION) {
+ if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
/*
* In the optimum case Creator Owner and Creator Group would be used for
*/
/* Create the canon_ace lists. */
- file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS );
+ file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
/* We must have *some* ACLS. */
return 0;
}
- if (fsp->is_directory && dir_acl) {
- dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
+ if (fsp->is_directory && def_acl) {
+ dir_ace = canonicalise_acl(fsp, def_acl, &sbuf,
&global_sid_Creator_Owner,
- &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT );
+ &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
}
/*
DLIST_REMOVE(file_ace, ace);
SAFE_FREE(ace);
}
- } else {
-
- 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(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
- if (ace && !ace->perms) {
- DLIST_REMOVE(dir_ace, ace);
- SAFE_FREE(ace);
- }
}
-
+
num_acls = count_canon_ace_list(file_ace);
- num_dir_acls = count_canon_ace_list(dir_ace);
+ num_def_acls = count_canon_ace_list(dir_ace);
/* Allocate the ace list. */
- if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
+ 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_dir_acls) * sizeof(SEC_ACE) );
+ memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
/*
* Create the NT ACE list from the canonical ace lists.
SEC_ACCESS acc;
acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
- init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
}
/* The User must have access to a profile share - even if we can't map the SID. */
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);
+ 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_dir_acls; i++, ace = ace->next) {
+ for (i = 0; i < num_def_acls; i++, ace = ace->next) {
SEC_ACCESS acc;
acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
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);
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|
+ (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
}
/* The User must have access to a profile share - even if we can't map the SID. */
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);
+ SEC_ACE_FLAG_INHERIT_ONLY|0);
}
/*
num_aces = merge_default_aces(nt_ace_list, num_aces);
- /*
- * Sort to force deny entries to the front.
- */
-
- if (num_aces)
- qsort( nt_ace_list, num_aces, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
}
if (num_aces) {
- if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
+ 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;
}
}
} /* security_info & DACL_SECURITY_INFORMATION */
- *ppdesc = make_standard_sec_desc( main_loop_talloc_get(),
+ 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(!*ppdesc) {
+ if(!psd) {
DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
sd_size = 0;
} else {
* inherited at file create time, so ACLs never contain
* any ACEs that are inherited dynamically. The DACL_PROTECTED
* flag doesn't seem to bother Windows NT.
+ * Always set this if map acl inherit is turned off.
*/
- (*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
+ 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->ace, (unsigned int)psd->dacl->num_aces);
+
+ *ppdesc = psd;
+
done:
if (posix_acl)
SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
- if (dir_acl)
- SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_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;
* Unpack the user/group/world id's.
*/
- if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
+ if (!unpack_nt_owners( SNUM(conn), &sbuf, &user, &grp, security_info_sent, psd))
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 we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
*/
return True;
}
+/****************************************************************************
+ Get the actual group bits stored on a file with an ACL. Has no effect if
+ the file has no ACL. Needed in dosmode code where the stat() will return
+ the mask bits, not the real group bits, for a file with an ACL.
+****************************************************************************/
+
+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;
+
+ /* get_next... */
+ 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;
+}
+
/****************************************************************************
Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
and set the mask to rwx. Needed to preserve complex ACLs set by NT.
BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
{
- SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
- BOOL has_acl = False;
- SMB_ACL_ENTRY_T entry;
+ 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 (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
- has_acl = True;
+ if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
+ has_acl = True;
+ }
- if (dir_acl)
- SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
+ if (def_acl) {
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+ }
return has_acl;
}
+
+/****************************************************************************
+ Map from wire type to permset.
+****************************************************************************/
+
+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;
+}
+
+/****************************************************************************
+ Map from wire type to tagtype.
+****************************************************************************/
+
+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;
+}
+
+/****************************************************************************
+ Create a new POSIX acl from wire permissions.
+ FIXME ! How does the share mask/mode fit into this.... ?
+****************************************************************************/
+
+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;
+ }
+
+ /* Get the permset pointer from the new ACL entry. */
+ 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;
+ }
+
+ /* Map from wire to permissions. */
+ 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;
+ }
+
+ /* Now apply to the new ACL entry. */
+ 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;
+}
+
+/****************************************************************************
+ Calls from UNIX extensions - Default POSIX ACL set.
+ If num_def_acls == 0 and not a directory just return. If it is a directory
+ and num_def_acls == 0 then remove the default acl. Else set the default acl
+ on the directory.
+****************************************************************************/
+
+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) {
+ /* Remove the default ACL. */
+ 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;
+}
+
+/****************************************************************************
+ Remove an ACL from a file. As we don't have acl_delete_entry() available
+ we must read the current acl and copy all entries except MASK, USER and GROUP
+ to a new acl, then set that. This (at least on Linux) causes any ACL to be
+ removed.
+ FIXME ! How does the share mask/mode fit into this.... ?
+****************************************************************************/
+
+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;
+ /* Create a new ACL with only 3 entries, u/g/w. */
+ 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;
+ }
+
+ /* Now create the u/g/w entries. */
+ 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;
+ }
+
+ /* Get the current file ACL. */
+ if (fsp && fsp->fd != -1) {
+ file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
+ } else {
+ file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (file_acl == NULL) {
+ /* This is only returned if an error occurred. Even for a file with
+ no acl a u/g/w acl should be returned. */
+ 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;
+
+ /* get_next... */
+ 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) ));
+ }
+ }
+ }
+
+ 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;
+}
+
+/****************************************************************************
+ Calls from UNIX extensions - POSIX ACL set.
+ If num_def_acls == 0 then read/modify/write acl after removing all entries
+ except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
+****************************************************************************/
+
+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) {
+ /* Remove the ACL from the file. */
+ 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->fd != -1) {
+ /* The preferred way - use an open fd. */
+ if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->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;
+}