2 * Convert NFSv4 acls stored per http://www.suse.de/~agruen/nfs4acl/ 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
6 * based on pvfs_acl_nfs4.c Copyright (C) Andrew Tridgell 2006
8 * based on vfs_fake_acls:
9 * Copyright (C) Tim Potter, 1999-2000
10 * Copyright (C) Alexander Bokovoy, 2002
11 * Copyright (C) Andrew Bartlett, 2002,2012
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <http://www.gnu.org/licenses/>.
29 #include "system/filesys.h"
30 #include "smbd/smbd.h"
31 #include "nfs4_acls.h"
32 #include "librpc/gen_ndr/ndr_nfs4acl.h"
35 #define DBGC_CLASS DBGC_VFS
37 static struct nfs4acl *nfs4acl_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
39 enum ndr_err_code ndr_err;
40 struct nfs4acl *acl = talloc(mem_ctx, struct nfs4acl);
46 ndr_err = ndr_pull_struct_blob(blob, acl, acl,
47 (ndr_pull_flags_fn_t)ndr_pull_nfs4acl);
49 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
50 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
51 ndr_errstr(ndr_err)));
58 static DATA_BLOB nfs4acl_acl2blob(TALLOC_CTX *mem_ctx, struct nfs4acl *acl)
60 enum ndr_err_code ndr_err;
62 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl,
63 (ndr_push_flags_fn_t)ndr_push_nfs4acl);
65 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
66 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
67 ndr_errstr(ndr_err)));
68 return data_blob_null;
73 static NTSTATUS nfs4_get_nfs4_acl_common(TALLOC_CTX *mem_ctx,
75 struct SMB4ACL_T **ppacl)
78 struct nfs4acl *nfs4acl = NULL;
79 struct SMB4ACL_T *pacl = NULL;
80 TALLOC_CTX *frame = talloc_stackframe();
81 nfs4acl = nfs4acl_blob2acl(blob, frame);
83 /* create SMB4ACL data */
84 if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
86 return NT_STATUS_NO_MEMORY;
88 for(i=0; i<nfs4acl->a_count; i++) {
89 SMB_ACE4PROP_T aceprop;
91 aceprop.aceType = (uint32_t) nfs4acl->ace[i].e_type;
92 aceprop.aceFlags = (uint32_t) nfs4acl->ace[i].e_flags;
93 aceprop.aceMask = (uint32_t) nfs4acl->ace[i].e_mask;
94 aceprop.who.id = (uint32_t) nfs4acl->ace[i].e_id;
95 if (!strcmp(nfs4acl->ace[i].e_who,
96 NFS4ACL_XATTR_OWNER_WHO)) {
97 aceprop.flags = SMB_ACE4_ID_SPECIAL;
98 aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
99 } else if (!strcmp(nfs4acl->ace[i].e_who,
100 NFS4ACL_XATTR_GROUP_WHO)) {
101 aceprop.flags = SMB_ACE4_ID_SPECIAL;
102 aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
103 } else if (!strcmp(nfs4acl->ace[i].e_who,
104 NFS4ACL_XATTR_EVERYONE_WHO)) {
105 aceprop.flags = SMB_ACE4_ID_SPECIAL;
106 aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
110 if(smb_add_ace4(pacl, &aceprop) == NULL) {
112 return NT_STATUS_NO_MEMORY;
121 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
122 static NTSTATUS nfs4_fget_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
123 files_struct *fsp, struct SMB4ACL_T **ppacl)
126 DATA_BLOB blob = data_blob_null;
128 TALLOC_CTX *frame = talloc_stackframe();
132 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
136 return NT_STATUS_NO_MEMORY;
138 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, NFS4ACL_XATTR_NAME, blob.data, blob.length);
139 blob.length = length;
140 } while (length == -1 && errno == ERANGE);
143 return map_nt_error_from_unix(errno);
145 status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
150 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
151 static NTSTATUS nfs4_get_nfs4_acl(vfs_handle_struct *handle,
153 const struct smb_filename *smb_fname,
154 struct SMB4ACL_T **ppacl)
157 DATA_BLOB blob = data_blob_null;
159 TALLOC_CTX *frame = talloc_stackframe();
163 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
167 return NT_STATUS_NO_MEMORY;
169 length = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
170 NFS4ACL_XATTR_NAME, blob.data, blob.length);
171 blob.length = length;
172 } while (length == -1 && errno == ERANGE);
175 return map_nt_error_from_unix(errno);
177 status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
182 static bool nfs4acl_smb4acl2nfs4acl(TALLOC_CTX *mem_ctx,
183 struct SMB4ACL_T *smbacl,
184 struct nfs4acl **pnfs4acl,
185 bool denymissingspecial)
187 struct nfs4acl *nfs4acl;
188 struct SMB4ACE_T *smbace;
189 bool have_special_id = false;
192 /* allocate the field of NFS4 aces */
193 nfs4acl = talloc_zero(mem_ctx, struct nfs4acl);
194 if(nfs4acl == NULL) {
199 nfs4acl->a_count = smb_get_naces(smbacl);
201 nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace,
203 if(nfs4acl->ace == NULL) {
204 TALLOC_FREE(nfs4acl);
209 /* handle all aces */
210 for(smbace = smb_first_ace4(smbacl), i = 0;
212 smbace = smb_next_ace4(smbace), i++) {
213 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
215 nfs4acl->ace[i].e_type = aceprop->aceType;
216 nfs4acl->ace[i].e_flags = aceprop->aceFlags;
217 nfs4acl->ace[i].e_mask = aceprop->aceMask;
218 nfs4acl->ace[i].e_id = aceprop->who.id;
219 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
220 switch(aceprop->who.special_id) {
221 case SMB_ACE4_WHO_EVERYONE:
222 nfs4acl->ace[i].e_who =
223 NFS4ACL_XATTR_EVERYONE_WHO;
225 case SMB_ACE4_WHO_OWNER:
226 nfs4acl->ace[i].e_who =
227 NFS4ACL_XATTR_OWNER_WHO;
229 case SMB_ACE4_WHO_GROUP:
230 nfs4acl->ace[i].e_who =
231 NFS4ACL_XATTR_GROUP_WHO;
234 DEBUG(8, ("unsupported special_id %d\n", \
235 aceprop->who.special_id));
236 continue; /* don't add it !!! */
238 have_special_id = true;
240 nfs4acl->ace[i].e_who = "";
244 if (!have_special_id && denymissingspecial) {
245 TALLOC_FREE(nfs4acl);
250 SMB_ASSERT(i == nfs4acl->a_count);
256 static bool nfs4acl_xattr_set_smb4acl(vfs_handle_struct *handle,
257 const struct smb_filename *smb_fname,
258 struct SMB4ACL_T *smbacl)
260 TALLOC_CTX *frame = talloc_stackframe();
261 struct nfs4acl *nfs4acl;
263 bool denymissingspecial;
266 denymissingspecial = lp_parm_bool(handle->conn->params->service,
268 "denymissingspecial", false);
270 if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
271 denymissingspecial)) {
272 DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
277 blob = nfs4acl_acl2blob(frame, nfs4acl);
279 DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
284 ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname, NFS4ACL_XATTR_NAME,
285 blob.data, blob.length, 0);
287 DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
293 /* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
294 static bool nfs4acl_xattr_fset_smb4acl(vfs_handle_struct *handle,
296 struct SMB4ACL_T *smbacl)
298 TALLOC_CTX *frame = talloc_stackframe();
299 struct nfs4acl *nfs4acl;
301 bool denymissingspecial;
304 denymissingspecial = lp_parm_bool(fsp->conn->params->service,
306 "denymissingspecial", false);
308 if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
309 denymissingspecial)) {
310 DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
315 blob = nfs4acl_acl2blob(frame, nfs4acl);
317 DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
322 if (fsp->fh->fd == -1) {
323 DEBUG(0, ("Error: fsp->fh->fd == -1\n"));
325 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_XATTR_NAME,
326 blob.data, blob.length, 0);
328 DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
335 * set the local file's acls obtaining it in NT form
336 * using the NFSv4 format conversion
338 static NTSTATUS nfs4_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
339 uint32_t security_info_sent,
340 const struct security_descriptor *psd)
342 return smb_set_nt_acl_nfs4(handle, fsp, NULL, security_info_sent, psd,
343 nfs4acl_xattr_fset_smb4acl);
346 static struct SMB4ACL_T *nfs4acls_defaultacl(TALLOC_CTX *mem_ctx)
348 struct SMB4ACL_T *pacl = NULL;
349 struct SMB4ACE_T *pace;
350 SMB_ACE4PROP_T ace = {
351 .flags = SMB_ACE4_ID_SPECIAL,
353 .id = SMB_ACE4_WHO_EVERYONE,
355 .aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE,
357 .aceMask = SMB_ACE4_ALL_MASKS,
360 DEBUG(10, ("Building default full access acl\n"));
362 pacl = smb_create_smb4acl(mem_ctx);
364 DEBUG(0, ("talloc failed\n"));
369 pace = smb_add_ace4(pacl, &ace);
371 DEBUG(0, ("talloc failed\n"));
381 * Because there is no good way to guarantee that a new xattr will be
382 * created on file creation there might be no acl xattr on a file when
383 * trying to read the acl. In this case the acl xattr will get
384 * constructed at that time from the parent acl.
385 * If the parent ACL doesn't have an xattr either the call will
386 * recurse to the next parent directory until the share root is
387 * reached. If the share root doesn't contain an ACL xattr either a
388 * default ACL will be used.
389 * Also a default ACL will be set if a non inheriting ACL is encountered.
392 * read acl xattr blob
393 * if acl xattr blob doesn't exist
394 * stat current directory to know if it's a file or directory
395 * read acl xattr blob from parent dir
396 * acl xattr blob to smb nfs4 acl
397 * calculate inherited smb nfs4 acl
398 * without inheritance use default smb nfs4 acl
399 * smb nfs4 acl to acl xattr blob
401 * return smb nfs4 acl
403 * acl xattr blob to smb nfs4 acl
405 * Todo: Really use mem_ctx after fixing interface of nfs4_acls
407 static struct SMB4ACL_T *nfs4acls_inheritacl(vfs_handle_struct *handle,
408 const struct smb_filename *smb_fname_in,
411 char *parent_dir = NULL;
412 struct SMB4ACL_T *pparentacl = NULL;
413 struct SMB4ACL_T *pchildacl = NULL;
414 struct SMB4ACE_T *pace;
417 struct smb_filename *smb_fname = NULL;
418 struct smb_filename *smb_fname_parent = NULL;
421 TALLOC_CTX *frame = talloc_stackframe();
423 DEBUG(10, ("nfs4acls_inheritacl invoked for %s\n",
424 smb_fname_in->base_name));
425 smb_fname = cp_smb_filename_nostream(frame, smb_fname_in);
426 if (smb_fname == NULL) {
432 ret = SMB_VFS_STAT(handle->conn, smb_fname);
434 DEBUG(0,("nfs4acls_inheritacl: failed to stat "
435 "directory %s. Error was %s\n",
436 smb_fname_str_dbg(smb_fname),
441 isdir = S_ISDIR(smb_fname->st.st_ex_mode);
443 if (!parent_dirname(talloc_tos(),
444 smb_fname->base_name,
452 smb_fname_parent = synthetic_smb_fname(talloc_tos(),
457 if (smb_fname_parent == NULL) {
463 status = nfs4_get_nfs4_acl(handle, frame, smb_fname_parent,
465 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)
466 && strncmp(parent_dir, ".", 2) != 0) {
467 pparentacl = nfs4acls_inheritacl(handle,
471 else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
472 pparentacl = nfs4acls_defaultacl(frame);
475 else if (!NT_STATUS_IS_OK(status)) {
480 pchildacl = smb_create_smb4acl(mem_ctx);
481 if (pchildacl == NULL) {
482 DEBUG(0, ("talloc failed\n"));
488 for (pace = smb_first_ace4(pparentacl); pace != NULL;
489 pace = smb_next_ace4(pace)) {
490 struct SMB4ACE_T *pchildace;
491 ace = *smb_get_ace4(pace);
492 if ((isdir && !(ace.aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)) ||
493 (!isdir && !(ace.aceFlags & SMB_ACE4_FILE_INHERIT_ACE))) {
494 DEBUG(10, ("non inheriting ace type: %d, iflags: %x, "
495 "flags: %x, mask: %x, who: %d\n",
496 ace.aceType, ace.flags, ace.aceFlags,
497 ace.aceMask, ace.who.id));
500 DEBUG(10, ("inheriting ace type: %d, iflags: %x, "
501 "flags: %x, mask: %x, who: %d\n",
502 ace.aceType, ace.flags, ace.aceFlags,
503 ace.aceMask, ace.who.id));
504 ace.aceFlags |= SMB_ACE4_INHERITED_ACE;
505 if (ace.aceFlags & SMB_ACE4_INHERIT_ONLY_ACE) {
506 ace.aceFlags &= ~SMB_ACE4_INHERIT_ONLY_ACE;
508 if (ace.aceFlags & SMB_ACE4_NO_PROPAGATE_INHERIT_ACE) {
509 ace.aceFlags &= ~SMB_ACE4_FILE_INHERIT_ACE;
510 ace.aceFlags &= ~SMB_ACE4_DIRECTORY_INHERIT_ACE;
511 ace.aceFlags &= ~SMB_ACE4_NO_PROPAGATE_INHERIT_ACE;
513 pchildace = smb_add_ace4(pchildacl, &ace);
514 if (pchildace == NULL) {
515 DEBUG(0, ("talloc failed\n"));
522 /* Set a default ACL if we didn't inherit anything. */
523 if (smb_first_ace4(pchildacl) == NULL) {
524 TALLOC_FREE(pchildacl);
525 pchildacl = nfs4acls_defaultacl(mem_ctx);
528 /* store the returned ACL to get it directly in the
529 future and avoid dynamic inheritance behavior. */
530 nfs4acl_xattr_set_smb4acl(handle, smb_fname, pchildacl);
536 static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
537 struct files_struct *fsp,
538 uint32_t security_info,
540 struct security_descriptor **ppdesc)
542 struct SMB4ACL_T *pacl;
544 TALLOC_CTX *frame = talloc_stackframe();
546 status = nfs4_fget_nfs4_acl(handle, frame, fsp, &pacl);
547 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
548 pacl = nfs4acls_inheritacl(handle, fsp->fsp_name,
551 else if (!NT_STATUS_IS_OK(status)) {
556 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
562 static NTSTATUS nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct *handle,
563 const struct smb_filename *smb_fname,
564 uint32_t security_info,
566 struct security_descriptor **ppdesc)
568 struct SMB4ACL_T *pacl;
570 TALLOC_CTX *frame = talloc_stackframe();
572 status = nfs4_get_nfs4_acl(handle, frame, smb_fname, &pacl);
573 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
574 pacl = nfs4acls_inheritacl(handle, smb_fname, frame);
576 else if (!NT_STATUS_IS_OK(status)) {
581 status = smb_get_nt_acl_nfs4(handle->conn, smb_fname, NULL,
582 security_info, mem_ctx, ppdesc,
588 static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
590 uint32_t security_info_sent,
591 const struct security_descriptor *psd)
593 return nfs4_set_nt_acl(handle, fsp, security_info_sent, psd);
597 As long as Samba does not support an exiplicit method for a module
598 to define conflicting vfs methods, we should override all conflicting
599 methods here. That way, we know we are using the NFSv4 storage
601 Function declarations taken from vfs_solarisacl
604 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_file(vfs_handle_struct *handle,
605 const struct smb_filename *smb_fname,
609 return (SMB_ACL_T)NULL;
612 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_fd(vfs_handle_struct *handle,
616 return (SMB_ACL_T)NULL;
619 static int nfs4acl_xattr_fail__sys_acl_set_file(vfs_handle_struct *handle,
620 const struct smb_filename *smb_fname,
627 static int nfs4acl_xattr_fail__sys_acl_set_fd(vfs_handle_struct *handle,
634 static int nfs4acl_xattr_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
635 const struct smb_filename *smb_fname)
640 static int nfs4acl_xattr_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
641 const struct smb_filename *smb_fname,
643 char **blob_description,
649 static int nfs4acl_xattr_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, files_struct *fsp, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob)
654 /* VFS operations structure */
656 static struct vfs_fn_pointers nfs4acl_xattr_fns = {
657 .sys_acl_get_file_fn = nfs4acl_xattr_fail__sys_acl_get_file,
658 .sys_acl_get_fd_fn = nfs4acl_xattr_fail__sys_acl_get_fd,
659 .sys_acl_blob_get_file_fn = nfs4acl_xattr_fail__sys_acl_blob_get_file,
660 .sys_acl_blob_get_fd_fn = nfs4acl_xattr_fail__sys_acl_blob_get_fd,
661 .sys_acl_set_file_fn = nfs4acl_xattr_fail__sys_acl_set_file,
662 .sys_acl_set_fd_fn = nfs4acl_xattr_fail__sys_acl_set_fd,
663 .sys_acl_delete_def_file_fn = nfs4acl_xattr_fail__sys_acl_delete_def_file,
664 .fget_nt_acl_fn = nfs4acl_xattr_fget_nt_acl,
665 .get_nt_acl_fn = nfs4acl_xattr_get_nt_acl,
666 .fset_nt_acl_fn = nfs4acl_xattr_fset_nt_acl,
669 NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *);
670 NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *ctx)
672 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "nfs4acl_xattr",