1use crate::db::{Cache, Db};
3use crate::idprovider::interface::{
4 AuthCredHandler,
5 AuthResult,
6 GroupToken,
7 GroupTokenState,
8 Id,
9 IdProvider,
10 IdpError,
11 ProviderOrigin,
12 UserToken,
14 UserTokenState,
15};
16use crate::idprovider::system::{
17 Shadow, SystemAuthResult, SystemProvider, SystemProviderAuthInit, SystemProviderSession,
18};
19use hashbrown::HashMap;
20use kanidm_unix_common::constants::DEFAULT_SHELL_SEARCH_PATHS;
21use kanidm_unix_common::unix_config::{HomeAttr, UidAttr};
22use kanidm_unix_common::unix_passwd::{EtcGroup, EtcShadow, EtcUser};
23use kanidm_unix_common::unix_proto::{
24 HomeDirectoryInfo, NssGroup, NssUser, PamAuthRequest, PamAuthResponse, PamServiceInfo,
25 ProviderStatus,
26};
27use lru::LruCache;
28use std::fmt::Display;
29use std::num::NonZeroUsize;
30use std::ops::DerefMut;
31use std::path::{Path, PathBuf};
32use std::string::ToString;
33use std::sync::Arc;
34use std::time::{Duration, SystemTime};
35use time::OffsetDateTime;
36use tokio::sync::Mutex;
37use uuid::Uuid;
38
39use kanidm_hsm_crypto::BoxedDynTpm;
40
41use tokio::sync::broadcast;
42
43const NXCACHE_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(128) };
44
45pub enum AuthSession {
46 Online {
47 client: Arc<dyn IdProvider + Sync + Send>,
48 account_id: String,
49 id: Id,
50 cred_handler: AuthCredHandler,
51 shutdown_rx: broadcast::Receiver<()>,
56 },
57 Offline {
58 account_id: String,
59 id: Id,
60 client: Arc<dyn IdProvider + Sync + Send>,
61 session_token: Box<UserToken>,
62 cred_handler: AuthCredHandler,
63 },
64 System {
65 account_id: String,
66 id: Id,
67 cred_handler: AuthCredHandler,
68 shadow: Arc<Shadow>,
69 },
70 Success,
71 Denied,
72}
73
74pub struct Resolver {
75 db: Db,
77 hsm: Mutex<BoxedDynTpm>,
78
79 system_provider: Arc<SystemProvider>,
81
82 client_ids: HashMap<ProviderOrigin, Arc<dyn IdProvider + Sync + Send>>,
84
85 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
87
88 primary_origin: ProviderOrigin,
90
91 timeout_seconds: u64,
92 default_shell: String,
93 home_prefix: PathBuf,
94 home_attr: HomeAttr,
95 home_alias: Option<HomeAttr>,
96 uid_attr_map: UidAttr,
97 gid_attr_map: UidAttr,
98 nxcache: Mutex<LruCache<Id, SystemTime>>,
99}
100
101impl Display for Id {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.write_str(&match self {
104 Id::Name(s) => s.to_string(),
105 Id::Gid(g) => g.to_string(),
106 })
107 }
108}
109
110impl Resolver {
111 #[allow(clippy::too_many_arguments)]
112 pub async fn new(
113 db: Db,
114 system_provider: Arc<SystemProvider>,
115 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
116 hsm: BoxedDynTpm,
117 timeout_seconds: u64,
118 default_shell: String,
119 home_prefix: PathBuf,
120 home_attr: HomeAttr,
121 home_alias: Option<HomeAttr>,
122 uid_attr_map: UidAttr,
123 gid_attr_map: UidAttr,
124 ) -> Result<Self, ()> {
125 let hsm = Mutex::new(hsm);
126
127 let primary_origin = clients.first().map(|c| c.origin()).unwrap_or_default();
128
129 let client_ids: HashMap<_, _> = clients
130 .iter()
131 .map(|provider| (provider.origin(), provider.clone()))
132 .collect();
133
134 Ok(Resolver {
137 db,
138 hsm,
139 system_provider,
140 clients,
141 primary_origin,
142 client_ids,
143 timeout_seconds,
144 default_shell,
145 home_prefix,
146 home_attr,
147 home_alias,
148 uid_attr_map,
149 gid_attr_map,
150 nxcache: Mutex::new(LruCache::new(NXCACHE_SIZE)),
151 })
152 }
153
154 #[instrument(level = "debug", skip_all)]
155 pub async fn mark_next_check_now(&self, now: SystemTime) {
156 for c in self.clients.iter() {
157 c.mark_next_check(now).await;
158 }
159 }
160
161 #[instrument(level = "debug", skip_all)]
162 pub async fn mark_offline(&self) {
163 for c in self.clients.iter() {
164 c.mark_offline().await;
165 }
166 }
167
168 #[instrument(level = "debug", skip_all)]
169 pub async fn clear_cache(&self) -> Result<(), ()> {
170 let mut dbtxn = self.db.write().await;
171 let mut nxcache_txn = self.nxcache.lock().await;
172 nxcache_txn.clear();
173 dbtxn.clear().and_then(|_| dbtxn.commit()).map_err(|_| ())
174 }
175
176 #[instrument(level = "debug", skip_all)]
177 pub async fn invalidate(&self) -> Result<(), ()> {
178 let mut dbtxn = self.db.write().await;
179 let mut nxcache_txn = self.nxcache.lock().await;
180 nxcache_txn.clear();
181 dbtxn
182 .invalidate()
183 .and_then(|_| dbtxn.commit())
184 .map_err(|_| ())
185 }
186
187 async fn get_cached_usertokens(&self) -> Result<Vec<UserToken>, ()> {
188 let mut dbtxn = self.db.write().await;
189 dbtxn.get_accounts().map_err(|_| ())
190 }
191
192 async fn get_cached_grouptokens(&self) -> Result<Vec<GroupToken>, ()> {
193 let mut dbtxn = self.db.write().await;
194 dbtxn.get_groups().map_err(|_| ())
195 }
196
197 async fn set_nxcache(&self, id: &Id) {
198 let mut nxcache_txn = self.nxcache.lock().await;
199 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
200 nxcache_txn.put(id.clone(), ex_time);
201 }
202
203 pub async fn check_nxcache(&self, id: &Id) -> Option<SystemTime> {
204 let mut nxcache_txn = self.nxcache.lock().await;
205 nxcache_txn.get(id).copied()
206 }
207
208 #[instrument(level = "info", skip_all)]
209 pub async fn reload_system_identities(
210 &self,
211 users: Vec<EtcUser>,
212 shadow: Vec<EtcShadow>,
213 groups: Vec<EtcGroup>,
214 ) {
215 self.system_provider.reload(users, shadow, groups).await
216 }
217
218 async fn get_cached_usertoken(&self, account_id: &Id) -> Result<(bool, Option<UserToken>), ()> {
219 let mut dbtxn = self.db.write().await;
226 let r = dbtxn.get_account(account_id).map_err(|err| {
227 debug!(?err, "get_cached_usertoken");
228 })?;
229
230 drop(dbtxn);
231
232 match r {
233 Some((ut, ex)) => {
234 let offset = Duration::from_secs(ex);
236 let ex_time = SystemTime::UNIX_EPOCH + offset;
237 let now = SystemTime::now();
238
239 if now >= ex_time {
240 Ok((true, Some(ut)))
241 } else {
242 Ok((false, Some(ut)))
243 }
244 }
245 None => {
246 match self.check_nxcache(account_id).await {
248 Some(ex_time) => {
249 let now = SystemTime::now();
250 if now >= ex_time {
251 Ok((true, None))
254 } else {
255 Ok((false, None))
258 }
259 }
260 None => {
261 Ok((true, None))
264 }
265 }
266 }
267 } }
269
270 async fn get_cached_grouptoken(&self, grp_id: &Id) -> Result<(bool, Option<GroupToken>), ()> {
271 let mut dbtxn = self.db.write().await;
278 let r = dbtxn.get_group(grp_id).map_err(|_| ())?;
279
280 drop(dbtxn);
281
282 match r {
283 Some((ut, ex)) => {
284 let offset = Duration::from_secs(ex);
286 let ex_time = SystemTime::UNIX_EPOCH + offset;
287 let now = SystemTime::now();
288
289 if now >= ex_time {
290 Ok((true, Some(ut)))
291 } else {
292 Ok((false, Some(ut)))
293 }
294 }
295 None => {
296 match self.check_nxcache(grp_id).await {
298 Some(ex_time) => {
299 let now = SystemTime::now();
300 if now >= ex_time {
301 Ok((true, None))
304 } else {
305 Ok((false, None))
308 }
309 }
310 None => {
311 Ok((true, None))
314 }
315 }
316 }
317 }
318 }
319
320 async fn set_cache_usertoken(
321 &self,
322 token: &mut UserToken,
323 _tpm: &mut BoxedDynTpm,
325 ) -> Result<(), ()> {
326 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
328 let offset = ex_time
329 .duration_since(SystemTime::UNIX_EPOCH)
330 .map_err(|e| {
331 error!(
332 "Time conversion error - cache expiry time became less than epoch? {:?}",
333 e
334 );
335 })?;
336
337 let maybe_shell = token.shell.as_ref().map(PathBuf::from);
339
340 let requested_shell_exists = if let Some(shell_path) = maybe_shell.as_ref() {
341 let mut exists = shell_path
343 .canonicalize()
344 .map_err(|err| {
345 debug!(
346 "Failed to canonicalize path, using base path. Tried: {} Error: {:?}",
347 shell_path.to_string_lossy(),
348 err
349 );
350 })
351 .unwrap_or(Path::new(shell_path).to_path_buf())
352 .exists();
353
354 if !exists {
355 if let Some(shell_binary_name) = shell_path.file_name() {
357 for search_path in DEFAULT_SHELL_SEARCH_PATHS {
358 let shell_path = Path::new(search_path).join(shell_binary_name);
360 if shell_path.exists() {
361 let Some(shell_path_utf8) = shell_path.to_str().map(String::from)
368 else {
369 warn!("Configured shell \"{}\" for {} was found but the complete path is not valid utf-8 and can not be used.",
370 shell_binary_name.to_string_lossy(), token.name);
371 continue;
372 };
373
374 token.shell = Some(shell_path_utf8);
376 exists = true;
378 break;
380 }
381 }
382 }
383 }
384
385 if !exists {
386 warn!(
387 "Configured shell \"{}\" for {} is not present on this system. Check `/etc/shells` for valid shell options.",
388 shell_path.to_string_lossy(), token.name
389 )
390 }
391
392 exists
393 } else {
394 info!("User has not specified a shell, using default");
395 false
396 };
397
398 if !requested_shell_exists {
399 token.shell = Some(self.default_shell.clone())
400 }
401
402 let mut dbtxn = self.db.write().await;
403 token
404 .groups
405 .iter()
406 .try_for_each(|g| dbtxn.update_group(g, offset.as_secs()))
408 .and_then(|_|
409 dbtxn
411 .update_account(token, offset.as_secs()))
412 .and_then(|_| dbtxn.commit())
413 .map_err(|_| ())
414 }
415
416 async fn set_cache_grouptoken(&self, token: &GroupToken) -> Result<(), ()> {
417 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
419 let offset = ex_time
420 .duration_since(SystemTime::UNIX_EPOCH)
421 .map_err(|e| {
422 error!("time conversion error - ex_time less than epoch? {:?}", e);
423 })?;
424
425 let mut dbtxn = self.db.write().await;
426 dbtxn
427 .update_group(token, offset.as_secs())
428 .and_then(|_| dbtxn.commit())
429 .map_err(|_| ())
430 }
431
432 async fn delete_cache_usertoken(&self, a_uuid: Uuid) -> Result<(), ()> {
433 let mut dbtxn = self.db.write().await;
434 dbtxn
435 .delete_account(a_uuid)
436 .and_then(|_| dbtxn.commit())
437 .map_err(|_| ())
438 }
439
440 async fn delete_cache_grouptoken(&self, g_uuid: Uuid) -> Result<(), ()> {
441 let mut dbtxn = self.db.write().await;
442 dbtxn
443 .delete_group(g_uuid)
444 .and_then(|_| dbtxn.commit())
445 .map_err(|_| ())
446 }
447
448 async fn refresh_usertoken(
449 &self,
450 account_id: &Id,
451 token: Option<UserToken>,
452 ) -> Result<Option<UserToken>, ()> {
453 let now = SystemTime::now();
455
456 let mut hsm_lock = self.hsm.lock().await;
457
458 let token = if token.is_some() {
463 self.get_cached_usertoken(account_id)
464 .await
465 .map(|(_expired, option_token)| option_token)
466 .map_err(|err| {
467 debug!(?err, "get_usertoken error");
468 })?
469 } else {
470 None
472 };
473
474 let user_get_result = if let Some(tok) = token.as_ref() {
475 match self.client_ids.get(&tok.provider) {
477 Some(client) => {
478 client
479 .unix_user_get(account_id, token.as_ref(), hsm_lock.deref_mut(), now)
480 .await
481 }
482 None => {
483 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
484
485 Ok(UserTokenState::NotFound)
490 }
491 }
492 } else {
493 'search: {
495 for client in self.clients.iter() {
496 match client
497 .unix_user_get(account_id, token.as_ref(), hsm_lock.deref_mut(), now)
498 .await
499 {
500 Ok(UserTokenState::NotFound) => {}
502 result => break 'search result,
503 }
504 }
505 break 'search Ok(UserTokenState::NotFound);
506 }
507 };
508
509 match user_get_result {
510 Ok(UserTokenState::Update(mut n_tok)) => {
511 self.set_cache_usertoken(&mut n_tok, hsm_lock.deref_mut())
513 .await?;
514 Ok(Some(n_tok))
515 }
516 Ok(UserTokenState::NotFound) => {
517 if let Some(tok) = token {
519 self.delete_cache_usertoken(tok.uuid).await?;
520 };
521 self.set_nxcache(account_id).await;
523 Ok(None)
524 }
525 Ok(UserTokenState::UseCached) => Ok(token),
526 Err(err) => {
527 error!(?err);
530 Ok(token)
531 }
532 }
533 }
534
535 async fn refresh_grouptoken(
536 &self,
537 grp_id: &Id,
538 token: Option<GroupToken>,
539 ) -> Result<Option<GroupToken>, ()> {
540 let now = SystemTime::now();
542
543 let mut hsm_lock = self.hsm.lock().await;
544
545 let group_get_result = if let Some(tok) = token.as_ref() {
546 match self.client_ids.get(&tok.provider) {
548 Some(client) => {
549 client
550 .unix_group_get(grp_id, hsm_lock.deref_mut(), now)
551 .await
552 }
553 None => {
554 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
555 Ok(GroupTokenState::NotFound)
560 }
561 }
562 } else {
563 'search: {
565 for client in self.clients.iter() {
566 match client
567 .unix_group_get(grp_id, hsm_lock.deref_mut(), now)
568 .await
569 {
570 Ok(GroupTokenState::NotFound) => {}
572 result => break 'search result,
573 }
574 }
575 break 'search Ok(GroupTokenState::NotFound);
576 }
577 };
578
579 drop(hsm_lock);
580
581 match group_get_result {
582 Ok(GroupTokenState::Update(n_tok)) => {
583 self.set_cache_grouptoken(&n_tok).await?;
584 Ok(Some(n_tok))
585 }
586 Ok(GroupTokenState::NotFound) => {
587 if let Some(tok) = token {
588 self.delete_cache_grouptoken(tok.uuid).await?;
589 };
590 self.set_nxcache(grp_id).await;
592 Ok(None)
593 }
594 Ok(GroupTokenState::UseCached) => Ok(token),
595 Err(err) => {
596 error!(?err);
598 Ok(token)
599 }
600 }
601 }
602
603 #[instrument(level = "debug", skip(self))]
604 async fn get_usertoken(&self, account_id: &Id) -> Result<Option<UserToken>, ()> {
605 let (expired, item) = self.get_cached_usertoken(account_id).await.map_err(|e| {
607 debug!("get_usertoken error -> {:?}", e);
608 })?;
609
610 if expired {
612 self.refresh_usertoken(account_id, item).await
613 } else {
614 Ok(item)
616 }
617 .map(|t| {
618 debug!("token -> {:?}", t);
619 t
620 })
621 }
622
623 #[instrument(level = "debug", skip(self))]
624 async fn get_grouptoken(&self, grp_id: Id) -> Result<Option<GroupToken>, ()> {
625 let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
626 debug!("get_grouptoken error -> {:?}", e);
627 })?;
628
629 if expired {
630 self.refresh_grouptoken(&grp_id, item).await
631 } else {
632 Ok(item)
634 }
635 .map(|t| {
636 debug!("token -> {:?}", t);
637 t
638 })
639 }
640
641 async fn get_groupmembers(&self, g_uuid: Uuid) -> Vec<String> {
642 let mut dbtxn = self.db.write().await;
643
644 dbtxn
645 .get_group_members(g_uuid)
646 .unwrap_or_else(|_| Vec::new())
647 .into_iter()
648 .map(|ut| self.token_uidattr(&ut))
649 .collect()
650 }
651
652 #[instrument(level = "debug", skip(self))]
654 pub async fn get_sshkeys(&self, account_id: &str) -> Result<Vec<String>, ()> {
655 let token = self
656 .get_usertoken(&Id::Name(account_id.to_string()))
657 .await?;
658 Ok(token
659 .map(|t| {
660 if t.valid {
662 t.sshkeys
663 } else {
664 Vec::with_capacity(0)
665 }
666 })
667 .unwrap_or_else(|| Vec::with_capacity(0)))
668 }
669
670 fn token_homedirectory_alias(&self, token: &UserToken) -> Option<String> {
671 let is_primary_origin = token.provider == self.primary_origin;
672 self.home_alias.map(|t| match t {
673 HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
675 HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
676 HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
677 })
678 }
679
680 fn token_homedirectory_attr(&self, token: &UserToken) -> String {
681 let is_primary_origin = token.provider == self.primary_origin;
682 match self.home_attr {
683 HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
684 HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
685 HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
686 }
687 }
688
689 fn token_homedirectory(&self, token: &UserToken) -> String {
690 self.token_homedirectory_alias(token)
691 .unwrap_or_else(|| self.token_homedirectory_attr(token))
692 }
693
694 fn token_abs_homedirectory(&self, token: &UserToken) -> String {
695 self.home_prefix
696 .join(self.token_homedirectory(token))
697 .to_string_lossy()
698 .to_string()
699 }
700
701 fn token_uidattr(&self, token: &UserToken) -> String {
702 let is_primary_origin = token.provider == self.primary_origin;
703 match self.uid_attr_map {
704 UidAttr::Name if is_primary_origin => token.name.as_str(),
705 UidAttr::Spn | UidAttr::Name => token.spn.as_str(),
706 }
707 .to_string()
708 }
709
710 #[instrument(level = "debug", skip_all)]
711 pub async fn get_nssaccounts(&self) -> Result<Vec<NssUser>, ()> {
712 let system_nss_users = self.system_provider.get_nssaccounts().await;
715
716 let cached = self.get_cached_usertokens().await?;
717
718 Ok(system_nss_users
719 .into_iter()
720 .chain(cached.into_iter().map(|tok| NssUser {
721 homedir: self.token_abs_homedirectory(&tok),
722 name: self.token_uidattr(&tok),
723 uid: tok.gidnumber,
724 gid: tok.gidnumber,
725 gecos: tok.displayname,
726 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
727 }))
728 .collect())
729 }
730
731 #[instrument(level = "debug", skip_all)]
732 async fn get_nssaccount(&self, account_id: Id) -> Result<Option<NssUser>, ()> {
733 if let Some(nss_user) = self.system_provider.get_nssaccount(&account_id).await {
734 debug!("system provider satisfied request");
735 return Ok(Some(nss_user));
736 }
737
738 let token = self.get_usertoken(&account_id).await?;
739 Ok(token.map(|tok| NssUser {
740 homedir: self.token_abs_homedirectory(&tok),
741 name: self.token_uidattr(&tok),
742 uid: tok.gidnumber,
743 gid: tok.gidnumber,
744 gecos: tok.displayname,
745 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
746 }))
747 }
748
749 #[instrument(level = "debug", skip(self))]
750 pub async fn get_nssaccount_name(&self, account_id: &str) -> Result<Option<NssUser>, ()> {
751 self.get_nssaccount(Id::Name(account_id.to_string())).await
752 }
753
754 #[instrument(level = "debug", skip(self))]
755 pub async fn get_nssaccount_gid(&self, gid: u32) -> Result<Option<NssUser>, ()> {
756 self.get_nssaccount(Id::Gid(gid)).await
757 }
758
759 fn token_gidattr(&self, token: &GroupToken) -> String {
760 match self.gid_attr_map {
761 UidAttr::Spn => token.spn.as_str(),
762 UidAttr::Name => token.name.as_str(),
763 }
764 .to_string()
765 }
766
767 #[instrument(level = "debug", skip_all)]
768 pub async fn get_nssgroups(&self) -> Result<Vec<NssGroup>, ()> {
769 let mut r = self.system_provider.get_nssgroups().await;
770
771 for nss_group in r.iter_mut() {
773 for client in self.clients.iter() {
774 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
775 let (_, token) = self.get_cached_grouptoken(extend_group_id).await?;
776 if let Some(token) = token {
777 let members = self.get_groupmembers(token.uuid).await;
778 nss_group.members.extend(members);
779 debug!(
780 "extended group {} with members from {}",
781 nss_group.name, token.name
782 );
783 }
784 }
785 }
786 }
787
788 let l = self.get_cached_grouptokens().await?;
789 r.reserve(l.len());
790 for tok in l.into_iter() {
791 let members = self.get_groupmembers(tok.uuid).await;
792 r.push(NssGroup {
793 name: self.token_gidattr(&tok),
794 gid: tok.gidnumber,
795 members,
796 })
797 }
798 Ok(r)
799 }
800
801 async fn get_nssgroup(&self, grp_id: Id) -> Result<Option<NssGroup>, ()> {
802 if let Some(mut nss_group) = self.system_provider.get_nssgroup(&grp_id).await {
803 debug!("system provider satisfied request");
804
805 for client in self.clients.iter() {
806 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
807 let token = self.get_grouptoken(extend_group_id.clone()).await?;
808 if let Some(token) = token {
809 let members = self.get_groupmembers(token.uuid).await;
810 nss_group.members.extend(members);
811 debug!(
812 "extended group {} with members from {}",
813 nss_group.name, token.name
814 );
815 }
816 }
817 }
818
819 nss_group.members.sort_unstable();
820 nss_group.members.dedup();
821
822 return Ok(Some(nss_group));
823 }
824
825 let token = self.get_grouptoken(grp_id).await?;
826 match token {
828 Some(tok) => {
829 let members = self.get_groupmembers(tok.uuid).await;
830 Ok(Some(NssGroup {
831 name: self.token_gidattr(&tok),
832 gid: tok.gidnumber,
833 members,
834 }))
835 }
836 None => Ok(None),
837 }
838 }
839
840 #[instrument(level = "debug", skip(self))]
841 pub async fn get_nssgroup_name(&self, grp_id: &str) -> Result<Option<NssGroup>, ()> {
842 self.get_nssgroup(Id::Name(grp_id.to_string())).await
843 }
844
845 #[instrument(level = "debug", skip(self))]
846 pub async fn get_nssgroup_gid(&self, gid: u32) -> Result<Option<NssGroup>, ()> {
847 self.get_nssgroup(Id::Gid(gid)).await
848 }
849
850 #[instrument(level = "debug", skip(self))]
851 pub async fn pam_account_allowed(&self, account_id: &str) -> Result<Option<bool>, ()> {
852 let id = Id::Name(account_id.to_string());
853
854 if let Some(answer) = self.system_provider.authorise(&id).await {
855 return Ok(Some(answer));
856 };
857
858 let token = self.get_usertoken(&id).await?;
860
861 match token {
863 Some(token) => {
864 let client = self.client_ids.get(&token.provider)
865 .cloned()
866 .ok_or_else(|| {
867 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
868 })?;
869
870 client.unix_user_authorise(&token).await.map_err(|err| {
871 error!(?err, "unable to authorise account");
872 })
873 }
874 None => Ok(None),
875 }
876 }
877
878 #[instrument(level = "debug", skip(self, shutdown_rx))]
879 pub async fn pam_account_authenticate_init(
880 &self,
881 account_id: &str,
882 pam_info: &PamServiceInfo,
883 current_time: OffsetDateTime,
884 shutdown_rx: broadcast::Receiver<()>,
885 ) -> Result<(AuthSession, PamAuthResponse), ()> {
886 let now = SystemTime::now();
892
893 let id = Id::Name(account_id.to_string());
894
895 match self.system_provider.auth_init(&id, current_time).await {
896 SystemProviderAuthInit::Ignore => {
898 debug!(?account_id, "account unknown to system provider, continue.");
899 }
900 SystemProviderAuthInit::ShadowMissing => {
904 warn!(
905 ?account_id,
906 "Resolver unable to proceed, /etc/shadow was not accessible."
907 );
908 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
909 }
910 SystemProviderAuthInit::CredentialsUnavailable => {
912 warn!(
913 ?account_id,
914 "Denying auth request for system user with no valid credentials"
915 );
916 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
917 }
918 SystemProviderAuthInit::Expired => {
920 warn!(
921 ?account_id,
922 "Denying auth request for system user with expired credentials"
923 );
924 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
925 }
926 SystemProviderAuthInit::Begin {
928 next_request,
929 cred_handler,
930 shadow,
931 } => {
932 let auth_session = AuthSession::System {
933 account_id: account_id.to_string(),
934 id,
935 shadow,
936 cred_handler,
937 };
938
939 return Ok((auth_session, next_request.into()));
940 }
941 }
942
943 let token = self.get_usertoken(&id).await?;
944
945 let mut hsm_lock = self.hsm.lock().await;
948
949 if let Some(token) = token {
953 let client = self.client_ids.get(&token.provider)
955 .cloned()
956 .ok_or_else(|| {
957 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
958 })?;
959
960 let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
961 debug!(?online_at_init);
963
964 if online_at_init {
965 let init_result = client
966 .unix_user_online_auth_init(
967 account_id,
968 &token,
969 hsm_lock.deref_mut(),
970 &shutdown_rx,
971 )
972 .await;
973
974 match init_result {
975 Ok((next_req, cred_handler)) => {
976 let auth_session = AuthSession::Online {
977 client,
978 account_id: account_id.to_string(),
979 id,
980 cred_handler,
981 shutdown_rx,
982 };
983 Ok((auth_session, next_req.into()))
984 }
985 Err(err) => {
986 error!(?err, "Unable to start authentication");
987 Err(())
988 }
989 }
990 } else {
991 let init_result = client.unix_user_offline_auth_init(&token).await;
993
994 match init_result {
995 Ok((next_req, cred_handler)) => {
996 let auth_session = AuthSession::Offline {
997 account_id: account_id.to_string(),
998 id,
999 client,
1000 session_token: Box::new(token),
1001 cred_handler,
1002 };
1003 Ok((auth_session, next_req.into()))
1004 }
1005 Err(err) => {
1006 error!(?err, "Unable to start authentication");
1007 Err(())
1008 }
1009 }
1010 }
1011 } else {
1012 for client in self.clients.iter() {
1019 let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1020 debug!(?online_at_init);
1021
1022 if !online_at_init {
1023 warn!(?account_id, "Unable to proceed with authentication, all providers must be online for unknown user authentication.");
1024 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1025 }
1026 }
1027
1028 for client in self.clients.iter() {
1029 let init_result = client
1030 .unix_unknown_user_online_auth_init(
1031 account_id,
1032 hsm_lock.deref_mut(),
1033 &shutdown_rx,
1034 )
1035 .await;
1036
1037 match init_result {
1038 Ok(Some((next_req, cred_handler))) => {
1039 let auth_session = AuthSession::Online {
1040 client: client.clone(),
1041 account_id: account_id.to_string(),
1042 id,
1043 cred_handler,
1044 shutdown_rx,
1045 };
1046 return Ok((auth_session, next_req.into()));
1047 }
1048 Ok(None) => {
1049 }
1051 Err(err) => {
1052 error!(?err, "Unable to start authentication");
1053 return Err(());
1054 }
1055 }
1056 }
1057
1058 warn!("No provider is willing to service authentication of unknown account.");
1060 Ok((AuthSession::Denied, PamAuthResponse::Unknown))
1061 }
1062 }
1063
1064 #[instrument(level = "debug", skip_all)]
1065 pub async fn pam_account_authenticate_step(
1066 &self,
1067 auth_session: &mut AuthSession,
1068 pam_next_req: PamAuthRequest,
1069 ) -> Result<PamAuthResponse, ()> {
1070 let mut hsm_lock = self.hsm.lock().await;
1071
1072 let maybe_err = match &mut *auth_session {
1073 &mut AuthSession::Online {
1074 ref client,
1075 ref account_id,
1076 ref id,
1077 ref mut cred_handler,
1078 ref shutdown_rx,
1079 } => {
1080 let current_token = self
1085 .get_cached_usertoken(id)
1086 .await
1087 .map(|(_expired, option_token)| option_token)
1088 .map_err(|err| {
1089 debug!(?err, "get_usertoken error");
1090 })?;
1091
1092 let result = client
1093 .unix_user_online_auth_step(
1094 account_id,
1095 current_token.as_ref(),
1096 cred_handler,
1097 pam_next_req,
1098 hsm_lock.deref_mut(),
1099 shutdown_rx,
1100 )
1101 .await;
1102
1103 match result {
1104 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1105 info!(?account_id, "Authentication Success");
1106 }
1107 Ok(AuthResult::Denied) => {
1108 info!(?account_id, "Authentication Denied");
1109 }
1110 Ok(AuthResult::Next(_)) => {
1111 info!(?account_id, "Authentication Continue");
1112 }
1113 _ => {}
1114 };
1115
1116 result
1117 }
1118 &mut AuthSession::Offline {
1119 ref account_id,
1120 ref id,
1121 ref client,
1122 ref session_token,
1123 ref mut cred_handler,
1124 } => {
1125 let current_token = self
1130 .get_cached_usertoken(id)
1131 .await
1132 .map(|(_expired, option_token)| option_token)
1133 .map_err(|err| {
1134 debug!(?err, "get_usertoken error");
1135 })?;
1136
1137 let result = client
1140 .unix_user_offline_auth_step(
1141 current_token.as_ref(),
1142 session_token,
1143 cred_handler,
1144 pam_next_req,
1145 hsm_lock.deref_mut(),
1146 )
1147 .await;
1148
1149 match result {
1150 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1151 info!(?account_id, "Authentication Success");
1152 }
1153 Ok(AuthResult::Denied) => {
1154 info!(?account_id, "Authentication Denied");
1155 }
1156 Ok(AuthResult::Next(_)) => {
1157 info!(?account_id, "Authentication Continue");
1158 }
1159 _ => {}
1160 };
1161
1162 result
1163 }
1164 &mut AuthSession::System {
1165 ref account_id,
1166 id: _,
1167 ref mut cred_handler,
1168 ref shadow,
1169 } => {
1170 let system_auth_result = shadow.auth_step(cred_handler, pam_next_req);
1174
1175 let next = match system_auth_result {
1176 SystemAuthResult::Denied => {
1177 info!(?account_id, "Authentication Denied");
1178
1179 *auth_session = AuthSession::Denied;
1180
1181 Ok(PamAuthResponse::Denied)
1182 }
1183 SystemAuthResult::Success => {
1184 info!(?account_id, "Authentication Success");
1185
1186 *auth_session = AuthSession::Success;
1187
1188 Ok(PamAuthResponse::Success)
1189 }
1190 SystemAuthResult::Next(req) => Ok(req.into()),
1191 };
1192
1193 return next;
1195 }
1196 &mut AuthSession::Success | &mut AuthSession::Denied => Err(IdpError::BadRequest),
1197 };
1198
1199 match maybe_err {
1200 Ok(AuthResult::Success) => {
1202 *auth_session = AuthSession::Success;
1203 Ok(PamAuthResponse::Success)
1204 }
1205 Ok(AuthResult::SuccessUpdate { mut new_token }) => {
1206 self.set_cache_usertoken(&mut new_token, hsm_lock.deref_mut())
1207 .await?;
1208 *auth_session = AuthSession::Success;
1209
1210 Ok(PamAuthResponse::Success)
1211 }
1212 Ok(AuthResult::Denied) => {
1213 *auth_session = AuthSession::Denied;
1214
1215 Ok(PamAuthResponse::Denied)
1216 }
1217 Ok(AuthResult::Next(req)) => Ok(req.into()),
1218 Err(IdpError::NotFound) => {
1219 *auth_session = AuthSession::Denied;
1220
1221 Ok(PamAuthResponse::Unknown)
1222 }
1223 Err(err) => {
1224 *auth_session = AuthSession::Denied;
1225
1226 error!(?err, "Unable to proceed, failing the session");
1227 Err(())
1228 }
1229 }
1230 }
1231
1232 #[instrument(level = "debug", skip(self, password))]
1234 pub async fn pam_account_authenticate(
1235 &self,
1236 account_id: &str,
1237 current_time: OffsetDateTime,
1238 password: &str,
1239 ) -> Result<Option<bool>, ()> {
1240 let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
1241
1242 let pam_info = PamServiceInfo {
1243 service: "kanidm-unix-test".to_string(),
1244 tty: Some("/dev/null".to_string()),
1245 rhost: None,
1246 };
1247
1248 let mut auth_session = match self
1249 .pam_account_authenticate_init(account_id, &pam_info, current_time, shutdown_rx)
1250 .await?
1251 {
1252 (auth_session, PamAuthResponse::Password) => {
1253 auth_session
1255 }
1256 (auth_session, PamAuthResponse::DeviceAuthorizationGrant { .. }) => {
1257 auth_session
1259 }
1260 (auth_session, PamAuthResponse::MFACode { .. }) => {
1261 auth_session
1263 }
1264 (auth_session, PamAuthResponse::MFAPoll { .. }) => {
1265 auth_session
1267 }
1268 (auth_session, PamAuthResponse::MFAPollWait) => {
1269 auth_session
1271 }
1272 (auth_session, PamAuthResponse::SetupPin { .. }) => {
1273 auth_session
1275 }
1276 (auth_session, PamAuthResponse::Pin) => {
1277 auth_session
1279 }
1280 (_, PamAuthResponse::Unknown) => return Ok(None),
1281 (_, PamAuthResponse::Denied) => return Ok(Some(false)),
1282 (_, PamAuthResponse::Success) => {
1283 debug_assert!(false);
1285 return Ok(Some(true));
1286 }
1287 };
1288
1289 let pam_next_req = PamAuthRequest::Password {
1291 cred: password.to_string(),
1292 };
1293 match self
1294 .pam_account_authenticate_step(&mut auth_session, pam_next_req)
1295 .await?
1296 {
1297 PamAuthResponse::Success => Ok(Some(true)),
1298 PamAuthResponse::Denied => Ok(Some(false)),
1299 _ => {
1300 Ok(None)
1304 }
1305 }
1306 }
1307
1308 #[instrument(level = "debug", skip(self))]
1309 pub async fn pam_account_beginsession(
1310 &self,
1311 account_id: &str,
1312 ) -> Result<Option<HomeDirectoryInfo>, ()> {
1313 let id = Id::Name(account_id.to_string());
1314
1315 match self.system_provider.begin_session(&id).await {
1316 SystemProviderSession::Start => {
1317 return Ok(None);
1318 }
1319 SystemProviderSession::Ignore => {}
1327 };
1328
1329 let token = self.get_usertoken(&id).await?;
1331 Ok(token.as_ref().map(|tok| HomeDirectoryInfo {
1332 uid: tok.gidnumber,
1333 gid: tok.gidnumber,
1334 name: self.token_homedirectory_attr(tok),
1335 aliases: self
1336 .token_homedirectory_alias(tok)
1337 .map(|s| vec![s])
1338 .unwrap_or_default(),
1339 }))
1340 }
1341
1342 pub async fn provider_status(&self) -> Vec<ProviderStatus> {
1343 let now = SystemTime::now();
1344 let mut hsm_lock = self.hsm.lock().await;
1345
1346 let mut results = Vec::with_capacity(self.clients.len() + 1);
1347
1348 results.push(ProviderStatus {
1349 name: "system".to_string(),
1350 online: true,
1351 });
1352
1353 for client in self.clients.iter() {
1354 let online = client.attempt_online(hsm_lock.deref_mut(), now).await;
1355
1356 let name = client.origin().to_string();
1357
1358 results.push(ProviderStatus { name, online })
1359 }
1360
1361 results
1362 }
1363
1364 #[instrument(level = "debug", skip_all)]
1365 pub async fn test_connection(&self) -> bool {
1366 let now = SystemTime::now();
1367 let mut hsm_lock = self.hsm.lock().await;
1368
1369 for client in self.clients.iter() {
1370 let status = client.attempt_online(hsm_lock.deref_mut(), now).await;
1371
1372 if !status {
1373 return false;
1374 }
1375 }
1376
1377 true
1379 }
1380}