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