diff --git a/Cargo.toml b/Cargo.toml
index eac7ed2d..7111e9ee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ members = [
"src/users",
"src/idmap",
"src/file_permissions",
+ "src/broker",
]
resolver = "2"
@@ -37,12 +38,12 @@ tracing-subscriber = "^0.3.17"
tracing = "^0.1.37"
himmelblau_unix_common = { path = "src/common" }
kanidm_unix_common = { path = "src/glue" }
-libhimmelblau = { version = "0.2.9" }
+libhimmelblau = { version = "0.3.0" }
clap = { version = "^4.5", features = ["derive", "env"] }
clap_complete = "^4.4.1"
reqwest = { version = "^0.12.2", features = ["json"] }
anyhow = "^1.0.71"
-tokio = { version = "^1.28.1", features = ["rt", "macros", "sync", "time", "net", "io-util", "signal"] }
+tokio = { version = "^1.28.1", features = ["rt", "macros", "sync", "time", "net", "io-util", "signal", "rt-multi-thread"] }
tokio-util = { version = "^0.7.8", features = ["codec"] }
async-trait = "^0.1.72"
pem = "^3.0.2"
@@ -51,6 +52,7 @@ os-release = "^0.1.0"
jsonwebtoken = "^9.2.0"
zeroize = "^1.7.0"
idmap = { path = "src/idmap" }
+identity_dbus_broker = "0.1.0"
# Kanidm deps
argon2 = { version = "0.5.2", features = ["alloc"] }
diff --git a/platform/debian/com.microsoft.identity.broker.service b/platform/debian/com.microsoft.identity.broker.service
new file mode 100644
index 00000000..c0f595f8
--- /dev/null
+++ b/platform/debian/com.microsoft.identity.broker.service
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=com.microsoft.identity.broker1
+Exec=/usr/sbin/broker
diff --git a/platform/debian/himmelblaud.service b/platform/debian/himmelblaud.service
index 04a30e5c..2ff9e0c4 100644
--- a/platform/debian/himmelblaud.service
+++ b/platform/debian/himmelblaud.service
@@ -6,13 +6,14 @@ Description=Himmelblau Authentication Daemon
After=chronyd.service ntpd.service network-online.target
[Service]
+BusName=org.samba.himmelblau
DynamicUser=yes
UMask=0027
CacheDirectory=himmelblaud # /var/cache/himmelblaud
RuntimeDirectory=himmelblaud # /run/himmelblaud
StateDirectory=himmelblaud # /var/lib/himmelblaud
-Type=simple
+Type=dbus
ExecStart=/usr/sbin/himmelblaud
# Implied by dynamic user.
diff --git a/platform/debian/org.samba.himmelblau.conf b/platform/debian/org.samba.himmelblau.conf
new file mode 100644
index 00000000..d508affa
--- /dev/null
+++ b/platform/debian/org.samba.himmelblau.conf
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/platform/opensuse/com.microsoft.identity.broker.service b/platform/opensuse/com.microsoft.identity.broker.service
new file mode 100644
index 00000000..c0f595f8
--- /dev/null
+++ b/platform/opensuse/com.microsoft.identity.broker.service
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=com.microsoft.identity.broker1
+Exec=/usr/sbin/broker
diff --git a/platform/opensuse/himmelblaud.service b/platform/opensuse/himmelblaud.service
index e9d25821..9402ae49 100644
--- a/platform/opensuse/himmelblaud.service
+++ b/platform/opensuse/himmelblaud.service
@@ -6,13 +6,14 @@ Description=Himmelblau Authentication Daemon
After=chronyd.service ntpd.service network-online.target suspend.target
[Service]
+BusName=org.samba.himmelblau
DynamicUser=yes
UMask=0027
CacheDirectory=himmelblaud # /var/cache/himmelblaud
RuntimeDirectory=himmelblaud # /run/himmelblaud
StateDirectory=himmelblaud # /var/lib/himmelblaud
-Type=simple
+Type=dbus
ExecStart=/usr/sbin/himmelblaud
# Implied by dynamic user.
diff --git a/platform/opensuse/org.samba.himmelblau.conf b/platform/opensuse/org.samba.himmelblau.conf
new file mode 100644
index 00000000..d508affa
--- /dev/null
+++ b/platform/opensuse/org.samba.himmelblau.conf
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/src/broker/Cargo.toml b/src/broker/Cargo.toml
new file mode 100644
index 00000000..1d1dde1e
--- /dev/null
+++ b/src/broker/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "broker"
+version.workspace = true
+authors.workspace = true
+rust-version.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+dbus = "0.9.7"
+identity_dbus_broker.workspace = true
+tokio.workspace = true
diff --git a/src/broker/src/main.rs b/src/broker/src/main.rs
new file mode 100644
index 00000000..f33f0440
--- /dev/null
+++ b/src/broker/src/main.rs
@@ -0,0 +1,23 @@
+/*
+ Unix Azure Entra ID implementation
+ Copyright (C) David Mulder 2024
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+use identity_dbus_broker::himmelblau_session_broker_serve;
+
+#[tokio::main]
+async fn main() -> Result<(), dbus::MethodErr> {
+ himmelblau_session_broker_serve().await
+}
diff --git a/src/common/src/idprovider/himmelblau.rs b/src/common/src/idprovider/himmelblau.rs
index c944c9c6..114ac2b8 100644
--- a/src/common/src/idprovider/himmelblau.rs
+++ b/src/common/src/idprovider/himmelblau.rs
@@ -149,6 +149,61 @@ impl IdProvider for HimmelblauMultiProvider {
Ok(())
}
+ async fn unix_user_access(
+ &self,
+ id: &Id,
+ scopes: Vec,
+ old_token: Option<&UserToken>,
+ tpm: &mut tpm::BoxedDynTpm,
+ machine_key: &tpm::MachineKey,
+ ) -> Result {
+ 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_access(id, scopes, old_token, tpm, machine_key)
+ .await
+ }
+ None => Err(IdpError::NotFound),
+ }
+ }
+ None => Err(IdpError::NotFound),
+ }
+ }
+
+ async fn unix_user_prt_cookie(
+ &self,
+ id: &Id,
+ old_token: Option<&UserToken>,
+ tpm: &mut tpm::BoxedDynTpm,
+ machine_key: &tpm::MachineKey,
+ ) -> Result {
+ 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_prt_cookie(id, old_token, tpm, machine_key)
+ .await
+ }
+ None => Err(IdpError::NotFound),
+ }
+ }
+ None => Err(IdpError::NotFound),
+ }
+ }
+
async fn unix_user_get(
&self,
id: &Id,
@@ -491,6 +546,61 @@ impl IdProvider for HimmelblauProvider {
}
}
+ async fn unix_user_access(
+ &self,
+ id: &Id,
+ scopes: Vec,
+ old_token: Option<&UserToken>,
+ tpm: &mut tpm::BoxedDynTpm,
+ machine_key: &tpm::MachineKey,
+ ) -> Result {
+ /* Use the prt mem cache to refresh the user token */
+ let account_id = match old_token {
+ Some(token) => token.spn.clone(),
+ None => id.to_string().clone(),
+ };
+ let prt = self.refresh_cache.refresh_token(&account_id).await?;
+ self.client
+ .write()
+ .await
+ .exchange_prt_for_access_token(
+ &prt,
+ scopes.iter().map(|s| s.as_ref()).collect(),
+ None,
+ tpm,
+ machine_key,
+ )
+ .await
+ .map_err(|e| {
+ error!("{:?}", e);
+ IdpError::BadRequest
+ })
+ }
+
+ async fn unix_user_prt_cookie(
+ &self,
+ id: &Id,
+ old_token: Option<&UserToken>,
+ tpm: &mut tpm::BoxedDynTpm,
+ machine_key: &tpm::MachineKey,
+ ) -> Result {
+ /* Use the prt mem cache to refresh the user token */
+ let account_id = match old_token {
+ Some(token) => token.spn.clone(),
+ None => id.to_string().clone(),
+ };
+ let prt = self.refresh_cache.refresh_token(&account_id).await?;
+ self.client
+ .write()
+ .await
+ .acquire_prt_sso_cookie(&prt, tpm, machine_key)
+ .await
+ .map_err(|e| {
+ error!("Failed to request prt cookie: {:?}", e);
+ IdpError::BadRequest
+ })
+ }
+
async fn unix_user_get(
&self,
id: &Id,
@@ -555,7 +665,10 @@ impl IdProvider for HimmelblauProvider {
displayname: "".to_string(),
shell: Some(config.get_shell(Some(&self.domain))),
groups,
- sshkeys: vec![],
+ tenant_id: Uuid::parse_str(&self.tenant_id).map_err(|e| {
+ error!("{:?}", e);
+ IdpError::BadRequest
+ })?,
valid: true,
});
} else {
@@ -1344,7 +1457,6 @@ impl HimmelblauProvider {
groups = vec![];
}
};
- let sshkeys: Vec = vec![];
let valid = true;
let idmap = self.idmap.read().await;
let gidnumber = match config.get_id_attr_map() {
@@ -1376,7 +1488,10 @@ impl HimmelblauProvider {
displayname: value.id_token.name.clone(),
shell: Some(config.get_shell(Some(&self.domain))),
groups,
- sshkeys,
+ tenant_id: Uuid::parse_str(&self.tenant_id).map_err(|e| {
+ error!("{:?}", e);
+ IdpError::BadRequest
+ })?,
valid,
})
}
diff --git a/src/common/src/idprovider/interface.rs b/src/common/src/idprovider/interface.rs
index 81ed9e98..c2b43f9d 100644
--- a/src/common/src/idprovider/interface.rs
+++ b/src/common/src/idprovider/interface.rs
@@ -7,6 +7,7 @@
use crate::db::KeyStoreTxn;
use crate::unix_proto::{DeviceAuthorizationResponse, PamAuthRequest, PamAuthResponse};
use async_trait::async_trait;
+use himmelblau::auth::UserToken as UnixUserToken;
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast;
use uuid::Uuid;
@@ -60,8 +61,7 @@ pub struct UserToken {
pub displayname: String,
pub shell: Option,
pub groups: Vec,
- // Could there be a better type here?
- pub sshkeys: Vec,
+ pub tenant_id: Uuid,
// Defaults to false.
pub valid: bool,
}
@@ -176,6 +176,23 @@ pub trait IdProvider {
_machine_key: &tpm::MachineKey,
) -> Result;
+ async fn unix_user_access(
+ &self,
+ _id: &Id,
+ _scopes: Vec,
+ _token: Option<&UserToken>,
+ _tpm: &mut tpm::BoxedDynTpm,
+ _machine_key: &tpm::MachineKey,
+ ) -> Result;
+
+ async fn unix_user_prt_cookie(
+ &self,
+ _id: &Id,
+ _token: Option<&UserToken>,
+ _tpm: &mut tpm::BoxedDynTpm,
+ _machine_key: &tpm::MachineKey,
+ ) -> Result;
+
async fn unix_user_online_auth_init(
&self,
_account_id: &str,
diff --git a/src/common/src/resolver.rs b/src/common/src/resolver.rs
index 211f369b..49d18296 100644
--- a/src/common/src/resolver.rs
+++ b/src/common/src/resolver.rs
@@ -37,6 +37,8 @@ use kanidm_hsm_crypto::{BoxedDynTpm, HmacKey, MachineKey, Tpm};
use tokio::sync::broadcast;
+use himmelblau::auth::UserToken as UnixUserToken;
+
const NXCACHE_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(128) };
#[derive(Debug, Clone)]
@@ -604,7 +606,76 @@ where
}
}
- async fn get_usertoken(&self, account_id: Id) -> Result