1use std::{env, ffi::CStr, os::raw::c_char};
3
4use crate::{conv, enums::*, functions::*, types::*};
5
6pub struct Client<'a, C: conv::Conversation> {
33 pub close_on_drop: bool,
35 handle: &'a mut PamHandle,
36 conversation: Box<C>,
37 is_authenticated: bool,
38 has_open_session: bool,
39 last_code: PamReturnCode,
40}
41
42impl<'a> Client<'a, conv::PasswordConv> {
43 pub fn with_password(service: &str) -> PamResult<Client<'a, conv::PasswordConv>> {
45 Client::with_conversation(service, conv::PasswordConv::new())
46 }
47}
48
49impl<'a, C: conv::Conversation> Client<'a, C> {
50 pub fn with_conversation(service: &str, conversation: C) -> PamResult<Client<'a, C>> {
52 let mut conversation = Box::new(conversation);
53 let conv = conv::into_pam_conv(&mut *conversation);
54
55 let handle = start(service, None, &conv)?;
56 Ok(Client {
57 close_on_drop: true,
58 handle,
59 conversation,
60 is_authenticated: false,
61 has_open_session: false,
62 last_code: PamReturnCode::Success,
63 })
64 }
65
66 pub fn conversation(&self) -> &C {
68 &*self.conversation
69 }
70
71 pub fn conversation_mut(&mut self) -> &mut C {
73 &mut *self.conversation
74 }
75
76 pub fn authenticate(&mut self, flags: PamFlag) -> PamResult<()> {
78 self.last_code = authenticate(self.handle, flags);
79 if self.last_code != PamReturnCode::Success {
80 return Err(From::from(self.last_code));
82 }
83
84 self.is_authenticated = true;
85
86 self.last_code = acct_mgmt(self.handle, flags);
87 if self.last_code != PamReturnCode::Success {
88 return self.reset();
90 }
91 Ok(())
92 }
93
94 pub fn change_authentication_token(&mut self, flags: PamFlag) -> PamResult<()> {
96 self.last_code = chauthtok(self.handle, flags);
97 if self.last_code != PamReturnCode::Success {
98 return Err(From::from(self.last_code));
100 }
101 Ok(())
102 }
103
104 pub fn get_user(&mut self) -> PamResult<String> {
106 get_item(self.handle, PamItemType::User).and_then(|result| {
107 let ptr: *const c_char = unsafe { std::mem::transmute(result) };
109 let username = unsafe { CStr::from_ptr(ptr) };
110 match username.to_str() {
111 Err(_) => Err(PamError(PamReturnCode::System_Err)),
112 Ok(username) => Ok(username.to_string()),
113 }
114 })
115 }
116
117 pub fn open_session(&mut self) -> PamResult<()> {
120 if !self.is_authenticated {
121 return Err(PamReturnCode::Perm_Denied.into());
123 }
124
125 self.last_code = setcred(self.handle, PamFlag::Establish_Cred);
126 if self.last_code != PamReturnCode::Success {
127 return self.reset();
128 }
129
130 self.last_code = open_session(self.handle, false);
131 if self.last_code != PamReturnCode::Success {
132 return self.reset();
133 }
134
135 self.last_code = setcred(self.handle, PamFlag::Reinitialize_Cred);
137 if self.last_code != PamReturnCode::Success {
138 return self.reset();
139 }
140
141 self.has_open_session = true;
142 self.initialize_environment()
143 }
144
145 fn initialize_environment(&mut self) -> PamResult<()> {
148 use uzers::os::unix::UserExt;
149
150 let user = uzers::get_user_by_name(&self.get_user()?).unwrap_or_else(|| {
151 panic!(
152 "Could not get user by name: {:?}",
153 self.get_user()
154 )
155 });
156
157 self.set_env(
159 "USER",
160 user.name()
161 .to_str()
162 .expect("Unix usernames should be valid UTF-8"),
163 )?;
164 self.set_env(
165 "LOGNAME",
166 user.name()
167 .to_str()
168 .expect("Unix usernames should be valid UTF-8"),
169 )?;
170 self.set_env("HOME", user.home_dir().to_str().unwrap())?;
171 self.set_env("PWD", user.home_dir().to_str().unwrap())?;
172 self.set_env("SHELL", user.shell().to_str().unwrap())?;
173 Ok(())
176 }
177
178 fn set_env(&mut self, key: &str, value: &str) -> PamResult<()> {
180 env::set_var(key, value);
182
183 if getenv(self.handle, key).is_ok() {
185 let name_value = format!("{}={}", key, value);
186 putenv(self.handle, &name_value)
187 } else {
188 Ok(())
189 }
190 }
191
192 fn reset(&mut self) -> PamResult<()> {
194 setcred(self.handle, PamFlag::Delete_Cred);
195 self.is_authenticated = false;
196 Err(From::from(self.last_code))
197 }
198}
199
200impl<'a, C: conv::Conversation> Drop for Client<'a, C> {
201 fn drop(&mut self) {
202 if self.has_open_session && self.close_on_drop {
203 close_session(self.handle, false);
204 }
205 let code = setcred(self.handle, PamFlag::Delete_Cred);
206 end(self.handle, code);
207 }
208}