//! Functionality for disabling `fs-mistrust` checks based on configuration or //! environment variables. use std::env::{self, VarError}; /// Convenience type to indicate whether permission checks are disabled. /// /// Used to avoid accidents with boolean meanings. #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) enum Status { /// We should indeed run permission checks, and treat some users as untrusted. CheckPermissions, /// We should treat every user as trusted, and therefore disable (most) /// permissions checks. DisableChecks, } impl Status { /// Return true if this `Status` tells us to disable checks. pub(crate) fn disabled(self) -> bool { self == Status::DisableChecks } } /// An environment variable which, if set, will cause a us to trust all users /// (and therefore, in effect, to disable all permissions checks.) pub const GLOBAL_DISABLE_VAR: &str = "FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS"; /// Value to configure when permission checks should be disabled. This type is /// set in the builder, and converted to a bool in the `Mistrust`. #[derive(Clone, Default, Debug, Eq, PartialEq)] pub(crate) enum Disable { /// Check a caller-provided environment variable, and honor it if it is set. /// If it is not set, fall back to checking /// `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS`. OnUserEnvVar(String), /// Disable permissions checks if the value of /// `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS` is something other than "false", /// "0", "no", etc.. /// /// This is the default. #[default] OnGlobalEnvVar, /// Perform permissions checks regardless of any values in the environment. Never, } /// Convert the result of `std::env::var` to a boolean, if the variable is set. /// /// Names that seem to say "don't disable" are treated as `Some(false)`. Any /// other value is treated as `Some(true)`. (That is, we err on the side of /// assuming that if you set a disable variable, you meant to disable.) /// /// Absent environment vars, or those set to the empty string, are treated as /// None. #[allow(clippy::match_like_matches_macro)] fn from_env_var_value(input: std::result::Result) -> Option { // WARNING: This behaviour of the environment variable parsing/evaluation is considered // stable and should not be modified unless necessary. // This behaviour is part of the public interface of applications which use fs-mistrust, // so changing the behaviour of this function may result in a breaking change for applications. let mut s = match input { Ok(s) => s, Err(VarError::NotPresent) => return None, Err(VarError::NotUnicode(_)) => return Some(Status::DisableChecks), }; s.make_ascii_lowercase(); let s = s.trim(); match s { "" => None, "0" | "no" | "never" | "false" | "n" => Some(Status::CheckPermissions), _ => Some(Status::DisableChecks), } } /// As `from_env_value`, but takes the name of the variable. fn from_env_var(varname: &str) -> Option { from_env_var_value(env::var(varname)) } impl Disable { /// Return true if, based on this [`Disable`] setting, and on the /// environment, we should disable permissions checking. pub(crate) fn should_disable_checks(&self) -> Status { match self { Disable::OnUserEnvVar(varname) => from_env_var(varname) .or_else(|| from_env_var(GLOBAL_DISABLE_VAR)) .unwrap_or(Status::CheckPermissions), Disable::OnGlobalEnvVar => { from_env_var(GLOBAL_DISABLE_VAR).unwrap_or(Status::CheckPermissions) } Disable::Never => Status::CheckPermissions, } } } #[cfg(test)] mod test { // @@ begin test lint list maintained by maint/add_warning @@ #![allow(clippy::bool_assert_comparison)] #![allow(clippy::clone_on_copy)] #![allow(clippy::dbg_macro)] #![allow(clippy::mixed_attributes_style)] #![allow(clippy::print_stderr)] #![allow(clippy::print_stdout)] #![allow(clippy::single_char_pattern)] #![allow(clippy::unwrap_used)] #![allow(clippy::unchecked_time_subtraction)] #![allow(clippy::useless_vec)] #![allow(clippy::needless_pass_by_value)] #![allow(clippy::string_slice)] // See arti#2571 //! use super::*; #[test] fn from_val() { for word in ["yes", "1", "true", "certainly", "whatever"] { assert_eq!( from_env_var_value(Ok(word.into())), Some(Status::DisableChecks) ); } for word in ["no", "0", "false", "NO", "Never", "n"] { assert_eq!( from_env_var_value(Ok(word.into())), Some(Status::CheckPermissions) ); } assert_eq!(from_env_var_value(Ok("".into())), None); assert_eq!(from_env_var_value(Ok(" ".into())), None); assert_eq!(from_env_var_value(Err(VarError::NotPresent)), None); assert_eq!( from_env_var_value(Err(VarError::NotUnicode("".into()))), Some(Status::DisableChecks) ); // see https://gitlab.torproject.org/tpo/core/arti/-/issues/1782 assert_eq!( from_env_var_value(Ok(" false ".to_string())), Some(Status::CheckPermissions), ); } }