2
2
3
3
# keyutils-persistent credential store
4
4
5
- TODO
5
+ This store is a combination of the [keyutils](crate::keyutils) store
6
+ backed up with a persistent [secret-service](crate::secret_service)
7
+ store.
6
8
7
9
*/
10
+
8
11
use log:: debug;
9
12
10
13
use super :: credential:: {
@@ -14,29 +17,49 @@ use super::error::{Error, Result};
14
17
use super :: keyutils:: KeyutilsCredential ;
15
18
use super :: secret_service:: SsCredential ;
16
19
20
+ /// Representation of a keyutils-persistent credential.
21
+ ///
22
+ /// The credential owns a [KeyutilsCredential] for in-memory usage and
23
+ /// a [SsCredential] for persistence.
17
24
#[ derive( Debug , Clone ) ]
18
25
pub struct KeyutilsPersistentCredential {
19
26
keyutils : KeyutilsCredential ,
20
27
ss : SsCredential ,
21
28
}
22
29
23
30
impl CredentialApi for KeyutilsPersistentCredential {
31
+ /// Set a password in the underlying store
24
32
fn set_password ( & self , password : & str ) -> Result < ( ) > {
25
33
self . set_secret ( password. as_bytes ( ) )
26
34
}
27
35
36
+ /// Set a secret in the underlying store
37
+ ///
38
+ /// It sets first the secret in keyutils, then in
39
+ /// secret-service. If the late one fails, keyutils secret change
40
+ /// is reverted.
28
41
fn set_secret ( & self , secret : & [ u8 ] ) -> Result < ( ) > {
29
- let prev_secret = self . keyutils . get_secret ( ) ? ;
42
+ let prev_secret = self . keyutils . get_secret ( ) ;
30
43
self . keyutils . set_secret ( secret) ?;
31
44
32
45
if let Err ( err) = self . ss . set_secret ( secret) {
33
- self . keyutils . set_secret ( & prev_secret) ?;
46
+ match prev_secret {
47
+ Ok ( ref secret) => self . keyutils . set_secret ( secret) ,
48
+ Err ( Error :: NoEntry ) => self . keyutils . delete_credential ( ) ,
49
+ Err ( err) => Err ( err) ,
50
+ } ?;
51
+
34
52
return Err ( err) ;
35
53
}
36
54
37
55
Ok ( ( ) )
38
56
}
39
57
58
+ /// Retrieve a password from the underlying store
59
+ ///
60
+ /// The password is retrieved from keyutils. In case of error, the
61
+ /// password is retrieved from secret-service instead (and
62
+ /// keyutils is updated).
40
63
fn get_password ( & self ) -> Result < String > {
41
64
match self . keyutils . get_password ( ) {
42
65
Ok ( password) => {
@@ -53,6 +76,11 @@ impl CredentialApi for KeyutilsPersistentCredential {
53
76
Ok ( password)
54
77
}
55
78
79
+ /// Retrieve a secret from the underlying store
80
+ ///
81
+ /// The secret is retrieved from keyutils. In case of error, the
82
+ /// secret is retrieved from secret-service instead (and keyutils
83
+ /// is updated).
56
84
fn get_secret ( & self ) -> Result < Vec < u8 > > {
57
85
match self . keyutils . get_secret ( ) {
58
86
Ok ( secret) => {
@@ -69,6 +97,10 @@ impl CredentialApi for KeyutilsPersistentCredential {
69
97
Ok ( secret)
70
98
}
71
99
100
+ /// Delete a password from the underlying store.
101
+ ///
102
+ /// The credential is deleted from both keyutils and
103
+ /// secret-service.
72
104
fn delete_credential ( & self ) -> Result < ( ) > {
73
105
if let Err ( err) = self . keyutils . delete_credential ( ) {
74
106
debug ! ( "cannot delete keyutils credential: {err}" ) ;
@@ -87,21 +119,26 @@ impl CredentialApi for KeyutilsPersistentCredential {
87
119
}
88
120
89
121
impl KeyutilsPersistentCredential {
122
+ /// Create the platform credential for a Keyutils entry.
123
+ ///
124
+ /// An explicit target string is interpreted as the KeyRing to use for the entry.
125
+ /// If none is provided, then we concatenate the user and service in the string
126
+ /// `keyring-rs:user@service`.
90
127
pub fn new_with_target ( target : Option < & str > , service : & str , user : & str ) -> Result < Self > {
91
128
let ss = SsCredential :: new_with_target ( target, service, user) ?;
92
129
let keyutils = KeyutilsCredential :: new_with_target ( target, service, user) ?;
93
130
Ok ( Self { keyutils, ss } )
94
131
}
95
132
}
96
133
97
- /// The builder for secret-service-with- keyutils credentials
134
+ /// The builder for keyutils-persistent credentials
98
135
#[ derive( Debug , Default ) ]
99
136
pub struct KeyutilsPersistentCredentialBuilder { }
100
137
101
- /// Returns an instance of the secret-service-with- keyutils credential builder.
138
+ /// Returns an instance of the keyutils-persistent credential builder.
102
139
///
103
- /// If secret-service-with- keyutils is the default credential store,
104
- /// this is called once when an entry is first created.
140
+ /// If keyutils-persistent is the default credential store, this is
141
+ /// called once when an entry is first created.
105
142
pub fn default_credential_builder ( ) -> Box < CredentialBuilder > {
106
143
Box :: new ( KeyutilsPersistentCredentialBuilder { } )
107
144
}
@@ -120,17 +157,94 @@ impl CredentialBuilderApi for KeyutilsPersistentCredentialBuilder {
120
157
self
121
158
}
122
159
123
- /// Since this keystore keeps credentials in kernel memory,
124
- /// they vanish on reboot
160
+ /// This keystore keeps credentials thanks to the inner secret-service store.
125
161
fn persistence ( & self ) -> CredentialPersistence {
126
162
CredentialPersistence :: UntilDelete
127
163
}
128
164
}
129
165
166
+ /// Replace any Ambiguous error with a NoEntry one
130
167
fn ambigous_to_no_entry ( err : Error ) -> Error {
131
168
if let Error :: Ambiguous ( _) = err {
132
169
return Error :: NoEntry ;
133
170
} ;
134
171
135
172
err
136
173
}
174
+
175
+ #[ cfg( test) ]
176
+ mod tests {
177
+ use crate :: credential:: CredentialPersistence ;
178
+ use crate :: { Entry , Error } ;
179
+
180
+ use super :: { default_credential_builder, KeyutilsPersistentCredential } ;
181
+
182
+ #[ test]
183
+ fn test_persistence ( ) {
184
+ assert ! ( matches!(
185
+ default_credential_builder( ) . persistence( ) ,
186
+ CredentialPersistence :: UntilDelete
187
+ ) )
188
+ }
189
+
190
+ fn entry_new ( service : & str , user : & str ) -> Entry {
191
+ crate :: tests:: entry_from_constructor (
192
+ KeyutilsPersistentCredential :: new_with_target,
193
+ service,
194
+ user,
195
+ )
196
+ }
197
+
198
+ #[ test]
199
+ fn test_invalid_parameter ( ) {
200
+ let credential = KeyutilsPersistentCredential :: new_with_target ( Some ( "" ) , "service" , "user" ) ;
201
+ assert ! (
202
+ matches!( credential, Err ( Error :: Invalid ( _, _) ) ) ,
203
+ "Created entry with empty target"
204
+ ) ;
205
+ }
206
+
207
+ #[ test]
208
+ fn test_empty_service_and_user ( ) {
209
+ crate :: tests:: test_empty_service_and_user ( entry_new) ;
210
+ }
211
+
212
+ #[ test]
213
+ fn test_missing_entry ( ) {
214
+ crate :: tests:: test_missing_entry ( entry_new) ;
215
+ }
216
+
217
+ #[ test]
218
+ fn test_empty_password ( ) {
219
+ let entry = entry_new ( "empty password service" , "empty password user" ) ;
220
+ assert ! (
221
+ matches!( entry. set_password( "" ) , Err ( Error :: Invalid ( _, _) ) ) ,
222
+ "Able to set empty password"
223
+ ) ;
224
+ }
225
+
226
+ #[ test]
227
+ fn test_round_trip_ascii_password ( ) {
228
+ crate :: tests:: test_round_trip_ascii_password ( entry_new) ;
229
+ }
230
+
231
+ #[ test]
232
+ fn test_round_trip_non_ascii_password ( ) {
233
+ crate :: tests:: test_round_trip_non_ascii_password ( entry_new) ;
234
+ }
235
+
236
+ #[ test]
237
+ fn test_round_trip_random_secret ( ) {
238
+ crate :: tests:: test_round_trip_random_secret ( entry_new) ;
239
+ }
240
+
241
+ #[ test]
242
+ fn test_update ( ) {
243
+ crate :: tests:: test_update ( entry_new) ;
244
+ }
245
+
246
+ #[ test]
247
+ fn test_noop_get_update_attributes ( ) {
248
+ crate :: tests:: test_noop_get_update_attributes ( entry_new) ;
249
+ }
250
+ }
0 commit comments