From 9e78911a2f46818cd98582b5006b9836b46ad912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20B=C3=B6nisch?= Date: Thu, 20 Aug 2026 17:34:38 +0200 Subject: [PATCH 1/3] Fix sendmail executable permission check (#7483) * Fix sendmail executable permission check * Use access check for sendmail command --- Cargo.lock | 19 +++++++++++++++++++ Cargo.toml | 1 + src/config.rs | 7 ++----- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21defe79..b0a36edf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -934,6 +934,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "chacha20" version = "0.10.1" @@ -3228,6 +3234,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -5867,6 +5885,7 @@ dependencies = [ "macros", "mimalloc", "moka", + "nix", "num-derive", "num-traits", "opendal", diff --git a/Cargo.toml b/Cargo.toml index f65ead29..3e187ff3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ oidc-accept-string-booleans = ["openidconnect/accept-string-booleans"] unstable = [] [target."cfg(unix)".dependencies] +nix = { version = "0.31.3", features = ["fs"] } # Logging syslog = "7.0.0" diff --git a/src/config.rs b/src/config.rs index d5b50146..72b58252 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1162,11 +1162,8 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> { } #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - if !metadata.permissions().mode() & 0o111 != 0 { - err!(format!("sendmail command at `{path:?}` isn't executable")); - } + if nix::unistd::access(&path, nix::unistd::AccessFlags::X_OK).is_err() { + err!(format!("sendmail command at `{path:?}` isn't executable")); } } } From 46d71107f5094460dd5ecbe1dbac6e6c71e5189a Mon Sep 17 00:00:00 2001 From: Stefan Melmuk <509385+stefan0xC@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:03:56 +0200 Subject: [PATCH 2/3] add dummy revisionDate (#7608) --- src/db/models/org_policy.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 88b7872c..d501f8b9 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -91,6 +91,7 @@ impl OrgPolicy { "type": self.atype, "data": data_json, "enabled": self.enabled, + "revisionDate": null, "object": "policy", }); From fa2566d14fc745937ce104011475eca9e6c7a6f6 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Mon, 24 Aug 2026 19:38:23 +0200 Subject: [PATCH 3/3] Fix password change with newer web-vault (#7634) --- src/api/core/accounts.rs | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 0cb4d3c0..626f22bb 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -595,29 +595,52 @@ async fn post_keys(data: Json, headers: Headers, conn: DbConn) -> Json #[serde(rename_all = "camelCase")] struct ChangePassData { master_password_hash: String, - new_master_password_hash: String, master_password_hint: Option, - key: String, + authentication_data: Option, + unlock_data: Option, + + // Outdated values, might still be used by older clients + new_master_password_hash: Option, + key: Option, } #[post("/accounts/password", data = "")] async fn post_password(data: Json, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: ChangePassData = data.into_inner(); - let mut user = headers.user; + let user = headers.user; if !user.check_valid_password(&data.master_password_hash) { err!("Invalid password") } - user.password_hint = clean_password_hint(data.master_password_hint.as_ref()); - enforce_password_hint_setting(user.password_hint.as_ref())?; - log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn) .await; + let (new_master_password_hash, new_key) = + if let (Some(unlock_data), Some(authentication_data)) = (data.unlock_data, data.authentication_data) { + if authentication_data.kdf != unlock_data.kdf { + err!("KDF settings must be equal for authentication and unlock") + } + + if user.email != authentication_data.salt || user.email != unlock_data.salt { + err!("Invalid master password salt") + } + + (authentication_data.master_password_authentication_hash, unlock_data.master_key_wrapped_user_key) + } else if let (Some(new_master_password_hash), Some(new_key)) = (data.new_master_password_hash, data.key) { + (new_master_password_hash, new_key) + } else { + err!("Invalid request!") + }; + + let mut user = user; + + user.password_hint = clean_password_hint(data.master_password_hint.as_ref()); + enforce_password_hint_setting(user.password_hint.as_ref())?; + user.set_password( - &data.new_master_password_hash, - Some(data.key), + &new_master_password_hash, + Some(new_key), true, Some(vec![ String::from("post_rotatekey"),