/
ssu.cpp
686 lines (565 loc) · 21.7 KB
1
2
3
4
5
6
7
/**
* @file ssu.cpp
* @copyright 2012 Jolla Ltd.
* @author Bernd Wachter <bernd.wachter@jollamobile.com>
* @date 2012
*/
8
#include <QtNetwork>
9
#include <QtXml/QDomDocument>
10
11
12
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingReply>
13
14
15
16
17
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QUrlQuery>
#endif
18
19
#include <getdef.h>
#include <pwd.h>
20
21
#include <sys/types.h>
#include <unistd.h>
22
23
#include "ssu.h"
24
#include "sandbox_p.h"
25
26
27
#include "ssulog_p.h"
#include "ssuvariables_p.h"
#include "ssucoreconfig_p.h"
28
#include "ssurepomanager.h"
29
#include "ssudeviceinfo.h"
30
31
32
#include "../constants.h"
33
34
#define SSU_NETWORK_REQUEST_DOMAIN_DATA (static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1))
35
36
37
38
39
40
41
static void restoreUid(){
if (getuid() == 0){
seteuid(0);
setegid(0);
}
}
42
Ssu::Ssu(): QObject(){
43
errorFlag = false;
44
pendingRequests = 0;
45
46
#ifdef SSUCONFHACK
47
48
49
50
51
52
53
54
55
56
// dirty hack to make sure we can write to the configuration
// this is currently required since there's no global gconf,
// and we migth not yet have users on bootstrap
QFileInfo settingsInfo(SSU_CONFIGURATION);
if (settingsInfo.groupId() != SSU_GROUP_ID ||
!settingsInfo.permission(QFile::WriteGroup)){
QProcess proc;
proc.start("/usr/bin/ssuconfperm");
proc.waitForFinished();
}
57
#endif
58
59
SsuCoreConfig *settings = SsuCoreConfig::instance();
60
61
62
63
64
65
66
67
68
69
#ifdef TARGET_ARCH
if (!settings->contains("arch"))
settings->setValue("arch", TARGET_ARCH);
#else
// FIXME, try to guess a matching architecture
#warning "TARGET_ARCH not defined"
#endif
settings->sync();
70
71
72
73
74
75
76
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)),
SLOT(requestFinished(QNetworkReply *)));
}
77
78
// FIXME, the whole credentials stuff needs reworking
// should probably be part of repo handling instead of core configuration
79
QPair<QString, QString> Ssu::credentials(QString scope){
80
81
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->credentials(scope);
82
83
84
}
QString Ssu::credentialsScope(QString repoName, bool rndRepo){
85
SsuCoreConfig *settings = SsuCoreConfig::instance();
86
SsuSettings repoSettings(SSU_REPO_CONFIGURATION, QSettings::IniFormat);
87
88
89
90
91
// hardcoded magic for doing special privileges store repositories
if (repoName == "store" || repoName.startsWith("store-c-"))
return "store";
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// check if some repos are marked for using store-credentials
// in current domain, checking first for rnd/release specific
// settings, and if not found in generic settings
QString storeAuthReposKey = QString("store-auth-repos-%1")
.arg(rndRepo ? "rnd" : "release");
QStringList storeAuthRepos =
SsuVariables::variable(&repoSettings,
domain() + "-domain",
storeAuthReposKey).toStringList();
if (storeAuthRepos.empty())
storeAuthRepos =
SsuVariables::variable(&repoSettings,
domain() + "-domain",
"store-auth-repos").toStringList();
if (storeAuthRepos.contains(repoName))
return "store";
110
return settings->credentialsScope(repoName, rndRepo);
111
112
}
113
QString Ssu::credentialsUrl(QString scope){
114
115
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->credentialsUrl(scope);
116
117
}
118
119
120
121
bool Ssu::error(){
return errorFlag;
}
122
// Wrappers around SsuCoreConfig
123
QString Ssu::flavour(){
124
125
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->flavour();
126
127
}
128
Ssu::DeviceModeFlags Ssu::deviceMode(){
129
130
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->deviceMode();
131
132
}
133
QString Ssu::domain(){
134
SsuCoreConfig *settings = SsuCoreConfig::instance();
135
return settings->domain(true);
136
137
}
138
bool Ssu::isRegistered(){
139
140
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->isRegistered();
141
142
143
}
QDateTime Ssu::lastCredentialsUpdate(){
144
145
146
147
148
149
150
151
152
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->lastCredentialsUpdate();
}
QString Ssu::release(bool rnd){
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->release(rnd);
}
153
void Ssu::setDeviceMode(Ssu::DeviceModeFlags mode, enum Ssu::EditMode editMode){
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
SsuCoreConfig *settings = SsuCoreConfig::instance();
settings->setDeviceMode(mode, editMode);
}
void Ssu::setFlavour(QString flavour){
SsuCoreConfig *settings = SsuCoreConfig::instance();
settings->setFlavour(flavour);
}
void Ssu::setRelease(QString release, bool rnd){
SsuCoreConfig *settings = SsuCoreConfig::instance();
settings->setRelease(release, rnd);
}
void Ssu::setDomain(QString domain){
SsuCoreConfig *settings = SsuCoreConfig::instance();
170
settings->setDomain(domain);
171
172
173
174
175
}
bool Ssu::useSslVerify(){
SsuCoreConfig *settings = SsuCoreConfig::instance();
return settings->useSslVerify();
176
177
}
178
179
180
181
//...
182
183
184
185
186
187
QString Ssu::lastError(){
return errorString;
}
bool Ssu::registerDevice(QDomDocument *response){
QString certificateString = response->elementsByTagName("certificate").at(0).toElement().text();
188
QSslCertificate certificate(certificateString.toLatin1());
189
SsuLog *ssuLog = SsuLog::instance();
190
SsuCoreConfig *settings = SsuCoreConfig::instance();
191
192
193
194
195
196
197
198
199
200
if (certificate.isNull()){
// make sure device is in unregistered state on failed registration
settings->setValue("registered", false);
setError("Certificate is invalid");
return false;
} else
settings->setValue("certificate", certificate.toPem());
QString privateKeyString = response->elementsByTagName("privateKey").at(0).toElement().text();
201
QSslKey privateKey(privateKeyString.toLatin1(), QSsl::Rsa);
202
203
204
205
206
207
208
209
210
211
212
if (privateKey.isNull()){
settings->setValue("registered", false);
setError("Private key is invalid");
return false;
} else
settings->setValue("privateKey", privateKey.toPem());
// oldUser is just for reference purposes, in case we want to notify
// about owner changes for the device
QString oldUser = response->elementsByTagName("user").at(0).toElement().text();
213
ssuLog->print(LOG_DEBUG, QString("Old user for your device was: %1").arg(oldUser));
214
215
216
217
// if we came that far everything required for device registration is done
settings->setValue("registered", true);
settings->sync();
218
219
220
221
222
223
if (!settings->isWritable()){
setError("Configuration is not writable, device registration failed.");
return false;
}
224
225
226
227
228
229
emit registrationStatusChanged();
return true;
}
// RND repos have flavour (devel, testing, release), and release (latest, next)
// Release repos only have release (latest, next, version number)
230
231
232
QString Ssu::repoUrl(QString repoName, bool rndRepo,
QHash<QString, QString> repoParameters,
QHash<QString, QString> parametersOverride){
233
234
SsuRepoManager manager;
return manager.url(repoName, rndRepo, repoParameters, parametersOverride);
235
236
237
238
}
void Ssu::requestFinished(QNetworkReply *reply){
QSslConfiguration sslConfiguration = reply->sslConfiguration();
239
SsuLog *ssuLog = SsuLog::instance();
240
SsuCoreConfig *settings = SsuCoreConfig::instance();
241
242
QNetworkRequest request = reply->request();
QVariant originalDomainVariant = request.attribute(SSU_NETWORK_REQUEST_DOMAIN_DATA);
243
244
245
246
247
248
249
250
251
252
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
ssuLog->print(LOG_DEBUG, QString("Certificate used was issued for '%1' by '%2'. Complete chain:")
.arg(sslConfiguration.peerCertificate().subjectInfo(QSslCertificate::CommonName).join(""))
.arg(sslConfiguration.peerCertificate().issuerInfo(QSslCertificate::CommonName).join("")));
foreach (const QSslCertificate cert, sslConfiguration.peerCertificateChain()){
ssuLog->print(LOG_DEBUG, QString("-> %1").arg(cert.subjectInfo(QSslCertificate::CommonName).join("")));
}
#else
253
ssuLog->print(LOG_DEBUG, QString("Certificate used was issued for '%1' by '%2'. Complete chain:")
254
255
.arg(sslConfiguration.peerCertificate().subjectInfo(QSslCertificate::CommonName))
.arg(sslConfiguration.peerCertificate().issuerInfo(QSslCertificate::CommonName)));
256
257
foreach (const QSslCertificate cert, sslConfiguration.peerCertificateChain()){
258
ssuLog->print(LOG_DEBUG, QString("-> %1").arg(cert.subjectInfo(QSslCertificate::CommonName)));
259
}
260
#endif
261
262
263
264
265
266
267
268
pendingRequests--;
QString action;
QByteArray data;
QDomDocument doc;
QString xmlError;
269
/// @TODO: indicate that the device is not registered if there's a 404 on credentials update url
270
271
272
273
274
275
276
277
278
if (settings->contains("home-url")){
QString homeUrl = settings->value("home-url").toString().arg("");
homeUrl.remove(QRegExp("//+$"));
if (request.url().toString().startsWith(homeUrl, Qt::CaseInsensitive)){
// we don't care about errors on download request
if (reply->error() == 0) {
QByteArray data = reply->readAll();
storeAuthorizedKeys(data);
279
}
280
281
goto success;
282
}
283
}
284
285
286
287
288
if (reply->error() > 0){
setError(reply->errorString());
goto failure;
}
289
290
291
292
data = reply->readAll();
ssuLog->print(LOG_DEBUG, QString("RequestOutput %1")
.arg(data.data()));
293
294
295
296
297
if (!doc.setContent(data, &xmlError)){
setError(tr("Unable to parse server response (%1)").arg(xmlError));
goto failure;
}
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
action = doc.elementsByTagName("action").at(0).toElement().text();
if (!verifyResponse(&doc)) {
goto failure;
}
ssuLog->print(LOG_DEBUG, QString("Handling request of type %1")
.arg(action));
if (action == "register") {
if (registerDevice(&doc)) {
goto success;
}
} else if (action == "credentials") {
if (setCredentials(&doc)) {
goto success;
314
}
315
316
317
} else {
setError(tr("Response to unknown action encountered: %1").arg(action));
}
318
319
320
321
322
323
324
325
failure:
// Restore the original domain in case of failures with the registration
if (!originalDomainVariant.isNull()) {
QString originalDomain = originalDomainVariant.toString();
ssuLog->print(LOG_DEBUG, QString("Restoring domain on error: '%1'").arg(originalDomain));
setDomain(originalDomain);
}
326
327
328
// Fall through to cleanup handling in success from failure label
success:
329
ssuLog->print(LOG_DEBUG, QString("Request finished, pending requests: %1").arg(pendingRequests));
330
if (pendingRequests == 0) {
331
emit done();
332
}
333
334
}
335
void Ssu::sendRegistration(QString usernameDomain, QString password){
336
337
338
errorFlag = false;
QString ssuCaCertificate, ssuRegisterUrl;
339
QString username, domainName;
340
341
SsuLog *ssuLog = SsuLog::instance();
342
SsuCoreConfig *settings = SsuCoreConfig::instance();
343
SsuDeviceInfo deviceInfo;
344
345
346
347
348
QNetworkRequest request;
request.setAttribute(SSU_NETWORK_REQUEST_DOMAIN_DATA, domain());
ssuLog->print(LOG_DEBUG, QString("Saving current domain before request: '%1'").arg(domain()));
349
350
351
352
// Username can include also domain, (user@domain), separate those
if (usernameDomain.contains('@')) {
// separate domain/username and set domain
username = usernameDomain.section('@', 0, 0);
353
354
domainName = usernameDomain.section('@', 1, 1);
setDomain(domainName);
355
356
357
} else {
// No domain defined
username = usernameDomain;
358
359
if (settings->contains("default-rnd-domain"))
setDomain(settings->value("default-rnd-domain").toString());
360
361
}
362
363
ssuCaCertificate = SsuRepoManager::caCertificatePath();
if (ssuCaCertificate.isEmpty()){
364
setError("CA certificate for ssu not set ('_ca-certificate in domain')");
365
return;
366
}
367
368
if (!settings->contains("register-url")){
369
370
ssuRegisterUrl = repoUrl("register-url");
if (ssuRegisterUrl.isEmpty()){
371
setError("URL for ssu registration not set (config key 'register-url')");
372
373
return;
}
374
375
376
} else
ssuRegisterUrl = settings->value("register-url").toString();
377
QString IMEI = deviceInfo.deviceUid();
378
379
380
381
if (IMEI == ""){
setError("No valid UID available for your device. For phones: is your modem online?");
return;
}
382
383
384
385
386
387
388
389
390
391
392
393
394
395
QSslConfiguration sslConfiguration;
if (!useSslVerify())
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
sslConfiguration.setCaCertificates(QSslCertificate::fromPath(ssuCaCertificate));
request.setUrl(QUrl(QString(ssuRegisterUrl)
.arg(IMEI)
));
request.setSslConfiguration(sslConfiguration);
request.setRawHeader("Authorization", "Basic " +
QByteArray(QString("%1:%2")
.arg(username).arg(password)
396
.toLatin1()).toBase64());
397
398
399
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QUrl form;
400
401
402
403
404
405
406
407
408
409
410
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QUrlQuery q;
q.addQueryItem("protocolVersion", SSU_PROTOCOL_VERSION);
q.addQueryItem("deviceModel", deviceInfo.deviceModel());
if (!domain().isEmpty()){
q.addQueryItem("domain", domain());
}
form.setQuery(q);
#else
411
form.addQueryItem("protocolVersion", SSU_PROTOCOL_VERSION);
412
form.addQueryItem("deviceModel", deviceInfo.deviceModel());
413
414
415
if (!domain().isEmpty()){
form.addQueryItem("domain", domain());
}
416
#endif
417
418
419
ssuLog->print(LOG_DEBUG, QString("Sending request to %1")
.arg(request.url().url()));
420
421
422
QNetworkReply *reply;
423
pendingRequests++;
424
425
426
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
reply = manager->post(request, form.query(QUrl::FullyEncoded).toStdString().c_str());
#else
427
reply = manager->post(request, form.encodedQuery());
428
#endif
429
// we could expose downloadProgress() from reply in case we want progress info
430
431
432
433
434
435
QString homeUrl = settings->value("home-url").toString().arg(username);
if (!homeUrl.isEmpty()){
// clear header, the other request bits are reusable
request.setHeader(QNetworkRequest::ContentTypeHeader, 0);
request.setUrl(homeUrl + "/authorized_keys");
436
ssuLog->print(LOG_DEBUG, QString("Trying to get SSH keys from %1").arg(request.url().toString()));
437
438
439
pendingRequests++;
manager->get(request);
}
440
441
442
}
bool Ssu::setCredentials(QDomDocument *response){
443
SsuCoreConfig *settings = SsuCoreConfig::instance();
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
// generate list with all scopes for generic section, add sections
QDomNodeList credentialsList = response->elementsByTagName("credentials");
QStringList credentialScopes;
for (int i=0;i<credentialsList.size();i++){
QDomNode node = credentialsList.at(i);
QString scope;
QDomNamedNodeMap attributes = node.attributes();
if (attributes.contains("scope")){
scope = attributes.namedItem("scope").toAttr().value();
} else {
setError(tr("Credentials element does not have scope"));
return false;
}
if (node.hasChildNodes()){
QDomElement username = node.firstChildElement("username");
QDomElement password = node.firstChildElement("password");
if (username.isNull() || password.isNull()){
setError(tr("Username and/or password not set"));
return false;
} else {
settings->beginGroup("credentials-" + scope);
settings->setValue("username", username.text());
settings->setValue("password", password.text());
settings->endGroup();
settings->sync();
credentialScopes.append(scope);
}
} else {
setError("");
return false;
}
}
settings->setValue("credentialScopes", credentialScopes);
settings->setValue("lastCredentialsUpdate", QDateTime::currentDateTime());
settings->sync();
emit credentialsChanged();
return true;
}
void Ssu::setError(QString errorMessage){
errorFlag = true;
errorString = errorMessage;
489
490
491
SsuLog *ssuLog = SsuLog::instance();
492
// dump error message to systemd journal for easier debugging
493
ssuLog->print(LOG_WARNING, errorMessage);
494
495
// assume that we don't even need to wait for other pending requests,
496
// and just die. This is only relevant for CLI, which will exit after done()
497
498
499
emit done();
}
500
501
void Ssu::storeAuthorizedKeys(QByteArray data){
QDir dir;
502
SsuLog *ssuLog = SsuLog::instance();
503
504
505
506
507
508
509
510
511
512
513
514
515
516
int uid_min = getdef_num("UID_MIN", -1);
QString homePath;
if (getuid() >= uid_min){
homePath = dir.homePath();
} else if (getuid() == 0){
// place authorized_keys in the default users home when run with uid0
struct passwd *pw = getpwuid(uid_min);
if (pw == NULL){
ssuLog->print(LOG_DEBUG, QString("Unable to find password entry for uid %1")
.arg(uid_min));
return;
}
517
518
519
520
521
522
523
524
525
526
527
//homePath = QString(pw->pw_dir);
homePath = pw->pw_dir;
// use users uid/gid for creating the directories and files
setegid(pw->pw_gid);
seteuid(uid_min);
ssuLog->print(LOG_DEBUG, QString("Dropping to %1/%2 for writing authorized keys")
.arg(uid_min)
.arg(pw->pw_gid));
} else
528
529
return;
530
531
homePath = Sandbox::map(homePath);
532
533
534
if (dir.exists(homePath + "/.ssh/authorized_keys")){
ssuLog->print(LOG_DEBUG, QString(".ssh/authorized_keys already exists in %1")
.arg(homePath));
535
restoreUid();
536
537
538
539
540
541
542
return;
}
if (!dir.exists(homePath + "/.ssh"))
if (!dir.mkdir(homePath + "/.ssh")){
ssuLog->print(LOG_DEBUG, QString("Unable to create .ssh in %1")
.arg(homePath));
543
restoreUid();
544
545
return;
}
546
547
QFile::setPermissions(homePath + "/.ssh",
548
549
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
550
QFile authorizedKeys(homePath + "/.ssh/authorized_keys");
551
552
553
554
555
556
authorizedKeys.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
authorizedKeys.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
QTextStream out(&authorizedKeys);
out << data;
out.flush();
authorizedKeys.close();
557
558
restoreUid();
559
560
}
561
void Ssu::updateCredentials(bool force){
562
SsuCoreConfig *settings = SsuCoreConfig::instance();
563
SsuDeviceInfo deviceInfo;
564
565
errorFlag = false;
566
567
SsuLog *ssuLog = SsuLog::instance();
568
if (deviceInfo.deviceUid() == ""){
569
570
571
572
setError("No valid UID available for your device. For phones: is your modem online?");
return;
}
573
QString ssuCaCertificate, ssuCredentialsUrl;
574
575
ssuCaCertificate = SsuRepoManager::caCertificatePath();
if (ssuCaCertificate.isEmpty()){
576
setError("CA certificate for ssu not set ('_ca-certificate in domain')");
577
return;
578
}
579
580
if (!settings->contains("credentials-url")){
581
582
583
584
585
ssuCredentialsUrl = repoUrl("credentials-url");
if (ssuCredentialsUrl.isEmpty()){
setError("URL for credentials update not set (config key 'credentials-url')");
return;
}
586
587
588
589
590
591
592
593
594
} else
ssuCredentialsUrl = settings->value("credentials-url").toString();
if (!isRegistered()){
setError("Device is not registered.");
return;
}
if (!force){
595
// skip updating if the last update was less than 30 minutes ago
596
597
598
599
QDateTime now = QDateTime::currentDateTime();
if (settings->contains("lastCredentialsUpdate")){
QDateTime last = settings->value("lastCredentialsUpdate").toDateTime();
600
if (last >= now.addSecs(-1800)){
601
ssuLog->print(LOG_DEBUG, QString("Skipping credentials update, last update was at %1")
602
.arg(last.toString()));
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
emit done();
return;
}
}
}
// check when the last update was, decide if an update is required
QSslConfiguration sslConfiguration;
if (!useSslVerify())
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
QSslKey privateKey(settings->value("privateKey").toByteArray(), QSsl::Rsa);
QSslCertificate certificate(settings->value("certificate").toByteArray());
QList<QSslCertificate> caCertificates;
caCertificates << QSslCertificate::fromPath(ssuCaCertificate);
sslConfiguration.setCaCertificates(caCertificates);
sslConfiguration.setPrivateKey(privateKey);
sslConfiguration.setLocalCertificate(certificate);
QNetworkRequest request;
625
request.setUrl(QUrl(ssuCredentialsUrl.arg(deviceInfo.deviceUid())));
626
627
ssuLog->print(LOG_DEBUG, QString("Sending credential update request to %1")
628
.arg(request.url().toString()));
629
630
request.setSslConfiguration(sslConfiguration);
631
632
pendingRequests++;
manager->get(request);
633
634
}
635
void Ssu::updateStoreCredentials(){
636
637
638
SsuCoreConfig *settings = SsuCoreConfig::instance();
SsuLog *ssuLog = SsuLog::instance();
639
640
641
642
QDBusMessage message = QDBusMessage::createMethodCall("com.jolla.jollastore",
"/StoreClient",
"com.jolla.jollastore",
"storeCredentials");
643
QDBusPendingReply<QString, QString> reply = SsuCoreConfig::userSessionBus().asyncCall(message);
644
645
reply.waitForFinished();
if (reply.isError()) {
646
647
648
649
650
if (settings->value("ignore-credential-errors").toBool() == true){
ssuLog->print(LOG_WARNING, QString("Warning: ignore-credential-errors is set, passing auth errors down to libzypp"));
ssuLog->print(LOG_WARNING, QString("Store credentials not received. %1").arg(reply.error().message()));
} else
setError(QString("Store credentials not received. %1").arg(reply.error().message()));
651
652
653
654
655
656
657
658
} else {
SsuCoreConfig *settings = SsuCoreConfig::instance();
settings->beginGroup("credentials-store");
settings->setValue("username", reply.argumentAt<0>());
settings->setValue("password", reply.argumentAt<1>());
settings->endGroup();
settings->sync();
}
659
}
660
661
void Ssu::unregister(){
662
SsuCoreConfig *settings = SsuCoreConfig::instance();
663
664
665
settings->setValue("privateKey", "");
settings->setValue("certificate", "");
settings->setValue("registered", false);
666
settings->sync();
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
emit registrationStatusChanged();
}
bool Ssu::verifyResponse(QDomDocument *response){
QString action = response->elementsByTagName("action").at(0).toElement().text();
QString deviceId = response->elementsByTagName("deviceId").at(0).toElement().text();
QString protocolVersion = response->elementsByTagName("protocolVersion").at(0).toElement().text();
// compare device ids
if (protocolVersion != SSU_PROTOCOL_VERSION){
setError(
tr("Response has unsupported protocol version %1, client requires version %2")
.arg(protocolVersion)
.arg(SSU_PROTOCOL_VERSION)
);
return false;
}
return true;
}