Re: [PATCH v2 7/8] verify_path(): disallow symlinks in .gitattributes and .gitignore

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jeff King <peff@xxxxxxxx> writes:

> diff --git a/environment.c b/environment.c
> index bb518c61cd..7c233e0e0e 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -73,6 +73,7 @@ int merge_log_config = -1;
>  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
>  unsigned long pack_size_limit_cfg;
>  enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
> +int allow_external_symlinks = 1;

OK, so by default it is not blocked...

> +static int symlink_leaves_repo(const char *target, const char *linkpath)
> +{
> +	/*
> +	 * Absolute paths are always considered to leave the repository (even
> +	 * if they happen to point to the working tree path).
> +	 */
> +	if (is_absolute_path(target))
> +		return 1;

Very sensible.

> +	/*
> +	 * Allow relative paths that start with a sequence of "../",
> +	 * as long as they do not break out of the symlink's root.
> +	 * This loop will detect break-out cases and return; otherwise, at the
> +	 * end of the loop "target" will point to the first non-".." component.
> +	 *
> +	 * We count the depth of linkpath by eating up directory components left
> +	 * to right. Technically the symlink would resolve right-to-left, but
> +	 * we don't care about the actual values, only the number.
> +	 */
> +	while (target[0] == '.') {
> +		if (!target[1]) {
> +			/* trailing "." -- ignore */
> +			target++;
> +		} else if (is_dir_sep(target[1])) {
> +			/* "./" -- ignore */
> +			target += 2;
> +		} else if (target[1] == '.' &&
> +			   (!target[2] || is_dir_sep(target[2]))) {
> +			/* ".." or "../" -- drop one from linkpath depth */
> +			while (!is_dir_sep(*linkpath)) {
> +				/* end-of-string; target exceeded our depth */
> +				if (!*linkpath)
> +					return 1;
> +				linkpath++;
> +			}
> +			/* skip final "/" */
> +			linkpath++;
> +
> +			/* skip past ".." */
> +			target += 2;
> +			/* and "/" if present */
> +			if (is_dir_sep(*target))
> +				target++;
> +		}
> +	}
> +
> +	/*
> +	 * Now we have a path in "target" that only go down into the tree.
> +	 * Disallow any interior "../", like "foo/../bar". These might be
> +	 * OK, but we cannot know unless we know whether "foo" is itself a
> +	 * symlink. So err on the side of caution.
> +	 */
> +	while (*target) {
> +		const char *v;
> +		if (skip_prefix(target, "..", &v) && (!*v || is_dir_sep(*v)))
> +			return 1;
> +		target++;
> +	}
> +
> +	return 0;
> +}
> +
> +int safe_symlink(const char *target, const char *linkpath)
> +{
> +	if (!allow_external_symlinks &&
> +	    symlink_leaves_repo(target, linkpath)) {
> +		errno = EPERM;
> +		return -1;
> +	}
> +
> +	return symlink(target, linkpath);
> +}

OK.  This is only about blocking creation of new symbolic links that
goes outside the working tree.  It obviously is a good thing to do.

We have some "symlink safety" in various parts of the system [*1*],
and I wonder if we can somehow consolidate the support to a more
central place.

Thanks.


[Footnote]

*1* For example, apply tries to be careful not to take the "path"
    recorded in the incoming patch blindly, and instead checks if
    any path component in it is a symbolic link before touching.
    Similarly, callers of has_symlink_leading_path() all try to be
    careful when the "path" they want to use to access a filesystem
    entity has a symbolic link in the middle on the filesystem.



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux