mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-06-10 02:14:53 +03:00
refact(password): encrypt (#15073)
* refact(password): encrypt Signed-off-by: fufesou <linlong1266@gmail.com> * refact(password): simplify preset password Signed-off-by: fufesou <linlong1266@gmail.com> * update hbb_common Signed-off-by: fufesou <linlong1266@gmail.com> * refact(password): clear password, do not clear salt * refact(password): update hbb_common Signed-off-by: fufesou <linlong1266@gmail.com> * refact(password): merge import Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
Submodule libs/hbb_common updated: 9043c15acc...822701e416
@@ -2471,23 +2471,13 @@ pub fn is_disable_installation() -> SyncReturn<bool> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_preset_password() -> bool {
|
pub fn is_preset_password() -> bool {
|
||||||
let hard = config::HARD_SETTINGS
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get("password")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
if hard.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On desktop, service owns the authoritative config; query it via IPC and return only a boolean.
|
// On desktop, service owns the authoritative config; query it via IPC and return only a boolean.
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
return crate::ipc::is_permanent_password_preset();
|
return crate::ipc::is_permanent_password_preset();
|
||||||
|
|
||||||
// On mobile, we have no service IPC; verify against local storage.
|
// On mobile, we have no service IPC; verify against local storage.
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
return config::Config::matches_permanent_password_plain(&hard);
|
return config::Config::is_using_preset_password();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't call this function for desktop version.
|
// Don't call this function for desktop version.
|
||||||
|
|||||||
17
src/ipc.rs
17
src/ipc.rs
@@ -839,15 +839,7 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
"N".to_owned()
|
"N".to_owned()
|
||||||
});
|
});
|
||||||
} else if name == "permanent-password-is-preset" {
|
} else if name == "permanent-password-is-preset" {
|
||||||
let hard = config::HARD_SETTINGS
|
value = Some(if Config::is_using_preset_password() {
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get("password")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
let is_preset =
|
|
||||||
!hard.is_empty() && Config::matches_permanent_password_plain(&hard);
|
|
||||||
value = Some(if is_preset {
|
|
||||||
"Y".to_owned()
|
"Y".to_owned()
|
||||||
} else {
|
} else {
|
||||||
"N".to_owned()
|
"N".to_owned()
|
||||||
@@ -898,7 +890,7 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
log::warn!("Changing permanent password is disabled");
|
log::warn!("Changing permanent password is disabled");
|
||||||
updated = false;
|
updated = false;
|
||||||
} else {
|
} else {
|
||||||
Config::set_permanent_password(&value);
|
updated = Config::set_permanent_password(&value);
|
||||||
}
|
}
|
||||||
// Explicitly ACK/NACK permanent-password writes. This allows UIs/FFI to
|
// Explicitly ACK/NACK permanent-password writes. This allows UIs/FFI to
|
||||||
// distinguish "accepted by daemon" vs "IPC send succeeded" without
|
// distinguish "accepted by daemon" vs "IPC send succeeded" without
|
||||||
@@ -1550,11 +1542,6 @@ fn apply_permanent_password_storage_and_salt_payload(payload: Option<&str>) -> R
|
|||||||
bail!("Invalid permanent-password-storage-and-salt payload");
|
bail!("Invalid permanent-password-storage-and-salt payload");
|
||||||
};
|
};
|
||||||
|
|
||||||
if storage.is_empty() {
|
|
||||||
Config::set_permanent_password_storage_for_sync("", "")?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::set_permanent_password_storage_for_sync(storage, salt)?;
|
Config::set_permanent_password_storage_for_sync(storage, salt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ use cidr_utils::cidr::IpCidr;
|
|||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use hbb_common::protobuf::EnumOrUnknown;
|
use hbb_common::protobuf::EnumOrUnknown;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::decode_permanent_password_h1_from_storage,
|
config::{
|
||||||
config::{self, keys, Config, TrustedDevice},
|
self, decode_permanent_password_h1_from_storage, decode_preset_password_h1_from_storage,
|
||||||
|
keys, local_permanent_password_storage_is_usable_for_auth,
|
||||||
|
preset_permanent_password_storage_is_usable_for_auth, Config, TrustedDevice,
|
||||||
|
},
|
||||||
fs::{self, can_enable_overwrite_detection, JobType},
|
fs::{self, can_enable_overwrite_detection, JobType},
|
||||||
futures::{SinkExt, StreamExt},
|
futures::{SinkExt, StreamExt},
|
||||||
get_time, get_version_number,
|
get_time, get_version_number,
|
||||||
@@ -412,8 +415,9 @@ impl Connection {
|
|||||||
let _raii_id = raii::ConnectionID::new(id);
|
let _raii_id = raii::ConnectionID::new(id);
|
||||||
let _raii_control_permissions_id =
|
let _raii_control_permissions_id =
|
||||||
raii::ControlPermissionsID::new(id, &control_permissions);
|
raii::ControlPermissionsID::new(id, &control_permissions);
|
||||||
|
let salt = Config::get_effective_permanent_password_salt();
|
||||||
let hash = Hash {
|
let hash = Hash {
|
||||||
salt: Config::get_salt(),
|
salt,
|
||||||
challenge: Config::get_auto_password(6),
|
challenge: Config::get_auto_password(6),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@@ -2109,6 +2113,16 @@ impl Connection {
|
|||||||
self.validate_password_plain(storage)
|
self.validate_password_plain(storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_preset_password_storage(&self, storage: &str, salt: &str) -> bool {
|
||||||
|
if salt.is_empty() {
|
||||||
|
return self.validate_password_plain(storage);
|
||||||
|
}
|
||||||
|
let Some(h1) = decode_preset_password_h1_from_storage(storage) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
self.verify_h1(&h1[..])
|
||||||
|
}
|
||||||
|
|
||||||
// This is coarse brute-force protection for the current temporary password value.
|
// This is coarse brute-force protection for the current temporary password value.
|
||||||
// We only care whether the active temporary password itself was presented correctly,
|
// We only care whether the active temporary password itself was presented correctly,
|
||||||
// not whether later authorization steps succeed. A successful temporary-password
|
// not whether later authorization steps succeed. A successful temporary-password
|
||||||
@@ -2180,23 +2194,22 @@ impl Connection {
|
|||||||
log::info!("Permanent password accepted via logon-screen fallback");
|
log::info!("Permanent password accepted via logon-screen fallback");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Since hashed storage uses a prefix-based encoding, a hard plaintext that
|
// Strictly check storage usability before auth so malformed encrypted/hash storage
|
||||||
// happens to look like hashed storage could be mis-detected. Validate local storage
|
// cannot fall back to being accepted as legacy plaintext.
|
||||||
// and hard/preset plaintext via separate paths to avoid that ambiguity.
|
let (local_storage, local_salt) =
|
||||||
let (local_storage, _) = Config::get_local_permanent_password_storage_and_salt();
|
Config::get_local_permanent_password_storage_and_salt();
|
||||||
if !local_storage.is_empty() {
|
if !local_storage.is_empty() {
|
||||||
if self.validate_password_storage(&local_storage) {
|
if local_permanent_password_storage_is_usable_for_auth(&local_storage, &local_salt)
|
||||||
|
&& self.validate_password_storage(&local_storage)
|
||||||
|
{
|
||||||
print_fallback();
|
print_fallback();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let hard = config::HARD_SETTINGS
|
let (hard, salt) = Config::get_preset_password_storage_and_salt();
|
||||||
.read()
|
if preset_permanent_password_storage_is_usable_for_auth(&hard, &salt)
|
||||||
.unwrap()
|
&& self.validate_preset_password_storage(&hard, &salt)
|
||||||
.get("password")
|
{
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
if !hard.is_empty() && self.validate_password_plain(&hard) {
|
|
||||||
print_fallback();
|
print_fallback();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -647,8 +647,7 @@ pub fn set_permanent_password_with_result(password: String) -> bool {
|
|||||||
}
|
}
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
{
|
{
|
||||||
config::Config::set_permanent_password(&password);
|
return config::Config::set_permanent_password(&password);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user