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 static int fake_acls_uid(vfs_handle_struct
*handle
,
39 struct smb_filename
*smb_fname
,
44 size
= SMB_VFS_NEXT_GETXATTR(handle
, smb_fname
,
45 FAKE_UID
, uid_buf
, sizeof(uid_buf
));
46 if (size
== -1 && errno
== ENOATTR
) {
52 *uid
= IVAL(uid_buf
, 0);
56 static int fake_acls_gid(vfs_handle_struct
*handle
,
57 struct smb_filename
*smb_fname
,
63 size
= SMB_VFS_NEXT_GETXATTR(handle
, smb_fname
,
64 FAKE_GID
, gid_buf
, sizeof(gid_buf
));
65 if (size
== -1 && errno
== ENOATTR
) {
71 *gid
= IVAL(gid_buf
, 0);
75 static int fake_acls_fuid(vfs_handle_struct
*handle
,
82 size
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, FAKE_UID
, uid_buf
, sizeof(uid_buf
));
83 if (size
== -1 && errno
== ENOATTR
) {
89 *uid
= IVAL(uid_buf
, 0);
93 static int fake_acls_fgid(vfs_handle_struct
*handle
,
100 size
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, FAKE_GID
, gid_buf
, sizeof(gid_buf
));
101 if (size
== -1 && errno
== ENOATTR
) {
107 *gid
= IVAL(gid_buf
, 0);
111 static int fake_acls_stat(vfs_handle_struct
*handle
,
112 struct smb_filename
*smb_fname
)
116 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
118 TALLOC_CTX
*frame
= talloc_stackframe();
120 struct smb_filename smb_fname_base
= {
121 .base_name
= smb_fname
->base_name
125 * As we're calling getxattr directly here
126 * we need to use only the base_name, not
127 * the full name containing any stream name.
129 status
= get_full_smb_filename(frame
, &smb_fname_base
, &path
);
130 if (!NT_STATUS_IS_OK(status
)) {
131 errno
= map_errno_from_nt_status(status
);
136 ret
= fake_acls_uid(handle
, &smb_fname_base
,
137 &smb_fname
->st
.st_ex_uid
);
142 ret
= fake_acls_gid(handle
, &smb_fname_base
,
143 &smb_fname
->st
.st_ex_gid
);
154 static int fake_acls_lstat(vfs_handle_struct
*handle
,
155 struct smb_filename
*smb_fname
)
159 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
161 TALLOC_CTX
*frame
= talloc_stackframe();
163 struct smb_filename smb_fname_base
= {
164 .base_name
= smb_fname
->base_name
168 * As we're calling getxattr directly here
169 * we need to use only the base_name, not
170 * the full name containing any stream name.
172 status
= get_full_smb_filename(frame
, &smb_fname_base
, &path
);
173 if (!NT_STATUS_IS_OK(status
)) {
174 errno
= map_errno_from_nt_status(status
);
179 /* This isn't quite right (calling getxattr not
180 * lgetxattr), but for the test purposes of this
181 * module (fake NT ACLs from windows clients), it is
182 * close enough. We removed the l*xattr functions
183 * because linux doesn't support using them, but we
184 * could fake them in xattr_tdb if we really wanted
185 * to. We ignore errors because the link might not point anywhere */
186 fake_acls_uid(handle
, &smb_fname_base
,
187 &smb_fname
->st
.st_ex_uid
);
188 fake_acls_gid(handle
, &smb_fname_base
,
189 &smb_fname
->st
.st_ex_gid
);
196 static int fake_acls_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
200 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
202 ret
= fake_acls_fuid(handle
, fsp
, &sbuf
->st_ex_uid
);
206 ret
= fake_acls_fgid(handle
, fsp
, &sbuf
->st_ex_gid
);
214 static SMB_ACL_T
fake_acls_blob2acl(DATA_BLOB
*blob
, TALLOC_CTX
*mem_ctx
)
216 enum ndr_err_code ndr_err
;
217 struct smb_acl_t
*acl
= talloc(mem_ctx
, struct smb_acl_t
);
223 ndr_err
= ndr_pull_struct_blob(blob
, acl
, acl
,
224 (ndr_pull_flags_fn_t
)ndr_pull_smb_acl_t
);
226 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
227 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
228 ndr_errstr(ndr_err
)));
235 static DATA_BLOB
fake_acls_acl2blob(TALLOC_CTX
*mem_ctx
, SMB_ACL_T acl
)
237 enum ndr_err_code ndr_err
;
239 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, acl
,
240 (ndr_push_flags_fn_t
)ndr_push_smb_acl_t
);
242 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
243 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
244 ndr_errstr(ndr_err
)));
245 return data_blob_null
;
250 static SMB_ACL_T
fake_acls_sys_acl_get_file(struct vfs_handle_struct
*handle
,
251 const struct smb_filename
*smb_fname
,
255 DATA_BLOB blob
= data_blob_null
;
257 const char *name
= NULL
;
258 struct smb_acl_t
*acl
= NULL
;
259 TALLOC_CTX
*frame
= talloc_stackframe();
261 case SMB_ACL_TYPE_ACCESS
:
262 name
= FAKE_ACL_ACCESS_XATTR
;
264 case SMB_ACL_TYPE_DEFAULT
:
265 name
= FAKE_ACL_DEFAULT_XATTR
;
271 blob
.data
= talloc_realloc(frame
, blob
.data
, uint8_t, blob
.length
);
277 length
= SMB_VFS_NEXT_GETXATTR(handle
, smb_fname
,
278 name
, blob
.data
, blob
.length
);
279 blob
.length
= length
;
280 } while (length
== -1 && errno
== ERANGE
);
281 if (length
== -1 && errno
== ENOATTR
) {
286 acl
= fake_acls_blob2acl(&blob
, mem_ctx
);
292 static SMB_ACL_T
fake_acls_sys_acl_get_fd(struct vfs_handle_struct
*handle
,
296 DATA_BLOB blob
= data_blob_null
;
298 const char *name
= FAKE_ACL_ACCESS_XATTR
;
299 struct smb_acl_t
*acl
= NULL
;
300 TALLOC_CTX
*frame
= talloc_stackframe();
304 blob
.data
= talloc_realloc(frame
, blob
.data
, uint8_t, blob
.length
);
310 length
= SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
);
311 blob
.length
= length
;
312 } while (length
== -1 && errno
== ERANGE
);
313 if (length
== -1 && errno
== ENOATTR
) {
318 acl
= fake_acls_blob2acl(&blob
, mem_ctx
);
325 static int fake_acls_sys_acl_set_file(vfs_handle_struct
*handle
,
326 const struct smb_filename
*smb_fname
,
327 SMB_ACL_TYPE_T acltype
,
331 const char *name
= NULL
;
332 TALLOC_CTX
*frame
= talloc_stackframe();
333 DATA_BLOB blob
= fake_acls_acl2blob(frame
, theacl
);
335 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
341 case SMB_ACL_TYPE_ACCESS
:
342 name
= FAKE_ACL_ACCESS_XATTR
;
344 case SMB_ACL_TYPE_DEFAULT
:
345 name
= FAKE_ACL_DEFAULT_XATTR
;
348 ret
= SMB_VFS_NEXT_SETXATTR(handle
, smb_fname
,
349 name
, blob
.data
, blob
.length
, 0);
354 static int fake_acls_sys_acl_set_fd(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_ACL_T theacl
)
357 const char *name
= FAKE_ACL_ACCESS_XATTR
;
358 TALLOC_CTX
*frame
= talloc_stackframe();
359 DATA_BLOB blob
= fake_acls_acl2blob(frame
, theacl
);
361 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
366 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, blob
.data
, blob
.length
, 0);
371 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct
*handle
,
372 const struct smb_filename
*smb_fname_in
)
375 const char *name
= FAKE_ACL_DEFAULT_XATTR
;
376 TALLOC_CTX
*frame
= talloc_stackframe();
377 struct smb_filename
*smb_fname
= cp_smb_filename_nostream(talloc_tos(),
380 if (smb_fname
== NULL
) {
386 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
392 if (!S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
398 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, smb_fname
, name
);
399 if (ret
== -1 && errno
== ENOATTR
) {
408 static int fake_acls_chown(vfs_handle_struct
*handle
,
409 const struct smb_filename
*smb_fname
,
416 uid_t current_uid
= get_current_uid(handle
->conn
);
418 if (current_uid
!= 0 && current_uid
!= uid
) {
422 SIVAL(id_buf
, 0, uid
);
423 ret
= SMB_VFS_NEXT_SETXATTR(handle
,
434 SIVAL(id_buf
, 0, gid
);
435 ret
= SMB_VFS_NEXT_SETXATTR(handle
,
448 static int fake_acls_lchown(vfs_handle_struct
*handle
,
449 const struct smb_filename
*smb_fname
,
456 uid_t current_uid
= get_current_uid(handle
->conn
);
458 if (current_uid
!= 0 && current_uid
!= uid
) {
462 /* This isn't quite right (calling setxattr not
463 * lsetxattr), but for the test purposes of this
464 * module (fake NT ACLs from windows clients), it is
465 * close enough. We removed the l*xattr functions
466 * because linux doesn't support using them, but we
467 * could fake them in xattr_tdb if we really wanted
470 SIVAL(id_buf
, 0, uid
);
471 ret
= SMB_VFS_NEXT_SETXATTR(handle
,
482 SIVAL(id_buf
, 0, gid
);
483 ret
= SMB_VFS_NEXT_SETXATTR(handle
,
496 static int fake_acls_fchown(vfs_handle_struct
*handle
, files_struct
*fsp
, uid_t uid
, gid_t gid
)
501 uid_t current_uid
= get_current_uid(handle
->conn
);
503 if (current_uid
!= 0 && current_uid
!= uid
) {
507 SIVAL(id_buf
, 0, uid
);
508 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_UID
, id_buf
, sizeof(id_buf
), 0);
514 SIVAL(id_buf
, 0, gid
);
515 ret
= SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, FAKE_GID
, id_buf
, sizeof(id_buf
), 0);
524 * Implement the chmod uid/mask/other mode changes on a fake ACL.
527 static int fake_acl_process_chmod(SMB_ACL_T
*pp_the_acl
,
531 bool got_mask
= false;
532 int entry_id
= SMB_ACL_FIRST_ENTRY
;
537 SMB_ACL_T the_acl
= *pp_the_acl
;
539 /* Split the mode into u/mask/other masks. */
540 umode
= unix_perms_to_acl_perms(mode
, S_IRUSR
, S_IWUSR
, S_IXUSR
);
541 mmode
= unix_perms_to_acl_perms(mode
, S_IRGRP
, S_IWGRP
, S_IXGRP
);
542 omode
= unix_perms_to_acl_perms(mode
, S_IROTH
, S_IWOTH
, S_IXOTH
);
545 SMB_ACL_ENTRY_T entry
;
546 SMB_ACL_TAG_T tagtype
;
547 SMB_ACL_PERMSET_T permset
;
550 ret
= sys_acl_get_entry(the_acl
,
561 ret
= sys_acl_get_tag_type(entry
, &tagtype
);
565 ret
= sys_acl_get_permset(entry
, &permset
);
570 case SMB_ACL_USER_OBJ
:
571 ret
= map_acl_perms_to_permset(umode
, &permset
);
577 puid
= (uid_t
*)sys_acl_get_qualifier(entry
);
581 if (owner
!= *puid
) {
584 ret
= map_acl_perms_to_permset(umode
, &permset
);
589 case SMB_ACL_GROUP_OBJ
:
591 /* Ignore all group entries. */
594 ret
= map_acl_perms_to_permset(mmode
, &permset
);
601 ret
= map_acl_perms_to_permset(omode
, &permset
);
610 ret
= sys_acl_set_permset(entry
, permset
);
614 /* Move to next entry. */
615 entry_id
= SMB_ACL_NEXT_ENTRY
;
619 * If we didn't see a mask entry, add one.
623 SMB_ACL_ENTRY_T mask_entry
;
624 SMB_ACL_PERMSET_T mask_permset
;
625 ret
= sys_acl_create_entry(&the_acl
, &mask_entry
);
629 ret
= map_acl_perms_to_permset(mmode
, &mask_permset
);
633 ret
= sys_acl_set_permset(mask_entry
, mask_permset
);
637 ret
= sys_acl_set_tag_type(mask_entry
, SMB_ACL_MASK
);
641 /* In case we were realloced and moved. */
642 *pp_the_acl
= the_acl
;
648 static int fake_acls_chmod(vfs_handle_struct
*handle
,
649 const struct smb_filename
*smb_fname_in
,
652 TALLOC_CTX
*frame
= talloc_stackframe();
654 SMB_ACL_T the_acl
= NULL
;
655 struct smb_filename
*smb_fname
= cp_smb_filename_nostream(talloc_tos(),
658 if (smb_fname
== NULL
) {
664 * Passthrough first to preserve the
665 * S_ISUID | S_ISGID | S_ISVTX
669 ret
= SMB_VFS_NEXT_CHMOD(handle
,
677 the_acl
= fake_acls_sys_acl_get_file(handle
,
681 if (the_acl
== NULL
) {
683 if (errno
== ENOATTR
) {
684 /* No ACL on this file. Just passthrough. */
689 ret
= fake_acl_process_chmod(&the_acl
,
690 smb_fname
->st
.st_ex_uid
,
696 ret
= fake_acls_sys_acl_set_file(handle
,
704 static int fake_acls_fchmod(vfs_handle_struct
*handle
,
708 TALLOC_CTX
*frame
= talloc_stackframe();
710 SMB_ACL_T the_acl
= NULL
;
713 * Passthrough first to preserve the
714 * S_ISUID | S_ISGID | S_ISVTX
718 ret
= SMB_VFS_NEXT_FCHMOD(handle
,
726 the_acl
= fake_acls_sys_acl_get_fd(handle
,
729 if (the_acl
== NULL
) {
731 if (errno
== ENOATTR
) {
732 /* No ACL on this file. Just passthrough. */
737 ret
= fake_acl_process_chmod(&the_acl
,
738 fsp
->fsp_name
->st
.st_ex_uid
,
744 ret
= fake_acls_sys_acl_set_fd(handle
,
751 static struct vfs_fn_pointers vfs_fake_acls_fns
= {
752 .stat_fn
= fake_acls_stat
,
753 .lstat_fn
= fake_acls_lstat
,
754 .fstat_fn
= fake_acls_fstat
,
755 .chmod_fn
= fake_acls_chmod
,
756 .fchmod_fn
= fake_acls_fchmod
,
757 .sys_acl_get_file_fn
= fake_acls_sys_acl_get_file
,
758 .sys_acl_get_fd_fn
= fake_acls_sys_acl_get_fd
,
759 .sys_acl_blob_get_file_fn
= posix_sys_acl_blob_get_file
,
760 .sys_acl_blob_get_fd_fn
= posix_sys_acl_blob_get_fd
,
761 .sys_acl_set_file_fn
= fake_acls_sys_acl_set_file
,
762 .sys_acl_set_fd_fn
= fake_acls_sys_acl_set_fd
,
763 .sys_acl_delete_def_file_fn
= fake_acls_sys_acl_delete_def_file
,
764 .chown_fn
= fake_acls_chown
,
765 .lchown_fn
= fake_acls_lchown
,
766 .fchown_fn
= fake_acls_fchown
,
771 NTSTATUS
vfs_fake_acls_init(TALLOC_CTX
*ctx
)
773 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fake_acls",