vfs: Add inheritance emulation to vfs_nfs4acl_xattr.
authorAlexander Werth <alexander.werth@de.ibm.com>
Tue, 16 Apr 2013 12:11:27 +0000 (14:11 +0200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 9 May 2013 04:18:21 +0000 (06:18 +0200)
Recursively inherit ACL from parent directory if no acl xattr is
found on the current file.
Use a default ACL if a non-inheriting ACL is encountered.
With this the nfs4acl_xattr.dynamic test passes.
But the nfs4acl_xattr.inheritance test results in an error because
of warnings that cause the test to pass a failed result.

Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/knownfail
source3/modules/vfs_nfs4acl_xattr.c

index 069afe7604caec579371a01a5f76b478f3ff479c..a7dfdf82891499e1c9ca671e1eb29dd558106dc6 100644 (file)
@@ -46,7 +46,6 @@
 ^samba3.raw.samba3hide.samba3hide\((s3dc|plugin_s4_dc)\) # This test fails against an smbd environment with NT ACLs enabled
 ^samba3.raw.samba3closeerr.samba3closeerr\(s3dc\) # This test fails against an smbd environment with NT ACLs enabled
 ^samba3.raw.acls nfs4acl_xattr.INHERITFLAGS\(s3dc\) # This (and the follow nfs4acl_xattr tests fail because our NFSv4 backend isn't a complete mapping yet.
-^samba3.raw.acls nfs4acl_xattr.dynamic\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr.sd\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr.create_file\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr.create_dir\(s3dc\)
index fedb768d0e8eb2ee7122e6699eca36f3e9d03676..bdaca82b8ca52b4a5bdf45f17412aa4d2ea2bc7d 100644 (file)
@@ -176,30 +176,29 @@ static NTSTATUS nfs4_get_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx
        return status;
 }
 
-/* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
-static bool nfs4_process_smbacl(vfs_handle_struct *handle, files_struct *fsp, SMB4ACL_T *smbacl)
+static bool nfs4acl_smb4acl2nfs4acl(TALLOC_CTX *mem_ctx,
+                                   SMB4ACL_T *smbacl,
+                                   struct nfs4acl **pnfs4acl,
+                                   bool denymissingspecial)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       int i;
        struct nfs4acl *nfs4acl;
        SMB4ACE_T *smbace;
        bool have_special_id = false;
-       int ret;
-       DATA_BLOB blob;
+       int i;
 
        /* allocate the field of NFS4 aces */
-       nfs4acl = talloc_zero(frame, struct nfs4acl);
+       nfs4acl = talloc_zero(mem_ctx, struct nfs4acl);
        if(nfs4acl == NULL) {
-               TALLOC_FREE(frame);
                errno = ENOMEM;
                return false;
        }
 
        nfs4acl->a_count = smb_get_naces(smbacl);
 
-       nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace, nfs4acl->a_count);
+       nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace,
+                                        nfs4acl->a_count);
        if(nfs4acl->ace == NULL) {
-               TALLOC_FREE(frame);
+               TALLOC_FREE(nfs4acl);
                errno = ENOMEM;
                return false;
        }
@@ -239,26 +238,93 @@ static bool nfs4_process_smbacl(vfs_handle_struct *handle, files_struct *fsp, SM
                }
        }
 
-       if (!have_special_id
-           && lp_parm_bool(fsp->conn->params->service, "nfs4acl_xattr",
-                           "denymissingspecial", false)) {
-               TALLOC_FREE(frame);
+       if (!have_special_id && denymissingspecial) {
+               TALLOC_FREE(nfs4acl);
                errno = EACCES;
                return false;
        }
 
        SMB_ASSERT(i == nfs4acl->a_count);
 
+       *pnfs4acl = nfs4acl;
+       return true;
+}
+
+static bool nfs4acl_xattr_set_smb4acl(vfs_handle_struct *handle,
+                                     const char *path,
+                                     SMB4ACL_T *smbacl)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct nfs4acl *nfs4acl;
+       int ret;
+       bool denymissingspecial;
+       DATA_BLOB blob;
+
+       denymissingspecial = lp_parm_bool(handle->conn->params->service,
+                                         "nfs4acl_xattr",
+                                         "denymissingspecial", false);
+
+       if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
+                                    denymissingspecial)) {
+               DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
+               TALLOC_FREE(frame);
+               return false;
+       }
+
        blob = nfs4acl_acl2blob(frame, nfs4acl);
        if (!blob.data) {
-               DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
+               DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
                TALLOC_FREE(frame);
                errno = EINVAL;
                return false;
        }
-       ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_XATTR_NAME, blob.data, blob.length, 0);
+       ret = SMB_VFS_NEXT_SETXATTR(handle, path, NFS4ACL_XATTR_NAME,
+                                   blob.data, blob.length, 0);
+       if (ret != 0) {
+               DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
+       }
        TALLOC_FREE(frame);
+       return ret == 0;
+}
 
+/* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
+static bool nfs4acl_xattr_fset_smb4acl(vfs_handle_struct *handle,
+                                      files_struct *fsp,
+                                      SMB4ACL_T *smbacl)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct nfs4acl *nfs4acl;
+       int ret;
+       bool denymissingspecial;
+       DATA_BLOB blob;
+
+       denymissingspecial = lp_parm_bool(fsp->conn->params->service,
+                                         "nfs4acl_xattr",
+                                         "denymissingspecial", false);
+
+       if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
+                                    denymissingspecial)) {
+               DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
+               TALLOC_FREE(frame);
+               return false;
+       }
+
+       blob = nfs4acl_acl2blob(frame, nfs4acl);
+       if (!blob.data) {
+               DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
+               TALLOC_FREE(frame);
+               errno = EINVAL;
+               return false;
+       }
+       if (fsp->fh->fd == -1) {
+               DEBUG(0, ("Error: fsp->fh->fd == -1\n"));
+       }
+       ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_XATTR_NAME,
+                                    blob.data, blob.length, 0);
+       if (ret != 0) {
+               DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
+       }
+       TALLOC_FREE(frame);
        return ret == 0;
 }
 
@@ -271,7 +337,180 @@ static NTSTATUS nfs4_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
                           const struct security_descriptor *psd)
 {
        return smb_set_nt_acl_nfs4(handle, fsp, security_info_sent, psd,
-                       nfs4_process_smbacl);
+                       nfs4acl_xattr_fset_smb4acl);
+}
+
+static SMB4ACL_T *nfs4acls_defaultacl(TALLOC_CTX *mem_ctx)
+{
+       SMB4ACL_T *pacl = NULL;
+       SMB4ACE_T *pace;
+       SMB_ACE4PROP_T ace = { SMB_ACE4_ID_SPECIAL,
+               SMB_ACE4_WHO_EVERYONE,
+               SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE,
+               0,
+               SMB_ACE4_ALL_MASKS };
+
+       DEBUG(10, ("Building default full access acl\n"));
+
+       pacl = smb_create_smb4acl(mem_ctx);
+       if (pacl == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       pace = smb_add_ace4(pacl, &ace);
+       if (pace == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               TALLOC_FREE(pacl);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       return pacl;
+}
+
+/*
+ * Because there is no good way to guarantee that a new xattr will be
+ * created on file creation there might be no acl xattr on a file when
+ * trying to read the acl. In this case the acl xattr will get
+ * constructed at that time from the parent acl.
+ * If the parent ACL doesn't have an xattr either the call will
+ * recurse to the next parent directory until the share root is
+ * reached. If the share root doesn't contain an ACL xattr either a
+ * default ACL will be used.
+ * Also a default ACL will be set if a non inheriting ACL is encountered.
+ *
+ * Basic algorithm:
+ *   read acl xattr blob
+ *   if acl xattr blob doesn't exist
+ *     stat current directory to know if it's a file or directory
+ *     read acl xattr blob from parent dir
+ *     acl xattr blob to smb nfs4 acl
+ *     calculate inherited smb nfs4 acl
+ *     without inheritance use default smb nfs4 acl
+ *     smb nfs4 acl to acl xattr blob
+ *     set acl xattr blob
+ *     return smb nfs4 acl
+ *   else
+ *     acl xattr blob to smb nfs4 acl
+ *
+ * Todo: Really use mem_ctx after fixing interface of nfs4_acls
+ */
+static SMB4ACL_T *nfs4acls_inheritacl(vfs_handle_struct *handle,
+       const char *path,
+       TALLOC_CTX *mem_ctx)
+{
+       char *parent_dir = NULL;
+       SMB4ACL_T *pparentacl = NULL;
+       SMB4ACL_T *pchildacl = NULL;
+       SMB4ACE_T *pace;
+       SMB_ACE4PROP_T ace;
+       bool isdir;
+       struct smb_filename *smb_fname = NULL;
+       NTSTATUS status;
+       int ret;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       DEBUG(10, ("nfs4acls_inheritacl invoked for %s\n", path));
+       smb_fname = synthetic_smb_fname(frame, path, NULL, NULL);
+       if (smb_fname == NULL) {
+               TALLOC_FREE(frame);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ret = SMB_VFS_STAT(handle->conn, smb_fname);
+       if (ret == -1) {
+               DEBUG(0,("nfs4acls_inheritacl: failed to stat "
+                        "directory %s. Error was %s\n",
+                        smb_fname_str_dbg(smb_fname),
+                        strerror(errno)));
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+       isdir = S_ISDIR(smb_fname->st.st_ex_mode);
+
+       if (!parent_dirname(talloc_tos(),
+                           path,
+                           &parent_dir,
+                           NULL)) {
+               TALLOC_FREE(frame);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       status = nfs4_get_nfs4_acl(handle, frame, parent_dir, &pparentacl);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)
+           && strncmp(parent_dir, ".", 2) != 0) {
+               pparentacl = nfs4acls_inheritacl(handle, parent_dir,
+                                                frame);
+       }
+       else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               pparentacl = nfs4acls_defaultacl(frame);
+
+       }
+       else if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       pchildacl = smb_create_smb4acl(mem_ctx);
+       if (pchildacl == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               TALLOC_FREE(frame);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       for (pace = smb_first_ace4(pparentacl); pace != NULL;
+            pace = smb_next_ace4(pace)) {
+               SMB4ACE_T *pchildace;
+               ace = *smb_get_ace4(pace);
+               if (isdir && !(ace.aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)
+                   || !isdir && !(ace.aceFlags & SMB_ACE4_FILE_INHERIT_ACE)) {
+                       DEBUG(10, ("non inheriting ace type: %d, iflags: %x, "
+                                  "flags: %x, mask: %x, who: %d\n",
+                                  ace.aceType, ace.flags, ace.aceFlags,
+                                  ace.aceMask, ace.who.id));
+                       continue;
+               }
+               DEBUG(10, ("inheriting ace type: %d, iflags: %x, "
+                          "flags: %x, mask: %x, who: %d\n",
+                          ace.aceType, ace.flags, ace.aceFlags,
+                          ace.aceMask, ace.who.id));
+               ace.aceFlags |= SMB_ACE4_INHERITED_ACE;
+               if ((isdir && (ace.aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)
+                    || !isdir && (ace.aceFlags & SMB_ACE4_FILE_INHERIT_ACE))
+                   && ace.aceFlags & SMB_ACE4_INHERIT_ONLY_ACE) {
+                       ace.aceFlags &= ~SMB_ACE4_INHERIT_ONLY_ACE;
+               }
+               if (ace.aceFlags & SMB_ACE4_NO_PROPAGATE_INHERIT_ACE) {
+                       ace.aceFlags &= ~SMB_ACE4_FILE_INHERIT_ACE;
+                       ace.aceFlags &= ~SMB_ACE4_DIRECTORY_INHERIT_ACE;
+                       ace.aceFlags &= ~SMB_ACE4_NO_PROPAGATE_INHERIT_ACE;
+               }
+               pchildace = smb_add_ace4(pchildacl, &ace);
+               if (pchildace == NULL) {
+                       DEBUG(0, ("talloc failed\n"));
+                       TALLOC_FREE(frame);
+                       errno = ENOMEM;
+                       return NULL;
+               }
+       }
+
+       /* Set a default ACL if we didn't inherit anything. */
+       if (smb_first_ace4(pchildacl) == NULL) {
+               TALLOC_FREE(pchildacl);
+               pchildacl = nfs4acls_defaultacl(mem_ctx);
+       }
+
+       /* store the returned ACL to get it directly in the
+          future and avoid dynamic inheritance behavior. */
+       nfs4acl_xattr_set_smb4acl(handle, path, pchildacl);
+
+       TALLOC_FREE(frame);
+       return pchildacl;
 }
 
 static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
@@ -285,11 +524,11 @@ static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
        TALLOC_CTX *frame = talloc_stackframe();
 
        status = nfs4_fget_nfs4_acl(handle, frame, fsp, &pacl);
-       if (!NT_STATUS_IS_OK(status)) {
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-                       status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
-                                                         mem_ctx, ppdesc);
-               }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               pacl = nfs4acls_inheritacl(handle, fsp->fsp_name->base_name,
+                                          frame);
+       }
+       else if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
                return status;
        }
@@ -310,14 +549,9 @@ static NTSTATUS nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct *handle,
 
        status = nfs4_get_nfs4_acl(handle, frame, name, &pacl);
        if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-                       status = SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
-                                                        mem_ctx, ppdesc);
-               }
-               TALLOC_FREE(frame);
-               return status;
+               pacl = nfs4acls_inheritacl(handle, name, frame);
        }
-       if (!NT_STATUS_IS_OK(status)) {
+       else if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
                return status;
        }