From 74663a8be6c1fd637eefb3c0d9d53a7e8a695ece Mon Sep 17 00:00:00 2001 From: Andrew Walker Date: Thu, 24 Mar 2022 21:59:47 -0400 Subject: [PATCH] Make samba aware of ACL brand and trivial ACEs --- source3/include/vfs.h | 1 + source3/modules/nfs41acl.x | 2 + source3/modules/nfs4_acls.c | 20 ++++ source3/modules/nfs4_acls.h | 4 + source3/modules/nfs4acl_xattr_xdr.c | 5 +- source3/modules/vfs_nfs4acl_xattr.c | 18 +++- source3/modules/vfs_recycle.c | 155 +++++++++++++++++++++++++++- 7 files changed, 197 insertions(+), 8 deletions(-) diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 4d9d9353e40..7d5d9fc61ea 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -440,6 +440,7 @@ typedef struct files_struct { bool closing : 1; bool lock_failure_seen : 1; bool encryption_required : 1; + bool acl_is_trivial : 1; } fsp_flags; struct tevent_timer *update_write_time_event; diff --git a/source3/modules/nfs41acl.x b/source3/modules/nfs41acl.x index 51ac89a0791..aa177afbfb4 100644 --- a/source3/modules/nfs41acl.x +++ b/source3/modules/nfs41acl.x @@ -93,6 +93,8 @@ const ACL4_XATTR_VERSION_DEFAULT = ACL4_XATTR_VERSION_40; const ACL4_AUTO_INHERIT = 0x00000001; const ACL4_PROTECTED = 0x00000002; const ACL4_DEFAULTED = 0x00000004; +const ACL4_IS_TRIVIAL = 0x00010000; +const ACL4_IS_DIR = 0x00020000; typedef u_int aclflag4; diff --git a/source3/modules/nfs4_acls.c b/source3/modules/nfs4_acls.c index c7808037a09..ab4886ec62c 100644 --- a/source3/modules/nfs4_acls.c +++ b/source3/modules/nfs4_acls.c @@ -49,6 +49,7 @@ struct SMB4ACL_T { uint16_t controlflags; uint32_t naces; + bool is_trivial; struct SMB4ACE_T *first; struct SMB4ACE_T *last; }; @@ -265,6 +266,25 @@ bool smbacl4_set_controlflags(struct SMB4ACL_T *acl, uint16_t controlflags) return true; } +bool smbacl4_get_trivial(const struct SMB4ACL_T *acl, bool *trivialp) +{ + if (acl == NULL) { + return false; + } + *trivialp = acl->is_trivial; + return true; +} + +bool smbacl4_set_trivial(struct SMB4ACL_T *acl, bool trivial) +{ + if (acl == NULL) { + return false; + } + acl->is_trivial = trivial; + return true; +} + + bool nfs_ace_is_inherit(SMB_ACE4PROP_T *ace) { return ace->aceFlags & (SMB_ACE4_INHERIT_ONLY_ACE| diff --git a/source3/modules/nfs4_acls.h b/source3/modules/nfs4_acls.h index c9fcf6d250b..6bcd3b5ffdc 100644 --- a/source3/modules/nfs4_acls.h +++ b/source3/modules/nfs4_acls.h @@ -140,6 +140,10 @@ bool smbacl4_set_controlflags(struct SMB4ACL_T *theacl, uint16_t controlflags); bool nfs_ace_is_inherit(SMB_ACE4PROP_T *ace); +bool smbacl4_get_trivial(const struct SMB4ACL_T *acl, bool *trivialp); + +bool smbacl4_set_trivial(struct SMB4ACL_T *acl, bool trivial); + NTSTATUS smb_fget_nt_acl_nfs4(files_struct *fsp, const struct smbacl4_vfs_params *pparams, uint32_t security_info, diff --git a/source3/modules/nfs4acl_xattr_xdr.c b/source3/modules/nfs4acl_xattr_xdr.c index b6e8e1bbc38..301152cab3d 100644 --- a/source3/modules/nfs4acl_xattr_xdr.c +++ b/source3/modules/nfs4acl_xattr_xdr.c @@ -276,6 +276,8 @@ static NTSTATUS nfs4acl_xdr_blob_to_nfs4acli(struct vfs_handle_struct *handle, return NT_STATUS_OK; } +#define ACL4_ALL_FLAGS (ACL4_AUTO_INHERIT | ACL4_PROTECTED | ACL4_DEFAULTED) + static NTSTATUS nfs4acli_to_smb4acl(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, nfsacl41i *nacl, @@ -300,7 +302,8 @@ static NTSTATUS nfs4acli_to_smb4acl(struct vfs_handle_struct *handle, if (config->nfs_version > ACL4_XATTR_VERSION_40) { nfsacl41_flag = nfs4acli_get_flags(nacl); smb4acl_flags = nfs4acl_to_smb4acl_flags(nfsacl41_flag); - smbacl4_set_controlflags(smb4acl, smb4acl_flags); + smbacl4_set_controlflags(smb4acl, smb4acl_flags & ACL4_ALL_FLAGS); + smbacl4_set_trivial(smb4acl, smb4acl_flags & ACL4_IS_TRIVIAL); } DBG_DEBUG("flags [%x] nace [%u]\n", smb4acl_flags, naces); diff --git a/source3/modules/vfs_nfs4acl_xattr.c b/source3/modules/vfs_nfs4acl_xattr.c index da24d9bb7d2..2ca2eecf2a5 100644 --- a/source3/modules/vfs_nfs4acl_xattr.c +++ b/source3/modules/vfs_nfs4acl_xattr.c @@ -232,6 +232,14 @@ static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle, status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx, sd, smb4acl); + + if (NT_STATUS_IS_OK(status)) { + bool ok, is_trivial; + ok = smbacl4_get_trivial(smb4acl, &is_trivial); + if (ok) { + fsp->fsp_flags.acl_is_trivial = is_trivial; + } + } TALLOC_FREE(frame); return status; } @@ -465,9 +473,11 @@ static int nfs4acl_connect(struct vfs_handle_struct *handle, switch (nfs_version) { case 40: config->nfs_version = ACL4_XATTR_VERSION_40; + handle->conn->aclbrand = SMB_ACL_BRAND_NFS40; break; case 41: config->nfs_version = ACL4_XATTR_VERSION_41; + handle->conn->aclbrand = SMB_ACL_BRAND_NFS41; break; default: config->nfs_version = ACL4_XATTR_VERSION_DEFAULT; @@ -516,11 +526,13 @@ static int nfs4acl_connect(struct vfs_handle_struct *handle, "'store dos attributes = yes' " "for service [%s]\n", service); - lp_do_parameter(SNUM(handle->conn), "inherit acls", "true"); + if (handle->conn->aclbrand != SMB_ACL_BRAND_NFS41) { + lp_do_parameter(SNUM(handle->conn), "inherit acls", "true"); + lp_do_parameter(SNUM(handle->conn), "create mask", "0666"); + lp_do_parameter(SNUM(handle->conn), "directory mask", "0777"); + } lp_do_parameter(SNUM(handle->conn), "dos filemode", "true"); lp_do_parameter(SNUM(handle->conn), "force unknown acl user", "true"); - lp_do_parameter(SNUM(handle->conn), "create mask", "0666"); - lp_do_parameter(SNUM(handle->conn), "directory mask", "0777"); lp_do_parameter(SNUM(handle->conn), "store dos attributes", "yes"); return 0; diff --git a/source3/modules/vfs_recycle.c b/source3/modules/vfs_recycle.c index 5bbd27a9214..4d95640b286 100644 --- a/source3/modules/vfs_recycle.c +++ b/source3/modules/vfs_recycle.c @@ -436,13 +436,143 @@ static bool recycle_file_exist(vfs_handle_struct *handle, return false; } +#ifdef FREEBSD +static bool acl_strip(files_struct *dirfsp, + struct smb_filename *smb_fname, + mode_t mode) +{ + bool ok; + int dirfd, fd, err; + acl_t old, new; + + dirfd = fsp_get_pathref_fd(dirfsp); + SMB_ASSERT(dirfd != -1); + + fd = openat(dirfd, smb_fname->base_name, O_DIRECTORY); + if (fd == -1) { + DBG_ERR("%s: failed to open directory: %s\n", + smb_fname->base_name, strerror(errno)); + return false; + } + + old = acl_get_fd_np(fd, ACL_TYPE_NFS4); + if (old == NULL) { + DBG_ERR("%s: acl_get_fd_np() failed: %s\n", + smb_fname->base_name, strerror(errno)); + ok = false; + goto done; + } + + new = acl_strip_np(old, 0); + if (new == NULL) { + DBG_ERR("%s: acl_get_strip_np() failed: %s\n", + smb_fname->base_name, strerror(errno)); + acl_free(old); + goto done; + } + acl_free(old); + + err = acl_set_fd_np(fd, new, ACL_TYPE_NFS4); + if (err) { + DBG_ERR("%s: acl_set_fd_np() failed: %s\n", + smb_fname->base_name, strerror(errno)); + acl_free(new); + goto done; + } + + acl_free(new); + + err = fchmod(fd, mode); + if (err) { + DBG_ERR("%s: chmod() failed: %s\n", + smb_fname->base_name, strerror(errno)); + } +done: + close(fd); + return ok; +} + +static bool acl_is_trivial(files_strut *dirfsp, + bool *trivialp) +{ + int dirfd, fd, err; + acl_t theacl; + bool is_trivial; + + dirfd = fsp_get_pathref_fd(dirfsp); + SMB_ASSERT(dirfd != -1); + + fd = openat(dirfd, "", O_DIRECTORY, AT_EMPTY_PATH); + if (fd == -1) { + DBG_ERR("%s: failed to open directory: %s\n", + fsp_str_dbg(dirfsp), strerror(errno)); + return false; + } + theacl = acl_get_fd_np(fd, ACL_TYPE_NFS4); + if (theacl == NULL) { + DBG_ERR("%s: acl_get_fd_np() failed: %s\n", + fsp_str_dbg(dirfsp), strerror(errno)); + close(fd); + return false; + } + + err = acl_is_trivial_np(theacl, &is_trivial); + if (err) { + DBG_ERR("%s: acl_is_trivial_np() failed: %s\n", + fsp_str_dbg(dirfsp), strerror(errno)); + } + return err ? false : true; +} + +#else +static bool acl_strip(files_struct *dirfsp, + struct smb_filename *smb_fname, + mode_t mode) +{ + /* + * Still need to implement functionality to strip ACL in Linux + * Current plan is to do through rmxattr on the NFS ACL xattr. + */ + + return false; +} + +static bool acl_is_trivial(files_strut *dirfsp, + bool *trivialp) +{ + NTSTATUS status; + struct security_descriptor *sd = NULL; + + status = SMB_VFS_FGET_NT_ACL(dirfsp, + (SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL), + talloc_tos(), + &sd); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("%s: failed to get NT ACL: %s\n", + fsp_str_dbg(dirfsp), nt_errstr(status)); + return false; + } + + TALLOC_FREE(sd); + *trivialp = dirfsp->fsp_flags.acl_is_trivial; + return true; +} + +#endif + /** * Create directory tree * @param conn connection * @param dname Directory tree to be created + * @param check_acl whether to check if ACL is trivial * @return Returns True for success **/ -static bool recycle_create_dir(vfs_handle_struct *handle, const files_struct *dirfsp, const char *dname) +static bool recycle_create_dir(vfs_handle_struct *handle, + const files_struct *dirfsp, + const char *dname, + bool check_acl) { size_t len; mode_t mode; @@ -451,9 +581,18 @@ static bool recycle_create_dir(vfs_handle_struct *handle, const files_struct *di char *token; char *tok_str; bool ret = False; + bool is_trivial = true; char *saveptr; + int depth; mode = recycle_directory_mode(handle); + if (check_acl && handle->conn->aclbrand == SMB_ACL_BRAND_NFS41) { + bool ok; + ok = acl_is_trivial(handle, dirfsp, &is_trivial); + if (!ok) { + return ok; + } + } tmp_str = SMB_STRDUP(dname); ALLOC_CHECK(tmp_str, done); @@ -471,8 +610,8 @@ static bool recycle_create_dir(vfs_handle_struct *handle, const files_struct *di } /* Create directory tree if necessary */ - for(token = strtok_r(tok_str, "/", &saveptr); token; - token = strtok_r(NULL, "/", &saveptr)) { + for(token = strtok_r(tok_str, "/", &saveptr), depth = 0; token; + token = strtok_r(NULL, "/", &saveptr), depth++) { if (strlcat(new_dir, token, len+1) >= len+1) { goto done; } @@ -490,7 +629,15 @@ static bool recycle_create_dir(vfs_handle_struct *handle, const files_struct *di dirfsp, &smb_fname, mode); - if (retval != 0) { + if (retval != 0 && errno == EPERM && !is_trivial) { + ret = acl_strip(handle, dirfsp, &smb_fname, mode); + if (!ret) { + DBG_ERR("%s: failed to strip ACL: %s\n", + new_dir, strerror(errno)); + goto done; + } + } + else if (retval != 0) { DBG_WARNING("recycle: mkdirat failed " "for %s with error: %s\n", new_dir,