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
,
78 struct nfs4acl
*nfs4acl
= NULL
;
79 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
) nfs4acl
->ace
[i
].e_type
;
92 aceprop
.aceFlags
= (uint32
) nfs4acl
->ace
[i
].e_flags
;
93 aceprop
.aceMask
= (uint32
) nfs4acl
->ace
[i
].e_mask
;
94 aceprop
.who
.id
= (uint32
) 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
, 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
, TALLOC_CTX
*mem_ctx
,
152 const char *path
, SMB4ACL_T
**ppacl
)
155 DATA_BLOB blob
= data_blob_null
;
157 TALLOC_CTX
*frame
= talloc_stackframe();
161 blob
.data
= talloc_realloc(frame
, blob
.data
, uint8_t, blob
.length
);
165 return NT_STATUS_NO_MEMORY
;
167 length
= SMB_VFS_NEXT_GETXATTR(handle
, path
, NFS4ACL_XATTR_NAME
, blob
.data
, blob
.length
);
168 blob
.length
= length
;
169 } while (length
== -1 && errno
== ERANGE
);
172 return map_nt_error_from_unix(errno
);
174 status
= nfs4_get_nfs4_acl_common(mem_ctx
, &blob
, ppacl
);
179 static bool nfs4acl_smb4acl2nfs4acl(TALLOC_CTX
*mem_ctx
,
181 struct nfs4acl
**pnfs4acl
,
182 bool denymissingspecial
)
184 struct nfs4acl
*nfs4acl
;
186 bool have_special_id
= false;
189 /* allocate the field of NFS4 aces */
190 nfs4acl
= talloc_zero(mem_ctx
, struct nfs4acl
);
191 if(nfs4acl
== NULL
) {
196 nfs4acl
->a_count
= smb_get_naces(smbacl
);
198 nfs4acl
->ace
= talloc_zero_array(nfs4acl
, struct nfs4ace
,
200 if(nfs4acl
->ace
== NULL
) {
201 TALLOC_FREE(nfs4acl
);
206 /* handle all aces */
207 for(smbace
= smb_first_ace4(smbacl
), i
= 0;
209 smbace
= smb_next_ace4(smbace
), i
++) {
210 SMB_ACE4PROP_T
*aceprop
= smb_get_ace4(smbace
);
212 nfs4acl
->ace
[i
].e_type
= aceprop
->aceType
;
213 nfs4acl
->ace
[i
].e_flags
= aceprop
->aceFlags
;
214 nfs4acl
->ace
[i
].e_mask
= aceprop
->aceMask
;
215 nfs4acl
->ace
[i
].e_id
= aceprop
->who
.id
;
216 if(aceprop
->flags
& SMB_ACE4_ID_SPECIAL
) {
217 switch(aceprop
->who
.special_id
) {
218 case SMB_ACE4_WHO_EVERYONE
:
219 nfs4acl
->ace
[i
].e_who
=
220 NFS4ACL_XATTR_EVERYONE_WHO
;
222 case SMB_ACE4_WHO_OWNER
:
223 nfs4acl
->ace
[i
].e_who
=
224 NFS4ACL_XATTR_OWNER_WHO
;
226 case SMB_ACE4_WHO_GROUP
:
227 nfs4acl
->ace
[i
].e_who
=
228 NFS4ACL_XATTR_GROUP_WHO
;
231 DEBUG(8, ("unsupported special_id %d\n", \
232 aceprop
->who
.special_id
));
233 continue; /* don't add it !!! */
235 have_special_id
= true;
237 nfs4acl
->ace
[i
].e_who
= "";
241 if (!have_special_id
&& denymissingspecial
) {
242 TALLOC_FREE(nfs4acl
);
247 SMB_ASSERT(i
== nfs4acl
->a_count
);
253 static bool nfs4acl_xattr_set_smb4acl(vfs_handle_struct
*handle
,
257 TALLOC_CTX
*frame
= talloc_stackframe();
258 struct nfs4acl
*nfs4acl
;
260 bool denymissingspecial
;
263 denymissingspecial
= lp_parm_bool(handle
->conn
->params
->service
,
265 "denymissingspecial", false);
267 if (!nfs4acl_smb4acl2nfs4acl(frame
, smbacl
, &nfs4acl
,
268 denymissingspecial
)) {
269 DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
274 blob
= nfs4acl_acl2blob(frame
, nfs4acl
);
276 DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
281 ret
= SMB_VFS_NEXT_SETXATTR(handle
, path
, NFS4ACL_XATTR_NAME
,
282 blob
.data
, blob
.length
, 0);
284 DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno
)));
290 /* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
291 static bool nfs4acl_xattr_fset_smb4acl(vfs_handle_struct
*handle
,
295 TALLOC_CTX
*frame
= talloc_stackframe();
296 struct nfs4acl
*nfs4acl
;
298 bool denymissingspecial
;
301 denymissingspecial
= lp_parm_bool(fsp
->conn
->params
->service
,
303 "denymissingspecial", false);
305 if (!nfs4acl_smb4acl2nfs4acl(frame
, smbacl
, &nfs4acl
,
306 denymissingspecial
)) {
307 DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
312 blob
= nfs4acl_acl2blob(frame
, nfs4acl
);
314 DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
319 if (fsp
->fh
->fd
== -1) {
320 DEBUG(0, ("Error: fsp->fh->fd == -1\n"));
322 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, NFS4ACL_XATTR_NAME
,
323 blob
.data
, blob
.length
, 0);
325 DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno
)));
332 * set the local file's acls obtaining it in NT form
333 * using the NFSv4 format conversion
335 static NTSTATUS
nfs4_set_nt_acl(vfs_handle_struct
*handle
, files_struct
*fsp
,
336 uint32 security_info_sent
,
337 const struct security_descriptor
*psd
)
339 return smb_set_nt_acl_nfs4(handle
, fsp
, security_info_sent
, psd
,
340 nfs4acl_xattr_fset_smb4acl
);
343 static SMB4ACL_T
*nfs4acls_defaultacl(TALLOC_CTX
*mem_ctx
)
345 SMB4ACL_T
*pacl
= NULL
;
347 SMB_ACE4PROP_T ace
= { SMB_ACE4_ID_SPECIAL
,
348 SMB_ACE4_WHO_EVERYONE
,
349 SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE
,
351 SMB_ACE4_ALL_MASKS
};
353 DEBUG(10, ("Building default full access acl\n"));
355 pacl
= smb_create_smb4acl(mem_ctx
);
357 DEBUG(0, ("talloc failed\n"));
362 pace
= smb_add_ace4(pacl
, &ace
);
364 DEBUG(0, ("talloc failed\n"));
374 * Because there is no good way to guarantee that a new xattr will be
375 * created on file creation there might be no acl xattr on a file when
376 * trying to read the acl. In this case the acl xattr will get
377 * constructed at that time from the parent acl.
378 * If the parent ACL doesn't have an xattr either the call will
379 * recurse to the next parent directory until the share root is
380 * reached. If the share root doesn't contain an ACL xattr either a
381 * default ACL will be used.
382 * Also a default ACL will be set if a non inheriting ACL is encountered.
385 * read acl xattr blob
386 * if acl xattr blob doesn't exist
387 * stat current directory to know if it's a file or directory
388 * read acl xattr blob from parent dir
389 * acl xattr blob to smb nfs4 acl
390 * calculate inherited smb nfs4 acl
391 * without inheritance use default smb nfs4 acl
392 * smb nfs4 acl to acl xattr blob
394 * return smb nfs4 acl
396 * acl xattr blob to smb nfs4 acl
398 * Todo: Really use mem_ctx after fixing interface of nfs4_acls
400 static SMB4ACL_T
*nfs4acls_inheritacl(vfs_handle_struct
*handle
,
404 char *parent_dir
= NULL
;
405 SMB4ACL_T
*pparentacl
= NULL
;
406 SMB4ACL_T
*pchildacl
= NULL
;
410 struct smb_filename
*smb_fname
= NULL
;
413 TALLOC_CTX
*frame
= talloc_stackframe();
415 DEBUG(10, ("nfs4acls_inheritacl invoked for %s\n", path
));
416 smb_fname
= synthetic_smb_fname(frame
, path
, NULL
, NULL
);
417 if (smb_fname
== NULL
) {
423 ret
= SMB_VFS_STAT(handle
->conn
, smb_fname
);
425 DEBUG(0,("nfs4acls_inheritacl: failed to stat "
426 "directory %s. Error was %s\n",
427 smb_fname_str_dbg(smb_fname
),
432 isdir
= S_ISDIR(smb_fname
->st
.st_ex_mode
);
434 if (!parent_dirname(talloc_tos(),
443 status
= nfs4_get_nfs4_acl(handle
, frame
, parent_dir
, &pparentacl
);
444 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)
445 && strncmp(parent_dir
, ".", 2) != 0) {
446 pparentacl
= nfs4acls_inheritacl(handle
, parent_dir
,
449 else if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
450 pparentacl
= nfs4acls_defaultacl(frame
);
453 else if (!NT_STATUS_IS_OK(status
)) {
458 pchildacl
= smb_create_smb4acl(mem_ctx
);
459 if (pchildacl
== NULL
) {
460 DEBUG(0, ("talloc failed\n"));
466 for (pace
= smb_first_ace4(pparentacl
); pace
!= NULL
;
467 pace
= smb_next_ace4(pace
)) {
468 SMB4ACE_T
*pchildace
;
469 ace
= *smb_get_ace4(pace
);
470 if (isdir
&& !(ace
.aceFlags
& SMB_ACE4_DIRECTORY_INHERIT_ACE
)
471 || !isdir
&& !(ace
.aceFlags
& SMB_ACE4_FILE_INHERIT_ACE
)) {
472 DEBUG(10, ("non inheriting ace type: %d, iflags: %x, "
473 "flags: %x, mask: %x, who: %d\n",
474 ace
.aceType
, ace
.flags
, ace
.aceFlags
,
475 ace
.aceMask
, ace
.who
.id
));
478 DEBUG(10, ("inheriting ace type: %d, iflags: %x, "
479 "flags: %x, mask: %x, who: %d\n",
480 ace
.aceType
, ace
.flags
, ace
.aceFlags
,
481 ace
.aceMask
, ace
.who
.id
));
482 ace
.aceFlags
|= SMB_ACE4_INHERITED_ACE
;
483 if ((isdir
&& (ace
.aceFlags
& SMB_ACE4_DIRECTORY_INHERIT_ACE
)
484 || !isdir
&& (ace
.aceFlags
& SMB_ACE4_FILE_INHERIT_ACE
))
485 && ace
.aceFlags
& SMB_ACE4_INHERIT_ONLY_ACE
) {
486 ace
.aceFlags
&= ~SMB_ACE4_INHERIT_ONLY_ACE
;
488 if (ace
.aceFlags
& SMB_ACE4_NO_PROPAGATE_INHERIT_ACE
) {
489 ace
.aceFlags
&= ~SMB_ACE4_FILE_INHERIT_ACE
;
490 ace
.aceFlags
&= ~SMB_ACE4_DIRECTORY_INHERIT_ACE
;
491 ace
.aceFlags
&= ~SMB_ACE4_NO_PROPAGATE_INHERIT_ACE
;
493 pchildace
= smb_add_ace4(pchildacl
, &ace
);
494 if (pchildace
== NULL
) {
495 DEBUG(0, ("talloc failed\n"));
502 /* Set a default ACL if we didn't inherit anything. */
503 if (smb_first_ace4(pchildacl
) == NULL
) {
504 TALLOC_FREE(pchildacl
);
505 pchildacl
= nfs4acls_defaultacl(mem_ctx
);
508 /* store the returned ACL to get it directly in the
509 future and avoid dynamic inheritance behavior. */
510 nfs4acl_xattr_set_smb4acl(handle
, path
, pchildacl
);
516 static NTSTATUS
nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct
*handle
,
517 struct files_struct
*fsp
,
518 uint32 security_info
,
520 struct security_descriptor
**ppdesc
)
524 TALLOC_CTX
*frame
= talloc_stackframe();
526 status
= nfs4_fget_nfs4_acl(handle
, frame
, fsp
, &pacl
);
527 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
528 pacl
= nfs4acls_inheritacl(handle
, fsp
->fsp_name
->base_name
,
531 else if (!NT_STATUS_IS_OK(status
)) {
536 status
= smb_fget_nt_acl_nfs4(fsp
, security_info
, mem_ctx
, ppdesc
, pacl
);
541 static NTSTATUS
nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct
*handle
,
542 const char *name
, uint32 security_info
,
544 struct security_descriptor
**ppdesc
)
548 TALLOC_CTX
*frame
= talloc_stackframe();
550 status
= nfs4_get_nfs4_acl(handle
, frame
, name
, &pacl
);
551 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
552 pacl
= nfs4acls_inheritacl(handle
, name
, frame
);
554 else if (!NT_STATUS_IS_OK(status
)) {
559 status
= smb_get_nt_acl_nfs4(handle
->conn
, name
, security_info
,
566 static NTSTATUS
nfs4acl_xattr_fset_nt_acl(vfs_handle_struct
*handle
,
568 uint32 security_info_sent
,
569 const struct security_descriptor
*psd
)
571 return nfs4_set_nt_acl(handle
, fsp
, security_info_sent
, psd
);
575 As long as Samba does not support an exiplicit method for a module
576 to define conflicting vfs methods, we should override all conflicting
577 methods here. That way, we know we are using the NFSv4 storage
579 Function declarations taken from vfs_solarisacl
582 static SMB_ACL_T
nfs4acl_xattr_fail__sys_acl_get_file(vfs_handle_struct
*handle
,
587 return (SMB_ACL_T
)NULL
;
590 static SMB_ACL_T
nfs4acl_xattr_fail__sys_acl_get_fd(vfs_handle_struct
*handle
,
594 return (SMB_ACL_T
)NULL
;
597 static int nfs4acl_xattr_fail__sys_acl_set_file(vfs_handle_struct
*handle
,
605 static int nfs4acl_xattr_fail__sys_acl_set_fd(vfs_handle_struct
*handle
,
612 static int nfs4acl_xattr_fail__sys_acl_delete_def_file(vfs_handle_struct
*handle
,
618 static int nfs4acl_xattr_fail__sys_acl_blob_get_file(vfs_handle_struct
*handle
, const char *path_p
, TALLOC_CTX
*mem_ctx
, char **blob_description
, DATA_BLOB
*blob
)
623 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
)
628 /* VFS operations structure */
630 static struct vfs_fn_pointers nfs4acl_xattr_fns
= {
631 .sys_acl_get_file_fn
= nfs4acl_xattr_fail__sys_acl_get_file
,
632 .sys_acl_get_fd_fn
= nfs4acl_xattr_fail__sys_acl_get_fd
,
633 .sys_acl_blob_get_file_fn
= nfs4acl_xattr_fail__sys_acl_blob_get_file
,
634 .sys_acl_blob_get_fd_fn
= nfs4acl_xattr_fail__sys_acl_blob_get_fd
,
635 .sys_acl_set_file_fn
= nfs4acl_xattr_fail__sys_acl_set_file
,
636 .sys_acl_set_fd_fn
= nfs4acl_xattr_fail__sys_acl_set_fd
,
637 .sys_acl_delete_def_file_fn
= nfs4acl_xattr_fail__sys_acl_delete_def_file
,
638 .fget_nt_acl_fn
= nfs4acl_xattr_fget_nt_acl
,
639 .get_nt_acl_fn
= nfs4acl_xattr_get_nt_acl
,
640 .fset_nt_acl_fn
= nfs4acl_xattr_fset_nt_acl
,
643 NTSTATUS
vfs_nfs4acl_xattr_init(void);
644 NTSTATUS
vfs_nfs4acl_xattr_init(void)
646 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "nfs4acl_xattr",