selftest/Samba4: make use of get_cmd_env_vars() to setup all relevant env variables
[samba.git] / source3 / modules / nfs4_acls.c
index 46b69ac3d826c8dad908595ad6ad4eef66328673..c80f8390170b92a3e87e76571f833e2a4a86b229 100644 (file)
@@ -2,6 +2,7 @@
  * NFS4 ACL handling
  *
  * Copyright (C) Jim McDonough, 2006
+ * Copyright (C) Christof Schmitt 2019
  *
  * 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
@@ -82,18 +83,27 @@ int smbacl4_get_vfs_params(struct connection_struct *conn,
                return -1;
        }
        params->mode = (enum smbacl4_mode_enum)enumval;
+       if (params->mode == e_special) {
+               DBG_WARNING("nfs4:mode special is deprecated.\n");
+       }
 
        params->do_chown = lp_parm_bool(SNUM(conn), SMBACL4_PARAM_TYPE_NAME,
                "chown", true);
 
        enumval = lp_parm_enum(SNUM(conn), SMBACL4_PARAM_TYPE_NAME, "acedup",
-                              enum_smbacl4_acedups, e_dontcare);
+                              enum_smbacl4_acedups, e_merge);
        if (enumval == -1) {
                DEBUG(10, ("value for %s:acedup unknown\n",
                           SMBACL4_PARAM_TYPE_NAME));
                return -1;
        }
        params->acedup = (enum smbacl4_acedup_enum)enumval;
+       if (params->acedup == e_ignore) {
+               DBG_WARNING("nfs4:acedup ignore is deprecated.\n");
+       }
+       if (params->acedup == e_reject) {
+               DBG_WARNING("nfs4:acedup ignore is deprecated.\n");
+       }
 
        params->map_full_control = lp_acl_map_full_control(SNUM(conn));
 
@@ -106,6 +116,155 @@ int smbacl4_get_vfs_params(struct connection_struct *conn,
        return 0;
 }
 
+static int fstatat_with_cap_dac_override(int fd,
+                                        const char *pathname,
+                                        SMB_STRUCT_STAT *sbuf,
+                                        int flags,
+                                        bool fake_dir_create_times)
+{
+       int ret;
+
+       set_effective_capability(DAC_OVERRIDE_CAPABILITY);
+       ret = sys_fstatat(fd,
+                         pathname,
+                         sbuf,
+                         flags,
+                         fake_dir_create_times);
+       drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
+
+       return ret;
+}
+
+static int stat_with_cap_dac_override(struct vfs_handle_struct *handle,
+                                     struct smb_filename *smb_fname, int flag)
+{
+       bool fake_dctime = lp_fake_directory_create_times(SNUM(handle->conn));
+       int fd = -1;
+       NTSTATUS status;
+       struct smb_filename *dir_name = NULL;
+       struct smb_filename *rel_name = NULL;
+       int ret = -1;
+#ifdef O_PATH
+       int open_flags = O_PATH;
+#else
+       int open_flags = O_RDONLY;
+#endif
+
+       status = SMB_VFS_PARENT_PATHNAME(handle->conn,
+                                        talloc_tos(),
+                                        smb_fname,
+                                        &dir_name,
+                                        &rel_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
+               return -1;
+       }
+
+       fd = open(dir_name->base_name, open_flags, 0);
+       if (fd == -1) {
+               TALLOC_FREE(dir_name);
+               return -1;
+       }
+
+       ret = fstatat_with_cap_dac_override(fd,
+                                           rel_name->base_name,
+                                           &smb_fname->st,
+                                           flag,
+                                           fake_dctime);
+
+       TALLOC_FREE(dir_name);
+       close(fd);
+
+       return ret;
+}
+
+int nfs4_acl_stat(struct vfs_handle_struct *handle,
+                 struct smb_filename *smb_fname)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+       if (ret == -1 && errno == EACCES) {
+               DEBUG(10, ("Trying stat with capability for %s\n",
+                          smb_fname->base_name));
+               ret = stat_with_cap_dac_override(handle, smb_fname, 0);
+       }
+       return ret;
+}
+
+static int fstat_with_cap_dac_override(int fd, SMB_STRUCT_STAT *sbuf,
+                                      bool fake_dir_create_times)
+{
+       int ret;
+
+       set_effective_capability(DAC_OVERRIDE_CAPABILITY);
+       ret = sys_fstat(fd, sbuf, fake_dir_create_times);
+       drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
+
+       return ret;
+}
+
+int nfs4_acl_fstat(struct vfs_handle_struct *handle,
+                  struct files_struct *fsp,
+                  SMB_STRUCT_STAT *sbuf)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+       if (ret == -1 && errno == EACCES) {
+               bool fake_dctime =
+                       lp_fake_directory_create_times(SNUM(handle->conn));
+
+               DBG_DEBUG("fstat for %s failed with EACCES. Trying with "
+                         "CAP_DAC_OVERRIDE.\n", fsp->fsp_name->base_name);
+               ret = fstat_with_cap_dac_override(fsp_get_pathref_fd(fsp),
+                                                 sbuf,
+                                                 fake_dctime);
+       }
+
+       return ret;
+}
+
+int nfs4_acl_lstat(struct vfs_handle_struct *handle,
+                  struct smb_filename *smb_fname)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+       if (ret == -1 && errno == EACCES) {
+               DEBUG(10, ("Trying lstat with capability for %s\n",
+                          smb_fname->base_name));
+               ret = stat_with_cap_dac_override(handle, smb_fname,
+                                                AT_SYMLINK_NOFOLLOW);
+       }
+       return ret;
+}
+
+int nfs4_acl_fstatat(struct vfs_handle_struct *handle,
+                    const struct files_struct *dirfsp,
+                    const struct smb_filename *smb_fname,
+                    SMB_STRUCT_STAT *sbuf,
+                    int flags)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_FSTATAT(handle, dirfsp, smb_fname, sbuf, flags);
+       if (ret == -1 && errno == EACCES) {
+               bool fake_dctime =
+                       lp_fake_directory_create_times(SNUM(handle->conn));
+
+               DBG_DEBUG("fstatat for %s failed with EACCES. Trying with "
+                         "CAP_DAC_OVERRIDE.\n", dirfsp->fsp_name->base_name);
+               ret = fstatat_with_cap_dac_override(fsp_get_pathref_fd(dirfsp),
+                                                   smb_fname->base_name,
+                                                   sbuf,
+                                                   flags,
+                                                   fake_dctime);
+       }
+
+       return ret;
+}
+
 /************************************************
  Split the ACE flag mapping between nfs4 and Windows
  into two separate functions rather than trying to do
@@ -279,24 +438,6 @@ static int smbacl4_GetFileOwner(struct connection_struct *conn,
        return 0;
 }
 
-static int smbacl4_fGetFileOwner(files_struct *fsp, SMB_STRUCT_STAT *psbuf)
-{
-       ZERO_STRUCTP(psbuf);
-
-       if (fsp->fh->fd == -1) {
-               return smbacl4_GetFileOwner(fsp->conn,
-                                           fsp->fsp_name, psbuf);
-       }
-       if (SMB_VFS_FSTAT(fsp, psbuf) != 0)
-       {
-               DEBUG(8, ("SMB_VFS_FSTAT failed with error %s\n",
-                       strerror(errno)));
-               return -1;
-       }
-
-       return 0;
-}
-
 static void check_for_duplicate_sec_ace(struct security_ace *nt_ace_list,
                                        int *good_aces)
 {
@@ -346,7 +487,7 @@ static bool smbacl4_nfs42win(TALLOC_CTX *mem_ctx,
                                        2 * acl->naces);
        if (nt_ace_list==NULL)
        {
-               DEBUG(10, ("talloc error with %d aces", acl->naces));
+               DEBUG(10, ("talloc error with %d aces\n", acl->naces));
                errno = ENOMEM;
                return false;
        }
@@ -407,7 +548,7 @@ static bool smbacl4_nfs42win(TALLOC_CTX *mem_ctx,
                    (win_ace_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
                                      SEC_ACE_FLAG_CONTAINER_INHERIT))) {
                        /*
-                        * GPFS sets inherits dir_inhert and file_inherit flags
+                        * GPFS sets inherits dir_inherit and file_inherit flags
                         * to files, too, which confuses windows, and seems to
                         * be wrong anyways. ==> Map these bits away for files.
                         */
@@ -476,7 +617,7 @@ static bool smbacl4_nfs42win(TALLOC_CTX *mem_ctx,
 
        /* returns a NULL ace list when good_aces is zero. */
        if (good_aces && nt_ace_list == NULL) {
-               DEBUG(10, ("realloc error with %d aces", good_aces));
+               DEBUG(10, ("realloc error with %d aces\n", good_aces));
                errno = ENOMEM;
                return false;
        }
@@ -555,21 +696,17 @@ NTSTATUS smb_fget_nt_acl_nfs4(files_struct *fsp,
                              struct security_descriptor **ppdesc,
                              struct SMB4ACL_T *theacl)
 {
-       SMB_STRUCT_STAT sbuf;
        struct smbacl4_vfs_params params;
-       SMB_STRUCT_STAT *psbuf = NULL;
 
        DEBUG(10, ("smb_fget_nt_acl_nfs4 invoked for %s\n", fsp_str_dbg(fsp)));
 
-       if (VALID_STAT(fsp->fsp_name->st)) {
-               psbuf = &fsp->fsp_name->st;
-       }
+       if (!VALID_STAT(fsp->fsp_name->st)) {
+               NTSTATUS status;
 
-       if (psbuf == NULL) {
-               if (smbacl4_fGetFileOwner(fsp, &sbuf)) {
-                       return map_nt_error_from_unix(errno);
+               status = vfs_stat_fsp(fsp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
                }
-               psbuf = &sbuf;
        }
 
        if (pparams == NULL) {
@@ -580,7 +717,8 @@ NTSTATUS smb_fget_nt_acl_nfs4(files_struct *fsp,
                pparams = &params;
        }
 
-       return smb_get_nt_acl_nfs4_common(psbuf, pparams, security_info,
+       return smb_get_nt_acl_nfs4_common(&fsp->fsp_name->st, pparams,
+                                         security_info,
                                          mem_ctx, ppdesc, theacl);
 }
 
@@ -989,8 +1127,6 @@ NTSTATUS smb_set_nt_acl_nfs4(vfs_handle_struct *handle, files_struct *fsp,
        bool    result, is_directory;
 
        bool set_acl_as_root = false;
-       uid_t newUID = (uid_t)-1;
-       gid_t newGID = (gid_t)-1;
        int saved_errno;
        NTSTATUS status;
        TALLOC_CTX *frame = talloc_stackframe();
@@ -1007,6 +1143,11 @@ NTSTATUS smb_set_nt_acl_nfs4(vfs_handle_struct *handle, files_struct *fsp,
                                      * refined... */
        }
 
+       if (security_descriptor_with_ms_nfs(psd)) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
+       }
+
        if (pparams == NULL) {
                /* Special behaviours */
                if (smbacl4_get_vfs_params(fsp->conn, &params)) {
@@ -1025,49 +1166,20 @@ NTSTATUS smb_set_nt_acl_nfs4(vfs_handle_struct *handle, files_struct *fsp,
        is_directory = S_ISDIR(fsp->fsp_name->st.st_ex_mode);
 
        if (pparams->do_chown) {
-               /* chown logic is a copy/paste from posix_acl.c:set_nt_acl */
-
-               uid_t old_uid = fsp->fsp_name->st.st_ex_uid;
-               uid_t old_gid = fsp->fsp_name->st.st_ex_uid;
-               status = unpack_nt_owners(fsp->conn, &newUID, &newGID,
-                                         security_info_sent, psd);
+               /*
+                * When the chown succeeds, the special entries in the
+                * file system ACL refer to the new owner. In order to
+                * apply the complete information from the DACL,
+                * setting the ACL then has to succeed. Track this
+                * case with set_acl_as_root and set the ACL as root
+                * accordingly.
+                */
+               status = chown_if_needed(fsp, security_info_sent, psd,
+                                        &set_acl_as_root);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(8, ("unpack_nt_owners failed"));
                        TALLOC_FREE(frame);
                        return status;
                }
-               if (((newUID != (uid_t)-1) && (old_uid != newUID)) ||
-                   ((newGID != (gid_t)-1) && (old_gid != newGID)))
-               {
-                       status = try_chown(fsp, newUID, newGID);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               DEBUG(3,("chown %s, %u, %u failed. Error = "
-                                        "%s.\n", fsp_str_dbg(fsp),
-                                        (unsigned int)newUID,
-                                        (unsigned int)newGID,
-                                        nt_errstr(status)));
-                               TALLOC_FREE(frame);
-                               return status;
-                       }
-
-                       DEBUG(10,("chown %s, %u, %u succeeded.\n",
-                                 fsp_str_dbg(fsp), (unsigned int)newUID,
-                                 (unsigned int)newGID));
-
-                       /*
-                        * Owner change, need to update stat info.
-                        */
-                       status = vfs_stat_fsp(fsp);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               TALLOC_FREE(frame);
-                               return status;
-                       }
-
-                       /* If we successfully chowned, we know we must
-                        * be able to set the acl, so do it as root.
-                        */
-                       set_acl_as_root = true;
-               }
        }
 
        if (!(security_info_sent & SECINFO_DACL) || psd->dacl ==NULL) {