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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
580
197044
        if let Some(err) = opt_error {
581
24318
            return Err(err);
582
172726
        }
583

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

            
602
16107
        let path = path.as_ref();
603
16107
        match self.clone().check(path) {
604
8323
            Err(Error::NotFound(_)) => {}
605
57
            Err(other_error) => return Err(other_error),
606
7727
            Ok(()) => return Ok(()), // no error; file exists.
607
        }
608

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

            
620
        // We built the path!  But for paranoia's sake, check it again.
621
8323
        self.check(path)
622
16107
    }
623

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

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

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

            
666
    #[cfg(target_family = "unix")]
667
    use testing::LinkType;
668

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

            
682
        let m = mistrust_build(&[
683
            MistrustOp::IgnorePrefix(d.canonical_root()),
684
            MistrustOp::TrustNoGroupId(),
685
        ]);
686

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

            
694
        m.check_directory(d.path("d")).unwrap();
695
    }
696

            
697
    #[cfg(target_family = "unix")]
698
    #[test]
699
    fn admin_only() {
700
        use std::os::unix::prelude::MetadataExt;
701

            
702
        let d = Dir::new();
703
        d.dir("a/b");
704
        d.chmod("a", 0o700);
705
        d.chmod("a/b", 0o700);
706

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

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

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

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

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

            
735
        let m = mistrust_build(&[
736
            MistrustOp::IgnorePrefix(d.canonical_root()),
737
            MistrustOp::TrustNoGroupId(),
738
        ]);
739

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

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

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

            
757
        // TODO: Possibly, make sure that a special file matches neither.
758
    }
759

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

            
770
        let m = mistrust_build(&[
771
            MistrustOp::IgnorePrefix(d.canonical_root()),
772
            MistrustOp::TrustNoGroupId(),
773
        ]);
774

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

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

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

            
799
        let m = mistrust_build(&[
800
            MistrustOp::IgnorePrefix(d.canonical_root()),
801
            MistrustOp::TrustNoGroupId(),
802
        ]);
803

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

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

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

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

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

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

            
844
        m.check_directory(d.path("a/b/c")).unwrap();
845

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

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

            
862
        let m = mistrust_build(&[
863
            MistrustOp::IgnorePrefix(d.canonical_root()),
864
            MistrustOp::TrustNoGroupId(),
865
        ]);
866

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

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

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

            
881
        m.check_directory(d.path("a/b")).unwrap();
882

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

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

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

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

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

            
907
            // Now make the permissions correct.
908
            d.chmod("a", 0o0700);
909
            d.chmod("a/b", 0o0700);
910
        }
911

            
912
        // Make the directory!
913
        m.make_directory(d.path("a/b/c/d")).unwrap();
914

            
915
        // Make sure it exists and has good permissions.
916
        m.check_directory(d.path("a/b/c/d")).unwrap();
917

            
918
        // Try make_directory again and make sure _that_ succeeds.
919
        m.make_directory(d.path("a/b/c/d")).unwrap();
920
    }
921

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

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

            
936
        // A check should work...
937
        m.check_directory(d.path("a/b")).unwrap();
938

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

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

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

            
963
        let m = mistrust_build(&[MistrustOp::DangerouslyTrustEveryone()]);
964

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

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

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

            
984
    #[test]
985
    fn empty_path() {
986
        let m = mistrust_build(&[MistrustOp::DangerouslyTrustEveryone()]);
987
        assert_matches!(m.check_directory(""), Err(Error::NotFound(_)));
988

            
989
        let m = Mistrust::default();
990
        assert_matches!(m.check_directory(""), Err(Error::NotFound(_)));
991
    }
992

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