| Index: UPDATING |
| =================================================================== |
| --- UPDATING (版本 306336) |
| +++ UPDATING (版本 306941) |
| @@ -16,6 +16,16 @@ |
| stable/10, and then rebuild without this option. The bootstrap process from |
| older version of current is a bit fragile. |
| |
| +20161010 p10 FreeBSD-SA-16:29.bspatch |
| + FreeBSD-SA-16:30.portsnap |
| + FreeBSD-SA-16:31.libarchive |
| + |
| + Fix bspatch heap overflow vulnerability. [SA-16:29] |
| + |
| + Fix multiple portsnap vulnerabilities. [SA-16:30] |
| + |
| + Fix multiple libarchive vulnerabilities. [SA-16:31] |
| + |
| 20160926 p9 FreeBSD-SA-16:26.openssl [revised] |
| |
| Fix OpenSSL regression introduced in SA-16:26. |
| Index: contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (版本 306336) |
| +++ contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (版本 306941) |
| @@ -409,9 +409,7 @@ |
| { |
| const char *accpath; |
| acl_t acl; |
| -#if HAVE_ACL_IS_TRIVIAL_NP |
| int r; |
| -#endif |
| |
| accpath = archive_entry_sourcepath(entry); |
| if (accpath == NULL) |
| @@ -443,9 +441,13 @@ |
| } |
| #endif |
| if (acl != NULL) { |
| - translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); |
| + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); |
| acl_free(acl); |
| - return (ARCHIVE_OK); |
| + if (r != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, errno, |
| + "Couldn't translate NFSv4 ACLs: %s", accpath); |
| + } |
| + return (r); |
| } |
| |
| /* Retrieve access ACL from file. */ |
| @@ -464,9 +466,14 @@ |
| else |
| acl = acl_get_file(accpath, ACL_TYPE_ACCESS); |
| if (acl != NULL) { |
| - translate_acl(a, entry, acl, |
| + r = translate_acl(a, entry, acl, |
| ARCHIVE_ENTRY_ACL_TYPE_ACCESS); |
| acl_free(acl); |
| + if (r != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, errno, |
| + "Couldn't translate access ACLs: %s", accpath); |
| + return (r); |
| + } |
| } |
| |
| /* Only directories can have default ACLs. */ |
| @@ -473,9 +480,15 @@ |
| if (S_ISDIR(archive_entry_mode(entry))) { |
| acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); |
| if (acl != NULL) { |
| - translate_acl(a, entry, acl, |
| + r = translate_acl(a, entry, acl, |
| ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); |
| acl_free(acl); |
| + if (r != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, errno, |
| + "Couldn't translate default ACLs: %s", |
| + accpath); |
| + return (r); |
| + } |
| } |
| } |
| return (ARCHIVE_OK); |
| @@ -536,7 +549,11 @@ |
| // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 |
| // Make sure the "brand" on this ACL is consistent |
| // with the default_entry_acl_type bits provided. |
| - acl_get_brand_np(acl, &brand); |
| + if (acl_get_brand_np(acl, &brand) != 0) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to read ACL brand"); |
| + return (ARCHIVE_WARN); |
| + } |
| switch (brand) { |
| case ACL_BRAND_POSIX: |
| switch (default_entry_acl_type) { |
| @@ -544,30 +561,42 @@ |
| case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: |
| break; |
| default: |
| - // XXX set warning message? |
| - return ARCHIVE_FAILED; |
| + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| + "Invalid ACL entry type for POSIX.1e ACL"); |
| + return (ARCHIVE_WARN); |
| } |
| break; |
| case ACL_BRAND_NFS4: |
| if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { |
| - // XXX set warning message? |
| - return ARCHIVE_FAILED; |
| + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| + "Invalid ACL entry type for NFSv4 ACL"); |
| + return (ARCHIVE_WARN); |
| } |
| break; |
| default: |
| - // XXX set warning message? |
| - return ARCHIVE_FAILED; |
| + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| + "Unknown ACL brand"); |
| + return (ARCHIVE_WARN); |
| break; |
| } |
| |
| |
| s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); |
| + if (s == -1) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to get first ACL entry"); |
| + return (ARCHIVE_WARN); |
| + } |
| while (s == 1) { |
| ae_id = -1; |
| ae_name = NULL; |
| ae_perm = 0; |
| |
| - acl_get_tag_type(acl_entry, &acl_tag); |
| + if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to get ACL tag type"); |
| + return (ARCHIVE_WARN); |
| + } |
| switch (acl_tag) { |
| case ACL_USER: |
| ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); |
| @@ -600,12 +629,17 @@ |
| continue; |
| } |
| |
| - // XXX acl type maps to allow/deny/audit/YYYY bits |
| - // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for |
| - // non-NFSv4 ACLs |
| + // XXX acl_type maps to allow/deny/audit/YYYY bits |
| entry_acl_type = default_entry_acl_type; |
| - r = acl_get_entry_type_np(acl_entry, &acl_type); |
| - if (r == 0) { |
| + if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { |
| + /* |
| + * acl_get_entry_type_np() falis with non-NFSv4 ACLs |
| + */ |
| + if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { |
| + archive_set_error(&a->archive, errno, "Failed " |
| + "to get ACL type from a NFSv4 ACL entry"); |
| + return (ARCHIVE_WARN); |
| + } |
| switch (acl_type) { |
| case ACL_ENTRY_TYPE_ALLOW: |
| entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; |
| @@ -619,28 +653,52 @@ |
| case ACL_ENTRY_TYPE_ALARM: |
| entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; |
| break; |
| + default: |
| + archive_set_error(&a->archive, errno, |
| + "Invalid NFSv4 ACL entry type"); |
| + return (ARCHIVE_WARN); |
| } |
| + |
| + /* |
| + * Libarchive stores "flag" (NFSv4 inheritance bits) |
| + * in the ae_perm bitmap. |
| + * |
| + * acl_get_flagset_np() fails with non-NFSv4 ACLs |
| + */ |
| + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to get flagset from a NFSv4 ACL entry"); |
| + return (ARCHIVE_WARN); |
| + } |
| + for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { |
| + r = acl_get_flag_np(acl_flagset, |
| + acl_inherit_map[i].platform_inherit); |
| + if (r == -1) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to check flag in a NFSv4 " |
| + "ACL flagset"); |
| + return (ARCHIVE_WARN); |
| + } else if (r) |
| + ae_perm |= acl_inherit_map[i].archive_inherit; |
| + } |
| } |
| |
| - /* |
| - * Libarchive stores "flag" (NFSv4 inheritance bits) |
| - * in the ae_perm bitmap. |
| - */ |
| - acl_get_flagset_np(acl_entry, &acl_flagset); |
| - for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { |
| - if (acl_get_flag_np(acl_flagset, |
| - acl_inherit_map[i].platform_inherit)) |
| - ae_perm |= acl_inherit_map[i].archive_inherit; |
| - |
| - } |
| - |
| - acl_get_permset(acl_entry, &acl_permset); |
| - for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { |
| + if (acl_get_permset(acl_entry, &acl_permset) != 0) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to get ACL permission set"); |
| + return (ARCHIVE_WARN); |
| + } |
| + for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { |
| /* |
| * acl_get_perm() is spelled differently on different |
| * platforms; see above. |
| */ |
| - if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) |
| + r = ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm); |
| + if (r == -1) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to check permission in an ACL permission set"); |
| + return (ARCHIVE_WARN); |
| + } else if (r) |
| ae_perm |= acl_perm_map[i].archive_perm; |
| } |
| |
| @@ -649,6 +707,11 @@ |
| ae_id, ae_name); |
| |
| s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); |
| + if (s == -1) { |
| + archive_set_error(&a->archive, errno, |
| + "Failed to get next ACL entry"); |
| + return (ARCHIVE_WARN); |
| + } |
| } |
| return (ARCHIVE_OK); |
| } |
| Index: contrib/libarchive/libarchive/archive_read_support_format_tar.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/archive_read_support_format_tar.c (版本 306336) |
| +++ contrib/libarchive/libarchive/archive_read_support_format_tar.c (版本 306941) |
| @@ -136,6 +136,7 @@ |
| int64_t entry_padding; |
| int64_t entry_bytes_unconsumed; |
| int64_t realsize; |
| + int sparse_allowed; |
| struct sparse_block *sparse_list; |
| struct sparse_block *sparse_last; |
| int64_t sparse_offset; |
| @@ -1226,6 +1227,14 @@ |
| * sparse information in the extended area. |
| */ |
| /* FALLTHROUGH */ |
| + case '0': |
| + /* |
| + * Enable sparse file "read" support only for regular |
| + * files and explicit GNU sparse files. However, we |
| + * don't allow non-standard file types to be sparse. |
| + */ |
| + tar->sparse_allowed = 1; |
| + /* FALLTHROUGH */ |
| default: /* Regular file and non-standard types */ |
| /* |
| * Per POSIX: non-recognized types should always be |
| @@ -1685,6 +1694,14 @@ |
| #endif |
| switch (key[0]) { |
| case 'G': |
| + /* Reject GNU.sparse.* headers on non-regular files. */ |
| + if (strncmp(key, "GNU.sparse", 10) == 0 && |
| + !tar->sparse_allowed) { |
| + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| + "Non-regular file cannot be sparse"); |
| + return (ARCHIVE_FATAL); |
| + } |
| + |
| /* GNU "0.0" sparse pax format. */ |
| if (strcmp(key, "GNU.sparse.numblocks") == 0) { |
| tar->sparse_offset = -1; |
| Index: contrib/libarchive/libarchive/archive_write_disk_acl.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/archive_write_disk_acl.c (版本 306336) |
| +++ contrib/libarchive/libarchive/archive_write_disk_acl.c (版本 306941) |
| @@ -131,6 +131,7 @@ |
| acl_entry_t acl_entry; |
| acl_permset_t acl_permset; |
| acl_flagset_t acl_flagset; |
| + int r; |
| int ret; |
| int ae_type, ae_permset, ae_tag, ae_id; |
| uid_t ae_uid; |
| @@ -144,9 +145,19 @@ |
| if (entries == 0) |
| return (ARCHIVE_OK); |
| acl = acl_init(entries); |
| + if (acl == (acl_t)NULL) { |
| + archive_set_error(a, errno, |
| + "Failed to initialize ACL working storage"); |
| + return (ARCHIVE_FAILED); |
| + } |
| while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, |
| &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { |
| - acl_create_entry(&acl, &acl_entry); |
| + if (acl_create_entry(&acl, &acl_entry) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to create a new ACL entry"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| |
| switch (ae_tag) { |
| case ARCHIVE_ENTRY_ACL_USER: |
| @@ -175,22 +186,25 @@ |
| acl_set_tag_type(acl_entry, ACL_EVERYONE); |
| break; |
| default: |
| - /* XXX */ |
| - break; |
| + archive_set_error(a, ARCHIVE_ERRNO_MISC, |
| + "Unknown ACL tag"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| } |
| |
| + r = 0; |
| switch (ae_type) { |
| case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: |
| - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); |
| + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); |
| break; |
| case ARCHIVE_ENTRY_ACL_TYPE_DENY: |
| - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); |
| + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); |
| break; |
| case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: |
| - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); |
| + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); |
| break; |
| case ARCHIVE_ENTRY_ACL_TYPE_ALARM: |
| - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); |
| + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); |
| break; |
| case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: |
| case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: |
| @@ -197,25 +211,70 @@ |
| // These don't translate directly into the system ACL. |
| break; |
| default: |
| - // XXX error handling here. |
| - break; |
| + archive_set_error(a, ARCHIVE_ERRNO_MISC, |
| + "Unknown ACL entry type"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| } |
| + if (r != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to set ACL entry type"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| |
| - acl_get_permset(acl_entry, &acl_permset); |
| - acl_clear_perms(acl_permset); |
| + if (acl_get_permset(acl_entry, &acl_permset) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to get ACL permission set"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| + if (acl_clear_perms(acl_permset) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to clear ACL permissions"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| |
| for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { |
| if (ae_permset & acl_perm_map[i].archive_perm) |
| - acl_add_perm(acl_permset, |
| - acl_perm_map[i].platform_perm); |
| + if (acl_add_perm(acl_permset, |
| + acl_perm_map[i].platform_perm) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to add ACL permission"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| } |
| |
| acl_get_flagset_np(acl_entry, &acl_flagset); |
| - acl_clear_flags_np(acl_flagset); |
| - for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { |
| - if (ae_permset & acl_inherit_map[i].archive_inherit) |
| - acl_add_flag_np(acl_flagset, |
| - acl_inherit_map[i].platform_inherit); |
| + if (acl_type == ACL_TYPE_NFS4) { |
| + /* |
| + * acl_get_flagset_np() fails with non-NFSv4 ACLs |
| + */ |
| + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to get flagset from an NFSv4 ACL entry"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| + if (acl_clear_flags_np(acl_flagset) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to clear flags from an NFSv4 ACL flagset"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| + for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { |
| + if (ae_permset & acl_inherit_map[i].archive_inherit) { |
| + if (acl_add_flag_np(acl_flagset, |
| + acl_inherit_map[i].platform_inherit) != 0) { |
| + archive_set_error(a, errno, |
| + "Failed to add flag to NFSv4 ACL flagset"); |
| + ret = ARCHIVE_FAILED; |
| + goto exit_free; |
| + } |
| + } |
| + } |
| } |
| } |
| |
| @@ -243,6 +302,7 @@ |
| ret = ARCHIVE_WARN; |
| } |
| #endif |
| +exit_free: |
| acl_free(acl); |
| return (ret); |
| } |
| Index: contrib/libarchive/libarchive/archive_write_disk_posix.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/archive_write_disk_posix.c (版本 306336) |
| +++ contrib/libarchive/libarchive/archive_write_disk_posix.c (版本 306941) |
| @@ -140,9 +140,19 @@ |
| #define O_BINARY 0 |
| #endif |
| #ifndef O_CLOEXEC |
| -#define O_CLOEXEC 0 |
| +#define O_CLOEXEC 0 |
| #endif |
| |
| +/* Ignore non-int O_NOFOLLOW constant. */ |
| +/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */ |
| +#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX) |
| +#undef O_NOFOLLOW |
| +#endif |
| + |
| +#ifndef O_NOFOLLOW |
| +#define O_NOFOLLOW 0 |
| +#endif |
| + |
| struct fixup_entry { |
| struct fixup_entry *next; |
| struct archive_acl acl; |
| @@ -326,6 +336,7 @@ |
| |
| #define HFS_BLOCKS(s) ((s) >> 12) |
| |
| +static int check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); |
| static int check_symlinks(struct archive_write_disk *); |
| static int create_filesystem_object(struct archive_write_disk *); |
| static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); |
| @@ -332,6 +343,7 @@ |
| #if defined(HAVE_FCHDIR) && defined(PATH_MAX) |
| static void edit_deep_directories(struct archive_write_disk *ad); |
| #endif |
| +static int cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); |
| static int cleanup_pathname(struct archive_write_disk *); |
| static int create_dir(struct archive_write_disk *, char *); |
| static int create_parent_dir(struct archive_write_disk *, char *); |
| @@ -1791,7 +1803,7 @@ |
| char *tail = a->name; |
| |
| /* If path is short, avoid the open() below. */ |
| - if (strlen(tail) <= PATH_MAX) |
| + if (strlen(tail) < PATH_MAX) |
| return; |
| |
| /* Try to record our starting dir. */ |
| @@ -1801,7 +1813,7 @@ |
| return; |
| |
| /* As long as the path is too long... */ |
| - while (strlen(tail) > PATH_MAX) { |
| + while (strlen(tail) >= PATH_MAX) { |
| /* Locate a dir prefix shorter than PATH_MAX. */ |
| tail += PATH_MAX - 8; |
| while (tail > a->name && *tail != '/') |
| @@ -1996,6 +2008,10 @@ |
| const char *linkname; |
| mode_t final_mode, mode; |
| int r; |
| + /* these for check_symlinks_fsobj */ |
| + char *linkname_copy; /* non-const copy of linkname */ |
| + struct archive_string error_string; |
| + int error_number; |
| |
| /* We identify hard/symlinks according to the link names. */ |
| /* Since link(2) and symlink(2) don't handle modes, we're done here. */ |
| @@ -2004,6 +2020,27 @@ |
| #if !HAVE_LINK |
| return (EPERM); |
| #else |
| + archive_string_init(&error_string); |
| + linkname_copy = strdup(linkname); |
| + if (linkname_copy == NULL) { |
| + return (EPERM); |
| + } |
| + /* TODO: consider using the cleaned-up path as the link target? */ |
| + r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); |
| + if (r != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, error_number, "%s", error_string.s); |
| + free(linkname_copy); |
| + /* EPERM is more appropriate than error_number for our callers */ |
| + return (EPERM); |
| + } |
| + r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); |
| + if (r != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, error_number, "%s", error_string.s); |
| + free(linkname_copy); |
| + /* EPERM is more appropriate than error_number for our callers */ |
| + return (EPERM); |
| + } |
| + free(linkname_copy); |
| r = link(linkname, a->name) ? errno : 0; |
| /* |
| * New cpio and pax formats allow hardlink entries |
| @@ -2022,7 +2059,7 @@ |
| a->deferred = 0; |
| } else if (r == 0 && a->filesize > 0) { |
| a->fd = open(a->name, |
| - O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC); |
| + O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); |
| __archive_ensure_cloexec_flag(a->fd); |
| if (a->fd < 0) |
| r = errno; |
| @@ -2332,70 +2369,140 @@ |
| return (a->current_fixup); |
| } |
| |
| -/* TODO: Make this work. */ |
| /* |
| - * TODO: The deep-directory support bypasses this; disable deep directory |
| - * support if we're doing symlink checks. |
| - */ |
| -/* |
| * TODO: Someday, integrate this with the deep dir support; they both |
| * scan the path and both can be optimized by comparing against other |
| * recent paths. |
| */ |
| /* TODO: Extend this to support symlinks on Windows Vista and later. */ |
| + |
| +/* |
| + * Checks the given path to see if any elements along it are symlinks. Returns |
| + * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. |
| + */ |
| static int |
| -check_symlinks(struct archive_write_disk *a) |
| +check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) |
| { |
| #if !defined(HAVE_LSTAT) |
| /* Platform doesn't have lstat, so we can't look for symlinks. */ |
| - (void)a; /* UNUSED */ |
| + (void)path; /* UNUSED */ |
| + (void)error_number; /* UNUSED */ |
| + (void)error_string; /* UNUSED */ |
| + (void)flags; /* UNUSED */ |
| return (ARCHIVE_OK); |
| #else |
| - char *pn; |
| + int res = ARCHIVE_OK; |
| + char *tail; |
| + char *head; |
| + int last; |
| char c; |
| int r; |
| struct stat st; |
| + int restore_pwd; |
| |
| + /* Nothing to do here if name is empty */ |
| + if(path[0] == '\0') |
| + return (ARCHIVE_OK); |
| + |
| /* |
| * Guard against symlink tricks. Reject any archive entry whose |
| * destination would be altered by a symlink. |
| + * |
| + * Walk the filename in chunks separated by '/'. For each segment: |
| + * - if it doesn't exist, continue |
| + * - if it's symlink, abort or remove it |
| + * - if it's a directory and it's not the last chunk, cd into it |
| + * As we go: |
| + * head points to the current (relative) path |
| + * tail points to the temporary \0 terminating the segment we're currently examining |
| + * c holds what used to be in *tail |
| + * last is 1 if this is the last tail |
| */ |
| - /* Whatever we checked last time doesn't need to be re-checked. */ |
| - pn = a->name; |
| - if (archive_strlen(&(a->path_safe)) > 0) { |
| - char *p = a->path_safe.s; |
| - while ((*pn != '\0') && (*p == *pn)) |
| - ++p, ++pn; |
| - } |
| - c = pn[0]; |
| - /* Keep going until we've checked the entire name. */ |
| - while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) { |
| + restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); |
| + __archive_ensure_cloexec_flag(restore_pwd); |
| + if (restore_pwd < 0) |
| + return (ARCHIVE_FATAL); |
| + head = path; |
| + tail = path; |
| + last = 0; |
| + /* TODO: reintroduce a safe cache here? */ |
| + /* Skip the root directory if the path is absolute. */ |
| + if(tail == path && tail[0] == '/') |
| + ++tail; |
| + /* Keep going until we've checked the entire name. |
| + * head, tail, path all alias the same string, which is |
| + * temporarily zeroed at tail, so be careful restoring the |
| + * stashed (c=tail[0]) for error messages. |
| + * Exiting the loop with break is okay; continue is not. |
| + */ |
| + while (!last) { |
| + /* Skip the separator we just consumed, plus any adjacent ones */ |
| + while (*tail == '/') |
| + ++tail; |
| /* Skip the next path element. */ |
| - while (*pn != '\0' && *pn != '/') |
| - ++pn; |
| - c = pn[0]; |
| - pn[0] = '\0'; |
| + while (*tail != '\0' && *tail != '/') |
| + ++tail; |
| + /* is this the last path component? */ |
| + last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); |
| + /* temporarily truncate the string here */ |
| + c = tail[0]; |
| + tail[0] = '\0'; |
| /* Check that we haven't hit a symlink. */ |
| - r = lstat(a->name, &st); |
| + r = lstat(head, &st); |
| if (r != 0) { |
| + tail[0] = c; |
| /* We've hit a dir that doesn't exist; stop now. */ |
| - if (errno == ENOENT) |
| + if (errno == ENOENT) { |
| break; |
| + } else { |
| + /* Treat any other error as fatal - best to be paranoid here |
| + * Note: This effectively disables deep directory |
| + * support when security checks are enabled. |
| + * Otherwise, very long pathnames that trigger |
| + * an error here could evade the sandbox. |
| + * TODO: We could do better, but it would probably |
| + * require merging the symlink checks with the |
| + * deep-directory editing. */ |
| + if (error_number) *error_number = errno; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Could not stat %s", |
| + path); |
| + res = ARCHIVE_FAILED; |
| + break; |
| + } |
| + } else if (S_ISDIR(st.st_mode)) { |
| + if (!last) { |
| + if (chdir(head) != 0) { |
| + tail[0] = c; |
| + if (error_number) *error_number = errno; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Could not chdir %s", |
| + path); |
| + res = (ARCHIVE_FATAL); |
| + break; |
| + } |
| + /* Our view is now from inside this dir: */ |
| + head = tail + 1; |
| + } |
| } else if (S_ISLNK(st.st_mode)) { |
| - if (c == '\0') { |
| + if (last) { |
| /* |
| * Last element is symlink; remove it |
| * so we can overwrite it with the |
| * item being extracted. |
| */ |
| - if (unlink(a->name)) { |
| - archive_set_error(&a->archive, errno, |
| - "Could not remove symlink %s", |
| - a->name); |
| - pn[0] = c; |
| - return (ARCHIVE_FAILED); |
| + if (unlink(head)) { |
| + tail[0] = c; |
| + if (error_number) *error_number = errno; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Could not remove symlink %s", |
| + path); |
| + res = ARCHIVE_FAILED; |
| + break; |
| } |
| - a->pst = NULL; |
| /* |
| * Even if we did remove it, a warning |
| * is in order. The warning is silly, |
| @@ -2402,40 +2509,93 @@ |
| * though, if we're just replacing one |
| * symlink with another symlink. |
| */ |
| - if (!S_ISLNK(a->mode)) { |
| - archive_set_error(&a->archive, 0, |
| - "Removing symlink %s", |
| - a->name); |
| + tail[0] = c; |
| + /* FIXME: not sure how important this is to restore |
| + if (!S_ISLNK(path)) { |
| + if (error_number) *error_number = 0; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Removing symlink %s", |
| + path); |
| } |
| + */ |
| /* Symlink gone. No more problem! */ |
| - pn[0] = c; |
| - return (0); |
| - } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { |
| + res = ARCHIVE_OK; |
| + break; |
| + } else if (flags & ARCHIVE_EXTRACT_UNLINK) { |
| /* User asked us to remove problems. */ |
| - if (unlink(a->name) != 0) { |
| - archive_set_error(&a->archive, 0, |
| - "Cannot remove intervening symlink %s", |
| - a->name); |
| - pn[0] = c; |
| - return (ARCHIVE_FAILED); |
| + if (unlink(head) != 0) { |
| + tail[0] = c; |
| + if (error_number) *error_number = 0; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Cannot remove intervening symlink %s", |
| + path); |
| + res = ARCHIVE_FAILED; |
| + break; |
| } |
| - a->pst = NULL; |
| + tail[0] = c; |
| } else { |
| - archive_set_error(&a->archive, 0, |
| - "Cannot extract through symlink %s", |
| - a->name); |
| - pn[0] = c; |
| - return (ARCHIVE_FAILED); |
| + tail[0] = c; |
| + if (error_number) *error_number = 0; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Cannot extract through symlink %s", |
| + path); |
| + res = ARCHIVE_FAILED; |
| + break; |
| } |
| } |
| + /* be sure to always maintain this */ |
| + tail[0] = c; |
| + if (tail[0] != '\0') |
| + tail++; /* Advance to the next segment. */ |
| } |
| - pn[0] = c; |
| - /* We've checked and/or cleaned the whole path, so remember it. */ |
| - archive_strcpy(&a->path_safe, a->name); |
| - return (ARCHIVE_OK); |
| + /* Catches loop exits via break */ |
| + tail[0] = c; |
| +#ifdef HAVE_FCHDIR |
| + /* If we changed directory above, restore it here. */ |
| + if (restore_pwd >= 0) { |
| + r = fchdir(restore_pwd); |
| + if (r != 0) { |
| + if(error_number) *error_number = errno; |
| + if(error_string) |
| + archive_string_sprintf(error_string, |
| + "chdir() failure"); |
| + } |
| + close(restore_pwd); |
| + restore_pwd = -1; |
| + if (r != 0) { |
| + res = (ARCHIVE_FATAL); |
| + } |
| + } |
| #endif |
| + /* TODO: reintroduce a safe cache here? */ |
| + return res; |
| +#endif |
| } |
| |
| +/* |
| + * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise |
| + * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} |
| + */ |
| +static int |
| +check_symlinks(struct archive_write_disk *a) |
| +{ |
| + struct archive_string error_string; |
| + int error_number; |
| + int rc; |
| + archive_string_init(&error_string); |
| + rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); |
| + if (rc != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, error_number, "%s", error_string.s); |
| + } |
| + archive_string_free(&error_string); |
| + a->pst = NULL; /* to be safe */ |
| + return rc; |
| +} |
| + |
| + |
| #if defined(__CYGWIN__) |
| /* |
| * 1. Convert a path separator from '\' to '/' . |
| @@ -2509,15 +2669,17 @@ |
| * is set) if the path is absolute. |
| */ |
| static int |
| -cleanup_pathname(struct archive_write_disk *a) |
| +cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) |
| { |
| char *dest, *src; |
| char separator = '\0'; |
| |
| - dest = src = a->name; |
| + dest = src = path; |
| if (*src == '\0') { |
| - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| - "Invalid empty pathname"); |
| + if (error_number) *error_number = ARCHIVE_ERRNO_MISC; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Invalid empty pathname"); |
| return (ARCHIVE_FAILED); |
| } |
| |
| @@ -2526,9 +2688,11 @@ |
| #endif |
| /* Skip leading '/'. */ |
| if (*src == '/') { |
| - if (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { |
| - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| - "Path is absolute"); |
| + if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { |
| + if (error_number) *error_number = ARCHIVE_ERRNO_MISC; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Path is absolute"); |
| return (ARCHIVE_FAILED); |
| } |
| |
| @@ -2555,10 +2719,11 @@ |
| } else if (src[1] == '.') { |
| if (src[2] == '/' || src[2] == '\0') { |
| /* Conditionally warn about '..' */ |
| - if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { |
| - archive_set_error(&a->archive, |
| - ARCHIVE_ERRNO_MISC, |
| - "Path contains '..'"); |
| + if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { |
| + if (error_number) *error_number = ARCHIVE_ERRNO_MISC; |
| + if (error_string) |
| + archive_string_sprintf(error_string, |
| + "Path contains '..'"); |
| return (ARCHIVE_FAILED); |
| } |
| } |
| @@ -2589,7 +2754,7 @@ |
| * We've just copied zero or more path elements, not including the |
| * final '/'. |
| */ |
| - if (dest == a->name) { |
| + if (dest == path) { |
| /* |
| * Nothing got copied. The path must have been something |
| * like '.' or '/' or './' or '/././././/./'. |
| @@ -2604,6 +2769,21 @@ |
| return (ARCHIVE_OK); |
| } |
| |
| +static int |
| +cleanup_pathname(struct archive_write_disk *a) |
| +{ |
| + struct archive_string error_string; |
| + int error_number; |
| + int rc; |
| + archive_string_init(&error_string); |
| + rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); |
| + if (rc != ARCHIVE_OK) { |
| + archive_set_error(&a->archive, error_number, "%s", error_string.s); |
| + } |
| + archive_string_free(&error_string); |
| + return rc; |
| +} |
| + |
| /* |
| * Create the parent directory of the specified path, assuming path |
| * is already in mutable storage. |
| Index: contrib/libarchive/libarchive/test/main.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/test/main.c (版本 306336) |
| +++ contrib/libarchive/libarchive/test/main.c (版本 306941) |
| @@ -1396,6 +1396,31 @@ |
| return (0); |
| } |
| |
| +/* Verify mode of 'pathname'. */ |
| +int |
| +assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) |
| +{ |
| + int mode; |
| + int r; |
| + |
| + assertion_count(file, line); |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + failure_start(file, line, "assertFileMode not yet implemented for Windows"); |
| +#else |
| + { |
| + struct stat st; |
| + r = lstat(pathname, &st); |
| + mode = (int)(st.st_mode & 0777); |
| + } |
| + if (r == 0 && mode == expected_mode) |
| + return (1); |
| + failure_start(file, line, "File %s has mode %o, expected %o", |
| + pathname, mode, expected_mode); |
| +#endif |
| + failure_finish(NULL); |
| + return (0); |
| +} |
| + |
| /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ |
| int |
| assertion_is_dir(const char *file, int line, const char *pathname, int mode) |
| Index: contrib/libarchive/libarchive/test/test.h |
| =================================================================== |
| --- contrib/libarchive/libarchive/test/test.h (版本 306336) |
| +++ contrib/libarchive/libarchive/test/test.h (版本 306941) |
| @@ -176,6 +176,8 @@ |
| assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) |
| #define assertFileSize(pathname, size) \ |
| assertion_file_size(__FILE__, __LINE__, pathname, size) |
| +#define assertFileMode(pathname, mode) \ |
| + assertion_file_mode(__FILE__, __LINE__, pathname, mode) |
| #define assertTextFileContents(text, pathname) \ |
| assertion_text_file_contents(__FILE__, __LINE__, text, pathname) |
| #define assertFileContainsLinesAnyOrder(pathname, lines) \ |
| @@ -239,6 +241,7 @@ |
| int assertion_file_nlinks(const char *, int, const char *, int); |
| int assertion_file_not_exists(const char *, int, const char *); |
| int assertion_file_size(const char *, int, const char *, long); |
| +int assertion_file_mode(const char *, int, const char *, int); |
| int assertion_is_dir(const char *, int, const char *, int); |
| int assertion_is_hardlink(const char *, int, const char *, const char *); |
| int assertion_is_not_hardlink(const char *, int, const char *, const char *); |
| Index: contrib/libarchive/libarchive/test/test_write_disk_secure744.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/test/test_write_disk_secure744.c (不存在的) |
| +++ contrib/libarchive/libarchive/test/test_write_disk_secure744.c (版本 306941) |
| @@ -0,0 +1,190 @@ |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #744 describes a bug in the sandboxing code that |
| + * causes very long pathnames to not get checked for symlinks. |
| + */ |
| + |
| +DEFINE_TEST(test_write_disk_secure744) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + size_t buff_size = 8192; |
| + char *buff = malloc(buff_size); |
| + char *p = buff; |
| + int n = 0; |
| + int t; |
| + |
| + assert(buff != NULL); |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + while (p + 500 < buff + buff_size) { |
| + memset(p, 'x', 100); |
| + p += 100; |
| + p[0] = '\0'; |
| + |
| + buff[0] = ((n / 1000) % 10) + '0'; |
| + buff[1] = ((n / 100) % 10)+ '0'; |
| + buff[2] = ((n / 10) % 10)+ '0'; |
| + buff[3] = ((n / 1) % 10)+ '0'; |
| + buff[4] = '_'; |
| + ++n; |
| + |
| + /* Create a symlink pointing to the testworkdir */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, buff); |
| + archive_entry_set_mode(ae, S_IFREG | 0777); |
| + archive_entry_copy_symlink(ae, testworkdir); |
| + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + *p++ = '/'; |
| + sprintf(p, "target%d", n); |
| + |
| + /* Try to create a file through the symlink, should fail. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, buff); |
| + archive_entry_set_mode(ae, S_IFDIR | 0777); |
| + |
| + t = archive_write_header(a, ae); |
| + archive_entry_free(ae); |
| + failure("Attempt to create target%d via %d-character symlink should have failed", n, (int)strlen(buff)); |
| + if(!assertEqualInt(ARCHIVE_FAILED, t)) { |
| + break; |
| + } |
| + } |
| + archive_free(a); |
| + free(buff); |
| +#endif |
| +} |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #744 describes a bug in the sandboxing code that |
| + * causes very long pathnames to not get checked for symlinks. |
| + */ |
| + |
| +DEFINE_TEST(test_write_disk_secure744) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + size_t buff_size = 8192; |
| + char *buff = malloc(buff_size); |
| + char *p = buff; |
| + int n = 0; |
| + int t; |
| + |
| + assert(buff != NULL); |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + while (p + 500 < buff + buff_size) { |
| + memset(p, 'x', 100); |
| + p += 100; |
| + p[0] = '\0'; |
| + |
| + buff[0] = ((n / 1000) % 10) + '0'; |
| + buff[1] = ((n / 100) % 10)+ '0'; |
| + buff[2] = ((n / 10) % 10)+ '0'; |
| + buff[3] = ((n / 1) % 10)+ '0'; |
| + buff[4] = '_'; |
| + ++n; |
| + |
| + /* Create a symlink pointing to the testworkdir */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, buff); |
| + archive_entry_set_mode(ae, S_IFREG | 0777); |
| + archive_entry_copy_symlink(ae, testworkdir); |
| + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + *p++ = '/'; |
| + sprintf(p, "target%d", n); |
| + |
| + /* Try to create a file through the symlink, should fail. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, buff); |
| + archive_entry_set_mode(ae, S_IFDIR | 0777); |
| + |
| + t = archive_write_header(a, ae); |
| + archive_entry_free(ae); |
| + failure("Attempt to create target%d via %d-character symlink should have failed", n, (int)strlen(buff)); |
| + if(!assertEqualInt(ARCHIVE_FAILED, t)) { |
| + break; |
| + } |
| + } |
| + archive_free(a); |
| + free(buff); |
| +#endif |
| +} |
| |
| 属性改变: contrib/libarchive/libarchive/test/test_write_disk_secure744.c |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| ## -0,0 +1 ## |
| +native |
| \ No newline at end of property |
| Added: svn:keywords |
| ## -0,0 +1 ## |
| +FreeBSD=%H |
| \ No newline at end of property |
| Added: svn:mime-type |
| ## -0,0 +1 ## |
| +text/plain |
| \ No newline at end of property |
| Index: contrib/libarchive/libarchive/test/test_write_disk_secure745.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/test/test_write_disk_secure745.c (不存在的) |
| +++ contrib/libarchive/libarchive/test/test_write_disk_secure745.c (版本 306941) |
| @@ -0,0 +1,158 @@ |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #745 describes a bug in the sandboxing code that |
| + * allows one to use a symlink to edit the permissions on a file or |
| + * directory outside of the sandbox. |
| + */ |
| + |
| +DEFINE_TEST(test_write_disk_secure745) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + /* The target dir: The one we're going to try to change permission on */ |
| + assertMakeDir("target", 0700); |
| + |
| + /* The sandbox dir we're going to run inside of. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create a symlink pointing to the target directory */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "sym"); |
| + archive_entry_set_mode(ae, AE_IFLNK | 0777); |
| + archive_entry_copy_symlink(ae, "../target"); |
| + assert(0 == archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Try to alter the target dir through the symlink; this should fail. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "sym"); |
| + archive_entry_set_mode(ae, S_IFDIR | 0777); |
| + assert(0 == archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Permission of target dir should not have changed. */ |
| + assertFileMode("../target", 0700); |
| + |
| + assert(0 == archive_write_close(a)); |
| + archive_write_free(a); |
| +#endif |
| +} |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #745 describes a bug in the sandboxing code that |
| + * allows one to use a symlink to edit the permissions on a file or |
| + * directory outside of the sandbox. |
| + */ |
| + |
| +DEFINE_TEST(test_write_disk_secure745) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + /* The target dir: The one we're going to try to change permission on */ |
| + assertMakeDir("target", 0700); |
| + |
| + /* The sandbox dir we're going to run inside of. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create a symlink pointing to the target directory */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "sym"); |
| + archive_entry_set_mode(ae, AE_IFLNK | 0777); |
| + archive_entry_copy_symlink(ae, "../target"); |
| + assert(0 == archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Try to alter the target dir through the symlink; this should fail. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "sym"); |
| + archive_entry_set_mode(ae, S_IFDIR | 0777); |
| + assert(0 == archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Permission of target dir should not have changed. */ |
| + assertFileMode("../target", 0700); |
| + |
| + assert(0 == archive_write_close(a)); |
| + archive_write_free(a); |
| +#endif |
| +} |
| |
| 属性改变: contrib/libarchive/libarchive/test/test_write_disk_secure745.c |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| ## -0,0 +1 ## |
| +native |
| \ No newline at end of property |
| Added: svn:keywords |
| ## -0,0 +1 ## |
| +FreeBSD=%H |
| \ No newline at end of property |
| Added: svn:mime-type |
| ## -0,0 +1 ## |
| +text/plain |
| \ No newline at end of property |
| Index: contrib/libarchive/libarchive/test/test_write_disk_secure746.c |
| =================================================================== |
| --- contrib/libarchive/libarchive/test/test_write_disk_secure746.c (不存在的) |
| +++ contrib/libarchive/libarchive/test/test_write_disk_secure746.c (版本 306941) |
| @@ -0,0 +1,258 @@ |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #746 describes a problem in which hardlink targets are |
| + * not adequately checked and can be used to modify entries outside of |
| + * the sandbox. |
| + */ |
| + |
| +/* |
| + * Verify that ARCHIVE_EXTRACT_SECURE_NODOTDOT disallows '..' in hardlink |
| + * targets. |
| + */ |
| +DEFINE_TEST(test_write_disk_secure746a) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* The target directory we're going to try to affect. */ |
| + assertMakeDir("target", 0700); |
| + assertMakeFile("target/foo", 0700, "unmodified"); |
| + |
| + /* The sandbox dir we're going to work within. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NODOTDOT); |
| + |
| + /* Attempt to hardlink to the target directory. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "bar"); |
| + archive_entry_set_mode(ae, AE_IFREG | 0777); |
| + archive_entry_set_size(ae, 8); |
| + archive_entry_copy_hardlink(ae, "../target/foo"); |
| + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); |
| + assertEqualInt(ARCHIVE_FATAL, archive_write_data(a, "modified", 8)); |
| + archive_entry_free(ae); |
| + |
| + /* Verify that target file contents are unchanged. */ |
| + assertTextFileContents("unmodified", "../target/foo"); |
| +#endif |
| +} |
| + |
| +/* |
| + * Verify that ARCHIVE_EXTRACT_SECURE_NOSYMLINK disallows symlinks in hardlink |
| + * targets. |
| + */ |
| +DEFINE_TEST(test_write_disk_secure746b) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* The target directory we're going to try to affect. */ |
| + assertMakeDir("target", 0700); |
| + assertMakeFile("target/foo", 0700, "unmodified"); |
| + |
| + /* The sandbox dir we're going to work within. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + /* Create a symlink to the target directory. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "symlink"); |
| + archive_entry_set_mode(ae, AE_IFLNK | 0777); |
| + archive_entry_copy_symlink(ae, "../target"); |
| + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Attempt to hardlink to the target directory via the symlink. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "bar"); |
| + archive_entry_set_mode(ae, AE_IFREG | 0777); |
| + archive_entry_set_size(ae, 8); |
| + archive_entry_copy_hardlink(ae, "symlink/foo"); |
| + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); |
| + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_data(a, "modified", 8)); |
| + archive_entry_free(ae); |
| + |
| + /* Verify that target file contents are unchanged. */ |
| + assertTextFileContents("unmodified", "../target/foo"); |
| + |
| + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); |
| + archive_write_free(a); |
| +#endif |
| +} |
| +/*- |
| + * Copyright (c) 2003-2007,2016 Tim Kientzle |
| + * All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| +#include "test.h" |
| +__FBSDID("$FreeBSD$"); |
| + |
| +#define UMASK 022 |
| + |
| +/* |
| + * Github Issue #746 describes a problem in which hardlink targets are |
| + * not adequately checked and can be used to modify entries outside of |
| + * the sandbox. |
| + */ |
| + |
| +/* |
| + * Verify that ARCHIVE_EXTRACT_SECURE_NODOTDOT disallows '..' in hardlink |
| + * targets. |
| + */ |
| +DEFINE_TEST(test_write_disk_secure746a) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* The target directory we're going to try to affect. */ |
| + assertMakeDir("target", 0700); |
| + assertMakeFile("target/foo", 0700, "unmodified"); |
| + |
| + /* The sandbox dir we're going to work within. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NODOTDOT); |
| + |
| + /* Attempt to hardlink to the target directory. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "bar"); |
| + archive_entry_set_mode(ae, AE_IFREG | 0777); |
| + archive_entry_set_size(ae, 8); |
| + archive_entry_copy_hardlink(ae, "../target/foo"); |
| + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); |
| + assertEqualInt(ARCHIVE_FATAL, archive_write_data(a, "modified", 8)); |
| + archive_entry_free(ae); |
| + |
| + /* Verify that target file contents are unchanged. */ |
| + assertTextFileContents("unmodified", "../target/foo"); |
| +#endif |
| +} |
| + |
| +/* |
| + * Verify that ARCHIVE_EXTRACT_SECURE_NOSYMLINK disallows symlinks in hardlink |
| + * targets. |
| + */ |
| +DEFINE_TEST(test_write_disk_secure746b) |
| +{ |
| +#if defined(_WIN32) && !defined(__CYGWIN__) |
| + skipping("archive_write_disk security checks not supported on Windows"); |
| +#else |
| + struct archive *a; |
| + struct archive_entry *ae; |
| + |
| + /* Start with a known umask. */ |
| + assertUmask(UMASK); |
| + |
| + /* The target directory we're going to try to affect. */ |
| + assertMakeDir("target", 0700); |
| + assertMakeFile("target/foo", 0700, "unmodified"); |
| + |
| + /* The sandbox dir we're going to work within. */ |
| + assertMakeDir("sandbox", 0700); |
| + assertChdir("sandbox"); |
| + |
| + /* Create an archive_write_disk object. */ |
| + assert((a = archive_write_disk_new()) != NULL); |
| + archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); |
| + |
| + /* Create a symlink to the target directory. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "symlink"); |
| + archive_entry_set_mode(ae, AE_IFLNK | 0777); |
| + archive_entry_copy_symlink(ae, "../target"); |
| + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); |
| + archive_entry_free(ae); |
| + |
| + /* Attempt to hardlink to the target directory via the symlink. */ |
| + assert((ae = archive_entry_new()) != NULL); |
| + archive_entry_copy_pathname(ae, "bar"); |
| + archive_entry_set_mode(ae, AE_IFREG | 0777); |
| + archive_entry_set_size(ae, 8); |
| + archive_entry_copy_hardlink(ae, "symlink/foo"); |
| + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); |
| + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_data(a, "modified", 8)); |
| + archive_entry_free(ae); |
| + |
| + /* Verify that target file contents are unchanged. */ |
| + assertTextFileContents("unmodified", "../target/foo"); |
| + |
| + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); |
| + archive_write_free(a); |
| +#endif |
| +} |
| |
| 属性改变: contrib/libarchive/libarchive/test/test_write_disk_secure746.c |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| ## -0,0 +1 ## |
| +native |
| \ No newline at end of property |
| Added: svn:keywords |
| ## -0,0 +1 ## |
| +FreeBSD=%H |
| \ No newline at end of property |
| Added: svn:mime-type |
| ## -0,0 +1 ## |
| +text/plain |
| \ No newline at end of property |
| Index: lib/libarchive/tests/Makefile |
| =================================================================== |
| --- lib/libarchive/tests/Makefile (版本 306336) |
| +++ lib/libarchive/tests/Makefile (版本 306941) |
| @@ -177,6 +177,9 @@ |
| test_write_disk_no_hfs_compression.c \ |
| test_write_disk_perms.c \ |
| test_write_disk_secure.c \ |
| + test_write_disk_secure744.c \ |
| + test_write_disk_secure745.c \ |
| + test_write_disk_secure746.c \ |
| test_write_disk_sparse.c \ |
| test_write_disk_symlink.c \ |
| test_write_disk_times.c \ |
| Index: sys/conf/newvers.sh |
| =================================================================== |
| --- sys/conf/newvers.sh (版本 306336) |
| +++ sys/conf/newvers.sh (版本 306941) |
| @@ -32,7 +32,7 @@ |
| |
| TYPE="FreeBSD" |
| REVISION="10.3" |
| -BRANCH="RELEASE-p9" |
| +BRANCH="RELEASE-p10" |
| if [ "X${BRANCH_OVERRIDE}" != "X" ]; then |
| BRANCH=${BRANCH_OVERRIDE} |
| fi |
| Index: usr.bin/bsdiff/bspatch/bspatch.c |
| =================================================================== |
| --- usr.bin/bsdiff/bspatch/bspatch.c (版本 306336) |
| +++ usr.bin/bsdiff/bspatch/bspatch.c (版本 306941) |
| @@ -27,50 +27,79 @@ |
| #include <sys/cdefs.h> |
| __FBSDID("$FreeBSD$"); |
| |
| +#if defined(__FreeBSD__) |
| +#include <sys/param.h> |
| +#if __FreeBSD_version >= 1001511 |
| +#include <sys/capsicum.h> |
| +#define HAVE_CAPSICUM |
| +#endif |
| +#endif |
| + |
| #include <bzlib.h> |
| +#include <err.h> |
| +#include <errno.h> |
| +#include <fcntl.h> |
| +#include <libgen.h> |
| +#include <limits.h> |
| +#include <stdint.h> |
| +#include <stdio.h> |
| #include <stdlib.h> |
| -#include <stdio.h> |
| #include <string.h> |
| -#include <err.h> |
| #include <unistd.h> |
| -#include <fcntl.h> |
| |
| #ifndef O_BINARY |
| #define O_BINARY 0 |
| #endif |
| +#define HEADER_SIZE 32 |
| |
| +static char *newfile; |
| +static int dirfd = -1; |
| + |
| +static void |
| +exit_cleanup(void) |
| +{ |
| + |
| + if (dirfd != -1 && newfile != NULL) |
| + if (unlinkat(dirfd, newfile, 0)) |
| + warn("unlinkat"); |
| +} |
| + |
| static off_t offtin(u_char *buf) |
| { |
| off_t y; |
| |
| - y=buf[7]&0x7F; |
| - y=y*256;y+=buf[6]; |
| - y=y*256;y+=buf[5]; |
| - y=y*256;y+=buf[4]; |
| - y=y*256;y+=buf[3]; |
| - y=y*256;y+=buf[2]; |
| - y=y*256;y+=buf[1]; |
| - y=y*256;y+=buf[0]; |
| + y = buf[7] & 0x7F; |
| + y = y * 256; y += buf[6]; |
| + y = y * 256; y += buf[5]; |
| + y = y * 256; y += buf[4]; |
| + y = y * 256; y += buf[3]; |
| + y = y * 256; y += buf[2]; |
| + y = y * 256; y += buf[1]; |
| + y = y * 256; y += buf[0]; |
| |
| - if(buf[7]&0x80) y=-y; |
| + if (buf[7] & 0x80) |
| + y = -y; |
| |
| - return y; |
| + return (y); |
| } |
| |
| -int main(int argc,char * argv[]) |
| +int main(int argc, char *argv[]) |
| { |
| - FILE * f, * cpf, * dpf, * epf; |
| - BZFILE * cpfbz2, * dpfbz2, * epfbz2; |
| + FILE *f, *cpf, *dpf, *epf; |
| + BZFILE *cpfbz2, *dpfbz2, *epfbz2; |
| + char *directory, *namebuf; |
| int cbz2err, dbz2err, ebz2err; |
| - int fd; |
| - ssize_t oldsize,newsize; |
| - ssize_t bzctrllen,bzdatalen; |
| - u_char header[32],buf[8]; |
| + int newfd, oldfd; |
| + off_t oldsize, newsize; |
| + off_t bzctrllen, bzdatalen; |
| + u_char header[HEADER_SIZE], buf[8]; |
| u_char *old, *new; |
| - off_t oldpos,newpos; |
| + off_t oldpos, newpos; |
| off_t ctrl[3]; |
| - off_t lenread; |
| - off_t i; |
| + off_t i, lenread, offset; |
| +#ifdef HAVE_CAPSICUM |
| + cap_rights_t rights_dir, rights_ro, rights_wr; |
| +#endif |
| |
| if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); |
| |
| @@ -77,7 +106,55 @@ |
| /* Open patch file */ |
| if ((f = fopen(argv[3], "rb")) == NULL) |
| err(1, "fopen(%s)", argv[3]); |
| + /* Open patch file for control block */ |
| + if ((cpf = fopen(argv[3], "rb")) == NULL) |
| + err(1, "fopen(%s)", argv[3]); |
| + /* open patch file for diff block */ |
| + if ((dpf = fopen(argv[3], "rb")) == NULL) |
| + err(1, "fopen(%s)", argv[3]); |
| + /* open patch file for extra block */ |
| + if ((epf = fopen(argv[3], "rb")) == NULL) |
| + err(1, "fopen(%s)", argv[3]); |
| + /* open oldfile */ |
| + if ((oldfd = open(argv[1], O_RDONLY | O_BINARY, 0)) < 0) |
| + err(1, "open(%s)", argv[1]); |
| + /* open directory where we'll write newfile */ |
| + if ((namebuf = strdup(argv[2])) == NULL || |
| + (directory = dirname(namebuf)) == NULL || |
| + (dirfd = open(directory, O_DIRECTORY)) < 0) |
| + err(1, "open %s", argv[2]); |
| + free(namebuf); |
| + if ((newfile = basename(argv[2])) == NULL) |
| + err(1, "basename"); |
| + /* open newfile */ |
| + if ((newfd = openat(dirfd, newfile, |
| + O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) |
| + err(1, "open(%s)", argv[2]); |
| + atexit(exit_cleanup); |
| |
| +#ifdef HAVE_CAPSICUM |
| + if (cap_enter() < 0) { |
| + /* Failed to sandbox, fatal if CAPABILITY_MODE enabled */ |
| + if (errno != ENOSYS) |
| + err(1, "failed to enter security sandbox"); |
| + } else { |
| + /* Capsicum Available */ |
| + cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK); |
| + cap_rights_init(&rights_wr, CAP_WRITE); |
| + cap_rights_init(&rights_dir, CAP_UNLINKAT); |
| + |
| + if (cap_rights_limit(fileno(f), &rights_ro) < 0 || |
| + cap_rights_limit(fileno(cpf), &rights_ro) < 0 || |
| + cap_rights_limit(fileno(dpf), &rights_ro) < 0 || |
| + cap_rights_limit(fileno(epf), &rights_ro) < 0 || |
| + cap_rights_limit(oldfd, &rights_ro) < 0 || |
| + cap_rights_limit(newfd, &rights_wr) < 0 || |
| + cap_rights_limit(dirfd, &rights_dir) < 0) |
| + err(1, "cap_rights_limit() failed, could not restrict" |
| + " capabilities"); |
| + } |
| +#endif |
| + |
| /* |
| File format: |
| 0 8 "BSDIFF40" |
| @@ -93,99 +170,99 @@ |
| */ |
| |
| /* Read header */ |
| - if (fread(header, 1, 32, f) < 32) { |
| + if (fread(header, 1, HEADER_SIZE, f) < HEADER_SIZE) { |
| if (feof(f)) |
| - errx(1, "Corrupt patch\n"); |
| + errx(1, "Corrupt patch"); |
| err(1, "fread(%s)", argv[3]); |
| } |
| |
| /* Check for appropriate magic */ |
| if (memcmp(header, "BSDIFF40", 8) != 0) |
| - errx(1, "Corrupt patch\n"); |
| + errx(1, "Corrupt patch"); |
| |
| /* Read lengths from header */ |
| - bzctrllen=offtin(header+8); |
| - bzdatalen=offtin(header+16); |
| - newsize=offtin(header+24); |
| - if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) |
| - errx(1,"Corrupt patch\n"); |
| + bzctrllen = offtin(header + 8); |
| + bzdatalen = offtin(header + 16); |
| + newsize = offtin(header + 24); |
| + if (bzctrllen < 0 || bzctrllen > OFF_MAX - HEADER_SIZE || |
| + bzdatalen < 0 || bzctrllen + HEADER_SIZE > OFF_MAX - bzdatalen || |
| + newsize < 0 || newsize > SSIZE_MAX) |
| + errx(1, "Corrupt patch"); |
| |
| /* Close patch file and re-open it via libbzip2 at the right places */ |
| if (fclose(f)) |
| err(1, "fclose(%s)", argv[3]); |
| - if ((cpf = fopen(argv[3], "rb")) == NULL) |
| - err(1, "fopen(%s)", argv[3]); |
| - if (fseeko(cpf, 32, SEEK_SET)) |
| - err(1, "fseeko(%s, %lld)", argv[3], |
| - (long long)32); |
| + offset = HEADER_SIZE; |
| + if (fseeko(cpf, offset, SEEK_SET)) |
| + err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); |
| if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) |
| errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); |
| - if ((dpf = fopen(argv[3], "rb")) == NULL) |
| - err(1, "fopen(%s)", argv[3]); |
| - if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) |
| - err(1, "fseeko(%s, %lld)", argv[3], |
| - (long long)(32 + bzctrllen)); |
| + offset += bzctrllen; |
| + if (fseeko(dpf, offset, SEEK_SET)) |
| + err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); |
| if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) |
| errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); |
| - if ((epf = fopen(argv[3], "rb")) == NULL) |
| - err(1, "fopen(%s)", argv[3]); |
| - if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) |
| - err(1, "fseeko(%s, %lld)", argv[3], |
| - (long long)(32 + bzctrllen + bzdatalen)); |
| + offset += bzdatalen; |
| + if (fseeko(epf, offset, SEEK_SET)) |
| + err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); |
| if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) |
| errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); |
| |
| - if(((fd=open(argv[1],O_RDONLY|O_BINARY,0))<0) || |
| - ((oldsize=lseek(fd,0,SEEK_END))==-1) || |
| - ((old=malloc(oldsize+1))==NULL) || |
| - (lseek(fd,0,SEEK_SET)!=0) || |
| - (read(fd,old,oldsize)!=oldsize) || |
| - (close(fd)==-1)) err(1,"%s",argv[1]); |
| - if((new=malloc(newsize+1))==NULL) err(1,NULL); |
| + if ((oldsize = lseek(oldfd, 0, SEEK_END)) == -1 || |
| + oldsize > SSIZE_MAX || |
| + (old = malloc(oldsize)) == NULL || |
| + lseek(oldfd, 0, SEEK_SET) != 0 || |
| + read(oldfd, old, oldsize) != oldsize || |
| + close(oldfd) == -1) |
| + err(1, "%s", argv[1]); |
| + if ((new = malloc(newsize)) == NULL) |
| + err(1, NULL); |
| |
| - oldpos=0;newpos=0; |
| - while(newpos<newsize) { |
| + oldpos = 0; |
| + newpos = 0; |
| + while (newpos < newsize) { |
| /* Read control data */ |
| - for(i=0;i<=2;i++) { |
| + for (i = 0; i <= 2; i++) { |
| lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); |
| if ((lenread < 8) || ((cbz2err != BZ_OK) && |
| (cbz2err != BZ_STREAM_END))) |
| - errx(1, "Corrupt patch\n"); |
| - ctrl[i]=offtin(buf); |
| + errx(1, "Corrupt patch"); |
| + ctrl[i] = offtin(buf); |
| }; |
| |
| /* Sanity-check */ |
| - if ((ctrl[0] < 0) || (ctrl[1] < 0)) |
| - errx(1,"Corrupt patch\n"); |
| + if (ctrl[0] < 0 || ctrl[0] > INT_MAX || |
| + ctrl[1] < 0 || ctrl[1] > INT_MAX) |
| + errx(1, "Corrupt patch"); |
| |
| /* Sanity-check */ |
| - if(newpos+ctrl[0]>newsize) |
| - errx(1,"Corrupt patch\n"); |
| + if (newpos + ctrl[0] > newsize) |
| + errx(1, "Corrupt patch"); |
| |
| /* Read diff string */ |
| lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]); |
| if ((lenread < ctrl[0]) || |
| ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) |
| - errx(1, "Corrupt patch\n"); |
| + errx(1, "Corrupt patch"); |
| |
| /* Add old data to diff string */ |
| - for(i=0;i<ctrl[0];i++) |
| - if((oldpos+i>=0) && (oldpos+i<oldsize)) |
| - new[newpos+i]+=old[oldpos+i]; |
| + for (i = 0; i < ctrl[0]; i++) |
| + if ((oldpos + i >= 0) && (oldpos + i < oldsize)) |
| + new[newpos + i] += old[oldpos + i]; |
| |
| /* Adjust pointers */ |
| - newpos+=ctrl[0]; |
| - oldpos+=ctrl[0]; |
| + newpos += ctrl[0]; |
| + oldpos += ctrl[0]; |
| |
| /* Sanity-check */ |
| - if(newpos+ctrl[1]>newsize) |
| - errx(1,"Corrupt patch\n"); |
| + if (newpos + ctrl[1] > newsize) |
| + errx(1, "Corrupt patch"); |
| |
| /* Read extra string */ |
| lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]); |
| if ((lenread < ctrl[1]) || |
| ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) |
| - errx(1, "Corrupt patch\n"); |
| + errx(1, "Corrupt patch"); |
| |
| /* Adjust pointers */ |
| newpos+=ctrl[1]; |
| @@ -200,12 +277,13 @@ |
| err(1, "fclose(%s)", argv[3]); |
| |
| /* Write the new file */ |
| - if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666))<0) || |
| - (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) |
| - err(1,"%s",argv[2]); |
| + if (write(newfd, new, newsize) != newsize || close(newfd) == -1) |
| + err(1, "%s", argv[2]); |
| + /* Disable atexit cleanup */ |
| + newfile = NULL; |
| |
| free(new); |
| free(old); |
| |
| - return 0; |
| + return (0); |
| } |
| Index: usr.sbin/portsnap/portsnap/portsnap.sh |
| =================================================================== |
| --- usr.sbin/portsnap/portsnap/portsnap.sh (版本 306336) |
| +++ usr.sbin/portsnap/portsnap/portsnap.sh (版本 306941) |
| @@ -646,7 +646,7 @@ |
| # Verify a list of files |
| fetch_snapshot_verify() { |
| while read F; do |
| - if [ "`gunzip -c snap/${F} | ${SHA256} -q`" != ${F} ]; then |
| + if [ "`gunzip -c < snap/${F}.gz | ${SHA256} -q`" != ${F} ]; then |
| echo "snapshot corrupt." |
| return 1 |
| fi |
| @@ -681,11 +681,18 @@ |
| cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1 |
| # Extract the index |
| rm -f INDEX.new |
| - gunzip -c snap/`look INDEX tINDEX.new | |
| + gunzip -c < snap/`look INDEX tINDEX.new | |
| cut -f 2 -d '|'`.gz > INDEX.new |
| fetch_index_sanity || return 1 |
| # Verify the snapshot contents |
| cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1 |
| + cut -f 2 -d '|' tINDEX.new INDEX.new | sort -u > files.expected |
| + find snap -mindepth 1 | sed -E 's^snap/(.*)\.gz^\1^' | sort > files.snap |
| + if ! cmp -s files.expected files.snap; then |
| + echo "unexpected files in snapshot." |
| + return 1 |
| + fi |
| + rm files.expected files.snap |
| echo "done." |
| |
| # Move files into their proper locations |
| @@ -777,7 +784,7 @@ |
| |
| # Extract the index |
| echo -n "Extracting index... " 1>${QUIETREDIR} |
| - gunzip -c files/`look INDEX tINDEX.new | |
| + gunzip -c < files/`look INDEX tINDEX.new | |
| cut -f 2 -d '|'`.gz > INDEX.new |
| fetch_index_sanity || return 1 |
| |
| @@ -897,7 +904,7 @@ |
| echo -n "$1 not provided by portsnap server; " |
| echo "$2 not being generated." |
| else |
| - gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX | |
| + gunzip -c < "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX | |
| cut -f 2 -d '|'`.gz" | |
| cat - ${LOCALDESC} | |
| ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2 |