2 * Convert ZFS/NFSv4 acls to NT acls and vice versa.
4 * Copyright (C) Jiri Sasek, 2007
5 * based on the foobar.c module which is copyrighted by Volker Lendecke
7 * Many thanks to Axel Apitz for help to fix the special ace's handling
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "nfs4_acls.h"
30 #ifdef HAVE_FREEBSD_SUNACL_H
35 #define DBGC_CLASS DBGC_VFS
37 #define ZFSACL_MODULE_NAME "zfsacl"
39 struct zfsacl_config_data {
40 struct smbacl4_vfs_params nfs4_params;
41 bool zfsacl_map_dacl_protected;
42 bool zfsacl_denymissingspecial;
43 bool zfsacl_block_special;
47 * read the local file's acls and return it in NT form
48 * using the NFSv4 format conversion
50 static NTSTATUS zfs_get_nt_acl_common(struct connection_struct *conn,
52 const struct smb_filename *smb_fname,
55 struct SMB4ACL_T **ppacl,
56 struct zfsacl_config_data *config)
59 struct SMB4ACL_T *pacl;
61 const SMB_STRUCT_STAT *psbuf = NULL;
63 bool inherited_is_present = false;
66 if (VALID_STAT(smb_fname->st)) {
67 psbuf = &smb_fname->st;
71 ret = vfs_stat_smb_basename(conn, smb_fname, &sbuf);
73 DBG_INFO("stat [%s]failed: %s\n",
74 smb_fname_str_dbg(smb_fname), strerror(errno));
75 return map_nt_error_from_unix(errno);
79 is_dir = S_ISDIR(psbuf->st_ex_mode);
81 mem_ctx = talloc_tos();
83 /* create SMB4ACL data */
84 if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
85 return NT_STATUS_NO_MEMORY;
87 for(i=0; i<naces; i++) {
88 SMB_ACE4PROP_T aceprop;
91 aceprop.aceType = (uint32_t) acebuf[i].a_type;
92 aceprop.aceFlags = (uint32_t) acebuf[i].a_flags;
93 aceprop.aceMask = (uint32_t) acebuf[i].a_access_mask;
94 aceprop.who.id = (uint32_t) acebuf[i].a_who;
96 if (config->zfsacl_block_special &&
97 (aceprop.aceMask == 0) &&
98 (aceprop.aceFlags & ACE_EVERYONE) &&
99 (aceprop.aceFlags & ACE_INHERITED_ACE))
104 * Windows clients expect SYNC on acls to correctly allow
105 * rename, cf bug #7909. But not on DENY ace entries, cf bug
108 if (aceprop.aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) {
109 aceprop.aceMask |= SMB_ACE4_SYNCHRONIZE;
112 special = acebuf[i].a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE);
115 (aceprop.aceMask & SMB_ACE4_ADD_FILE) &&
118 aceprop.aceMask |= SMB_ACE4_DELETE_CHILD;
121 #ifdef ACE_INHERITED_ACE
122 if (aceprop.aceFlags & ACE_INHERITED_ACE) {
123 inherited_is_present = true;
128 aceprop.flags = SMB_ACE4_ID_SPECIAL;
129 aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
132 aceprop.flags = SMB_ACE4_ID_SPECIAL;
133 aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
136 aceprop.flags = SMB_ACE4_ID_SPECIAL;
137 aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
142 if (smb_add_ace4(pacl, &aceprop) == NULL) {
143 return NT_STATUS_NO_MEMORY;
147 #ifdef ACE_INHERITED_ACE
148 if (!inherited_is_present && config->zfsacl_map_dacl_protected) {
149 DBG_DEBUG("Setting SEC_DESC_DACL_PROTECTED on [%s]\n",
150 smb_fname_str_dbg(smb_fname));
151 smbacl4_set_controlflags(pacl,
152 SEC_DESC_DACL_PROTECTED |
153 SEC_DESC_SELF_RELATIVE);
160 /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
161 static bool zfs_process_smbacl(vfs_handle_struct *handle, files_struct *fsp,
162 struct SMB4ACL_T *smbacl)
164 int naces = smb_get_naces(smbacl), i, rv;
166 struct SMB4ACE_T *smbace;
168 bool have_special_id = false;
169 bool must_add_empty_ace = false;
170 struct zfsacl_config_data *config = NULL;
172 SMB_VFS_HANDLE_GET_DATA(handle, config,
173 struct zfsacl_config_data,
176 if (config->zfsacl_block_special && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
178 must_add_empty_ace = true;
180 /* allocate the field of ZFS aces */
181 mem_ctx = talloc_tos();
182 acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
187 /* handle all aces */
188 for(smbace = smb_first_ace4(smbacl), i = 0;
190 smbace = smb_next_ace4(smbace), i++) {
191 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
193 acebuf[i].a_type = aceprop->aceType;
194 acebuf[i].a_flags = aceprop->aceFlags;
195 acebuf[i].a_access_mask = aceprop->aceMask;
196 /* SYNC on acls is a no-op on ZFS.
198 acebuf[i].a_access_mask &= ~SMB_ACE4_SYNCHRONIZE;
199 acebuf[i].a_who = aceprop->who.id;
200 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
201 switch(aceprop->who.special_id) {
202 case SMB_ACE4_WHO_EVERYONE:
203 acebuf[i].a_flags |= ACE_EVERYONE;
205 case SMB_ACE4_WHO_OWNER:
206 acebuf[i].a_flags |= ACE_OWNER;
208 case SMB_ACE4_WHO_GROUP:
209 acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP;
212 DEBUG(8, ("unsupported special_id %d\n", \
213 aceprop->who.special_id));
214 continue; /* don't add it !!! */
216 have_special_id = true;
219 if (must_add_empty_ace) {
220 acebuf[i].a_type = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
221 acebuf[i].a_flags = SMB_ACE4_DIRECTORY_INHERIT_ACE |
222 SMB_ACE4_FILE_INHERIT_ACE |
225 acebuf[i].a_access_mask = 0;
229 if (!have_special_id && config->zfsacl_denymissingspecial) {
234 SMB_ASSERT(i == naces);
237 if (fsp->fh->fd != -1) {
238 rv = facl(fsp->fh->fd, ACE_SETACL, naces, acebuf);
241 rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf);
244 if(errno == ENOSYS) {
245 DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
246 "supported on the filesystem where the file "
247 "reside", fsp_str_dbg(fsp)));
249 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
259 * set the local file's acls obtaining it in NT form
260 * using the NFSv4 format conversion
262 static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
263 uint32_t security_info_sent,
264 const struct security_descriptor *psd)
266 struct zfsacl_config_data *config = NULL;
268 SMB_VFS_HANDLE_GET_DATA(handle, config,
269 struct zfsacl_config_data,
270 return NT_STATUS_INTERNAL_ERROR);
272 return smb_set_nt_acl_nfs4(handle,
274 &config->nfs4_params,
280 static int get_zfsacl(TALLOC_CTX *mem_ctx,
281 const struct smb_filename *smb_fname,
285 ace_t *acebuf = NULL;
287 naces = acl(smb_fname->base_name, ACE_GETACLCNT, 0, NULL);
291 if (errno == ENOSYS) {
294 DEBUG(dbg_level, ("acl(ACE_GETACLCNT, %s): %s ",
295 smb_fname->base_name, strerror(errno)));
298 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
299 if (acebuf == NULL) {
304 rv = acl(smb_fname->base_name, ACE_GETACL, naces, acebuf);
306 DBG_DEBUG("acl(ACE_GETACL, %s) failed: %s ",
307 smb_fname->base_name, strerror(errno));
315 static int fget_zfsacl(TALLOC_CTX *mem_ctx,
316 struct files_struct *fsp,
320 ace_t *acebuf = NULL;
322 if (fsp->fh->fd == -1) {
323 return get_zfsacl(mem_ctx, fsp->fsp_name, outbuf);
326 naces = facl(fsp->fh->fd, ACE_GETACLCNT, 0, NULL);
330 if (errno == ENOSYS) {
333 DEBUG(dbg_level, ("facl(ACE_GETACLCNT, %s): %s ",
334 fsp_str_dbg(fsp), strerror(errno)));
338 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
339 if (acebuf == NULL) {
344 rv = facl(fsp->fh->fd, ACE_GETACL, naces, acebuf);
346 DBG_DEBUG("acl(ACE_GETACL, %s): %s ",
347 fsp_str_dbg(fsp), strerror(errno));
355 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
356 struct files_struct *fsp,
357 uint32_t security_info,
359 struct security_descriptor **ppdesc)
361 struct SMB4ACL_T *pacl;
363 struct zfsacl_config_data *config = NULL;
364 ace_t *acebuf = NULL;
367 SMB_VFS_HANDLE_GET_DATA(handle, config,
368 struct zfsacl_config_data,
369 return NT_STATUS_INTERNAL_ERROR);
371 TALLOC_CTX *frame = talloc_stackframe();
373 naces = fget_zfsacl(talloc_tos(), fsp, &acebuf);
375 status = map_nt_error_from_unix(errno);
377 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
381 status = make_default_filesystem_acl(mem_ctx,
383 fsp->fsp_name->base_name,
386 if (!NT_STATUS_IS_OK(status)) {
389 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
393 status = zfs_get_nt_acl_common(handle->conn,
400 if (!NT_STATUS_IS_OK(status)) {
405 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
411 static NTSTATUS zfsacl_get_nt_acl_at(struct vfs_handle_struct *handle,
412 struct files_struct *dirfsp,
413 const struct smb_filename *smb_fname,
414 uint32_t security_info,
416 struct security_descriptor **ppdesc)
418 struct SMB4ACL_T *pacl = NULL;
420 struct zfsacl_config_data *config = NULL;
421 TALLOC_CTX *frame = NULL;
423 ace_t *acebuf = NULL;
425 SMB_ASSERT(dirfsp == handle->conn->cwd_fsp);
427 SMB_VFS_HANDLE_GET_DATA(handle,
429 struct zfsacl_config_data,
430 return NT_STATUS_INTERNAL_ERROR);
432 frame = talloc_stackframe();
434 naces = get_zfsacl(frame, smb_fname, &acebuf);
436 status = map_nt_error_from_unix(errno);
438 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
442 if (!VALID_STAT(smb_fname->st)) {
443 DBG_ERR("No stat info for [%s]\n",
444 smb_fname_str_dbg(smb_fname));
445 return NT_STATUS_INTERNAL_ERROR;
448 status = make_default_filesystem_acl(mem_ctx,
450 smb_fname->base_name,
453 if (!NT_STATUS_IS_OK(status)) {
456 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
460 status = zfs_get_nt_acl_common(handle->conn,
467 if (!NT_STATUS_IS_OK(status)) {
472 status = smb_get_nt_acl_nfs4(handle->conn,
483 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
485 uint32_t security_info_sent,
486 const struct security_descriptor *psd)
488 return zfs_set_nt_acl(handle, fsp, security_info_sent, psd);
491 /* nils.goroll@hamburg.de 2008-06-16 :
494 - https://bugzilla.samba.org/show_bug.cgi?id=5446
495 - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
497 Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
498 with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
499 use by samba in this module.
501 As the acl(2) interface is identical for ZFS and for NFS, this module,
502 vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
505 But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
506 / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
507 implements a compatibility wrapper, which will make calls to
508 traditional ACL calls though vfs_solarisacl succeed. As the
509 compatibility wrapper's implementation is (by design) incomplete,
510 we want to make sure that it is never being called.
512 As long as Samba does not support an explicit method for a module
513 to define conflicting vfs methods, we should override all conflicting
516 For this to work, we need to make sure that this module is initialised
517 *after* vfs_solarisacl
519 Function declarations taken from vfs_solarisacl
522 static SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle,
523 const struct smb_filename *smb_fname,
527 return (SMB_ACL_T)NULL;
530 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
534 return (SMB_ACL_T)NULL;
537 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
545 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
546 const struct smb_filename *smb_fname)
551 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
552 const struct smb_filename *smb_fname,
554 char **blob_description,
560 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)
565 static int zfsacl_connect(struct vfs_handle_struct *handle,
566 const char *service, const char *user)
568 struct zfsacl_config_data *config = NULL;
571 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
576 config = talloc_zero(handle->conn, struct zfsacl_config_data);
578 DBG_ERR("talloc_zero() failed\n");
583 config->zfsacl_map_dacl_protected = lp_parm_bool(SNUM(handle->conn),
584 "zfsacl", "map_dacl_protected", false);
586 config->zfsacl_denymissingspecial = lp_parm_bool(SNUM(handle->conn),
587 "zfsacl", "denymissingspecial", false);
589 config->zfsacl_block_special = lp_parm_bool(SNUM(handle->conn),
590 "zfsacl", "block_special", true);
592 ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
598 SMB_VFS_HANDLE_SET_DATA(handle, config,
599 NULL, struct zfsacl_config_data,
605 /* VFS operations structure */
607 static struct vfs_fn_pointers zfsacl_fns = {
608 .connect_fn = zfsacl_connect,
609 .sys_acl_get_file_fn = zfsacl_fail__sys_acl_get_file,
610 .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd,
611 .sys_acl_blob_get_file_fn = zfsacl_fail__sys_acl_blob_get_file,
612 .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd,
613 .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd,
614 .sys_acl_delete_def_file_fn = zfsacl_fail__sys_acl_delete_def_file,
615 .fget_nt_acl_fn = zfsacl_fget_nt_acl,
616 .get_nt_acl_at_fn = zfsacl_get_nt_acl_at,
617 .fset_nt_acl_fn = zfsacl_fset_nt_acl,
621 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx)
623 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl",