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) -> PamResult<()> {
78 self.last_code = authenticate(self.handle, PamFlag::None);
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, PamFlag::None);
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 = result as *const libc::c_void as *const i8;
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()?)
151 .unwrap_or_else(|| panic!("Could not get user by name: {:?}", self.get_user()));
152
153 self.set_env(
155 "USER",
156 user.name()
157 .to_str()
158 .expect("Unix usernames should be valid UTF-8"),
159 )?;
160 self.set_env(
161 "LOGNAME",
162 user.name()
163 .to_str()
164 .expect("Unix usernames should be valid UTF-8"),
165 )?;
166 self.set_env("HOME", user.home_dir().to_str().unwrap())?;
167 self.set_env("PWD", user.home_dir().to_str().unwrap())?;
168 self.set_env("SHELL", user.shell().to_str().unwrap())?;
169 Ok(())
172 }
173
174 fn set_env(&mut self, key: &str, value: &str) -> PamResult<()> {
176 env::set_var(key, value);
178
179 if getenv(self.handle, key).is_ok() {
181 let name_value = format!("{}={}", key, value);
182 putenv(self.handle, &name_value)
183 } else {
184 Ok(())
185 }
186 }
187
188 fn reset(&mut self) -> PamResult<()> {
190 setcred(self.handle, PamFlag::Delete_Cred);
191 self.is_authenticated = false;
192 Err(From::from(self.last_code))
193 }
194}
195
196impl<C: conv::Conversation> Drop for Client<'_, C> {
197 fn drop(&mut self) {
198 if self.has_open_session && self.close_on_drop {
199 close_session(self.handle, false);
200 }
201 let code = setcred(self.handle, PamFlag::Delete_Cred);
202 end(self.handle, code);
203 }
204}