mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 10:18:19 +02:00 
			
		
		
		
	Fix for RSA Keys which are read only (#4744)
* Fix for RSA Keys which are read only Sometimes an RSA Key file could be read only. We currently failed because we also wanted to write. Added an extra check if the file exists already and is not 0 in size. If it does already exists and is larger then 0, then open in read only mode. Fixes #4644 * Updated code to work atomically - Changed the code to work atomically - Also show the alert generated from `Io` * Fix spelling
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							54bfcb8bc3
						
					
				
				
					commit
					505b30eec2
				
			
							
								
								
									
										38
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/auth.rs
									
									
									
									
									
								
							| @@ -1,13 +1,18 @@ | |||||||
| // JWT Handling | // JWT Handling | ||||||
| // | // | ||||||
| use chrono::{TimeDelta, Utc}; | use chrono::{TimeDelta, Utc}; | ||||||
|  | use jsonwebtoken::{errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header}; | ||||||
| use num_traits::FromPrimitive; | use num_traits::FromPrimitive; | ||||||
| use once_cell::sync::{Lazy, OnceCell}; | use once_cell::sync::{Lazy, OnceCell}; | ||||||
|  |  | ||||||
| use jsonwebtoken::{errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header}; |  | ||||||
| use openssl::rsa::Rsa; | use openssl::rsa::Rsa; | ||||||
| use serde::de::DeserializeOwned; | use serde::de::DeserializeOwned; | ||||||
| use serde::ser::Serialize; | use serde::ser::Serialize; | ||||||
|  | use std::{ | ||||||
|  |     env, | ||||||
|  |     fs::File, | ||||||
|  |     io::{Read, Write}, | ||||||
|  |     net::IpAddr, | ||||||
|  | }; | ||||||
|  |  | ||||||
| use crate::{error::Error, CONFIG}; | use crate::{error::Error, CONFIG}; | ||||||
|  |  | ||||||
| @@ -31,27 +36,36 @@ static PRIVATE_RSA_KEY: OnceCell<EncodingKey> = OnceCell::new(); | |||||||
| static PUBLIC_RSA_KEY: OnceCell<DecodingKey> = OnceCell::new(); | static PUBLIC_RSA_KEY: OnceCell<DecodingKey> = OnceCell::new(); | ||||||
|  |  | ||||||
| pub fn initialize_keys() -> Result<(), crate::error::Error> { | pub fn initialize_keys() -> Result<(), crate::error::Error> { | ||||||
|  |     fn read_key(create_if_missing: bool) -> Result<(Rsa<openssl::pkey::Private>, Vec<u8>), crate::error::Error> { | ||||||
|         let mut priv_key_buffer = Vec::with_capacity(2048); |         let mut priv_key_buffer = Vec::with_capacity(2048); | ||||||
|  |  | ||||||
|     let priv_key = { |         let mut priv_key_file = File::options() | ||||||
|         let mut priv_key_file = |             .create(create_if_missing) | ||||||
|             File::options().create(true).truncate(false).read(true).write(true).open(CONFIG.private_rsa_key())?; |             .truncate(false) | ||||||
|  |             .read(true) | ||||||
|  |             .write(create_if_missing) | ||||||
|  |             .open(CONFIG.private_rsa_key())?; | ||||||
|  |  | ||||||
|         #[allow(clippy::verbose_file_reads)] |         #[allow(clippy::verbose_file_reads)] | ||||||
|         let bytes_read = priv_key_file.read_to_end(&mut priv_key_buffer)?; |         let bytes_read = priv_key_file.read_to_end(&mut priv_key_buffer)?; | ||||||
|  |  | ||||||
|         if bytes_read > 0 { |         let rsa_key = if bytes_read > 0 { | ||||||
|             Rsa::private_key_from_pem(&priv_key_buffer[..bytes_read])? |             Rsa::private_key_from_pem(&priv_key_buffer[..bytes_read])? | ||||||
|         } else { |         } else if create_if_missing { | ||||||
|             // Only create the key if the file doesn't exist or is empty |             // Only create the key if the file doesn't exist or is empty | ||||||
|             let rsa_key = openssl::rsa::Rsa::generate(2048)?; |             let rsa_key = openssl::rsa::Rsa::generate(2048)?; | ||||||
|             priv_key_buffer = rsa_key.private_key_to_pem()?; |             priv_key_buffer = rsa_key.private_key_to_pem()?; | ||||||
|             priv_key_file.write_all(&priv_key_buffer)?; |             priv_key_file.write_all(&priv_key_buffer)?; | ||||||
|             info!("Private key created correctly."); |             info!("Private key '{}' created correctly", CONFIG.private_rsa_key()); | ||||||
|             rsa_key |             rsa_key | ||||||
|         } |         } else { | ||||||
|  |             err!("Private key does not exist or invalid format", CONFIG.private_rsa_key()); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         Ok((rsa_key, priv_key_buffer)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let (priv_key, priv_key_buffer) = read_key(true).or_else(|_| read_key(false))?; | ||||||
|     let pub_key_buffer = priv_key.public_key_to_pem()?; |     let pub_key_buffer = priv_key.public_key_to_pem()?; | ||||||
|  |  | ||||||
|     let enc = EncodingKey::from_rsa_pem(&priv_key_buffer)?; |     let enc = EncodingKey::from_rsa_pem(&priv_key_buffer)?; | ||||||
| @@ -803,12 +817,6 @@ impl<'r> FromRequest<'r> for OwnerHeaders { | |||||||
| // | // | ||||||
| // Client IP address detection | // Client IP address detection | ||||||
| // | // | ||||||
| use std::{ |  | ||||||
|     env, |  | ||||||
|     fs::File, |  | ||||||
|     io::{Read, Write}, |  | ||||||
|     net::IpAddr, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| pub struct ClientIp { | pub struct ClientIp { | ||||||
|     pub ip: IpAddr, |     pub ip: IpAddr, | ||||||
|   | |||||||
| @@ -73,11 +73,9 @@ async fn main() -> Result<(), Error> { | |||||||
|     }); |     }); | ||||||
|     init_logging(level).ok(); |     init_logging(level).ok(); | ||||||
|  |  | ||||||
|     let extra_debug = matches!(level, LF::Trace | LF::Debug); |  | ||||||
|  |  | ||||||
|     check_data_folder().await; |     check_data_folder().await; | ||||||
|     auth::initialize_keys().unwrap_or_else(|_| { |     auth::initialize_keys().unwrap_or_else(|e| { | ||||||
|         error!("Error creating keys, exiting..."); |         error!("Error creating private key '{}'\n{e:?}\nExiting Vaultwarden!", CONFIG.private_rsa_key()); | ||||||
|         exit(1); |         exit(1); | ||||||
|     }); |     }); | ||||||
|     check_web_vault(); |     check_web_vault(); | ||||||
| @@ -91,6 +89,7 @@ async fn main() -> Result<(), Error> { | |||||||
|     schedule_jobs(pool.clone()); |     schedule_jobs(pool.clone()); | ||||||
|     crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap(); |     crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap(); | ||||||
|  |  | ||||||
|  |     let extra_debug = matches!(level, LF::Trace | LF::Debug); | ||||||
|     launch_rocket(pool, extra_debug).await // Blocks until program termination. |     launch_rocket(pool, extra_debug).await // Blocks until program termination. | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -514,7 +513,7 @@ async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error> | |||||||
|  |  | ||||||
|     tokio::spawn(async move { |     tokio::spawn(async move { | ||||||
|         tokio::signal::ctrl_c().await.expect("Error setting Ctrl-C handler"); |         tokio::signal::ctrl_c().await.expect("Error setting Ctrl-C handler"); | ||||||
|         info!("Exiting vaultwarden!"); |         info!("Exiting Vaultwarden!"); | ||||||
|         CONFIG.shutdown(); |         CONFIG.shutdown(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user