| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2019 Jan Grulich <jgrulich@redhat.com> |
| 3 | |
| 4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 5 | */ |
| 6 | |
| 7 | #include "wireguardsetting.h" |
| 8 | #include "wireguardsetting_p.h" |
| 9 | |
| 10 | #include <QDebug> |
| 11 | |
| 12 | #if !NM_CHECK_VERSION(1, 16, 0) |
| 13 | #define NM_SETTING_WIREGUARD_SETTING_NAME "wireguard" |
| 14 | |
| 15 | #define NM_SETTING_WIREGUARD_FWMARK "fwmark" |
| 16 | #define NM_SETTING_WIREGUARD_LISTEN_PORT "listen-port" |
| 17 | #define NM_SETTING_WIREGUARD_PRIVATE_KEY "private-key" |
| 18 | #define NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS "private-key-flags" |
| 19 | #define NM_SETTING_WIREGUARD_PEERS "peers" |
| 20 | #define NM_SETTING_WIREGUARD_MTU "mtu" |
| 21 | #define NM_SETTING_WIREGUARD_PEER_ROUTES "peer-routes" |
| 22 | |
| 23 | #define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY "preshared-key" |
| 24 | #define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS "preshared-key-flags" |
| 25 | #define NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY "public-key" |
| 26 | #endif |
| 27 | |
| 28 | NetworkManager::WireGuardSettingPrivate::WireGuardSettingPrivate() |
| 29 | : name(NM_SETTING_WIREGUARD_SETTING_NAME) |
| 30 | , fwmark(0) |
| 31 | , listenPort(0) |
| 32 | , mtu(0) |
| 33 | , peerRoutes(true) |
| 34 | , privateKeyFlags(NetworkManager::Setting::None) |
| 35 | { |
| 36 | } |
| 37 | |
| 38 | NetworkManager::WireGuardSetting::WireGuardSetting() |
| 39 | : Setting(Setting::WireGuard) |
| 40 | , d_ptr(new WireGuardSettingPrivate()) |
| 41 | { |
| 42 | } |
| 43 | |
| 44 | NetworkManager::WireGuardSetting::WireGuardSetting(const Ptr &other) |
| 45 | : Setting(other) |
| 46 | , d_ptr(new WireGuardSettingPrivate()) |
| 47 | { |
| 48 | setFwmark(other->fwmark()); |
| 49 | setListenPort(other->listenPort()); |
| 50 | setMtu(other->mtu()); |
| 51 | setPeerRoutes(other->peerRoutes()); |
| 52 | setPeers(other->peers()); |
| 53 | setPrivateKey(other->privateKey()); |
| 54 | setPrivateKeyFlags(other->privateKeyFlags()); |
| 55 | } |
| 56 | |
| 57 | NetworkManager::WireGuardSetting::~WireGuardSetting() |
| 58 | { |
| 59 | delete d_ptr; |
| 60 | } |
| 61 | |
| 62 | QString NetworkManager::WireGuardSetting::name() const |
| 63 | { |
| 64 | Q_D(const WireGuardSetting); |
| 65 | |
| 66 | return d->name; |
| 67 | } |
| 68 | |
| 69 | quint32 NetworkManager::WireGuardSetting::fwmark() const |
| 70 | { |
| 71 | Q_D(const WireGuardSetting); |
| 72 | |
| 73 | return d->fwmark; |
| 74 | } |
| 75 | |
| 76 | void NetworkManager::WireGuardSetting::setFwmark(quint32 fwmark) |
| 77 | { |
| 78 | Q_D(WireGuardSetting); |
| 79 | |
| 80 | d->fwmark = fwmark; |
| 81 | } |
| 82 | |
| 83 | quint32 NetworkManager::WireGuardSetting::listenPort() const |
| 84 | { |
| 85 | Q_D(const WireGuardSetting); |
| 86 | |
| 87 | return d->listenPort; |
| 88 | } |
| 89 | |
| 90 | void NetworkManager::WireGuardSetting::setListenPort(quint32 port) |
| 91 | { |
| 92 | Q_D(WireGuardSetting); |
| 93 | |
| 94 | d->listenPort = port; |
| 95 | } |
| 96 | |
| 97 | quint32 NetworkManager::WireGuardSetting::mtu() const |
| 98 | { |
| 99 | Q_D(const WireGuardSetting); |
| 100 | |
| 101 | return d->mtu; |
| 102 | } |
| 103 | |
| 104 | void NetworkManager::WireGuardSetting::setMtu(quint32 mtu) |
| 105 | { |
| 106 | Q_D(WireGuardSetting); |
| 107 | |
| 108 | d->mtu = mtu; |
| 109 | } |
| 110 | |
| 111 | bool NetworkManager::WireGuardSetting::peerRoutes() const |
| 112 | { |
| 113 | Q_D(const WireGuardSetting); |
| 114 | |
| 115 | return d->peerRoutes; |
| 116 | } |
| 117 | |
| 118 | void NetworkManager::WireGuardSetting::setPeerRoutes(bool peerRoutes) |
| 119 | { |
| 120 | Q_D(WireGuardSetting); |
| 121 | |
| 122 | d->peerRoutes = peerRoutes; |
| 123 | } |
| 124 | |
| 125 | NMVariantMapList NetworkManager::WireGuardSetting::peers() const |
| 126 | { |
| 127 | Q_D(const WireGuardSetting); |
| 128 | |
| 129 | return d->peers; |
| 130 | } |
| 131 | |
| 132 | void NetworkManager::WireGuardSetting::setPeers(const NMVariantMapList &peers) |
| 133 | { |
| 134 | Q_D(WireGuardSetting); |
| 135 | |
| 136 | d->peers = peers; |
| 137 | } |
| 138 | |
| 139 | QString NetworkManager::WireGuardSetting::privateKey() const |
| 140 | { |
| 141 | Q_D(const WireGuardSetting); |
| 142 | |
| 143 | return d->privateKey; |
| 144 | } |
| 145 | |
| 146 | void NetworkManager::WireGuardSetting::setPrivateKey(const QString &key) |
| 147 | { |
| 148 | Q_D(WireGuardSetting); |
| 149 | |
| 150 | d->privateKey = key; |
| 151 | } |
| 152 | |
| 153 | NetworkManager::Setting::SecretFlags NetworkManager::WireGuardSetting::privateKeyFlags() const |
| 154 | { |
| 155 | Q_D(const WireGuardSetting); |
| 156 | |
| 157 | return d->privateKeyFlags; |
| 158 | } |
| 159 | |
| 160 | void NetworkManager::WireGuardSetting::setPrivateKeyFlags(NetworkManager::Setting::SecretFlags flags) |
| 161 | { |
| 162 | Q_D(WireGuardSetting); |
| 163 | |
| 164 | d->privateKeyFlags = flags; |
| 165 | } |
| 166 | |
| 167 | void NetworkManager::WireGuardSetting::secretsFromMap(const QVariantMap &secrets) |
| 168 | { |
| 169 | if (secrets.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY))) { |
| 170 | setPrivateKey(secrets.value(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY)).toString()); |
| 171 | } |
| 172 | |
| 173 | if (secrets.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS))) { |
| 174 | NMVariantMapList listOfPeers = qdbus_cast<NMVariantMapList>(v: secrets.value(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS))); |
| 175 | NMVariantMapList origPeers = peers(); |
| 176 | |
| 177 | for (const QVariantMap &peer : listOfPeers) { |
| 178 | if (peer.contains(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY))) { |
| 179 | QString presharedKey = peer.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)).toString(); |
| 180 | QString publicKey = peer.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)).toString(); |
| 181 | for (int i = 0; i < origPeers.size(); i++) { |
| 182 | if (origPeers[i][QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)].toString() == publicKey) { |
| 183 | origPeers[i].insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY), value: presharedKey); |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | setPeers(origPeers); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | QVariantMap NetworkManager::WireGuardSetting::secretsToMap() const |
| 193 | { |
| 194 | QVariantMap secrets; |
| 195 | |
| 196 | if (!privateKey().isEmpty()) { |
| 197 | secrets.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY), value: privateKey()); |
| 198 | } |
| 199 | |
| 200 | NMVariantMapList ; |
| 201 | |
| 202 | for (const QVariantMap &map : peers()) { |
| 203 | if (map.contains(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)) && map.contains(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY))) { |
| 204 | QVariantMap newMap; |
| 205 | newMap.insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY), value: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY))); |
| 206 | newMap.insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY), value: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY))); |
| 207 | |
| 208 | peersSecrets << newMap; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | if (!peersSecrets.isEmpty()) { |
| 213 | secrets.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS), value: QVariant::fromValue(value: peersSecrets)); |
| 214 | } |
| 215 | |
| 216 | return secrets; |
| 217 | } |
| 218 | |
| 219 | void NetworkManager::WireGuardSetting::secretsFromStringMap(const NMStringMap &map) |
| 220 | { |
| 221 | QVariantMap secretsMap; |
| 222 | NMVariantMapList peers; |
| 223 | |
| 224 | auto it = map.constBegin(); |
| 225 | while (it != map.constEnd()) { |
| 226 | if (it.key() == QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY)) { |
| 227 | secretsMap.insert(key: it.key(), value: it.value()); |
| 228 | } |
| 229 | |
| 230 | if (it.key().startsWith(s: QLatin1String(NM_SETTING_WIREGUARD_PEERS)) && it.key().endsWith(s: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY))) { |
| 231 | QStringList peerStrList = it.key().split(sep: QLatin1Char('.')); |
| 232 | |
| 233 | QVariantMap peer; |
| 234 | peer.insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY), value: peerStrList.at(i: 1)); |
| 235 | peer.insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY), value: it.value()); |
| 236 | |
| 237 | peers << peer; |
| 238 | } |
| 239 | ++it; |
| 240 | } |
| 241 | |
| 242 | if (!peers.isEmpty()) { |
| 243 | secretsMap.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS), value: QVariant::fromValue(value: peers)); |
| 244 | } |
| 245 | |
| 246 | secretsFromMap(secrets: secretsMap); |
| 247 | } |
| 248 | |
| 249 | NMStringMap NetworkManager::WireGuardSetting::secretsToStringMap() const |
| 250 | { |
| 251 | NMStringMap ret; |
| 252 | QVariantMap secretsMap = secretsToMap(); |
| 253 | |
| 254 | auto it = secretsMap.constBegin(); |
| 255 | while (it != secretsMap.constEnd()) { |
| 256 | if (it.key() == QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY)) { |
| 257 | ret.insert(key: it.key(), value: it.value().toString()); |
| 258 | } |
| 259 | |
| 260 | if (it.key() == QLatin1String(NM_SETTING_WIREGUARD_PEERS)) { |
| 261 | NMVariantMapList listOfPeers = qdbus_cast<NMVariantMapList>(v: it.value()); |
| 262 | |
| 263 | for (const QVariantMap &map : listOfPeers) { |
| 264 | const QString str = QStringLiteral("%1.%2.%3" ) |
| 265 | .arg(a: QLatin1String(NM_SETTING_WIREGUARD_PEERS)) |
| 266 | .arg(a: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)).toString()) |
| 267 | .arg(a: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)); |
| 268 | ret.insert(key: str, value: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)).toString()); |
| 269 | } |
| 270 | } |
| 271 | ++it; |
| 272 | } |
| 273 | |
| 274 | return ret; |
| 275 | } |
| 276 | |
| 277 | QStringList NetworkManager::WireGuardSetting::needSecrets(bool requestNew) const |
| 278 | { |
| 279 | QStringList secrets; |
| 280 | |
| 281 | if (!privateKeyFlags().testFlag(flag: Setting::NotRequired)) { |
| 282 | if (privateKey().isEmpty() || requestNew) { |
| 283 | secrets << QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY); |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | for (const QVariantMap &map : peers()) { |
| 288 | const QString presharedKey = map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)).toString(); |
| 289 | SecretFlags preSharedKeyFlags = (SecretFlags)map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS)).toInt(); |
| 290 | |
| 291 | if (!presharedKey.isEmpty()) { |
| 292 | continue; |
| 293 | } |
| 294 | |
| 295 | if (preSharedKeyFlags.testFlag(flag: Setting::NotRequired)) { |
| 296 | continue; |
| 297 | } |
| 298 | |
| 299 | const QString str = QStringLiteral("%1.%2.%3" ) |
| 300 | .arg(a: QLatin1String(NM_SETTING_WIREGUARD_PEERS)) |
| 301 | .arg(a: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)).toString()) |
| 302 | .arg(a: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)); |
| 303 | secrets << str; |
| 304 | } |
| 305 | |
| 306 | return secrets; |
| 307 | } |
| 308 | |
| 309 | void NetworkManager::WireGuardSetting::fromMap(const QVariantMap &setting) |
| 310 | { |
| 311 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_FWMARK))) { |
| 312 | setFwmark(setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_FWMARK)).toInt()); |
| 313 | } |
| 314 | |
| 315 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_LISTEN_PORT))) { |
| 316 | setListenPort(setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_LISTEN_PORT)).toInt()); |
| 317 | } |
| 318 | |
| 319 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_MTU))) { |
| 320 | setMtu(setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_MTU)).toInt()); |
| 321 | } |
| 322 | |
| 323 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PEER_ROUTES))) { |
| 324 | setPeerRoutes(setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_PEER_ROUTES)).toBool()); |
| 325 | } |
| 326 | |
| 327 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS))) { |
| 328 | setPeers(qdbus_cast<NMVariantMapList>(v: setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS)))); |
| 329 | } |
| 330 | |
| 331 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY))) { |
| 332 | setPrivateKey(setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY)).toString()); |
| 333 | } |
| 334 | |
| 335 | if (setting.contains(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS))) { |
| 336 | setPrivateKeyFlags((SecretFlags)setting.value(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS)).toInt()); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | QVariantMap NetworkManager::WireGuardSetting::toMap() const |
| 341 | { |
| 342 | QVariantMap setting; |
| 343 | |
| 344 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_FWMARK), value: fwmark()); |
| 345 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_LISTEN_PORT), value: listenPort()); |
| 346 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_MTU), value: mtu()); |
| 347 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PEER_ROUTES), value: peerRoutes()); |
| 348 | |
| 349 | if (!peers().isEmpty()) { |
| 350 | // FIXME we seem to have SecretFlags as an int, but NM expects an uint, while this is not |
| 351 | // problem for rest of *-flags properties, it's problem for "preshared-key" which NM handless |
| 352 | // as GVariant and asks for "u" when getting it's value |
| 353 | NMVariantMapList fixedPeers = peers(); |
| 354 | for (QVariantMap &map : fixedPeers) { |
| 355 | if (map.contains(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS))) { |
| 356 | map.insert(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS), |
| 357 | value: map.value(key: QLatin1String(NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS)).toUInt()); |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PEERS), value: QVariant::fromValue(value: fixedPeers)); |
| 362 | } |
| 363 | |
| 364 | if (!privateKey().isEmpty()) { |
| 365 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY), value: privateKey()); |
| 366 | } |
| 367 | setting.insert(key: QLatin1String(NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS), value: (int)privateKeyFlags()); |
| 368 | |
| 369 | return setting; |
| 370 | } |
| 371 | |
| 372 | QDebug NetworkManager::operator<<(QDebug dbg, const NetworkManager::WireGuardSetting &setting) |
| 373 | { |
| 374 | dbg.nospace() << "type: " << setting.typeAsString(type: setting.type()) << '\n'; |
| 375 | dbg.nospace() << "initialized: " << !setting.isNull() << '\n'; |
| 376 | |
| 377 | dbg.nospace() << NM_SETTING_WIREGUARD_FWMARK << ": " << setting.fwmark() << '\n'; |
| 378 | dbg.nospace() << NM_SETTING_WIREGUARD_LISTEN_PORT << ": " << setting.listenPort() << '\n'; |
| 379 | dbg.nospace() << NM_SETTING_WIREGUARD_MTU << ": " << setting.mtu() << '\n'; |
| 380 | dbg.nospace() << NM_SETTING_WIREGUARD_PEER_ROUTES << ": " << setting.peerRoutes() << '\n'; |
| 381 | dbg.nospace() << NM_SETTING_WIREGUARD_PEERS << ": " << setting.peers() << '\n'; |
| 382 | dbg.nospace() << NM_SETTING_WIREGUARD_PRIVATE_KEY << ": " << setting.privateKey() << '\n'; |
| 383 | dbg.nospace() << NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS << ": " << setting.privateKeyFlags() << '\n'; |
| 384 | |
| 385 | return dbg.maybeSpace(); |
| 386 | } |
| 387 | |