1use crate::env_list::EnvList;
12use crate::error::{Error, ErrorCode};
13use crate::ffi::{from_pam_conv, into_pam_conv};
14use crate::session::{Session, SessionToken};
15use crate::{char_ptr_to_str, ConversationHandler};
16extern crate libc;
17extern crate pam_sys2 as pam_sys;
18
19use crate::{ExtResult, Flag, Result, PAM_SUCCESS};
20
21use libc::{c_char, c_int, c_void};
22use pam_sys::pam_conv as PamConversation;
23use pam_sys::pam_handle_t as RawPamHandle;
24use pam_sys::{
25 pam_acct_mgmt, pam_authenticate, pam_chauthtok, pam_close_session, pam_end, pam_get_item,
26 pam_getenv, pam_getenvlist, pam_open_session, pam_putenv, pam_set_item, pam_setcred, pam_start,
27};
28use std::cell::Cell;
29use std::ffi::{CStr, CString, OsStr};
30use std::marker::PhantomData;
31use std::mem::ManuallyDrop;
32use std::os::unix::ffi::OsStrExt;
33use std::ptr::NonNull;
34use std::{ptr, slice};
35
36macro_rules! impl_pam_str_item {
38 ($name:ident, $set_name:ident, $item_type:expr$(, $doc:literal$(, $extdoc:literal)?)?$(,)?) => {
39 $(#[doc = "Returns "]#[doc = $doc]$(#[doc = "\n\n"]#[doc = $extdoc])?)?
40 pub fn $name(&self) -> Result<String> {
41 let ptr = self.get_item($item_type as c_int)?;
42 if ptr.is_null() {
43 return Err(Error::new(self.handle(), ErrorCode::PERM_DENIED));
44 }
45 let string = unsafe { CStr::from_ptr(ptr.cast()) }.to_string_lossy().into_owned();
46 return Ok(string);
47 }
48
49 $(#[doc = "Sets "]#[doc = $doc])?
50 pub fn $set_name(&mut self, value: Option<&str>) -> Result<()> {
51 match value {
52 None => unsafe { self.set_item($item_type as c_int, ptr::null()) },
53 Some(string) => {
54 let cstring = CString::new(string).map_err(|_| Error::new(self.handle(), ErrorCode::BUF_ERR))?;
55 unsafe { self.set_item($item_type as c_int, cstring.as_ptr().cast()) }
56 }
57 }
58 }
59 }
60}
61
62#[derive(Debug, Clone, Copy)]
63pub(crate) struct PamHandle(NonNull<RawPamHandle>);
64
65impl PamHandle {
67 #[inline]
72 pub unsafe fn new(ptr: *mut RawPamHandle) -> Option<Self> {
73 NonNull::new(ptr).map(Self)
74 }
75
76 #[inline]
77 pub const fn as_ptr(self) -> *mut RawPamHandle {
78 self.0.as_ptr()
79 }
80}
81
82impl From<PamHandle> for *mut RawPamHandle {
83 #[inline]
84 fn from(handle: PamHandle) -> Self {
85 handle.as_ptr()
86 }
87}
88
89impl From<PamHandle> for *const RawPamHandle {
90 #[inline]
91 fn from(handle: PamHandle) -> Self {
92 handle.as_ptr()
93 }
94}
95
96#[cfg(any(target_os = "linux", doc))]
102#[repr(C)]
103#[derive(Debug)]
104struct XAuthData {
105 pub namelen: c_int,
107 pub name: *const c_char,
109 pub datalen: c_int,
111 pub data: *const c_char,
113}
114
115pub struct Context<ConvT> {
121 handle: PamHandle,
122 last_status: Cell<c_int>,
123 _conversation: PhantomData<ConvT>,
124}
125
126impl<ConvT> Context<ConvT>
127where
128 ConvT: ConversationHandler,
129{
130 #[rustversion::attr(since(1.48), doc(alias = "pam_start"))]
152 pub fn new(service: &str, username: Option<&str>, conversation: ConvT) -> Result<Self> {
153 Self::from_boxed_conv(service, username, Box::new(conversation))
155 }
156
157 pub fn from_boxed_conv(
162 service: &str,
163 username: Option<&str>,
164 boxed_conv: Box<ConvT>,
165 ) -> Result<Self> {
166 let mut handle: *mut RawPamHandle = ptr::null_mut();
167
168 let c_service = CString::new(service).map_err(|_| Error::from(ErrorCode::BUF_ERR))?;
169 let c_username = match username {
170 None => None,
171 Some(name) => Some(CString::new(name).map_err(|_| Error::from(ErrorCode::BUF_ERR))?),
172 };
173
174 let pam_conv = into_pam_conv(boxed_conv);
176
177 match unsafe {
179 pam_start(
180 c_service.as_ptr(),
181 c_username.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
182 &pam_conv,
183 &mut handle,
184 )
185 } {
186 PAM_SUCCESS => {
187 let handle = unsafe { PamHandle::new(handle) }
190 .ok_or_else(|| Error::from(ErrorCode::ABORT))?;
191 let mut result = Self {
192 handle,
193 last_status: Cell::new(PAM_SUCCESS),
194 _conversation: PhantomData,
195 };
196 result.conversation_mut().init(username);
198 Ok(result)
199 }
200 code => Err(ErrorCode::from_repr(code)
201 .unwrap_or(ErrorCode::ABORT)
202 .into()),
203 }
204 }
205
206 #[rustversion::attr(since(1.48), doc(alias = "pam_authenticate"))]
232 pub fn authenticate(&mut self, flags: Flag) -> Result<()> {
233 self.wrap_pam_return(unsafe { pam_authenticate(self.handle().into(), flags.bits()) })
234 }
235
236 #[rustversion::attr(since(1.48), doc(alias = "pam_acct_mgmt"))]
259 pub fn acct_mgmt(&mut self, flags: Flag) -> Result<()> {
260 self.wrap_pam_return(unsafe { pam_acct_mgmt(self.handle().into(), flags.bits()) })
261 }
262
263 pub fn reinitialize_credentials(&mut self, flags: Flag) -> Result<()> {
280 self.wrap_pam_return(unsafe {
281 pam_setcred(
282 self.handle().into(),
283 (Flag::REINITIALIZE_CRED | flags).bits(),
284 )
285 })
286 }
287
288 #[rustversion::attr(since(1.48), doc(alias = "pam_chauthtok"))]
311 pub fn chauthtok(&mut self, flags: Flag) -> Result<()> {
312 self.wrap_pam_return(unsafe { pam_chauthtok(self.handle().into(), flags.bits()) })
313 }
314
315 #[rustversion::attr(since(1.48), doc(alias = "pam_open_session"))]
346 pub fn open_session(&mut self, flags: Flag) -> Result<Session<'_, ConvT>> {
347 let bits = flags.bits();
348 let handle = self.handle().as_ptr();
349 self.wrap_pam_return(unsafe {
350 pam_setcred(handle, (Flag::ESTABLISH_CRED | flags).bits())
351 })?;
352
353 if let Err(e) = self.wrap_pam_return(unsafe { pam_open_session(handle, bits) }) {
354 let _ = self.wrap_pam_return(unsafe {
355 pam_setcred(handle, (Flag::DELETE_CRED | flags).bits())
356 });
357 return Err(e);
358 }
359
360 if let Err(e) = self.wrap_pam_return(unsafe {
365 pam_setcred(handle, (Flag::REINITIALIZE_CRED | flags).bits())
366 }) {
367 let _ = self.wrap_pam_return(unsafe { pam_close_session(handle, bits) });
368 let _ = self.wrap_pam_return(unsafe {
369 pam_setcred(handle, (Flag::DELETE_CRED | flags).bits())
370 });
371 return Err(e);
372 }
373
374 Ok(Session::new(self, true))
375 }
376
377 pub fn open_pseudo_session(&mut self, flags: Flag) -> Result<Session<'_, ConvT>> {
400 self.wrap_pam_return(unsafe {
401 pam_setcred(self.handle().into(), (Flag::ESTABLISH_CRED | flags).bits())
402 })?;
403
404 Ok(Session::new(self, false))
405 }
406
407 pub fn unleak_session(&mut self, token: SessionToken) -> Session<'_, ConvT> {
409 Session::new(self, matches!(token, SessionToken::FullSession))
410 }
411}
412
413impl<ConvT> Context<ConvT> {
414 #[inline]
416 pub(crate) fn handle(&self) -> PamHandle {
417 self.handle
418 }
419
420 #[inline]
422 pub(crate) fn wrap_pam_return(&self, status: c_int) -> Result<()> {
423 self.last_status.set(status);
424 match status {
425 PAM_SUCCESS => Ok(()),
426 code => Err(Error::new(
427 self.handle(),
428 ErrorCode::from_repr(code).unwrap_or(ErrorCode::ABORT),
429 )),
430 }
431 }
432
433 #[rustversion::attr(since(1.48), doc(alias = "pam_get_item"))]
444 pub fn get_item(&self, item_type: c_int) -> Result<*const c_void> {
445 let mut result: *const c_void = ptr::null();
446 self.wrap_pam_return(unsafe {
447 pam_get_item(self.handle().into(), item_type, &mut result)
448 })?;
449 Ok(result)
450 }
451
452 #[rustversion::attr(since(1.48), doc(alias = "pam_set_item"))]
469 pub unsafe fn set_item(&mut self, item_type: c_int, value: *const c_void) -> Result<()> {
470 self.wrap_pam_return(pam_set_item(self.handle().into(), item_type, value))
471 }
472
473 #[inline]
479 fn conversation_raw(&self) -> *mut ConvT {
480 let ptr = self
481 .get_item(pam_sys::PAM_CONV as c_int)
482 .expect("Extracting the conversation handler should never fail")
483 .cast::<PamConversation>();
484 unsafe {
485 from_pam_conv(
486 ptr.as_ref()
487 .expect("Invalid state: conversation handler should never be null"),
488 )
489 }
490 }
491
492 pub fn conversation(&self) -> &ConvT {
494 let ptr: *const ConvT = self.conversation_raw();
495 unsafe { &*ptr }
498 }
499
500 pub fn conversation_mut(&mut self) -> &mut ConvT {
502 let ptr = self.conversation_raw();
503 unsafe { &mut *ptr }
506 }
507
508 impl_pam_str_item!(
509 service,
510 set_service,
511 pam_sys::PAM_SERVICE,
512 "the service name"
513 );
514 impl_pam_str_item!(user, set_user, pam_sys::PAM_USER, "the username of the entity under whose identity service will be given",
515 "This value can be mapped by any module in the PAM stack, so don't assume it stays unchanged after calling other methods on `Self`.");
516 impl_pam_str_item!(
517 user_prompt,
518 set_user_prompt,
519 pam_sys::PAM_USER_PROMPT,
520 "the string used when prompting for a user's name"
521 );
522 impl_pam_str_item!(tty, set_tty, pam_sys::PAM_TTY, "the terminal name");
523 impl_pam_str_item!(
524 ruser,
525 set_ruser,
526 pam_sys::PAM_RUSER,
527 "the requesting user name"
528 );
529 impl_pam_str_item!(
530 rhost,
531 set_rhost,
532 pam_sys::PAM_RHOST,
533 "the requesting hostname"
534 );
535 #[cfg(any(target_os = "linux", doc))]
536 impl_pam_str_item!(
537 authtok_type,
538 set_authtok_type,
539 pam_sys::PAM_AUTHTOK_TYPE,
540 "the default password type in the prompt (Linux specific)",
541 "E.g. \"UNIX\" for \"Enter UNIX password:\""
542 );
543 #[cfg(any(target_os = "linux", doc))]
544 impl_pam_str_item!(
545 xdisplay,
546 set_xdisplay,
547 pam_sys::PAM_XDISPLAY,
548 "the name of the X display (Linux specific)"
549 );
550
551 #[cfg(any(target_os = "linux", doc))]
553 pub fn xauthdata(&self) -> Result<(&CStr, &[u8])> {
554 let handle = self.handle();
555 let ptr = self
556 .get_item(pam_sys::PAM_XAUTHDATA as c_int)?
557 .cast::<XAuthData>();
558 if ptr.is_null() {
559 return Err(Error::new(handle, ErrorCode::PERM_DENIED));
560 }
561 let data = unsafe { &*ptr };
562
563 if data.namelen < 0 || data.datalen < 0 || data.name.is_null() || data.data.is_null() {
566 return Err(Error::new(handle, ErrorCode::BUF_ERR));
567 }
568
569 #[allow(clippy::cast_sign_loss)]
570 Ok((
571 CStr::from_bytes_with_nul(unsafe {
572 slice::from_raw_parts(data.name.cast(), data.namelen as usize + 1)
573 })
574 .map_err(|_| Error::new(handle, ErrorCode::BUF_ERR))?,
575 unsafe { slice::from_raw_parts(data.data.cast(), data.datalen as usize) },
576 ))
577 }
578
579 #[cfg(any(target_os = "linux", doc))]
586 pub fn set_xauthdata(&mut self, value: Option<(&CStr, &[u8])>) -> Result<()> {
587 match value {
588 None => unsafe { self.set_item(pam_sys::PAM_XAUTHDATA as c_int, ptr::null()) },
589 Some((name, data)) => {
590 let name_bytes = name.to_bytes_with_nul();
591
592 if name_bytes.len() > i32::MAX as usize || data.len() > i32::MAX as usize {
593 return Err(Error::new(self.handle(), ErrorCode::BUF_ERR));
594 }
595
596 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
597 let xauthdata = XAuthData {
598 namelen: name_bytes.len() as i32 - 1,
599 name: name_bytes.as_ptr().cast(),
600 datalen: data.len() as i32,
601 data: data.as_ptr().cast(),
602 };
603 unsafe {
604 self.set_item(
605 pam_sys::PAM_XAUTHDATA as c_int,
606 &xauthdata as *const _ as *const c_void,
607 )
608 }
609 }
610 }
611 }
612
613 #[must_use]
618 #[rustversion::attr(since(1.48), doc(alias = "pam_getenv"))]
619 pub fn getenv(&self, name: impl AsRef<OsStr>) -> Option<&str> {
620 let c_name = match CString::new(name.as_ref().as_bytes()) {
621 Err(_) => return None,
622 Ok(s) => s,
623 };
624 char_ptr_to_str(unsafe { pam_getenv(self.handle().into(), c_name.as_ptr()) })
625 }
626
627 #[rustversion::attr(since(1.48), doc(alias = "pam_putenv"))]
637 pub fn putenv(&mut self, name_value: impl AsRef<OsStr>) -> Result<()> {
638 let c_name_value = CString::new(name_value.as_ref().as_bytes())
639 .map_err(|_| Error::from(ErrorCode::BUF_ERR))?;
640 self.wrap_pam_return(unsafe { pam_putenv(self.handle().into(), c_name_value.as_ptr()) })
641 }
642
643 #[must_use]
653 #[rustversion::attr(since(1.48), doc(alias = "pam_getenvlist"))]
654 pub fn envlist(&self) -> EnvList {
655 unsafe { EnvList::new(pam_getenvlist(self.handle().into()).cast()) }
656 }
657
658 pub fn replace_conversation<T: ConversationHandler>(
670 self,
671 new_handler: T,
672 ) -> ExtResult<(Context<T>, ConvT), (Self, T)> {
673 match self.replace_conversation_boxed(new_handler.into()) {
674 Ok((context, boxed_old_conv)) => Ok((context, *boxed_old_conv)),
675 Err(error) => Err(error.map(|(ctx, b_conv)| (ctx, *b_conv))),
676 }
677 }
678 pub fn replace_conversation_boxed<T: ConversationHandler>(
682 mut self,
683 new_handler: Box<T>,
684 ) -> ExtResult<(Context<T>, Box<ConvT>), (Self, Box<T>)> {
685 let username = match self.user() {
687 Ok(u) => Some(u),
688 Err(e) => {
689 if e.code() != ErrorCode::PERM_DENIED {
690 return Err(e.into_with_payload((self, new_handler)));
691 }
692 None
693 }
694 };
695 let old_handler_ptr = self.conversation_raw();
697
698 let pam_conv = into_pam_conv(new_handler);
700 if let Err(e) = unsafe {
701 self.set_item(
702 pam_sys::PAM_CONV as c_int,
703 &pam_conv as *const _ as *const _,
704 )
705 } {
706 let new_handler = unsafe { Box::from_raw(from_pam_conv(&pam_conv)) };
707 Err(e.into_with_payload((self, new_handler)))
708 } else {
709 let old = ManuallyDrop::new(self);
711
712 let old_handler = unsafe { Box::from_raw(old_handler_ptr) };
717
718 let mut context = Context::<T> {
720 handle: old.handle,
721 last_status: Cell::new(old.last_status.replace(PAM_SUCCESS)),
722 _conversation: PhantomData,
723 };
724
725 context.conversation_mut().init(username.as_deref());
727
728 Ok((context, old_handler))
730 }
731 }
732}
733
734impl<ConvT> Drop for Context<ConvT> {
736 #[rustversion::attr(since(1.48), doc(alias = "pam_end"))]
737 fn drop(&mut self) {
738 let conv = self.conversation_raw();
739 unsafe { pam_end(self.handle.into(), self.last_status.get()) };
740 drop(unsafe { Box::from_raw(conv) });
741 }
742}
743
744unsafe impl<ConvT> Send for Context<ConvT> where ConvT: Send {}
748
749#[cfg(test)]
750mod tests {
751 use super::*;
752 use std::ffi::{OsStr, OsString};
753
754 #[test]
755 fn test_basic() {
756 let mut context =
757 Context::new("test", Some("user"), crate::conv_null::Conversation::new()).unwrap();
758 assert_eq!(context.service().unwrap(), "test");
760 assert_eq!(context.user().unwrap(), "user");
761 let h = context.handle();
763 assert_eq!(&h.0, &h.0);
764 assert!(format!("{:?}", h).contains(&format!("{:?}", h.as_ptr())));
765 context.set_user_prompt(Some("Who art thou? ")).unwrap();
767 assert_eq!(context.user_prompt().unwrap(), "Who art thou? ");
768 context.set_tty(Some("/dev/tty")).unwrap();
769 assert_eq!(context.tty().unwrap(), "/dev/tty");
770 context.set_ruser(Some("nobody")).unwrap();
771 assert_eq!(context.ruser().unwrap(), "nobody");
772 context.set_rhost(Some("nowhere")).unwrap();
773 assert_eq!(context.rhost().unwrap(), "nowhere");
774 #[cfg(target_os = "linux")]
776 {
777 context.set_authtok_type(Some("TEST")).unwrap();
778 assert_eq!(context.authtok_type().unwrap(), "TEST");
779 context.set_xdisplay(Some(":0")).unwrap();
780 assert_eq!(context.xdisplay().unwrap(), ":0");
781 let xauthname = CString::new("TEST_DATA").unwrap();
782 let xauthdata = [];
783 let _ = context.xauthdata();
784 context
785 .set_xauthdata(Some((&xauthname, &xauthdata)))
786 .unwrap();
787 let (resultname, resultdata) = context.xauthdata().unwrap();
788 assert_eq!(resultname, xauthname.as_c_str());
789 assert_eq!(resultdata, &xauthdata);
790 };
791 assert_eq!(
793 context.conversation_mut() as *mut _ as *const _,
794 context.conversation() as *const _
795 );
796 context
797 .conversation_mut()
798 .text_info(&CString::new("").unwrap());
799 assert!(context.get_item(pam_sys::PAM_AUTHTOK as c_int).is_err());
801 context.putenv("TEST=1").unwrap();
803 context.putenv("TEST2=2").unwrap();
804 let _ = context.putenv("\0=\0").unwrap_err();
805 assert_eq!(context.getenv("TEST").unwrap(), "1");
806 assert!(context.getenv("TESTNONEXIST").is_none());
807 let env = context.envlist();
808 assert!(!env.is_empty());
809 let _ = env.get("TEST").unwrap();
810 let _ = env.get("TESTNONEXIST").is_none();
811 for (key, value) in env.iter_tuples() {
812 if key.to_string_lossy() == "TEST" {
813 assert_eq!(value.to_string_lossy(), "1");
814 }
815 }
816 assert!(format!("{:?}", &env.iter_tuples()).contains("EnvItem"));
817 for item in &env {
818 let string = item.to_string();
819 if string.starts_with("TEST=") {
820 assert_eq!(string, "TEST=1");
821 assert!(format!("{:?}", &item).contains("EnvItem"));
822 } else if string.starts_with("TEST2=") {
823 let (_, v): (&OsStr, &OsStr) = item.into();
824 assert_eq!(v.to_string_lossy(), "2");
825 }
826 let _ = item.as_ref();
827 }
828 let _ = format!("{:?}", &env);
829 assert!(!env.is_empty());
830 assert_eq!(env.len(), env.as_ref().len());
831 assert_eq!(env.as_ref(), context.envlist().as_ref());
832 assert_eq!(
833 env.as_ref().partial_cmp(context.envlist().as_ref()),
834 Some(std::cmp::Ordering::Equal)
835 );
836 assert_eq!(
837 env.as_ref().cmp(context.envlist().as_ref()),
838 std::cmp::Ordering::Equal
839 );
840 assert_eq!(&env["TEST"], "1");
841 assert_eq!(env.len(), env.iter_tuples().size_hint().0);
842 let list: std::vec::Vec<&CStr> = (&env).into();
843 assert_eq!(list.len(), env.len());
844 let list: std::vec::Vec<(&OsStr, _)> = (&env).into();
845 assert_eq!(list.len(), env.len());
846 let map: std::collections::HashMap<&OsStr, _> = (&env).into();
847 assert_eq!(map.len(), map.len());
848 assert_eq!(
849 map.get(&OsString::from("TEST".to_string()).as_ref()),
850 Some(&OsString::from("1".to_string()).as_ref())
851 );
852 assert!(env.to_string().contains("TEST=1"));
853 let list: std::vec::Vec<(std::ffi::OsString, _)> = context.envlist().into();
854 assert_eq!(list.len(), env.len());
855 let list: std::vec::Vec<CString> = context.envlist().into();
856 assert_eq!(list.len(), env.len());
857 let map: std::collections::HashMap<_, _> = context.envlist().into();
858 assert_eq!(map.len(), env.len());
859 assert_eq!(
860 map.get(&OsString::from("TEST".to_string())),
861 Some(&OsString::from("1".to_string()))
862 );
863 drop(context)
864 }
865
866 #[test]
867 fn test_conv_replace() {
868 let mut context =
869 Context::new("test", Some("user"), crate::conv_null::Conversation::new()).unwrap();
870 context.set_user(Some("anybody")).unwrap();
872 let (mut context, old_conv) = context
874 .replace_conversation(crate::conv_mock::Conversation::default())
875 .unwrap();
876 assert_eq!(context.conversation().username, "anybody");
878 context.set_user(None).unwrap();
879 let (context, _) = context.replace_conversation(old_conv).unwrap();
880 assert!(context.user().is_err());
882 }
883
884 #[test]
885 fn test_dyn_ref() {
886 let mut handler_a = crate::conv_null::Conversation::new();
887 let mut handler_b = crate::conv_mock::Conversation::new();
888
889 let mut context = Context::new(
890 "test",
891 Some("user"),
892 &mut handler_a as &mut dyn ConversationHandler,
893 )
894 .unwrap();
895 context.set_user(Some("anybody")).unwrap();
897 let (context, _) = context
899 .replace_conversation(
900 &mut handler_b as &mut dyn ConversationHandler
901 )
902 .unwrap();
903
904 drop(handler_a);
906
907 drop(context);
909
910 assert_eq!(handler_b.username, "anybody");
912 }
913
914 #[test]
915 fn test_dyn() {
916 let mut context = Context::new(
917 "test",
918 Some("user"),
919 Box::new(crate::conv_null::Conversation::new()) as Box<dyn ConversationHandler>,
920 )
921 .unwrap();
922 context.set_user(Some("anybody")).unwrap();
924 let (context, _) = context
926 .replace_conversation(
927 Box::new(crate::conv_mock::Conversation::new()) as Box<dyn ConversationHandler>
928 )
929 .unwrap();
930
931 let mock_handler: &crate::conv_mock::Conversation =
934 unsafe { &*(&**context.conversation() as *const _ as *const _) };
935 assert_eq!(mock_handler.username, "anybody");
936 }
937
938 #[test]
945 #[cfg_attr(not(feature = "full_test"), ignore)]
946 fn test_full() {
947 let mut context = Context::new(
948 "test_rust_pam_client",
949 Some("nobody"),
950 crate::conv_null::Conversation::new(),
951 )
952 .unwrap();
953 let _ = context.authenticate(Flag::SILENT);
954 let _ = context.acct_mgmt(Flag::SILENT);
955 let _ = context.chauthtok(Flag::CHANGE_EXPIRED_AUTHTOK);
956 let _ = context.reinitialize_credentials(Flag::SILENT | Flag::NONE);
957 drop(context.open_session(Flag::SILENT));
958 drop(context.open_pseudo_session(Flag::SILENT));
959 }
960
961 #[test]
970 #[cfg_attr(not(feature = "full_test"), ignore)]
971 fn test_full_unauth() {
972 let mut context = Context::new(
973 "test_rust_pam_client",
974 Some("nobody"),
975 crate::conv_null::Conversation::new(),
976 )
977 .unwrap();
978 let _ = context.acct_mgmt(Flag::SILENT);
979 let _ = context.chauthtok(Flag::CHANGE_EXPIRED_AUTHTOK);
980 let _ = context.reinitialize_credentials(Flag::SILENT | Flag::NONE);
981 if let Ok(mut session) = context.open_session(Flag::SILENT) {
982 let _ = session.refresh_credentials(Flag::SILENT);
983 let _ = session.reinitialize_credentials(Flag::SILENT);
984 let _ = session.envlist();
985 let _ = session.close(Flag::SILENT);
986 };
987 if let Ok(mut session) = context.open_pseudo_session(Flag::SILENT) {
988 let _ = session.refresh_credentials(Flag::SILENT);
989 };
990 }
991}