1
#![cfg_attr(docsrs, feature(doc_auto_cfg, 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
#![warn(clippy::cognitive_complexity)]
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_duration_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
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
52

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

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

            
74
#[cfg(feature = "anon_home")]
75
pub mod anon_home;
76
#[cfg(test)]
77
pub(crate) mod testing;
78
pub mod walk;
79

            
80
#[cfg(feature = "serde")]
81
use serde::{Deserialize, Serialize};
82
use std::{
83
    fs::DirBuilder,
84
    path::{Path, PathBuf},
85
    sync::Arc,
86
};
87

            
88
pub use dir::CheckedDir;
89
pub use disable::GLOBAL_DISABLE_VAR;
90
pub use err::{Error, format_access_bits};
91
pub use file_access::FileAccess;
92

            
93
/// A result type as returned by this crate
94
pub type Result<T> = std::result::Result<T, Error>;
95

            
96
#[cfg(all(
97
    target_family = "unix",
98
    not(target_os = "ios"),
99
    not(target_os = "android"),
100
    not(target_os = "tvos")
101
))]
102
pub use user::{TrustedGroup, TrustedUser};
103

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
337
    /// Considering our settings, determine whether we should trust all users
338
    /// (and thereby disable our permission checks.)
339
18751
    fn should_be_enabled(&self) -> disable::Status {
340
18751
        // If we've disabled checks in our configuration, then that settles it.
341
18751
        if self.dangerously_trust_everyone == Some(true) {
342
6778
            return disable::Status::DisableChecks;
343
11973
        }
344
11973

            
345
11973
        // Otherwise, we use our "disable_by_environment" setting to see whether
346
11973
        // we should check the environment.
347
11973
        self.disable_by_environment
348
11973
            .as_ref()
349
11973
            .unwrap_or(&disable::Disable::default())
350
11973
            .should_disable_checks()
351
18751
    }
352
}
353

            
354
impl Default for Mistrust {
355
5238
    fn default() -> Self {
356
5238
        MistrustBuilder::default()
357
5238
            .build()
358
5238
            .expect("Could not build default")
359
5238
    }
360
}
361

            
362
/// An object used to perform a single check.
363
///
364
/// Obtained from [`Mistrust::verifier()`].
365
///
366
/// A `Verifier` is used when [`Mistrust::check_directory`] and
367
/// [`Mistrust::make_directory`] are not sufficient for your needs.
368
#[derive(Clone, Debug)]
369
#[must_use]
370
pub struct Verifier<'a> {
371
    /// The [`Mistrust`] that was used to create this verifier.
372
    mistrust: &'a Mistrust,
373

            
374
    /// Has the user called [`Verifier::permit_readable`]?
375
    readable_okay: bool,
376

            
377
    /// Has the user called [`Verifier::all_errors`]?
378
    collect_multiple_errors: bool,
379

            
380
    /// If the user called [`Verifier::require_file`] or
381
    /// [`Verifier::require_directory`], which did they call?
382
    enforce_type: Type,
383

            
384
    /// If true, we want to check all the contents of this directory as well as
385
    /// the directory itself.  Requires the `walkdir` feature.
386
    check_contents: bool,
387
}
388

            
389
/// A type of object that we have been told to require.
390
#[derive(Debug, Clone, Copy)]
391
enum Type {
392
    /// A directory.
393
    Dir,
394
    /// A regular file.
395
    File,
396
    /// A directory or a regular file.
397
    DirOrFile,
398
    /// Absolutely anything at all.
399
    Anything,
400
}
401

            
402
impl Mistrust {
403
    /// Return a new [`MistrustBuilder`].
404
5813
    pub fn builder() -> MistrustBuilder {
405
5813
        MistrustBuilder::default()
406
5813
    }
407

            
408
    /// Initialize a new default `Mistrust`.
409
    ///
410
    /// By default:
411
    ///    *  we will inspect all directories that are used to resolve any path that is checked.
412
    pub fn new() -> Self {
413
        Self::default()
414
    }
415

            
416
    /// Construct a new `Mistrust` that trusts all users and all groups.
417
    ///
418
    /// (In effect, this `Mistrust` will have all of its permissions checks
419
    /// disabled, since if all users and groups are trusted, it doesn't matter
420
    /// what the permissions on any file and directory are.)
421
4081
    pub fn new_dangerously_trust_everyone() -> Self {
422
4081
        Self::builder()
423
4081
            .dangerously_trust_everyone()
424
4081
            .build()
425
4081
            .expect("Could not construct a Mistrust")
426
4081
    }
427

            
428
    /// Create a new [`Verifier`] with this configuration, to perform a single check.
429
128705
    pub fn verifier(&self) -> Verifier<'_> {
430
128705
        Verifier {
431
128705
            mistrust: self,
432
128705
            readable_okay: false,
433
128705
            collect_multiple_errors: false,
434
128705
            enforce_type: Type::DirOrFile,
435
128705
            check_contents: false,
436
128705
        }
437
128705
    }
438

            
439
    /// Verify that `dir` is a directory that only trusted users can read from,
440
    /// list the files in,  or write to.
441
    ///
442
    /// If it is, and we can verify that, return `Ok(())`.  Otherwise, return
443
    /// the first problem that we encountered when verifying it.
444
    ///
445
    /// `m.check_directory(dir)` is equivalent to
446
    /// `m.verifier().require_directory().check(dir)`.  If you need different
447
    /// behavior, see [`Verifier`] for more options.
448
24
    pub fn check_directory<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
449
24
        self.verifier().require_directory().check(dir)
450
24
    }
451

            
452
    /// As `check_directory`, but create the directory if needed.
453
    ///
454
    /// `m.check_directory(dir)` is equivalent to
455
    /// `m.verifier().make_directory(dir)`.  If you need different behavior, see
456
    /// [`Verifier`] for more options.
457
22
    pub fn make_directory<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
458
22
        self.verifier().make_directory(dir)
459
22
    }
460

            
461
    /// Return true if this `Mistrust` object has been configured to trust all
462
    /// users.
463
126527
    pub(crate) fn is_disabled(&self) -> bool {
464
126527
        self.status.disabled()
465
126527
    }
466

            
467
    /// Create a new [`FileAccess`] for reading or writing files
468
    /// while enforcing the rules of this `Mistrust`.
469
168
    pub fn file_access(&self) -> FileAccess<'_> {
470
168
        self.verifier().file_access()
471
168
    }
472
}
473

            
474
impl<'a> Verifier<'a> {
475
    /// Create a new [`FileAccess`] for reading or writing files
476
    /// while enforcing the rules of this `Verifier`.
477
630
    pub fn file_access(self) -> FileAccess<'a> {
478
630
        FileAccess::from_verifier(self)
479
630
    }
480

            
481
    /// Configure this `Verifier` to require that all paths it checks be
482
    /// files (not directories).
483
2778
    pub fn require_file(mut self) -> Self {
484
2778
        self.enforce_type = Type::File;
485
2778
        self
486
2778
    }
487

            
488
    /// Configure this `Verifier` to require that all paths it checks be
489
    /// directories.
490
29229
    pub fn require_directory(mut self) -> Self {
491
29229
        self.enforce_type = Type::Dir;
492
29229
        self
493
29229
    }
494

            
495
    /// Configure this `Verifier` to allow the paths that it checks to be
496
    /// filesystem objects of any type.
497
    ///
498
    /// By default, the final path (after resolving all links) must be a
499
    /// directory or a regular file, not (for example) a block device or a named
500
    /// pipe.
501
    pub fn permit_all_object_types(mut self) -> Self {
502
        self.enforce_type = Type::Anything;
503
        self
504
    }
505

            
506
    /// Configure this `Verifier` to permit the target files/directory to be
507
    /// _readable_ by untrusted users.
508
    ///
509
    /// By default, we assume that the caller wants the target file or directory
510
    /// to be only readable or writable by trusted users.  With this flag, we
511
    /// permit the target file or directory to be readable by untrusted users,
512
    /// but not writable.
513
    ///
514
    /// (Note that we always allow the _parent directories_ of the target to be
515
    /// readable by untrusted users, since their readability does not make the
516
    /// target readable.)
517
9321
    pub fn permit_readable(mut self) -> Self {
518
9321
        self.readable_okay = true;
519
9321
        self
520
9321
    }
521

            
522
    /// Tell this `Verifier` to accumulate as many errors as possible, rather
523
    /// than stopping at the first one.
524
    ///
525
    /// If a single error is found, that error will be returned.  Otherwise, the
526
    /// resulting error type will be [`Error::Multiple`].
527
    ///
528
    /// # Example
529
    ///
530
    /// ```
531
    /// # use fs_mistrust::Mistrust;
532
    /// if let Err(e) = Mistrust::new().verifier().all_errors().check("/home/gardenGnostic/.gnupg/") {
533
    ///    for error in e.errors() {
534
    ///       println!("{}", e)
535
    ///    }
536
    /// }
537
    /// ```
538
6
    pub fn all_errors(mut self) -> Self {
539
6
        self.collect_multiple_errors = true;
540
6
        self
541
6
    }
542

            
543
    /// Configure this verifier so that, after checking the directory, check all
544
    /// of its contents.
545
    ///
546
    /// Symlinks are not permitted; both files and directories are allowed. This
547
    /// option implies `require_directory()`, since only a directory can have
548
    /// contents.
549
    ///
550
    /// Requires that the `walkdir` feature is enabled.
551
    #[cfg(feature = "walkdir")]
552
5623
    pub fn check_content(mut self) -> Self {
553
5623
        self.check_contents = true;
554
5623
        self.require_directory()
555
5623
    }
556

            
557
    /// Check whether the file or directory at `path` conforms to the
558
    /// requirements of this `Verifier` and the [`Mistrust`] that created it.
559
115952
    pub fn check<P: AsRef<Path>>(&self, path: P) -> Result<()> {
560
115952
        let path = path.as_ref();
561
115952

            
562
115952
        // This is the powerhouse of our verifier code:
563
115952
        //
564
115952
        // See the `imp` module for actual implementation logic.
565
115952
        let mut error_iterator = self
566
115952
            .check_errors(path.as_ref())
567
115952
            .chain(self.check_content_errors(path.as_ref()));
568

            
569
        // Collect either the first error, or all errors.
570
115952
        let opt_error: Option<Error> = if self.collect_multiple_errors {
571
6
            error_iterator.collect()
572
        } else {
573
115946
            let next = error_iterator.next();
574
115946
            drop(error_iterator); // so that "canonical" is no longer borrowed.
575
115946
            next
576
        };
577

            
578
115952
        if let Some(err) = opt_error {
579
16951
            return Err(err);
580
99001
        }
581
99001

            
582
99001
        Ok(())
583
115952
    }
584
    /// Check whether `path` is a valid directory, and create it if it doesn't
585
    /// exist.
586
    ///
587
    /// Returns `Ok` if the directory already existed or if it was just created,
588
    /// and it conforms to the requirements of this `Verifier` and the
589
    /// [`Mistrust`] that created it.
590
    ///
591
    /// Return an error if:
592
    ///  * there was a permissions or ownership problem in the path or any of
593
    ///    its ancestors,
594
    ///  * there was a problem when creating the directory
595
    ///  * after creating the directory, we found that it had a permissions or
596
    ///    ownership problem.
597
7777
    pub fn make_directory<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
598
7777
        self.enforce_type = Type::Dir;
599
7777

            
600
7777
        let path = path.as_ref();
601
7777
        match self.clone().check(path) {
602
3530
            Err(Error::NotFound(_)) => {}
603
49
            Err(other_error) => return Err(other_error),
604
4198
            Ok(()) => return Ok(()), // no error; file exists.
605
        }
606

            
607
        // Looks like we got a "not found", so we're creating the path.
608
3530
        let mut bld = DirBuilder::new();
609
3530
        #[cfg(target_family = "unix")]
610
3530
        {
611
3530
            use std::os::unix::fs::DirBuilderExt;
612
3530
            bld.mode(0o700);
613
3530
        }
614
3530
        bld.recursive(true)
615
3530
            .create(path)
616
3530
            .map_err(|e| Error::CreatingDir(Arc::new(e)))?;
617

            
618
        // We built the path!  But for paranoia's sake, check it again.
619
3530
        self.check(path)
620
7777
    }
621

            
622
    /// Check whether `path` is a directory conforming to the requirements of
623
    /// this `Verifier` and the [`Mistrust`] that created it.
624
    ///
625
    /// If it is, then return a new [`CheckedDir`] that can be used to securely access
626
    /// the contents of this directory.  
627
1864
    pub fn secure_dir<P: AsRef<Path>>(self, path: P) -> Result<CheckedDir> {
628
1864
        let path = path.as_ref();
629
1864
        self.clone().require_directory().check(path)?;
630
1817
        CheckedDir::new(&self, path)
631
1864
    }
632

            
633
    /// Check whether `path` is a directory conforming to the requirements of
634
    /// this `Verifier` and the [`Mistrust`] that created it.
635
    ///
636
    /// If successful, then return a new [`CheckedDir`] that can be used to
637
    /// securely access the contents of this directory.  
638
1448
    pub fn make_secure_dir<P: AsRef<Path>>(self, path: P) -> Result<CheckedDir> {
639
1448
        let path = path.as_ref();
640
1448
        self.clone().require_directory().make_directory(path)?;
641
1446
        CheckedDir::new(&self, path)
642
1448
    }
643
}
644

            
645
#[cfg(test)]
646
mod test {
647
    // @@ begin test lint list maintained by maint/add_warning @@
648
    #![allow(clippy::bool_assert_comparison)]
649
    #![allow(clippy::clone_on_copy)]
650
    #![allow(clippy::dbg_macro)]
651
    #![allow(clippy::mixed_attributes_style)]
652
    #![allow(clippy::print_stderr)]
653
    #![allow(clippy::print_stdout)]
654
    #![allow(clippy::single_char_pattern)]
655
    #![allow(clippy::unwrap_used)]
656
    #![allow(clippy::unchecked_duration_subtraction)]
657
    #![allow(clippy::useless_vec)]
658
    #![allow(clippy::needless_pass_by_value)]
659
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
660
    use super::*;
661
    use testing::{Dir, MistrustOp, mistrust_build};
662

            
663
    #[cfg(target_family = "unix")]
664
    use testing::LinkType;
665

            
666
    #[cfg(target_family = "unix")]
667
    #[test]
668
    fn simple_cases() {
669
        let d = Dir::new();
670
        d.dir("a/b/c");
671
        d.dir("e/f/g");
672
        d.chmod("a", 0o755);
673
        d.chmod("a/b", 0o755);
674
        d.chmod("a/b/c", 0o700);
675
        d.chmod("e", 0o755);
676
        d.chmod("e/f", 0o777);
677
        d.link_rel(LinkType::Dir, "a/b/c", "d");
678

            
679
        let m = mistrust_build(&[
680
            MistrustOp::IgnorePrefix(d.canonical_root()),
681
            MistrustOp::TrustNoGroupId(),
682
        ]);
683

            
684
        // /a/b/c should be fine...
685
        m.check_directory(d.path("a/b/c")).unwrap();
686
        // /e/f/g should not.
687
        let e = m.check_directory(d.path("e/f/g")).unwrap_err();
688
        assert!(matches!(e, Error::BadPermission(_, 0o777, 0o022)));
689
        assert_eq!(e.path().unwrap(), d.path("e/f").canonicalize().unwrap());
690

            
691
        m.check_directory(d.path("d")).unwrap();
692
    }
693

            
694
    #[cfg(target_family = "unix")]
695
    #[test]
696
    fn admin_only() {
697
        use std::os::unix::prelude::MetadataExt;
698

            
699
        let d = Dir::new();
700
        d.dir("a/b");
701
        d.chmod("a", 0o700);
702
        d.chmod("a/b", 0o700);
703

            
704
        if d.path("a/b").metadata().unwrap().uid() == 0 {
705
            // Nothing to do here; we _are_ root.
706
            return;
707
        }
708

            
709
        // With normal settings should be okay...
710
        let m = mistrust_build(&[MistrustOp::IgnorePrefix(d.canonical_root())]);
711
        m.check_directory(d.path("a/b")).unwrap();
712

            
713
        // With admin_only, it'll fail.
714
        let m = mistrust_build(&[
715
            MistrustOp::IgnorePrefix(d.canonical_root()),
716
            MistrustOp::TrustAdminOnly(),
717
        ]);
718

            
719
        let err = m.check_directory(d.path("a/b")).unwrap_err();
720
        assert!(matches!(err, Error::BadOwner(_, _)));
721
        assert_eq!(err.path().unwrap(), d.path("a").canonicalize().unwrap());
722
    }
723

            
724
    #[test]
725
    fn want_type() {
726
        let d = Dir::new();
727
        d.dir("a");
728
        d.file("b");
729
        d.chmod("a", 0o700);
730
        d.chmod("b", 0o600);
731

            
732
        let m = mistrust_build(&[
733
            MistrustOp::IgnorePrefix(d.canonical_root()),
734
            MistrustOp::TrustNoGroupId(),
735
        ]);
736

            
737
        // If we insist stuff is its own type, it works fine.
738
        m.verifier().require_directory().check(d.path("a")).unwrap();
739
        m.verifier().require_file().check(d.path("b")).unwrap();
740

            
741
        // If we insist on a different type, we hit an error.
742
        let e = m
743
            .verifier()
744
            .require_directory()
745
            .check(d.path("b"))
746
            .unwrap_err();
747
        assert!(matches!(e, Error::BadType(_)));
748
        assert_eq!(e.path().unwrap(), d.path("b").canonicalize().unwrap());
749

            
750
        let e = m.verifier().require_file().check(d.path("a")).unwrap_err();
751
        assert!(matches!(e, Error::BadType(_)));
752
        assert_eq!(e.path().unwrap(), d.path("a").canonicalize().unwrap());
753

            
754
        // TODO: Possibly, make sure that a special file matches neither.
755
    }
756

            
757
    #[cfg(target_family = "unix")]
758
    #[test]
759
    fn readable_ok() {
760
        let d = Dir::new();
761
        d.dir("a/b");
762
        d.file("a/b/c");
763
        d.chmod("a", 0o750);
764
        d.chmod("a/b", 0o750);
765
        d.chmod("a/b/c", 0o640);
766

            
767
        let m = mistrust_build(&[
768
            MistrustOp::IgnorePrefix(d.canonical_root()),
769
            MistrustOp::TrustNoGroupId(),
770
        ]);
771

            
772
        // These will fail, since the file or directory is readable.
773
        let e = m.verifier().check(d.path("a/b")).unwrap_err();
774
        assert!(matches!(e, Error::BadPermission(..)));
775
        assert_eq!(e.path().unwrap(), d.path("a/b").canonicalize().unwrap());
776
        let e = m.verifier().check(d.path("a/b/c")).unwrap_err();
777
        assert!(matches!(e, Error::BadPermission(..)));
778
        assert_eq!(e.path().unwrap(), d.path("a/b/c").canonicalize().unwrap());
779

            
780
        // Now allow readable targets.
781
        m.verifier().permit_readable().check(d.path("a/b")).unwrap();
782
        m.verifier()
783
            .permit_readable()
784
            .check(d.path("a/b/c"))
785
            .unwrap();
786
    }
787

            
788
    #[cfg(target_family = "unix")]
789
    #[test]
790
    fn multiple_errors() {
791
        let d = Dir::new();
792
        d.dir("a/b");
793
        d.chmod("a", 0o700);
794
        d.chmod("a/b", 0o700);
795

            
796
        let m = mistrust_build(&[
797
            MistrustOp::IgnorePrefix(d.canonical_root()),
798
            MistrustOp::TrustNoGroupId(),
799
        ]);
800

            
801
        // Only one error occurs, so we get that error.
802
        let e = m
803
            .verifier()
804
            .all_errors()
805
            .check(d.path("a/b/c"))
806
            .unwrap_err();
807
        assert!(matches!(e, Error::NotFound(_)));
808
        assert_eq!(1, e.errors().count());
809

            
810
        // Introduce a second error...
811
        d.chmod("a/b", 0o770);
812
        let e = m
813
            .verifier()
814
            .all_errors()
815
            .check(d.path("a/b/c"))
816
            .unwrap_err();
817
        assert!(matches!(e, Error::Multiple(_)));
818
        let errs: Vec<_> = e.errors().collect();
819
        assert_eq!(2, errs.len());
820
        assert!(matches!(&errs[0], Error::BadPermission(..)));
821
        assert!(matches!(&errs[1], Error::NotFound(_)));
822
    }
823

            
824
    #[cfg(target_family = "unix")]
825
    #[test]
826
    fn sticky() {
827
        let d = Dir::new();
828
        d.dir("a/b/c");
829
        d.chmod("a", 0o777);
830
        d.chmod("a/b", 0o755);
831
        d.chmod("a/b/c", 0o700);
832

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

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

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

            
841
        m.check_directory(d.path("a/b/c")).unwrap();
842

            
843
        // Make sure we got the right definition!
844
        #[allow(clippy::useless_conversion)]
845
        {
846
            assert_eq!(crate::imp::STICKY_BIT, u32::from(libc::S_ISVTX));
847
        }
848
    }
849

            
850
    #[cfg(target_family = "unix")]
851
    #[test]
852
    fn trust_gid() {
853
        use std::os::unix::prelude::MetadataExt;
854
        let d = Dir::new();
855
        d.dir("a/b");
856
        d.chmod("a", 0o770);
857
        d.chmod("a/b", 0o770);
858

            
859
        let m = mistrust_build(&[
860
            MistrustOp::IgnorePrefix(d.canonical_root()),
861
            MistrustOp::TrustNoGroupId(),
862
        ]);
863

            
864
        // By default, we shouldn't be accept this directory, since it is
865
        // group-writable.
866
        let e = m.check_directory(d.path("a/b")).unwrap_err();
867
        assert!(matches!(e, Error::BadPermission(..)));
868

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

            
873
        let m = mistrust_build(&[
874
            MistrustOp::IgnorePrefix(d.canonical_root()),
875
            MistrustOp::TrustGroup(gid),
876
        ]);
877

            
878
        m.check_directory(d.path("a/b")).unwrap();
879

            
880
        // OTOH, if we made a _different_ group trusted, it'll fail.
881
        let m = mistrust_build(&[
882
            MistrustOp::IgnorePrefix(d.canonical_root()),
883
            MistrustOp::TrustGroup(gid ^ 1),
884
        ]);
885

            
886
        let e = m.check_directory(d.path("a/b")).unwrap_err();
887
        assert!(matches!(e, Error::BadPermission(..)));
888
    }
889

            
890
    #[test]
891
    fn make_directory() {
892
        let d = Dir::new();
893
        d.dir("a/b");
894

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

            
897
        #[cfg(target_family = "unix")]
898
        {
899
            // Try once with bad permissions.
900
            d.chmod("a", 0o777);
901
            let e = m.make_directory(d.path("a/b/c/d")).unwrap_err();
902
            assert!(matches!(e, Error::BadPermission(..)));
903

            
904
            // Now make the permissions correct.
905
            d.chmod("a", 0o0700);
906
            d.chmod("a/b", 0o0700);
907
        }
908

            
909
        // Make the directory!
910
        m.make_directory(d.path("a/b/c/d")).unwrap();
911

            
912
        // Make sure it exists and has good permissions.
913
        m.check_directory(d.path("a/b/c/d")).unwrap();
914

            
915
        // Try make_directory again and make sure _that_ succeeds.
916
        m.make_directory(d.path("a/b/c/d")).unwrap();
917
    }
918

            
919
    #[cfg(target_family = "unix")]
920
    #[cfg(feature = "walkdir")]
921
    #[test]
922
    fn check_contents() {
923
        let d = Dir::new();
924
        d.dir("a/b/c");
925
        d.file("a/b/c/d");
926
        d.chmod("a", 0o700);
927
        d.chmod("a/b", 0o700);
928
        d.chmod("a/b/c", 0o755);
929
        d.chmod("a/b/c/d", 0o666);
930

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

            
933
        // A check should work...
934
        m.check_directory(d.path("a/b")).unwrap();
935

            
936
        // But we get an error if we check the contents.
937
        let e = m
938
            .verifier()
939
            .all_errors()
940
            .check_content()
941
            .check(d.path("a/b"))
942
            .unwrap_err();
943
        assert_eq!(1, e.errors().count());
944

            
945
        // We only expect an error on the _writable_ contents: the _readable_
946
        // a/b/c is okay.
947
        assert_eq!(e.path().unwrap(), d.path("a/b/c/d"));
948
    }
949

            
950
    #[test]
951
    fn trust_everyone() {
952
        let d = Dir::new();
953
        d.dir("a/b/c");
954
        d.file("a/b/c/d");
955
        d.chmod("a", 0o777);
956
        d.chmod("a/b", 0o777);
957
        d.chmod("a/b/c", 0o777);
958
        d.chmod("a/b/c/d", 0o666);
959

            
960
        let m = mistrust_build(&[MistrustOp::DangerouslyTrustEveryone()]);
961

            
962
        // This is fine.
963
        m.check_directory(d.path("a/b/c")).unwrap();
964
        // This isn't a directory!
965
        let err = m.check_directory(d.path("a/b/c/d")).unwrap_err();
966
        assert!(matches!(err, Error::BadType(_)));
967

            
968
        // But it _is_ a file.
969
        m.verifier()
970
            .require_file()
971
            .check(d.path("a/b/c/d"))
972
            .unwrap();
973
    }
974

            
975
    #[test]
976
    fn default_mistrust() {
977
        // we can't test a mistrust without ignore_prefix, but we should make sure that we can build one.
978
        let _m = Mistrust::default();
979
    }
980

            
981
    // TODO: Write far more tests.
982
    // * Can there be a test for a failed readlink()?  I can't see an easy way
983
    //   to provoke that without trying to make a time-of-check/time-of-use race
984
    //   condition, since we stat the link before we call readlink on it.
985
    // * Can there be a test for a failing call to std::env::current_dir?  Seems
986
    //   hard to provoke without calling set_current_dir(), which isn't good
987
    //   manners in a test.
988
}