Skip to content

Commit f59afd5

Browse files
committed
Updated docs for new keystore.
Also updated feature descriptions in the library docs, and the readme. Updated the cross-platform doc builder script to know about the new keystore.
1 parent 658174e commit f59afd5

File tree

4 files changed

+75
-47
lines changed

4 files changed

+75
-47
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ This crate allows clients to "bring their own credential store" by providing tra
5656

5757
This crate provides built-in implementations of the following platform-specific credential stores:
5858

59-
* _Linux_: The DBus-based Secret Service and the kernel keyutils.
59+
* _Linux_: The DBus-based Secret Service, the kernel keyutils, and a combo of the two.
6060
* _FreeBSD_, _OpenBSD_: The DBus-based Secret Service.
6161
* _macOS_, _iOS_: The local keychain.
6262
* _Windows_: The Windows Credential Manager.
6363

6464
To enable the stores you want, you use features: there is one feature for each possibly-included credential store. If you specify a feature (e.g., `dbus-secret-service`) _and_ your target platform (e.g., `freebsd`) supports that credential store, it will be included as the default credential store in that build. That way you can have a build command that specifies a single credential store for each of your target platforms, and use that same build command for all targets.
6565

66-
If you don't enable any credential stores that are supported on a given platform, or you enable multiple credential stores for some platform, the _mock_ keystore will be the default on that platform. See the [developer docs](https://docs.rs/keyring/) for details of which features control the inclusion of which credential stores (and which platforms each credential store targets).
66+
If you don't enable any credential stores that are supported on a given platform, the _mock_ keystore will be the default on that platform. See the [developer docs](https://docs.rs/keyring/) for details of which features control the inclusion of which credential stores.
6767

6868
### Platform-specific issues
6969

build-xplat-docs.sh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#!/bin/bash
2-
cargo doc --no-deps --features=linux-native --target aarch64-unknown-linux-musl $OPEN_DOCS
3-
cargo doc --no-deps --features=windows-native --target aarch64-pc-windows-msvc $OPEN_DOCS
4-
cargo doc --no-deps --features=apple-native --target aarch64-apple-darwin $OPEN_DOCS
5-
cargo doc --no-deps --features=apple-native --target aarch64-apple-ios $OPEN_DOCS
2+
if [[ "$OSTYPE" == "linux"* ]]; then
3+
cargo doc --no-deps --features=linux-native-sync-persistent $OPEN_DOCS
4+
cargo doc --no-deps --features=sync-secret-service $OPEN_DOCS
5+
cargo doc --no-deps --features=linux-native $OPEN_DOCS
6+
elif [[ "$OSTYPE" == "darwin"* ]]; then
7+
cargo doc --no-deps --features=linux-native --target aarch64-unknown-linux-musl $OPEN_DOCS
8+
cargo doc --no-deps --features=windows-native --target aarch64-pc-windows-msvc $OPEN_DOCS
9+
cargo doc --no-deps --features=apple-native --target aarch64-apple-darwin $OPEN_DOCS
10+
cargo doc --no-deps --features=apple-native --target aarch64-apple-ios $OPEN_DOCS
11+
fi

src/keyutils_persistent.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
11
/*!
22
3-
# keyutils-persistent credential store
4-
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.
8-
3+
# Linux (keyutils) store with Secret Service backing
4+
5+
This store, contributed by [@soywod](https://github.com/soywod),
6+
uses the [keyutils module](crate::keyutils) as a cache
7+
available to headless processes, while using the
8+
[secret-service module](crate::secret_service)
9+
to provide credential storage beyond reboot.
10+
The expected usage pattern
11+
for this module is as follows:
12+
13+
- Processes that run on headless systems are built with `keyutils` support via the
14+
`linux-native` feature of this crate. After each reboot, these processes
15+
are either launched after the keyutils cache has been reloaded from the secret service,
16+
or (if launched immediately) they wait until the keyutils cache has been reloaded.
17+
- A headed "configuration" process is built with this module that allows its user
18+
to configure the credentials needed by the headless processes. After each reboot,
19+
this process unlocks the secret service (see both the keyutils and secret-service
20+
module for information about how this can be done headlessly, if desired) and then
21+
accesses each of the configured credentials (which loads them into keyutils). At
22+
that point the headless clients can be started (or become active, if already started).
23+
24+
This store works by creating a keyutils entry and a secret-service entry for
25+
each of its entries. Because keyutils entries don't have attributes, entries
26+
in this store don't expose attributes either. Because keyutils entries can't
27+
store empty passwords/secrets, this store's entries can't either.
28+
29+
See the documentation for the `keyutils` and `secret-service` modules if you
30+
want details about how the underlying storage is handled.
931
*/
1032

1133
use log::debug;
@@ -15,7 +37,7 @@ use super::credential::{
1537
};
1638
use super::error::{Error, Result};
1739
use super::keyutils::KeyutilsCredential;
18-
use super::secret_service::SsCredential;
40+
use super::secret_service::{SsCredential, SsCredentialBuilder};
1941

2042
/// Representation of a keyutils-persistent credential.
2143
///
@@ -36,13 +58,14 @@ impl CredentialApi for KeyutilsPersistentCredential {
3658
/// Set a secret in the underlying store
3759
///
3860
/// It sets first the secret in keyutils, then in
39-
/// secret-service. If the late one fails, keyutils secret change
61+
/// secret-service. If the latter set fails, the former
4062
/// is reverted.
4163
fn set_secret(&self, secret: &[u8]) -> Result<()> {
4264
let prev_secret = self.keyutils.get_secret();
4365
self.keyutils.set_secret(secret)?;
4466

4567
if let Err(err) = self.ss.set_secret(secret) {
68+
debug!("Failed set of secret-service: {err}; reverting keyutils");
4669
match prev_secret {
4770
Ok(ref secret) => self.keyutils.set_secret(secret),
4871
Err(Error::NoEntry) => self.keyutils.delete_credential(),
@@ -66,11 +89,11 @@ impl CredentialApi for KeyutilsPersistentCredential {
6689
return Ok(password);
6790
}
6891
Err(err) => {
69-
debug!("cannot get password from keyutils: {err}, trying from secret service")
92+
debug!("Failed get from keyutils: {err}; trying secret service")
7093
}
7194
}
7295

73-
let password = self.ss.get_password().map_err(ambigous_to_no_entry)?;
96+
let password = self.ss.get_password().map_err(ambiguous_to_no_entry)?;
7497
self.keyutils.set_password(&password)?;
7598

7699
Ok(password)
@@ -87,11 +110,11 @@ impl CredentialApi for KeyutilsPersistentCredential {
87110
return Ok(secret);
88111
}
89112
Err(err) => {
90-
debug!("cannot get secret from keyutils: {err}, trying from secret service")
113+
debug!("Failed get from keyutils: {err}; trying secret service")
91114
}
92115
}
93116

94-
let secret = self.ss.get_secret().map_err(ambigous_to_no_entry)?;
117+
let secret = self.ss.get_secret().map_err(ambiguous_to_no_entry)?;
95118
self.keyutils.set_secret(&secret)?;
96119

97120
Ok(secret)
@@ -121,9 +144,8 @@ impl CredentialApi for KeyutilsPersistentCredential {
121144
impl KeyutilsPersistentCredential {
122145
/// Create the platform credential for a Keyutils entry.
123146
///
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`.
147+
/// This just passes the arguments to the underlying two stores
148+
/// and wraps their results with an entry that holds both.
127149
pub fn new_with_target(target: Option<&str>, service: &str, user: &str) -> Result<Self> {
128150
let ss = SsCredential::new_with_target(target, service, user)?;
129151
let keyutils = KeyutilsCredential::new_with_target(target, service, user)?;
@@ -144,27 +166,29 @@ pub fn default_credential_builder() -> Box<CredentialBuilder> {
144166
}
145167

146168
impl CredentialBuilderApi for KeyutilsPersistentCredentialBuilder {
147-
/// Build an [KeyutilsPersistentCredential] for the given target, service, and user.
169+
/// Build a [KeyutilsPersistentCredential] for the given target, service, and user.
148170
fn build(&self, target: Option<&str>, service: &str, user: &str) -> Result<Box<Credential>> {
149171
Ok(Box::new(SsCredential::new_with_target(
150172
target, service, user,
151173
)?))
152174
}
153175

154176
/// Return the underlying builder object with an `Any` type so that it can
155-
/// be downgraded to an [KeyutilsPersistentCredentialBuilder] for platform-specific processing.
177+
/// be downgraded to a [KeyutilsPersistentCredentialBuilder] for platform-specific processing.
156178
fn as_any(&self) -> &dyn std::any::Any {
157179
self
158180
}
159181

160-
/// This keystore keeps credentials thanks to the inner secret-service store.
182+
/// Return the persistence of this store.
183+
///
184+
/// This store's persistence derives from that of the secret service.
161185
fn persistence(&self) -> CredentialPersistence {
162-
CredentialPersistence::UntilDelete
186+
SsCredentialBuilder {}.persistence()
163187
}
164188
}
165189

166190
/// Replace any Ambiguous error with a NoEntry one
167-
fn ambigous_to_no_entry(err: Error) -> Error {
191+
fn ambiguous_to_no_entry(err: Error) -> Error {
168192
if let Error::Ambiguous(_) = err {
169193
return Error::NoEntry;
170194
};
@@ -174,18 +198,9 @@ fn ambigous_to_no_entry(err: Error) -> Error {
174198

175199
#[cfg(test)]
176200
mod tests {
177-
use crate::credential::CredentialPersistence;
178201
use crate::{Entry, Error};
179202

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-
}
203+
use super::KeyutilsPersistentCredential;
189204

190205
fn entry_new(service: &str, user: &str) -> Entry {
191206
crate::tests::entry_from_constructor(

src/lib.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ example, the macOS Keychain credential store is only included if the `"apple-nat
6464
feature is specified (and the crate is built with a macOS target).
6565
6666
If no specified credential store features apply to a given platform,
67-
or multiple credential store features apply to a given platform,
6867
this crate will use the (platform-independent) _mock_ credential store (see below)
6968
on that platform. There are no
7069
default features in this crate: you must specify explicitly which platform-specific
@@ -78,6 +77,18 @@ Here are the available credential store features:
7877
7978
- `linux-native`: Provides access to the `keyutils` storage on Linux.
8079
80+
- `linux-native-sync-persistent`: Uses both `keyutils` and `sync-secret-service`
81+
(see below) for storage. See the docs for the `keyutils_persistent`
82+
module for a full explanation of why both are used. Because this
83+
store uses the `sync-secret-service`, you can use additional features related
84+
to that store (described below).
85+
86+
- `linux-native-async-persistent`: Uses both `keyutils` and `async-secret-service`
87+
(see below) for storage. See the docs for the `keyutils_persistent`
88+
module for a full explanation of why both are used.
89+
Because this store uses the `async-secret-service`, you
90+
must specify the additional features required by that store (described below).
91+
8192
- `sync-secret-service`: Provides access to the DBus-based
8293
[Secret Service](https://specifications.freedesktop.org/secret-service/latest/)
8394
storage on Linux, FreeBSD, and OpenBSD. This is a _synchronous_ keystore that provides
@@ -99,17 +110,13 @@ Here are the available credential store features:
99110
installed on the user's machine, specify the `vendored` feature
100111
to statically link them with the built crate.
101112
102-
You cannot specify both the `sync-secret-service` and `async-secret-service` features;
103-
this will produce a compile error. You must pick one or the other if you want to use
104-
the secret service for credential storage.
105-
106113
The Linux platform is the only one for which this crate supplies multiple keystores:
107-
secret-service and keyutils. The secret-service is the more widely used store, because
108-
it provides persistence of credentials beyond reboot (which keyutils does not). However,
109-
because secret-service relies on system UI for unlocking credentials, it often isn't
110-
available on headless Linux installations, so keyutils is provided for those situations.
111-
If you enable both the secret-service store and the keyutils store, the secret-service
112-
store will be used as the default.
114+
native (keyutils), sync or async secret service, and sync or async "combo" (both
115+
keyutils and secret service). You cannot specify use of both sync and async
116+
keystores; this will lead to a compile error. If you enable a combo keystore on Linux,
117+
that will be the default keystore. If you don't enable a
118+
combo keystore on Linux, but you do enable both the native and secret service keystores,
119+
the secret service will be the default.
113120
114121
## Client-provided Credential Stores
115122

0 commit comments

Comments
 (0)