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