new file mode 100644
@@ -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;
+}