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 SMB_ACE4PROP_T blocking_ace;
62 const SMB_STRUCT_STAT *psbuf = NULL;
64 bool inherited_is_present = false;
67 if (VALID_STAT(smb_fname->st)) {
68 psbuf = &smb_fname->st;
72 ret = vfs_stat_smb_basename(conn, smb_fname, &sbuf);
74 DBG_INFO("stat [%s]failed: %s\n",
75 smb_fname_str_dbg(smb_fname), strerror(errno));
76 return map_nt_error_from_unix(errno);
80 is_dir = S_ISDIR(psbuf->st_ex_mode);
82 mem_ctx = talloc_tos();
84 /* create SMB4ACL data */
85 if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
86 return NT_STATUS_NO_MEMORY;
88 for(i=0; i<naces; i++) {
89 SMB_ACE4PROP_T aceprop;
92 aceprop.aceType = (uint32_t) acebuf[i].a_type;
93 aceprop.aceFlags = (uint32_t) acebuf[i].a_flags;
94 aceprop.aceMask = (uint32_t) acebuf[i].a_access_mask;
95 aceprop.who.id = (uint32_t) acebuf[i].a_who;
97 if (config->zfsacl_block_special &&
98 (aceprop.aceMask == 0) &&
99 (aceprop.aceFlags & ACE_EVERYONE) &&
100 (aceprop.aceFlags & ACE_INHERITED_ACE))
105 * Windows clients expect SYNC on acls to correctly allow
106 * rename, cf bug #7909. But not on DENY ace entries, cf bug
109 if (aceprop.aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) {
110 aceprop.aceMask |= SMB_ACE4_SYNCHRONIZE;
113 special = acebuf[i].a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE);
116 (aceprop.aceMask & SMB_ACE4_ADD_FILE) &&
119 aceprop.aceMask |= SMB_ACE4_DELETE_CHILD;
122 #ifdef ACE_INHERITED_ACE
123 if (aceprop.aceFlags & ACE_INHERITED_ACE) {
124 inherited_is_present = true;
129 aceprop.flags = SMB_ACE4_ID_SPECIAL;
130 aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
133 aceprop.flags = SMB_ACE4_ID_SPECIAL;
134 aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
137 aceprop.flags = SMB_ACE4_ID_SPECIAL;
138 aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
143 if (smb_add_ace4(pacl, &aceprop) == NULL) {
144 return NT_STATUS_NO_MEMORY;
148 #ifdef ACE_INHERITED_ACE
149 if (!inherited_is_present && config->zfsacl_map_dacl_protected) {
150 DBG_DEBUG("Setting SEC_DESC_DACL_PROTECTED on [%s]\n",
151 smb_fname_str_dbg(smb_fname));
152 smbacl4_set_controlflags(pacl,
153 SEC_DESC_DACL_PROTECTED |
154 SEC_DESC_SELF_RELATIVE);
161 /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
162 static bool zfs_process_smbacl(vfs_handle_struct *handle, files_struct *fsp,
163 struct SMB4ACL_T *smbacl)
165 int naces = smb_get_naces(smbacl), i, rv;
167 struct SMB4ACE_T *smbace;
169 bool have_special_id = false;
170 bool must_add_empty_ace = false;
171 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 if (fsp->fh->fd != -1) {
239 rv = facl(fsp->fh->fd, ACE_SETACL, naces, acebuf);
242 rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf);
245 if(errno == ENOSYS) {
246 DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
247 "supported on the filesystem where the file "
248 "reside", fsp_str_dbg(fsp)));
250 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
260 * set the local file's acls obtaining it in NT form
261 * using the NFSv4 format conversion
263 static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
264 uint32_t security_info_sent,
265 const struct security_descriptor *psd)
267 struct zfsacl_config_data *config = NULL;
269 SMB_VFS_HANDLE_GET_DATA(handle, config,
270 struct zfsacl_config_data,
271 return NT_STATUS_INTERNAL_ERROR);
273 return smb_set_nt_acl_nfs4(handle,
275 &config->nfs4_params,
281 static int get_zfsacl(TALLOC_CTX *mem_ctx,
282 const struct smb_filename *smb_fname,
286 ace_t *acebuf = NULL;
288 naces = acl(smb_fname->base_name, ACE_GETACLCNT, 0, NULL);
292 if (errno == ENOSYS) {
295 DEBUG(dbg_level, ("acl(ACE_GETACLCNT, %s): %s ",
296 smb_fname->base_name, strerror(errno)));
299 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
300 if (acebuf == NULL) {
305 rv = acl(smb_fname->base_name, ACE_GETACL, naces, acebuf);
307 DBG_DEBUG("acl(ACE_GETACL, %s) failed: %s ",
308 smb_fname->base_name, strerror(errno));
316 static int fget_zfsacl(TALLOC_CTX *mem_ctx,
317 struct files_struct *fsp,
321 ace_t *acebuf = NULL;
323 if (fsp->fh->fd == -1) {
324 return get_zfsacl(mem_ctx, fsp->fsp_name, outbuf);
327 naces = facl(fsp->fh->fd, ACE_GETACLCNT, 0, NULL);
331 if (errno == ENOSYS) {
334 DEBUG(dbg_level, ("facl(ACE_GETACLCNT, %s): %s ",
335 fsp_str_dbg(fsp), strerror(errno)));
339 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
340 if (acebuf == NULL) {
345 rv = facl(fsp->fh->fd, ACE_GETACL, naces, acebuf);
347 DBG_DEBUG("acl(ACE_GETACL, %s): %s ",
348 fsp_str_dbg(fsp), strerror(errno));
356 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
357 struct files_struct *fsp,
358 uint32_t security_info,
360 struct security_descriptor **ppdesc)
362 struct SMB4ACL_T *pacl;
364 struct zfsacl_config_data *config = NULL;
365 ace_t *acebuf = NULL;
368 SMB_VFS_HANDLE_GET_DATA(handle, config,
369 struct zfsacl_config_data,
370 return NT_STATUS_INTERNAL_ERROR);
372 TALLOC_CTX *frame = talloc_stackframe();
374 naces = fget_zfsacl(talloc_tos(), fsp, &acebuf);
376 status = map_nt_error_from_unix(errno);
378 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
382 status = make_default_filesystem_acl(mem_ctx,
384 fsp->fsp_name->base_name,
387 if (!NT_STATUS_IS_OK(status)) {
390 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
394 status = zfs_get_nt_acl_common(handle->conn,
401 if (!NT_STATUS_IS_OK(status)) {
406 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
412 static NTSTATUS zfsacl_get_nt_acl_at(struct vfs_handle_struct *handle,
413 struct files_struct *dirfsp,
414 const struct smb_filename *smb_fname,
415 uint32_t security_info,
417 struct security_descriptor **ppdesc)
419 struct SMB4ACL_T *pacl = NULL;
421 struct zfsacl_config_data *config = NULL;
422 TALLOC_CTX *frame = NULL;
424 ace_t *acebuf = NULL;
426 SMB_ASSERT(dirfsp == handle->conn->cwd_fsp);
428 SMB_VFS_HANDLE_GET_DATA(handle,
430 struct zfsacl_config_data,
431 return NT_STATUS_INTERNAL_ERROR);
433 frame = talloc_stackframe();
435 naces = get_zfsacl(frame, smb_fname, &acebuf);
437 status = map_nt_error_from_unix(errno);
439 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
443 if (!VALID_STAT(smb_fname->st)) {
444 DBG_ERR("No stat info for [%s]\n",
445 smb_fname_str_dbg(smb_fname));
446 return NT_STATUS_INTERNAL_ERROR;
449 status = make_default_filesystem_acl(mem_ctx,
451 smb_fname->base_name,
454 if (!NT_STATUS_IS_OK(status)) {
457 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
461 status = zfs_get_nt_acl_common(handle->conn,
468 if (!NT_STATUS_IS_OK(status)) {
473 status = smb_get_nt_acl_nfs4(handle->conn,
484 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
486 uint32_t security_info_sent,
487 const struct security_descriptor *psd)
489 return zfs_set_nt_acl(handle, fsp, security_info_sent, psd);
492 /* nils.goroll@hamburg.de 2008-06-16 :
495 - https://bugzilla.samba.org/show_bug.cgi?id=5446
496 - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
498 Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
499 with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
500 use by samba in this module.
502 As the acl(2) interface is identical for ZFS and for NFS, this module,
503 vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
506 But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
507 / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
508 implements a compatibility wrapper, which will make calls to
509 traditional ACL calls though vfs_solarisacl succeed. As the
510 compatibility wrapper's implementation is (by design) incomplete,
511 we want to make sure that it is never being called.
513 As long as Samba does not support an explicit method for a module
514 to define conflicting vfs methods, we should override all conflicting
517 For this to work, we need to make sure that this module is initialised
518 *after* vfs_solarisacl
520 Function declarations taken from vfs_solarisacl
523 static SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle,
524 const struct smb_filename *smb_fname,
528 return (SMB_ACL_T)NULL;
531 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
535 return (SMB_ACL_T)NULL;
538 static int zfsacl_fail__sys_acl_set_file(vfs_handle_struct *handle,
539 const struct smb_filename *smb_fname,
546 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
553 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
554 const struct smb_filename *smb_fname)
559 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
560 const struct smb_filename *smb_fname,
562 char **blob_description,
568 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)
573 static int zfsacl_connect(struct vfs_handle_struct *handle,
574 const char *service, const char *user)
576 struct zfsacl_config_data *config = NULL;
579 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
584 config = talloc_zero(handle->conn, struct zfsacl_config_data);
586 DBG_ERR("talloc_zero() failed\n");
591 config->zfsacl_map_dacl_protected = lp_parm_bool(SNUM(handle->conn),
592 "zfsacl", "map_dacl_protected", false);
594 config->zfsacl_denymissingspecial = lp_parm_bool(SNUM(handle->conn),
595 "zfsacl", "denymissingspecial", false);
597 config->zfsacl_block_special = lp_parm_bool(SNUM(handle->conn),
598 "zfsacl", "block_special", true);
600 ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
606 SMB_VFS_HANDLE_SET_DATA(handle, config,
607 NULL, struct zfsacl_config_data,
613 /* VFS operations structure */
615 static struct vfs_fn_pointers zfsacl_fns = {
616 .connect_fn = zfsacl_connect,
617 .sys_acl_get_file_fn = zfsacl_fail__sys_acl_get_file,
618 .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd,
619 .sys_acl_blob_get_file_fn = zfsacl_fail__sys_acl_blob_get_file,
620 .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd,
621 .sys_acl_set_file_fn = zfsacl_fail__sys_acl_set_file,
622 .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd,
623 .sys_acl_delete_def_file_fn = zfsacl_fail__sys_acl_delete_def_file,
624 .fget_nt_acl_fn = zfsacl_fget_nt_acl,
625 .get_nt_acl_at_fn = zfsacl_get_nt_acl_at,
626 .fset_nt_acl_fn = zfsacl_fset_nt_acl,
630 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx)
632 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl",