1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2024-11-22 05:10:29 +01:00

Fix conflict resolution logic for read_only and hide_passwords flags

For one of these flags to be in effect for a cipher, upstream requires all of
(rather than any of) the collections the cipher is in to have that flag set.

Also, some of the logic for loading access restrictions was wrong. I think
that only malicious clients that also had knowledge of the UUIDs of ciphers
they didn't have access to would have been able to take advantage of that.
Dieser Commit ist enthalten in:
Jeremy Lin 2021-10-29 11:45:43 -07:00
Ursprung a2316ca091
Commit 6cbb724069

Datei anzeigen

@ -343,36 +343,39 @@ impl Cipher {
db_run! {conn: { db_run! {conn: {
// Check whether this cipher is in any collections accessible to the // Check whether this cipher is in any collections accessible to the
// user. If so, retrieve the access flags for each collection. // user. If so, retrieve the access flags for each collection.
let query = ciphers::table let rows = ciphers::table
.filter(ciphers::uuid.eq(&self.uuid)) .filter(ciphers::uuid.eq(&self.uuid))
.inner_join(ciphers_collections::table.on( .inner_join(ciphers_collections::table.on(
ciphers::uuid.eq(ciphers_collections::cipher_uuid))) ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
.inner_join(users_collections::table.on( .inner_join(users_collections::table.on(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
.and(users_collections::user_uuid.eq(user_uuid)))) .and(users_collections::user_uuid.eq(user_uuid))))
.select((users_collections::read_only, users_collections::hide_passwords)); .select((users_collections::read_only, users_collections::hide_passwords))
.load::<(bool, bool)>(conn)
.expect("Error getting access restrictions");
// There's an edge case where a cipher can be in multiple collections if rows.is_empty() {
// with inconsistent access flags. For example, a cipher could be in // This cipher isn't in any collections accessible to the user.
// one collection where the user has read-only access, but also in return None;
// another collection where the user has read/write access. To handle }
// this, we do a boolean OR of all values in each of the `read_only`
// and `hide_passwords` columns. This could ideally be done as part // A cipher can be in multiple collections with inconsistent access flags.
// of the query, but Diesel doesn't support a max() or bool_or() // For example, a cipher could be in one collection where the user has
// function on booleans and this behavior isn't portable anyway. // read-only access, but also in another collection where the user has
if let Ok(vec) = query.load::<(bool, bool)>(conn) { // read/write access. For a flag to be in effect for a cipher, upstream
let mut read_only = false; // requires all collections the cipher is in to have that flag set.
let mut hide_passwords = false; // Therefore, we do a boolean AND of all values in each of the `read_only`
for (ro, hp) in vec.iter() { // and `hide_passwords` columns. This could ideally be done as part of the
read_only |= ro; // query, but Diesel doesn't support a min() or bool_and() function on
hide_passwords |= hp; // booleans and this behavior isn't portable anyway.
let mut read_only = true;
let mut hide_passwords = true;
for (ro, hp) in rows.iter() {
read_only &= ro;
hide_passwords &= hp;
} }
Some((read_only, hide_passwords)) Some((read_only, hide_passwords))
} else {
// This cipher isn't in any collections accessible to the user.
None
}
}} }}
} }