7eeeaa6c5cfc69fb645cd559a0d52b5970eff409
[jlayton/cifs-utils.git] / setcifsacl.c
1 /*
2 * setcifsacl utility
3 *
4 * Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
5 *
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.
9 *
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
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <string.h>
28 #include <getopt.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <ctype.h>
37 #include <sys/xattr.h>
38
39 #include "cifsacl.h"
40 #include "idmap_plugin.h"
41
42 enum setcifsacl_actions {
43         ActUnknown = -1,
44         ActDelete,
45         ActModify,
46         ActAdd,
47         ActSet
48 };
49
50 static void *plugin_handle;
51 static bool plugin_loaded;
52
53 static void
54 copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src)
55 {
56         int i;
57
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];
64 }
65
66 static void
67 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
68                 int numaces, int acessize)
69 {
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;
74
75         /* copy security descriptor control portion */
76         osidsoffset = le32toh(pntsd->osidoffset);
77         gsidsoffset = le32toh(pntsd->gsidoffset);
78         dacloffset = le32toh(pntsd->dacloffset);
79
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;
85
86         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
87         ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
88
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);
92
93         /* copy owner sid */
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);
97
98         /* copy group sid */
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);
102
103         return;
104 }
105
106 static int
107 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
108 {
109         dace->type = sace->type;
110         dace->flags = sace->flags;
111         dace->access_req = sace->access_req;
112
113         copy_cifs_sid(&dace->sid, &sace->sid);
114
115         dace->size = sace->size;
116
117         return le16toh(dace->size);
118 }
119
120 static int
121 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
122 {
123         int i;
124
125         if (compflags & COMPSID) {
126                 if (dace->sid.revision != sace->sid.revision)
127                         return 0;
128                 if (dace->sid.num_subauth != sace->sid.num_subauth)
129                         return 0;
130                 for (i = 0; i < NUM_AUTHS; i++) {
131                         if (dace->sid.authority[i] != sace->sid.authority[i])
132                                 return 0;
133                 }
134                 for (i = 0; i < sace->sid.num_subauth; i++) {
135                         if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
136                                 return 0;
137                 }
138         }
139
140         if (compflags & COMPTYPE) {
141                 if (dace->type != sace->type)
142                         return 0;
143         }
144
145         if (compflags & COMPFLAG) {
146                 if (dace->flags != sace->flags)
147                         return 0;
148         }
149
150         if (compflags & COMPMASK) {
151                 if (dace->access_req != sace->access_req)
152                         return 0;
153         }
154
155         return 1;
156 }
157
158 static int
159 get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
160                         int aces, ssize_t *bufsize, size_t *acesoffset)
161 {
162         unsigned int size, acessize, dacloffset;
163
164         size = sizeof(struct cifs_ntsd) +
165                 2 * sizeof(struct cifs_sid) +
166                 sizeof(struct cifs_ctrl_acl);
167
168         dacloffset = le32toh(pntsd->dacloffset);
169
170         *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
171         acessize = aces * sizeof(struct cifs_ace);
172         *bufsize = size + acessize;
173
174         *npntsd = malloc(*bufsize);
175         if (!*npntsd) {
176                 printf("%s: Memory allocation failure", __func__);
177                 return errno;
178         }
179
180         return 0;
181 }
182
183 static int
184 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
185                         struct cifs_ace **cacesptr, int numcaces)
186 {
187         int i, rc, size = 0, acessize = 0;
188         size_t acesoffset;
189         char *acesptr;
190
191         rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
192         if (rc)
193                 return rc;
194
195         acesptr = (char *)*npntsd + acesoffset;
196         for (i = 0; i < numcaces; ++i) {
197                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
198                 acessize += size;
199                 acesptr += size;
200         }
201         copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
202         acesptr = (char *)*npntsd + acesoffset;
203
204
205         return 0;
206 }
207
208 static int
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)
212 {
213         int i, rc, numaces, size, acessize = 0;
214         size_t acesoffset;
215         char *acesptr;
216
217         numaces = numfaces + numcaces;
218         rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
219         if (rc)
220                 return rc;
221
222         acesptr = (char *)*npntsd + acesoffset;
223         for (i = 0; i < numfaces; ++i) {
224                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
225                 acesptr += size;
226                 acessize += size;
227         }
228         for (i = 0; i < numcaces; ++i) {
229                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
230                 acesptr += size;
231                 acessize += size;
232         }
233         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
234
235         return 0;
236 }
237
238 static int
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)
242 {
243         int i, j, rc, size, acessize = 0;
244         size_t acesoffset;
245         char *acesptr;
246
247         if (numfaces == 0) {
248                 printf("%s: No entries to modify", __func__);
249                 return -1;
250         }
251
252         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
253         if (rc)
254                 return rc;
255
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]);
261                                 break;
262                         }
263                 }
264         }
265
266         acesptr = (char *)*npntsd + acesoffset;
267         for (i = 0; i < numfaces; ++i) {
268                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
269                 acesptr += size;
270                 acessize += size;
271         }
272
273         copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
274
275         return 0;
276 }
277
278 static int
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)
282 {
283         int i, j, numaces = 0, rc, size, acessize = 0;
284         size_t acesoffset;
285         char *acesptr;
286
287         if (numfaces == 0) {
288                 printf("%s: No entries to delete\n", __func__);
289                 return -1;
290         }
291
292         if (numfaces < numcaces) {
293                 printf("%s: Invalid entries to delete\n", __func__);
294                 return -1;
295         }
296
297         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
298         if (rc)
299                 return rc;
300
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))
305                                 break;
306                 }
307                 if (j == numcaces) {
308                         size = copy_ace((struct cifs_ace *)acesptr,
309                                                                 facesptr[i]);
310                         acessize += size;
311                         acesptr += size;
312                         ++numaces;
313                 }
314         }
315
316         if (numaces == numfaces) {
317                 printf("%s: Nothing to delete\n", __func__);
318                 return 1;
319         }
320         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
321
322         return 0;
323 }
324
325 static int
326 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
327                         struct cifs_ctrl_acl **daclptr)
328 {
329         int numfaces = 0;
330         uint32_t dacloffset;
331         struct cifs_ctrl_acl *ldaclptr;
332         char *end_of_acl = ((char *)pntsd) + acl_len;
333
334         dacloffset = le32toh(pntsd->dacloffset);
335         if (!dacloffset)
336                 return 0;
337
338         ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
339
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);
343                 *daclptr = ldaclptr;
344         }
345
346         return numfaces;
347 }
348
349 static struct cifs_ace **
350 build_fetched_aces(char *daclptr, int numfaces)
351 {
352         int i, acl_size;
353         char *acl_base;
354         struct cifs_ace *pace, **facesptr;
355
356         facesptr = calloc(numfaces, sizeof(struct cifs_aces *));
357         if (!facesptr) {
358                 printf("%s: Error %d allocating ACE array",
359                                 __func__, errno);
360                 return facesptr;
361         }
362
363         acl_base = daclptr;
364         acl_size = sizeof(struct cifs_ctrl_acl);
365         for (i = 0; i < numfaces; ++i) {
366                 facesptr[i] = malloc(sizeof(struct cifs_ace));
367                 if (!facesptr[i])
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);
373         }
374         return facesptr;
375
376 build_fetched_aces_err:
377         printf("%s: Invalid fetched ace\n", __func__);
378         for (i = 0; i < numfaces; ++i)
379                 free(facesptr[i]);
380         free(facesptr);
381         return NULL;
382 }
383
384 static int
385 verify_ace_type(char *typestr, uint8_t *typeval)
386 {
387         int i, len;
388         char *invaltype;
389
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",
398                                                 __func__, typestr);
399                                         return 1;
400                         }
401                         return 0;
402                 }
403         }
404
405         len = strlen(typestr);
406         for (i = 0; i < len; ++i)
407                 *(typestr + i) = toupper(*(typestr + i));
408         if (!strcmp(typestr, "ALLOWED"))
409                 *typeval = 0x0;
410         else if (!strcmp(typestr, "DENIED"))
411                 *typeval = 0x1;
412         else if (!strcmp(typestr, "ALLOWED_OBJECT"))
413                 *typeval = 0x5;
414         else if (!strcmp(typestr, "DENIED_OBJECT"))
415                 *typeval = 0x6;
416         else {
417                 printf("%s: Invalid type: %s\n", __func__, typestr);
418                 return 1;
419         }
420
421         return 0;
422 }
423
424 static uint8_t
425 ace_flag_value(char *flagstr)
426 {
427         uint8_t flagval = 0x0;
428         char *iflag;
429
430         iflag = strtok(flagstr, "|"); /* everything before | */
431         while (iflag) {
432                 if (!strcmp(iflag, "OI"))
433                         flagval += 0x1;
434                 else if (!strcmp(iflag, "CI"))
435                         flagval += 0x2;
436                 else if (!strcmp(iflag, "NP"))
437                         flagval += 0x4;
438                 else if (!strcmp(iflag, "IO"))
439                         flagval += 0x8;
440                 else if (!strcmp(iflag, "I"))
441                         flagval += 0x10;
442                 else
443                         return 0x0; /* Invalid flag */
444                 iflag = strtok(NULL, "|"); /* everything before | */
445         }
446
447         return flagval;
448 }
449
450 static int
451 verify_ace_flags(char *flagstr, uint8_t *flagval)
452 {
453         char *invalflag;
454
455         if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
456                 return 0;
457
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);
462                         return 1;
463                 }
464         } else
465                 *flagval = ace_flag_value(flagstr);
466
467         if (!*flagval || (*flagval & ~VFLAGS)) {
468                 printf("%s: Invalid flag %s and value: 0x%x\n",
469                         __func__, flagstr, *flagval);
470                 return 1;
471         }
472
473         return 0;
474 }
475
476 static uint32_t
477 ace_mask_value(char *mask)
478 {
479         uint32_t maskval = 0;
480         char cur;
481
482         if (!strcmp(mask, "FULL"))
483                 return FULL_CONTROL;
484         if (!strcmp(mask, "CHANGE"))
485                 return CHANGE;
486         if (!strcmp(mask, "READ"))
487                 return EREAD;
488
489         while((cur = *mask++)) {
490                 switch(cur) {
491                 case 'R':
492                         maskval |= EREAD;
493                         break;
494                 case 'W':
495                         maskval |= EWRITE;
496                         break;
497                 case 'X':
498                         maskval |= EXEC;
499                         break;
500                 case 'D':
501                         maskval |= DELETE;
502                         break;
503                 case 'P':
504                         maskval |= WRITE_DAC;
505                         break;
506                 case 'O':
507                         maskval |= WRITE_OWNER;
508                         break;
509                 default:
510                         return 0;
511                 }
512         }
513         return maskval;
514 }
515
516 static int
517 verify_ace_mask(char *maskstr, uint32_t *maskval)
518 {
519         unsigned long val;
520         char *ep;
521
522         errno = 0;
523         val = strtoul(maskstr, &ep, 0);
524         if (errno == 0 && *ep == '\0')
525                 *maskval = htole32((uint32_t)val);
526         else
527                 *maskval = htole32(ace_mask_value(maskstr));
528
529         if (!*maskval) {
530                 printf("%s: Invalid mask %s (value 0x%x)\n", __func__,
531                         maskstr, *maskval);
532                 return 1;
533         }
534
535         return 0;
536 }
537
538 #define AUTHORITY_MASK (~(0xffffffffffffULL))
539
540 static int
541 raw_str_to_sid(const char *str, struct cifs_sid *csid)
542 {
543         const char *p;
544         char *q;
545         unsigned long long x;
546
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-\"";
550                 return -EINVAL;
551         }
552
553         /* Get the SID revision number */
554         p = str + 2;
555         x = strtoull(p, &q, 10);
556         if (x == 0 || x > UCHAR_MAX || !q || *q != '-') {
557                 plugin_errmsg = "Invalid SID revision number";
558                 return -EINVAL;
559         }
560         csid->revision = (uint8_t)x;
561
562         /*
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.
566          */
567         p = q + 1;
568         x = strtoull(p, &q, 0);
569         if ((x & AUTHORITY_MASK) || !q || *q !='-') {
570                 plugin_errmsg = "Invalid SID authority";
571                 return -EINVAL;
572         }
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) >> 40;
579
580         /* now read the the subauthorities and store as __le32 vals */
581         p = q + 1;
582         csid->num_subauth = 0;
583         while (csid->num_subauth < SID_MAX_SUB_AUTHORITIES) {
584                 x = strtoul(p, &q, 10);
585                 if (p == q)
586                         break;
587                 if (x > UINT_MAX) {
588                         plugin_errmsg = "Invalid sub authority value";
589                         return -EINVAL;
590                 }
591                 csid->sub_auth[csid->num_subauth++] = htole32((uint32_t)x);
592
593                 if (*q != '-')
594                         break;
595                 p = q + 1;
596         }
597
598         /* IF we ended early, then the SID could not be converted */
599         if (q && *q != '\0') {
600                 plugin_errmsg = "Invalid sub authority value";
601                 return -EINVAL;
602         }
603
604         return 0;
605 }
606
607 static int
608 setcifsacl_str_to_sid(const char *str, struct cifs_sid *sid)
609 {
610         if (plugin_loaded)
611                 return str_to_sid(plugin_handle, str, sid);
612         return raw_str_to_sid(str, sid);
613 }
614
615 static struct cifs_ace **
616 build_cmdline_aces(char **arrptr, int numcaces)
617 {
618         int i;
619         char *acesid, *acetype, *aceflag, *acemask;
620         struct cifs_ace **cacesptr;
621
622         cacesptr = calloc(numcaces, sizeof(struct cifs_aces *));
623         if (!cacesptr) {
624                 printf("%s: Error %d allocating ACE array", __func__, errno);
625                 return NULL;
626         }
627
628         for (i = 0; i < numcaces; ++i) {
629                 acesid = strtok(arrptr[i], ":");
630                 acetype = strtok(NULL, "/");
631                 aceflag = strtok(NULL, "/");
632                 acemask = strtok(NULL, "/");
633
634                 if (!acesid || !acetype || !aceflag || !acemask) {
635                         printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
636                         goto build_cmdline_aces_ret;
637                 }
638
639                 cacesptr[i] = malloc(sizeof(struct cifs_ace));
640                 if (!cacesptr[i]) {
641                         printf("%s: ACE alloc error %d\n", __func__, errno);
642                         goto build_cmdline_aces_ret;
643                 }
644
645                 if (setcifsacl_str_to_sid(acesid, &cacesptr[i]->sid)) {
646                         printf("%s: Invalid SID (%s): %s\n", __func__, arrptr[i],
647                                 plugin_errmsg);
648                         goto build_cmdline_aces_ret;
649                 }
650
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;
655                 }
656
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;
661                 }
662
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;
667                 }
668
669                 cacesptr[i]->size = htole16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
670                                             cacesptr[i]->sid.num_subauth * 4);
671         }
672         return cacesptr;
673
674 build_cmdline_aces_ret:
675         for (i = 0; i < numcaces; ++i)
676                 free(cacesptr[i]);
677         free(cacesptr);
678         return NULL;
679 }
680
681 static char **
682 parse_cmdline_aces(char *acelist, int numcaces)
683 {
684         int i = 0;
685         char *acestr, *vacestr, **arrptr = NULL;
686
687         arrptr = (char **)malloc(numcaces * sizeof(char *));
688         if (!arrptr) {
689                 printf("%s: Unable to allocate char array\n", __func__);
690                 return NULL;
691         }
692
693         while (i < numcaces) {
694                 acestr = strtok(acelist, ","); /* everything before , */
695                 if (!acestr)
696                         goto parse_cmdline_aces_err;
697
698                 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
699                 if (!vacestr)
700                         goto parse_cmdline_aces_err;
701                 vacestr += 4; /* skip past "ACL:" */
702                 if (*vacestr) {
703                         arrptr[i] = vacestr;
704                         ++i;
705                 }
706                 acelist = NULL;
707         }
708         return arrptr;
709
710 parse_cmdline_aces_err:
711         printf("%s: Error parsing ACEs\n", __func__);
712         free(arrptr);
713         return NULL;
714 }
715
716 /* How many aces were provided on the command-line? Count the commas. */
717 static unsigned int
718 get_numcaces(const char *aces)
719 {
720         unsigned int num = 1;
721         const char *current;
722
723         current = aces;
724         while((current = strchr(current, ','))) {
725                 ++current;
726                 ++num;
727         }
728
729         return num;
730 }
731
732 static int
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)
737 {
738         int rc = 1;
739
740         switch (maction) {
741         case ActDelete:
742                 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
743                                 numfaces, cacesptr, numcaces);
744                 break;
745         case ActModify:
746                 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
747                                 numfaces, cacesptr, numcaces);
748                 break;
749         case ActAdd:
750                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
751                                 numfaces, cacesptr, numcaces);
752                 break;
753         case ActSet:
754                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
755                 break;
756         default:
757                 printf("%s: Invalid action: %d\n", __func__, maction);
758                 break;
759         }
760
761         return rc;
762 }
763
764 static void
765 setcifsacl_usage(const char *prog)
766 {
767         fprintf(stderr,
768         "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
769                 prog);
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");
774         fprintf(stderr,
775         "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
776         fprintf(stderr, "\n");
777         fprintf(stderr,
778         "\t-D   Delete ACE(s), separated by a comma, from an ACL\n");
779         fprintf(stderr,
780         "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
781         fprintf(stderr, "\n");
782         fprintf(stderr,
783         "\t-M   Modify ACE(s), separated by a comma, in an ACL\n");
784         fprintf(stderr,
785         "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
786         fprintf(stderr,
787         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
788         fprintf(stderr,
789         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
790         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
791 }
792
793 int
794 main(const int argc, char *const argv[])
795 {
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;
803
804         c = getopt(argc, argv, "hvD:M:a:S:");
805         switch (c) {
806         case 'D':
807                 maction = ActDelete;
808                 ace_list = optarg;
809                 break;
810         case 'M':
811                 maction = ActModify;
812                 ace_list = optarg;
813                 break;
814         case 'a':
815                 maction = ActAdd;
816                 ace_list = optarg;
817                 break;
818         case 'S':
819                 maction = ActSet;
820                 ace_list = optarg;
821                 break;
822         case 'h':
823                 setcifsacl_usage(basename(argv[0]));
824                 return 0;
825         case 'v':
826                 printf("Version: %s\n", VERSION);
827                 return 0;
828         default:
829                 setcifsacl_usage(basename(argv[0]));
830                 return -1;
831         }
832
833         /* We expect 1 argument in addition to the option */
834         if (argc != 4) {
835                 setcifsacl_usage(basename(argv[0]));
836                 return -1;
837         }
838         filename = argv[3];
839
840         if (!ace_list) {
841                 printf("%s: No valid ACEs specified\n", __func__);
842                 return -1;
843         }
844
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;
850         } else {
851                 plugin_loaded = true;
852         }
853
854         numcaces = get_numcaces(ace_list);
855
856         arrptr = parse_cmdline_aces(ace_list, numcaces);
857         if (!arrptr)
858                 goto setcifsacl_numcaces_ret;
859
860         cacesptr = build_cmdline_aces(arrptr, numcaces);
861         if (!cacesptr)
862                 goto setcifsacl_cmdlineparse_ret;
863
864 cifsacl:
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;
869         }
870
871         attrval = malloc(bufsize * sizeof(char));
872         if (!attrval) {
873                 printf("error allocating memory for attribute value buffer\n");
874                 goto setcifsacl_cmdlineverify_ret;
875         }
876
877         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
878         if (attrlen == -1) {
879                 if (errno == ERANGE) {
880                         free(attrval);
881                         bufsize += BUFSIZE;
882                         goto cifsacl;
883                 } else {
884                         printf("getxattr error: %d\n", errno);
885                         goto setcifsacl_getx_ret;
886                 }
887         }
888
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;
893         }
894
895         facesptr = build_fetched_aces((char *)daclptr, numfaces);
896         if (!facesptr)
897                 goto setcifsacl_facenum_ret;
898
899         bufsize = 0;
900         rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
901                 facesptr, numfaces, cacesptr, numcaces, maction);
902         if (rc)
903                 goto setcifsacl_action_ret;
904
905         attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
906         if (attrlen == -1) {
907                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
908                 goto setcifsacl_facenum_ret;
909         }
910
911         if (plugin_loaded)
912                 exit_plugin(plugin_handle);
913         return 0;
914
915 setcifsacl_action_ret:
916         free(ntsdptr);
917
918 setcifsacl_facenum_ret:
919         for (i = 0; i < numfaces; ++i)
920                 free(facesptr[i]);
921         free(facesptr);
922
923 setcifsacl_getx_ret:
924         free(attrval);
925
926 setcifsacl_cmdlineverify_ret:
927         for (i = 0; i < numcaces; ++i)
928                 free(cacesptr[i]);
929         free(cacesptr);
930
931 setcifsacl_cmdlineparse_ret:
932         free(arrptr);
933
934 setcifsacl_numcaces_ret:
935         exit_plugin(plugin_handle);
936         return -1;
937 }