setcifsacl: fix overrun of subauths array when copying SIDs
[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 = "setcifsacl";
43
44 static void
45 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
46                 int numaces, int acessize)
47 {
48         int i;
49
50         int osidsoffset, gsidsoffset, dacloffset;
51         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
52         struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
53         struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
54
55         /* copy security descriptor control portion */
56         osidsoffset = htole32(pntsd->osidoffset);
57         gsidsoffset = htole32(pntsd->gsidoffset);
58         dacloffset = htole32(pntsd->dacloffset);
59
60         pnntsd->revision = pntsd->revision;
61         pnntsd->type = pntsd->type;
62         pnntsd->osidoffset = pntsd->osidoffset;
63         pnntsd->gsidoffset = pntsd->gsidoffset;
64         pnntsd->dacloffset = pntsd->dacloffset;
65
66         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
67         ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
68
69         ndacl_ptr->revision = dacl_ptr->revision;
70         ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
71         ndacl_ptr->num_aces = htole32(numaces);
72
73         /* copy owner sid */
74         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
75         nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
76
77         nowner_sid_ptr->revision = owner_sid_ptr->revision;
78         nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
79         for (i = 0; i < NUM_AUTHS; i++)
80                 nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
81         for (i = 0; i < owner_sid_ptr->num_subauth; i++)
82                 nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
83
84         /* copy group sid */
85         group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
86         ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
87
88         ngroup_sid_ptr->revision = group_sid_ptr->revision;
89         ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
90         for (i = 0; i < NUM_AUTHS; i++)
91                 ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
92         for (i = 0; i < group_sid_ptr->num_subauth; i++)
93                 ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
94
95         return;
96 }
97
98 static int
99 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
100 {
101         int i;
102
103         dace->type = sace->type;
104         dace->flags = sace->flags;
105         dace->access_req = htole32(sace->access_req);
106
107         dace->sid.revision = sace->sid.revision;
108         dace->sid.num_subauth = sace->sid.num_subauth;
109         for (i = 0; i < NUM_AUTHS; i++)
110                 dace->sid.authority[i] = sace->sid.authority[i];
111         for (i = 0; i < sace->sid.num_subauth; i++)
112                 dace->sid.sub_auth[i] = sace->sid.sub_auth[i];
113
114         dace->size = htole16(sace->size);
115
116         return 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 != htole32(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         if (pntsd == NULL)
333                 return 0;
334
335         dacloffset = le32toh(pntsd->dacloffset);
336         if (!dacloffset)
337                 return 0;
338         else {
339                 ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
340                 /* validate that we do not go past end of acl */
341                 if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
342                         numfaces = le32toh(ldaclptr->num_aces);
343                         *daclptr = ldaclptr;
344                 }
345         }
346
347         return numfaces;
348 }
349
350 static struct cifs_ace **
351 build_fetched_aces(char *daclptr, int numfaces)
352 {
353         int i, j, rc = 0, acl_size;
354         char *acl_base;
355         struct cifs_ace *pace, **facesptr;
356
357         facesptr = (struct cifs_ace **)malloc(numfaces *
358                                         sizeof(struct cifs_aces *));
359         if (!facesptr) {
360                 printf("%s: Error %d allocating ACE array",
361                                 __func__, errno);
362                 rc = errno;
363         }
364
365         acl_base = daclptr;
366         acl_size = sizeof(struct cifs_ctrl_acl);
367         for (i = 0; i < numfaces; ++i) {
368                 facesptr[i] = malloc(sizeof(struct cifs_ace));
369                 if (!facesptr[i]) {
370                         rc = errno;
371                         goto build_fetched_aces_ret;
372                 }
373                 pace = (struct cifs_ace *) (acl_base + acl_size);
374                 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
375                 acl_base = (char *)pace;
376                 acl_size = le16toh(pace->size);
377         }
378
379 build_fetched_aces_ret:
380         if (rc) {
381                 printf("%s: Invalid fetched ace\n", __func__);
382                 if (i) {
383                         for (j = i; j >= 0; --j)
384                                 free(facesptr[j]);
385                 }
386                 free(facesptr);
387         }
388         return facesptr;
389 }
390
391 static int
392 verify_ace_sid(char *sidstr, struct cifs_sid *sid)
393 {
394         int rc;
395         char *lstr;
396         struct passwd *winpswdptr;
397
398         lstr = strstr(sidstr, "\\"); /* everything before | */
399         if (lstr)
400                 ++lstr;
401         else
402                 lstr = sidstr;
403
404         /* Check if it is a (raw) SID (string) */
405         rc = wbcStringToSid(lstr, (struct wbcDomainSid *)sid);
406         if (!rc)
407                 return rc;
408
409         /* Check if it a name (string) which can be resolved to a SID*/
410         rc = wbcGetpwnam(lstr, &winpswdptr);
411         if (rc) {
412                 printf("%s: Invalid user name: %s\n", __func__, sidstr);
413                 return rc;
414         }
415         rc = wbcUidToSid(winpswdptr->pw_uid, (struct wbcDomainSid *)sid);
416         if (rc) {
417                 printf("%s: Invalid user: %s\n", __func__, sidstr);
418                 return rc;
419         }
420
421         return 0;
422 }
423
424 static int
425 verify_ace_type(char *typestr, uint8_t *typeval)
426 {
427         int i, len;
428         char *invaltype;
429
430         if (strstr(typestr, "0x")) { /* hex type value */
431                 *typeval = strtol(typestr, &invaltype, 16);
432                 if (!strlen(invaltype)) {
433                         if (*typeval != ACCESS_ALLOWED &&
434                                 *typeval != ACCESS_DENIED &&
435                                 *typeval != ACCESS_ALLOWED_OBJECT &&
436                                 *typeval != ACCESS_DENIED_OBJECT) {
437                                         printf("%s: Invalid type: %s\n",
438                                                 __func__, typestr);
439                                         return 1;
440                         }
441                         return 0;
442                 }
443         }
444
445         len = strlen(typestr);
446         for (i = 0; i < len; ++i)
447                 *(typestr + i) = toupper(*(typestr + i));
448         if (!strcmp(typestr, "ALLOWED"))
449                 *typeval = 0x0;
450         else if (!strcmp(typestr, "DENIED"))
451                 *typeval = 0x1;
452         else if (!strcmp(typestr, "ALLOWED_OBJECT"))
453                 *typeval = 0x5;
454         else if (!strcmp(typestr, "DENIED_OBJECT"))
455                 *typeval = 0x6;
456         else {
457                 printf("%s: Invalid type: %s\n", __func__, typestr);
458                 return 1;
459         }
460
461         return 0;
462 }
463
464 static uint8_t
465 ace_flag_value(char *flagstr)
466 {
467         uint8_t flagval = 0x0;
468         char *iflag;
469
470         iflag = strtok(flagstr, "|"); /* everything before | */
471         while (iflag) {
472                 if (!strcmp(iflag, "OI"))
473                         flagval += 0x1;
474                 else if (!strcmp(iflag, "CI"))
475                         flagval += 0x2;
476                 else if (!strcmp(iflag, "NP"))
477                         flagval += 0x4;
478                 else if (!strcmp(iflag, "IO"))
479                         flagval += 0x8;
480                 else if (!strcmp(iflag, "I"))
481                         flagval += 0x10;
482                 else
483                         return 0x0; /* Invalid flag */
484                 iflag = strtok(NULL, "|"); /* everything before | */
485         }
486
487         return flagval;
488 }
489
490 static int
491 verify_ace_flags(char *flagstr, uint8_t *flagval)
492 {
493         char *invalflag;
494
495         if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
496                 return 0;
497
498         if (strstr(flagstr, "0x")) { /* hex flag value */
499                 *flagval = strtol(flagstr, &invalflag, 16);
500                 if (strlen(invalflag)) {
501                         printf("%s: Invalid flags: %s\n", __func__, flagstr);
502                         return 1;
503                 }
504         } else
505                 *flagval = ace_flag_value(flagstr);
506
507         if (!*flagval || (*flagval & ~VFLAGS)) {
508                 printf("%s: Invalid flag %s and value: 0x%x\n",
509                         __func__, flagstr, *flagval);
510                 return 1;
511         }
512
513         return 0;
514 }
515
516 static uint32_t
517 ace_mask_value(char *maskstr)
518 {
519         int i, len;
520         uint32_t maskval = 0x0;
521         char *lmask;
522
523         if (!strcmp(maskstr, "FULL"))
524                 return FULL_CONTROL;
525         else if (!strcmp(maskstr, "CHANGE"))
526                 return CHANGE;
527         else if (!strcmp(maskstr, "D"))
528                 return DELETE;
529         else if (!strcmp(maskstr, "READ"))
530                 return EREAD;
531         else {
532                 len = strlen(maskstr);
533                 lmask = maskstr;
534                 for (i = 0; i < len; ++i, ++lmask) {
535                         if (*lmask == 'R')
536                                 maskval |= EREAD;
537                         else if (*lmask == 'W')
538                                 maskval |= EWRITE;
539                         else if (*lmask == 'X')
540                                 maskval |= EXEC;
541                         else if (*lmask == 'D')
542                                 maskval |= DELETE;
543                         else if (*lmask == 'P')
544                                 maskval |= WRITE_DAC;
545                         else if (*lmask == 'O')
546                                 maskval |= WRITE_OWNER;
547                         else
548                                 return 0;
549                 }
550                 return maskval;
551         }
552
553         return 0;
554 }
555
556 static int
557 verify_ace_mask(char *maskstr, uint32_t *maskval)
558 {
559         char *invalflag;
560
561         if (strstr(maskstr, "0x") || !strcmp(maskstr, "DELDHLD")) {
562                 *maskval = strtol(maskstr, &invalflag, 16);
563                 if (!invalflag) {
564                         printf("%s: Invalid mask: %s\n", __func__, maskstr);
565                         return 1;
566                 }
567         } else
568                 *maskval = ace_mask_value(maskstr);
569
570         if (!*maskval) {
571                 printf("%s: Invalid mask %s and value: 0x%x\n",
572                         __func__, maskstr, *maskval);
573                 return 1;
574         }
575
576         return 0;
577 }
578
579 static struct cifs_ace **
580 build_cmdline_aces(char **arrptr, int numcaces)
581 {
582         int i;
583         char *acesid, *acetype, *aceflag, *acemask;
584         struct cifs_ace **cacesptr;
585
586         cacesptr = (struct cifs_ace **)malloc(numcaces *
587                                 sizeof(struct cifs_aces *));
588         if (!cacesptr) {
589                 printf("%s: Error %d allocating ACE array", __func__, errno);
590                 return NULL;
591         }
592
593         for (i = 0; i < numcaces; ++i) {
594                 acesid = strtok(arrptr[i], ":");
595                 acetype = strtok(NULL, "/");
596                 aceflag = strtok(NULL, "/");
597                 acemask = strtok(NULL, "/");
598
599                 if (!acesid || !acetype || !aceflag || !acemask) {
600                         printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
601                         goto build_cmdline_aces_ret;
602                 }
603
604                 cacesptr[i] = malloc(sizeof(struct cifs_ace));
605                 if (!cacesptr[i]) {
606                         printf("%s: ACE alloc error %d\n", __func__, errno);
607                         goto build_cmdline_aces_ret;
608                 }
609
610                 if (verify_ace_sid(acesid, &cacesptr[i]->sid)) {
611                         printf("%s: Invalid SID: %s\n", __func__, arrptr[i]);
612                         goto build_cmdline_aces_ret;
613                 }
614
615                 if (verify_ace_type(acetype, &cacesptr[i]->type)) {
616                         printf("%s: Invalid ACE type: %s\n",
617                                         __func__, arrptr[i]);
618                         goto build_cmdline_aces_ret;
619                 }
620
621                 if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
622                         printf("%s: Invalid ACE flag: %s\n",
623                                 __func__, arrptr[i]);
624                         goto build_cmdline_aces_ret;
625                 }
626
627                 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
628                         printf("%s: Invalid ACE mask: %s\n",
629                                 __func__, arrptr[i]);
630                         goto build_cmdline_aces_ret;
631                 }
632
633                 cacesptr[i]->size = 1 + 1 + 2 + 4 + 1 + 1 + 6 +
634                                 (cacesptr[i]->sid.num_subauth * 4);
635         }
636         return cacesptr;
637
638 build_cmdline_aces_ret:
639         for (; i >= 0; --i)
640                 free(cacesptr[i]);
641         free(cacesptr);
642         return NULL;
643 }
644
645 static char **
646 parse_cmdline_aces(char *optarg, int numcaces)
647 {
648         int i = 0, len;
649         char *acestr, *vacestr, **arrptr = NULL;
650
651         errno = EINVAL;
652         arrptr = (char **)malloc(numcaces * sizeof(char *));
653         if (!arrptr) {
654                 printf("%s: Error %d allocating char array\n", __func__, errno);
655                 return NULL;
656         }
657
658         while (i < numcaces) {
659                 acestr = strtok(optarg, ","); /* everything before , */
660                 if (acestr) {
661                         vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
662                         if (vacestr) {
663                                 vacestr = strchr(vacestr, ':');
664                                 if (vacestr)
665                                         ++vacestr; /* go past : */
666                                 if (vacestr) {
667                                         len = strlen(vacestr);
668                                         arrptr[i] = malloc(len + 1);
669                                         if (!arrptr[i])
670                                                 goto parse_cmdline_aces_ret;
671                                         strcpy(arrptr[i], vacestr);
672                                         ++i;
673                                 } else
674                                         goto parse_cmdline_aces_ret;
675                         } else
676                                 goto parse_cmdline_aces_ret;
677                 } else
678                         goto parse_cmdline_aces_ret;
679                 optarg = NULL;
680         }
681         errno = 0;
682         return arrptr;
683
684 parse_cmdline_aces_ret:
685         printf("%s: Error %d parsing ACEs\n", __func__, errno);
686         for (;  i >= 0; --i)
687                 free(arrptr[i]);
688         free(arrptr);
689         return NULL;
690 }
691
692 static unsigned int
693 get_numcaces(const char *optarg)
694 {
695         int i, len;
696         unsigned int numcaces = 1;
697
698         if (!optarg)
699                 return 0;
700
701         len = strlen(optarg);
702         for (i = 0; i < len; ++i) {
703                 if (*(optarg + i) == ',')
704                         ++numcaces;
705         }
706
707         return numcaces;
708 }
709
710 static int
711 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
712                 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
713                 struct cifs_ace **cacesptr, int numcaces,
714                 int maction)
715 {
716         int rc = 1;
717
718         switch (maction) {
719         case 0:
720                 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
721                                 numfaces, cacesptr, numcaces);
722                 break;
723         case 1:
724                 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
725                                 numfaces, cacesptr, numcaces);
726                 break;
727         case 2:
728                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
729                                 numfaces, cacesptr, numcaces);
730                 break;
731         case 3:
732                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
733                 break;
734         default:
735                 printf("%s: Invalid action: %d\n", __func__, maction);
736                 break;
737         }
738
739         return rc;
740 }
741
742 static void
743 setcifsacl_usage(void)
744 {
745         fprintf(stderr,
746         "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
747                 prog);
748         fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
749         fprintf(stderr, "Valid options:\n");
750         fprintf(stderr, "\t-v   Version of the program\n");
751         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
752         fprintf(stderr,
753         "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
754         fprintf(stderr, "\n");
755         fprintf(stderr,
756         "\t-D   Delete ACE(s), separated by a comma, from an ACL\n");
757         fprintf(stderr,
758         "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
759         fprintf(stderr, "\n");
760         fprintf(stderr,
761         "\t-M   Modify ACE(s), separated by a comma, in an ACL\n");
762         fprintf(stderr,
763         "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
764         fprintf(stderr,
765         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
766         fprintf(stderr,
767         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
768         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
769 }
770
771 int
772 main(const int argc, char *const argv[])
773 {
774         int i, rc, c, numcaces, numfaces, maction = -1;
775         ssize_t attrlen, bufsize = BUFSIZE;
776         char *filename, *attrval, **arrptr = NULL;
777         struct cifs_ctrl_acl *daclptr = NULL;
778         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
779         struct cifs_ntsd *ntsdptr = NULL;
780
781         openlog(prog, 0, LOG_DAEMON);
782
783         c = getopt(argc, argv, "v:D:M:a:S:?");
784         switch (c) {
785         case 'v':
786                 printf("Version: %s\n", VERSION);
787                 goto out;
788         case 'D':
789                 maction = 0;
790                 break;
791         case 'M':
792                 maction = 1;
793                 break;
794         case 'a':
795                 maction = 2;
796                 break;
797         case 'S':
798                 maction = 3;
799                 break;
800         case '?':
801                 setcifsacl_usage();
802                 return 0;
803         default:
804                 break;
805         }
806
807         if (argc != 4) {
808                 setcifsacl_usage();
809                 return -1;
810         }
811         filename = argv[3];
812
813         numcaces = get_numcaces(optarg);
814         if (!numcaces) {
815                 printf("%s: No valid ACEs specified\n", __func__);
816                 return -1;
817         }
818
819         arrptr = parse_cmdline_aces(optarg, numcaces);
820         if (!arrptr)
821                 goto setcifsacl_numcaces_ret;
822
823         cacesptr = build_cmdline_aces(arrptr, numcaces);
824         if (!cacesptr)
825                 goto setcifsacl_cmdlineparse_ret;
826
827 cifsacl:
828         if (bufsize >= XATTR_SIZE_MAX) {
829                 printf("%s: Buffer size %ld exceeds max size of %d\n",
830                                 __func__, bufsize, XATTR_SIZE_MAX);
831                 goto setcifsacl_cmdlineverify_ret;
832         }
833
834         attrval = malloc(bufsize * sizeof(char));
835         if (!attrval) {
836                 printf("error allocating memory for attribute value buffer\n");
837                 goto setcifsacl_cmdlineverify_ret;
838         }
839
840         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
841         if (attrlen == -1) {
842                 if (errno == ERANGE) {
843                         free(attrval);
844                         bufsize += BUFSIZE;
845                         goto cifsacl;
846                 } else {
847                         printf("getxattr error: %d\n", errno);
848                         goto setcifsacl_getx_ret;
849                 }
850         }
851
852         numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
853         if (!numfaces && maction != 2) { /* if we are not adding aces */
854                 printf("%s: Empty DACL\n", __func__);
855                 goto setcifsacl_facenum_ret;
856         }
857
858         facesptr = build_fetched_aces((char *)daclptr, numfaces);
859         if (!facesptr)
860                 goto setcifsacl_facenum_ret;
861
862         bufsize = 0;
863         rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
864                 facesptr, numfaces, cacesptr, numcaces, maction);
865         if (rc)
866                 goto setcifsacl_action_ret;
867
868         attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
869         if (attrlen == -1)
870                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
871         goto setcifsacl_facenum_ret;
872
873 out:
874         return 0;
875
876 setcifsacl_action_ret:
877         free(ntsdptr);
878
879 setcifsacl_facenum_ret:
880         for (i = 0; i < numfaces; ++i)
881                 free(facesptr[i]);
882         free(facesptr);
883
884 setcifsacl_getx_ret:
885         free(attrval);
886
887 setcifsacl_cmdlineverify_ret:
888         for (i = 0; i < numcaces; ++i)
889                 free(cacesptr[i]);
890         free(cacesptr);
891
892 setcifsacl_cmdlineparse_ret:
893         for (i = 0; i < numcaces; ++i)
894                 free(arrptr[i]);
895         free(arrptr);
896
897 setcifsacl_numcaces_ret:
898         return -1;
899 }