4 * Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
6 * Used to alter entries of an ACL or replace an entire ACL in a
7 * security descriptor of a file system object that belongs to a
8 * share mounted using option cifsacl.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #endif /* HAVE_CONFIG_H */
37 #include <sys/xattr.h>
40 #include "idmap_plugin.h"
42 enum setcifsacl_actions {
50 static void *plugin_handle;
51 static bool plugin_loaded;
54 copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src)
58 dst->revision = src->revision;
59 dst->num_subauth = src->num_subauth;
60 for (i = 0; i < NUM_AUTHS; i++)
61 dst->authority[i] = src->authority[i];
62 for (i = 0; i < src->num_subauth; i++)
63 dst->sub_auth[i] = src->sub_auth[i];
67 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
68 int numaces, int acessize)
70 int osidsoffset, gsidsoffset, dacloffset;
71 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
72 struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
73 struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
75 /* copy security descriptor control portion */
76 osidsoffset = le32toh(pntsd->osidoffset);
77 gsidsoffset = le32toh(pntsd->gsidoffset);
78 dacloffset = le32toh(pntsd->dacloffset);
80 pnntsd->revision = pntsd->revision;
81 pnntsd->type = pntsd->type;
82 pnntsd->osidoffset = pntsd->osidoffset;
83 pnntsd->gsidoffset = pntsd->gsidoffset;
84 pnntsd->dacloffset = pntsd->dacloffset;
86 dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
87 ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
89 ndacl_ptr->revision = dacl_ptr->revision;
90 ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
91 ndacl_ptr->num_aces = htole32(numaces);
94 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
95 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
96 copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
99 group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
100 ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
101 copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
107 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
109 dace->type = sace->type;
110 dace->flags = sace->flags;
111 dace->access_req = sace->access_req;
113 copy_cifs_sid(&dace->sid, &sace->sid);
115 dace->size = sace->size;
117 return le16toh(dace->size);
121 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
125 if (compflags & COMPSID) {
126 if (dace->sid.revision != sace->sid.revision)
128 if (dace->sid.num_subauth != sace->sid.num_subauth)
130 for (i = 0; i < NUM_AUTHS; i++) {
131 if (dace->sid.authority[i] != sace->sid.authority[i])
134 for (i = 0; i < sace->sid.num_subauth; i++) {
135 if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
140 if (compflags & COMPTYPE) {
141 if (dace->type != sace->type)
145 if (compflags & COMPFLAG) {
146 if (dace->flags != sace->flags)
150 if (compflags & COMPMASK) {
151 if (dace->access_req != sace->access_req)
159 get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
160 int aces, ssize_t *bufsize, size_t *acesoffset)
162 unsigned int size, acessize, dacloffset;
164 size = sizeof(struct cifs_ntsd) +
165 2 * sizeof(struct cifs_sid) +
166 sizeof(struct cifs_ctrl_acl);
168 dacloffset = le32toh(pntsd->dacloffset);
170 *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
171 acessize = aces * sizeof(struct cifs_ace);
172 *bufsize = size + acessize;
174 *npntsd = malloc(*bufsize);
176 printf("%s: Memory allocation failure", __func__);
184 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
185 struct cifs_ace **cacesptr, int numcaces)
187 int i, rc, size = 0, acessize = 0;
191 rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
195 acesptr = (char *)*npntsd + acesoffset;
196 for (i = 0; i < numcaces; ++i) {
197 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
201 copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
202 acesptr = (char *)*npntsd + acesoffset;
209 ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
210 struct cifs_ace **facesptr, int numfaces,
211 struct cifs_ace **cacesptr, int numcaces)
213 int i, rc, numaces, size, acessize = 0;
217 numaces = numfaces + numcaces;
218 rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
222 acesptr = (char *)*npntsd + acesoffset;
223 for (i = 0; i < numfaces; ++i) {
224 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
228 for (i = 0; i < numcaces; ++i) {
229 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
233 copy_sec_desc(pntsd, *npntsd, numaces, acessize);
239 ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
240 struct cifs_ace **facesptr, int numfaces,
241 struct cifs_ace **cacesptr, int numcaces)
243 int i, j, rc, size, acessize = 0;
248 printf("%s: No entries to modify", __func__);
252 rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
256 for (j = 0; j < numcaces; ++j) {
257 for (i = 0; i < numfaces; ++i) {
258 if (compare_aces(facesptr[i], cacesptr[j],
259 COMPSID | COMPTYPE)) {
260 copy_ace(facesptr[i], cacesptr[j]);
266 acesptr = (char *)*npntsd + acesoffset;
267 for (i = 0; i < numfaces; ++i) {
268 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
273 copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
279 ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
280 struct cifs_ace **facesptr, int numfaces,
281 struct cifs_ace **cacesptr, int numcaces)
283 int i, j, numaces = 0, rc, size, acessize = 0;
288 printf("%s: No entries to delete\n", __func__);
292 if (numfaces < numcaces) {
293 printf("%s: Invalid entries to delete\n", __func__);
297 rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
301 acesptr = (char *)*npntsd + acesoffset;
302 for (i = 0; i < numfaces; ++i) {
303 for (j = 0; j < numcaces; ++j) {
304 if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
308 size = copy_ace((struct cifs_ace *)acesptr,
316 if (numaces == numfaces) {
317 printf("%s: Nothing to delete\n", __func__);
320 copy_sec_desc(pntsd, *npntsd, numaces, acessize);
326 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
327 struct cifs_ctrl_acl **daclptr)
331 struct cifs_ctrl_acl *ldaclptr;
332 char *end_of_acl = ((char *)pntsd) + acl_len;
334 dacloffset = le32toh(pntsd->dacloffset);
338 ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
340 /* validate that we do not go past end of acl */
341 if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
342 numfaces = le32toh(ldaclptr->num_aces);
349 static struct cifs_ace **
350 build_fetched_aces(char *daclptr, int numfaces)
354 struct cifs_ace *pace, **facesptr;
356 facesptr = calloc(numfaces, sizeof(struct cifs_aces *));
358 printf("%s: Error %d allocating ACE array",
364 acl_size = sizeof(struct cifs_ctrl_acl);
365 for (i = 0; i < numfaces; ++i) {
366 facesptr[i] = malloc(sizeof(struct cifs_ace));
368 goto build_fetched_aces_err;
369 pace = (struct cifs_ace *) (acl_base + acl_size);
370 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
371 acl_base = (char *)pace;
372 acl_size = le16toh(pace->size);
376 build_fetched_aces_err:
377 printf("%s: Invalid fetched ace\n", __func__);
378 for (i = 0; i < numfaces; ++i)
385 verify_ace_type(char *typestr, uint8_t *typeval)
390 if (strstr(typestr, "0x")) { /* hex type value */
391 *typeval = strtol(typestr, &invaltype, 16);
392 if (!strlen(invaltype)) {
393 if (*typeval != ACCESS_ALLOWED &&
394 *typeval != ACCESS_DENIED &&
395 *typeval != ACCESS_ALLOWED_OBJECT &&
396 *typeval != ACCESS_DENIED_OBJECT) {
397 printf("%s: Invalid type: %s\n",
405 len = strlen(typestr);
406 for (i = 0; i < len; ++i)
407 *(typestr + i) = toupper(*(typestr + i));
408 if (!strcmp(typestr, "ALLOWED"))
410 else if (!strcmp(typestr, "DENIED"))
412 else if (!strcmp(typestr, "ALLOWED_OBJECT"))
414 else if (!strcmp(typestr, "DENIED_OBJECT"))
417 printf("%s: Invalid type: %s\n", __func__, typestr);
425 ace_flag_value(char *flagstr)
427 uint8_t flagval = 0x0;
430 iflag = strtok(flagstr, "|"); /* everything before | */
432 if (!strcmp(iflag, "OI"))
434 else if (!strcmp(iflag, "CI"))
436 else if (!strcmp(iflag, "NP"))
438 else if (!strcmp(iflag, "IO"))
440 else if (!strcmp(iflag, "I"))
443 return 0x0; /* Invalid flag */
444 iflag = strtok(NULL, "|"); /* everything before | */
451 verify_ace_flags(char *flagstr, uint8_t *flagval)
455 if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
458 if (strstr(flagstr, "0x")) { /* hex flag value */
459 *flagval = strtol(flagstr, &invalflag, 16);
460 if (strlen(invalflag)) {
461 printf("%s: Invalid flags: %s\n", __func__, flagstr);
465 *flagval = ace_flag_value(flagstr);
467 if (!*flagval || (*flagval & ~VFLAGS)) {
468 printf("%s: Invalid flag %s and value: 0x%x\n",
469 __func__, flagstr, *flagval);
477 ace_mask_value(char *mask)
479 uint32_t maskval = 0;
482 if (!strcmp(mask, "FULL"))
484 if (!strcmp(mask, "CHANGE"))
486 if (!strcmp(mask, "READ"))
489 while((cur = *mask++)) {
504 maskval |= WRITE_DAC;
507 maskval |= WRITE_OWNER;
517 verify_ace_mask(char *maskstr, uint32_t *maskval)
523 val = strtoul(maskstr, &ep, 0);
524 if (errno == 0 && *ep == '\0')
525 *maskval = htole32((uint32_t)val);
527 *maskval = htole32(ace_mask_value(maskstr));
530 printf("%s: Invalid mask %s (value 0x%x)\n", __func__,
538 #define AUTHORITY_MASK (~(0xffffffffffffULL))
541 raw_str_to_sid(const char *str, struct cifs_sid *csid)
545 unsigned long long x;
547 /* Sanity check for either "S-" or "s-" */
548 if ((str[0] != 'S' && str[0] != 's') || (str[1]!='-')) {
549 plugin_errmsg = "SID string does not start with \"S-\"";
553 /* Get the SID revision number */
555 x = strtoull(p, &q, 10);
556 if (x == 0 || x > UCHAR_MAX || !q || *q != '-') {
557 plugin_errmsg = "Invalid SID revision number";
560 csid->revision = (uint8_t)x;
563 * Next the Identifier Authority. This is stored in big-endian in a
564 * 6 byte array. If the authority value is > UINT_MAX, then it should
565 * be expressed as a hex value.
568 x = strtoull(p, &q, 0);
569 if ((x & AUTHORITY_MASK) || !q || *q !='-') {
570 plugin_errmsg = "Invalid SID authority";
573 csid->authority[5] = (x & 0x0000000000ffULL);
574 csid->authority[4] = (x & 0x00000000ff00ULL) >> 8;
575 csid->authority[3] = (x & 0x000000ff0000ULL) >> 16;
576 csid->authority[2] = (x & 0x0000ff000000ULL) >> 24;
577 csid->authority[1] = (x & 0x00ff00000000ULL) >> 32;
578 csid->authority[0] = (x & 0xff0000000000ULL) >> 48;
580 /* now read the the subauthorities and store as __le32 vals */
582 csid->num_subauth = 0;
583 while (csid->num_subauth < SID_MAX_SUB_AUTHORITIES) {
584 x = strtoul(p, &q, 10);
588 plugin_errmsg = "Invalid sub authority value";
591 csid->sub_auth[csid->num_subauth++] = htole32((uint32_t)x);
598 /* IF we ended early, then the SID could not be converted */
599 if (q && *q != '\0') {
600 plugin_errmsg = "Invalid sub authority value";
608 setcifsacl_str_to_sid(const char *str, struct cifs_sid *sid)
611 return str_to_sid(plugin_handle, str, sid);
612 return raw_str_to_sid(str, sid);
615 static struct cifs_ace **
616 build_cmdline_aces(char **arrptr, int numcaces)
619 char *acesid, *acetype, *aceflag, *acemask;
620 struct cifs_ace **cacesptr;
622 cacesptr = calloc(numcaces, sizeof(struct cifs_aces *));
624 printf("%s: Error %d allocating ACE array", __func__, errno);
628 for (i = 0; i < numcaces; ++i) {
629 acesid = strtok(arrptr[i], ":");
630 acetype = strtok(NULL, "/");
631 aceflag = strtok(NULL, "/");
632 acemask = strtok(NULL, "/");
634 if (!acesid || !acetype || !aceflag || !acemask) {
635 printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
636 goto build_cmdline_aces_ret;
639 cacesptr[i] = malloc(sizeof(struct cifs_ace));
641 printf("%s: ACE alloc error %d\n", __func__, errno);
642 goto build_cmdline_aces_ret;
645 if (setcifsacl_str_to_sid(acesid, &cacesptr[i]->sid)) {
646 printf("%s: Invalid SID (%s): %s\n", __func__, arrptr[i],
648 goto build_cmdline_aces_ret;
651 if (verify_ace_type(acetype, &cacesptr[i]->type)) {
652 printf("%s: Invalid ACE type: %s\n",
653 __func__, arrptr[i]);
654 goto build_cmdline_aces_ret;
657 if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
658 printf("%s: Invalid ACE flag: %s\n",
659 __func__, arrptr[i]);
660 goto build_cmdline_aces_ret;
663 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
664 printf("%s: Invalid ACE mask: %s\n",
665 __func__, arrptr[i]);
666 goto build_cmdline_aces_ret;
669 cacesptr[i]->size = htole16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
670 cacesptr[i]->sid.num_subauth * 4);
674 build_cmdline_aces_ret:
675 for (i = 0; i < numcaces; ++i)
682 parse_cmdline_aces(char *acelist, int numcaces)
685 char *acestr, *vacestr, **arrptr = NULL;
687 arrptr = (char **)malloc(numcaces * sizeof(char *));
689 printf("%s: Unable to allocate char array\n", __func__);
693 while (i < numcaces) {
694 acestr = strtok(acelist, ","); /* everything before , */
696 goto parse_cmdline_aces_err;
698 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
700 goto parse_cmdline_aces_err;
701 vacestr += 4; /* skip past "ACL:" */
710 parse_cmdline_aces_err:
711 printf("%s: Error parsing ACEs\n", __func__);
716 /* How many aces were provided on the command-line? Count the commas. */
718 get_numcaces(const char *aces)
720 unsigned int num = 1;
724 while((current = strchr(current, ','))) {
733 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
734 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
735 struct cifs_ace **cacesptr, int numcaces,
736 enum setcifsacl_actions maction)
742 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
743 numfaces, cacesptr, numcaces);
746 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
747 numfaces, cacesptr, numcaces);
750 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
751 numfaces, cacesptr, numcaces);
754 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
757 printf("%s: Invalid action: %d\n", __func__, maction);
765 setcifsacl_usage(const char *prog)
768 "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
770 fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
771 fprintf(stderr, "Valid options:\n");
772 fprintf(stderr, "\t-v Version of the program\n");
773 fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
775 "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
776 fprintf(stderr, "\n");
778 "\t-D Delete ACE(s), separated by a comma, from an ACL\n");
780 "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
781 fprintf(stderr, "\n");
783 "\t-M Modify ACE(s), separated by a comma, in an ACL\n");
785 "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
787 "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
789 "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
790 fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
794 main(const int argc, char *const argv[])
796 int i, rc, c, numcaces, numfaces;
797 enum setcifsacl_actions maction = ActUnknown;
798 ssize_t attrlen, bufsize = BUFSIZE;
799 char *ace_list, *filename, *attrval, **arrptr = NULL;
800 struct cifs_ctrl_acl *daclptr = NULL;
801 struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
802 struct cifs_ntsd *ntsdptr = NULL;
804 c = getopt(argc, argv, "hvD:M:a:S:");
823 setcifsacl_usage(basename(argv[0]));
826 printf("Version: %s\n", VERSION);
829 setcifsacl_usage(basename(argv[0]));
833 /* We expect 1 argument in addition to the option */
835 setcifsacl_usage(basename(argv[0]));
841 printf("%s: No valid ACEs specified\n", __func__);
845 if (init_plugin(&plugin_handle)) {
846 fprintf(stderr, "WARNING: unable to initialize idmapping "
847 "plugin. Only \"raw\" SID strings will be "
848 "accepted: %s\n", plugin_errmsg);
849 plugin_loaded = false;
851 plugin_loaded = true;
854 numcaces = get_numcaces(ace_list);
856 arrptr = parse_cmdline_aces(ace_list, numcaces);
858 goto setcifsacl_numcaces_ret;
860 cacesptr = build_cmdline_aces(arrptr, numcaces);
862 goto setcifsacl_cmdlineparse_ret;
865 if (bufsize >= XATTR_SIZE_MAX) {
866 printf("%s: Buffer size %zd exceeds max size of %d\n",
867 __func__, bufsize, XATTR_SIZE_MAX);
868 goto setcifsacl_cmdlineverify_ret;
871 attrval = malloc(bufsize * sizeof(char));
873 printf("error allocating memory for attribute value buffer\n");
874 goto setcifsacl_cmdlineverify_ret;
877 attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
879 if (errno == ERANGE) {
884 printf("getxattr error: %d\n", errno);
885 goto setcifsacl_getx_ret;
889 numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
890 if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
891 printf("%s: Empty DACL\n", __func__);
892 goto setcifsacl_facenum_ret;
895 facesptr = build_fetched_aces((char *)daclptr, numfaces);
897 goto setcifsacl_facenum_ret;
900 rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
901 facesptr, numfaces, cacesptr, numcaces, maction);
903 goto setcifsacl_action_ret;
905 attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
907 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
908 goto setcifsacl_facenum_ret;
912 exit_plugin(plugin_handle);
915 setcifsacl_action_ret:
918 setcifsacl_facenum_ret:
919 for (i = 0; i < numfaces; ++i)
926 setcifsacl_cmdlineverify_ret:
927 for (i = 0; i < numcaces; ++i)
931 setcifsacl_cmdlineparse_ret:
934 setcifsacl_numcaces_ret:
935 exit_plugin(plugin_handle);