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
) || (errno
== EBADF
))) {
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
) || (errno
== EBADF
))) {
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 if (fsp_get_pathref_fd(handle
->conn
->cwd_fsp
) == -1) {
131 * No tcon around, fail as if we don't have
134 status
= NT_STATUS_INVALID_HANDLE
;
136 /* Recursion guard. */
137 prd
->calling_pathref_fsp
= true;
138 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
,
140 /* End recursion guard. */
141 prd
->calling_pathref_fsp
= false;
144 if (!NT_STATUS_IS_OK(status
)) {
146 * Ignore errors here. We know
147 * the path exists (the SMB_VFS_NEXT_STAT()
148 * above succeeded. So being unable to
149 * open a pathref fsp can be due to a
150 * range of errors (startup path beginning
151 * with '/' for example, path = ".." when
152 * enumerating a directory. Just treat this
153 * the same way as the path not having the
154 * FAKE_UID or FAKE_GID EA's present. For the
155 * test purposes of this module (fake NT ACLs
156 * from windows clients) this is close enough.
157 * Just report for debugging purposes.
159 DBG_DEBUG("Unable to get pathref fsp on %s. "
161 smb_fname_str_dbg(smb_fname_cp
),
163 TALLOC_FREE(smb_fname_cp
);
166 fsp
= smb_fname_cp
->fsp
;
169 ret
= fake_acls_fuid(handle
,
171 &smb_fname
->st
.st_ex_uid
);
173 TALLOC_FREE(smb_fname_cp
);
176 ret
= fake_acls_fgid(handle
,
178 &smb_fname
->st
.st_ex_gid
);
180 TALLOC_FREE(smb_fname_cp
);
183 TALLOC_FREE(smb_fname_cp
);
187 static int fake_acls_lstat(vfs_handle_struct
*handle
,
188 struct smb_filename
*smb_fname
)
191 struct in_pathref_data
*prd
= NULL
;
193 SMB_VFS_HANDLE_GET_DATA(handle
,
195 struct in_pathref_data
,
198 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
200 struct smb_filename
*smb_fname_base
= NULL
;
201 SMB_STRUCT_STAT sbuf
= { 0 };
205 * Ensure synthetic_pathref()
206 * can't recurse into fake_acls_lstat().
207 * synthetic_pathref() doesn't care
208 * about the uid/gid values, it only
209 * wants a valid/invalid stat answer
210 * and we know smb_fname exists as
211 * the SMB_VFS_NEXT_LSTAT() returned
214 if (prd
->calling_pathref_fsp
) {
218 /* Recursion guard. */
219 prd
->calling_pathref_fsp
= true;
220 status
= synthetic_pathref(talloc_tos(),
221 handle
->conn
->cwd_fsp
,
222 smb_fname
->base_name
,
226 0, /* we want stat, not lstat. */
228 /* End recursion guard. */
229 prd
->calling_pathref_fsp
= false;
230 if (NT_STATUS_IS_OK(status
)) {
232 * This isn't quite right (calling fgetxattr not
233 * lgetxattr), but for the test purposes of this
234 * module (fake NT ACLs from windows clients), it is
235 * close enough. We removed the l*xattr functions
236 * because linux doesn't support using them, but we
237 * could fake them in xattr_tdb if we really wanted
238 * to. We ignore errors because the link might not
240 fake_acls_fuid(handle
,
242 &smb_fname
->st
.st_ex_uid
);
243 fake_acls_fgid(handle
,
245 &smb_fname
->st
.st_ex_gid
);
247 TALLOC_FREE(smb_fname_base
);
253 static int fake_acls_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
257 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
259 ret
= fake_acls_fuid(handle
, fsp
, &sbuf
->st_ex_uid
);
263 ret
= fake_acls_fgid(handle
, fsp
, &sbuf
->st_ex_gid
);
271 static SMB_ACL_T
fake_acls_blob2acl(DATA_BLOB
*blob
, TALLOC_CTX
*mem_ctx
)
273 enum ndr_err_code ndr_err
;
274 struct smb_acl_t
*acl
= talloc(mem_ctx
, struct smb_acl_t
);
280 ndr_err
= ndr_pull_struct_blob(blob
, acl
, acl
,
281 (ndr_pull_flags_fn_t
)ndr_pull_smb_acl_t
);
283 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
284 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
285 ndr_errstr(ndr_err
)));
292 static DATA_BLOB
fake_acls_acl2blob(TALLOC_CTX
*mem_ctx
, SMB_ACL_T acl
)
294 enum ndr_err_code ndr_err
;
296 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, acl
,
297 (ndr_push_flags_fn_t
)ndr_push_smb_acl_t
);
299 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
300 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
301 ndr_errstr(ndr_err
)));
302 return data_blob_null
;
307 static SMB_ACL_T
fake_acls_sys_acl_get_fd(struct vfs_handle_struct
*handle
,
312 DATA_BLOB blob
= data_blob_null
;
314 const char *name
= NULL
;
315 struct smb_acl_t
*acl
= NULL
;
316 TALLOC_CTX
*frame
= talloc_stackframe();
319 case SMB_ACL_TYPE_ACCESS
:
320 name
= FAKE_ACL_ACCESS_XATTR
;
322 case SMB_ACL_TYPE_DEFAULT
:
323 name
= FAKE_ACL_DEFAULT_XATTR
;
326 DBG_ERR("Illegal ACL type %d\n", (int)type
);
337 blob
.data
= talloc_realloc(frame
, blob
.data
, uint8_t, blob
.length
);
343 length
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
);
344 blob
.length
= length
;
345 } while (length
== -1 && errno
== ERANGE
);
346 if (length
== -1 && ((errno
== ENOATTR
) || (errno
== EBADF
))) {
351 acl
= fake_acls_blob2acl(&blob
, mem_ctx
);
357 static int fake_acls_sys_acl_set_fd(vfs_handle_struct
*handle
,
358 struct files_struct
*fsp
,
363 const char *name
= NULL
;
364 TALLOC_CTX
*frame
= talloc_stackframe();
365 DATA_BLOB blob
= fake_acls_acl2blob(frame
, theacl
);
367 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
374 case SMB_ACL_TYPE_ACCESS
:
375 name
= FAKE_ACL_ACCESS_XATTR
;
377 case SMB_ACL_TYPE_DEFAULT
:
378 name
= FAKE_ACL_DEFAULT_XATTR
;
385 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
, 0);
390 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct
*handle
,
391 struct files_struct
*fsp
)
394 const char *name
= FAKE_ACL_DEFAULT_XATTR
;
396 if (!fsp
->fsp_flags
.is_directory
) {
401 ret
= SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
402 if (ret
== -1 && ((errno
== ENOATTR
) || (errno
== EBADF
))) {
410 static int fake_acls_lchown(vfs_handle_struct
*handle
,
411 const struct smb_filename
*smb_fname
,
418 uid_t current_uid
= get_current_uid(handle
->conn
);
420 if (current_uid
!= 0 && current_uid
!= uid
) {
424 /* This isn't quite right (calling setxattr not
425 * lsetxattr), but for the test purposes of this
426 * module (fake NT ACLs from windows clients), it is
427 * close enough. We removed the l*xattr functions
428 * because linux doesn't support using them, but we
429 * could fake them in xattr_tdb if we really wanted
432 SIVAL(id_buf
, 0, uid
);
433 ret
= SMB_VFS_NEXT_FSETXATTR(handle
,
444 SIVAL(id_buf
, 0, gid
);
445 ret
= SMB_VFS_NEXT_FSETXATTR(handle
,
458 static int fake_acls_fchown(vfs_handle_struct
*handle
, files_struct
*fsp
, uid_t uid
, gid_t gid
)
463 uid_t current_uid
= get_current_uid(handle
->conn
);
465 if (current_uid
!= 0 && current_uid
!= uid
) {
469 SIVAL(id_buf
, 0, uid
);
470 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_UID
, id_buf
, sizeof(id_buf
), 0);
476 SIVAL(id_buf
, 0, gid
);
477 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_GID
, id_buf
, sizeof(id_buf
), 0);
486 * Implement the chmod uid/mask/other mode changes on a fake ACL.
489 static int fake_acl_process_chmod(SMB_ACL_T
*pp_the_acl
,
493 bool got_mask
= false;
494 int entry_id
= SMB_ACL_FIRST_ENTRY
;
499 SMB_ACL_T the_acl
= *pp_the_acl
;
501 /* Split the mode into u/mask/other masks. */
502 umode
= unix_perms_to_acl_perms(mode
, S_IRUSR
, S_IWUSR
, S_IXUSR
);
503 mmode
= unix_perms_to_acl_perms(mode
, S_IRGRP
, S_IWGRP
, S_IXGRP
);
504 omode
= unix_perms_to_acl_perms(mode
, S_IROTH
, S_IWOTH
, S_IXOTH
);
507 SMB_ACL_ENTRY_T entry
;
508 SMB_ACL_TAG_T tagtype
;
509 SMB_ACL_PERMSET_T permset
;
512 ret
= sys_acl_get_entry(the_acl
,
523 ret
= sys_acl_get_tag_type(entry
, &tagtype
);
527 ret
= sys_acl_get_permset(entry
, &permset
);
532 case SMB_ACL_USER_OBJ
:
533 ret
= map_acl_perms_to_permset(umode
, &permset
);
539 puid
= (uid_t
*)sys_acl_get_qualifier(entry
);
543 if (owner
!= *puid
) {
546 ret
= map_acl_perms_to_permset(umode
, &permset
);
551 case SMB_ACL_GROUP_OBJ
:
553 /* Ignore all group entries. */
556 ret
= map_acl_perms_to_permset(mmode
, &permset
);
563 ret
= map_acl_perms_to_permset(omode
, &permset
);
572 ret
= sys_acl_set_permset(entry
, permset
);
576 /* Move to next entry. */
577 entry_id
= SMB_ACL_NEXT_ENTRY
;
581 * If we didn't see a mask entry, add one.
585 SMB_ACL_ENTRY_T mask_entry
;
586 uint32_t mask_perm
= 0;
587 SMB_ACL_PERMSET_T mask_permset
= &mask_perm
;
588 ret
= sys_acl_create_entry(&the_acl
, &mask_entry
);
592 ret
= map_acl_perms_to_permset(mmode
, &mask_permset
);
596 ret
= sys_acl_set_permset(mask_entry
, mask_permset
);
600 ret
= sys_acl_set_tag_type(mask_entry
, SMB_ACL_MASK
);
604 /* In case we were realloced and moved. */
605 *pp_the_acl
= the_acl
;
611 static int fake_acls_fchmod(vfs_handle_struct
*handle
,
615 TALLOC_CTX
*frame
= talloc_stackframe();
617 SMB_ACL_T the_acl
= NULL
;
620 * Passthrough first to preserve the
621 * S_ISUID | S_ISGID | S_ISVTX
625 ret
= SMB_VFS_NEXT_FCHMOD(handle
,
633 the_acl
= fake_acls_sys_acl_get_fd(handle
,
637 if (the_acl
== NULL
) {
639 if (((errno
== ENOATTR
) || (errno
== EBADF
))) {
640 /* No ACL on this file. Just passthrough. */
645 ret
= fake_acl_process_chmod(&the_acl
,
646 fsp
->fsp_name
->st
.st_ex_uid
,
652 ret
= fake_acls_sys_acl_set_fd(handle
,
660 static int fake_acls_connect(struct vfs_handle_struct
*handle
,
664 struct in_pathref_data
*prd
= NULL
;
667 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
672 * Create a struct can tell us if we're recursing
673 * into openat_pathref_fsp() in this module. This will
674 * go away once we have SMB_VFS_STATX() and we will
675 * have a way for a caller to as for specific stat
676 * fields in a granular way. Then we will know exactly
677 * what fields the caller wants, so we won't have to
678 * fill in everything.
680 prd
= talloc_zero(handle
->conn
, struct in_pathref_data
);
684 SMB_VFS_HANDLE_SET_DATA(handle
,
687 struct in_pathref_data
,
692 static struct vfs_fn_pointers vfs_fake_acls_fns
= {
693 .connect_fn
= fake_acls_connect
,
694 .stat_fn
= fake_acls_stat
,
695 .lstat_fn
= fake_acls_lstat
,
696 .fstat_fn
= fake_acls_fstat
,
697 .fchmod_fn
= fake_acls_fchmod
,
698 .sys_acl_get_fd_fn
= fake_acls_sys_acl_get_fd
,
699 .sys_acl_blob_get_fd_fn
= posix_sys_acl_blob_get_fd
,
700 .sys_acl_set_fd_fn
= fake_acls_sys_acl_set_fd
,
701 .sys_acl_delete_def_fd_fn
= fake_acls_sys_acl_delete_def_fd
,
702 .lchown_fn
= fake_acls_lchown
,
703 .fchown_fn
= fake_acls_fchown
,
708 NTSTATUS
vfs_fake_acls_init(TALLOC_CTX
*ctx
)
710 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fake_acls",