diff mbox

[3/8] cifs-utils: cifsacl utilities: Add file setcifsacl.c (try #2)

Message ID 1314126237-12012-1-git-send-email-shirishpargaonkar@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Shirish Pargaonkar Aug. 23, 2011, 7:03 p.m. UTC
From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>


Parse the blob that contains a security descriptor obtained by
calling getxattr API using attribute system.cifs_acl .
Start parsing and printing security descriptor including
the a DACL within the security descriptor, printing each ACE of
the DACL by printing SID, type, flags, and mask.

Winbind apis are used to translate raw SID to a name.


Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
---
 setcifsacl.c |  905 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 905 insertions(+), 0 deletions(-)
 create mode 100644 setcifsacl.c
diff mbox

Patch

diff --git a/setcifsacl.c b/setcifsacl.c
new file mode 100644
index 0000000..0f283cd
--- /dev/null
+++ b/setcifsacl.c
@@ -0,0 +1,905 @@ 
+/*
+* setcifsacl utility
+*
+* Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
+*
+* Used to alter entries of an ACL or replace an entire ACL in a
+* security descriptor of a file system object that belongs to a
+* share mounted using option cifsacl.
+*
+* 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 2 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, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <wbclient.h>
+#include <ctype.h>
+#include <sys/xattr.h>
+#include "cifsacl.h"
+
+static const char *prog = "setcifsacl";
+
+static void
+copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+		int numaces, int acessize)
+{
+	int i;
+
+	int osidsoffset, gsidsoffset, dacloffset;
+	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 */
+	osidsoffset = htole32(pntsd->osidoffset);
+	gsidsoffset = htole32(pntsd->gsidoffset);
+	dacloffset = htole32(pntsd->dacloffset);
+
+	pnntsd->revision = pntsd->revision;
+	pnntsd->type = pntsd->type;
+	pnntsd->osidoffset = pntsd->osidoffset;
+	pnntsd->gsidoffset = pntsd->gsidoffset;
+	pnntsd->dacloffset = pntsd->dacloffset;
+
+	dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
+	ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
+
+	ndacl_ptr->revision = dacl_ptr->revision;
+	ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
+	ndacl_ptr->num_aces = htole32(numaces);
+
+	/* copy owner sid */
+	owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
+	nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
+
+	nowner_sid_ptr->revision = owner_sid_ptr->revision;
+	nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
+	for (i = 0; i < 6; i++)
+		nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
+	for (i = 0; i < 5; i++)
+		nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
+
+	/* copy group sid */
+	group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
+	ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
+
+	ngroup_sid_ptr->revision = group_sid_ptr->revision;
+	ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
+	for (i = 0; i < 6; i++)
+		ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
+	for (i = 0; i < 5; i++)
+		ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
+
+	return;
+}
+
+static int
+copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
+{
+	int i;
+
+	dace->type = sace->type;
+	dace->flags = sace->flags;
+	dace->access_req = htole32(sace->access_req);
+
+	dace->sid.revision = sace->sid.revision;
+	dace->sid.num_subauth = sace->sid.num_subauth;
+	for (i = 0; i < 6; i++)
+		dace->sid.authority[i] = sace->sid.authority[i];
+	for (i = 0; i < sace->sid.num_subauth; i++)
+		dace->sid.sub_auth[i] = sace->sid.sub_auth[i];
+
+	dace->size = htole16(sace->size);
+
+	return dace->size;
+}
+
+static int
+compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
+{
+	int i;
+
+	if (compflags & COMPSID) {
+		if (dace->sid.revision != sace->sid.revision)
+			return 0;
+		if (dace->sid.num_subauth != sace->sid.num_subauth)
+			return 0;
+		for (i = 0; i < 6; i++) {
+			if (dace->sid.authority[i] != sace->sid.authority[i])
+				return 0;
+		}
+		for (i = 0; i < sace->sid.num_subauth; i++) {
+			if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
+				return 0;
+		}
+	}
+
+	if (compflags & COMPTYPE) {
+		if (dace->type != sace->type)
+			return 0;
+	}
+
+	if (compflags & COMPFLAG) {
+		if (dace->flags != sace->flags)
+			return 0;
+	}
+
+	if (compflags & COMPMASK) {
+		if (dace->access_req != htole32(sace->access_req))
+			return 0;
+	}
+
+	return 1;
+}
+
+static int
+get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
+			int aces, ssize_t *bufsize, size_t *acesoffset)
+{
+	unsigned int size, acessize, dacloffset;
+
+	size = sizeof(struct cifs_ntsd) +
+		2 * sizeof(struct cifs_sid) +
+		sizeof(struct cifs_ctrl_acl);
+
+	dacloffset = le32toh(pntsd->dacloffset);
+
+	*acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
+	acessize = aces * sizeof(struct cifs_ace);
+	*bufsize = size + acessize;
+
+	*npntsd = malloc(*bufsize);
+	if (!*npntsd) {
+		printf("%s: Memory allocation failure", __func__);
+		return errno;
+	}
+
+	return 0;
+}
+
+static int
+ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
+			struct cifs_ace **cacesptr, int numcaces)
+{
+	int i, rc, acessize = 0;
+	size_t acesoffset;
+	char *acesptr;
+
+	rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
+	if (rc)
+		return rc;
+
+	acesptr = (char *)*npntsd + acesoffset;
+	for (i = 0; i < numcaces; ++i) {
+		acessize += copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
+		acesptr += sizeof(struct cifs_ace);
+	}
+	copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
+	acesptr = (char *)*npntsd + acesoffset;
+
+
+	return 0;
+}
+
+static int
+ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
+		struct cifs_ace **facesptr, int numfaces,
+		struct cifs_ace **cacesptr, int numcaces)
+{
+	int i, rc, numaces, size, acessize = 0;
+	size_t acesoffset;
+	char *acesptr;
+
+	numaces = numfaces + numcaces;
+	rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
+	if (rc)
+		return rc;
+
+	acesptr = (char *)*npntsd + acesoffset;
+	for (i = 0; i < numfaces; ++i) {
+		size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
+		acesptr += size;
+		acessize += size;
+	}
+	for (i = 0; i < numcaces; ++i) {
+		size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
+		acesptr += size;
+		acessize += size;
+	}
+	copy_sec_desc(pntsd, *npntsd, numaces, acessize);
+
+	return 0;
+}
+
+static int
+ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
+		struct cifs_ace **facesptr, int numfaces,
+		struct cifs_ace **cacesptr, int numcaces)
+{
+	int i, j, rc, size, acessize = 0;
+	size_t acesoffset;
+	char *acesptr;
+
+	if (numfaces == 0) {
+		printf("%s: No entries to modify", __func__);
+		return -1;
+	}
+
+	rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
+	if (rc)
+		return rc;
+
+	for (j = 0; j < numcaces; ++j) {
+		for (i = 0; i < numfaces; ++i) {
+			if (compare_aces(facesptr[i], cacesptr[j],
+					COMPSID | COMPTYPE)) {
+				copy_ace(facesptr[i], cacesptr[j]);
+				break;
+			}
+		}
+	}
+
+	acesptr = (char *)*npntsd + acesoffset;
+	for (i = 0; i < numfaces; ++i) {
+		size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
+		acesptr += size;
+		acessize += size;
+	}
+
+	copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
+
+	return 0;
+}
+
+static int
+ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
+		struct cifs_ace **facesptr, int numfaces,
+		struct cifs_ace **cacesptr, int numcaces)
+{
+	int i, j, numaces = 0, rc, size, acessize = 0;
+	size_t acesoffset;
+	char *acesptr;
+
+	if (numfaces == 0) {
+		printf("%s: No entries to delete\n", __func__);
+		return -1;
+	}
+
+	if (numfaces < numcaces) {
+		printf("%s: Invalid entries to delete\n", __func__);
+		return -1;
+	}
+
+	rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
+	if (rc)
+		return rc;
+
+	acesptr = (char *)*npntsd + acesoffset;
+	for (i = 0; i < numfaces; ++i) {
+		for (j = 0; j < numcaces; ++j) {
+			if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
+				break;
+		}
+		if (j == numcaces) {
+			size = copy_ace((struct cifs_ace *)acesptr,
+								facesptr[i]);
+			acessize += size;
+			acesptr += size;
+			++numaces;
+		}
+	}
+
+	if (numaces == numfaces) {
+		printf("%s: Nothing to delete\n", __func__);
+		return 1;
+	}
+	copy_sec_desc(pntsd, *npntsd, numaces, acessize);
+
+	return 0;
+}
+
+static int
+get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
+			struct cifs_ctrl_acl **daclptr)
+{
+	int numfaces = 0;
+	uint32_t dacloffset;
+	struct cifs_ctrl_acl *ldaclptr;
+	char *end_of_acl = ((char *)pntsd) + acl_len;
+
+	if (pntsd == NULL)
+		return 0;
+
+	dacloffset = le32toh(pntsd->dacloffset);
+	if (!dacloffset)
+		return 0;
+	else {
+		ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
+		/* validate that we do not go past end of acl */
+		if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
+			numfaces = le32toh(ldaclptr->num_aces);
+			*daclptr = ldaclptr;
+		}
+	}
+
+	return numfaces;
+}
+
+static struct cifs_ace **
+build_fetched_aces(char *daclptr, int numfaces)
+{
+	int i, j, rc = 0, acl_size;
+	char *acl_base;
+	struct cifs_ace *pace, **facesptr;
+
+	facesptr = (struct cifs_ace **)malloc(numfaces *
+					sizeof(struct cifs_aces *));
+	if (!facesptr) {
+		printf("%s: Error %d allocating ACE array",
+				__func__, errno);
+		rc = errno;
+	}
+
+	acl_base = daclptr;
+	acl_size = sizeof(struct cifs_ctrl_acl);
+	for (i = 0; i < numfaces; ++i) {
+		facesptr[i] = malloc(sizeof(struct cifs_ace));
+		if (!facesptr[i]) {
+			rc = errno;
+			goto build_fetched_aces_ret;
+		}
+		pace = (struct cifs_ace *) (acl_base + acl_size);
+		memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
+		acl_base = (char *)pace;
+		acl_size = le16toh(pace->size);
+	}
+
+build_fetched_aces_ret:
+	if (rc) {
+		printf("%s: Invalid fetched ace\n", __func__);
+		if (i) {
+			for (j = i; j >= 0; --j)
+				free(facesptr[j]);
+		}
+		free(facesptr);
+	}
+	return facesptr;
+}
+
+static int
+verify_ace_sid(char *sidstr, struct cifs_sid *sid)
+{
+	int rc;
+	char *lstr;
+	struct passwd *winpswdptr;
+
+	lstr = strstr(sidstr, "\\"); /* everything before | */
+	if (lstr)
+		++lstr;
+	else
+		lstr = sidstr;
+
+	/* Check if it is a (raw) SID (string) */
+	rc = wbcStringToSid(lstr, (struct wbcDomainSid *)sid);
+	if (!rc)
+		return rc;
+
+	/* Check if it a name (string) which can be resolved to a SID*/
+	rc = wbcGetpwnam(lstr, &winpswdptr);
+	if (rc) {
+		printf("%s: Invalid user name: %s\n", __func__, sidstr);
+		return rc;
+	}
+	rc = wbcUidToSid(winpswdptr->pw_uid, (struct wbcDomainSid *)sid);
+	if (rc) {
+		printf("%s: Invalid user: %s\n", __func__, sidstr);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+verify_ace_type(char *typestr, uint8_t *typeval)
+{
+	int i, len;
+	char *invaltype;
+
+	if (strstr(typestr, "0x")) { /* hex type value */
+		*typeval = strtol(typestr, &invaltype, 16);
+		if (!strlen(invaltype)) {
+			if (*typeval != ACCESS_ALLOWED &&
+				*typeval != ACCESS_DENIED &&
+				*typeval != ACCESS_ALLOWED_OBJECT &&
+				*typeval != ACCESS_DENIED_OBJECT) {
+					printf("%s: Invalid type: %s\n",
+						__func__, typestr);
+					return 1;
+			}
+			return 0;
+		}
+	}
+
+	len = strlen(typestr);
+	for (i = 0; i < len; ++i)
+		*(typestr + i) = toupper(*(typestr + i));
+	if (!strcmp(typestr, "ALLOWED"))
+		*typeval = 0x0;
+	else if (!strcmp(typestr, "DENIED"))
+		*typeval = 0x1;
+	else if (!strcmp(typestr, "ALLOWED_OBJECT"))
+		*typeval = 0x5;
+	else if (!strcmp(typestr, "DENIED_OBJECT"))
+		*typeval = 0x6;
+	else {
+		printf("%s: Invalid type: %s\n", __func__, typestr);
+		return 1;
+	}
+
+	return 0;
+}
+
+static uint8_t
+ace_flag_value(char *flagstr)
+{
+	uint8_t flagval = 0x0;
+	char *iflag;
+
+	iflag = strtok(flagstr, "|"); /* everything before | */
+	while (iflag) {
+		if (!strcmp(iflag, "OI"))
+			flagval += 0x1;
+		else if (!strcmp(iflag, "CI"))
+			flagval += 0x2;
+		else if (!strcmp(iflag, "NP"))
+			flagval += 0x4;
+		else if (!strcmp(iflag, "IO"))
+			flagval += 0x8;
+		else if (!strcmp(iflag, "I"))
+			flagval += 0x10;
+		else
+			return 0x0; /* Invalid flag */
+		iflag = strtok(NULL, "|"); /* everything before | */
+	}
+
+	return flagval;
+}
+
+static int
+verify_ace_flags(char *flagstr, uint8_t *flagval)
+{
+	char *invalflag;
+
+	if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
+		return 0;
+
+	if (strstr(flagstr, "0x")) { /* hex flag value */
+		*flagval = strtol(flagstr, &invalflag, 16);
+		if (strlen(invalflag)) {
+			printf("%s: Invalid flags: %s\n", __func__, flagstr);
+			return 1;
+		}
+	} else
+		*flagval = ace_flag_value(flagstr);
+
+	if (!*flagval || (*flagval & ~VFLAGS)) {
+		printf("%s: Invalid flag %s and value: 0x%x\n",
+			__func__, flagstr, *flagval);
+		return 1;
+	}
+
+	return 0;
+}
+
+static uint32_t
+ace_mask_value(char *maskstr)
+{
+	int i, len;
+	uint32_t maskval = 0x0;
+	char *lmask;
+
+	if (!strcmp(maskstr, "FULL"))
+		return FULL_CONTROL;
+	else if (!strcmp(maskstr, "CHANGE"))
+		return CHANGE;
+	else if (!strcmp(maskstr, "D"))
+		return DELETE;
+	else if (!strcmp(maskstr, "READ"))
+		return EREAD;
+	else {
+		len = strlen(maskstr);
+		lmask = maskstr;
+		for (i = 0; i < len; ++i, ++lmask) {
+			if (*lmask == 'R')
+				maskval |= EREAD;
+			else if (*lmask == 'W')
+				maskval |= EWRITE;
+			else if (*lmask == 'X')
+				maskval |= EXEC;
+			else if (*lmask == 'D')
+				maskval |= DELETE;
+			else if (*lmask == 'P')
+				maskval |= WRITE_DAC;
+			else if (*lmask == 'O')
+				maskval |= WRITE_OWNER;
+			else
+				return 0;
+		}
+		return maskval;
+	}
+
+	return 0;
+}
+
+static int
+verify_ace_mask(char *maskstr, uint32_t *maskval)
+{
+	char *invalflag;
+
+	if (strstr(maskstr, "0x") || !strcmp(maskstr, "DELDHLD")) {
+		*maskval = strtol(maskstr, &invalflag, 16);
+		if (!invalflag) {
+			printf("%s: Invalid mask: %s\n", __func__, maskstr);
+			return 1;
+		}
+	} else
+		*maskval = ace_mask_value(maskstr);
+
+	if (!*maskval) {
+		printf("%s: Invalid mask %s and value: 0x%x\n",
+			__func__, maskstr, *maskval);
+		return 1;
+	}
+
+	return 0;
+}
+
+static struct cifs_ace **
+build_cmdline_aces(char **arrptr, int numcaces)
+{
+	int i, rc = 0;
+	char *acesid, *acetype, *aceflag, *acemask;
+	struct cifs_ace **cacesptr;
+
+	cacesptr = (struct cifs_ace **)malloc(numcaces *
+				sizeof(struct cifs_aces *));
+	if (!cacesptr) {
+		printf("%s: Error %d allocating ACE array", __func__, errno);
+		return NULL;
+	}
+
+	for (i = 0; i < numcaces; ++i) {
+		acesid = strtok(arrptr[i], ":");
+		acetype = strtok(NULL, "/");
+		aceflag = strtok(NULL, "/");
+		acemask = strtok(NULL, "/");
+
+		if (!acesid || !acetype || !aceflag || !acemask) {
+			printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
+			rc = 1;
+			goto build_cmdline_aces_ret;
+		}
+
+		cacesptr[i] = malloc(sizeof(struct cifs_ace));
+		if (!cacesptr[i]) {
+			printf("%s: ACE alloc error %d\n", __func__, errno);
+			rc = errno;
+			goto build_cmdline_aces_ret;
+		}
+
+		if (verify_ace_sid(acesid, &cacesptr[i]->sid)) {
+			printf("%s: Invalid SID: %s\n", __func__, arrptr[i]);
+			rc = 1;
+			goto build_cmdline_aces_ret;
+		}
+
+		if (verify_ace_type(acetype, &cacesptr[i]->type)) {
+			printf("%s: Invalid ACE type: %s\n",
+					__func__, arrptr[i]);
+			rc = 1;
+			goto build_cmdline_aces_ret;
+		}
+
+		if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
+			printf("%s: Invalid ACE flag: %s\n",
+				__func__, arrptr[i]);
+			rc = 1;
+			goto build_cmdline_aces_ret;
+		}
+
+		if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
+			printf("%s: Invalid ACE mask: %s\n",
+				__func__, arrptr[i]);
+			rc = 1;
+			goto build_cmdline_aces_ret;
+		}
+
+		cacesptr[i]->size = 1 + 1 + 2 + 4 + 1 + 1 + 6 +
+				(cacesptr[i]->sid.num_subauth * 4);
+	}
+	return cacesptr;
+
+build_cmdline_aces_ret:
+	for (; i >= 0; --i)
+		free(cacesptr[i]);
+	free(cacesptr);
+	return NULL;
+}
+
+static char **
+parse_cmdline_aces(char *optarg, int numcaces)
+{
+	int i = 0, len;
+	char *acestr, *vacestr, **arrptr = NULL;
+
+	errno = EINVAL;
+	arrptr = (char **)malloc(numcaces * sizeof(char *));
+	if (!arrptr) {
+		printf("%s: Error %d allocating char array\n", __func__, errno);
+		return NULL;
+	}
+
+	while (i < numcaces) {
+		acestr = strtok(optarg, ","); /* everything before , */
+		if (acestr) {
+			vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
+			if (vacestr) {
+				vacestr = strchr(vacestr, ':');
+				if (vacestr)
+					++vacestr; /* go past : */
+				if (vacestr) {
+					len = strlen(vacestr);
+					arrptr[i] = malloc(len + 1);
+					if (!arrptr[i])
+						goto parse_cmdline_aces_ret;
+					strcpy(arrptr[i], vacestr);
+					++i;
+				} else
+					goto parse_cmdline_aces_ret;
+			} else
+				goto parse_cmdline_aces_ret;
+		} else
+			goto parse_cmdline_aces_ret;
+		optarg = NULL;
+	}
+	errno = 0;
+	return arrptr;
+
+parse_cmdline_aces_ret:
+	printf("%s: Error %d parsing ACEs\n", __func__, errno);
+	for (;  i >= 0; --i)
+		free(arrptr[i]);
+	free(arrptr);
+	return NULL;
+}
+
+static unsigned int
+get_numcaces(const char *optarg)
+{
+	int i, len;
+	unsigned int numcaces = 1;
+
+	if (!optarg)
+		return 0;
+
+	len = strlen(optarg);
+	for (i = 0; i < len; ++i) {
+		if (*(optarg + i) == ',')
+			++numcaces;
+	}
+
+	return numcaces;
+}
+
+static int
+setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
+		ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
+		struct cifs_ace **cacesptr, int numcaces,
+		int maction)
+{
+	int rc = 1;
+
+	switch (maction) {
+	case 0:
+		rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
+				numfaces, cacesptr, numcaces);
+		break;
+	case 1:
+		rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
+				numfaces, cacesptr, numcaces);
+		break;
+	case 2:
+		rc = ace_add(pntsd, npntsd, bufsize, facesptr,
+				numfaces, cacesptr, numcaces);
+		break;
+	case 3:
+		rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
+		break;
+	default:
+		printf("%s: Invalid action: %d\n", __func__, maction);
+		break;
+	}
+
+	return rc;
+}
+
+static void
+setcifsacl_usage(void)
+{
+	fprintf(stderr,
+	"%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\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");
+	fprintf(stderr,
+	"\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr,
+	"\t-D	Delete ACE(s), separated by a comma, from an ACL\n");
+	fprintf(stderr,
+	"\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr,
+	"\t-M	Modify ACE(s), separated by a comma, in an ACL\n");
+	fprintf(stderr,
+	"\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
+	fprintf(stderr,
+	"\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, "\nRefer to setcifsacl(8) manpage for details\n");
+}
+
+int
+main(const int argc, char *const argv[])
+{
+	int i, rc, c, numcaces, numfaces, maction = -1;
+	ssize_t attrlen, bufsize = BUFSIZE;
+	char *filename, *attrval, **arrptr = NULL;
+	struct cifs_ctrl_acl *daclptr = NULL;
+	struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
+	struct cifs_ntsd *ntsdptr = NULL;
+
+	openlog(prog, 0, LOG_DAEMON);
+
+	c = getopt(argc, argv, "v:D:M:a:S:?");
+	switch (c) {
+	case 'v':
+		printf("Version: %s\n", VERSION);
+		goto out;
+	case 'D':
+		maction = 0;
+		break;
+	case 'M':
+		maction = 1;
+		break;
+	case 'a':
+		maction = 2;
+		break;
+	case 'S':
+		maction = 3;
+		break;
+	case '?':
+		setcifsacl_usage();
+		return 0;
+	default:
+		break;
+	}
+
+	if (argc != 4) {
+		setcifsacl_usage();
+		return -1;
+	}
+	filename = argv[3];
+	
+	numcaces = get_numcaces(optarg);
+	if (!numcaces) {
+		printf("%s: No valid ACEs specified\n", __func__);
+		return -1;
+	}
+
+	arrptr = parse_cmdline_aces(optarg, 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 %ld exceeds max size of %d\n",
+				__func__, bufsize, XATTR_SIZE_MAX);
+		goto setcifsacl_cmdlineverify_ret;
+	}
+
+	attrval = malloc(bufsize * sizeof(char));
+	if (!attrval) {
+		printf("error allocating memory for attribute value buffer\n");
+		goto setcifsacl_cmdlineverify_ret;
+	}
+
+	attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
+	if (attrlen == -1) {
+		if (errno == ERANGE) {
+			free(attrval);
+			bufsize += BUFSIZE;
+			goto cifsacl;
+		} else {
+			printf("getxattr error: %d\n", errno);
+			goto setcifsacl_getx_ret;
+		}
+	}
+
+	numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
+	if (!numfaces && maction != 2) { /* 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;
+
+	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;
+
+out:
+	return 0;
+
+setcifsacl_action_ret:
+	free(ntsdptr);
+
+setcifsacl_facenum_ret:
+	for (i = 0; i < numfaces; ++i)
+		free(facesptr[i]);
+	free(facesptr);
+
+setcifsacl_getx_ret:
+	free(attrval);
+
+setcifsacl_cmdlineverify_ret:
+	for (i = 0; i < numcaces; ++i)
+		free(cacesptr[i]);
+	free(cacesptr);
+
+setcifsacl_cmdlineparse_ret:
+	for (i = 0; i < numcaces; ++i)
+		free(arrptr[i]);
+	free(arrptr);
+
+setcifsacl_numcaces_ret:
+	return -1;
+}