diff options
Diffstat (limited to 'source3/modules/vfs_fake_acls.c')
-rw-r--r-- | source3/modules/vfs_fake_acls.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/source3/modules/vfs_fake_acls.c b/source3/modules/vfs_fake_acls.c new file mode 100644 index 0000000000..175d6d2dc4 --- /dev/null +++ b/source3/modules/vfs_fake_acls.c @@ -0,0 +1,374 @@ +/* + * Fake ACLs VFS module. Implements passthrough operation of all VFS + * calls to disk functions, except for file ownership and ACLs, which + * are stored in xattrs. + * + * Copyright (C) Tim Potter, 1999-2000 + * Copyright (C) Alexander Bokovoy, 2002 + * Copyright (C) Andrew Bartlett, 2002,2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbd/smbd.h" +#include "system/filesys.h" +#include "auth.h" +#include "librpc/gen_ndr/ndr_smb_acl.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +#define FAKE_UID "FAKE.uid" +#define FAKE_GID "FAKE.gid" +#define FAKE_ACL_ACCESS_XATTR "FAKE.access_acl" +#define FAKE_ACL_DEFAULT_XATTR "FAKE.default_acl" + +static int fake_acls_uid(vfs_handle_struct *handle, + const char *path, + uid_t *uid) +{ + ssize_t size; + uint8_t uid_buf[4]; + size = SMB_VFS_NEXT_GETXATTR(handle, path, FAKE_UID, uid_buf, sizeof(uid_buf)); + if (size == -1 && errno == ENOATTR) { + return 0; + } + if (size != 4) { + return -1; + } + *uid = IVAL(uid_buf, 0); + return 0; +} + +static int fake_acls_gid(vfs_handle_struct *handle, + const char *path, + uid_t *gid) +{ + ssize_t size; + uint8_t gid_buf[4]; + + size = SMB_VFS_NEXT_GETXATTR(handle, path, FAKE_GID, gid_buf, sizeof(gid_buf)); + if (size == -1 && errno == ENOATTR) { + return 0; + } + if (size != 4) { + return -1; + } + *gid = IVAL(gid_buf, 0); + return 0; +} + +static int fake_acls_fuid(vfs_handle_struct *handle, + files_struct *fsp, + uid_t *uid) +{ + ssize_t size; + uint8_t uid_buf[4]; + + size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf)); + if (size == -1 && errno == ENOATTR) { + return 0; + } + if (size != 4) { + return -1; + } + *uid = IVAL(uid_buf, 0); + return 0; +} + +static int fake_acls_fgid(vfs_handle_struct *handle, + files_struct *fsp, + uid_t *gid) +{ + ssize_t size; + uint8_t gid_buf[4]; + + size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf)); + if (size == -1 && errno == ENOATTR) { + return 0; + } + if (size != 4) { + return -1; + } + *gid = IVAL(gid_buf, 0); + return 0; +} + +static int fake_acls_stat(vfs_handle_struct *handle, + struct smb_filename *smb_fname) +{ + int ret = -1; + + ret = SMB_VFS_NEXT_STAT(handle, smb_fname); + if (ret == 0) { + TALLOC_CTX *frame = talloc_stackframe(); + char *path; + NTSTATUS status; + status = get_full_smb_filename(frame, smb_fname, &path); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + TALLOC_FREE(frame); + return -1; + } + + ret = fake_acls_uid(handle, path, &smb_fname->st.st_ex_uid); + if (ret != 0) { + TALLOC_FREE(frame); + return ret; + } + ret = fake_acls_gid(handle, path, &smb_fname->st.st_ex_gid); + if (ret != 0) { + TALLOC_FREE(frame); + return ret; + } + TALLOC_FREE(frame); + } + + return ret; +} + +static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) +{ + int ret = -1; + + ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); + if (ret == 0) { + ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid); + if (ret != 0) { + return ret; + } + ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid); + if (ret != 0) { + return ret; + } + } + return ret; +} + +static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob) +{ + enum ndr_err_code ndr_err; + /* For now, ACLs are allocated on NULL */ + struct smb_acl_t *acl = talloc(NULL, struct smb_acl_t); + if (!acl) { + errno = ENOMEM; + return NULL; + } + + ndr_err = ndr_pull_struct_blob(blob, acl, acl, + (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_pull_acl_t failed: %s\n", + ndr_errstr(ndr_err))); + TALLOC_FREE(acl); + return NULL; + } + return acl; +} + +static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl, + (ndr_push_flags_fn_t)ndr_push_smb_acl_t); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_push_acl_t failed: %s\n", + ndr_errstr(ndr_err))); + return data_blob_null; + } + return blob; +} + +static SMB_ACL_T fake_acls_sys_acl_get_file(struct vfs_handle_struct *handle, const char *path, SMB_ACL_TYPE_T type) +{ + DATA_BLOB blob = data_blob_null; + ssize_t length; + const char *name = NULL; + struct smb_acl_t *acl = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + switch (type) { + case SMB_ACL_TYPE_ACCESS: + name = FAKE_ACL_ACCESS_XATTR; + break; + case SMB_ACL_TYPE_DEFAULT: + name = FAKE_ACL_DEFAULT_XATTR; + break; + } + + do { + blob.length += 1000; + blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length); + if (!blob.data) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + length = SMB_VFS_NEXT_GETXATTR(handle, path, name, blob.data, blob.length); + blob.length = length; + } while (length == -1 && errno == ERANGE); + if (length == -1 && errno == ENOATTR) { + TALLOC_FREE(frame); + return NULL; + } + if (length != -1) { + acl = fake_acls_blob2acl(&blob); + } + TALLOC_FREE(frame); + return acl; +} + +static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle, files_struct *fsp) +{ + DATA_BLOB blob = data_blob_null; + ssize_t length; + const char *name = FAKE_ACL_ACCESS_XATTR; + struct smb_acl_t *acl; + TALLOC_CTX *frame = talloc_stackframe(); + + do { + blob.length += 1000; + blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length); + if (!blob.data) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length); + blob.length = length; + } while (length == -1 && errno == ERANGE); + if (length == -1 && errno == ENOATTR) { + TALLOC_FREE(frame); + return NULL; + } + if (length != -1) { + acl = fake_acls_blob2acl(&blob); + } + TALLOC_FREE(frame); + return acl; +} + +static int fake_acls_sys_acl_set_file(vfs_handle_struct *handle, const char *path, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + int ret; + const char *name = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB blob = fake_acls_acl2blob(frame, theacl); + if (!blob.data) { + DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n")); + TALLOC_FREE(frame); + errno = EINVAL; + return -1; + } + switch (acltype) { + case SMB_ACL_TYPE_ACCESS: + name = FAKE_ACL_ACCESS_XATTR; + break; + case SMB_ACL_TYPE_DEFAULT: + name = FAKE_ACL_DEFAULT_XATTR; + break; + } + ret = SMB_VFS_NEXT_SETXATTR(handle, path, name, blob.data, blob.length, 0); + TALLOC_FREE(frame); + return ret; +} + +static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl) +{ + int ret; + const char *name = FAKE_ACL_ACCESS_XATTR; + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB blob = fake_acls_acl2blob(frame, theacl); + if (!blob.data) { + DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n")); + TALLOC_FREE(frame); + errno = EINVAL; + return -1; + } + ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0); + TALLOC_FREE(frame); + return ret; +} + +static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle, const char *path) +{ + const char *name = FAKE_ACL_DEFAULT_XATTR; + return SMB_VFS_NEXT_REMOVEXATTR(handle, path, name); +} + +static int fake_acls_chown(vfs_handle_struct *handle, const char *path, uid_t uid, gid_t gid) +{ + int ret; + uint8_t id_buf[4]; + if (uid != -1) { + SIVAL(id_buf, 0, uid); + ret = SMB_VFS_NEXT_SETXATTR(handle, path, FAKE_UID, id_buf, sizeof(id_buf), 0); + if (ret != 0) { + return ret; + } + } + if (gid != -1) { + SIVAL(id_buf, 0, gid); + ret = SMB_VFS_NEXT_SETXATTR(handle, path, FAKE_GID, id_buf, sizeof(id_buf), 0); + if (ret != 0) { + return ret; + } + } + return 0; +} + +static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid) +{ + int ret; + uint8_t id_buf[4]; + if (uid != -1) { + SIVAL(id_buf, 0, uid); + ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0); + if (ret != 0) { + return ret; + } + } + if (gid != -1) { + SIVAL(id_buf, 0, gid); + ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0); + if (ret != 0) { + return ret; + } + } + return 0; +} + + +static struct vfs_fn_pointers vfs_fake_acls_fns = { + .stat_fn = fake_acls_stat, + .fstat_fn = fake_acls_fstat, + .sys_acl_get_file_fn = fake_acls_sys_acl_get_file, + .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd, + .sys_acl_set_file_fn = fake_acls_sys_acl_set_file, + .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd, + .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file, + .chown_fn = fake_acls_chown, + .fchown_fn = fake_acls_fchown, + +}; + +NTSTATUS vfs_fake_acls_init(void); +NTSTATUS vfs_fake_acls_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls", + &vfs_fake_acls_fns); +} |