fix: ensure known_hosts is up to date

This commit is contained in:
Nikita Galaiko 2023-12-08 14:51:16 +01:00 committed by GitButler
parent 9e57b90f7d
commit 14254fe23c
6 changed files with 120 additions and 11 deletions

56
Cargo.lock generated
View File

@ -974,7 +974,7 @@ dependencies = [
"hashbrown 0.14.0",
"lock_api",
"once_cell",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
@ -1805,6 +1805,7 @@ dependencies = [
"similar",
"slug",
"ssh-key",
"ssh2",
"tauri",
"tauri-build",
"tauri-plugin-context-menu",
@ -1911,7 +1912,7 @@ dependencies = [
"futures-timer",
"no-std-compat",
"nonzero_ext",
"parking_lot",
"parking_lot 0.12.1",
"quanta",
"rand 0.8.5",
"smallvec",
@ -2975,7 +2976,7 @@ dependencies = [
"file-id",
"log",
"notify",
"parking_lot",
"parking_lot 0.12.1",
"walkdir",
]
@ -3293,6 +3294,17 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -3300,7 +3312,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
]
[[package]]
@ -3749,7 +3775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
dependencies = [
"log",
"parking_lot",
"parking_lot 0.12.1",
"scheduled-thread-pool",
]
@ -4214,7 +4240,7 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
dependencies = [
"parking_lot",
"parking_lot 0.12.1",
]
[[package]]
@ -4767,6 +4793,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "ssh2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7fe461910559f6d5604c3731d00d2aafc4a83d1665922e280f42f9a168d5455"
dependencies = [
"bitflags 1.3.2",
"libc",
"libssh2-sys",
"parking_lot 0.11.2",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -4796,7 +4834,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",
@ -4937,7 +4975,7 @@ dependencies = [
"ndk-sys",
"objc",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"png",
"raw-window-handle",
"scopeguard",
@ -5335,7 +5373,7 @@ dependencies = [
"libc",
"mio",
"num_cpus",
"parking_lot",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.5.5",

View File

@ -58,6 +58,7 @@ sha2 = "0.10.8"
similar = { version = "2.2.1", features = ["unicode"] }
slug = "0.1.5"
ssh-key = { version = "0.6.3", features = [ "alloc", "ed25519" ] }
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
tauri = { version = "1.5.2", features = ["dialog-open", "fs-read-file", "path-all", "process-relaunch", "protocol-asset", "shell-open", "system-tray", "window-maximize", "window-start-dragging", "window-unmaximize"] }
tauri-plugin-context-menu = { git = "https://github.com/gitbutlerapp/tauri-plugin-context-menu", branch = "main" }
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }

View File

@ -18,7 +18,7 @@ pub struct Url {
/// The password associated with a user.
password: Option<String>,
/// The host to which to connect. Localhost is implied if `None`.
host: Option<String>,
pub host: Option<String>,
/// When serializing, use the alternative forms as it was parsed as such.
serialize_alternative_form: bool,
/// The port to use when connecting to a host. If `None`, standard ports depending on `scheme` will be used.

View File

@ -22,6 +22,7 @@ pub mod projects;
pub mod reader;
pub mod sentry;
pub mod sessions;
pub mod ssh;
pub mod storage;
pub mod users;
pub mod virtual_branches;

View File

@ -7,7 +7,7 @@ use anyhow::{Context, Result};
use crate::{
git::{self, credentials::HelpError, Url},
keys, projects, reader, users,
keys, projects, reader, ssh, users,
virtual_branches::Branch,
};
@ -352,6 +352,9 @@ impl Repository {
let auth_flows = credentials.help(self, branch.remote())?;
for (mut remote, callbacks) in auth_flows {
if let Some(url) = remote.url().context("failed to get remote url")? {
ssh::check_known_host(&url).context("failed to check known host")?;
}
for callback in callbacks {
match remote.push(
&[refspec.as_str()],
@ -391,6 +394,9 @@ impl Repository {
let refspec = &format!("+refs/heads/*:refs/remotes/{}/*", remote_name);
let auth_flows = credentials.help(self, remote_name)?;
for (mut remote, callbacks) in auth_flows {
if let Some(url) = remote.url().context("failed to get remote url")? {
ssh::check_known_host(&url).context("failed to check known host")?;
}
for callback in callbacks {
let mut fetch_opts = git2::FetchOptions::new();
fetch_opts.remote_callbacks(callback.into());

63
packages/tauri/src/ssh.rs Normal file
View File

@ -0,0 +1,63 @@
use std::{env, fs, path::Path};
use ssh2::{self, CheckResult, KnownHostFileKind};
use crate::git;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Ssh(ssh2::Error),
#[error(transparent)]
Io(std::io::Error),
#[error("mismatched host key")]
MismatchedHostKey,
#[error("failed to check the known hosts")]
Failure,
}
pub fn check_known_host(remote_url: &git::Url) -> Result<(), Error> {
let host = if let Some(host) = remote_url.host.as_ref() {
host
} else {
return Ok(());
};
let mut session = ssh2::Session::new().map_err(Error::Ssh)?;
session
.set_tcp_stream(std::net::TcpStream::connect(format!("{}:22", host)).map_err(Error::Io)?);
session.handshake().map_err(Error::Ssh)?;
let mut known_hosts = session.known_hosts().map_err(Error::Ssh)?;
// Initialize the known hosts with a global known hosts file
let dotssh = Path::new(&env::var("HOME").unwrap()).join(".ssh");
let file = dotssh.join("known_hosts");
if !file.exists() {
fs::create_dir_all(&dotssh).map_err(Error::Io)?;
fs::File::create(&file).map_err(Error::Io)?;
}
known_hosts
.read_file(&file, KnownHostFileKind::OpenSSH)
.map_err(Error::Ssh)?;
// Now check to see if the seesion's host key is anywhere in the known
// hosts file
let (key, key_type) = session.host_key().unwrap();
match known_hosts.check(host, key) {
CheckResult::Match => Ok(()),
CheckResult::Mismatch => Err(Error::MismatchedHostKey),
CheckResult::Failure => Err(Error::Failure),
CheckResult::NotFound => {
tracing::info!("adding host key for {}", host);
known_hosts
.add(host, key, "added by gitbutler client", key_type.into())
.map_err(Error::Ssh)?;
known_hosts
.write_file(&file, KnownHostFileKind::OpenSSH)
.map_err(Error::Ssh)?;
Ok(())
}
}
}