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);
324 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle,
325 struct files_struct *fsp,
330 const char *name = NULL;
331 TALLOC_CTX *frame = talloc_stackframe();
332 DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
334 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;
352 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
357 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle,
358 const struct smb_filename *smb_fname)
361 const char *name = FAKE_ACL_DEFAULT_XATTR;
363 if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
368 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, smb_fname->fsp, name);
369 if (ret == -1 && errno == ENOATTR) {
377 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct *handle,
378 struct files_struct *fsp)
381 const char *name = FAKE_ACL_DEFAULT_XATTR;
383 if (!fsp->fsp_flags.is_directory) {
388 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
389 if (ret == -1 && errno == ENOATTR) {
397 static int fake_acls_lchown(vfs_handle_struct *handle,
398 const struct smb_filename *smb_fname,
405 uid_t current_uid = get_current_uid(handle->conn);
407 if (current_uid != 0 && current_uid != uid) {
411 /* This isn't quite right (calling setxattr not
412 * lsetxattr), but for the test purposes of this
413 * module (fake NT ACLs from windows clients), it is
414 * close enough. We removed the l*xattr functions
415 * because linux doesn't support using them, but we
416 * could fake them in xattr_tdb if we really wanted
419 SIVAL(id_buf, 0, uid);
420 ret = SMB_VFS_NEXT_FSETXATTR(handle,
431 SIVAL(id_buf, 0, gid);
432 ret = SMB_VFS_NEXT_FSETXATTR(handle,
445 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
450 uid_t current_uid = get_current_uid(handle->conn);
452 if (current_uid != 0 && current_uid != uid) {
456 SIVAL(id_buf, 0, uid);
457 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
463 SIVAL(id_buf, 0, gid);
464 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
473 * Implement the chmod uid/mask/other mode changes on a fake ACL.
476 static int fake_acl_process_chmod(SMB_ACL_T *pp_the_acl,
480 bool got_mask = false;
481 int entry_id = SMB_ACL_FIRST_ENTRY;
486 SMB_ACL_T the_acl = *pp_the_acl;
488 /* Split the mode into u/mask/other masks. */
489 umode = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
490 mmode = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
491 omode = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
494 SMB_ACL_ENTRY_T entry;
495 SMB_ACL_TAG_T tagtype;
496 SMB_ACL_PERMSET_T permset;
499 ret = sys_acl_get_entry(the_acl,
510 ret = sys_acl_get_tag_type(entry, &tagtype);
514 ret = sys_acl_get_permset(entry, &permset);
519 case SMB_ACL_USER_OBJ:
520 ret = map_acl_perms_to_permset(umode, &permset);
526 puid = (uid_t *)sys_acl_get_qualifier(entry);
530 if (owner != *puid) {
533 ret = map_acl_perms_to_permset(umode, &permset);
538 case SMB_ACL_GROUP_OBJ:
540 /* Ignore all group entries. */
543 ret = map_acl_perms_to_permset(mmode, &permset);
550 ret = map_acl_perms_to_permset(omode, &permset);
559 ret = sys_acl_set_permset(entry, permset);
563 /* Move to next entry. */
564 entry_id = SMB_ACL_NEXT_ENTRY;
568 * If we didn't see a mask entry, add one.
572 SMB_ACL_ENTRY_T mask_entry;
573 SMB_ACL_PERMSET_T mask_permset;
574 ret = sys_acl_create_entry(&the_acl, &mask_entry);
578 ret = map_acl_perms_to_permset(mmode, &mask_permset);
582 ret = sys_acl_set_permset(mask_entry, mask_permset);
586 ret = sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK);
590 /* In case we were realloced and moved. */
591 *pp_the_acl = the_acl;
597 static int fake_acls_fchmod(vfs_handle_struct *handle,
601 TALLOC_CTX *frame = talloc_stackframe();
603 SMB_ACL_T the_acl = NULL;
606 * Passthrough first to preserve the
607 * S_ISUID | S_ISGID | S_ISVTX
611 ret = SMB_VFS_NEXT_FCHMOD(handle,
619 the_acl = fake_acls_sys_acl_get_fd(handle,
622 if (the_acl == NULL) {
624 if (errno == ENOATTR) {
625 /* No ACL on this file. Just passthrough. */
630 ret = fake_acl_process_chmod(&the_acl,
631 fsp->fsp_name->st.st_ex_uid,
637 ret = fake_acls_sys_acl_set_fd(handle,
645 static struct vfs_fn_pointers vfs_fake_acls_fns = {
646 .stat_fn = fake_acls_stat,
647 .lstat_fn = fake_acls_lstat,
648 .fstat_fn = fake_acls_fstat,
649 .fchmod_fn = fake_acls_fchmod,
650 .sys_acl_get_file_fn = fake_acls_sys_acl_get_file,
651 .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
652 .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
653 .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
654 .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
655 .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file,
656 .sys_acl_delete_def_fd_fn = fake_acls_sys_acl_delete_def_fd,
657 .lchown_fn = fake_acls_lchown,
658 .fchown_fn = fake_acls_fchown,
663 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
665 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",