userdb: Reference-count DBusUserInfo, DBusGroupInfo
authorSimon McVittie <smcv@collabora.com>
Tue, 30 Jun 2020 18:29:06 +0000 (19:29 +0100)
committerSimon McVittie <smcv@collabora.com>
Thu, 2 Jul 2020 09:08:49 +0000 (10:08 +0100)
commitf3b2574f0c9faa32a59efec905921f7ef4438a60
tree34b7b965d60dc498ea69acbc67953845d4374422
parent37b36d49a67b0aee09051f5f76d33f69b67709ab
userdb: Reference-count DBusUserInfo, DBusGroupInfo

Previously, the hash table indexed by uid (or gid) took ownership of the
single reference to the heap-allocated struct, and the hash table
indexed by username (or group name) had a borrowed pointer to the same
struct that exists in the other hash table.

However, this can break down if you have two or more distinct usernames
that share a numeric identifier. This is generally a bad idea, because
the user-space model in such situations does not match the kernel-space
reality, and in particular there is no effective kernel-level security
boundary between such users, but it is sometimes done anyway.

In this case, when the second username is looked up in the userdb, it
overwrites (replaces) the entry in the hash table that is indexed by
uid, freeing the DBusUserInfo. This results in both the key and the
value in the hash table that is indexed by username becoming dangling
pointers (use-after-free), leading to undefined behaviour, which is
certainly not what we want to see when doing access control.

An equivalent situation can occur with groups, in the rare case where
a numeric group ID has two names (although I have not heard of this
being done in practice).

Solve this by reference-counting the data structure. There are up to
three references in practice: one held temporarily while the lookup
function is populating and storing it, one held by the hash table that
is indexed by uid, and one held by the hash table that is indexed by
name.

Closes: dbus#305
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 2b7948ef907669e844b52c4fa2268d6e3162a70c)
dbus/dbus-sysdeps-unix.h
dbus/dbus-userdb-util.c
dbus/dbus-userdb.c
dbus/dbus-userdb.h