From 46e0f3c43a81ce9411612c152e414162a9c220ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 25 Jun 2021 20:49:44 +0200 Subject: [PATCH] Load RSA keys as pem format directly, and using openssl crate, backported from async branch --- src/api/core/mod.rs | 5 +-- src/api/core/two_factor/duo.rs | 2 +- src/auth.rs | 28 ++++++++------ src/config.rs | 5 +-- src/error.rs | 2 + src/main.rs | 68 ++++++++++++---------------------- src/util.rs | 8 ++++ 7 files changed, 55 insertions(+), 63 deletions(-) diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 6f9db9bc..139de2a0 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -27,7 +27,6 @@ pub fn routes() -> Vec { // // Move this somewhere else // -use rocket::response::Response; use rocket::Route; use rocket_contrib::json::Json; use serde_json::Value; @@ -41,7 +40,7 @@ use crate::{ }; #[put("/devices/identifier//clear-token")] -fn clear_device_token<'a>(uuid: String) -> Response<'a> { +fn clear_device_token<'a>(uuid: String) -> &'static str { // This endpoint doesn't have auth header let _ = uuid; @@ -50,7 +49,7 @@ fn clear_device_token<'a>(uuid: String) -> Response<'a> { // This only clears push token // https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109 // https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37 - Response::new() + "" } #[put("/devices/identifier//token", data = "")] diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs index a45724df..606e32b2 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -343,7 +343,7 @@ fn parse_duo_values(key: &str, val: &str, ikey: &str, prefix: &str, time: i64) - err!("Invalid ikey") } - let expire = match expire.parse() { + let expire: i64 = match expire.parse() { Ok(e) => e, Err(_) => err!("Invalid expire time"), }; diff --git a/src/auth.rs b/src/auth.rs index 9b163aaa..190cfacb 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -27,17 +27,26 @@ static JWT_VERIFYEMAIL_ISSUER: Lazy = Lazy::new(|| format!("{}|verifyema static JWT_ADMIN_ISSUER: Lazy = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin())); static JWT_SEND_ISSUER: Lazy = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); -static PRIVATE_RSA_KEY: Lazy> = Lazy::new(|| match read_file(&CONFIG.private_rsa_key()) { - Ok(key) => key, - Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e), +static PRIVATE_RSA_KEY_VEC: Lazy> = Lazy::new(|| { + read_file(&CONFIG.private_rsa_key()).unwrap_or_else(|e| panic!("Error loading private RSA Key.\n{}", e)) }); -static PUBLIC_RSA_KEY: Lazy> = Lazy::new(|| match read_file(&CONFIG.public_rsa_key()) { - Ok(key) => key, - Err(e) => panic!("Error loading public RSA Key.\n Error: {}", e), +static PRIVATE_RSA_KEY: Lazy = Lazy::new(|| { + EncodingKey::from_rsa_pem(&PRIVATE_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding private RSA Key.\n{}", e)) +}); +static PUBLIC_RSA_KEY_VEC: Lazy> = Lazy::new(|| { + read_file(&CONFIG.public_rsa_key()).unwrap_or_else(|e| panic!("Error loading public RSA Key.\n{}", e)) +}); +static PUBLIC_RSA_KEY: Lazy = Lazy::new(|| { + DecodingKey::from_rsa_pem(&PUBLIC_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding public RSA Key.\n{}", e)) }); +pub fn load_keys() { + Lazy::force(&PRIVATE_RSA_KEY); + Lazy::force(&PUBLIC_RSA_KEY); +} + pub fn encode_jwt(claims: &T) -> String { - match jsonwebtoken::encode(&JWT_HEADER, claims, &EncodingKey::from_rsa_der(&PRIVATE_RSA_KEY)) { + match jsonwebtoken::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) { Ok(token) => token, Err(e) => panic!("Error encoding jwt {}", e), } @@ -55,10 +64,7 @@ fn decode_jwt(token: &str, issuer: String) -> Result Result { diff --git a/src/config.rs b/src/config.rs index 73a8f886..6b4fce59 100644 --- a/src/config.rs +++ b/src/config.rs @@ -770,13 +770,10 @@ impl Config { } pub fn private_rsa_key(&self) -> String { - format!("{}.der", CONFIG.rsa_key_filename()) - } - pub fn private_rsa_key_pem(&self) -> String { format!("{}.pem", CONFIG.rsa_key_filename()) } pub fn public_rsa_key(&self) -> String { - format!("{}.pub.der", CONFIG.rsa_key_filename()) + format!("{}.pub.pem", CONFIG.rsa_key_filename()) } pub fn mail_enabled(&self) -> bool { let inner = &self.inner.read().unwrap().config; diff --git a/src/error.rs b/src/error.rs index b1994560..beaf1db3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -50,6 +50,7 @@ use std::time::SystemTimeError as TimeErr; use u2f::u2ferror::U2fError as U2fErr; use webauthn_rs::error::WebauthnError as WebauthnErr; use yubico::yubicoerror::YubicoError as YubiErr; +use openssl::error::ErrorStack as SSLErr; #[derive(Serialize)] pub struct Empty {} @@ -82,6 +83,7 @@ make_error! { Lettre(LettreErr): _has_source, _api_error, Address(AddrErr): _has_source, _api_error, Smtp(SmtpErr): _has_source, _api_error, + OpenSSL(SSLErr): _has_source, _api_error, DieselCon(DieselConErr): _has_source, _api_error, DieselMig(DieselMigErr): _has_source, _api_error, diff --git a/src/main.rs b/src/main.rs index c60d16df..34c65f97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ use std::{ fs::create_dir_all, panic, path::Path, - process::{exit, Command}, + process::exit, str::FromStr, thread, time::Duration, @@ -53,7 +53,10 @@ fn main() { let extra_debug = matches!(level, LF::Trace | LF::Debug); check_data_folder(); - check_rsa_keys(); + check_rsa_keys().unwrap_or_else(|_| { + error!("Error creating keys, exiting..."); + exit(1); + }); check_web_vault(); create_icon_cache_folder(); @@ -249,52 +252,29 @@ fn check_data_folder() { } } -fn check_rsa_keys() { +fn check_rsa_keys()-> Result<(), crate::error::Error> { // If the RSA keys don't exist, try to create them - if !util::file_exists(&CONFIG.private_rsa_key()) || !util::file_exists(&CONFIG.public_rsa_key()) { - info!("JWT keys don't exist, checking if OpenSSL is available..."); + let priv_path = CONFIG.private_rsa_key(); + let pub_path = CONFIG.public_rsa_key(); - Command::new("openssl").arg("version").status().unwrap_or_else(|_| { - info!( - "Can't create keys because OpenSSL is not available, make sure it's installed and available on the PATH" - ); - exit(1); - }); + if !util::file_exists(&priv_path) { + let rsa_key = openssl::rsa::Rsa::generate(2048)?; - info!("OpenSSL detected, creating keys..."); - - let key = CONFIG.rsa_key_filename(); - - let pem = format!("{}.pem", key); - let priv_der = format!("{}.der", key); - let pub_der = format!("{}.pub.der", key); - - let mut success = Command::new("openssl") - .args(&["genrsa", "-out", &pem]) - .status() - .expect("Failed to create private pem file") - .success(); - - success &= Command::new("openssl") - .args(&["rsa", "-in", &pem, "-outform", "DER", "-out", &priv_der]) - .status() - .expect("Failed to create private der file") - .success(); - - success &= Command::new("openssl") - .args(&["rsa", "-in", &priv_der, "-inform", "DER"]) - .args(&["-RSAPublicKey_out", "-outform", "DER", "-out", &pub_der]) - .status() - .expect("Failed to create public der file") - .success(); - - if success { - info!("Keys created correctly."); - } else { - error!("Error creating keys, exiting..."); - exit(1); - } + let priv_key = rsa_key.private_key_to_pem()?; + crate::util::write_file(&priv_path, &priv_key)?; + info!("Private key created correctly."); } + + if !util::file_exists(&pub_path) { + let rsa_key = openssl::rsa::Rsa::private_key_from_pem(&util::read_file(&priv_path)?)?; + + let pub_key = rsa_key.public_key_to_pem()?; + crate::util::write_file(&pub_path, &pub_key)?; + info!("Public key created correctly."); + } + + auth::load_keys(); + Ok(()) } fn check_web_vault() { diff --git a/src/util.rs b/src/util.rs index b33f5890..8512bc7b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -219,6 +219,14 @@ pub fn read_file(path: &str) -> IOResult> { Ok(contents) } +pub fn write_file(path: &str, content: &[u8]) -> Result<(), crate::error::Error> { + use std::io::Write; + let mut f = File::create(path)?; + f.write_all(content)?; + f.flush()?; + Ok(()) +} + pub fn read_file_string(path: &str) -> IOResult { let mut contents = String::new();