xref: /dragonfly/contrib/libarchive/libarchive/archive_acl.c (revision afd311f52496a4b5c3df02ea6d4bdab591886c60)
1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * Copyright (c) 2016 Martin Matuska
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD$");
29 
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_LIMITS_H
34 #include <limits.h>
35 #endif
36 #ifdef HAVE_WCHAR_H
37 #include <wchar.h>
38 #endif
39 
40 #include "archive_acl_private.h"
41 #include "archive_entry.h"
42 #include "archive_private.h"
43 
44 #undef max
45 #define   max(a, b) ((a)>(b)?(a):(b))
46 
47 #ifndef HAVE_WMEMCMP
48 /* Good enough for simple equality testing, but not for sorting. */
49 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
50 #endif
51 
52 static int          acl_special(struct archive_acl *acl,
53                         int type, int permset, int tag);
54 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
55                         int type, int permset, int tag, int id);
56 static int          archive_acl_add_entry_len_l(struct archive_acl *acl,
57                         int type, int permset, int tag, int id, const char *name,
58                         size_t len, struct archive_string_conv *sc);
59 static int          archive_acl_text_want_type(struct archive_acl *acl, int flags);
60 static ssize_t      archive_acl_text_len(struct archive_acl *acl, int want_type,
61                         int flags, int wide, struct archive *a,
62                         struct archive_string_conv *sc);
63 static int          isint_w(const wchar_t *start, const wchar_t *end, int *result);
64 static int          ismode_w(const wchar_t *start, const wchar_t *end, int *result);
65 static int          is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
66                         int *result);
67 static int          is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
68                         int *result);
69 static void         next_field_w(const wchar_t **wp, const wchar_t **start,
70                         const wchar_t **end, wchar_t *sep);
71 static void         append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
72                         int tag, int flags, const wchar_t *wname, int perm, int id);
73 static void         append_id_w(wchar_t **wp, int id);
74 static int          isint(const char *start, const char *end, int *result);
75 static int          ismode(const char *start, const char *end, int *result);
76 static int          is_nfs4_flags(const char *start, const char *end,
77                         int *result);
78 static int          is_nfs4_perms(const char *start, const char *end,
79                         int *result);
80 static void         next_field(const char **p, const char **start,
81                         const char **end, char *sep);
82 static void         append_entry(char **p, const char *prefix, int type,
83                         int tag, int flags, const char *name, int perm, int id);
84 static void         append_id(char **p, int id);
85 
86 static const struct {
87           const int perm;
88           const char c;
89           const wchar_t wc;
90 } nfsv4_acl_perm_map[] = {
91           { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
92               L'r' },
93           { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
94               L'w' },
95           { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
96           { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
97               'p', L'p' },
98           { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
99           { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
100           { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
101           { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
102           { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
103           { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
104           { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
105           { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
106           { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
107           { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
108 };
109 
110 static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
111     sizeof(nfsv4_acl_perm_map[0]));
112 
113 static const struct {
114           const int perm;
115           const char c;
116           const wchar_t wc;
117 } nfsv4_acl_flag_map[] = {
118           { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
119           { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
120           { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
121           { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
122           { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
123           { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
124           { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
125 };
126 
127 static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
128     sizeof(nfsv4_acl_flag_map[0]));
129 
130 void
archive_acl_clear(struct archive_acl * acl)131 archive_acl_clear(struct archive_acl *acl)
132 {
133           struct archive_acl_entry *ap;
134 
135           while (acl->acl_head != NULL) {
136                     ap = acl->acl_head->next;
137                     archive_mstring_clean(&acl->acl_head->name);
138                     free(acl->acl_head);
139                     acl->acl_head = ap;
140           }
141           free(acl->acl_text_w);
142           acl->acl_text_w = NULL;
143           free(acl->acl_text);
144           acl->acl_text = NULL;
145           acl->acl_p = NULL;
146           acl->acl_types = 0;
147           acl->acl_state = 0; /* Not counting. */
148 }
149 
150 void
archive_acl_copy(struct archive_acl * dest,struct archive_acl * src)151 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
152 {
153           struct archive_acl_entry *ap, *ap2;
154 
155           archive_acl_clear(dest);
156 
157           dest->mode = src->mode;
158           ap = src->acl_head;
159           while (ap != NULL) {
160                     ap2 = acl_new_entry(dest,
161                         ap->type, ap->permset, ap->tag, ap->id);
162                     if (ap2 != NULL)
163                               archive_mstring_copy(&ap2->name, &ap->name);
164                     ap = ap->next;
165           }
166 }
167 
168 int
archive_acl_add_entry(struct archive_acl * acl,int type,int permset,int tag,int id,const char * name)169 archive_acl_add_entry(struct archive_acl *acl,
170     int type, int permset, int tag, int id, const char *name)
171 {
172           struct archive_acl_entry *ap;
173 
174           if (acl_special(acl, type, permset, tag) == 0)
175                     return ARCHIVE_OK;
176           ap = acl_new_entry(acl, type, permset, tag, id);
177           if (ap == NULL) {
178                     /* XXX Error XXX */
179                     return ARCHIVE_FAILED;
180           }
181           if (name != NULL  &&  *name != '\0')
182                     archive_mstring_copy_mbs(&ap->name, name);
183           else
184                     archive_mstring_clean(&ap->name);
185           return ARCHIVE_OK;
186 }
187 
188 int
archive_acl_add_entry_w_len(struct archive_acl * acl,int type,int permset,int tag,int id,const wchar_t * name,size_t len)189 archive_acl_add_entry_w_len(struct archive_acl *acl,
190     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
191 {
192           struct archive_acl_entry *ap;
193 
194           if (acl_special(acl, type, permset, tag) == 0)
195                     return ARCHIVE_OK;
196           ap = acl_new_entry(acl, type, permset, tag, id);
197           if (ap == NULL) {
198                     /* XXX Error XXX */
199                     return ARCHIVE_FAILED;
200           }
201           if (name != NULL  &&  *name != L'\0' && len > 0)
202                     archive_mstring_copy_wcs_len(&ap->name, name, len);
203           else
204                     archive_mstring_clean(&ap->name);
205           return ARCHIVE_OK;
206 }
207 
208 static int
archive_acl_add_entry_len_l(struct archive_acl * acl,int type,int permset,int tag,int id,const char * name,size_t len,struct archive_string_conv * sc)209 archive_acl_add_entry_len_l(struct archive_acl *acl,
210     int type, int permset, int tag, int id, const char *name, size_t len,
211     struct archive_string_conv *sc)
212 {
213           struct archive_acl_entry *ap;
214           int r;
215 
216           if (acl_special(acl, type, permset, tag) == 0)
217                     return ARCHIVE_OK;
218           ap = acl_new_entry(acl, type, permset, tag, id);
219           if (ap == NULL) {
220                     /* XXX Error XXX */
221                     return ARCHIVE_FAILED;
222           }
223           if (name != NULL  &&  *name != '\0' && len > 0) {
224                     r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
225           } else {
226                     r = 0;
227                     archive_mstring_clean(&ap->name);
228           }
229           if (r == 0)
230                     return (ARCHIVE_OK);
231           else if (errno == ENOMEM)
232                     return (ARCHIVE_FATAL);
233           else
234                     return (ARCHIVE_WARN);
235 }
236 
237 /*
238  * If this ACL entry is part of the standard POSIX permissions set,
239  * store the permissions in the stat structure and return zero.
240  */
241 static int
acl_special(struct archive_acl * acl,int type,int permset,int tag)242 acl_special(struct archive_acl *acl, int type, int permset, int tag)
243 {
244           if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
245               && ((permset & ~007) == 0)) {
246                     switch (tag) {
247                     case ARCHIVE_ENTRY_ACL_USER_OBJ:
248                               acl->mode &= ~0700;
249                               acl->mode |= (permset & 7) << 6;
250                               return (0);
251                     case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
252                               acl->mode &= ~0070;
253                               acl->mode |= (permset & 7) << 3;
254                               return (0);
255                     case ARCHIVE_ENTRY_ACL_OTHER:
256                               acl->mode &= ~0007;
257                               acl->mode |= permset & 7;
258                               return (0);
259                     }
260           }
261           return (1);
262 }
263 
264 /*
265  * Allocate and populate a new ACL entry with everything but the
266  * name.
267  */
268 static struct archive_acl_entry *
acl_new_entry(struct archive_acl * acl,int type,int permset,int tag,int id)269 acl_new_entry(struct archive_acl *acl,
270     int type, int permset, int tag, int id)
271 {
272           struct archive_acl_entry *ap, *aq;
273 
274           /* Type argument must be a valid NFS4 or POSIX.1e type.
275            * The type must agree with anything already set and
276            * the permset must be compatible. */
277           if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
278                     if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
279                               return (NULL);
280                     }
281                     if (permset &
282                         ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
283                               | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
284                               return (NULL);
285                     }
286           } else    if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
287                     if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
288                               return (NULL);
289                     }
290                     if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
291                               return (NULL);
292                     }
293           } else {
294                     return (NULL);
295           }
296 
297           /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
298           switch (tag) {
299           case ARCHIVE_ENTRY_ACL_USER:
300           case ARCHIVE_ENTRY_ACL_USER_OBJ:
301           case ARCHIVE_ENTRY_ACL_GROUP:
302           case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
303                     /* Tags valid in both NFS4 and POSIX.1e */
304                     break;
305           case ARCHIVE_ENTRY_ACL_MASK:
306           case ARCHIVE_ENTRY_ACL_OTHER:
307                     /* Tags valid only in POSIX.1e. */
308                     if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
309                               return (NULL);
310                     }
311                     break;
312           case ARCHIVE_ENTRY_ACL_EVERYONE:
313                     /* Tags valid only in NFS4. */
314                     if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
315                               return (NULL);
316                     }
317                     break;
318           default:
319                     /* No other values are valid. */
320                     return (NULL);
321           }
322 
323           free(acl->acl_text_w);
324           acl->acl_text_w = NULL;
325           free(acl->acl_text);
326           acl->acl_text = NULL;
327 
328           /*
329            * If there's a matching entry already in the list, overwrite it.
330            * NFSv4 entries may be repeated and are not overwritten.
331            *
332            * TODO: compare names of no id is provided (needs more rework)
333            */
334           ap = acl->acl_head;
335           aq = NULL;
336           while (ap != NULL) {
337                     if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
338                         ap->type == type && ap->tag == tag && ap->id == id) {
339                               if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
340                                   tag != ARCHIVE_ENTRY_ACL_GROUP)) {
341                                         ap->permset = permset;
342                                         return (ap);
343                               }
344                     }
345                     aq = ap;
346                     ap = ap->next;
347           }
348 
349           /* Add a new entry to the end of the list. */
350           ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
351           if (ap == NULL)
352                     return (NULL);
353           if (aq == NULL)
354                     acl->acl_head = ap;
355           else
356                     aq->next = ap;
357           ap->type = type;
358           ap->tag = tag;
359           ap->id = id;
360           ap->permset = permset;
361           acl->acl_types |= type;
362           return (ap);
363 }
364 
365 /*
366  * Return a count of entries matching "want_type".
367  */
368 int
archive_acl_count(struct archive_acl * acl,int want_type)369 archive_acl_count(struct archive_acl *acl, int want_type)
370 {
371           int count;
372           struct archive_acl_entry *ap;
373 
374           count = 0;
375           ap = acl->acl_head;
376           while (ap != NULL) {
377                     if ((ap->type & want_type) != 0)
378                               count++;
379                     ap = ap->next;
380           }
381 
382           if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
383                     count += 3;
384           return (count);
385 }
386 
387 /*
388  * Return a bitmask of stored ACL types in an ACL list
389  */
390 int
archive_acl_types(struct archive_acl * acl)391 archive_acl_types(struct archive_acl *acl)
392 {
393           return (acl->acl_types);
394 }
395 
396 /*
397  * Prepare for reading entries from the ACL data.  Returns a count
398  * of entries matching "want_type", or zero if there are no
399  * non-extended ACL entries of that type.
400  */
401 int
archive_acl_reset(struct archive_acl * acl,int want_type)402 archive_acl_reset(struct archive_acl *acl, int want_type)
403 {
404           int count, cutoff;
405 
406           count = archive_acl_count(acl, want_type);
407 
408           /*
409            * If the only entries are the three standard ones,
410            * then don't return any ACL data.  (In this case,
411            * client can just use chmod(2) to set permissions.)
412            */
413           if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
414                     cutoff = 3;
415           else
416                     cutoff = 0;
417 
418           if (count > cutoff)
419                     acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
420           else
421                     acl->acl_state = 0;
422           acl->acl_p = acl->acl_head;
423           return (count);
424 }
425 
426 
427 /*
428  * Return the next ACL entry in the list.  Fake entries for the
429  * standard permissions and include them in the returned list.
430  */
431 int
archive_acl_next(struct archive * a,struct archive_acl * acl,int want_type,int * type,int * permset,int * tag,int * id,const char ** name)432 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
433     int *type, int *permset, int *tag, int *id, const char **name)
434 {
435           *name = NULL;
436           *id = -1;
437 
438           /*
439            * The acl_state is either zero (no entries available), -1
440            * (reading from list), or an entry type (retrieve that type
441            * from ae_stat.aest_mode).
442            */
443           if (acl->acl_state == 0)
444                     return (ARCHIVE_WARN);
445 
446           /* The first three access entries are special. */
447           if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
448                     switch (acl->acl_state) {
449                     case ARCHIVE_ENTRY_ACL_USER_OBJ:
450                               *permset = (acl->mode >> 6) & 7;
451                               *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
452                               *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
453                               acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
454                               return (ARCHIVE_OK);
455                     case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
456                               *permset = (acl->mode >> 3) & 7;
457                               *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
458                               *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
459                               acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
460                               return (ARCHIVE_OK);
461                     case ARCHIVE_ENTRY_ACL_OTHER:
462                               *permset = acl->mode & 7;
463                               *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
464                               *tag = ARCHIVE_ENTRY_ACL_OTHER;
465                               acl->acl_state = -1;
466                               acl->acl_p = acl->acl_head;
467                               return (ARCHIVE_OK);
468                     default:
469                               break;
470                     }
471           }
472 
473           while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
474                     acl->acl_p = acl->acl_p->next;
475           if (acl->acl_p == NULL) {
476                     acl->acl_state = 0;
477                     *type = 0;
478                     *permset = 0;
479                     *tag = 0;
480                     *id = -1;
481                     *name = NULL;
482                     return (ARCHIVE_EOF); /* End of ACL entries. */
483           }
484           *type = acl->acl_p->type;
485           *permset = acl->acl_p->permset;
486           *tag = acl->acl_p->tag;
487           *id = acl->acl_p->id;
488           if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
489                     if (errno == ENOMEM)
490                               return (ARCHIVE_FATAL);
491                     *name = NULL;
492           }
493           acl->acl_p = acl->acl_p->next;
494           return (ARCHIVE_OK);
495 }
496 
497 /*
498  * Determine what type of ACL do we want
499  */
500 static int
archive_acl_text_want_type(struct archive_acl * acl,int flags)501 archive_acl_text_want_type(struct archive_acl *acl, int flags)
502 {
503           int want_type;
504 
505           /* Check if ACL is NFSv4 */
506           if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
507                     /* NFSv4 should never mix with POSIX.1e */
508                     if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
509                               return (0);
510                     else
511                               return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
512           }
513 
514           /* Now deal with POSIX.1e ACLs */
515 
516           want_type = 0;
517           if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
518                     want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
519           if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
520                     want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
521 
522           /* By default we want both access and default ACLs */
523           if (want_type == 0)
524                     return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
525 
526           return (want_type);
527 }
528 
529 /*
530  * Calculate ACL text string length
531  */
532 static ssize_t
archive_acl_text_len(struct archive_acl * acl,int want_type,int flags,int wide,struct archive * a,struct archive_string_conv * sc)533 archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
534     int wide, struct archive *a, struct archive_string_conv *sc) {
535           struct archive_acl_entry *ap;
536           const char *name;
537           const wchar_t *wname;
538           int count, idlen, tmp, r;
539           ssize_t length;
540           size_t len;
541 
542           count = 0;
543           length = 0;
544           for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
545                     if ((ap->type & want_type) == 0)
546                               continue;
547                     /*
548                      * Filemode-mapping ACL entries are stored exclusively in
549                      * ap->mode so they should not be in the list
550                      */
551                     if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
552                         && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
553                         || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
554                         || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
555                               continue;
556                     count++;
557                     if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
558                         && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
559                               length += 8; /* "default:" */
560                     switch (ap->tag) {
561                     case ARCHIVE_ENTRY_ACL_USER_OBJ:
562                               if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
563                                         length += 6; /* "owner@" */
564                                         break;
565                               }
566                               /* FALLTHROUGH */
567                     case ARCHIVE_ENTRY_ACL_USER:
568                     case ARCHIVE_ENTRY_ACL_MASK:
569                               length += 4; /* "user", "mask" */
570                               break;
571                     case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
572                               if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
573                                         length += 6; /* "group@" */
574                                         break;
575                               }
576                               /* FALLTHROUGH */
577                     case ARCHIVE_ENTRY_ACL_GROUP:
578                     case ARCHIVE_ENTRY_ACL_OTHER:
579                               length += 5; /* "group", "other" */
580                               break;
581                     case ARCHIVE_ENTRY_ACL_EVERYONE:
582                               length += 9; /* "everyone@" */
583                               break;
584                     }
585                     length += 1; /* colon after tag */
586                     if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
587                         ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
588                               if (wide) {
589                                         r = archive_mstring_get_wcs(a, &ap->name,
590                                             &wname);
591                                         if (r == 0 && wname != NULL)
592                                                   length += wcslen(wname);
593                                         else if (r < 0 && errno == ENOMEM)
594                                                   return (0);
595                                         else
596                                                   length += sizeof(uid_t) * 3 + 1;
597                               } else {
598                                         r = archive_mstring_get_mbs_l(a, &ap->name, &name,
599                                             &len, sc);
600                                         if (r != 0)
601                                                   return (0);
602                                         if (len > 0 && name != NULL)
603                                                   length += len;
604                                         else
605                                                   length += sizeof(uid_t) * 3 + 1;
606                               }
607                               length += 1; /* colon after user or group name */
608                     } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
609                               length += 1; /* 2nd colon empty user,group or other */
610 
611                     if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
612                         && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
613                         && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
614                         || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
615                               /* Solaris has no colon after other: and mask: */
616                               length = length - 1;
617                     }
618 
619                     if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
620                               /* rwxpdDaARWcCos:fdinSFI:deny */
621                               length += 27;
622                               if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
623                                         length += 1; /* allow, alarm, audit */
624                     } else
625                               length += 3; /* rwx */
626 
627                     if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
628                         ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
629                         (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
630                               length += 1; /* colon */
631                               /* ID digit count */
632                               idlen = 1;
633                               tmp = ap->id;
634                               while (tmp > 9) {
635                                         tmp = tmp / 10;
636                                         idlen++;
637                               }
638                               length += idlen;
639                     }
640                     length ++; /* entry separator */
641           }
642 
643           /* Add filemode-mapping access entries to the length */
644           if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
645                     if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
646                               /* "user::rwx\ngroup::rwx\nother:rwx\n" */
647                               length += 31;
648                     } else {
649                               /* "user::rwx\ngroup::rwx\nother::rwx\n" */
650                               length += 32;
651                     }
652           } else if (count == 0)
653                     return (0);
654 
655           /* The terminating character is included in count */
656           return (length);
657 }
658 
659 /*
660  * Generate a wide text version of the ACL. The flags parameter controls
661  * the type and style of the generated ACL.
662  */
663 wchar_t *
archive_acl_to_text_w(struct archive_acl * acl,ssize_t * text_len,int flags,struct archive * a)664 archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
665     struct archive *a)
666 {
667           int count;
668           ssize_t length;
669           size_t len;
670           const wchar_t *wname;
671           const wchar_t *prefix;
672           wchar_t separator;
673           struct archive_acl_entry *ap;
674           int id, r, want_type;
675           wchar_t *wp, *ws;
676 
677           want_type = archive_acl_text_want_type(acl, flags);
678 
679           /* Both NFSv4 and POSIX.1 types found */
680           if (want_type == 0)
681                     return (NULL);
682 
683           if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
684                     flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
685 
686           length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
687 
688           if (length == 0)
689                     return (NULL);
690 
691           if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
692                     separator = L',';
693           else
694                     separator = L'\n';
695 
696           /* Now, allocate the string and actually populate it. */
697           wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
698           if (wp == NULL) {
699                     if (errno == ENOMEM)
700                               __archive_errx(1, "No memory");
701                     return (NULL);
702           }
703           count = 0;
704 
705           if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
706                     append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
707                         ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
708                         acl->mode & 0700, -1);
709                     *wp++ = separator;
710                     append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
711                         ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
712                         acl->mode & 0070, -1);
713                     *wp++ = separator;
714                     append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715                         ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
716                         acl->mode & 0007, -1);
717                     count += 3;
718           }
719 
720           for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
721                     if ((ap->type & want_type) == 0)
722                               continue;
723                     /*
724                      * Filemode-mapping ACL entries are stored exclusively in
725                      * ap->mode so they should not be in the list
726                      */
727                     if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
728                         && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
729                         || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
730                         || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
731                               continue;
732                     if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
733                         (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
734                               prefix = L"default:";
735                     else
736                               prefix = NULL;
737                     r = archive_mstring_get_wcs(a, &ap->name, &wname);
738                     if (r == 0) {
739                               if (count > 0)
740                                         *wp++ = separator;
741                               if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
742                                         id = ap->id;
743                               else
744                                         id = -1;
745                               append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
746                                   wname, ap->permset, id);
747                               count++;
748                     } else if (r < 0 && errno == ENOMEM) {
749                               free(ws);
750                               return (NULL);
751                     }
752           }
753 
754           /* Add terminating character */
755           *wp++ = L'\0';
756 
757           len = wcslen(ws);
758 
759           if ((ssize_t)len > (length - 1))
760                     __archive_errx(1, "Buffer overrun");
761 
762           if (text_len != NULL)
763                     *text_len = len;
764 
765           return (ws);
766 }
767 
768 static void
append_id_w(wchar_t ** wp,int id)769 append_id_w(wchar_t **wp, int id)
770 {
771           if (id < 0)
772                     id = 0;
773           if (id > 9)
774                     append_id_w(wp, id / 10);
775           *(*wp)++ = L"0123456789"[id % 10];
776 }
777 
778 static void
append_entry_w(wchar_t ** wp,const wchar_t * prefix,int type,int tag,int flags,const wchar_t * wname,int perm,int id)779 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
780     int tag, int flags, const wchar_t *wname, int perm, int id)
781 {
782           int i;
783 
784           if (prefix != NULL) {
785                     wcscpy(*wp, prefix);
786                     *wp += wcslen(*wp);
787           }
788           switch (tag) {
789           case ARCHIVE_ENTRY_ACL_USER_OBJ:
790                     wname = NULL;
791                     id = -1;
792                     if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
793                               wcscpy(*wp, L"owner@");
794                               break;
795                     }
796                     /* FALLTHROUGH */
797           case ARCHIVE_ENTRY_ACL_USER:
798                     wcscpy(*wp, L"user");
799                     break;
800           case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
801                     wname = NULL;
802                     id = -1;
803                     if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
804                               wcscpy(*wp, L"group@");
805                               break;
806                     }
807                     /* FALLTHROUGH */
808           case ARCHIVE_ENTRY_ACL_GROUP:
809                     wcscpy(*wp, L"group");
810                     break;
811           case ARCHIVE_ENTRY_ACL_MASK:
812                     wcscpy(*wp, L"mask");
813                     wname = NULL;
814                     id = -1;
815                     break;
816           case ARCHIVE_ENTRY_ACL_OTHER:
817                     wcscpy(*wp, L"other");
818                     wname = NULL;
819                     id = -1;
820                     break;
821           case ARCHIVE_ENTRY_ACL_EVERYONE:
822                     wcscpy(*wp, L"everyone@");
823                     wname = NULL;
824                     id = -1;
825                     break;
826           }
827           *wp += wcslen(*wp);
828           *(*wp)++ = L':';
829           if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
830               tag == ARCHIVE_ENTRY_ACL_USER ||
831               tag == ARCHIVE_ENTRY_ACL_GROUP) {
832                     if (wname != NULL) {
833                               wcscpy(*wp, wname);
834                               *wp += wcslen(*wp);
835                     } else if (tag == ARCHIVE_ENTRY_ACL_USER
836                         || tag == ARCHIVE_ENTRY_ACL_GROUP) {
837                               append_id_w(wp, id);
838                               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
839                                         id = -1;
840                     }
841                     /* Solaris style has no second colon after other and mask */
842                     if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
843                         || (tag != ARCHIVE_ENTRY_ACL_OTHER
844                         && tag != ARCHIVE_ENTRY_ACL_MASK))
845                               *(*wp)++ = L':';
846           }
847           if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
848                     /* POSIX.1e ACL perms */
849                     *(*wp)++ = (perm & 0444) ? L'r' : L'-';
850                     *(*wp)++ = (perm & 0222) ? L'w' : L'-';
851                     *(*wp)++ = (perm & 0111) ? L'x' : L'-';
852           } else {
853                     /* NFSv4 ACL perms */
854                     for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
855                               if (perm & nfsv4_acl_perm_map[i].perm)
856                                         *(*wp)++ = nfsv4_acl_perm_map[i].wc;
857                               else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
858                                         *(*wp)++ = L'-';
859                     }
860                     *(*wp)++ = L':';
861                     for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
862                               if (perm & nfsv4_acl_flag_map[i].perm)
863                                         *(*wp)++ = nfsv4_acl_flag_map[i].wc;
864                               else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
865                                         *(*wp)++ = L'-';
866                     }
867                     *(*wp)++ = L':';
868                     switch (type) {
869                     case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
870                               wcscpy(*wp, L"allow");
871                               break;
872                     case ARCHIVE_ENTRY_ACL_TYPE_DENY:
873                               wcscpy(*wp, L"deny");
874                               break;
875                     case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
876                               wcscpy(*wp, L"audit");
877                               break;
878                     case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
879                               wcscpy(*wp, L"alarm");
880                               break;
881                     default:
882                               break;
883                     }
884                     *wp += wcslen(*wp);
885           }
886           if (id != -1) {
887                     *(*wp)++ = L':';
888                     append_id_w(wp, id);
889           }
890 }
891 
892 /*
893  * Generate a text version of the ACL. The flags parameter controls
894  * the type and style of the generated ACL.
895  */
896 char *
archive_acl_to_text_l(struct archive_acl * acl,ssize_t * text_len,int flags,struct archive_string_conv * sc)897 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
898     struct archive_string_conv *sc)
899 {
900           int count;
901           ssize_t length;
902           size_t len;
903           const char *name;
904           const char *prefix;
905           char separator;
906           struct archive_acl_entry *ap;
907           int id, r, want_type;
908           char *p, *s;
909 
910           want_type = archive_acl_text_want_type(acl, flags);
911 
912           /* Both NFSv4 and POSIX.1 types found */
913           if (want_type == 0)
914                     return (NULL);
915 
916           if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
917                     flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
918 
919           length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
920 
921           if (length == 0)
922                     return (NULL);
923 
924           if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
925                     separator = ',';
926           else
927                     separator = '\n';
928 
929           /* Now, allocate the string and actually populate it. */
930           p = s = (char *)malloc(length * sizeof(char));
931           if (p == NULL) {
932                     if (errno == ENOMEM)
933                               __archive_errx(1, "No memory");
934                     return (NULL);
935           }
936           count = 0;
937 
938           if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
939                     append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
940                         ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
941                         acl->mode & 0700, -1);
942                     *p++ = separator;
943                     append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
944                         ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
945                         acl->mode & 0070, -1);
946                     *p++ = separator;
947                     append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
948                         ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
949                         acl->mode & 0007, -1);
950                     count += 3;
951           }
952 
953           for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
954                     if ((ap->type & want_type) == 0)
955                               continue;
956                     /*
957                      * Filemode-mapping ACL entries are stored exclusively in
958                      * ap->mode so they should not be in the list
959                      */
960                     if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
961                         && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
962                         || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
963                         || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
964                               continue;
965                     if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
966                         (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
967                               prefix = "default:";
968                     else
969                               prefix = NULL;
970                     r = archive_mstring_get_mbs_l(
971                         NULL, &ap->name, &name, &len, sc);
972                     if (r != 0) {
973                               free(s);
974                               return (NULL);
975                     }
976                     if (count > 0)
977                               *p++ = separator;
978                     if (name == NULL ||
979                         (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
980                               id = ap->id;
981                     } else {
982                               id = -1;
983                     }
984                     append_entry(&p, prefix, ap->type, ap->tag, flags, name,
985                         ap->permset, id);
986                     count++;
987           }
988 
989           /* Add terminating character */
990           *p++ = '\0';
991 
992           len = strlen(s);
993 
994           if ((ssize_t)len > (length - 1))
995                     __archive_errx(1, "Buffer overrun");
996 
997           if (text_len != NULL)
998                     *text_len = len;
999 
1000           return (s);
1001 }
1002 
1003 static void
append_id(char ** p,int id)1004 append_id(char **p, int id)
1005 {
1006           if (id < 0)
1007                     id = 0;
1008           if (id > 9)
1009                     append_id(p, id / 10);
1010           *(*p)++ = "0123456789"[id % 10];
1011 }
1012 
1013 static void
append_entry(char ** p,const char * prefix,int type,int tag,int flags,const char * name,int perm,int id)1014 append_entry(char **p, const char *prefix, int type,
1015     int tag, int flags, const char *name, int perm, int id)
1016 {
1017           int i;
1018 
1019           if (prefix != NULL) {
1020                     strcpy(*p, prefix);
1021                     *p += strlen(*p);
1022           }
1023           switch (tag) {
1024           case ARCHIVE_ENTRY_ACL_USER_OBJ:
1025                     name = NULL;
1026                     id = -1;
1027                     if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1028                               strcpy(*p, "owner@");
1029                               break;
1030                     }
1031                     /* FALLTHROUGH */
1032           case ARCHIVE_ENTRY_ACL_USER:
1033                     strcpy(*p, "user");
1034                     break;
1035           case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1036                     name = NULL;
1037                     id = -1;
1038                     if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1039                               strcpy(*p, "group@");
1040                               break;
1041                     }
1042                     /* FALLTHROUGH */
1043           case ARCHIVE_ENTRY_ACL_GROUP:
1044                     strcpy(*p, "group");
1045                     break;
1046           case ARCHIVE_ENTRY_ACL_MASK:
1047                     strcpy(*p, "mask");
1048                     name = NULL;
1049                     id = -1;
1050                     break;
1051           case ARCHIVE_ENTRY_ACL_OTHER:
1052                     strcpy(*p, "other");
1053                     name = NULL;
1054                     id = -1;
1055                     break;
1056           case ARCHIVE_ENTRY_ACL_EVERYONE:
1057                     strcpy(*p, "everyone@");
1058                     name = NULL;
1059                     id = -1;
1060                     break;
1061           }
1062           *p += strlen(*p);
1063           *(*p)++ = ':';
1064           if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1065               tag == ARCHIVE_ENTRY_ACL_USER ||
1066               tag == ARCHIVE_ENTRY_ACL_GROUP) {
1067                     if (name != NULL) {
1068                               strcpy(*p, name);
1069                               *p += strlen(*p);
1070                     } else if (tag == ARCHIVE_ENTRY_ACL_USER
1071                         || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1072                               append_id(p, id);
1073                               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1074                                         id = -1;
1075                     }
1076                     /* Solaris style has no second colon after other and mask */
1077                     if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1078                         || (tag != ARCHIVE_ENTRY_ACL_OTHER
1079                         && tag != ARCHIVE_ENTRY_ACL_MASK))
1080                               *(*p)++ = ':';
1081           }
1082           if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1083                     /* POSIX.1e ACL perms */
1084                     *(*p)++ = (perm & 0444) ? 'r' : '-';
1085                     *(*p)++ = (perm & 0222) ? 'w' : '-';
1086                     *(*p)++ = (perm & 0111) ? 'x' : '-';
1087           } else {
1088                     /* NFSv4 ACL perms */
1089                     for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1090                               if (perm & nfsv4_acl_perm_map[i].perm)
1091                                         *(*p)++ = nfsv4_acl_perm_map[i].c;
1092                               else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1093                                         *(*p)++ = '-';
1094                     }
1095                     *(*p)++ = ':';
1096                     for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1097                               if (perm & nfsv4_acl_flag_map[i].perm)
1098                                         *(*p)++ = nfsv4_acl_flag_map[i].c;
1099                               else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100                                         *(*p)++ = '-';
1101                     }
1102                     *(*p)++ = ':';
1103                     switch (type) {
1104                     case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1105                               strcpy(*p, "allow");
1106                               break;
1107                     case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1108                               strcpy(*p, "deny");
1109                               break;
1110                     case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1111                               strcpy(*p, "audit");
1112                               break;
1113                     case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1114                               strcpy(*p, "alarm");
1115                               break;
1116                     }
1117                     *p += strlen(*p);
1118           }
1119           if (id != -1) {
1120                     *(*p)++ = ':';
1121                     append_id(p, id);
1122           }
1123 }
1124 
1125 /*
1126  * Parse a wide ACL text string.
1127  *
1128  * The want_type argument may be one of the following:
1129  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1130  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1131  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1132  *
1133  * POSIX.1e ACL entries prefixed with "default:" are treated as
1134  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1135  */
1136 int
archive_acl_from_text_w(struct archive_acl * acl,const wchar_t * text,int want_type)1137 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1138     int want_type)
1139 {
1140           struct {
1141                     const wchar_t *start;
1142                     const wchar_t *end;
1143           } field[6], name;
1144 
1145           const wchar_t *s, *st;
1146 
1147           int numfields, fields, n, r, sol, ret;
1148           int type, types, tag, permset, id;
1149           size_t len;
1150           wchar_t sep;
1151 
1152           ret = ARCHIVE_OK;
1153           types = 0;
1154 
1155           switch (want_type) {
1156           case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1157                     want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1158                     __LA_FALLTHROUGH;
1159           case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1160           case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1161                     numfields = 5;
1162                     break;
1163           case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1164                     numfields = 6;
1165                     break;
1166           default:
1167                     return (ARCHIVE_FATAL);
1168           }
1169 
1170           while (text != NULL && *text != L'\0') {
1171                     /*
1172                      * Parse the fields out of the next entry,
1173                      * advance 'text' to start of next entry.
1174                      */
1175                     fields = 0;
1176                     do {
1177                               const wchar_t *start, *end;
1178                               next_field_w(&text, &start, &end, &sep);
1179                               if (fields < numfields) {
1180                                         field[fields].start = start;
1181                                         field[fields].end = end;
1182                               }
1183                               ++fields;
1184                     } while (sep == L':');
1185 
1186                     /* Set remaining fields to blank. */
1187                     for (n = fields; n < numfields; ++n)
1188                               field[n].start = field[n].end = NULL;
1189 
1190                     if (field[0].start != NULL && *(field[0].start) == L'#') {
1191                               /* Comment, skip entry */
1192                               continue;
1193                     }
1194 
1195                     n = 0;
1196                     sol = 0;
1197                     id = -1;
1198                     permset = 0;
1199                     name.start = name.end = NULL;
1200 
1201                     if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1202                               /* POSIX.1e ACLs */
1203                               /*
1204                                * Default keyword "default:user::rwx"
1205                                * if found, we have one more field
1206                                *
1207                                * We also support old Solaris extension:
1208                                * "defaultuser::rwx" is the default ACL corresponding
1209                                * to "user::rwx", etc. valid only for first field
1210                                */
1211                               s = field[0].start;
1212                               len = field[0].end - field[0].start;
1213                               if (*s == L'd' && (len == 1 || (len >= 7
1214                                   && wmemcmp((s + 1), L"efault", 6) == 0))) {
1215                                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1216                                         if (len > 7)
1217                                                   field[0].start += 7;
1218                                         else
1219                                                   n = 1;
1220                               } else
1221                                         type = want_type;
1222 
1223                               /* Check for a numeric ID in field n+1 or n+3. */
1224                               isint_w(field[n + 1].start, field[n + 1].end, &id);
1225                               /* Field n+3 is optional. */
1226                               if (id == -1 && fields > n+3)
1227                                         isint_w(field[n + 3].start, field[n + 3].end,
1228                                             &id);
1229 
1230                               tag = 0;
1231                               s = field[n].start;
1232                               st = field[n].start + 1;
1233                               len = field[n].end - field[n].start;
1234 
1235                               switch (*s) {
1236                               case L'u':
1237                                         if (len == 1 || (len == 4
1238                                             && wmemcmp(st, L"ser", 3) == 0))
1239                                                   tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1240                                         break;
1241                               case L'g':
1242                                         if (len == 1 || (len == 5
1243                                             && wmemcmp(st, L"roup", 4) == 0))
1244                                                   tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1245                                         break;
1246                               case L'o':
1247                                         if (len == 1 || (len == 5
1248                                             && wmemcmp(st, L"ther", 4) == 0))
1249                                                   tag = ARCHIVE_ENTRY_ACL_OTHER;
1250                                         break;
1251                               case L'm':
1252                                         if (len == 1 || (len == 4
1253                                             && wmemcmp(st, L"ask", 3) == 0))
1254                                                   tag = ARCHIVE_ENTRY_ACL_MASK;
1255                                         break;
1256                               default:
1257                                                   break;
1258                               }
1259 
1260                               switch (tag) {
1261                               case ARCHIVE_ENTRY_ACL_OTHER:
1262                               case ARCHIVE_ENTRY_ACL_MASK:
1263                                         if (fields == (n + 2)
1264                                             && field[n + 1].start < field[n + 1].end
1265                                             && ismode_w(field[n + 1].start,
1266                                             field[n + 1].end, &permset)) {
1267                                                   /* This is Solaris-style "other:rwx" */
1268                                                   sol = 1;
1269                                         } else if (fields == (n + 3) &&
1270                                             field[n + 1].start < field[n + 1].end) {
1271                                                   /* Invalid mask or other field */
1272                                                   ret = ARCHIVE_WARN;
1273                                                   continue;
1274                                         }
1275                                         break;
1276                               case ARCHIVE_ENTRY_ACL_USER_OBJ:
1277                               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1278                                         if (id != -1 ||
1279                                             field[n + 1].start < field[n + 1].end) {
1280                                                   name = field[n + 1];
1281                                                   if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1282                                                             tag = ARCHIVE_ENTRY_ACL_USER;
1283                                                   else
1284                                                             tag = ARCHIVE_ENTRY_ACL_GROUP;
1285                                         }
1286                                         break;
1287                               default:
1288                                         /* Invalid tag, skip entry */
1289                                         ret = ARCHIVE_WARN;
1290                                         continue;
1291                               }
1292 
1293                               /*
1294                                * Without "default:" we expect mode in field 2
1295                                * Exception: Solaris other and mask fields
1296                                */
1297                               if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1298                                   field[n + 2 - sol].end, &permset)) {
1299                                         /* Invalid mode, skip entry */
1300                                         ret = ARCHIVE_WARN;
1301                                         continue;
1302                               }
1303                     } else {
1304                               /* NFS4 ACLs */
1305                               s = field[0].start;
1306                               len = field[0].end - field[0].start;
1307                               tag = 0;
1308 
1309                               switch (len) {
1310                               case 4:
1311                                         if (wmemcmp(s, L"user", 4) == 0)
1312                                                   tag = ARCHIVE_ENTRY_ACL_USER;
1313                                         break;
1314                               case 5:
1315                                         if (wmemcmp(s, L"group", 5) == 0)
1316                                                   tag = ARCHIVE_ENTRY_ACL_GROUP;
1317                                         break;
1318                               case 6:
1319                                         if (wmemcmp(s, L"owner@", 6) == 0)
1320                                                   tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1321                                         else if (wmemcmp(s, L"group@", len) == 0)
1322                                                   tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1323                                         break;
1324                               case 9:
1325                                         if (wmemcmp(s, L"everyone@", 9) == 0)
1326                                                   tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1327                               default:
1328                                         break;
1329                               }
1330 
1331                               if (tag == 0) {
1332                                         /* Invalid tag, skip entry */
1333                                         ret = ARCHIVE_WARN;
1334                                         continue;
1335                               } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1336                                   tag == ARCHIVE_ENTRY_ACL_GROUP) {
1337                                         n = 1;
1338                                         name = field[1];
1339                                         isint_w(name.start, name.end, &id);
1340                               } else
1341                                         n = 0;
1342 
1343                               if (!is_nfs4_perms_w(field[1 + n].start,
1344                                   field[1 + n].end, &permset)) {
1345                                         /* Invalid NFSv4 perms, skip entry */
1346                                         ret = ARCHIVE_WARN;
1347                                         continue;
1348                               }
1349                               if (!is_nfs4_flags_w(field[2 + n].start,
1350                                   field[2 + n].end, &permset)) {
1351                                         /* Invalid NFSv4 flags, skip entry */
1352                                         ret = ARCHIVE_WARN;
1353                                         continue;
1354                               }
1355                               s = field[3 + n].start;
1356                               len = field[3 + n].end - field[3 + n].start;
1357                               type = 0;
1358                               if (len == 4) {
1359                                         if (wmemcmp(s, L"deny", 4) == 0)
1360                                                   type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1361                               } else if (len == 5) {
1362                                         if (wmemcmp(s, L"allow", 5) == 0)
1363                                                   type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1364                                         else if (wmemcmp(s, L"audit", 5) == 0)
1365                                                   type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1366                                         else if (wmemcmp(s, L"alarm", 5) == 0)
1367                                                   type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1368                               }
1369                               if (type == 0) {
1370                                         /* Invalid entry type, skip entry */
1371                                         ret = ARCHIVE_WARN;
1372                                         continue;
1373                               }
1374                               isint_w(field[4 + n].start, field[4 + n].end, &id);
1375                     }
1376 
1377                     /* Add entry to the internal list. */
1378                     r = archive_acl_add_entry_w_len(acl, type, permset,
1379                         tag, id, name.start, name.end - name.start);
1380                     if (r < ARCHIVE_WARN)
1381                               return (r);
1382                     if (r != ARCHIVE_OK)
1383                               ret = ARCHIVE_WARN;
1384                     types |= type;
1385           }
1386 
1387           /* Reset ACL */
1388           archive_acl_reset(acl, types);
1389 
1390           return (ret);
1391 }
1392 
1393 /*
1394  * Parse a string to a positive decimal integer.  Returns true if
1395  * the string is non-empty and consists only of decimal digits,
1396  * false otherwise.
1397  */
1398 static int
isint_w(const wchar_t * start,const wchar_t * end,int * result)1399 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1400 {
1401           int n = 0;
1402           if (start >= end)
1403                     return (0);
1404           while (start < end) {
1405                     if (*start < L'0' || *start > L'9')
1406                               return (0);
1407                     if (n > (INT_MAX / 10) ||
1408                         (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1409                               n = INT_MAX;
1410                     } else {
1411                               n *= 10;
1412                               n += *start - L'0';
1413                     }
1414                     start++;
1415           }
1416           *result = n;
1417           return (1);
1418 }
1419 
1420 /*
1421  * Parse a string as a mode field.  Returns true if
1422  * the string is non-empty and consists only of mode characters,
1423  * false otherwise.
1424  */
1425 static int
ismode_w(const wchar_t * start,const wchar_t * end,int * permset)1426 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1427 {
1428           const wchar_t *p;
1429 
1430           if (start >= end)
1431                     return (0);
1432           p = start;
1433           *permset = 0;
1434           while (p < end) {
1435                     switch (*p++) {
1436                     case L'r': case L'R':
1437                               *permset |= ARCHIVE_ENTRY_ACL_READ;
1438                               break;
1439                     case L'w': case L'W':
1440                               *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1441                               break;
1442                     case L'x': case L'X':
1443                               *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1444                               break;
1445                     case L'-':
1446                               break;
1447                     default:
1448                               return (0);
1449                     }
1450           }
1451           return (1);
1452 }
1453 
1454 /*
1455  * Parse a string as a NFS4 ACL permission field.
1456  * Returns true if the string is non-empty and consists only of NFS4 ACL
1457  * permission characters, false otherwise
1458  */
1459 static int
is_nfs4_perms_w(const wchar_t * start,const wchar_t * end,int * permset)1460 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1461 {
1462           const wchar_t *p = start;
1463 
1464           while (p < end) {
1465                     switch (*p++) {
1466                     case L'r':
1467                               *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1468                               break;
1469                     case L'w':
1470                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1471                               break;
1472                     case L'x':
1473                               *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1474                               break;
1475                     case L'p':
1476                               *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1477                               break;
1478                     case L'D':
1479                               *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1480                               break;
1481                     case L'd':
1482                               *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1483                               break;
1484                     case L'a':
1485                               *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1486                               break;
1487                     case L'A':
1488                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1489                               break;
1490                     case L'R':
1491                               *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1492                               break;
1493                     case L'W':
1494                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1495                               break;
1496                     case L'c':
1497                               *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1498                               break;
1499                     case L'C':
1500                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1501                               break;
1502                     case L'o':
1503                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1504                               break;
1505                     case L's':
1506                               *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1507                               break;
1508                     case L'-':
1509                               break;
1510                     default:
1511                               return(0);
1512                     }
1513           }
1514           return (1);
1515 }
1516 
1517 /*
1518  * Parse a string as a NFS4 ACL flags field.
1519  * Returns true if the string is non-empty and consists only of NFS4 ACL
1520  * flag characters, false otherwise
1521  */
1522 static int
is_nfs4_flags_w(const wchar_t * start,const wchar_t * end,int * permset)1523 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1524 {
1525           const wchar_t *p = start;
1526 
1527           while (p < end) {
1528                     switch(*p++) {
1529                     case L'f':
1530                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1531                               break;
1532                     case L'd':
1533                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1534                               break;
1535                     case L'i':
1536                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1537                               break;
1538                     case L'n':
1539                               *permset |=
1540                                   ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1541                               break;
1542                     case L'S':
1543                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1544                               break;
1545                     case L'F':
1546                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1547                               break;
1548                     case L'I':
1549                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1550                               break;
1551                     case L'-':
1552                               break;
1553                     default:
1554                               return (0);
1555                     }
1556           }
1557           return (1);
1558 }
1559 
1560 /*
1561  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1562  * to point to just after the separator.  *start points to the first
1563  * character of the matched text and *end just after the last
1564  * character of the matched identifier.  In particular *end - *start
1565  * is the length of the field body, not including leading or trailing
1566  * whitespace.
1567  */
1568 static void
next_field_w(const wchar_t ** wp,const wchar_t ** start,const wchar_t ** end,wchar_t * sep)1569 next_field_w(const wchar_t **wp, const wchar_t **start,
1570     const wchar_t **end, wchar_t *sep)
1571 {
1572           /* Skip leading whitespace to find start of field. */
1573           while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1574                     (*wp)++;
1575           }
1576           *start = *wp;
1577 
1578           /* Scan for the separator. */
1579           while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1580               **wp != L'\n' && **wp != L'#') {
1581                     (*wp)++;
1582           }
1583           *sep = **wp;
1584 
1585           /* Locate end of field, trim trailing whitespace if necessary */
1586           if (*wp == *start) {
1587                     *end = *wp;
1588           } else {
1589                     *end = *wp - 1;
1590                     while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1591                               (*end)--;
1592                     }
1593                     (*end)++;
1594           }
1595 
1596           /* Handle in-field comments */
1597           if (*sep == L'#') {
1598                     while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1599                               (*wp)++;
1600                     }
1601                     *sep = **wp;
1602           }
1603 
1604           /* Adjust scanner location. */
1605           if (**wp != L'\0')
1606                     (*wp)++;
1607 }
1608 
1609 /*
1610  * Parse an ACL text string.
1611  *
1612  * The want_type argument may be one of the following:
1613  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1614  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1615  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1616  *
1617  * POSIX.1e ACL entries prefixed with "default:" are treated as
1618  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1619  */
1620 int
archive_acl_from_text_l(struct archive_acl * acl,const char * text,int want_type,struct archive_string_conv * sc)1621 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1622     int want_type, struct archive_string_conv *sc)
1623 {
1624           struct {
1625                     const char *start;
1626                     const char *end;
1627           } field[6], name;
1628 
1629           const char *s, *st;
1630           int numfields, fields, n, r, sol, ret;
1631           int type, types, tag, permset, id;
1632           size_t len;
1633           char sep;
1634 
1635           switch (want_type) {
1636           case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1637                     want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1638                     __LA_FALLTHROUGH;
1639           case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1640           case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1641                     numfields = 5;
1642                     break;
1643           case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1644                     numfields = 6;
1645                     break;
1646           default:
1647                     return (ARCHIVE_FATAL);
1648           }
1649 
1650           ret = ARCHIVE_OK;
1651           types = 0;
1652 
1653           while (text != NULL &&  *text != '\0') {
1654                     /*
1655                      * Parse the fields out of the next entry,
1656                      * advance 'text' to start of next entry.
1657                      */
1658                     fields = 0;
1659                     do {
1660                               const char *start, *end;
1661                               next_field(&text, &start, &end, &sep);
1662                               if (fields < numfields) {
1663                                         field[fields].start = start;
1664                                         field[fields].end = end;
1665                               }
1666                               ++fields;
1667                     } while (sep == ':');
1668 
1669                     /* Set remaining fields to blank. */
1670                     for (n = fields; n < numfields; ++n)
1671                               field[n].start = field[n].end = NULL;
1672 
1673                     if (field[0].start != NULL && *(field[0].start) == '#') {
1674                               /* Comment, skip entry */
1675                               continue;
1676                     }
1677 
1678                     n = 0;
1679                     sol = 0;
1680                     id = -1;
1681                     permset = 0;
1682                     name.start = name.end = NULL;
1683 
1684                     if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1685                               /* POSIX.1e ACLs */
1686                               /*
1687                                * Default keyword "default:user::rwx"
1688                                * if found, we have one more field
1689                                *
1690                                * We also support old Solaris extension:
1691                                * "defaultuser::rwx" is the default ACL corresponding
1692                                * to "user::rwx", etc. valid only for first field
1693                                */
1694                               s = field[0].start;
1695                               len = field[0].end - field[0].start;
1696                               if (*s == 'd' && (len == 1 || (len >= 7
1697                                   && memcmp((s + 1), "efault", 6) == 0))) {
1698                                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1699                                         if (len > 7)
1700                                                   field[0].start += 7;
1701                                         else
1702                                                   n = 1;
1703                               } else
1704                                         type = want_type;
1705 
1706                               /* Check for a numeric ID in field n+1 or n+3. */
1707                               isint(field[n + 1].start, field[n + 1].end, &id);
1708                               /* Field n+3 is optional. */
1709                               if (id == -1 && fields > (n + 3))
1710                                         isint(field[n + 3].start, field[n + 3].end,
1711                                             &id);
1712 
1713                               tag = 0;
1714                               s = field[n].start;
1715                               st = field[n].start + 1;
1716                               len = field[n].end - field[n].start;
1717 
1718                               if (len == 0) {
1719                                         ret = ARCHIVE_WARN;
1720                                         continue;
1721                               }
1722 
1723                               switch (*s) {
1724                               case 'u':
1725                                         if (len == 1 || (len == 4
1726                                             && memcmp(st, "ser", 3) == 0))
1727                                                   tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1728                                         break;
1729                               case 'g':
1730                                         if (len == 1 || (len == 5
1731                                             && memcmp(st, "roup", 4) == 0))
1732                                                   tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1733                                         break;
1734                               case 'o':
1735                                         if (len == 1 || (len == 5
1736                                             && memcmp(st, "ther", 4) == 0))
1737                                                   tag = ARCHIVE_ENTRY_ACL_OTHER;
1738                                         break;
1739                               case 'm':
1740                                         if (len == 1 || (len == 4
1741                                             && memcmp(st, "ask", 3) == 0))
1742                                                   tag = ARCHIVE_ENTRY_ACL_MASK;
1743                                         break;
1744                               default:
1745                                                   break;
1746                               }
1747 
1748                               switch (tag) {
1749                               case ARCHIVE_ENTRY_ACL_OTHER:
1750                               case ARCHIVE_ENTRY_ACL_MASK:
1751                                         if (fields == (n + 2)
1752                                             && field[n + 1].start < field[n + 1].end
1753                                             && ismode(field[n + 1].start,
1754                                             field[n + 1].end, &permset)) {
1755                                                   /* This is Solaris-style "other:rwx" */
1756                                                   sol = 1;
1757                                         } else if (fields == (n + 3) &&
1758                                             field[n + 1].start < field[n + 1].end) {
1759                                                   /* Invalid mask or other field */
1760                                                   ret = ARCHIVE_WARN;
1761                                                   continue;
1762                                         }
1763                                         break;
1764                               case ARCHIVE_ENTRY_ACL_USER_OBJ:
1765                               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1766                                         if (id != -1 ||
1767                                             field[n + 1].start < field[n + 1].end) {
1768                                                   name = field[n + 1];
1769                                                   if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1770                                                             tag = ARCHIVE_ENTRY_ACL_USER;
1771                                                   else
1772                                                             tag = ARCHIVE_ENTRY_ACL_GROUP;
1773                                         }
1774                                         break;
1775                               default:
1776                                         /* Invalid tag, skip entry */
1777                                         ret = ARCHIVE_WARN;
1778                                         continue;
1779                               }
1780 
1781                               /*
1782                                * Without "default:" we expect mode in field 3
1783                                * Exception: Solaris other and mask fields
1784                                */
1785                               if (permset == 0 && !ismode(field[n + 2 - sol].start,
1786                                   field[n + 2 - sol].end, &permset)) {
1787                                         /* Invalid mode, skip entry */
1788                                         ret = ARCHIVE_WARN;
1789                                         continue;
1790                               }
1791                     } else {
1792                               /* NFS4 ACLs */
1793                               s = field[0].start;
1794                               len = field[0].end - field[0].start;
1795                               tag = 0;
1796 
1797                               switch (len) {
1798                               case 4:
1799                                         if (memcmp(s, "user", 4) == 0)
1800                                                   tag = ARCHIVE_ENTRY_ACL_USER;
1801                                         break;
1802                               case 5:
1803                                         if (memcmp(s, "group", 5) == 0)
1804                                                   tag = ARCHIVE_ENTRY_ACL_GROUP;
1805                                         break;
1806                               case 6:
1807                                         if (memcmp(s, "owner@", 6) == 0)
1808                                                   tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1809                                         else if (memcmp(s, "group@", 6) == 0)
1810                                                   tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1811                                         break;
1812                               case 9:
1813                                         if (memcmp(s, "everyone@", 9) == 0)
1814                                                   tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1815                                         break;
1816                               default:
1817                                         break;
1818                               }
1819 
1820                               if (tag == 0) {
1821                                         /* Invalid tag, skip entry */
1822                                         ret = ARCHIVE_WARN;
1823                                         continue;
1824                               } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1825                                   tag == ARCHIVE_ENTRY_ACL_GROUP) {
1826                                         n = 1;
1827                                         name = field[1];
1828                                         isint(name.start, name.end, &id);
1829                               } else
1830                                         n = 0;
1831 
1832                               if (!is_nfs4_perms(field[1 + n].start,
1833                                   field[1 + n].end, &permset)) {
1834                                         /* Invalid NFSv4 perms, skip entry */
1835                                         ret = ARCHIVE_WARN;
1836                                         continue;
1837                               }
1838                               if (!is_nfs4_flags(field[2 + n].start,
1839                                   field[2 + n].end, &permset)) {
1840                                         /* Invalid NFSv4 flags, skip entry */
1841                                         ret = ARCHIVE_WARN;
1842                                         continue;
1843                               }
1844                               s = field[3 + n].start;
1845                               len = field[3 + n].end - field[3 + n].start;
1846                               type = 0;
1847                               if (len == 4) {
1848                                         if (memcmp(s, "deny", 4) == 0)
1849                                                   type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1850                               } else if (len == 5) {
1851                                         if (memcmp(s, "allow", 5) == 0)
1852                                                   type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1853                                         else if (memcmp(s, "audit", 5) == 0)
1854                                                   type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1855                                         else if (memcmp(s, "alarm", 5) == 0)
1856                                                   type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1857                               }
1858                               if (type == 0) {
1859                                         /* Invalid entry type, skip entry */
1860                                         ret = ARCHIVE_WARN;
1861                                         continue;
1862                               }
1863                               isint(field[4 + n].start, field[4 + n].end,
1864                                   &id);
1865                     }
1866 
1867                     /* Add entry to the internal list. */
1868                     r = archive_acl_add_entry_len_l(acl, type, permset,
1869                         tag, id, name.start, name.end - name.start, sc);
1870                     if (r < ARCHIVE_WARN)
1871                               return (r);
1872                     if (r != ARCHIVE_OK)
1873                               ret = ARCHIVE_WARN;
1874                     types |= type;
1875           }
1876 
1877           /* Reset ACL */
1878           archive_acl_reset(acl, types);
1879 
1880           return (ret);
1881 }
1882 
1883 /*
1884  * Parse a string to a positive decimal integer.  Returns true if
1885  * the string is non-empty and consists only of decimal digits,
1886  * false otherwise.
1887  */
1888 static int
isint(const char * start,const char * end,int * result)1889 isint(const char *start, const char *end, int *result)
1890 {
1891           int n = 0;
1892           if (start >= end)
1893                     return (0);
1894           while (start < end) {
1895                     if (*start < '0' || *start > '9')
1896                               return (0);
1897                     if (n > (INT_MAX / 10) ||
1898                         (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1899                               n = INT_MAX;
1900                     } else {
1901                               n *= 10;
1902                               n += *start - '0';
1903                     }
1904                     start++;
1905           }
1906           *result = n;
1907           return (1);
1908 }
1909 
1910 /*
1911  * Parse a string as a mode field.  Returns true if
1912  * the string is non-empty and consists only of mode characters,
1913  * false otherwise.
1914  */
1915 static int
ismode(const char * start,const char * end,int * permset)1916 ismode(const char *start, const char *end, int *permset)
1917 {
1918           const char *p;
1919 
1920           if (start >= end)
1921                     return (0);
1922           p = start;
1923           *permset = 0;
1924           while (p < end) {
1925                     switch (*p++) {
1926                     case 'r': case 'R':
1927                               *permset |= ARCHIVE_ENTRY_ACL_READ;
1928                               break;
1929                     case 'w': case 'W':
1930                               *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1931                               break;
1932                     case 'x': case 'X':
1933                               *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1934                               break;
1935                     case '-':
1936                               break;
1937                     default:
1938                               return (0);
1939                     }
1940           }
1941           return (1);
1942 }
1943 
1944 /*
1945  * Parse a string as a NFS4 ACL permission field.
1946  * Returns true if the string is non-empty and consists only of NFS4 ACL
1947  * permission characters, false otherwise
1948  */
1949 static int
is_nfs4_perms(const char * start,const char * end,int * permset)1950 is_nfs4_perms(const char *start, const char *end, int *permset)
1951 {
1952           const char *p = start;
1953 
1954           while (p < end) {
1955                     switch (*p++) {
1956                     case 'r':
1957                               *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1958                               break;
1959                     case 'w':
1960                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1961                               break;
1962                     case 'x':
1963                               *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1964                               break;
1965                     case 'p':
1966                               *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1967                               break;
1968                     case 'D':
1969                               *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1970                               break;
1971                     case 'd':
1972                               *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1973                               break;
1974                     case 'a':
1975                               *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1976                               break;
1977                     case 'A':
1978                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1979                               break;
1980                     case 'R':
1981                               *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1982                               break;
1983                     case 'W':
1984                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1985                               break;
1986                     case 'c':
1987                               *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1988                               break;
1989                     case 'C':
1990                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1991                               break;
1992                     case 'o':
1993                               *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1994                               break;
1995                     case 's':
1996                               *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1997                               break;
1998                     case '-':
1999                               break;
2000                     default:
2001                               return(0);
2002                     }
2003           }
2004           return (1);
2005 }
2006 
2007 /*
2008  * Parse a string as a NFS4 ACL flags field.
2009  * Returns true if the string is non-empty and consists only of NFS4 ACL
2010  * flag characters, false otherwise
2011  */
2012 static int
is_nfs4_flags(const char * start,const char * end,int * permset)2013 is_nfs4_flags(const char *start, const char *end, int *permset)
2014 {
2015           const char *p = start;
2016 
2017           while (p < end) {
2018                     switch(*p++) {
2019                     case 'f':
2020                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2021                               break;
2022                     case 'd':
2023                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2024                               break;
2025                     case 'i':
2026                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2027                               break;
2028                     case 'n':
2029                               *permset |=
2030                                   ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2031                               break;
2032                     case 'S':
2033                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2034                               break;
2035                     case 'F':
2036                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2037                               break;
2038                     case 'I':
2039                               *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2040                               break;
2041                     case '-':
2042                               break;
2043                     default:
2044                               return (0);
2045                     }
2046           }
2047           return (1);
2048 }
2049 
2050 /*
2051  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2052  * to point to just after the separator.  *start points to the first
2053  * character of the matched text and *end just after the last
2054  * character of the matched identifier.  In particular *end - *start
2055  * is the length of the field body, not including leading or trailing
2056  * whitespace.
2057  */
2058 static void
next_field(const char ** p,const char ** start,const char ** end,char * sep)2059 next_field(const char **p, const char **start,
2060     const char **end, char *sep)
2061 {
2062           /* Skip leading whitespace to find start of field. */
2063           while (**p == ' ' || **p == '\t' || **p == '\n') {
2064                     (*p)++;
2065           }
2066           *start = *p;
2067 
2068           /* Scan for the separator. */
2069           while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
2070               **p != '#') {
2071                     (*p)++;
2072           }
2073           *sep = **p;
2074 
2075           /* Locate end of field, trim trailing whitespace if necessary */
2076           if (*p == *start) {
2077                     *end = *p;
2078           } else {
2079                     *end = *p - 1;
2080                     while (**end == ' ' || **end == '\t' || **end == '\n') {
2081                               (*end)--;
2082                     }
2083                     (*end)++;
2084           }
2085 
2086           /* Handle in-field comments */
2087           if (*sep == '#') {
2088                     while (**p != '\0' && **p != ',' && **p != '\n') {
2089                               (*p)++;
2090                     }
2091                     *sep = **p;
2092           }
2093 
2094           /* Adjust scanner location. */
2095           if (**p != '\0')
2096                     (*p)++;
2097 }
2098