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