From 82d7ca48bb6d879d01c2a6f28166128159ff26cd Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 19 Aug 2023 20:41:07 +0100 Subject: [PATCH] feat: added verification for the ssh keys Closes #15 --- Cargo.lock | 4 +- Cargo.toml | 3 +- silver/.ssh/authorized_keys | 3 + src/main.rs | 1 + src/methods/account_recover.rs | 100 ++++++++++++++++++++++++++++++++- 5 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 silver/.ssh/authorized_keys diff --git a/Cargo.lock b/Cargo.lock index 3ec38b1..8ee6a84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2690,9 +2690,9 @@ dependencies = [ [[package]] name = "ssh-key" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0a17fec6ea344bfa1cda3aed2f0696fddc6295cfcc8c454a3bf58b8ffaabeb" +checksum = "728fdf5286c394f12d83eaad51190af629aade5494813ce6b57f7425f1ca51b7" dependencies = [ "dsa", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 1825971..7556d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,5 +42,4 @@ lettre = "0.10.4" maud = "0.25.0" # verifying ssh keys -ssh-key = { version = "0.6.0", features = ["ecdsa", "rand_core", "std", "dsa", "ed25519", "rsa" ] } - +ssh-key = { version = "0.6.1", features = ["crypto", "dsa"] } diff --git a/silver/.ssh/authorized_keys b/silver/.ssh/authorized_keys new file mode 100644 index 0000000..776f908 --- /dev/null +++ b/silver/.ssh/authorized_keys @@ -0,0 +1,3 @@ +# --- BEGIN PVE --- +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBHbsOjxIcLasz+CHA8gUg1pvc8dPHwMKdWoIwNvPxLp Desktop_WSL +# --- END PVE --- \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e8e80fc..b7ddedf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,7 @@ async fn main() -> tide::Result<()> { app.at("/ldap/recover/password/auth").post(account_recover::password::auth); app.at("/ldap/recover/username").post(account_recover::username::submit); app.at("/ldap/recover/ssh/request").post(account_recover::ssh::request); + app.at("/ldap/recover/ssh/verify").post(account_recover::ssh::verify); app.listen(host_port).await?; Ok(()) diff --git a/src/methods/account_recover.rs b/src/methods/account_recover.rs index 7737ee8..68df84c 100644 --- a/src/methods/account_recover.rs +++ b/src/methods/account_recover.rs @@ -426,7 +426,9 @@ pub mod username { pub mod ssh { use super::*; use crate::AccountsSSH; - use ssh_key::AuthorizedKeys; + use ldap3::Mod; + use ssh_key::{AuthorizedKeys, SshSig}; + use std::collections::HashSet; use std::fs; // this is for a legacy member who has forgotten their account password to be able to set an email. @@ -479,7 +481,7 @@ pub mod ssh { // check if there is ane listing entry, use that auth if exists if let Ok(result) = sqlx::query_as::<_, AccountsSSH>( - r#" + r#" SELECT * FROM accounts_ssh WHERE user == ? @@ -495,7 +497,7 @@ pub mod ssh { // not in db, generate auth and save let auth = random_string(50); if sqlx::query_as::<_, AccountsSSH>( - " + " INSERT OR REPLACE INTO accounts_ssh (user, auth_code, email) VALUES (?1, ?2, ?3) ", @@ -514,4 +516,96 @@ pub mod ssh { // return the full thing Ok(json!({"result": "success","success": { "auth": auth,"keys": keys }}).into()) } + + #[derive(Debug, Deserialize)] + struct RequestVerify { + user: String, + auth_signed: String, + } + + // echo "auth code" | ssh-keygen -Y sign -n file -f /path/to/key + pub async fn verify(mut req: Request) -> tide::Result { + let RequestVerify { + user, + auth_signed, + } = req.body_json().await?; + + let db = &req.state().db; + let config = &req.state().config; + let details = if let Ok(result) = sqlx::query_as::<_, AccountsSSH>( + r#" + SELECT * + FROM accounts_ssh + WHERE user == ? + "#, + ) + .bind(&user) + .fetch_one(db) + .await + { + result + } else { + return Ok(json!({ "result": "error"}).into()); + }; + + // check if //.ssh/authorized_keys exists + //let root = "/skynet_old"; + let root = "."; + let path = format!("{}/{}/.ssh/authorized_keys", root, user); + + let sig = match SshSig::from_pem(auth_signed) { + Ok(x) => x, + Err(_) => { + return Ok(json!({ "result": "error", "error": "Incorrect signed format"}).into()); + } + }; + + // when ye echo it adds a newline to the end of the "file", need to duplicate it here. + let msg_tmp = format!("{}\n", details.auth_code); + let msg = msg_tmp.as_bytes(); + + let mut valid = false; + if fs::read_to_string(&path).is_ok() { + if let Ok(x) = AuthorizedKeys::read_file(path) { + for entry in x { + let key = entry.public_key(); + match key.verify("file", msg, &sig) { + Ok(_) => { + valid = true; + break; + } + Err(_) => {} + } + } + } + } + + if !valid { + return Ok(json!({"result": "error", "error": "no valid key"}).into()); + } + + // add to ldap + let mut ldap = LdapConn::new(&config.ldap_host)?; + ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?; + + let mods = vec![Mod::Replace(String::from("mail"), HashSet::from([details.email]))]; + + let dn = format!("uid={},ou=users,dc=skynet,dc=ie", &user); + ldap.modify(&dn, mods)?.success()?; + ldap.unbind()?; + + // delete from tmp + sqlx::query_as::<_, AccountsSSH>( + r#" + DELETE FROM accounts_ssh + WHERE user == ? + "#, + ) + .bind(&user) + .fetch_optional(db) + .await?; + + // return the full thing + Ok(json!({"result": "success", "success": "key valid"}).into()) + } }