summaryrefslogtreecommitdiffstats
path: root/source3/modules/nfs4acl_xattr_nfs.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source3/modules/nfs4acl_xattr_nfs.c
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/modules/nfs4acl_xattr_nfs.c')
-rw-r--r--source3/modules/nfs4acl_xattr_nfs.c893
1 files changed, 893 insertions, 0 deletions
diff --git a/source3/modules/nfs4acl_xattr_nfs.c b/source3/modules/nfs4acl_xattr_nfs.c
new file mode 100644
index 0000000..59e02bf
--- /dev/null
+++ b/source3/modules/nfs4acl_xattr_nfs.c
@@ -0,0 +1,893 @@
+/*
+ * Copyright (C) Ralph Boehme 2018
+ *
+ * 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/proto.h"
+#include "system/passwd.h"
+#include "libcli/security/security_descriptor.h"
+#include "libcli/security/security_token.h"
+
+#ifdef HAVE_RPC_XDR_H
+/* <rpc/xdr.h> uses TRUE and FALSE */
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#ifdef HAVE_RPC_TYPES_H
+#include <rpc/types.h>
+#endif
+#include <rpc/xdr.h>
+
+#include "nfs4_acls.h"
+#include "nfs41acl.h"
+#include "nfs4acl_xattr.h"
+#include "nfs4acl_xattr_nfs.h"
+#include "nfs4acl_xattr_util.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define OVERFLOW_CHECK(val1, val2) ((val1) + (val2) < (val1))
+#define XDR_UTF8STR_ALIGNMENT 4
+#define XDR_UTF8STR_ALIGN(l) \
+ (((l) + ((XDR_UTF8STR_ALIGNMENT) - 1)) & ~((XDR_UTF8STR_ALIGNMENT) - 1))
+
+static struct nfs4_to_smb4_id_map {
+ const char *nfs4_id;
+ uint32_t smb4_id;
+} nfs4_to_smb4_id_map[] = {
+ {"OWNER@", SMB_ACE4_WHO_OWNER},
+ {"GROUP@", SMB_ACE4_WHO_GROUP},
+ {"EVERYONE@", SMB_ACE4_WHO_EVERYONE},
+ {"INTERACTIVE@", SMB_ACE4_WHO_INTERACTIVE},
+ {"NETWORK@", SMB_ACE4_WHO_NETWORK},
+ {"DIALUP@", SMB_ACE4_WHO_DIALUP},
+ {"BATCH@", SMB_ACE4_WHO_BATCH},
+ {"ANONYMOUS@", SMB_ACE4_WHO_ANONYMOUS},
+ {"AUTHENTICATED@", SMB_ACE4_WHO_AUTHENTICATED},
+ {"SERVICE@", SMB_ACE4_WHO_SERVICE},
+};
+
+static bool is_special_nfs4_id(const char *nfs4_id)
+{
+ char *at = NULL;
+
+ at = strchr(nfs4_id, '@');
+ if (at == NULL) {
+ return false;
+ }
+ if (at[1] != '\0') {
+ return false;
+ }
+ return true;
+}
+
+static bool map_special_nfs4_to_smb4_id(const char *nfs4_id, uint32_t *smb4_id)
+{
+ size_t i;
+ int cmp;
+
+ for (i = 0; i < ARRAY_SIZE(nfs4_to_smb4_id_map); i++) {
+ cmp = strcmp(nfs4_to_smb4_id_map[i].nfs4_id, nfs4_id);
+ if (cmp != 0) {
+ continue;
+ }
+ *smb4_id = nfs4_to_smb4_id_map[i].smb4_id;
+ return true;
+ }
+ return false;
+}
+
+static bool map_special_smb4_to_nfs4_id(uint32_t smb4_id, const char **nfs4_id)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(nfs4_to_smb4_id_map); i++) {
+ if (nfs4_to_smb4_id_map[i].smb4_id != smb4_id) {
+ continue;
+ }
+ *nfs4_id = nfs4_to_smb4_id_map[i].nfs4_id;
+ return true;
+ }
+ return false;
+}
+
+static unsigned nfs40acl_get_naces(nfsacl40 *nacl)
+{
+ return nacl->na40_aces.na40_aces_len;
+}
+
+static unsigned nfs41acl_get_naces(nfsacl41 *nacl)
+{
+ return nacl->na41_aces.na41_aces_len;
+}
+
+static void nfs40acl_set_naces(nfsacl40 *nacl, unsigned naces)
+{
+ nacl->na40_aces.na40_aces_len = naces;
+}
+
+static void nfs41acl_set_naces(nfsacl41 *nacl, unsigned naces)
+{
+ nacl->na41_aces.na41_aces_len = naces;
+}
+
+static unsigned nfs41acl_get_flags(nfsacl41 *nacl)
+{
+ return nacl->na41_flag;
+}
+
+static void nfs41acl_set_flags(nfsacl41 *nacl, unsigned flags)
+{
+ nacl->na41_flag = flags;
+}
+
+static nfsace4 *nfs40acl_get_ace(nfsacl40 *nacl, size_t n)
+{
+ return &nacl->na40_aces.na40_aces_val[n];
+}
+
+static nfsace4 *nfs41acl_get_ace(nfsacl41 *nacl, size_t n)
+{
+ return &nacl->na41_aces.na41_aces_val[n];
+}
+
+static size_t nfs40acl_get_xdrblob_size(nfsacl40 *nacl)
+{
+ size_t acl_size;
+ size_t aces_size;
+ size_t identifier_size;
+ unsigned i;
+ unsigned naces = nfs40acl_get_naces(nacl);
+
+ /* ACE structure minus actual identifier strings */
+ struct nfsace4_size {
+ acetype4 type;
+ aceflag4 flag;
+ acemask4 access_mask;
+ u_int who_length;
+ };
+
+ /*
+ * acl_size =
+ * sizeof(ace_count) +
+ * (ace_count * (sizeof(nfsace4_size)) +
+ * length of all identifiers strings
+ */
+
+ acl_size = sizeof(unsigned);
+
+ if (naces > NFS4ACL_XDR_MAX_ACES) {
+ DBG_ERR("Too many ACEs: %u", naces);
+ return 0;
+ }
+
+ aces_size = naces * sizeof(struct nfsace4_size);
+
+ if (OVERFLOW_CHECK(acl_size, aces_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ acl_size += aces_size;
+
+ identifier_size = 0;
+ for (i = 0; i < naces; i++) {
+ nfsace4 *nace = nfs40acl_get_ace(nacl, i);
+ size_t string_size = nace->who.utf8string_len;
+ size_t id_size;
+
+ id_size = XDR_UTF8STR_ALIGN(string_size);
+
+ if (OVERFLOW_CHECK(identifier_size, id_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ identifier_size += id_size;
+ }
+
+ if (OVERFLOW_CHECK(acl_size, identifier_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ acl_size += identifier_size;
+
+ DBG_DEBUG("acl_size: %zd\n", acl_size);
+ return acl_size;
+}
+
+static size_t nfs41acl_get_xdrblob_size(nfsacl41 *nacl)
+{
+ size_t acl_size;
+ size_t aces_size;
+ size_t identifier_size;
+ unsigned i;
+ unsigned naces = nfs41acl_get_naces(nacl);
+
+ /* ACE structure minus actual identifier strings */
+ struct nfsace4_size {
+ acetype4 type;
+ aceflag4 flag;
+ acemask4 access_mask;
+ u_int who_length;
+ };
+
+ /*
+ * acl_size =
+ * sizeof(acl_flag) +
+ * sizeof(ace_count) +
+ * (ace_count * (sizeof(nfsace4_size)) +
+ * length of all identifiers strings
+ */
+
+ acl_size = 2 * sizeof(unsigned);
+
+ if (naces > NFS4ACL_XDR_MAX_ACES) {
+ DBG_ERR("Too many ACEs: %u", naces);
+ return 0;
+ }
+
+ aces_size = naces * sizeof(struct nfsace4_size);
+
+ if (OVERFLOW_CHECK(acl_size, aces_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ acl_size += aces_size;
+
+ identifier_size = 0;
+ for (i = 0; i < naces; i++) {
+ nfsace4 *nace = nfs41acl_get_ace(nacl, i);
+ size_t string_size = nace->who.utf8string_len;
+ size_t id_size;
+
+ id_size = XDR_UTF8STR_ALIGN(string_size);
+
+ if (OVERFLOW_CHECK(identifier_size, id_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ identifier_size += id_size;
+ }
+
+ if (OVERFLOW_CHECK(acl_size, identifier_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return 0;
+ }
+ acl_size += identifier_size;
+
+ DBG_DEBUG("acl_size: %zd\n", acl_size);
+ return acl_size;
+}
+
+static nfsacl40 *nfs40acl_alloc(TALLOC_CTX *mem_ctx, unsigned naces)
+{
+ size_t acl_size;
+ size_t aces_size;
+ nfsacl40 *nacl = NULL;
+
+ if (naces > NFS4ACL_XDR_MAX_ACES) {
+ DBG_ERR("Too many ACEs: %d\n", naces);
+ return NULL;
+ }
+
+ acl_size = sizeof(nfsacl40);
+ aces_size = (naces * sizeof(struct nfsace4));
+
+ if (OVERFLOW_CHECK(acl_size, aces_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return NULL;
+ }
+ acl_size += aces_size;
+
+ nacl = talloc_zero_size(mem_ctx, acl_size);
+ if (nacl == NULL) {
+ DBG_ERR("talloc_zero_size failed\n");
+ return NULL;
+ }
+
+ nfs40acl_set_naces(nacl, naces);
+ nacl->na40_aces.na40_aces_val =
+ (nfsace4 *)((uint8_t *)nacl + sizeof(nfsacl40));
+
+ return nacl;
+}
+
+static nfsacl41 *nfs41acl_alloc(TALLOC_CTX *mem_ctx, unsigned naces)
+{
+ size_t acl_size;
+ size_t aces_size;
+ nfsacl41 *nacl = NULL;
+
+ if (naces > NFS4ACL_XDR_MAX_ACES) {
+ DBG_ERR("Too many ACEs: %d\n", naces);
+ return NULL;
+ }
+
+ acl_size = sizeof(nfsacl41);
+ aces_size = (naces * sizeof(struct nfsace4));
+
+ if (OVERFLOW_CHECK(acl_size, aces_size)) {
+ DBG_ERR("Integer Overflow error\n");
+ return NULL;
+ }
+ acl_size += aces_size;
+
+ nacl = talloc_zero_size(mem_ctx, acl_size);
+ if (nacl == NULL) {
+ DBG_ERR("talloc_zero_size failed\n");
+ return NULL;
+ }
+
+ nfs41acl_set_naces(nacl, naces);
+ nacl->na41_aces.na41_aces_val =
+ (nfsace4 *)((uint8_t *)nacl + sizeof(nfsacl41));
+
+ return nacl;
+}
+
+static bool create_special_id(TALLOC_CTX *mem_ctx,
+ nfsace4 *nace,
+ const char *id)
+{
+ char *s = talloc_strdup(mem_ctx, id);
+
+ if (s == NULL) {
+ DBG_ERR("talloc_strdup failed\n");
+ return false;
+ }
+ nace->who.utf8string_val = s;
+ nace->who.utf8string_len = talloc_get_size(s) - 1;
+ return true;
+}
+
+static bool map_smb4_to_nfs4_id(TALLOC_CTX *mem_ctx,
+ struct nfs4acl_config *config,
+ nfsace4 *nace,
+ SMB_ACE4PROP_T *sace)
+{
+ const char *nfs4_id = NULL;
+ const char *name = NULL;
+ char *ace_name = NULL;
+ uid_t id;
+ bool ok;
+
+ if (sace->flags & SMB_ACE4_ID_SPECIAL) {
+ ok = map_special_smb4_to_nfs4_id(sace->who.special_id,
+ &nfs4_id);
+ if (!ok) {
+ DBG_ERR("Unsupported special id [%"PRIu32"]\n",
+ sace->who.special_id);
+ return false;
+ }
+
+ ok = create_special_id(mem_ctx, nace, nfs4_id);
+ if (!ok) {
+ return false;
+ }
+ DBG_DEBUG("Special id [%s]\n", nace->who.utf8string_val);
+ return true;
+ }
+
+ if (sace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP) {
+ nace->flag |= ACE4_IDENTIFIER_GROUP;
+ }
+
+ if (config->nfs4_id_numeric) {
+ char *strid = NULL;
+
+ if (sace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP) {
+ id = sace->who.gid;
+ } else {
+ id = sace->who.uid;
+ }
+
+ strid = talloc_asprintf(mem_ctx, "%jd", (intmax_t)id);
+ if (strid == NULL) {
+ DBG_ERR("talloc_asprintf failed\n");
+ return false;
+ }
+ nace->who.utf8string_val = strid;
+ nace->who.utf8string_len = talloc_get_size(strid) - 1;
+ DBG_DEBUG("Numeric id [%s]\n", nace->who.utf8string_val);
+ return true;
+ }
+
+ if (sace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP) {
+ struct group *grp = NULL;
+
+ grp = getgrgid(sace->who.gid);
+ if (grp == NULL) {
+ DBG_ERR("Unknown gid [%jd]\n", (intmax_t)sace->who.gid);
+ return false;
+ }
+ name = grp->gr_name;
+ } else {
+ struct passwd *pwd = NULL;
+
+ pwd = getpwuid(sace->who.uid);
+ if (pwd == NULL) {
+ DBG_ERR("Unknown uid [%jd]\n", (intmax_t)sace->who.uid);
+ return false;
+ }
+ name = pwd->pw_name;
+ }
+
+ ace_name = talloc_strdup(mem_ctx, name);
+ if (ace_name == NULL) {
+ DBG_ERR("talloc_asprintf failed\n");
+ return false;
+ }
+ nace->who.utf8string_val = ace_name;
+ nace->who.utf8string_len = talloc_get_size(ace_name) - 1;
+
+ DBG_DEBUG("id [%s]\n", nace->who.utf8string_val);
+ return true;
+}
+
+static bool smb4acl_to_nfs40acl(vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct SMB4ACL_T *smb4acl,
+ nfsacl40 **_nacl)
+{
+ struct nfs4acl_config *config = NULL;
+ struct SMB4ACE_T *smb4ace = NULL;
+ nfsacl40 *nacl = NULL;
+ size_t naces = smb_get_naces(smb4acl);
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return false);
+
+ nacl = nfs40acl_alloc(mem_ctx, naces);
+ nfs40acl_set_naces(nacl, 0);
+
+ smb4ace = smb_first_ace4(smb4acl);
+ while (smb4ace != NULL) {
+ SMB_ACE4PROP_T *ace4prop = smb_get_ace4(smb4ace);
+ size_t nace_count = nfs40acl_get_naces(nacl);
+ nfsace4 *nace = nfs40acl_get_ace(nacl, nace_count);
+
+ nace->type = ace4prop->aceType;
+ nace->flag = ace4prop->aceFlags;
+ nace->access_mask = ace4prop->aceMask;
+
+ ok = map_smb4_to_nfs4_id(nacl, config, nace, ace4prop);
+ if (!ok) {
+ smb4ace = smb_next_ace4(smb4ace);
+ continue;
+ }
+
+ nace_count++;
+ nfs40acl_set_naces(nacl, nace_count);
+ smb4ace = smb_next_ace4(smb4ace);
+ }
+
+ *_nacl = nacl;
+ return true;
+}
+
+static bool smb4acl_to_nfs41acl(vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct SMB4ACL_T *smb4acl,
+ nfsacl41 **_nacl)
+{
+ struct nfs4acl_config *config = NULL;
+ struct SMB4ACE_T *smb4ace = NULL;
+ nfsacl41 *nacl = NULL;
+ size_t naces = smb_get_naces(smb4acl);
+ uint16_t smb4acl_flags;
+ unsigned nacl_flags;
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return false);
+
+ nacl = nfs41acl_alloc(mem_ctx, naces);
+ nfs41acl_set_naces(nacl, 0);
+
+ smb4acl_flags = smbacl4_get_controlflags(smb4acl);
+ nacl_flags = smb4acl_to_nfs4acl_flags(smb4acl_flags);
+ nfs41acl_set_flags(nacl, nacl_flags);
+
+ smb4ace = smb_first_ace4(smb4acl);
+ while (smb4ace != NULL) {
+ SMB_ACE4PROP_T *ace4prop = smb_get_ace4(smb4ace);
+ size_t nace_count = nfs41acl_get_naces(nacl);
+ nfsace4 *nace = nfs41acl_get_ace(nacl, nace_count);
+
+ nace->type = ace4prop->aceType;
+ nace->flag = ace4prop->aceFlags;
+ nace->access_mask = ace4prop->aceMask;
+
+ ok = map_smb4_to_nfs4_id(nacl, config, nace, ace4prop);
+ if (!ok) {
+ smb4ace = smb_next_ace4(smb4ace);
+ continue;
+ }
+
+ nace_count++;
+ nfs41acl_set_naces(nacl, nace_count);
+ smb4ace = smb_next_ace4(smb4ace);
+ }
+
+ *_nacl = nacl;
+ return true;
+}
+
+NTSTATUS nfs4acl_smb4acl_to_nfs_blob(vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct SMB4ACL_T *smb4acl,
+ DATA_BLOB *_blob)
+{
+ struct nfs4acl_config *config = NULL;
+ nfsacl40 *nacl40 = NULL;
+ nfsacl41 *nacl41 = NULL;
+ XDR xdr = {0};
+ size_t aclblobsize;
+ DATA_BLOB blob;
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ if (config->nfs_version == ACL4_XATTR_VERSION_40) {
+ ok = smb4acl_to_nfs40acl(handle, mem_ctx, smb4acl, &nacl40);
+ if (!ok) {
+ DBG_ERR("smb4acl_to_nfs4acl failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ aclblobsize = nfs40acl_get_xdrblob_size(nacl40);
+ if (aclblobsize == 0) {
+ DBG_ERR("Error calculating XDR blob size\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ok = smb4acl_to_nfs41acl(handle, mem_ctx, smb4acl, &nacl41);
+ if (!ok) {
+ DBG_ERR("smb4acl_to_nfs4acl failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ aclblobsize = nfs41acl_get_xdrblob_size(nacl41);
+ if (aclblobsize == 0) {
+ DBG_ERR("Error calculating XDR blob size\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ blob = data_blob_talloc(mem_ctx, NULL, aclblobsize);
+ if (blob.data == NULL) {
+ TALLOC_FREE(nacl40);
+ TALLOC_FREE(nacl41);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ xdrmem_create(&xdr, (char *)blob.data, blob.length, XDR_ENCODE);
+
+ if (config->nfs_version == ACL4_XATTR_VERSION_40) {
+ ok = xdr_nfsacl40(&xdr, nacl40);
+ TALLOC_FREE(nacl40);
+ if (!ok) {
+ DBG_ERR("xdr_nfs4acl40 failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ ok = xdr_nfsacl41(&xdr, nacl41);
+ TALLOC_FREE(nacl41);
+ if (!ok) {
+ DBG_ERR("xdr_nfs4acl40 failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nfs4acl_nfs_blob_to_nfs40acl(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ nfsacl40 **_nacl)
+{
+ nfsacl40 *nacl = NULL;
+ XDR xdr = {0};
+ bool ok;
+
+ nacl = talloc_zero_size(mem_ctx, sizeof(nfsacl40));
+ if (nacl == NULL) {
+ DBG_ERR("talloc_zero_size failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ xdrmem_create(&xdr, (char *)blob->data, blob->length, XDR_DECODE);
+
+ ok = xdr_nfsacl40(&xdr, nacl);
+ if (!ok) {
+ DBG_ERR("xdr_nfsacl40 failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ DBG_DEBUG("naces = %d \n", nacl->na40_aces.na40_aces_len);
+
+ *_nacl = nacl;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nfs4acl_nfs_blob_to_nfs41acl(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ nfsacl41 **_nacl)
+{
+ nfsacl41 *nacl = NULL;
+ XDR xdr = {0};
+ bool ok;
+
+ nacl = talloc_zero_size(mem_ctx, sizeof(nfsacl41));
+ if (nacl == NULL) {
+ DBG_ERR("talloc_zero_size failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ xdrmem_create(&xdr, (char *)blob->data, blob->length, XDR_DECODE);
+
+ ok = xdr_nfsacl41(&xdr, nacl);
+ if (!ok) {
+ DBG_ERR("xdr_nfsacl40 failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ DBG_DEBUG("naces = %d \n", nacl->na41_aces.na41_aces_len);
+
+ *_nacl = nacl;
+ return NT_STATUS_OK;
+}
+
+static bool map_ace_nfs4_to_smb4(struct nfs4acl_config *config,
+ const nfsace4 *nace,
+ SMB_ACE4PROP_T *sace)
+{
+ char *name = NULL;
+ char *p = NULL;
+ uint32_t smb4_id;
+ bool ok;
+
+ name = talloc_strndup(talloc_tos(),
+ nace->who.utf8string_val,
+ nace->who.utf8string_len);
+ if (name == NULL) {
+ return false;
+ }
+
+ sace->aceType = nace->type;
+ sace->aceFlags = nace->flag;
+ sace->aceMask = nace->access_mask;
+
+ if (is_special_nfs4_id(name)) {
+ ok = map_special_nfs4_to_smb4_id(name, &smb4_id);
+ if (!ok) {
+ DBG_WARNING("Unknown special id [%s]\n", name);
+ return false;
+ }
+ sace->flags |= SMB_ACE4_ID_SPECIAL;
+ sace->who.special_id = smb4_id;
+ return true;
+ }
+
+ p = strtok(name, "@");
+ if (p == NULL && !config->nfs4_id_numeric) {
+ DBG_ERR("Unqualified name [%s]\n", name);
+ TALLOC_FREE(name);
+ return false;
+ }
+
+ /*
+ * nametouid() and nametogid() work with both names and numbers...
+ */
+
+ if (nace->flag & ACE4_IDENTIFIER_GROUP) {
+ sace->who.gid = nametogid(name);
+ if (sace->who.gid == (gid_t)-1) {
+ DBG_ERR("converting id [%s] failed\n", name);
+ TALLOC_FREE(name);
+ return false;
+ }
+ TALLOC_FREE(name);
+ return true;
+ }
+
+ sace->who.uid = nametouid(name);
+ if (sace->who.uid == (gid_t)-1) {
+ DBG_ERR("converting id [%s] failed\n", name);
+ TALLOC_FREE(name);
+ return false;
+ }
+ TALLOC_FREE(name);
+ return true;
+}
+
+static NTSTATUS nfs40acl_to_smb4acl(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ nfsacl40 *nacl,
+ struct SMB4ACL_T **_smb4acl)
+{
+ struct nfs4acl_config *config = NULL;
+ struct SMB4ACL_T *smb4acl = NULL;
+ unsigned naces = nfs40acl_get_naces(nacl);
+ unsigned int i;
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ smb4acl = smb_create_smb4acl(mem_ctx);
+ if (smb4acl == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ DBG_DEBUG("nace [%u]\n", naces);
+
+ for (i = 0; i < naces; i++) {
+ nfsace4 *nace = nfs40acl_get_ace(nacl, i);
+ SMB_ACE4PROP_T sace = { 0 };
+
+ DBG_DEBUG("type [%d] flag [%x] mask [%x] who [%*s]\n",
+ nace->type, nace->flag,
+ nace->access_mask,
+ nace->who.utf8string_len,
+ nace->who.utf8string_val);
+
+ ok = map_ace_nfs4_to_smb4(config, nace, &sace);
+ if (!ok) {
+ continue;
+ }
+
+ smb_add_ace4(smb4acl, &sace);
+ }
+
+ *_smb4acl = smb4acl;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nfs41acl_to_smb4acl(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ nfsacl41 *nacl,
+ struct SMB4ACL_T **_smb4acl)
+{
+ struct nfs4acl_config *config = NULL;
+ struct SMB4ACL_T *smb4acl = NULL;
+ unsigned nfsacl41_flag = 0;
+ uint16_t smb4acl_flags = 0;
+ unsigned naces = nfs41acl_get_naces(nacl);
+ unsigned int i;
+ bool ok;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ smb4acl = smb_create_smb4acl(mem_ctx);
+ if (smb4acl == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ nfsacl41_flag = nfs41acl_get_flags(nacl);
+ smb4acl_flags = nfs4acl_to_smb4acl_flags(nfsacl41_flag);
+ smbacl4_set_controlflags(smb4acl, smb4acl_flags);
+
+ DBG_DEBUG("flags [%x] nace [%u]\n", smb4acl_flags, naces);
+
+ for (i = 0; i < naces; i++) {
+ nfsace4 *nace = nfs41acl_get_ace(nacl, i);
+ SMB_ACE4PROP_T sace = { 0 };
+
+ DBG_DEBUG("type [%d] flag [%x] mask [%x] who [%*s]\n",
+ nace->type, nace->flag,
+ nace->access_mask,
+ nace->who.utf8string_len,
+ nace->who.utf8string_val);
+
+ ok = map_ace_nfs4_to_smb4(config, nace, &sace);
+ if (!ok) {
+ continue;
+ }
+
+ smb_add_ace4(smb4acl, &sace);
+ }
+
+ *_smb4acl = smb4acl;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS nfs4acl_nfs_blob_to_smb4(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ struct SMB4ACL_T **_smb4acl)
+{
+ struct nfs4acl_config *config = NULL;
+ struct SMB4ACL_T *smb4acl = NULL;
+ NTSTATUS status;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct nfs4acl_config,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ if (config->nfs_version == ACL4_XATTR_VERSION_40) {
+ nfsacl40 *nacl = NULL;
+
+ status = nfs4acl_nfs_blob_to_nfs40acl(handle,
+ talloc_tos(),
+ blob,
+ &nacl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = nfs40acl_to_smb4acl(handle, mem_ctx, nacl, &smb4acl);
+ TALLOC_FREE(nacl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ nfsacl41 *nacl = NULL;
+
+ status = nfs4acl_nfs_blob_to_nfs41acl(handle,
+ talloc_tos(),
+ blob,
+ &nacl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = nfs41acl_to_smb4acl(handle, mem_ctx, nacl, &smb4acl);
+ TALLOC_FREE(nacl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ *_smb4acl = smb4acl;
+ return NT_STATUS_OK;
+}
+
+#else /* !HAVE_RPC_XDR_H */
+#include "nfs4acl_xattr_nfs.h"
+NTSTATUS nfs4acl_nfs_blob_to_smb4(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ struct SMB4ACL_T **_smb4acl)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS nfs4acl_smb4acl_to_nfs_blob(vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct SMB4ACL_T *smbacl,
+ DATA_BLOB *blob)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+#endif /* HAVE_RPC_XDR_H */