1use crate::env_list::EnvList;
12use crate::Context;
13use crate::ConversationHandler;
14use crate::{ExtResult, Flag, Result};
15
16use pam_sys::{pam_close_session, pam_setcred};
17use std::ffi::OsStr;
18use std::mem::drop;
19
20#[derive(Debug, Clone, Copy)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[must_use]
26pub enum SessionToken {
27 FullSession,
28 PseudoSession,
29}
30
31#[must_use]
33pub struct Session<'a, ConvT> {
34 context: &'a mut Context<ConvT>,
35 session_active: bool,
36 credentials_active: bool,
37}
38
39impl<'a, ConvT> Session<'a, ConvT>
40where
41 ConvT: ConversationHandler,
42{
43 pub(crate) fn new(context: &'a mut Context<ConvT>, real: bool) -> Session<'a, ConvT> {
45 Self {
46 context,
47 session_active: real,
48 credentials_active: true,
49 }
50 }
51
52 pub fn refresh_credentials(&mut self, flags: Flag) -> Result<()> {
68 self.context.wrap_pam_return(unsafe {
69 pam_setcred(
70 self.context.handle().into(),
71 (Flag::REFRESH_CRED | flags).bits(),
72 )
73 })
74 }
75
76 pub fn reinitialize_credentials(&mut self, flags: Flag) -> Result<()> {
82 self.context.wrap_pam_return(unsafe {
83 pam_setcred(
84 self.context.handle().into(),
85 (Flag::REINITIALIZE_CRED | flags).bits(),
86 )
87 })
88 }
89
90 pub fn leak(mut self) -> SessionToken {
104 let result = if self.session_active {
105 SessionToken::FullSession
106 } else {
107 SessionToken::PseudoSession
108 };
109 self.session_active = false;
110 self.credentials_active = false;
111 result
112 }
113
114 #[must_use]
118 #[rustversion::attr(since(1.48), doc(alias = "pam_getenv"))]
119 pub fn getenv(&self, name: impl AsRef<OsStr>) -> Option<&str> {
120 self.context.getenv(name)
121 }
122
123 #[rustversion::attr(since(1.48), doc(alias = "pam_putenv"))]
127 pub fn putenv(&mut self, name_value: impl AsRef<OsStr>) -> Result<()> {
128 self.context.putenv(name_value)
129 }
130
131 #[must_use]
135 #[rustversion::attr(since(1.48), doc(alias = "pam_getenvlist"))]
136 pub fn envlist(&self) -> EnvList {
137 self.context.envlist()
138 }
139
140 #[rustversion::attr(since(1.48), doc(alias = "pam_close_session"))]
163 pub fn close(mut self, flags: Flag) -> ExtResult<(), Self> {
164 let handle = self.context.handle().as_ptr();
165 if self.session_active {
166 let status = unsafe { pam_close_session(handle, flags.bits()) };
167 if let Err(e) = self.context.wrap_pam_return(status) {
168 return Err(e.into_with_payload(self));
169 }
170 self.session_active = false;
171 }
172 if self.credentials_active {
173 let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | flags).bits()) };
174 if let Err(e) = self.context.wrap_pam_return(status) {
175 return Err(e.into_with_payload(self));
176 }
177 self.credentials_active = false;
178 }
179 Ok(())
180 }
181}
182
183impl<'a, ConvT> Drop for Session<'a, ConvT> {
185 fn drop(&mut self) {
186 let handle = self.context.handle().as_ptr();
187 if self.session_active {
188 let status = unsafe { pam_close_session(handle, Flag::NONE.bits()) };
189 self.session_active = false;
190 drop(self.context.wrap_pam_return(status));
191 }
192 if self.credentials_active {
193 let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | Flag::SILENT).bits()) };
194 self.credentials_active = false;
195 drop(self.context.wrap_pam_return(status));
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_token() {
206 let token = SessionToken::FullSession;
207
208 let mut context = Context::new(
209 "test",
210 Some("user"),
211 crate::conv_null::Conversation::default(),
212 )
213 .unwrap();
214 let mut session = context.unleak_session(token);
215 let _ = session.putenv("TEST=1");
216 let _ = session.getenv("TEST");
217 let _ = session.envlist();
218 let _ = session.leak();
219
220 let session = context.unleak_session(SessionToken::PseudoSession);
221 let _ = session.leak();
222 }
223}