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;
173 SMB_VFS_HANDLE_GET_DATA(handle, config,
174 struct zfsacl_config_data,
177 if (config->zfsacl_block_special && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
179 must_add_empty_ace = true;
181 /* allocate the field of ZFS aces */
182 mem_ctx = talloc_tos();
183 acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
188 /* handle all aces */
189 for(smbace = smb_first_ace4(smbacl), i = 0;
191 smbace = smb_next_ace4(smbace), i++) {
192 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
194 acebuf[i].a_type = aceprop->aceType;
195 acebuf[i].a_flags = aceprop->aceFlags;
196 acebuf[i].a_access_mask = aceprop->aceMask;
197 /* SYNC on acls is a no-op on ZFS.
199 acebuf[i].a_access_mask &= ~SMB_ACE4_SYNCHRONIZE;
200 acebuf[i].a_who = aceprop->who.id;
201 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
202 switch(aceprop->who.special_id) {
203 case SMB_ACE4_WHO_EVERYONE:
204 acebuf[i].a_flags |= ACE_EVERYONE;
206 case SMB_ACE4_WHO_OWNER:
207 acebuf[i].a_flags |= ACE_OWNER;
209 case SMB_ACE4_WHO_GROUP:
210 acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP;
213 DEBUG(8, ("unsupported special_id %d\n", \
214 aceprop->who.special_id));
215 continue; /* don't add it !!! */
217 have_special_id = true;
220 if (must_add_empty_ace) {
221 acebuf[i].a_type = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
222 acebuf[i].a_flags = SMB_ACE4_DIRECTORY_INHERIT_ACE |
223 SMB_ACE4_FILE_INHERIT_ACE |
226 acebuf[i].a_access_mask = 0;
230 if (!have_special_id && config->zfsacl_denymissingspecial) {
235 SMB_ASSERT(i == naces);
238 fd = fsp_get_io_fd(fsp);
240 rv = facl(fd, ACE_SETACL, naces, acebuf);
243 rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf);
246 if(errno == ENOSYS) {
247 DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
248 "supported on the filesystem where the file "
249 "reside", fsp_str_dbg(fsp)));
251 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
261 * set the local file's acls obtaining it in NT form
262 * using the NFSv4 format conversion
264 static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
265 uint32_t security_info_sent,
266 const struct security_descriptor *psd)
268 struct zfsacl_config_data *config = NULL;
270 SMB_VFS_HANDLE_GET_DATA(handle, config,
271 struct zfsacl_config_data,
272 return NT_STATUS_INTERNAL_ERROR);
274 return smb_set_nt_acl_nfs4(handle,
276 &config->nfs4_params,
282 static int get_zfsacl(TALLOC_CTX *mem_ctx,
283 const struct smb_filename *smb_fname,
287 ace_t *acebuf = NULL;
289 naces = acl(smb_fname->base_name, ACE_GETACLCNT, 0, NULL);
293 if (errno == ENOSYS) {
296 DEBUG(dbg_level, ("acl(ACE_GETACLCNT, %s): %s ",
297 smb_fname->base_name, strerror(errno)));
300 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
301 if (acebuf == NULL) {
306 rv = acl(smb_fname->base_name, ACE_GETACL, naces, acebuf);
308 DBG_DEBUG("acl(ACE_GETACL, %s) failed: %s ",
309 smb_fname->base_name, strerror(errno));
317 static int fget_zfsacl(TALLOC_CTX *mem_ctx,
318 struct files_struct *fsp,
322 ace_t *acebuf = NULL;
325 fd = fsp_get_io_fd(fsp);
327 return get_zfsacl(mem_ctx, fsp->fsp_name, outbuf);
330 naces = facl(fd, ACE_GETACLCNT, 0, NULL);
334 if (errno == ENOSYS) {
337 DEBUG(dbg_level, ("facl(ACE_GETACLCNT, %s): %s ",
338 fsp_str_dbg(fsp), strerror(errno)));
342 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
343 if (acebuf == NULL) {
348 rv = facl(fd, ACE_GETACL, naces, acebuf);
350 DBG_DEBUG("acl(ACE_GETACL, %s): %s ",
351 fsp_str_dbg(fsp), strerror(errno));
359 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
360 struct files_struct *fsp,
361 uint32_t security_info,
363 struct security_descriptor **ppdesc)
365 struct SMB4ACL_T *pacl;
367 struct zfsacl_config_data *config = NULL;
368 ace_t *acebuf = NULL;
371 SMB_VFS_HANDLE_GET_DATA(handle, config,
372 struct zfsacl_config_data,
373 return NT_STATUS_INTERNAL_ERROR);
375 TALLOC_CTX *frame = talloc_stackframe();
377 naces = fget_zfsacl(talloc_tos(), fsp, &acebuf);
379 status = map_nt_error_from_unix(errno);
381 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
385 status = make_default_filesystem_acl(mem_ctx,
387 fsp->fsp_name->base_name,
390 if (!NT_STATUS_IS_OK(status)) {
393 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
397 status = zfs_get_nt_acl_common(handle->conn,
404 if (!NT_STATUS_IS_OK(status)) {
409 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
415 static NTSTATUS zfsacl_get_nt_acl_at(struct vfs_handle_struct *handle,
416 struct files_struct *dirfsp,
417 const struct smb_filename *smb_fname,
418 uint32_t security_info,
420 struct security_descriptor **ppdesc)
422 struct SMB4ACL_T *pacl = NULL;
424 struct zfsacl_config_data *config = NULL;
425 TALLOC_CTX *frame = NULL;
427 ace_t *acebuf = NULL;
429 SMB_ASSERT(dirfsp == handle->conn->cwd_fsp);
431 SMB_VFS_HANDLE_GET_DATA(handle,
433 struct zfsacl_config_data,
434 return NT_STATUS_INTERNAL_ERROR);
436 frame = talloc_stackframe();
438 naces = get_zfsacl(frame, smb_fname, &acebuf);
440 status = map_nt_error_from_unix(errno);
442 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
446 if (!VALID_STAT(smb_fname->st)) {
447 DBG_ERR("No stat info for [%s]\n",
448 smb_fname_str_dbg(smb_fname));
449 return NT_STATUS_INTERNAL_ERROR;
452 status = make_default_filesystem_acl(mem_ctx,
454 smb_fname->base_name,
457 if (!NT_STATUS_IS_OK(status)) {
460 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
464 status = zfs_get_nt_acl_common(handle->conn,
471 if (!NT_STATUS_IS_OK(status)) {
476 status = smb_get_nt_acl_nfs4(handle->conn,
487 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
489 uint32_t security_info_sent,
490 const struct security_descriptor *psd)
492 return zfs_set_nt_acl(handle, fsp, security_info_sent, psd);
495 /* nils.goroll@hamburg.de 2008-06-16 :
498 - https://bugzilla.samba.org/show_bug.cgi?id=5446
499 - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
501 Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
502 with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
503 use by samba in this module.
505 As the acl(2) interface is identical for ZFS and for NFS, this module,
506 vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
509 But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
510 / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
511 implements a compatibility wrapper, which will make calls to
512 traditional ACL calls though vfs_solarisacl succeed. As the
513 compatibility wrapper's implementation is (by design) incomplete,
514 we want to make sure that it is never being called.
516 As long as Samba does not support an explicit method for a module
517 to define conflicting vfs methods, we should override all conflicting
520 For this to work, we need to make sure that this module is initialised
521 *after* vfs_solarisacl
523 Function declarations taken from vfs_solarisacl
526 static SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle,
527 const struct smb_filename *smb_fname,
531 return (SMB_ACL_T)NULL;
534 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
538 return (SMB_ACL_T)NULL;
541 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
549 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
550 const struct smb_filename *smb_fname)
555 static int zfsacl_fail__sys_acl_delete_def_fd(vfs_handle_struct *handle,
561 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
562 const struct smb_filename *smb_fname,
564 char **blob_description,
570 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)
575 static int zfsacl_connect(struct vfs_handle_struct *handle,
576 const char *service, const char *user)
578 struct zfsacl_config_data *config = NULL;
581 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
586 config = talloc_zero(handle->conn, struct zfsacl_config_data);
588 DBG_ERR("talloc_zero() failed\n");
593 config->zfsacl_map_dacl_protected = lp_parm_bool(SNUM(handle->conn),
594 "zfsacl", "map_dacl_protected", false);
596 config->zfsacl_denymissingspecial = lp_parm_bool(SNUM(handle->conn),
597 "zfsacl", "denymissingspecial", false);
599 config->zfsacl_block_special = lp_parm_bool(SNUM(handle->conn),
600 "zfsacl", "block_special", true);
602 ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
608 SMB_VFS_HANDLE_SET_DATA(handle, config,
609 NULL, struct zfsacl_config_data,
615 /* VFS operations structure */
617 static struct vfs_fn_pointers zfsacl_fns = {
618 .connect_fn = zfsacl_connect,
619 .sys_acl_get_file_fn = zfsacl_fail__sys_acl_get_file,
620 .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd,
621 .sys_acl_blob_get_file_fn = zfsacl_fail__sys_acl_blob_get_file,
622 .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd,
623 .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd,
624 .sys_acl_delete_def_file_fn = zfsacl_fail__sys_acl_delete_def_file,
625 .sys_acl_delete_def_fd_fn = zfsacl_fail__sys_acl_delete_def_fd,
626 .fget_nt_acl_fn = zfsacl_fget_nt_acl,
627 .get_nt_acl_at_fn = zfsacl_get_nt_acl_at,
628 .fset_nt_acl_fn = zfsacl_fset_nt_acl,
632 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx)
634 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl",