commit 3c1ec782ab68abf6084ad937bc2a3e4a9c8fe049 from: Stefan Sperling date: Thu Jul 23 14:22:38 2020 UTC put checks for bad symlink target paths into a dedicated function commit - ea7786be381853efad88beae518c19a391d13791 commit + 3c1ec782ab68abf6084ad937bc2a3e4a9c8fe049 blob - d94da1d659df18bb44c73bf7b04d0cfb808d6bd4 blob + f912c374a4ff37850113d396d182a47f7d72ef55 --- lib/worktree.c +++ lib/worktree.c @@ -1251,6 +1251,67 @@ replace_existing_symlink(const char *ondisk_path, cons if (fd != -1 && close(fd) == -1 && err == NULL) err = got_error_from_errno2("close", ondisk_path); return err; +} + +static const struct got_error * +is_bad_symlink_target(int *is_bad_symlink, const char *target_path, + size_t target_len, const char *ondisk_path, const char *wtroot_path) +{ + const struct got_error *err = NULL; + char canonpath[PATH_MAX]; + char *path_got = NULL; + + *is_bad_symlink = 0; + + if (target_len >= sizeof(canonpath)) { + *is_bad_symlink = 1; + return NULL; + } + + /* + * We do not use realpath(3) to resolve the symlink's target + * path because we don't want to resolve symlinks recursively. + * Instead we make the path absolute and then canonicalize it. + * Relative symlink target lookup should begin at the directory + * in which the blob object is being installed. + */ + if (!got_path_is_absolute(target_path)) { + char *abspath; + char *parent = dirname(ondisk_path); + if (parent == NULL) + return got_error_from_errno2("dirname", ondisk_path); + if (asprintf(&abspath, "%s/%s", parent, target_path) == -1) + return got_error_from_errno("asprintf"); + if (strlen(abspath) >= sizeof(canonpath)) { + err = got_error_path(abspath, GOT_ERR_BAD_PATH); + free(abspath); + return err; + } + err = got_canonpath(abspath, canonpath, sizeof(canonpath)); + free(abspath); + if (err) + return err; + } else { + err = got_canonpath(target_path, canonpath, sizeof(canonpath)); + if (err) + return err; + } + + /* Only allow symlinks pointing at paths within the work tree. */ + if (!got_path_is_child(canonpath, wtroot_path, strlen(wtroot_path))) { + *is_bad_symlink = 1; + return NULL; + } + + /* Do not allow symlinks pointing into the .got directory. */ + if (asprintf(&path_got, "%s/%s", wtroot_path, + GOT_WORKTREE_GOT_DIR) == -1) + return got_error_from_errno("asprintf"); + if (got_path_is_child(canonpath, path_got, strlen(path_got))) + *is_bad_symlink = 1; + + free(path_got); + return NULL; } static const struct got_error * @@ -1262,7 +1323,6 @@ install_symlink(int *is_bad_symlink, struct got_worktr { const struct got_error *err = NULL; char target_path[PATH_MAX]; - char canonpath[PATH_MAX]; size_t len, target_len = 0; char *path_got = NULL; const uint8_t *buf = got_object_blob_get_read_buf(blob); @@ -1298,54 +1358,12 @@ install_symlink(int *is_bad_symlink, struct got_worktr } while (len != 0); target_path[target_len] = '\0'; - /* - * We do not use realpath(3) to resolve the symlink's target path - * because we don't want to resolve symlinks recursively. Instead - * we make the path absolute and then canonicalize it. - * Relative symlink target lookup should begin at the directory - * in which the blob object is being installed. - */ - if (!got_path_is_absolute(target_path)) { - char *abspath; - char *parent = dirname(ondisk_path); - if (parent == NULL) { - err = got_error_from_errno2("dirname", ondisk_path); - goto done; - } - if (asprintf(&abspath, "%s/%s", parent, target_path) == -1) { - err = got_error_from_errno("asprintf"); - goto done; - } - err = got_canonpath(abspath, canonpath, sizeof(canonpath)); - free(abspath); - if (err) - goto done; - } else { - err = got_canonpath(target_path, canonpath, sizeof(canonpath)); - if (err) - goto done; - } + err = is_bad_symlink_target(is_bad_symlink, target_path, target_len, + ondisk_path, worktree->root_path); + if (err) + return err; - /* Only allow symlinks pointing at paths within the work tree. */ - if (!got_path_is_child(canonpath, worktree->root_path, - strlen(worktree->root_path))) { - /* install as a regular file */ - *is_bad_symlink = 1; - got_object_blob_rewind(blob); - err = install_blob(worktree, ondisk_path, path, - GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob, - restoring_missing_file, reverting_versioned_file, 1, - path_is_unversioned, repo, progress_cb, progress_arg); - goto done; - } - - /* Do not allow symlinks pointing into the .got directory. */ - if (asprintf(&path_got, "%s/%s", worktree->root_path, - GOT_WORKTREE_GOT_DIR) == -1) { - err = got_error_from_errno("asprintf"); - goto done; - } - if (got_path_is_child(canonpath, path_got, strlen(path_got))) { + if (*is_bad_symlink) { /* install as a regular file */ *is_bad_symlink = 1; got_object_blob_rewind(blob);