summary refs log tree commit diff
path: root/gnu/packages/patches/util-linux-CVE-2021-3995.patch
blob: 7faea838019237e0410d95a593f448879093b0eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
Fix CVE-2021-3995:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3995
https://seclists.org/oss-sec/2022/q1/66

Patch copied from upstream source repository:

https://github.com/util-linux/util-linux/commit/f3db9bd609494099f0c1b95231c5dfe383346929

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] 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(-)

diff --git a/include/strutils.h b/include/strutils.h
index 6e95707ea..a84d29594 100644
--- a/include/strutils.h
+++ b/include/strutils.h
@@ -106,8 +106,8 @@ static inline char *mem2strcpy(char *dest, const void *src, size_t n, size_t nma
 	if (n + 1 > nmax)
 		n = nmax - 1;
 
+	memset(dest, '\0', nmax);
 	memcpy(dest, src, n);
-	dest[nmax-1] = '\0';
 	return dest;
 }
 
diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c
index 173637a15..8773c65ff 100644
--- a/libmount/src/context_umount.c
+++ b/libmount/src/context_umount.c
@@ -453,10 +453,7 @@ static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv)
 	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;
 
@@ -473,11 +470,7 @@ static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv)
 	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 */
@@ -494,8 +487,7 @@ static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv)
 		return 0;
 	}
 
-	snprintf(uidstr, sizeof(uidstr), "%lu", (unsigned long) uid);
-	return strncmp(user_id, uidstr, sz) == 0;
+	return uid == entry_uid;
 }
 
 /*
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index d43a83541..22442ec55 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -399,6 +399,7 @@ extern const struct libmnt_optmap *mnt_optmap_get_entry(
 			     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);
diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c
index 921b9318e..16800f571 100644
--- a/libmount/src/optstr.c
+++ b/libmount/src/optstr.c
@@ -1076,6 +1076,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
-- 
2.34.0