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 */
39 #include <sys/xattr.h>
42 static const char *prog;
44 enum setcifsacl_actions {
53 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
54 int numaces, int acessize)
58 int osidsoffset, gsidsoffset, dacloffset;
59 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
60 struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
61 struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
63 /* copy security descriptor control portion */
64 osidsoffset = htole32(pntsd->osidoffset);
65 gsidsoffset = htole32(pntsd->gsidoffset);
66 dacloffset = htole32(pntsd->dacloffset);
68 pnntsd->revision = pntsd->revision;
69 pnntsd->type = pntsd->type;
70 pnntsd->osidoffset = pntsd->osidoffset;
71 pnntsd->gsidoffset = pntsd->gsidoffset;
72 pnntsd->dacloffset = pntsd->dacloffset;
74 dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
75 ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
77 ndacl_ptr->revision = dacl_ptr->revision;
78 ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
79 ndacl_ptr->num_aces = htole32(numaces);
82 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
83 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
85 nowner_sid_ptr->revision = owner_sid_ptr->revision;
86 nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
87 for (i = 0; i < NUM_AUTHS; i++)
88 nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
89 for (i = 0; i < owner_sid_ptr->num_subauth; i++)
90 nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
93 group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
94 ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
96 ngroup_sid_ptr->revision = group_sid_ptr->revision;
97 ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
98 for (i = 0; i < NUM_AUTHS; i++)
99 ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
100 for (i = 0; i < group_sid_ptr->num_subauth; i++)
101 ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
107 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
111 dace->type = sace->type;
112 dace->flags = sace->flags;
113 dace->access_req = htole32(sace->access_req);
115 dace->sid.revision = sace->sid.revision;
116 dace->sid.num_subauth = sace->sid.num_subauth;
117 for (i = 0; i < NUM_AUTHS; i++)
118 dace->sid.authority[i] = sace->sid.authority[i];
119 for (i = 0; i < sace->sid.num_subauth; i++)
120 dace->sid.sub_auth[i] = sace->sid.sub_auth[i];
122 dace->size = htole16(sace->size);
128 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
132 if (compflags & COMPSID) {
133 if (dace->sid.revision != sace->sid.revision)
135 if (dace->sid.num_subauth != sace->sid.num_subauth)
137 for (i = 0; i < NUM_AUTHS; i++) {
138 if (dace->sid.authority[i] != sace->sid.authority[i])
141 for (i = 0; i < sace->sid.num_subauth; i++) {
142 if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
147 if (compflags & COMPTYPE) {
148 if (dace->type != sace->type)
152 if (compflags & COMPFLAG) {
153 if (dace->flags != sace->flags)
157 if (compflags & COMPMASK) {
158 if (dace->access_req != htole32(sace->access_req))
166 get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
167 int aces, ssize_t *bufsize, size_t *acesoffset)
169 unsigned int size, acessize, dacloffset;
171 size = sizeof(struct cifs_ntsd) +
172 2 * sizeof(struct cifs_sid) +
173 sizeof(struct cifs_ctrl_acl);
175 dacloffset = le32toh(pntsd->dacloffset);
177 *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
178 acessize = aces * sizeof(struct cifs_ace);
179 *bufsize = size + acessize;
181 *npntsd = malloc(*bufsize);
183 printf("%s: Memory allocation failure", __func__);
191 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
192 struct cifs_ace **cacesptr, int numcaces)
194 int i, rc, acessize = 0;
198 rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
202 acesptr = (char *)*npntsd + acesoffset;
203 for (i = 0; i < numcaces; ++i) {
204 acessize += copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
205 acesptr += sizeof(struct cifs_ace);
207 copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
208 acesptr = (char *)*npntsd + acesoffset;
215 ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
216 struct cifs_ace **facesptr, int numfaces,
217 struct cifs_ace **cacesptr, int numcaces)
219 int i, rc, numaces, size, acessize = 0;
223 numaces = numfaces + numcaces;
224 rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
228 acesptr = (char *)*npntsd + acesoffset;
229 for (i = 0; i < numfaces; ++i) {
230 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
234 for (i = 0; i < numcaces; ++i) {
235 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
239 copy_sec_desc(pntsd, *npntsd, numaces, acessize);
245 ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
246 struct cifs_ace **facesptr, int numfaces,
247 struct cifs_ace **cacesptr, int numcaces)
249 int i, j, rc, size, acessize = 0;
254 printf("%s: No entries to modify", __func__);
258 rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
262 for (j = 0; j < numcaces; ++j) {
263 for (i = 0; i < numfaces; ++i) {
264 if (compare_aces(facesptr[i], cacesptr[j],
265 COMPSID | COMPTYPE)) {
266 copy_ace(facesptr[i], cacesptr[j]);
272 acesptr = (char *)*npntsd + acesoffset;
273 for (i = 0; i < numfaces; ++i) {
274 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
279 copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
285 ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
286 struct cifs_ace **facesptr, int numfaces,
287 struct cifs_ace **cacesptr, int numcaces)
289 int i, j, numaces = 0, rc, size, acessize = 0;
294 printf("%s: No entries to delete\n", __func__);
298 if (numfaces < numcaces) {
299 printf("%s: Invalid entries to delete\n", __func__);
303 rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
307 acesptr = (char *)*npntsd + acesoffset;
308 for (i = 0; i < numfaces; ++i) {
309 for (j = 0; j < numcaces; ++j) {
310 if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
314 size = copy_ace((struct cifs_ace *)acesptr,
322 if (numaces == numfaces) {
323 printf("%s: Nothing to delete\n", __func__);
326 copy_sec_desc(pntsd, *npntsd, numaces, acessize);
332 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
333 struct cifs_ctrl_acl **daclptr)
337 struct cifs_ctrl_acl *ldaclptr;
338 char *end_of_acl = ((char *)pntsd) + acl_len;
340 dacloffset = le32toh(pntsd->dacloffset);
344 ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
346 /* validate that we do not go past end of acl */
347 if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
348 numfaces = le32toh(ldaclptr->num_aces);
355 static struct cifs_ace **
356 build_fetched_aces(char *daclptr, int numfaces)
358 int i, j, rc = 0, acl_size;
360 struct cifs_ace *pace, **facesptr;
362 facesptr = (struct cifs_ace **)malloc(numfaces *
363 sizeof(struct cifs_aces *));
365 printf("%s: Error %d allocating ACE array",
371 acl_size = sizeof(struct cifs_ctrl_acl);
372 for (i = 0; i < numfaces; ++i) {
373 facesptr[i] = malloc(sizeof(struct cifs_ace));
376 goto build_fetched_aces_ret;
378 pace = (struct cifs_ace *) (acl_base + acl_size);
379 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
380 acl_base = (char *)pace;
381 acl_size = le16toh(pace->size);
384 build_fetched_aces_ret:
386 printf("%s: Invalid fetched ace\n", __func__);
388 for (j = i; j >= 0; --j)
397 verify_ace_sid(char *sidstr, struct cifs_sid *sid)
401 struct passwd *winpswdptr;
403 lstr = strstr(sidstr, "\\"); /* everything before | */
409 /* Check if it is a (raw) SID (string) */
410 rc = wbcStringToSid(lstr, (struct wbcDomainSid *)sid);
414 /* Check if it a name (string) which can be resolved to a SID*/
415 rc = wbcGetpwnam(lstr, &winpswdptr);
417 printf("%s: Invalid user name: %s\n", __func__, sidstr);
420 rc = wbcUidToSid(winpswdptr->pw_uid, (struct wbcDomainSid *)sid);
422 printf("%s: Invalid user: %s\n", __func__, sidstr);
428 * Winbind keeps wbcDomainSid fields in host-endian. So, we must
429 * convert that to little endian since the server will expect that.
431 for (i = 0; i < sid->num_subauth; i++)
432 sid->sub_auth[i] = htole32(sid->sub_auth[i]);
437 verify_ace_type(char *typestr, uint8_t *typeval)
442 if (strstr(typestr, "0x")) { /* hex type value */
443 *typeval = strtol(typestr, &invaltype, 16);
444 if (!strlen(invaltype)) {
445 if (*typeval != ACCESS_ALLOWED &&
446 *typeval != ACCESS_DENIED &&
447 *typeval != ACCESS_ALLOWED_OBJECT &&
448 *typeval != ACCESS_DENIED_OBJECT) {
449 printf("%s: Invalid type: %s\n",
457 len = strlen(typestr);
458 for (i = 0; i < len; ++i)
459 *(typestr + i) = toupper(*(typestr + i));
460 if (!strcmp(typestr, "ALLOWED"))
462 else if (!strcmp(typestr, "DENIED"))
464 else if (!strcmp(typestr, "ALLOWED_OBJECT"))
466 else if (!strcmp(typestr, "DENIED_OBJECT"))
469 printf("%s: Invalid type: %s\n", __func__, typestr);
477 ace_flag_value(char *flagstr)
479 uint8_t flagval = 0x0;
482 iflag = strtok(flagstr, "|"); /* everything before | */
484 if (!strcmp(iflag, "OI"))
486 else if (!strcmp(iflag, "CI"))
488 else if (!strcmp(iflag, "NP"))
490 else if (!strcmp(iflag, "IO"))
492 else if (!strcmp(iflag, "I"))
495 return 0x0; /* Invalid flag */
496 iflag = strtok(NULL, "|"); /* everything before | */
503 verify_ace_flags(char *flagstr, uint8_t *flagval)
507 if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
510 if (strstr(flagstr, "0x")) { /* hex flag value */
511 *flagval = strtol(flagstr, &invalflag, 16);
512 if (strlen(invalflag)) {
513 printf("%s: Invalid flags: %s\n", __func__, flagstr);
517 *flagval = ace_flag_value(flagstr);
519 if (!*flagval || (*flagval & ~VFLAGS)) {
520 printf("%s: Invalid flag %s and value: 0x%x\n",
521 __func__, flagstr, *flagval);
529 ace_mask_value(char *maskstr)
532 uint32_t maskval = 0x0;
535 if (!strcmp(maskstr, "FULL"))
537 else if (!strcmp(maskstr, "CHANGE"))
539 else if (!strcmp(maskstr, "D"))
541 else if (!strcmp(maskstr, "READ"))
544 len = strlen(maskstr);
546 for (i = 0; i < len; ++i, ++lmask) {
549 else if (*lmask == 'W')
551 else if (*lmask == 'X')
553 else if (*lmask == 'D')
555 else if (*lmask == 'P')
556 maskval |= WRITE_DAC;
557 else if (*lmask == 'O')
558 maskval |= WRITE_OWNER;
569 verify_ace_mask(char *maskstr, uint32_t *maskval)
573 if (strstr(maskstr, "0x") || !strcmp(maskstr, "DELDHLD")) {
574 *maskval = strtol(maskstr, &invalflag, 16);
576 printf("%s: Invalid mask: %s\n", __func__, maskstr);
580 *maskval = ace_mask_value(maskstr);
583 printf("%s: Invalid mask %s and value: 0x%x\n",
584 __func__, maskstr, *maskval);
591 static struct cifs_ace **
592 build_cmdline_aces(char **arrptr, int numcaces)
595 char *acesid, *acetype, *aceflag, *acemask;
596 struct cifs_ace **cacesptr;
598 cacesptr = (struct cifs_ace **)malloc(numcaces *
599 sizeof(struct cifs_aces *));
601 printf("%s: Error %d allocating ACE array", __func__, errno);
605 for (i = 0; i < numcaces; ++i) {
606 acesid = strtok(arrptr[i], ":");
607 acetype = strtok(NULL, "/");
608 aceflag = strtok(NULL, "/");
609 acemask = strtok(NULL, "/");
611 if (!acesid || !acetype || !aceflag || !acemask) {
612 printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
613 goto build_cmdline_aces_ret;
616 cacesptr[i] = malloc(sizeof(struct cifs_ace));
618 printf("%s: ACE alloc error %d\n", __func__, errno);
619 goto build_cmdline_aces_ret;
622 if (verify_ace_sid(acesid, &cacesptr[i]->sid)) {
623 printf("%s: Invalid SID: %s\n", __func__, arrptr[i]);
624 goto build_cmdline_aces_ret;
627 if (verify_ace_type(acetype, &cacesptr[i]->type)) {
628 printf("%s: Invalid ACE type: %s\n",
629 __func__, arrptr[i]);
630 goto build_cmdline_aces_ret;
633 if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
634 printf("%s: Invalid ACE flag: %s\n",
635 __func__, arrptr[i]);
636 goto build_cmdline_aces_ret;
639 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
640 printf("%s: Invalid ACE mask: %s\n",
641 __func__, arrptr[i]);
642 goto build_cmdline_aces_ret;
645 cacesptr[i]->size = 1 + 1 + 2 + 4 + 1 + 1 + 6 +
646 (cacesptr[i]->sid.num_subauth * 4);
650 build_cmdline_aces_ret:
658 parse_cmdline_aces(char *optarg, int numcaces)
661 char *acestr, *vacestr, **arrptr = NULL;
664 arrptr = (char **)malloc(numcaces * sizeof(char *));
666 printf("%s: Error %d allocating char array\n", __func__, errno);
670 while (i < numcaces) {
671 acestr = strtok(optarg, ","); /* everything before , */
673 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
675 vacestr = strchr(vacestr, ':');
677 ++vacestr; /* go past : */
679 len = strlen(vacestr);
680 arrptr[i] = malloc(len + 1);
682 goto parse_cmdline_aces_ret;
683 strcpy(arrptr[i], vacestr);
686 goto parse_cmdline_aces_ret;
688 goto parse_cmdline_aces_ret;
690 goto parse_cmdline_aces_ret;
696 parse_cmdline_aces_ret:
697 printf("%s: Error %d parsing ACEs\n", __func__, errno);
705 get_numcaces(const char *optarg)
708 unsigned int numcaces = 1;
713 len = strlen(optarg);
714 for (i = 0; i < len; ++i) {
715 if (*(optarg + i) == ',')
723 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
724 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
725 struct cifs_ace **cacesptr, int numcaces,
726 enum setcifsacl_actions maction)
732 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
733 numfaces, cacesptr, numcaces);
736 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
737 numfaces, cacesptr, numcaces);
740 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
741 numfaces, cacesptr, numcaces);
744 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
747 printf("%s: Invalid action: %d\n", __func__, maction);
755 setcifsacl_usage(void)
758 "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
760 fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
761 fprintf(stderr, "Valid options:\n");
762 fprintf(stderr, "\t-v Version of the program\n");
763 fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
765 "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
766 fprintf(stderr, "\n");
768 "\t-D Delete ACE(s), separated by a comma, from an ACL\n");
770 "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
771 fprintf(stderr, "\n");
773 "\t-M Modify ACE(s), separated by a comma, in an ACL\n");
775 "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
777 "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
779 "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
780 fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
784 main(const int argc, char *const argv[])
786 int i, rc, c, numcaces, numfaces;
787 enum setcifsacl_actions maction = ActUnknown;
788 ssize_t attrlen, bufsize = BUFSIZE;
789 char *ace_list, *filename, *attrval, **arrptr = NULL;
790 struct cifs_ctrl_acl *daclptr = NULL;
791 struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
792 struct cifs_ntsd *ntsdptr = NULL;
794 prog = basename(argv[0]);
796 openlog(prog, 0, LOG_DAEMON);
798 c = getopt(argc, argv, "hvD:M:a:S:");
820 printf("Version: %s\n", VERSION);
827 /* We expect 1 argument in addition to the option */
834 numcaces = get_numcaces(ace_list);
836 printf("%s: No valid ACEs specified\n", __func__);
840 arrptr = parse_cmdline_aces(ace_list, numcaces);
842 goto setcifsacl_numcaces_ret;
844 cacesptr = build_cmdline_aces(arrptr, numcaces);
846 goto setcifsacl_cmdlineparse_ret;
849 if (bufsize >= XATTR_SIZE_MAX) {
850 printf("%s: Buffer size %ld exceeds max size of %d\n",
851 __func__, bufsize, XATTR_SIZE_MAX);
852 goto setcifsacl_cmdlineverify_ret;
855 attrval = malloc(bufsize * sizeof(char));
857 printf("error allocating memory for attribute value buffer\n");
858 goto setcifsacl_cmdlineverify_ret;
861 attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
863 if (errno == ERANGE) {
868 printf("getxattr error: %d\n", errno);
869 goto setcifsacl_getx_ret;
873 numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
874 if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
875 printf("%s: Empty DACL\n", __func__);
876 goto setcifsacl_facenum_ret;
879 facesptr = build_fetched_aces((char *)daclptr, numfaces);
881 goto setcifsacl_facenum_ret;
884 rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
885 facesptr, numfaces, cacesptr, numcaces, maction);
887 goto setcifsacl_action_ret;
889 attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
891 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
892 goto setcifsacl_facenum_ret;
896 setcifsacl_action_ret:
899 setcifsacl_facenum_ret:
900 for (i = 0; i < numfaces; ++i)
907 setcifsacl_cmdlineverify_ret:
908 for (i = 0; i < numcaces; ++i)
912 setcifsacl_cmdlineparse_ret:
913 for (i = 0; i < numcaces; ++i)
917 setcifsacl_numcaces_ret: