235 lines
7.7 KiB
C++
235 lines
7.7 KiB
C++
#include "nm_connection.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <random>
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
|
|
namespace radiantwave::network {
|
|
|
|
// ─── UUID Generation (standard RFC 4122 v4) ─────────────────────────────
|
|
|
|
namespace {
|
|
|
|
std::string generateUuid() {
|
|
static std::random_device rd;
|
|
static std::mt19937_64 gen(rd());
|
|
static std::uniform_int_distribution<uint64_t> dis;
|
|
|
|
uint8_t bytes[16];
|
|
uint64_t v1 = dis(gen);
|
|
uint64_t v2 = dis(gen);
|
|
std::memcpy(bytes, &v1, 8);
|
|
std::memcpy(bytes + 8, &v2, 8);
|
|
|
|
bytes[6] = (bytes[6] & 0x0F) | 0x40; // Version 4
|
|
bytes[8] = (bytes[8] & 0x3F) | 0x80; // Variant RFC 4122
|
|
|
|
static const char hex[] = "0123456789abcdef";
|
|
char buf[37];
|
|
int p = 0;
|
|
for (int i = 0; i < 16; ++i) {
|
|
if (i == 4 || i == 6 || i == 8 || i == 10) {
|
|
buf[p++] = '-';
|
|
}
|
|
buf[p++] = hex[(bytes[i] >> 4) & 0x0F];
|
|
buf[p++] = hex[bytes[i] & 0x0F];
|
|
}
|
|
buf[36] = '\0';
|
|
return std::string(buf, 36);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// ─── Connection Builder ────────────────────────────────────────────────
|
|
|
|
ConnectionSettings buildConnectionSettings(
|
|
const std::string& ssid,
|
|
const std::string& password,
|
|
SecurityType security,
|
|
const std::string& interface_name
|
|
) {
|
|
// Validate SSID
|
|
if (!isValidSsid(ssid)) {
|
|
throw std::invalid_argument("Invalid SSID: must be 1-32 bytes with no null characters");
|
|
}
|
|
|
|
// Validate password if required
|
|
if (requiresPassword(security)) {
|
|
std::string error = validatePassword(password, security);
|
|
if (!error.empty()) {
|
|
throw std::invalid_argument(error);
|
|
}
|
|
}
|
|
|
|
ConnectionSettings settings;
|
|
|
|
// ── connection section ─────────────────────────────────────────────
|
|
settings["connection"] = {
|
|
{"id", ssid},
|
|
{"type", std::string("802-11-wireless")},
|
|
{"uuid", generateUuid()},
|
|
{"interface-name", interface_name},
|
|
{"autoconnect", true},
|
|
{"permissions", std::vector<std::string>()}
|
|
};
|
|
|
|
// ── 802-11-wireless section ────────────────────────────────────────
|
|
settings["802-11-wireless"] = {
|
|
{"ssid", ssidToBytes(ssid)},
|
|
{"mode", std::string("infrastructure")}
|
|
};
|
|
|
|
// ── 802-11-wireless-security section ───────────────────────────────
|
|
if (security != SecurityType::Open) {
|
|
SettingDict security_settings;
|
|
|
|
switch (security) {
|
|
case SecurityType::WEP:
|
|
security_settings["key-mgmt"] = std::string("none");
|
|
security_settings["wep-key0"] = password;
|
|
security_settings["wep-key-type"] = static_cast<uint32_t>(1); // WEP_KEY_TYPE_PASSPHRASE
|
|
break;
|
|
|
|
case SecurityType::WPA:
|
|
case SecurityType::WPA2:
|
|
security_settings["key-mgmt"] = std::string("wpa-psk");
|
|
security_settings["auth-alg"] = std::string("open");
|
|
security_settings["psk"] = password;
|
|
security_settings["psk-flags"] = static_cast<uint32_t>(0);
|
|
break;
|
|
|
|
case SecurityType::WPA3:
|
|
security_settings["key-mgmt"] = std::string("sae");
|
|
security_settings["psk"] = password;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
settings["802-11-wireless-security"] = security_settings;
|
|
}
|
|
|
|
// ── ipv4 section ───────────────────────────────────────────────────
|
|
settings["ipv4"] = {
|
|
{"method", std::string("auto")}
|
|
};
|
|
|
|
// ── ipv6 section ───────────────────────────────────────────────────
|
|
settings["ipv6"] = {
|
|
{"method", std::string("auto")}
|
|
};
|
|
|
|
return settings;
|
|
}
|
|
|
|
// ─── Security Detection ────────────────────────────────────────────────
|
|
|
|
SecurityType detectSecurityType(
|
|
uint32_t ap_flags,
|
|
uint32_t wpa_flags,
|
|
uint32_t rsn_flags
|
|
) {
|
|
// WPA3 uses SAE (flag 0x0400)
|
|
if (rsn_flags & nm_ap_wpa_flags::RsnSae) {
|
|
return SecurityType::WPA3;
|
|
}
|
|
|
|
// WPA2 uses RSN PSK (flag 0x0100)
|
|
if (rsn_flags & nm_ap_wpa_flags::RsnPsk) {
|
|
return SecurityType::WPA2;
|
|
}
|
|
|
|
// WPA uses WPA flags (not RSN)
|
|
if (wpa_flags != 0) {
|
|
return SecurityType::WPA;
|
|
}
|
|
|
|
// WEP uses privacy flag
|
|
if (ap_flags & nm_ap_flags::Privacy) {
|
|
return SecurityType::WEP;
|
|
}
|
|
|
|
return SecurityType::Open;
|
|
}
|
|
|
|
bool requiresPassword(SecurityType type) {
|
|
return type != SecurityType::Open;
|
|
}
|
|
|
|
// ─── SSID Utilities ────────────────────────────────────────────────────
|
|
|
|
std::vector<uint8_t> ssidToBytes(const std::string& ssid) {
|
|
return std::vector<uint8_t>(ssid.begin(), ssid.end());
|
|
}
|
|
|
|
std::string bytesToSsid(const std::vector<uint8_t>& bytes) {
|
|
return std::string(bytes.begin(), bytes.end());
|
|
}
|
|
|
|
bool isValidSsid(const std::string& ssid) {
|
|
// SSID must be 1-32 bytes
|
|
if (ssid.empty() || ssid.size() > 32) {
|
|
return false;
|
|
}
|
|
|
|
// SSID must not contain null bytes
|
|
for (char c : ssid) {
|
|
if (c == '\0') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ─── Password Validation ───────────────────────────────────────────────
|
|
|
|
std::string validatePassword(const std::string& password, SecurityType type) {
|
|
switch (type) {
|
|
case SecurityType::WEP:
|
|
// WEP keys can be 5 or 13 ASCII characters, or 10/26 hex digits
|
|
// For simplicity, we accept any non-empty string
|
|
if (password.empty()) {
|
|
return "WEP network requires a password";
|
|
}
|
|
break;
|
|
|
|
case SecurityType::WPA:
|
|
case SecurityType::WPA2:
|
|
// WPA/WPA2 PSK requires 8-63 characters
|
|
if (password.empty()) {
|
|
return "WPA/WPA2 network requires a password";
|
|
}
|
|
if (password.size() < 8) {
|
|
return "WPA/WPA2 password must be at least 8 characters";
|
|
}
|
|
if (password.size() > 63) {
|
|
return "WPA/WPA2 password must be at most 63 characters";
|
|
}
|
|
break;
|
|
|
|
case SecurityType::WPA3:
|
|
// WPA3 SAE requires 8-63 characters
|
|
if (password.empty()) {
|
|
return "WPA3 network requires a password";
|
|
}
|
|
if (password.size() < 8) {
|
|
return "WPA3 password must be at least 8 characters";
|
|
}
|
|
if (password.size() > 63) {
|
|
return "WPA3 password must be at most 63 characters";
|
|
}
|
|
break;
|
|
|
|
case SecurityType::Open:
|
|
// No password required
|
|
break;
|
|
}
|
|
|
|
return ""; // Valid
|
|
}
|
|
|
|
} // namespace radiantwave::network
|