-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.rs
128 lines (102 loc) · 3.84 KB
/
lib.rs
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
#[macro_use]
extern crate pamsm;
mod args;
use args::Args;
use error_stack::{Report, ResultExt};
use pam_utils::do_call_handler;
use pamsm::{Pam, PamError, PamFlags, PamServiceModule};
use path_ratchet::prelude::*;
use std::fs::{remove_file, File};
use std::path::PathBuf;
#[derive(thiserror::Error, Debug, Clone)]
enum Error {
#[error("A panic happened in the sandboxed thread")]
SandboxPanic,
#[error("There is no `store=/<dir>` given.")]
MissingUserStoreArg,
#[error("Couldn't build sandbox")]
Sandbox,
#[error("Internal PAM error")]
Pam,
#[error("User not known")]
UnknownUser,
#[error("Username has dissalowed characters")]
InvalidUsername,
#[error("Couldn't authenticate the user")]
Auth,
#[error("Couldn't reset the account authentication state")]
Reset,
}
type Result<T> = error_stack::Result<T, Error>;
fn user_file(dir: PathBuf, username: String) -> Result<PathBuf> {
let mut user_data_file = dir;
let user_file_name = SingleComponentPath::new(&username).ok_or(Error::InvalidUsername)?;
user_data_file.push_component(user_file_name);
Ok(user_data_file)
}
struct PamDirectFallback;
impl PamDirectFallback {
fn start_session(pamh: &Pam, _flags: PamFlags, args: Vec<String>) -> Result<()> {
let args = Args::try_from(args).attach(PamError::IGNORE)?;
#[cfg(feature = "sandbox")]
Self::setup_sandbox(&args)?;
let username = pam_utils::get_username(pamh, Error::Pam, Error::UnknownUser)?;
let user_data_file = user_file(args.user_store, username)?;
Self::reset(user_data_file)
}
fn auth(pamh: &Pam, _flags: PamFlags, args: Vec<String>) -> Result<()> {
let args = Args::try_from(args).attach(PamError::IGNORE)?;
#[cfg(feature = "sandbox")]
Self::setup_sandbox(&args)?;
let username = pam_utils::get_username(pamh, Error::Pam, Error::UnknownUser)?;
let user_data_file = user_file(args.user_store, username)?;
if args.reset {
Self::reset(user_data_file)
} else {
Self::set(user_data_file)
}
}
#[cfg(feature = "sandbox")]
fn setup_sandbox(args: &args::Args) -> Result<()> {
use birdcage::{Birdcage, Sandbox};
let mut birdcage = Birdcage::new()
.change_context(Error::Sandbox)
.attach_printable("Initialization failed")?;
birdcage
.add_exception(birdcage::Exception::Write(args.user_store.clone()))
.change_context(Error::Sandbox)
.attach_printable("Couldn't set the user store as writeable")?;
birdcage
.lock()
.change_context(Error::Sandbox)
.attach_printable("Couldn't activate sandbox")
}
fn set(user_data_file: PathBuf) -> Result<()> {
File::options()
.write(true)
.create_new(true)
.open(user_data_file)
.map(|_| ())
.map_err(|e| match e.kind() {
std::io::ErrorKind::AlreadyExists => Report::new(e)
.change_context(Error::Auth)
.attach(PamError::AUTH_ERR),
_ => Report::new(e).change_context(Error::Auth),
})
}
fn reset(user_data_file: PathBuf) -> Result<()> {
remove_file(user_data_file).change_context(Error::Reset)
}
}
impl PamServiceModule for PamDirectFallback {
fn open_session(pamh: Pam, flags: PamFlags, args: Vec<String>) -> PamError {
do_call_handler(Self::start_session, pamh, flags, args, Error::SandboxPanic)
}
fn close_session(_pamh: Pam, _flags: PamFlags, _args: Vec<String>) -> PamError {
PamError::SUCCESS
}
fn authenticate(pamh: Pam, flags: PamFlags, args: Vec<String>) -> PamError {
do_call_handler(Self::auth, pamh, flags, args, Error::SandboxPanic)
}
}
pam_module!(PamDirectFallback);