1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
use nix::unistd::Group;
use crate::{persist, secure};
pub struct Config {
pub identitys: Vec<(String, bool)>,
}
/// Returns a option containing the config ad the specific pile path.
/// This function will print out config syntax errors to the standard
/// output if it failes to parse certain lines.
/// #### Arguments
/// * `path` - The path to the config file
/// #### Returns
/// * `None` - If the config failed to load
/// * `Some(Config)` - If the config was sucessfully parsed.
pub fn load_config(path: &str) -> Option<Config> {
let file = match std::fs::read_to_string(path) {
Err(e) => {
eprintln!("{}: {}", &path, e);
return None
},
Ok(data) => data
};
let mut identitys = vec![];
for (line_num, line) in file.split("\n").enumerate() {
let args: Vec<&str> = line.split(" ").collect();
if line.starts_with("#") || line.trim() == "" {
continue;
}
if args.len() < 2 {
eprintln!("Error in config at line {}: Not enough arguments", line_num);
continue;
}
let identity: String = args[0].to_string();
let persist: bool = match args[1].parse() {
Err(e) => {
eprintln!("Error in config at line {}: {}", line_num, e);
continue;
},
Ok(data) => data
};
identitys.push((identity, persist));
}
Some(Config{identitys})
}
/// Returns a vector of group names of the groups the current effective uid is in
/// #### Returns
/// A vector of strings of the groups the user is in. If the vector is empty,
/// either the function coudn't retrieve the users groups, or the user is not in
/// any groups.
fn get_groups() -> Vec<String> {
let groups = match nix::unistd::getgroups() {
Ok(data) => data,
Err(_) => return vec![]
};
let names = groups.iter()
.map(|gid| Group::from_gid(*gid))
.flatten().flatten()
.map(|group| group.name)
.collect();
names
}
/// Returns if the user is authorized given a sepcific config and user name.
/// #### Arguments
/// * `config` - A config struct contaning the authorization settings for crab
/// * `user` - The name of the user to check is authorized
/// #### Returns
/// * `None` - If the user is not authorized
/// * `Some(bool)` - If the user is authorized, returning the boolean if the
/// user is set to persist
pub fn authorize(config: &Config, user: &str) -> Option<bool> {
let groups = get_groups();
for (identity, persist) in &config.identitys {
if identity.starts_with(":") {
let group = &identity[1..];
if groups.contains(&group.to_string()) {
return Some(persist.clone());
};
} else if identity == user {
return Some(persist.clone());
}
}
None
}
/// Authenticates an authorized user for the current session.
/// If the user is already persisted, it will attempt to
/// read the persist file, and then skip authentication if
/// the is still persisted.
/// #### Arguments
/// * `user` - The login name of the linux user
/// * `persist` - If the user's login should persist
/// #### Returns
/// * `true` - If the user authenticated sucessfully, or the user is persisted
/// * `false` - If the user failed to authenticate
pub fn authenticate(user: &str, persist: bool) -> bool {
if persist && persist::get_persist(user) {
secure::elevate_privilages(0, 0);
return true;
}
let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) {
Ok(data) => data,
Err(_) => return false
};
let mut auth = match pam::Authenticator::with_password("crab") {
Ok(data) => data,
Err(_) => return false
};
auth.get_handler().set_credentials(user.to_owned(), input);
if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
return false;
}
if persist {
persist::set_persist(user);
}
secure::elevate_privilages(0, 0);
return true;
}
|