use crate::env_list::EnvList;
use crate::Context;
use crate::ConversationHandler;
use crate::{ExtResult, Flag, Result};
use pam_sys::{pam_close_session, pam_setcred};
use std::ffi::OsStr;
use std::mem::drop;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[must_use]
pub enum SessionToken {
FullSession,
PseudoSession,
}
#[must_use]
pub struct Session<'a, ConvT> {
context: &'a mut Context<ConvT>,
session_active: bool,
credentials_active: bool,
}
impl<'a, ConvT> Session<'a, ConvT>
where
ConvT: ConversationHandler,
{
pub(crate) fn new(context: &'a mut Context<ConvT>, real: bool) -> Session<'a, ConvT> {
Self {
context,
session_active: real,
credentials_active: true,
}
}
pub fn refresh_credentials(&mut self, flags: Flag) -> Result<()> {
self.context.wrap_pam_return(unsafe {
pam_setcred(
self.context.handle().into(),
(Flag::REFRESH_CRED | flags).bits(),
)
})
}
pub fn reinitialize_credentials(&mut self, flags: Flag) -> Result<()> {
self.context.wrap_pam_return(unsafe {
pam_setcred(
self.context.handle().into(),
(Flag::REINITIALIZE_CRED | flags).bits(),
)
})
}
pub fn leak(mut self) -> SessionToken {
let result = if self.session_active {
SessionToken::FullSession
} else {
SessionToken::PseudoSession
};
self.session_active = false;
self.credentials_active = false;
result
}
#[must_use]
#[rustversion::attr(since(1.48), doc(alias = "pam_getenv"))]
pub fn getenv(&self, name: impl AsRef<OsStr>) -> Option<&str> {
self.context.getenv(name)
}
#[rustversion::attr(since(1.48), doc(alias = "pam_putenv"))]
pub fn putenv(&mut self, name_value: impl AsRef<OsStr>) -> Result<()> {
self.context.putenv(name_value)
}
#[must_use]
#[rustversion::attr(since(1.48), doc(alias = "pam_getenvlist"))]
pub fn envlist(&self) -> EnvList {
self.context.envlist()
}
#[rustversion::attr(since(1.48), doc(alias = "pam_close_session"))]
pub fn close(mut self, flags: Flag) -> ExtResult<(), Self> {
let handle = self.context.handle().as_ptr();
if self.session_active {
let status = unsafe { pam_close_session(handle, flags.bits()) };
if let Err(e) = self.context.wrap_pam_return(status) {
return Err(e.into_with_payload(self));
}
self.session_active = false;
}
if self.credentials_active {
let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | flags).bits()) };
if let Err(e) = self.context.wrap_pam_return(status) {
return Err(e.into_with_payload(self));
}
self.credentials_active = false;
}
Ok(())
}
}
impl<'a, ConvT> Drop for Session<'a, ConvT> {
fn drop(&mut self) {
let handle = self.context.handle().as_ptr();
if self.session_active {
let status = unsafe { pam_close_session(handle, Flag::NONE.bits()) };
self.session_active = false;
drop(self.context.wrap_pam_return(status));
}
if self.credentials_active {
let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | Flag::SILENT).bits()) };
self.credentials_active = false;
drop(self.context.wrap_pam_return(status));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token() {
let token = SessionToken::FullSession;
let mut context = Context::new(
"test",
Some("user"),
crate::conv_null::Conversation::default(),
)
.unwrap();
let mut session = context.unleak_session(token);
let _ = session.putenv("TEST=1");
let _ = session.getenv("TEST");
let _ = session.envlist();
let _ = session.leak();
let session = context.unleak_session(SessionToken::PseudoSession);
let _ = session.leak();
}
}