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