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>
34where
35 ConvT: ConversationHandler,
36{
37 context: &'a mut Context<ConvT>,
38 session_active: bool,
39 credentials_active: bool,
40}
41
42impl<'a, ConvT> Session<'a, ConvT>
43where
44 ConvT: ConversationHandler,
45{
46 pub(crate) fn new(context: &'a mut Context<ConvT>, real: bool) -> Session<'a, ConvT> {
48 Self {
49 context,
50 session_active: real,
51 credentials_active: true,
52 }
53 }
54
55 pub fn refresh_credentials(&mut self, flags: Flag) -> Result<()> {
71 self.context.wrap_pam_return(unsafe {
72 pam_setcred(
73 self.context.handle().into(),
74 (Flag::REFRESH_CRED | flags).bits(),
75 )
76 })
77 }
78
79 pub fn reinitialize_credentials(&mut self, flags: Flag) -> Result<()> {
85 self.context.wrap_pam_return(unsafe {
86 pam_setcred(
87 self.context.handle().into(),
88 (Flag::REINITIALIZE_CRED | flags).bits(),
89 )
90 })
91 }
92
93 pub fn leak(mut self) -> SessionToken {
107 let result = if self.session_active {
108 SessionToken::FullSession
109 } else {
110 SessionToken::PseudoSession
111 };
112 self.session_active = false;
113 self.credentials_active = false;
114 result
115 }
116
117 #[must_use]
121 #[rustversion::attr(since(1.48), doc(alias = "pam_getenv"))]
122 pub fn getenv(&self, name: impl AsRef<OsStr>) -> Option<&str> {
123 self.context.getenv(name)
124 }
125
126 #[rustversion::attr(since(1.48), doc(alias = "pam_putenv"))]
130 pub fn putenv(&mut self, name_value: impl AsRef<OsStr>) -> Result<()> {
131 self.context.putenv(name_value)
132 }
133
134 #[must_use]
138 #[rustversion::attr(since(1.48), doc(alias = "pam_getenvlist"))]
139 pub fn envlist(&self) -> EnvList {
140 self.context.envlist()
141 }
142
143 #[rustversion::attr(since(1.48), doc(alias = "pam_close_session"))]
166 pub fn close(mut self, flags: Flag) -> ExtResult<(), Self> {
167 let handle = self.context.handle().as_ptr();
168 if self.session_active {
169 let status = unsafe { pam_close_session(handle, flags.bits()) };
170 if let Err(e) = self.context.wrap_pam_return(status) {
171 return Err(e.into_with_payload(self));
172 }
173 self.session_active = false;
174 }
175 if self.credentials_active {
176 let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | flags).bits()) };
177 if let Err(e) = self.context.wrap_pam_return(status) {
178 return Err(e.into_with_payload(self));
179 }
180 self.credentials_active = false;
181 }
182 Ok(())
183 }
184}
185
186impl<'a, ConvT> Drop for Session<'a, ConvT>
188where
189 ConvT: ConversationHandler,
190{
191 fn drop(&mut self) {
192 let handle = self.context.handle().as_ptr();
193 if self.session_active {
194 let status = unsafe { pam_close_session(handle, Flag::NONE.bits()) };
195 self.session_active = false;
196 drop(self.context.wrap_pam_return(status));
197 }
198 if self.credentials_active {
199 let status = unsafe { pam_setcred(handle, (Flag::DELETE_CRED | Flag::SILENT).bits()) };
200 self.credentials_active = false;
201 drop(self.context.wrap_pam_return(status));
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_token() {
212 let token = SessionToken::FullSession;
213
214 let mut context = Context::new(
215 "test",
216 Some("user"),
217 crate::conv_null::Conversation::default(),
218 )
219 .unwrap();
220 let mut session = context.unleak_session(token);
221 let _ = session.putenv("TEST=1");
222 let _ = session.getenv("TEST");
223 let _ = session.envlist();
224 let _ = session.leak();
225
226 let session = context.unleak_session(SessionToken::PseudoSession);
227 let _ = session.leak();
228 }
229}