pam_kanidm/pam/
mod.rs

1//! Interface to the pluggable authentication module framework (PAM).
2//!
3//! The goal of this library is to provide a type-safe API that can be used to
4//! interact with PAM.  The library is incomplete - currently it supports
5//! a subset of functions for use in a pam authentication module.  A pam module
6//! is a shared library that is invoked to authenticate a user, or to perform
7//! other functions.
8//!
9//! For general information on writing pam modules, see
10//! [The Linux-PAM Module Writers' Guide][module-guide]
11//!
12//! [module-guide]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_MWG.html
13//!
14//! A typical authentication module will define an external function called
15//! `pam_sm_authenticate()`, which will use functions in this library to
16//! interrogate the program that requested authentication for more information,
17//! and to render a result.  For a working example that uses this library, see
18//! [toznyauth-pam][].
19//!
20//! [toznyauth-pam]: https://github.com/tozny/toznyauth-pam
21//!
22//! Note that constants that are normally read from pam header files are
23//! hard-coded in the `constants` module.  The values there are taken from
24//! a Linux system.  That means that it might take some work to get this library
25//! to work on other platforms.
26
27pub mod constants;
28pub mod conv;
29pub mod items;
30#[doc(hidden)]
31pub mod macros;
32pub mod module;
33
34use std::collections::BTreeSet;
35use std::convert::TryFrom;
36use std::ffi::CStr;
37
38use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
39use kanidm_unix_common::unix_config::PamNssConfig;
40
41use crate::core::{self, RequestOptions};
42use crate::pam::constants::*;
43use crate::pam::module::{PamHandle, PamHooks};
44use crate::pam_hooks;
45use constants::PamResultCode;
46use time::OffsetDateTime;
47
48use tracing::debug;
49use tracing_subscriber::filter::LevelFilter;
50use tracing_subscriber::fmt;
51use tracing_subscriber::prelude::*;
52
53pub fn get_cfg() -> Result<PamNssConfig, PamResultCode> {
54    PamNssConfig::new()
55        .read_options_from_optional_config(DEFAULT_CONFIG_PATH)
56        .map_err(|_| PamResultCode::PAM_SERVICE_ERR)
57}
58
59fn install_subscriber(debug: bool) {
60    let fmt_layer = fmt::layer().with_target(false);
61
62    let filter_layer = if debug {
63        LevelFilter::DEBUG
64    } else {
65        LevelFilter::ERROR
66    };
67
68    let _ = tracing_subscriber::registry()
69        .with(filter_layer)
70        .with(fmt_layer)
71        .try_init();
72}
73
74#[derive(Debug, Default)]
75pub struct ModuleOptions {
76    pub debug: bool,
77    pub use_first_pass: bool,
78    pub ignore_unknown_user: bool,
79}
80
81impl TryFrom<&Vec<&CStr>> for ModuleOptions {
82    type Error = ();
83
84    fn try_from(args: &Vec<&CStr>) -> Result<Self, Self::Error> {
85        let opts: Result<BTreeSet<&str>, _> = args.iter().map(|cs| cs.to_str()).collect();
86        let gopts = match opts {
87            Ok(o) => o,
88            Err(e) => {
89                println!("Error in module args -> {:?}", e);
90                return Err(());
91            }
92        };
93
94        Ok(ModuleOptions {
95            debug: gopts.contains("debug"),
96            use_first_pass: gopts.contains("use_first_pass"),
97            ignore_unknown_user: gopts.contains("ignore_unknown_user"),
98        })
99    }
100}
101
102pub struct PamKanidm;
103
104pam_hooks!(PamKanidm);
105
106impl PamHooks for PamKanidm {
107    fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
108        let opts = match ModuleOptions::try_from(&args) {
109            Ok(o) => o,
110            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
111        };
112
113        install_subscriber(opts.debug);
114
115        debug!(?args, ?opts, "acct_mgmt");
116
117        let current_time = OffsetDateTime::now_utc();
118
119        let req_opt = RequestOptions::Main {
120            config_path: DEFAULT_CONFIG_PATH,
121        };
122
123        core::sm_authenticate(pamh, &opts, req_opt, current_time)
124    }
125
126    fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
127        let opts = match ModuleOptions::try_from(&args) {
128            Ok(o) => o,
129            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
130        };
131
132        install_subscriber(opts.debug);
133
134        debug!(?args, ?opts, "acct_mgmt");
135
136        let current_time = OffsetDateTime::now_utc();
137
138        let req_opt = RequestOptions::Main {
139            config_path: DEFAULT_CONFIG_PATH,
140        };
141
142        core::acct_mgmt(pamh, &opts, req_opt, current_time)
143    }
144
145    fn sm_open_session(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
146        let opts = match ModuleOptions::try_from(&args) {
147            Ok(o) => o,
148            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
149        };
150
151        install_subscriber(opts.debug);
152
153        debug!(?args, ?opts, "sm_open_session");
154
155        let req_opt = RequestOptions::Main {
156            config_path: DEFAULT_CONFIG_PATH,
157        };
158
159        core::sm_open_session(pamh, &opts, req_opt)
160    }
161
162    fn sm_close_session(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
163        let opts = match ModuleOptions::try_from(&args) {
164            Ok(o) => o,
165            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
166        };
167
168        install_subscriber(opts.debug);
169
170        debug!(?args, ?opts, "sm_close_session");
171
172        core::sm_close_session(pamh, &opts)
173    }
174
175    fn sm_chauthtok(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
176        let opts = match ModuleOptions::try_from(&args) {
177            Ok(o) => o,
178            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
179        };
180
181        install_subscriber(opts.debug);
182
183        debug!(?args, ?opts, "sm_chauthtok");
184
185        core::sm_chauthtok(pamh, &opts)
186    }
187
188    fn sm_setcred(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
189        let opts = match ModuleOptions::try_from(&args) {
190            Ok(o) => o,
191            Err(_) => return PamResultCode::PAM_SERVICE_ERR,
192        };
193
194        install_subscriber(opts.debug);
195
196        debug!(?args, ?opts, "sm_setcred");
197
198        core::sm_setcred(pamh, &opts)
199    }
200}