[PATCH] SMB2 AAPL create context (was: Mac OS Mavericks über slow)

Ralph Böhme rb at sernet.de
Wed Oct 15 09:04:45 MDT 2014


On Fri, Oct 10, 2014 at 07:14:24AM +0200, Ralph Böhme wrote:
> On Thu, Oct 09, 2014 at 05:37:44PM -0700, Jeremy Allison wrote:
> > On Mon, Oct 06, 2014 at 05:08:39PM +0200, Ralph Böhme wrote:
> > > 
> > > as pointed out by Volker off-list, this needs to be further refined in
> > > order to support VFS modules that don't stack (eg gpfs, zfs). Working
> > > on it...
> > 
> > Ping me when this is ready for review. I'm eager
> > to get improved Mac support into 4.2.x !
> 
> will do. I have something that is working fine on Linux with POSIX
> ACLs.
> 
> Repo: <https://git.samba.org/?p=slow/samba.git;a=log;h=refs/heads/aapl-chmod-in-nttrans>
> MS NFS stuff commit: <https://git.samba.org/?p=slow/samba.git;a=commitdiff;h=a568196bd31816e8dfaa17600fe3a12e92758cda>
> 
> I wanted to give it a whirl on FreeBSD with ZFS before posting for
> review. Will follow-up as soon as I finished that.

done. Turned out to be subtle and tricky.

The basic problem I was facing when implementing the MS NFS ACL stuff
is that the DACL may not roundtrip.

The OS X client will performs the following sequence of actions for a
file where something did a chmod(file, mode):

1) client> query the existing sd 
2) server> compute sd with DACLs, add MS NFS SIDs when enabled by AAPL
           context
3) client> check and pontentially modify MS NFS SID(mode), send
           otherwise *unmodified* sd back to server
4) server> a) apply DACL from sd
           b) chmod() with the request MS NFS mode

So for a file with no excplitic ACEs, the server will nevertheless
generate a DACL with ACEs in NT form. The client sends them back and
when the server applies them, the resulting file explodes into a set
of complex ACEs, where all we wanted is a simple change of the UNIX
mode. :(

There are three possibly implementation choices how we can deal with
that, all have in common that as a first step (iirc in
source/smnb/nttrans.c:set_sd()), *before* going into the VFS with
set_nt_acl(), we check whether a set_sd() request contains a MS NFS
SID with a UNIX mode. If it doesn't, we don't care and the code
proceeds as usual. If it does, we can either:

1) ignore the NT ACL and apply the mode by calling SMB_VFS_CHMOD() [1]

2) remove the MS NFS SIDs from the DACL, apply DACL, apply mode

3) apply the unmodified DACL, then apply the mode

Implementation 1) seems like a natural choice given that it matches
exactly the given use case: an OS X client wanting to do a
chmod(). But there's a serious drawback: if we follow that route,
we've preclude any future use of set_sd() requests containing both MS
NFS SIDs *and* a new or modified DACL. We therefor choose to reject
option 1).

Implementation 2) looks like a good way out of this, unfortunately
after removing the MS NFS SIDs from the DACL, VFS modules (or the
default backend) have no way of knowing the request originated from a
"AAPL chmod() request", resulting in not-roundtripped-ACL-explosion in
the case of POSIX ACLs.

We need a way for the VFS modules to detect these requests and act
accoding to what makes most sense for the module, which in the case of
POSIX ACLs is ignore the DACL request. This is implementation 3) and
this is what the attached patchseries does.

I've checked and modified all VFS modules (and the default backend)
that needed a change to either completely ignore set DACL requests
(POSIX ACLs), or just skip the MS NFS ACEs (all modules that use
nfs4_acls.c, eg vfs_zfsacl, vfs_gpfs).

Woa, that was a lot of typing, I hope you can make some sense of it.

Patchset attached.
www: <https://git.samba.org/?p=slow/samba.git;a=shortlog;h=refs/heads/aapl-chmod-in-nttrans>

Review and push, if ok, appreciated!

Thanks!
-Ralph

[1] There's an additional choice wrt to the order applying the DACL
and the chmod(). Initial testing suggested that the order given makes
most sense, but I haven't really analysed this in more detail.

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de,mailto:kontakt@sernet.de
-------------- next part --------------
>From c1ff91b6dd0ced0a3934cb2dadb21d9b218d9fd2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Fri, 22 Aug 2014 03:48:50 +0200
Subject: [PATCH 01/11] s3:smbd: add SMB2 AAPL create context defines and
 options

Add a global SMB2 specific option 'use_aapl_crtctx'. It's off by
default, but later enabled in the vfs_fruit module.

Add a AAPL specific capability flag 'smb2_crtctx_aapl_readdir_attr',
when used several fields in an SMB2/FIND reply are repurposed
returning Mac specific file attributes including resource fork length,
unix mode and max_access.

Add define and flag for NFS ACE in AAPL create context. With this
extension, a client can use get_info(sec)/set_info(sec) for

- retrieving UNIX uid,gid and mode of a file with get_info
- modifying UNIX mode with set_info

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 libcli/smb/smb2_constants.h  |  1 +
 libcli/smb/smb2_create_ctx.h | 46 ++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/globals.h       |  3 +++
 3 files changed, 50 insertions(+)
 create mode 100644 libcli/smb/smb2_create_ctx.h

diff --git a/libcli/smb/smb2_constants.h b/libcli/smb/smb2_constants.h
index 191de2b..1a6c5ad 100644
--- a/libcli/smb/smb2_constants.h
+++ b/libcli/smb/smb2_constants.h
@@ -222,6 +222,7 @@
 #define SMB2_CREATE_TAG_RQLS "RqLs"
 #define SMB2_CREATE_TAG_DH2Q "DH2Q"
 #define SMB2_CREATE_TAG_DH2C "DH2C"
+#define SMB2_CREATE_TAG_AAPL "AAPL"
 #define SMB2_CREATE_TAG_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74"
 
 /* SMB2 notify flags */
diff --git a/libcli/smb/smb2_create_ctx.h b/libcli/smb/smb2_create_ctx.h
new file mode 100644
index 0000000..cb194f5
--- /dev/null
+++ b/libcli/smb/smb2_create_ctx.h
@@ -0,0 +1,46 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   SMB2 create context specifc stuff
+
+   Copyright (C) Ralph Boehme 2014
+
+   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/>.
+*/
+
+#ifndef __LIBCLI_SMB2_CREATE_CTX_H__
+#define __LIBCLI_SMB2_CREATE_CTX_H__
+
+/* http://opensource.apple.com/source/smb/smb-697.1.1/kernel/netsmb/smb_2.h */
+
+/* "AAPL" Context Command Codes */
+#define SMB2_CRTCTX_AAPL_SERVER_QUERY 1
+#define SMB2_CRTCTX_AAPL_RESOLVE_ID   2
+
+/* "AAPL" Server Query request/response bitmap */
+#define SMB2_CRTCTX_AAPL_SERVER_CAPS 1
+#define SMB2_CRTCTX_AAPL_VOLUME_CAPS 2
+#define SMB2_CRTCTX_AAPL_MODEL_INFO  4
+
+/* "AAPL" Client/Server Capabilities bitmap */
+#define SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR 1
+#define SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE  2
+#define SMB2_CRTCTX_AAPL_UNIX_BASED             4
+#define SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE       8
+
+/* "AAPL" Volume Capabilities bitmap */
+#define SMB2_CRTCTX_AAPL_SUPPORT_RESOLVE_ID 1
+#define SMB2_CRTCTX_AAPL_CASE_SENSITIVE     2
+
+#endif
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index d0015d5..5137e5e 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -504,6 +504,9 @@ struct smbXsrv_connection {
 			uint32_t max_trans;
 			uint32_t max_read;
 			uint32_t max_write;
+			bool use_aapl_crtctx;
+			bool smb2_crtctx_aapl_readdir_attr;
+			bool smb2_crtctx_aapl_unix_info;
 		} server;
 
 		struct smbd_smb2_request *requests;
-- 
1.9.3


>From 4d87ab4745996c59235891fe874db6b4ab984b81 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Mon, 8 Sep 2014 23:18:35 +0200
Subject: [PATCH 02/11] libcli/security: add NFS SID mappings

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 libcli/security/dom_sid.h  |  5 +++++
 libcli/security/util_sid.c | 17 +++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/libcli/security/dom_sid.h b/libcli/security/dom_sid.h
index c4a417b..cf3cedea 100644
--- a/libcli/security/dom_sid.h
+++ b/libcli/security/dom_sid.h
@@ -53,6 +53,11 @@ extern const struct dom_sid global_sid_Builtin_Replicator;
 extern const struct dom_sid global_sid_Builtin_PreWin2kAccess;
 extern const struct dom_sid global_sid_Unix_Users;
 extern const struct dom_sid global_sid_Unix_Groups;
+extern const struct dom_sid global_sid_Unix_NFS;
+extern const struct dom_sid global_sid_Unix_NFS_Users;
+extern const struct dom_sid global_sid_Unix_NFS_Groups;
+extern const struct dom_sid global_sid_Unix_NFS_Mode;
+extern const struct dom_sid global_sid_Unix_NFS_Other;
 
 int dom_sid_compare_auth(const struct dom_sid *sid1,
 			 const struct dom_sid *sid2);
diff --git a/libcli/security/util_sid.c b/libcli/security/util_sid.c
index 8e42826..5127109 100644
--- a/libcli/security/util_sid.c
+++ b/libcli/security/util_sid.c
@@ -96,6 +96,23 @@ const struct dom_sid global_sid_Unix_Users =			/* Unmapped Unix users */
 const struct dom_sid global_sid_Unix_Groups =			/* Unmapped Unix groups */
 { 1, 1, {0,0,0,0,0,22}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
 
+/*
+ * http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
+ */
+const struct dom_sid global_sid_Unix_NFS =             /* MS NFS and Apple style */
+{ 1, 1, {0,0,0,0,0,5}, {88,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+const struct dom_sid global_sid_Unix_NFS_Users =		/* Unix uid, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,1,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+const struct dom_sid global_sid_Unix_NFS_Groups =		/* Unix gid, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,2,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+const struct dom_sid global_sid_Unix_NFS_Mode =			/* Unix mode */
+{ 1, 2, {0,0,0,0,0,5}, {88,3,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* Unused, left here for documentary purposes */
+#if 0
+const struct dom_sid global_sid_Unix_NFS_Other =		/* Unix other, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,4,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+#endif
+
 /* Unused, left here for documentary purposes */
 #if 0
 #define SECURITY_NULL_SID_AUTHORITY    0
-- 
1.9.3


>From 5467a378f330a4ecfa7af94386c7276b3dd95a63 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Tue, 16 Sep 2014 02:16:28 +0200
Subject: [PATCH 03/11] vfs_fruit: check option "smb2 create tag:aapl"

The default for the option is true, which means we support Apple's
AAPL SMB2 create context stuff.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source3/modules/vfs_fruit.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index c1555f0..98547d4 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1718,6 +1718,10 @@ static int fruit_connect(vfs_handle_struct *handle,
 			"0x0d:0xf00d");
 	}
 
+	if (lp_parm_bool(-1, "smb2 create tag", "aapl", true)) {
+		handle->conn->sconn->client->connections->smb2.server.use_aapl_crtctx = true;
+	}
+
 	return rc;
 }
 
-- 
1.9.3


>From 5d454d70d9619081c87d55da3f7dd208fece0b97 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Fri, 22 Aug 2014 03:54:33 +0200
Subject: [PATCH 04/11] s3:smbd: SMB2 AAPL create context: SMB2/FIND extension

smb2_crtctx_aapl_readdir_attr = true | false

If enabled, several fields in an SMB2/FIND reply are repurposed
returning Mac specific file attributes including resource fork
length, UNIX mode, FinderInfo and max_access.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source3/smbd/smb2_create.c | 106 +++++++++++++++++++++++++++++
 source3/smbd/trans2.c      | 165 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 265 insertions(+), 6 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 48bc486..36640a0 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -24,6 +24,7 @@
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
+#include "../libcli/smb/smb2_create_ctx.h"
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "messages.h"
@@ -598,6 +599,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		uint64_t allocation_size = 0;
 		struct smb2_create_blob *twrp = NULL;
 		struct smb2_create_blob *qfid = NULL;
+		struct smb2_create_blob *aapl = NULL;
 		struct GUID _create_guid = GUID_zero();
 		struct GUID *create_guid = NULL;
 		bool update_open = false;
@@ -618,6 +620,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					     SMB2_CREATE_TAG_TWRP);
 		qfid = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_QFID);
+		if (smb2req->xconn->smb2.server.use_aapl_crtctx) {
+			aapl = smb2_create_blob_find(&in_context_blobs,
+						     SMB2_CREATE_TAG_AAPL);
+		}
 
 		fname = talloc_strdup(state, in_name);
 		if (tevent_req_nomem(fname, req)) {
@@ -804,6 +810,24 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			}
 		}
 
+		if (aapl) {
+			uint32_t cmd;
+
+			if (aapl->data.length != 24) {
+				DEBUG(1, ("unexpected AAPL ctxt legnth: %ju\n",
+					  (uintmax_t)aapl->data.length));
+				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+				return tevent_req_post(req, ev);
+			}
+
+			cmd = IVAL(aapl->data.data, 0);
+			if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
+				DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
+				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+				return tevent_req_post(req, ev);
+			}
+		}
+
 		/* these are ignored for SMB2 */
 		in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
 		in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
@@ -1086,6 +1110,88 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				return tevent_req_post(req, ev);
 			}
 		}
+
+		if (aapl) {
+			/* We know we have a SMB2_CRTCTX_AAPL_SERVER_QUERY query */
+			bool ok;
+			uint8_t p[16];
+			DATA_BLOB blob = data_blob_talloc(smb2req, NULL, 0);
+			uint64_t req_bitmap, client_caps;
+			uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
+
+			req_bitmap = BVAL(aapl->data.data, 8);
+			client_caps = BVAL(aapl->data.data, 16);
+
+			SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
+			SIVAL(p, 4, 0);
+			SBVAL(p, 8, req_bitmap);
+			ok = data_blob_append(smb2req, &blob, p, 16);
+			if (!ok) {
+				tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+				return tevent_req_post(req, ev);
+			}
+
+			if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
+				if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
+				    (smb2req->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
+					server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
+					smb2req->xconn->smb2.server.smb2_crtctx_aapl_readdir_attr = true;
+				}
+				SBVAL(p, 0, server_caps);
+				ok = data_blob_append(smb2req, &blob, p, 8);
+				if (!ok) {
+					tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+					return tevent_req_post(req, ev);
+				}
+			}
+
+			if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
+				SBVAL(p, 0,
+				      lp_case_sensitive(SNUM(smb2req->tcon->compat)) ?
+				      SMB2_CRTCTX_AAPL_CASE_SENSITIVE : 0);
+				ok = data_blob_append(smb2req, &blob, p, 8);
+				if (!ok) {
+					tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+					return tevent_req_post(req, ev);
+				}
+			}
+
+			if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
+				smb_ucs2_t *model;
+				size_t modellen;
+				ok = convert_string_talloc(smb2req,
+							   CH_UNIX, CH_UTF16LE,
+							   "Samba", strlen("Samba"),
+							   &model, &modellen);
+				if (!ok) {
+					tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+					return tevent_req_post(req, ev);
+				}
+
+				SIVAL(p, 0, 0);
+				SIVAL(p + 4, 0, modellen);
+				ok = data_blob_append(smb2req, &blob, p, 8);
+				if (!ok) {
+					tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+					return tevent_req_post(req, ev);
+				}
+
+				ok = data_blob_append(smb2req, &blob, model, modellen);
+				if (!ok) {
+					tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+					return tevent_req_post(req, ev);
+				}
+				talloc_free(model);
+			}
+
+			status = smb2_create_blob_add(state, &out_context_blobs,
+						      SMB2_CREATE_TAG_AAPL,
+						      blob);
+			if (!NT_STATUS_IS_OK(status)) {
+				tevent_req_nterror(req, status);
+				return tevent_req_post(req, ev);
+			}
+		}
 	}
 
 	smb2req->compat_chain_fsp = smb1req->chain_fsp;
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 60128ef..c556162 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -40,6 +40,7 @@
 #include "rpc_server/srv_pipe_hnd.h"
 #include "printing.h"
 #include "lib/util_ea.h"
+#include "MacExtensions.h"
 
 #define DIR_ENTRY_SAFETY_MARGIN 4096
 
@@ -1594,6 +1595,96 @@ static bool smbd_dirptr_lanman2_mode_fn(TALLOC_CTX *ctx,
 	return true;
 }
 
+/**
+ * Check and possibly read AFPINFO_STREAM stream from a file or dir
+ *
+ * If the file has an associated AFPINFO_STREAM stream, FinderInfo is
+ * returned in packed format in the buffer pointed to by
+ * compressed_finder_info. The buffer must suitably sized (16 bytes).
+ **/
+static void get_compressed_finder_info(connection_struct *conn,
+				       const struct smb_filename *smb_fname,
+				       char *compressed_finder_info)
+{
+	struct smb_filename *infoname = NULL;
+	files_struct *fsp = NULL;
+	NTSTATUS status;
+	ssize_t len;
+	uint8_t ai[AFP_INFO_SIZE];
+	uint32_t date_added;
+	int ret;
+
+	memset(compressed_finder_info, '\0', 16);
+
+	infoname = synthetic_smb_fname(talloc_tos(),
+				       smb_fname->base_name,
+				       AFPINFO_STREAM,
+				       NULL);
+
+	DEBUG(10, ("reading AFPINFO_STREAM for %s\n",
+		   smb_fname_str_dbg(infoname)));
+
+	ret = SMB_VFS_STAT(conn, infoname);
+	if (ret != 0) {
+		goto error;
+	}
+
+        status = SMB_VFS_CREATE_FILE(
+	    conn,                                   /* conn */
+	    NULL,                                   /* req */
+	    0,                                      /* root_dir_fid */
+	    infoname,                               /* fname */
+	    FILE_READ_DATA,                         /* access_mask */
+	    (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
+	     FILE_SHARE_DELETE),
+	    FILE_OPEN,                              /* create_disposition*/
+	    0,                                      /* create_options */
+	    0,                                      /* file_attributes */
+	    INTERNAL_OPEN_ONLY,                     /* oplock_request */
+	    NULL,                                   /* lease */
+	    0,                                      /* allocation_size */
+	    0,                                      /* private_flags */
+	    NULL,                                   /* sd */
+	    NULL,                                   /* ea_list */
+	    &fsp,                                   /* result */
+	    NULL);                                  /* pinfo */
+
+	if (!NT_STATUS_IS_OK(status)) {
+		goto error;
+	}
+
+	len = SMB_VFS_PREAD(fsp, ai, AFP_INFO_SIZE, 0);
+	if (len != AFP_INFO_SIZE) {
+		DEBUG(1, ("bad length: %ju\n", (intmax_t)len));
+		goto error;
+	}
+
+	/* AD_DATE_DELTA = 946684800 */
+	date_added = convert_time_t_to_uint32_t(
+		smb_fname->st.st_ex_btime.tv_sec)
+		- 946684800;
+
+	if (S_ISREG(smb_fname->st.st_ex_mode)) {
+		/* finder_type */
+		memcpy(compressed_finder_info, ai + 16, 4);
+		/* finder_creator */
+		memcpy(compressed_finder_info + 4, ai + 20, 4);
+	}
+	/* finder_flags */
+	memcpy(compressed_finder_info + 8, ai + 24, 2);
+	/* finder_ext_flags */
+	memcpy(compressed_finder_info + 10, ai + 40, 2);
+	/* finder_date_added, could also use stat_ex.btime instead */
+	RSIVAL(compressed_finder_info, 12, date_added);
+
+error:
+	if (fsp != NULL) {
+		SMB_VFS_CLOSE(fsp);
+	}
+	talloc_free(infoname);
+	return;
+}
+
 static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
 				    connection_struct *conn,
 				    uint16_t flags2,
@@ -2097,17 +2188,61 @@ static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
 		q = p; p += 4; /* q is placeholder for name length */
 		if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
 			SIVAL(p, 0, IO_REPARSE_TAG_DFS);
+		} else if ((conn->sconn->client->connections->smb2.server.smb2_crtctx_aapl_readdir_attr) &&
+			   (conn->fs_capabilities & FILE_NAMED_STREAMS)) {
+			/*
+			 * OS X specific SMB2 extension negotiated via
+			 * AAPL create context: return max_access in
+			 * ea_size field.
+			 */
+			uint32_t max_access_granted;
+			status = smbd_calculate_access_mask(conn,
+							    smb_fname,
+							    false,
+							    SEC_FLAG_MAXIMUM_ALLOWED,
+							    &max_access_granted);
+			if (!NT_STATUS_IS_OK(status)) {
+				max_access_granted = 0;
+			}
+			SIVAL(p, 0, max_access_granted);
 		} else {
 			unsigned int ea_size = estimate_ea_size(conn, NULL,
 								smb_fname);
 			SIVAL(p,0,ea_size); /* Extended attributes */
 		}
 		p += 4;
-		/* Clear the short name buffer. This is
-		 * IMPORTANT as not doing so will trigger
-		 * a Win2k client bug. JRA.
-		 */
-		if (!was_8_3 && check_mangled_names) {
+
+		if ((conn->sconn->client->connections->smb2.server.smb2_crtctx_aapl_readdir_attr) &&
+		    (conn->fs_capabilities & FILE_NAMED_STREAMS)) {
+			/*
+			 * OS X specific SMB2 extension negotiated via
+			 * AAPL create context: return resource fork
+			 * lenght and compressed FinderInfo in
+			 * shortname field.
+			 */
+			int ret;
+			struct smb_filename *resoname;
+			uint64_t rfork_size = 0;
+			/*
+			 * According to documentation short_name_len
+			 * should be 0, but on the wire behaviour
+			 * shows its set to 24 by clients.
+			 */
+			SSVAL(p, 0, 24);
+
+			resoname = synthetic_smb_fname(talloc_tos(),
+						       smb_fname->base_name,
+						       AFPRESOURCE_STREAM,
+						       NULL);
+			ret = SMB_VFS_STAT(conn, resoname);
+			if (ret == 0) {
+				rfork_size = resoname->st.st_ex_size;
+			}
+			TALLOC_FREE(resoname);
+			SBVAL(p + 2, 0, rfork_size);
+
+			get_compressed_finder_info(conn, smb_fname, p + 10);
+		} else if (!was_8_3 && check_mangled_names) {
 			char mangled_name[13]; /* mangled 8.3 name. */
 			if (!name_to_8_3(fname,mangled_name,True,
 					conn->params)) {
@@ -2127,10 +2262,28 @@ static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
 			}
 			SSVAL(p, 0, len);
 		} else {
+			/* Clear the short name buffer. This is
+			 * IMPORTANT as not doing so will trigger
+			 * a Win2k client bug. JRA.
+			 */
 			memset(p,'\0',26);
 		}
 		p += 26;
-		SSVAL(p,0,0); p += 2; /* Reserved ? */
+
+		/* Reserved ? */
+		if (conn->sconn->client->connections->smb2.server.smb2_crtctx_aapl_readdir_attr &&
+		    (conn->fs_capabilities & FILE_NAMED_STREAMS)) {
+			/*
+			 * OS X specific SMB2 extension negotiated via
+			 * AAPL create context: return UNIX mode in
+			 * reserved field.
+			 */
+			SSVAL(p, 0, (uint16_t)smb_fname->st.st_ex_mode);
+		} else {
+			SSVAL(p, 0, 0);
+		}
+		p += 2;
+
 		SBVAL(p,0,file_index); p += 8;
 		status = srvstr_push(base_data, flags2, p,
 				  fname, PTR_DIFF(end_data, p),
-- 
1.9.3


>From 0d2a738ab9f9cc21832d15b4691ae10537e9abcb Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 11 Oct 2014 09:12:05 +0200
Subject: [PATCH 05/11] libcli/security: add a function to remove MS NFS SIDs
 from sd

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 libcli/security/security_descriptor.c | 53 +++++++++++++++++++++++++++++++++++
 libcli/security/security_descriptor.h |  2 ++
 2 files changed, 55 insertions(+)

diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c
index 8304b20..c39f181 100644
--- a/libcli/security/security_descriptor.c
+++ b/libcli/security/security_descriptor.c
@@ -595,3 +595,56 @@ struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
 
 	return ace;
 }
+
+/*******************************************************************
+ Remove MS NFS ACEs from a sd
+*******************************************************************/
+
+NTSTATUS security_descriptor_ms_nfs_rm(struct security_descriptor *psd)
+{
+	int i;
+	struct security_acl *acl = psd->dacl;
+
+	if (acl == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	for (i=0; i < acl->num_aces;) {
+		if (dom_sid_compare_domain(&global_sid_Unix_NFS_Users,
+					   &acl->aces[i].trustee) == 0) {
+			memmove(&acl->aces[i], &acl->aces[i+1],
+				sizeof(acl->aces[i]) * (acl->num_aces - (i+1)));
+			acl->num_aces--;
+			if (acl->num_aces == 0) {
+				acl->aces = NULL;
+			}
+			continue;
+		}
+
+		if (dom_sid_compare_domain(&global_sid_Unix_NFS_Groups,
+					   &acl->aces[i].trustee) == 0) {
+			memmove(&acl->aces[i], &acl->aces[i+1],
+				sizeof(acl->aces[i]) * (acl->num_aces - (i+1)));
+			acl->num_aces--;
+			if (acl->num_aces == 0) {
+				acl->aces = NULL;
+			}
+			continue;
+		}
+
+		if (dom_sid_compare_domain(&global_sid_Unix_NFS_Mode,
+					   &acl->aces[i].trustee) == 0) {
+			memmove(&acl->aces[i], &acl->aces[i+1],
+				sizeof(acl->aces[i]) * (acl->num_aces - (i+1)));
+			acl->num_aces--;
+			if (acl->num_aces == 0) {
+				acl->aces = NULL;
+			}
+			continue;
+		}
+
+		i++;
+	}
+
+	return NT_STATUS_OK;
+}
diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h
index 1c7f893..17fb1dc 100644
--- a/libcli/security/security_descriptor.h
+++ b/libcli/security/security_descriptor.h
@@ -81,4 +81,6 @@ struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
 						       struct dom_sid *default_group, /* valid only for DS, NULL for the other RSs */
 						       uint32_t (*generic_map)(uint32_t access_mask));
 
+NTSTATUS security_descriptor_ms_nfs_rm(struct security_descriptor *psd);
+
 #endif /* __SECURITY_DESCRIPTOR_H__ */
-- 
1.9.3


>From 0570ee74158e1d4c6790d46ae39b6e8d24d7faab Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 14 Oct 2014 13:54:05 +0200
Subject: [PATCH 06/11] libcli/security: add a function that checks for MS NFS
 ACEs

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 libcli/security/security_descriptor.c | 22 ++++++++++++++++++++++
 libcli/security/security_descriptor.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c
index c39f181..f0e1f19 100644
--- a/libcli/security/security_descriptor.c
+++ b/libcli/security/security_descriptor.c
@@ -648,3 +648,25 @@ NTSTATUS security_descriptor_ms_nfs_rm(struct security_descriptor *psd)
 
 	return NT_STATUS_OK;
 }
+
+/*******************************************************************
+ Remove MS NFS ACEs from a sd
+*******************************************************************/
+bool security_descriptor_with_ms_nfs(const struct security_descriptor *psd)
+{
+	int i;
+
+	if (psd->dacl == NULL) {
+		return false;
+	}
+
+	for (i = 0; i < psd->dacl->num_aces; i++) {
+		if (dom_sid_compare_domain(
+			    &global_sid_Unix_NFS,
+			    &psd->dacl->aces[i].trustee) == 0) {
+			return true;
+		}
+	}
+
+	return false;
+}
diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h
index 17fb1dc..6511f04 100644
--- a/libcli/security/security_descriptor.h
+++ b/libcli/security/security_descriptor.h
@@ -82,5 +82,6 @@ struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
 						       uint32_t (*generic_map)(uint32_t access_mask));
 
 NTSTATUS security_descriptor_ms_nfs_rm(struct security_descriptor *psd);
+bool security_descriptor_with_ms_nfs(const struct security_descriptor *psd);
 
 #endif /* __SECURITY_DESCRIPTOR_H__ */
-- 
1.9.3


>From f64eb605a9fca1a2f29be882110411ea97e48956 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 11 Oct 2014 09:13:39 +0200
Subject: [PATCH 07/11] s3:smbd: support for MS NFS uid, gid and mode

Add optional support for MS NFS style mapping. uid, gid and mode can
be queried with get_secinfo, mode can be changed with
set_secinfo. This is only enabled by smb2/create context AAPL
capability negotiation atm.

Do the checking for MS NFS mode in nttrans.c, this facilitates all VFS
modules can work with it.

Due to semantic defficiencies roundtripping POSIX ACLs, if a set_acl
request contains a MS NFS mode SID, just apply the mode and skip the
rest.

Other ACL models that map mode<->ACEs in one way or another (in
contrast to Darwin were both are completely distinct domains), other
VFS modules may have to adapt for this change too.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/smbd/nttrans.c     | 104 +++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/posix_acls.c  |  18 ++++++++
 source3/smbd/smb2_create.c |   8 ++++
 3 files changed, 130 insertions(+)

diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index edad3e4..6528349 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -858,6 +858,44 @@ static void canonicalize_inheritance_bits(struct security_descriptor *psd)
 }
 
 /****************************************************************************
+ Search MS NFS style ACE with UNIX mode
+****************************************************************************/
+static NTSTATUS check_ms_nfs(files_struct *fsp,
+			     struct security_descriptor *psd,
+			     mode_t *pmode,
+			     bool *pdo_chmod)
+{
+	int i;
+
+	*pdo_chmod = false;
+
+	if (psd->dacl == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	if (!fsp->conn->sconn->client->connections->
+	    smb2.server.smb2_crtctx_aapl_unix_info) {
+		return NT_STATUS_OK;
+	}
+
+	for (i = 0; i < psd->dacl->num_aces; i++) {
+		if (dom_sid_compare_domain(
+			    &global_sid_Unix_NFS_Mode,
+			    &psd->dacl->aces[i].trustee) == 0) {
+			*pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
+			*pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+			*pdo_chmod = true;
+
+			DEBUG(10, ("MS NFS chmod request %s, %04o\n",
+				   fsp_str_dbg(fsp), *pmode));
+			break;
+		}
+	}
+
+	return NT_STATUS_OK;
+}
+
+/****************************************************************************
  Internal fn to set security descriptors.
 ****************************************************************************/
 
@@ -865,6 +903,9 @@ NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd,
 		       uint32_t security_info_sent)
 {
 	NTSTATUS status;
+	bool do_chmod;
+	mode_t ms_nfs_mode;
+	int result;
 
 	if (!CAN_WRITE(fsp->conn)) {
 		return NT_STATUS_ACCESS_DENIED;
@@ -927,8 +968,34 @@ NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd,
 		NDR_PRINT_DEBUG(security_descriptor, psd);
 	}
 
+	status = check_ms_nfs(fsp, psd, &ms_nfs_mode, &do_chmod);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto exit;
+	}
+
 	status = SMB_VFS_FSET_NT_ACL(fsp, security_info_sent, psd);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto exit;
+	}
+
+	if (do_chmod) {
+		if (fsp->fh->fd != -1) {
+			result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
+		} else {
+			result = SMB_VFS_CHMOD(fsp->conn,
+					       fsp->fsp_name->base_name,
+					       ms_nfs_mode);
+		}
 
+		if (result != 0) {
+			DEBUG(2, ("chmod: %s, %04o error %s\n", fsp_str_dbg(fsp),
+				  ms_nfs_mode, strerror(errno)));
+			status = map_nt_error_from_unix(errno);
+			return status;
+		}
+	}
+
+exit:
 	TALLOC_FREE(psd);
 
 	return status;
@@ -1922,6 +1989,43 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn,
 		return status;
 	}
 
+	/*
+	 * Add MS NFS style ACEs with uid, gid and mode
+	 */
+	if (conn->sconn->client->connections->smb2.server.smb2_crtctx_aapl_unix_info) {
+		struct security_ace ace;
+		struct dom_sid sid;
+		/* MS NFS style mode */
+		sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
+		init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+		status = security_descriptor_dacl_add(psd, &ace);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1,("failed to add MS NFS style ACE\n"));
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		/* MS NFS style uid */
+		sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
+		init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+		status = security_descriptor_dacl_add(psd, &ace);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1,("failed to add MS NFS style ACE\n"));
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		/* MS NFS style gid */
+		sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
+		init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+		status = security_descriptor_dacl_add(psd, &ace);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1,("failed to add MS NFS style ACE\n"));
+			TALLOC_FREE(frame);
+			return status;
+		}
+	}
+
 	if (!(security_info_wanted & SECINFO_OWNER)) {
 		psd->owner_sid = NULL;
 	}
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
index 126b822..bc75fa5 100644
--- a/source3/smbd/posix_acls.c
+++ b/source3/smbd/posix_acls.c
@@ -29,6 +29,7 @@
 #include "../librpc/gen_ndr/idmap.h"
 #include "../librpc/gen_ndr/ndr_smb_acl.h"
 #include "lib/param/loadparm.h"
+#include "smbd/globals.h"
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -3653,6 +3654,7 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const struct s
 	bool acl_set_support = false;
 	bool ret = false;
 	struct security_descriptor *psd = NULL;
+	int i;
 
 	DEBUG(10,("set_nt_acl: called for file %s\n",
 		  fsp_str_dbg(fsp)));
@@ -3666,6 +3668,22 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const struct s
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
+	/*
+	 * MS NFS mode, here's the deal: the client merely wants to
+	 * modify the mode, but roundtripping get_acl/set/acl would
+	 * add additional POSIX ACEs.  So in case we get a request
+	 * containing a MS NFS mode SID, we do nothing here.
+	 */
+	if ((security_info_sent & SECINFO_DACL) && (psd_orig->dacl)) {
+		for (i = 0; i < psd_orig->dacl->num_aces; i++) {
+			if (dom_sid_compare_domain(
+				    &global_sid_Unix_NFS,
+				    &psd_orig->dacl->aces[i].trustee) == 0) {
+			return NT_STATUS_OK;
+			}
+		}
+	}
+
 	psd = security_descriptor_copy(talloc_tos(), psd_orig);
 	if (psd == NULL) {
 		return NT_STATUS_NO_MEMORY;
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 36640a0..53c6d7d 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -1137,6 +1137,14 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
 					smb2req->xconn->smb2.server.smb2_crtctx_aapl_readdir_attr = true;
 				}
+
+				/*
+				 * The client doesn't set the flag, so we can't
+				 * check for it and just set it unconditionally
+				 */
+				server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
+				smb2req->xconn->smb2.server.smb2_crtctx_aapl_unix_info = true;
+
 				SBVAL(p, 0, server_caps);
 				ok = data_blob_append(smb2req, &blob, p, 8);
 				if (!ok) {
-- 
1.9.3


>From adcbabd9cba72da111470951a93f996412478921 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 11 Oct 2014 09:16:24 +0200
Subject: [PATCH 08/11] vfs:nfs4alcs: ignore MS NFS ACLs

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/nfs4_acls.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source3/modules/nfs4_acls.c b/source3/modules/nfs4_acls.c
index cf61af9..1aa819a 100644
--- a/source3/modules/nfs4_acls.c
+++ b/source3/modules/nfs4_acls.c
@@ -778,6 +778,9 @@ static bool smbacl4_fill_ace4(
 			ace_v4->who.gid = gid;
 		} else if (sid_to_uid(&ace_nt->trustee, &uid)) {
 			ace_v4->who.uid = uid;
+		} else if (dom_sid_compare_domain(&ace_nt->trustee,
+						  &global_sid_Unix_NFS) == 0) {
+			return false;
 		} else {
 			DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
 				  "convert %s to uid or gid\n",
-- 
1.9.3


>From 0bca1a875fa2a619b5d92d4d175edbd4882ba50f Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 11 Oct 2014 09:17:24 +0200
Subject: [PATCH 09/11] vfs_acl_xattr/tdb: ignore MS NFS ACLs

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_acl_common.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/source3/modules/vfs_acl_common.c b/source3/modules/vfs_acl_common.c
index b749157..920c811 100644
--- a/source3/modules/vfs_acl_common.c
+++ b/source3/modules/vfs_acl_common.c
@@ -775,6 +775,15 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp,
 		psd->group_sid = orig_psd->group_sid;
 	}
 	if (security_info_sent & SECINFO_DACL) {
+		if (security_descriptor_with_ms_nfs(orig_psd)) {
+			/*
+			 * If the sd contains a MS NFS SID, do
+			 * nothing, it's a chmod() request from OS X
+			 * with AAPL context.
+			 */
+			TALLOC_FREE(frame);
+			return NT_STATUS_OK;
+		}
 		psd->dacl = orig_psd->dacl;
 		psd->type |= SEC_DESC_DACL_PRESENT;
 	}
-- 
1.9.3


>From 6aed36ca19add31c6a8a4505447f37d309d2a8f4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Wed, 1 Oct 2014 14:36:43 +0200
Subject: [PATCH 10/11] s4:libcli/raw: make short_name available in buffer

This will be used in smb2/create AAPL context torture tests, where the
server returns an Mac OS X specific data blob in the short name
buffer. It's not a string, so the existing string extraction doesn't
cut it.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source4/libcli/raw/interfaces.h | 1 +
 source4/libcli/raw/rawsearch.c  | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h
index 9003c12..fcb5082 100644
--- a/source4/libcli/raw/interfaces.h
+++ b/source4/libcli/raw/interfaces.h
@@ -2739,6 +2739,7 @@ union smb_search_data {
 		uint32_t  attrib;
 		uint32_t  ea_size;
 		uint64_t file_id;
+		uint8_t short_name_buf[24];
 		struct smb_wire_string short_name;
 		struct smb_wire_string name;
 	} id_both_directory_info;
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
index 0705faa..31d0b50 100644
--- a/source4/libcli/raw/rawsearch.c
+++ b/source4/libcli/raw/rawsearch.c
@@ -456,6 +456,11 @@ NTSTATUS smb_raw_search_common(TALLOC_CTX *mem_ctx,
 		smbcli_blob_pull_string(NULL, mem_ctx, blob,
 				     &data->id_both_directory_info.short_name,
 				     68, 70, STR_LEN8BIT | STR_UNICODE);
+		if (data->id_both_directory_info.short_name.private_length == 24) {
+		    memcpy(data->id_both_directory_info.short_name_buf, blob->data + 70, 24);
+		} else {
+		    memset(data->id_both_directory_info.short_name_buf, 0, 24);
+		}
 		data->id_both_directory_info.file_id     = BVAL(blob->data,            96);
 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
 					      &data->id_both_directory_info.name,
-- 
1.9.3


>From 4e264e89907260760fcc6077772e7c3910872edf Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Sat, 27 Sep 2014 17:21:12 +0200
Subject: [PATCH 11/11] s4:torture:vfs_fruit: smb2/create context AAPL test

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source4/torture/vfs/fruit.c | 273 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 273 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 0c8d3a4..5d4a411 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -24,6 +24,7 @@
 #include "libcli/libcli.h"
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb/smb2_create_ctx.h"
 #include "lib/cmdline/popt_common.h"
 #include "param/param.h"
 #include "libcli/resolve/resolve.h"
@@ -1358,6 +1359,277 @@ done:
 	return ret;
 }
 
+static bool test_aapl(struct torture_context *tctx,
+		      struct smb2_tree *tree1,
+		      struct smb2_tree *tree2)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	const char *fname = BASEDIR "\\test_aapl";
+	NTSTATUS status;
+	struct smb2_handle testdirh;
+	bool ret = true;
+	struct smb2_create io;
+	DATA_BLOB data;
+	struct smb2_create_blob *aapl = NULL;
+	AfpInfo *info;
+	const char *type_creator = "SMB,OLE!";
+	char type_creator_buf[9];
+	uint32_t aapl_cmd;
+	uint32_t aapl_reply_bitmap;
+	uint32_t aapl_server_caps;
+	uint32_t aapl_vol_caps;
+	char *model;
+	struct smb2_find f;
+	unsigned int count;
+	union smb_search_data *d;
+	uint64_t rfork_len;
+
+	smb2_deltree(tree1, BASEDIR);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &testdirh);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, testdirh);
+
+	ZERO_STRUCT(io);
+	io.in.desired_access     = SEC_FLAG_MAXIMUM_ALLOWED;
+	io.in.file_attributes    = FILE_ATTRIBUTE_NORMAL;
+	io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+	io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE |
+			      NTCREATEX_SHARE_ACCESS_READ |
+			      NTCREATEX_SHARE_ACCESS_WRITE);
+	io.in.fname = fname;
+
+	/*
+	 * Issuing an SMB2/CREATE with a suitably formed AAPL context,
+	 * controls behaviour of Apple's SMB2 extensions for the whole
+	 * session!
+	 */
+
+	data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t));
+	SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
+	SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS |
+			     SMB2_CRTCTX_AAPL_VOLUME_CAPS |
+			     SMB2_CRTCTX_AAPL_MODEL_INFO));
+	SBVAL(data.data, 16, (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
+			      SMB2_CRTCTX_AAPL_UNIX_BASED |
+			      SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE));
+
+	torture_comment(tctx, "Testing SMB2 create context AAPL\n");
+	status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	status = smb2_create(tree1, tctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	status = smb2_util_close(tree1, io.out.file.handle);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/*
+	 * Now check returned AAPL context
+	 */
+	torture_comment(tctx, "Comparing returned AAPL capabilites\n");
+
+	aapl = smb2_create_blob_find(&io.out.blobs,
+				     SMB2_CREATE_TAG_AAPL);
+
+	if (aapl->data.length != 50) {
+		/*
+		 * uint32_t CommandCode = kAAPL_SERVER_QUERY
+		 * uint32_t Reserved = 0;
+		 * uint64_t ReplyBitmap = kAAPL_SERVER_CAPS |
+		 *                        kAAPL_VOLUME_CAPS |
+		 *                        kAAPL_MODEL_INFO;
+		 * uint64_t ServerCaps = kAAPL_SUPPORTS_READDIR_ATTR |
+		 *                       kAAPL_SUPPORTS_OSX_COPYFILE;
+		 * uint64_t VolumeCaps = kAAPL_SUPPORT_RESOLVE_ID |
+		 *                       kAAPL_CASE_SENSITIVE;
+		 * uint32_t Pad2 = 0;
+		 * uint32_t ModelStringLen = 10;
+		 * ucs2_t ModelString[5] = "Samba";
+		 */
+		ret = false;
+		goto done;
+	}
+
+	aapl_cmd = IVAL(aapl->data.data, 0);
+	if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) unexpected cmd: %d",
+			       __location__, (int)aapl_cmd);
+		ret = false;
+		goto done;
+	}
+
+	aapl_reply_bitmap = BVAL(aapl->data.data, 8);
+	if (aapl_reply_bitmap != (SMB2_CRTCTX_AAPL_SERVER_CAPS |
+				  SMB2_CRTCTX_AAPL_VOLUME_CAPS |
+				  SMB2_CRTCTX_AAPL_MODEL_INFO)) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) unexpected reply_bitmap: %d",
+			       __location__, (int)aapl_reply_bitmap);
+		ret = false;
+		goto done;
+	}
+
+	aapl_server_caps = BVAL(aapl->data.data, 16);
+	if (aapl_server_caps != (SMB2_CRTCTX_AAPL_UNIX_BASED |
+				 SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
+				 SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE)) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) unexpected server_caps: %d",
+			       __location__, (int)aapl_server_caps);
+		ret = false;
+		goto done;
+	}
+
+	aapl_vol_caps = BVAL(aapl->data.data, 24);
+	if (aapl_vol_caps != SMB2_CRTCTX_AAPL_CASE_SENSITIVE) {
+		/* this will fail on a case insensitive fs ... */
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) unexpected vol_caps: %d",
+			       __location__, (int)aapl_vol_caps);
+		ret = false;
+		goto done;
+	}
+
+	ret = convert_string_talloc(mem_ctx,
+				    CH_UTF16LE, CH_UNIX,
+				    aapl->data.data + 40, 10,
+				    &model, NULL);
+	if (ret == false) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) convert_string_talloc() failed",
+			       __location__);
+		goto done;
+	}
+	if (strncmp(model, "Samba", 5) != 0) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) expected model \"Samba\", got: \"%s\"",
+			       __location__, model);
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * Now that Requested AAPL extensions are enabled, setup some
+	 * Mac files with metadata and resource fork
+	 */
+	ret = torture_setup_file(mem_ctx, tree1, fname, false);
+	if (ret == false) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) torture_setup_file() failed",
+			       __location__);
+		goto done;
+	}
+
+	info = torture_afpinfo_new(mem_ctx);
+	if (info == NULL) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) torture_afpinfo_new() failed",
+			       __location__);
+		ret = false;
+		goto done;
+	}
+
+	memcpy(info->afpi_FinderInfo, type_creator, 8);
+	ret = torture_write_afpinfo(tree1, tctx, mem_ctx, fname, info);
+	if (ret == false) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) torture_write_afpinfo() failed",
+			       __location__);
+		goto done;
+	}
+
+	ret = write_stream(tree1, __location__, tctx, mem_ctx,
+			   fname, AFPRESOURCE_STREAM,
+			   0, 3, "foo");
+	if (ret == false) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) write_stream() failed",
+			       __location__);
+		goto done;
+	}
+
+	/*
+	 * Ok, file is prepared, now call smb2/find
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.desired_access = SEC_RIGHTS_DIR_ALL;
+	io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+	io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+	io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
+			      NTCREATEX_SHARE_ACCESS_WRITE |
+			      NTCREATEX_SHARE_ACCESS_DELETE);
+	io.in.create_disposition = NTCREATEX_DISP_OPEN;
+	io.in.fname = BASEDIR;
+	status = smb2_create(tree1, tctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ZERO_STRUCT(f);
+	f.in.file.handle	= io.out.file.handle;
+	f.in.pattern		= "test_aapl";
+	f.in.continue_flags	= SMB2_CONTINUE_FLAG_SINGLE;
+	f.in.max_response_size	= 0x1000;
+	f.in.level              = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+
+	status = smb2_find_level(tree1, tree1, &f, &count, &d);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	status = smb2_util_close(tree1, io.out.file.handle);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	if (strcmp(d[0].id_both_directory_info.name.s, "test_aapl") != 0) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) write_stream() failed",
+			       __location__);
+		ret = false;
+		goto done;
+	}
+
+	if (d[0].id_both_directory_info.short_name.private_length != 24) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) bad short_name length %" PRIu32 ", expected 24",
+			       __location__, d[0].id_both_directory_info.short_name.private_length);
+		ret = false;
+		goto done;
+	}
+
+	torture_comment(tctx, "short_name buffer:\n");
+	dump_data(0, d[0].id_both_directory_info.short_name_buf, 24);
+
+	/*
+	 * Extract data as specified by the AAPL extension:
+	 * - ea_size contains max_access
+	 * - short_name contains resource fork length + FinderInfo
+	 * - reserved2 contains the unix mode
+	 */
+	torture_comment(tctx, "mac_access: %" PRIx32 "\n",
+			d[0].id_both_directory_info.ea_size);
+
+	rfork_len = BVAL(d[0].id_both_directory_info.short_name_buf, 0);
+	if (rfork_len != 3) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) expected resource fork length 3, got: %" PRIu64,
+			       __location__, rfork_len);
+		ret = false;
+		goto done;
+	}
+
+	memcpy(type_creator_buf, d[0].id_both_directory_info.short_name_buf + 8, 8);
+	type_creator_buf[8] = 0;
+	if (strcmp(type_creator, type_creator_buf) != 0) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "(%s) expected type/creator \"%s\" , got: %s",
+			       __location__, type_creator, type_creator_buf);
+		ret = false;
+		goto done;
+	}
+
+done:
+	talloc_free(mem_ctx);
+	return ret;
+}
+
 /*
  * Note: This test depends on "vfs objects = catia fruit
  * streams_xattr".  Note: To run this test, use
@@ -1376,6 +1648,7 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_2ns_smb2_test(suite, "write metadata", test_write_atalk_metadata);
 	torture_suite_add_2ns_smb2_test(suite, "resource fork IO", test_write_atalk_rfork_io);
 	torture_suite_add_2ns_smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion);
+	torture_suite_add_2ns_smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl);
 
 	return suite;
 }
-- 
1.9.3



More information about the samba-technical mailing list