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