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;
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 if (is_dir && (aceprop.aceMask & SMB_ACE4_ADD_FILE)) {
113 aceprop.aceMask |= SMB_ACE4_DELETE_CHILD;
116 #ifdef ACE_INHERITED_ACE
117 if (aceprop.aceFlags & ACE_INHERITED_ACE) {
118 inherited_is_present = true;
121 if(aceprop.aceFlags & ACE_OWNER) {
122 aceprop.flags = SMB_ACE4_ID_SPECIAL;
123 aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
124 } else if(aceprop.aceFlags & ACE_GROUP) {
125 aceprop.flags = SMB_ACE4_ID_SPECIAL;
126 aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
127 } else if(aceprop.aceFlags & ACE_EVERYONE) {
128 aceprop.flags = SMB_ACE4_ID_SPECIAL;
129 aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
133 if (smb_add_ace4(pacl, &aceprop) == NULL) {
134 return NT_STATUS_NO_MEMORY;
138 #ifdef ACE_INHERITED_ACE
139 if (!inherited_is_present && config->zfsacl_map_dacl_protected) {
140 DBG_DEBUG("Setting SEC_DESC_DACL_PROTECTED on [%s]\n",
141 smb_fname_str_dbg(smb_fname));
142 smbacl4_set_controlflags(pacl,
143 SEC_DESC_DACL_PROTECTED |
144 SEC_DESC_SELF_RELATIVE);
151 /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
152 static bool zfs_process_smbacl(vfs_handle_struct *handle, files_struct *fsp,
153 struct SMB4ACL_T *smbacl)
155 int naces = smb_get_naces(smbacl), i, rv;
157 struct SMB4ACE_T *smbace;
159 bool have_special_id = false;
160 bool must_add_empty_ace = false;
161 struct zfsacl_config_data *config = NULL;
163 SMB_VFS_HANDLE_GET_DATA(handle, config,
164 struct zfsacl_config_data,
167 if (config->zfsacl_block_special && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
169 must_add_empty_ace = true;
171 /* allocate the field of ZFS aces */
172 mem_ctx = talloc_tos();
173 acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
178 /* handle all aces */
179 for(smbace = smb_first_ace4(smbacl), i = 0;
181 smbace = smb_next_ace4(smbace), i++) {
182 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
184 acebuf[i].a_type = aceprop->aceType;
185 acebuf[i].a_flags = aceprop->aceFlags;
186 acebuf[i].a_access_mask = aceprop->aceMask;
187 /* SYNC on acls is a no-op on ZFS.
189 acebuf[i].a_access_mask &= ~SMB_ACE4_SYNCHRONIZE;
190 acebuf[i].a_who = aceprop->who.id;
191 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
192 switch(aceprop->who.special_id) {
193 case SMB_ACE4_WHO_EVERYONE:
194 acebuf[i].a_flags |= ACE_EVERYONE;
196 case SMB_ACE4_WHO_OWNER:
197 acebuf[i].a_flags |= ACE_OWNER;
199 case SMB_ACE4_WHO_GROUP:
200 acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP;
203 DEBUG(8, ("unsupported special_id %d\n", \
204 aceprop->who.special_id));
205 continue; /* don't add it !!! */
207 have_special_id = true;
210 if (must_add_empty_ace) {
211 acebuf[i].a_type = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
212 acebuf[i].a_flags = SMB_ACE4_DIRECTORY_INHERIT_ACE| \
213 SMB_ACE4_FILE_INHERIT_ACE|ACE_EVERYONE;
214 acebuf[i].a_access_mask = 0;
218 if (!have_special_id && config->zfsacl_denymissingspecial) {
223 SMB_ASSERT(i == naces);
226 if (fsp->fh->fd != -1) {
227 rv = facl(fsp->fh->fd, ACE_SETACL, naces, acebuf);
230 rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf);
233 if(errno == ENOSYS) {
234 DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
235 "supported on the filesystem where the file "
236 "reside", fsp_str_dbg(fsp)));
238 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
248 * set the local file's acls obtaining it in NT form
249 * using the NFSv4 format conversion
251 static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
252 uint32_t security_info_sent,
253 const struct security_descriptor *psd)
255 struct zfsacl_config_data *config = NULL;
257 SMB_VFS_HANDLE_GET_DATA(handle, config,
258 struct zfsacl_config_data,
259 return NT_STATUS_INTERNAL_ERROR);
261 return smb_set_nt_acl_nfs4(handle,
263 &config->nfs4_params,
269 static int get_zfsacl(TALLOC_CTX *mem_ctx,
270 const struct smb_filename *smb_fname,
274 ace_t *acebuf = NULL;
276 naces = acl(smb_fname->base_name, ACE_GETACLCNT, 0, NULL);
280 if (errno == ENOSYS) {
283 DEBUG(dbg_level, ("acl(ACE_GETACLCNT, %s): %s ",
284 smb_fname->base_name, strerror(errno)));
287 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
288 if (acebuf == NULL) {
293 rv = acl(smb_fname->base_name, ACE_GETACL, naces, acebuf);
295 DBG_DEBUG("acl(ACE_GETACL, %s) failed: %s ",
296 smb_fname->base_name, strerror(errno));
304 static int fget_zfsacl(TALLOC_CTX *mem_ctx,
305 struct files_struct *fsp,
309 ace_t *acebuf = NULL;
311 if (fsp->fh->fd == -1) {
312 return get_zfsacl(mem_ctx, fsp->fsp_name, outbuf);
315 naces = facl(fsp->fh->fd, ACE_GETACLCNT, 0, NULL);
319 if (errno == ENOSYS) {
322 DEBUG(dbg_level, ("facl(ACE_GETACLCNT, %s): %s ",
323 fsp_str_dbg(fsp), strerror(errno)));
327 acebuf = talloc_size(mem_ctx, sizeof(ace_t)*naces);
328 if (acebuf == NULL) {
333 rv = facl(fsp->fh->fd, ACE_GETACL, naces, acebuf);
335 DBG_DEBUG("acl(ACE_GETACL, %s): %s ",
336 fsp_str_dbg(fsp), strerror(errno));
344 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
345 struct files_struct *fsp,
346 uint32_t security_info,
348 struct security_descriptor **ppdesc)
350 struct SMB4ACL_T *pacl;
352 struct zfsacl_config_data *config = NULL;
353 ace_t *acebuf = NULL;
356 SMB_VFS_HANDLE_GET_DATA(handle, config,
357 struct zfsacl_config_data,
358 return NT_STATUS_INTERNAL_ERROR);
360 TALLOC_CTX *frame = talloc_stackframe();
362 naces = fget_zfsacl(talloc_tos(), fsp, &acebuf);
364 status = map_nt_error_from_unix(errno);
366 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
370 status = make_default_filesystem_acl(mem_ctx,
372 fsp->fsp_name->base_name,
375 if (!NT_STATUS_IS_OK(status)) {
378 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
382 status = zfs_get_nt_acl_common(handle->conn,
389 if (!NT_STATUS_IS_OK(status)) {
394 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
400 static NTSTATUS zfsacl_get_nt_acl_at(struct vfs_handle_struct *handle,
401 struct files_struct *dirfsp,
402 const struct smb_filename *smb_fname,
403 uint32_t security_info,
405 struct security_descriptor **ppdesc)
407 struct SMB4ACL_T *pacl = NULL;
409 struct zfsacl_config_data *config = NULL;
410 TALLOC_CTX *frame = NULL;
412 ace_t *acebuf = NULL;
414 SMB_ASSERT(dirfsp == handle->conn->cwd_fsp);
416 SMB_VFS_HANDLE_GET_DATA(handle,
418 struct zfsacl_config_data,
419 return NT_STATUS_INTERNAL_ERROR);
421 frame = talloc_stackframe();
423 naces = get_zfsacl(frame, smb_fname, &acebuf);
425 status = map_nt_error_from_unix(errno);
427 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
431 if (!VALID_STAT(smb_fname->st)) {
432 DBG_ERR("No stat info for [%s]\n",
433 smb_fname_str_dbg(smb_fname));
434 return NT_STATUS_INTERNAL_ERROR;
437 status = make_default_filesystem_acl(mem_ctx,
439 smb_fname->base_name,
442 if (!NT_STATUS_IS_OK(status)) {
445 (*ppdesc)->type |= SEC_DESC_DACL_PROTECTED;
449 status = zfs_get_nt_acl_common(handle->conn,
456 if (!NT_STATUS_IS_OK(status)) {
461 status = smb_get_nt_acl_nfs4(handle->conn,
472 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
474 uint32_t security_info_sent,
475 const struct security_descriptor *psd)
477 return zfs_set_nt_acl(handle, fsp, security_info_sent, psd);
480 /* nils.goroll@hamburg.de 2008-06-16 :
483 - https://bugzilla.samba.org/show_bug.cgi?id=5446
484 - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
486 Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
487 with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
488 use by samba in this module.
490 As the acl(2) interface is identical for ZFS and for NFS, this module,
491 vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
494 But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
495 / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
496 implements a compatibility wrapper, which will make calls to
497 traditional ACL calls though vfs_solarisacl succeed. As the
498 compatibility wrapper's implementation is (by design) incomplete,
499 we want to make sure that it is never being called.
501 As long as Samba does not support an explicit method for a module
502 to define conflicting vfs methods, we should override all conflicting
505 For this to work, we need to make sure that this module is initialised
506 *after* vfs_solarisacl
508 Function declarations taken from vfs_solarisacl
511 static SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle,
512 const struct smb_filename *smb_fname,
516 return (SMB_ACL_T)NULL;
519 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
523 return (SMB_ACL_T)NULL;
526 static int zfsacl_fail__sys_acl_set_file(vfs_handle_struct *handle,
527 const struct smb_filename *smb_fname,
534 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
541 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname)
547 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
548 const struct smb_filename *smb_fname,
550 char **blob_description,
556 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)
561 static int zfsacl_connect(struct vfs_handle_struct *handle,
562 const char *service, const char *user)
564 struct zfsacl_config_data *config = NULL;
567 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
572 config = talloc_zero(handle->conn, struct zfsacl_config_data);
574 DBG_ERR("talloc_zero() failed\n");
579 config->zfsacl_map_dacl_protected = lp_parm_bool(SNUM(handle->conn),
580 "zfsacl", "map_dacl_protected", false);
582 config->zfsacl_denymissingspecial = lp_parm_bool(SNUM(handle->conn),
583 "zfsacl", "denymissingspecial", false);
585 config->zfsacl_block_special = lp_parm_bool(SNUM(handle->conn),
586 "zfsacl", "block_special", true);
588 ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
594 SMB_VFS_HANDLE_SET_DATA(handle, config,
595 NULL, struct zfsacl_config_data,
601 /* VFS operations structure */
603 static struct vfs_fn_pointers zfsacl_fns = {
604 .connect_fn = zfsacl_connect,
605 .sys_acl_get_file_fn = zfsacl_fail__sys_acl_get_file,
606 .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd,
607 .sys_acl_blob_get_file_fn = zfsacl_fail__sys_acl_blob_get_file,
608 .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd,
609 .sys_acl_set_file_fn = zfsacl_fail__sys_acl_set_file,
610 .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd,
611 .sys_acl_delete_def_file_fn = zfsacl_fail__sys_acl_delete_def_file,
612 .fget_nt_acl_fn = zfsacl_fget_nt_acl,
613 .get_nt_acl_at_fn = zfsacl_get_nt_acl_at,
614 .fset_nt_acl_fn = zfsacl_fset_nt_acl,
618 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx)
620 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl",