1
0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2024-11-25 04:13:32 +01:00

Load RSA keys as pem format directly, and using openssl crate, backported from async branch

This commit is contained in:
Daniel García 2021-06-25 20:49:44 +02:00
parent 2cd17fe7af
commit 46e0f3c43a
No known key found for this signature in database
GPG Key ID: FC8A7D14C3CD543A
7 changed files with 55 additions and 63 deletions

View File

@ -27,7 +27,6 @@ pub fn routes() -> Vec<Route> {
// //
// Move this somewhere else // Move this somewhere else
// //
use rocket::response::Response;
use rocket::Route; use rocket::Route;
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde_json::Value; use serde_json::Value;
@ -41,7 +40,7 @@ use crate::{
}; };
#[put("/devices/identifier/<uuid>/clear-token")] #[put("/devices/identifier/<uuid>/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 // This endpoint doesn't have auth header
let _ = uuid; let _ = uuid;
@ -50,7 +49,7 @@ fn clear_device_token<'a>(uuid: String) -> Response<'a> {
// This only clears push token // 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/Api/Controllers/DevicesController.cs#L109
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37 // https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
Response::new() ""
} }
#[put("/devices/identifier/<uuid>/token", data = "<data>")] #[put("/devices/identifier/<uuid>/token", data = "<data>")]

View File

@ -343,7 +343,7 @@ fn parse_duo_values(key: &str, val: &str, ikey: &str, prefix: &str, time: i64) -
err!("Invalid ikey") err!("Invalid ikey")
} }
let expire = match expire.parse() { let expire: i64 = match expire.parse() {
Ok(e) => e, Ok(e) => e,
Err(_) => err!("Invalid expire time"), Err(_) => err!("Invalid expire time"),
}; };

View File

@ -27,17 +27,26 @@ static JWT_VERIFYEMAIL_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|verifyema
static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin())); static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin()));
static JWT_SEND_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); static JWT_SEND_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|send", CONFIG.domain_origin()));
static PRIVATE_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.private_rsa_key()) { static PRIVATE_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| {
Ok(key) => key, read_file(&CONFIG.private_rsa_key()).unwrap_or_else(|e| panic!("Error loading private RSA Key.\n{}", e))
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
}); });
static PUBLIC_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.public_rsa_key()) { static PRIVATE_RSA_KEY: Lazy<EncodingKey> = Lazy::new(|| {
Ok(key) => key, EncodingKey::from_rsa_pem(&PRIVATE_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding private RSA Key.\n{}", e))
Err(e) => panic!("Error loading public RSA Key.\n Error: {}", e), });
static PUBLIC_RSA_KEY_VEC: Lazy<Vec<u8>> = 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<DecodingKey> = 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<T: Serialize>(claims: &T) -> String { pub fn encode_jwt<T: Serialize>(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, Ok(token) => token,
Err(e) => panic!("Error encoding jwt {}", e), Err(e) => panic!("Error encoding jwt {}", e),
} }
@ -55,10 +64,7 @@ fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Err
}; };
let token = token.replace(char::is_whitespace, ""); let token = token.replace(char::is_whitespace, "");
jsonwebtoken::decode(&token, &&PUBLIC_RSA_KEY, &validation).map(|d| d.claims).map_res("Error decoding JWT")
jsonwebtoken::decode(&token, &DecodingKey::from_rsa_der(&PUBLIC_RSA_KEY), &validation)
.map(|d| d.claims)
.map_res("Error decoding JWT")
} }
pub fn decode_login(token: &str) -> Result<LoginJwtClaims, Error> { pub fn decode_login(token: &str) -> Result<LoginJwtClaims, Error> {

View File

@ -770,13 +770,10 @@ impl Config {
} }
pub fn private_rsa_key(&self) -> String { 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()) format!("{}.pem", CONFIG.rsa_key_filename())
} }
pub fn public_rsa_key(&self) -> String { 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 { pub fn mail_enabled(&self) -> bool {
let inner = &self.inner.read().unwrap().config; let inner = &self.inner.read().unwrap().config;

View File

@ -50,6 +50,7 @@ use std::time::SystemTimeError as TimeErr;
use u2f::u2ferror::U2fError as U2fErr; use u2f::u2ferror::U2fError as U2fErr;
use webauthn_rs::error::WebauthnError as WebauthnErr; use webauthn_rs::error::WebauthnError as WebauthnErr;
use yubico::yubicoerror::YubicoError as YubiErr; use yubico::yubicoerror::YubicoError as YubiErr;
use openssl::error::ErrorStack as SSLErr;
#[derive(Serialize)] #[derive(Serialize)]
pub struct Empty {} pub struct Empty {}
@ -82,6 +83,7 @@ make_error! {
Lettre(LettreErr): _has_source, _api_error, Lettre(LettreErr): _has_source, _api_error,
Address(AddrErr): _has_source, _api_error, Address(AddrErr): _has_source, _api_error,
Smtp(SmtpErr): _has_source, _api_error, Smtp(SmtpErr): _has_source, _api_error,
OpenSSL(SSLErr): _has_source, _api_error,
DieselCon(DieselConErr): _has_source, _api_error, DieselCon(DieselConErr): _has_source, _api_error,
DieselMig(DieselMigErr): _has_source, _api_error, DieselMig(DieselMigErr): _has_source, _api_error,

View File

@ -21,7 +21,7 @@ use std::{
fs::create_dir_all, fs::create_dir_all,
panic, panic,
path::Path, path::Path,
process::{exit, Command}, process::exit,
str::FromStr, str::FromStr,
thread, thread,
time::Duration, time::Duration,
@ -53,7 +53,10 @@ fn main() {
let extra_debug = matches!(level, LF::Trace | LF::Debug); let extra_debug = matches!(level, LF::Trace | LF::Debug);
check_data_folder(); check_data_folder();
check_rsa_keys(); check_rsa_keys().unwrap_or_else(|_| {
error!("Error creating keys, exiting...");
exit(1);
});
check_web_vault(); check_web_vault();
create_icon_cache_folder(); 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 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()) { let priv_path = CONFIG.private_rsa_key();
info!("JWT keys don't exist, checking if OpenSSL is available..."); let pub_path = CONFIG.public_rsa_key();
Command::new("openssl").arg("version").status().unwrap_or_else(|_| { if !util::file_exists(&priv_path) {
info!( let rsa_key = openssl::rsa::Rsa::generate(2048)?;
"Can't create keys because OpenSSL is not available, make sure it's installed and available on the PATH"
);
exit(1);
});
info!("OpenSSL detected, creating keys..."); let priv_key = rsa_key.private_key_to_pem()?;
crate::util::write_file(&priv_path, &priv_key)?;
let key = CONFIG.rsa_key_filename(); info!("Private key created correctly.");
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);
}
} }
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() { fn check_web_vault() {

View File

@ -219,6 +219,14 @@ pub fn read_file(path: &str) -> IOResult<Vec<u8>> {
Ok(contents) 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<String> { pub fn read_file_string(path: &str) -> IOResult<String> {
let mut contents = String::new(); let mut contents = String::new();