Because check_symlinks is handled separately from the deep-directory
support, very long pathnames cause problems. Previously, the code
ignored most failures to lstat() a path component. In particular,
this led to check_symlinks always passing for very long paths, which
in turn provides a way to evade the symlink checks in the sandboxing
code.
We now fail on unrecognized lstat() failures, which plugs this
hole at the cost of disabling deep directory support when the
user requests sandboxing.
TODO: This probably cannot be completely fixed without
entirely reimplementing the deep directory support to
integrate the symlink checks. I want to reimplement the
deep directory hanlding someday anyway; openat() and
related system calls now provide a much cleaner way to
handle deep directories than the chdir approach used by this
code.
r = lstat(a->name, &st);
if (r != 0) {
/* We've hit a dir that doesn't exist; stop now. */
- if (errno == ENOENT)
+ if (errno == ENOENT) {
break;
+ } else {
+ /* 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. */
+ return (ARCHIVE_FAILED);
+ }
} else if (S_ISLNK(st.st_mode)) {
if (c == '\0') {
/*