Re: [RFC v2] libmount: accept X-mount.{owner,group,mode}=

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

 



On Thu, Apr 07, 2022 at 08:39:13PM +0200, наб wrote:
> Thanks for the pointers!

Some other notes (sorry) ;)

> diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
> index f09e68860..13209d861 100644
> --- a/libmount/src/context_mount.c
> +++ b/libmount/src/context_mount.c
> @@ -1020,6 +1020,132 @@ static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
>  	return rc;
>  }
>  
> +/* Extracted from mnt_optstr_get_uid() */
> +static int parse_ugid(const char *value, size_t valsz, unsigned *uid)

Can you move it to libmount/src/utils.c as mnt_parse_uid() and mnt_parse_gid() ?

And use mnt_parse_uid() in libmount/src/optstr.c:mnt_optstr_get_uid()?

It would be nice to have only one place (code) where we convert string
to uid (or gid).

Later we can re-use these functions for ID-mapping patches too.

In this case it would be nice to be strict and use uid_t and gid_t in
the functions.

We already had CVE for this code, so I'd like to be paranoid here :-)

> +{
> +	char buf[sizeof(stringify_value(UINT64_MAX))];
> +	int rc;
> +	uint64_t num;
> +
> +	assert(value);
> +	assert(uid);
> +
> +	if (valsz > sizeof(buf) - 1) {
> +		rc = -ERANGE;
> +		goto fail;
> +	}
> +	mem2strcpy(buf, value, valsz, sizeof(buf));
> +
> +	rc = ul_strtou64(buf, &num, 10);
> +	if (rc != 0) {
> +		errno = ENOENT;
> +		goto fail;
> +	}
> +	if (num > ULONG_MAX || (unsigned) num != num) {

 || (uid_t) num != num

for mnt_parse_uid() and

 || (gid_t) num != num

for mnt_parse_gid()

> +		errno = ERANGE;
> +		goto fail;
> +	}
> +	*uid = (unsigned) num;
> +
> +	return 0;
> +fail:
> +	DBG(UTILS, ul_debug("failed to convert '%.*s' to number [errno=%d]", (int) valsz, value, errno));
> +	return -1;
> +}
> +
> +static int parse_mode(const char *value, size_t valsz, mode_t *uid)

Let's also introduce libmount/src/optstr.c:mnt_parse_mode() to be
consistent.

...

> +/*
> + * Process X-mount.owner=, X-mount.group=, X-mount.mode=.
> + */
> +static int parse_ownership_mode(struct libmnt_context *cxt)
> +{
> +	int rc;
> +
> +	const char *o = mnt_fs_get_user_options(cxt->fs);
> +	if (!o)
> +		return 0;
> +
> +	char *owner;
> +	size_t owner_len;

Please, please, define variables at the beginning of the code block.

> +	if ((rc = mnt_optstr_get_option(o, "X-mount.owner", &owner, &owner_len)) < 0) {
> +		return -MNT_ERR_MOUNTOPT;}
> +	if (!rc) {
> +		if (!owner_len)
> +			return errno = EINVAL, -MNT_ERR_MOUNTOPT;
> +
> +		char *owner_tofree = NULL;

here too

> +		rc = mnt_get_uid(owner[owner_len] ? (owner_tofree = strndup(owner, owner_len)) : owner, &cxt->tgt_owner);
> +		free(owner_tofree);
> +		if (cxt->tgt_owner == (uid_t) -1 && isdigit(*owner))
> +			rc = parse_ugid(owner, owner_len, &cxt->tgt_owner);
> +		if (rc)
> +			return -MNT_ERR_MOUNTOPT;
> +	}
> +
> +	char *group;
> +	size_t group_len;

here too

> +	if ((rc = mnt_optstr_get_option(o, "X-mount.group", &group, &group_len)) < 0)
> +		return -MNT_ERR_MOUNTOPT;
> +	if (!rc) {
> +		if (!group_len)
> +			return errno = EINVAL, -MNT_ERR_MOUNTOPT;
> +
> +		char *group_tofree = NULL;

here too

> +		rc = mnt_get_uid(group[group_len] ? (group_tofree = strndup(group, group_len)) : group, &cxt->tgt_group);
> +		free(group_tofree);
> +		if (cxt->tgt_group == (uid_t) -1 && isdigit(*group))
> +			rc = parse_ugid(group, group_len, &cxt->tgt_group);
> +		if (rc)
> +			return errno = ENOENT, -MNT_ERR_MOUNTOPT;
> +	}
> +
> +	char *mode;
> +	size_t mode_len;

here too

> +	if ((rc = mnt_optstr_get_option(o, "X-mount.mode", &mode, &mode_len)) < 0)
> +		return -MNT_ERR_MOUNTOPT;
> +	if (!rc) {
> +		if (!mode_len)
> +			return errno = EINVAL, -MNT_ERR_MOUNTOPT;
> +		if ((rc = parse_mode(mode, mode_len, &cxt->tgt_mode)))
> +			return -MNT_ERR_MOUNTOPT;
> +	}
> +
> +	DBG(CXT, ul_debugobj(cxt, "ownership %d:%d, mode %04o", cxt->tgt_owner, cxt->tgt_group, cxt->tgt_mode));
> +
> +	return 0;
> +}
> +
>  /**
>   * mnt_context_prepare_mount:
>   * @cxt: context
> @@ -1064,6 +1190,8 @@ int mnt_context_prepare_mount(struct libmnt_context *cxt)
>  		rc = mnt_context_guess_fstype(cxt);
>  	if (!rc)
>  		rc = mnt_context_prepare_target(cxt);
> +	if (!rc)
> +		rc = parse_ownership_mode(cxt);
>  	if (!rc)
>  		rc = mnt_context_prepare_helper(cxt, "mount", NULL);
>  
> @@ -1089,6 +1217,21 @@ end:
>  	return rc;
>  }
>  
> +static int set_ownership_mode(struct libmnt_context *cxt)
> +{
> +	const char *target = mnt_fs_get_target(cxt->fs);
> +
> +	if (cxt->tgt_owner != (uid_t) -1 || cxt->tgt_group != (uid_t) -1)

Please, add here some debug message,

> +		if (lchown(target, cxt->tgt_owner, cxt->tgt_group) == -1)
> +			return -MNT_ERR_CHOWN;
> +
> +	if (cxt->tgt_mode != (mode_t) -1)

and here too.

> +		if (chmod(target, cxt->tgt_mode) == -1)
> +			return -MNT_ERR_CHMOD;
> +
> +	return 0;
> +}
> +
>  /**
>   * mnt_context_do_mount
>   * @cxt: context
> @@ -1191,6 +1334,9 @@ int mnt_context_do_mount(struct libmnt_context *cxt)
>  	if (mnt_context_is_veritydev(cxt))
>  		mnt_context_deferred_delete_veritydev(cxt);
>  
> +	if (!res)
> +		res = set_ownership_mode(cxt);
> +
>  	if (!mnt_context_switch_ns(cxt, ns_old))
>  		return -MNT_ERR_NAMESPACE;
>  
> @@ -1841,7 +1987,18 @@ int mnt_context_get_mount_excode(
>  			if (buf)
>  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to switch namespace back"));
>  			return MNT_EX_SYSERR;
> +		}
>  
> +		if (rc == -MNT_ERR_CHOWN) {
> +			if (buf)
> +				snprintf(buf, bufsz, _("filesystem was mounted, but failed to change ownership to %u:%u: %m"), cxt->tgt_owner, cxt->tgt_group);
> +			return MNT_EX_SYSERR;
> +		}
> +
> +		if (rc == -MNT_ERR_CHMOD) {
> +			if (buf)
> +				snprintf(buf, bufsz, _("filesystem was mounted, but failed to change mode to %04o: %m"), cxt->tgt_mode);
> +			return MNT_EX_SYSERR;
>  		}

Yes, this is probably the best solution.

Thanks for your work. (Don't forget Signed-off-by:)

  Karel


-- 
 Karel Zak  <kzak@xxxxxxxxxx>
 http://karelzak.blogspot.com




[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux