+#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 SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
+
+#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 = 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 = 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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 && errno != ENOSYS)
+ 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 = malloc(sizeof(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 = malloc(sizeof(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 = malloc(sizeof(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 = 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 = malloc(pai_buf_size)) == NULL)
+ return NULL;
+ }
+ } while (ret == -1);
+
+ DEBUG(10,("load_inherited_info: ret = %d for file %s\n", 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.
+****************************************************************************/
+
+/****************************************************************************
+ Count a linked list of canonical ACE entries.
+****************************************************************************/
+
+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;
+}
+
+/****************************************************************************
+ Free a linked list of canonical ACE entries.
+****************************************************************************/
+
+static void free_canon_ace_list( canon_ace *list_head )
+{
+ while (list_head) {
+ canon_ace *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head);
+ }
+}
+
+/****************************************************************************
+ Function to duplicate a canon_ace entry.
+****************************************************************************/
+
+static canon_ace *dup_canon_ace( canon_ace *src_ace)
+{
+ canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+
+ if (dst_ace == NULL)
+ return NULL;
+
+ *dst_ace = *src_ace;
+ dst_ace->prev = dst_ace->next = NULL;
+ return dst_ace;
+}
+
+/****************************************************************************
+ Print out a canon 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;
+ }
+ 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' : '-');
+}
+
+/****************************************************************************
+ Print out a canon ace list.
+****************************************************************************/
+
+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 );
+ }
+}
+
+/****************************************************************************
+ Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+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;
+}
+
+/****************************************************************************
+ Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+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;
+}
+
+/****************************************************************************
+ Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
+ an SMB_ACL_PERMSET_T.
+****************************************************************************/
+
+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;
+}
+/****************************************************************************
+ Function to create owner and group SIDs from a SMB_STRUCT_STAT.
+****************************************************************************/
+
+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 );
+}
+
+/****************************************************************************
+ Merge aces with a common sid - if both are allow or deny, OR the permissions together and
+ delete the second one. If the first is deny, mask the permissions off and delete the allow
+ if the permissions become zero, delete the deny if the permissions are non zero.
+****************************************************************************/
+
+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;
+
+ /*
+ * First, merge allow entries with identical SIDs, and deny entries
+ * with identical SIDs.
+ */
+
+ 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; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (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);
+ }
+
+ /* Merge two allow or two deny ACE's. */
+
+ 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; /* We may have deleted the link. */
+ }
+ }
+ }
+
+ /*
+ * Now go through and mask off allow permissions with deny permissions.
+ * We can delete either the allow or deny here as we know that each SID
+ * appears only once in the list.
+ */
+
+ 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; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ /*
+ * Subtract ACE's with different entries. Due to the ordering constraints
+ * we've put on the ACL, we know the deny must be the first one.
+ */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (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) {
+
+ /*
+ * The deny overrides the allow. Remove the allow.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+
+ } else {
+
+ /*
+ * Even after removing permissions, there
+ * are still allow permissions - delete the deny.
+ * It is safe to delete the deny here,
+ * as we are guarenteed by the deny first
+ * ordering that all the deny entries for
+ * this SID have already been merged into one
+ * before we can get to an allow ace.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace_outer);
+ SAFE_FREE(curr_ace_outer);
+ break;
+ }
+ }
+
+ } /* end for curr_ace */
+ } /* end for curr_ace_outer */
+
+ /* We may have modified the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Check if we need to return NT4.x compatible ACL entries.
+****************************************************************************/
+
+static BOOL nt4_compatible_acls(void)
+{
+ const char *compat = lp_acl_compatibility();
+
+ if (*compat == '\0') {
+ enum remote_arch_types ra_type = get_remote_arch();
+
+ /* Automatically adapt to client */
+ return (ra_type <= RA_WINNT);
+ } else
+ return (strequal(compat, "winnt"));
+}
+
+
+/****************************************************************************
+ Map canon_ace perms to permission bits NT.
+ The attr element is not used here - we only process deny entries on set,
+ not get. Deny entries are implicit on get with ace->perms = 0.
+****************************************************************************/
+
+static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
+{
+ SEC_ACCESS sa;
+ uint32 nt_mask = 0;
+
+ *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+ if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
+ nt_mask = UNIX_ACCESS_RWX;
+ } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+ /*
+ * Windows NT refuses to display ACEs with no permissions in them (but
+ * they are perfectly legal with Windows 2000). If the ACE has empty
+ * permissions we cannot use 0, so we use the otherwise unused
+ * WRITE_OWNER permission, which we ignore when we set an ACL.
+ * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
+ * to be changed in the future.
+ */
+
+ if (nt4_compatible_acls())
+ nt_mask = UNIX_ACCESS_NONE;
+ else
+ nt_mask = 0;
+ } else {
+ nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
+ nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
+ nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
+ }
+
+ DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
+ (unsigned int)ace->perms, (unsigned int)nt_mask ));
+
+ init_sec_access(&sa,nt_mask);
+ return sa;
+}
+
+/****************************************************************************
+ Map NT perms to a UNIX mode_t.
+****************************************************************************/
+
+#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( SEC_ACCESS sec_access, int type)
+{
+ mode_t mode = 0;
+
+ switch(type) {
+ case S_IRUSR:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRUSR|S_IWUSR|S_IXUSR;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+ }
+ break;
+ case S_IRGRP:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRGRP|S_IWGRP|S_IXGRP;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+ }
+ break;
+ case S_IROTH:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IROTH|S_IWOTH|S_IXOTH;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+ }
+ break;
+ }
+
+ return mode;
+}
+
+/****************************************************************************
+ 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)
+{
+ 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;
+ }
+
+ /*
+ * Validate the owner and group SID's.
+ */
+
+ memset(&owner_sid, '\0', sizeof(owner_sid));
+ memset(&grp_sid, '\0', sizeof(grp_sid));
+
+ DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
+
+ /*
+ * Don't immediately fail if the owner sid cannot be validated.
+ * This may be a group chown only set.
+ */
+
+ if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+ sid_copy(&owner_sid, psd->owner_sid);
+ if (!NT_STATUS_IS_OK(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
+ }
+ }
+
+ /*
+ * Don't immediately fail if the group sid cannot be validated.
+ * This may be an owner chown only set.
+ */
+
+ if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+ sid_copy(&grp_sid, psd->grp_sid);
+ if (!NT_STATUS_IS_OK(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
+ }
+ }
+
+ DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Ensure the enforced permissions for this share apply.
+****************************************************************************/
+
+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;
+
+ /* Get the initial bits to apply. */
+
+ 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);
+ }
+
+ /* Now bounce them into the S_USR space. */
+ switch(type) {
+ case S_IRUSR:
+ /* Ensure owner has read access. */
+ 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);
+}
+