secretagent.cpp 20 KB
Newer Older
Jan Grulich's avatar
Jan Grulich committed
1
/*
Jan Grulich's avatar
Jan Grulich committed
2
    Copyright 2013 Jan Grulich <jgrulich@redhat.com>
3
    Copyright 2013 Lukas Tinkl <ltinkl@redhat.com>
4
    Copyright 2013 by Daniel Nicoletti <dantti12@gmail.com>
Jan Grulich's avatar
Jan Grulich committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) version 3, or any
    later version accepted by the membership of KDE e.V. (or its
    successor approved by the membership of KDE e.V.), which shall
    act as a proxy defined in Section 6 of version 3 of the license.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "secretagent.h"
24
#include "passworddialog.h"
Jan Grulich's avatar
Jan Grulich committed
25

Jan Grulich's avatar
Jan Grulich committed
26
#include <NetworkManagerQt/Settings>
27
#include <NetworkManagerQt/ConnectionSettings>
Jan Grulich's avatar
Jan Grulich committed
28
#include <NetworkManagerQt/GenericTypes>
Jan Grulich's avatar
Jan Grulich committed
29
30
#include <NetworkManagerQt/GsmSetting>
#include <NetworkManagerQt/Security8021xSetting>
31
#include <NetworkManagerQt/VpnSetting>
Jan Grulich's avatar
Jan Grulich committed
32
33
#include <NetworkManagerQt/WirelessSecuritySetting>
#include <NetworkManagerQt/WirelessSetting>
34

35
#include <QStringBuilder>
36
#include <QDialog>
37

38
#include <KConfigGroup>
39
#include <KConfig>
40
#include <KPluginFactory>
41
#include <KWindowSystem>
42
#include <KWallet/Wallet>
43

Sebastian Kügler's avatar
Sebastian Kügler committed
44
#include <QDebug>
45

Jan Grulich's avatar
Jan Grulich committed
46
SecretAgent::SecretAgent(QObject* parent):
47
    NetworkManager::SecretAgent("org.kde.plasma.networkmanagement", parent),
48
49
    m_wallet(0),
    m_dialog(0)
Jan Grulich's avatar
Jan Grulich committed
50
{
51
52
    connect(NetworkManager::notifier(), SIGNAL(serviceDisappeared()),
            this, SLOT(killDialogs()));
Jan Grulich's avatar
Jan Grulich committed
53
54
55
56
57
58
}

SecretAgent::~SecretAgent()
{
}

59
NMVariantMapMap SecretAgent::GetSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path, const QString &setting_name,
Lukáš Tinkl's avatar
Lukáš Tinkl committed
60
                                        const QStringList &hints, uint flags)
Jan Grulich's avatar
Jan Grulich committed
61
{
62
63
64
65
66
67
    qDebug() << Q_FUNC_INFO;
    qDebug() << "Path:" << connection_path.path();
    qDebug() << "Setting name:" << setting_name;
    qDebug() << "Hints:" << hints;
    qDebug() << "Flags:" << flags;

68
    QString callId = connection_path.path() % setting_name;
69
    foreach (const SecretsRequest & request, m_calls) {
70
        if (request == callId) {
Sebastian Kügler's avatar
Sebastian Kügler committed
71
            qWarning() << "GetSecrets was called again! This should not happen, cancelling first call" << connection_path.path() << setting_name;
72
73
            CancelGetSecrets(connection_path, setting_name);
            break;
74
        }
75
    }
Jan Grulich's avatar
Jan Grulich committed
76

77
    setDelayedReply(true);
78
    SecretsRequest request(SecretsRequest::GetSecrets);
79
80
81
82
83
84
85
86
87
    request.callId = callId;
    request.connection = connection;
    request.connection_path = connection_path;
    request.flags = static_cast<NetworkManager::SecretAgent::GetSecretsFlags>(flags);
    request.hints = hints;
    request.setting_name = setting_name;
    request.message = message();
    m_calls << request;

Lukáš Tinkl's avatar
Lukáš Tinkl committed
88
    processNext();
89

90
    return NMVariantMapMap();
Jan Grulich's avatar
Jan Grulich committed
91
92
}

93
void SecretAgent::SaveSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path)
Jan Grulich's avatar
Jan Grulich committed
94
{
Sebastian Kügler's avatar
Sebastian Kügler committed
95
    qDebug() << connection_path.path();
96

97
    setDelayedReply(true);
98
99
100
101
102
103
104
    SecretsRequest::Type type;
    if (hasSecrets(connection)) {
        type = SecretsRequest::SaveSecrets;
    } else {
        type = SecretsRequest::DeleteSecrets;
    }
    SecretsRequest request(type);
105
106
107
108
    request.connection = connection;
    request.connection_path = connection_path;
    request.message = message();
    m_calls << request;
Jan Grulich's avatar
Jan Grulich committed
109

110
    processNext();
Jan Grulich's avatar
Jan Grulich committed
111
112
}

113
void SecretAgent::DeleteSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path)
Jan Grulich's avatar
Jan Grulich committed
114
{
Sebastian Kügler's avatar
Sebastian Kügler committed
115
    qDebug() << connection_path.path();
116

117
118
119
120
121
122
    setDelayedReply(true);
    SecretsRequest request(SecretsRequest::DeleteSecrets);
    request.connection = connection;
    request.connection_path = connection_path;
    request.message = message();
    m_calls << request;
Jan Grulich's avatar
Jan Grulich committed
123

124
    processNext();
Jan Grulich's avatar
Jan Grulich committed
125
126
127
128
}

void SecretAgent::CancelGetSecrets(const QDBusObjectPath &connection_path, const QString &setting_name)
{
Sebastian Kügler's avatar
Sebastian Kügler committed
129
    qDebug() << connection_path.path() << setting_name;
130
131
    QString callId = connection_path.path() % setting_name;
    for (int i = 0; i < m_calls.size(); ++i) {
132
133
134
135
136
        SecretsRequest request = m_calls.at(i);
        if (request.type == SecretsRequest::GetSecrets && callId == request.callId) {
            if (m_dialog == request.dialog) {
                m_dialog = 0;
            }
137
138
139
140
141
142
143
144
            delete request.dialog;
            sendError(SecretAgent::AgentCanceled,
                      QLatin1String("Agent canceled the password dialog"),
                      request.message);
            m_calls.removeAt(i);
            break;
        }
    }
Jan Grulich's avatar
Jan Grulich committed
145

Lukáš Tinkl's avatar
Lukáš Tinkl committed
146
    processNext();
Jan Grulich's avatar
Jan Grulich committed
147
}
148
149
150

void SecretAgent::dialogAccepted()
{
151
152
153
    for (int i = 0; i < m_calls.size(); ++i) {
        SecretsRequest request = m_calls[i];
        if (request.type == SecretsRequest::GetSecrets && request.dialog == m_dialog) {
154
155
            NMVariantMapMap connection = request.dialog->secrets();
            sendSecrets(connection, request.message);
Jan Grulich's avatar
Jan Grulich committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
            NetworkManager::ConnectionSettings::Ptr connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(connection));
            NetworkManager::ConnectionSettings::Ptr completeConnectionSettings;
            NetworkManager::Connection::Ptr con = NetworkManager::findConnectionByUuid(connectionSettings->uuid());
            if (con) {
                completeConnectionSettings = con->settings();
            } else {
                completeConnectionSettings = connectionSettings;
            }
            if (request.saveSecretsWithoutReply && completeConnectionSettings->connectionType() != NetworkManager::ConnectionSettings::Vpn) {
                bool requestOffline = true;
                if (completeConnectionSettings->connectionType() == NetworkManager::ConnectionSettings::Gsm) {
                    NetworkManager::GsmSetting::Ptr gsmSetting = completeConnectionSettings->setting(NetworkManager::Setting::Gsm).staticCast<NetworkManager::GsmSetting>();
                    if (gsmSetting) {
                        if (gsmSetting->passwordFlags().testFlag(NetworkManager::Setting::NotSaved) ||
                            gsmSetting->passwordFlags().testFlag(NetworkManager::Setting::NotRequired)) {
                            requestOffline = false;
                        } else if (gsmSetting->pinFlags().testFlag(NetworkManager::Setting::NotSaved) ||
                                   gsmSetting->pinFlags().testFlag(NetworkManager::Setting::NotRequired)) {
                            requestOffline = false;
                        }
                    }
                } else if (completeConnectionSettings->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
                    NetworkManager::WirelessSetting::Ptr wirelessSetting = completeConnectionSettings->setting(NetworkManager::Setting::Wireless).staticCast<NetworkManager::WirelessSetting>();
                    if (wirelessSetting && !wirelessSetting->security().isEmpty()) {
                        NetworkManager::WirelessSecuritySetting::Ptr wirelessSecuritySetting = completeConnectionSettings->setting(NetworkManager::Setting::WirelessSecurity).staticCast<NetworkManager::WirelessSecuritySetting>();
                        if (wirelessSecuritySetting && wirelessSecuritySetting->keyMgmt() == NetworkManager::WirelessSecuritySetting::WpaEap) {
                            NetworkManager::Security8021xSetting::Ptr security8021xSetting = completeConnectionSettings->setting(NetworkManager::Setting::Security8021x).staticCast<NetworkManager::Security8021xSetting>();
                            if (security8021xSetting) {
                                if (security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodFast) ||
                                    security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodTtls) ||
                                    security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodPeap)) {
                                    if (security8021xSetting->passwordFlags().testFlag(NetworkManager::Setting::NotSaved) ||
                                        security8021xSetting->passwordFlags().testFlag(NetworkManager::Setting::NotRequired)) {
                                        requestOffline = false;
                                    }
                                }
                            }
                        }
                    }
                }

                if (requestOffline) {
                    SecretsRequest requestOffline(SecretsRequest::SaveSecrets);
                    requestOffline.connection = connection;
                    requestOffline.connection_path = request.connection_path;
                    requestOffline.saveSecretsWithoutReply = true;
                    m_calls << requestOffline;
                }
204
205
            }

206
207
208
209
            m_calls.removeAt(i);
            break;
        }
    }
210

211
212
    m_dialog->deleteLater();
    m_dialog = 0;
213

Lukáš Tinkl's avatar
Lukáš Tinkl committed
214
    processNext();
215
216
217
218
}

void SecretAgent::dialogRejected()
{
219
220
221
222
223
224
225
226
227
228
    for (int i = 0; i < m_calls.size(); ++i) {
        SecretsRequest request = m_calls[i];
        if (request.type == SecretsRequest::GetSecrets && request.dialog == m_dialog) {
            sendError(SecretAgent::UserCanceled,
                      QLatin1String("User canceled the password dialog"),
                      request.message);
            m_calls.removeAt(i);
            break;
        }
    }
229

230
231
    m_dialog->deleteLater();
    m_dialog = 0;
232

Lukáš Tinkl's avatar
Lukáš Tinkl committed
233
    processNext();
234
235
236
237
}

void SecretAgent::killDialogs()
{
238
239
240
241
242
243
244
245
246
    int i = 0;
    while (i < m_calls.size()) {
        SecretsRequest request = m_calls[i];
        if (request.type == SecretsRequest::GetSecrets) {
            delete request.dialog;
            m_calls.removeAt(i);
        }

        ++i;
247
248
    }
}
249

250
void SecretAgent::walletOpened(bool success)
251
{
252
253
254
255
    if (!success) {
        m_wallet->deleteLater();
        m_wallet = 0;
    }
256
257
258
259
260
261
262
263
264
265
266
267
268
    processNext(!success);
}

void SecretAgent::walletClosed()
{
    if (m_wallet) {
        m_wallet->deleteLater();
    }
    m_wallet = 0;
}

void SecretAgent::processNext(bool ignoreWallet)
{
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
    int i = 0;
    while (i < m_calls.size()) {
        SecretsRequest &request = m_calls[i];
        switch (request.type) {
        case SecretsRequest::GetSecrets:
            if (processGetSecrets(request, ignoreWallet)) {
                m_calls.removeAt(i);
                continue;
            }
            break;
        case SecretsRequest::SaveSecrets:
            if (processSaveSecrets(request, ignoreWallet)) {
                m_calls.removeAt(i);
                continue;
            }
            break;
        case SecretsRequest::DeleteSecrets:
            if (processDeleteSecrets(request, ignoreWallet)) {
                m_calls.removeAt(i);
                continue;
            }
            break;
        }
        ++i;
293
    }
294
}
295

296
297
298
299
300
bool SecretAgent::processGetSecrets(SecretsRequest &request, bool ignoreWallet) const
{
    if (m_dialog) {
        return false;
    }
301

302
    NetworkManager::ConnectionSettings connectionSettings(request.connection);
303

304
    NetworkManager::Setting::Ptr setting = connectionSettings.setting(request.setting_name);
305

Daniel Nicoletti's avatar
Daniel Nicoletti committed
306
307
308
    const bool requestNew = request.flags & RequestNew;
    const bool userRequested = request.flags & UserRequested;
    const bool allowInteraction = request.flags & AllowInteraction;
309
    const bool isVpn = (setting->type() == NetworkManager::Setting::Vpn);
310

311
    NMStringMap secretsMap;
312
313
    if (!ignoreWallet && !requestNew && useWallet()) {
        if (m_wallet->isOpen()) {
314
315
            if (m_wallet->hasFolder("Network Management") && m_wallet->setFolder("Network Management")) {
                QString key = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % request.setting_name;
316
                m_wallet->readMap(key, secretsMap);
317
318
            }
        } else {
Sebastian Kügler's avatar
Sebastian Kügler committed
319
            qDebug() << "Waiting for the wallet to open";
320
            return false;
321
        }
322
323
    } else if (!requestNew && !m_wallet) {
        // If the wallet is disabled fallback to plain text
324
        KConfig config("plasma-networkmanagement");
325
        KConfigGroup secretsGroup(&config, QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % request.setting_name);
326
        secretsMap = secretsGroup.entryMap();
327
328
329
    }

    if (!secretsMap.isEmpty()) {
330
        setting->secretsFromStringMap(secretsMap);
331
        if (!isVpn && setting->needSecrets(requestNew).isEmpty()) {
332
333
334
335
336
            // Enough secrets were retrieved from storage
            request.connection[request.setting_name] = setting->secretsToMap();
            sendSecrets(request.connection, request.message);
            return true;
        }
337
338
339
    }

    if (requestNew || (allowInteraction && !setting->needSecrets(requestNew).isEmpty()) || (allowInteraction && userRequested) || (isVpn && allowInteraction)) {
340
        m_dialog = new PasswordDialog(request.connection, request.flags, request.setting_name);
341
342
        connect(m_dialog, SIGNAL(accepted()), this, SLOT(dialogAccepted()));
        connect(m_dialog, SIGNAL(rejected()), this, SLOT(dialogRejected()));
343
        if (isVpn) {
344
            m_dialog->setupVpnUi(connectionSettings);
345
        } else {
346
            m_dialog->setupGenericUi(connectionSettings);
347
348
        }

349
350
351
        if (m_dialog->hasError()) {
            sendError(m_dialog->error(),
                      m_dialog->errorMessage(),
352
                      request.message);
353
354
355
            delete m_dialog;
            m_dialog = 0;
            return true;
356
        } else {
357
            request.dialog = m_dialog;
358
            request.saveSecretsWithoutReply = !connectionSettings.permissions().isEmpty();
359
360
361
362
            m_dialog->show();
            KWindowSystem::setState(m_dialog->winId(), NET::KeepAbove);
            KWindowSystem::forceActiveWindow(m_dialog->winId());
            return false;
363
364
        }
    } else if (isVpn && userRequested) { // just return what we have
365
        NMVariantMapMap result;
366
367
        NetworkManager::VpnSetting::Ptr vpnSetting;
        vpnSetting = connectionSettings.setting(NetworkManager::Setting::Vpn).dynamicCast<NetworkManager::VpnSetting>();
368
369
        result.insert("vpn", vpnSetting->secretsToMap());
        sendSecrets(result, request.message);
370
        return true;
371
    } else if (setting->needSecrets().isEmpty()) {
372
373
374
        NMVariantMapMap result;
        result.insert(setting->name(), setting->secretsToMap());
        sendSecrets(result, request.message);
375
        return true;
376
377
378
379
    } else {
        sendError(SecretAgent::InternalError,
                  QLatin1String("Plasma-nm did not know how to handle the request"),
                  request.message);
380
        return true;
381
382
    }
}
383

384
385
386
387
bool SecretAgent::processSaveSecrets(SecretsRequest &request, bool ignoreWallet) const
{
    if (!ignoreWallet && useWallet()) {
        if (m_wallet->isOpen()) {
388
            NetworkManager::ConnectionSettings connectionSettings(request.connection);
389

390
391
            if (!m_wallet->hasFolder("Network Management")) {
                m_wallet->createFolder("Network Management");
392
393
            }

394
            if (m_wallet->setFolder("Network Management")) {
395
                foreach (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) {
396
                    NMStringMap secretsMap = setting->secretsToStringMap();
397

398
                    if (!secretsMap.isEmpty()) {
399
                        QString entryName = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name();
400
                        m_wallet->writeMap(entryName, secretsMap);
401
402
                    }
                }
403
            } else if (!request.saveSecretsWithoutReply) {
404
405
406
407
                sendError(SecretAgent::InternalError,
                          QLatin1String("Could not store secrets in the wallet."),
                          request.message);
                return true;
408
409
            }
        } else {
Sebastian Kügler's avatar
Sebastian Kügler committed
410
            qDebug() << "Waiting for the wallet to open";
411
412
            return false;
        }
413
    } else if (!m_wallet) {
414
        NetworkManager::ConnectionSettings connectionSettings(request.connection);
415
        KConfig config("plasma-networkmanagement");
416
        foreach (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) {
417
            KConfigGroup secretsGroup(&config, QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name());
418
419
            NMStringMap secretsMap = setting->secretsToStringMap();
            NMStringMap::ConstIterator i = secretsMap.constBegin();
420
            while (i != secretsMap.constEnd()) {
421
                secretsGroup.writeEntry(i.key(), i.value());
422
423
424
                ++i;
            }
        }
425
    }
426

427
    if (!request.saveSecretsWithoutReply) {
428
429
        QDBusMessage reply = request.message.createReply();
        if (!QDBusConnection::systemBus().send(reply)) {
Sebastian Kügler's avatar
Sebastian Kügler committed
430
            qWarning() << "Failed put save secrets reply into the queue";
431
        }
432
    }
433
434
435
436
437
438
439
440

    return true;
}

bool SecretAgent::processDeleteSecrets(SecretsRequest &request, bool ignoreWallet) const
{
    if (!ignoreWallet && useWallet()) {
        if (m_wallet->isOpen()) {
Jan Grulich's avatar
Jan Grulich committed
441
            if (m_wallet->hasFolder("Network Management") && m_wallet->setFolder("Network Management")) {
442
                NetworkManager::ConnectionSettings connectionSettings(request.connection);
Jan Grulich's avatar
Jan Grulich committed
443
444
445
446
447
448
                foreach (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) {
                    QString entryName = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name();
                    foreach (const QString &entry, m_wallet->entryList()) {
                        if (entry.startsWith(entryName)) {
                            m_wallet->removeEntry(entryName);
                        }
449
450
451
452
                    }
                }
            }
        } else {
Sebastian Kügler's avatar
Sebastian Kügler committed
453
            qDebug() << "Waiting for the wallet to open";
454
455
            return false;
        }
456
    } else if (!m_wallet) {
457
        NetworkManager::ConnectionSettings connectionSettings(request.connection);
458

459
        KConfig config("plasma-networkmanagement");
Jan Grulich's avatar
Jan Grulich committed
460
461
462
463
464
        foreach (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) {
            foreach (const QString &group, config.groupList()) {
                if (group.startsWith(QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name())) {
                    config.deleteGroup(group);
                }
465
466
            }
        }
467
    }
468

469
470
    QDBusMessage reply = request.message.createReply();
    if (!QDBusConnection::systemBus().send(reply)) {
Sebastian Kügler's avatar
Sebastian Kügler committed
471
        qWarning() << "Failed put delete secrets reply into the queue";
472
    }
473
474
475
476
477

    return true;
}

bool SecretAgent::useWallet() const
478
479
480
481
482
483
484
485
486
487
488
489
{
    if (m_wallet) {
        return true;
    }

    if (KWallet::Wallet::isEnabled()) {
        m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0, KWallet::Wallet::Asynchronous);
        if (m_wallet) {
            connect(m_wallet, SIGNAL(walletOpened(bool)), this, SLOT(walletOpened(bool)));
            connect(m_wallet, SIGNAL(walletClosed()), this, SLOT(walletClosed()));
            return true;
        } else {
Sebastian Kügler's avatar
Sebastian Kügler committed
490
            qWarning() << "Error opening kwallet.";
491
        }
492
493
494
    } else if (m_wallet) {
        m_wallet->deleteLater();
        m_wallet = 0;
495
496
497
498
499
    }

    return false;
}

500
501
bool SecretAgent::hasSecrets(const NMVariantMapMap &connection) const
{
502
503
    NetworkManager::ConnectionSettings connectionSettings(connection);
    foreach (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) {
504
505
506
507
508
509
510
511
        if (!setting->secretsToMap().isEmpty()) {
            return true;
        }
    }

    return false;
}

512
void SecretAgent::sendSecrets(const NMVariantMapMap &secrets, const QDBusMessage &message) const
513
514
{
    QDBusMessage reply;
Lukáš Tinkl's avatar
Lukáš Tinkl committed
515
    reply = message.createReply(QVariant::fromValue(secrets));
516
    if (!QDBusConnection::systemBus().send(reply)) {
Sebastian Kügler's avatar
Sebastian Kügler committed
517
        qWarning() << "Failed put the secret into the queue";
518
519
    }
}