]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: fix UID check for FUSE umount [CVE-2021-3995]
authorKarel Zak <kzak@redhat.com>
Wed, 24 Nov 2021 12:53:25 +0000 (13:53 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 24 Jan 2022 08:49:16 +0000 (09:49 +0100)
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
libmount/src/context_umount.c
libmount/src/mountP.h
libmount/src/optstr.c

index 6e95707ea94260f784c47a9230702e25da8d2e7c..a84d29594da2eb8e7b7dd5a22768c7a2114978bd 100644 (file)
@@ -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;
 }
 
index 173637a15ad20dbcd0d1eb02bc332fd80befb7ab..8773c65ffa8cea047021281a608d133d5c0e0c7f 100644 (file)
@@ -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;
 }
 
 /*
index d43a8354186bdab923fd50938170079810253c12..22442ec55e75f0ada2cbb86fa5be28ce1079cfb9 100644 (file)
@@ -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);
index 921b9318e76fe6b974534f73ded8154499297fd0..16800f571c41ce1e3f1237dd57a0b08c4e62da8e 100644 (file)
@@ -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