From 97eb8f73e5d106781344895d3bb02767b0ac5c94 Mon Sep 17 00:00:00 2001 From: Alexander Werth Date: Tue, 16 Apr 2013 14:11:27 +0200 Subject: [PATCH] vfs: Add inheritance emulation to vfs_nfs4acl_xattr. 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 Reviewed-by: Jeremy Allison --- selftest/knownfail | 1 - source3/modules/vfs_nfs4acl_xattr.c | 292 +++++++++++++++++++++++++--- 2 files changed, 263 insertions(+), 30 deletions(-) diff --git a/selftest/knownfail b/selftest/knownfail index 069afe7604c..a7dfdf82891 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -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\) diff --git a/source3/modules/vfs_nfs4acl_xattr.c b/source3/modules/vfs_nfs4acl_xattr.c index fedb768d0e8..bdaca82b8ca 100644 --- a/source3/modules/vfs_nfs4acl_xattr.c +++ b/source3/modules/vfs_nfs4acl_xattr.c @@ -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; } -- 2.34.1