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