xref: /freebsd/contrib/libarchive/libarchive/archive_acl.c (revision 2e113ef82465598b8c26e0ca415fbe90677fbd47)
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].end == NULL) {
1190 			/* This should never happen */
1191 			return (ARCHIVE_FATAL);
1192 		}
1193 
1194 		if (*(field[0].start) == L'#') {
1195 			/* Comment, skip entry */
1196 			continue;
1197 		}
1198 
1199 		n = 0;
1200 		sol = 0;
1201 		id = -1;
1202 		permset = 0;
1203 		name.start = name.end = NULL;
1204 
1205 		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1206 			/* POSIX.1e ACLs */
1207 			/*
1208 			 * Default keyword "default:user::rwx"
1209 			 * if found, we have one more field
1210 			 *
1211 			 * We also support old Solaris extension:
1212 			 * "defaultuser::rwx" is the default ACL corresponding
1213 			 * to "user::rwx", etc. valid only for first field
1214 			 */
1215 			s = field[0].start;
1216 			len = field[0].end - field[0].start;
1217 			if (*s == L'd' && (len == 1 || (len >= 7
1218 			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1219 				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1220 				if (len > 7)
1221 					field[0].start += 7;
1222 				else
1223 					n = 1;
1224 			} else
1225 				type = want_type;
1226 
1227 			/* Check for a numeric ID in field n+1 or n+3. */
1228 			isint_w(field[n + 1].start, field[n + 1].end, &id);
1229 			/* Field n+3 is optional. */
1230 			if (id == -1 && fields > n+3)
1231 				isint_w(field[n + 3].start, field[n + 3].end,
1232 				    &id);
1233 
1234 			tag = 0;
1235 			s = field[n].start;
1236 			st = field[n].start + 1;
1237 			len = field[n].end - field[n].start;
1238 
1239 			switch (*s) {
1240 			case L'u':
1241 				if (len == 1 || (len == 4
1242 				    && wmemcmp(st, L"ser", 3) == 0))
1243 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1244 				break;
1245 			case L'g':
1246 				if (len == 1 || (len == 5
1247 				    && wmemcmp(st, L"roup", 4) == 0))
1248 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1249 				break;
1250 			case L'o':
1251 				if (len == 1 || (len == 5
1252 				    && wmemcmp(st, L"ther", 4) == 0))
1253 					tag = ARCHIVE_ENTRY_ACL_OTHER;
1254 				break;
1255 			case L'm':
1256 				if (len == 1 || (len == 4
1257 				    && wmemcmp(st, L"ask", 3) == 0))
1258 					tag = ARCHIVE_ENTRY_ACL_MASK;
1259 				break;
1260 			default:
1261 					break;
1262 			}
1263 
1264 			switch (tag) {
1265 			case ARCHIVE_ENTRY_ACL_OTHER:
1266 			case ARCHIVE_ENTRY_ACL_MASK:
1267 				if (fields == (n + 2)
1268 				    && field[n + 1].start < field[n + 1].end
1269 				    && ismode_w(field[n + 1].start,
1270 				    field[n + 1].end, &permset)) {
1271 					/* This is Solaris-style "other:rwx" */
1272 					sol = 1;
1273 				} else if (fields == (n + 3) &&
1274 				    field[n + 1].start < field[n + 1].end) {
1275 					/* Invalid mask or other field */
1276 					ret = ARCHIVE_WARN;
1277 					continue;
1278 				}
1279 				break;
1280 			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1281 			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1282 				if (id != -1 ||
1283 				    field[n + 1].start < field[n + 1].end) {
1284 					name = field[n + 1];
1285 					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1286 						tag = ARCHIVE_ENTRY_ACL_USER;
1287 					else
1288 						tag = ARCHIVE_ENTRY_ACL_GROUP;
1289 				}
1290 				break;
1291 			default:
1292 				/* Invalid tag, skip entry */
1293 				ret = ARCHIVE_WARN;
1294 				continue;
1295 			}
1296 
1297 			/*
1298 			 * Without "default:" we expect mode in field 2
1299 			 * Exception: Solaris other and mask fields
1300 			 */
1301 			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1302 			    field[n + 2 - sol].end, &permset)) {
1303 				/* Invalid mode, skip entry */
1304 				ret = ARCHIVE_WARN;
1305 				continue;
1306 			}
1307 		} else {
1308 			/* NFS4 ACLs */
1309 			s = field[0].start;
1310 			len = field[0].end - field[0].start;
1311 			tag = 0;
1312 
1313 			switch (len) {
1314 			case 4:
1315 				if (wmemcmp(s, L"user", 4) == 0)
1316 					tag = ARCHIVE_ENTRY_ACL_USER;
1317 				break;
1318 			case 5:
1319 				if (wmemcmp(s, L"group", 5) == 0)
1320 					tag = ARCHIVE_ENTRY_ACL_GROUP;
1321 				break;
1322 			case 6:
1323 				if (wmemcmp(s, L"owner@", 6) == 0)
1324 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1325 				else if (wmemcmp(s, L"group@", len) == 0)
1326 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1327 				break;
1328 			case 9:
1329 				if (wmemcmp(s, L"everyone@", 9) == 0)
1330 					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1331 			default:
1332 				break;
1333 			}
1334 
1335 			if (tag == 0) {
1336 				/* Invalid tag, skip entry */
1337 				ret = ARCHIVE_WARN;
1338 				continue;
1339 			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1340 			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1341 				n = 1;
1342 				name = field[1];
1343 				isint_w(name.start, name.end, &id);
1344 			} else
1345 				n = 0;
1346 
1347 			if (!is_nfs4_perms_w(field[1 + n].start,
1348 			    field[1 + n].end, &permset)) {
1349 				/* Invalid NFSv4 perms, skip entry */
1350 				ret = ARCHIVE_WARN;
1351 				continue;
1352 			}
1353 			if (!is_nfs4_flags_w(field[2 + n].start,
1354 			    field[2 + n].end, &permset)) {
1355 				/* Invalid NFSv4 flags, skip entry */
1356 				ret = ARCHIVE_WARN;
1357 				continue;
1358 			}
1359 			s = field[3 + n].start;
1360 			len = field[3 + n].end - field[3 + n].start;
1361 			type = 0;
1362 			if (len == 4) {
1363 				if (wmemcmp(s, L"deny", 4) == 0)
1364 					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1365 			} else if (len == 5) {
1366 				if (wmemcmp(s, L"allow", 5) == 0)
1367 					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1368 				else if (wmemcmp(s, L"audit", 5) == 0)
1369 					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1370 				else if (wmemcmp(s, L"alarm", 5) == 0)
1371 					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1372 			}
1373 			if (type == 0) {
1374 				/* Invalid entry type, skip entry */
1375 				ret = ARCHIVE_WARN;
1376 				continue;
1377 			}
1378 			isint_w(field[4 + n].start, field[4 + n].end, &id);
1379 		}
1380 
1381 		/* Add entry to the internal list. */
1382 		r = archive_acl_add_entry_w_len(acl, type, permset,
1383 		    tag, id, name.start, name.end - name.start);
1384 		if (r < ARCHIVE_WARN)
1385 			return (r);
1386 		if (r != ARCHIVE_OK)
1387 			ret = ARCHIVE_WARN;
1388 		types |= type;
1389 	}
1390 
1391 	/* Reset ACL */
1392 	archive_acl_reset(acl, types);
1393 
1394 	return (ret);
1395 }
1396 
1397 /*
1398  * Parse a string to a positive decimal integer.  Returns true if
1399  * the string is non-empty and consists only of decimal digits,
1400  * false otherwise.
1401  */
1402 static int
isint_w(const wchar_t * start,const wchar_t * end,int * result)1403 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1404 {
1405 	int n = 0;
1406 	if (start >= end)
1407 		return (0);
1408 	while (start < end) {
1409 		if (*start < L'0' || *start > L'9')
1410 			return (0);
1411 		if (n > (INT_MAX / 10) ||
1412 		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1413 			n = INT_MAX;
1414 		} else {
1415 			n *= 10;
1416 			n += *start - L'0';
1417 		}
1418 		start++;
1419 	}
1420 	*result = n;
1421 	return (1);
1422 }
1423 
1424 /*
1425  * Parse a string as a mode field.  Returns true if
1426  * the string is non-empty and consists only of mode characters,
1427  * false otherwise.
1428  */
1429 static int
ismode_w(const wchar_t * start,const wchar_t * end,int * permset)1430 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1431 {
1432 	const wchar_t *p;
1433 
1434 	if (start >= end)
1435 		return (0);
1436 	p = start;
1437 	*permset = 0;
1438 	while (p < end) {
1439 		switch (*p++) {
1440 		case L'r': case L'R':
1441 			*permset |= ARCHIVE_ENTRY_ACL_READ;
1442 			break;
1443 		case L'w': case L'W':
1444 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1445 			break;
1446 		case L'x': case L'X':
1447 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1448 			break;
1449 		case L'-':
1450 			break;
1451 		default:
1452 			return (0);
1453 		}
1454 	}
1455 	return (1);
1456 }
1457 
1458 /*
1459  * Parse a string as a NFS4 ACL permission field.
1460  * Returns true if the string is non-empty and consists only of NFS4 ACL
1461  * permission characters, false otherwise
1462  */
1463 static int
is_nfs4_perms_w(const wchar_t * start,const wchar_t * end,int * permset)1464 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1465 {
1466 	const wchar_t *p = start;
1467 
1468 	while (p < end) {
1469 		switch (*p++) {
1470 		case L'r':
1471 			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1472 			break;
1473 		case L'w':
1474 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1475 			break;
1476 		case L'x':
1477 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1478 			break;
1479 		case L'p':
1480 			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1481 			break;
1482 		case L'D':
1483 			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1484 			break;
1485 		case L'd':
1486 			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1487 			break;
1488 		case L'a':
1489 			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1490 			break;
1491 		case L'A':
1492 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1493 			break;
1494 		case L'R':
1495 			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1496 			break;
1497 		case L'W':
1498 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1499 			break;
1500 		case L'c':
1501 			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1502 			break;
1503 		case L'C':
1504 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1505 			break;
1506 		case L'o':
1507 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1508 			break;
1509 		case L's':
1510 			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1511 			break;
1512 		case L'-':
1513 			break;
1514 		default:
1515 			return(0);
1516 		}
1517 	}
1518 	return (1);
1519 }
1520 
1521 /*
1522  * Parse a string as a NFS4 ACL flags field.
1523  * Returns true if the string is non-empty and consists only of NFS4 ACL
1524  * flag characters, false otherwise
1525  */
1526 static int
is_nfs4_flags_w(const wchar_t * start,const wchar_t * end,int * permset)1527 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1528 {
1529 	const wchar_t *p = start;
1530 
1531 	while (p < end) {
1532 		switch(*p++) {
1533 		case L'f':
1534 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1535 			break;
1536 		case L'd':
1537 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1538 			break;
1539 		case L'i':
1540 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1541 			break;
1542 		case L'n':
1543 			*permset |=
1544 			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1545 			break;
1546 		case L'S':
1547 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1548 			break;
1549 		case L'F':
1550 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1551 			break;
1552 		case L'I':
1553 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1554 			break;
1555 		case L'-':
1556 			break;
1557 		default:
1558 			return (0);
1559 		}
1560 	}
1561 	return (1);
1562 }
1563 
1564 /*
1565  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1566  * to point to just after the separator.  *start points to the first
1567  * character of the matched text and *end just after the last
1568  * character of the matched identifier.  In particular *end - *start
1569  * is the length of the field body, not including leading or trailing
1570  * whitespace.
1571  */
1572 static void
next_field_w(const wchar_t ** wp,const wchar_t ** start,const wchar_t ** end,wchar_t * sep)1573 next_field_w(const wchar_t **wp, const wchar_t **start,
1574     const wchar_t **end, wchar_t *sep)
1575 {
1576 	/* Skip leading whitespace to find start of field. */
1577 	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1578 		(*wp)++;
1579 	}
1580 	*start = *wp;
1581 
1582 	/* Scan for the separator. */
1583 	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1584 	    **wp != L'\n' && **wp != L'#') {
1585 		(*wp)++;
1586 	}
1587 	*sep = **wp;
1588 
1589 	/* Locate end of field, trim trailing whitespace if necessary */
1590 	if (*wp == *start) {
1591 		*end = *wp;
1592 	} else {
1593 		*end = *wp - 1;
1594 		while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1595 			(*end)--;
1596 		}
1597 		(*end)++;
1598 	}
1599 
1600 	/* Handle in-field comments */
1601 	if (*sep == L'#') {
1602 		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1603 			(*wp)++;
1604 		}
1605 		*sep = **wp;
1606 	}
1607 
1608 	/* Adjust scanner location. */
1609 	if (**wp != L'\0')
1610 		(*wp)++;
1611 }
1612 
1613 /*
1614  * Parse an ACL text string.
1615  *
1616  * The want_type argument may be one of the following:
1617  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1618  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1619  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1620  *
1621  * POSIX.1e ACL entries prefixed with "default:" are treated as
1622  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1623  */
1624 int
archive_acl_from_text_l(struct archive_acl * acl,const char * text,int want_type,struct archive_string_conv * sc)1625 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1626     int want_type, struct archive_string_conv *sc)
1627 {
1628 	return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);
1629 }
1630 
1631 int
archive_acl_from_text_nl(struct archive_acl * acl,const char * text,size_t length,int want_type,struct archive_string_conv * sc)1632 archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
1633     size_t length, int want_type, struct archive_string_conv *sc)
1634 {
1635 	struct {
1636 		const char *start;
1637 		const char *end;
1638 	} field[6], name;
1639 
1640 	const char *s, *st;
1641 	int numfields, fields, n, r, sol, ret;
1642 	int type, types, tag, permset, id;
1643 	size_t len;
1644 	char sep;
1645 
1646 	switch (want_type) {
1647 	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1648 		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1649 		__LA_FALLTHROUGH;
1650 	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1651 	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1652 		numfields = 5;
1653 		break;
1654 	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1655 		numfields = 6;
1656 		break;
1657 	default:
1658 		return (ARCHIVE_FATAL);
1659 	}
1660 
1661 	ret = ARCHIVE_OK;
1662 	types = 0;
1663 
1664 	while (text != NULL && length > 0 && *text != '\0') {
1665 		/*
1666 		 * Parse the fields out of the next entry,
1667 		 * advance 'text' to start of next entry.
1668 		 */
1669 		fields = 0;
1670 		do {
1671 			const char *start, *end;
1672 			next_field(&text, &length, &start, &end, &sep);
1673 			if (fields < numfields) {
1674 				field[fields].start = start;
1675 				field[fields].end = end;
1676 			}
1677 			++fields;
1678 		} while (sep == ':');
1679 
1680 		/* Set remaining fields to blank. */
1681 		for (n = fields; n < numfields; ++n)
1682 			field[n].start = field[n].end = NULL;
1683 
1684 		if (field[0].start == NULL || field[0].end == NULL) {
1685 			/* This should never happen */
1686 			return (ARCHIVE_FATAL);
1687 		}
1688 
1689 		if (*(field[0].start) == '#') {
1690 			/* Comment, skip entry */
1691 			continue;
1692 		}
1693 
1694 		n = 0;
1695 		sol = 0;
1696 		id = -1;
1697 		permset = 0;
1698 		name.start = name.end = NULL;
1699 
1700 		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1701 			/* POSIX.1e ACLs */
1702 			/*
1703 			 * Default keyword "default:user::rwx"
1704 			 * if found, we have one more field
1705 			 *
1706 			 * We also support old Solaris extension:
1707 			 * "defaultuser::rwx" is the default ACL corresponding
1708 			 * to "user::rwx", etc. valid only for first field
1709 			 */
1710 			s = field[0].start;
1711 			len = field[0].end - field[0].start;
1712 			if (*s == 'd' && (len == 1 || (len >= 7
1713 			    && memcmp((s + 1), "efault", 6) == 0))) {
1714 				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1715 				if (len > 7)
1716 					field[0].start += 7;
1717 				else
1718 					n = 1;
1719 			} else
1720 				type = want_type;
1721 
1722 			/* Check for a numeric ID in field n+1 or n+3. */
1723 			isint(field[n + 1].start, field[n + 1].end, &id);
1724 			/* Field n+3 is optional. */
1725 			if (id == -1 && fields > (n + 3))
1726 				isint(field[n + 3].start, field[n + 3].end,
1727 				    &id);
1728 
1729 			tag = 0;
1730 			s = field[n].start;
1731 			st = field[n].start + 1;
1732 			len = field[n].end - field[n].start;
1733 
1734 			if (len == 0) {
1735 				ret = ARCHIVE_WARN;
1736 				continue;
1737 			}
1738 
1739 			switch (*s) {
1740 			case 'u':
1741 				if (len == 1 || (len == 4
1742 				    && memcmp(st, "ser", 3) == 0))
1743 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1744 				break;
1745 			case 'g':
1746 				if (len == 1 || (len == 5
1747 				    && memcmp(st, "roup", 4) == 0))
1748 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1749 				break;
1750 			case 'o':
1751 				if (len == 1 || (len == 5
1752 				    && memcmp(st, "ther", 4) == 0))
1753 					tag = ARCHIVE_ENTRY_ACL_OTHER;
1754 				break;
1755 			case 'm':
1756 				if (len == 1 || (len == 4
1757 				    && memcmp(st, "ask", 3) == 0))
1758 					tag = ARCHIVE_ENTRY_ACL_MASK;
1759 				break;
1760 			default:
1761 					break;
1762 			}
1763 
1764 			switch (tag) {
1765 			case ARCHIVE_ENTRY_ACL_OTHER:
1766 			case ARCHIVE_ENTRY_ACL_MASK:
1767 				if (fields == (n + 2)
1768 				    && field[n + 1].start < field[n + 1].end
1769 				    && ismode(field[n + 1].start,
1770 				    field[n + 1].end, &permset)) {
1771 					/* This is Solaris-style "other:rwx" */
1772 					sol = 1;
1773 				} else if (fields == (n + 3) &&
1774 				    field[n + 1].start < field[n + 1].end) {
1775 					/* Invalid mask or other field */
1776 					ret = ARCHIVE_WARN;
1777 					continue;
1778 				}
1779 				break;
1780 			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1781 			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1782 				if (id != -1 ||
1783 				    field[n + 1].start < field[n + 1].end) {
1784 					name = field[n + 1];
1785 					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1786 						tag = ARCHIVE_ENTRY_ACL_USER;
1787 					else
1788 						tag = ARCHIVE_ENTRY_ACL_GROUP;
1789 				}
1790 				break;
1791 			default:
1792 				/* Invalid tag, skip entry */
1793 				ret = ARCHIVE_WARN;
1794 				continue;
1795 			}
1796 
1797 			/*
1798 			 * Without "default:" we expect mode in field 3
1799 			 * Exception: Solaris other and mask fields
1800 			 */
1801 			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1802 			    field[n + 2 - sol].end, &permset)) {
1803 				/* Invalid mode, skip entry */
1804 				ret = ARCHIVE_WARN;
1805 				continue;
1806 			}
1807 		} else {
1808 			/* NFS4 ACLs */
1809 			s = field[0].start;
1810 			len = field[0].end - field[0].start;
1811 			tag = 0;
1812 
1813 			switch (len) {
1814 			case 4:
1815 				if (memcmp(s, "user", 4) == 0)
1816 					tag = ARCHIVE_ENTRY_ACL_USER;
1817 				break;
1818 			case 5:
1819 				if (memcmp(s, "group", 5) == 0)
1820 					tag = ARCHIVE_ENTRY_ACL_GROUP;
1821 				break;
1822 			case 6:
1823 				if (memcmp(s, "owner@", 6) == 0)
1824 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1825 				else if (memcmp(s, "group@", 6) == 0)
1826 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1827 				break;
1828 			case 9:
1829 				if (memcmp(s, "everyone@", 9) == 0)
1830 					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1831 				break;
1832 			default:
1833 				break;
1834 			}
1835 
1836 			if (tag == 0) {
1837 				/* Invalid tag, skip entry */
1838 				ret = ARCHIVE_WARN;
1839 				continue;
1840 			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1841 			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1842 				n = 1;
1843 				name = field[1];
1844 				isint(name.start, name.end, &id);
1845 			} else
1846 				n = 0;
1847 
1848 			if (!is_nfs4_perms(field[1 + n].start,
1849 			    field[1 + n].end, &permset)) {
1850 				/* Invalid NFSv4 perms, skip entry */
1851 				ret = ARCHIVE_WARN;
1852 				continue;
1853 			}
1854 			if (!is_nfs4_flags(field[2 + n].start,
1855 			    field[2 + n].end, &permset)) {
1856 				/* Invalid NFSv4 flags, skip entry */
1857 				ret = ARCHIVE_WARN;
1858 				continue;
1859 			}
1860 			s = field[3 + n].start;
1861 			len = field[3 + n].end - field[3 + n].start;
1862 			type = 0;
1863 			if (len == 4) {
1864 				if (memcmp(s, "deny", 4) == 0)
1865 					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1866 			} else if (len == 5) {
1867 				if (memcmp(s, "allow", 5) == 0)
1868 					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1869 				else if (memcmp(s, "audit", 5) == 0)
1870 					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1871 				else if (memcmp(s, "alarm", 5) == 0)
1872 					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1873 			}
1874 			if (type == 0) {
1875 				/* Invalid entry type, skip entry */
1876 				ret = ARCHIVE_WARN;
1877 				continue;
1878 			}
1879 			isint(field[4 + n].start, field[4 + n].end,
1880 			    &id);
1881 		}
1882 
1883 		/* Add entry to the internal list. */
1884 		r = archive_acl_add_entry_len_l(acl, type, permset,
1885 		    tag, id, name.start, name.end - name.start, sc);
1886 		if (r < ARCHIVE_WARN)
1887 			return (r);
1888 		if (r != ARCHIVE_OK)
1889 			ret = ARCHIVE_WARN;
1890 		types |= type;
1891 	}
1892 
1893 	/* Reset ACL */
1894 	archive_acl_reset(acl, types);
1895 
1896 	return (ret);
1897 }
1898 
1899 /*
1900  * Parse a string to a positive decimal integer.  Returns true if
1901  * the string is non-empty and consists only of decimal digits,
1902  * false otherwise.
1903  */
1904 static int
isint(const char * start,const char * end,int * result)1905 isint(const char *start, const char *end, int *result)
1906 {
1907 	int n = 0;
1908 	if (start >= end)
1909 		return (0);
1910 	while (start < end) {
1911 		if (*start < '0' || *start > '9')
1912 			return (0);
1913 		if (n > (INT_MAX / 10) ||
1914 		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1915 			n = INT_MAX;
1916 		} else {
1917 			n *= 10;
1918 			n += *start - '0';
1919 		}
1920 		start++;
1921 	}
1922 	*result = n;
1923 	return (1);
1924 }
1925 
1926 /*
1927  * Parse a string as a mode field.  Returns true if
1928  * the string is non-empty and consists only of mode characters,
1929  * false otherwise.
1930  */
1931 static int
ismode(const char * start,const char * end,int * permset)1932 ismode(const char *start, const char *end, int *permset)
1933 {
1934 	const char *p;
1935 
1936 	if (start >= end)
1937 		return (0);
1938 	p = start;
1939 	*permset = 0;
1940 	while (p < end) {
1941 		switch (*p++) {
1942 		case 'r': case 'R':
1943 			*permset |= ARCHIVE_ENTRY_ACL_READ;
1944 			break;
1945 		case 'w': case 'W':
1946 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1947 			break;
1948 		case 'x': case 'X':
1949 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1950 			break;
1951 		case '-':
1952 			break;
1953 		default:
1954 			return (0);
1955 		}
1956 	}
1957 	return (1);
1958 }
1959 
1960 /*
1961  * Parse a string as a NFS4 ACL permission field.
1962  * Returns true if the string is non-empty and consists only of NFS4 ACL
1963  * permission characters, false otherwise
1964  */
1965 static int
is_nfs4_perms(const char * start,const char * end,int * permset)1966 is_nfs4_perms(const char *start, const char *end, int *permset)
1967 {
1968 	const char *p = start;
1969 
1970 	while (p < end) {
1971 		switch (*p++) {
1972 		case 'r':
1973 			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1974 			break;
1975 		case 'w':
1976 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1977 			break;
1978 		case 'x':
1979 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1980 			break;
1981 		case 'p':
1982 			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1983 			break;
1984 		case 'D':
1985 			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1986 			break;
1987 		case 'd':
1988 			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1989 			break;
1990 		case 'a':
1991 			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1992 			break;
1993 		case 'A':
1994 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1995 			break;
1996 		case 'R':
1997 			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1998 			break;
1999 		case 'W':
2000 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
2001 			break;
2002 		case 'c':
2003 			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
2004 			break;
2005 		case 'C':
2006 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
2007 			break;
2008 		case 'o':
2009 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
2010 			break;
2011 		case 's':
2012 			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
2013 			break;
2014 		case '-':
2015 			break;
2016 		default:
2017 			return(0);
2018 		}
2019 	}
2020 	return (1);
2021 }
2022 
2023 /*
2024  * Parse a string as a NFS4 ACL flags field.
2025  * Returns true if the string is non-empty and consists only of NFS4 ACL
2026  * flag characters, false otherwise
2027  */
2028 static int
is_nfs4_flags(const char * start,const char * end,int * permset)2029 is_nfs4_flags(const char *start, const char *end, int *permset)
2030 {
2031 	const char *p = start;
2032 
2033 	while (p < end) {
2034 		switch(*p++) {
2035 		case 'f':
2036 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2037 			break;
2038 		case 'd':
2039 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2040 			break;
2041 		case 'i':
2042 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2043 			break;
2044 		case 'n':
2045 			*permset |=
2046 			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2047 			break;
2048 		case 'S':
2049 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2050 			break;
2051 		case 'F':
2052 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2053 			break;
2054 		case 'I':
2055 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2056 			break;
2057 		case '-':
2058 			break;
2059 		default:
2060 			return (0);
2061 		}
2062 	}
2063 	return (1);
2064 }
2065 
2066 /*
2067  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *p is updated
2068  * to point to just after the separator.  *start points to the first
2069  * character of the matched text and *end just after the last
2070  * character of the matched identifier.  In particular *end - *start
2071  * is the length of the field body, not including leading or trailing
2072  * whitespace.
2073  */
2074 static void
next_field(const char ** p,size_t * l,const char ** start,const char ** end,char * sep)2075 next_field(const char **p, size_t *l, const char **start,
2076     const char **end, char *sep)
2077 {
2078 	/* Skip leading whitespace to find start of field. */
2079 	while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {
2080 		(*p)++;
2081 		(*l)--;
2082 	}
2083 	*start = *p;
2084 
2085 	/* Locate end of field, trim trailing whitespace if necessary */
2086 	while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {
2087 		(*p)++;
2088 		(*l)--;
2089 	}
2090 	*end = *p;
2091 
2092 	/* Scan for the separator. */
2093 	while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {
2094 		(*p)++;
2095 		(*l)--;
2096 	}
2097 	*sep = **p;
2098 
2099 	/* Handle in-field comments */
2100 	if (*sep == '#') {
2101 		while (*l > 0 && **p != ',' && **p != '\n') {
2102 			(*p)++;
2103 			(*l)--;
2104 		}
2105 		*sep = **p;
2106 	}
2107 
2108 	/* Skip separator. */
2109 	if (*l > 0) {
2110 		(*p)++;
2111 		(*l)--;
2112 	}
2113 }
2114