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
79impl AuthSession {
80 pub fn is_complete(&self) -> bool {
81 match &self {
82 Self::Success | Self::Denied => true,
83 Self::Online { .. } | Self::Offline { .. } | Self::System { .. } => false,
84 }
85 }
86}
87
88#[derive(Debug, PartialEq)]
89enum ExpiryState {
91 Valid,
93 ValidRefresh,
96 Expired,
98}
99
100pub struct Resolver {
101 db: Db,
103 hsm: Mutex<BoxedDynTpm>,
104
105 system_provider: Arc<SystemProvider>,
107
108 client_ids: HashMap<ProviderOrigin, Arc<dyn IdProvider + Sync + Send>>,
110
111 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
113
114 primary_origin: ProviderOrigin,
116
117 timeout_seconds: u64,
118 async_refresh_seconds: u64,
119 default_shell: String,
120 home_prefix: PathBuf,
121 home_attr: HomeAttr,
122 home_alias: Option<HomeAttr>,
123 uid_attr_map: UidAttr,
124 gid_attr_map: UidAttr,
125 nxcache: Mutex<LruCache<Id, SystemTime>>,
126 async_refresh_tx: mpsc::Sender<Id>,
127}
128
129impl Display for Id {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.write_str(&match self {
132 Id::Name(s) => s.to_string(),
133 Id::Gid(g) => g.to_string(),
134 })
135 }
136}
137
138impl Resolver {
139 #[allow(clippy::too_many_arguments)]
140 pub async fn new(
141 db: Db,
142 system_provider: Arc<SystemProvider>,
143 clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
144 hsm: BoxedDynTpm,
145 timeout_seconds: u64,
146 default_shell: String,
147 home_prefix: PathBuf,
148 home_attr: HomeAttr,
149 home_alias: Option<HomeAttr>,
150 uid_attr_map: UidAttr,
151 gid_attr_map: UidAttr,
152 ) -> Result<(Self, mpsc::Receiver<Id>), ()> {
153 let hsm = Mutex::new(hsm);
154
155 let primary_origin = clients.first().map(|c| c.origin()).unwrap_or_default();
156
157 let client_ids: HashMap<_, _> = clients
158 .iter()
159 .map(|provider| (provider.origin(), provider.clone()))
160 .collect();
161
162 let (async_refresh_tx, async_refresh_rx) = mpsc::channel(ASYNC_REFRESH_QUEUE);
163
164 let timeout_seconds =
170 timeout_seconds.clamp(DEFAULT_CACHE_TIMEOUT_MINIMUM, DEFAULT_CACHE_TIMEOUT_MAXIMUM);
171 let async_refresh_seconds = (timeout_seconds / 3) * 2;
172
173 Ok((
176 Resolver {
177 db,
178 hsm,
179 system_provider,
180 clients,
181 primary_origin,
182 client_ids,
183 timeout_seconds,
184 async_refresh_seconds,
185 default_shell,
186 home_prefix,
187 home_attr,
188 home_alias,
189 uid_attr_map,
190 gid_attr_map,
191 nxcache: Mutex::new(LruCache::new(NXCACHE_SIZE)),
192 async_refresh_tx,
193 },
194 async_refresh_rx,
195 ))
196 }
197
198 #[instrument(level = "debug", skip_all)]
199 pub async fn mark_next_check_now(&self, now: SystemTime) {
200 for c in self.clients.iter() {
201 c.mark_next_check(now).await;
202 }
203 }
204
205 #[instrument(level = "debug", skip_all)]
206 pub async fn mark_offline(&self) {
207 for c in self.clients.iter() {
208 c.mark_offline().await;
209 }
210 }
211
212 #[instrument(level = "debug", skip_all)]
213 pub async fn clear_cache(&self) -> Result<(), ()> {
214 let mut dbtxn = self.db.write().await;
215 let mut nxcache_txn = self.nxcache.lock().await;
216 nxcache_txn.clear();
217 dbtxn.clear().and_then(|_| dbtxn.commit()).map_err(|_| ())
218 }
219
220 #[instrument(level = "debug", skip_all)]
221 pub async fn invalidate(&self) -> Result<(), ()> {
222 let mut dbtxn = self.db.write().await;
223 let mut nxcache_txn = self.nxcache.lock().await;
224 nxcache_txn.clear();
225 dbtxn
226 .invalidate()
227 .and_then(|_| dbtxn.commit())
228 .map_err(|_| ())
229 }
230
231 #[instrument(level = "debug", skip_all)]
232 async fn get_cached_usertokens(&self) -> Result<Vec<UserToken>, ()> {
233 let mut dbtxn = self.db.write().await;
234 dbtxn.get_accounts().map_err(|_| ())
235 }
236
237 #[instrument(level = "debug", skip_all)]
238 async fn get_cached_grouptokens(&self) -> Result<Vec<GroupToken>, ()> {
239 let mut dbtxn = self.db.write().await;
240 dbtxn.get_groups().map_err(|_| ())
241 }
242
243 #[instrument(level = "debug", skip_all)]
244 async fn set_nxcache(&self, id: &Id) {
245 let mut nxcache_txn = self.nxcache.lock().await;
246 let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
249 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
250 - Duration::from_millis(jitter);
251 nxcache_txn.put(id.clone(), ex_time);
252 }
253
254 #[instrument(level = "debug", skip_all)]
255 pub async fn check_nxcache(&self, id: &Id) -> Option<SystemTime> {
256 let mut nxcache_txn = self.nxcache.lock().await;
257 nxcache_txn.get(id).copied()
258 }
259
260 #[instrument(level = "info", skip_all)]
261 pub async fn reload_system_identities(
262 &self,
263 users: Vec<EtcUser>,
264 shadow: Vec<EtcShadow>,
265 groups: Vec<EtcGroup>,
266 ) {
267 self.system_provider.reload(users, shadow, groups).await
268 }
269
270 #[instrument(level = "debug", skip_all)]
271 async fn get_cached_usertoken(
272 &self,
273 account_id: &Id,
274 current_time: SystemTime,
275 ) -> Result<(ExpiryState, Option<UserToken>), ()> {
276 if let Some(ex_time) = self.check_nxcache(account_id).await {
278 if current_time >= ex_time {
279 return Ok((ExpiryState::ValidRefresh, None));
282 } else {
283 return Ok((ExpiryState::Valid, None));
286 }
287 }
288
289 let mut dbtxn = self.db.write().await;
296 let r = dbtxn.get_account(account_id).map_err(|err| {
297 debug!(?err, "get_cached_usertoken");
298 })?;
299
300 drop(dbtxn);
301
302 match r {
303 Some((ut, ex)) => {
304 let ex_time = SystemTime::UNIX_EPOCH + Duration::from_secs(ex);
306
307 let async_ref_time = if ex > self.async_refresh_seconds {
309 SystemTime::UNIX_EPOCH + Duration::from_secs(ex - self.async_refresh_seconds)
310 } else {
311 ex_time
312 };
313
314 if current_time >= ex_time {
315 Ok((ExpiryState::Expired, Some(ut)))
316 } else if current_time >= async_ref_time {
317 Ok((ExpiryState::ValidRefresh, Some(ut)))
318 } else {
319 Ok((ExpiryState::Valid, Some(ut)))
320 }
321 }
322 None => {
323 Ok((ExpiryState::Expired, None))
325 }
326 } }
328
329 #[instrument(level = "debug", skip_all)]
330 async fn get_cached_grouptoken(&self, grp_id: &Id) -> Result<(bool, Option<GroupToken>), ()> {
331 let current_time = SystemTime::now();
332 if let Some(ex_time) = self.check_nxcache(grp_id).await {
334 if current_time >= ex_time {
335 return Ok((true, None));
338 } else {
339 return Ok((false, None));
342 }
343 }
344 let mut dbtxn = self.db.write().await;
351 let r = dbtxn.get_group(grp_id).map_err(|err| {
352 debug!(?err, "get_cached_grouptoken");
353 })?;
354
355 drop(dbtxn);
356
357 match r {
358 Some((ut, ex)) => {
359 let offset = Duration::from_secs(ex);
361 let ex_time = SystemTime::UNIX_EPOCH + offset;
362
363 if current_time >= ex_time {
364 Ok((true, Some(ut)))
365 } else {
366 Ok((false, Some(ut)))
367 }
368 }
369 None => {
370 Ok((true, None))
372 }
373 }
374 }
375
376 #[instrument(level = "debug", skip_all)]
377 async fn set_cache_usertoken(
378 &self,
379 token: &mut UserToken,
380 _tpm: &mut BoxedDynTpm,
382 ) -> Result<(), ()> {
383 let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
387 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
388 - Duration::from_millis(jitter);
389 let offset = ex_time
390 .duration_since(SystemTime::UNIX_EPOCH)
391 .map_err(|e| {
392 error!(
393 "Time conversion error - cache expiry time became less than epoch? {:?}",
394 e
395 );
396 })?;
397
398 let maybe_shell = token.shell.as_ref().map(PathBuf::from);
400
401 let requested_shell_exists = if let Some(shell_path) = maybe_shell.as_ref() {
402 let mut exists = shell_path
404 .canonicalize()
405 .map_err(|err| {
406 debug!(
407 "Failed to canonicalize path, using base path. Tried: {} Error: {:?}",
408 shell_path.to_string_lossy(),
409 err
410 );
411 })
412 .unwrap_or(Path::new(shell_path).to_path_buf())
413 .exists();
414
415 if !exists {
416 if let Some(shell_binary_name) = shell_path.file_name() {
418 for search_path in DEFAULT_SHELL_SEARCH_PATHS {
419 let shell_path = Path::new(search_path).join(shell_binary_name);
421 if shell_path.exists() {
422 let Some(shell_path_utf8) = shell_path.to_str().map(String::from)
429 else {
430 warn!("Configured shell \"{}\" for {} was found but the complete path is not valid utf-8 and can not be used.",
431 shell_binary_name.to_string_lossy(), token.name);
432 continue;
433 };
434
435 token.shell = Some(shell_path_utf8);
437 exists = true;
439 break;
441 }
442 }
443 }
444 }
445
446 if !exists {
447 warn!(
448 "Configured shell \"{}\" for {} is not present on this system. Check `/etc/shells` for valid shell options.",
449 shell_path.to_string_lossy(), token.name
450 )
451 }
452
453 exists
454 } else {
455 info!("User has not specified a shell, using default");
456 false
457 };
458
459 if !requested_shell_exists {
460 token.shell = Some(self.default_shell.clone())
461 }
462
463 let mut dbtxn = self.db.write().await;
464 token
465 .groups
466 .iter()
467 .try_for_each(|g| dbtxn.update_group(g, offset.as_secs()))
469 .and_then(|_|
470 dbtxn
472 .update_account(token, offset.as_secs()))
473 .and_then(|_| dbtxn.commit())
474 .map_err(|_| ())
475 }
476
477 #[instrument(level = "debug", skip_all)]
478 async fn set_cache_grouptoken(&self, token: &GroupToken) -> Result<(), ()> {
479 let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
481 let offset = ex_time
482 .duration_since(SystemTime::UNIX_EPOCH)
483 .map_err(|e| {
484 error!("time conversion error - ex_time less than epoch? {:?}", e);
485 })?;
486
487 let mut dbtxn = self.db.write().await;
488 dbtxn
489 .update_group(token, offset.as_secs())
490 .and_then(|_| dbtxn.commit())
491 .map_err(|_| ())
492 }
493
494 #[instrument(level = "debug", skip_all)]
495 async fn delete_cache_usertoken(&self, a_uuid: Uuid) -> Result<(), ()> {
496 let mut dbtxn = self.db.write().await;
497 dbtxn
498 .delete_account(a_uuid)
499 .and_then(|_| dbtxn.commit())
500 .map_err(|_| ())
501 }
502
503 #[instrument(level = "debug", skip_all)]
504 async fn delete_cache_grouptoken(&self, g_uuid: Uuid) -> Result<(), ()> {
505 let mut dbtxn = self.db.write().await;
506 dbtxn
507 .delete_group(g_uuid)
508 .and_then(|_| dbtxn.commit())
509 .map_err(|_| ())
510 }
511
512 #[instrument(level = "debug", skip_all)]
513 pub async fn refresh_usertoken(
514 &self,
515 account_id: &Id,
516 current_time: SystemTime,
517 ) -> Result<Option<UserToken>, ()> {
518 let mut hsm_lock = self.hsm.lock().await;
519
520 let token = self
525 .get_cached_usertoken(account_id, current_time)
526 .await
527 .map(|(_expired, option_token)| option_token)
528 .map_err(|err| {
529 debug!(?err, "get_usertoken error");
530 })?;
531
532 let user_get_result = if let Some(tok) = token.as_ref() {
533 match self.client_ids.get(&tok.provider) {
535 Some(client) => {
536 client
537 .unix_user_get(
538 account_id,
539 token.as_ref(),
540 hsm_lock.deref_mut(),
541 current_time,
542 )
543 .await
544 }
545 None => {
546 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
547
548 Ok(UserTokenState::NotFound)
553 }
554 }
555 } else {
556 'search: {
558 for client in self.clients.iter() {
559 match client
560 .unix_user_get(
561 account_id,
562 token.as_ref(),
563 hsm_lock.deref_mut(),
564 current_time,
565 )
566 .await
567 {
568 Ok(UserTokenState::NotFound) => {}
570 result => break 'search result,
571 }
572 }
573 break 'search Ok(UserTokenState::NotFound);
574 }
575 };
576
577 match user_get_result {
578 Ok(UserTokenState::Update(mut n_tok)) => {
579 self.set_cache_usertoken(&mut n_tok, hsm_lock.deref_mut())
581 .await?;
582 Ok(Some(n_tok))
583 }
584 Ok(UserTokenState::NotFound) => {
585 if let Some(tok) = token {
587 self.delete_cache_usertoken(tok.uuid).await?;
588 };
589 self.set_nxcache(account_id).await;
591 Ok(None)
592 }
593 Ok(UserTokenState::UseCached) => Ok(token),
594 Err(err) => {
595 error!(?err);
598 Ok(token)
599 }
600 }
601 }
602
603 #[instrument(level = "debug", skip_all)]
604 async fn refresh_grouptoken(
605 &self,
606 grp_id: &Id,
607 token: Option<GroupToken>,
608 current_time: SystemTime,
609 ) -> Result<Option<GroupToken>, ()> {
610 let mut hsm_lock = self.hsm.lock().await;
611
612 let group_get_result = if let Some(tok) = token.as_ref() {
613 match self.client_ids.get(&tok.provider) {
615 Some(client) => {
616 client
617 .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
618 .await
619 }
620 None => {
621 error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
622 Ok(GroupTokenState::NotFound)
627 }
628 }
629 } else {
630 'search: {
632 for client in self.clients.iter() {
633 match client
634 .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
635 .await
636 {
637 Ok(GroupTokenState::NotFound) => {}
639 result => break 'search result,
640 }
641 }
642 break 'search Ok(GroupTokenState::NotFound);
643 }
644 };
645
646 drop(hsm_lock);
647
648 match group_get_result {
649 Ok(GroupTokenState::Update(n_tok)) => {
650 self.set_cache_grouptoken(&n_tok).await?;
651 Ok(Some(n_tok))
652 }
653 Ok(GroupTokenState::NotFound) => {
654 if let Some(tok) = token {
655 self.delete_cache_grouptoken(tok.uuid).await?;
656 };
657 self.set_nxcache(grp_id).await;
659 Ok(None)
660 }
661 Ok(GroupTokenState::UseCached) => Ok(token),
662 Err(err) => {
663 error!(?err);
665 Ok(token)
666 }
667 }
668 }
669
670 #[instrument(level = "trace", skip_all)]
671 async fn get_usertoken(
672 &self,
673 account_id: &Id,
674 current_time: SystemTime,
675 ) -> Result<Option<UserToken>, ()> {
676 let (expiry_state, item) = self
678 .get_cached_usertoken(account_id, current_time)
679 .await
680 .map_err(|e| {
681 debug!("get_usertoken error -> {:?}", e);
682 })?;
683
684 if expiry_state == ExpiryState::Expired {
686 self.refresh_usertoken(account_id, current_time).await
687 } else {
688 if expiry_state == ExpiryState::ValidRefresh {
689 if self.async_refresh_tx.try_send(account_id.clone()).is_err() {
691 debug!(?account_id, "unable to queue async refresh");
692 }
693 }
694 Ok(item)
696 }
697 .map(|t| {
698 trace!("token -> {:?}", t);
699 t
700 })
701 }
702
703 #[instrument(level = "trace", skip(self))]
704 async fn get_grouptoken(
705 &self,
706 grp_id: Id,
707 current_time: SystemTime,
708 ) -> Result<Option<GroupToken>, ()> {
709 let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
710 debug!("get_grouptoken error -> {:?}", e);
711 })?;
712
713 if expired {
714 self.refresh_grouptoken(&grp_id, item, current_time).await
715 } else {
716 Ok(item)
718 }
719 .map(|t| {
720 trace!("token -> {:?}", t);
721 t
722 })
723 }
724
725 async fn get_groupmembers(&self, g_uuid: Uuid) -> Vec<String> {
726 let mut dbtxn = self.db.write().await;
727
728 dbtxn
729 .get_group_members(g_uuid)
730 .unwrap_or_else(|_| Vec::new())
731 .into_iter()
732 .map(|ut| self.token_uidattr(&ut))
733 .collect()
734 }
735
736 #[instrument(level = "debug", skip(self))]
738 pub async fn get_sshkeys(&self, account_id: &str) -> Result<Vec<String>, ()> {
739 let current_time = SystemTime::now();
740 let token = self
741 .get_usertoken(&Id::Name(account_id.to_string()), current_time)
742 .await?;
743 Ok(token
744 .map(|t| {
745 if t.valid {
747 t.sshkeys
748 } else {
749 Vec::with_capacity(0)
750 }
751 })
752 .unwrap_or_else(|| Vec::with_capacity(0)))
753 }
754
755 fn token_homedirectory_alias(&self, token: &UserToken) -> Option<String> {
756 let is_primary_origin = token.provider == self.primary_origin;
757 self.home_alias.map(|t| match t {
758 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_attr(&self, token: &UserToken) -> String {
766 let is_primary_origin = token.provider == self.primary_origin;
767 match self.home_attr {
768 HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
769 HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
770 HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
771 }
772 }
773
774 fn token_homedirectory(&self, token: &UserToken) -> String {
775 self.token_homedirectory_alias(token)
776 .unwrap_or_else(|| self.token_homedirectory_attr(token))
777 }
778
779 fn token_abs_homedirectory(&self, token: &UserToken) -> String {
780 self.home_prefix
781 .join(self.token_homedirectory(token))
782 .to_string_lossy()
783 .to_string()
784 }
785
786 fn token_uidattr(&self, token: &UserToken) -> String {
787 let is_primary_origin = token.provider == self.primary_origin;
788 match self.uid_attr_map {
789 UidAttr::Name if is_primary_origin => token.name.as_str(),
790 UidAttr::Spn | UidAttr::Name => token.spn.as_str(),
791 }
792 .to_string()
793 }
794
795 #[instrument(level = "debug", skip_all)]
796 pub async fn get_nssaccounts(&self) -> Result<Vec<NssUser>, ()> {
797 let system_nss_users = self.system_provider.get_nssaccounts().await;
800
801 let cached = self.get_cached_usertokens().await?;
802
803 Ok(system_nss_users
804 .into_iter()
805 .chain(cached.into_iter().map(|tok| NssUser {
806 homedir: self.token_abs_homedirectory(&tok),
807 name: self.token_uidattr(&tok),
808 uid: tok.gidnumber,
809 gid: tok.gidnumber,
810 gecos: tok.displayname,
811 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
812 }))
813 .collect())
814 }
815
816 #[instrument(level = "trace", skip_all)]
817 async fn get_nssaccount(
818 &self,
819 account_id: Id,
820 current_time: SystemTime,
821 ) -> Result<Option<NssUser>, ()> {
822 if let Some(nss_user) = self.system_provider.get_nssaccount(&account_id).await {
823 debug!("system provider satisfied request");
824 return Ok(Some(nss_user));
825 }
826
827 let token = self.get_usertoken(&account_id, current_time).await?;
828 Ok(token.map(|tok| NssUser {
829 homedir: self.token_abs_homedirectory(&tok),
830 name: self.token_uidattr(&tok),
831 uid: tok.gidnumber,
832 gid: tok.gidnumber,
833 gecos: tok.displayname,
834 shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
835 }))
836 }
837
838 #[instrument(level = "debug", skip(self))]
839 pub async fn get_nssaccount_name_time(
840 &self,
841 account_id: &str,
842 current_time: SystemTime,
843 ) -> Result<Option<NssUser>, ()> {
844 self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
845 .await
846 }
847
848 #[instrument(level = "debug", skip(self))]
849 pub async fn get_nssaccount_name(&self, account_id: &str) -> Result<Option<NssUser>, ()> {
850 let current_time = SystemTime::now();
851 self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
852 .await
853 }
854
855 #[instrument(level = "debug", skip(self))]
856 pub async fn get_nssaccount_gid(&self, gid: u32) -> Result<Option<NssUser>, ()> {
857 let current_time = SystemTime::now();
858 self.get_nssaccount(Id::Gid(gid), current_time).await
859 }
860
861 fn token_gidattr(&self, token: &GroupToken) -> String {
862 match self.gid_attr_map {
863 UidAttr::Spn => token.spn.as_str(),
864 UidAttr::Name => token.name.as_str(),
865 }
866 .to_string()
867 }
868
869 #[instrument(level = "debug", skip_all)]
870 pub async fn get_nssgroups(&self) -> Result<Vec<NssGroup>, ()> {
871 let mut r = self.system_provider.get_nssgroups().await;
872
873 for nss_group in r.iter_mut() {
875 for client in self.clients.iter() {
876 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
877 let (_, token) = self.get_cached_grouptoken(extend_group_id).await?;
878 if let Some(token) = token {
879 let members = self.get_groupmembers(token.uuid).await;
880 nss_group.members.extend(members);
881 debug!(
882 "extended group {} with members from {}",
883 nss_group.name, token.name
884 );
885 }
886 }
887 }
888 }
889
890 let l = self.get_cached_grouptokens().await?;
891 r.reserve(l.len());
892 for tok in l.into_iter() {
893 let members = self.get_groupmembers(tok.uuid).await;
894 r.push(NssGroup {
895 name: self.token_gidattr(&tok),
896 gid: tok.gidnumber,
897 members,
898 })
899 }
900 Ok(r)
901 }
902
903 #[instrument(level = "trace", skip_all)]
904 async fn get_nssgroup(&self, grp_id: Id) -> Result<Option<NssGroup>, ()> {
905 if let Some(mut nss_group) = self.system_provider.get_nssgroup(&grp_id).await {
906 debug!("system provider satisfied request");
907
908 for client in self.clients.iter() {
909 if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
910 let token = self
911 .get_grouptoken(extend_group_id.clone(), SystemTime::now())
912 .await?;
913 if let Some(token) = token {
914 let members = self.get_groupmembers(token.uuid).await;
915 nss_group.members.extend(members);
916 debug!(
917 "extended group {} with members from {}",
918 nss_group.name, token.name
919 );
920 }
921 }
922 }
923
924 nss_group.members.sort_unstable();
925 nss_group.members.dedup();
926
927 return Ok(Some(nss_group));
928 }
929
930 let token = self.get_grouptoken(grp_id, SystemTime::now()).await?;
931 match token {
933 Some(tok) => {
934 let members = self.get_groupmembers(tok.uuid).await;
935 Ok(Some(NssGroup {
936 name: self.token_gidattr(&tok),
937 gid: tok.gidnumber,
938 members,
939 }))
940 }
941 None => Ok(None),
942 }
943 }
944
945 #[instrument(level = "debug", skip(self))]
946 pub async fn get_nssgroup_name(&self, grp_id: &str) -> Result<Option<NssGroup>, ()> {
947 self.get_nssgroup(Id::Name(grp_id.to_string())).await
948 }
949
950 #[instrument(level = "debug", skip(self))]
951 pub async fn get_nssgroup_gid(&self, gid: u32) -> Result<Option<NssGroup>, ()> {
952 self.get_nssgroup(Id::Gid(gid)).await
953 }
954
955 #[instrument(level = "debug", skip(self))]
956 pub async fn pam_account_allowed(&self, account_id: &str) -> Result<Option<bool>, ()> {
957 let current_time = SystemTime::now();
958 let id = Id::Name(account_id.to_string());
959
960 if let Some(answer) = self.system_provider.authorise(&id).await {
961 return Ok(Some(answer));
962 };
963
964 let token = self.get_usertoken(&id, current_time).await?;
966
967 match token {
969 Some(token) => {
970 let client = self.client_ids.get(&token.provider)
971 .cloned()
972 .ok_or_else(|| {
973 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
974 })?;
975
976 client.unix_user_authorise(&token).await.map_err(|err| {
977 error!(?err, "unable to authorise account");
978 })
979 }
980 None => Ok(None),
981 }
982 }
983
984 #[instrument(level = "debug", skip(self, shutdown_rx, current_time))]
985 pub async fn pam_account_authenticate_init(
986 &self,
987 account_id: &str,
988 pam_info: &PamServiceInfo,
989 current_time: OffsetDateTime,
990 shutdown_rx: broadcast::Receiver<()>,
991 ) -> Result<(AuthSession, PamAuthResponse), ()> {
992 let now = SystemTime::now();
998
999 let id = Id::Name(account_id.to_string());
1000
1001 match self.system_provider.auth_init(&id, current_time).await {
1002 SystemProviderAuthInit::Ignore => {
1004 debug!(?account_id, "account unknown to system provider, continue.");
1005 }
1006 SystemProviderAuthInit::ShadowMissing => {
1010 warn!(
1011 ?account_id,
1012 "Resolver unable to proceed, {SYSTEM_SHADOW_PATH} was not accessible."
1013 );
1014 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1015 }
1016 SystemProviderAuthInit::CredentialsUnavailable => {
1018 warn!(
1019 ?account_id,
1020 "Denying auth request for system user with no valid credentials"
1021 );
1022 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1023 }
1024 SystemProviderAuthInit::Expired => {
1026 warn!(
1027 ?account_id,
1028 "Denying auth request for system user with expired credentials"
1029 );
1030 return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1031 }
1032 SystemProviderAuthInit::Begin {
1034 next_request,
1035 cred_handler,
1036 shadow,
1037 } => {
1038 let auth_session = AuthSession::System {
1039 account_id: account_id.to_string(),
1040 id,
1041 shadow,
1042 cred_handler,
1043 };
1044
1045 return Ok((auth_session, next_request.into()));
1046 }
1047 }
1048
1049 let token = self.get_usertoken(&id, now).await?;
1050
1051 let mut hsm_lock = self.hsm.lock().await;
1054
1055 if let Some(token) = token {
1059 let client = self.client_ids.get(&token.provider)
1061 .cloned()
1062 .ok_or_else(|| {
1063 error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
1064 })?;
1065
1066 let can_proceed_offline = client.unix_user_can_offline_auth(&token).await;
1069
1070 let online_at_init = if can_proceed_offline {
1077 client.is_online().await
1079 } else {
1080 client.attempt_online(hsm_lock.deref_mut(), now).await
1083 };
1084
1085 if online_at_init {
1087 let init_result = client
1088 .unix_user_online_auth_init(
1089 account_id,
1090 &token,
1091 hsm_lock.deref_mut(),
1092 &shutdown_rx,
1093 )
1094 .await;
1095
1096 match init_result {
1097 Ok((next_req, cred_handler)) => {
1098 let auth_session = AuthSession::Online {
1099 client,
1100 account_id: account_id.to_string(),
1101 id,
1102 cred_handler,
1103 shutdown_rx,
1104 };
1105 Ok((auth_session, next_req.into()))
1106 }
1107 Err(err) => {
1108 error!(?err, "Unable to start authentication");
1109 Err(())
1110 }
1111 }
1112 } else {
1113 let init_result = client.unix_user_offline_auth_init(&token).await;
1114 match init_result {
1115 Ok((next_req, cred_handler)) => {
1116 let auth_session = AuthSession::Offline {
1117 account_id: account_id.to_string(),
1118 id,
1119 client,
1120 session_token: Box::new(token),
1121 cred_handler,
1122 };
1123 Ok((auth_session, next_req.into()))
1124 }
1125 Err(err) => {
1126 error!(?err, "Unable to start authentication");
1127 Err(())
1128 }
1129 }
1130 }
1131 } else {
1132 for client in self.clients.iter() {
1139 let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1140 debug!(?online_at_init);
1141
1142 if !online_at_init {
1143 warn!(?account_id, "Unable to proceed with authentication, all providers must be online for unknown user authentication.");
1144 return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1145 }
1146 }
1147
1148 for client in self.clients.iter() {
1149 let init_result = client
1150 .unix_unknown_user_online_auth_init(
1151 account_id,
1152 hsm_lock.deref_mut(),
1153 &shutdown_rx,
1154 )
1155 .await;
1156
1157 match init_result {
1158 Ok(Some((next_req, cred_handler))) => {
1159 let auth_session = AuthSession::Online {
1160 client: client.clone(),
1161 account_id: account_id.to_string(),
1162 id,
1163 cred_handler,
1164 shutdown_rx,
1165 };
1166 return Ok((auth_session, next_req.into()));
1167 }
1168 Ok(None) => {
1169 }
1171 Err(err) => {
1172 error!(?err, "Unable to start authentication");
1173 return Err(());
1174 }
1175 }
1176 }
1177
1178 warn!("No provider is willing to service authentication of unknown account.");
1180 Ok((AuthSession::Denied, PamAuthResponse::Unknown))
1181 }
1182 }
1183
1184 #[instrument(level = "debug", skip_all)]
1185 pub async fn pam_account_authenticate_step(
1186 &self,
1187 auth_session: &mut AuthSession,
1188 pam_next_req: PamAuthRequest,
1189 ) -> Result<PamAuthResponse, ()> {
1190 let current_time = SystemTime::now();
1191 let mut hsm_lock = self.hsm.lock().await;
1192
1193 let maybe_err = match &mut *auth_session {
1194 &mut AuthSession::Online {
1195 ref client,
1196 ref account_id,
1197 ref id,
1198 ref mut cred_handler,
1199 ref shutdown_rx,
1200 } => {
1201 let current_token = self
1206 .get_cached_usertoken(id, current_time)
1207 .await
1208 .map(|(_expired, option_token)| option_token)
1209 .map_err(|err| {
1210 debug!(?err, "get_usertoken error");
1211 })?;
1212
1213 let result = client
1214 .unix_user_online_auth_step(
1215 account_id,
1216 current_token.as_ref(),
1217 cred_handler,
1218 pam_next_req,
1219 hsm_lock.deref_mut(),
1220 shutdown_rx,
1221 )
1222 .await;
1223
1224 match result {
1225 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1226 info!(?account_id, "Authentication Success");
1227 }
1228 Ok(AuthResult::Denied) => {
1229 info!(?account_id, "Authentication Denied");
1230 }
1231 Ok(AuthResult::Next(_)) => {
1232 info!(?account_id, "Authentication Continue");
1233 }
1234 _ => {}
1235 };
1236
1237 result
1238 }
1239 &mut AuthSession::Offline {
1240 ref account_id,
1241 ref id,
1242 ref client,
1243 ref session_token,
1244 ref mut cred_handler,
1245 } => {
1246 let current_token = self
1251 .get_cached_usertoken(id, current_time)
1252 .await
1253 .map(|(_expired, option_token)| option_token)
1254 .map_err(|err| {
1255 debug!(?err, "get_usertoken error");
1256 })?;
1257
1258 let result = client
1261 .unix_user_offline_auth_step(
1262 current_token.as_ref(),
1263 session_token,
1264 cred_handler,
1265 pam_next_req,
1266 hsm_lock.deref_mut(),
1267 )
1268 .await;
1269
1270 match result {
1271 Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1272 info!(?account_id, "Authentication Success");
1273 }
1274 Ok(AuthResult::Denied) => {
1275 info!(?account_id, "Authentication Denied");
1276 }
1277 Ok(AuthResult::Next(_)) => {
1278 info!(?account_id, "Authentication Continue");
1279 }
1280 _ => {}
1281 };
1282
1283 result
1284 }
1285 &mut AuthSession::System {
1286 ref account_id,
1287 id: _,
1288 ref mut cred_handler,
1289 ref shadow,
1290 } => {
1291 let system_auth_result = shadow.auth_step(cred_handler, pam_next_req);
1295
1296 let next = match system_auth_result {
1297 SystemAuthResult::Denied => {
1298 info!(?account_id, "Authentication Denied");
1299
1300 *auth_session = AuthSession::Denied;
1301
1302 Ok(PamAuthResponse::Denied)
1303 }
1304 SystemAuthResult::Success => {
1305 info!(?account_id, "Authentication Success");
1306
1307 *auth_session = AuthSession::Success;
1308
1309 Ok(PamAuthResponse::Success)
1310 }
1311 SystemAuthResult::Next(req) => Ok(req.into()),
1312 };
1313
1314 return next;
1316 }
1317 &mut AuthSession::Success | &mut AuthSession::Denied => Err(IdpError::BadRequest),
1318 };
1319
1320 match maybe_err {
1321 Ok(AuthResult::Success) => {
1323 *auth_session = AuthSession::Success;
1324 Ok(PamAuthResponse::Success)
1325 }
1326 Ok(AuthResult::SuccessUpdate { mut new_token }) => {
1327 self.set_cache_usertoken(&mut new_token, hsm_lock.deref_mut())
1328 .await?;
1329 *auth_session = AuthSession::Success;
1330
1331 Ok(PamAuthResponse::Success)
1332 }
1333 Ok(AuthResult::Denied) => {
1334 *auth_session = AuthSession::Denied;
1335
1336 Ok(PamAuthResponse::Denied)
1337 }
1338 Ok(AuthResult::Next(req)) => Ok(req.into()),
1339 Err(IdpError::NotFound) => {
1340 *auth_session = AuthSession::Denied;
1341
1342 Ok(PamAuthResponse::Unknown)
1343 }
1344 Err(err) => {
1345 *auth_session = AuthSession::Denied;
1346
1347 error!(?err, "Unable to proceed, failing the session");
1348 Err(())
1349 }
1350 }
1351 }
1352
1353 #[instrument(level = "debug", skip(self, password, current_time))]
1355 pub async fn pam_account_authenticate(
1356 &self,
1357 account_id: &str,
1358 current_time: OffsetDateTime,
1359 password: &str,
1360 ) -> Result<Option<bool>, ()> {
1361 let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
1362
1363 let pam_info = PamServiceInfo {
1364 service: "kanidm-unix-test".to_string(),
1365 tty: Some("/dev/null".to_string()),
1366 rhost: None,
1367 };
1368
1369 let mut auth_session = match self
1370 .pam_account_authenticate_init(account_id, &pam_info, current_time, shutdown_rx)
1371 .await?
1372 {
1373 (auth_session, PamAuthResponse::Password) => {
1374 auth_session
1376 }
1377 (auth_session, PamAuthResponse::DeviceAuthorizationGrant { .. }) => {
1378 auth_session
1380 }
1381 (auth_session, PamAuthResponse::MFACode { .. }) => {
1382 auth_session
1384 }
1385 (auth_session, PamAuthResponse::MFAPoll { .. }) => {
1386 auth_session
1388 }
1389 (auth_session, PamAuthResponse::MFAPollWait) => {
1390 auth_session
1392 }
1393 (auth_session, PamAuthResponse::SetupPin { .. }) => {
1394 auth_session
1396 }
1397 (auth_session, PamAuthResponse::Pin) => {
1398 auth_session
1400 }
1401 (_, PamAuthResponse::Unknown) => return Ok(None),
1402 (_, PamAuthResponse::Denied) => return Ok(Some(false)),
1403 (_, PamAuthResponse::Success) => {
1404 debug_assert!(false);
1406 return Ok(Some(true));
1407 }
1408 };
1409
1410 let pam_next_req = PamAuthRequest::Password {
1412 cred: password.to_string(),
1413 };
1414 match self
1415 .pam_account_authenticate_step(&mut auth_session, pam_next_req)
1416 .await?
1417 {
1418 PamAuthResponse::Success => Ok(Some(true)),
1419 PamAuthResponse::Denied => Ok(Some(false)),
1420 _ => {
1421 Ok(None)
1425 }
1426 }
1427 }
1428
1429 #[instrument(level = "debug", skip(self))]
1430 pub async fn pam_account_beginsession(
1431 &self,
1432 account_id: &str,
1433 ) -> Result<Option<HomeDirectoryInfo>, ()> {
1434 let current_time = SystemTime::now();
1435 let id = Id::Name(account_id.to_string());
1436
1437 match self.system_provider.begin_session(&id).await {
1438 SystemProviderSession::Start => {
1439 return Ok(None);
1440 }
1441 SystemProviderSession::Ignore => {}
1449 };
1450
1451 let token = self.get_usertoken(&id, current_time).await?;
1453 Ok(token.as_ref().map(|tok| HomeDirectoryInfo {
1454 uid: tok.gidnumber,
1455 gid: tok.gidnumber,
1456 name: self.token_homedirectory_attr(tok),
1457 aliases: self
1458 .token_homedirectory_alias(tok)
1459 .map(|s| vec![s])
1460 .unwrap_or_default(),
1461 }))
1462 }
1463
1464 #[instrument(level = "debug", skip_all)]
1465 pub async fn provider_status(&self) -> Vec<ProviderStatus> {
1466 let now = SystemTime::now();
1467 let mut hsm_lock = self.hsm.lock().await;
1468
1469 let mut results = Vec::with_capacity(self.clients.len() + 1);
1470
1471 results.push(ProviderStatus {
1472 name: "system".to_string(),
1473 online: true,
1474 });
1475
1476 for client in self.clients.iter() {
1477 let online = client.attempt_online(hsm_lock.deref_mut(), now).await;
1478
1479 let name = client.origin().to_string();
1480
1481 results.push(ProviderStatus { name, online })
1482 }
1483
1484 results
1485 }
1486
1487 #[instrument(level = "debug", skip_all)]
1488 pub async fn test_connection(&self) -> bool {
1489 let now = SystemTime::now();
1490 let mut hsm_lock = self.hsm.lock().await;
1491
1492 for client in self.clients.iter() {
1493 let status = client.attempt_online(hsm_lock.deref_mut(), now).await;
1494
1495 if !status {
1496 return false;
1497 }
1498 }
1499
1500 true
1502 }
1503}