1 /*-
2 * Copyright (c) 2003-2010 Tim Kientzle
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
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 isint_w(const wchar_t *start, const wchar_t *end, int *result);
59 static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
60 static void next_field_w(const wchar_t **wp, const wchar_t **start,
61 const wchar_t **end, wchar_t *sep);
62 static int prefix_w(const wchar_t *start, const wchar_t *end,
63 const wchar_t *test);
64 static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
65 const wchar_t *wname, int perm, int id);
66 static void append_id_w(wchar_t **wp, int id);
67 static int isint(const char *start, const char *end, int *result);
68 static int ismode(const char *start, const char *end, int *result);
69 static void next_field(const char **p, const char **start,
70 const char **end, char *sep);
71 static int prefix_c(const char *start, const char *end,
72 const char *test);
73 static void append_entry(char **p, const char *prefix, int tag,
74 const char *name, int perm, int id);
75 static void append_id(char **p, int id);
76
77 void
archive_acl_clear(struct archive_acl * acl)78 archive_acl_clear(struct archive_acl *acl)
79 {
80 struct archive_acl_entry *ap;
81
82 while (acl->acl_head != NULL) {
83 ap = acl->acl_head->next;
84 archive_mstring_clean(&acl->acl_head->name);
85 free(acl->acl_head);
86 acl->acl_head = ap;
87 }
88 if (acl->acl_text_w != NULL) {
89 free(acl->acl_text_w);
90 acl->acl_text_w = NULL;
91 }
92 if (acl->acl_text != NULL) {
93 free(acl->acl_text);
94 acl->acl_text = NULL;
95 }
96 acl->acl_p = NULL;
97 acl->acl_state = 0; /* Not counting. */
98 }
99
100 void
archive_acl_copy(struct archive_acl * dest,struct archive_acl * src)101 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
102 {
103 struct archive_acl_entry *ap, *ap2;
104
105 archive_acl_clear(dest);
106
107 dest->mode = src->mode;
108 ap = src->acl_head;
109 while (ap != NULL) {
110 ap2 = acl_new_entry(dest,
111 ap->type, ap->permset, ap->tag, ap->id);
112 if (ap2 != NULL)
113 archive_mstring_copy(&ap2->name, &ap->name);
114 ap = ap->next;
115 }
116 }
117
118 int
archive_acl_add_entry(struct archive_acl * acl,int type,int permset,int tag,int id,const char * name)119 archive_acl_add_entry(struct archive_acl *acl,
120 int type, int permset, int tag, int id, const char *name)
121 {
122 struct archive_acl_entry *ap;
123
124 if (acl_special(acl, type, permset, tag) == 0)
125 return ARCHIVE_OK;
126 ap = acl_new_entry(acl, type, permset, tag, id);
127 if (ap == NULL) {
128 /* XXX Error XXX */
129 return ARCHIVE_FAILED;
130 }
131 if (name != NULL && *name != '\0')
132 archive_mstring_copy_mbs(&ap->name, name);
133 else
134 archive_mstring_clean(&ap->name);
135 return ARCHIVE_OK;
136 }
137
138 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)139 archive_acl_add_entry_w_len(struct archive_acl *acl,
140 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
141 {
142 struct archive_acl_entry *ap;
143
144 if (acl_special(acl, type, permset, tag) == 0)
145 return ARCHIVE_OK;
146 ap = acl_new_entry(acl, type, permset, tag, id);
147 if (ap == NULL) {
148 /* XXX Error XXX */
149 return ARCHIVE_FAILED;
150 }
151 if (name != NULL && *name != L'\0' && len > 0)
152 archive_mstring_copy_wcs_len(&ap->name, name, len);
153 else
154 archive_mstring_clean(&ap->name);
155 return ARCHIVE_OK;
156 }
157
158 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)159 archive_acl_add_entry_len_l(struct archive_acl *acl,
160 int type, int permset, int tag, int id, const char *name, size_t len,
161 struct archive_string_conv *sc)
162 {
163 struct archive_acl_entry *ap;
164 int r;
165
166 if (acl_special(acl, type, permset, tag) == 0)
167 return ARCHIVE_OK;
168 ap = acl_new_entry(acl, type, permset, tag, id);
169 if (ap == NULL) {
170 /* XXX Error XXX */
171 return ARCHIVE_FAILED;
172 }
173 if (name != NULL && *name != '\0' && len > 0) {
174 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
175 } else {
176 r = 0;
177 archive_mstring_clean(&ap->name);
178 }
179 if (r == 0)
180 return (ARCHIVE_OK);
181 else if (errno == ENOMEM)
182 return (ARCHIVE_FATAL);
183 else
184 return (ARCHIVE_WARN);
185 }
186
187 /*
188 * If this ACL entry is part of the standard POSIX permissions set,
189 * store the permissions in the stat structure and return zero.
190 */
191 static int
acl_special(struct archive_acl * acl,int type,int permset,int tag)192 acl_special(struct archive_acl *acl, int type, int permset, int tag)
193 {
194 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
195 && ((permset & ~007) == 0)) {
196 switch (tag) {
197 case ARCHIVE_ENTRY_ACL_USER_OBJ:
198 acl->mode &= ~0700;
199 acl->mode |= (permset & 7) << 6;
200 return (0);
201 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
202 acl->mode &= ~0070;
203 acl->mode |= (permset & 7) << 3;
204 return (0);
205 case ARCHIVE_ENTRY_ACL_OTHER:
206 acl->mode &= ~0007;
207 acl->mode |= permset & 7;
208 return (0);
209 }
210 }
211 return (1);
212 }
213
214 /*
215 * Allocate and populate a new ACL entry with everything but the
216 * name.
217 */
218 static struct archive_acl_entry *
acl_new_entry(struct archive_acl * acl,int type,int permset,int tag,int id)219 acl_new_entry(struct archive_acl *acl,
220 int type, int permset, int tag, int id)
221 {
222 struct archive_acl_entry *ap, *aq;
223
224 /* Type argument must be a valid NFS4 or POSIX.1e type.
225 * The type must agree with anything already set and
226 * the permset must be compatible. */
227 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
228 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
229 return (NULL);
230 }
231 if (permset &
232 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
233 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
234 return (NULL);
235 }
236 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
237 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
238 return (NULL);
239 }
240 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
241 return (NULL);
242 }
243 } else {
244 return (NULL);
245 }
246
247 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
248 switch (tag) {
249 case ARCHIVE_ENTRY_ACL_USER:
250 case ARCHIVE_ENTRY_ACL_USER_OBJ:
251 case ARCHIVE_ENTRY_ACL_GROUP:
252 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253 /* Tags valid in both NFS4 and POSIX.1e */
254 break;
255 case ARCHIVE_ENTRY_ACL_MASK:
256 case ARCHIVE_ENTRY_ACL_OTHER:
257 /* Tags valid only in POSIX.1e. */
258 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
259 return (NULL);
260 }
261 break;
262 case ARCHIVE_ENTRY_ACL_EVERYONE:
263 /* Tags valid only in NFS4. */
264 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
265 return (NULL);
266 }
267 break;
268 default:
269 /* No other values are valid. */
270 return (NULL);
271 }
272
273 if (acl->acl_text_w != NULL) {
274 free(acl->acl_text_w);
275 acl->acl_text_w = NULL;
276 }
277 if (acl->acl_text != NULL) {
278 free(acl->acl_text);
279 acl->acl_text = NULL;
280 }
281
282 /* If there's a matching entry already in the list, overwrite it. */
283 ap = acl->acl_head;
284 aq = NULL;
285 while (ap != NULL) {
286 if (ap->type == type && ap->tag == tag && ap->id == id) {
287 ap->permset = permset;
288 return (ap);
289 }
290 aq = ap;
291 ap = ap->next;
292 }
293
294 /* Add a new entry to the end of the list. */
295 ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
296 if (ap == NULL)
297 return (NULL);
298 memset(ap, 0, sizeof(*ap));
299 if (aq == NULL)
300 acl->acl_head = ap;
301 else
302 aq->next = ap;
303 ap->type = type;
304 ap->tag = tag;
305 ap->id = id;
306 ap->permset = permset;
307 acl->acl_types |= type;
308 return (ap);
309 }
310
311 /*
312 * Return a count of entries matching "want_type".
313 */
314 int
archive_acl_count(struct archive_acl * acl,int want_type)315 archive_acl_count(struct archive_acl *acl, int want_type)
316 {
317 int count;
318 struct archive_acl_entry *ap;
319
320 count = 0;
321 ap = acl->acl_head;
322 while (ap != NULL) {
323 if ((ap->type & want_type) != 0)
324 count++;
325 ap = ap->next;
326 }
327
328 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
329 count += 3;
330 return (count);
331 }
332
333 /*
334 * Prepare for reading entries from the ACL data. Returns a count
335 * of entries matching "want_type", or zero if there are no
336 * non-extended ACL entries of that type.
337 */
338 int
archive_acl_reset(struct archive_acl * acl,int want_type)339 archive_acl_reset(struct archive_acl *acl, int want_type)
340 {
341 int count, cutoff;
342
343 count = archive_acl_count(acl, want_type);
344
345 /*
346 * If the only entries are the three standard ones,
347 * then don't return any ACL data. (In this case,
348 * client can just use chmod(2) to set permissions.)
349 */
350 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
351 cutoff = 3;
352 else
353 cutoff = 0;
354
355 if (count > cutoff)
356 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
357 else
358 acl->acl_state = 0;
359 acl->acl_p = acl->acl_head;
360 return (count);
361 }
362
363
364 /*
365 * Return the next ACL entry in the list. Fake entries for the
366 * standard permissions and include them in the returned list.
367 */
368 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)369 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
370 int *permset, int *tag, int *id, const char **name)
371 {
372 *name = NULL;
373 *id = -1;
374
375 /*
376 * The acl_state is either zero (no entries available), -1
377 * (reading from list), or an entry type (retrieve that type
378 * from ae_stat.aest_mode).
379 */
380 if (acl->acl_state == 0)
381 return (ARCHIVE_WARN);
382
383 /* The first three access entries are special. */
384 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
385 switch (acl->acl_state) {
386 case ARCHIVE_ENTRY_ACL_USER_OBJ:
387 *permset = (acl->mode >> 6) & 7;
388 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
389 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
390 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
391 return (ARCHIVE_OK);
392 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
393 *permset = (acl->mode >> 3) & 7;
394 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
395 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
396 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
397 return (ARCHIVE_OK);
398 case ARCHIVE_ENTRY_ACL_OTHER:
399 *permset = acl->mode & 7;
400 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
401 *tag = ARCHIVE_ENTRY_ACL_OTHER;
402 acl->acl_state = -1;
403 acl->acl_p = acl->acl_head;
404 return (ARCHIVE_OK);
405 default:
406 break;
407 }
408 }
409
410 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
411 acl->acl_p = acl->acl_p->next;
412 if (acl->acl_p == NULL) {
413 acl->acl_state = 0;
414 *type = 0;
415 *permset = 0;
416 *tag = 0;
417 *id = -1;
418 *name = NULL;
419 return (ARCHIVE_EOF); /* End of ACL entries. */
420 }
421 *type = acl->acl_p->type;
422 *permset = acl->acl_p->permset;
423 *tag = acl->acl_p->tag;
424 *id = acl->acl_p->id;
425 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
426 if (errno == ENOMEM)
427 return (ARCHIVE_FATAL);
428 *name = NULL;
429 }
430 acl->acl_p = acl->acl_p->next;
431 return (ARCHIVE_OK);
432 }
433
434 /*
435 * Generate a text version of the ACL. The flags parameter controls
436 * the style of the generated ACL.
437 */
438 const wchar_t *
archive_acl_text_w(struct archive * a,struct archive_acl * acl,int flags)439 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
440 {
441 int count;
442 size_t length;
443 const wchar_t *wname;
444 const wchar_t *prefix;
445 wchar_t separator;
446 struct archive_acl_entry *ap;
447 int id, r;
448 wchar_t *wp;
449
450 if (acl->acl_text_w != NULL) {
451 free (acl->acl_text_w);
452 acl->acl_text_w = NULL;
453 }
454
455 separator = L',';
456 count = 0;
457 length = 0;
458 ap = acl->acl_head;
459 while (ap != NULL) {
460 if ((ap->type & flags) != 0) {
461 count++;
462 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
463 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
464 length += 8; /* "default:" */
465 length += 5; /* tag name */
466 length += 1; /* colon */
467 r = archive_mstring_get_wcs(a, &ap->name, &wname);
468 if (r == 0 && wname != NULL)
469 length += wcslen(wname);
470 else if (r < 0 && errno == ENOMEM)
471 return (NULL);
472 else
473 length += sizeof(uid_t) * 3 + 1;
474 length ++; /* colon */
475 length += 3; /* rwx */
476 length += 1; /* colon */
477 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
478 length ++; /* newline */
479 }
480 ap = ap->next;
481 }
482
483 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
484 length += 10; /* "user::rwx\n" */
485 length += 11; /* "group::rwx\n" */
486 length += 11; /* "other::rwx\n" */
487 }
488
489 if (count == 0)
490 return (NULL);
491
492 /* Now, allocate the string and actually populate it. */
493 wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
494 if (wp == NULL)
495 return (NULL);
496 count = 0;
497 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
498 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
499 acl->mode & 0700, -1);
500 *wp++ = ',';
501 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
502 acl->mode & 0070, -1);
503 *wp++ = ',';
504 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
505 acl->mode & 0007, -1);
506 count += 3;
507
508 ap = acl->acl_head;
509 while (ap != NULL) {
510 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
511 r = archive_mstring_get_wcs(a, &ap->name, &wname);
512 if (r == 0) {
513 *wp++ = separator;
514 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
515 id = ap->id;
516 else
517 id = -1;
518 append_entry_w(&wp, NULL, ap->tag, wname,
519 ap->permset, id);
520 count++;
521 } else if (r < 0 && errno == ENOMEM)
522 return (NULL);
523 }
524 ap = ap->next;
525 }
526 }
527
528
529 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
530 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
531 prefix = L"default:";
532 else
533 prefix = NULL;
534 ap = acl->acl_head;
535 count = 0;
536 while (ap != NULL) {
537 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
538 r = archive_mstring_get_wcs(a, &ap->name, &wname);
539 if (r == 0) {
540 if (count > 0)
541 *wp++ = separator;
542 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
543 id = ap->id;
544 else
545 id = -1;
546 append_entry_w(&wp, prefix, ap->tag,
547 wname, ap->permset, id);
548 count ++;
549 } else if (r < 0 && errno == ENOMEM)
550 return (NULL);
551 }
552 ap = ap->next;
553 }
554 }
555
556 return (acl->acl_text_w);
557 }
558
559
560 static void
append_id_w(wchar_t ** wp,int id)561 append_id_w(wchar_t **wp, int id)
562 {
563 if (id < 0)
564 id = 0;
565 if (id > 9)
566 append_id_w(wp, id / 10);
567 *(*wp)++ = L"0123456789"[id % 10];
568 }
569
570 static void
append_entry_w(wchar_t ** wp,const wchar_t * prefix,int tag,const wchar_t * wname,int perm,int id)571 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
572 const wchar_t *wname, int perm, int id)
573 {
574 if (prefix != NULL) {
575 wcscpy(*wp, prefix);
576 *wp += wcslen(*wp);
577 }
578 switch (tag) {
579 case ARCHIVE_ENTRY_ACL_USER_OBJ:
580 wname = NULL;
581 id = -1;
582 /* FALLTHROUGH */
583 case ARCHIVE_ENTRY_ACL_USER:
584 wcscpy(*wp, L"user");
585 break;
586 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
587 wname = NULL;
588 id = -1;
589 /* FALLTHROUGH */
590 case ARCHIVE_ENTRY_ACL_GROUP:
591 wcscpy(*wp, L"group");
592 break;
593 case ARCHIVE_ENTRY_ACL_MASK:
594 wcscpy(*wp, L"mask");
595 wname = NULL;
596 id = -1;
597 break;
598 case ARCHIVE_ENTRY_ACL_OTHER:
599 wcscpy(*wp, L"other");
600 wname = NULL;
601 id = -1;
602 break;
603 }
604 *wp += wcslen(*wp);
605 *(*wp)++ = L':';
606 if (wname != NULL) {
607 wcscpy(*wp, wname);
608 *wp += wcslen(*wp);
609 } else if (tag == ARCHIVE_ENTRY_ACL_USER
610 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
611 append_id_w(wp, id);
612 id = -1;
613 }
614 *(*wp)++ = L':';
615 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
616 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
617 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
618 if (id != -1) {
619 *(*wp)++ = L':';
620 append_id_w(wp, id);
621 }
622 **wp = L'\0';
623 }
624
625 int
archive_acl_text_l(struct archive_acl * acl,int flags,const char ** acl_text,size_t * acl_text_len,struct archive_string_conv * sc)626 archive_acl_text_l(struct archive_acl *acl, int flags,
627 const char **acl_text, size_t *acl_text_len,
628 struct archive_string_conv *sc)
629 {
630 int count;
631 size_t length;
632 const char *name;
633 const char *prefix;
634 char separator;
635 struct archive_acl_entry *ap;
636 size_t len;
637 int id, r;
638 char *p;
639
640 if (acl->acl_text != NULL) {
641 free (acl->acl_text);
642 acl->acl_text = NULL;
643 }
644
645 *acl_text = NULL;
646 if (acl_text_len != NULL)
647 *acl_text_len = 0;
648 separator = ',';
649 count = 0;
650 length = 0;
651 ap = acl->acl_head;
652 while (ap != NULL) {
653 if ((ap->type & flags) != 0) {
654 count++;
655 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
656 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
657 length += 8; /* "default:" */
658 length += 5; /* tag name */
659 length += 1; /* colon */
660 r = archive_mstring_get_mbs_l(
661 &ap->name, &name, &len, sc);
662 if (r != 0)
663 return (-1);
664 if (len > 0 && name != NULL)
665 length += len;
666 else
667 length += sizeof(uid_t) * 3 + 1;
668 length ++; /* colon */
669 length += 3; /* rwx */
670 length += 1; /* colon */
671 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
672 length ++; /* newline */
673 }
674 ap = ap->next;
675 }
676
677 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
678 length += 10; /* "user::rwx\n" */
679 length += 11; /* "group::rwx\n" */
680 length += 11; /* "other::rwx\n" */
681 }
682
683 if (count == 0)
684 return (0);
685
686 /* Now, allocate the string and actually populate it. */
687 p = acl->acl_text = (char *)malloc(length);
688 if (p == NULL)
689 return (-1);
690 count = 0;
691 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
692 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
693 acl->mode & 0700, -1);
694 *p++ = ',';
695 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
696 acl->mode & 0070, -1);
697 *p++ = ',';
698 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
699 acl->mode & 0007, -1);
700 count += 3;
701
702 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
703 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
704 continue;
705 r = archive_mstring_get_mbs_l(
706 &ap->name, &name, &len, sc);
707 if (r != 0)
708 return (-1);
709 *p++ = separator;
710 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
711 id = ap->id;
712 else
713 id = -1;
714 append_entry(&p, NULL, ap->tag, name,
715 ap->permset, id);
716 count++;
717 }
718 }
719
720
721 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
722 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
723 prefix = "default:";
724 else
725 prefix = NULL;
726 count = 0;
727 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
728 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
729 continue;
730 r = archive_mstring_get_mbs_l(
731 &ap->name, &name, &len, sc);
732 if (r != 0)
733 return (-1);
734 if (count > 0)
735 *p++ = separator;
736 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
737 id = ap->id;
738 else
739 id = -1;
740 append_entry(&p, prefix, ap->tag,
741 name, ap->permset, id);
742 count ++;
743 }
744 }
745
746 *acl_text = acl->acl_text;
747 if (acl_text_len != NULL)
748 *acl_text_len = strlen(acl->acl_text);
749 return (0);
750 }
751
752 static void
append_id(char ** p,int id)753 append_id(char **p, int id)
754 {
755 if (id < 0)
756 id = 0;
757 if (id > 9)
758 append_id(p, id / 10);
759 *(*p)++ = "0123456789"[id % 10];
760 }
761
762 static void
append_entry(char ** p,const char * prefix,int tag,const char * name,int perm,int id)763 append_entry(char **p, const char *prefix, int tag,
764 const char *name, int perm, int id)
765 {
766 if (prefix != NULL) {
767 strcpy(*p, prefix);
768 *p += strlen(*p);
769 }
770 switch (tag) {
771 case ARCHIVE_ENTRY_ACL_USER_OBJ:
772 name = NULL;
773 id = -1;
774 /* FALLTHROUGH */
775 case ARCHIVE_ENTRY_ACL_USER:
776 strcpy(*p, "user");
777 break;
778 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
779 name = NULL;
780 id = -1;
781 /* FALLTHROUGH */
782 case ARCHIVE_ENTRY_ACL_GROUP:
783 strcpy(*p, "group");
784 break;
785 case ARCHIVE_ENTRY_ACL_MASK:
786 strcpy(*p, "mask");
787 name = NULL;
788 id = -1;
789 break;
790 case ARCHIVE_ENTRY_ACL_OTHER:
791 strcpy(*p, "other");
792 name = NULL;
793 id = -1;
794 break;
795 }
796 *p += strlen(*p);
797 *(*p)++ = ':';
798 if (name != NULL) {
799 strcpy(*p, name);
800 *p += strlen(*p);
801 } else if (tag == ARCHIVE_ENTRY_ACL_USER
802 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
803 append_id(p, id);
804 id = -1;
805 }
806 *(*p)++ = ':';
807 *(*p)++ = (perm & 0444) ? 'r' : '-';
808 *(*p)++ = (perm & 0222) ? 'w' : '-';
809 *(*p)++ = (perm & 0111) ? 'x' : '-';
810 if (id != -1) {
811 *(*p)++ = ':';
812 append_id(p, id);
813 }
814 **p = '\0';
815 }
816
817 /*
818 * Parse a textual ACL. This automatically recognizes and supports
819 * extensions described above. The 'type' argument is used to
820 * indicate the type that should be used for any entries not
821 * explicitly marked as "default:".
822 */
823 int
archive_acl_parse_w(struct archive_acl * acl,const wchar_t * text,int default_type)824 archive_acl_parse_w(struct archive_acl *acl,
825 const wchar_t *text, int default_type)
826 {
827 struct {
828 const wchar_t *start;
829 const wchar_t *end;
830 } field[4], name;
831
832 int fields, n;
833 int type, tag, permset, id;
834 wchar_t sep;
835
836 while (text != NULL && *text != L'\0') {
837 /*
838 * Parse the fields out of the next entry,
839 * advance 'text' to start of next entry.
840 */
841 fields = 0;
842 do {
843 const wchar_t *start, *end;
844 next_field_w(&text, &start, &end, &sep);
845 if (fields < 4) {
846 field[fields].start = start;
847 field[fields].end = end;
848 }
849 ++fields;
850 } while (sep == L':');
851
852 /* Set remaining fields to blank. */
853 for (n = fields; n < 4; ++n)
854 field[n].start = field[n].end = NULL;
855
856 /* Check for a numeric ID in field 1 or 3. */
857 id = -1;
858 isint_w(field[1].start, field[1].end, &id);
859 /* Field 3 is optional. */
860 if (id == -1 && fields > 3)
861 isint_w(field[3].start, field[3].end, &id);
862
863 /*
864 * Solaris extension: "defaultuser::rwx" is the
865 * default ACL corresponding to "user::rwx", etc.
866 */
867 if (field[0].end - field[0].start > 7
868 && wmemcmp(field[0].start, L"default", 7) == 0) {
869 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
870 field[0].start += 7;
871 } else
872 type = default_type;
873
874 name.start = name.end = NULL;
875 if (prefix_w(field[0].start, field[0].end, L"user")) {
876 if (!ismode_w(field[2].start, field[2].end, &permset))
877 return (ARCHIVE_WARN);
878 if (id != -1 || field[1].start < field[1].end) {
879 tag = ARCHIVE_ENTRY_ACL_USER;
880 name = field[1];
881 } else
882 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
883 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
884 if (!ismode_w(field[2].start, field[2].end, &permset))
885 return (ARCHIVE_WARN);
886 if (id != -1 || field[1].start < field[1].end) {
887 tag = ARCHIVE_ENTRY_ACL_GROUP;
888 name = field[1];
889 } else
890 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
891 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
892 if (fields == 2
893 && field[1].start < field[1].end
894 && ismode_w(field[1].start, field[1].end, &permset)) {
895 /* This is Solaris-style "other:rwx" */
896 } else if (fields == 3
897 && field[1].start == field[1].end
898 && field[2].start < field[2].end
899 && ismode_w(field[2].start, field[2].end, &permset)) {
900 /* This is FreeBSD-style "other::rwx" */
901 } else
902 return (ARCHIVE_WARN);
903 tag = ARCHIVE_ENTRY_ACL_OTHER;
904 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
905 if (fields == 2
906 && field[1].start < field[1].end
907 && ismode_w(field[1].start, field[1].end, &permset)) {
908 /* This is Solaris-style "mask:rwx" */
909 } else if (fields == 3
910 && field[1].start == field[1].end
911 && field[2].start < field[2].end
912 && ismode_w(field[2].start, field[2].end, &permset)) {
913 /* This is FreeBSD-style "mask::rwx" */
914 } else
915 return (ARCHIVE_WARN);
916 tag = ARCHIVE_ENTRY_ACL_MASK;
917 } else
918 return (ARCHIVE_WARN);
919
920 /* Add entry to the internal list. */
921 archive_acl_add_entry_w_len(acl, type, permset,
922 tag, id, name.start, name.end - name.start);
923 }
924 return (ARCHIVE_OK);
925 }
926
927 /*
928 * Parse a string to a positive decimal integer. Returns true if
929 * the string is non-empty and consists only of decimal digits,
930 * false otherwise.
931 */
932 static int
isint_w(const wchar_t * start,const wchar_t * end,int * result)933 isint_w(const wchar_t *start, const wchar_t *end, int *result)
934 {
935 int n = 0;
936 if (start >= end)
937 return (0);
938 while (start < end) {
939 if (*start < '0' || *start > '9')
940 return (0);
941 if (n > (INT_MAX / 10) ||
942 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
943 n = INT_MAX;
944 } else {
945 n *= 10;
946 n += *start - '0';
947 }
948 start++;
949 }
950 *result = n;
951 return (1);
952 }
953
954 /*
955 * Parse a string as a mode field. Returns true if
956 * the string is non-empty and consists only of mode characters,
957 * false otherwise.
958 */
959 static int
ismode_w(const wchar_t * start,const wchar_t * end,int * permset)960 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
961 {
962 const wchar_t *p;
963
964 if (start >= end)
965 return (0);
966 p = start;
967 *permset = 0;
968 while (p < end) {
969 switch (*p++) {
970 case 'r': case 'R':
971 *permset |= ARCHIVE_ENTRY_ACL_READ;
972 break;
973 case 'w': case 'W':
974 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
975 break;
976 case 'x': case 'X':
977 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
978 break;
979 case '-':
980 break;
981 default:
982 return (0);
983 }
984 }
985 return (1);
986 }
987
988 /*
989 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
990 * to point to just after the separator. *start points to the first
991 * character of the matched text and *end just after the last
992 * character of the matched identifier. In particular *end - *start
993 * is the length of the field body, not including leading or trailing
994 * whitespace.
995 */
996 static void
next_field_w(const wchar_t ** wp,const wchar_t ** start,const wchar_t ** end,wchar_t * sep)997 next_field_w(const wchar_t **wp, const wchar_t **start,
998 const wchar_t **end, wchar_t *sep)
999 {
1000 /* Skip leading whitespace to find start of field. */
1001 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1002 (*wp)++;
1003 }
1004 *start = *wp;
1005
1006 /* Scan for the separator. */
1007 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1008 **wp != L'\n') {
1009 (*wp)++;
1010 }
1011 *sep = **wp;
1012
1013 /* Trim trailing whitespace to locate end of field. */
1014 *end = *wp - 1;
1015 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1016 (*end)--;
1017 }
1018 (*end)++;
1019
1020 /* Adjust scanner location. */
1021 if (**wp != L'\0')
1022 (*wp)++;
1023 }
1024
1025 /*
1026 * Return true if the characters [start...end) are a prefix of 'test'.
1027 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1028 */
1029 static int
prefix_w(const wchar_t * start,const wchar_t * end,const wchar_t * test)1030 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1031 {
1032 if (start == end)
1033 return (0);
1034
1035 if (*start++ != *test++)
1036 return (0);
1037
1038 while (start < end && *start++ == *test++)
1039 ;
1040
1041 if (start < end)
1042 return (0);
1043
1044 return (1);
1045 }
1046
1047 /*
1048 * Parse a textual ACL. This automatically recognizes and supports
1049 * extensions described above. The 'type' argument is used to
1050 * indicate the type that should be used for any entries not
1051 * explicitly marked as "default:".
1052 */
1053 int
archive_acl_parse_l(struct archive_acl * acl,const char * text,int default_type,struct archive_string_conv * sc)1054 archive_acl_parse_l(struct archive_acl *acl,
1055 const char *text, int default_type, struct archive_string_conv *sc)
1056 {
1057 struct {
1058 const char *start;
1059 const char *end;
1060 } field[4], name;
1061
1062 int fields, n, r, ret = ARCHIVE_OK;
1063 int type, tag, permset, id;
1064 char sep;
1065
1066 while (text != NULL && *text != '\0') {
1067 /*
1068 * Parse the fields out of the next entry,
1069 * advance 'text' to start of next entry.
1070 */
1071 fields = 0;
1072 do {
1073 const char *start, *end;
1074 next_field(&text, &start, &end, &sep);
1075 if (fields < 4) {
1076 field[fields].start = start;
1077 field[fields].end = end;
1078 }
1079 ++fields;
1080 } while (sep == ':');
1081
1082 /* Set remaining fields to blank. */
1083 for (n = fields; n < 4; ++n)
1084 field[n].start = field[n].end = NULL;
1085
1086 /* Check for a numeric ID in field 1 or 3. */
1087 id = -1;
1088 isint(field[1].start, field[1].end, &id);
1089 /* Field 3 is optional. */
1090 if (id == -1 && fields > 3)
1091 isint(field[3].start, field[3].end, &id);
1092
1093 /*
1094 * Solaris extension: "defaultuser::rwx" is the
1095 * default ACL corresponding to "user::rwx", etc.
1096 */
1097 if (field[0].end - field[0].start > 7
1098 && memcmp(field[0].start, "default", 7) == 0) {
1099 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1100 field[0].start += 7;
1101 } else
1102 type = default_type;
1103
1104 name.start = name.end = NULL;
1105 if (prefix_c(field[0].start, field[0].end, "user")) {
1106 if (!ismode(field[2].start, field[2].end, &permset))
1107 return (ARCHIVE_WARN);
1108 if (id != -1 || field[1].start < field[1].end) {
1109 tag = ARCHIVE_ENTRY_ACL_USER;
1110 name = field[1];
1111 } else
1112 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1113 } else if (prefix_c(field[0].start, field[0].end, "group")) {
1114 if (!ismode(field[2].start, field[2].end, &permset))
1115 return (ARCHIVE_WARN);
1116 if (id != -1 || field[1].start < field[1].end) {
1117 tag = ARCHIVE_ENTRY_ACL_GROUP;
1118 name = field[1];
1119 } else
1120 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1121 } else if (prefix_c(field[0].start, field[0].end, "other")) {
1122 if (fields == 2
1123 && field[1].start < field[1].end
1124 && ismode(field[1].start, field[1].end, &permset)) {
1125 /* This is Solaris-style "other:rwx" */
1126 } else if (fields == 3
1127 && field[1].start == field[1].end
1128 && field[2].start < field[2].end
1129 && ismode(field[2].start, field[2].end, &permset)) {
1130 /* This is FreeBSD-style "other::rwx" */
1131 } else
1132 return (ARCHIVE_WARN);
1133 tag = ARCHIVE_ENTRY_ACL_OTHER;
1134 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
1135 if (fields == 2
1136 && field[1].start < field[1].end
1137 && ismode(field[1].start, field[1].end, &permset)) {
1138 /* This is Solaris-style "mask:rwx" */
1139 } else if (fields == 3
1140 && field[1].start == field[1].end
1141 && field[2].start < field[2].end
1142 && ismode(field[2].start, field[2].end, &permset)) {
1143 /* This is FreeBSD-style "mask::rwx" */
1144 } else
1145 return (ARCHIVE_WARN);
1146 tag = ARCHIVE_ENTRY_ACL_MASK;
1147 } else
1148 return (ARCHIVE_WARN);
1149
1150 /* Add entry to the internal list. */
1151 r = archive_acl_add_entry_len_l(acl, type, permset,
1152 tag, id, name.start, name.end - name.start, sc);
1153 if (r < ARCHIVE_WARN)
1154 return (r);
1155 if (r != ARCHIVE_OK)
1156 ret = ARCHIVE_WARN;
1157 }
1158 return (ret);
1159 }
1160
1161 /*
1162 * Parse a string to a positive decimal integer. Returns true if
1163 * the string is non-empty and consists only of decimal digits,
1164 * false otherwise.
1165 */
1166 static int
isint(const char * start,const char * end,int * result)1167 isint(const char *start, const char *end, int *result)
1168 {
1169 int n = 0;
1170 if (start >= end)
1171 return (0);
1172 while (start < end) {
1173 if (*start < '0' || *start > '9')
1174 return (0);
1175 if (n > (INT_MAX / 10) ||
1176 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1177 n = INT_MAX;
1178 } else {
1179 n *= 10;
1180 n += *start - '0';
1181 }
1182 start++;
1183 }
1184 *result = n;
1185 return (1);
1186 }
1187
1188 /*
1189 * Parse a string as a mode field. Returns true if
1190 * the string is non-empty and consists only of mode characters,
1191 * false otherwise.
1192 */
1193 static int
ismode(const char * start,const char * end,int * permset)1194 ismode(const char *start, const char *end, int *permset)
1195 {
1196 const char *p;
1197
1198 if (start >= end)
1199 return (0);
1200 p = start;
1201 *permset = 0;
1202 while (p < end) {
1203 switch (*p++) {
1204 case 'r': case 'R':
1205 *permset |= ARCHIVE_ENTRY_ACL_READ;
1206 break;
1207 case 'w': case 'W':
1208 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1209 break;
1210 case 'x': case 'X':
1211 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1212 break;
1213 case '-':
1214 break;
1215 default:
1216 return (0);
1217 }
1218 }
1219 return (1);
1220 }
1221
1222 /*
1223 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1224 * to point to just after the separator. *start points to the first
1225 * character of the matched text and *end just after the last
1226 * character of the matched identifier. In particular *end - *start
1227 * is the length of the field body, not including leading or trailing
1228 * whitespace.
1229 */
1230 static void
next_field(const char ** p,const char ** start,const char ** end,char * sep)1231 next_field(const char **p, const char **start,
1232 const char **end, char *sep)
1233 {
1234 /* Skip leading whitespace to find start of field. */
1235 while (**p == ' ' || **p == '\t' || **p == '\n') {
1236 (*p)++;
1237 }
1238 *start = *p;
1239
1240 /* Scan for the separator. */
1241 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1242 (*p)++;
1243 }
1244 *sep = **p;
1245
1246 /* Trim trailing whitespace to locate end of field. */
1247 *end = *p - 1;
1248 while (**end == ' ' || **end == '\t' || **end == '\n') {
1249 (*end)--;
1250 }
1251 (*end)++;
1252
1253 /* Adjust scanner location. */
1254 if (**p != '\0')
1255 (*p)++;
1256 }
1257
1258 /*
1259 * Return true if the characters [start...end) are a prefix of 'test'.
1260 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1261 */
1262 static int
prefix_c(const char * start,const char * end,const char * test)1263 prefix_c(const char *start, const char *end, const char *test)
1264 {
1265 if (start == end)
1266 return (0);
1267
1268 if (*start++ != *test++)
1269 return (0);
1270
1271 while (start < end && *start++ == *test++)
1272 ;
1273
1274 if (start < end)
1275 return (0);
1276
1277 return (1);
1278 }
1279