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 online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1058 debug!(?online_at_init);
1060
1061 if online_at_init {
1062 let init_result = client
1063 .unix_user_online_auth_init(
1064 account_id,
1065 &token,
1066 hsm_lock.deref_mut(),
1067 &shutdown_rx,
1068 )
1069 .await;
1070
1071 match init_result {
1072 Ok((next_req, cred_handler)) => {
1073 let auth_session = AuthSession::Online {
1074 client,
1075 account_id: account_id.to_string(),
1076 id,
1077 cred_handler,
1078 shutdown_rx,
1079 };
1080 Ok((auth_session, next_req.into()))
1081 }
1082 Err(err) => {
1083 error!(?err, "Unable to start authentication");
1084 Err(())
1085 }
1086 }
1087 } else {
1088 let init_result = client.unix_user_offline_auth_init(&token).await;
1090
1091 match init_result {
1092 Ok((next_req, cred_handler)) => {
1093 let auth_session = AuthSession::Offline {
1094 account_id: account_id.to_string(),
1095 id,
1096 client,
1097 session_token: Box::new(token),
1098 cred_handler,
1099 };
1100 Ok((auth_session, next_req.into()))
1101 }
1102 Err(err) => {
1103 error!(?err, "Unable to start authentication");
1104 Err(())
1105 }
1106 }
1107 }
1108 } else {
1109 for client in self.clients.iter() {
1116 let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1117 debug!(?online_at_init);
1118
1119 if !online_at_init {
1120 warn!(?account_id, "Unable to proceed with authentication, all providers must be online for unknown user authentication.");
1121 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1122 }
1123 }
1124
1125 for client in self.clients.iter() {
1126 let init_result = client
1127 .unix_unknown_user_online_auth_init(
1128 account_id,
1129 hsm_lock.deref_mut(),
1130 &shutdown_rx,
1131 )
1132 .await;
1133
1134 match init_result {
1135 Ok(Some((next_req, cred_handler))) => {
1136 let auth_session = AuthSession::Online {
1137 client: client.clone(),
1138 account_id: account_id.to_string(),
1139 id,
1140 cred_handler,
1141 shutdown_rx,
1142 };
1143 return Ok((auth_session, next_req.into()));
1144 }
1145 Ok(None) => {
1146 }
1148 Err(err) => {
1149 error!(?err, "Unable to start authentication");
1150 return Err(());
1151 }
1152 }
1153 }
1154
1155 warn!("No provider is willing to service authentication of unknown account.");
1157 Ok((AuthSession::Denied, PamAuthResponse::Unknown))
1158 }
1159 }
1160
1161 #[instrument(level = "debug", skip_all)]
1162 pub async fn pam_account_authenticate_step(
1163 &self,
1164 auth_session: &mut AuthSession,
1165 pam_next_req: PamAuthRequest,
1166 ) -> Result<PamAuthResponse, ()> {
1167 let current_time = SystemTime::now();
1168 let mut hsm_lock = self.hsm.lock().await;
1169
1170 let maybe_err = match &mut *auth_session {
1171 &mut AuthSession::Online {
1172 ref client,
1173 ref account_id,
1174 ref id,
1175 ref mut cred_handler,
1176 ref shutdown_rx,
1177 } => {
1178 let current_token = self
1183 .get_cached_usertoken(id, current_time)
1184 .await
1185 .map(|(_expired, option_token)| option_token)
1186 .map_err(|err| {
1187 debug!(?err, "get_usertoken error");
1188 })?;
1189
1190 let result = client
1191 .unix_user_online_auth_step(
1192 account_id,
1193 current_token.as_ref(),
1194 cred_handler,
1195 pam_next_req,
1196 hsm_lock.deref_mut(),
1197 shutdown_rx,
1198 )
1199 .await;
1200
1201 match result {
1202 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1203 info!(?account_id, "Authentication Success");
1204 }
1205 Ok(AuthResult::Denied) => {
1206 info!(?account_id, "Authentication Denied");
1207 }
1208 Ok(AuthResult::Next(_)) => {
1209 info!(?account_id, "Authentication Continue");
1210 }
1211 _ => {}
1212 };
1213
1214 result
1215 }
1216 &mut AuthSession::Offline {
1217 ref account_id,
1218 ref id,
1219 ref client,
1220 ref session_token,
1221 ref mut cred_handler,
1222 } => {
1223 let current_token = self
1228 .get_cached_usertoken(id, current_time)
1229 .await
1230 .map(|(_expired, option_token)| option_token)
1231 .map_err(|err| {
1232 debug!(?err, "get_usertoken error");
1233 })?;
1234
1235 let result = client
1238 .unix_user_offline_auth_step(
1239 current_token.as_ref(),
1240 session_token,
1241 cred_handler,
1242 pam_next_req,
1243 hsm_lock.deref_mut(),
1244 )
1245 .await;
1246
1247 match result {
1248 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1249 info!(?account_id, "Authentication Success");
1250 }
1251 Ok(AuthResult::Denied) => {
1252 info!(?account_id, "Authentication Denied");
1253 }
1254 Ok(AuthResult::Next(_)) => {
1255 info!(?account_id, "Authentication Continue");
1256 }
1257 _ => {}
1258 };
1259
1260 result
1261 }
1262 &mut AuthSession::System {
1263 ref account_id,
1264 id: _,
1265 ref mut cred_handler,
1266 ref shadow,
1267 } => {
1268 let system_auth_result = shadow.auth_step(cred_handler, pam_next_req);
1272
1273 let next = match system_auth_result {
1274 SystemAuthResult::Denied => {
1275 info!(?account_id, "Authentication Denied");
1276
1277 *auth_session = AuthSession::Denied;
1278
1279 Ok(PamAuthResponse::Denied)
1280 }
1281 SystemAuthResult::Success => {
1282 info!(?account_id, "Authentication Success");
1283
1284 *auth_session = AuthSession::Success;
1285
1286 Ok(PamAuthResponse::Success)
1287 }
1288 SystemAuthResult::Next(req) => Ok(req.into()),
1289 };
1290
1291 return next;
1293 }
1294 &mut AuthSession::Success | &mut AuthSession::Denied => Err(IdpError::BadRequest),
1295 };
1296
1297 match maybe_err {
1298 Ok(AuthResult::Success) => {
1300 *auth_session = AuthSession::Success;
1301 Ok(PamAuthResponse::Success)
1302 }
1303 Ok(AuthResult::SuccessUpdate { mut new_token }) => {
1304 self.set_cache_usertoken(&mut new_token, hsm_lock.deref_mut())
1305 .await?;
1306 *auth_session = AuthSession::Success;
1307
1308 Ok(PamAuthResponse::Success)
1309 }
1310 Ok(AuthResult::Denied) => {
1311 *auth_session = AuthSession::Denied;
1312
1313 Ok(PamAuthResponse::Denied)
1314 }
1315 Ok(AuthResult::Next(req)) => Ok(req.into()),
1316 Err(IdpError::NotFound) => {
1317 *auth_session = AuthSession::Denied;
1318
1319 Ok(PamAuthResponse::Unknown)
1320 }
1321 Err(err) => {
1322 *auth_session = AuthSession::Denied;
1323
1324 error!(?err, "Unable to proceed, failing the session");
1325 Err(())
1326 }
1327 }
1328 }
1329
1330 #[instrument(level = "debug", skip(self, password, current_time))]
1332 pub async fn pam_account_authenticate(
1333 &self,
1334 account_id: &str,
1335 current_time: OffsetDateTime,
1336 password: &str,
1337 ) -> Result<Option<bool>, ()> {
1338 let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
1339
1340 let pam_info = PamServiceInfo {
1341 service: "kanidm-unix-test".to_string(),
1342 tty: Some("/dev/null".to_string()),
1343 rhost: None,
1344 };
1345
1346 let mut auth_session = match self
1347 .pam_account_authenticate_init(account_id, &pam_info, current_time, shutdown_rx)
1348 .await?
1349 {
1350 (auth_session, PamAuthResponse::Password) => {
1351 auth_session
1353 }
1354 (auth_session, PamAuthResponse::DeviceAuthorizationGrant { .. }) => {
1355 auth_session
1357 }
1358 (auth_session, PamAuthResponse::MFACode { .. }) => {
1359 auth_session
1361 }
1362 (auth_session, PamAuthResponse::MFAPoll { .. }) => {
1363 auth_session
1365 }
1366 (auth_session, PamAuthResponse::MFAPollWait) => {
1367 auth_session
1369 }
1370 (auth_session, PamAuthResponse::SetupPin { .. }) => {
1371 auth_session
1373 }
1374 (auth_session, PamAuthResponse::Pin) => {
1375 auth_session
1377 }
1378 (_, PamAuthResponse::Unknown) => return Ok(None),
1379 (_, PamAuthResponse::Denied) => return Ok(Some(false)),
1380 (_, PamAuthResponse::Success) => {
1381 debug_assert!(false);
1383 return Ok(Some(true));
1384 }
1385 };
1386
1387 let pam_next_req = PamAuthRequest::Password {
1389 cred: password.to_string(),
1390 };
1391 match self
1392 .pam_account_authenticate_step(&mut auth_session, pam_next_req)
1393 .await?
1394 {
1395 PamAuthResponse::Success => Ok(Some(true)),
1396 PamAuthResponse::Denied => Ok(Some(false)),
1397 _ => {
1398 Ok(None)
1402 }
1403 }
1404 }
1405
1406 #[instrument(level = "debug", skip(self))]
1407 pub async fn pam_account_beginsession(
1408 &self,
1409 account_id: &str,
1410 ) -> Result<Option<HomeDirectoryInfo>, ()> {
1411 let current_time = SystemTime::now();
1412 let id = Id::Name(account_id.to_string());
1413
1414 match self.system_provider.begin_session(&id).await {
1415 SystemProviderSession::Start => {
1416 return Ok(None);
1417 }
1418 SystemProviderSession::Ignore => {}
1426 };
1427
1428 let token = self.get_usertoken(&id, current_time).await?;
1430 Ok(token.as_ref().map(|tok| HomeDirectoryInfo {
1431 uid: tok.gidnumber,
1432 gid: tok.gidnumber,
1433 name: self.token_homedirectory_attr(tok),
1434 aliases: self
1435 .token_homedirectory_alias(tok)
1436 .map(|s| vec![s])
1437 .unwrap_or_default(),
1438 }))
1439 }
1440
1441 #[instrument(level = "debug", skip_all)]
1442 pub async fn provider_status(&self) -> Vec<ProviderStatus> {
1443 let now = SystemTime::now();
1444 let mut hsm_lock = self.hsm.lock().await;
1445
1446 let mut results = Vec::with_capacity(self.clients.len() + 1);
1447
1448 results.push(ProviderStatus {
1449 name: "system".to_string(),
1450 online: true,
1451 });
1452
1453 for client in self.clients.iter() {
1454 let online = client.attempt_online(hsm_lock.deref_mut(), now).await;
1455
1456 let name = client.origin().to_string();
1457
1458 results.push(ProviderStatus { name, online })
1459 }
1460
1461 results
1462 }
1463
1464 #[instrument(level = "debug", skip_all)]
1465 pub async fn test_connection(&self) -> bool {
1466 let now = SystemTime::now();
1467 let mut hsm_lock = self.hsm.lock().await;
1468
1469 for client in self.clients.iter() {
1470 let status = client.attempt_online(hsm_lock.deref_mut(), now).await;
1471
1472 if !status {
1473 return false;
1474 }
1475 }
1476
1477 true
1479 }
1480}