signstar_config/non_admin_credentials.rs
1//! Non-administrative credentials handling for a NetHSM backend.
2use std::{
3 fmt::{Debug, Display},
4 fs::{File, Permissions, create_dir_all, read_to_string, set_permissions},
5 io::Write,
6 os::unix::fs::{PermissionsExt, chown},
7 path::{Path, PathBuf},
8 process::{Command, Stdio},
9};
10
11#[cfg(doc)]
12use nethsm::NetHsm;
13use nethsm::{FullCredentials, Passphrase, UserId};
14#[cfg(doc)]
15use nethsm_config::HermeticParallelConfig;
16use nethsm_config::{ExtendedUserMapping, NonAdministrativeSecretHandling, SystemUserId};
17use rand::{Rng, distributions::Alphanumeric, thread_rng};
18use signstar_common::{
19 common::SECRET_FILE_MODE,
20 system_user::{
21 get_home_base_dir_path,
22 get_plaintext_secret_file,
23 get_systemd_creds_secret_file,
24 get_user_secrets_dir,
25 },
26};
27
28use crate::{
29 config::load_config,
30 utils::{
31 fail_if_not_root,
32 fail_if_root,
33 get_command,
34 get_current_system_user,
35 get_system_user_pair,
36 match_current_system_user,
37 },
38};
39
40/// An error that may occur when handling non-administrative credentials for a NetHSM backend.
41#[derive(Debug, thiserror::Error)]
42pub enum Error {
43 /// There are one or more errors when loading credentials for a specific system user.
44 #[error("Errors occurred when loading credentials for system user {system_user}:\n{errors}")]
45 CredentialsLoading {
46 /// The system user for which loading of backend user credentials led to errors.
47 system_user: SystemUserId,
48 /// The errors that occurred during loading of backend user credentials for `system_user`.
49 errors: CredentialsLoadingErrors,
50 },
51
52 /// There are no credentials for a specific system user.
53 #[error("There are no credentials for system user {system_user}")]
54 CredentialsMissing {
55 /// The system user for which credentials are missing.
56 system_user: SystemUserId,
57 },
58
59 /// A mapping does not offer a system user.
60 #[error("There is no system user in the mapping.")]
61 NoSystemUser,
62
63 /// A user is not a signing user for the NetHSM backend.
64 #[error("The user is not an operator user in the NetHSM backend used for signing.")]
65 NotSigningUser,
66
67 /// A passphrase directory can not be created.
68 #[error("Passphrase directory {path} for user {system_user} can not be created:\n{source}")]
69 SecretsDirCreate {
70 /// The path to a secrets directory that could not be created.
71 path: PathBuf,
72 /// The system user in whose home directory `path` could not be created.
73 system_user: SystemUserId,
74 /// The source error.
75 source: std::io::Error,
76 },
77
78 /// A secrets file can not be created.
79 #[error("The secrets file {path} can not be created for user {system_user}:\n{source}")]
80 SecretsFileCreate {
81 /// The path to a secrets file that could not be created.
82 path: PathBuf,
83 /// The system user in whose home directory `path` could not be created.
84 system_user: SystemUserId,
85 /// The source error.
86 source: std::io::Error,
87 },
88
89 /// The file metadata of a secrets file cannot be retrieved.
90 #[error("File metadata of secrets file {path} cannot be retrieved")]
91 SecretsFileMetadata {
92 /// The path to a secrets file for which metadata could not be retrieved.
93 path: PathBuf,
94 /// The source error.
95 source: std::io::Error,
96 },
97
98 /// A secrets file does not exist.
99 #[error("Secrets file not found: {path}")]
100 SecretsFileMissing {
101 /// The path to a secrets file that is missing.
102 path: PathBuf,
103 },
104
105 /// A secrets file is not a file.
106 #[error("Secrets file is not a file: {path}")]
107 SecretsFileNotAFile {
108 /// The path to a secrets file that is not a file.
109 path: PathBuf,
110 },
111
112 /// A secrets file does not have the correct permissions.
113 #[error("Secrets file {path} has permissions {mode}, but {SECRET_FILE_MODE} is required")]
114 SecretsFilePermissions {
115 /// The path to a secrets file for which permissions could not be set.
116 path: PathBuf,
117 /// The file mode that should be applied to the file at `path`.
118 mode: u32,
119 },
120
121 /// A secrets file cannot be read.
122 #[error("Failed reading secrets file {path}:\n{source}")]
123 SecretsFileRead {
124 /// The path to a secrets file that could not be read.
125 path: PathBuf,
126 /// The source error.
127 source: std::io::Error,
128 },
129
130 /// A secrets file can not be written to.
131 #[error("The secrets file {path} can not be written to for user {system_user}: {source}")]
132 SecretsFileWrite {
133 /// The path to a secrets file that could not be written to.
134 path: PathBuf,
135 /// The system user in whose home directory `path` resides.
136 system_user: SystemUserId,
137 /// The source error.
138 source: std::io::Error,
139 },
140}
141
142/// An error that may occur when loading credentials for a [`SystemUserId`].
143///
144/// Alongside an [`Error`][`crate::Error`] contains a target [`UserId`] for which the error
145/// occurred.
146#[derive(Debug)]
147pub struct CredentialsLoadingError {
148 user_id: UserId,
149 error: crate::Error,
150}
151
152impl CredentialsLoadingError {
153 /// Creates a new [`CredentialsLoadingError`].
154 pub fn new(user_id: UserId, error: crate::Error) -> Self {
155 Self { user_id, error }
156 }
157
158 /// Returns a reference to the [`UserId`].
159 pub fn get_user_id(&self) -> &UserId {
160 &self.user_id
161 }
162
163 /// Returns a reference to the [`Error`][crate::Error].
164 pub fn get_error(&self) -> &crate::Error {
165 &self.error
166 }
167}
168
169impl Display for CredentialsLoadingError {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "{}: {}", self.user_id, self.error)
172 }
173}
174
175/// A wrapper for a list of [`CredentialsLoadingError`]s.
176#[derive(Debug)]
177pub struct CredentialsLoadingErrors {
178 errors: Vec<CredentialsLoadingError>,
179}
180
181impl CredentialsLoadingErrors {
182 /// Creates a new [`CredentialsLoadingError`].
183 pub fn new(errors: Vec<CredentialsLoadingError>) -> Self {
184 Self { errors }
185 }
186
187 /// Returns a reference to the list of [`CredentialsLoadingError`]s.
188 pub fn get_errors(&self) -> &[CredentialsLoadingError] {
189 &self.errors
190 }
191}
192
193impl Display for CredentialsLoadingErrors {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 write!(
196 f,
197 "{}",
198 self.errors
199 .iter()
200 .map(|error| error.to_string())
201 .collect::<Vec<String>>()
202 .join("\n")
203 )
204 }
205}
206
207/// A collection of credentials and credential loading errors for a system user.
208///
209/// Tracks a [`SystemUserId`], zero or more [`FullCredentials`] mapped to it, as well as zero or
210/// more errors related to loading the passphrase for a [`UserId`].
211#[derive(Debug)]
212pub struct CredentialsLoading {
213 mapping: ExtendedUserMapping,
214 credentials: Vec<FullCredentials>,
215 errors: CredentialsLoadingErrors,
216}
217
218impl CredentialsLoading {
219 /// Creates a new [`CredentialsLoading`].
220 pub fn new(
221 mapping: ExtendedUserMapping,
222 credentials: Vec<FullCredentials>,
223 errors: CredentialsLoadingErrors,
224 ) -> Self {
225 Self {
226 mapping,
227 credentials,
228 errors,
229 }
230 }
231
232 /// Creates a [`CredentialsLoading`] for the calling system user.
233 ///
234 /// Uses the data of the calling system user to derive the specific mapping for it from the
235 /// Signstar configuration (a [`HermeticParallelConfig`]).
236 /// Then continues to retrieve the credentials for all associated [`NetHsm`] users of the
237 /// mapping.
238 ///
239 /// # Errors
240 ///
241 /// Returns an error if
242 /// - it is not possible to derive user data from the calling process,
243 /// - if there is no user data for the calling process,
244 /// - the Signstar configuration file does not exist,
245 /// - it is not possible to load the Signstar configuration,
246 /// - not exactly one user mapping exists for the calling system user,
247 /// - or if credentials loading fails due to a severe error.
248 pub fn from_system_user() -> Result<Self, crate::Error> {
249 let user = get_current_system_user()?;
250
251 let system_config = load_config()?;
252
253 let mapping = system_config
254 .get_extended_mapping_for_user(&user.name)
255 .map_err(|source| crate::Error::Config(crate::config::Error::NetHsmConfig(source)))?;
256
257 // get all credentials for the mapping
258 let credentials_loading = mapping.load_credentials()?;
259
260 Ok(credentials_loading)
261 }
262
263 /// Returns the [`ExtendedUserMapping`].
264 pub fn get_mapping(&self) -> &ExtendedUserMapping {
265 &self.mapping
266 }
267
268 /// Returns all [`FullCredentials`].
269 pub fn get_credentials(&self) -> &[FullCredentials] {
270 &self.credentials
271 }
272
273 /// Returns a reference to a [`SystemUserId`].
274 ///
275 /// # Errors
276 ///
277 /// Returns an error if there is no system user in the tracked mapping.
278 pub fn get_system_user_id(&self) -> Result<&SystemUserId, crate::Error> {
279 match self.mapping.get_user_mapping().get_system_user() {
280 Some(system_user) => Ok(system_user),
281 None => Err(crate::Error::NonAdminSecretHandling(Error::NoSystemUser)),
282 }
283 }
284
285 /// Indicates whether there are any errors with [`UserId`]s.
286 ///
287 /// Returns `true` if there are errors, `false` otherwise.
288 pub fn has_userid_errors(&self) -> bool {
289 !self.errors.get_errors().is_empty()
290 }
291
292 /// Returns the collected errors for [`UserId`]s.
293 pub fn get_userid_errors(self) -> CredentialsLoadingErrors {
294 self.errors
295 }
296
297 /// Indicates whether the contained [`ExtendedUserMapping`] is that of a signing user.
298 pub fn has_signing_user(&self) -> bool {
299 matches!(
300 self.mapping.get_user_mapping(),
301 nethsm_config::UserMapping::SystemNetHsmOperatorSigning {
302 nethsm_user: _,
303 nethsm_key_setup: _,
304 ssh_authorized_key: _,
305 system_user: _,
306 tag: _,
307 }
308 )
309 }
310
311 /// Returns the credentials for a signing user.
312 ///
313 /// # Errors
314 ///
315 /// Returns an error if
316 /// - the tracked user is not a signing user
317 /// - errors occurred when loading the system user's credentials
318 /// - or there are no credentials for the system user.
319 pub fn credentials_for_signing_user(self) -> Result<FullCredentials, crate::Error> {
320 if !self.has_signing_user() {
321 return Err(crate::Error::NonAdminSecretHandling(Error::NotSigningUser));
322 }
323
324 if !self.errors.get_errors().is_empty() {
325 return Err(crate::Error::NonAdminSecretHandling(
326 Error::CredentialsLoading {
327 system_user: self.get_system_user_id()?.clone(),
328 errors: self.errors,
329 },
330 ));
331 }
332
333 if let Some(credentials) = self.credentials.first() {
334 Ok(credentials.clone())
335 } else {
336 return Err(crate::Error::NonAdminSecretHandling(
337 Error::CredentialsMissing {
338 system_user: self.get_system_user_id()?.clone(),
339 },
340 ));
341 }
342 }
343}
344
345/// A trait to implement loading of credentials, which includes reading of secrets.
346pub trait SecretsReader {
347 /// Loads credentials.
348 fn load_credentials(self) -> Result<CredentialsLoading, crate::Error>;
349}
350
351/// Checks the accessibility of a secrets file.
352///
353/// Checks whether file at `path`
354/// - exists,
355/// - is a file,
356/// - has accessible metadata,
357/// - and has the file mode [`SECRET_FILE_MODE`].
358///
359/// # Errors
360///
361/// Returns an error, if the file at `path`
362/// - does not exist,
363/// - is not a file,
364/// - does not have accessible metadata,
365/// - or has a file mode other than [`SECRET_FILE_MODE`].
366fn check_secrets_file(path: &Path) -> Result<(), crate::Error> {
367 // check if a path exists
368 if !path.exists() {
369 return Err(crate::Error::NonAdminSecretHandling(
370 Error::SecretsFileMissing {
371 path: path.to_path_buf(),
372 },
373 ));
374 }
375
376 // check if this is a file
377 if !path.is_file() {
378 return Err(crate::Error::NonAdminSecretHandling(
379 Error::SecretsFileNotAFile {
380 path: path.to_path_buf(),
381 },
382 ));
383 }
384
385 // check for correct permissions
386 match path.metadata() {
387 Ok(metadata) => {
388 let mode = metadata.permissions().mode();
389 if mode != SECRET_FILE_MODE {
390 return Err(crate::Error::NonAdminSecretHandling(
391 Error::SecretsFilePermissions {
392 path: path.to_path_buf(),
393 mode,
394 },
395 ));
396 }
397 }
398 Err(source) => {
399 return Err(crate::Error::NonAdminSecretHandling(
400 Error::SecretsFileMetadata {
401 path: path.to_path_buf(),
402 source,
403 },
404 ));
405 }
406 }
407
408 Ok(())
409}
410
411impl SecretsReader for ExtendedUserMapping {
412 /// Loads credentials for each [`UserId`] associated with a [`SystemUserId`].
413 ///
414 /// The [`SystemUserId`] of the mapping must be equal to the current system user calling this
415 /// function.
416 /// Relies on [`get_plaintext_secret_file`] and [`get_systemd_creds_secret_file`] to retrieve
417 /// the specific path to a secret file for each [`UserId`] mapped to a [`SystemUserId`].
418 ///
419 /// Returns a [`CredentialsLoading`], which may contain critical errors related to loading a
420 /// passphrase for each available [`UserId`].
421 /// The caller is expected to handle any errors tracked in the returned object based on context.
422 ///
423 /// # Errors
424 ///
425 /// Returns an error if
426 /// - the [`ExtendedUserMapping`] provides no [`SystemUserId`],
427 /// - no system user equal to the [`SystemUserId`] exists,
428 /// - the [`SystemUserId`] is not equal to the currently calling system user,
429 /// - or the [systemd-creds] command is not available when trying to decrypt secrets.
430 ///
431 /// [systemd-creds]: https://man.archlinux.org/man/systemd-creds.1
432 fn load_credentials(self) -> Result<CredentialsLoading, crate::Error> {
433 // Retrieve required SystemUserId and User and compare with current User.
434 let (system_user, user) = get_system_user_pair(&self)?;
435 let current_system_user = get_current_system_user()?;
436
437 // fail if running as root
438 fail_if_root(¤t_system_user)?;
439 match_current_system_user(¤t_system_user, &user)?;
440
441 let secret_handling = self.get_non_admin_secret_handling();
442 let mut credentials = Vec::new();
443 let mut errors = Vec::new();
444
445 for user_id in self.get_user_mapping().get_nethsm_users() {
446 let secrets_file = match secret_handling {
447 NonAdministrativeSecretHandling::Plaintext => {
448 get_plaintext_secret_file(system_user.as_ref(), &user_id.to_string())
449 }
450 NonAdministrativeSecretHandling::SystemdCreds => {
451 get_systemd_creds_secret_file(system_user.as_ref(), &user_id.to_string())
452 }
453 };
454 // Ensure the secrets file has correct ownership and permissions.
455 if let Err(error) = check_secrets_file(secrets_file.as_path()) {
456 errors.push(CredentialsLoadingError::new(user_id, error));
457 continue;
458 };
459
460 match secret_handling {
461 // Read from plaintext secrets file.
462 NonAdministrativeSecretHandling::Plaintext => {
463 // get passphrase or error
464 match read_to_string(&secrets_file)
465 .map_err(|source| Error::SecretsFileRead {
466 path: secrets_file,
467 source,
468 })
469 .map_err(crate::Error::NonAdminSecretHandling)
470 {
471 Ok(passphrase) => credentials
472 .push(FullCredentials::new(user_id, Passphrase::new(passphrase))),
473 Err(error) => {
474 errors.push(CredentialsLoadingError::new(user_id, error));
475 continue;
476 }
477 }
478 }
479 // Read from systemd-creds encrypted secrets file.
480 NonAdministrativeSecretHandling::SystemdCreds => {
481 // Decrypt secret using systemd-creds.
482 let creds_command = get_command("systemd-creds")?;
483 let mut command = Command::new(creds_command);
484 let command = command
485 .arg("--user")
486 .arg("decrypt")
487 .arg(&secrets_file)
488 .arg("-");
489 match command
490 .output()
491 .map_err(|source| crate::Error::CommandExec {
492 command: format!("{command:?}"),
493 source,
494 }) {
495 Ok(command_output) => {
496 // fail if decryption did not result in a successful status code
497 if !command_output.status.success() {
498 errors.push(CredentialsLoadingError::new(
499 user_id,
500 crate::Error::CommandNonZero {
501 command: format!("{command:?}"),
502 exit_status: command_output.status,
503 stderr: String::from_utf8_lossy(&command_output.stderr)
504 .into_owned(),
505 },
506 ));
507 continue;
508 }
509
510 let creds = match String::from_utf8(command_output.stdout) {
511 Ok(creds) => creds,
512 Err(source) => {
513 errors.push(CredentialsLoadingError::new(
514 user_id.clone(),
515 crate::Error::Utf8String {
516 path: secrets_file,
517 context: format!(
518 "converting stdout of {command:?} to string"
519 ),
520 source,
521 },
522 ));
523 continue;
524 }
525 };
526
527 credentials.push(FullCredentials::new(user_id, Passphrase::new(creds)));
528 }
529 Err(error) => {
530 errors.push(CredentialsLoadingError::new(user_id, error));
531 continue;
532 }
533 }
534 }
535 }
536 }
537
538 Ok(CredentialsLoading::new(
539 self,
540 credentials,
541 CredentialsLoadingErrors { errors },
542 ))
543 }
544}
545
546/// A trait to create non-administrative secrets and accompanying directories.
547pub trait SecretsWriter {
548 /// Creates secrets directories for all non-administrative mappings.
549 fn create_secrets_dir(&self) -> Result<(), crate::Error>;
550
551 /// Creates non-administrative secrets for all mappings of system users to backend users.
552 fn create_non_administrative_secrets(&self) -> Result<(), crate::Error>;
553}
554
555impl SecretsWriter for ExtendedUserMapping {
556 /// Creates secrets directories for all non-administrative mappings.
557 ///
558 /// Matches the [`SystemUserId`] in a mapping with an actual user on the system.
559 /// Creates the passphrase directory for the user and ensures correct ownership of it and all
560 /// parent directories up until the user's home directory.
561 ///
562 /// # Errors
563 ///
564 /// Returns an error if
565 /// - no system user is available in the mapping,
566 /// - the system user of the mapping is not available on the system,
567 /// - the directory could not be created,
568 /// - the ownership of any directory between the user's home and the passphrase directory can
569 /// not be changed.
570 fn create_secrets_dir(&self) -> Result<(), crate::Error> {
571 // Retrieve required SystemUserId and User and compare with current User.
572 let (system_user, user) = get_system_user_pair(self)?;
573
574 // fail if not running as root
575 fail_if_not_root(&get_current_system_user()?)?;
576
577 // get and create the user's passphrase directory
578 let secrets_dir = get_user_secrets_dir(system_user.as_ref());
579 create_dir_all(&secrets_dir).map_err(|source| Error::SecretsDirCreate {
580 path: secrets_dir.clone(),
581 system_user: system_user.clone(),
582 source,
583 })?;
584
585 // Recursively chown all directories to the user and group, until `HOME_BASE_DIR` is
586 // reached.
587 let home_dir = get_home_base_dir_path().join(PathBuf::from(system_user.as_ref()));
588 let mut chown_dir = secrets_dir.clone();
589 while chown_dir != home_dir {
590 chown(&chown_dir, Some(user.uid.as_raw()), Some(user.gid.as_raw())).map_err(
591 |source| crate::Error::Chown {
592 path: chown_dir.to_path_buf(),
593 user: system_user.to_string(),
594 source,
595 },
596 )?;
597 if let Some(parent) = &chown_dir.parent() {
598 chown_dir = parent.to_path_buf()
599 } else {
600 break;
601 }
602 }
603
604 Ok(())
605 }
606
607 /// Creates passphrases for all non-administrative mappings.
608 ///
609 /// Creates a random alphanumeric, 30-char long passphrase for each backend user of each
610 /// non-administrative user mapping.
611 ///
612 /// - If `self` is configured to use [`NonAdministrativeSecretHandling::Plaintext`], the
613 /// passphrase is stored in a secrets file, defined by [`get_plaintext_secret_file`].
614 /// - If `self` is configured to use [`NonAdministrativeSecretHandling::SystemdCreds`], the
615 /// passphrase is encrypted using [systemd-creds] and stored in a secrets file, defined by
616 /// [`get_systemd_creds_secret_file`].
617 ///
618 /// # Errors
619 ///
620 /// Returns an error if
621 /// - the targeted system user does not exist in the mapping or on the system,
622 /// - the function is called using a non-root user,
623 /// - the [systemd-creds] command is not available when trying to encrypt the passphrase,
624 /// - the encryption of the passphrase using [systemd-creds] fails,
625 /// - the secrets file can not be created,
626 /// - the secrets file can not be written to,
627 /// - or the ownership and permissions of the secrets file can not be changed.
628 ///
629 /// [systemd-creds]: https://man.archlinux.org/man/systemd-creds.1
630 fn create_non_administrative_secrets(&self) -> Result<(), crate::Error> {
631 // Retrieve required SystemUserId and User.
632 let (system_user, user) = get_system_user_pair(self)?;
633
634 // fail if not running as root
635 fail_if_not_root(&get_current_system_user()?)?;
636
637 let secret_handling = self.get_non_admin_secret_handling();
638
639 // add a secret for each NetHSM user
640 for user_id in self.get_user_mapping().get_nethsm_users() {
641 let secrets_file = match secret_handling {
642 NonAdministrativeSecretHandling::Plaintext => {
643 get_plaintext_secret_file(system_user.as_ref(), &user_id.to_string())
644 }
645 NonAdministrativeSecretHandling::SystemdCreds => {
646 get_systemd_creds_secret_file(system_user.as_ref(), &user_id.to_string())
647 }
648 };
649 println!(
650 "Create secret for system user {system_user} and backend user {user_id} in file: {secrets_file:?}"
651 );
652 let secret = {
653 // create initial (unencrypted) secret
654 let initial_secret: String = thread_rng()
655 .sample_iter(&Alphanumeric)
656 .take(30)
657 .map(char::from)
658 .collect();
659 // Create credentials files depending on secret handling
660 match secret_handling {
661 NonAdministrativeSecretHandling::Plaintext => {
662 initial_secret.as_bytes().to_vec()
663 }
664 NonAdministrativeSecretHandling::SystemdCreds => {
665 // Create systemd-creds encrypted secret.
666 let creds_command = get_command("systemd-creds")?;
667 let mut command = Command::new(creds_command);
668 let command = command
669 .arg("--user")
670 .arg("--name=")
671 .arg("--uid")
672 .arg(system_user.as_ref())
673 .arg("encrypt")
674 .arg("-")
675 .arg("-");
676 let mut command_child = command
677 .stdin(Stdio::piped())
678 .stdout(Stdio::piped())
679 .spawn()
680 .map_err(|source| crate::Error::CommandBackground {
681 command: format!("{command:?}"),
682 source,
683 })?;
684 let Some(mut stdin) = command_child.stdin.take() else {
685 return Err(crate::Error::CommandAttachToStdin {
686 command: format!("{command:?}"),
687 })?;
688 };
689
690 let system_user_thread = system_user.clone();
691 let handle = std::thread::spawn(move || {
692 stdin
693 .write_all(initial_secret.as_bytes())
694 .map_err(|source| crate::Error::CommandWriteToStdin {
695 command:
696 format!("systemd-creds --user --name= --uid {system_user_thread} encrypt - -"),
697 source,
698 })
699 });
700
701 let _handle_result = handle.join().map_err(|source| crate::Error::Thread {
702 context: format!(
703 "storing systemd-creds encrypted non-administrative secrets: {source:?}"
704 ),
705 })?;
706
707 let command_output =
708 command_child.wait_with_output().map_err(|source| {
709 crate::Error::CommandExec {
710 command: format!("{command:?}"),
711 source,
712 }
713 })?;
714
715 if !command_output.status.success() {
716 return Err(crate::Error::CommandNonZero {
717 command: format!("{command:?}"),
718 exit_status: command_output.status,
719 stderr: String::from_utf8_lossy(&command_output.stderr)
720 .into_owned(),
721 });
722 }
723 command_output.stdout
724 }
725 }
726 };
727
728 // Write secret to file and adjust permission and ownership of file.
729 let mut file = File::create(secrets_file.as_path()).map_err(|source| {
730 Error::SecretsFileCreate {
731 path: secrets_file.clone(),
732 system_user: system_user.clone(),
733 source,
734 }
735 })?;
736 file.write_all(&secret)
737 .map_err(|source| Error::SecretsFileWrite {
738 path: secrets_file.clone(),
739 system_user: system_user.clone(),
740 source,
741 })?;
742 chown(
743 &secrets_file,
744 Some(user.uid.as_raw()),
745 Some(user.gid.as_raw()),
746 )
747 .map_err(|source| crate::Error::Chown {
748 path: secrets_file.clone(),
749 user: system_user.to_string(),
750 source,
751 })?;
752 set_permissions(
753 secrets_file.as_path(),
754 Permissions::from_mode(SECRET_FILE_MODE),
755 )
756 .map_err(|source| crate::Error::ApplyPermissions {
757 path: secrets_file.clone(),
758 mode: SECRET_FILE_MODE,
759 source,
760 })?;
761 }
762 Ok(())
763 }
764}