use libc::mode_t; use openat::{Dir, AsPath}; use pam::constants::{PamFlag, PamResultCode}; use pam::module::{PamHandle, PamHooks}; use std::ffi::CStr; use std::io::{ErrorKind, Write}; use std::process; const CG_MOUNT: &str = "/sys/fs/cgroup"; struct PAMUserCG; pam::pam_hooks!(PAMUserCG); fn create_and_open_dir( d: &Dir, path: P, mode: mode_t, ) -> std::io::Result { match d.create_dir(path, mode) { Ok(()) => Ok(()), Err(e) => match e.kind() { ErrorKind::AlreadyExists => Ok(()), _ => Err(e), } }?; d.sub_dir(path) } struct SessionError; fn open_session(h: &mut PamHandle) -> Result<(), SessionError> { let user = h.get_user(None).or(Err(SessionError))?; let user = users::get_user_by_name(&user).ok_or(SessionError)?; let d = Dir::open(CG_MOUNT).or(Err(SessionError))?; let d = create_and_open_dir(&d, "user", 0o777).or(Err(SessionError))?; let d = create_and_open_dir(&d, &user.uid().to_string(), 0o777) .or(Err(SessionError))?; let d = create_and_open_dir(&d, "leaf", 0o777).or(Err(SessionError))?; let pid = process::id().to_string(); let mut procs = d.open_file_ex("cgroup.procs", libc::O_WRONLY, 0) .or(Err(SessionError))?; procs.write_all(pid.as_bytes()).or(Err(SessionError))?; Ok(()) } impl PamHooks for PAMUserCG { fn sm_open_session( h: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag ) -> PamResultCode { if open_session(h).is_ok() { PamResultCode::PAM_SUCCESS } else { PamResultCode::PAM_SESSION_ERR } } }