X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fmodules%2Fvfs_zfsacl.c;h=c5277a6b2c4e97a3138d3100ae7d689eeaa90c44;hb=HEAD;hp=d265931cf2a9c5d81cd1445bebebe127262a422b;hpb=15953b82eb3b49d736b4b835b1d0d3cf0da0bff8;p=samba.git diff --git a/source3/modules/vfs_zfsacl.c b/source3/modules/vfs_zfsacl.c index d265931cf2a..695abf1e0df 100644 --- a/source3/modules/vfs_zfsacl.c +++ b/source3/modules/vfs_zfsacl.c @@ -23,84 +23,161 @@ */ #include "includes.h" +#include "system/filesys.h" +#include "smbd/smbd.h" #include "nfs4_acls.h" +#ifdef HAVE_FREEBSD_SUNACL_H +#include "sunacl.h" +#endif + #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS #define ZFSACL_MODULE_NAME "zfsacl" +struct zfsacl_config_data { + struct smbacl4_vfs_params nfs4_params; + bool zfsacl_map_dacl_protected; + bool zfsacl_denymissingspecial; + bool zfsacl_block_special; +}; + /* zfs_get_nt_acl() * read the local file's acls and return it in NT form * using the NFSv4 format conversion */ -static NTSTATUS zfs_get_nt_acl(struct files_struct *fsp, uint32 security_info, - struct security_descriptor **ppdesc) +static NTSTATUS zfs_get_nt_acl_common(struct connection_struct *conn, + TALLOC_CTX *mem_ctx, + const struct smb_filename *smb_fname, + const ace_t *acebuf, + int naces, + struct SMB4ACL_T **ppacl, + struct zfsacl_config_data *config) { - int naces, i; - ace_t *acebuf; - SMB4ACL_T *pacl; - TALLOC_CTX *mem_ctx; + int i; + struct SMB4ACL_T *pacl; + SMB_STRUCT_STAT sbuf; + const SMB_STRUCT_STAT *psbuf = NULL; + int ret; + bool inherited_is_present = false; + bool is_dir; - /* read the number of file aces */ - if((naces = acl(fsp->fsp_name, ACE_GETACLCNT, 0, NULL)) == -1) { - if(errno == ENOSYS) { - DEBUG(9, ("acl(ACE_GETACLCNT, %s): Operation is not supported on the filesystem where the file reside")); - } else { - DEBUG(9, ("acl(ACE_GETACLCNT, %s): %s ", fsp->fsp_name, - strerror(errno))); + if (VALID_STAT(smb_fname->st)) { + psbuf = &smb_fname->st; + } + + if (psbuf == NULL) { + ret = vfs_stat_smb_basename(conn, smb_fname, &sbuf); + if (ret != 0) { + DBG_INFO("stat [%s]failed: %s\n", + smb_fname_str_dbg(smb_fname), strerror(errno)); + return map_nt_error_from_unix(errno); } - return map_nt_error_from_unix(errno); + psbuf = &sbuf; } - /* allocate the field of ZFS aces */ + is_dir = S_ISDIR(psbuf->st_ex_mode); + mem_ctx = talloc_tos(); - acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces); - if(acebuf == NULL) { + + /* create SMB4ACL data */ + if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) { return NT_STATUS_NO_MEMORY; } - /* read the aces into the field */ - if(acl(fsp->fsp_name, ACE_GETACL, naces, acebuf) < 0) { - DEBUG(9, ("acl(ACE_GETACL, %s): %s ", fsp->fsp_name, - strerror(errno))); - return map_nt_error_from_unix(errno); - } - /* create SMB4ACL data */ - if((pacl = smb_create_smb4acl()) == NULL) return 0; for(i=0; izfsacl_block_special && + (aceprop.aceMask == 0) && + (aceprop.aceFlags & ACE_EVERYONE) && + (aceprop.aceFlags & ACE_INHERITED_ACE)) + { + continue; + } + /* + * Windows clients expect SYNC on acls to correctly allow + * rename, cf bug #7909. But not on DENY ace entries, cf bug + * #8442. + */ + if (aceprop.aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) { + aceprop.aceMask |= SMB_ACE4_SYNCHRONIZE; + } + + special = acebuf[i].a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE); - if(aceprop.aceFlags & ACE_OWNER) { + if (is_dir && + (aceprop.aceMask & SMB_ACE4_ADD_FILE) && + (special != 0)) + { + aceprop.aceMask |= SMB_ACE4_DELETE_CHILD; + } + +#ifdef ACE_INHERITED_ACE + if (aceprop.aceFlags & ACE_INHERITED_ACE) { + inherited_is_present = true; + } +#endif + switch(special) { + case(ACE_OWNER): aceprop.flags = SMB_ACE4_ID_SPECIAL; aceprop.who.special_id = SMB_ACE4_WHO_OWNER; - } else if(aceprop.aceFlags & ACE_GROUP) { + break; + case(ACE_GROUP): aceprop.flags = SMB_ACE4_ID_SPECIAL; aceprop.who.special_id = SMB_ACE4_WHO_GROUP; - } else if(aceprop.aceFlags & ACE_EVERYONE) { + break; + case(ACE_EVERYONE): aceprop.flags = SMB_ACE4_ID_SPECIAL; aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE; - } else { + break; + default: aceprop.flags = 0; } - if(smb_add_ace4(pacl, &aceprop) == NULL) + if (smb_add_ace4(pacl, &aceprop) == NULL) { return NT_STATUS_NO_MEMORY; + } } - return smb_get_nt_acl_nfs4(fsp, security_info, ppdesc, pacl); +#ifdef ACE_INHERITED_ACE + if (!inherited_is_present && config->zfsacl_map_dacl_protected) { + DBG_DEBUG("Setting SEC_DESC_DACL_PROTECTED on [%s]\n", + smb_fname_str_dbg(smb_fname)); + smbacl4_set_controlflags(pacl, + SEC_DESC_DACL_PROTECTED | + SEC_DESC_SELF_RELATIVE); + } +#endif + *ppacl = pacl; + return NT_STATUS_OK; } /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */ -static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) +static bool zfs_process_smbacl(vfs_handle_struct *handle, files_struct *fsp, + struct SMB4ACL_T *smbacl) { - int naces = smb_get_naces(smbacl), i; + int naces = smb_get_naces(smbacl), i, rv; ace_t *acebuf; - SMB4ACE_T *smbace; + struct SMB4ACE_T *smbace; TALLOC_CTX *mem_ctx; + bool have_special_id = false; + bool must_add_empty_ace = false; + struct zfsacl_config_data *config = NULL; + int fd; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct zfsacl_config_data, + return False); + if (config->zfsacl_block_special && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) { + naces++; + must_add_empty_ace = true; + } /* allocate the field of ZFS aces */ mem_ctx = talloc_tos(); acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces); @@ -117,6 +194,9 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) acebuf[i].a_type = aceprop->aceType; acebuf[i].a_flags = aceprop->aceFlags; acebuf[i].a_access_mask = aceprop->aceMask; + /* SYNC on acls is a no-op on ZFS. + See bug #7909. */ + acebuf[i].a_access_mask &= ~SMB_ACE4_SYNCHRONIZE; acebuf[i].a_who = aceprop->who.id; if(aceprop->flags & SMB_ACE4_ID_SPECIAL) { switch(aceprop->who.special_id) { @@ -127,26 +207,50 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) acebuf[i].a_flags |= ACE_OWNER; break; case SMB_ACE4_WHO_GROUP: - acebuf[i].a_flags |= ACE_GROUP; + acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP; break; default: DEBUG(8, ("unsupported special_id %d\n", \ aceprop->who.special_id)); continue; /* don't add it !!! */ } + have_special_id = true; } } + if (must_add_empty_ace) { + acebuf[i].a_type = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE; + acebuf[i].a_flags = SMB_ACE4_DIRECTORY_INHERIT_ACE | + SMB_ACE4_FILE_INHERIT_ACE | + ACE_EVERYONE | + ACE_INHERITED_ACE; + acebuf[i].a_access_mask = 0; + i++; + } + + if (!have_special_id && config->zfsacl_denymissingspecial) { + errno = EACCES; + return false; + } + SMB_ASSERT(i == naces); /* store acl */ - if(acl(fsp->fsp_name, ACE_SETACL, naces, acebuf)) { + fd = fsp_get_pathref_fd(fsp); + if (fd == -1) { + errno = EBADF; + return false; + } + rv = facl(fd, ACE_SETACL, naces, acebuf); + if (rv != 0) { if(errno == ENOSYS) { - DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not supported on the filesystem where the file reside")); + DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not " + "supported on the filesystem where the file " + "resides\n", fsp_str_dbg(fsp))); } else { - DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp->fsp_name, - strerror(errno))); + DEBUG(9, ("acl(ACE_SETACL, %s): %s\n", fsp_str_dbg(fsp), + strerror(errno))); } - return 0; + return false; } return True; @@ -157,77 +261,247 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) * using the NFSv4 format conversion */ static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, - uint32 security_info_sent, - struct security_descriptor *psd) + uint32_t security_info_sent, + const struct security_descriptor *psd) { - return smb_set_nt_acl_nfs4(fsp, security_info_sent, psd, - zfs_process_smbacl); + struct zfsacl_config_data *config = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct zfsacl_config_data, + return NT_STATUS_INTERNAL_ERROR); + + return smb_set_nt_acl_nfs4(handle, + fsp, + &config->nfs4_params, + security_info_sent, + psd, + zfs_process_smbacl); } -static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle, - struct files_struct *fsp, - int fd, uint32 security_info, - struct security_descriptor **ppdesc) +static int fget_zfsacl(TALLOC_CTX *mem_ctx, + struct files_struct *fsp, + ace_t **outbuf) { - return zfs_get_nt_acl(fsp, security_info, ppdesc); + int naces, rv; + ace_t *acebuf = NULL; + int fd; + + fd = fsp_get_pathref_fd(fsp); + if (fd == -1) { + errno = EBADF; + return -1; + } + naces = facl(fd, ACE_GETACLCNT, 0, NULL); + if (naces == -1) { + int dbg_level = 10; + + if (errno == ENOSYS) { + dbg_level = 1; + } + DEBUG(dbg_level, ("facl(ACE_GETACLCNT, %s): %s\n", + fsp_str_dbg(fsp), strerror(errno))); + return naces; + } + + acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces); + if (acebuf == NULL) { + errno = ENOMEM; + return -1; + } + + rv = facl(fd, ACE_GETACL, naces, acebuf); + if (rv == -1) { + DBG_DEBUG("acl(ACE_GETACL, %s): %s\n", + fsp_str_dbg(fsp), strerror(errno)); + return -1; + } + + *outbuf = acebuf; + return naces; } -static NTSTATUS zfsacl_get_nt_acl(struct vfs_handle_struct *handle, - struct files_struct *fsp, - const char *name, uint32 security_info, - struct security_descriptor **ppdesc) +static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle, + struct files_struct *fsp, + uint32_t security_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **ppdesc) { - return zfs_get_nt_acl(fsp, security_info, ppdesc); + TALLOC_CTX *frame = NULL; + struct SMB4ACL_T *pacl; + NTSTATUS status; + struct zfsacl_config_data *config = NULL; + ace_t *acebuf = NULL; + int naces; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct zfsacl_config_data, + return NT_STATUS_INTERNAL_ERROR); + + frame = talloc_stackframe(); + + naces = fget_zfsacl(talloc_tos(), fsp, &acebuf); + if (naces == -1) { + status = map_nt_error_from_unix(errno); + TALLOC_FREE(frame); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + return status; + } + + status = make_default_filesystem_acl(mem_ctx, + DEFAULT_ACL_POSIX, + fsp->fsp_name->base_name, + &fsp->fsp_name->st, + ppdesc); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED; + return NT_STATUS_OK; + } + + status = zfs_get_nt_acl_common(handle->conn, + frame, + fsp->fsp_name, + acebuf, + naces, + &pacl, + config); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx, + ppdesc, pacl); + TALLOC_FREE(frame); + return status; } static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, - int fd, uint32 security_info_sent, - SEC_DESC *psd) + uint32_t security_info_sent, + const struct security_descriptor *psd) { return zfs_set_nt_acl(handle, fsp, security_info_sent, psd); } -static NTSTATUS zfsacl_set_nt_acl(vfs_handle_struct *handle, - files_struct *fsp, - const char *name, uint32 security_info_sent, - SEC_DESC *psd) +/* nils.goroll@hamburg.de 2008-06-16 : + + See also + - https://bugzilla.samba.org/show_bug.cgi?id=5446 + - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240 + + Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2) + with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for + use by samba in this module. + + As the acl(2) interface is identical for ZFS and for NFS, this module, + vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4 + mounts on Solaris. + + But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL + / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client + implements a compatibility wrapper, which will make calls to + traditional ACL calls though vfs_solarisacl succeed. As the + compatibility wrapper's implementation is (by design) incomplete, + we want to make sure that it is never being called. + + As long as Samba does not support an explicit method for a module + to define conflicting vfs methods, we should override all conflicting + methods here. + + For this to work, we need to make sure that this module is initialised + *after* vfs_solarisacl + + Function declarations taken from vfs_solarisacl +*/ + +static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) { - return zfs_set_nt_acl(handle, fsp, security_info_sent, psd); + return (SMB_ACL_T)NULL; } -/* VFS operations structure */ +static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T theacl) +{ + return -1; +} -static vfs_op_tuple zfsacl_ops[] = { - {SMB_VFS_OP(zfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, - SMB_VFS_LAYER_OPAQUE}, - {SMB_VFS_OP(zfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, - SMB_VFS_LAYER_OPAQUE}, - {SMB_VFS_OP(zfsacl_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, - SMB_VFS_LAYER_OPAQUE}, - {SMB_VFS_OP(zfsacl_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, - SMB_VFS_LAYER_OPAQUE}, - {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} -}; +static int zfsacl_fail__sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ + return -1; +} + +static int zfsacl_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, files_struct *fsp, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob) +{ + return -1; +} -/* != 0 if this module will be compiled as static */ +static int zfsacl_connect(struct vfs_handle_struct *handle, + const char *service, const char *user) +{ + struct zfsacl_config_data *config = NULL; + int ret; -#define STATIC 0 + ret = SMB_VFS_NEXT_CONNECT(handle, service, user); + if (ret < 0) { + return ret; + } -#if STATIC -NTSTATUS vfs_zfsacl_init(void); -#else -NTSTATUS init_module(void); -#endif + config = talloc_zero(handle->conn, struct zfsacl_config_data); + if (!config) { + DBG_ERR("talloc_zero() failed\n"); + errno = ENOMEM; + return -1; + } -NTSTATUS -#if STATIC - vfs_zfsacl_init -#else - init_module -#endif - (void) + config->zfsacl_map_dacl_protected = lp_parm_bool(SNUM(handle->conn), + "zfsacl", "map_dacl_protected", false); + + config->zfsacl_denymissingspecial = lp_parm_bool(SNUM(handle->conn), + "zfsacl", "denymissingspecial", false); + + config->zfsacl_block_special = lp_parm_bool(SNUM(handle->conn), + "zfsacl", "block_special", true); + + ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params); + if (ret < 0) { + TALLOC_FREE(config); + return ret; + } + + SMB_VFS_HANDLE_SET_DATA(handle, config, + NULL, struct zfsacl_config_data, + return -1); + + return 0; +} + +/* VFS operations structure */ + +static struct vfs_fn_pointers zfsacl_fns = { + .connect_fn = zfsacl_connect, + .stat_fn = nfs4_acl_stat, + .fstat_fn = nfs4_acl_fstat, + .lstat_fn = nfs4_acl_lstat, + .fstatat_fn = nfs4_acl_fstatat, + .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd, + .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd, + .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd, + .sys_acl_delete_def_fd_fn = zfsacl_fail__sys_acl_delete_def_fd, + .fget_nt_acl_fn = zfsacl_fget_nt_acl, + .fset_nt_acl_fn = zfsacl_fset_nt_acl, +}; + +static_decl_vfs; +NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx) { return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl", - zfsacl_ops); + &zfsacl_fns); }