s3-posix_acls: Handle IDMAP_BOTH by setting an ACL for both the UID and GID form
[abartlet/samba.git/.git] / source3 / smbd / posix_acls.c
index ecacecc759903e9151bfe19c2f18fc45d934e39d..cafc301e791e0da66c0196674ec6d02a068da13c 100644 (file)
@@ -26,6 +26,7 @@
 #include "trans2.h"
 #include "passdb/lookup_sid.h"
 #include "auth.h"
+#include "../librpc/gen_ndr/idmap.h"
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -1493,6 +1494,68 @@ static bool ensure_canon_entry_valid(connection_struct *conn, canon_ace **pp_ace
                pace_other = pace;
        }
 
+       if (setting_acl) {
+               /* Ensure when setting a POSIX ACL, that the uid for a
+                  SMB_ACL_USER_OBJ ACE (the owner ACE entry) has a duplicate
+                  permission entry as an SMB_ACL_USER, and a gid for a
+                  SMB_ACL_GROUP_OBJ ACE (the primary group ACE entry) also has
+                  a duplicate permission entry as an SMB_ACL_GROUP. If not,
+                  then if the ownership or group ownership of this file or
+                  directory gets changed, the user or group can lose their
+                  access. */
+               bool got_duplicate_user = false;
+               bool got_duplicate_group = false;
+
+               for (pace = *pp_ace; pace; pace = pace->next) {
+                       if (pace->type == SMB_ACL_USER &&
+                                       pace->unix_ug.uid == pace_user->unix_ug.uid) {
+                               /* Already got one. */
+                               got_duplicate_user = true;
+                       } else if (pace->type == SMB_ACL_USER &&
+                                       pace->unix_ug.uid == pace_user->unix_ug.uid) {
+                               /* Already got one. */
+                               got_duplicate_group = true;
+                       }
+               }
+
+               if (!got_duplicate_user) {
+                       /* Add a duplicate SMB_ACL_USER entry. */
+                       if ((pace = talloc(talloc_tos(), canon_ace)) == NULL) {
+                               DEBUG(0,("ensure_canon_entry_valid: talloc fail.\n"));
+                               return false;
+                       }
+
+                       ZERO_STRUCTP(pace);
+                       pace->type = SMB_ACL_USER;;
+                       pace->owner_type = UID_ACE;
+                       pace->unix_ug.uid = pace_user->unix_ug.uid;
+                       pace->trustee = pace_user->trustee;
+                       pace->attr = pace_user->attr;
+                       pace->perms = pace_user->perms;
+
+                       DLIST_ADD(*pp_ace, pace);
+               }
+
+               if (!got_duplicate_group) {
+                       /* Add a duplicate SMB_ACL_GROUP entry. */
+                       if ((pace = talloc(talloc_tos(), canon_ace)) == NULL) {
+                               DEBUG(0,("ensure_canon_entry_valid: talloc fail.\n"));
+                               return false;
+                       }
+
+                       ZERO_STRUCTP(pace);
+                       pace->type = SMB_ACL_GROUP;;
+                       pace->owner_type = GID_ACE;
+                       pace->unix_ug.gid = pace_group->unix_ug.gid;
+                       pace->trustee = pace_group->trustee;
+                       pace->attr = pace_group->attr;
+                       pace->perms = pace_group->perms;
+
+                       DLIST_ADD(*pp_ace, pace);
+               }
+
+       }
+
        return True;
 }
 
@@ -1542,6 +1605,181 @@ static void check_owning_objs(canon_ace *ace, struct dom_sid *pfile_owner_sid, s
                DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
 }
 
+static bool add_current_ace_to_acl(files_struct *fsp, struct security_ace *psa, 
+                                  canon_ace **file_ace, canon_ace **dir_ace, 
+                                  bool *got_file_allow, bool *got_dir_allow, 
+                                  bool *all_aces_are_inherit_only,
+                                  canon_ace *current_ace)
+{
+
+       /*
+        * Map the given NT permissions into a UNIX mode_t containing only
+        * S_I(R|W|X)USR bits.
+        */
+
+       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;
+
+       /* Store the ace_flag. */
+       current_ace->ace_flags = psa->flags;
+
+       /*
+        * Now add the created ace to either the file list, the directory
+        * list, or both. We *MUST* preserve the order here (hence we use
+        * DLIST_ADD_END) as NT ACLs are order dependent.
+        */
+
+       if (fsp->is_directory) {
+
+               /*
+                * We can only add to the default POSIX ACE list if the ACE is
+                * designed to be inherited by both files and directories.
+                */
+
+               if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
+                   (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
+                       canon_ace *current_dir_ace = current_ace;
+                       DLIST_ADD_END(*dir_ace, current_ace, canon_ace *);
+
+                       /*
+                        * Note if this was an allow ace. We can't process
+                        * any further deny ace's after this.
+                        */
+
+                       if (current_ace->attr == ALLOW_ACE)
+                               *got_dir_allow = True;
+
+                       if ((current_ace->attr == DENY_ACE) && *got_dir_allow) {
+                               DEBUG(0,("create_canon_ace_lists: "
+                                        "malformed ACL in "
+                                        "inheritable ACL! Deny entry "
+                                        "after Allow entry. Failing "
+                                        "to set on file %s.\n",
+                                        fsp_str_dbg(fsp)));
+                               return False;
+                       }       
+
+                       if( DEBUGLVL( 10 )) {
+                               dbgtext("create_canon_ace_lists: adding dir ACL:\n");
+                               print_canon_ace( current_ace, 0);
+                       }
+
+                       /*
+                        * If this is not an inherit only ACE we need to add a duplicate
+                        * to the file acl.
+                        */
+
+                       if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+                               canon_ace *dup_ace = dup_canon_ace(current_ace);
+
+                               if (!dup_ace) {
+                                       DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+                                       return False;
+                               }
+
+                               /*
+                                * We must not free current_ace here as its
+                                * pointer is now owned by the dir_ace list.
+                                */
+                               current_ace = dup_ace;
+                               /* We've essentially split this ace into two,
+                                * and added the ace with inheritance request
+                                * bits to the directory ACL. Drop those bits for
+                                * the ACE we're adding to the file list. */
+                               current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
+                                                           SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                                           SEC_ACE_FLAG_INHERIT_ONLY);
+                       } else {
+                               /*
+                                * We must not free current_ace here as its
+                                * pointer is now owned by the dir_ace list.
+                                */
+                               current_ace = NULL;
+                       }
+
+                       /*
+                        * current_ace is now either owned by file_ace
+                        * or is NULL. We can safely operate on current_dir_ace
+                        * to treat mapping for default acl entries differently
+                        * than access acl entries.
+                        */
+
+                       if (current_dir_ace->owner_type == UID_ACE) {
+                               /*
+                                * We already decided above this is a uid,
+                                * for default acls ace's only CREATOR_OWNER
+                                * maps to ACL_USER_OBJ. All other uid
+                                * ace's are ACL_USER.
+                                */
+                               if (dom_sid_equal(&current_dir_ace->trustee,
+                                                 &global_sid_Creator_Owner)) {
+                                       current_dir_ace->type = SMB_ACL_USER_OBJ;
+                               } else {
+                                       current_dir_ace->type = SMB_ACL_USER;
+                               }
+                       }
+
+                       if (current_dir_ace->owner_type == GID_ACE) {
+                               /*
+                                * We already decided above this is a gid,
+                                * for default acls ace's only CREATOR_GROUP
+                                * maps to ACL_GROUP_OBJ. All other uid
+                                * ace's are ACL_GROUP.
+                                */
+                               if (dom_sid_equal(&current_dir_ace->trustee,
+                                                 &global_sid_Creator_Group)) {
+                                       current_dir_ace->type = SMB_ACL_GROUP_OBJ;
+                               } else {
+                                       current_dir_ace->type = SMB_ACL_GROUP;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Only add to the file ACL if not inherit only.
+        */
+
+       if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+               DLIST_ADD_END(*file_ace, current_ace, canon_ace *);
+
+               /*
+                * Note if this was an allow ace. We can't process
+                * any further deny ace's after this.
+                */
+
+               if (current_ace->attr == ALLOW_ACE)
+                       *got_file_allow = True;
+
+               if ((current_ace->attr == DENY_ACE) && got_file_allow) {
+                       DEBUG(0,("create_canon_ace_lists: malformed "
+                                "ACL in file ACL ! Deny entry after "
+                                "Allow entry. Failing to set on file "
+                                "%s.\n", fsp_str_dbg(fsp)));
+                       return False;
+               }       
+
+               if( DEBUGLVL( 10 )) {
+                       dbgtext("create_canon_ace_lists: adding file ACL:\n");
+                       print_canon_ace( current_ace, 0);
+               }
+               *all_aces_are_inherit_only = False;
+               /*
+                * We must not free current_ace here as its
+                * pointer is now owned by the file_ace list.
+                */
+               current_ace = NULL;
+       }
+
+       /*
+        * Free if ACE was not added.
+        */
+
+       TALLOC_FREE(current_ace);
+       return true;
+}
+
 /****************************************************************************
  Unpack a struct security_descriptor into two canonical ace lists.
 ****************************************************************************/
@@ -1639,7 +1877,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
 
        for(i = 0; i < dacl->num_aces; i++) {
                struct security_ace *psa = &dacl->aces[i];
-
+               struct unixid unixid;
                /*
                 * Create a canon_ace entry representing this NT DACL ACE.
                 */
@@ -1690,225 +1928,117 @@ static bool create_canon_ace_lists(files_struct *fsp,
                         */
                        psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
 
-               } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
-                       current_ace->owner_type = UID_ACE;
-                       /* If it's the owning user, this is a user_obj, not
-                        * a user. */
-                       if (current_ace->unix_ug.uid == pst->st_ex_uid) {
-                               current_ace->type = SMB_ACL_USER_OBJ;
-                       } else {
-                               current_ace->type = SMB_ACL_USER;
-                       }
-               } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
-                       current_ace->owner_type = GID_ACE;
-                       /* If it's the primary group, this is a group_obj, not
-                        * a group. */
-                       if (current_ace->unix_ug.gid == pst->st_ex_gid) {
-                               current_ace->type = SMB_ACL_GROUP_OBJ;
-                       } else {
-                               current_ace->type = SMB_ACL_GROUP;
-                       }
                } else {
-                       /*
-                        * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
-                        */
-
-                       if (non_mappable_sid(&psa->trustee)) {
-                               DEBUG(10, ("create_canon_ace_lists: ignoring "
-                                          "non-mappable SID %s\n",
-                                          sid_string_dbg(&psa->trustee)));
-                               TALLOC_FREE(current_ace);
-                               continue;
-                       }
-
-                       if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
-                               DEBUG(10, ("create_canon_ace_lists: ignoring "
-                                       "unknown or foreign SID %s\n",
-                                       sid_string_dbg(&psa->trustee)));
+                       if (!sids_to_unixids( &current_ace->trustee, 1, &unixid)) {
+                               free_canon_ace_list(file_ace);
+                               free_canon_ace_list(dir_ace);
                                TALLOC_FREE(current_ace);
-                               continue;
+                               DEBUG(0, ("sids_to_unixids failed for %s (allocation failure)\n",
+                                         sid_string_dbg(&current_ace->trustee)));
+                               return false;
                        }
 
-                       free_canon_ace_list(file_ace);
-                       free_canon_ace_list(dir_ace);
-                       DEBUG(0, ("create_canon_ace_lists: unable to map SID "
-                                 "%s to uid or gid.\n",
-                                 sid_string_dbg(&current_ace->trustee)));
-                       TALLOC_FREE(current_ace);
-                       return False;
-               }
-
-               /*
-                * Map the given NT permissions into a UNIX mode_t containing only
-                * S_I(R|W|X)USR bits.
-                */
-
-               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;
-
-               /* Store the ace_flag. */
-               current_ace->ace_flags = psa->flags;
-
-               /*
-                * Now add the created ace to either the file list, the directory
-                * list, or both. We *MUST* preserve the order here (hence we use
-                * DLIST_ADD_END) as NT ACLs are order dependent.
-                */
-
-               if (fsp->is_directory) {
-
-                       /*
-                        * We can only add to the default POSIX ACE list if the ACE is
-                        * designed to be inherited by both files and directories.
-                        */
-
-                       if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
-                               (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
-
-                               canon_ace *current_dir_ace = current_ace;
-                               DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
-
-                               /*
-                                * Note if this was an allow ace. We can't process
-                                * any further deny ace's after this.
-                                */
-
-                               if (current_ace->attr == ALLOW_ACE)
-                                       got_dir_allow = True;
+                       if (unixid.type == ID_TYPE_BOTH) {
+                               current_ace->owner_type = UID_ACE;
+                               current_ace->unix_ug.uid = unixid.id;
+                               /* If it's the owning user, this is a user_obj, not
+                                * a user. */
+                               if (current_ace->unix_ug.uid == pst->st_ex_uid) {
+                                       current_ace->type = SMB_ACL_USER_OBJ;
+                               } else {
+                                       current_ace->type = SMB_ACL_USER;
+                               }
+                               
+                               /* add the user object to the posix ACL, and proceed to the group mapping below  
+                                  This handles the talloc_free of current_ace if not added for some reason */
+                               if (!add_current_ace_to_acl(fsp, psa, &file_ace, &dir_ace, 
+                                                           &got_file_allow, &got_dir_allow, 
+                                                           &all_aces_are_inherit_only,
+                                                           current_ace)) {
+                                       free_canon_ace_list(file_ace);
+                                       free_canon_ace_list(dir_ace);
+                                       return false;
+                               }
 
-                               if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
-                                       DEBUG(0,("create_canon_ace_lists: "
-                                                "malformed ACL in "
-                                                "inheritable ACL! Deny entry "
-                                                "after Allow entry. Failing "
-                                                "to set on file %s.\n",
-                                                fsp_str_dbg(fsp)));
+                               if ((current_ace = talloc(talloc_tos(), canon_ace)) == NULL) {
                                        free_canon_ace_list(file_ace);
                                        free_canon_ace_list(dir_ace);
+                                       DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
                                        return False;
-                               }       
-
-                               if( DEBUGLVL( 10 )) {
-                                       dbgtext("create_canon_ace_lists: adding dir ACL:\n");
-                                       print_canon_ace( current_ace, 0);
                                }
-
-                               /*
-                                * If this is not an inherit only ACE we need to add a duplicate
-                                * to the file acl.
-                                */
-
-                               if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
-                                       canon_ace *dup_ace = dup_canon_ace(current_ace);
-
-                                       if (!dup_ace) {
-                                               DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
-                                               free_canon_ace_list(file_ace);
-                                               free_canon_ace_list(dir_ace);
-                                               return False;
-                                       }
-
-                                       /*
-                                        * We must not free current_ace here as its
-                                        * pointer is now owned by the dir_ace list.
-                                        */
-                                       current_ace = dup_ace;
-                                       /* We've essentially split this ace into two,
-                                        * and added the ace with inheritance request
-                                        * bits to the directory ACL. Drop those bits for
-                                        * the ACE we're adding to the file list. */
-                                       current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
-                                                               SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                                               SEC_ACE_FLAG_INHERIT_ONLY);
+                               
+                               ZERO_STRUCTP(current_ace);
+                               
+                               sid_copy(&current_ace->trustee, &psa->trustee);
+
+                               current_ace->unix_ug.gid = unixid.id;
+                               current_ace->owner_type = GID_ACE;
+                               /* If it's the primary group, this is a group_obj, not
+                                * a group. */
+                               if (current_ace->unix_ug.gid == pst->st_ex_gid) {
+                                       current_ace->type = SMB_ACL_GROUP_OBJ;
                                } else {
-                                       /*
-                                        * We must not free current_ace here as its
-                                        * pointer is now owned by the dir_ace list.
-                                        */
-                                       current_ace = NULL;
+                                       current_ace->type = SMB_ACL_GROUP;
                                }
 
+                       } else if (unixid.type == ID_TYPE_UID) {
+                               current_ace->owner_type = UID_ACE;
+                               current_ace->unix_ug.uid = unixid.id;
+                               /* If it's the owning user, this is a user_obj, not
+                                * a user. */
+                               if (current_ace->unix_ug.uid == pst->st_ex_uid) {
+                                       current_ace->type = SMB_ACL_USER_OBJ;
+                               } else {
+                                       current_ace->type = SMB_ACL_USER;
+                               }
+                       } else if (unixid.type == ID_TYPE_GID) {
+                               current_ace->unix_ug.gid = unixid.id;
+                               current_ace->owner_type = GID_ACE;
+                               /* If it's the primary group, this is a group_obj, not
+                                * a group. */
+                               if (current_ace->unix_ug.gid == pst->st_ex_gid) {
+                                       current_ace->type = SMB_ACL_GROUP_OBJ;
+                               } else {
+                                       current_ace->type = SMB_ACL_GROUP;
+                               }
+                       } else {
                                /*
-                                * current_ace is now either owned by file_ace
-                                * or is NULL. We can safely operate on current_dir_ace
-                                * to treat mapping for default acl entries differently
-                                * than access acl entries.
+                                * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
                                 */
-
-                               if (current_dir_ace->owner_type == UID_ACE) {
-                                       /*
-                                        * We already decided above this is a uid,
-                                        * for default acls ace's only CREATOR_OWNER
-                                        * maps to ACL_USER_OBJ. All other uid
-                                        * ace's are ACL_USER.
-                                        */
-                                       if (dom_sid_equal(&current_dir_ace->trustee,
-                                                       &global_sid_Creator_Owner)) {
-                                               current_dir_ace->type = SMB_ACL_USER_OBJ;
-                                       } else {
-                                               current_dir_ace->type = SMB_ACL_USER;
-                                       }
+                               
+                               if (non_mappable_sid(&psa->trustee)) {
+                                       DEBUG(10, ("create_canon_ace_lists: ignoring "
+                                                  "non-mappable SID %s\n",
+                                                  sid_string_dbg(&psa->trustee)));
+                                       TALLOC_FREE(current_ace);
+                                       continue;
                                }
-
-                               if (current_dir_ace->owner_type == GID_ACE) {
-                                       /*
-                                        * We already decided above this is a gid,
-                                        * for default acls ace's only CREATOR_GROUP
-                                        * maps to ACL_GROUP_OBJ. All other uid
-                                        * ace's are ACL_GROUP.
-                                        */
-                                       if (dom_sid_equal(&current_dir_ace->trustee,
-                                                       &global_sid_Creator_Group)) {
-                                               current_dir_ace->type = SMB_ACL_GROUP_OBJ;
-                                       } else {
-                                               current_dir_ace->type = SMB_ACL_GROUP;
-                                       }
+                               
+                               if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+                                       DEBUG(10, ("create_canon_ace_lists: ignoring "
+                                                  "unknown or foreign SID %s\n",
+                                                  sid_string_dbg(&psa->trustee)));
+                                       TALLOC_FREE(current_ace);
+                                       continue;
                                }
-                       }
-               }
-
-               /*
-                * Only add to the file ACL if not inherit only.
-                */
-
-               if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
-                       DLIST_ADD_END(file_ace, current_ace, canon_ace *);
-
-                       /*
-                        * Note if this was an allow ace. We can't process
-                        * any further deny ace's after this.
-                        */
-
-                       if (current_ace->attr == ALLOW_ACE)
-                               got_file_allow = True;
-
-                       if ((current_ace->attr == DENY_ACE) && got_file_allow) {
-                               DEBUG(0,("create_canon_ace_lists: malformed "
-                                        "ACL in file ACL ! Deny entry after "
-                                        "Allow entry. Failing to set on file "
-                                        "%s.\n", fsp_str_dbg(fsp)));
+                               
                                free_canon_ace_list(file_ace);
                                free_canon_ace_list(dir_ace);
-                               return False;
-                       }       
-
-                       if( DEBUGLVL( 10 )) {
-                               dbgtext("create_canon_ace_lists: adding file ACL:\n");
-                               print_canon_ace( current_ace, 0);
+                               DEBUG(0, ("create_canon_ace_lists: unable to map SID "
+                                         "%s to uid or gid.\n",
+                                         sid_string_dbg(&current_ace->trustee)));
+                               TALLOC_FREE(current_ace);
+                               return false;
                        }
-                       all_aces_are_inherit_only = False;
-                       /*
-                        * We must not free current_ace here as its
-                        * pointer is now owned by the file_ace list.
-                        */
-                       current_ace = NULL;
                }
-
-               /*
-                * Free if ACE was not added.
-                */
-
-               TALLOC_FREE(current_ace);
+               /* handles the talloc_free of current_ace if not added for some reason */
+               if (!add_current_ace_to_acl(fsp, psa, &file_ace, &dir_ace, 
+                                           &got_file_allow, &got_dir_allow, 
+                                           &all_aces_are_inherit_only, current_ace)) {
+                       free_canon_ace_list(file_ace);
+                       free_canon_ace_list(dir_ace);
+                       return false;
+               }
        }
 
        if (fsp->is_directory && all_aces_are_inherit_only) {