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