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