diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 210a726bcf7..94875728367 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -410,10 +410,10 @@ typedef union unid_t { } unid_t; enum acl_brand { - SMB_ACL_BRAND_POSIX, - SMB_ACL_BRAND_NFS40, - SMB_ACL_BRAND_NFS41, - SMB_ACL_BRAND_NONE, + TRUENAS_ACL_BRAND_UNKNOWN, + TRUENAS_ACL_BRAND_POSIX, + TRUENAS_ACL_BRAND_NFS4, + TRUENAS_ACL_BRAND_NONE, }; struct fd_handle; diff --git a/source3/modules/vfs_ixnas.c b/source3/modules/vfs_ixnas.c index d1b1efb9a21..100bd27d9ba 100644 --- a/source3/modules/vfs_ixnas.c +++ b/source3/modules/vfs_ixnas.c @@ -1,8 +1,5 @@ /* * Unix SMB/CIFS implementation. - * A dumping ground for FreeBSD-specific VFS functions. For testing case - * of reducing number enabled VFS modules to bare minimum by creating - * single large VFS module. * * 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 @@ -18,9 +15,12 @@ * along with this program; if not, see . */ #include "includes.h" +#include "libcli/security/dom_sid.h" #include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" #include "smbd/smbd.h" #include "system/filesys.h" +#include "passdb/lookup_sid.h" #include "nfs4_acls.h" #include "zfsacl.h" @@ -37,7 +37,12 @@ struct ixnas_config_data { bool zfs_acl_chmod_enabled; }; -#ifndef FREEBSD +enum ixnas_dacl_type { + IXNAS_NULL_DACL, + IXNAS_EMPTY_DACL, + IXNAS_NORMAL_DACL +}; + #define UF_READONLY 0x0000000100000000ull #define UF_HIDDEN 0x0000000200000000ull #define UF_SYSTEM 0x0000000400000000ull @@ -46,20 +51,11 @@ struct ixnas_config_data { #define UF_OFFLINE 0x0000100000000000ull #define UF_SPARSE 0x0000200000000000ull -#define ACL_BRAND_UNKNOWN 0 -#define ACL_BRAND_POSIX 1 -#define ACL_BRAND_NFS4 2 -#define ACL_BRAND_NONE 3 - - #define ACL4_XATTR "system.nfs4_acl_xdr" #define ACL_XATTR "system.posix_acl_access" #define ZFS_IOC_GETDOSFLAGS _IOR(0x83, 1, uint64_t) #define ZFS_IOC_SETDOSFLAGS _IOW(0x83, 2, uint64_t) -#else -#define ACL_BRAND_NONE 3 -#endif /* FREEBSD */ static const struct { uint32_t dosmode; @@ -98,7 +94,10 @@ static void _dump_acl_info(zfsacl_t theacl, const char *fn) } #define dump_acl_info(x) _dump_acl_info(x, __func__) -#ifndef FREEBSD +/* + * This function converts a file opened via O_PATH to one opened under + * different flags by using procfs. + */ static int ixnas_pathref_reopen(const files_struct *fsp, int flags) { int fd_out = -1; @@ -117,13 +116,9 @@ static int ixnas_pathref_reopen(const files_struct *fsp, int flags) } return fd_out; } -#endif static bool ixnas_get_native_dosmode(struct files_struct *fsp, uint64_t *_dosmode) { -#if defined (FREEBSD) - *_dosmode = fsp->fsp_name->st.st_ex_flags & KERN_DOSMODES; -#else int err; if (!fsp->fsp_flags.is_pathref) { err = ioctl(fsp_get_io_fd(fsp), ZFS_IOC_GETDOSFLAGS, _dosmode); @@ -151,16 +146,12 @@ static bool ixnas_get_native_dosmode(struct files_struct *fsp, uint64_t *_dosmod fsp_str_dbg(fsp), strerror(errno)); return false; } -#endif /* FREEBSD */ return true; } static bool ixnas_set_native_dosmode(struct files_struct *fsp, uint64_t dosmode) { int err; -#if defined (FREEBSD) - err = SMB_VFS_FCHFLAGS(fsp, dosmode); -#else if (!fsp->fsp_flags.is_pathref) { err = ioctl(fsp_get_io_fd(fsp), ZFS_IOC_SETDOSFLAGS, &dosmode); } else { @@ -175,7 +166,6 @@ static bool ixnas_set_native_dosmode(struct files_struct *fsp, uint64_t dosmode) close(fd); } -#endif /* FREEBSD */ if (err) { if ((errno != EACCES) && (errno != EPERM)) { DBG_WARNING("Setting dosmode failed for %s: %s\n", @@ -196,23 +186,17 @@ static NTSTATUS ixnas_fget_dos_attributes(struct vfs_handle_struct *handle, struct ixnas_config_data *config = NULL; int i; bool ok; - uint64_t kern_dosmodes = 0; + uint64_t kern_dosmode = 0; + uint32_t xattr_dosmode = 0; + NTSTATUS status; SMB_VFS_HANDLE_GET_DATA(handle, config, struct ixnas_config_data, return NT_STATUS_INTERNAL_ERROR); -#if defined (FREEBSD) - if (config->dosattrib_xattr) { - return SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, - fsp, - dosmode); - } -#else - NTSTATUS status; status = SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, - dosmode); + &xattr_dosmode); if (config->dosattrib_xattr) { return status; @@ -227,33 +211,35 @@ static NTSTATUS ixnas_fget_dos_attributes(struct vfs_handle_struct *handle, } } -#endif /* FREEBSD */ - if (is_named_stream(fsp->fsp_name)) { // Streams don't have separate dos attribute metadata - ok = ixnas_get_native_dosmode(fsp->base_fsp, &kern_dosmodes); + ok = ixnas_get_native_dosmode(fsp->base_fsp, &kern_dosmode); } else { - ok = ixnas_get_native_dosmode(fsp, &kern_dosmodes); + ok = ixnas_get_native_dosmode(fsp, &kern_dosmode); } if (!ok) { return map_nt_error_from_unix(errno); } + *dosmode = xattr_dosmode; + for (i = 0; i < ARRAY_SIZE(dosmode2flag); i++) { - if (kern_dosmodes & dosmode2flag[i].flag) { + if (kern_dosmode & dosmode2flag[i].flag) { *dosmode |= dosmode2flag[i].dosmode; } } - if (S_ISDIR(fsp->fsp_name->st.st_ex_mode)) { /* * Windows default behavior appears to be that the archive bit - * on a directory is only explicitly set by clients. FreeBSD + * on a directory is only explicitly set by clients. ZFS * sets this bit when the directory's contents are modified. - * This is a temporary hack until we can make OS behavior - * configurable + * + * This means that we _must_ rely on the xattr-encoded dosmode + * to provide guidance as to whether it is set for the file. */ + if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) && + ((xattr_dosmode & FILE_ATTRIBUTE_ARCHIVE) == 0)) { *dosmode &= ~FILE_ATTRIBUTE_ARCHIVE; } @@ -336,18 +322,9 @@ static NTSTATUS ixnas_fset_dos_attributes(struct vfs_handle_struct *handle, } out: -#if defined (FREEBSD) - return NT_STATUS_OK; -#else - /* - * On Linux need to pass through - * so that we can set synthetic timestamps - * and file id. - */ return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode); -#endif /* FREEBSD */ } static zfsacl_t fsp_get_zfsacl(files_struct *fsp) @@ -393,33 +370,26 @@ static bool fsp_set_zfsacl(files_struct *fsp, zfsacl_t zfsacl) return zfsacl_set_file(proc_fd_path, zfsacl); } +/* + * fsp_get_aclbrand() and path_get_aclbrand() both get the ACL brand on the + * underlying filesystem. In almost all cases we rely on the ACL brand we + * detected on the initial SMB tree connect, which is preferable to detecting + * on a per-file basis. Unfortunately, certain types of users disregard UI + * warnings and alerts and nest datasets with different ACL properties (or + * mount random filesystems via the unix shell) and so we need to handle the + * unexpected if initial attempt to read the file's ACL fails. + */ static int fsp_get_acl_brand(files_struct *fsp) { -#if defined (FREEBSD) - int saved_errno; - saved_errno = errno; - long ret; - - ret = fpathconf(fsp_get_pathref_fd(fsp), _PC_ACL_NFS4); - if (ret == -1) { - if (saved_errno == errno) { - return ACL_BRAND_POSIX; - } - DBG_ERR("%s: fpathconf failed: %s\n", - fsp_str_dbg(fsp), strerror(errno)); - errno = saved_errno; - return ACL_BRAND_UNKNOWN; - } -#else ssize_t rv; rv = SMB_VFS_FGETXATTR(fsp, ACL_XATTR, NULL, 0); if (rv == -1) { if (errno == ENODATA) { - return ACL_BRAND_POSIX; + return TRUENAS_ACL_BRAND_POSIX; } else if (errno != EOPNOTSUPP) { DBG_ERR("%s: fgetxattr() for %s failed: %s\n", fsp_str_dbg(fsp), ACL_XATTR, strerror(errno)); - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } } @@ -427,47 +397,30 @@ static int fsp_get_acl_brand(files_struct *fsp) if (rv == -1) { if (errno == ENODATA) { /* probably need to add disabled */ - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } else if (errno == EOPNOTSUPP) { /* Neither NFSv4 nor POSIX acls are supported */ - return ACL_BRAND_NONE; + return TRUENAS_ACL_BRAND_NONE; } DBG_ERR("%s: fgetxattr() for %s failed: %s\n", fsp_str_dbg(fsp), ACL4_XATTR, strerror(errno)); - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } -#endif /* FREEBSD */ - return ACL_BRAND_NFS4; + return TRUENAS_ACL_BRAND_NFS4; } static int path_get_aclbrand(const char *path) { -#if defined (FREEBSD) - int saved_errno; - saved_errno = errno; - long ret; - - ret = pathconf(path, _PC_ACL_NFS4); - if (ret == -1) { - if (saved_errno == errno) { - return ACL_BRAND_POSIX; - } - DBG_ERR("%s: pathconf failed: %s\n", - path, strerror(errno)); - errno = saved_errno; - return ACL_BRAND_UNKNOWN; - } -#else /* LINUX */ ssize_t rv; rv = getxattr(path, ACL_XATTR, NULL, 0); if (rv == -1) { if (errno == ENODATA) { - return ACL_BRAND_POSIX; + return TRUENAS_ACL_BRAND_POSIX; } else if (errno != EOPNOTSUPP) { DBG_ERR("%s: getxattr() for %s failed: %s\n", path, ACL_XATTR, strerror(errno)); - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } } @@ -475,17 +428,17 @@ static int path_get_aclbrand(const char *path) if (rv == -1) { if (errno == ENODATA) { /* probably need to add disabled */ - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } DBG_ERR("%s: getxattr() for %s failed: %s\n", path, ACL4_XATTR, strerror(errno)); - return ACL_BRAND_UNKNOWN; + return TRUENAS_ACL_BRAND_UNKNOWN; } -#endif /* FREEBSD */ - return ACL_BRAND_NFS4; + return TRUENAS_ACL_BRAND_NFS4; } +/* Convert the native ZFS ACE format to the generic Samba NFSv4 format */ static bool zfsentry2smbace(zfsacl_entry_t ae, SMB_ACE4PROP_T *aceprop) { int i; @@ -564,6 +517,7 @@ static bool zfsentry2smbace(zfsacl_entry_t ae, SMB_ACE4PROP_T *aceprop) return true; } +/* convert Samba's generic NFSv4 ACE format to the native ZFS format */ static bool smbace2zfsentry(zfsacl_t zfsacl, SMB_ACE4PROP_T *aceprop) { bool ok; @@ -638,6 +592,85 @@ static bool smbace2zfsentry(zfsacl_t zfsacl, SMB_ACE4PROP_T *aceprop) return true; } +/* + * Determine the DACL type (for generating an NT security descriptor) from + * the native ZFS ACL format. If the ZFS ACL encodes a NULL DACL then + * dtype_out will be set to IXNAS_NULL_DACL, if it encodes an empty dacl + * then dtype_out will be set to IXNAS_EMPTY_DACL, otherwise it will + * be set to IXNAS_NORMAL_DACL. + * + * @param[in] zfacl Native ZFS ACL + * @param[out] dtype_out DACL type (on success) + * @return boolean - true on success + * + * NOTE: this may fail if we get a malformed ACL from ZFS. + */ +static bool ixnas_zfsacl_get_dacl_type(zfsacl_t zfsacl, + enum ixnas_dacl_type *dtype_out) +{ + bool ok; + uint cnt; + enum ixnas_dacl_type dtype = IXNAS_NORMAL_DACL; + zfsacl_entry_t ae = NULL; + zfsace_permset_t perms = 0; + zfsace_flagset_t flags = 0; + zfsace_entry_type_t entry_type = 0; + zfsace_id_t who_id = ZFSACL_UNDEFINED_ID; + zfsace_who_t who_type = ZFSACL_UNDEFINED_TAG; + + ok = zfsacl_get_acecnt(zfsacl, &cnt); + if (!ok) { + DBG_ERR("zfsacl_get_acecnt() failed: %s\n", strerror(errno)); + return false; + } + + ok = zfsacl_get_aclentry(zfsacl, 0, &ae); + if (!ok) { + DBG_ERR("zfsacl_get_aclentry() failed: %s\n", strerror(errno)); + return false; + } + + ok = zfsace_get_permset(ae, &perms); + if (!ok) { + DBG_ERR("zfsace_get_permset() failed: %s\n", strerror(errno)); + return false; + } + + ok = zfsace_get_flagset(ae, &flags); + if (!ok) { + DBG_ERR("zfsace_get_flagset() failed: %s\n", strerror(errno)); + return false; + } + + ok = zfsace_get_who(ae, &who_type, &who_id); + if (!ok) { + DBG_ERR("zfsace_get_who() failed: %s\n", strerror(errno)); + return false; + } + + ok = zfsace_get_entry_type(ae, &entry_type); + if (!ok) { + DBG_ERR("zfsace_get_entry_type() failed: %s\n", strerror(errno)); + return false; + } + + // our special DACL types have precisely one entry; + if ((cnt == 1) && + (entry_type == ZFSACL_ENTRY_TYPE_ALLOW) && + (who_type == ZFSACL_EVERYONE) && + (who_id == ZFSACL_UNDEFINED_ID) && + (flags == 0)) { + if (perms == ZFSACE_FULL_SET) { + dtype = IXNAS_NULL_DACL; + } else if (perms == 0) { + dtype = IXNAS_EMPTY_DACL; + } + } + + *dtype_out = dtype; + return true; +} + static NTSTATUS ixnas_get_nt_acl_nfs4_common(struct connection_struct *conn, TALLOC_CTX *mem_ctx, files_struct *fsp, @@ -775,6 +808,59 @@ static zfsacl_t fsp_get_zfsacl_from_mode(struct files_struct *fsp) return zfsacl; } +static NTSTATUS ixnas_generate_special_dacl_sd(struct vfs_handle_struct *handle, + struct files_struct *fsp, + uint32_t security_info, + TALLOC_CTX *mem_ctx, + enum ixnas_dacl_type dtype, + struct security_descriptor **ppdesc) +{ + NTSTATUS status; + struct dom_sid sid_owner, sid_group; + size_t sd_size = 0; + struct security_ace *nt_ace_list = NULL; + struct security_acl *psa = NULL; + uint16_t controlflags = SEC_DESC_SELF_RELATIVE | SEC_DESC_DACL_PROTECTED | SEC_DESC_DACL_PRESENT; + + SMB_ASSERT((dtype == IXNAS_EMPTY_DACL) || (dtype == IXNAS_NULL_DACL)); + + if (!VALID_STAT(fsp->fsp_name->st)) { + status = vfs_stat_fsp(fsp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + uid_to_sid(&sid_owner, fsp->fsp_name->st.st_ex_uid); + gid_to_sid(&sid_group, fsp->fsp_name->st.st_ex_gid); + + if (dtype == IXNAS_EMPTY_DACL) { + psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 0, NULL); + if (psa == NULL) { + DBG_ERR("make_sec_acl failed\n"); + return NT_STATUS_NO_MEMORY; + } + } + + *ppdesc = make_sec_desc( + mem_ctx, SD_REVISION, controlflags, + (security_info & SECINFO_OWNER) ? &sid_owner : NULL, + (security_info & SECINFO_GROUP) ? &sid_group : NULL, + NULL, psa, &sd_size); + + TALLOC_FREE(psa); + + if (*ppdesc==NULL) { + DBG_ERR("make_sec_desc failed\n"); + return NT_STATUS_NO_MEMORY; + } + + DBG_DEBUG("smb_get_nt_acl_nfs4_common successfully exited with " + "sd_size %d\n", (int)ndr_size_security_descriptor(*ppdesc, 0)); + + return NT_STATUS_OK; +} + static NTSTATUS ixnas_fget_nt_acl(struct vfs_handle_struct *handle, struct files_struct *fsp, uint32_t security_info, @@ -786,6 +872,8 @@ static NTSTATUS ixnas_fget_nt_acl(struct vfs_handle_struct *handle, struct files_struct *to_check = NULL; NTSTATUS status; zfsacl_t zfsacl; + bool ok; + enum ixnas_dacl_type dtype = IXNAS_NORMAL_DACL; struct ixnas_config_data *config = NULL; SMB_VFS_HANDLE_GET_DATA(handle, config, @@ -801,14 +889,14 @@ static NTSTATUS ixnas_fget_nt_acl(struct vfs_handle_struct *handle, if (zfsacl == NULL) { if ((errno == EINVAL) || (errno == EOPNOTSUPP)) { switch (fsp_get_acl_brand(fsp)) { - case ACL_BRAND_POSIX: - case ACL_BRAND_UNKNOWN: + case TRUENAS_ACL_BRAND_POSIX: + case TRUENAS_ACL_BRAND_UNKNOWN: status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, mem_ctx, ppdesc); if (NT_STATUS_IS_OK(status)) { (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED; } return status; - case ACL_BRAND_NONE: + case TRUENAS_ACL_BRAND_NONE: zfsacl = fsp_get_zfsacl_from_mode(fsp); if (zfsacl == NULL) { return map_nt_error_from_unix(errno); @@ -823,6 +911,25 @@ static NTSTATUS ixnas_fget_nt_acl(struct vfs_handle_struct *handle, } dump_acl_info(zfsacl); + ok = ixnas_zfsacl_get_dacl_type(zfsacl, &dtype); + if (!ok) { + return map_nt_error_from_unix(errno); + } + switch (dtype) { + case IXNAS_NULL_DACL: + case IXNAS_EMPTY_DACL: + return ixnas_generate_special_dacl_sd(handle, + fsp, + security_info, + mem_ctx, + dtype, + ppdesc); + case IXNAS_NORMAL_DACL: + break; + default: + smb_panic("Unexpected ixnas_dacl_type"); + }; + frame = talloc_stackframe(); status = ixnas_get_nt_acl_nfs4_common(handle->conn, frame, @@ -851,7 +958,6 @@ static bool ixnas_add_hidden_entry(zfsacl_t zfsacl, uint acecnt; zfsacl_entry_t hidden_entry = NULL; - if (!has_inheritable) { ok = zfsacl_get_acecnt(zfsacl, &acecnt); if (!ok) { @@ -965,6 +1071,95 @@ static bool ixnas_process_smbacl(vfs_handle_struct *handle, return true; } +static NTSTATUS ixnas_fset_special_dacl(vfs_handle_struct *handle, + files_struct *fsp, + enum ixnas_dacl_type dtype) +{ + /* + * A null DACL grants full access to any user that requests it; normal + * security checking is not performed with respect to the object. + * + * Example use case: + * Adobe applications may use these for locking files. + * + * For our purposes we indicate this special ACL type with the following + * characteristics: + * 1. A single ACL entry granting everyone@ full control + * 2. No additional hidden locking ACE + * 3. No additional ACL control flags + * + * An empty DACL grants no access to the object it is assigned to. + * 1. A single ACL entry granting everyone@ no access at all + * 2. No additional hidden locking ACE + * 3. No additional ACL control flags + * + * Example use case: + * Adobe applications may use an empty DACL on some files while in + * protected mode to prevent anyone other than the file owner or users + * with elevated system privileges that allow ignoring DACL from + * accessing the files. + * + * SDDL sample: O:G:D:PAI + * c.f https://learn.microsoft.com/en-us/windows/win32/secauthz/null-dacls-and-empty-dacls + */ + + zfsacl_t zfsacl; + struct SMB4ACE_T *smbace = NULL; + zfsacl_entry_t placeholder_entry = NULL; + bool ok; + zfsace_permset_t perms; + + switch (dtype) { + case IXNAS_NULL_DACL: + perms = ZFSACE_FULL_SET; + break; + case IXNAS_EMPTY_DACL: + perms = 0; + break; + default: + smb_panic("unexpected ixnas_dacl_type"); + }; + + zfsacl = zfsacl_init(1, ZFSACL_BRAND_NFSV4); + if (zfsacl == NULL) { + DBG_ERR("%s: acl_init failed: %s\n", + fsp_str_dbg(fsp), strerror(errno)); + return NT_STATUS_ACCESS_DENIED; + } + + ok = zfsacl_create_aclentry(zfsacl, ZFSACL_APPEND_ENTRY, &placeholder_entry); + if (!ok) { + DBG_ERR("zfsacl_create_aclentry() failed: %s\n", strerror(errno)); + zfsacl_free(&zfsacl); + return NT_STATUS_ACCESS_DENIED; + } + + ok = zfsace_set_permset(placeholder_entry, perms); + if (!ok) { + DBG_ERR("zfsacl_set_permset() failed: %s\n", strerror(errno)); + zfsacl_free(&zfsacl); + return NT_STATUS_ACCESS_DENIED; + } + + ok = zfsace_set_who(placeholder_entry, ZFSACL_EVERYONE, ZFSACL_UNDEFINED_ID); + if (!ok) { + DBG_ERR("zfsacl_set_who() failed: %s\n", strerror(errno)); + zfsacl_free(&zfsacl); + return NT_STATUS_ACCESS_DENIED; + } + + dump_acl_info(zfsacl); + if (!fsp_set_zfsacl(fsp, zfsacl)) { + DBG_ERR("%s: failed to set acl: %s\n", + fsp_str_dbg(fsp), strerror(errno)); + zfsacl_free(&zfsacl); + return map_nt_error_from_unix(errno); + } + + zfsacl_free(&zfsacl); + return NT_STATUS_OK; +} + static NTSTATUS ixnas_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32_t security_info_sent, @@ -976,6 +1171,14 @@ static NTSTATUS ixnas_fset_nt_acl(vfs_handle_struct *handle, struct ixnas_config_data, return NT_STATUS_INTERNAL_ERROR); + if ((security_info_sent & SECINFO_DACL) && + (psd->dacl == NULL)) { + return ixnas_fset_special_dacl(handle, fsp, IXNAS_NULL_DACL); + } else if ((security_info_sent & SECINFO_DACL) && + ((psd->type & SEC_DESC_DACL_PRESENT) == 0)) { + return ixnas_fset_special_dacl(handle, fsp, IXNAS_EMPTY_DACL); + } + return smb_set_nt_acl_nfs4(handle, fsp, &config->nfs4_params, @@ -1015,7 +1218,7 @@ static int ixnas_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, /******************************************************************** Convert chmod() requests into an appropriate non-inheriting ACL - entry. We don't rely on FreeBSD kernel behavior in this case, + entry. We don't rely on ZFS behavior in this case, because it strips some bits that we actually care about (WRITE_ATTRIBUTES, DELETE, etc.). If DELETE is stripped, then users will no longer be able to rename files. @@ -1412,214 +1615,6 @@ static int ixnas_fchmod(vfs_handle_struct *handle, return -1; } -#if 0 /*pending work on FILE IDs from FreeBSD */ -#if defined (FREEBSD) -static struct file_id ixnas_file_id_create(struct vfs_handle_struct *handle, - const SMB_STRUCT_STAT *sbuf) -{ - struct file_id key = (struct file_id) { - .devid = sbuf->st_ex_dev, - .inode = sbuf->st_ex_ino, - .extid = sbuf->st_ex_gen, - }; - - return key; -} - -static inline uint64_t gen_id_comp(uint64_t p) { - uint64_t out = (p & UINT32_MAX) ^ (p >> 32); - return out; -}; - -#endif - -static uint64_t ixnas_fs_file_id(struct vfs_handle_struct *handle, - const SMB_STRUCT_STAT *psbuf); - -static int ixnas_renameat(vfs_handle_struct *handle, - files_struct *srcfsp, - const struct smb_filename *smb_fname_src, - files_struct *dstfsp, - const struct smb_filename *smb_fname_dst) -{ - int result = 1; - struct ixnas_config_data *config = NULL; - char *tmp_base_name = NULL; - uint64_t srcid, dstid; - - SMB_VFS_HANDLE_GET_DATA(handle, config, - struct ixnas_config_data, - return -1); - - if (config->props->casesens != SMBZFS_INSENSITIVE) { - return SMB_VFS_NEXT_RENAMEAT(handle, - srcfsp, - smb_fname_src, - dstfsp, - smb_fname_dst); - } - - srcid = ixnas_fs_file_id(handle, &srcfsp->fsp_name->st); - dstid = ixnas_fs_file_id(handle, &dstfsp->fsp_name->st); - - if (srcid == dstid) { - result = strcasecmp_m(smb_fname_src->base_name, - smb_fname_dst->base_name); - } - if (result != 0) { - return SMB_VFS_NEXT_RENAMEAT(handle, - srcfsp, - smb_fname_src, - dstfsp, - smb_fname_dst); - } - - dstid = ixnas_fs_file_id(handle, &smb_fname_src->st); - tmp_base_name = talloc_asprintf(talloc_tos(), "%s_%lu", - smb_fname_src->base_name, dstid); - if (tmp_base_name == NULL) { - errno = ENOMEM; - return -1; - } - result = renameat( - fsp_get_pathref_fd(srcfsp), smb_fname_src->base_name, - fsp_get_pathref_fd(dstfsp), tmp_base_name - ); - if (result != 0) { - DBG_ERR("Failed to rename %s to intermediate name %s\n", - smb_fname_src->base_name, tmp_base_name); - TALLOC_FREE(tmp_base_name); - return result; - } - result = renameat( - fsp_get_pathref_fd(dstfsp), tmp_base_name, - fsp_get_pathref_fd(srcfsp), smb_fname_dst->base_name - ); - TALLOC_FREE(tmp_base_name); - return result; -} - -static struct file_id ixnas_file_id_create(struct vfs_handle_struct *handle, - const SMB_STRUCT_STAT *sbuf) -{ - struct file_id key = (struct file_id) { - .devid = sbuf->st_ex_dev, - .inode = sbuf->st_ex_ino, - .extid = sbuf->st_ex_gen, - }; - - return key; -} - -static inline uint64_t gen_id_comp(uint64_t p) { - uint64_t out = (p & UINT32_MAX) ^ (p >> 32); - return out; -}; - -static uint64_t ixnas_fs_file_id(struct vfs_handle_struct *handle, - const SMB_STRUCT_STAT *psbuf) -{ - uint64_t file_id; - if (!(psbuf->st_ex_iflags & ST_EX_IFLAG_CALCULATED_FILE_ID)) { - return psbuf->st_ex_file_id; - } - - file_id = gen_id_comp(psbuf->st_ex_ino); - file_id |= gen_id_comp(psbuf->st_ex_gen) << 32; - return file_id; -} - -static int fsp_set_times(files_struct *fsp, struct timespec *times, bool set_btime) -{ - int flag = set_btime ? AT_UTIMENSAT_BTIME : 0; - if (fsp->fsp_flags.have_proc_fds) { - int fd = fsp_get_pathref_fd(fsp); - const char *p = NULL; - struct sys_proc_fd_path_buf buf; - - p = sys_proc_fd_path(fd, &buf); - if (p != NULL) { - return utimensat(AT_FDCWD, p, times, 0); - } - - return -1; - } - - /* fallback to path-based call */ - return utimensat(AT_FDCWD, fsp->fsp_name->base_name, times, 0); -} - -static int ixnas_ntimes(vfs_handle_struct *handle, - files_struct *fsp, - struct smb_file_time *ft) -{ - int result = -1; - struct ixnas_config_data *config = NULL; - struct timespec ts[2], *times = NULL; - - if (is_named_stream(fsp->fsp_name)) { - errno = ENOENT; - return result; - } - - SMB_VFS_HANDLE_GET_DATA(handle, config, - struct ixnas_config_data, - return -1); - - if (config->dosattrib_xattr) { - return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft); - } - - /* - * man utimensat(2) - * If times is non-NULL, it is assumed to point to an array of two - * timespec structures. The access time is set to the value of the - * second element. For filesystems that support file birth (creation) times, - * the birth time will be set to the value of the second element if the - * second element is older than the currently set birthtime. To set both - * a birth time and a modification tie, two calls are required. The first - * to set the birth time and the second to set the (presumabley newer). - */ - if (ft != NULL) { - if (is_robocopy_init(ft)) { - return 0; - } - if (is_omit_timespec(&ft->atime)) { - ft->atime= fsp->fsp_name->st.st_ex_atime; - } - if (is_omit_timespec(&ft->mtime)) { - ft->mtime = fsp->fsp_name->st.st_ex_mtime; - } - /* mtime and atime are unchanged */ - if ((timespec_compare(&ft->atime, - &fsp->fsp_name->st.st_ex_atime) == 0) && - (timespec_compare(&ft->mtime, - &fsp->fsp_name->st.st_ex_mtime) == 0)) { - return 0; - } - /* - * Perform two utimensat() calls if needed to set the specified - * timestamps. - */ - if (is_omit_timespec(&ft->create_time)) { - ft->create_time = ft->mtime; - } - ts[0] = ft->atime; - ts[1] = ft->create_time; - result = fsp_set_times(fsp, ts); - if (timespec_compare(&ft->mtime, &ft->create_time) != 0) { - ts[1] = ft->mtime; - result = fsp_set_times(fsp, ts); - } - } - - if (result != 0) { - DBG_ERR("utimensat failed: %s \n", strerror(errno)); - } - return result; -} -#endif /* FREEBSD */ - static bool set_acl_parameters(struct vfs_handle_struct *handle, struct ixnas_config_data *config) { @@ -1660,7 +1655,8 @@ static bool set_acl_parameters(struct vfs_handle_struct *handle, * middleware will probably not let the user get this far, but it's better to * be somewhat safer. */ - if (path_get_aclbrand(handle->conn->connectpath) != ACL_BRAND_NFS4) { + handle->conn->aclbrand = path_get_aclbrand(handle->conn->connectpath); + if (handle->conn->aclbrand != TRUENAS_ACL_BRAND_NFS4) { DBG_ERR("Connectpath does not support NFSv4 ACLs. Disabling ZFS ACL handling.\n"); config->zfs_acl_enabled = false; } @@ -1708,32 +1704,6 @@ static int ixnas_connect(struct vfs_handle_struct *handle, config->dosattrib_xattr = lp_parm_bool(SNUM(handle->conn), "ixnas", "dosattrib_xattr", false); -#if defined (FREEBSD) - if (!config->dosattrib_xattr) { - if ((lp_map_readonly(SNUM(handle->conn))) == MAP_READONLY_YES) { - DBG_INFO("ixnas:dosmode to file flag mapping enabled," - "disabling 'map readonly'\n"); - lp_do_parameter(SNUM(handle->conn), "map readonly", - "no"); - } - - if (lp_map_archive(SNUM(handle->conn))) { - DBG_INFO("ixnas:dosmode to file flag mapping enabled," - "disabling 'map archive'\n"); - lp_do_parameter(SNUM(handle->conn), "map archive", - "no"); - } - - if (lp_store_dos_attributes(SNUM(handle->conn))){ - DBG_INFO("ixnas:dosmode to file flag mapping enabled," - "disabling 'store dos attributes'\n"); - lp_do_parameter(SNUM(handle->conn), "store dos attributes", - "no"); - } - lp_do_parameter(SNUM(handle->conn), "kernel dosmodes", "yes"); - } -#endif - ok = set_acl_parameters(handle, config); if (!ok) { TALLOC_FREE(config); @@ -1754,10 +1724,6 @@ static struct vfs_fn_pointers ixnas_fns = { .fset_dos_attributes_fn = ixnas_fset_dos_attributes, /* zfs_acl_enabled = true */ .fchmod_fn = ixnas_fchmod, -#if defined (FREEBSD) - .fntimes_fn = ixnas_ntimes, - .file_id_create_fn = ixnas_file_id_create, -#endif .fget_nt_acl_fn = ixnas_fget_nt_acl, .fset_nt_acl_fn = ixnas_fset_nt_acl, .sys_acl_get_fd_fn = ixnas_fail__sys_acl_get_fd, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 4e4359b3d06..e95fccafeeb 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -172,6 +172,17 @@ static NTSTATUS smbd_check_access_rights_sd( goto access_denied; } + /* + * A null DACL grants full access to any user that requests it; normal + * security checking is not performed with respect to the object. + * + * c.f. https://learn.microsoft.com/en-us/windows/win32/secauthz/null-dacls-and-empty-dacls + */ + if ((conn->aclbrand == TRUENAS_ACL_BRAND_NFS4) && + (sd->type & SEC_DESC_DACL_PRESENT) && (sd->dacl == NULL)) { + return NT_STATUS_OK; + } + status = se_file_access_check(sd, get_current_nttok(conn), use_privs,