s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source3 / modules / vfs_nfs4acl_xattr.c
index 7465ba8afbf11bbc3ccf0733e7189f227f9cde08..1fd3519ca020c889828dfb4edf2d53d461101651 100644 (file)
@@ -9,6 +9,7 @@
  * 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
 #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 struct nfs4acl *nfs4acl_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
+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)
 {
-       enum ndr_err_code ndr_err;
-       struct nfs4acl *acl = talloc(mem_ctx, struct nfs4acl);
-       if (!acl) {
-               errno = ENOMEM;
-               return NULL;
-       }
+       struct nfs4acl_config *config = NULL;
+       mode_t expected_mode;
+       int ret;
 
-       ndr_err = ndr_pull_struct_blob(blob, acl, acl,
-               (ndr_pull_flags_fn_t)ndr_pull_nfs4acl);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct nfs4acl_config,
+                               return false);
 
-       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;
+       if (!config->validate_mode) {
+               return true;
        }
-       return acl;
-}
 
-static DATA_BLOB nfs4acl_acl2blob(TALLOC_CTX *mem_ctx, struct nfs4acl *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_nfs4acl);
-
-       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;
+       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;
        }
-       return blob;
-}
-
-static NTSTATUS nfs4_get_nfs4_acl_common(TALLOC_CTX *mem_ctx,
-                                        DATA_BLOB *blob,
-                                        struct SMB4ACL_T **ppacl)
-{
-       int i;
-       struct nfs4acl *nfs4acl = NULL;
-       struct SMB4ACL_T *pacl = NULL;
-       TALLOC_CTX *frame = talloc_stackframe();
-       nfs4acl = nfs4acl_blob2acl(blob, frame);
 
-       /* create SMB4ACL data */
-       if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
-       for(i=0; i<nfs4acl->a_count; i++) {
-               SMB_ACE4PROP_T aceprop;
-
-               aceprop.aceType  = (uint32_t) nfs4acl->ace[i].e_type;
-               aceprop.aceFlags = (uint32_t) nfs4acl->ace[i].e_flags;
-               aceprop.aceMask  = (uint32_t) nfs4acl->ace[i].e_mask;
-               aceprop.who.id   = (uint32_t) nfs4acl->ace[i].e_id;
-               if (!strcmp(nfs4acl->ace[i].e_who,
-                           NFS4ACL_XATTR_OWNER_WHO)) {
-                       aceprop.flags = SMB_ACE4_ID_SPECIAL;
-                       aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
-               } else if (!strcmp(nfs4acl->ace[i].e_who,
-                                  NFS4ACL_XATTR_GROUP_WHO)) {
-                       aceprop.flags = SMB_ACE4_ID_SPECIAL;
-                       aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
-               } else if (!strcmp(nfs4acl->ace[i].e_who,
-                                  NFS4ACL_XATTR_EVERYONE_WHO)) {
-                       aceprop.flags = SMB_ACE4_ID_SPECIAL;
-                       aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
-               } else {
-                       aceprop.flags = 0;
-               }
-               if(smb_add_ace4(pacl, &aceprop) == NULL) {
-                       TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
-               }
+       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;
        }
 
-       *ppacl = pacl;
-       TALLOC_FREE(frame);
-       return NT_STATUS_OK;
+       return true;
 }
 
-/* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
-static NTSTATUS nfs4_fget_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
-                                  files_struct *fsp, struct SMB4ACL_T **ppacl)
+static NTSTATUS nfs4acl_get_blob(struct vfs_handle_struct *handle,
+                                files_struct *fsp,
+                                TALLOC_CTX *mem_ctx,
+                                DATA_BLOB *blob)
 {
-       NTSTATUS status;
-       DATA_BLOB blob = data_blob_null;
+       struct nfs4acl_config *config = NULL;
+       size_t allocsize = 256;
        ssize_t length;
-       TALLOC_CTX *frame = talloc_stackframe();
+       bool ok;
 
-       do {
-               blob.length += 1000;
-               blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
-               if (!blob.data) {
-                       TALLOC_FREE(frame);
-                       errno = ENOMEM;
-                       return NT_STATUS_NO_MEMORY;
-               }
-               length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, NFS4ACL_XATTR_NAME, blob.data, blob.length);
-               blob.length = length;
-       } while (length == -1 && errno == ERANGE);
-       if (length == -1) {
-               TALLOC_FREE(frame);
-               return map_nt_error_from_unix(errno);
-       }
-       status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
-       TALLOC_FREE(frame);
-       return status;
-}
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct nfs4acl_config,
+                               return NT_STATUS_INTERNAL_ERROR);
 
-/* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
-static NTSTATUS nfs4_get_nfs4_acl(vfs_handle_struct *handle,
-                               TALLOC_CTX *mem_ctx,
-                               const struct smb_filename *smb_fname,
-                               struct SMB4ACL_T **ppacl)
-{
-       NTSTATUS status;
-       DATA_BLOB blob = data_blob_null;
-       ssize_t length;
-       TALLOC_CTX *frame = talloc_stackframe();
+       *blob = data_blob_null;
+
+       ok = nfs4acl_validate_blob(handle, fsp);
+       if (!ok) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
        do {
-               blob.length += 1000;
-               blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
-               if (!blob.data) {
-                       TALLOC_FREE(frame);
-                       errno = ENOMEM;
+
+               allocsize *= 4;
+               ok = data_blob_realloc(mem_ctx, blob, allocsize);
+               if (!ok) {
                        return NT_STATUS_NO_MEMORY;
                }
-               length = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
-                               NFS4ACL_XATTR_NAME, blob.data, blob.length);
-               blob.length = length;
-       } while (length == -1 && errno == ERANGE);
+
+               length = SMB_VFS_NEXT_FGETXATTR(handle,
+                                               fsp,
+                                               config->xattr_name,
+                                               blob->data,
+                                               blob->length);
+       } while (length == -1 && errno == ERANGE && allocsize <= 65536);
+
        if (length == -1) {
-               TALLOC_FREE(frame);
                return map_nt_error_from_unix(errno);
        }
-       status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
-       TALLOC_FREE(frame);
-       return status;
+
+       return NT_STATUS_OK;
 }
 
-static bool nfs4acl_smb4acl2nfs4acl(TALLOC_CTX *mem_ctx,
-                                   struct SMB4ACL_T *smbacl,
-                                   struct nfs4acl **pnfs4acl,
-                                   bool denymissingspecial)
+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 *nfs4acl;
-       struct SMB4ACE_T *smbace;
-       bool have_special_id = false;
-       int i;
-
-       /* allocate the field of NFS4 aces */
-       nfs4acl = talloc_zero(mem_ctx, struct nfs4acl);
-       if(nfs4acl == NULL) {
-               errno = ENOMEM;
-               return false;
-       }
+       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;
 
-       nfs4acl->a_count = smb_get_naces(smbacl);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct nfs4acl_config,
+                               return NT_STATUS_INTERNAL_ERROR);
 
-       nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace,
-                                        nfs4acl->a_count);
-       if(nfs4acl->ace == NULL) {
-               TALLOC_FREE(nfs4acl);
-               errno = ENOMEM;
-               return false;
-       }
+       default_acl_style = config->default_acl_style;
 
-       /* handle all aces */
-       for(smbace = smb_first_ace4(smbacl), i = 0;
-                       smbace!=NULL;
-                       smbace = smb_next_ace4(smbace), i++) {
-               SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
-
-               nfs4acl->ace[i].e_type        = aceprop->aceType;
-               nfs4acl->ace[i].e_flags       = aceprop->aceFlags;
-               nfs4acl->ace[i].e_mask        = aceprop->aceMask;
-               nfs4acl->ace[i].e_id          = aceprop->who.id;
-               if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
-                       switch(aceprop->who.special_id) {
-                       case SMB_ACE4_WHO_EVERYONE:
-                               nfs4acl->ace[i].e_who =
-                                       NFS4ACL_XATTR_EVERYONE_WHO;
-                               break;
-                       case SMB_ACE4_WHO_OWNER:
-                               nfs4acl->ace[i].e_who =
-                                       NFS4ACL_XATTR_OWNER_WHO;
-                               break;
-                       case SMB_ACE4_WHO_GROUP:
-                               nfs4acl->ace[i].e_who =
-                                       NFS4ACL_XATTR_GROUP_WHO;
-                               break;
-                       default:
-                               DEBUG(8, ("unsupported special_id %d\n", \
-                                       aceprop->who.special_id));
-                               continue; /* don't add it !!! */
-                       }
-                       have_special_id = true;
-               } else {
-                       nfs4acl->ace[i].e_who = "";
+       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 (!have_special_id && denymissingspecial) {
-               TALLOC_FREE(nfs4acl);
-               errno = EACCES;
-               return false;
+       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;
        }
 
-       SMB_ASSERT(i == nfs4acl->a_count);
+       return make_default_filesystem_acl(mem_ctx,
+                                          default_acl_style,
+                                          smb_fname->base_name,
+                                          &sbuf,
+                                          sd);
+}
 
-       *pnfs4acl = nfs4acl;
-       return true;
+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 bool nfs4acl_xattr_set_smb4acl(vfs_handle_struct *handle,
-                                     const struct smb_filename *smb_fname,
-                                     struct SMB4ACL_T *smbacl)
+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();
-       struct nfs4acl *nfs4acl;
-       int ret;
-       bool denymissingspecial;
        DATA_BLOB blob;
+       NTSTATUS status;
 
-       denymissingspecial = lp_parm_bool(handle->conn->params->service,
-                                         "nfs4acl_xattr",
-                                         "denymissingspecial", false);
-
-       if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
-                                    denymissingspecial)) {
-               DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
+       status = nfs4acl_get_blob(handle, fsp, frame, &blob);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
                TALLOC_FREE(frame);
-               return false;
+               return nfs4acl_xattr_default_sd(
+                       handle, fsp->fsp_name, mem_ctx, sd);
        }
-
-       blob = nfs4acl_acl2blob(frame, nfs4acl);
-       if (!blob.data) {
-               DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
+       if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
-               errno = EINVAL;
-               return false;
+               return status;
        }
-       ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname, NFS4ACL_XATTR_NAME,
-                                   blob.data, blob.length, 0);
-       if (ret != 0) {
-               DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
+
+       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 ret == 0;
+       return status;
 }
 
-/* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
-static bool nfs4acl_xattr_fset_smb4acl(vfs_handle_struct *handle,
-                                      files_struct *fsp,
-                                      struct SMB4ACL_T *smbacl)
+static bool nfs4acl_smb4acl_set_fn(vfs_handle_struct *handle,
+                                  files_struct *fsp,
+                                  struct SMB4ACL_T *smb4acl)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct nfs4acl *nfs4acl;
-       int ret;
-       bool denymissingspecial;
+       struct nfs4acl_config *config = NULL;
        DATA_BLOB blob;
+       NTSTATUS status;
+       int saved_errno = 0;
+       int ret;
 
-       denymissingspecial = lp_parm_bool(fsp->conn->params->service,
-                                         "nfs4acl_xattr",
-                                         "denymissingspecial", false);
-
-       if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
-                                    denymissingspecial)) {
-               DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
-               TALLOC_FREE(frame);
+       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;
        }
 
-       blob = nfs4acl_acl2blob(frame, nfs4acl);
-       if (!blob.data) {
-               DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
-               TALLOC_FREE(frame);
-               errno = EINVAL;
-               return false;
+       ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, config->xattr_name,
+                                    blob.data, blob.length, 0);
+       if (ret != 0) {
+               saved_errno = errno;
        }
-       if (fsp->fh->fd == -1) {
-               DEBUG(0, ("Error: fsp->fh->fd == -1\n"));
+       data_blob_free(&blob);
+       if (saved_errno != 0) {
+               errno = saved_errno;
        }
-       ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_XATTR_NAME,
-                                    blob.data, blob.length, 0);
        if (ret != 0) {
-               DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
+               DBG_ERR("can't store acl in xattr: %s\n", strerror(errno));
+               return false;
        }
-       TALLOC_FREE(frame);
-       return ret == 0;
-}
 
-static struct SMB4ACL_T *nfs4acls_defaultacl(TALLOC_CTX *mem_ctx)
-{
-       struct SMB4ACL_T *pacl = NULL;
-       struct SMB4ACE_T *pace;
-       SMB_ACE4PROP_T ace = {
-               .flags = SMB_ACE4_ID_SPECIAL,
-               .who = {
-                       .id = SMB_ACE4_WHO_EVERYONE,
-               },
-               .aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE,
-               .aceFlags = 0,
-               .aceMask = SMB_ACE4_ALL_MASKS,
-       };
-
-       DEBUG(10, ("Building default full access acl\n"));
-
-       pacl = smb_create_smb4acl(mem_ctx);
-       if (pacl == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       pace = smb_add_ace4(pacl, &ace);
-       if (pace == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               TALLOC_FREE(pacl);
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       return pacl;
+       return true;
 }
 
-/*
- * Because there is no good way to guarantee that a new xattr will be
- * created on file creation there might be no acl xattr on a file when
- * trying to read the acl. In this case the acl xattr will get
- * constructed at that time from the parent acl.
- * If the parent ACL doesn't have an xattr either the call will
- * recurse to the next parent directory until the share root is
- * reached. If the share root doesn't contain an ACL xattr either a
- * default ACL will be used.
- * Also a default ACL will be set if a non inheriting ACL is encountered.
- *
- * Basic algorithm:
- *   read acl xattr blob
- *   if acl xattr blob doesn't exist
- *     stat current directory to know if it's a file or directory
- *     read acl xattr blob from parent dir
- *     acl xattr blob to smb nfs4 acl
- *     calculate inherited smb nfs4 acl
- *     without inheritance use default smb nfs4 acl
- *     smb nfs4 acl to acl xattr blob
- *     set acl xattr blob
- *     return smb nfs4 acl
- *   else
- *     acl xattr blob to smb nfs4 acl
- *
- * Todo: Really use mem_ctx after fixing interface of nfs4_acls
- */
-static struct SMB4ACL_T *nfs4acls_inheritacl(vfs_handle_struct *handle,
-       const struct smb_filename *smb_fname_in,
-       TALLOC_CTX *mem_ctx)
+static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
+                        files_struct *fsp,
+                        uint32_t security_info_sent,
+                        const struct security_descriptor *psd)
 {
-       char *parent_dir = NULL;
-       struct SMB4ACL_T *pparentacl = NULL;
-       struct SMB4ACL_T *pchildacl = NULL;
-       struct SMB4ACE_T *pace;
-       SMB_ACE4PROP_T ace;
-       bool isdir;
-       struct smb_filename *smb_fname = NULL;
-       struct smb_filename *smb_fname_parent = NULL;
+       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;
-       TALLOC_CTX *frame = talloc_stackframe();
 
-       DEBUG(10, ("nfs4acls_inheritacl invoked for %s\n",
-                       smb_fname_in->base_name));
-       smb_fname = cp_smb_filename_nostream(frame, smb_fname_in);
-       if (smb_fname == NULL) {
-               TALLOC_FREE(frame);
-               errno = ENOMEM;
-               return NULL;
-       }
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct nfs4acl_config,
+                               return NT_STATUS_INTERNAL_ERROR);
 
-       ret = SMB_VFS_STAT(handle->conn, smb_fname);
-       if (ret == -1) {
-               DEBUG(0,("nfs4acls_inheritacl: failed to stat "
-                        "directory %s. Error was %s\n",
-                        smb_fname_str_dbg(smb_fname),
-                        strerror(errno)));
-               TALLOC_FREE(frame);
-               return NULL;
+       if (!VALID_STAT(fsp->fsp_name->st)) {
+               DBG_ERR("Invalid stat info on [%s]\n", fsp_str_dbg(fsp));
+               return NT_STATUS_INTERNAL_ERROR;
        }
-       isdir = S_ISDIR(smb_fname->st.st_ex_mode);
 
-       if (!parent_dirname(talloc_tos(),
-                           smb_fname->base_name,
-                           &parent_dir,
-                           NULL)) {
-               TALLOC_FREE(frame);
-               errno = ENOMEM;
-               return NULL;
+       existing_mode = fsp->fsp_name->st.st_ex_mode;
+       if (S_ISDIR(existing_mode)) {
+               expected_mode = 0777;
+       } else {
+               expected_mode = 0666;
        }
-
-       smb_fname_parent = synthetic_smb_fname(talloc_tos(),
-                               parent_dir,
-                               NULL,
-                               NULL,
-                               0);
-       if (smb_fname_parent == NULL) {
-               TALLOC_FREE(frame);
-               errno = ENOMEM;
-               return NULL;
+       if (!config->validate_mode) {
+               existing_mode = 0;
+               expected_mode = 0;
        }
+       if ((existing_mode & expected_mode) != expected_mode) {
 
-       status = nfs4_get_nfs4_acl(handle, frame, smb_fname_parent,
-                                       &pparentacl);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)
-           && strncmp(parent_dir, ".", 2) != 0) {
-               pparentacl = nfs4acls_inheritacl(handle,
-                                               smb_fname_parent,
-                                               frame);
-       }
-       else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               pparentacl = nfs4acls_defaultacl(frame);
+               restored_mode = existing_mode | expected_mode;
 
-       }
-       else if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(frame);
-               return NULL;
-       }
-
-       pchildacl = smb_create_smb4acl(mem_ctx);
-       if (pchildacl == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               TALLOC_FREE(frame);
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       for (pace = smb_first_ace4(pparentacl); pace != NULL;
-            pace = smb_next_ace4(pace)) {
-               struct SMB4ACE_T *pchildace;
-               ace = *smb_get_ace4(pace);
-               if ((isdir && !(ace.aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)) ||
-                   (!isdir && !(ace.aceFlags & SMB_ACE4_FILE_INHERIT_ACE))) {
-                       DEBUG(10, ("non inheriting ace type: %d, iflags: %x, "
-                                  "flags: %x, mask: %x, who: %d\n",
-                                  ace.aceType, ace.flags, ace.aceFlags,
-                                  ace.aceMask, ace.who.id));
-                       continue;
-               }
-               DEBUG(10, ("inheriting ace type: %d, iflags: %x, "
-                          "flags: %x, mask: %x, who: %d\n",
-                          ace.aceType, ace.flags, ace.aceFlags,
-                          ace.aceMask, ace.who.id));
-               ace.aceFlags |= SMB_ACE4_INHERITED_ACE;
-               if (ace.aceFlags & SMB_ACE4_INHERIT_ONLY_ACE) {
-                       ace.aceFlags &= ~SMB_ACE4_INHERIT_ONLY_ACE;
-               }
-               if (ace.aceFlags & SMB_ACE4_NO_PROPAGATE_INHERIT_ACE) {
-                       ace.aceFlags &= ~SMB_ACE4_FILE_INHERIT_ACE;
-                       ace.aceFlags &= ~SMB_ACE4_DIRECTORY_INHERIT_ACE;
-                       ace.aceFlags &= ~SMB_ACE4_NO_PROPAGATE_INHERIT_ACE;
-               }
-               pchildace = smb_add_ace4(pchildacl, &ace);
-               if (pchildace == NULL) {
-                       DEBUG(0, ("talloc failed\n"));
-                       TALLOC_FREE(frame);
-                       errno = ENOMEM;
-                       return NULL;
+               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);
                }
        }
 
-       /* Set a default ACL if we didn't inherit anything. */
-       if (smb_first_ace4(pchildacl) == NULL) {
-               TALLOC_FREE(pchildacl);
-               pchildacl = nfs4acls_defaultacl(mem_ctx);
+       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;
        }
-
-       /* store the returned ACL to get it directly in the
-          future and avoid dynamic inheritance behavior. */
-       nfs4acl_xattr_set_smb4acl(handle, smb_fname, pchildacl);
-
-       TALLOC_FREE(frame);
-       return pchildacl;
-}
-
-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 **ppdesc)
-{
-       struct SMB4ACL_T *pacl;
-       NTSTATUS status;
-       TALLOC_CTX *frame = talloc_stackframe();
-
-       status = nfs4_fget_nfs4_acl(handle, frame, fsp, &pacl);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               pacl = nfs4acls_inheritacl(handle, fsp->fsp_name,
-                                          frame);
-       }
-       else if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(frame);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
                return status;
        }
 
-       status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
-                                     ppdesc, pacl);
-       TALLOC_FREE(frame);
-       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.
+        */
 
-static NTSTATUS nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct *handle,
-                                 const struct smb_filename *smb_fname,
-                                 uint32_t security_info,
-                                 TALLOC_CTX *mem_ctx,
-                                 struct security_descriptor **ppdesc)
-{
-       struct SMB4ACL_T *pacl;
-       NTSTATUS status;
-       TALLOC_CTX *frame = talloc_stackframe();
+       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;
+       }
 
-       status = nfs4_get_nfs4_acl(handle, frame, smb_fname, &pacl);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               pacl = nfs4acls_inheritacl(handle, smb_fname, frame);
+       if (get_current_uid(handle->conn) == 0 ||
+           chown_needed == false)
+       {
+               return NT_STATUS_ACCESS_DENIED;
        }
-       else if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(frame);
+       status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
+       if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       status = smb_get_nt_acl_nfs4(handle->conn, smb_fname, NULL,
-                                    security_info, mem_ctx, ppdesc,
-                                    pacl);
-       TALLOC_FREE(frame);
+       /*
+        * 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 NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
-                        files_struct *fsp,
-                        uint32_t security_info_sent,
-                        const struct security_descriptor *psd)
+static int nfs4acl_connect(struct vfs_handle_struct *handle,
+                          const char *service,
+                          const char *user)
 {
-       return smb_set_nt_acl_nfs4(handle, fsp, NULL, security_info_sent,
-                                  psd, nfs4acl_xattr_fset_smb4acl);
+       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;
 }
 
 /*
@@ -590,47 +532,24 @@ static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
    Function declarations taken from vfs_solarisacl
 */
 
-static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_file(vfs_handle_struct *handle,
-                                       const struct smb_filename *smb_fname,
-                                       SMB_ACL_TYPE_T type,
-                                       TALLOC_CTX *mem_ctx)
-{
-       return (SMB_ACL_T)NULL;
-}
-
 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_file(vfs_handle_struct *handle,
-                                        const struct smb_filename *smb_fname,
-                                        SMB_ACL_TYPE_T type,
-                                        SMB_ACL_T theacl)
-{
-       return -1;
-}
-
 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_file(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
-{
-       return -1;
-}
-
-static int nfs4acl_xattr_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname,
-                       TALLOC_CTX *mem_ctx,
-                       char **blob_description,
-                       DATA_BLOB *blob)
+static int nfs4acl_xattr_fail__sys_acl_delete_def_fd(vfs_handle_struct *handle,
+                       files_struct *fsp)
 {
        return -1;
 }
@@ -643,20 +562,17 @@ static int nfs4acl_xattr_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, fi
 /* VFS operations structure */
 
 static struct vfs_fn_pointers nfs4acl_xattr_fns = {
+       .connect_fn = nfs4acl_connect,
        .fget_nt_acl_fn = nfs4acl_xattr_fget_nt_acl,
-       .get_nt_acl_fn = nfs4acl_xattr_get_nt_acl,
        .fset_nt_acl_fn = nfs4acl_xattr_fset_nt_acl,
 
-       .sys_acl_get_file_fn = nfs4acl_xattr_fail__sys_acl_get_file,
        .sys_acl_get_fd_fn = nfs4acl_xattr_fail__sys_acl_get_fd,
-       .sys_acl_blob_get_file_fn = nfs4acl_xattr_fail__sys_acl_blob_get_file,
        .sys_acl_blob_get_fd_fn = nfs4acl_xattr_fail__sys_acl_blob_get_fd,
-       .sys_acl_set_file_fn = nfs4acl_xattr_fail__sys_acl_set_file,
        .sys_acl_set_fd_fn = nfs4acl_xattr_fail__sys_acl_set_fd,
-       .sys_acl_delete_def_file_fn = nfs4acl_xattr_fail__sys_acl_delete_def_file,
+       .sys_acl_delete_def_fd_fn = nfs4acl_xattr_fail__sys_acl_delete_def_fd,
 };
 
-NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *);
+static_decl_vfs;
 NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *ctx)
 {
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "nfs4acl_xattr",