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