File util-linux-libmount-check-fuse-umount-CVE-2021-3995.patch of Package util-linux.32042

Includes backport of ul_strtou64().

From f3db9bd609494099f0c1b95231c5dfe383346929 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Wed, 24 Nov 2021 13:53:25 +0100
Subject: [PATCH 1/2] libmount: fix UID check for FUSE umount [CVE-2021-3995]

Improper UID check allows an unprivileged user to unmount FUSE
filesystems of users with similar UID.

Signed-off-by: Karel Zak <kzak@redhat.com>
---
 include/strutils.h            |  2 +-
 libmount/src/context_umount.c | 14 +++---------
 libmount/src/mountP.h         |  1 +
 libmount/src/optstr.c         | 42 +++++++++++++++++++++++++++++++++++
 4 files changed, 47 insertions(+), 12 deletions(-)

Index: util-linux-2.36.2/include/strutils.h
===================================================================
--- util-linux-2.36.2.orig/include/strutils.h
+++ util-linux-2.36.2/include/strutils.h
@@ -88,8 +88,8 @@ static inline char *mem2strcpy(char *des
 	if (n + 1 > nmax)
 		n = nmax - 1;
 
+	memset(dest, '\0', nmax);
 	memcpy(dest, src, n);
-	dest[nmax-1] = '\0';
 	return dest;
 }
 
Index: util-linux-2.36.2/libmount/src/context_umount.c
===================================================================
--- util-linux-2.36.2.orig/libmount/src/context_umount.c
+++ util-linux-2.36.2/libmount/src/context_umount.c
@@ -447,10 +447,7 @@ static int is_fuse_usermount(struct libm
 	struct libmnt_ns *ns_old;
 	const char *type = mnt_fs_get_fstype(cxt->fs);
 	const char *optstr;
-	char *user_id = NULL;
-	size_t sz;
-	uid_t uid;
-	char uidstr[sizeof(stringify_value(ULONG_MAX))];
+	uid_t uid, entry_uid;
 
 	*errsv = 0;
 
@@ -467,11 +464,7 @@ static int is_fuse_usermount(struct libm
 	optstr = mnt_fs_get_fs_options(cxt->fs);
 	if (!optstr)
 		return 0;
-
-	if (mnt_optstr_get_option(optstr, "user_id", &user_id, &sz) != 0)
-		return 0;
-
-	if (sz == 0 || user_id == NULL)
+	if (mnt_optstr_get_uid(optstr, "user_id", &entry_uid) != 0)
 		return 0;
 
 	/* get current user */
@@ -488,8 +481,7 @@ static int is_fuse_usermount(struct libm
 		return 0;
 	}
 
-	snprintf(uidstr, sizeof(uidstr), "%lu", (unsigned long) uid);
-	return strncmp(user_id, uidstr, sz) == 0;
+	return uid == entry_uid;
 }
 
 /*
Index: util-linux-2.36.2/libmount/src/mountP.h
===================================================================
--- util-linux-2.36.2.orig/libmount/src/mountP.h
+++ util-linux-2.36.2/libmount/src/mountP.h
@@ -401,6 +401,7 @@ extern const struct libmnt_optmap *mnt_o
 			     const struct libmnt_optmap **mapent);
 
 /* optstr.c */
+extern int mnt_optstr_get_uid(const char *optstr, const char *name, uid_t *uid);
 extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end);
 extern int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next);
 extern int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next);
Index: util-linux-2.36.2/libmount/src/optstr.c
===================================================================
--- util-linux-2.36.2.orig/libmount/src/optstr.c
+++ util-linux-2.36.2/libmount/src/optstr.c
@@ -44,6 +44,31 @@ struct libmnt_optloc {
 #define mnt_optmap_entry_novalue(e) \
 		(e && (e)->name && !strchr((e)->name, '=') && !((e)->mask & MNT_PREFIX))
 
+static int ul_strtou64(const char *str, uint64_t *num, int base)
+{
+	char *end = NULL;
+	int64_t tmp;
+
+	errno = 0;
+	if (str == NULL || *str == '\0')
+		return -EINVAL;
+
+	/* we need to ignore negative numbers, note that for invalid negative
+	 * number strtoimax() returns negative number too, so we do not
+	 * need to check errno here */
+	tmp = (int64_t) strtoimax(str, &end, base);
+	if (tmp < 0)
+		errno = ERANGE;
+	else {
+		errno = 0;
+		*num = strtoumax(str, &end, base);
+	}
+
+	if (errno || str == end || (end && *end))
+		return -EINVAL;
+	return 0;
+}
+
 /*
  * Parses the first option from @optstr. The @optstr pointer is set to the beginning
  * of the next option.
@@ -1090,6 +1115,48 @@ int mnt_optstr_fix_user(char **optstr)
 	return rc;
 }
 
+/*
+ * Converts value from @optstr addressed by @name to uid.
+ *
+ * Returns: 0 on success, 1 if not found, <0 on error
+ */
+int mnt_optstr_get_uid(const char *optstr, const char *name, uid_t *uid)
+{
+	char *value = NULL;
+	size_t valsz = 0;
+	char buf[sizeof(stringify_value(UINT64_MAX))];
+	int rc;
+	uint64_t num;
+
+	assert(optstr);
+	assert(name);
+	assert(uid);
+
+	rc = mnt_optstr_get_option(optstr, name, &value, &valsz);
+	if (rc != 0)
+		goto fail;
+
+	if (valsz > sizeof(buf) - 1) {
+		rc = -ERANGE;
+		goto fail;
+	}
+	mem2strcpy(buf, value, valsz, sizeof(buf));
+
+	rc = ul_strtou64(buf, &num, 10);
+	if (rc != 0)
+		goto fail;
+	if (num > ULONG_MAX || (uid_t) num != num) {
+		rc = -ERANGE;
+		goto fail;
+	}
+	*uid = (uid_t) num;
+
+	return 0;
+fail:
+	DBG(UTILS, ul_debug("failed to convert '%s'= to number [rc=%d]", name, rc));
+	return rc;
+}
+
 /**
  * mnt_match_options:
  * @optstr: options string
openSUSE Build Service is sponsored by