Skip to content

Commit c6f4f44

Browse files
authoredJan 2, 2017
Fix up SSL client certificates #5213 #69 (#5289)
The re-enables the UI, uses Qt API for importing and stores the certificate/key in the system keychain. People who had set up client certs need to re-setup the account. This is ok since it was an undocumented feature anyway.
1 parent 0865c63 commit c6f4f44

22 files changed

+193
-318
lines changed
 

‎csync/src/csync.h

-5
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@
4444
extern "C" {
4545
#endif
4646

47-
struct csync_client_certs_s {
48-
char *certificatePath;
49-
char *certificatePasswd;
50-
};
51-
5247
enum csync_status_codes_e {
5348
CSYNC_STATUS_OK = 0,
5449

‎csync/src/csync_private.h

-3
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ struct csync_s {
103103

104104
} callbacks;
105105
c_strlist_t *excludes;
106-
107-
// needed for SSL client certificate support
108-
struct csync_client_certs_s *clientCerts;
109106

110107
struct {
111108
char *file;

‎src/3rdparty/certificates/p12topem.cpp

-110
This file was deleted.

‎src/3rdparty/certificates/p12topem.h

-62
This file was deleted.

‎src/cmd/cmd.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ QString queryPassword(const QString &user)
121121
class HttpCredentialsText : public HttpCredentials {
122122
public:
123123
HttpCredentialsText(const QString& user, const QString& password)
124-
: HttpCredentials(user, password, "", ""), // FIXME: not working with client certs yet (qknight)
124+
: HttpCredentials(user, password), // FIXME: not working with client certs yet (qknight)
125125
_sslTrusted(false)
126126
{}
127127

‎src/gui/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ set(3rdparty_SRC
151151
../3rdparty/qtsingleapplication/qtlocalpeer.cpp
152152
../3rdparty/qtsingleapplication/qtsingleapplication.cpp
153153
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
154-
../3rdparty/certificates/p12topem.cpp
155154
)
156155

157156
if (APPLE)

‎src/gui/accountmanager.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
237237

238238
acc->setCredentials(CredentialsFactory::create(authType));
239239

240-
// now the cert, it is in the general group
240+
// now the server cert, it is in the general group
241241
settings.beginGroup(QLatin1String("General"));
242242
acc->setApprovedCerts(QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray()));
243243
settings.endGroup();

‎src/gui/addcertificatedialog.ui

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<x>0</x>
1111
<y>0</y>
1212
<width>462</width>
13-
<height>186</height>
13+
<height>188</height>
1414
</rect>
1515
</property>
1616
<property name="windowTitle">
@@ -32,7 +32,7 @@
3232
<item row="0" column="0">
3333
<widget class="QLabel" name="labelCertificateFile">
3434
<property name="text">
35-
<string>Certificate :</string>
35+
<string>Certificate &amp; Key (pkcs12) :</string>
3636
</property>
3737
</widget>
3838
</item>

‎src/gui/creds/httpcredentialsgui.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class HttpCredentialsGui : public HttpCredentials {
2727
Q_OBJECT
2828
public:
2929
explicit HttpCredentialsGui() : HttpCredentials() {}
30-
HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificatePasswd) {}
30+
HttpCredentialsGui(const QString& user, const QString& password, const QSslCertificate& certificate, const QSslKey& key) : HttpCredentials(user, password, certificate, key) {}
3131
void askFromUser() Q_DECL_OVERRIDE;
3232
Q_INVOKABLE void askFromUserAsync();
3333

‎src/gui/wizard/owncloudconnectionmethoddialog.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ OwncloudConnectionMethodDialog::OwncloudConnectionMethodDialog(QWidget *parent)
2929
connect(ui->btnClientSideTLS, SIGNAL(clicked(bool)), this, SLOT(returnClientSideTLS()));
3030
connect(ui->btnBack, SIGNAL(clicked(bool)), this, SLOT(returnBack()));
3131

32-
// DM: TLS Client Cert GUI support disabled for now
32+
33+
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0)
34+
// We support only from Qt 5.4.x because of https://doc.qt.io/qt-5/qsslcertificate.html#importPkcs12
3335
ui->btnClientSideTLS->hide();
36+
#endif
3437
}
3538

3639
void OwncloudConnectionMethodDialog::setUrl(const QUrl &url)

‎src/gui/wizard/owncloudhttpcredspage.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ void OwncloudHttpCredsPage::setErrorString(const QString& err)
192192

193193
AbstractCredentials* OwncloudHttpCredsPage::getCredentials() const
194194
{
195-
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->ownCloudCertificatePath, _ocWizard->ownCloudCertificatePasswd);
195+
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->_clientSslCertificate, _ocWizard->_clientSslKey);
196196
}
197197

198198

‎src/gui/wizard/owncloudsetuppage.cpp

+27-27
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@
2121
#include <QMessageBox>
2222
#include <QSsl>
2323
#include <QSslCertificate>
24+
#include <QNetworkAccessManager>
2425

2526
#include "QProgressIndicator.h"
2627

2728
#include "wizard/owncloudwizardcommon.h"
2829
#include "wizard/owncloudsetuppage.h"
2930
#include "wizard/owncloudconnectionmethoddialog.h"
30-
#include "../3rdparty/certificates/p12topem.h"
3131
#include "theme.h"
3232
#include "account.h"
3333

@@ -71,7 +71,6 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
7171
connect(_ui.leUrl, SIGNAL(editingFinished()), SLOT(slotUrlEditFinished()));
7272

7373
addCertDial = new AddCertificateDialog(this);
74-
connect(_ocWizard,SIGNAL(needCertificate()),this,SLOT(slotAskSSLClientCertificate()));
7574
}
7675

7776
void OwncloudSetupPage::setServerUrl( const QString& newUrl )
@@ -269,7 +268,10 @@ void OwncloudSetupPage::setErrorString( const QString& err, bool retryHTTPonly )
269268
}
270269
break;
271270
case OwncloudConnectionMethodDialog::Client_Side_TLS:
272-
slotAskSSLClientCertificate();
271+
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
272+
addCertDial->show();
273+
connect(addCertDial, SIGNAL(accepted()),this,SLOT(slotCertificateAccepted()));
274+
#endif
273275
break;
274276
case OwncloudConnectionMethodDialog::Closed:
275277
case OwncloudConnectionMethodDialog::Back:
@@ -302,12 +304,6 @@ void OwncloudSetupPage::stopSpinner()
302304
_progressIndi->stopAnimation();
303305
}
304306

305-
void OwncloudSetupPage::slotAskSSLClientCertificate()
306-
{
307-
addCertDial->show();
308-
connect(addCertDial, SIGNAL(accepted()),this,SLOT(slotCertificateAccepted()));
309-
}
310-
311307
QString subjectInfoHelper(const QSslCertificate& cert, const QByteArray &qa)
312308
{
313309
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
@@ -320,36 +316,40 @@ QString subjectInfoHelper(const QSslCertificate& cert, const QByteArray &qa)
320316
//called during the validation of the client certificate.
321317
void OwncloudSetupPage::slotCertificateAccepted()
322318
{
323-
QSslCertificate sslCertificate;
319+
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
320+
QList<QSslCertificate> clientCaCertificates;
321+
QFile certFile(addCertDial->getCertificatePath());
322+
certFile.open(QFile::ReadOnly);
323+
if(QSslCertificate::importPkcs12(&certFile,
324+
&_ocWizard->_clientSslKey, &_ocWizard->_clientSslCertificate,
325+
&clientCaCertificates,
326+
addCertDial->getCertificatePasswd().toLocal8Bit())){
327+
AccountPtr acc = _ocWizard->account();
324328

325-
resultP12ToPem certif = p12ToPem(addCertDial->getCertificatePath().toStdString() , addCertDial->getCertificatePasswd().toStdString());
326-
if(certif.ReturnCode){
327-
QString s = QString::fromStdString(certif.Certificate);
328-
QByteArray ba = s.toLocal8Bit();
329+
// to re-create the session ticket because we added a key/cert
330+
acc->setSslConfiguration(QSslConfiguration());
331+
QSslConfiguration sslConfiguration = acc->getOrCreateSslConfig();
329332

330-
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(ba, QSsl::Pem);
331-
sslCertificate = sslCertificateList.takeAt(0);
333+
// We're stuffing the certificate into the configuration form here. Later the
334+
// cert will come via the HttpCredentials
335+
sslConfiguration.setLocalCertificate(_ocWizard->_clientSslCertificate);
336+
sslConfiguration.setPrivateKey(_ocWizard->_clientSslKey);
337+
acc->setSslConfiguration(sslConfiguration);
332338

333-
_ocWizard->ownCloudCertificate = ba;
334-
_ocWizard->ownCloudPrivateKey = certif.PrivateKey.c_str();
335-
_ocWizard->ownCloudCertificatePath = addCertDial->getCertificatePath();
336-
_ocWizard->ownCloudCertificatePasswd = addCertDial->getCertificatePasswd();
339+
// Make sure TCP connections get re-established
340+
acc->networkAccessManager()->clearAccessCache();
337341

338-
AccountPtr acc = _ocWizard->account();
339-
acc->setCertificate(_ocWizard->ownCloudCertificate, _ocWizard->ownCloudPrivateKey);
340-
addCertDial->reinit();
342+
addCertDial->reinit(); // FIXME: Why not just have this only created on use?
341343
validatePage();
342344
} else {
343-
QString message;
344-
message = certif.Comment.c_str();
345-
addCertDial->showErrorMessage(message);
345+
addCertDial->showErrorMessage("Could not load certificate");
346346
addCertDial->show();
347347
}
348+
#endif
348349
}
349350

350351
OwncloudSetupPage::~OwncloudSetupPage()
351352
{
352-
delete addCertDial;
353353
}
354354

355355
} // namespace OCC

‎src/gui/wizard/owncloudsetuppage.h

-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public slots:
5959
void setErrorString( const QString&, bool retryHTTPonly );
6060
void startSpinner();
6161
void stopSpinner();
62-
void slotAskSSLClientCertificate();
6362
void slotCertificateAccepted();
6463

6564
protected slots:

‎src/gui/wizard/owncloudwizard.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,4 @@ AbstractCredentials* OwncloudWizard::getCredentials() const
224224
return 0;
225225
}
226226

227-
// outputs the signal needed to authenticate a certificate
228-
void OwncloudWizard::raiseCertificatePopup()
229-
{
230-
emit needCertificate();
231-
}
232-
233-
234227
} // end namespace

‎src/gui/wizard/owncloudwizard.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#define MIRALL_OWNCLOUD_WIZARD_H
1818

1919
#include <QWizard>
20+
#include <QSslKey>
21+
#include <QSslCertificate>
2022

2123
#include "wizard/owncloudwizardcommon.h"
2224
#include "accountfwd.h"
@@ -63,11 +65,10 @@ class OwncloudWizard : public QWizard
6365
void displayError( const QString&, bool retryHTTPonly);
6466
AbstractCredentials* getCredentials() const;
6567

66-
void raiseCertificatePopup();
67-
QByteArray ownCloudCertificate;
68-
QString ownCloudPrivateKey;
69-
QString ownCloudCertificatePath;
70-
QString ownCloudCertificatePasswd;
68+
// FIXME: Can those be local variables?
69+
// Set from the OwncloudSetupPage, later used from OwncloudHttpCredsPage
70+
QSslKey _clientSslKey;
71+
QSslCertificate _clientSslCertificate;
7172

7273
public slots:
7374
void setAuthType(WizardCommon::AuthType type);

‎src/libsync/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ set(libsync_SRCS
7070
creds/abstractcredentials.cpp
7171
creds/credentialscommon.cpp
7272
../3rdparty/qjson/json.cpp
73-
../3rdparty/certificates/p12topem.cpp
7473
)
7574

7675
if(TOKEN_AUTH_ONLY)

‎src/libsync/account.cpp

-31
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "configfile.h"
1919
#include "accessmanager.h"
2020
#include "creds/abstractcredentials.h"
21-
#include "../3rdparty/certificates/p12topem.h"
2221
#include "capabilities.h"
2322
#include "theme.h"
2423

@@ -242,12 +241,6 @@ QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNet
242241
return _am->sendCustomRequest(req, verb, data);
243242
}
244243

245-
void Account::setCertificate(const QByteArray certficate, const QString privateKey)
246-
{
247-
_pemCertificate=certficate;
248-
_pemPrivateKey=privateKey;
249-
}
250-
251244
void Account::setSslConfiguration(const QSslConfiguration &config)
252245
{
253246
_sslConfiguration = config;
@@ -264,31 +257,7 @@ QSslConfiguration Account::getOrCreateSslConfig()
264257
// if setting the client certificate fails, you will probably get an error similar to this:
265258
// "An internal error number 1060 happened. SSL handshake failed, client certificate was requested: SSL error: sslv3 alert handshake failure"
266259
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
267-
QSslCertificate sslClientCertificate;
268260

269-
ConfigFile cfgFile;
270-
if(!cfgFile.certificatePath().isEmpty() && !cfgFile.certificatePasswd().isEmpty()) {
271-
resultP12ToPem certif = p12ToPem(cfgFile.certificatePath().toStdString(), cfgFile.certificatePasswd().toStdString());
272-
QString s = QString::fromStdString(certif.Certificate);
273-
QByteArray ba = s.toLocal8Bit();
274-
this->setCertificate(ba, QString::fromStdString(certif.PrivateKey));
275-
}
276-
if((!_pemCertificate.isEmpty())&&(!_pemPrivateKey.isEmpty())) {
277-
// Read certificates
278-
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(_pemCertificate, QSsl::Pem);
279-
if(sslCertificateList.length() != 0) {
280-
sslClientCertificate = sslCertificateList.takeAt(0);
281-
}
282-
// Read key from file
283-
QSslKey privateKey(_pemPrivateKey.toLocal8Bit(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey , "");
284-
285-
// SSL configuration
286-
sslConfig.setCaCertificates(QSslSocket::systemCaCertificates());
287-
sslConfig.setLocalCertificate(sslClientCertificate);
288-
sslConfig.setPrivateKey(privateKey);
289-
qDebug() << "Added SSL client certificate to the query";
290-
}
291-
292261
#if QT_VERSION > QT_VERSION_CHECK(5, 2, 0)
293262
// Try hard to re-use session for different requests
294263
sslConfig.setSslOption(QSsl::SslOptionDisableSessionTickets, false);

‎src/libsync/account.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,7 @@ protected Q_SLOTS:
224224
QList<QSslCertificate> _rejectedCertificates;
225225

226226
static QString _configFileName;
227-
QByteArray _pemCertificate;
228-
QString _pemPrivateKey;
227+
229228
QString _davPath; // defaults to value from theme, might be overwritten in brandings
230229
friend class AccountManager;
231230
};

‎src/libsync/connectionvalidator.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,12 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf
144144
// status.php could not be loaded (network or server issue!).
145145
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
146146
{
147-
qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString();
147+
qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString() << reply->peek(1024);
148+
if (reply && !_account->credentials()->ready()) {
149+
// This could be needed for SSL client certificates
150+
// We need to load them from keychain and try
151+
reportResult( CredentialsMissingOrWrong );
152+
} else
148153
if( reply && ! _account->credentials()->stillValid(reply)) {
149154
_errors.append(tr("Authentication error: Either username or password are wrong."));
150155
} else {

‎src/libsync/creds/httpcredentials.cpp

+127-45
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <QDebug>
1818
#include <QNetworkReply>
1919
#include <QSettings>
20+
#include <QSslKey>
2021

2122
#include <keychain.h>
2223

@@ -36,8 +37,8 @@ namespace OCC
3637
namespace
3738
{
3839
const char userC[] = "user";
39-
const char certifPathC[] = "certificatePath";
40-
const char certifPasswdC[] = "certificatePasswd";
40+
const char clientCertificatePEMC[] = "_clientCertificatePEM";
41+
const char clientKeyPEMC[] = "_clientKeyPEM";
4142
const char authenticationFailedC[] = "owncloud-authentication-failed";
4243
} // ns
4344

@@ -50,24 +51,47 @@ class HttpCredentialsAccessManager : public AccessManager {
5051
QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
5152
QNetworkRequest req(request);
5253
req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
53-
//qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
54+
//qDebug() << "Request for " << req.url() << "with authorization"
55+
// << QByteArray::fromBase64(credHash)
56+
// << _cred->_clientSslKey << _cred->_clientSslCertificate
57+
// << _cred->_clientSslKey.isNull() << _cred->_clientSslCertificate.isNull();
58+
59+
if (!_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) {
60+
// SSL configuration
61+
QSslConfiguration sslConfiguration = req.sslConfiguration();
62+
sslConfiguration.setLocalCertificate(_cred->_clientSslCertificate);
63+
sslConfiguration.setPrivateKey(_cred->_clientSslKey);
64+
req.setSslConfiguration(sslConfiguration);
65+
}
66+
67+
5468
return AccessManager::createRequest(op, req, outgoingData);
5569
}
5670
private:
5771
const HttpCredentials *_cred;
5872
};
5973

74+
75+
static void addSettingsToJob(Account *account, QKeychain::Job *job)
76+
{
77+
Q_UNUSED(account);
78+
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
79+
settings->setParent(job); // make the job parent to make setting deleted properly
80+
job->setSettings(settings.release());
81+
}
82+
6083
HttpCredentials::HttpCredentials()
6184
: _ready(false)
6285
{
6386
}
6487

65-
HttpCredentials::HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd)
88+
// From wizard
89+
HttpCredentials::HttpCredentials(const QString& user, const QString& password, const QSslCertificate& certificate, const QSslKey& key)
6690
: _user(user),
6791
_password(password),
6892
_ready(true),
69-
_certificatePath(certificatePath),
70-
_certificatePasswd(certificatePasswd)
93+
_clientSslKey(key),
94+
_clientSslCertificate(certificate)
7195
{
7296
}
7397

@@ -86,16 +110,6 @@ QString HttpCredentials::password() const
86110
return _password;
87111
}
88112

89-
QString HttpCredentials::certificatePath() const
90-
{
91-
return _certificatePath;
92-
}
93-
94-
QString HttpCredentials::certificatePasswd() const
95-
{
96-
return _certificatePasswd;
97-
}
98-
99113
void HttpCredentials::setAccount(Account* account)
100114
{
101115
AbstractCredentials::setAccount(account);
@@ -129,35 +143,83 @@ void HttpCredentials::fetchFromKeychain()
129143
{
130144
// User must be fetched from config file
131145
fetchUser();
132-
_certificatePath = _account->credentialSetting(QLatin1String(certifPathC)).toString();
133-
_certificatePasswd = _account->credentialSetting(QLatin1String(certifPasswdC)).toString();
134146

135-
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
136147
const QString kck = keychainKey(_account->url().toString(), _user );
137148

138-
QString key = QString::fromLatin1( "%1/data" ).arg( kck );
139-
if( settings && settings->contains(key) ) {
140-
// Clean the password from the config file if it is in there.
141-
// we do not want a security problem.
142-
settings->remove(key);
143-
key = QString::fromLatin1( "%1/type" ).arg( kck );
144-
settings->remove(key);
145-
settings->sync();
146-
}
147-
148149
if (_ready) {
149150
Q_EMIT fetched();
150151
} else {
152+
// Read client cert from keychain
153+
const QString kck = keychainKey(_account->url().toString(), _user + clientCertificatePEMC);
151154
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
152-
settings->setParent(job); // make the job parent to make setting deleted properly
153-
job->setSettings(settings.release());
154-
155+
addSettingsToJob(_account, job);
155156
job->setInsecureFallback(false);
156157
job->setKey(kck);
157-
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
158+
qDebug() << "-------- ----->" << _clientSslCertificate << _clientSslKey;
159+
160+
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadClientCertPEMJobDone(QKeychain::Job*)));
158161
job->start();
159162
}
160163
}
164+
165+
void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job* incoming)
166+
{
167+
// Store PEM in memory
168+
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(incoming);
169+
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
170+
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
171+
if(sslCertificateList.length() >= 1) {
172+
_clientSslCertificate = sslCertificateList.at(0);
173+
}
174+
}
175+
176+
// Load key too
177+
const QString kck = keychainKey(_account->url().toString(), _user + clientKeyPEMC);
178+
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
179+
addSettingsToJob(_account, job);
180+
job->setInsecureFallback(false);
181+
job->setKey(kck);
182+
183+
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadClientKeyPEMJobDone(QKeychain::Job*)));
184+
job->start();
185+
}
186+
187+
void HttpCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job* incoming)
188+
{
189+
// Store key in memory
190+
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(incoming);
191+
192+
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
193+
QByteArray clientKeyPEM = readJob->binaryData();
194+
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
195+
// load whatever we have. So we try until it works.
196+
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Rsa);
197+
if (_clientSslKey.isNull()) {
198+
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Dsa);
199+
}
200+
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
201+
// ec keys are Qt 5.5
202+
if (_clientSslKey.isNull()) {
203+
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Ec);
204+
}
205+
#endif
206+
if (_clientSslKey.isNull()) {
207+
qDebug() << "Warning: Could not load SSL key into Qt!";
208+
}
209+
}
210+
211+
// Now fetch the actual server password
212+
const QString kck = keychainKey(_account->url().toString(), _user );
213+
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
214+
addSettingsToJob(_account, job);
215+
job->setInsecureFallback(false);
216+
job->setKey(kck);
217+
218+
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
219+
job->start();
220+
}
221+
222+
161223
bool HttpCredentials::stillValid(QNetworkReply *reply)
162224
{
163225
return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
@@ -166,10 +228,10 @@ bool HttpCredentials::stillValid(QNetworkReply *reply)
166228
|| !reply->property(authenticationFailedC).toBool()));
167229
}
168230

169-
void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
231+
void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
170232
{
171-
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(job);
172-
_password = readJob->textData();
233+
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob*>(incomingJob);
234+
_password = job->textData();
173235

174236
if( _user.isEmpty()) {
175237
qDebug() << "Strange: User is empty!";
@@ -178,7 +240,6 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
178240
QKeychain::Error error = job->error();
179241

180242
if( !_password.isEmpty() && error == NoError ) {
181-
182243
// All cool, the keychain did not come back with error.
183244
// Still, the password can be empty which indicates a problem and
184245
// the password dialog has to be opened.
@@ -214,9 +275,7 @@ void HttpCredentials::invalidateToken()
214275
}
215276

216277
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
217-
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
218-
settings->setParent(job); // make the job parent to make setting deleted properly
219-
job->setSettings(settings.release());
278+
addSettingsToJob(_account, job);
220279
job->setInsecureFallback(true);
221280
job->setKey(kck);
222281
job->start();
@@ -261,14 +320,37 @@ void HttpCredentials::persist()
261320
// We never connected or fetched the user, there is nothing to save.
262321
return;
263322
}
323+
264324
_account->setCredentialSetting(QLatin1String(userC), _user);
265-
_account->setCredentialSetting(QLatin1String(certifPathC), _certificatePath);
266-
_account->setCredentialSetting(QLatin1String(certifPasswdC), _certificatePasswd);
325+
326+
// write cert
267327
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
268-
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
269-
settings->setParent(job); // make the job parent to make setting deleted properly
270-
job->setSettings(settings.release());
328+
addSettingsToJob(_account, job);
329+
job->setInsecureFallback(false);
330+
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteClientCertPEMJobDone(QKeychain::Job*)));
331+
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC));
332+
job->setBinaryData(_clientSslCertificate.toPem());
333+
job->start();
334+
}
271335

336+
void HttpCredentials::slotWriteClientCertPEMJobDone(Job *incomingJob)
337+
{
338+
Q_UNUSED(incomingJob);
339+
// write ssl key
340+
WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName());
341+
addSettingsToJob(_account, job);
342+
job->setInsecureFallback(false);
343+
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteClientKeyPEMJobDone(QKeychain::Job*)));
344+
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC));
345+
job->setBinaryData(_clientSslKey.toPem());
346+
job->start();
347+
}
348+
349+
void HttpCredentials::slotWriteClientKeyPEMJobDone(Job *incomingJob)
350+
{
351+
Q_UNUSED(incomingJob);
352+
WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName());
353+
addSettingsToJob(_account, job);
272354
job->setInsecureFallback(false);
273355
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
274356
job->setKey(keychainKey(_account->url().toString(), _user));

‎src/libsync/creds/httpcredentials.h

+15-9
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@
1717
#define MIRALL_CREDS_HTTP_CREDENTIALS_H
1818

1919
#include <QMap>
20-
20+
#include <QSslCertificate>
21+
#include <QSslKey>
2122
#include "creds/abstractcredentials.h"
2223

2324
class QNetworkReply;
2425
class QAuthenticator;
2526

2627
namespace QKeychain {
2728
class Job;
29+
class WritePasswordJob;
30+
class ReadPasswordJob;
2831
}
2932

3033
namespace OCC
@@ -33,10 +36,10 @@ namespace OCC
3336
class OWNCLOUDSYNC_EXPORT HttpCredentials : public AbstractCredentials
3437
{
3538
Q_OBJECT
36-
39+
friend class HttpCredentialsAccessManager;
3740
public:
3841
explicit HttpCredentials();
39-
HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd);
42+
HttpCredentials(const QString& user, const QString& password, const QSslCertificate& certificate = QSslCertificate(), const QSslKey& key = QSslKey());
4043

4144
QString authType() const Q_DECL_OVERRIDE;
4245
QNetworkAccessManager* getQNAM() const Q_DECL_OVERRIDE;
@@ -50,28 +53,31 @@ class OWNCLOUDSYNC_EXPORT HttpCredentials : public AbstractCredentials
5053
void forgetSensitiveData() Q_DECL_OVERRIDE;
5154
QString fetchUser();
5255
virtual bool sslIsTrusted() { return false; }
53-
QString certificatePath() const;
54-
QString certificatePasswd() const;
5556

5657
// To fetch the user name as early as possible
5758
void setAccount(Account* account) Q_DECL_OVERRIDE;
5859

5960
private Q_SLOTS:
6061
void slotAuthentication(QNetworkReply*, QAuthenticator*);
62+
63+
void slotReadClientCertPEMJobDone(QKeychain::Job*);
64+
void slotReadClientKeyPEMJobDone(QKeychain::Job*);
6165
void slotReadJobDone(QKeychain::Job*);
66+
67+
void slotWriteClientCertPEMJobDone(QKeychain::Job*);
68+
void slotWriteClientKeyPEMJobDone(QKeychain::Job*);
6269
void slotWriteJobDone(QKeychain::Job*);
6370
void clearQNAMCache();
6471

6572
protected:
6673
QString _user;
6774
QString _password;
6875
QString _previousPassword;
76+
6977
QString _fetchErrorString;
7078
bool _ready;
71-
72-
private:
73-
QString _certificatePath;
74-
QString _certificatePasswd;
79+
QSslKey _clientSslKey;
80+
QSslCertificate _clientSslCertificate;
7581
};
7682

7783
} // namespace OCC

‎src/libsync/networkjobs.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ bool CheckServerJob::finished()
472472
QVariantMap status = QtJson::parse(QString::fromUtf8(body), success).toMap();
473473
// empty or invalid response
474474
if (!success || status.isEmpty()) {
475-
qDebug() << "status.php from server is not valid JSON!";
475+
qDebug() << "status.php from server is not valid JSON!" << body << reply()->request().url();
476476
}
477477

478478
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();

0 commit comments

Comments
 (0)
Please sign in to comment.