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
