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