Avoid duplicate aces
[ira/wip.git] / source3 / smbd / posix_acls.c
index 627bfb4634688e6bc94e8dfe05f2584a8511cfa8..39fb32f654c6f34013d0e7f20681e64919bd024f 100644 (file)
@@ -1,8 +1,9 @@
 /*
    Unix SMB/CIFS implementation.
    SMB NT Security Descriptor / Unix permission conversion.
-   Copyright (C) Jeremy Allison 1994-2000.
+   Copyright (C) Jeremy Allison 1994-2009.
    Copyright (C) Andreas Gruenbacher 2002.
+   Copyright (C) Simo Sorce <idra@samba.org> 2009.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -47,30 +48,65 @@ typedef struct canon_ace {
        enum ace_owner owner_type;
        enum ace_attribute attr;
        posix_id unix_ug;
-       bool inherited;
+       uint8_t ace_flags; /* From windows ACE entry. */
 } 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.
+ * attribute on disk - version 1.
+ * All values are little endian.
  *
- * |  1   |  1   |   2         |         2           |  .... 
+ * |  1   |  1   |   2         |         2           |  ....
  * +------+------+-------------+---------------------+-------------+--------------------+
  * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
  * +------+------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * |  1   |       4           |
+ * +------+-------------------+
+ * | value|  uid/gid or world |
+ * | type |  value            |
+ * +------+-------------------+
+ *
+ * Version 2 format. Stores extra Windows metadata about an ACL.
+ *
+ * |  1   |  2       |   2         |         2           |  ....
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ * | vers | ace      | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * |   2  |  type    |             |                     |             |                    |
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * |  1   |  1   |       4           |
+ * +------+------+-------------------+
+ * | ace  | value|  uid/gid or world |
+ * | flag | type |  value            |
+ * +------+-------------------+------+
+ *
  */
 
-#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_OFFSET                     0
+
+#define PAI_V1_FLAG_OFFSET                     1
+#define PAI_V1_NUM_ENTRIES_OFFSET              2
+#define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET      4
+#define PAI_V1_ENTRIES_BASE                    6
+#define PAI_V1_ACL_FLAG_PROTECTED              0x1
+#define PAI_V1_ENTRY_LENGTH                    5
 
-#define PAI_VERSION            1
-#define PAI_ACL_FLAG_PROTECTED 0x1
-#define PAI_ENTRY_LENGTH       5
+#define PAI_V1_VERSION                         1
+
+#define PAI_V2_TYPE_OFFSET                     1
+#define PAI_V2_NUM_ENTRIES_OFFSET              3
+#define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET      5
+#define PAI_V2_ENTRIES_BASE                    7
+#define PAI_V2_ENTRY_LENGTH                    6
+
+#define PAI_V2_VERSION                         2
 
 /*
  * In memory format of user.SAMBA_PAI attribute.
@@ -78,12 +114,13 @@ typedef struct canon_ace {
 
 struct pai_entry {
        struct pai_entry *next, *prev;
+       uint8_t ace_flags;
        enum ace_owner owner_type;
        posix_id unix_ug;
 };
 
 struct pai_val {
-       bool pai_protected;
+       uint16_t sd_type;
        unsigned int num_entries;
        struct pai_entry *entry_list;
        unsigned int num_def_entries;
@@ -94,19 +131,19 @@ struct pai_val {
  Return a uint32 of the pai_entry principal.
 ************************************************************************/
 
-static uint32 get_pai_entry_val(struct pai_entry *paie)
+static uint32_t 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;
+                       return (uint32_t)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;
+                       return (uint32_t)paie->unix_ug.gid;
                case WORLD_ACE:
                default:
                        DEBUG(10,("get_pai_entry_val: world ace\n"));
-                       return (uint32)-1;
+                       return (uint32_t)-1;
        }
 }
 
@@ -114,41 +151,30 @@ static uint32 get_pai_entry_val(struct pai_entry *paie)
  Return a uint32 of the entry principal.
 ************************************************************************/
 
-static uint32 get_entry_val(canon_ace *ace_entry)
+static uint32_t 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;
+                       return (uint32_t)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;
+                       return (uint32_t)ace_entry->unix_ug.gid;
                case WORLD_ACE:
                default:
                        DEBUG(10,("get_entry_val: world ace\n"));
-                       return (uint32)-1;
+                       return (uint32_t)-1;
        }
 }
 
 /************************************************************************
- Count the inherited entries.
+ Create the on-disk format (always v2 now). Caller must free.
 ************************************************************************/
 
-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 pai_protected, size_t *store_size)
+static char *create_pai_buf_v2(canon_ace *file_ace_list,
+                               canon_ace *dir_ace_list,
+                               uint16_t sd_type,
+                               size_t *store_size)
 {
        char *pai_buf = NULL;
        canon_ace *ace_list = NULL;
@@ -156,17 +182,18 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, b
        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 = file_ace_list; ace_list; ace_list = ace_list->next) {
+               num_entries++;
+       }
 
-       for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
-               if (ace_list->inherited)
-                       num_def_entries++;
+       for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
+               num_def_entries++;
+       }
 
-       DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
+       DEBUG(10,("create_pai_buf_v2: 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);
+       *store_size = PAI_V2_ENTRIES_BASE +
+               ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
 
        pai_buf = (char *)SMB_MALLOC(*store_size);
        if (!pai_buf) {
@@ -174,34 +201,32 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, b
        }
 
        /* 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,(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);
+       memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
+       SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
+       SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
+       SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
+       SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
 
-       entry_offset = pai_buf + PAI_ENTRIES_BASE;
+       entry_offset = pai_buf + PAI_V2_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);
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
 
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               entry_offset += PAI_V2_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);
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
 
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               entry_offset += PAI_V2_ENTRY_LENGTH;
        }
 
        return pai_buf;
@@ -211,44 +236,39 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, b
  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 pai_protected)
+static void store_inheritance_attributes(files_struct *fsp,
+                                       canon_ace *file_ace_list,
+                                       canon_ace *dir_ace_list,
+                                       uint16_t sd_type)
 {
        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 (!pai_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->fh->fd != -1)
-                       SMB_VFS_FREMOVEXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME);
-               else
-                       SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
+       if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
                return;
        }
 
-       pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size);
+       pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
+                               sd_type, &store_size);
 
-       if (fsp->fh->fd != -1)
+       if (fsp->fh->fd != -1) {
                ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
                                pai_buf, store_size, 0);
-       else
+       } 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(10,("store_inheritance_attribute: type 0x%x for file %s\n",
+               (unsigned int)sd_type,
+               fsp->fsp_name));
+
+       if (ret == -1 && !no_acl_syscall_error(errno)) {
                DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+       }
 }
 
 /************************************************************************
@@ -272,160 +292,289 @@ static void free_inherited_info(struct pai_val *pal)
 }
 
 /************************************************************************
- Was this ACL protected ?
-************************************************************************/
-
-static bool get_protected_flag(struct pai_val *pal)
-{
-       if (!pal)
-               return False;
-       return pal->pai_protected;
-}
-
-/************************************************************************
- Was this ACE inherited ?
+ Get any stored ACE flags.
 ************************************************************************/
 
-static bool get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
+static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
 {
        struct pai_entry *paie;
 
-       if (!pal)
-               return False;
+       if (!pal) {
+               return 0;
+       }
 
        /* 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 paie->ace_flags;
        }
-       return False;
+       return 0;
 }
 
 /************************************************************************
- Ensure an attribute just read is valid.
+ Ensure an attribute just read is valid - v1.
 ************************************************************************/
 
-static bool check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
+static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
 {
        uint16 num_entries;
        uint16 num_def_entries;
 
-       if (pai_buf_data_size < PAI_ENTRIES_BASE) {
+       if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
                /* Corrupted - too small. */
-               return False;
+               return false;
        }
 
-       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
-               return False;
+       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
+               return false;
+       }
 
-       num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
-       num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+       num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
+       num_def_entries = SVAL(pai_buf,PAI_V1_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;
+       if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
+                       PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
+               return false;
+       }
 
-       return True;
+       return true;
 }
 
+/************************************************************************
+ Ensure an attribute just read is valid - v2.
+************************************************************************/
+
+static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
+{
+       uint16 num_entries;
+       uint16 num_def_entries;
+
+       if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
+               /* Corrupted - too small. */
+               return false;
+       }
+
+       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
+               return false;
+       }
+
+       num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
+       num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+       /* Check the entry lists match. */
+       /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
+
+       if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
+                       PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
+               return false;
+       }
+
+       return true;
+}
 
 /************************************************************************
Convert to in-memory format.
Decode the owner.
 ************************************************************************/
 
-static struct pai_val *create_pai_val(char *buf, size_t size)
+static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
+{
+       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,("get_pai_owner_type: 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,("get_pai_owner_type: gid = %u\n",
+                               (unsigned int)paie->unix_ug.gid ));
+                       break;
+               case WORLD_ACE:
+                       paie->unix_ug.world = -1;
+                       DEBUG(10,("get_pai_owner_type: world ace\n"));
+                       break;
+               default:
+                       return false;
+       }
+       return true;
+}
+
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+static const char *create_pai_v1_entries(struct pai_val *paiv,
+                               const char *entry_offset,
+                               bool def_entry)
 {
-       char *entry_offset;
-       struct pai_val *paiv = NULL;
        int i;
 
-       if (!check_pai_ok(buf, size))
+       for (i = 0; i < paiv->num_entries; i++) {
+               struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
+               if (!paie) {
+                       return NULL;
+               }
+
+               paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
+               if (!get_pai_owner_type(paie, entry_offset)) {
+                       return NULL;
+               }
+
+               if (!def_entry) {
+                       DLIST_ADD(paiv->entry_list, paie);
+               } else {
+                       DLIST_ADD(paiv->def_entry_list, paie);
+               }
+               entry_offset += PAI_V1_ENTRY_LENGTH;
+       }
+       return entry_offset;
+}
+
+/************************************************************************
+ Convert to in-memory format from version 1.
+************************************************************************/
+
+static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
+{
+       const char *entry_offset;
+       struct pai_val *paiv = NULL;
+
+       if (!check_pai_ok_v1(buf, size)) {
                return NULL;
+       }
 
        paiv = SMB_MALLOC_P(struct pai_val);
-       if (!paiv)
+       if (!paiv) {
                return NULL;
+       }
 
        memset(paiv, '\0', sizeof(struct pai_val));
 
-       paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
+       paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
+                       SE_DESC_DACL_PROTECTED : 0;
 
-       paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
-       paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+       paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
+       paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
 
-       entry_offset = buf + PAI_ENTRIES_BASE;
+       entry_offset = buf + PAI_V1_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 ));
+       DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
+                       paiv->num_entries, paiv->num_def_entries ));
 
-       for (i = 0; i < paiv->num_entries; i++) {
-               struct pai_entry *paie;
+       entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+       entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+
+       return paiv;
+}
 
-               paie = SMB_MALLOC_P(struct pai_entry);
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+static const char *create_pai_v2_entries(struct pai_val *paiv,
+                               const char *entry_offset,
+                               bool def_entry)
+{
+       int i;
+
+       for (i = 0; i < paiv->num_entries; i++) {
+               struct pai_entry *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);
-       }
+               paie->ace_flags = CVAL(entry_offset,0);
 
-       for (i = 0; i < paiv->num_def_entries; i++) {
-               struct pai_entry *paie;
+               entry_offset++;
 
-               paie = SMB_MALLOC_P(struct pai_entry);
-               if (!paie) {
-                       free_inherited_info(paiv);
+               if (!get_pai_owner_type(paie, entry_offset)) {
                        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;
+               if (!def_entry) {
+                       DLIST_ADD(paiv->entry_list, paie);
+               } else {
+                       DLIST_ADD(paiv->def_entry_list, paie);
                }
-               entry_offset += PAI_ENTRY_LENGTH;
-               DLIST_ADD(paiv->def_entry_list, paie);
+               entry_offset += PAI_V2_ENTRY_LENGTH;
+       }
+       return entry_offset;
+}
+
+/************************************************************************
+ Convert to in-memory format from version 2.
+************************************************************************/
+
+static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
+{
+       const char *entry_offset;
+       struct pai_val *paiv = NULL;
+
+       if (!check_pai_ok_v2(buf, size)) {
+               return NULL;
+       }
+
+       paiv = SMB_MALLOC_P(struct pai_val);
+       if (!paiv) {
+               return NULL;
+       }
+
+       memset(paiv, '\0', sizeof(struct pai_val));
+
+       paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
+
+       paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
+       paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+       entry_offset = buf + PAI_V2_ENTRIES_BASE;
+
+       DEBUG(10,("create_pai_val_v2: num_entries = %u, num_def_entries = %u\n",
+                       paiv->num_entries, paiv->num_def_entries ));
+
+       entry_offset = create_pai_v2_entries(paiv, entry_offset, false);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+       entry_offset = create_pai_v2_entries(paiv, entry_offset, true);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
        }
 
        return paiv;
 }
 
+/************************************************************************
+ Convert to in-memory format - from either version 1 or 2.
+************************************************************************/
+
+static struct pai_val *create_pai_val(const char *buf, size_t size)
+{
+       if (size < 1) {
+               return NULL;
+       }
+       if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
+               return create_pai_val_v1(buf, size);
+       } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
+               return create_pai_val_v2(buf, size);
+       } else {
+               return NULL;
+       }
+}
+
 /************************************************************************
  Load the user.SAMBA_PAI attribute.
 ************************************************************************/
@@ -437,19 +586,22 @@ static struct pai_val *fload_inherited_info(files_struct *fsp)
        struct pai_val *paiv = NULL;
        ssize_t ret;
 
-       if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+       if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
                return NULL;
+       }
 
-       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
+       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
                return NULL;
+       }
 
        do {
-               if (fsp->fh->fd != -1)
+               if (fsp->fh->fd != -1) {
                        ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
                                        pai_buf, pai_buf_size);
-               else
+               } 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) {
@@ -483,8 +635,11 @@ static struct pai_val *fload_inherited_info(files_struct *fsp)
 
        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));
+       if (paiv) {
+               DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
+                       (unsigned int)paiv->sd_type,
+                       fsp->fsp_name));
+       }
 
        SAFE_FREE(pai_buf);
        return paiv;
@@ -547,8 +702,10 @@ static struct pai_val *load_inherited_info(const struct connection_struct *conn,
 
        paiv = create_pai_val(pai_buf, ret);
 
-       if (paiv && paiv->pai_protected) {
-               DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fname));
+       if (paiv) {
+               DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
+                       (unsigned int)paiv->sd_type,
+                       fname));
        }
 
        SAFE_FREE(pai_buf);
@@ -563,12 +720,12 @@ static struct pai_val *load_inherited_info(const struct connection_struct *conn,
  Count a linked list of canonical ACE entries.
 ****************************************************************************/
 
-static size_t count_canon_ace_list( canon_ace *list_head )
+static size_t count_canon_ace_list( canon_ace *l_head )
 {
        size_t count = 0;
        canon_ace *ace;
 
-       for (ace = list_head; ace; ace = ace->next)
+       for (ace = l_head; ace; ace = ace->next)
                count++;
 
        return count;
@@ -578,13 +735,13 @@ static size_t count_canon_ace_list( canon_ace *list_head )
  Free a linked list of canonical ACE entries.
 ****************************************************************************/
 
-static void free_canon_ace_list( canon_ace *list_head )
+static void free_canon_ace_list( canon_ace *l_head )
 {
        canon_ace *list, *next;
 
-       for (list = list_head; list; list = next) {
+       for (list = l_head; list; list = next) {
                next = list->next;
-               DLIST_REMOVE(list_head, list);
+               DLIST_REMOVE(l_head, list);
                SAFE_FREE(list);
        }
 }
@@ -641,8 +798,8 @@ static void print_canon_ace(canon_ace *pace, int num)
                        dbgtext( "MASK " );
                        break;
        }
-       if (pace->inherited)
-               dbgtext( "(inherited) ");
+
+       dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
        dbgtext( "perms ");
        dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
        dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
@@ -760,7 +917,7 @@ static bool identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
 
 static void merge_aces( canon_ace **pp_list_head )
 {
-       canon_ace *list_head = *pp_list_head;
+       canon_ace *l_head = *pp_list_head;
        canon_ace *curr_ace_outer;
        canon_ace *curr_ace_outer_next;
 
@@ -769,7 +926,7 @@ static void merge_aces( canon_ace **pp_list_head )
         * with identical SIDs.
         */
 
-       for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+       for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
                canon_ace *curr_ace;
                canon_ace *curr_ace_next;
 
@@ -791,7 +948,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                /* Merge two allow or two deny ACE's. */
 
                                curr_ace_outer->perms |= curr_ace->perms;
-                               DLIST_REMOVE(list_head, curr_ace);
+                               DLIST_REMOVE(l_head, curr_ace);
                                SAFE_FREE(curr_ace);
                                curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
                        }
@@ -804,7 +961,7 @@ static void merge_aces( canon_ace **pp_list_head )
         * appears only once in the list.
         */
 
-       for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+       for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
                canon_ace *curr_ace;
                canon_ace *curr_ace_next;
 
@@ -836,7 +993,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                         * The deny overrides the allow. Remove the allow.
                                         */
 
-                                       DLIST_REMOVE(list_head, curr_ace);
+                                       DLIST_REMOVE(l_head, curr_ace);
                                        SAFE_FREE(curr_ace);
                                        curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
 
@@ -852,7 +1009,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                         * before we can get to an allow ace.
                                         */
 
-                                       DLIST_REMOVE(list_head, curr_ace_outer);
+                                       DLIST_REMOVE(l_head, curr_ace_outer);
                                        SAFE_FREE(curr_ace_outer);
                                        break;
                                }
@@ -863,7 +1020,7 @@ static void merge_aces( canon_ace **pp_list_head )
 
        /* We may have modified the list. */
 
-       *pp_list_head = list_head;
+       *pp_list_head = l_head;
 }
 
 /****************************************************************************
@@ -1519,7 +1676,9 @@ static bool create_canon_ace_lists(files_struct *fsp,
 
                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);
+
+               /* Store the ace_flag. */
+               current_ace->ace_flags = psa->flags;
 
                /*
                 * Now add the created ace to either the file list, the directory
@@ -2147,12 +2306,12 @@ static bool unpack_canon_ace(files_struct *fsp,
 
 static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
 {
-       canon_ace *list_head = *pp_list_head;
+       canon_ace *l_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) {
+       for (ace = l_head; ace; ace = ace->next) {
                if (ace->type == SMB_ACL_USER_OBJ)
                        owner_ace = ace;
                else if (ace->type == SMB_ACL_OTHER) {
@@ -2160,7 +2319,7 @@ static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
                        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 ));
@@ -2173,18 +2332,18 @@ static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
         */
 
        if (owner_ace) {
-               DLIST_PROMOTE(list_head, owner_ace);
+               DLIST_PROMOTE(l_head, owner_ace);
        }
 
        if (other_ace) {
-               DLIST_DEMOTE(list_head, other_ace, canon_ace *);
+               DLIST_DEMOTE(l_head, other_ace, canon_ace *);
        }
 
        /* We have probably changed the head of the list. */
 
-       *pp_list_head = list_head;
+       *pp_list_head = l_head;
 }
-               
+
 /****************************************************************************
  Create a linked list of canonical ACE entries.
 ****************************************************************************/
@@ -2195,7 +2354,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                                   const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
 {
        mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
-       canon_ace *list_head = NULL;
+       canon_ace *l_head = NULL;
        canon_ace *ace = NULL;
        canon_ace *next_ace = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
@@ -2297,16 +2456,16 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                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));
+               ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
 
-               DLIST_ADD(list_head, ace);
+               DLIST_ADD(l_head, ace);
        }
 
        /*
         * This next call will ensure we have at least a user/group/world set.
         */
 
-       if (!ensure_canon_entry_valid(&list_head, conn->params,
+       if (!ensure_canon_entry_valid(&l_head, conn->params,
                                      S_ISDIR(psbuf->st_mode), powner, pgroup,
                                      psbuf, False))
                goto fail;
@@ -2318,7 +2477,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
 
        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++) {
+       for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
                next_ace = ace->next;
 
                /* Masks are only applied to entries other than USER_OBJ and OTHER. */
@@ -2326,7 +2485,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                        ace->perms &= acl_mask;
 
                if (ace->perms == 0) {
-                       DLIST_PROMOTE(list_head, ace);
+                       DLIST_PROMOTE(l_head, ace);
                }
 
                if( DEBUGLVL( 10 ) ) {
@@ -2334,15 +2493,15 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                }
        }
 
-       arrange_posix_perms(fname,&list_head );
+       arrange_posix_perms(fname,&l_head );
 
-       print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+       print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
 
-       return list_head;
+       return l_head;
 
   fail:
 
-       free_canon_ace_list(list_head);
+       free_canon_ace_list(l_head);
        return NULL;
 }
 
@@ -2814,6 +2973,42 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
        return num_aces;
 }
 
+/*
+ * Add or Replace ACE entry.
+ * In some cases we need to add a specific ACE for compatibility reasons.
+ * When doing that we must make sure we are not actually creating a duplicate
+ * entry. So we need to search whether an ACE entry already exist and eventually
+ * replacce the access mask, or add a completely new entry if none was found.
+ *
+ * This function assumes the array has enough space to add a new entry without
+ * any reallocation of memory.
+ */
+
+static void add_or_replace_ace(SEC_ACE *nt_ace_list, size_t *num_aces,
+                               const DOM_SID *sid, enum security_ace_type type,
+                               uint32_t mask, uint8_t flags)
+{
+       int i;
+
+       /* first search for a duplicate */
+       for (i = 0; i < *num_aces; i++) {
+               if (sid_equal(&nt_ace_list[i].trustee, sid) &&
+                   (nt_ace_list[i].flags == flags)) break;
+       }
+
+       if (i < *num_aces) { /* found */
+               nt_ace_list[i].type = type;
+               nt_ace_list[i].access_mask = mask;
+               DEBUG(10, ("Replacing ACE %d with SID %s and flags %02x\n",
+                          i, sid_string_dbg(sid), flags));
+               return;
+       }
+
+       /* not found, append it */
+       init_sec_ace(&nt_ace_list[(*num_aces)++], sid, type, mask, flags);
+}
+
+
 /****************************************************************************
  Reply to query a security descriptor from an fsp. If it succeeds it allocates
  the space for the return elements and returns the size needed to return the
@@ -2968,17 +3163,16 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                                        &ace->trustee,
                                        nt_acl_type,
                                        acc,
-                                       ace->inherited ?
-                                               SEC_ACE_FLAG_INHERITED_ACE : 0);
+                                       ace->ace_flags);
                        }
 
                        /* The User must have access to a profile share - even
                         * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
-                               init_sec_ace(&nt_ace_list[num_aces++],
-                                               &global_sid_Builtin_Users,
-                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
-                                               FILE_GENERIC_ALL, 0);
+                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                                  &global_sid_Builtin_Users,
+                                                  SEC_ACE_TYPE_ACCESS_ALLOWED,
+                                                  FILE_GENERIC_ALL, 0);
                        }
 
                        for (ace = dir_ace; ace != NULL; ace = ace->next) {
@@ -2990,19 +3184,22 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                                        &ace->trustee,
                                        nt_acl_type,
                                        acc,
+                                       ace->ace_flags |
                                        SEC_ACE_FLAG_OBJECT_INHERIT|
                                        SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                       SEC_ACE_FLAG_INHERIT_ONLY|
-                                       (ace->inherited ?
-                                          SEC_ACE_FLAG_INHERITED_ACE : 0));
+                                       SEC_ACE_FLAG_INHERIT_ONLY);
                        }
 
                        /* The User must have access to a profile share - even
                         * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
-                               init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, FILE_GENERIC_ALL,
-                                               SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                               SEC_ACE_FLAG_INHERIT_ONLY|0);
+                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                               &global_sid_Builtin_Users,
+                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                                               FILE_GENERIC_ALL,
+                                               SEC_ACE_FLAG_OBJECT_INHERIT |
+                                               SEC_ACE_FLAG_CONTAINER_INHERIT |
+                                               SEC_ACE_FLAG_INHERIT_ONLY);
                        }
 
                        /*
@@ -3045,8 +3242,10 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
         * flag doesn't seem to bother Windows NT.
         * Always set this if map acl inherit is turned off.
         */
-       if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
-               psd->type |= SE_DESC_DACL_PROTECTED;
+       if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
+               psd->type |= SEC_DESC_DACL_PROTECTED;
+       } else {
+               psd->type |= pal->sd_type;
        }
 
        if (psd->dacl) {
@@ -3187,6 +3386,15 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
                return -1;
        }
 
+       /* only allow chown to the current user. This is more secure,
+          and also copes with the case where the SID in a take ownership ACL is
+          a local SID on the users workstation
+       */
+       if (uid != current_user.ut.uid) {
+               errno = EPERM;
+               return -1;
+       }
+
        if (SMB_VFS_STAT(conn,fname,&st)) {
                return -1;
        }
@@ -3195,12 +3403,6 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
                return -1;
        }
 
-       /* only allow chown to the current user. This is more secure,
-          and also copes with the case where the SID in a take ownership ACL is
-          a local SID on the users workstation 
-       */
-       uid = current_user.ut.uid;
-
        become_root();
        /* Keep the current file gid the same. */
        ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
@@ -3233,7 +3435,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
        int info;
        unsigned int i, j;
        SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
-       bool is_dacl_protected = (pcsd->type & SE_DESC_DACL_PROTECTED);
+       bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
 
        ZERO_STRUCT(sbuf);
 
@@ -3614,8 +3816,10 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                if (set_acl_as_root) {
                        become_root();
                }
-               store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
-                               (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
+               store_inheritance_attributes(fsp,
+                               file_ace_list,
+                               dir_ace_list,
+                               psd->type);
                if (set_acl_as_root) {
                        unbecome_root();
                }
@@ -4046,10 +4250,14 @@ bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_
 {
        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 (!S_ISDIR(psbuf->st_mode)) {
+               if (num_def_acls) {
+                       DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
+                       errno = EISDIR;
+                       return False;
+               } else {
+                       return True;
+               }
        }
 
        if (!num_def_acls) {