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::{
22 DEFAULT_CACHE_TIMEOUT_JITTER_MS, DEFAULT_CACHE_TIMEOUT_MAXIMUM, DEFAULT_CACHE_TIMEOUT_MINIMUM,
23 DEFAULT_SHELL_SEARCH_PATHS, SYSTEM_SHADOW_PATH,
24};
25use kanidm_unix_common::unix_config::{HomeAttr, UidAttr};
26use kanidm_unix_common::unix_passwd::{EtcGroup, EtcShadow, EtcUser};
27use kanidm_unix_common::unix_proto::{
28 HomeDirectoryInfo, NssGroup, NssUser, PamAuthRequest, PamAuthResponse, PamServiceInfo,
29 ProviderStatus,
30};
31use lru::LruCache;
32use std::fmt::Display;
33use std::num::NonZeroUsize;
34use std::ops::DerefMut;
35use std::path::{Path, PathBuf};
36use std::string::ToString;
37use std::sync::Arc;
38use std::time::{Duration, SystemTime};
39use time::OffsetDateTime;
40use tokio::sync::broadcast;
41use tokio::sync::mpsc;
42use tokio::sync::Mutex;
43use uuid::Uuid;
44
45const NXCACHE_SIZE: NonZeroUsize =
46 NonZeroUsize::new(256).expect("Invalid NXCACHE constant at compile time");
47const ASYNC_REFRESH_QUEUE: usize = 128;
49
50pub enum AuthSession {
51 Online {
52 client: Arc<dyn IdProvider + Sync + Send>,
53 account_id: String,
54 id: Id,
55 cred_handler: AuthCredHandler,
56 shutdown_rx: broadcast::Receiver<()>,
61 },
62 Offline {
63 account_id: String,
64 id: Id,
65 client: Arc<dyn IdProvider + Sync + Send>,
66 session_token: Box<UserToken>,
67 cred_handler: AuthCredHandler,
68 },
69 System {
70 account_id: String,
71 id: Id,
72 cred_handler: AuthCredHandler,
73 shadow: Arc<Shadow>,
74 },
75 Success,
76 Denied,
77}
78
79#[derive(Debug, PartialEq)]
80enum ExpiryState {
82 Valid,
84 ValidRefresh,
87 Expired,
89}
90
91pub struct Resolver {
92 db: Db,
94 hsm: Mutex<BoxedDynTpm>,
95
96 system_provider: Arc<SystemProvider>,
98
99 client_ids: HashMap<ProviderOrigin, Arc<dyn IdProvider + Sync + Send>>,
101
102 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
104
105 primary_origin: ProviderOrigin,
107
108 timeout_seconds: u64,
109 async_refresh_seconds: u64,
110 default_shell: String,
111 home_prefix: PathBuf,
112 home_attr: HomeAttr,
113 home_alias: Option<HomeAttr>,
114 uid_attr_map: UidAttr,
115 gid_attr_map: UidAttr,
116 nxcache: Mutex<LruCache<Id, SystemTime>>,
117 async_refresh_tx: mpsc::Sender<Id>,
118}
119
120impl Display for Id {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.write_str(&match self {
123 Id::Name(s) => s.to_string(),
124 Id::Gid(g) => g.to_string(),
125 })
126 }
127}
128
129impl Resolver {
130 #[allow(clippy::too_many_arguments)]
131 pub async fn new(
132 db: Db,
133 system_provider: Arc<SystemProvider>,
134 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
135 hsm: BoxedDynTpm,
136 timeout_seconds: u64,
137 default_shell: String,
138 home_prefix: PathBuf,
139 home_attr: HomeAttr,
140 home_alias: Option<HomeAttr>,
141 uid_attr_map: UidAttr,
142 gid_attr_map: UidAttr,
143 ) -> Result<(Self, mpsc::Receiver<Id>), ()> {
144 let hsm = Mutex::new(hsm);
145
146 let primary_origin = clients.first().map(|c| c.origin()).unwrap_or_default();
147
148 let client_ids: HashMap<_, _> = clients
149 .iter()
150 .map(|provider| (provider.origin(), provider.clone()))
151 .collect();
152
153 let (async_refresh_tx, async_refresh_rx) = mpsc::channel(ASYNC_REFRESH_QUEUE);
154
155 let timeout_seconds =
161 timeout_seconds.clamp(DEFAULT_CACHE_TIMEOUT_MINIMUM, DEFAULT_CACHE_TIMEOUT_MAXIMUM);
162 let async_refresh_seconds = (timeout_seconds / 3) * 2;
163
164 Ok((
167 Resolver {
168 db,
169 hsm,
170 system_provider,
171 clients,
172 primary_origin,
173 client_ids,
174 timeout_seconds,
175 async_refresh_seconds,
176 default_shell,
177 home_prefix,
178 home_attr,
179 home_alias,
180 uid_attr_map,
181 gid_attr_map,
182 nxcache: Mutex::new(LruCache::new(NXCACHE_SIZE)),
183 async_refresh_tx,
184 },
185 async_refresh_rx,
186 ))
187 }
188
189 #[instrument(level = "debug", skip_all)]
190 pub async fn mark_next_check_now(&self, now: SystemTime) {
191 for c in self.clients.iter() {
192 c.mark_next_check(now).await;
193 }
194 }
195
196 #[instrument(level = "debug", skip_all)]
197 pub async fn mark_offline(&self) {
198 for c in self.clients.iter() {
199 c.mark_offline().await;
200 }
201 }
202
203 #[instrument(level = "debug", skip_all)]
204 pub async fn clear_cache(&self) -> Result<(), ()> {
205 let mut dbtxn = self.db.write().await;
206 let mut nxcache_txn = self.nxcache.lock().await;
207 nxcache_txn.clear();
208 dbtxn.clear().and_then(|_| dbtxn.commit()).map_err(|_| ())
209 }
210
211 #[instrument(level = "debug", skip_all)]
212 pub async fn invalidate(&self) -> Result<(), ()> {
213 let mut dbtxn = self.db.write().await;
214 let mut nxcache_txn = self.nxcache.lock().await;
215 nxcache_txn.clear();
216 dbtxn
217 .invalidate()
218 .and_then(|_| dbtxn.commit())
219 .map_err(|_| ())
220 }
221
222 #[instrument(level = "debug", skip_all)]
223 async fn get_cached_usertokens(&self) -> Result<Vec<UserToken>, ()> {
224 let mut dbtxn = self.db.write().await;
225 dbtxn.get_accounts().map_err(|_| ())
226 }
227
228 #[instrument(level = "debug", skip_all)]
229 async fn get_cached_grouptokens(&self) -> Result<Vec<GroupToken>, ()> {
230 let mut dbtxn = self.db.write().await;
231 dbtxn.get_groups().map_err(|_| ())
232 }
233
234 #[instrument(level = "debug", skip_all)]
235 async fn set_nxcache(&self, id: &Id) {
236 let mut nxcache_txn = self.nxcache.lock().await;
237 let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
240 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
241 - Duration::from_millis(jitter);
242 nxcache_txn.put(id.clone(), ex_time);
243 }
244
245 #[instrument(level = "debug", skip_all)]
246 pub async fn check_nxcache(&self, id: &Id) -> Option<SystemTime> {
247 let mut nxcache_txn = self.nxcache.lock().await;
248 nxcache_txn.get(id).copied()
249 }
250
251 #[instrument(level = "info", skip_all)]
252 pub async fn reload_system_identities(
253 &self,
254 users: Vec<EtcUser>,
255 shadow: Vec<EtcShadow>,
256 groups: Vec<EtcGroup>,
257 ) {
258 self.system_provider.reload(users, shadow, groups).await
259 }
260
261 #[instrument(level = "debug", skip_all)]
262 async fn get_cached_usertoken(
263 &self,
264 account_id: &Id,
265 current_time: SystemTime,
266 ) -> Result<(ExpiryState, Option<UserToken>), ()> {
267 if let Some(ex_time) = self.check_nxcache(account_id).await {
269 if current_time >= ex_time {
270 return Ok((ExpiryState::ValidRefresh, None));
273 } else {
274 return Ok((ExpiryState::Valid, None));
277 }
278 }
279
280 let mut dbtxn = self.db.write().await;
287 let r = dbtxn.get_account(account_id).map_err(|err| {
288 debug!(?err, "get_cached_usertoken");
289 })?;
290
291 drop(dbtxn);
292
293 match r {
294 Some((ut, ex)) => {
295 let ex_time = SystemTime::UNIX_EPOCH + Duration::from_secs(ex);
297
298 let async_ref_time = if ex > self.async_refresh_seconds {
300 SystemTime::UNIX_EPOCH + Duration::from_secs(ex - self.async_refresh_seconds)
301 } else {
302 ex_time
303 };
304
305 if current_time >= ex_time {
306 Ok((ExpiryState::Expired, Some(ut)))
307 } else if current_time >= async_ref_time {
308 Ok((ExpiryState::ValidRefresh, Some(ut)))
309 } else {
310 Ok((ExpiryState::Valid, Some(ut)))
311 }
312 }
313 None => {
314 Ok((ExpiryState::Expired, None))
316 }
317 } }
319
320 #[instrument(level = "debug", skip_all)]
321 async fn get_cached_grouptoken(&self, grp_id: &Id) -> Result<(bool, Option<GroupToken>), ()> {
322 let current_time = SystemTime::now();
323 if let Some(ex_time) = self.check_nxcache(grp_id).await {
325 if current_time >= ex_time {
326 return Ok((true, None));
329 } else {
330 return Ok((false, None));
333 }
334 }
335 let mut dbtxn = self.db.write().await;
342 let r = dbtxn.get_group(grp_id).map_err(|err| {
343 debug!(?err, "get_cached_grouptoken");
344 })?;
345
346 drop(dbtxn);
347
348 match r {
349 Some((ut, ex)) => {
350 let offset = Duration::from_secs(ex);
352 let ex_time = SystemTime::UNIX_EPOCH + offset;
353
354 if current_time >= ex_time {
355 Ok((true, Some(ut)))
356 } else {
357 Ok((false, Some(ut)))
358 }
359 }
360 None => {
361 Ok((true, None))
363 }
364 }
365 }
366
367 #[instrument(level = "debug", skip_all)]
368 async fn set_cache_usertoken(
369 &self,
370 token: &mut UserToken,
371 _tpm: &mut BoxedDynTpm,
373 ) -> Result<(), ()> {
374 let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
378 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
379 - Duration::from_millis(jitter);
380 let offset = ex_time
381 .duration_since(SystemTime::UNIX_EPOCH)
382 .map_err(|e| {
383 error!(
384 "Time conversion error - cache expiry time became less than epoch? {:?}",
385 e
386 );
387 })?;
388
389 let maybe_shell = token.shell.as_ref().map(PathBuf::from);
391
392 let requested_shell_exists = if let Some(shell_path) = maybe_shell.as_ref() {
393 let mut exists = shell_path
395 .canonicalize()
396 .map_err(|err| {
397 debug!(
398 "Failed to canonicalize path, using base path. Tried: {} Error: {:?}",
399 shell_path.to_string_lossy(),
400 err
401 );
402 })
403 .unwrap_or(Path::new(shell_path).to_path_buf())
404 .exists();
405
406 if !exists {
407 if let Some(shell_binary_name) = shell_path.file_name() {
409 for search_path in DEFAULT_SHELL_SEARCH_PATHS {
410 let shell_path = Path::new(search_path).join(shell_binary_name);
412 if shell_path.exists() {
413 let Some(shell_path_utf8) = shell_path.to_str().map(String::from)
420 else {
421 warn!("Configured shell \"{}\" for {} was found but the complete path is not valid utf-8 and can not be used.",
422 shell_binary_name.to_string_lossy(), token.name);
423 continue;
424 };
425
426 token.shell = Some(shell_path_utf8);
428 exists = true;
430 break;
432 }
433 }
434 }
435 }
436
437 if !exists {
438 warn!(
439 "Configured shell \"{}\" for {} is not present on this system. Check `/etc/shells` for valid shell options.",
440 shell_path.to_string_lossy(), token.name
441 )
442 }
443
444 exists
445 } else {
446 info!("User has not specified a shell, using default");
447 false
448 };
449
450 if !requested_shell_exists {
451 token.shell = Some(self.default_shell.clone())
452 }
453
454 let mut dbtxn = self.db.write().await;
455 token
456 .groups
457 .iter()
458 .try_for_each(|g| dbtxn.update_group(g, offset.as_secs()))
460 .and_then(|_|
461 dbtxn
463 .update_account(token, offset.as_secs()))
464 .and_then(|_| dbtxn.commit())
465 .map_err(|_| ())
466 }
467
468 #[instrument(level = "debug", skip_all)]
469 async fn set_cache_grouptoken(&self, token: &GroupToken) -> Result<(), ()> {
470 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
472 let offset = ex_time
473 .duration_since(SystemTime::UNIX_EPOCH)
474 .map_err(|e| {
475 error!("time conversion error - ex_time less than epoch? {:?}", e);
476 })?;
477
478 let mut dbtxn = self.db.write().await;
479 dbtxn
480 .update_group(token, offset.as_secs())
481 .and_then(|_| dbtxn.commit())
482 .map_err(|_| ())
483 }
484
485 #[instrument(level = "debug", skip_all)]
486 async fn delete_cache_usertoken(&self, a_uuid: Uuid) -> Result<(), ()> {
487 let mut dbtxn = self.db.write().await;
488 dbtxn
489 .delete_account(a_uuid)
490 .and_then(|_| dbtxn.commit())
491 .map_err(|_| ())
492 }
493
494 #[instrument(level = "debug", skip_all)]
495 async fn delete_cache_grouptoken(&self, g_uuid: Uuid) -> Result<(), ()> {
496 let mut dbtxn = self.db.write().await;
497 dbtxn
498 .delete_group(g_uuid)
499 .and_then(|_| dbtxn.commit())
500 .map_err(|_| ())
501 }
502
503 #[instrument(level = "debug", skip_all)]
504 pub async fn refresh_usertoken(
505 &self,
506 account_id: &Id,
507 current_time: SystemTime,
508 ) -> Result<Option<UserToken>, ()> {
509 let mut hsm_lock = self.hsm.lock().await;
510
511 let token = self
516 .get_cached_usertoken(account_id, current_time)
517 .await
518 .map(|(_expired, option_token)| option_token)
519 .map_err(|err| {
520 debug!(?err, "get_usertoken error");
521 })?;
522
523 let user_get_result = if let Some(tok) = token.as_ref() {
524 match self.client_ids.get(&tok.provider) {
526 Some(client) => {
527 client
528 .unix_user_get(
529 account_id,
530 token.as_ref(),
531 hsm_lock.deref_mut(),
532 current_time,
533 )
534 .await
535 }
536 None => {
537 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
538
539 Ok(UserTokenState::NotFound)
544 }
545 }
546 } else {
547 'search: {
549 for client in self.clients.iter() {
550 match client
551 .unix_user_get(
552 account_id,
553 token.as_ref(),
554 hsm_lock.deref_mut(),
555 current_time,
556 )
557 .await
558 {
559 Ok(UserTokenState::NotFound) => {}
561 result => break 'search result,
562 }
563 }
564 break 'search Ok(UserTokenState::NotFound);
565 }
566 };
567
568 match user_get_result {
569 Ok(UserTokenState::Update(mut n_tok)) => {
570 self.set_cache_usertoken(&mut n_tok, hsm_lock.deref_mut())
572 .await?;
573 Ok(Some(n_tok))
574 }
575 Ok(UserTokenState::NotFound) => {
576 if let Some(tok) = token {
578 self.delete_cache_usertoken(tok.uuid).await?;
579 };
580 self.set_nxcache(account_id).await;
582 Ok(None)
583 }
584 Ok(UserTokenState::UseCached) => Ok(token),
585 Err(err) => {
586 error!(?err);
589 Ok(token)
590 }
591 }
592 }
593
594 #[instrument(level = "debug", skip_all)]
595 async fn refresh_grouptoken(
596 &self,
597 grp_id: &Id,
598 token: Option<GroupToken>,
599 current_time: SystemTime,
600 ) -> Result<Option<GroupToken>, ()> {
601 let mut hsm_lock = self.hsm.lock().await;
602
603 let group_get_result = if let Some(tok) = token.as_ref() {
604 match self.client_ids.get(&tok.provider) {
606 Some(client) => {
607 client
608 .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
609 .await
610 }
611 None => {
612 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
613 Ok(GroupTokenState::NotFound)
618 }
619 }
620 } else {
621 'search: {
623 for client in self.clients.iter() {
624 match client
625 .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
626 .await
627 {
628 Ok(GroupTokenState::NotFound) => {}
630 result => break 'search result,
631 }
632 }
633 break 'search Ok(GroupTokenState::NotFound);
634 }
635 };
636
637 drop(hsm_lock);
638
639 match group_get_result {
640 Ok(GroupTokenState::Update(n_tok)) => {
641 self.set_cache_grouptoken(&n_tok).await?;
642 Ok(Some(n_tok))
643 }
644 Ok(GroupTokenState::NotFound) => {
645 if let Some(tok) = token {
646 self.delete_cache_grouptoken(tok.uuid).await?;
647 };
648 self.set_nxcache(grp_id).await;
650 Ok(None)
651 }
652 Ok(GroupTokenState::UseCached) => Ok(token),
653 Err(err) => {
654 error!(?err);
656 Ok(token)
657 }
658 }
659 }
660
661 #[instrument(level = "trace", skip_all)]
662 async fn get_usertoken(
663 &self,
664 account_id: &Id,
665 current_time: SystemTime,
666 ) -> Result<Option<UserToken>, ()> {
667 let (expiry_state, item) = self
669 .get_cached_usertoken(account_id, current_time)
670 .await
671 .map_err(|e| {
672 debug!("get_usertoken error -> {:?}", e);
673 })?;
674
675 if expiry_state == ExpiryState::Expired {
677 self.refresh_usertoken(account_id, current_time).await
678 } else {
679 if expiry_state == ExpiryState::ValidRefresh {
680 if self.async_refresh_tx.try_send(account_id.clone()).is_err() {
682 debug!(?account_id, "unable to queue async refresh");
683 }
684 }
685 Ok(item)
687 }
688 .map(|t| {
689 trace!("token -> {:?}", t);
690 t
691 })
692 }
693
694 #[instrument(level = "trace", skip(self))]
695 async fn get_grouptoken(
696 &self,
697 grp_id: Id,
698 current_time: SystemTime,
699 ) -> Result<Option<GroupToken>, ()> {
700 let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
701 debug!("get_grouptoken error -> {:?}", e);
702 })?;
703
704 if expired {
705 self.refresh_grouptoken(&grp_id, item, current_time).await
706 } else {
707 Ok(item)
709 }
710 .map(|t| {
711 trace!("token -> {:?}", t);
712 t
713 })
714 }
715
716 async fn get_groupmembers(&self, g_uuid: Uuid) -> Vec<String> {
717 let mut dbtxn = self.db.write().await;
718
719 dbtxn
720 .get_group_members(g_uuid)
721 .unwrap_or_else(|_| Vec::new())
722 .into_iter()
723 .map(|ut| self.token_uidattr(&ut))
724 .collect()
725 }
726
727 #[instrument(level = "debug", skip(self))]
729 pub async fn get_sshkeys(&self, account_id: &str) -> Result<Vec<String>, ()> {
730 let current_time = SystemTime::now();
731 let token = self
732 .get_usertoken(&Id::Name(account_id.to_string()), current_time)
733 .await?;
734 Ok(token
735 .map(|t| {
736 if t.valid {
738 t.sshkeys
739 } else {
740 Vec::with_capacity(0)
741 }
742 })
743 .unwrap_or_else(|| Vec::with_capacity(0)))
744 }
745
746 fn token_homedirectory_alias(&self, token: &UserToken) -> Option<String> {
747 let is_primary_origin = token.provider == self.primary_origin;
748 self.home_alias.map(|t| match t {
749 HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
751 HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
752 HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
753 })
754 }
755
756 fn token_homedirectory_attr(&self, token: &UserToken) -> String {
757 let is_primary_origin = token.provider == self.primary_origin;
758 match self.home_attr {
759 HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
760 HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
761 HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
762 }
763 }
764
765 fn token_homedirectory(&self, token: &UserToken) -> String {
766 self.token_homedirectory_alias(token)
767 .unwrap_or_else(|| self.token_homedirectory_attr(token))
768 }
769
770 fn token_abs_homedirectory(&self, token: &UserToken) -> String {
771 self.home_prefix
772 .join(self.token_homedirectory(token))
773 .to_string_lossy()
774 .to_string()
775 }
776
777 fn token_uidattr(&self, token: &UserToken) -> String {
778 let is_primary_origin = token.provider == self.primary_origin;
779 match self.uid_attr_map {
780 UidAttr::Name if is_primary_origin => token.name.as_str(),
781 UidAttr::Spn | UidAttr::Name => token.spn.as_str(),
782 }
783 .to_string()
784 }
785
786 #[instrument(level = "debug", skip_all)]
787 pub async fn get_nssaccounts(&self) -> Result<Vec<NssUser>, ()> {
788 let system_nss_users = self.system_provider.get_nssaccounts().await;
791
792 let cached = self.get_cached_usertokens().await?;
793
794 Ok(system_nss_users
795 .into_iter()
796 .chain(cached.into_iter().map(|tok| NssUser {
797 homedir: self.token_abs_homedirectory(&tok),
798 name: self.token_uidattr(&tok),
799 uid: tok.gidnumber,
800 gid: tok.gidnumber,
801 gecos: tok.displayname,
802 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
803 }))
804 .collect())
805 }
806
807 #[instrument(level = "trace", skip_all)]
808 async fn get_nssaccount(
809 &self,
810 account_id: Id,
811 current_time: SystemTime,
812 ) -> Result<Option<NssUser>, ()> {
813 if let Some(nss_user) = self.system_provider.get_nssaccount(&account_id).await {
814 debug!("system provider satisfied request");
815 return Ok(Some(nss_user));
816 }
817
818 let token = self.get_usertoken(&account_id, current_time).await?;
819 Ok(token.map(|tok| NssUser {
820 homedir: self.token_abs_homedirectory(&tok),
821 name: self.token_uidattr(&tok),
822 uid: tok.gidnumber,
823 gid: tok.gidnumber,
824 gecos: tok.displayname,
825 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
826 }))
827 }
828
829 #[instrument(level = "debug", skip(self))]
830 pub async fn get_nssaccount_name_time(
831 &self,
832 account_id: &str,
833 current_time: SystemTime,
834 ) -> Result<Option<NssUser>, ()> {
835 self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
836 .await
837 }
838
839 #[instrument(level = "debug", skip(self))]
840 pub async fn get_nssaccount_name(&self, account_id: &str) -> Result<Option<NssUser>, ()> {
841 let current_time = SystemTime::now();
842 self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
843 .await
844 }
845
846 #[instrument(level = "debug", skip(self))]
847 pub async fn get_nssaccount_gid(&self, gid: u32) -> Result<Option<NssUser>, ()> {
848 let current_time = SystemTime::now();
849 self.get_nssaccount(Id::Gid(gid), current_time).await
850 }
851
852 fn token_gidattr(&self, token: &GroupToken) -> String {
853 match self.gid_attr_map {
854 UidAttr::Spn => token.spn.as_str(),
855 UidAttr::Name => token.name.as_str(),
856 }
857 .to_string()
858 }
859
860 #[instrument(level = "debug", skip_all)]
861 pub async fn get_nssgroups(&self) -> Result<Vec<NssGroup>, ()> {
862 let mut r = self.system_provider.get_nssgroups().await;
863
864 for nss_group in r.iter_mut() {
866 for client in self.clients.iter() {
867 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
868 let (_, token) = self.get_cached_grouptoken(extend_group_id).await?;
869 if let Some(token) = token {
870 let members = self.get_groupmembers(token.uuid).await;
871 nss_group.members.extend(members);
872 debug!(
873 "extended group {} with members from {}",
874 nss_group.name, token.name
875 );
876 }
877 }
878 }
879 }
880
881 let l = self.get_cached_grouptokens().await?;
882 r.reserve(l.len());
883 for tok in l.into_iter() {
884 let members = self.get_groupmembers(tok.uuid).await;
885 r.push(NssGroup {
886 name: self.token_gidattr(&tok),
887 gid: tok.gidnumber,
888 members,
889 })
890 }
891 Ok(r)
892 }
893
894 #[instrument(level = "trace", skip_all)]
895 async fn get_nssgroup(&self, grp_id: Id) -> Result<Option<NssGroup>, ()> {
896 if let Some(mut nss_group) = self.system_provider.get_nssgroup(&grp_id).await {
897 debug!("system provider satisfied request");
898
899 for client in self.clients.iter() {
900 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
901 let token = self
902 .get_grouptoken(extend_group_id.clone(), SystemTime::now())
903 .await?;
904 if let Some(token) = token {
905 let members = self.get_groupmembers(token.uuid).await;
906 nss_group.members.extend(members);
907 debug!(
908 "extended group {} with members from {}",
909 nss_group.name, token.name
910 );
911 }
912 }
913 }
914
915 nss_group.members.sort_unstable();
916 nss_group.members.dedup();
917
918 return Ok(Some(nss_group));
919 }
920
921 let token = self.get_grouptoken(grp_id, SystemTime::now()).await?;
922 match token {
924 Some(tok) => {
925 let members = self.get_groupmembers(tok.uuid).await;
926 Ok(Some(NssGroup {
927 name: self.token_gidattr(&tok),
928 gid: tok.gidnumber,
929 members,
930 }))
931 }
932 None => Ok(None),
933 }
934 }
935
936 #[instrument(level = "debug", skip(self))]
937 pub async fn get_nssgroup_name(&self, grp_id: &str) -> Result<Option<NssGroup>, ()> {
938 self.get_nssgroup(Id::Name(grp_id.to_string())).await
939 }
940
941 #[instrument(level = "debug", skip(self))]
942 pub async fn get_nssgroup_gid(&self, gid: u32) -> Result<Option<NssGroup>, ()> {
943 self.get_nssgroup(Id::Gid(gid)).await
944 }
945
946 #[instrument(level = "debug", skip(self))]
947 pub async fn pam_account_allowed(&self, account_id: &str) -> Result<Option<bool>, ()> {
948 let current_time = SystemTime::now();
949 let id = Id::Name(account_id.to_string());
950
951 if let Some(answer) = self.system_provider.authorise(&id).await {
952 return Ok(Some(answer));
953 };
954
955 let token = self.get_usertoken(&id, current_time).await?;
957
958 match token {
960 Some(token) => {
961 let client = self.client_ids.get(&token.provider)
962 .cloned()
963 .ok_or_else(|| {
964 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
965 })?;
966
967 client.unix_user_authorise(&token).await.map_err(|err| {
968 error!(?err, "unable to authorise account");
969 })
970 }
971 None => Ok(None),
972 }
973 }
974
975 #[instrument(level = "debug", skip(self, shutdown_rx, current_time))]
976 pub async fn pam_account_authenticate_init(
977 &self,
978 account_id: &str,
979 pam_info: &PamServiceInfo,
980 current_time: OffsetDateTime,
981 shutdown_rx: broadcast::Receiver<()>,
982 ) -> Result<(AuthSession, PamAuthResponse), ()> {
983 let now = SystemTime::now();
989
990 let id = Id::Name(account_id.to_string());
991
992 match self.system_provider.auth_init(&id, current_time).await {
993 SystemProviderAuthInit::Ignore => {
995 debug!(?account_id, "account unknown to system provider, continue.");
996 }
997 SystemProviderAuthInit::ShadowMissing => {
1001 warn!(
1002 ?account_id,
1003 "Resolver unable to proceed, {SYSTEM_SHADOW_PATH} was not accessible."
1004 );
1005 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1006 }
1007 SystemProviderAuthInit::CredentialsUnavailable => {
1009 warn!(
1010 ?account_id,
1011 "Denying auth request for system user with no valid credentials"
1012 );
1013 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1014 }
1015 SystemProviderAuthInit::Expired => {
1017 warn!(
1018 ?account_id,
1019 "Denying auth request for system user with expired credentials"
1020 );
1021 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1022 }
1023 SystemProviderAuthInit::Begin {
1025 next_request,
1026 cred_handler,
1027 shadow,
1028 } => {
1029 let auth_session = AuthSession::System {
1030 account_id: account_id.to_string(),
1031 id,
1032 shadow,
1033 cred_handler,
1034 };
1035
1036 return Ok((auth_session, next_request.into()));
1037 }
1038 }
1039
1040 let token = self.get_usertoken(&id, now).await?;
1041
1042 let mut hsm_lock = self.hsm.lock().await;
1045
1046 if let Some(token) = token {
1050 let client = self.client_ids.get(&token.provider)
1052 .cloned()
1053 .ok_or_else(|| {
1054 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
1055 })?;
1056
1057 let can_proceed_offline = client.unix_user_can_offline_auth(&token).await;
1060
1061 let online_at_init = if can_proceed_offline {
1068 client.is_online().await
1070 } else {
1071 client.attempt_online(hsm_lock.deref_mut(), now).await
1074 };
1075
1076 if online_at_init {
1078 let init_result = client
1079 .unix_user_online_auth_init(
1080 account_id,
1081 &token,
1082 hsm_lock.deref_mut(),
1083 &shutdown_rx,
1084 )
1085 .await;
1086
1087 match init_result {
1088 Ok((next_req, cred_handler)) => {
1089 let auth_session = AuthSession::Online {
1090 client,
1091 account_id: account_id.to_string(),
1092 id,
1093 cred_handler,
1094 shutdown_rx,
1095 };
1096 Ok((auth_session, next_req.into()))
1097 }
1098 Err(err) => {
1099 error!(?err, "Unable to start authentication");
1100 Err(())
1101 }
1102 }
1103 } else {
1104 let init_result = client.unix_user_offline_auth_init(&token).await;
1105 match init_result {
1106 Ok((next_req, cred_handler)) => {
1107 let auth_session = AuthSession::Offline {
1108 account_id: account_id.to_string(),
1109 id,
1110 client,
1111 session_token: Box::new(token),
1112 cred_handler,
1113 };
1114 Ok((auth_session, next_req.into()))
1115 }
1116 Err(err) => {
1117 error!(?err, "Unable to start authentication");
1118 Err(())
1119 }
1120 }
1121 }
1122 } else {
1123 for client in self.clients.iter() {
1130 let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1131 debug!(?online_at_init);
1132
1133 if !online_at_init {
1134 warn!(?account_id, "Unable to proceed with authentication, all providers must be online for unknown user authentication.");
1135 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1136 }
1137 }
1138
1139 for client in self.clients.iter() {
1140 let init_result = client
1141 .unix_unknown_user_online_auth_init(
1142 account_id,
1143 hsm_lock.deref_mut(),
1144 &shutdown_rx,
1145 )
1146 .await;
1147
1148 match init_result {
1149 Ok(Some((next_req, cred_handler))) => {
1150 let auth_session = AuthSession::Online {
1151 client: client.clone(),
1152 account_id: account_id.to_string(),
1153 id,
1154 cred_handler,
1155 shutdown_rx,
1156 };
1157 return Ok((auth_session, next_req.into()));
1158 }
1159 Ok(None) => {
1160 }
1162 Err(err) => {
1163 error!(?err, "Unable to start authentication");
1164 return Err(());
1165 }
1166 }
1167 }
1168
1169 warn!("No provider is willing to service authentication of unknown account.");
1171 Ok((AuthSession::Denied, PamAuthResponse::Unknown))
1172 }
1173 }
1174
1175 #[instrument(level = "debug", skip_all)]
1176 pub async fn pam_account_authenticate_step(
1177 &self,
1178 auth_session: &mut AuthSession,
1179 pam_next_req: PamAuthRequest,
1180 ) -> Result<PamAuthResponse, ()> {
1181 let current_time = SystemTime::now();
1182 let mut hsm_lock = self.hsm.lock().await;
1183
1184 let maybe_err = match &mut *auth_session {
1185 &mut AuthSession::Online {
1186 ref client,
1187 ref account_id,
1188 ref id,
1189 ref mut cred_handler,
1190 ref shutdown_rx,
1191 } => {
1192 let current_token = self
1197 .get_cached_usertoken(id, current_time)
1198 .await
1199 .map(|(_expired, option_token)| option_token)
1200 .map_err(|err| {
1201 debug!(?err, "get_usertoken error");
1202 })?;
1203
1204 let result = client
1205 .unix_user_online_auth_step(
1206 account_id,
1207 current_token.as_ref(),
1208 cred_handler,
1209 pam_next_req,
1210 hsm_lock.deref_mut(),
1211 shutdown_rx,
1212 )
1213 .await;
1214
1215 match result {
1216 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1217 info!(?account_id, "Authentication Success");
1218 }
1219 Ok(AuthResult::Denied) => {
1220 info!(?account_id, "Authentication Denied");
1221 }
1222 Ok(AuthResult::Next(_)) => {
1223 info!(?account_id, "Authentication Continue");
1224 }
1225 _ => {}
1226 };
1227
1228 result
1229 }
1230 &mut AuthSession::Offline {
1231 ref account_id,
1232 ref id,
1233 ref client,
1234 ref session_token,
1235 ref mut cred_handler,
1236 } => {
1237 let current_token = self
1242 .get_cached_usertoken(id, current_time)
1243 .await
1244 .map(|(_expired, option_token)| option_token)
1245 .map_err(|err| {
1246 debug!(?err, "get_usertoken error");
1247 })?;
1248
1249 let result = client
1252 .unix_user_offline_auth_step(
1253 current_token.as_ref(),
1254 session_token,
1255 cred_handler,
1256 pam_next_req,
1257 hsm_lock.deref_mut(),
1258 )
1259 .await;
1260
1261 match result {
1262 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1263 info!(?account_id, "Authentication Success");
1264 }
1265 Ok(AuthResult::Denied) => {
1266 info!(?account_id, "Authentication Denied");
1267 }
1268 Ok(AuthResult::Next(_)) => {
1269 info!(?account_id, "Authentication Continue");
1270 }
1271 _ => {}
1272 };
1273
1274 result
1275 }
1276 &mut AuthSession::System {
1277 ref account_id,
1278 id: _,
1279 ref mut cred_handler,
1280 ref shadow,
1281 } => {
1282 let system_auth_result = shadow.auth_step(cred_handler, pam_next_req);
1286
1287 let next = match system_auth_result {
1288 SystemAuthResult::Denied => {
1289 info!(?account_id, "Authentication Denied");
1290
1291 *auth_session = AuthSession::Denied;
1292
1293 Ok(PamAuthResponse::Denied)
1294 }
1295 SystemAuthResult::Success => {
1296 info!(?account_id, "Authentication Success");
1297
1298 *auth_session = AuthSession::Success;
1299
1300 Ok(PamAuthResponse::Success)
1301 }
1302 SystemAuthResult::Next(req) => Ok(req.into()),
1303 };
1304
1305 return next;
1307 }
1308 &mut AuthSession::Success | &mut AuthSession::Denied => Err(IdpError::BadRequest),
1309 };
1310
1311 match maybe_err {
1312 Ok(AuthResult::Success) => {
1314 *auth_session = AuthSession::Success;
1315 Ok(PamAuthResponse::Success)
1316 }
1317 Ok(AuthResult::SuccessUpdate { mut new_token }) => {
1318 self.set_cache_usertoken(&mut new_token, hsm_lock.deref_mut())
1319 .await?;
1320 *auth_session = AuthSession::Success;
1321
1322 Ok(PamAuthResponse::Success)
1323 }
1324 Ok(AuthResult::Denied) => {
1325 *auth_session = AuthSession::Denied;
1326
1327 Ok(PamAuthResponse::Denied)
1328 }
1329 Ok(AuthResult::Next(req)) => Ok(req.into()),
1330 Err(IdpError::NotFound) => {
1331 *auth_session = AuthSession::Denied;
1332
1333 Ok(PamAuthResponse::Unknown)
1334 }
1335 Err(err) => {
1336 *auth_session = AuthSession::Denied;
1337
1338 error!(?err, "Unable to proceed, failing the session");
1339 Err(())
1340 }
1341 }
1342 }
1343
1344 #[instrument(level = "debug", skip(self, password, current_time))]
1346 pub async fn pam_account_authenticate(
1347 &self,
1348 account_id: &str,
1349 current_time: OffsetDateTime,
1350 password: &str,
1351 ) -> Result<Option<bool>, ()> {
1352 let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
1353
1354 let pam_info = PamServiceInfo {
1355 service: "kanidm-unix-test".to_string(),
1356 tty: Some("/dev/null".to_string()),
1357 rhost: None,
1358 };
1359
1360 let mut auth_session = match self
1361 .pam_account_authenticate_init(account_id, &pam_info, current_time, shutdown_rx)
1362 .await?
1363 {
1364 (auth_session, PamAuthResponse::Password) => {
1365 auth_session
1367 }
1368 (auth_session, PamAuthResponse::DeviceAuthorizationGrant { .. }) => {
1369 auth_session
1371 }
1372 (auth_session, PamAuthResponse::MFACode { .. }) => {
1373 auth_session
1375 }
1376 (auth_session, PamAuthResponse::MFAPoll { .. }) => {
1377 auth_session
1379 }
1380 (auth_session, PamAuthResponse::MFAPollWait) => {
1381 auth_session
1383 }
1384 (auth_session, PamAuthResponse::SetupPin { .. }) => {
1385 auth_session
1387 }
1388 (auth_session, PamAuthResponse::Pin) => {
1389 auth_session
1391 }
1392 (_, PamAuthResponse::Unknown) => return Ok(None),
1393 (_, PamAuthResponse::Denied) => return Ok(Some(false)),
1394 (_, PamAuthResponse::Success) => {
1395 debug_assert!(false);
1397 return Ok(Some(true));
1398 }
1399 };
1400
1401 let pam_next_req = PamAuthRequest::Password {
1403 cred: password.to_string(),
1404 };
1405 match self
1406 .pam_account_authenticate_step(&mut auth_session, pam_next_req)
1407 .await?
1408 {
1409 PamAuthResponse::Success => Ok(Some(true)),
1410 PamAuthResponse::Denied => Ok(Some(false)),
1411 _ => {
1412 Ok(None)
1416 }
1417 }
1418 }
1419
1420 #[instrument(level = "debug", skip(self))]
1421 pub async fn pam_account_beginsession(
1422 &self,
1423 account_id: &str,
1424 ) -> Result<Option<HomeDirectoryInfo>, ()> {
1425 let current_time = SystemTime::now();
1426 let id = Id::Name(account_id.to_string());
1427
1428 match self.system_provider.begin_session(&id).await {
1429 SystemProviderSession::Start => {
1430 return Ok(None);
1431 }
1432 SystemProviderSession::Ignore => {}
1440 };
1441
1442 let token = self.get_usertoken(&id, current_time).await?;
1444 Ok(token.as_ref().map(|tok| HomeDirectoryInfo {
1445 uid: tok.gidnumber,
1446 gid: tok.gidnumber,
1447 name: self.token_homedirectory_attr(tok),
1448 aliases: self
1449 .token_homedirectory_alias(tok)
1450 .map(|s| vec![s])
1451 .unwrap_or_default(),
1452 }))
1453 }
1454
1455 #[instrument(level = "debug", skip_all)]
1456 pub async fn provider_status(&self) -> Vec<ProviderStatus> {
1457 let now = SystemTime::now();
1458 let mut hsm_lock = self.hsm.lock().await;
1459
1460 let mut results = Vec::with_capacity(self.clients.len() + 1);
1461
1462 results.push(ProviderStatus {
1463 name: "system".to_string(),
1464 online: true,
1465 });
1466
1467 for client in self.clients.iter() {
1468 let online = client.attempt_online(hsm_lock.deref_mut(), now).await;
1469
1470 let name = client.origin().to_string();
1471
1472 results.push(ProviderStatus { name, online })
1473 }
1474
1475 results
1476 }
1477
1478 #[instrument(level = "debug", skip_all)]
1479 pub async fn test_connection(&self) -> bool {
1480 let now = SystemTime::now();
1481 let mut hsm_lock = self.hsm.lock().await;
1482
1483 for client in self.clients.iter() {
1484 let status = client.attempt_online(hsm_lock.deref_mut(), now).await;
1485
1486 if !status {
1487 return false;
1488 }
1489 }
1490
1491 true
1493 }
1494}