setcifsacl: fix some bugs in build_cmdline_aces
[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 <syslog.h>
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <wbclient.h>
38 #include <ctype.h>
39 #include <sys/xattr.h>
40 #include "cifsacl.h"
41
42 static const char *prog;
43
44 enum setcifsacl_actions {
45         ActUnknown = -1,
46         ActDelete,
47         ActModify,
48         ActAdd,
49         ActSet
50 };
51
52 static void
53 copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src)
54 {
55         int i;
56
57         dst->revision = src->revision;
58         dst->num_subauth = src->num_subauth;
59         for (i = 0; i < NUM_AUTHS; i++)
60                 dst->authority[i] = src->authority[i];
61         for (i = 0; i < src->num_subauth; i++)
62                 dst->sub_auth[i] = src->sub_auth[i];
63 }
64
65 static void
66 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
67                 int numaces, int acessize)
68 {
69         int osidsoffset, gsidsoffset, dacloffset;
70         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
71         struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
72         struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
73
74         /* copy security descriptor control portion */
75         osidsoffset = le32toh(pntsd->osidoffset);
76         gsidsoffset = le32toh(pntsd->gsidoffset);
77         dacloffset = le32toh(pntsd->dacloffset);
78
79         pnntsd->revision = pntsd->revision;
80         pnntsd->type = pntsd->type;
81         pnntsd->osidoffset = pntsd->osidoffset;
82         pnntsd->gsidoffset = pntsd->gsidoffset;
83         pnntsd->dacloffset = pntsd->dacloffset;
84
85         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
86         ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
87
88         ndacl_ptr->revision = dacl_ptr->revision;
89         ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
90         ndacl_ptr->num_aces = htole32(numaces);
91
92         /* copy owner sid */
93         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
94         nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
95         copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
96
97         /* copy group sid */
98         group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
99         ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
100         copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
101
102         return;
103 }
104
105 static int
106 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
107 {
108         dace->type = sace->type;
109         dace->flags = sace->flags;
110         dace->access_req = sace->access_req;
111
112         copy_cifs_sid(&dace->sid, &sace->sid);
113
114         dace->size = sace->size;
115
116         return le16toh(dace->size);
117 }
118
119 static int
120 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
121 {
122         int i;
123
124         if (compflags & COMPSID) {
125                 if (dace->sid.revision != sace->sid.revision)
126                         return 0;
127                 if (dace->sid.num_subauth != sace->sid.num_subauth)
128                         return 0;
129                 for (i = 0; i < NUM_AUTHS; i++) {
130                         if (dace->sid.authority[i] != sace->sid.authority[i])
131                                 return 0;
132                 }
133                 for (i = 0; i < sace->sid.num_subauth; i++) {
134                         if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
135                                 return 0;
136                 }
137         }
138
139         if (compflags & COMPTYPE) {
140                 if (dace->type != sace->type)
141                         return 0;
142         }
143
144         if (compflags & COMPFLAG) {
145                 if (dace->flags != sace->flags)
146                         return 0;
147         }
148
149         if (compflags & COMPMASK) {
150                 if (dace->access_req != sace->access_req)
151                         return 0;
152         }
153
154         return 1;
155 }
156
157 static int
158 get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
159                         int aces, ssize_t *bufsize, size_t *acesoffset)
160 {
161         unsigned int size, acessize, dacloffset;
162
163         size = sizeof(struct cifs_ntsd) +
164                 2 * sizeof(struct cifs_sid) +
165                 sizeof(struct cifs_ctrl_acl);
166
167         dacloffset = le32toh(pntsd->dacloffset);
168
169         *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
170         acessize = aces * sizeof(struct cifs_ace);
171         *bufsize = size + acessize;
172
173         *npntsd = malloc(*bufsize);
174         if (!*npntsd) {
175                 printf("%s: Memory allocation failure", __func__);
176                 return errno;
177         }
178
179         return 0;
180 }
181
182 static int
183 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
184                         struct cifs_ace **cacesptr, int numcaces)
185 {
186         int i, rc, acessize = 0;
187         size_t acesoffset;
188         char *acesptr;
189
190         rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
191         if (rc)
192                 return rc;
193
194         acesptr = (char *)*npntsd + acesoffset;
195         for (i = 0; i < numcaces; ++i) {
196                 acessize += copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
197                 acesptr += sizeof(struct cifs_ace);
198         }
199         copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
200         acesptr = (char *)*npntsd + acesoffset;
201
202
203         return 0;
204 }
205
206 static int
207 ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
208                 struct cifs_ace **facesptr, int numfaces,
209                 struct cifs_ace **cacesptr, int numcaces)
210 {
211         int i, rc, numaces, size, acessize = 0;
212         size_t acesoffset;
213         char *acesptr;
214
215         numaces = numfaces + numcaces;
216         rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
217         if (rc)
218                 return rc;
219
220         acesptr = (char *)*npntsd + acesoffset;
221         for (i = 0; i < numfaces; ++i) {
222                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
223                 acesptr += size;
224                 acessize += size;
225         }
226         for (i = 0; i < numcaces; ++i) {
227                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
228                 acesptr += size;
229                 acessize += size;
230         }
231         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
232
233         return 0;
234 }
235
236 static int
237 ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
238                 struct cifs_ace **facesptr, int numfaces,
239                 struct cifs_ace **cacesptr, int numcaces)
240 {
241         int i, j, rc, size, acessize = 0;
242         size_t acesoffset;
243         char *acesptr;
244
245         if (numfaces == 0) {
246                 printf("%s: No entries to modify", __func__);
247                 return -1;
248         }
249
250         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
251         if (rc)
252                 return rc;
253
254         for (j = 0; j < numcaces; ++j) {
255                 for (i = 0; i < numfaces; ++i) {
256                         if (compare_aces(facesptr[i], cacesptr[j],
257                                         COMPSID | COMPTYPE)) {
258                                 copy_ace(facesptr[i], cacesptr[j]);
259                                 break;
260                         }
261                 }
262         }
263
264         acesptr = (char *)*npntsd + acesoffset;
265         for (i = 0; i < numfaces; ++i) {
266                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
267                 acesptr += size;
268                 acessize += size;
269         }
270
271         copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
272
273         return 0;
274 }
275
276 static int
277 ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
278                 struct cifs_ace **facesptr, int numfaces,
279                 struct cifs_ace **cacesptr, int numcaces)
280 {
281         int i, j, numaces = 0, rc, size, acessize = 0;
282         size_t acesoffset;
283         char *acesptr;
284
285         if (numfaces == 0) {
286                 printf("%s: No entries to delete\n", __func__);
287                 return -1;
288         }
289
290         if (numfaces < numcaces) {
291                 printf("%s: Invalid entries to delete\n", __func__);
292                 return -1;
293         }
294
295         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
296         if (rc)
297                 return rc;
298
299         acesptr = (char *)*npntsd + acesoffset;
300         for (i = 0; i < numfaces; ++i) {
301                 for (j = 0; j < numcaces; ++j) {
302                         if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
303                                 break;
304                 }
305                 if (j == numcaces) {
306                         size = copy_ace((struct cifs_ace *)acesptr,
307                                                                 facesptr[i]);
308                         acessize += size;
309                         acesptr += size;
310                         ++numaces;
311                 }
312         }
313
314         if (numaces == numfaces) {
315                 printf("%s: Nothing to delete\n", __func__);
316                 return 1;
317         }
318         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
319
320         return 0;
321 }
322
323 static int
324 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
325                         struct cifs_ctrl_acl **daclptr)
326 {
327         int numfaces = 0;
328         uint32_t dacloffset;
329         struct cifs_ctrl_acl *ldaclptr;
330         char *end_of_acl = ((char *)pntsd) + acl_len;
331
332         dacloffset = le32toh(pntsd->dacloffset);
333         if (!dacloffset)
334                 return 0;
335
336         ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
337
338         /* validate that we do not go past end of acl */
339         if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
340                 numfaces = le32toh(ldaclptr->num_aces);
341                 *daclptr = ldaclptr;
342         }
343
344         return numfaces;
345 }
346
347 static struct cifs_ace **
348 build_fetched_aces(char *daclptr, int numfaces)
349 {
350         int i, acl_size;
351         char *acl_base;
352         struct cifs_ace *pace, **facesptr;
353
354         facesptr = calloc(numfaces, sizeof(struct cifs_aces *));
355         if (!facesptr) {
356                 printf("%s: Error %d allocating ACE array",
357                                 __func__, errno);
358                 return facesptr;
359         }
360
361         acl_base = daclptr;
362         acl_size = sizeof(struct cifs_ctrl_acl);
363         for (i = 0; i < numfaces; ++i) {
364                 facesptr[i] = malloc(sizeof(struct cifs_ace));
365                 if (!facesptr[i])
366                         goto build_fetched_aces_err;
367                 pace = (struct cifs_ace *) (acl_base + acl_size);
368                 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
369                 acl_base = (char *)pace;
370                 acl_size = le16toh(pace->size);
371         }
372         return facesptr;
373
374 build_fetched_aces_err:
375         printf("%s: Invalid fetched ace\n", __func__);
376         for (i = 0; i < numfaces; ++i)
377                 free(facesptr[i]);
378         free(facesptr);
379         return NULL;
380 }
381
382 static int
383 verify_ace_sid(char *sidstr, struct cifs_sid *sid)
384 {
385         int i;
386         wbcErr rc;
387         char *name, *domain;
388         enum wbcSidType type;
389
390         name = strchr(sidstr, '\\');
391         if (!name) {
392                 /* might be a raw string representation of SID */
393                 rc = wbcStringToSid(sidstr, (struct wbcDomainSid *)sid);
394                 if (WBC_ERROR_IS_OK(rc))
395                         goto fix_endianness;
396
397                 domain = "";
398                 name = sidstr;
399         } else {
400                 domain = sidstr;
401                 *name = '\0';
402                 ++name;
403         }
404
405         rc = wbcLookupName(domain, name, (struct wbcDomainSid *)sid, &type);
406         if (!WBC_ERROR_IS_OK(rc)) {
407                 printf("%s: Error converting %s\\%s to SID: %s\n",
408                         __func__, domain, name, wbcErrorString(rc));
409                 return rc;
410         }
411
412 fix_endianness:
413         /*
414          * Winbind keeps wbcDomainSid fields in host-endian. So, we must
415          * convert that to little endian since the server will expect that.
416          */
417         for (i = 0; i < sid->num_subauth; i++)
418                 sid->sub_auth[i] = htole32(sid->sub_auth[i]);
419         return 0;
420 }
421
422 static int
423 verify_ace_type(char *typestr, uint8_t *typeval)
424 {
425         int i, len;
426         char *invaltype;
427
428         if (strstr(typestr, "0x")) { /* hex type value */
429                 *typeval = strtol(typestr, &invaltype, 16);
430                 if (!strlen(invaltype)) {
431                         if (*typeval != ACCESS_ALLOWED &&
432                                 *typeval != ACCESS_DENIED &&
433                                 *typeval != ACCESS_ALLOWED_OBJECT &&
434                                 *typeval != ACCESS_DENIED_OBJECT) {
435                                         printf("%s: Invalid type: %s\n",
436                                                 __func__, typestr);
437                                         return 1;
438                         }
439                         return 0;
440                 }
441         }
442
443         len = strlen(typestr);
444         for (i = 0; i < len; ++i)
445                 *(typestr + i) = toupper(*(typestr + i));
446         if (!strcmp(typestr, "ALLOWED"))
447                 *typeval = 0x0;
448         else if (!strcmp(typestr, "DENIED"))
449                 *typeval = 0x1;
450         else if (!strcmp(typestr, "ALLOWED_OBJECT"))
451                 *typeval = 0x5;
452         else if (!strcmp(typestr, "DENIED_OBJECT"))
453                 *typeval = 0x6;
454         else {
455                 printf("%s: Invalid type: %s\n", __func__, typestr);
456                 return 1;
457         }
458
459         return 0;
460 }
461
462 static uint8_t
463 ace_flag_value(char *flagstr)
464 {
465         uint8_t flagval = 0x0;
466         char *iflag;
467
468         iflag = strtok(flagstr, "|"); /* everything before | */
469         while (iflag) {
470                 if (!strcmp(iflag, "OI"))
471                         flagval += 0x1;
472                 else if (!strcmp(iflag, "CI"))
473                         flagval += 0x2;
474                 else if (!strcmp(iflag, "NP"))
475                         flagval += 0x4;
476                 else if (!strcmp(iflag, "IO"))
477                         flagval += 0x8;
478                 else if (!strcmp(iflag, "I"))
479                         flagval += 0x10;
480                 else
481                         return 0x0; /* Invalid flag */
482                 iflag = strtok(NULL, "|"); /* everything before | */
483         }
484
485         return flagval;
486 }
487
488 static int
489 verify_ace_flags(char *flagstr, uint8_t *flagval)
490 {
491         char *invalflag;
492
493         if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
494                 return 0;
495
496         if (strstr(flagstr, "0x")) { /* hex flag value */
497                 *flagval = strtol(flagstr, &invalflag, 16);
498                 if (strlen(invalflag)) {
499                         printf("%s: Invalid flags: %s\n", __func__, flagstr);
500                         return 1;
501                 }
502         } else
503                 *flagval = ace_flag_value(flagstr);
504
505         if (!*flagval || (*flagval & ~VFLAGS)) {
506                 printf("%s: Invalid flag %s and value: 0x%x\n",
507                         __func__, flagstr, *flagval);
508                 return 1;
509         }
510
511         return 0;
512 }
513
514 static uint32_t
515 ace_mask_value(char *maskstr)
516 {
517         int i, len;
518         uint32_t maskval = 0x0;
519         char *lmask;
520
521         if (!strcmp(maskstr, "FULL"))
522                 return FULL_CONTROL;
523         else if (!strcmp(maskstr, "CHANGE"))
524                 return CHANGE;
525         else if (!strcmp(maskstr, "D"))
526                 return DELETE;
527         else if (!strcmp(maskstr, "READ"))
528                 return EREAD;
529         else {
530                 len = strlen(maskstr);
531                 lmask = maskstr;
532                 for (i = 0; i < len; ++i, ++lmask) {
533                         if (*lmask == 'R')
534                                 maskval |= EREAD;
535                         else if (*lmask == 'W')
536                                 maskval |= EWRITE;
537                         else if (*lmask == 'X')
538                                 maskval |= EXEC;
539                         else if (*lmask == 'D')
540                                 maskval |= DELETE;
541                         else if (*lmask == 'P')
542                                 maskval |= WRITE_DAC;
543                         else if (*lmask == 'O')
544                                 maskval |= WRITE_OWNER;
545                         else
546                                 return 0;
547                 }
548                 return maskval;
549         }
550
551         return 0;
552 }
553
554 static int
555 verify_ace_mask(char *maskstr, uint32_t *maskval)
556 {
557         char *invalflag;
558
559         if (strstr(maskstr, "0x") || !strcmp(maskstr, "DELDHLD")) {
560                 *maskval = strtol(maskstr, &invalflag, 16);
561                 if (!invalflag) {
562                         printf("%s: Invalid mask: %s\n", __func__, maskstr);
563                         return 1;
564                 }
565         } else
566                 *maskval = ace_mask_value(maskstr);
567
568         if (!*maskval) {
569                 printf("%s: Invalid mask %s and value: 0x%x\n",
570                         __func__, maskstr, *maskval);
571                 return 1;
572         }
573
574         return 0;
575 }
576
577 static struct cifs_ace **
578 build_cmdline_aces(char **arrptr, int numcaces)
579 {
580         int i;
581         char *acesid, *acetype, *aceflag, *acemask;
582         struct cifs_ace **cacesptr;
583
584         cacesptr = calloc(numcaces, sizeof(struct cifs_aces *));
585         if (!cacesptr) {
586                 printf("%s: Error %d allocating ACE array", __func__, errno);
587                 return NULL;
588         }
589
590         for (i = 0; i < numcaces; ++i) {
591                 acesid = strtok(arrptr[i], ":");
592                 acetype = strtok(NULL, "/");
593                 aceflag = strtok(NULL, "/");
594                 acemask = strtok(NULL, "/");
595
596                 if (!acesid || !acetype || !aceflag || !acemask) {
597                         printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
598                         goto build_cmdline_aces_ret;
599                 }
600
601                 cacesptr[i] = malloc(sizeof(struct cifs_ace));
602                 if (!cacesptr[i]) {
603                         printf("%s: ACE alloc error %d\n", __func__, errno);
604                         goto build_cmdline_aces_ret;
605                 }
606
607                 if (verify_ace_sid(acesid, &cacesptr[i]->sid)) {
608                         printf("%s: Invalid SID: %s\n", __func__, arrptr[i]);
609                         goto build_cmdline_aces_ret;
610                 }
611
612                 if (verify_ace_type(acetype, &cacesptr[i]->type)) {
613                         printf("%s: Invalid ACE type: %s\n",
614                                         __func__, arrptr[i]);
615                         goto build_cmdline_aces_ret;
616                 }
617
618                 if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
619                         printf("%s: Invalid ACE flag: %s\n",
620                                 __func__, arrptr[i]);
621                         goto build_cmdline_aces_ret;
622                 }
623
624                 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
625                         printf("%s: Invalid ACE mask: %s\n",
626                                 __func__, arrptr[i]);
627                         goto build_cmdline_aces_ret;
628                 }
629
630                 cacesptr[i]->size = 1 + 1 + 2 + 4 + 1 + 1 + 6 +
631                                 (cacesptr[i]->sid.num_subauth * 4);
632         }
633         return cacesptr;
634
635 build_cmdline_aces_ret:
636         for (i = 0; i < numcaces; ++i)
637                 free(cacesptr[i]);
638         free(cacesptr);
639         return NULL;
640 }
641
642 static char **
643 parse_cmdline_aces(char *acelist, int numcaces)
644 {
645         int i = 0, len;
646         char *acestr, *vacestr, **arrptr = NULL;
647
648         arrptr = (char **)malloc(numcaces * sizeof(char *));
649         if (!arrptr) {
650                 printf("%s: Unable to allocate char array\n", __func__);
651                 return NULL;
652         }
653
654         while (i < numcaces) {
655                 acestr = strtok(acelist, ","); /* everything before , */
656                 if (!acestr)
657                         goto parse_cmdline_aces_err;
658
659                 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
660                 if (!vacestr)
661                         goto parse_cmdline_aces_err;
662                 vacestr += 4; /* skip past "ACL:" */
663                 if (*vacestr) {
664                         arrptr[i] = vacestr;
665                         ++i;
666                 }
667                 acelist = NULL;
668         }
669         return arrptr;
670
671 parse_cmdline_aces_err:
672         printf("%s: Error parsing ACEs\n", __func__);
673         free(arrptr);
674         return NULL;
675 }
676
677 /* How many aces were provided on the command-line? Count the commas. */
678 static unsigned int
679 get_numcaces(const char *aces)
680 {
681         int i, len;
682         unsigned int num = 1;
683         const char *current;
684
685         current = aces;
686         while((current = strchr(current, ',')))
687                 ++num;
688
689         return num;
690 }
691
692 static int
693 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
694                 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
695                 struct cifs_ace **cacesptr, int numcaces,
696                 enum setcifsacl_actions maction)
697 {
698         int rc = 1;
699
700         switch (maction) {
701         case ActDelete:
702                 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
703                                 numfaces, cacesptr, numcaces);
704                 break;
705         case ActModify:
706                 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
707                                 numfaces, cacesptr, numcaces);
708                 break;
709         case ActAdd:
710                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
711                                 numfaces, cacesptr, numcaces);
712                 break;
713         case ActSet:
714                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
715                 break;
716         default:
717                 printf("%s: Invalid action: %d\n", __func__, maction);
718                 break;
719         }
720
721         return rc;
722 }
723
724 static void
725 setcifsacl_usage(void)
726 {
727         fprintf(stderr,
728         "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
729                 prog);
730         fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
731         fprintf(stderr, "Valid options:\n");
732         fprintf(stderr, "\t-v   Version of the program\n");
733         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
734         fprintf(stderr,
735         "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
736         fprintf(stderr, "\n");
737         fprintf(stderr,
738         "\t-D   Delete ACE(s), separated by a comma, from an ACL\n");
739         fprintf(stderr,
740         "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
741         fprintf(stderr, "\n");
742         fprintf(stderr,
743         "\t-M   Modify ACE(s), separated by a comma, in an ACL\n");
744         fprintf(stderr,
745         "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
746         fprintf(stderr,
747         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
748         fprintf(stderr,
749         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
750         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
751 }
752
753 int
754 main(const int argc, char *const argv[])
755 {
756         int i, rc, c, numcaces, numfaces;
757         enum setcifsacl_actions maction = ActUnknown;
758         ssize_t attrlen, bufsize = BUFSIZE;
759         char *ace_list, *filename, *attrval, **arrptr = NULL;
760         struct cifs_ctrl_acl *daclptr = NULL;
761         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
762         struct cifs_ntsd *ntsdptr = NULL;
763
764         prog = basename(argv[0]);
765
766         openlog(prog, 0, LOG_DAEMON);
767
768         c = getopt(argc, argv, "hvD:M:a:S:");
769         switch (c) {
770         case 'D':
771                 maction = ActDelete;
772                 ace_list = optarg;
773                 break;
774         case 'M':
775                 maction = ActModify;
776                 ace_list = optarg;
777                 break;
778         case 'a':
779                 maction = ActAdd;
780                 ace_list = optarg;
781                 break;
782         case 'S':
783                 maction = ActSet;
784                 ace_list = optarg;
785                 break;
786         case 'h':
787                 setcifsacl_usage();
788                 return 0;
789         case 'v':
790                 printf("Version: %s\n", VERSION);
791                 return 0;
792         default:
793                 setcifsacl_usage();
794                 return -1;
795         }
796
797         /* We expect 1 argument in addition to the option */
798         if (argc != 4) {
799                 setcifsacl_usage();
800                 return -1;
801         }
802         filename = argv[3];
803
804         if (!ace_list) {
805                 printf("%s: No valid ACEs specified\n", __func__);
806                 return -1;
807         }
808
809         numcaces = get_numcaces(ace_list);
810
811         arrptr = parse_cmdline_aces(ace_list, numcaces);
812         if (!arrptr)
813                 goto setcifsacl_numcaces_ret;
814
815         cacesptr = build_cmdline_aces(arrptr, numcaces);
816         if (!cacesptr)
817                 goto setcifsacl_cmdlineparse_ret;
818
819 cifsacl:
820         if (bufsize >= XATTR_SIZE_MAX) {
821                 printf("%s: Buffer size %ld exceeds max size of %d\n",
822                                 __func__, bufsize, XATTR_SIZE_MAX);
823                 goto setcifsacl_cmdlineverify_ret;
824         }
825
826         attrval = malloc(bufsize * sizeof(char));
827         if (!attrval) {
828                 printf("error allocating memory for attribute value buffer\n");
829                 goto setcifsacl_cmdlineverify_ret;
830         }
831
832         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
833         if (attrlen == -1) {
834                 if (errno == ERANGE) {
835                         free(attrval);
836                         bufsize += BUFSIZE;
837                         goto cifsacl;
838                 } else {
839                         printf("getxattr error: %d\n", errno);
840                         goto setcifsacl_getx_ret;
841                 }
842         }
843
844         numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
845         if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
846                 printf("%s: Empty DACL\n", __func__);
847                 goto setcifsacl_facenum_ret;
848         }
849
850         facesptr = build_fetched_aces((char *)daclptr, numfaces);
851         if (!facesptr)
852                 goto setcifsacl_facenum_ret;
853
854         bufsize = 0;
855         rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
856                 facesptr, numfaces, cacesptr, numcaces, maction);
857         if (rc)
858                 goto setcifsacl_action_ret;
859
860         attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
861         if (attrlen == -1)
862                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
863         goto setcifsacl_facenum_ret;
864
865         return 0;
866
867 setcifsacl_action_ret:
868         free(ntsdptr);
869
870 setcifsacl_facenum_ret:
871         for (i = 0; i < numfaces; ++i)
872                 free(facesptr[i]);
873         free(facesptr);
874
875 setcifsacl_getx_ret:
876         free(attrval);
877
878 setcifsacl_cmdlineverify_ret:
879         for (i = 0; i < numcaces; ++i)
880                 free(cacesptr[i]);
881         free(cacesptr);
882
883 setcifsacl_cmdlineparse_ret:
884         free(arrptr);
885
886 setcifsacl_numcaces_ret:
887         return -1;
888 }