[PATCH] Add support for setting owner and group in ntsd

Boris Protopopov boris.v.protopopov at gmail.com
Mon Jan 6 16:31:19 UTC 2020


Extend setcifsacl utility to allow setting owner and group SIDs
in the security descriptor in addition to setting ACLs. This is
a user-friendly intefrace for setting owner and group SIDs that
takes advantage of the recent extensions in the CIFS kernel
client, and it complements setting raw values via setfattr.

Signed-off-by: Boris Protopopov <boris.v.protopopov at gmail.com>
---
 cifsacl.h         |   4 +-
 setcifsacl.c      | 253 ++++++++++++++++++++++++++++++++++++++++++++----------
 setcifsacl.rst.in |  27 ++++--
 3 files changed, 235 insertions(+), 49 deletions(-)

diff --git a/cifsacl.h b/cifsacl.h
index ca72dd4..bd0c695 100644
--- a/cifsacl.h
+++ b/cifsacl.h
@@ -26,7 +26,9 @@
 #define _CIFSACL_H
 
 #define BUFSIZE 1024
-#define ATTRNAME "system.cifs_acl"
+#define ATTRNAME	"system.cifs_acl"
+#define ATTRNAME_ACL	ATTRNAME
+#define ATTRNAME_NTSD	"system.cifs_ntsd"
 
 #define MAX_NUM_AUTHS 6
 
diff --git a/setcifsacl.c b/setcifsacl.c
index 9a301e2..6e5a633 100644
--- a/setcifsacl.c
+++ b/setcifsacl.c
@@ -44,7 +44,9 @@ enum setcifsacl_actions {
 	ActDelete,
 	ActModify,
 	ActAdd,
-	ActSet
+	ActSetAcl,
+	ActSetOwner,
+	ActSetGroup
 };
 
 static void *plugin_handle;
@@ -140,6 +142,90 @@ copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
 	return bufsize;
 }
 
+/*
+ * This function (and the one above) does not need to set the SACL-related
+ * fields, and this works fine because on the SMB protocol level, setting owner
+ * info, DACL, and SACL requires one to use separate flags that control which
+ * part of the descriptor is begin looked at on the server side
+ */
+static ssize_t
+copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+		struct cifs_sid *sid, int maction)
+{
+	int size, daclsize;
+	int osidoffset, gsidoffset, dacloffset;
+	int nosidoffset, ngsidoffset, ndacloffset, nsidssize;
+	ssize_t bufsize;
+	struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+	struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+	struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
+
+	/* copy security descriptor control portion */
+	osidoffset = le32toh(pntsd->osidoffset);
+	gsidoffset = le32toh(pntsd->gsidoffset);
+	dacloffset = le32toh(pntsd->dacloffset);
+	/*
+	 * the size of the owner or group sid might be different from the old
+	 * one, so the group sid offest might change, and if the owner is
+	 * positioned before the DACL, the dacl offset might change as well;
+	 * note however, that the owner sid offset does not change
+	 */
+	nosidoffset = osidoffset;
+	size = sizeof(struct cifs_ntsd);
+	pnntsd->revision = pntsd->revision;
+	pnntsd->type = pntsd->type;
+	pnntsd->osidoffset = pntsd->osidoffset;
+	bufsize = size;
+
+	/* set the pointers for source sids */
+	if (maction == ActSetOwner) {
+		owner_sid_ptr = sid;
+		group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidoffset);
+	}
+	if (maction == ActSetGroup) {
+		owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidoffset);
+		group_sid_ptr = sid;
+	}
+
+	dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
+	daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl);
+
+	/* copy owner sid */
+	nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset);
+	size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
+	bufsize += size;
+	nsidssize = size;
+
+	/* copy group sid */
+	ngsidoffset = nosidoffset + size;
+	ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + ngsidoffset);
+	pnntsd->gsidoffset = htole32(ngsidoffset);
+	size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
+	bufsize += size;
+	nsidssize += size;
+
+	/* position the dacl control info as in the fetched descriptor */
+	if (dacloffset <= osidoffset)
+		ndacloffset = dacloffset;
+	else
+		ndacloffset = nosidoffset + nsidssize;
+	ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + ndacloffset);
+	pnntsd->dacloffset = htole32(ndacloffset);
+
+	/* the DACL control fields do not change */
+	ndacl_ptr->revision = dacl_ptr->revision;
+	ndacl_ptr->size = dacl_ptr->size;
+	ndacl_ptr->num_aces = dacl_ptr->num_aces;
+
+	/*
+	 * add DACL size (control portion and the array of aces) to the
+	 * buffer size
+	 */
+	bufsize += daclsize;
+
+	return bufsize;
+}
+
 static int
 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
 {
@@ -788,7 +874,7 @@ setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
 		rc = ace_add(pntsd, npntsd, bufsize, facesptr,
 				numfaces, cacesptr, numcaces);
 		break;
-	case ActSet:
+	case ActSetAcl:
 		rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
 		break;
 	default:
@@ -803,9 +889,10 @@ static void
 setcifsacl_usage(const char *prog)
 {
 	fprintf(stderr,
-	"%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
+	"%s: Alter CIFS/NTFS ACL or owner/group in a security descriptor of a file object\n",
+		prog);
+	fprintf(stderr, "Usage: %s option [<list_of_ACEs>|<SID>] <file_name>\n",
 		prog);
-	fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
 	fprintf(stderr, "Valid options:\n");
 	fprintf(stderr, "\t-v	Version of the program\n");
 	fprintf(stderr, "\n\t-a	Add ACE(s), separated by a comma, to an ACL\n");
@@ -825,21 +912,32 @@ setcifsacl_usage(const char *prog)
 	"\n\t-S	Replace existing ACL with ACE(s), separated by a comma\n");
 	fprintf(stderr,
 	"\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
+	fprintf(stderr,
+	"\n\t-o	Set owner using specified SID (name or raw format)\n");
+	fprintf(stderr,
+	"\tsetcifsacl -o \"Administrator\" <file_name>\n");
+	fprintf(stderr,
+	"\n\t-g	Set group using specified SID (name or raw format)\n");
+	fprintf(stderr,
+	"\tsetcifsacl -g \"Administrators\" <file_name>\n");
 	fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
 }
 
 int
 main(const int argc, char *const argv[])
 {
-	int i, rc, c, numcaces, numfaces;
+	int i, rc, c, numcaces = 0, numfaces = 0;
 	enum setcifsacl_actions maction = ActUnknown;
 	ssize_t attrlen, bufsize = BUFSIZE;
-	char *ace_list, *filename, *attrval, **arrptr = NULL;
+	char *ace_list = NULL, *filename = NULL, *attrval = NULL,
+		**arrptr = NULL, *sid_str = NULL;
 	struct cifs_ctrl_acl *daclptr = NULL;
 	struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
 	struct cifs_ntsd *ntsdptr = NULL;
+	struct cifs_sid sid;
+	char *attrname = ATTRNAME_ACL;
 
-	c = getopt(argc, argv, "hvD:M:a:S:");
+	c = getopt(argc, argv, "hvD:M:a:S:o:g:");
 	switch (c) {
 	case 'D':
 		maction = ActDelete;
@@ -854,9 +952,19 @@ main(const int argc, char *const argv[])
 		ace_list = optarg;
 		break;
 	case 'S':
-		maction = ActSet;
+		maction = ActSetAcl;
 		ace_list = optarg;
 		break;
+	case 'o':
+		maction = ActSetOwner;
+		sid_str = optarg;
+		attrname = ATTRNAME_NTSD;
+		break;
+	case 'g':
+		maction = ActSetGroup;
+		sid_str = optarg;
+		attrname = ATTRNAME_NTSD;
+		break;
 	case 'h':
 		setcifsacl_usage(basename(argv[0]));
 		return 0;
@@ -875,11 +983,16 @@ main(const int argc, char *const argv[])
 	}
 	filename = argv[3];
 
-	if (!ace_list) {
+	if (!ace_list && maction != ActSetOwner && maction != ActSetGroup) {
 		printf("%s: No valid ACEs specified\n", __func__);
 		return -1;
 	}
 
+	if (!sid_str && (maction == ActSetOwner || maction == ActSetGroup)) {
+		printf("%s: No valid SIDs specified\n", __func__);
+		return -1;
+	}
+
 	if (init_plugin(&plugin_handle)) {
 		fprintf(stderr, "WARNING: unable to initialize idmapping "
 				"plugin. Only \"raw\" SID strings will be "
@@ -889,16 +1002,24 @@ main(const int argc, char *const argv[])
 		plugin_loaded = true;
 	}
 
-	numcaces = get_numcaces(ace_list);
-
-	arrptr = parse_cmdline_aces(ace_list, numcaces);
-	if (!arrptr)
-		goto setcifsacl_numcaces_ret;
+	if (maction == ActSetOwner || maction == ActSetGroup) {
+		/* parse the sid */
+		if (setcifsacl_str_to_sid(sid_str, &sid)) {
+			printf("%s: failed to parce \'%s\' as SID\n", __func__,
+				sid_str);
+			goto setcifsacl_numcaces_ret;
+		}
+	} else {
+		numcaces = get_numcaces(ace_list);
 
-	cacesptr = build_cmdline_aces(arrptr, numcaces);
-	if (!cacesptr)
-		goto setcifsacl_cmdlineparse_ret;
+		arrptr = parse_cmdline_aces(ace_list, numcaces);
+		if (!arrptr)
+			goto setcifsacl_numcaces_ret;
 
+		cacesptr = build_cmdline_aces(arrptr, numcaces);
+		if (!cacesptr)
+			goto setcifsacl_cmdlineparse_ret;
+	}
 cifsacl:
 	if (bufsize >= XATTR_SIZE_MAX) {
 		printf("%s: Buffer size %zd exceeds max size of %d\n",
@@ -912,7 +1033,7 @@ cifsacl:
 		goto setcifsacl_cmdlineverify_ret;
 	}
 
-	attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
+	attrlen = getxattr(filename, attrname, attrval, bufsize);
 	if (attrlen == -1) {
 		if (errno == ERANGE) {
 			free(attrval);
@@ -924,26 +1045,64 @@ cifsacl:
 		}
 	}
 
-	numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
-	if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
-		printf("%s: Empty DACL\n", __func__);
-		goto setcifsacl_facenum_ret;
-	}
+	if (maction == ActSetOwner || maction == ActSetGroup) {
+		struct cifs_ntsd *pfntsd = (struct cifs_ntsd *)attrval;
+		int dacloffset = le32toh(pfntsd->dacloffset);
+		struct cifs_ctrl_acl *daclinfo =
+				(struct cifs_ctrl_acl *)(attrval + dacloffset);
+		int numaces = le16toh(daclinfo->num_aces);
+		int acessize = le32toh(daclinfo->size);
+		size_t faceoffset, naceoffset;
+		char *faceptr, *naceptr;
 
-	facesptr = build_fetched_aces((char *)daclptr, numfaces);
-	if (!facesptr)
-		goto setcifsacl_facenum_ret;
+		/*
+		 * this allocates large enough buffer for max sid size and the
+		 * dacl info from the fetched security descriptor
+		 */
+		rc = alloc_sec_desc(pfntsd, &ntsdptr, numaces, &faceoffset);
+		if (rc)
+			goto setcifsacl_numcaces_ret;
 
-	bufsize = 0;
-	rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
-		facesptr, numfaces, cacesptr, numcaces, maction);
-	if (rc)
-		goto setcifsacl_action_ret;
+		/*
+		 * copy the control structures from the fetched descriptor, the
+		 * sid specified by the user, and adjust the offsets/move dacl
+		 * control structure if needed
+		 */
+		bufsize = copy_sec_desc_with_sid(pfntsd, ntsdptr, &sid,
+				maction);
+
+		/* copy aces verbatim as they have not changed */
+		faceptr = attrval + faceoffset;
+		naceoffset = le32toh(ntsdptr->dacloffset) +
+				sizeof(struct cifs_ctrl_acl);
+		naceptr = (char *)ntsdptr + naceoffset;
+		memcpy(naceptr, faceptr, acessize);
+	} else {
+		bufsize = 0;
+
+		numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen,
+				&daclptr);
+		if (!numfaces && maction != ActAdd) {
+			/* if we are not adding aces */
+			printf("%s: Empty DACL\n", __func__);
+			goto setcifsacl_facenum_ret;
+		}
+
+		facesptr = build_fetched_aces((char *)daclptr, numfaces);
+		if (!facesptr)
+			goto setcifsacl_facenum_ret;
 
-	attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
+		rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr,
+				&bufsize, facesptr, numfaces, cacesptr,
+				numcaces, maction);
+		if (rc)
+			goto setcifsacl_action_ret;
+	}
+
+	attrlen = setxattr(filename, attrname, ntsdptr, bufsize, 0);
 	if (attrlen == -1) {
 		printf("%s: setxattr error: %s\n", __func__, strerror(errno));
-		goto setcifsacl_facenum_ret;
+		goto setcifsacl_action_ret;
 	}
 
 	if (plugin_loaded)
@@ -951,25 +1110,33 @@ cifsacl:
 	return 0;
 
 setcifsacl_action_ret:
-	free(ntsdptr);
+	if (ntsdptr)
+		free(ntsdptr);
 
 setcifsacl_facenum_ret:
-	for (i = 0; i < numfaces; ++i)
-		free(facesptr[i]);
-	free(facesptr);
+	if (facesptr) {
+		for (i = 0; i < numfaces; ++i)
+			free(facesptr[i]);
+		free(facesptr);
+	}
 
 setcifsacl_getx_ret:
-	free(attrval);
+	if (attrval)
+		free(attrval);
 
 setcifsacl_cmdlineverify_ret:
-	for (i = 0; i < numcaces; ++i)
-		free(cacesptr[i]);
-	free(cacesptr);
+	if (cacesptr) {
+		for (i = 0; i < numcaces; ++i)
+			free(cacesptr[i]);
+		free(cacesptr);
+	}
 
 setcifsacl_cmdlineparse_ret:
-	free(arrptr);
+	if (arrptr)
+		free(arrptr);
 
 setcifsacl_numcaces_ret:
-	exit_plugin(plugin_handle);
+	if (plugin_loaded)
+		exit_plugin(plugin_handle);
 	return -1;
 }
diff --git a/setcifsacl.rst.in b/setcifsacl.rst.in
index de9c758..985af7c 100644
--- a/setcifsacl.rst.in
+++ b/setcifsacl.rst.in
@@ -2,16 +2,16 @@
 setcifsacl
 ==========
 
-------------------------------------------------------------------------------------------------
-Userspace helper to alter an ACL in a security descriptor for Common Internet File System (CIFS)
-------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------
+Userspace helper to alter an ACL or owner/group SID in a security descriptor for Common Internet File System (CIFS)
+-------------------------------------------------------------------------------------------------------------------
 :Manual section: 1
 
 ********
 SYNOPSIS
 ********
 
-  setcifsacl [-v|-a|-D|-M|-S] "{one or more ACEs}" {file system object}
+  setcifsacl [-v|-a|-D|-M|-S|-o|-g] "{one or more ACEs or a SID}" {file system object}
 
 ***********
 DESCRIPTION
@@ -20,7 +20,7 @@ DESCRIPTION
 This tool is part of the cifs-utils suite.
 
 ``setcifsacl`` is a userspace helper program for the Linux CIFS client
-file system. It is intended to alter an ACL of a security descriptor
+file system. It is intended to alter an ACL or set owner/group SID of a security descriptor
 for a file system object. Whether a security descriptor to be set is
 applied or not is determined by the CIFS/SMB server.
 
@@ -55,6 +55,13 @@ OPTIONS
   Set an ACL of security descriptor with the list of ACEs Existing ACL
   is replaced entirely with the specified ACEs.
 
+-o
+  Set owner SID to one specified as a command line argument.
+
+-g
+  Set group SID to one specified as a command line argument.
+
+  The owner/group SID can be specified as a name or a raw SID value.
   Every ACE entry starts with "ACL:" One or more ACEs are specified
   within double quotes.  Multiple ACEs are separated by a comma.
 
@@ -93,6 +100,16 @@ Set an ACL
 
   setcifsacl -S "ACL:CIFSTESTDOM\Administrator:0x0/0x0/FULL,ACL:CIFSTESTDOM\user2:0x0/0x0/FULL" <file_name>
 
+Set owner SID
+=============
+
+  setcifsacl -o "S-1-5-21-3338130290-3403600371-1423429424-2102" <file_name>
+
+Set group SID
+=============
+
+  setcifsacl -g "Administrators at BUILTIN" <file_name>
+
 *****
 NOTES
 *****
-- 
2.14.5




More information about the samba-technical mailing list