summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: ede875162d3a891b079a5d4e9d7250505b6a7135 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::env;
use std::process::ExitCode;
use pwd::Passwd;
use nix::unistd;

extern crate time;

const ERROR_ARGS:           u8 = 1;
const ERROR_CONFIG:         u8 = 2;
const ERROR_NO_USER:        u8 = 3;
const ERROR_NOT_AUTHORIZED: u8 = 4;
const ERROR_AUTH_FAILED:    u8 = 5;
const ERROR_RUN_ROOT:       u8 = 6;

mod persist;
mod flags;
mod help;

fn main() -> ExitCode {
    let args: Vec<String> = env::args().collect();
    let flags = match flags::parse(&args[1..]) {
        Some(data) => data,
        None => {
            help::help();
            return ExitCode::from(ERROR_ARGS);
        }
    };
    if flags.version {
        println!("crab version 0.0.4");
        return ExitCode::SUCCESS;
    }
    if flags.help {
        help::help();
        return ExitCode::SUCCESS;
    }
    if args.len() - flags.arg_count < 2 {
        println!("usage: crab [-d] command [args]");
        return ExitCode::SUCCESS;
    }
    let config = match config("/etc/crab.conf") {
        Some(data) => data,
        None => return ExitCode::from(ERROR_CONFIG)
    };
    let user = match Passwd::current_user() {
        Some(data) => data,
        None => {
            eprintln!("You dont exist.");
            return ExitCode::from(ERROR_NO_USER);
        }
    };
    let persist = match allowed(&config, &user.name) {
        Some(data) => data && !flags.dont_persist,
        None => {
            eprintln!("Operation Not Permitted.");
            return ExitCode::from(ERROR_NOT_AUTHORIZED);
        }
    };

    if !validate(&user.name, persist) {
        eprintln!("Authentication failed.");
        return ExitCode::from(ERROR_AUTH_FAILED);
    }

    if !unistd::setuid(unistd::geteuid()).is_ok() || !unistd::setgid(unistd::getegid()).is_ok() {
        eprintln!("Failed to set root permissions");
        return ExitCode::from(ERROR_RUN_ROOT);
    };
    let start = 1 + flags.arg_count;
    let err = exec::execvp(&args[start], &args[start..]);
    
    eprintln!("{}", err);
    
    ExitCode::SUCCESS
}

struct Config {
    users: Vec<(String, bool)>
}

fn validate(user: &str, persist: bool) -> bool {
    if persist && persist::get_persist(user) {
        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);
    }
    return true;
}

fn allowed(config: &Config, user: &str) -> Option<bool> {
    for (name, persist) in &config.users {
        if name == user {
            return Some(persist.clone());
        }
    }
    None
}

fn 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 users = vec![];
    for (line_num, line) in file.split("\n").enumerate() {
        let args: Vec<&str> = line.split(" ").collect();
        if line.trim() == "" {
            continue;
        }
        if args.len() < 2 {
            eprintln!("Error in config at line {}: Not enough arguments", line_num);
            continue;
        }
        let user: 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
        };
        users.push((user, persist));
    }
    Some(Config{users})
}