Skip to content
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
497 changes: 434 additions & 63 deletions nmrs-core/src/dbus.rs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions nmrs-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct Network {
pub ssid: String,
pub bssid: Option<String>,
pub strength: Option<u8>,
pub frequency: Option<u32>,
pub secured: bool,
pub is_psk: bool,
pub is_eap: bool,
Expand Down Expand Up @@ -58,6 +59,12 @@ pub struct EapOptions {
pub phase2: Phase2,
}

pub struct ConnectionOptions {
pub autoconnect: bool,
pub autoconnect_priority: Option<i32>,
pub autoconnect_retries: Option<i32>,
}

pub enum WifiSecurity {
Open,
WpaPsk { psk: String },
Expand Down
122 changes: 63 additions & 59 deletions nmrs-core/src/wifi_builders.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,58 @@
use models::ConnectionOptions;
use std::collections::HashMap;
use zvariant::Value;

use crate::models;
use crate::models::{self, EapMethod};

/*fn bytes(val: &str) -> Vec<u8> {
fn bytes(val: &str) -> Vec<u8> {
val.as_bytes().to_vec()
}

fn string_array(xs: &[&str]) -> Value<'static> {
let vals: Vec<String> = xs.iter().map(|s| s.to_string()).collect();
Value::from(vals)
}

fn base_wifi_section(ssid: &str) -> HashMap<&'static str, Value<'static>> {
let mut s = HashMap::new();
s.insert("ssid", Value::from(bytes(ssid)));
s.insert("mode", Value::from("infrastructure"));
s
}

fn base_connection_section(ssid: &str) -> HashMap<&'static str, Value<'static>> {
fn base_connection_section(
ssid: &str,
opts: &ConnectionOptions,
) -> HashMap<&'static str, Value<'static>> {
let mut s = HashMap::new();
s.insert("type", Value::from("802-11-wireless"));
s.insert("id", Value::from(ssid.to_string()));
s.insert("uuid", Value::from(uuid::Uuid::new_v4().to_string()));
s.insert("autoconnect", Value::from(true));
s.insert("autoconnect", Value::from(opts.autoconnect));

if let Some(p) = opts.autoconnect_priority {
s.insert("autoconnect-priority", Value::from(p));
}

if let Some(r) = opts.autoconnect_retries {
s.insert("autoconnect-retries", Value::from(r));
}

s
}

fn build_psk_security(psk: &str) -> HashMap<&'static str, Value<'static>> {
let mut sec = HashMap::new();

sec.insert("key-mgmt", Value::from("wpa-psk"));
sec.insert("psk", Value::from(psk.to_string()));
// hardening maybe
// sec.insert("proto", Value::from(vec!["rsn"]));
// pairwise
// etc...
sec.insert("psk-flags", Value::from(0u32)); // 0 = agent-owned, provided during activation
sec.insert("auth-alg", Value::from("open"));

sec.insert("proto", string_array(&["rsn"]));
sec.insert("pairwise", string_array(&["ccmp"]));
sec.insert("group", string_array(&["ccmp"]));

sec
}

Expand All @@ -42,17 +64,18 @@ fn build_eap_security(
) {
let mut sec = HashMap::new();
sec.insert("key-mgmt", Value::from("wpa-eap"));
sec.insert("auth-alg", Value::from("OPEN"));
sec.insert("auth-alg", Value::from("open"));
// same hardening tips as psk
// proto, pairwise, group, etc.

// 802-1x
let mut e1x = HashMap::new();
let eap_vec = match opts.method {
models::EapMethod::Peap => vec!["peap"],
models::EapMethod::Ttls => vec!["ttls"],

let eap_str = match opts.method {
EapMethod::Peap => "peap",
EapMethod::Ttls => "ttls",
};
e1x.insert("eap", Value::from(eap_vec));
e1x.insert("eap", string_array(&[eap_str]));
e1x.insert("identity", Value::from(opts.identity.clone()));
e1x.insert("password", Value::from(opts.password.clone()));

Expand Down Expand Up @@ -86,67 +109,48 @@ fn build_eap_security(
pub fn build_wifi_connection(
ssid: &str,
security: &models::WifiSecurity,
opts: &ConnectionOptions,
) -> HashMap<&'static str, HashMap<&'static str, Value<'static>>> {
let mut conn: HashMap<&'static str, HashMap<&'static str, Value<'static>>> = HashMap::new();
conn.insert("connection", base_connection_section(ssid));

// base connections
conn.insert("connection", base_connection_section(ssid, opts));
conn.insert("802-11-wireless", base_wifi_section(ssid));

// Add IPv4 and IPv6 configuration to prevent state 60 stall
// TODO: Expand upon auto/manual configuration options
let mut ipv4 = HashMap::new();
ipv4.insert("method", Value::from("auto"));
conn.insert("ipv4", ipv4);

let mut ipv6 = HashMap::new();
ipv6.insert("method", Value::from("auto"));
conn.insert("ipv6", ipv6);

match security {
models::WifiSecurity::Open => {}

models::WifiSecurity::WpaPsk { psk } => {
conn.insert("802-11-wireless-security", build_psk_security(psk.as_str()));
}
// point wireless at security section
if let Some(w) = conn.get_mut("802-11-wireless") {
w.insert("security", Value::from("802-11-wireless-security"));
}

models::WifiSecurity::WpaEap { opts } => {
let (sec, e1x) = build_eap_security(&opts);
let sec = build_psk_security(psk);
conn.insert("802-11-wireless-security", sec);
conn.insert("802-1x", e1x);
}
}
conn
}*/

pub fn build_wifi_connection(
ssid: &str,
security: &models::WifiSecurity,
) -> HashMap<&'static str, HashMap<&'static str, zvariant::Value<'static>>> {
let mut conn = HashMap::new();

let mut s_conn = HashMap::new();
s_conn.insert("type", Value::from("802-11-wireless"));
s_conn.insert("id", Value::from(ssid.to_string()));
s_conn.insert("uuid", Value::from(uuid::Uuid::new_v4().to_string()));
s_conn.insert("autoconnect", Value::from(true));
s_conn.insert("interface-name", Value::from("wlan0"));
conn.insert("connection", s_conn);

let mut s_wifi = HashMap::new();
s_wifi.insert("ssid", Value::from(ssid.as_bytes().to_vec()));
s_wifi.insert("mode", Value::from("infrastructure"));
models::WifiSecurity::WpaEap { opts } => {
if let Some(w) = conn.get_mut("802-11-wireless") {
w.insert("security", Value::from("802-11-wireless-security"));
}

match security {
models::WifiSecurity::Open => {}
models::WifiSecurity::WpaPsk { psk } => {
s_wifi.insert("security", Value::from("802-11-wireless-security"));
let mut s_sec = HashMap::new();
s_sec.insert("key-mgmt", Value::from("wpa-psk"));
s_sec.insert("auth-alg", Value::from("open"));
s_sec.insert("psk", Value::from(psk.to_string()));
conn.insert("802-11-wireless-security", s_sec);
let (mut sec, e1x) = build_eap_security(opts);
sec.insert("auth-alg", Value::from("open"));
conn.insert("802-11-wireless-security", sec);
conn.insert("802-1x", e1x);
}
_ => {}
}

conn.insert("802-11-wireless", s_wifi);

let mut ipv4 = HashMap::new();
ipv4.insert("method", Value::from("auto"));
conn.insert("ipv4", ipv4);

let mut ipv6 = HashMap::new();
ipv6.insert("method", Value::from("auto"));
conn.insert("ipv6", ipv6);

conn
}
13 changes: 11 additions & 2 deletions nmrs-core/tests/wifi_buillders_test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
use nmrs_core::models::WifiSecurity;
use nmrs_core::models::{ConnectionOptions, WifiSecurity};
use nmrs_core::wifi_builders::build_wifi_connection;
use zvariant::Value;

fn opts() -> ConnectionOptions {
ConnectionOptions {
autoconnect: true,
autoconnect_priority: None,
autoconnect_retries: None,
}
}

#[test]
fn builds_open_wifi_connection() {
let conn = build_wifi_connection("testnet", &WifiSecurity::Open);
let conn = build_wifi_connection("testnet", &WifiSecurity::Open, &opts());
assert!(conn.contains_key("connection"));
assert!(conn.contains_key("802-11-wireless"));
assert!(conn.contains_key("ipv4"));
Expand All @@ -18,6 +26,7 @@ fn builds_psk_wifi_connection_with_security_section() {
&WifiSecurity::WpaPsk {
psk: "pw123".into(),
},
&opts(),
);
let has_sec = conn.contains_key("802-11-wireless-security");
assert!(has_sec, "security section missing");
Expand Down
37 changes: 30 additions & 7 deletions nmrs-ui/src/ui/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,29 @@ fn draw_connect_modal(parent: &ApplicationWindow, ssid: &str, is_eap: bool) {
let ssid_owned = ssid.to_string();
let user_entry_clone = user_entry.clone();

let status_label = Label::new(Some(""));
status_label.add_css_class("status-label");
vbox.append(&status_label);

{
let dialog_rc = dialog_rc.clone();
let status_label = status_label.clone();

entry.connect_activate(move |entry| {
let pwd = entry.text().to_string();

let username = user_entry_clone
.as_ref()
.map(|e| e.text().to_string())
.unwrap_or_default();
let ssid = ssid_owned.clone();
let dialog = dialog_rc.clone();
let status = status_label.clone();
let entry_clone = entry.clone();

eprintln!("User entered username={username}, password={pwd}");
// Prevent double submission
entry.set_sensitive(false);
status.set_text("Connecting...");

glib::MainContext::default().spawn_local(async move {
eprintln!("---in spawned task here--");
Expand Down Expand Up @@ -107,17 +119,28 @@ fn draw_connect_modal(parent: &ApplicationWindow, ssid: &str, is_eap: bool) {

println!("Calling nm.connect() for '{ssid}'");
match nm.connect(&ssid, creds).await {
Ok(_) => println!("nm.connect() succeeded!"),
Err(err) => eprintln!("nm.connect() failed: {err}"),
Ok(_) => {
println!("nm.connect() succeeded!");
status.set_text("✓ Connected!");
glib::timeout_future_seconds(1).await;
dialog.close();
}
Err(err) => {
eprintln!("nm.connect() failed: {err}");
status.set_text(&format!("✗ Failed: {err}"));
entry_clone.set_sensitive(true);
}
}
}
Err(err) => eprintln!("Failed to create NetworkManager: {err}"),
Err(err) => {
eprintln!("Failed to create NetworkManager: {err}");
status.set_text(&format!("✗ Error: {err}"));
entry_clone.set_sensitive(true);
}
}

println!("---finsihed spawned task---");
println!("---finished spawned task---");
});

dialog_rc.close();
});
}

Expand Down
Loading