c0ecd41f593494db93cb082e3c5451bdfb18e425
[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 /*
24  * This utility modifies various components of the security descriptor. These
25  * actions require different permissions and different SMB protocol-level flags.
26  * The user needs to make sure the share is mounted using the user credentials
27  * for the user who has appropriate permissions and privileges. The kernel
28  * CIFS client knows which flags to use based on the extended attribute name:
29  * - system.cifs_acl - set dacl only
30  * - system.cifs_ndst - set dacl and owner info
31  * - system.cifs_ntsd_full - set dacl, owner, and sacl
32  *
33  * For simplicity, the utility modifies one component of the descriptor:
34  * owner sid, group sid, DACL, or SACL. The rest of the descriptor is unchanged.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif /* HAVE_CONFIG_H */
40
41 #include <string.h>
42 #include <getopt.h>
43 #include <stdint.h>
44 #include <stdbool.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <limits.h>
50 #include <ctype.h>
51 #include <sys/xattr.h>
52
53 #include "cifsacl.h"
54 #include "idmap_plugin.h"
55
56 enum setcifsacl_actions {
57         ActUnknown = -1,
58         ActDelete,
59         ActModify,
60         ActAdd,
61         ActSetAcl,
62         ActSetOwner,
63         ActSetGroup,
64         ActSetSacl,
65         ActAddReorder
66 };
67
68 static void *plugin_handle;
69 static bool plugin_loaded;
70
71 static int
72 copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src)
73 {
74         int i, size = 0;
75
76         dst->revision = src->revision;
77         size += sizeof(uint8_t);
78
79         dst->num_subauth = src->num_subauth;
80         size += sizeof(uint8_t);
81
82         for (i = 0; i < NUM_AUTHS; i++)
83                 dst->authority[i] = src->authority[i];
84         size += (sizeof(uint8_t) * NUM_AUTHS);
85
86         for (i = 0; i < src->num_subauth; i++)
87                 dst->sub_auth[i] = src->sub_auth[i];
88         size += (sizeof(uint32_t) * src->num_subauth);
89
90         return size;
91 }
92
93 static int
94 get_cifs_sid_size(const struct cifs_sid *sid)
95 {
96         return (2 * sizeof(uint8_t) +
97                 sizeof(uint8_t) * NUM_AUTHS +
98                 sizeof(uint32_t) * sid->num_subauth);
99 }
100
101 /*
102  * This function takes a pointer of the fetched (original) descriptor, and
103  * it returns the offset of the ACL in the new descriptor.
104  *
105  * If the original descriptor does not have an ACL, the corresponding offset
106  * is 0, and we need to determine where to place the ACL in the new descriptor.
107  * If SACL offset is zero, and there is DACL (dacloffset is not 0), then we will
108  * put SACL after DACL. If the DACL is not present either, we do not know if the
109  * ACLs should go before or after the owner and group SIDs (see below), and so
110  * we will use the offset right past the group SID.
111  * Similarly, if DACL offset is zero, we will use the offset the past the end
112  * of group SID.
113  * @todo: need to add a command-line argument to know if this is
114  *        Azure-style descriptor or a regular-style descriptor
115  */
116 static int get_aces_offset(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) {
117         int dacloffset, sacloffset, acesoffset;
118
119         switch(ace_kind) {
120         case ACE_KIND_SACL:
121                 sacloffset = le32toh(pntsd->sacloffset);
122                 if (sacloffset) {
123                         acesoffset = sacloffset + sizeof(struct cifs_ctrl_acl);
124                 } else {
125                         dacloffset = le32toh(pntsd->dacloffset);
126                         if (dacloffset) {
127                                 struct cifs_ctrl_acl *dacl_ptr =
128                                         (struct cifs_ctrl_acl *)((char *)pntsd +
129                                                         dacloffset);
130                                 acesoffset = dacloffset +
131                                         le16toh(dacl_ptr->size) +
132                                         sizeof(struct cifs_ctrl_acl);
133                         } else {
134                                 int gsidoffset = le32toh(pntsd->gsidoffset);
135                                 struct cifs_sid *group_sid_ptr =
136                                         (struct cifs_sid *)((char *)pntsd +
137                                                         gsidoffset);
138                                 int gsidsize = get_cifs_sid_size(group_sid_ptr);
139                                 acesoffset = gsidoffset + gsidsize +
140                                         sizeof(struct cifs_ctrl_acl);
141                         }
142                 }
143                 break;
144         case ACE_KIND_DACL:
145         default:
146                 dacloffset = le32toh(pntsd->dacloffset);
147                 if (dacloffset) {
148                         acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
149                 } else {
150                         int gsidoffset = le32toh(pntsd->gsidoffset);
151                         struct cifs_sid *group_sid_ptr =
152                                 (struct cifs_sid *)((char *)pntsd +
153                                                 gsidoffset);
154                         int gsidsize = get_cifs_sid_size(group_sid_ptr);
155                         acesoffset = gsidoffset + gsidsize +
156                                 sizeof(struct cifs_ctrl_acl);
157                 }
158                 break;
159         }
160         return acesoffset;
161 }
162
163 int get_aces_size(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) {
164         int acloffset, size;
165         struct cifs_ctrl_acl *acl_ptr;
166
167         switch(ace_kind) {
168         case ACE_KIND_SACL:
169                 acloffset = le32toh(pntsd->sacloffset);
170                 break;
171         case ACE_KIND_DACL:
172         default:
173                 acloffset = le32toh(pntsd->dacloffset);
174         }
175         if (acloffset) {
176                 acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + acloffset);
177                 size = le16toh(acl_ptr->size);
178         } else {
179                 size = 0;
180         }
181         return size;
182 }
183
184 uint16_t get_acl_revision(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) {
185         struct cifs_ctrl_acl *acl_ptr;
186         int acloffset;
187         switch(ace_kind) {
188         case ACE_KIND_SACL:
189                 acloffset = le32toh(pntsd->sacloffset);
190                 if (acloffset) {
191                         acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd +
192                                                            acloffset);
193                         return acl_ptr->revision;
194                 }
195         /* intentional fall through */
196         case ACE_KIND_DACL:
197         default:
198                 acloffset = le32toh(pntsd->dacloffset);
199                 if (acloffset) {
200                         acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd +
201                                                            acloffset);
202                         return acl_ptr->revision;
203                 } else {
204                         return DEFAULT_ACL_REVISION;
205                 }
206                 break;
207         }
208 }
209
210 /*
211  * The actual changes to the ACL specified in ace_kind are performed by the
212  * caller of this function; this function copies/backfills the remaining
213  * relevant compoenents of the security descriptor that remain unchanged.
214  */
215 static ssize_t
216 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
217                 int numaces, int acessize, ace_kinds ace_kind)
218 {
219         int size, osidsoffset, gsidsoffset, acloffset, dacloffset;
220         ssize_t bufsize;
221         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
222         struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
223         struct cifs_ctrl_acl *nacl_ptr, *dacl_ptr;
224         char *ndacl_ptr;
225
226         /* copy security descriptor control portion */
227         osidsoffset = le32toh(pntsd->osidoffset);
228         gsidsoffset = le32toh(pntsd->gsidoffset);
229
230         size = sizeof(struct cifs_ntsd);
231         pnntsd->revision = pntsd->revision;
232         pnntsd->type = pntsd->type;
233         pnntsd->osidoffset = pntsd->osidoffset;
234         pnntsd->gsidoffset = pntsd->gsidoffset;
235         pnntsd->dacloffset = pntsd->dacloffset;
236         bufsize = size;
237
238         /* owner and group SIDs in the original defscriptor */
239         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
240         group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
241
242         /* get the offset of the acl control structure to initialize */
243         acloffset = get_aces_offset(pntsd, ace_kind) - sizeof(struct cifs_ctrl_acl);
244         if (ace_kind == ACE_KIND_SACL) {
245                 /* copy (unchanged) DACL if present, increment bufsize */
246                 dacloffset = le32toh(pntsd->dacloffset);
247                 if (dacloffset) {
248                         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
249                         ndacl_ptr = (char *)pnntsd + dacloffset;
250                         size = sizeof(struct cifs_ctrl_acl) + le16toh(dacl_ptr->size);
251                         memcpy(ndacl_ptr, (char *)dacl_ptr, size);
252                         bufsize += size;
253                 }
254                 /* initialize SACL offset */
255                 pnntsd->sacloffset = acloffset;
256         }
257
258         nacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + acloffset);
259         nacl_ptr->revision = get_acl_revision(pntsd, ace_kind);
260         size = acessize + sizeof(struct cifs_ctrl_acl);
261         nacl_ptr->size = htole16(size);
262         nacl_ptr->num_aces = htole32(numaces);
263         bufsize += size;
264
265         /* copy owner sid */
266
267         /*
268          * some servers like Azure return the owner and group SIDs at end rather
269          * than at the beginning of the ACL so don't want to overwrite the last ACEs
270          */
271         if (acloffset <= osidsoffset) {
272                 /* owners placed at end of ACL */
273                 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + acloffset + size);
274                 osidsoffset = acloffset + size;
275                 pnntsd->osidoffset = htole32(osidsoffset);
276                 size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
277                 bufsize += size;
278                 /* put group SID after owner SID */
279                 ngroup_sid_ptr = (struct cifs_sid *)((char *)nowner_sid_ptr + size);
280                 gsidsoffset = osidsoffset + size;
281                 pnntsd->gsidoffset = htole32(gsidsoffset);
282         } else {
283                 /*
284                  * Most servers put the owner information at the beginning,
285                  * before the ACL
286                  */
287                 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
288                 size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
289                 bufsize += size;
290                 ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
291         }
292
293         /* copy group sid */
294         size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
295         bufsize += size;
296
297         return bufsize;
298 }
299
300 /*
301  * This function does not need to set the SACL-related fields, and this works
302  * fine because the code path calling this function picks the 'system.cifs_ntsd'
303  * attribute name. This name tells Linux CIFS client that SACL is not modified.
304  */
305 static ssize_t
306 copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
307                 struct cifs_sid *sid, int maction)
308 {
309         int size, daclsize;
310         int osidoffset, gsidoffset, dacloffset;
311         int nosidoffset, ngsidoffset, ndacloffset, nsidssize;
312         ssize_t bufsize;
313         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
314         struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
315         struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
316
317         /* copy security descriptor control portion */
318         osidoffset = le32toh(pntsd->osidoffset);
319         gsidoffset = le32toh(pntsd->gsidoffset);
320         dacloffset = le32toh(pntsd->dacloffset);
321         /*
322          * the size of the owner or group sid might be different from the old
323          * one, so the group sid offest might change, and if the owner is
324          * positioned before the DACL, the dacl offset might change as well;
325          * note however, that the owner sid offset does not change
326          */
327         nosidoffset = osidoffset;
328         size = sizeof(struct cifs_ntsd);
329         pnntsd->revision = pntsd->revision;
330         pnntsd->type = pntsd->type;
331         pnntsd->osidoffset = pntsd->osidoffset;
332         bufsize = size;
333
334         /* set the pointers for source sids */
335         if (maction == ActSetOwner) {
336                 owner_sid_ptr = sid;
337                 group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidoffset);
338         }
339         if (maction == ActSetGroup) {
340                 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidoffset);
341                 group_sid_ptr = sid;
342         }
343
344         if (dacloffset) {
345                 dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
346                 daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl);
347         } else {
348                 dacl_ptr = NULL;
349                 daclsize = 0;
350         }
351
352         /* copy owner sid */
353         nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset);
354         size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
355         bufsize += size;
356         nsidssize = size;
357
358         /* copy group sid */
359         ngsidoffset = nosidoffset + size;
360         ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + ngsidoffset);
361         pnntsd->gsidoffset = htole32(ngsidoffset);
362         size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
363         bufsize += size;
364         nsidssize += size;
365
366         /* position the dacl control info as in the fetched descriptor */
367         if (dacloffset) {
368                 if (dacloffset <= osidoffset)
369                         ndacloffset = dacloffset;
370                 else
371                         ndacloffset = nosidoffset + nsidssize;
372                 ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + ndacloffset);
373                 pnntsd->dacloffset = htole32(ndacloffset);
374
375                 /* the DACL control fields do not change */
376                 ndacl_ptr->revision = dacl_ptr->revision;
377                 ndacl_ptr->size = dacl_ptr->size;
378                 ndacl_ptr->num_aces = dacl_ptr->num_aces;
379         } else {
380                 pnntsd->dacloffset = 0;
381         }
382         /*
383          * add DACL size (control portion and the array of aces) to the
384          * buffer size
385          */
386         bufsize += daclsize;
387
388         return bufsize;
389 }
390
391 static int
392 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
393 {
394         dace->type = sace->type;
395         dace->flags = sace->flags;
396         dace->access_req = sace->access_req;
397
398         copy_cifs_sid(&dace->sid, &sace->sid);
399
400         dace->size = sace->size;
401
402         return le16toh(dace->size);
403 }
404
405 static int
406 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
407 {
408         int i;
409
410         if (compflags & COMPSID) {
411                 if (dace->sid.revision != sace->sid.revision)
412                         return 0;
413                 if (dace->sid.num_subauth != sace->sid.num_subauth)
414                         return 0;
415                 for (i = 0; i < NUM_AUTHS; i++) {
416                         if (dace->sid.authority[i] != sace->sid.authority[i])
417                                 return 0;
418                 }
419                 for (i = 0; i < sace->sid.num_subauth; i++) {
420                         if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
421                                 return 0;
422                 }
423         }
424
425         if (compflags & COMPTYPE) {
426                 if (dace->type != sace->type)
427                         return 0;
428         }
429
430         if (compflags & COMPFLAG) {
431                 if (dace->flags != sace->flags)
432                         return 0;
433         }
434
435         if (compflags & COMPMASK) {
436                 if (dace->access_req != sace->access_req)
437                         return 0;
438         }
439
440         return 1;
441 }
442
443 /*
444  * This is somewhat suboptimal, but to keep the code simple, we will still
445  * allocate the ACL control headers for DACL and SACL even thought there is
446  * no corresponding ACL (dacloffset = 0 or sacloffset = 0).
447  * When seetting DACL, we allocate sufficient space for the descriptor control
448  * structure, owner and group sids, and the DACL (ACL control structure and
449  * the aces).
450  * When setting SACL, we allocate sufficient space to copy the above components
451  * plus the SACL to be set (ACL controla and aces).
452  */
453 static int
454 alloc_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
455                 int aces, size_t *acesoffset, ace_kinds ace_kind)
456 {
457         unsigned int size, acessize, bufsize;
458
459         switch(ace_kind) {
460         case ACE_KIND_SACL:
461                 size = sizeof(struct cifs_ntsd) +
462                         2 * sizeof(struct cifs_sid) +
463                         sizeof(struct cifs_ctrl_acl) +
464                         get_aces_size(pntsd, ACE_KIND_DACL) +
465                         sizeof(struct cifs_ctrl_acl);
466                 break;
467         case ACE_KIND_DACL:
468         default:
469                 size = sizeof(struct cifs_ntsd) +
470                         2 * sizeof(struct cifs_sid) +
471                         sizeof(struct cifs_ctrl_acl);
472                 break;
473         }
474
475         *acesoffset = get_aces_offset(pntsd, ace_kind);
476         acessize = aces * sizeof(struct cifs_ace);
477         bufsize = size + acessize;
478         *npntsd = calloc(1, bufsize);
479         if (!*npntsd) {
480                 fprintf(stderr, "%s: Memory allocation failure", __func__);
481                 return errno;
482         }
483
484         return 0;
485 }
486
487 static struct cifs_ace **
488 build_reorder_aces(struct cifs_ace **facesptr, int numfaces)
489 {
490         struct cifs_ace *pace, **allowedacesptr, **deniedacesptr,
491                         **allowedinhacesptr, **deniedinhacesptr, **reorderacesptr;
492         int i, numallowedaces, numdeniedaces,
493             numallowedinhaces, numdeniedinhaces, numreorderaces;
494
495         allowedacesptr = calloc(numfaces, sizeof(struct cifs_aces *));
496         deniedacesptr = calloc(numfaces, sizeof(struct cifs_aces *));
497         allowedinhacesptr = calloc(numfaces, sizeof(struct cifs_aces *));
498         deniedinhacesptr = calloc(numfaces, sizeof(struct cifs_aces *));
499         reorderacesptr = calloc(numfaces, sizeof(struct cifs_aces *));
500
501         numallowedaces = 0;
502         numdeniedaces = 0;
503         numallowedinhaces = 0;
504         numdeniedinhaces = 0;
505         numreorderaces = 0;
506
507         for (i = 0; i < numfaces; i++) {
508                 pace = facesptr[i];
509                 if ((pace->type == ACCESS_DENIED) || (pace->type == ACCESS_DENIED_OBJECT)) {
510                         if (!(pace->flags & INHERITED_ACE_FLAG)) {
511                                 deniedacesptr[numdeniedaces] = malloc(sizeof(struct cifs_ace));
512                                 memcpy(deniedacesptr[numdeniedaces], pace, sizeof(struct cifs_ace));
513                                 numdeniedaces++;
514                         } else {
515                                 deniedinhacesptr[numdeniedinhaces] = malloc(sizeof(struct cifs_ace));
516                                 memcpy(deniedinhacesptr[numdeniedinhaces], pace, sizeof(struct cifs_ace));
517                                 numdeniedinhaces++;
518                         }
519                 } else if ((pace->type == ACCESS_ALLOWED) || (pace->type == ACCESS_ALLOWED_OBJECT)) {
520                         if (!(pace->flags & INHERITED_ACE_FLAG)) {
521                                 allowedacesptr[numallowedaces] = malloc(sizeof(struct cifs_ace));
522                                 memcpy(allowedacesptr[numallowedaces], pace, sizeof(struct cifs_ace));
523                                 numallowedaces++;
524                         } else {
525                                 allowedinhacesptr[numallowedinhaces] = malloc(sizeof(struct cifs_ace));
526                                 memcpy(allowedinhacesptr[numallowedinhaces], pace, sizeof(struct cifs_ace));
527                                 numallowedinhaces++;
528                         }
529                 }
530         }
531
532         for (i = 0; i < numdeniedaces; i++) {
533                 reorderacesptr[numreorderaces] = malloc(sizeof(struct cifs_ace));
534                 memcpy(reorderacesptr[numreorderaces], deniedacesptr[i], sizeof(struct cifs_ace));
535                 numreorderaces++;
536                 free(deniedacesptr[i]);
537         }
538
539         for (i = 0; i < numallowedaces; i++) {
540                 reorderacesptr[numreorderaces] = malloc(sizeof(struct cifs_ace));
541                 memcpy(reorderacesptr[numreorderaces], allowedacesptr[i], sizeof(struct cifs_ace));
542                 numreorderaces++;
543                 free(allowedacesptr[i]);
544         }
545
546         for (i = 0; i < numdeniedinhaces; i++) {
547                 reorderacesptr[numreorderaces] = malloc(sizeof(struct cifs_ace));
548                 memcpy(reorderacesptr[numreorderaces], deniedinhacesptr[i], sizeof(struct cifs_ace));
549                 numreorderaces++;
550                 free(deniedinhacesptr[i]);
551         }
552
553         for (i = 0; i < numallowedinhaces; i++) {
554                 reorderacesptr[numreorderaces] = malloc(sizeof(struct cifs_ace));
555                 memcpy(reorderacesptr[numreorderaces], allowedinhacesptr[i], sizeof(struct cifs_ace));
556                 numreorderaces++;
557                 free(allowedinhacesptr[i]);
558         }
559
560         free(deniedacesptr);
561         free(allowedacesptr);
562         free(deniedinhacesptr);
563         free(allowedinhacesptr);
564
565         return reorderacesptr;
566 }
567
568 static int
569 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
570                 struct cifs_ace **cacesptr, int numcaces, ace_kinds ace_kind)
571 {
572         int i, rc, size = 0, acessize = 0;
573         size_t acesoffset;
574         char *acesptr;
575
576         rc = alloc_sec_desc(pntsd, npntsd, numcaces, &acesoffset, ace_kind);
577         if (rc)
578                 return rc;
579
580         acesptr = (char *)*npntsd + acesoffset;
581         for (i = 0; i < numcaces; ++i) {
582                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
583                 acessize += size;
584                 acesptr += size;
585         }
586
587         *bufsize = copy_sec_desc(pntsd, *npntsd, numcaces, acessize, ace_kind);
588
589         return 0;
590 }
591
592 static int
593 ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
594                 struct cifs_ace **facesptr, int numfaces,
595                 struct cifs_ace **cacesptr, int numcaces,
596                 ace_kinds ace_kind)
597 {
598         int i, rc, numaces, size, acessize = 0;
599         size_t acesoffset;
600         char *acesptr;
601
602         numaces = numfaces + numcaces;
603         rc = alloc_sec_desc(pntsd, npntsd, numaces, &acesoffset, ace_kind);
604         if (rc)
605                 return rc;
606
607         acesptr = (char *)*npntsd + acesoffset;
608         for (i = 0; i < numfaces; ++i) {
609                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
610                 acesptr += size;
611                 acessize += size;
612         }
613         for (i = 0; i < numcaces; ++i) {
614                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
615                 acesptr += size;
616                 acessize += size;
617         }
618
619         *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize, ace_kind);
620
621         return 0;
622 }
623
624 static int
625 ace_add_reorder(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
626                 struct cifs_ace **facesptr, int numfaces,
627                 struct cifs_ace **cacesptr, int numcaces,
628                 ace_kinds ace_kind)
629 {
630         struct cifs_ace **reorderacesptr, **totalacesptr;
631         int i, rc, numaces;
632
633         numaces = numfaces + numcaces;
634         totalacesptr = calloc(numaces, sizeof(struct cifs_aces *));
635
636         for (i = 0; i < numfaces; i++) {
637                 totalacesptr[i] = facesptr[i];
638         }
639
640         for (i = numfaces; i < numaces; i++) {
641                 totalacesptr[i] = cacesptr[i - numfaces];
642         }
643
644         reorderacesptr = build_reorder_aces(totalacesptr, numaces);
645         rc = ace_add(pntsd, npntsd, bufsize, reorderacesptr,
646                         numaces, cacesptr, 0, ace_kind);
647
648         free(totalacesptr);
649         free(reorderacesptr);
650         return rc;
651 }
652
653 static int
654 ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
655                 struct cifs_ace **facesptr, int numfaces,
656                 struct cifs_ace **cacesptr, int numcaces,
657                 ace_kinds ace_kind)
658 {
659         int i, j, rc, size, acessize = 0;
660         size_t acesoffset;
661         char *acesptr;
662
663         if (numfaces == 0) {
664                 fprintf(stderr, "%s: No entries to modify", __func__);
665                 return -1;
666         }
667
668         rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset, ace_kind);
669         if (rc)
670                 return rc;
671
672         for (j = 0; j < numcaces; ++j) {
673                 for (i = 0; i < numfaces; ++i) {
674                         if (compare_aces(facesptr[i], cacesptr[j],
675                                         COMPSID | COMPTYPE)) {
676                                 copy_ace(facesptr[i], cacesptr[j]);
677                                 break;
678                         }
679                 }
680         }
681
682         acesptr = (char *)*npntsd + acesoffset;
683         for (i = 0; i < numfaces; ++i) {
684                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
685                 acesptr += size;
686                 acessize += size;
687         }
688
689         *bufsize = copy_sec_desc(pntsd, *npntsd, numfaces, acessize, ace_kind);
690
691         return 0;
692 }
693
694 static int
695 ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
696                 struct cifs_ace **facesptr, int numfaces,
697                 struct cifs_ace **cacesptr, int numcaces,
698                 ace_kinds ace_kind)
699 {
700         int i, j, numaces = 0, rc, size, acessize = 0;
701         size_t acesoffset;
702         char *acesptr;
703
704         if (numfaces == 0) {
705                 fprintf(stderr, "%s: No entries to delete\n", __func__);
706                 return -1;
707         }
708
709         if (numfaces < numcaces) {
710                 fprintf(stderr, "%s: Invalid entries to delete\n", __func__);
711                 return -1;
712         }
713
714         rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset, ace_kind);
715         if (rc)
716                 return rc;
717
718         acesptr = (char *)*npntsd + acesoffset;
719         for (i = 0; i < numfaces; ++i) {
720                 for (j = 0; j < numcaces; ++j) {
721                         if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
722                                 break;
723                 }
724                 if (j == numcaces) {
725                         size = copy_ace((struct cifs_ace *)acesptr,
726                                         facesptr[i]);
727                         acessize += size;
728                         acesptr += size;
729                         ++numaces;
730                 }
731         }
732
733         if (numaces == numfaces) {
734                 fprintf(stderr, "%s: Nothing to delete\n", __func__);
735                 return 1;
736         }
737
738         *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize, ace_kind);
739
740         return 0;
741 }
742
743 static int
744 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
745                 struct cifs_ctrl_acl **aclptr, ace_kinds ace_kind)
746 {
747         int numfaces = 0;
748         uint32_t acloffset;
749         struct cifs_ctrl_acl *laclptr;
750         char *end_of_acl = ((char *)pntsd) + acl_len;
751
752         switch(ace_kind) {
753         case ACE_KIND_SACL:
754                 acloffset = le32toh(pntsd->sacloffset);
755                 break;
756         case ACE_KIND_DACL:
757         default:
758                 acloffset = le32toh(pntsd->dacloffset);
759                 break;
760         }
761
762         if (!acloffset)
763                 return 0;
764
765         laclptr = (struct cifs_ctrl_acl *)((char *)pntsd + acloffset);
766
767         /* validate that we do not go past end of acl */
768         if (end_of_acl >= (char *)laclptr + le16toh(laclptr->size)) {
769                 numfaces = le32toh(laclptr->num_aces);
770                 *aclptr = laclptr;
771         }
772
773         return numfaces;
774 }
775
776 static struct cifs_ace **
777 build_fetched_aces(char *aclptr, int numfaces)
778 {
779         int i, acl_size;
780         char *acl_base;
781         struct cifs_ace *pace, **facesptr;
782
783         facesptr = calloc(numfaces, sizeof(struct cifs_aces *));
784         if (!facesptr) {
785                 fprintf(stderr, "%s: Error %d allocating ACE array",
786                                 __func__, errno);
787                 return facesptr;
788         }
789
790         acl_base = aclptr;
791         acl_size = sizeof(struct cifs_ctrl_acl);
792         for (i = 0; i < numfaces; ++i) {
793                 facesptr[i] = malloc(sizeof(struct cifs_ace));
794                 if (!facesptr[i])
795                         goto build_fetched_aces_err;
796                 pace = (struct cifs_ace *) (acl_base + acl_size);
797                 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
798                 acl_base = (char *)pace;
799                 acl_size = le16toh(pace->size);
800         }
801         return facesptr;
802
803 build_fetched_aces_err:
804         fprintf(stderr, "%s: Invalid fetched ace\n", __func__);
805         for (i = 0; i < numfaces; ++i)
806                 free(facesptr[i]);
807         free(facesptr);
808         return NULL;
809 }
810
811 static int
812 verify_ace_type(char *typestr, uint8_t *typeval, ace_kinds ace_kind)
813 {
814         int i, len;
815         char *invaltype;
816         uint8_t ace_type_mask;
817
818         switch(ace_kind) {
819         case ACE_KIND_SACL:
820                 ace_type_mask = SACL_VTYPES;
821                 break;
822         case ACE_KIND_DACL:
823         default:
824                 ace_type_mask = DACL_VTYPES;
825                 break;
826         }
827
828         if (strstr(typestr, "0x")) { /* hex type value */
829                 *typeval = strtol(typestr, &invaltype, 16);
830                 if (!strlen(invaltype)) {
831                         /* the type must be a single bit from the bit mask */
832                         if (*typeval != (*typeval & ace_type_mask)) {
833                                 fprintf(stderr, "%s: Invalid type: %s\n",
834                                         __func__, typestr);
835                                 return 1;
836                         }
837                         return 0;
838                 }
839         }
840
841         len = strlen(typestr);
842         switch(ace_kind) {
843         case ACE_KIND_SACL:
844                 for (i = 0; i < len; ++i)
845                         *(typestr + i) = toupper(*(typestr + i));
846                 if (!strcmp(typestr, "AUDIT"))
847                         *typeval = SYSTEM_AUDIT;
848                 else if (!strcmp(typestr, "AUDIT_OBJECT"))
849                         *typeval = SYSTEM_AUDIT_OBJECT;
850                 else if (!strcmp(typestr, "AUDIT_CALLBACK"))
851                         *typeval = SYSTEM_AUDIT_CALLBACK;
852                 else if (!strcmp(typestr, "AUDIT_CALLBACK_OBJECT"))
853                         *typeval = SYSTEM_AUDIT_CALLBACK_OBJECT;
854                 else if (!strcmp(typestr, "MANDATODY_LABEL"))
855                         *typeval = SYSTEM_MANDATORY_LABEL;
856                 else if (!strcmp(typestr, "RESOURCE_ATTRIBUTE"))
857                         *typeval = SYSTEM_RESOURCE_ATTRIBUTE;
858                 else if (!strcmp(typestr, "SCOPED_POLICY_ID"))
859                         *typeval = SYSTEM_SCOPED_POLICY_ID;
860                 else {
861                         fprintf(stderr, "%s: Invalid type: %s\n", __func__,
862                                 typestr);
863                         return 1;
864                 }
865                 break;
866         case ACE_KIND_DACL:
867         default:
868                 for (i = 0; i < len; ++i)
869                         *(typestr + i) = toupper(*(typestr + i));
870                 if (!strcmp(typestr, "ALLOWED"))
871                         *typeval = ACCESS_ALLOWED;
872                 else if (!strcmp(typestr, "DENIED"))
873                         *typeval = ACCESS_DENIED;
874                 else if (!strcmp(typestr, "ALLOWED_OBJECT"))
875                         *typeval = ACCESS_ALLOWED_OBJECT;
876                 else if (!strcmp(typestr, "DENIED_OBJECT"))
877                         *typeval = ACCESS_DENIED_OBJECT;
878                 else {
879                 fprintf(stderr, "%s: Invalid type: %s\n", __func__, typestr);
880                         return 1;
881                 }
882                 break;
883         }
884
885         return 0;
886 }
887
888 static uint8_t
889 ace_flag_value(char *flagstr, ace_kinds ace_kind)
890 {
891         uint8_t flagval = 0x0;
892         char *iflag;
893
894         iflag = strtok(flagstr, "|"); /* everything before | */
895         switch(ace_kind) {
896         case ACE_KIND_SACL:
897                 while (iflag) {
898                         if (!strcmp(iflag, "SA"))
899                                 flagval |= SUCCESSFUL_ACCESS;
900                         else if (!strcmp(iflag, "FA"))
901                                 flagval |= FAILED_ACCESS;
902                         else
903                                 return 0x0; /* Invalid flag */
904                         iflag = strtok(NULL, "|"); /* everything before | */
905                 }
906                 break;
907         case ACE_KIND_DACL:
908         default:
909                 while (iflag) {
910                         if (!strcmp(iflag, "OI"))
911                                 flagval |= OBJECT_INHERIT_FLAG;
912                         else if (!strcmp(iflag, "CI"))
913                                 flagval |= CONTAINER_INHERIT_FLAG;
914                         else if (!strcmp(iflag, "NP"))
915                                 flagval |= NO_PROPAGATE_INHERIT_FLAG;
916                         else if (!strcmp(iflag, "IO"))
917                                 flagval |= INHERIT_ONLY_FLAG;
918                         else if (!strcmp(iflag, "I"))
919                                 flagval |= INHERITED_ACE_FLAG;
920                         else
921                                 return 0x0; /* Invalid flag */
922                         iflag = strtok(NULL, "|"); /* everything before | */
923                 }
924                 break;
925         }
926
927         return flagval;
928 }
929
930 static int
931 verify_ace_flags(char *flagstr, uint8_t *flagval, ace_kinds ace_kind)
932 {
933         char *invalflag;
934         uint8_t ace_flag_mask = 0;
935
936         if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
937                 return 0;
938
939         if (strstr(flagstr, "0x")) { /* hex flag value */
940                 *flagval = strtol(flagstr, &invalflag, 16);
941                 if (strlen(invalflag)) {
942                         fprintf(stderr, "%s: Invalid flags: %s\n", __func__,
943                                 flagstr);
944                         return 1;
945                 }
946         } else
947                 *flagval = ace_flag_value(flagstr, ace_kind);
948
949         switch(ace_kind) {
950         case ACE_KIND_SACL:
951                 ace_flag_mask = SACL_VFLAGS;
952                 break;
953         case ACE_KIND_DACL:
954         default:
955                 ace_flag_mask = DACL_VFLAGS;
956                 break;
957         }
958         if (!*flagval || (*flagval & ~ace_flag_mask)) {
959                 fprintf(stderr, "%s: Invalid flag %s and value: 0x%x\n",
960                         __func__, flagstr, *flagval);
961                 return 1;
962         }
963
964         return 0;
965 }
966
967 static uint32_t
968 ace_mask_value(char *mask)
969 {
970         uint32_t maskval = 0;
971         char cur;
972
973         if (!strcmp(mask, "FULL"))
974                 return FULL_CONTROL;
975         if (!strcmp(mask, "CHANGE"))
976                 return CHANGE;
977         if (!strcmp(mask, "READ"))
978                 return EREAD;
979         if (!strcmp(mask, "RWXDPO"))
980                 return ALL_ACCESS_BITS;
981
982         while((cur = *mask++)) {
983                 switch(cur) {
984                 case 'R':
985                         maskval |= EREAD;
986                         break;
987                 case 'W':
988                         maskval |= EWRITE;
989                         break;
990                 case 'X':
991                         maskval |= EXEC;
992                         break;
993                 case 'D':
994                         maskval |= DELETE;
995                         break;
996                 case 'P':
997                         maskval |= WRITE_DAC;
998                         break;
999                 case 'O':
1000                         maskval |= WRITE_OWNER;
1001                         break;
1002                 default:
1003                         return 0;
1004                 }
1005         }
1006         return maskval;
1007 }
1008
1009 static int
1010 verify_ace_mask(char *maskstr, uint32_t *maskval)
1011 {
1012         unsigned long val;
1013         char *ep;
1014
1015         errno = 0;
1016         val = strtoul(maskstr, &ep, 0);
1017         if (errno == 0 && *ep == '\0')
1018                 *maskval = htole32((uint32_t)val);
1019         else
1020                 *maskval = htole32(ace_mask_value(maskstr));
1021
1022         if (!*maskval) {
1023                 fprintf(stderr, "%s: Invalid mask %s (value 0x%x)\n", __func__,
1024                         maskstr, *maskval);
1025                 return 1;
1026         }
1027
1028         return 0;
1029 }
1030
1031 #define AUTHORITY_MASK (~(0xffffffffffffULL))
1032
1033 static int
1034 raw_str_to_sid(const char *str, struct cifs_sid *csid)
1035 {
1036         const char *p;
1037         char *q;
1038         unsigned long long x;
1039
1040         /* Sanity check for either "S-" or "s-" */
1041         if ((str[0] != 'S' && str[0] != 's') || (str[1]!='-')) {
1042                 plugin_errmsg = "SID string does not start with \"S-\"";
1043                 return -EINVAL;
1044         }
1045
1046         /* Get the SID revision number */
1047         p = str + 2;
1048         x = strtoull(p, &q, 10);
1049         if (x == 0 || x > UCHAR_MAX || !q || *q != '-') {
1050                 plugin_errmsg = "Invalid SID revision number";
1051                 return -EINVAL;
1052         }
1053         csid->revision = (uint8_t)x;
1054
1055         /*
1056          * Next the Identifier Authority. This is stored in big-endian in a
1057          * 6 byte array. If the authority value is > UINT_MAX, then it should
1058          * be expressed as a hex value.
1059          */
1060         p = q + 1;
1061         x = strtoull(p, &q, 0);
1062         if ((x & AUTHORITY_MASK) || !q || *q !='-') {
1063                 plugin_errmsg = "Invalid SID authority";
1064                 return -EINVAL;
1065         }
1066         csid->authority[5] = (x & 0x0000000000ffULL);
1067         csid->authority[4] = (x & 0x00000000ff00ULL) >> 8;
1068         csid->authority[3] = (x & 0x000000ff0000ULL) >> 16;
1069         csid->authority[2] = (x & 0x0000ff000000ULL) >> 24;
1070         csid->authority[1] = (x & 0x00ff00000000ULL) >> 32;
1071         csid->authority[0] = (x & 0xff0000000000ULL) >> 40;
1072
1073         /* now read the the subauthorities and store as __le32 vals */
1074         p = q + 1;
1075         csid->num_subauth = 0;
1076         while (csid->num_subauth < SID_MAX_SUB_AUTHORITIES) {
1077                 x = strtoul(p, &q, 10);
1078                 if (p == q)
1079                         break;
1080                 if (x > UINT_MAX) {
1081                         plugin_errmsg = "Invalid sub authority value";
1082                         return -EINVAL;
1083                 }
1084                 csid->sub_auth[csid->num_subauth++] = htole32((uint32_t)x);
1085
1086                 if (*q != '-')
1087                         break;
1088                 p = q + 1;
1089         }
1090
1091         /* IF we ended early, then the SID could not be converted */
1092         if (q && *q != '\0') {
1093                 plugin_errmsg = "Invalid sub authority value";
1094                 return -EINVAL;
1095         }
1096
1097         return 0;
1098 }
1099
1100 static int
1101 setcifsacl_str_to_sid(const char *str, struct cifs_sid *sid)
1102 {
1103         if (plugin_loaded)
1104                 return str_to_sid(plugin_handle, str, sid);
1105         return raw_str_to_sid(str, sid);
1106 }
1107
1108 static struct cifs_ace **
1109 build_cmdline_aces(char **arrptr, int numcaces, ace_kinds ace_kind)
1110 {
1111         int i;
1112         char *acesid, *acetype, *aceflag, *acemask;
1113         struct cifs_ace **cacesptr;
1114
1115         cacesptr = calloc(numcaces, sizeof(struct cifs_aces *));
1116         if (!cacesptr) {
1117                 fprintf(stderr, "%s: Error %d allocating ACE array", __func__,
1118                         errno);
1119                 return NULL;
1120         }
1121
1122         for (i = 0; i < numcaces; ++i) {
1123                 acesid = strtok(arrptr[i], ":");
1124                 acetype = strtok(NULL, "/");
1125                 aceflag = strtok(NULL, "/");
1126                 acemask = strtok(NULL, "/");
1127
1128                 if (!acesid || !acetype || !aceflag || !acemask) {
1129                         fprintf(stderr, "%s: Incomplete ACE: %s\n", __func__,
1130                                 arrptr[i]);
1131                         goto build_cmdline_aces_ret;
1132                 }
1133
1134                 cacesptr[i] = calloc(1, sizeof(struct cifs_ace));
1135                 if (!cacesptr[i]) {
1136                         fprintf(stderr, "%s: ACE alloc error %d\n", __func__,
1137                                 errno);
1138                         goto build_cmdline_aces_ret;
1139                 }
1140
1141                 if (setcifsacl_str_to_sid(acesid, &cacesptr[i]->sid)) {
1142                         fprintf(stderr, "%s: Invalid SID (%s): %s\n", __func__,
1143                                 arrptr[i], plugin_errmsg);
1144                         goto build_cmdline_aces_ret;
1145                 }
1146
1147                 if (verify_ace_type(acetype, &cacesptr[i]->type, ace_kind)) {
1148                         fprintf(stderr, "%s: Invalid ACE type: %s\n",
1149                                         __func__, arrptr[i]);
1150                         goto build_cmdline_aces_ret;
1151                 }
1152
1153                 if (verify_ace_flags(aceflag, &cacesptr[i]->flags, ace_kind)) {
1154                         fprintf(stderr, "%s: Invalid ACE flag: %s\n",
1155                                 __func__, arrptr[i]);
1156                         goto build_cmdline_aces_ret;
1157                 }
1158
1159                 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
1160                         fprintf(stderr, "%s: Invalid ACE mask: %s\n",
1161                                 __func__, arrptr[i]);
1162                         goto build_cmdline_aces_ret;
1163                 }
1164
1165                 cacesptr[i]->size = htole16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
1166                                             cacesptr[i]->sid.num_subauth * 4);
1167         }
1168         return cacesptr;
1169
1170 build_cmdline_aces_ret:
1171         for (i = 0; i < numcaces; ++i)
1172                 free(cacesptr[i]);
1173         free(cacesptr);
1174         return NULL;
1175 }
1176
1177 static char **
1178 parse_cmdline_aces(char *acelist, int numcaces)
1179 {
1180         int i = 0;
1181         char *acestr, *vacestr, **arrptr = NULL;
1182
1183         arrptr = (char **)malloc(numcaces * sizeof(char *));
1184         if (!arrptr) {
1185                 fprintf(stderr, "%s: Unable to allocate char array\n",
1186                         __func__);
1187                 return NULL;
1188         }
1189
1190         while (i < numcaces) {
1191                 acestr = strtok(acelist, ","); /* everything before , */
1192                 if (!acestr)
1193                         goto parse_cmdline_aces_err;
1194
1195                 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
1196                 if (!vacestr)
1197                         goto parse_cmdline_aces_err;
1198                 vacestr += 4; /* skip past "ACL:" */
1199                 if (*vacestr) {
1200                         arrptr[i] = vacestr;
1201                         ++i;
1202                 }
1203                 acelist = NULL;
1204         }
1205         return arrptr;
1206
1207 parse_cmdline_aces_err:
1208         fprintf(stderr, "%s: Error parsing ACEs\n", __func__);
1209         free(arrptr);
1210         return NULL;
1211 }
1212
1213 /* How many aces were provided on the command-line? Count the commas. */
1214 static unsigned int
1215 get_numcaces(const char *aces)
1216 {
1217         unsigned int num = 1;
1218         const char *current;
1219
1220         current = aces;
1221         while((current = strchr(current, ','))) {
1222                 ++current;
1223                 ++num;
1224         }
1225
1226         return num;
1227 }
1228
1229 static int
1230 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
1231                 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
1232                 struct cifs_ace **cacesptr, int numcaces,
1233                 enum setcifsacl_actions maction, ace_kinds ace_kind)
1234 {
1235         int rc = 1;
1236
1237         switch (maction) {
1238         case ActDelete:
1239                 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
1240                                 numfaces, cacesptr, numcaces, ace_kind);
1241                 break;
1242         case ActModify:
1243                 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
1244                                 numfaces, cacesptr, numcaces, ace_kind);
1245                 break;
1246         case ActAdd:
1247                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
1248                                 numfaces, cacesptr, numcaces, ace_kind);
1249                 break;
1250         case ActSetAcl:
1251                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces,
1252                                 ace_kind);
1253                 break;
1254         case ActAddReorder:
1255                 rc = ace_add_reorder(pntsd, npntsd, bufsize, facesptr,
1256                                 numfaces, cacesptr, numcaces, ace_kind);
1257                 break;
1258         default:
1259                 fprintf(stderr, "%s: Invalid action: %d\n", __func__, maction);
1260                 break;
1261         }
1262
1263         return rc;
1264 }
1265
1266 static void
1267 setcifsacl_usage(const char *prog)
1268 {
1269         fprintf(stderr,
1270         "%s: Alter components of CIFS/NTFS security descriptor of a file object\n",
1271                 prog);
1272         fprintf(stderr, "Usage: %s option [<list_of_ACEs>|<SID>] <file_name>\n",
1273                 prog);
1274         fprintf(stderr, "Valid options:\n");
1275         fprintf(stderr, "\t-v   Version of the program\n");
1276         fprintf(stderr, "\t-U   Used in combination with -a, -D, -M, -S in order to ");
1277         fprintf(stderr, "\n\t   apply the actions to SALC (aUdit ACL); if not specified, ");
1278         fprintf(stderr, "\n\t   the actions apply to DACL\n");
1279         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
1280         fprintf(stderr,
1281         "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
1282         fprintf(stderr, "\n");
1283         fprintf(stderr, "\t-A   Add ACE(s) and reorder, separated by a comma, to an ACL\n");
1284         fprintf(stderr,
1285         "\tsetcifsacl -A \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
1286         fprintf(stderr, "\n");
1287         fprintf(stderr,
1288         "\t-D   Delete ACE(s), separated by a comma, from an ACL\n");
1289         fprintf(stderr,
1290         "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
1291         fprintf(stderr, "\n");
1292         fprintf(stderr,
1293         "\t-M   Modify ACE(s), separated by a comma, in an ACL\n");
1294         fprintf(stderr,
1295         "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
1296         fprintf(stderr,
1297         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
1298         fprintf(stderr,
1299         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
1300         fprintf(stderr,
1301         "\n\t-o Set owner using specified SID (name or raw format)\n");
1302         fprintf(stderr,
1303         "\tsetcifsacl -o \"Administrator\" <file_name>\n");
1304         fprintf(stderr,
1305         "\n\t-g Set group using specified SID (name or raw format)\n");
1306         fprintf(stderr,
1307         "\tsetcifsacl -g \"Administrators\" <file_name>\n");
1308         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
1309 }
1310
1311 int
1312 main(const int argc, char *const argv[])
1313 {
1314         int i, rc, c, numcaces = 0, numfaces = 0;
1315         enum setcifsacl_actions maction = ActUnknown;
1316         ssize_t attrlen, bufsize = BUFSIZE;
1317         char *ace_list = NULL, *filename = NULL, *attrval = NULL,
1318                 **arrptr = NULL, *sid_str = NULL;
1319         struct cifs_ctrl_acl *aclptr = NULL;
1320         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
1321         struct cifs_ntsd *ntsdptr = NULL;
1322         struct cifs_sid sid;
1323         char *attrname = ATTRNAME_ACL;
1324         ace_kinds ace_kind = ACE_KIND_DACL;
1325
1326         while ((c = getopt(argc, argv, "hvD:M:a:A:S:o:g:U")) != -1) {
1327                 switch (c) {
1328                 case 'U':
1329                         ace_kind = ACE_KIND_SACL;
1330                         attrname = ATTRNAME_NTSD_FULL;
1331                         break;
1332                 case 'D':
1333                         maction = ActDelete;
1334                         ace_list = optarg;
1335                         break;
1336                 case 'M':
1337                         maction = ActModify;
1338                         ace_list = optarg;
1339                         break;
1340                 case 'a':
1341                         maction = ActAdd;
1342                         ace_list = optarg;
1343                         break;
1344                 case 'A':
1345                         maction = ActAddReorder;
1346                         ace_list = optarg;
1347                         break;
1348                 case 'S':
1349                         maction = ActSetAcl;
1350                         ace_list = optarg;
1351                         break;
1352                 case 'o':
1353                         maction = ActSetOwner;
1354                         sid_str = optarg;
1355                         attrname = ATTRNAME_NTSD;
1356                         break;
1357                 case 'g':
1358                         maction = ActSetGroup;
1359                         sid_str = optarg;
1360                         attrname = ATTRNAME_NTSD;
1361                         break;
1362                 case 'h':
1363                         setcifsacl_usage(basename(argv[0]));
1364                         return 0;
1365                 case 'v':
1366                         printf("Version: %s\n", VERSION);
1367                         return 0;
1368                 default:
1369                         setcifsacl_usage(basename(argv[0]));
1370                         return -1;
1371                 }
1372         }
1373
1374         /* We expect 1 required and one optional argument in addition to the option */
1375         if (argc < 4 || argc > 5) {
1376                 setcifsacl_usage(basename(argv[0]));
1377                 return -1;
1378         }
1379         filename = argv[argc-1];
1380
1381         if (!ace_list && maction != ActSetOwner && maction != ActSetGroup) {
1382                 fprintf(stderr, "%s: No valid ACEs specified\n", __func__);
1383                 return -1;
1384         }
1385
1386         if (!sid_str && (maction == ActSetOwner || maction == ActSetGroup)) {
1387                 fprintf(stderr, "%s: No valid SIDs specified\n", __func__);
1388                 return -1;
1389         }
1390
1391         if (init_plugin(&plugin_handle)) {
1392                 fprintf(stderr, "WARNING: unable to initialize idmapping "
1393                                 "plugin. Only \"raw\" SID strings will be "
1394                                 "accepted: %s\n", plugin_errmsg);
1395                 plugin_loaded = false;
1396         } else {
1397                 plugin_loaded = true;
1398         }
1399
1400         if (maction == ActSetOwner || maction == ActSetGroup) {
1401                 if (ace_kind == ACE_KIND_SACL) {
1402                         fprintf(stderr, "WARNING: disregarding -U when setting"
1403                                         " owner/group\n");
1404                         ace_kind = ACE_KIND_DACL;
1405                 }
1406                 /* parse the sid */
1407                 if (setcifsacl_str_to_sid(sid_str, &sid)) {
1408                         fprintf(stderr, "%s: failed to parce \'%s\' as SID\n",
1409                                 __func__, sid_str);
1410                         goto setcifsacl_numcaces_ret;
1411                 }
1412         } else {
1413                 numcaces = get_numcaces(ace_list);
1414
1415                 arrptr = parse_cmdline_aces(ace_list, numcaces);
1416                 if (!arrptr)
1417                         goto setcifsacl_numcaces_ret;
1418
1419                 cacesptr = build_cmdline_aces(arrptr, numcaces, ace_kind);
1420                 if (!cacesptr)
1421                         goto setcifsacl_cmdlineparse_ret;
1422         }
1423 cifsacl:
1424         if (bufsize >= XATTR_SIZE_MAX) {
1425                 fprintf(stderr, "%s: Buffer size %zd exceeds max size of %d\n",
1426                                 __func__, bufsize, XATTR_SIZE_MAX);
1427                 goto setcifsacl_cmdlineverify_ret;
1428         }
1429
1430         attrval = malloc(bufsize * sizeof(char));
1431         if (!attrval) {
1432                 fprintf(stderr, "error allocating memory for attribute value "
1433                         "buffer\n");
1434                 goto setcifsacl_cmdlineverify_ret;
1435         }
1436
1437         attrlen = getxattr(filename, attrname, attrval, bufsize);
1438         if (attrlen == -1) {
1439                 if (errno == ERANGE) {
1440                         free(attrval);
1441                         bufsize += BUFSIZE;
1442                         goto cifsacl;
1443                 } else {
1444                         fprintf(stderr, "getxattr error: %d\n", errno);
1445                         goto setcifsacl_getx_ret;
1446                 }
1447         }
1448
1449         if (maction == ActSetOwner || maction == ActSetGroup) {
1450                 struct cifs_ntsd *pfntsd = (struct cifs_ntsd *)attrval;
1451                 int dacloffset = le32toh(pfntsd->dacloffset);
1452                 struct cifs_ctrl_acl *daclinfo;
1453                 int numaces, acessize;
1454                 size_t faceoffset, naceoffset;
1455                 char *faceptr, *naceptr;
1456                 /*
1457                  * dacloffset of 0 means "no DACL - all access for everyone"
1458                  * if dacloffset is not 0, it is still possible that DACL is
1459                  * empty - numaces is zero - "no access for anyone"
1460                  */
1461                 if (dacloffset) {
1462                         daclinfo = (struct cifs_ctrl_acl *)(attrval + dacloffset);
1463                         numaces = le16toh(daclinfo->num_aces);
1464                         acessize = le32toh(daclinfo->size);
1465                 } else {
1466                         daclinfo = NULL;
1467                         numaces = 0;
1468                         acessize = 0;
1469                 }
1470                 /*
1471                  * this allocates large enough buffer for max sid size and the
1472                  * dacl info from the fetched security descriptor
1473                  */
1474                 rc = alloc_sec_desc(pfntsd, &ntsdptr, numaces, &faceoffset,
1475                                 ACE_KIND_DACL);
1476                 if (rc)
1477                         goto setcifsacl_numcaces_ret;
1478
1479                 /*
1480                  * copy the control structures from the fetched descriptor, the
1481                  * sid specified by the user, and adjust the offsets/move dacl
1482                  * control structure if needed
1483                  */
1484                 bufsize = copy_sec_desc_with_sid(pfntsd, ntsdptr, &sid,
1485                                 maction);
1486
1487                 /* copy DACL aces verbatim as they have not changed */
1488                 if (dacloffset) {
1489                         faceptr = attrval + faceoffset;
1490                         naceoffset = le32toh(ntsdptr->dacloffset) +
1491                                 sizeof(struct cifs_ctrl_acl);
1492                         naceptr = (char *)ntsdptr + naceoffset;
1493                         memcpy(naceptr, faceptr, acessize);
1494                 }
1495         } else {
1496                 bufsize = 0;
1497
1498                 numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen,
1499                                 &aclptr, ace_kind);
1500                 if (!numfaces && (maction != ActAdd && maction != ActAddReorder)) {
1501                         /* if we are not adding aces */
1502                         fprintf(stderr, "%s: Empty DACL\n", __func__);
1503                         goto setcifsacl_facenum_ret;
1504                 }
1505
1506                 facesptr = build_fetched_aces((char *)aclptr, numfaces);
1507                 if (!facesptr)
1508                         goto setcifsacl_facenum_ret;
1509
1510                 rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr,
1511                                 &bufsize, facesptr, numfaces, cacesptr,
1512                                 numcaces, maction, ace_kind);
1513                 if (rc)
1514                         goto setcifsacl_action_ret;
1515         }
1516
1517         attrlen = setxattr(filename, attrname, ntsdptr, bufsize, 0);
1518         if (attrlen == -1) {
1519                 fprintf(stderr, "%s: setxattr error: %s\n", __func__,
1520                         strerror(errno));
1521                 goto setcifsacl_action_ret;
1522         }
1523
1524         if (plugin_loaded)
1525                 exit_plugin(plugin_handle);
1526         return 0;
1527
1528 setcifsacl_action_ret:
1529         if (ntsdptr)
1530                 free(ntsdptr);
1531
1532 setcifsacl_facenum_ret:
1533         if (facesptr) {
1534                 for (i = 0; i < numfaces; ++i)
1535                         free(facesptr[i]);
1536                 free(facesptr);
1537         }
1538
1539 setcifsacl_getx_ret:
1540         if (attrval)
1541                 free(attrval);
1542
1543 setcifsacl_cmdlineverify_ret:
1544         if (cacesptr) {
1545                 for (i = 0; i < numcaces; ++i)
1546                         free(cacesptr[i]);
1547                 free(cacesptr);
1548         }
1549
1550 setcifsacl_cmdlineparse_ret:
1551         if (arrptr)
1552                 free(arrptr);
1553
1554 setcifsacl_numcaces_ret:
1555         if (plugin_loaded)
1556                 exit_plugin(plugin_handle);
1557         return -1;
1558 }