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
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
577
173304
        if let Some(err) = opt_error {
578
18625
            return Err(err);
579
154679
        }
580
154679

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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