Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Kerberos CCache support #286

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ tracing-subscriber = "^0.3.17"
tracing = "^0.1.37"
himmelblau_unix_common = { path = "src/common" }
kanidm_unix_common = { path = "src/glue" }
libhimmelblau = { version = "0.3.6" }
libhimmelblau = { version = "0.3.8" }
clap = { version = "^4.5", features = ["derive", "env"] }
clap_complete = "^4.4.1"
reqwest = { version = "^0.12.2", features = ["json"] }
Expand Down
3 changes: 1 addition & 2 deletions platform/debian/himmelblaud-tasks.service
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ ExecStart=/usr/sbin/himmelblaud_tasks
CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH
# SystemCallFilter=@aio @basic-io @chown @file-system @io-event @network-io @sync
ProtectSystem=strict
ReadWritePaths=/home /var/run/himmelblaud
ReadWritePaths=/home /var/run/himmelblaud /tmp /etc/krb5.conf.d
RestrictAddressFamilies=AF_UNIX
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
PrivateNetwork=true
ProtectHostname=true
Expand Down
3 changes: 1 addition & 2 deletions platform/opensuse/himmelblaud-tasks.service
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ ExecStart=/usr/sbin/himmelblaud_tasks
CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH
# SystemCallFilter=@aio @basic-io @chown @file-system @io-event @network-io @sync
ProtectSystem=strict
ReadWritePaths=/home /var/run/himmelblaud
ReadWritePaths=/home /var/run/himmelblaud /tmp /etc/krb5.conf.d
RestrictAddressFamilies=AF_UNIX
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
PrivateNetwork=true
ProtectHostname=true
Expand Down
1 change: 1 addition & 0 deletions src/common/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ pub const BROKER_APP_ID: &str = "29d9ed98-a469-4536-ade2-f981bc1d605e";
pub const BROKER_CLIENT_IDENT: &str = "38aa3b87-a06d-4817-b275-7a316988d93b";
pub const CN_NAME_MAPPING: bool = true;
pub const DEFAULT_HELLO_PIN_MIN_LEN: usize = 6;
pub const DEFAULT_CCACHE_DIR: &str = "/tmp/krb5cc_";
60 changes: 60 additions & 0 deletions src/common/src/idprovider/himmelblau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,33 @@ impl IdProvider for HimmelblauMultiProvider {
}
}

async fn unix_user_ccaches(
&self,
id: &Id,
old_token: Option<&UserToken>,
tpm: &mut tpm::BoxedDynTpm,
machine_key: &tpm::MachineKey,
) -> (Vec<u8>, Vec<u8>) {
let account_id = match old_token {
Some(token) => token.spn.clone(),
None => id.to_string().clone(),
};
match split_username(&account_id) {
Some((_sam, domain)) => {
let providers = self.providers.read().await;
match providers.get(domain) {
Some(provider) => {
provider
.unix_user_ccaches(id, old_token, tpm, machine_key)
.await
}
None => (vec![], vec![]),
}
}
None => (vec![], vec![]),
}
}

async fn unix_user_prt_cookie(
&self,
id: &Id,
Expand Down Expand Up @@ -490,6 +517,39 @@ impl IdProvider for HimmelblauProvider {
})
}

async fn unix_user_ccaches(
&self,
id: &Id,
old_token: Option<&UserToken>,
tpm: &mut tpm::BoxedDynTpm,
machine_key: &tpm::MachineKey,
) -> (Vec<u8>, Vec<u8>) {
let account_id = match old_token {
Some(token) => token.spn.clone(),
None => id.to_string().clone(),
};
let prt = match self.refresh_cache.refresh_token(&account_id).await {
Ok(prt) => prt,
Err(e) => {
error!("Failed fetching PRT for Kerberos CCache: {:?}", e);
return (vec![], vec![]);
}
};
let cloud_ccache = self
.client
.write()
.await
.fetch_cloud_ccache(&prt, tpm, machine_key)
.unwrap_or(vec![]);
let ad_ccache = self
.client
.write()
.await
.fetch_ad_ccache(&prt, tpm, machine_key)
.unwrap_or(vec![]);
(cloud_ccache, ad_ccache)
}

async fn unix_user_prt_cookie(
&self,
id: &Id,
Expand Down
8 changes: 8 additions & 0 deletions src/common/src/idprovider/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ pub trait IdProvider {
_machine_key: &tpm::MachineKey,
) -> Result<UnixUserToken, IdpError>;

async fn unix_user_ccaches(
&self,
_id: &Id,
_old_token: Option<&UserToken>,
_tpm: &mut tpm::BoxedDynTpm,
_machine_key: &tpm::MachineKey,
) -> (Vec<u8>, Vec<u8>);

async fn unix_user_prt_cookie(
&self,
_id: &Id,
Expand Down
27 changes: 27 additions & 0 deletions src/common/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

// use async_trait::async_trait;
use hashbrown::HashSet;
use libc::uid_t;
use std::collections::BTreeSet;
use std::fmt::Display;
use std::num::NonZeroUsize;
Expand Down Expand Up @@ -647,6 +648,32 @@ where
}
}

pub async fn get_user_ccaches(&self, account_id: Id) -> Option<(uid_t, Vec<u8>, Vec<u8>)> {
let token = match self.get_usertoken(account_id.clone()).await {
Ok(Some(token)) => token,
_ => {
error!("Failed to fetch unix user token during access token request!");
return None;
}
};

let mut hsm_lock = self.hsm.lock().await;

let (cloud_ccache, ad_ccache) = self
.client
.unix_user_ccaches(
&account_id,
Some(&token),
hsm_lock.deref_mut(),
&self.machine_key,
)
.await;

drop(hsm_lock);

Some((token.gidnumber, cloud_ccache, ad_ccache))
}

pub async fn get_user_prt_cookie(&self, account_id: Id) -> Option<String> {
let token = match self.get_usertoken(account_id.clone()).await {
Ok(Some(token)) => token,
Expand Down
2 changes: 2 additions & 0 deletions src/common/src/unix_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use libc::uid_t;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -133,6 +134,7 @@ pub enum TaskRequest {
HomeDirectory(HomeDirectoryInfo),
LocalGroups(String),
LogonScript(String, String),
KerberosCCache(uid_t, Vec<u8>, Vec<u8>),
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down
10 changes: 10 additions & 0 deletions src/config/krb5_himmelblau.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[libdefaults]
default_ccache_name = DIR:/tmp/krb5cc_%{uid}
[domain_realm]
.windows.net = KERBEROS.MICROSOFTONLINE.COM
.azure.net = KERBEROS.MICROSOFTONLINE.COM
[realm]
KERBEROS.MICROSOFTONLINE.COM = {
kdc = https://login.microsoftonline.com/common/kerberos
kpasswd_server = https://login.microsoftonline.com/common/kerberos
}
5 changes: 4 additions & 1 deletion src/daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ async-trait = "0.1.83"
name = "himmelblau"
maintainer = "David Mulder <[email protected]>"
depends = ["libssl3", "libsqlite3-0", "libutf8proc3"]
recommends = ["nss-himmelblau", "pam-himmelblau"]
recommends = ["nss-himmelblau", "pam-himmelblau", "krb5-user"]
assets = [
["../../platform/debian/himmelblau.conf.example", "etc/himmelblau/himmelblau.conf", "644"],
["../../src/config/krb5_himmelblau.conf", "etc/krb5.conf.d/", "644"],
["target/release/aad-tool", "usr/bin/", "755"],
["../../platform/debian/himmelblaud-tasks.service", "etc/systemd/system/", "644"],
["../../platform/debian/himmelblaud.service", "etc/systemd/system/", "644"],
Expand All @@ -70,6 +71,7 @@ name = "himmelblau"
maintainer = "David Mulder <[email protected]>"
assets = [
{ source = "../../src/config/himmelblau.conf.example", dest = "/etc/himmelblau/himmelblau.conf", mode = "644" },
{ source = "../../src/config/krb5_himmelblau.conf", dest = "/etc/krb5.conf.d/", mode = "644" },
{ source = "target/release/aad-tool", dest = "/usr/bin/", mode = "755" },
{ source = "../../platform/opensuse/himmelblaud-tasks.service", dest = "/usr/lib/systemd/system/", mode = "644" },
{ source = "../../platform/opensuse/himmelblaud.service", dest = "/usr/lib/systemd/system/", mode = "644" },
Expand All @@ -88,3 +90,4 @@ assets = [
[package.metadata.generate-rpm.recommends]
nss-himmelblau = "*"
pam-himmelblau = "*"
krb5 = "*"
74 changes: 59 additions & 15 deletions src/daemon/src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,7 @@ async fn handle_client(
.map(|pam_auth_response| pam_auth_response.into())
.unwrap_or(ClientResponse::Error)
{
ClientResponse::PamAuthenticateStepResponse(resp) => {
macro_rules! ret {
() => {
ClientResponse::PamAuthenticateStepResponse(resp)
};
}
ClientResponse::PamAuthenticateStepResponse(mut resp) => {
match auth_session {
AuthSession::Success(account_id) => {
match resp {
Expand Down Expand Up @@ -381,30 +376,79 @@ async fn handle_client(
Ok(Ok(status)) => {
if status == 2 {
debug!("Authentication was explicitly denied by the logon script");
ClientResponse::PamAuthenticateStepResponse(PamAuthResponse::Denied)
} else {
ret!()
resp =
PamAuthResponse::Denied;
}
}
_ => {
error!("Execution of logon script failed");
ret!()
}
}
}
Err(e) => {
error!("Execution of logon script failed: {:?}", e);
ret!()
}
}
} else {
ret!()
}

// Initialize the user Kerberos ccache
if let Some((uid, cloud_ccache, ad_ccache)) =
cachelayer
.get_user_ccaches(Id::Name(
account_id.to_string(),
))
.await
{
let (tx, rx) = oneshot::channel();

match task_channel_tx
.send_timeout(
(
TaskRequest::KerberosCCache(
uid,
cloud_ccache,
ad_ccache,
),
tx,
),
Duration::from_millis(100),
)
.await
{
Ok(()) => {
// Now wait for the other end OR timeout.
match time::timeout_at(
time::Instant::now()
+ Duration::from_secs(60),
rx,
)
.await
{
Ok(Ok(status)) => {
if status != 0 {
error!("Kerberos credential cache load failed for {}: Status code: {}", account_id, status);
}
}
Ok(Err(e)) => {
error!("Kerberos credential cache load failed for {}: {:?}", account_id, e);
}
Err(e) => {
error!("Kerberos credential cache load failed for {}: {:?}", account_id, e);
}
}
}
Err(e) => {
error!("Kerberos credential cache load failed for {}: {:?}", account_id, e);
}
}
}

ClientResponse::PamAuthenticateStepResponse(resp)
}
_ => ret!(),
_ => ClientResponse::PamAuthenticateStepResponse(resp),
}
}
_ => ret!(),
_ => ClientResponse::PamAuthenticateStepResponse(resp),
}
}
other => other,
Expand Down
Loading
Loading