Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2024-11-22 05:10:29 +01:00
Load RSA keys as pem format directly, and using openssl crate, backported from async branch
Dieser Commit ist enthalten in:
Ursprung
2cd17fe7af
Commit
46e0f3c43a
7 geänderte Dateien mit 55 neuen und 63 gelöschten Zeilen
|
@ -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>")]
|
||||||
|
|
|
@ -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"),
|
||||||
};
|
};
|
||||||
|
|
28
src/auth.rs
28
src/auth.rs
|
@ -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> {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
66
src/main.rs
66
src/main.rs
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren