1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// TODO: Stuff to add before this crate is ready....
4
//  - Test the absolute heck out of it.
5

            
6
// POSSIBLY TODO:
7
//  - Cache information across runs.
8

            
9
// @@ begin lint list maintained by maint/add_warning @@
10
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
11
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
12
#![warn(missing_docs)]
13
#![warn(noop_method_call)]
14
#![warn(unreachable_pub)]
15
#![warn(clippy::all)]
16
#![deny(clippy::await_holding_lock)]
17
#![deny(clippy::cargo_common_metadata)]
18
#![deny(clippy::cast_lossless)]
19
#![deny(clippy::checked_conversions)]
20
#![allow(clippy::cognitive_complexity)] // See arti#2556
21
#![deny(clippy::debug_assert_with_mut_call)]
22
#![deny(clippy::exhaustive_enums)]
23
#![deny(clippy::exhaustive_structs)]
24
#![deny(clippy::expl_impl_clone_on_copy)]
25
#![deny(clippy::fallible_impl_from)]
26
#![deny(clippy::implicit_clone)]
27
#![deny(clippy::large_stack_arrays)]
28
#![warn(clippy::manual_ok_or)]
29
#![deny(clippy::missing_docs_in_private_items)]
30
#![warn(clippy::needless_borrow)]
31
#![warn(clippy::needless_pass_by_value)]
32
#![warn(clippy::option_option)]
33
#![deny(clippy::print_stderr)]
34
#![deny(clippy::print_stdout)]
35
#![warn(clippy::rc_buffer)]
36
#![deny(clippy::ref_option_ref)]
37
#![warn(clippy::semicolon_if_nothing_returned)]
38
#![warn(clippy::trait_duplication_in_bounds)]
39
#![deny(clippy::unchecked_time_subtraction)]
40
#![deny(clippy::unnecessary_wraps)]
41
#![warn(clippy::unseparated_literal_suffix)]
42
#![deny(clippy::unwrap_used)]
43
#![deny(clippy::mod_module_files)]
44
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
45
#![allow(clippy::uninlined_format_args)]
46
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
47
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
48
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
49
#![allow(clippy::needless_lifetimes)] // See arti#1765
50
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
51
#![allow(clippy::collapsible_if)] // See arti#2342
52
#![deny(clippy::unused_async)]
53
#![deny(clippy::string_slice)] // See arti#2571
54
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
55
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
56

            
57
// This crate used to have unsafe code to interact with various libc functions.
58
// Nowadays we use pwd_grp, which is tested with miri.
59
// This #[forbid] assures us that we have removed all direct unsafe libc access.
60
//
61
// If this crate grows some other reason to want some unsafe, it is OK to remove this,
62
// subject to all the usual considerations when writing unsafe.
63
#![forbid(unsafe_code)]
64

            
65
mod dir;
66
mod disable;
67
mod err;
68
mod file_access;
69
mod imp;
70
#[cfg(all(
71
    target_family = "unix",
72
    not(target_os = "ios"),
73
    not(target_os = "android"),
74
    not(target_os = "tvos")
75
))]
76
mod user;
77

            
78
#[cfg(feature = "anon_home")]
79
pub mod anon_home;
80
#[cfg(test)]
81
pub(crate) mod testing;
82
pub mod walk;
83

            
84
#[cfg(feature = "serde")]
85
use serde::{Deserialize, Serialize};
86
use std::{
87
    fs::DirBuilder,
88
    path::{Path, PathBuf},
89
    sync::Arc,
90
};
91

            
92
pub use dir::CheckedDir;
93
pub use disable::GLOBAL_DISABLE_VAR;
94
pub use err::{Error, format_access_bits};
95
pub use file_access::FileAccess;
96

            
97
/// A result type as returned by this crate
98
pub type Result<T> = std::result::Result<T, Error>;
99

            
100
#[cfg(all(
101
    target_family = "unix",
102
    not(target_os = "ios"),
103
    not(target_os = "android"),
104
    not(target_os = "tvos")
105
))]
106
pub use user::{TrustedGroup, TrustedUser};
107

            
108
/// Configuration for verifying that a file or directory is really "private".
109
///
110
/// By default, we mistrust everything that we can: we assume  that every
111
/// directory on the filesystem is potentially misconfigured.  This object can
112
/// be used to change that.
113
///
114
/// Once you have a working [`Mistrust`], you can call its "`check_*`" methods
115
/// directly, or use [`verifier()`](Mistrust::verifier) to configure a more
116
/// complicated check.
117
///  
118
/// See the [crate documentation](crate) for more information.
119
///
120
/// # Environment variables
121
///
122
/// The [`Mistrust`] can be configured to consider an environment variable.
123
/// See [`MistrustBuilder::controlled_by_default_env_var`] and similar methods.
124
///
125
/// Names that seem to say "don't disable" are treated as "false". Any
126
/// other value is treated as "true".  (That is, we err on the side of
127
/// assuming that if you set a disable variable, you meant to disable.)
128
///
129
/// If the `Mistrust` is configured to use an environment variable,
130
/// this environment variable typically becomes part of the application's public interface,
131
/// so this library commits to a stable behaviour for parsing these variables.
132
/// Specifically the following case-insensitive strings are considered "false":
133
/// "false", "no", "never", "n", "0", "".
134
///
135
/// Examples using the default environment variable:
136
///
137
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS="false"` — checks enabled
138
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS=" false "` — checks enabled
139
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS="NO"` — checks enabled
140
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS=0` — checks enabled
141
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS=` — checks enabled
142
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS=" "` — checks enabled
143
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS="true"` — checks disabled
144
/// - `FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS="asdf"` — checks disabled
145
///
146
/// # TODO
147
///
148
/// *  support more kinds of trust configuration, including more trusted users,
149
///    trusted groups, multiple trusted directories, etc?
150
#[derive(Debug, Clone, derive_builder::Builder, Eq, PartialEq)]
151
#[cfg_attr(feature = "serde", builder(derive(Debug, Serialize, Deserialize)))]
152
#[cfg_attr(not(feature = "serde"), builder(derive(Debug)))]
153
#[builder(build_fn(error = "Error"))]
154
#[cfg_attr(feature = "serde", builder_struct_attr(serde(default)))]
155
pub struct Mistrust {
156
    /// If the user called [`MistrustBuilder::ignore_prefix`], what did they give us?
157
    ///
158
    /// (This is stored in canonical form.)
159
    #[builder(
160
        setter(into, strip_option),
161
        field(build = "canonicalize_opt_prefix(&self.ignore_prefix)?")
162
    )]
163
    ignore_prefix: Option<PathBuf>,
164

            
165
    /// Are we configured to disable all permission and ownership tests?
166
    ///
167
    /// (This field is present in the builder only.)
168
    #[builder(setter(custom), field(type = "Option<bool>", build = "()"))]
169
    dangerously_trust_everyone: (),
170

            
171
    /// Should we check the environment to decide whether to disable permission
172
    /// and ownership tests?
173
    ///
174
    /// (This field is present in the builder only.)
175
    #[builder(setter(custom), field(type = "Option<disable::Disable>", build = "()"))]
176
    #[cfg_attr(feature = "serde", builder_field_attr(serde(skip)))]
177
    disable_by_environment: (),
178

            
179
    /// Internal value combining `dangerously_trust_everyone` and
180
    /// `disable_by_environment` to decide whether we're doing permissions
181
    /// checks or not.
182
    #[builder(setter(custom), field(build = "self.should_be_enabled()"))]
183
    #[cfg_attr(feature = "serde", builder_field_attr(serde(skip)))]
184
    status: disable::Status,
185

            
186
    /// What user ID do we trust by default (if any?)
187
    #[cfg(all(
188
        target_family = "unix",
189
        not(target_os = "ios"),
190
        not(target_os = "android"),
191
        not(target_os = "tvos")
192
    ))]
193
    #[builder(
194
        setter(into),
195
        field(type = "TrustedUser", build = "self.trust_user.get_uid()?")
196
    )]
197
    trust_user: Option<u32>,
198

            
199
    /// What group ID do we trust by default (if any?)
200
    #[cfg(all(
201
        target_family = "unix",
202
        not(target_os = "ios"),
203
        not(target_os = "android"),
204
        not(target_os = "tvos")
205
    ))]
206
    #[builder(
207
        setter(into),
208
        field(type = "TrustedGroup", build = "self.trust_group.get_gid()?")
209
    )]
210
    trust_group: Option<u32>,
211
}
212

            
213
/// Compute the canonical prefix for a given path prefix.
214
///
215
/// The funny types here are used to please derive_builder.
216
#[allow(clippy::option_option)]
217
56970
fn canonicalize_opt_prefix(prefix: &Option<Option<PathBuf>>) -> Result<Option<PathBuf>> {
218
29592
    match prefix {
219
29592
        Some(Some(path)) if path.as_os_str().is_empty() => Ok(None),
220
29452
        Some(Some(path)) => Ok(Some(
221
29452
            path.canonicalize()
222
29452
                .map_err(|e| Error::inspecting(e, path))?,
223
        )),
224
27378
        _ => Ok(None),
225
    }
226
    // TODO: Permit "not found?" .
227
56970
}
228

            
229
impl MistrustBuilder {
230
    /// Configure this `Mistrust` to trust only the admin (root) user.
231
    ///
232
    /// By default, both the currently running user and the root user will be
233
    /// trusted.
234
    ///
235
    /// This option disables the default group-trust behavior as well.
236
    #[cfg(all(
237
        target_family = "unix",
238
        not(target_os = "ios"),
239
        not(target_os = "android"),
240
        not(target_os = "tvos"),
241
    ))]
242
    pub fn trust_admin_only(&mut self) -> &mut Self {
243
        self.trust_user = TrustedUser::None;
244
        self.trust_group = TrustedGroup::None;
245
        self
246
    }
247

            
248
    /// Configure this `Mistrust` to trust no groups at all.
249
    ///
250
    /// By default, we trust the group (if any) with the same name as the
251
    /// current user if we are currently running as a member of that group.
252
    ///
253
    /// With this option set, no group is trusted, and any group-readable or
254
    /// group-writable objects are treated the same as world-readable and
255
    /// world-writable objects respectively.
256
    #[cfg(all(
257
        target_family = "unix",
258
        not(target_os = "ios"),
259
        not(target_os = "android"),
260
        not(target_os = "tvos"),
261
    ))]
262
10
    pub fn trust_no_group_id(&mut self) -> &mut Self {
263
10
        self.trust_group = TrustedGroup::None;
264
10
        self
265
10
    }
266

            
267
    /// Configure this `Mistrust` to trust every user and every group.
268
    ///
269
    /// With this option set, every file and directory is treated as having
270
    /// valid permissions: even world-writeable files are allowed.  File-type
271
    /// checks are still performed.
272
    ///
273
    /// This option is mainly useful to handle cases where you want to make
274
    /// these checks optional, and still use [`CheckedDir`] without having to
275
    /// implement separate code paths for the "checking on" and "checking off"
276
    /// cases.
277
    ///
278
    /// Setting this flag will supersede any value set in the environment.
279
6234
    pub fn dangerously_trust_everyone(&mut self) -> &mut Self {
280
6234
        self.dangerously_trust_everyone = Some(true);
281
6234
        self
282
6234
    }
283

            
284
    /// Remove any ignored prefix, restoring this [`MistrustBuilder`] to a state
285
    /// as if [`MistrustBuilder::ignore_prefix`] had not been called.
286
    pub fn remove_ignored_prefix(&mut self) -> &mut Self {
287
        self.ignore_prefix = Some(None);
288
        self
289
    }
290

            
291
    /// Configure this [`MistrustBuilder`] to become disabled based on the
292
    /// environment variable `var`.
293
    ///
294
    /// See [`Mistrust`](Mistrust#environment-variables) for details about
295
    /// the handling of the environment variable.
296
    ///
297
    /// If `var` is not set, then we'll look at
298
    /// `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS`.
299
14280
    pub fn controlled_by_env_var(&mut self, var: &str) -> &mut Self {
300
14280
        self.disable_by_environment = Some(disable::Disable::OnUserEnvVar(var.to_string()));
301
14280
        self
302
14280
    }
303

            
304
    /// Like `controlled_by_env_var`, but do not override any previously set
305
    /// environment settings.
306
    ///
307
    /// See [`Mistrust`](Mistrust#environment-variables) for details about
308
    /// the handling of the environment variable.
309
    ///
310
    /// (The `arti-client` wants this, so that it can inform a caller-supplied
311
    /// `MistrustBuilder` about its Arti-specific env var, but only if the
312
    /// caller has not already provided a variable of its own. Other code
313
    /// embedding `fs-mistrust` may want it too.)
314
14280
    pub fn controlled_by_env_var_if_not_set(&mut self, var: &str) -> &mut Self {
315
14280
        if self.disable_by_environment.is_none() {
316
14280
            self.controlled_by_env_var(var)
317
        } else {
318
            self
319
        }
320
14280
    }
321

            
322
    /// Configure this [`MistrustBuilder`] to become disabled based on the
323
    /// environment variable `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS` only,
324
    ///
325
    /// See [`Mistrust`](Mistrust#environment-variables) for details about
326
    /// the handling of the environment variable.
327
    ///
328
    /// This is the default.
329
    pub fn controlled_by_default_env_var(&mut self) -> &mut Self {
330
        self.disable_by_environment = Some(disable::Disable::OnGlobalEnvVar);
331
        self
332
    }
333

            
334
    /// Configure this [`MistrustBuilder`] to never consult the environment to
335
    /// see whether it should be disabled.
336
    pub fn ignore_environment(&mut self) -> &mut Self {
337
        self.disable_by_environment = Some(disable::Disable::Never);
338
        self
339
    }
340

            
341
    /// Considering our settings, determine whether we should trust all users
342
    /// (and thereby disable our permission checks.)
343
27764
    fn should_be_enabled(&self) -> disable::Status {
344
        // If we've disabled checks in our configuration, then that settles it.
345
27764
        if self.dangerously_trust_everyone == Some(true) {
346
9524
            return disable::Status::DisableChecks;
347
18240
        }
348

            
349
        // Otherwise, we use our "disable_by_environment" setting to see whether
350
        // we should check the environment.
351
18240
        self.disable_by_environment
352
18240
            .as_ref()
353
18240
            .unwrap_or(&disable::Disable::default())
354
18240
            .should_disable_checks()
355
27764
    }
356

            
357
    /// Fill in any values for this `MistrustBuilder` that have defaults,
358
    /// and are not already set.
359
    ///
360
    /// It is not necessary to call this method if you're just planning
361
    /// to build a [`Mistrust`]; you only need it if you are going to
362
    /// re-serialize the builder and you want to make the defaults explicit.
363
210
    pub fn apply_defaults(&mut self) -> std::result::Result<(), void::Void> {
364
210
        self.dangerously_trust_everyone.get_or_insert_default();
365
210
        self.disable_by_environment.get_or_insert_default();
366
210
        Ok(())
367
210
    }
368
}
369

            
370
impl Default for Mistrust {
371
7354
    fn default() -> Self {
372
7354
        MistrustBuilder::default()
373
7354
            .build()
374
7354
            .expect("Could not build default")
375
7354
    }
376
}
377

            
378
/// An object used to perform a single check.
379
///
380
/// Obtained from [`Mistrust::verifier()`].
381
///
382
/// A `Verifier` is used when [`Mistrust::check_directory`] and
383
/// [`Mistrust::make_directory`] are not sufficient for your needs.
384
#[derive(Clone, Debug)]
385
#[must_use]
386
pub struct Verifier<'a> {
387
    /// The [`Mistrust`] that was used to create this verifier.
388
    mistrust: &'a Mistrust,
389

            
390
    /// Has the user called [`Verifier::permit_readable`]?
391
    readable_okay: bool,
392

            
393
    /// Has the user called [`Verifier::all_errors`]?
394
    collect_multiple_errors: bool,
395

            
396
    /// If the user called [`Verifier::require_file`] or
397
    /// [`Verifier::require_directory`], which did they call?
398
    enforce_type: Type,
399

            
400
    /// If true, we want to check all the contents of this directory as well as
401
    /// the directory itself.  Requires the `walkdir` feature.
402
    check_contents: bool,
403
}
404

            
405
/// A type of object that we have been told to require.
406
#[derive(Debug, Clone, Copy)]
407
enum Type {
408
    /// A directory.
409
    Dir,
410
    /// A regular file.
411
    File,
412
    /// A directory or a regular file.
413
    DirOrFile,
414
    /// Absolutely anything at all.
415
    Anything,
416
}
417

            
418
impl Mistrust {
419
    /// Return a new [`MistrustBuilder`].
420
6130
    pub fn builder() -> MistrustBuilder {
421
6130
        MistrustBuilder::default()
422
6130
    }
423

            
424
    /// Initialize a new default `Mistrust`.
425
    ///
426
    /// By default:
427
    ///    *  we will inspect all directories that are used to resolve any path that is checked.
428
    pub fn new() -> Self {
429
        Self::default()
430
    }
431

            
432
    /// Construct a new `Mistrust` that trusts all users and all groups.
433
    ///
434
    /// (In effect, this `Mistrust` will have all of its permissions checks
435
    /// disabled, since if all users and groups are trusted, it doesn't matter
436
    /// what the permissions on any file and directory are.)
437
4550
    pub fn new_dangerously_trust_everyone() -> Self {
438
4550
        Self::builder()
439
4550
            .dangerously_trust_everyone()
440
4550
            .build()
441
4550
            .expect("Could not construct a Mistrust")
442
4550
    }
443

            
444
    /// Create a new [`Verifier`] with this configuration, to perform a single check.
445
220556
    pub fn verifier(&self) -> Verifier<'_> {
446
220556
        Verifier {
447
220556
            mistrust: self,
448
220556
            readable_okay: false,
449
220556
            collect_multiple_errors: false,
450
220556
            enforce_type: Type::DirOrFile,
451
220556
            check_contents: false,
452
220556
        }
453
220556
    }
454

            
455
    /// Verify that `dir` is a directory that only trusted users can read from,
456
    /// list the files in,  or write to.
457
    ///
458
    /// If it is, and we can verify that, return `Ok(())`.  Otherwise, return
459
    /// the first problem that we encountered when verifying it.
460
    ///
461
    /// `m.check_directory(dir)` is equivalent to
462
    /// `m.verifier().require_directory().check(dir)`.  If you need different
463
    /// behavior, see [`Verifier`] for more options.
464
28
    pub fn check_directory<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
465
28
        self.verifier().require_directory().check(dir)
466
28
    }
467

            
468
    /// As `check_directory`, but create the directory if needed.
469
    ///
470
    /// `m.check_directory(dir)` is equivalent to
471
    /// `m.verifier().make_directory(dir)`.  If you need different behavior, see
472
    /// [`Verifier`] for more options.
473
14
    pub fn make_directory<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
474
14
        self.verifier().make_directory(dir)
475
14
    }
476

            
477
    /// Return true if this `Mistrust` object has been configured to trust all
478
    /// users.
479
193584
    pub(crate) fn is_disabled(&self) -> bool {
480
193584
        self.status.disabled()
481
193584
    }
482

            
483
    /// Create a new [`FileAccess`] for reading or writing files
484
    /// while enforcing the rules of this `Mistrust`.
485
154
    pub fn file_access(&self) -> FileAccess<'_> {
486
154
        self.verifier().file_access()
487
154
    }
488
}
489

            
490
impl<'a> Verifier<'a> {
491
    /// Create a new [`FileAccess`] for reading or writing files
492
    /// while enforcing the rules of this `Verifier`.
493
574
    pub fn file_access(self) -> FileAccess<'a> {
494
574
        FileAccess::from_verifier(self)
495
574
    }
496

            
497
    /// Configure this `Verifier` to require that all paths it checks be
498
    /// files (not directories).
499
3506
    pub fn require_file(mut self) -> Self {
500
3506
        self.enforce_type = Type::File;
501
3506
        self
502
3506
    }
503

            
504
    /// Configure this `Verifier` to require that all paths it checks be
505
    /// directories.
506
39950
    pub fn require_directory(mut self) -> Self {
507
39950
        self.enforce_type = Type::Dir;
508
39950
        self
509
39950
    }
510

            
511
    /// Configure this `Verifier` to allow the paths that it checks to be
512
    /// filesystem objects of any type.
513
    ///
514
    /// By default, the final path (after resolving all links) must be a
515
    /// directory or a regular file, not (for example) a block device or a named
516
    /// pipe.
517
    pub fn permit_all_object_types(mut self) -> Self {
518
        self.enforce_type = Type::Anything;
519
        self
520
    }
521

            
522
    /// Configure this `Verifier` to permit the target files/directory to be
523
    /// _readable_ by untrusted users.
524
    ///
525
    /// By default, we assume that the caller wants the target file or directory
526
    /// to be only readable or writable by trusted users.  With this flag, we
527
    /// permit the target file or directory to be readable by untrusted users,
528
    /// but not writable.
529
    ///
530
    /// (Note that we always allow the _parent directories_ of the target to be
531
    /// readable by untrusted users, since their readability does not make the
532
    /// target readable.)
533
12044
    pub fn permit_readable(mut self) -> Self {
534
12044
        self.readable_okay = true;
535
12044
        self
536
12044
    }
537

            
538
    /// Tell this `Verifier` to accumulate as many errors as possible, rather
539
    /// than stopping at the first one.
540
    ///
541
    /// If a single error is found, that error will be returned.  Otherwise, the
542
    /// resulting error type will be [`Error::Multiple`].
543
    ///
544
    /// # Example
545
    ///
546
    /// ```
547
    /// # use fs_mistrust::Mistrust;
548
    /// if let Err(e) = Mistrust::new().verifier().all_errors().check("/home/gardenGnostic/.gnupg/") {
549
    ///    for error in e.errors() {
550
    ///       println!("{}", e)
551
    ///    }
552
    /// }
553
    /// ```
554
6
    pub fn all_errors(mut self) -> Self {
555
6
        self.collect_multiple_errors = true;
556
6
        self
557
6
    }
558

            
559
    /// Configure this verifier so that, after checking the directory, check all
560
    /// of its contents.
561
    ///
562
    /// Symlinks are not permitted; both files and directories are allowed. This
563
    /// option implies `require_directory()`, since only a directory can have
564
    /// contents.
565
    ///
566
    /// Requires that the `walkdir` feature is enabled.
567
    #[cfg(feature = "walkdir")]
568
10362
    pub fn check_content(mut self) -> Self {
569
10362
        self.check_contents = true;
570
10362
        self.require_directory()
571
10362
    }
572

            
573
    /// Check whether the file or directory at `path` conforms to the
574
    /// requirements of this `Verifier` and the [`Mistrust`] that created it.
575
175296
    pub fn check<P: AsRef<Path>>(&self, path: P) -> Result<()> {
576
175296
        let path = path.as_ref();
577

            
578
        // This is the powerhouse of our verifier code:
579
        //
580
        // See the `imp` module for actual implementation logic.
581
175296
        let mut error_iterator = self
582
175296
            .check_errors(path.as_ref())
583
175296
            .chain(self.check_content_errors(path.as_ref()));
584

            
585
        // Collect either the first error, or all errors.
586
175296
        let opt_error: Option<Error> = if self.collect_multiple_errors {
587
6
            error_iterator.collect()
588
        } else {
589
175290
            let next = error_iterator.next();
590
175290
            drop(error_iterator); // so that "canonical" is no longer borrowed.
591
175290
            next
592
        };
593

            
594
175296
        if let Some(err) = opt_error {
595
22242
            return Err(err);
596
153054
        }
597

            
598
153054
        Ok(())
599
175296
    }
600
    /// Check whether `path` is a valid directory, and create it if it doesn't
601
    /// exist.
602
    ///
603
    /// Returns `Ok` if the directory already existed or if it was just created,
604
    /// and it conforms to the requirements of this `Verifier` and the
605
    /// [`Mistrust`] that created it.
606
    ///
607
    /// Return an error if:
608
    ///  * there was a permissions or ownership problem in the path or any of
609
    ///    its ancestors,
610
    ///  * there was a problem when creating the directory
611
    ///  * after creating the directory, we found that it had a permissions or
612
    ///    ownership problem.
613
15820
    pub fn make_directory<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
614
15820
        self.enforce_type = Type::Dir;
615

            
616
15820
        let path = path.as_ref();
617
15820
        match self.clone().check(path) {
618
7983
            Err(Error::NotFound(_)) => {}
619
57
            Err(other_error) => return Err(other_error),
620
7780
            Ok(()) => return Ok(()), // no error; file exists.
621
        }
622

            
623
        // Looks like we got a "not found", so we're creating the path.
624
7983
        let mut bld = DirBuilder::new();
625
        #[cfg(target_family = "unix")]
626
        {
627
            use std::os::unix::fs::DirBuilderExt;
628
7983
            bld.mode(0o700);
629
        }
630
7983
        bld.recursive(true)
631
7983
            .create(path)
632
7983
            .map_err(|e| Error::CreatingDir(Arc::new(e)))?;
633

            
634
        // We built the path!  But for paranoia's sake, check it again.
635
7983
        self.check(path)
636
15820
    }
637

            
638
    /// Check whether `path` is a directory conforming to the requirements of
639
    /// this `Verifier` and the [`Mistrust`] that created it.
640
    ///
641
    /// If it is, then return a new [`CheckedDir`] that can be used to securely access
642
    /// the contents of this directory.  
643
3808
    pub fn secure_dir<P: AsRef<Path>>(self, path: P) -> Result<CheckedDir> {
644
3808
        let path = path.as_ref();
645
3808
        self.clone().require_directory().check(path)?;
646
3753
        CheckedDir::new(&self, path)
647
3808
    }
648

            
649
    /// Check whether `path` is a directory conforming to the requirements of
650
    /// this `Verifier` and the [`Mistrust`] that created it.
651
    ///
652
    /// If successful, then return a new [`CheckedDir`] that can be used to
653
    /// securely access the contents of this directory.  
654
4213
    pub fn make_secure_dir<P: AsRef<Path>>(self, path: P) -> Result<CheckedDir> {
655
4213
        let path = path.as_ref();
656
4213
        self.clone().require_directory().make_directory(path)?;
657
4211
        CheckedDir::new(&self, path)
658
4213
    }
659
}
660

            
661
#[cfg(test)]
662
mod test {
663
    // @@ begin test lint list maintained by maint/add_warning @@
664
    #![allow(clippy::bool_assert_comparison)]
665
    #![allow(clippy::clone_on_copy)]
666
    #![allow(clippy::dbg_macro)]
667
    #![allow(clippy::mixed_attributes_style)]
668
    #![allow(clippy::print_stderr)]
669
    #![allow(clippy::print_stdout)]
670
    #![allow(clippy::single_char_pattern)]
671
    #![allow(clippy::unwrap_used)]
672
    #![allow(clippy::unchecked_time_subtraction)]
673
    #![allow(clippy::useless_vec)]
674
    #![allow(clippy::needless_pass_by_value)]
675
    #![allow(clippy::string_slice)] // See arti#2571
676
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
677
    use super::*;
678
    use assert_matches::assert_matches;
679
    use testing::{Dir, MistrustOp, mistrust_build};
680

            
681
    #[cfg(target_family = "unix")]
682
    use testing::LinkType;
683

            
684
    #[cfg(target_family = "unix")]
685
    #[test]
686
    fn simple_cases() {
687
        let d = Dir::new();
688
        d.dir("a/b/c");
689
        d.dir("e/f/g");
690
        d.chmod("a", 0o755);
691
        d.chmod("a/b", 0o755);
692
        d.chmod("a/b/c", 0o700);
693
        d.chmod("e", 0o755);
694
        d.chmod("e/f", 0o777);
695
        d.link_rel(LinkType::Dir, "a/b/c", "d");
696

            
697
        let m = mistrust_build(&[
698
            MistrustOp::IgnorePrefix(d.canonical_root()),
699
            MistrustOp::TrustNoGroupId(),
700
        ]);
701

            
702
        // /a/b/c should be fine...
703
        m.check_directory(d.path("a/b/c")).unwrap();
704
        // /e/f/g should not.
705
        let e = m.check_directory(d.path("e/f/g")).unwrap_err();
706
        assert!(matches!(e, Error::BadPermission(_, 0o777, 0o022)));
707
        assert_eq!(e.path().unwrap(), d.path("e/f").canonicalize().unwrap());
708

            
709
        m.check_directory(d.path("d")).unwrap();
710
    }
711

            
712
    #[cfg(target_family = "unix")]
713
    #[test]
714
    fn admin_only() {
715
        use std::os::unix::prelude::MetadataExt;
716

            
717
        let d = Dir::new();
718
        d.dir("a/b");
719
        d.chmod("a", 0o700);
720
        d.chmod("a/b", 0o700);
721

            
722
        if d.path("a/b").metadata().unwrap().uid() == 0 {
723
            // Nothing to do here; we _are_ root.
724
            return;
725
        }
726

            
727
        // With normal settings should be okay...
728
        let m = mistrust_build(&[MistrustOp::IgnorePrefix(d.canonical_root())]);
729
        m.check_directory(d.path("a/b")).unwrap();
730

            
731
        // With admin_only, it'll fail.
732
        let m = mistrust_build(&[
733
            MistrustOp::IgnorePrefix(d.canonical_root()),
734
            MistrustOp::TrustAdminOnly(),
735
        ]);
736

            
737
        let err = m.check_directory(d.path("a/b")).unwrap_err();
738
        assert!(matches!(err, Error::BadOwner(_, _)));
739
        assert_eq!(err.path().unwrap(), d.path("a").canonicalize().unwrap());
740
    }
741

            
742
    #[test]
743
    fn want_type() {
744
        let d = Dir::new();
745
        d.dir("a");
746
        d.file("b");
747
        d.chmod("a", 0o700);
748
        d.chmod("b", 0o600);
749

            
750
        let m = mistrust_build(&[
751
            MistrustOp::IgnorePrefix(d.canonical_root()),
752
            MistrustOp::TrustNoGroupId(),
753
        ]);
754

            
755
        // If we insist stuff is its own type, it works fine.
756
        m.verifier().require_directory().check(d.path("a")).unwrap();
757
        m.verifier().require_file().check(d.path("b")).unwrap();
758

            
759
        // If we insist on a different type, we hit an error.
760
        let e = m
761
            .verifier()
762
            .require_directory()
763
            .check(d.path("b"))
764
            .unwrap_err();
765
        assert!(matches!(e, Error::BadType(_)));
766
        assert_eq!(e.path().unwrap(), d.path("b").canonicalize().unwrap());
767

            
768
        let e = m.verifier().require_file().check(d.path("a")).unwrap_err();
769
        assert!(matches!(e, Error::BadType(_)));
770
        assert_eq!(e.path().unwrap(), d.path("a").canonicalize().unwrap());
771

            
772
        // TODO: Possibly, make sure that a special file matches neither.
773
    }
774

            
775
    #[cfg(target_family = "unix")]
776
    #[test]
777
    fn readable_ok() {
778
        let d = Dir::new();
779
        d.dir("a/b");
780
        d.file("a/b/c");
781
        d.chmod("a", 0o750);
782
        d.chmod("a/b", 0o750);
783
        d.chmod("a/b/c", 0o640);
784

            
785
        let m = mistrust_build(&[
786
            MistrustOp::IgnorePrefix(d.canonical_root()),
787
            MistrustOp::TrustNoGroupId(),
788
        ]);
789

            
790
        // These will fail, since the file or directory is readable.
791
        let e = m.verifier().check(d.path("a/b")).unwrap_err();
792
        assert!(matches!(e, Error::BadPermission(..)));
793
        assert_eq!(e.path().unwrap(), d.path("a/b").canonicalize().unwrap());
794
        let e = m.verifier().check(d.path("a/b/c")).unwrap_err();
795
        assert!(matches!(e, Error::BadPermission(..)));
796
        assert_eq!(e.path().unwrap(), d.path("a/b/c").canonicalize().unwrap());
797

            
798
        // Now allow readable targets.
799
        m.verifier().permit_readable().check(d.path("a/b")).unwrap();
800
        m.verifier()
801
            .permit_readable()
802
            .check(d.path("a/b/c"))
803
            .unwrap();
804
    }
805

            
806
    #[cfg(target_family = "unix")]
807
    #[test]
808
    fn multiple_errors() {
809
        let d = Dir::new();
810
        d.dir("a/b");
811
        d.chmod("a", 0o700);
812
        d.chmod("a/b", 0o700);
813

            
814
        let m = mistrust_build(&[
815
            MistrustOp::IgnorePrefix(d.canonical_root()),
816
            MistrustOp::TrustNoGroupId(),
817
        ]);
818

            
819
        // Only one error occurs, so we get that error.
820
        let e = m
821
            .verifier()
822
            .all_errors()
823
            .check(d.path("a/b/c"))
824
            .unwrap_err();
825
        assert!(matches!(e, Error::NotFound(_)));
826
        assert_eq!(1, e.errors().count());
827

            
828
        // Introduce a second error...
829
        d.chmod("a/b", 0o770);
830
        let e = m
831
            .verifier()
832
            .all_errors()
833
            .check(d.path("a/b/c"))
834
            .unwrap_err();
835
        assert!(matches!(e, Error::Multiple(_)));
836
        let errs: Vec<_> = e.errors().collect();
837
        assert_eq!(2, errs.len());
838
        assert!(matches!(&errs[0], Error::BadPermission(..)));
839
        assert!(matches!(&errs[1], Error::NotFound(_)));
840
    }
841

            
842
    #[cfg(target_family = "unix")]
843
    #[test]
844
    fn sticky() {
845
        let d = Dir::new();
846
        d.dir("a/b/c");
847
        d.chmod("a", 0o777);
848
        d.chmod("a/b", 0o755);
849
        d.chmod("a/b/c", 0o700);
850

            
851
        let m = mistrust_build(&[MistrustOp::IgnorePrefix(d.canonical_root())]);
852

            
853
        // `a` is world-writable, so the first check will fail.
854
        m.check_directory(d.path("a/b/c")).unwrap_err();
855

            
856
        // Now `a` is world-writable _and_ sticky, so the check should succeed.
857
        d.chmod("a", 0o777 | crate::imp::STICKY_BIT);
858

            
859
        m.check_directory(d.path("a/b/c")).unwrap();
860

            
861
        // Make sure we got the right definition!
862
        #[allow(clippy::useless_conversion)]
863
        {
864
            assert_eq!(crate::imp::STICKY_BIT, u32::from(libc::S_ISVTX));
865
        }
866
    }
867

            
868
    #[cfg(target_family = "unix")]
869
    #[test]
870
    fn trust_gid() {
871
        use std::os::unix::prelude::MetadataExt;
872
        let d = Dir::new();
873
        d.dir("a/b");
874
        d.chmod("a", 0o770);
875
        d.chmod("a/b", 0o770);
876

            
877
        let m = mistrust_build(&[
878
            MistrustOp::IgnorePrefix(d.canonical_root()),
879
            MistrustOp::TrustNoGroupId(),
880
        ]);
881

            
882
        // By default, we shouldn't be accept this directory, since it is
883
        // group-writable.
884
        let e = m.check_directory(d.path("a/b")).unwrap_err();
885
        assert!(matches!(e, Error::BadPermission(..)));
886

            
887
        // But we can make the group trusted, which will make it okay for the
888
        // directory to be group-writable.
889
        let gid = d.path("a/b").metadata().unwrap().gid();
890

            
891
        let m = mistrust_build(&[
892
            MistrustOp::IgnorePrefix(d.canonical_root()),
893
            MistrustOp::TrustGroup(gid),
894
        ]);
895

            
896
        m.check_directory(d.path("a/b")).unwrap();
897

            
898
        // OTOH, if we made a _different_ group trusted, it'll fail.
899
        let m = mistrust_build(&[
900
            MistrustOp::IgnorePrefix(d.canonical_root()),
901
            MistrustOp::TrustGroup(gid ^ 1),
902
        ]);
903

            
904
        let e = m.check_directory(d.path("a/b")).unwrap_err();
905
        assert!(matches!(e, Error::BadPermission(..)));
906
    }
907

            
908
    #[test]
909
    fn make_directory() {
910
        let d = Dir::new();
911
        d.dir("a/b");
912

            
913
        let m = mistrust_build(&[MistrustOp::IgnorePrefix(d.canonical_root())]);
914

            
915
        #[cfg(target_family = "unix")]
916
        {
917
            // Try once with bad permissions.
918
            d.chmod("a", 0o777);
919
            let e = m.make_directory(d.path("a/b/c/d")).unwrap_err();
920
            assert!(matches!(e, Error::BadPermission(..)));
921

            
922
            // Now make the permissions correct.
923
            d.chmod("a", 0o0700);
924
            d.chmod("a/b", 0o0700);
925
        }
926

            
927
        // Make the directory!
928
        m.make_directory(d.path("a/b/c/d")).unwrap();
929

            
930
        // Make sure it exists and has good permissions.
931
        m.check_directory(d.path("a/b/c/d")).unwrap();
932

            
933
        // Try make_directory again and make sure _that_ succeeds.
934
        m.make_directory(d.path("a/b/c/d")).unwrap();
935
    }
936

            
937
    #[cfg(target_family = "unix")]
938
    #[cfg(feature = "walkdir")]
939
    #[test]
940
    fn check_contents() {
941
        let d = Dir::new();
942
        d.dir("a/b/c");
943
        d.file("a/b/c/d");
944
        d.chmod("a", 0o700);
945
        d.chmod("a/b", 0o700);
946
        d.chmod("a/b/c", 0o755);
947
        d.chmod("a/b/c/d", 0o666);
948

            
949
        let m = mistrust_build(&[MistrustOp::IgnorePrefix(d.canonical_root())]);
950

            
951
        // A check should work...
952
        m.check_directory(d.path("a/b")).unwrap();
953

            
954
        // But we get an error if we check the contents.
955
        let e = m
956
            .verifier()
957
            .all_errors()
958
            .check_content()
959
            .check(d.path("a/b"))
960
            .unwrap_err();
961
        assert_eq!(1, e.errors().count());
962

            
963
        // We only expect an error on the _writable_ contents: the _readable_
964
        // a/b/c is okay.
965
        assert_eq!(e.path().unwrap(), d.path("a/b/c/d"));
966
    }
967

            
968
    #[test]
969
    fn trust_everyone() {
970
        let d = Dir::new();
971
        d.dir("a/b/c");
972
        d.file("a/b/c/d");
973
        d.chmod("a", 0o777);
974
        d.chmod("a/b", 0o777);
975
        d.chmod("a/b/c", 0o777);
976
        d.chmod("a/b/c/d", 0o666);
977

            
978
        let m = mistrust_build(&[MistrustOp::DangerouslyTrustEveryone()]);
979

            
980
        // This is fine.
981
        m.check_directory(d.path("a/b/c")).unwrap();
982
        // This isn't a directory!
983
        let err = m.check_directory(d.path("a/b/c/d")).unwrap_err();
984
        assert!(matches!(err, Error::BadType(_)));
985

            
986
        // But it _is_ a file.
987
        m.verifier()
988
            .require_file()
989
            .check(d.path("a/b/c/d"))
990
            .unwrap();
991
    }
992

            
993
    #[test]
994
    fn default_mistrust() {
995
        // we can't test a mistrust without ignore_prefix, but we should make sure that we can build one.
996
        let _m = Mistrust::default();
997
    }
998

            
999
    #[test]
    fn empty_path() {
        let m = mistrust_build(&[MistrustOp::DangerouslyTrustEveryone()]);
        assert_matches!(m.check_directory(""), Err(Error::NotFound(_)));
        let m = Mistrust::default();
        assert_matches!(m.check_directory(""), Err(Error::NotFound(_)));
    }
    // TODO: Write far more tests.
    // * Can there be a test for a failed readlink()?  I can't see an easy way
    //   to provoke that without trying to make a time-of-check/time-of-use race
    //   condition, since we stat the link before we call readlink on it.
    // * Can there be a test for a failing call to std::env::current_dir?  Seems
    //   hard to provoke without calling set_current_dir(), which isn't good
    //   manners in a test.
}