#![forbid(unsafe_code)]
pub type SettingsModifier = Box<dyn FnOnce(&mut Settings)>;
pub enum ListMode {
Set,
Add,
Del,
}
#[repr(u32)]
pub enum SettingKind {
Flag(SettingsModifier) = crate::common::HARDENED_ENUM_VALUE_0,
Integer(fn(&str) -> Option<SettingsModifier>) = crate::common::HARDENED_ENUM_VALUE_1,
Text(fn(&str) -> Option<SettingsModifier>) = crate::common::HARDENED_ENUM_VALUE_2,
List(fn(ListMode, Vec<String>) -> SettingsModifier) = crate::common::HARDENED_ENUM_VALUE_3,
}
mod settings_dsl;
use settings_dsl::{
defaults, emit, has_standard_negator, ifdef, initializer_of, modifier_of, referent_of,
result_of, storage_of,
};
pub const SYSTEM_EDITOR: &str = if cfg!(target_os = "linux") {
"/usr/bin/editor:/usr/bin/nano:/usr/bin/vi"
} else {
"/usr/bin/vi"
};
defaults! {
always_query_group_plugin = false #ignored
always_set_home = false #ignored
env_reset = true #ignored
fqdn = false #ignored
ignore_dot = true #ignored
lecture = never (!= never) [always, once, never] #ignored
mailerpath = None (!= None) #ignored
mail_badpass = true #ignored
match_group_by_gid = false #ignored
use_pty = true
visiblepw = false #ignored
pwfeedback = true
rootpw = false
targetpw = false
noexec = false
noninteractive_auth = false
insults = false #ignored
setenv = false
apparmor_profile = None (!= None)
umask = 0o022 (!= 0o777) {octal_mode}
umask_override = false
passwd_tries = 3 [0..=1000]
secure_path = None (!= None)
verifypw = all (!= never) [all, always, any, never] #ignored
passwd_timeout = (5*60) (!= 0) {fractional_minutes}
timestamp_timeout = (15*60) (!= 0) {fractional_minutes}
editor = SYSTEM_EDITOR
env_editor = true
env_keep = ["COLORS", "DISPLAY", "HOSTNAME", "KRB5CCNAME", "LS_COLORS", "PATH",
"PS1", "PS2", "XAUTHORITY", "XAUTHORIZATION", "XDG_CURRENT_DESKTOP"]
env_check = ["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"]
env_delete = ["IFS", "CDPATH", "LOCALDOMAIN", "RES_OPTIONS", "HOSTALIASES",
"NLSPATH", "PATH_LOCALE", "LD_*", "_RLD*", "TERMINFO", "TERMINFO_DIRS",
"TERMPATH", "TERMCAP", "ENV", "BASH_ENV", "PS4", "GLOBIGNORE",
"BASHOPTS", "SHELLOPTS", "JAVA_TOOL_OPTIONS", "PERLIO_DEBUG",
"PERLLIB", "PERL5LIB", "PERL5OPT", "PERL5DB", "FPATH", "NULLCMD",
"READNULLCMD", "ZDOTDIR", "TMPPREFIX", "PYTHONHOME", "PYTHONPATH",
"PYTHONINSPECT", "PYTHONUSERBASE", "RUBYLIB", "RUBYOPT", "*=()*"] #ignored
}
fn octal_mode(input: &str) -> Option<u64> {
<libc::mode_t>::from_str_radix(input.strip_prefix('0')?, 8)
.ok()
.map(Into::into)
}
fn fractional_minutes(input: &str) -> Option<u64> {
if let Some((integral, fractional)) = input.split_once('.') {
let shift = 10u64.pow(fractional.len().try_into().ok()?);
let seconds = integral.parse::<u64>().ok()? * shift + fractional.parse::<u64>().ok()?;
Some(seconds * 60 / shift)
} else {
input.parse::<u64>().ok()?.checked_mul(60)
}
}
#[cfg(test)]
mod test {
use super::*;
#[allow(clippy::bool_assert_comparison)]
#[test]
fn check() {
let mut def = Settings::default();
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, true };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 3 };
assert_eq! { def.secure_path, None };
assert_eq! { def.env_check, ["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"].iter().map(|s| s.to_string()).collect() };
assert_eq! { def.verifypw, enums::verifypw::all };
negate("env_check").unwrap()(&mut def);
negate("env_reset").unwrap()(&mut def);
negate("secure_path").unwrap()(&mut def);
negate("verifypw").unwrap()(&mut def);
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, false };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 3 };
assert_eq! { def.secure_path, None };
assert! { def.env_check.is_empty() };
assert_eq! { def.verifypw, enums::verifypw::never };
let SettingKind::Text(f) = set("lecture").unwrap() else {
panic!()
};
f("always").unwrap()(&mut def);
assert_eq! { def.lecture, enums::lecture::always };
f("once").unwrap()(&mut def);
assert_eq! { def.lecture, enums::lecture::once };
f("never").unwrap()(&mut def);
assert_eq! { def.lecture, enums::lecture::never};
negate("lecture").unwrap()(&mut def);
assert_eq! { def.lecture, enums::lecture::never };
let SettingKind::Flag(f) = set("env_reset").unwrap() else {
panic!()
};
f(&mut def);
let SettingKind::Text(f) = set("secure_path").unwrap() else {
panic!()
};
f("/bin").unwrap()(&mut def);
let SettingKind::Integer(f) = set("passwd_tries").unwrap() else {
panic!()
};
f("5").unwrap()(&mut def);
let SettingKind::Text(f) = set("verifypw").unwrap() else {
panic!()
};
f("any").unwrap()(&mut def);
let SettingKind::Integer(f) = set("timestamp_timeout").unwrap() else {
panic!()
};
f("25.25").unwrap()(&mut def);
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, true };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 5 };
assert_eq! { def.timestamp_timeout, 25*60 + 60/4 };
assert_eq! { def.secure_path, Some("/bin".into()) };
assert! { def.env_check.is_empty() };
assert_eq! { def.verifypw, enums::verifypw::any };
assert!(set("notanoption").is_none());
assert!(f("notanoption").is_none());
}
}