@@ -8,8 +8,8 @@ use sanitization::SecretVec;
88#[ cfg( target_os = "linux" ) ]
99use super :: UNIX_O_NOFOLLOW ;
1010use super :: {
11- AcmeAccountCredentialsPath , AcmeAccountStoreError , MAX_ACCOUNT_CREDENTIALS_BYTES ,
12- managed_certificate_segment,
11+ AcmeAccountCredentialsPath , AcmeAccountStoreError , AcmeMutationLock ,
12+ MAX_ACCOUNT_CREDENTIALS_BYTES , managed_certificate_segment, unique_transaction_id ,
1313} ;
1414
1515const ACME_ACCOUNT_DIR : & str = "accounts" ;
@@ -47,20 +47,32 @@ pub fn load_account_credentials(
4747 } ) ;
4848 }
4949
50- let file = open_regular_account_credentials_file ( & path)
50+ let mut file = open_regular_account_credentials_file ( & path)
5151 . map_err ( |error| account_store_io_error ( & path, error) ) ?;
52- let mut contents = Vec :: new ( ) ;
53- file. take ( MAX_ACCOUNT_CREDENTIALS_BYTES . saturating_add ( 1 ) )
54- . read_to_end ( & mut contents)
52+ let admitted =
53+ usize:: try_from ( metadata. len ( ) ) . map_err ( |_| AcmeAccountStoreError :: Oversized {
54+ path : path. clone ( ) ,
55+ max_bytes : MAX_ACCOUNT_CREDENTIALS_BYTES ,
56+ } ) ?;
57+ let mut contents = SecretVec :: from_fn ( admitted, |_| 0 ) ;
58+ contents
59+ . with_secret_mut ( |contents| file. read_exact ( contents) )
5560 . map_err ( |error| account_store_io_error ( & path, error) ) ?;
56- if contents. len ( ) as u64 > MAX_ACCOUNT_CREDENTIALS_BYTES {
61+ let mut growth_probe = [ 0_u8 ; 1 ] ;
62+ let grew = file
63+ . read ( & mut growth_probe)
64+ . map_err ( |error| account_store_io_error ( & path, error) ) ?
65+ != 0 ;
66+ sanitization:: SecureSanitize :: secure_sanitize ( & mut growth_probe) ;
67+ if grew {
5768 return Err ( AcmeAccountStoreError :: Oversized {
5869 path,
5970 max_bytes : MAX_ACCOUNT_CREDENTIALS_BYTES ,
6071 } ) ;
6172 }
6273
63- serde_json:: from_slice ( & contents)
74+ contents
75+ . with_secret ( |contents| serde_json:: from_slice ( contents) )
6476 . map ( Some )
6577 . map_err ( |error| AcmeAccountStoreError :: Deserialize {
6678 path,
@@ -83,6 +95,8 @@ pub fn store_account_credentials(
8395 message : "credentials path has no parent directory" . to_owned ( ) ,
8496 } ) ?;
8597 ensure_safe_account_directory ( directory) ?;
98+ let _mutation_lock = AcmeMutationLock :: acquire ( directory)
99+ . map_err ( |error| account_store_io_error ( & directory. join ( ".fluxheim-acme.lock" ) , error) ) ?;
86100 ensure_safe_account_destination ( & credentials_path. path ) ?;
87101
88102 let contents = SecretVec :: from_vec ( serde_json:: to_vec ( credentials) . map_err ( |error| {
@@ -97,7 +111,9 @@ pub fn store_account_credentials(
97111 } ) ;
98112 }
99113
100- let tmp_path = directory. join ( ".credentials.json.tmp" ) ;
114+ let transaction =
115+ unique_transaction_id ( ) . map_err ( |error| account_store_io_error ( directory, error) ) ?;
116+ let tmp_path = directory. join ( format ! ( ".credentials.{transaction}.tmp" ) ) ;
101117 let result = ( || {
102118 contents. with_secret ( |contents| write_account_credentials_file ( & tmp_path, contents) ) ?;
103119 fs:: rename ( & tmp_path, & credentials_path. path )
@@ -114,6 +130,33 @@ pub fn store_account_credentials(
114130 Ok ( credentials_path)
115131}
116132
133+ pub fn remove_account_credentials (
134+ storage : & Path ,
135+ issuer_name : & str ,
136+ ) -> Result < bool , AcmeAccountStoreError > {
137+ let credentials_path = account_credentials_path ( storage, issuer_name) ;
138+ let directory =
139+ credentials_path
140+ . path
141+ . parent ( )
142+ . ok_or_else ( || AcmeAccountStoreError :: UnsafePath {
143+ path : credentials_path. path . clone ( ) ,
144+ message : "credentials path has no parent directory" . to_owned ( ) ,
145+ } ) ?;
146+ ensure_safe_account_directory ( directory) ?;
147+ let _mutation_lock = AcmeMutationLock :: acquire ( directory)
148+ . map_err ( |error| account_store_io_error ( & directory. join ( ".fluxheim-acme.lock" ) , error) ) ?;
149+ ensure_safe_account_destination ( & credentials_path. path ) ?;
150+ match fs:: remove_file ( & credentials_path. path ) {
151+ Ok ( ( ) ) => {
152+ sync_account_directory ( directory) ?;
153+ Ok ( true )
154+ }
155+ Err ( error) if error. kind ( ) == io:: ErrorKind :: NotFound => Ok ( false ) ,
156+ Err ( error) => Err ( account_store_io_error ( & credentials_path. path , error) ) ,
157+ }
158+ }
159+
117160fn ensure_safe_account_directory ( directory : & Path ) -> Result < ( ) , AcmeAccountStoreError > {
118161 reject_existing_symlink_in_account_path ( directory) ?;
119162 fs:: create_dir_all ( directory) . map_err ( |error| account_store_io_error ( directory, error) ) ?;
0 commit comments