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
#![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_time_subtraction)]
40
#![deny(clippy::unnecessary_wraps)]
41
#![warn(clippy::unseparated_literal_suffix)]
42
#![deny(clippy::unwrap_used)]
43
#![deny(clippy::mod_module_files)]
44
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
45
#![allow(clippy::uninlined_format_args)]
46
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
47
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
48
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
49
#![allow(clippy::needless_lifetimes)] // See arti#1765
50
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
51
#![allow(clippy::collapsible_if)] // See arti#2342
52
#![deny(clippy::unused_async)]
53
#![deny(clippy::string_slice)] // See arti#2571
54
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
55

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
593
190914
        if let Some(err) = opt_error {
594
23588
            return Err(err);
595
167326
        }
596

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

            
615
14491
        let path = path.as_ref();
616
14491
        match self.clone().check(path) {
617
7430
            Err(Error::NotFound(_)) => {}
618
51
            Err(other_error) => return Err(other_error),
619
7010
            Ok(()) => return Ok(()), // no error; file exists.
620
        }
621

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
998
    #[test]
999
    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.
}