~goorzhel/radm

ref: d822cd6f6b83262c129d34f6543994b6bc2b763b radm/src/auth.rs -rw-r--r-- 1.9 KiB
d822cd6f — Antonio Gurgel Add missing comments 4 months ago
                                                                                
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
//! Authentication methods.

use anyhow::{anyhow, Context, Result};
use log::{error, info};
use nix::unistd::gethostname;

use crate::{
    pam::PamPassword,
    tui::{prompt, EchoInput},
};

/// Authenticates a user.
pub trait Authenticator {
    fn authenticate(&mut self) -> Result<()>;
    fn username(&self) -> &str; // For lack of trait fields.
}

/// Prompts user to log in.
pub fn login() -> Result<impl Authenticator> {
    let hostname = || -> Result<String> {
        let mut buf = [0u8; 64];
        let hostname = gethostname(&mut buf).context("Couldn't call gethostname")?;
        let hostname = hostname.to_str().context("Hostname isn't valid UTF-8")?;
        Ok(hostname.to_owned())
    };

    let mut attempts = 0;
    let max_attempts = 3;
    let hostname = hostname().context("Couldn't get hostname")?;
    let username_prompt = format!("{} login", hostname);

    loop {
        // TODO: login(1) eats SIGINTs. This function should too.
        let username = prompt(&username_prompt, EchoInput::Echo)?;
        if username.is_empty() {
            continue;
        }
        // TODO: Here's the inflection point between various auth possibilities.
        // It can't be outside this function because the username is the determiner.
        // 1. PAM password: Status quo.
        // 2. PAM autologin: Identical authenticate() except no `let password`.
        // 3. !PAM password: I'll have to bone up on scrypt or something.
        // 4. !PAM autologin: Probably a `getgrouplist` call.
        let mut pam = PamPassword::new(username)?;
        match pam.authenticate() {
            Err(e) => {
                error!("Couldn't authenticate {}: {}", pam.username(), e);
            }
            Ok(_) => {
                info!("Authenticated {}", pam.username());
                return Ok(pam);
            }
        }
        attempts += 1;
        if attempts == max_attempts {
            return Err(anyhow!("Too many login attempts"));
        }
    }
}