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

Ralph Böhme rb at sernet.de
Thu Sep 11 07:22:44 MDT 2014


On Thu, Sep 04, 2014 at 06:48:33AM -0700, Jeremy Allison wrote:
> On Thu, Sep 04, 2014 at 03:17:13PM +0200, Ralph Böhme wrote:
> > 
> > Is there a way to improve this? You bet there is! ;)
> > 
> > Apple uses a custom smb2 protocol extension in its own server (and
> > client): a smb2 create context "AAPL". If both server and client
> 
> I didn't know about this ! Have Apple documented
> this anywhere ?
> 
> > support this it, the smb2_find reply (amongst other things depending
> > on a set of flags [2]) is modified to contain resource fork size
> > information, which means after step 2 (see above) is completed, the
> > Finder will display the folder content.
> > 
> > I have a working PoC here: [3]. Opening a folder with 1000 files over
> > a slow link with 50 ms latency takes just a few seconds instead of 40
> > seconds with just vfs_streams_xattr or vfs_fruit alone.
> > 
> > This is still incomplete and needs more polishing.
> 
> W00t! Go Ralph !!!!! As soon as you have something
> for master please post.

here it is: implement SMB2 AAPL create context.

What is it good for? Client and server negotiate a set of SMB2
extensions:

* repurpose several fields in SMB2/FIND reply for retrieval of the
  following attributes: UNIX mode, resource fork length, FinderInfo
  and max_acess

* use NFS customs SIDs in ACEs [1] for getting and setting UNIX mode

Please review and push if ok.

These patches are in my aapl branch:
<https://github.com/slowfranklin/samba/commits/aapl>

Thanks!
-Ralph

[1] <http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx>
    Couldn't find any other reference from MS that this one.

-- 
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 7a023ac43b052bf8796b5648701a80874bcee2e4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Fri, 22 Aug 2014 03:45:15 +0200
Subject: [PATCH 1/6] s3:smbd: make get_file_handle_for_metadata() public

In preperation of SMB2 AAPL create context: will be used when
enumerating directories in order to internally retrieve stream
information of filesystem objects.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source3/smbd/dosmode.c | 13 ++++---------
 source3/smbd/proto.h   |  5 +++++
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
index 162684b..5b409c9 100644
--- a/source3/smbd/dosmode.c
+++ b/source3/smbd/dosmode.c
@@ -26,11 +26,6 @@
 #include "smbd/smbd.h"
 #include "lib/param/loadparm.h"
 
-static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
-				struct smb_filename *smb_fname,
-				files_struct **ret_fsp,
-				bool *need_close);
-
 static void dos_mode_debug_print(uint32_t mode)
 {
 	DEBUG(8,("dos_mode returning "));
@@ -1102,10 +1097,10 @@ struct timespec get_change_timespec(connection_struct *conn,
  avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
 ****************************************************************************/
 
-static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
-				struct smb_filename *smb_fname,
-				files_struct **ret_fsp,
-				bool *need_close)
+NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
+				      struct smb_filename *smb_fname,
+				      files_struct **ret_fsp,
+				      bool *need_close)
 {
 	NTSTATUS status;
 	files_struct *fsp;
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 692f582..c4fed64 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -284,6 +284,11 @@ struct timespec get_change_timespec(connection_struct *conn,
 				struct files_struct *fsp,
 				const struct smb_filename *smb_fname);
 
+NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
+				      struct smb_filename *smb_fname,
+				      files_struct **ret_fsp,
+				      bool *need_close);
+
 /* The following definitions come from smbd/error.c  */
 
 bool use_nt_status(void);
-- 
1.9.3


>From 64b080e1c6cad9b05c102590ceaeafd6abf668b5 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 2/6] s3: smbd: add SMB2 AAPL create context defines and option
 flags

smb2_crtctx_aapl_readdir_attr = true | false

  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.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 libcli/smb/smb2_constants.h  |  1 +
 libcli/smb/smb2_create_ctx.h | 45 ++++++++++++++++++++++++++++++++++++++++++++
 source3/include/vfs.h        |  1 +
 3 files changed, 47 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 0b34723..fbab3e1 100644
--- a/libcli/smb/smb2_constants.h
+++ b/libcli/smb/smb2_constants.h
@@ -207,6 +207,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..f301c7c
--- /dev/null
+++ b/libcli/smb/smb2_create_ctx.h
@@ -0,0 +1,45 @@
+/*
+   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
+
+/* "AAPL" Volume Capabilities bitmap */
+#define SMB2_CRTCTX_AAPL_SUPPORT_RESOLVE_ID 1
+#define SMB2_CRTCTX_AAPL_CASE_SENSITIVE     2
+
+#endif
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 3702b75..d90a631 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -355,6 +355,7 @@ typedef struct connection_struct {
 	bool case_sensitive;
 	bool case_preserve;
 	bool short_case_preserve;
+	bool smb2_crtctx_aapl_readdir_attr;
 
 	/* Semantics provided by the underlying filesystem. */
 	int fs_capabilities;
-- 
1.9.3


>From 9ab4811c52808e8c42b308270170abda842667fd 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 3/6] s3:smbd: SMB2 AAPL create context implementation

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 | 107 +++++++++++++++++++++++++++++++
 source3/smbd/trans2.c      | 152 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 253 insertions(+), 6 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 1853434..5bbe777 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,11 @@ 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 (lp_parm_bool(SNUM(smb2req->tcon->compat),
+				 "smb2 create tag", "aapl", true)) {
+			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 +811,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 +1111,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[8];
+			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->tcon->compat->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 1e2c02e..eb52611 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
 
@@ -1570,6 +1571,82 @@ 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;
+	NTSTATUS status;
+	bool need_close;
+	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 = get_file_handle_for_metadata(conn, infoname,
+					      &fsp, &need_close);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("get_file_handle_for_metadata %s: %s\n",
+			   smb_fname_str_dbg(infoname),
+			   nt_errstr(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 (need_close) {
+		SMB_VFS_CLOSE(fsp);
+	}
+	talloc_free(infoname);
+	return;
+}
+
 static bool smbd_marshall_dir_entry(TALLOC_CTX *ctx,
 				    connection_struct *conn,
 				    uint16_t flags2,
@@ -2051,17 +2128,62 @@ static bool 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->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;
+			NTSTATUS status;
+			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->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)) {
@@ -2078,10 +2200,28 @@ static bool 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->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;
 		len = srvstr_push(base_data, flags2, p,
 				  fname, PTR_DIFF(end_data, p),
-- 
1.9.3


>From 5430f921959073f66915aec012fa3843483b9376 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Mon, 8 Sep 2014 23:05:32 +0200
Subject: [PATCH 4/6] s3:smbd: flags for NFS ACE in AAPL create context

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

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

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 libcli/smb/smb2_create_ctx.h | 1 +
 source3/include/vfs.h        | 1 +
 source3/smbd/smb2_create.c   | 8 ++++++++
 3 files changed, 10 insertions(+)

diff --git a/libcli/smb/smb2_create_ctx.h b/libcli/smb/smb2_create_ctx.h
index f301c7c..cb194f5 100644
--- a/libcli/smb/smb2_create_ctx.h
+++ b/libcli/smb/smb2_create_ctx.h
@@ -37,6 +37,7 @@
 #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
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index d90a631..cd126c2 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -356,6 +356,7 @@ typedef struct connection_struct {
 	bool case_preserve;
 	bool short_case_preserve;
 	bool smb2_crtctx_aapl_readdir_attr;
+	bool smb2_crtctx_aapl_unix_info;
 
 	/* Semantics provided by the underlying filesystem. */
 	int fs_capabilities;
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 5bbe777..3be8650 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -1138,6 +1138,14 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
 					smb2req->tcon->compat->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->tcon->compat->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 55cc00706f9bf2c49da92494deec4c8f63fa0aa7 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 5/6] libcli/security: add NFS SID mappings

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

diff --git a/libcli/security/dom_sid.h b/libcli/security/dom_sid.h
index c4a417b..a3f52cb 100644
--- a/libcli/security/dom_sid.h
+++ b/libcli/security/dom_sid.h
@@ -53,6 +53,10 @@ 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_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..eda7247 100644
--- a/libcli/security/util_sid.c
+++ b/libcli/security/util_sid.c
@@ -96,6 +96,21 @@ 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_Users =		/* Unix users, 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 groups, 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 6327c245f364cf7d65771533adab11d8dc60cb8a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Mon, 8 Sep 2014 23:22:44 +0200
Subject: [PATCH 6/6] vfs_fruit: SMB2 AAPL create_context: NFS ACEs

Return uid, gid and mode in NFS ACEs append them to the ACL we get
from the next VFS module.

Extract mode from client NFS ACE and apply it.

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

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 1226dc0..74fe8cc 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2899,6 +2899,176 @@ fail:
 	return status;
 }
 
+/* NT ACL operations. */
+
+static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
+				  files_struct *fsp,
+				  uint32 security_info,
+				  TALLOC_CTX *mem_ctx,
+				  struct security_descriptor **ppdesc)
+{
+	NTSTATUS result;
+	struct security_descriptor *sd;
+	struct security_ace ace;
+	struct dom_sid sid;
+
+	result = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+					  mem_ctx, ppdesc);
+	if (!NT_STATUS_IS_OK(result)) {
+	    return result;
+	}
+
+	if (!handle->conn->smb2_crtctx_aapl_unix_info) {
+	     return result;
+	}
+
+	sd = *ppdesc;
+	if (sd == NULL) {
+		return result;
+	}
+
+	/* Add special NFS ACE with 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);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+	    return result;
+	}
+
+	/* Add special NFS ACE with 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);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+	    return result;
+	}
+
+	/* Add special NFS ACE with 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);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+	    return result;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_get_nt_acl(vfs_handle_struct *handle,
+				 const char *name,
+				 uint32 security_info,
+				 TALLOC_CTX *mem_ctx,
+				 struct security_descriptor **ppdesc)
+{
+	NTSTATUS result;
+	struct security_descriptor *sd;
+	struct security_ace ace;
+	struct dom_sid sid;
+	struct smb_filename *smb_fname = NULL;
+	int rc;
+
+	result = SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
+					 mem_ctx, ppdesc);
+
+	if (!NT_STATUS_IS_OK(result)) {
+	    return result;
+	}
+
+	if (!handle->conn->smb2_crtctx_aapl_unix_info) {
+	     return NT_STATUS_OK;
+	}
+
+	sd = *ppdesc;
+	if (sd == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	smb_fname = synthetic_smb_fname(talloc_tos(), name, NULL, NULL);
+	if (smb_fname == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	rc = SMB_VFS_STAT(handle->conn, smb_fname);
+	if (rc != 0) {
+		goto out;
+	}
+
+	/* Add special NFS ACE with mode */
+	sid_compose(&sid, &global_sid_Unix_NFS_Mode,
+		    smb_fname->st.st_ex_mode);
+	init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+		goto out;
+	}
+
+	/* Add special NFS ACE with uid */
+	sid_compose(&sid, &global_sid_Unix_NFS_Users,
+		    smb_fname->st.st_ex_uid);
+	init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+		goto out;
+	}
+
+	/* Add special NFS ACE with gid */
+	sid_compose(&sid, &global_sid_Unix_NFS_Groups,
+		    smb_fname->st.st_ex_gid);
+	init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+	result = security_descriptor_dacl_add(sd, &ace);
+	if (!NT_STATUS_IS_OK(result)) {
+		goto out;
+	}
+
+out:
+	TALLOC_FREE(smb_fname);
+	return result;
+}
+
+static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
+				  files_struct *fsp,
+				  uint32 security_info_sent,
+				  const struct security_descriptor *psd)
+{
+	int i, rc;
+	bool do_chmod = false;
+	mode_t mode;
+
+	if (!handle->conn->smb2_crtctx_aapl_unix_info
+	    || psd->dacl == NULL
+	    || psd->dacl->aces == NULL) {
+		return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp,
+						security_info_sent, psd);
+	}
+
+	/* Search NFS ACE with mode */
+	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) {
+			mode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
+			mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+			do_chmod = true;
+			break;
+		}
+	}
+
+	if (do_chmod) {
+		/* Apply mode and return without calling VFS_NEXT */
+		DEBUG(10, ("fruit_fset_nt_acl: %s, %04o\n",
+			   fsp_str_dbg(fsp), mode));
+		rc = SMB_VFS_CHMOD(handle->conn, fsp->fsp_name->base_name,
+				   mode);
+		if (rc != 0) {
+			return map_nt_error_from_unix_common(rc);
+		}
+		return NT_STATUS_OK;
+	}
+
+	return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+}
+
 static struct vfs_fn_pointers vfs_fruit_fns = {
 	.connect_fn = fruit_connect,
 
@@ -2920,6 +3090,12 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
 	.ftruncate_fn = fruit_ftruncate,
 	.fallocate_fn = fruit_fallocate,
 	.create_file_fn = fruit_create_file,
+
+	/* NT ACL operations. */
+
+	.fget_nt_acl_fn = fruit_fget_nt_acl,
+	.get_nt_acl_fn = fruit_get_nt_acl,
+	.fset_nt_acl_fn = fruit_fset_nt_acl,
 };
 
 NTSTATUS vfs_fruit_init(void);
-- 
1.9.3



More information about the samba-technical mailing list