summaryrefslogtreecommitdiffstats
path: root/source3/modules/vfs_nfs4acl_xattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/modules/vfs_nfs4acl_xattr.c')
-rw-r--r--source3/modules/vfs_nfs4acl_xattr.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/source3/modules/vfs_nfs4acl_xattr.c b/source3/modules/vfs_nfs4acl_xattr.c
new file mode 100644
index 0000000..1fd3519
--- /dev/null
+++ b/source3/modules/vfs_nfs4acl_xattr.c
@@ -0,0 +1,580 @@
+/*
+ * Convert NFSv4 acls stored per http://www.suse.de/~agruen/nfs4acl/ to NT acls and vice versa.
+ *
+ * Copyright (C) Jiri Sasek, 2007
+ * based on the foobar.c module which is copyrighted by Volker Lendecke
+ * based on pvfs_acl_nfs4.c Copyright (C) Andrew Tridgell 2006
+ *
+ * based on vfs_fake_acls:
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) Andrew Bartlett, 2002,2012
+ * Copyright (C) Ralph Boehme 2017
+ *
+ * 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 "system/filesys.h"
+#include "smbd/smbd.h"
+#include "libcli/security/security_token.h"
+#include "libcli/security/dom_sid.h"
+#include "nfs4_acls.h"
+#include "librpc/gen_ndr/ndr_nfs4acl.h"
+#include "nfs4acl_xattr.h"
+#include "nfs4acl_xattr_ndr.h"
+#include "nfs4acl_xattr_xdr.h"
+#include "nfs4acl_xattr_nfs.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+static const struct enum_list nfs4acl_encoding[] = {
+ {NFS4ACL_ENCODING_NDR, "ndr"},
+ {NFS4ACL_ENCODING_XDR, "xdr"},
+ {NFS4ACL_ENCODING_NFS, "nfs"},
+};
+
+/*
+ * Check if someone changed the POSIX mode, for files we expect 0666, for
+ * directories 0777. Discard the ACL blob if the mode is different.
+ */
+static bool nfs4acl_validate_blob(vfs_handle_struct *handle,
+ files_struct *fsp)
+{
+ struct nfs4acl_config *config = NULL;
+ mode_t expected_mode;
+ int ret;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return false);
+
+ if (!config->validate_mode) {
+ return true;
+ }
+
+ if (S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
+ expected_mode = 0777;
+ } else {
+ expected_mode = 0666;
+ }
+ if ((fsp->fsp_name->st.st_ex_mode & expected_mode) == expected_mode) {
+ return true;
+ }
+
+ ret = SMB_VFS_NEXT_FREMOVEXATTR(handle,
+ fsp,
+ config->xattr_name);
+ if (ret != 0 && errno != ENOATTR) {
+ DBG_ERR("Removing NFS4 xattr failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS nfs4acl_get_blob(struct vfs_handle_struct *handle,
+ files_struct *fsp,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct nfs4acl_config *config = NULL;
+ size_t allocsize = 256;
+ ssize_t length;
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ *blob = data_blob_null;
+
+ ok = nfs4acl_validate_blob(handle, fsp);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+
+ allocsize *= 4;
+ ok = data_blob_realloc(mem_ctx, blob, allocsize);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ length = SMB_VFS_NEXT_FGETXATTR(handle,
+ fsp,
+ config->xattr_name,
+ blob->data,
+ blob->length);
+ } while (length == -1 && errno == ERANGE && allocsize <= 65536);
+
+ if (length == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nfs4acl_xattr_default_sd(
+ struct vfs_handle_struct *handle,
+ const struct smb_filename *smb_fname,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ struct nfs4acl_config *config = NULL;
+ enum default_acl_style default_acl_style;
+ mode_t required_mode;
+ SMB_STRUCT_STAT sbuf = smb_fname->st;
+ int ret;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ default_acl_style = config->default_acl_style;
+
+ if (!VALID_STAT(sbuf)) {
+ ret = vfs_stat_smb_basename(handle->conn,
+ smb_fname,
+ &sbuf);
+ if (ret != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (S_ISDIR(sbuf.st_ex_mode)) {
+ required_mode = 0777;
+ } else {
+ required_mode = 0666;
+ }
+ if ((sbuf.st_ex_mode & required_mode) != required_mode) {
+ default_acl_style = DEFAULT_ACL_POSIX;
+ }
+
+ return make_default_filesystem_acl(mem_ctx,
+ default_acl_style,
+ smb_fname->base_name,
+ &sbuf,
+ sd);
+}
+
+static NTSTATUS nfs4acl_blob_to_smb4(struct vfs_handle_struct *handle,
+ DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct SMB4ACL_T **smb4acl)
+{
+ struct nfs4acl_config *config = NULL;
+ NTSTATUS status;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ switch (config->encoding) {
+ case NFS4ACL_ENCODING_NDR:
+ status = nfs4acl_ndr_blob_to_smb4(handle, mem_ctx, blob, smb4acl);
+ break;
+ case NFS4ACL_ENCODING_XDR:
+ status = nfs4acl_xdr_blob_to_smb4(handle, mem_ctx, blob, smb4acl);
+ break;
+ case NFS4ACL_ENCODING_NFS:
+ status = nfs4acl_nfs_blob_to_smb4(handle, mem_ctx, blob, smb4acl);
+ break;
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ return status;
+}
+
+static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ uint32_t security_info,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ struct SMB4ACL_T *smb4acl = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ status = nfs4acl_get_blob(handle, fsp, frame, &blob);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ TALLOC_FREE(frame);
+ return nfs4acl_xattr_default_sd(
+ handle, fsp->fsp_name, mem_ctx, sd);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = nfs4acl_blob_to_smb4(handle, &blob, frame, &smb4acl);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
+ sd, smb4acl);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool nfs4acl_smb4acl_set_fn(vfs_handle_struct *handle,
+ files_struct *fsp,
+ struct SMB4ACL_T *smb4acl)
+{
+ struct nfs4acl_config *config = NULL;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ int saved_errno = 0;
+ int ret;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return false);
+
+ switch (config->encoding) {
+ case NFS4ACL_ENCODING_NDR:
+ status = nfs4acl_smb4acl_to_ndr_blob(handle, talloc_tos(),
+ smb4acl, &blob);
+ break;
+ case NFS4ACL_ENCODING_XDR:
+ status = nfs4acl_smb4acl_to_xdr_blob(handle, talloc_tos(),
+ smb4acl, &blob);
+ break;
+ case NFS4ACL_ENCODING_NFS:
+ status = nfs4acl_smb4acl_to_nfs_blob(handle, talloc_tos(),
+ smb4acl, &blob);
+ break;
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, config->xattr_name,
+ blob.data, blob.length, 0);
+ if (ret != 0) {
+ saved_errno = errno;
+ }
+ data_blob_free(&blob);
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ if (ret != 0) {
+ DBG_ERR("can't store acl in xattr: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
+ files_struct *fsp,
+ uint32_t security_info_sent,
+ const struct security_descriptor *psd)
+{
+ struct nfs4acl_config *config = NULL;
+ const struct security_token *token = NULL;
+ mode_t existing_mode;
+ mode_t expected_mode;
+ mode_t restored_mode;
+ bool chown_needed = false;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+ int ret;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ if (!VALID_STAT(fsp->fsp_name->st)) {
+ DBG_ERR("Invalid stat info on [%s]\n", fsp_str_dbg(fsp));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ existing_mode = fsp->fsp_name->st.st_ex_mode;
+ if (S_ISDIR(existing_mode)) {
+ expected_mode = 0777;
+ } else {
+ expected_mode = 0666;
+ }
+ if (!config->validate_mode) {
+ existing_mode = 0;
+ expected_mode = 0;
+ }
+ if ((existing_mode & expected_mode) != expected_mode) {
+
+ restored_mode = existing_mode | expected_mode;
+
+ ret = SMB_VFS_NEXT_FCHMOD(handle,
+ fsp,
+ restored_mode);
+ if (ret != 0) {
+ DBG_ERR("Resetting POSIX mode on [%s] from [0%o]: %s\n",
+ fsp_str_dbg(fsp), existing_mode,
+ strerror(errno));
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ status = smb_set_nt_acl_nfs4(handle,
+ fsp,
+ &config->nfs4_params,
+ security_info_sent,
+ psd,
+ nfs4acl_smb4acl_set_fn);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ return status;
+ }
+
+ /*
+ * We got access denied. If we're already root, or we didn't
+ * need to do a chown, or the fsp isn't open with WRITE_OWNER
+ * access, just return.
+ */
+
+ if ((security_info_sent & SECINFO_OWNER) &&
+ (psd->owner_sid != NULL))
+ {
+ chown_needed = true;
+ }
+ if ((security_info_sent & SECINFO_GROUP) &&
+ (psd->group_sid != NULL))
+ {
+ chown_needed = true;
+ }
+
+ if (get_current_uid(handle->conn) == 0 ||
+ chown_needed == false)
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Only allow take-ownership, not give-ownership. That's the way Windows
+ * implements SEC_STD_WRITE_OWNER. MS-FSA 2.1.5.16 just states: If
+ * InputBuffer.OwnerSid is not a valid owner SID for a file in the
+ * objectstore, as determined in an implementation specific manner, the
+ * object store MUST return STATUS_INVALID_OWNER.
+ */
+ token = get_current_nttok(fsp->conn);
+ if (!security_token_is_sid(token, psd->owner_sid)) {
+ return NT_STATUS_INVALID_OWNER;
+ }
+
+ DBG_DEBUG("overriding chown on file %s for sid %s\n",
+ fsp_str_dbg(fsp),
+ dom_sid_str_buf(psd->owner_sid, &buf));
+
+ status = smb_set_nt_acl_nfs4(handle,
+ fsp,
+ &config->nfs4_params,
+ security_info_sent,
+ psd,
+ nfs4acl_smb4acl_set_fn);
+ return status;
+}
+
+static int nfs4acl_connect(struct vfs_handle_struct *handle,
+ const char *service,
+ const char *user)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct nfs4acl_config *config = NULL;
+ const struct enum_list *default_acl_style_list = NULL;
+ const char *default_xattr_name = NULL;
+ bool default_validate_mode = true;
+ int enumval;
+ unsigned nfs_version;
+ int ret;
+
+ default_acl_style_list = get_default_acl_style_list();
+
+ config = talloc_zero(handle->conn, struct nfs4acl_config);
+ if (config == NULL) {
+ DBG_ERR("talloc_zero() failed\n");
+ return -1;
+ }
+
+ ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+ if (ret < 0) {
+ TALLOC_FREE(config);
+ return ret;
+ }
+
+ ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
+ if (ret < 0) {
+ TALLOC_FREE(config);
+ return ret;
+ }
+
+ enumval = lp_parm_enum(SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "encoding",
+ nfs4acl_encoding,
+ NFS4ACL_ENCODING_NDR);
+ if (enumval == -1) {
+ DBG_ERR("Invalid \"nfs4acl_xattr:encoding\" parameter\n");
+ return -1;
+ }
+ config->encoding = (enum nfs4acl_encoding)enumval;
+
+ switch (config->encoding) {
+ case NFS4ACL_ENCODING_XDR:
+ default_xattr_name = NFS4ACL_XDR_XATTR_NAME;
+ break;
+ case NFS4ACL_ENCODING_NFS:
+ default_xattr_name = NFS4ACL_NFS_XATTR_NAME;
+ default_validate_mode = false;
+ break;
+ case NFS4ACL_ENCODING_NDR:
+ default:
+ default_xattr_name = NFS4ACL_NDR_XATTR_NAME;
+ break;
+ }
+
+ nfs_version = (unsigned)lp_parm_int(SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "version",
+ 41);
+ switch (nfs_version) {
+ case 40:
+ config->nfs_version = ACL4_XATTR_VERSION_40;
+ break;
+ case 41:
+ config->nfs_version = ACL4_XATTR_VERSION_41;
+ break;
+ default:
+ config->nfs_version = ACL4_XATTR_VERSION_DEFAULT;
+ break;
+ }
+
+ config->default_acl_style = lp_parm_enum(SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "default acl style",
+ default_acl_style_list,
+ DEFAULT_ACL_EVERYONE);
+
+ config->xattr_name = lp_parm_substituted_string(config, lp_sub,
+ SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "xattr_name",
+ default_xattr_name);
+
+ config->nfs4_id_numeric = lp_parm_bool(SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "nfs4_id_numeric",
+ false);
+
+
+ config->validate_mode = lp_parm_bool(SNUM(handle->conn),
+ "nfs4acl_xattr",
+ "validate_mode",
+ default_validate_mode);
+
+ SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct nfs4acl_config,
+ return -1);
+
+ /*
+ * Ensure we have the parameters correct if we're using this module.
+ */
+ DBG_NOTICE("Setting 'inherit acls = true', "
+ "'dos filemode = true', "
+ "'force unknown acl user = true', "
+ "'create mask = 0666', "
+ "'directory mask = 0777' and "
+ "'store dos attributes = yes' "
+ "for service [%s]\n", service);
+
+ lp_do_parameter(SNUM(handle->conn), "inherit acls", "true");
+ lp_do_parameter(SNUM(handle->conn), "dos filemode", "true");
+ lp_do_parameter(SNUM(handle->conn), "force unknown acl user", "true");
+ lp_do_parameter(SNUM(handle->conn), "create mask", "0666");
+ lp_do_parameter(SNUM(handle->conn), "directory mask", "0777");
+ lp_do_parameter(SNUM(handle->conn), "store dos attributes", "yes");
+
+ return 0;
+}
+
+/*
+ As long as Samba does not support an exiplicit method for a module
+ to define conflicting vfs methods, we should override all conflicting
+ methods here. That way, we know we are using the NFSv4 storage
+
+ Function declarations taken from vfs_solarisacl
+*/
+
+static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_fd(vfs_handle_struct *handle,
+ files_struct *fsp,
+ SMB_ACL_TYPE_T type,
+ TALLOC_CTX *mem_ctx)
+{
+ return (SMB_ACL_T)NULL;
+}
+
+static int nfs4acl_xattr_fail__sys_acl_set_fd(vfs_handle_struct *handle,
+ files_struct *fsp,
+ SMB_ACL_TYPE_T type,
+ SMB_ACL_T theacl)
+{
+ return -1;
+}
+
+static int nfs4acl_xattr_fail__sys_acl_delete_def_fd(vfs_handle_struct *handle,
+ files_struct *fsp)
+{
+ return -1;
+}
+
+static int nfs4acl_xattr_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, files_struct *fsp, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob)
+{
+ return -1;
+}
+
+/* VFS operations structure */
+
+static struct vfs_fn_pointers nfs4acl_xattr_fns = {
+ .connect_fn = nfs4acl_connect,
+ .fget_nt_acl_fn = nfs4acl_xattr_fget_nt_acl,
+ .fset_nt_acl_fn = nfs4acl_xattr_fset_nt_acl,
+
+ .sys_acl_get_fd_fn = nfs4acl_xattr_fail__sys_acl_get_fd,
+ .sys_acl_blob_get_fd_fn = nfs4acl_xattr_fail__sys_acl_blob_get_fd,
+ .sys_acl_set_fd_fn = nfs4acl_xattr_fail__sys_acl_set_fd,
+ .sys_acl_delete_def_fd_fn = nfs4acl_xattr_fail__sys_acl_delete_def_fd,
+};
+
+static_decl_vfs;
+NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *ctx)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "nfs4acl_xattr",
+ &nfs4acl_xattr_fns);
+}