ssu.cpp 20.1 KB
Newer Older
Aard's avatar
Aard committed
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>
Aard's avatar
Aard committed
9
#include <QtXml/QDomDocument>
10 11 12
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingReply>
13

Aard's avatar
Aard committed
14 15 16 17
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QUrlQuery>
#endif

18 19 20
#include <getdef.h>
#include <pwd.h>

Aard's avatar
Aard committed
21
#include "ssu.h"
22
#include "sandbox_p.h"
23
#include "ssulog.h"
24
#include "ssuvariables.h"
25 26
#include "ssucoreconfig.h"
#include "ssurepomanager.h"
27
#include "ssudeviceinfo.h"
Aard's avatar
Aard committed
28

29 30
#include "../constants.h"

31 32 33 34 35 36 37
static void restoreUid(){
  if (getuid() == 0){
    seteuid(0);
    setegid(0);
  }
}

38
Ssu::Ssu(): QObject(){
Aard's avatar
Aard committed
39
  errorFlag = false;
40
  pendingRequests = 0;
Aard's avatar
Aard committed
41

42
#ifdef SSUCONFHACK
Aard's avatar
Aard committed
43 44 45 46 47 48 49 50 51 52
  // 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();
  }
53
#endif
Aard's avatar
Aard committed
54

55
  SsuCoreConfig *settings = SsuCoreConfig::instance();
Aard's avatar
Aard committed
56 57 58 59 60 61 62 63 64 65

#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();

66 67


Aard's avatar
Aard committed
68 69 70 71 72
  manager = new QNetworkAccessManager(this);
  connect(manager, SIGNAL(finished(QNetworkReply *)),
          SLOT(requestFinished(QNetworkReply *)));
}

73 74
// FIXME, the whole credentials stuff needs reworking
// should probably be part of repo handling instead of core configuration
Aard's avatar
Aard committed
75
QPair<QString, QString> Ssu::credentials(QString scope){
76 77
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->credentials(scope);
Aard's avatar
Aard committed
78 79 80
}

QString Ssu::credentialsScope(QString repoName, bool rndRepo){
81
  SsuCoreConfig *settings = SsuCoreConfig::instance();
82
  SsuSettings repoSettings(SSU_REPO_CONFIGURATION, QSettings::IniFormat);
83 84 85 86 87

  // hardcoded magic for doing special privileges store repositories
  if (repoName == "store" || repoName.startsWith("store-c-"))
    return "store";

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
  // 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";

106
  return settings->credentialsScope(repoName, rndRepo);
Aard's avatar
Aard committed
107 108
}

109
QString Ssu::credentialsUrl(QString scope){
110 111
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->credentialsUrl(scope);
112 113
}

Aard's avatar
Aard committed
114 115 116 117
bool Ssu::error(){
  return errorFlag;
}

118
// Wrappers around SsuCoreConfig
Aard's avatar
Aard committed
119
QString Ssu::flavour(){
120 121
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->flavour();
Aard's avatar
Aard committed
122 123
}

124
int Ssu::deviceMode(){
125 126
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->deviceMode();
127 128
}

129
QString Ssu::domain(){
130
  SsuCoreConfig *settings = SsuCoreConfig::instance();
131
  return settings->domain(true);
132 133
}

Aard's avatar
Aard committed
134
bool Ssu::isRegistered(){
135 136
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->isRegistered();
Aard's avatar
Aard committed
137 138 139
}

QDateTime Ssu::lastCredentialsUpdate(){
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->lastCredentialsUpdate();
}

QString Ssu::release(bool rnd){
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->release(rnd);
}

void Ssu::setDeviceMode(int mode, int editMode){
  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();
166
  settings->setDomain(domain);
167 168 169 170 171
}

bool Ssu::useSslVerify(){
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  return settings->useSslVerify();
Aard's avatar
Aard committed
172 173
}

174 175 176 177
//...



Aard's avatar
Aard committed
178 179 180 181 182 183
QString Ssu::lastError(){
  return errorString;
}

bool Ssu::registerDevice(QDomDocument *response){
  QString certificateString = response->elementsByTagName("certificate").at(0).toElement().text();
Aard's avatar
Aard committed
184
  QSslCertificate certificate(certificateString.toLatin1());
185
  SsuLog *ssuLog = SsuLog::instance();
186
  SsuCoreConfig *settings = SsuCoreConfig::instance();
Aard's avatar
Aard committed
187 188 189 190 191 192 193 194 195 196

  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();
Aard's avatar
Aard committed
197
  QSslKey privateKey(privateKeyString.toLatin1(), QSsl::Rsa);
Aard's avatar
Aard committed
198 199 200 201 202 203 204 205 206 207 208

  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();
209
  ssuLog->print(LOG_DEBUG, QString("Old user for your device was: %1").arg(oldUser));
Aard's avatar
Aard committed
210 211 212 213

  // if we came that far everything required for device registration is done
  settings->setValue("registered", true);
  settings->sync();
214 215 216 217 218 219

  if (!settings->isWritable()){
    setError("Configuration is not writable, device registration failed.");
    return false;
  }

Aard's avatar
Aard committed
220 221 222 223 224 225
  emit registrationStatusChanged();
  return true;
}

// RND repos have flavour (devel, testing, release), and release (latest, next)
// Release repos only have release (latest, next, version number)
226 227 228
QString Ssu::repoUrl(QString repoName, bool rndRepo,
                     QHash<QString, QString> repoParameters,
                     QHash<QString, QString> parametersOverride){
229 230
  SsuRepoManager manager;
  return manager.url(repoName, rndRepo, repoParameters, parametersOverride);
Aard's avatar
Aard committed
231 232 233 234
}

void Ssu::requestFinished(QNetworkReply *reply){
  QSslConfiguration sslConfiguration = reply->sslConfiguration();
235
  SsuLog *ssuLog = SsuLog::instance();
236
  SsuCoreConfig *settings = SsuCoreConfig::instance();
Aard's avatar
Aard committed
237

Aard's avatar
Aard committed
238 239 240 241 242 243 244 245 246
#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
247
  ssuLog->print(LOG_DEBUG, QString("Certificate used was issued for '%1' by '%2'. Complete chain:")
248 249
               .arg(sslConfiguration.peerCertificate().subjectInfo(QSslCertificate::CommonName))
               .arg(sslConfiguration.peerCertificate().issuerInfo(QSslCertificate::CommonName)));
Aard's avatar
Aard committed
250 251

  foreach (const QSslCertificate cert, sslConfiguration.peerCertificateChain()){
252
    ssuLog->print(LOG_DEBUG, QString("-> %1").arg(cert.subjectInfo(QSslCertificate::CommonName)));
Aard's avatar
Aard committed
253
  }
Aard's avatar
Aard committed
254
#endif
Aard's avatar
Aard committed
255

256
  /// @TODO: indicate that the device is not registered if there's a 404 on credentials update url
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
  // what sucks more, this or goto?
  do {
    if (settings->contains("home-url")){
      QString homeUrl = settings->value("home-url").toString().arg("");
      homeUrl.remove(QRegExp("//+$"));
      QNetworkRequest request = reply->request();

      if (request.url().toString().startsWith(homeUrl, Qt::CaseInsensitive)){
        // we don't care about errors on download request
        if (reply->error() > 0) break;
        QByteArray data = reply->readAll();
        storeAuthorizedKeys(data);
        break;
      }
    }
Aard's avatar
Aard committed
272

273 274 275
    if (reply->error() > 0){
      pendingRequests--;
      setError(reply->errorString());
Aard's avatar
Aard committed
276
      return;
277 278
    } else {
      QByteArray data = reply->readAll();
279 280
      ssuLog->print(LOG_DEBUG, QString("RequestOutput %1")
                    .arg(data.data()));
281 282 283 284 285 286 287 288

      QDomDocument doc;
      QString xmlError;
      if (!doc.setContent(data, &xmlError)){
        pendingRequests--;
        setError(tr("Unable to parse server response (%1)").arg(xmlError));
        return;
      }
Aard's avatar
Aard committed
289

290
      QString action = doc.elementsByTagName("action").at(0).toElement().text();
Aard's avatar
Aard committed
291

292
      if (!verifyResponse(&doc)) break;
Aard's avatar
Aard committed
293

294 295
      ssuLog->print(LOG_DEBUG, QString("Handling request of type %1")
                    .arg(action));
296 297 298 299 300 301 302 303 304
      if (action == "register"){
        if (!registerDevice(&doc)) break;
      } else if (action == "credentials"){
        if (!setCredentials(&doc)) break;
      } else {
        pendingRequests--;
        setError(tr("Response to unknown action encountered: %1").arg(action));
        return;
      }
Aard's avatar
Aard committed
305
    }
306
  } while (false);
Aard's avatar
Aard committed
307

308
  pendingRequests--;
309

310
  ssuLog->print(LOG_DEBUG, QString("Request finished, pending requests: %1").arg(pendingRequests));
311
  if (pendingRequests == 0)
Aard's avatar
Aard committed
312 313 314
    emit done();
}

315
void Ssu::sendRegistration(QString usernameDomain, QString password){
Aard's avatar
Aard committed
316 317 318
  errorFlag = false;

  QString ssuCaCertificate, ssuRegisterUrl;
319
  QString username, domainName;
320

321
  SsuLog *ssuLog = SsuLog::instance();
322
  SsuCoreConfig *settings = SsuCoreConfig::instance();
323
  SsuDeviceInfo deviceInfo;
324

325 326 327 328
  // Username can include also domain, (user@domain), separate those
  if (usernameDomain.contains('@')) {
      // separate domain/username and set domain
      username = usernameDomain.section('@', 0, 0);
329 330
      domainName = usernameDomain.section('@', 1, 1);
      setDomain(domainName);
331 332 333
  } else {
      // No domain defined
      username = usernameDomain;
334 335
      if (settings->contains("default-rnd-domain"))
        setDomain(settings->value("default-rnd-domain").toString());
336 337
  }

338 339 340
  ssuCaCertificate = SsuRepoManager::caCertificatePath();
  if (ssuCaCertificate.isEmpty()){
    setError("CA certificate for SSU not set ('_ca-certificate in domain')");
Aard's avatar
Aard committed
341
    return;
342
  }
Aard's avatar
Aard committed
343 344

  if (!settings->contains("register-url")){
345 346 347 348 349
    ssuRegisterUrl = repoUrl("register-url");
    if (ssuRegisterUrl.isEmpty()){
      setError("URL for SSU registration not set (config key 'register-url')");
      return;
    }
Aard's avatar
Aard committed
350 351 352
  } else
    ssuRegisterUrl = settings->value("register-url").toString();

353
  QString IMEI = deviceInfo.deviceUid();
354 355 356 357
  if (IMEI == ""){
    setError("No valid UID available for your device. For phones: is your modem online?");
    return;
  }
Aard's avatar
Aard committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

  QSslConfiguration sslConfiguration;
  if (!useSslVerify())
    sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);

  sslConfiguration.setCaCertificates(QSslCertificate::fromPath(ssuCaCertificate));

  QNetworkRequest request;
  request.setUrl(QUrl(QString(ssuRegisterUrl)
                      .arg(IMEI)
                   ));
  request.setSslConfiguration(sslConfiguration);
  request.setRawHeader("Authorization", "Basic " +
                       QByteArray(QString("%1:%2")
                                  .arg(username).arg(password)
Aard's avatar
Aard committed
373
                                  .toLatin1()).toBase64());
Aard's avatar
Aard committed
374 375 376
  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

  QUrl form;
Aard's avatar
Aard committed
377 378 379 380 381 382 383 384 385 386 387

#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
Aard's avatar
Aard committed
388
  form.addQueryItem("protocolVersion", SSU_PROTOCOL_VERSION);
389
  form.addQueryItem("deviceModel", deviceInfo.deviceModel());
390 391 392
  if (!domain().isEmpty()){
    form.addQueryItem("domain", domain());
  }
Aard's avatar
Aard committed
393
#endif
Aard's avatar
Aard committed
394

395 396
  ssuLog->print(LOG_DEBUG, QString("Sending request to %1")
                .arg(request.url().url()));
397

Aard's avatar
Aard committed
398 399
  QNetworkReply *reply;

400
  pendingRequests++;
Aard's avatar
Aard committed
401 402 403
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  reply = manager->post(request, form.query(QUrl::FullyEncoded).toStdString().c_str());
#else
Aard's avatar
Aard committed
404
  reply = manager->post(request, form.encodedQuery());
Aard's avatar
Aard committed
405
#endif
Aard's avatar
Aard committed
406
  // we could expose downloadProgress() from reply in case we want progress info
407 408 409 410 411 412

  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");
413
    ssuLog->print(LOG_DEBUG, QString("Trying to get SSH keys from %1").arg(request.url().toString()));
414 415 416
    pendingRequests++;
    manager->get(request);
  }
Aard's avatar
Aard committed
417 418 419
}

bool Ssu::setCredentials(QDomDocument *response){
420
  SsuCoreConfig *settings = SsuCoreConfig::instance();
Aard's avatar
Aard committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
  // 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;
466

467 468
  SsuLog *ssuLog = SsuLog::instance();

469
  // dump error message to systemd journal for easier debugging
470
  ssuLog->print(LOG_WARNING, errorMessage);
471

472
  // assume that we don't even need to wait for other pending requests,
473
  // and just die. This is only relevant for CLI, which will exit after done()
Aard's avatar
Aard committed
474 475 476
  emit done();
}

477 478
void Ssu::storeAuthorizedKeys(QByteArray data){
  QDir dir;
479
  SsuLog *ssuLog = SsuLog::instance();
480

481 482 483 484 485 486 487 488 489 490 491 492 493
  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;
    }
494

495 496 497 498 499 500 501 502 503 504
    //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
505 506
    return;

507 508
  homePath = Sandbox::map(homePath);

509 510 511
  if (dir.exists(homePath + "/.ssh/authorized_keys")){
    ssuLog->print(LOG_DEBUG, QString(".ssh/authorized_keys already exists in %1")
                  .arg(homePath));
512
    restoreUid();
513 514 515 516 517 518 519
    return;
  }

  if (!dir.exists(homePath + "/.ssh"))
    if (!dir.mkdir(homePath + "/.ssh")){
      ssuLog->print(LOG_DEBUG, QString("Unable to create .ssh in %1")
                    .arg(homePath));
520
      restoreUid();
521 522
      return;
    }
523

524
  QFile::setPermissions(homePath + "/.ssh",
525 526
                        QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);

527
  QFile authorizedKeys(homePath + "/.ssh/authorized_keys");
528 529 530 531 532 533
  authorizedKeys.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
  authorizedKeys.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
  QTextStream out(&authorizedKeys);
  out << data;
  out.flush();
  authorizedKeys.close();
534

535
  restoreUid();
536 537
}

Aard's avatar
Aard committed
538
void Ssu::updateCredentials(bool force){
539
  SsuCoreConfig *settings = SsuCoreConfig::instance();
540
  SsuDeviceInfo deviceInfo;
Aard's avatar
Aard committed
541 542
  errorFlag = false;

543 544
  SsuLog *ssuLog = SsuLog::instance();

545
  if (deviceInfo.deviceUid() == ""){
546 547 548 549
    setError("No valid UID available for your device. For phones: is your modem online?");
    return;
  }

Aard's avatar
Aard committed
550
  QString ssuCaCertificate, ssuCredentialsUrl;
551 552 553
  ssuCaCertificate = SsuRepoManager::caCertificatePath();
  if (ssuCaCertificate.isEmpty()){
    setError("CA certificate for SSU not set ('_ca-certificate in domain')");
Aard's avatar
Aard committed
554
    return;
555
  }
Aard's avatar
Aard committed
556 557

  if (!settings->contains("credentials-url")){
558 559 560 561 562
    ssuCredentialsUrl = repoUrl("credentials-url");
    if (ssuCredentialsUrl.isEmpty()){
      setError("URL for credentials update not set (config key 'credentials-url')");
      return;
    }
Aard's avatar
Aard committed
563 564 565 566 567 568 569 570 571
  } else
    ssuCredentialsUrl = settings->value("credentials-url").toString();

  if (!isRegistered()){
    setError("Device is not registered.");
    return;
  }

  if (!force){
572
    // skip updating if the last update was less than 30 minutes ago
Aard's avatar
Aard committed
573 574 575 576
    QDateTime now = QDateTime::currentDateTime();

    if (settings->contains("lastCredentialsUpdate")){
      QDateTime last = settings->value("lastCredentialsUpdate").toDateTime();
577
      if (last >= now.addSecs(-1800)){
578
        ssuLog->print(LOG_DEBUG, QString("Skipping credentials update, last update was at %1")
579
                     .arg(last.toString()));
Aard's avatar
Aard committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
        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;
602
  request.setUrl(QUrl(ssuCredentialsUrl.arg(deviceInfo.deviceUid())));
Aard's avatar
Aard committed
603

604
  ssuLog->print(LOG_DEBUG, QString("Sending credential update request to %1")
605
               .arg(request.url().toString()));
Aard's avatar
Aard committed
606 607
  request.setSslConfiguration(sslConfiguration);

608 609
  pendingRequests++;
  manager->get(request);
Aard's avatar
Aard committed
610 611
}

612 613 614 615 616 617 618
void Ssu::updateStoreCredentials(const QString &userName, const QString &accessToken){
  SsuCoreConfig *settings = SsuCoreConfig::instance();
  settings->beginGroup("credentials-store");
  settings->setValue("username", userName);
  settings->setValue("password", accessToken);
  settings->endGroup();
  settings->sync();
619
}
Aard's avatar
Aard committed
620 621

void Ssu::unregister(){
622
  SsuCoreConfig *settings = SsuCoreConfig::instance();
Aard's avatar
Aard committed
623 624 625
  settings->setValue("privateKey", "");
  settings->setValue("certificate", "");
  settings->setValue("registered", false);
626
  settings->sync();
Aard's avatar
Aard committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
  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;
}