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
#![warn(clippy::or_fun_call)]
34
#![deny(clippy::print_stderr)]
35
#![deny(clippy::print_stdout)]
36
#![warn(clippy::rc_buffer)]
37
#![deny(clippy::ref_option_ref)]
38
#![warn(clippy::semicolon_if_nothing_returned)]
39
#![warn(clippy::trait_duplication_in_bounds)]
40
#![deny(clippy::unchecked_time_subtraction)]
41
#![deny(clippy::unnecessary_wraps)]
42
#![warn(clippy::unseparated_literal_suffix)]
43
#![deny(clippy::unwrap_used)]
44
#![deny(clippy::mod_module_files)]
45
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
46
#![allow(clippy::uninlined_format_args)]
47
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
48
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
49
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
50
#![allow(clippy::needless_lifetimes)] // See arti#1765
51
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
52
#![allow(clippy::collapsible_if)] // See arti#2342
53
#![deny(clippy::unused_async)]
54
#![deny(clippy::string_slice)] // See arti#2571
55
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
56
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
350
        // Otherwise, we use our "disable_by_environment" setting to see whether
351
        // we should check the environment.
352
18220
        match &self.disable_by_environment {
353
10640
            Some(d) => d.should_disable_checks(),
354
7580
            None => disable::Disable::default().should_disable_checks(),
355
        }
356
27812
    }
357

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
595
185188
        if let Some(err) = opt_error {
596
23820
            return Err(err);
597
161368
        }
598

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

            
617
15824
        let path = path.as_ref();
618
15824
        match self.clone().check(path) {
619
8000
            Err(Error::NotFound(_)) => {}
620
54
            Err(other_error) => return Err(other_error),
621
7770
            Ok(()) => return Ok(()), // no error; file exists.
622
        }
623

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
994
    #[test]
995
    fn default_mistrust() {
996
        // we can't test a mistrust without ignore_prefix, but we should make sure that we can build one.
997
        let _m = Mistrust::default();
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.
}