From owner-svn-src-all@freebsd.org Fri Aug 26 23:50:45 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 6025FB761EF; Fri, 26 Aug 2016 23:50:45 +0000 (UTC) (envelope-from mm@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id F39CB9C6; Fri, 26 Aug 2016 23:50:44 +0000 (UTC) (envelope-from mm@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u7QNoi5T055731; Fri, 26 Aug 2016 23:50:44 GMT (envelope-from mm@FreeBSD.org) Received: (from mm@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u7QNoi6p055729; Fri, 26 Aug 2016 23:50:44 GMT (envelope-from mm@FreeBSD.org) Message-Id: <201608262350.u7QNoi6p055729@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mm set sender to mm@FreeBSD.org using -f From: Martin Matuska Date: Fri, 26 Aug 2016 23:50:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r304869 - in head/contrib/libarchive: libarchive tar X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 26 Aug 2016 23:50:45 -0000 Author: mm Date: Fri Aug 26 23:50:44 2016 New Revision: 304869 URL: https://svnweb.freebsd.org/changeset/base/304869 Log: MFV r304866: Sync libarchive with vendor including security fixes Vendor issues fixed: Issue #731: Reject tar entries >= INT64_MAX Issue #744 (part of Issue #743): Enforce sandbox with very long pathnames Issue #748: Zip decompression failure with highly-compressed data Issue #767: Buffer overflow printing a filename Issue #770: Zip read: be more careful about extra_length MFC after: 3 days Modified: head/contrib/libarchive/libarchive/archive_acl.c head/contrib/libarchive/libarchive/archive_entry.h head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c head/contrib/libarchive/libarchive/archive_read_support_format_tar.c head/contrib/libarchive/libarchive/archive_read_support_format_zip.c head/contrib/libarchive/libarchive/archive_write_disk_acl.c head/contrib/libarchive/libarchive/archive_write_disk_posix.c head/contrib/libarchive/libarchive/archive_write_set_format_pax.c head/contrib/libarchive/tar/util.c Directory Properties: head/contrib/libarchive/ (props changed) head/contrib/libarchive/libarchive/ (props changed) head/contrib/libarchive/tar/ (props changed) Modified: head/contrib/libarchive/libarchive/archive_acl.c ============================================================================== --- head/contrib/libarchive/libarchive/archive_acl.c Fri Aug 26 22:56:23 2016 (r304868) +++ head/contrib/libarchive/libarchive/archive_acl.c Fri Aug 26 23:50:44 2016 (r304869) @@ -57,21 +57,27 @@ static int archive_acl_add_entry_len_l(s size_t len, struct archive_string_conv *sc); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); +static int parse_nfs4_flags_w(const wchar_t *start, const wchar_t *end, + int *result); +static int parse_nfs4_perms_w(const wchar_t *start, const wchar_t *end, + int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test); -static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id); +static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, + int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); +static int parse_nfs4_flags(const char *start, const char *end, int *result); +static int parse_nfs4_perms(const char *start, const char *end, int *result); static void next_field(const char **p, const char **start, const char **end, char *sep); static int prefix_c(const char *start, const char *end, const char *test); -static void append_entry(char **p, const char *prefix, int tag, - const char *name, int perm, int id); +static void append_entry(char **p, const char *prefix, int type, + int tag, const char *name, int perm, int id); static void append_id(char **p, int id); void @@ -447,6 +453,16 @@ archive_acl_text_w(struct archive *a, st int id, r; wchar_t *wp; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) && + (flags & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))) { + /* cannot convert NFSv4 ACLs and POSIX1e ACLs at the same time */ + return (NULL); + } + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) && (flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) { + /* cannot have access and default at the same time */ + return (NULL); + } + if (acl->acl_text_w != NULL) { free (acl->acl_text_w); acl->acl_text_w = NULL; @@ -462,17 +478,57 @@ archive_acl_text_w(struct archive *a, st if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ - length += 5; /* tag name */ + switch (ap->tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 6; /* "owner@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + length += 4; /* "user" */ + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 6; /* "group@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + case ARCHIVE_ENTRY_ACL_OTHER: + length += 5; /* "group", "other" */ + break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + length += 9; /* "everyone@" */ + break; + } length += 1; /* colon */ - r = archive_mstring_get_wcs(a, &ap->name, &wname); - if (r == 0 && wname != NULL) - length += wcslen(wname); - else if (r < 0 && errno == ENOMEM) - return (NULL); + if (((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) || + ap->tag == ARCHIVE_ENTRY_ACL_USER || + ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { + r = archive_mstring_get_wcs(a, &ap->name, &wname); + if (r == 0 && wname != NULL) + length += wcslen(wname); + else if (r < 0 && errno == ENOMEM) + return (NULL); + else + length += sizeof(uid_t) * 3 + 1; + length += 1; /* colon */ + } + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) + length += 14; /* rwxpdDaARWcCos */ else - length += sizeof(uid_t) * 3 + 1; - length ++; /* colon */ - length += 3; /* rwx */ + length += 3; /* rwx */ + length += 1; /* colon */ + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 7; /* fdinSFI */ + length += 1; /* colon */ + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) != 0) + length += 4; /* deny */ + else + length += 5; /* allow, alarm, audit */ + length += 1; /* colon */ + } length += 1; /* colon */ length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ @@ -480,34 +536,39 @@ archive_acl_text_w(struct archive *a, st ap = ap->next; } - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { + if (count == 0) + return (NULL); + + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } - if (count == 0) - return (NULL); - /* Now, allocate the string and actually populate it. */ wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) return (NULL); count = 0; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, - acl->mode & 0700, -1); + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, acl->mode & 0700, -1); *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, - acl->mode & 0070, -1); + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, acl->mode & 0070, -1); *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, - acl->mode & 0007, -1); + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_OTHER, NULL, acl->mode & 0007, -1); count += 3; + } + if ((flags & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_TYPE_NFS4)) != 0) { ap = acl->acl_head; while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + if ((ap->type & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_TYPE_NFS4)) != 0) { r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { *wp++ = separator; @@ -515,8 +576,8 @@ archive_acl_text_w(struct archive *a, st id = ap->id; else id = -1; - append_entry_w(&wp, NULL, ap->tag, wname, - ap->permset, id); + append_entry_w(&wp, NULL, ap->type, ap->tag, + wname, ap->permset, id); count++; } else if (r < 0 && errno == ENOMEM) return (NULL); @@ -525,7 +586,6 @@ archive_acl_text_w(struct archive *a, st } } - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) prefix = L"default:"; @@ -543,8 +603,8 @@ archive_acl_text_w(struct archive *a, st id = ap->id; else id = -1; - append_entry_w(&wp, prefix, ap->tag, - wname, ap->permset, id); + append_entry_w(&wp, prefix, ap->type, + ap->tag, wname, ap->permset, id); count ++; } else if (r < 0 && errno == ENOMEM) return (NULL); @@ -568,8 +628,8 @@ append_id_w(wchar_t **wp, int id) } static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id) +append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, + int tag, const wchar_t *wname, int perm, int id) { if (prefix != NULL) { wcscpy(*wp, prefix); @@ -579,6 +639,11 @@ append_entry_w(wchar_t **wp, const wchar case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; + if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + wcscpy(*wp, L"owner@"); + break; + } + /* FALLTHROUGH */ /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); @@ -603,18 +668,57 @@ append_entry_w(wchar_t **wp, const wchar } *wp += wcslen(*wp); *(*wp)++ = L':'; - if (wname != NULL) { - wcscpy(*wp, wname); + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0 || + tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + if (wname != NULL) { + wcscpy(*wp, wname); + *wp += wcslen(*wp); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id_w(wp, id); + id = -1; + } + *(*wp)++ = L':'; + } + *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_READ | + ARCHIVE_ENTRY_ACL_READ_DATA | + ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? L'r' : L'-'; + *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE | + ARCHIVE_ENTRY_ACL_WRITE_DATA | + ARCHIVE_ENTRY_ACL_ADD_FILE)) ? L'w' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? L'x' : L'-'; + if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? L'p' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? L'd' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? L'D' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? L'a' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? L'A' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? L'R' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? L'W' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? L'c' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? L'C' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? L'o' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? L's' : L'-'; + *(*wp)++ = L':'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? L'f' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? L'd' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? L'i' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? L'n' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? L'S' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? L'F' : L'-'; + *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? L'I' : L'-'; + *(*wp)++ = L':'; + if (type & ARCHIVE_ENTRY_ACL_TYPE_ALLOW) + wcscpy(*wp, L"allow"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_DENY) + wcscpy(*wp, L"deny"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_AUDIT) + wcscpy(*wp, L"audit"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_ALARM) + wcscpy(*wp, L"alarm"); *wp += wcslen(*wp); - } else if (tag == ARCHIVE_ENTRY_ACL_USER - || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id_w(wp, id); - id = -1; } - *(*wp)++ = L':'; - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); @@ -637,6 +741,16 @@ archive_acl_text_l(struct archive_acl *a int id, r; char *p; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) && + (flags & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))) { + /* cannot convert NFSv4 ACLs and POSIX1e ACLs at the same time */ + return (-1); + } + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) && (flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) { + /* cannot have access and default at the same time */ + return (-1); + } + if (acl->acl_text != NULL) { free (acl->acl_text); acl->acl_text = NULL; @@ -655,63 +769,109 @@ archive_acl_text_l(struct archive_acl *a if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ - length += 5; /* tag name */ + switch (ap->tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 6; /* "owner@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + length += 4; /* "user" */ + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 6; /* "group@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + case ARCHIVE_ENTRY_ACL_OTHER: + length += 5; /* "group", "other" */ + break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + length += 9; /* "everyone@" */ + break; + } + length += 1; /* colon */ - r = archive_mstring_get_mbs_l( - &ap->name, &name, &len, sc); - if (r != 0) - return (-1); - if (len > 0 && name != NULL) - length += len; + if (((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) || + ap->tag == ARCHIVE_ENTRY_ACL_USER || + ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { + r = archive_mstring_get_mbs_l( + &ap->name, &name, &len, sc); + if (r != 0) + return (-1); + if (len > 0 && name != NULL) + length += len; + else + length += sizeof(uid_t) * 3 + 1; + length += 1; /* colon */ + } + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) + length += 14; /* rwxpdDaARWcCos */ else - length += sizeof(uid_t) * 3 + 1; - length ++; /* colon */ - length += 3; /* rwx */ + length += 3; /* rwx */ length += 1; /* colon */ + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + length += 7; /* fdinSFI */ + length += 1; /* colon */ + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DENY) != 0) + length += 4; /* deny */ + else + length += 5; /* allow, alarm, audit */ + length += 1; /* colon */ + } + length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ } ap = ap->next; } - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { + if (count == 0) + return (0); + + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } - if (count == 0) - return (0); - /* Now, allocate the string and actually populate it. */ p = acl->acl_text = (char *)malloc(length); if (p == NULL) return (-1); count = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, - acl->mode & 0700, -1); + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, acl->mode & 0700, -1); *p++ = ','; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, - acl->mode & 0070, -1); + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, acl->mode & 0070, -1); *p++ = ','; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, - acl->mode & 0007, -1); + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_OTHER, NULL, acl->mode & 0007, -1); count += 3; + } + if ((flags & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_TYPE_NFS4)) != 0) { for (ap = acl->acl_head; ap != NULL; ap = ap->next) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) + if ((ap->type & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_TYPE_NFS4)) == 0) continue; r = archive_mstring_get_mbs_l( &ap->name, &name, &len, sc); if (r != 0) return (-1); - *p++ = separator; + if (count > 0) + *p++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; - append_entry(&p, NULL, ap->tag, name, + append_entry(&p, NULL, ap->type, ap->tag, name, ap->permset, id); count++; } @@ -737,7 +897,7 @@ archive_acl_text_l(struct archive_acl *a id = ap->id; else id = -1; - append_entry(&p, prefix, ap->tag, + append_entry(&p, prefix, ap->type, ap->tag, name, ap->permset, id); count ++; } @@ -760,8 +920,8 @@ append_id(char **p, int id) } static void -append_entry(char **p, const char *prefix, int tag, - const char *name, int perm, int id) +append_entry(char **p, const char *prefix, int type, + int tag, const char *name, int perm, int id) { if (prefix != NULL) { strcpy(*p, prefix); @@ -771,6 +931,10 @@ append_entry(char **p, const char *prefi case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; + if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + strcpy(*p, "owner@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: strcpy(*p, "user"); @@ -778,6 +942,10 @@ append_entry(char **p, const char *prefi case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; + if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + strcpy(*p, "group@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: strcpy(*p, "group"); @@ -792,21 +960,65 @@ append_entry(char **p, const char *prefi name = NULL; id = -1; break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + strcpy(*p, "everyone@"); + name = NULL; + id = -1; + break; } *p += strlen(*p); *(*p)++ = ':'; - if (name != NULL) { - strcpy(*p, name); + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0 || + tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + if (name != NULL) { + strcpy(*p, name); + *p += strlen(*p); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id(p, id); + id = -1; + } + *(*p)++ = ':'; + } + *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_READ | + ARCHIVE_ENTRY_ACL_READ_DATA | + ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? 'r' : '-'; + *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE | + ARCHIVE_ENTRY_ACL_WRITE_DATA | + ARCHIVE_ENTRY_ACL_ADD_FILE)) ? 'w' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? 'x' : '-'; + if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? 'p' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? 'd' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? 'D' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? 'a' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? 'A' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? 'R' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? 'W' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? 'c' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? 'C' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? 'o' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? 's' : '-'; + *(*p)++ = ':'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? 'f' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? 'd' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? 'i' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? 'n' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? 'S' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? 'F' : '-'; + *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? 'I' : '-'; + *(*p)++ = ':'; + if (type & ARCHIVE_ENTRY_ACL_TYPE_ALLOW) + strcpy(*p, "allow"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_DENY) + strcpy(*p, "deny"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_AUDIT) + strcpy(*p, "audit"); + else if (type & ARCHIVE_ENTRY_ACL_TYPE_ALARM) + strcpy(*p, "alarm"); *p += strlen(*p); - } else if (tag == ARCHIVE_ENTRY_ACL_USER - || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id(p, id); - id = -1; } - *(*p)++ = ':'; - *(*p)++ = (perm & 0444) ? 'r' : '-'; - *(*p)++ = (perm & 0222) ? 'w' : '-'; - *(*p)++ = (perm & 0111) ? 'x' : '-'; if (id != -1) { *(*p)++ = ':'; append_id(p, id); @@ -827,12 +1039,19 @@ archive_acl_parse_w(struct archive_acl * struct { const wchar_t *start; const wchar_t *end; - } field[4], name; + } field[6], name; - int fields, n; + int numfields, fields, n; int type, tag, permset, id; + int offset; wchar_t sep; + if (default_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) + numfields = 6; + else + numfields = 4; + + while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, @@ -842,7 +1061,7 @@ archive_acl_parse_w(struct archive_acl * do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); - if (fields < 4) { + if (fields < numfields) { field[fields].start = start; field[fields].end = end; } @@ -850,72 +1069,148 @@ archive_acl_parse_w(struct archive_acl * } while (sep == L':'); /* Set remaining fields to blank. */ - for (n = fields; n < 4; ++n) + for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; - /* Check for a numeric ID in field 1 or 3. */ - id = -1; - isint_w(field[1].start, field[1].end, &id); - /* Field 3 is optional. */ - if (id == -1 && fields > 3) - isint_w(field[3].start, field[3].end, &id); + if (default_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* POSIX.1e ACLs */ + /* Check for a numeric ID in field 1 or 3. */ + id = -1; + isint_w(field[1].start, field[1].end, &id); + /* Field 3 is optional. */ + if (id == -1 && fields > 3) + isint_w(field[3].start, field[3].end, &id); + + /* + * Solaris extension: "defaultuser::rwx" is the + * default ACL corresponding to "user::rwx", etc. + */ + if (field[0].end - field[0].start > 7 + && wmemcmp(field[0].start, L"default", 7) == 0) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + field[0].start += 7; + } else + type = default_type; - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (field[0].end - field[0].start > 7 - && wmemcmp(field[0].start, L"default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - field[0].start += 7; - } else - type = default_type; - - name.start = name.end = NULL; - if (prefix_w(field[0].start, field[0].end, L"user")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) + name.start = name.end = NULL; + if (prefix_w(field[0].start, field[0].end, L"user")) { + if (!ismode_w(field[2].start, field[2].end, + &permset)) return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_USER; - name = field[1]; + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_USER; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (prefix_w(field[0].start, field[0].end, + L"group")) { + if (!ismode_w(field[2].start, field[2].end, + &permset)) + return (ARCHIVE_WARN); + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_GROUP; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (prefix_w(field[0].start, field[0].end, + L"other")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode_w(field[1].start, field[1].end, + &permset)) { + /* This is Solaris-style "other:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode_w(field[2].start, field[2].end, + &permset)) { + /* This is FreeBSD-style "other::rwx" */ + } else + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_OTHER; + } else if (prefix_w(field[0].start, field[0].end, + L"mask")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode_w(field[1].start, field[1].end, + &permset)) { + /* This is Solaris-style "mask:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode_w(field[2].start, field[2].end, + &permset)) { + /* This is FreeBSD-style "mask::rwx" */ + } else + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_MASK; } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"group")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { + } else { + /* NFSv4 ACLs */ + if (wcsncmp(field[0].start, L"user", + field[0].end - field[0].start) == 0) + tag = ARCHIVE_ENTRY_ACL_USER; + else if (wcsncmp(field[0].start, L"group", + field[0].end - field[0].start) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP; - name = field[1]; - } else + else if (wcsncmp(field[0].start, L"owner@", + field[0].end - field[0].start) == 0) + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + else if (wcsncmp(field[0].start, L"group@", + field[0].end - field[0].start) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"other")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "other:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "other::rwx" */ - } else + else if (wcsncmp(field[0].start, L"everyone@", + field[0].end - field[0].start) == 0) + tag = ARCHIVE_ENTRY_ACL_EVERYONE; + else { + /* Unknown entry */ return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_w(field[0].start, field[0].end, L"mask")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "mask:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "mask::rwx" */ + } + + permset = 0; + name.start = name.end = NULL; + + if (tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + offset = 1; + name = field[1]; } else + offset = 0; + + if (parse_nfs4_perms_w(field[1 + offset].start, + field[1 + offset].end, &permset) != 0) { + /* NFS4 perms are invalid */ + return (ARCHIVE_WARN); + } + if (parse_nfs4_flags_w(field[2 + offset].start, + field[2 + offset].end, &permset) != 0) { + /* NFS4 flags are invalid */ + return (ARCHIVE_WARN); + } + if (wcsncmp(field[3 + offset].start, L"allow", + field[3 + offset].end - field[3 + offset].start) + == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; + else if (wcsncmp(field[3 + offset].start, L"deny", + field[3 + offset].end - field[3 + offset].start) + == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_DENY; + else if (wcsncmp(field[3 + offset].start, L"audit", + field[3 + offset].end - field[3 + offset].start) + == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; + else if (wcsncmp(field[3 + offset].start, L"alarm", + field[3 + offset].end - field[3 + offset].start) + == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; + else { + /* Unknown type */ return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - return (ARCHIVE_WARN); + } + isint_w(field[4 + offset].start, field[4 + offset].end, + &id); + } /* Add entry to the internal list. */ archive_acl_add_entry_w_len(acl, type, permset, @@ -985,6 +1280,78 @@ ismode_w(const wchar_t *start, const wch return (1); } +/* Parse a wstring as a strict NFSv4 ACL permission field. */ +static int +parse_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) +{ + const wchar_t *p; + int pos; + const wchar_t *letter = L"rwxpdDaARWcCos"; + const int perms[14] = { + ARCHIVE_ENTRY_ACL_READ_DATA, + ARCHIVE_ENTRY_ACL_WRITE_DATA, + ARCHIVE_ENTRY_ACL_EXECUTE, + ARCHIVE_ENTRY_ACL_APPEND_DATA, + ARCHIVE_ENTRY_ACL_DELETE, + ARCHIVE_ENTRY_ACL_DELETE_CHILD, + ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, + ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, + ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, + ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, + ARCHIVE_ENTRY_ACL_READ_ACL, + ARCHIVE_ENTRY_ACL_WRITE_ACL, + ARCHIVE_ENTRY_ACL_WRITE_OWNER, + ARCHIVE_ENTRY_ACL_SYNCHRONIZE + }; + + if (start >= end) + return (0); + p = start; + pos = 0; + while (p < end && pos < 14) { + if (*p == letter[pos]) + *permset |= perms[pos]; + else if (*p != '-') + return (-1); + p = p + sizeof(wchar_t); + pos++; + } + return (0); +} + +/* Parse a string as a strict NFSv4 ACL flags field. */ +static int +parse_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) +{ + const wchar_t *p; + int pos; + const wchar_t *letter = L"fdinSFI"; + const int perms[7] = { + ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, + ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, + ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, + ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, + ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, + ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, + ARCHIVE_ENTRY_ACL_ENTRY_INHERITED + }; + + if (start >= end) + return (0); + p = start; + pos = 0; + while (p < end && pos < 7) { + if (*p == letter[pos]) + *permset |= perms[pos]; + else if (*p != '-') + return (-1); + p = p + sizeof(wchar_t); + pos++; + } + return (0); +} + + /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first @@ -1057,12 +1424,18 @@ archive_acl_parse_l(struct archive_acl * struct { const char *start; const char *end; - } field[4], name; + } field[6], name; - int fields, n, r, ret = ARCHIVE_OK; + int numfields, fields, n, r, ret = ARCHIVE_OK; int type, tag, permset, id; + int offset; char sep; + if (default_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) + numfields = 6; + else + numfields = 4; + while (text != NULL && *text != '\0') { /* * Parse the fields out of the next entry, @@ -1072,7 +1445,7 @@ archive_acl_parse_l(struct archive_acl * do { const char *start, *end; next_field(&text, &start, &end, &sep); - if (fields < 4) { + if (fields < numfields) { field[fields].start = start; field[fields].end = end; } @@ -1080,72 +1453,148 @@ archive_acl_parse_l(struct archive_acl * } while (sep == ':'); /* Set remaining fields to blank. */ - for (n = fields; n < 4; ++n) + for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; - /* Check for a numeric ID in field 1 or 3. */ - id = -1; - isint(field[1].start, field[1].end, &id); - /* Field 3 is optional. */ - if (id == -1 && fields > 3) - isint(field[3].start, field[3].end, &id); + if (default_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* POSIX.1e ACLs */ + /* Check for a numeric ID in field 1 or 3. */ + id = -1; + isint(field[1].start, field[1].end, &id); + /* Field 3 is optional. */ + if (id == -1 && fields > 3) + isint(field[3].start, field[3].end, &id); + + /* + * Solaris extension: "defaultuser::rwx" is the + * default ACL corresponding to "user::rwx", etc. + */ + if (field[0].end - field[0].start > 7 + && memcmp(field[0].start, "default", 7) == 0) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + field[0].start += 7; + } else + type = default_type; - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (field[0].end - field[0].start > 7 - && memcmp(field[0].start, "default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - field[0].start += 7; - } else - type = default_type; - - name.start = name.end = NULL; - if (prefix_c(field[0].start, field[0].end, "user")) { - if (!ismode(field[2].start, field[2].end, &permset)) + name.start = name.end = NULL; + if (prefix_c(field[0].start, field[0].end, "user")) { + if (!ismode(field[2].start, field[2].end, + &permset)) + return (ARCHIVE_WARN); + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_USER; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (prefix_c(field[0].start, field[0].end, + "group")) { + if (!ismode(field[2].start, field[2].end, + &permset)) + return (ARCHIVE_WARN); + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_GROUP; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (prefix_c(field[0].start, field[0].end, + "other")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode(field[1].start, field[1].end, + &permset)) { + /* This is Solaris-style "other:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode(field[2].start, field[2].end, + &permset)) { + /* This is FreeBSD-style "other::rwx" */ + } else + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_OTHER; + } else if (prefix_c(field[0].start, field[0].end, + "mask")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode(field[1].start, field[1].end, + &permset)) { + /* This is Solaris-style "mask:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode(field[2].start, field[2].end, + &permset)) { + /* This is FreeBSD-style "mask::rwx" */ + } else *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***