2 * Fake ACLs VFS module. Implements passthrough operation of all VFS
3 * calls to disk functions, except for file ownership and ACLs, which
4 * are stored in xattrs.
6 * Copyright (C) Tim Potter, 1999-2000
7 * Copyright (C) Alexander Bokovoy, 2002
8 * Copyright (C) Andrew Bartlett, 2002,2012
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/>.
25 #include "smbd/smbd.h"
26 #include "system/filesys.h"
28 #include "librpc/gen_ndr/ndr_smb_acl.h"
31 #define DBGC_CLASS DBGC_VFS
33 #define FAKE_UID "system.fake_uid"
34 #define FAKE_GID "system.fake_gid"
35 #define FAKE_ACL_ACCESS_XATTR "system.fake_access_acl"
36 #define FAKE_ACL_DEFAULT_XATTR "system.fake_default_acl"
38 struct in_pathref_data
{
39 bool calling_pathref_fsp
;
42 static int fake_acls_fuid(vfs_handle_struct
*handle
,
49 size
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, FAKE_UID
, uid_buf
, sizeof(uid_buf
));
50 if (size
== -1 && errno
== ENOATTR
) {
56 *uid
= IVAL(uid_buf
, 0);
60 static int fake_acls_fgid(vfs_handle_struct
*handle
,
67 size
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, FAKE_GID
, gid_buf
, sizeof(gid_buf
));
68 if (size
== -1 && errno
== ENOATTR
) {
74 *gid
= IVAL(gid_buf
, 0);
78 static int fake_acls_stat(vfs_handle_struct
*handle
,
79 struct smb_filename
*smb_fname
)
82 struct in_pathref_data
*prd
= NULL
;
83 struct smb_filename
*smb_fname_cp
= NULL
;
84 struct files_struct
*fsp
= NULL
;
86 SMB_VFS_HANDLE_GET_DATA(handle
,
88 struct in_pathref_data
,
91 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
96 if (smb_fname
->fsp
!= NULL
) {
97 fsp
= metadata_fsp(smb_fname
->fsp
);
102 * Ensure openat_pathref_fsp()
103 * can't recurse into fake_acls_stat().
104 * openat_pathref_fsp() doesn't care
105 * about the uid/gid values, it only
106 * wants a valid/invalid stat answer
107 * and we know smb_fname exists as
108 * the SMB_VFS_NEXT_STAT() returned
111 if (prd
->calling_pathref_fsp
) {
116 * openat_pathref_fsp() expects a talloc'ed
117 * smb_filename. stat can be passed a struct
118 * from the stack. Make a talloc'ed copy
119 * so openat_pathref_fsp() can add its
122 smb_fname_cp
= cp_smb_filename(talloc_tos(),
124 if (smb_fname_cp
== NULL
) {
129 /* Recursion guard. */
130 prd
->calling_pathref_fsp
= true;
131 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
,
133 /* End recursion guard. */
134 prd
->calling_pathref_fsp
= false;
136 if (!NT_STATUS_IS_OK(status
)) {
138 * Ignore errors here. We know
139 * the path exists (the SMB_VFS_NEXT_STAT()
140 * above succeeded. So being unable to
141 * open a pathref fsp can be due to a
142 * range of errors (startup path beginning
143 * with '/' for example, path = ".." when
144 * enumerating a directory. Just treat this
145 * the same way as the path not having the
146 * FAKE_UID or FAKE_GID EA's present. For the
147 * test purposes of this module (fake NT ACLs
148 * from windows clients) this is close enough.
149 * Just report for debugging purposes.
151 DBG_DEBUG("Unable to get pathref fsp on %s. "
153 smb_fname_str_dbg(smb_fname_cp
),
155 TALLOC_FREE(smb_fname_cp
);
158 fsp
= smb_fname_cp
->fsp
;
161 ret
= fake_acls_fuid(handle
,
163 &smb_fname
->st
.st_ex_uid
);
165 TALLOC_FREE(smb_fname_cp
);
168 ret
= fake_acls_fgid(handle
,
170 &smb_fname
->st
.st_ex_gid
);
172 TALLOC_FREE(smb_fname_cp
);
175 TALLOC_FREE(smb_fname_cp
);
179 static int fake_acls_lstat(vfs_handle_struct
*handle
,
180 struct smb_filename
*smb_fname
)
183 struct in_pathref_data
*prd
= NULL
;
185 SMB_VFS_HANDLE_GET_DATA(handle
,
187 struct in_pathref_data
,
190 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
192 struct smb_filename
*smb_fname_base
= NULL
;
193 SMB_STRUCT_STAT sbuf
= { 0 };
197 * Ensure synthetic_pathref()
198 * can't recurse into fake_acls_lstat().
199 * synthetic_pathref() doesn't care
200 * about the uid/gid values, it only
201 * wants a valid/invalid stat answer
202 * and we know smb_fname exists as
203 * the SMB_VFS_NEXT_LSTAT() returned
206 if (prd
->calling_pathref_fsp
) {
210 /* Recursion guard. */
211 prd
->calling_pathref_fsp
= true;
212 status
= synthetic_pathref(talloc_tos(),
213 handle
->conn
->cwd_fsp
,
214 smb_fname
->base_name
,
218 0, /* we want stat, not lstat. */
220 /* End recursion guard. */
221 prd
->calling_pathref_fsp
= false;
222 if (NT_STATUS_IS_OK(status
)) {
224 * This isn't quite right (calling fgetxattr not
225 * lgetxattr), but for the test purposes of this
226 * module (fake NT ACLs from windows clients), it is
227 * close enough. We removed the l*xattr functions
228 * because linux doesn't support using them, but we
229 * could fake them in xattr_tdb if we really wanted
230 * to. We ignore errors because the link might not
232 fake_acls_fuid(handle
,
234 &smb_fname
->st
.st_ex_uid
);
235 fake_acls_fgid(handle
,
237 &smb_fname
->st
.st_ex_gid
);
239 TALLOC_FREE(smb_fname_base
);
245 static int fake_acls_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
249 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
251 ret
= fake_acls_fuid(handle
, fsp
, &sbuf
->st_ex_uid
);
255 ret
= fake_acls_fgid(handle
, fsp
, &sbuf
->st_ex_gid
);
263 static SMB_ACL_T
fake_acls_blob2acl(DATA_BLOB
*blob
, TALLOC_CTX
*mem_ctx
)
265 enum ndr_err_code ndr_err
;
266 struct smb_acl_t
*acl
= talloc(mem_ctx
, struct smb_acl_t
);
272 ndr_err
= ndr_pull_struct_blob(blob
, acl
, acl
,
273 (ndr_pull_flags_fn_t
)ndr_pull_smb_acl_t
);
275 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
276 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
277 ndr_errstr(ndr_err
)));
284 static DATA_BLOB
fake_acls_acl2blob(TALLOC_CTX
*mem_ctx
, SMB_ACL_T acl
)
286 enum ndr_err_code ndr_err
;
288 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, acl
,
289 (ndr_push_flags_fn_t
)ndr_push_smb_acl_t
);
291 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
292 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
293 ndr_errstr(ndr_err
)));
294 return data_blob_null
;
299 static SMB_ACL_T
fake_acls_sys_acl_get_fd(struct vfs_handle_struct
*handle
,
304 DATA_BLOB blob
= data_blob_null
;
306 const char *name
= NULL
;
307 struct smb_acl_t
*acl
= NULL
;
308 TALLOC_CTX
*frame
= talloc_stackframe();
311 case SMB_ACL_TYPE_ACCESS
:
312 name
= FAKE_ACL_ACCESS_XATTR
;
314 case SMB_ACL_TYPE_DEFAULT
:
315 name
= FAKE_ACL_DEFAULT_XATTR
;
318 DBG_ERR("Illegal ACL type %d\n", (int)type
);
329 blob
.data
= talloc_realloc(frame
, blob
.data
, uint8_t, blob
.length
);
335 length
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
);
336 blob
.length
= length
;
337 } while (length
== -1 && errno
== ERANGE
);
338 if (length
== -1 && errno
== ENOATTR
) {
343 acl
= fake_acls_blob2acl(&blob
, mem_ctx
);
349 static int fake_acls_sys_acl_set_fd(vfs_handle_struct
*handle
,
350 struct files_struct
*fsp
,
355 const char *name
= NULL
;
356 TALLOC_CTX
*frame
= talloc_stackframe();
357 DATA_BLOB blob
= fake_acls_acl2blob(frame
, theacl
);
359 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
366 case SMB_ACL_TYPE_ACCESS
:
367 name
= FAKE_ACL_ACCESS_XATTR
;
369 case SMB_ACL_TYPE_DEFAULT
:
370 name
= FAKE_ACL_DEFAULT_XATTR
;
377 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
, 0);
382 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct
*handle
,
383 struct files_struct
*fsp
)
386 const char *name
= FAKE_ACL_DEFAULT_XATTR
;
388 if (!fsp
->fsp_flags
.is_directory
) {
393 ret
= SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
394 if (ret
== -1 && errno
== ENOATTR
) {
402 static int fake_acls_lchown(vfs_handle_struct
*handle
,
403 const struct smb_filename
*smb_fname
,
410 uid_t current_uid
= get_current_uid(handle
->conn
);
412 if (current_uid
!= 0 && current_uid
!= uid
) {
416 /* This isn't quite right (calling setxattr not
417 * lsetxattr), but for the test purposes of this
418 * module (fake NT ACLs from windows clients), it is
419 * close enough. We removed the l*xattr functions
420 * because linux doesn't support using them, but we
421 * could fake them in xattr_tdb if we really wanted
424 SIVAL(id_buf
, 0, uid
);
425 ret
= SMB_VFS_NEXT_FSETXATTR(handle
,
436 SIVAL(id_buf
, 0, gid
);
437 ret
= SMB_VFS_NEXT_FSETXATTR(handle
,
450 static int fake_acls_fchown(vfs_handle_struct
*handle
, files_struct
*fsp
, uid_t uid
, gid_t gid
)
455 uid_t current_uid
= get_current_uid(handle
->conn
);
457 if (current_uid
!= 0 && current_uid
!= uid
) {
461 SIVAL(id_buf
, 0, uid
);
462 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_UID
, id_buf
, sizeof(id_buf
), 0);
468 SIVAL(id_buf
, 0, gid
);
469 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_GID
, id_buf
, sizeof(id_buf
), 0);
478 * Implement the chmod uid/mask/other mode changes on a fake ACL.
481 static int fake_acl_process_chmod(SMB_ACL_T
*pp_the_acl
,
485 bool got_mask
= false;
486 int entry_id
= SMB_ACL_FIRST_ENTRY
;
491 SMB_ACL_T the_acl
= *pp_the_acl
;
493 /* Split the mode into u/mask/other masks. */
494 umode
= unix_perms_to_acl_perms(mode
, S_IRUSR
, S_IWUSR
, S_IXUSR
);
495 mmode
= unix_perms_to_acl_perms(mode
, S_IRGRP
, S_IWGRP
, S_IXGRP
);
496 omode
= unix_perms_to_acl_perms(mode
, S_IROTH
, S_IWOTH
, S_IXOTH
);
499 SMB_ACL_ENTRY_T entry
;
500 SMB_ACL_TAG_T tagtype
;
501 SMB_ACL_PERMSET_T permset
;
504 ret
= sys_acl_get_entry(the_acl
,
515 ret
= sys_acl_get_tag_type(entry
, &tagtype
);
519 ret
= sys_acl_get_permset(entry
, &permset
);
524 case SMB_ACL_USER_OBJ
:
525 ret
= map_acl_perms_to_permset(umode
, &permset
);
531 puid
= (uid_t
*)sys_acl_get_qualifier(entry
);
535 if (owner
!= *puid
) {
538 ret
= map_acl_perms_to_permset(umode
, &permset
);
543 case SMB_ACL_GROUP_OBJ
:
545 /* Ignore all group entries. */
548 ret
= map_acl_perms_to_permset(mmode
, &permset
);
555 ret
= map_acl_perms_to_permset(omode
, &permset
);
564 ret
= sys_acl_set_permset(entry
, permset
);
568 /* Move to next entry. */
569 entry_id
= SMB_ACL_NEXT_ENTRY
;
573 * If we didn't see a mask entry, add one.
577 SMB_ACL_ENTRY_T mask_entry
;
578 SMB_ACL_PERMSET_T mask_permset
;
579 ret
= sys_acl_create_entry(&the_acl
, &mask_entry
);
583 ret
= map_acl_perms_to_permset(mmode
, &mask_permset
);
587 ret
= sys_acl_set_permset(mask_entry
, mask_permset
);
591 ret
= sys_acl_set_tag_type(mask_entry
, SMB_ACL_MASK
);
595 /* In case we were realloced and moved. */
596 *pp_the_acl
= the_acl
;
602 static int fake_acls_fchmod(vfs_handle_struct
*handle
,
606 TALLOC_CTX
*frame
= talloc_stackframe();
608 SMB_ACL_T the_acl
= NULL
;
611 * Passthrough first to preserve the
612 * S_ISUID | S_ISGID | S_ISVTX
616 ret
= SMB_VFS_NEXT_FCHMOD(handle
,
624 the_acl
= fake_acls_sys_acl_get_fd(handle
,
628 if (the_acl
== NULL
) {
630 if (errno
== ENOATTR
) {
631 /* No ACL on this file. Just passthrough. */
636 ret
= fake_acl_process_chmod(&the_acl
,
637 fsp
->fsp_name
->st
.st_ex_uid
,
643 ret
= fake_acls_sys_acl_set_fd(handle
,
651 static int fake_acls_connect(struct vfs_handle_struct
*handle
,
655 struct in_pathref_data
*prd
= NULL
;
658 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
663 * Create a struct can tell us if we're recursing
664 * into openat_pathref_fsp() in this module. This will
665 * go away once we have SMB_VFS_STATX() and we will
666 * have a way for a caller to as for specific stat
667 * fields in a granular way. Then we will know exactly
668 * what fields the caller wants, so we won't have to
669 * fill in everything.
671 prd
= talloc_zero(handle
->conn
, struct in_pathref_data
);
675 SMB_VFS_HANDLE_SET_DATA(handle
,
678 struct in_pathref_data
,
683 static struct vfs_fn_pointers vfs_fake_acls_fns
= {
684 .connect_fn
= fake_acls_connect
,
685 .stat_fn
= fake_acls_stat
,
686 .lstat_fn
= fake_acls_lstat
,
687 .fstat_fn
= fake_acls_fstat
,
688 .fchmod_fn
= fake_acls_fchmod
,
689 .sys_acl_get_fd_fn
= fake_acls_sys_acl_get_fd
,
690 .sys_acl_blob_get_fd_fn
= posix_sys_acl_blob_get_fd
,
691 .sys_acl_set_fd_fn
= fake_acls_sys_acl_set_fd
,
692 .sys_acl_delete_def_fd_fn
= fake_acls_sys_acl_delete_def_fd
,
693 .lchown_fn
= fake_acls_lchown
,
694 .fchown_fn
= fake_acls_fchown
,
699 NTSTATUS
vfs_fake_acls_init(TALLOC_CTX
*ctx
)
701 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fake_acls",