fix(network): read WireGuard lists back from D-Bus arguments

pull/18/head
reionwong 3 weeks ago
parent 35314c1a1c
commit 3c3a8f808d

@ -651,8 +651,10 @@ void Handler::loadVpnSecrets(const QString &connection)
getSecrets(connection, QStringLiteral("wireguard"), [this, connection](const NMVariantMapMap &secrets) {
const QVariantMap wireguard = secrets.value(QStringLiteral("wireguard"));
QVariantMap presharedKeys;
const auto peers = wireguard.value(QStringLiteral("peers")).value<QList<QVariantMap>>();
for (const QVariantMap &peer : peers) {
// Straight from D-Bus the aa{sv} is still a QDBusArgument.
const QVariantList peers = NetworkDBus::plain(wireguard.value(QStringLiteral("peers"))).toList();
for (const QVariant &entry : peers) {
const QVariantMap peer = entry.toMap();
if (peer.contains(QStringLiteral("preshared-key")))
presharedKeys.insert(peer.value(QStringLiteral("public-key")).toString(), peer.value(QStringLiteral("preshared-key")));
}

@ -9,12 +9,15 @@
#include "fakesecrets.h"
#include "fixture.h"
#include <QDBusArgument>
#include <QSignalSpy>
#include <QTemporaryDir>
#include <QTest>
#include <memory>
#include <QtEndian>
using namespace Fixture;
class NetworkClientTest : public QObject
@ -522,6 +525,72 @@ private slots:
QDBusConnection::disconnectFromBus(QStringLiteral("fake-secrets"));
}
// Settings fetched over D-Bus keep nested lists as QDBusArgument; the
// WireGuard editor and exporter read them anyway.
void wireGuardReadsBackFromDBus()
{
const QString path = FakeNM::managerPath + QStringLiteral("/Settings/33");
nm->settings[path] = {
{QStringLiteral("connection"),
{{QStringLiteral("id"), QStringLiteral("Work WG")},
{QStringLiteral("uuid"), QStringLiteral("work wg")},
{QStringLiteral("type"), QStringLiteral("wireguard")},
{QStringLiteral("interface-name"), QStringLiteral("wg0")}}},
{QStringLiteral("wireguard"),
{{QStringLiteral("private-key-flags"), uint(NetworkDBus::SecretSystemOwned)},
{QStringLiteral("mtu"), 1280u},
{QStringLiteral("peers"), QVariant::fromValue(QList<QVariantMap> {{
{QStringLiteral("public-key"), QStringLiteral("xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=")},
{QStringLiteral("preshared-key-flags"), uint(NetworkDBus::SecretSystemOwned)},
{QStringLiteral("endpoint"), QStringLiteral("vpn.example:51820")},
{QStringLiteral("allowed-ips"), QStringList {QStringLiteral("0.0.0.0/0")}},
{QStringLiteral("persistent-keepalive"), 25u},
}})}}},
{QStringLiteral("ipv4"),
{{QStringLiteral("method"), QStringLiteral("manual")},
{QStringLiteral("address-data"), QVariant::fromValue(QList<QVariantMap> {
{{QStringLiteral("address"), QStringLiteral("10.0.0.2")}, {QStringLiteral("prefix"), 24u}}})},
{QStringLiteral("dns"), QVariant::fromValue(QList<uint> {qToBigEndian(0x0a000001u)})},
{QStringLiteral("dns-search"), QStringList {QStringLiteral("corp.example")}}}},
{QStringLiteral("ipv6"),
{{QStringLiteral("method"), QStringLiteral("manual")},
{QStringLiteral("address-data"), QVariant::fromValue(QList<QVariantMap> {
{{QStringLiteral("address"), QStringLiteral("fd00::2")}, {QStringLiteral("prefix"), 64u}}})},
{QStringLiteral("dns"), QVariant::fromValue(QList<QByteArray> {
QByteArray::fromHex("fd000000000000000000000000000001")})}}},
};
nm->add(path, {{FakeNM::connection, {{QStringLiteral("Flags"), 0u}}}});
QTRY_VERIFY(!NetworkClient::instance()->settings(path).isEmpty());
const NMVariantMapMap settings = NetworkClient::instance()->settings(path);
QVERIFY(settings.value(QStringLiteral("wireguard")).value(QStringLiteral("peers")).canConvert<QDBusArgument>());
const QVariantMap form = VpnUtils::wireGuardForm(settings);
QCOMPARE(form.value(QStringLiteral("addresses")).toString(), QStringLiteral("10.0.0.2/24, fd00::2/64"));
QCOMPARE(form.value(QStringLiteral("dns")).toString(), QStringLiteral("10.0.0.1, corp.example, fd00::1"));
QCOMPARE(form.value(QStringLiteral("mtu")).toUInt(), 1280u);
const QVariantList peers = form.value(QStringLiteral("peers")).toList();
QCOMPARE(peers.size(), 1);
const QVariantMap peer = peers.first().toMap();
QCOMPARE(peer.value(QStringLiteral("publicKey")).toString(), QStringLiteral("xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="));
QCOMPARE(peer.value(QStringLiteral("endpoint")).toString(), QStringLiteral("vpn.example:51820"));
QCOMPARE(peer.value(QStringLiteral("allowedIps")).toString(), QStringLiteral("0.0.0.0/0"));
QCOMPARE(peer.value(QStringLiteral("keepalive")).toUInt(), 25u);
QVERIFY(peer.value(QStringLiteral("hasPresharedKey")).toBool());
// Saved again with the keys left empty, the retained preshared key stays.
QString error;
const NMVariantMapMap edited = VpnUtils::wireGuardSettings(form, settings, &error);
QVERIFY2(!edited.isEmpty(), qPrintable(error));
const auto editedPeers = edited.value(QStringLiteral("wireguard")).value(QStringLiteral("peers")).value<QList<QVariantMap>>();
QCOMPARE(editedPeers.size(), 1);
QCOMPARE(editedPeers.first().value(QStringLiteral("preshared-key-flags")).toUInt(), uint(NetworkDBus::SecretSystemOwned));
const QByteArray config = VpnUtils::wireGuardConfig(settings);
QVERIFY(config.contains("Address = 10.0.0.2/24, fd00::2/64"));
QVERIFY(config.contains("[Peer]\nPublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="));
QVERIFY(config.contains("AllowedIPs = 0.0.0.0/0"));
}
void vpnAddedByHandIsPrivate()
{
Handler handler;

@ -593,6 +593,20 @@ QVariantList manualKinds()
return kinds;
}
// Settings read back from NetworkManager hold nested lists as QDBusArgument;
// ones built in-process hold typed lists. Both read the same way here.
template<typename T>
static QList<T> listOf(const QVariant &value)
{
if (value.metaType() == QMetaType::fromType<QList<T>>())
return value.value<QList<T>>();
QList<T> result;
const QVariantList entries = NetworkDBus::plain(value).toList();
for (const QVariant &entry : entries)
result << entry.value<T>();
return result;
}
QVariantMap wireGuardForm(const NMVariantMapMap &settings)
{
const QVariantMap wireguard = settings.value(QStringLiteral("wireguard"));
@ -600,17 +614,17 @@ QVariantMap wireGuardForm(const NMVariantMapMap &settings)
QStringList dns;
for (const QString &family : {QStringLiteral("ipv4"), QStringLiteral("ipv6")}) {
const QVariantMap ip = settings.value(family);
const auto addressData = ip.value(QStringLiteral("address-data")).value<QList<QVariantMap>>();
const auto addressData = listOf<QVariantMap>(ip.value(QStringLiteral("address-data")));
for (const QVariantMap &address : addressData) {
addresses << QStringLiteral("%1/%2").arg(address.value(QStringLiteral("address")).toString())
.arg(address.value(QStringLiteral("prefix")).toUInt());
}
if (family == QLatin1String("ipv4")) {
const auto servers = ip.value(QStringLiteral("dns")).value<QList<uint>>();
const auto servers = listOf<uint>(ip.value(QStringLiteral("dns")));
for (uint server : servers)
dns << QHostAddress(qFromBigEndian(server)).toString();
} else {
const auto servers = ip.value(QStringLiteral("dns")).value<QList<QByteArray>>();
const auto servers = listOf<QByteArray>(ip.value(QStringLiteral("dns")));
for (const QByteArray &server : servers) {
Q_IPV6ADDR raw;
memcpy(raw.c, server.constData(), qMin(16, int(server.size())));
@ -625,7 +639,7 @@ QVariantMap wireGuardForm(const NMVariantMapMap &settings)
}
QVariantList peers;
const auto storedPeers = wireguard.value(QStringLiteral("peers")).value<QList<QVariantMap>>();
const auto storedPeers = listOf<QVariantMap>(wireguard.value(QStringLiteral("peers")));
for (const QVariantMap &peer : storedPeers) {
peers << QVariantMap {
{QStringLiteral("publicKey"), peer.value(QStringLiteral("public-key")).toString()},
@ -708,7 +722,7 @@ NMVariantMapMap wireGuardSettings(const QVariantMap &form, const NMVariantMapMap
if (!ok)
return fail(QObject::tr("The MTU must be a number."));
const auto storedPeers = stored.value(QStringLiteral("peers")).value<QList<QVariantMap>>();
const auto storedPeers = listOf<QVariantMap>(stored.value(QStringLiteral("peers")));
QList<QVariantMap> peers;
const QVariantList formPeers = form.value(QStringLiteral("peers")).toList();
if (formPeers.isEmpty())
@ -813,7 +827,7 @@ QByteArray wireGuardConfig(const NMVariantMapMap &settings)
if (!form.value(QStringLiteral("peerRoutes")).toBool())
out += QStringLiteral("Table = off\n");
const auto peers = wireguard.value(QStringLiteral("peers")).value<QList<QVariantMap>>();
const auto peers = listOf<QVariantMap>(wireguard.value(QStringLiteral("peers")));
for (const QVariantMap &peer : peers) {
out += QStringLiteral("\n[Peer]\nPublicKey = %1\n").arg(peer.value(QStringLiteral("public-key")).toString());
if (peer.contains(QStringLiteral("preshared-key")))

Loading…
Cancel
Save