Skip to content

Commit

Permalink
WIP: 8021x
Browse files Browse the repository at this point in the history
  • Loading branch information
jcronenberg committed Sep 2, 2024
1 parent ee5b812 commit c954913
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
63 changes: 63 additions & 0 deletions rust/agama-server/src/network/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ pub struct Connection {
pub port_config: PortConfig,
pub match_config: MatchConfig,
pub config: ConnectionConfig,
pub ieee_8021x_config: Option<IEEE8021XConfig>,
}

impl Connection {
Expand Down Expand Up @@ -560,6 +561,7 @@ impl Default for Connection {
port_config: Default::default(),
match_config: Default::default(),
config: Default::default(),
ieee_8021x_config: Default::default(),
}
}
}
Expand Down Expand Up @@ -1353,3 +1355,64 @@ pub enum NetworkChange {
/// device gets renamed.
DeviceUpdated(String, Device),
}

#[derive(Default, Debug, PartialEq, Clone, Serialize)]
pub struct IEEE8021XConfig {
pub eap: Vec<EAPMethod>,
pub identity: Option<String>,
pub password: Option<String>,
pub ca_cert: Option<String>,
pub ca_cert_password: Option<String>,
pub client_cert: Option<String>,
pub client_cert_password: Option<String>,
pub private_key: Option<String>,
pub private_key_password: Option<String>,
pub anonymous_identity: Option<String>,
}

#[derive(Debug, Error)]
#[error("Invalid eap method: {0}")]
pub struct InvalidEAPMethod(String);

#[derive(Debug, PartialEq, Clone, Serialize)]
pub enum EAPMethod {
LEAP,
MD5,
TLS,
PEAP,
TTLS,
PWD,
FAST,
}

impl FromStr for EAPMethod {
type Err = InvalidEAPMethod;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"leap" => Ok(Self::LEAP),
"md5" => Ok(Self::MD5),
"tls" => Ok(Self::TLS),
"peap" => Ok(Self::PEAP),
"ttls" => Ok(Self::TTLS),
"pwd" => Ok(Self::PWD),
"fast" => Ok(Self::FAST),
_ => Err(InvalidEAPMethod(s.to_string())),
}
}
}

impl fmt::Display for EAPMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match &self {
Self::LEAP => "leap",
Self::MD5 => "md5",
Self::TLS => "tls",
Self::PEAP => "peap",
Self::TTLS => "ttls",
Self::PWD => "pwd",
Self::FAST => "fast",
};
write!(f, "{}", value)
}
}
105 changes: 105 additions & 0 deletions rust/agama-server/src/network/nm/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const BRIDGE_KEY: &str = "bridge";
const BRIDGE_PORT_KEY: &str = "bridge-port";
const INFINIBAND_KEY: &str = "infiniband";
const TUN_KEY: &str = "tun";
const IEEE_8021X_KEY: &str = "802-1x";

/// Converts a connection struct into a HashMap that can be sent over D-Bus.
///
Expand Down Expand Up @@ -137,6 +138,10 @@ pub fn connection_to_dbus<'a>(
PortConfig::None => {}
}

if let Some(ieee_8021x_config) = &conn.ieee_8021x_config {
result.insert(IEEE_8021X_KEY, ieee_8021x_config_to_dbus(ieee_8021x_config));
}

result.insert("connection", connection_dbus);
result
}
Expand All @@ -151,6 +156,10 @@ pub fn connection_from_dbus(conn: OwnedNestedHash) -> Option<Connection> {
connection.port_config = PortConfig::Bridge(bridge_port_config);
}

if let Some(ieee_8021x_config) = ieee_8021x_config_from_dbus(&conn) {
connection.ieee_8021x_config = Some(ieee_8021x_config);
}

if let Some(wireless_config) = wireless_config_from_dbus(&conn) {
connection.config = ConnectionConfig::Wireless(wireless_config);
return Some(connection);
Expand Down Expand Up @@ -981,6 +990,102 @@ fn vlan_config_from_dbus(conn: &OwnedNestedHash) -> Option<VlanConfig> {
})
}

fn ieee_8021x_config_to_dbus(config: &IEEE8021XConfig) -> HashMap<&str, zvariant::Value> {
let mut ieee_8021x_config: HashMap<&str, zvariant::Value> = HashMap::from([(
"eap",
config
.eap
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.into(),
)]);

if let Some(identity) = &config.identity {
ieee_8021x_config.insert("identity", identity.into());
}
if let Some(password) = &config.password {
ieee_8021x_config.insert("password", password.into());
}
if let Some(ca_cert) = &config.ca_cert {
ieee_8021x_config.insert("ca-cert", ca_cert.into());
}
if let Some(ca_cert_password) = &config.ca_cert_password {
ieee_8021x_config.insert("ca-cert-password", ca_cert_password.into());
}
if let Some(client_cert) = &config.client_cert {
ieee_8021x_config.insert("client-cert", client_cert.into());
}
if let Some(client_cert_password) = &config.client_cert_password {
ieee_8021x_config.insert("client-cert-password", client_cert_password.into());
}
if let Some(private_key) = &config.private_key {
ieee_8021x_config.insert("private-key", private_key.into());
}
if let Some(private_key_password) = &config.private_key_password {
ieee_8021x_config.insert("private-key-password", private_key_password.into());
}
if let Some(anonymous_identity) = &config.anonymous_identity {
ieee_8021x_config.insert("anonymous-identity", anonymous_identity.into());
}

ieee_8021x_config
}

fn ieee_8021x_config_from_dbus(conn: &OwnedNestedHash) -> Option<IEEE8021XConfig> {
let ieee_8021x = conn.get(IEEE_8021X_KEY)?;

let mut ieee_8021x_config = IEEE8021XConfig::default();

if let Some(eap) = ieee_8021x.get("eap") {
let eap: &zvariant::Array = eap.downcast_ref()?;
let eap: Vec<&str> = eap
.iter()
.map(|x| x.downcast_ref::<str>())
.collect::<Option<Vec<&str>>>()?;
let eap: Vec<EAPMethod> = eap
.iter()
.map(|x| EAPMethod::from_str(x))
.collect::<Result<Vec<EAPMethod>, InvalidEAPMethod>>()
.ok()?;
ieee_8021x_config.eap = eap;
}

if let Some(identity) = ieee_8021x.get("identity") {
ieee_8021x_config.identity = Some(identity.downcast_ref::<str>()?.to_string());
}
if let Some(password) = ieee_8021x.get("password") {
ieee_8021x_config.password = Some(password.downcast_ref::<str>()?.to_string());
}
if let Some(ca_cert) = ieee_8021x.get("ca_cert") {
ieee_8021x_config.ca_cert = Some(ca_cert.downcast_ref::<str>()?.to_string());
}
if let Some(ca_cert_password) = ieee_8021x.get("ca_cert_password") {
ieee_8021x_config.ca_cert_password =
Some(ca_cert_password.downcast_ref::<str>()?.to_string());
}
if let Some(client_cert) = ieee_8021x.get("client_cert") {
ieee_8021x_config.client_cert = Some(client_cert.downcast_ref::<str>()?.to_string());
}
if let Some(client_cert_password) = ieee_8021x.get("client_cert_password") {
ieee_8021x_config.client_cert_password =
Some(client_cert_password.downcast_ref::<str>()?.to_string());
}
if let Some(private_key) = ieee_8021x.get("private_key") {
ieee_8021x_config.private_key = Some(private_key.downcast_ref::<str>()?.to_string());
}
if let Some(private_key_password) = ieee_8021x.get("private_key_password") {
ieee_8021x_config.private_key_password =
Some(private_key_password.downcast_ref::<str>()?.to_string());
}
if let Some(anonymous_identity) = ieee_8021x.get("anonymous_identity") {
ieee_8021x_config.anonymous_identity =
Some(anonymous_identity.downcast_ref::<str>()?.to_string());
}

Some(ieee_8021x_config)
}

/// Determines whether a value is empty.
///
/// TODO: Generalize for other kind of values, like dicts or arrays.
Expand Down
105 changes: 105 additions & 0 deletions rust/migrate-wicked/src/wireless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct Network {
#[serde(rename = "access-point")]
pub access_point: Option<String>,
pub wep: Option<Wep>,
#[serde(rename = "wpa-eap")]
pub wpa_eap: Option<WpaEap>,
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
Expand Down Expand Up @@ -73,6 +75,107 @@ pub struct Wep {
pub key: Vec<String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct WpaEap {
pub method: EapMethod,
#[serde(rename = "auth-proto")]
pub auth_proto: EapAuthProto,
#[serde(rename = "pairwise-cipher")]
pub pairwise_cipher: EapPairwiseCipher,
#[serde(rename = "group-cipher")]
pub group_cipher: EapGroupCipher,
pub identity: String,
pub tls: Option<WickedTLS>,
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
#[strum(serialize_all = "kebab-case")]
pub enum EapMethod {
#[default]
TLS,
PEAP,
TTLS,
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
// TODO i don't think this is correct
// but tbh this is probably overkill anyway
#[strum(serialize_all = "kebab-case")]
pub enum EapAuthProto {
#[default]
WPA,
NONE,
MD5,
TLS,
PAP,
CHAP,
MSCHAP,
MSCHAPV2,
PEAP,
TTLS,
GTC,
OTP,
LEAP,
PSK,
PAX,
SAKE,
GPSK,
WSC,
IKEV2,
TNC,
FAST,
AKA,
AkaPrime,
SIM,
}

// TODO will have to look into wicked code into what options the "inner" and "outer" get translated
impl TryFrom<EapAuthProto> for model::EAPMethod {
type Error = anyhow::Error;

fn try_from(value: EapAuthProto) -> Result<Self, Self::Error> {
match value {
EapAuthProto::LEAP => Ok(model::EAPMethod::LEAP),
EapAuthProto::MD5 => Ok(model::EAPMethod::MD5),
EapAuthProto::TLS => Ok(model::EAPMethod::TLS),
EapAuthProto::PEAP => Ok(model::EAPMethod::PEAP),
EapAuthProto::TTLS => Ok(model::EAPMethod::TTLS),
EapAuthProto::FAST => Ok(model::EAPMethod::FAST),
_ => Err(anyhow!("EAP auth-proto isn't supported by NetworkManager")),
}
}
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
#[strum(serialize_all = "UPPERCASE")]
pub enum EapPairwiseCipher {
#[default]
TKIP,
CCMP,
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
#[strum(serialize_all = "UPPERCASE")]
pub enum EapGroupCipher {
#[default]
TKIP,
CCMP,
WEP104,
WEP40,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct WickedTLS {
#[serde(rename = "ca-cert")]
pub ca_cert: String,
#[serde(rename = "client-cert")]
pub client_cert: String,
#[serde(rename = "client-key")]
pub client_key: String,
#[serde(rename = "client-key-passwd")]
pub client_key_passwd: String,
}

fn unwrap_wireless_networks<'de, D>(deserializer: D) -> Result<Option<Vec<Network>>, D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -202,6 +305,7 @@ mod tests {
key_management: vec!["wpa-psk".to_string()],
access_point: None,
wep: None,
wpa_eap: None,
}]),
ap_scan: 0,
}),
Expand Down Expand Up @@ -252,6 +356,7 @@ mod tests {
default_key: 1,
key: vec!["01020304ff".to_string(), "s:hello".to_string()],
}),
wpa_eap: None,
}]),
ap_scan: 0,
}),
Expand Down
39 changes: 39 additions & 0 deletions rust/migrate-wicked/tests/wireless/wicked_xml/wireless.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,42 @@
<enabled>false</enabled>
</ipv6>
</interface>
<interface origin="compat:suse:/etc/sysconfig/network/ifcfg-wlan2">
<name>wlan2</name>
<control>
<mode>manual</mode>
</control>
<firewall/>
<wireless>
<ap-scan>1</ap-scan>
<networks>
<network>
<essid>test</essid>
<scan-ssid>true</scan-ssid>
<mode>ap</mode>
<access-point>12:34:56:78:9a:bc</access-point>
<key-management>wpa-eap</key-management>
<wpa-eap>
<method>tls</method>
<auth-proto>wpa</auth-proto>
<pairwise-cipher>TKIP</pairwise-cipher>
<group-cipher>TKIP</group-cipher>
<identity>test</identity>
<tls>
<ca-cert type="path">/etc/sysconfig/network/./ca_cert</ca-cert>
<client-cert type="path">/etc/sysconfig/network/./client_cert</client-cert>
<client-key type="path">/etc/sysconfig/network/./client_key</client-key>
<client-key-passwd>testclientpw</client-key-passwd>
</tls>
</wpa-eap>
</network>
</networks>
</wireless>
<link/>
<ipv4>
<enabled>false</enabled>
</ipv4>
<ipv6>
<enabled>false</enabled>
</ipv6>
</interface>

0 comments on commit c954913

Please sign in to comment.