From ba8b1bbe06aacb02b00737968503dc9ca95ba23a Mon Sep 17 00:00:00 2001 From: Nourredine Date: Tue, 18 Nov 2014 16:44:14 +0100 Subject: [PATCH 1/3] adds 'SSL client certificate' support from n.octeau with qknight changes as: * removed broken QSsl::SslV3 default * rewrote slotHandleErrors(): no longer claim errors which are none. * hack reverted: lib64 was not the cause for NixOS issues related to libraries. * refactored csync/src/csync_owncloud.c and discovered+fixed why the dav_connect was never getting the certPath+certPassoword * cleanup of code but seems this crushed the ssl client certificate support * fixes the https://github.com/owncloud/client/issues/69#issuecomment-69358377 issue * lots of cleanup * From TODO list : translate all french comments into english * changed _pemCertificate type from QString to QByteArray --- csync/src/CMakeLists.txt | 4 + csync/src/csync.h | 5 + csync/src/csync_owncloud.c | 53 +++++- csync/src/csync_owncloud_private.h | 1 + csync/src/csync_private.h | 4 + src/3rdparty/certificates/p12topem.cpp | 116 ++++++++++++ src/3rdparty/certificates/p12topem.h | 62 +++++++ src/cmd/cmd.cpp | 2 +- src/gui/CMakeLists.txt | 6 +- src/gui/addcertificatedialog.cpp | 49 +++++ src/gui/addcertificatedialog.h | 35 ++++ src/gui/addcertificatedialog.ui | 220 +++++++++++++++++++++++ src/gui/folder.cpp | 1 + src/gui/wizard/owncloudhttpcredspage.cpp | 9 +- src/gui/wizard/owncloudhttpcredspage.h | 4 +- src/gui/wizard/owncloudsetuppage.cpp | 102 ++++++++++- src/gui/wizard/owncloudsetuppage.h | 15 +- src/gui/wizard/owncloudwizard.cpp | 10 +- src/gui/wizard/owncloudwizard.h | 7 + src/libsync/CMakeLists.txt | 1 + src/libsync/account.cpp | 83 +++++++-- src/libsync/account.h | 8 + src/libsync/configfile.cpp | 45 +++++ src/libsync/configfile.h | 13 +- src/libsync/creds/http/httpconfigfile.h | 6 + src/libsync/creds/httpcredentials.cpp | 42 ++++- src/libsync/creds/httpcredentials.h | 10 +- src/libsync/networkjobs.cpp | 4 + 28 files changed, 875 insertions(+), 42 deletions(-) create mode 100644 src/3rdparty/certificates/p12topem.cpp create mode 100644 src/3rdparty/certificates/p12topem.h create mode 100644 src/gui/addcertificatedialog.cpp create mode 100644 src/gui/addcertificatedialog.h create mode 100644 src/gui/addcertificatedialog.ui diff --git a/csync/src/CMakeLists.txt b/csync/src/CMakeLists.txt index 415fdd5650..3b2f5f058c 100644 --- a/csync/src/CMakeLists.txt +++ b/csync/src/CMakeLists.txt @@ -30,6 +30,7 @@ set(CSYNC_LINK_LIBRARIES ${SQLITE3_LIBRARIES} ${NEON_LIBRARIES} ${HTTPBF_LIBRARY} + ${CRYPTO_LIBRARY} ) if(HAVE_ICONV AND WITH_ICONV) @@ -88,6 +89,9 @@ include_directories( ${CSYNC_PRIVATE_INCLUDE_DIRS} ) +find_package(OpenSSL) +FIND_LIBRARY(CRYPTO_LIBRARY NAMES crypto) + add_library(${CSYNC_LIBRARY} SHARED ${csync_SRCS}) #add_library(${CSYNC_LIBRARY}_static STATIC ${csync_SRCS}) diff --git a/csync/src/csync.h b/csync/src/csync.h index 78a092ff78..001b5f224a 100644 --- a/csync/src/csync.h +++ b/csync/src/csync.h @@ -44,6 +44,11 @@ extern "C" { #endif +struct csync_client_certs_s { + char *certificatePath; + char *certificatePasswd; +}; + /** * Instruction enum. In the file traversal structure, it describes * the csync state of a file. diff --git a/csync/src/csync_owncloud.c b/csync/src/csync_owncloud.c index 5cf08c43f4..a5e0644dec 100644 --- a/csync/src/csync_owncloud.c +++ b/csync/src/csync_owncloud.c @@ -374,7 +374,7 @@ static int post_send_hook(ne_request *req, void *userdata, * This function sets the flag _connected if the connection is established * and returns if the flag is set, so calling it frequently is save. */ -static int dav_connect(csync_owncloud_ctx_t *ctx, const char *base_url) { +static int dav_connect(csync_owncloud_ctx_t *ctx, struct csync_client_certs_s* clientCerts, const char *base_url) { int useSSL = 0; int rc; char protocol[6] = {'\0'}; @@ -447,6 +447,29 @@ static int dav_connect(csync_owncloud_ctx_t *ctx, const char *base_url) { goto out; } + if(clientCerts != NULL) { + ne_ssl_client_cert *clicert; + + DEBUG_WEBDAV("dav_connect: certificatePath and certificatePasswd are set, so we use it" ); + DEBUG_WEBDAV(" with certificatePath: %s", clientCerts->certificatePath ); + DEBUG_WEBDAV(" with certificatePasswd: %s", clientCerts->certificatePasswd ); + clicert = ne_ssl_clicert_read ( clientCerts->certificatePath ); + if ( clicert == NULL ) { + DEBUG_WEBDAV ( "Error read certificate : %s", ne_get_error ( ctx->dav_session.ctx ) ); + } else { + if ( ne_ssl_clicert_encrypted ( clicert ) ) { + int rtn = ne_ssl_clicert_decrypt ( clicert, clientCerts->certificatePasswd ); + if ( !rtn ) { + DEBUG_WEBDAV ( "Certificate was deciphered successfully." ); + ne_ssl_set_clicert ( ctx->dav_session.ctx, clicert ); + } else { + DEBUG_WEBDAV ( "Errors while deciphering certificate: %s", ne_get_error ( ctx->dav_session.ctx ) ); + } + } + } + } else { + DEBUG_WEBDAV("dav_connect: error with csync_client_certs_s* clientCerts"); + } ne_ssl_trust_default_ca( ctx->dav_session.ctx ); ne_ssl_set_verify( ctx->dav_session.ctx, ssl_callback_by_neon, ctx); } @@ -654,7 +677,7 @@ csync_vio_handle_t *owncloud_opendir(CSYNC *ctx, const char *uri) { DEBUG_WEBDAV("opendir method called on %s", uri ); - if (dav_connect( ctx->owncloud_context, uri ) < 0) { + if (dav_connect( ctx->owncloud_context, ctx->clientCerts, uri ) < 0) { DEBUG_WEBDAV("connection failed"); return NULL; } @@ -691,6 +714,7 @@ csync_vio_handle_t *owncloud_opendir(CSYNC *ctx, const char *uri) { int owncloud_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) { struct listdir_context *fetchCtx = dhandle; + free_fetchCtx(fetchCtx); (void)ctx; return 0; @@ -789,6 +813,12 @@ void owncloud_destroy(CSYNC* ctx) { owncloud_commit(ctx); SAFE_FREE(ctx->owncloud_context); + + SAFE_FREE(ctx->clientCerts->certificatePasswd); + SAFE_FREE(ctx->clientCerts->certificatePath); + SAFE_FREE(ctx->clientCerts); + ctx->clientCerts = NULL; + ctx->owncloud_context = 0; } @@ -826,12 +856,28 @@ int owncloud_set_property(CSYNC* ctx, const char *key, void *data) { if( c_streq(key, "redirect_callback")) { if (data) { csync_owncloud_redirect_callback_t* cb_wrapper = data; - ctx->owncloud_context->dav_session.redir_callback = *cb_wrapper; } else { ctx->owncloud_context->dav_session.redir_callback = NULL; } } + if( c_streq(key, "SSLClientCerts")) { + if(ctx->clientCerts != NULL) { + SAFE_FREE(ctx->clientCerts->certificatePasswd); + SAFE_FREE(ctx->clientCerts->certificatePath); + SAFE_FREE(ctx->clientCerts); + ctx->clientCerts = NULL; + } + if (data) { + struct csync_client_certs_s* clientCerts = (struct csync_client_certs_s*) data; + struct csync_client_certs_s* newCerts = c_malloc(sizeof(struct csync_client_certs_s)); + newCerts->certificatePath = c_strdup(clientCerts->certificatePath); + newCerts->certificatePasswd = c_strdup(clientCerts->certificatePasswd); + ctx->clientCerts = newCerts; + } else { + DEBUG_WEBDAV("error: in owncloud_set_property for 'SSLClientCerts'" ); + } + } return -1; } @@ -847,3 +893,4 @@ void owncloud_init(CSYNC* ctx) { } /* vim: set ts=4 sw=4 et cindent: */ + diff --git a/csync/src/csync_owncloud_private.h b/csync/src/csync_owncloud_private.h index 6da6419611..fc75006e1f 100644 --- a/csync/src/csync_owncloud_private.h +++ b/csync/src/csync_owncloud_private.h @@ -108,6 +108,7 @@ struct csync_owncloud_ctx_s { int _connected; /* flag to indicate if a connection exists, ie. the dav_session is valid */ }; + typedef struct csync_owncloud_ctx_s csync_owncloud_ctx_t; //typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p; diff --git a/csync/src/csync_private.h b/csync/src/csync_private.h index 764491647c..2938ee6d30 100644 --- a/csync/src/csync_private.h +++ b/csync/src/csync_private.h @@ -80,6 +80,7 @@ typedef struct csync_file_stat_s csync_file_stat_t; struct csync_owncloud_ctx_s; // csync_owncloud.c + /** * @brief csync public structure */ @@ -92,6 +93,9 @@ struct csync_s { } callbacks; c_strlist_t *excludes; + // needed for SSL client certificate support + struct csync_client_certs_s *clientCerts; + struct { char *file; sqlite3 *db; diff --git a/src/3rdparty/certificates/p12topem.cpp b/src/3rdparty/certificates/p12topem.cpp new file mode 100644 index 0000000000..aa6f7b42ba --- /dev/null +++ b/src/3rdparty/certificates/p12topem.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) by Pierre MOREAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program 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 General Public License + * for more details. + */ + +/** + * \file p12topem.cpp + * \brief Static library to convert p12 to pem + * \author Pierre MOREAU + * \version 1.0.0 + * \date 09 January 2014 + */ + +#include "p12topem.h" + +using namespace std; + +/** + * \fn string x509ToString (BIO) + * \brief Return string from BIO SSL + * \param BIO o PEM_write_BIO_... + * \return string PEM + */ +string x509ToString(BIO *o) { + int len = 0; + BUF_MEM *bptr; + void* data; + string ret = ""; + + BIO_get_mem_ptr(o, &bptr); + len = bptr->length; + data = calloc(len+10, sizeof(char)); + BIO_read(o, data, len); + ret = strdup((char*)data); + free(data); + + return ret; +} + +/** + * \fn resultP12ToPem p12ToPem (string, string) + * \brief Convert P12 to PEM + * \param string p12File Path to P12 file + * \param string p12Passwd Password to open P12 file + * \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate) + */ +resultP12ToPem p12ToPem(string p12File, string p12Passwd) { + FILE *fp; + PKCS12 *p12 = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + STACK_OF(X509) *ca = NULL; + + BIO *o = BIO_new(BIO_s_mem()); + + string privateKey = ""; + string certificate = ""; + + resultP12ToPem ret; + ret.ReturnCode = false; + ret.ErrorCode = 0; + ret.Comment = ""; + ret.PrivateKey = ""; + ret.Certificate = ""; + + SSLeay_add_all_algorithms(); + ERR_load_crypto_strings(); + if(!(fp = fopen(p12File.c_str(), "rb"))) { + ret.ErrorCode = 1; + ret.Comment = strerror(errno); + return ret; + } + + p12 = d2i_PKCS12_fp(fp, &p12); + fclose (fp); + + if (!p12) { + ret.ErrorCode = 2; + ret.Comment = "Unable to open PKCS#12 file"; + return ret; + } + if (!PKCS12_parse(p12, p12Passwd.c_str(), &pkey, &cert, &ca)) { + ret.ErrorCode = 3; + ret.Comment = "Unable to parse PKCS#12 file (wrong password ?)"; + return ret; + } + PKCS12_free(p12); + + if (!(pkey && cert)) { + ret.ErrorCode = 4; + ret.Comment = "Certificate and/or key file doesn't exists"; + } else { + PEM_write_bio_PrivateKey(o, pkey, 0, 0, 0, NULL, 0); + privateKey = x509ToString(o); + + PEM_write_bio_X509(o, cert); + certificate = x509ToString(o); + + BIO_free(o); + + ret.ReturnCode = true; + ret.ErrorCode = 0; + ret.Comment = "All is fine"; + ret.PrivateKey = privateKey; + ret.Certificate = certificate; + } + return ret; +} diff --git a/src/3rdparty/certificates/p12topem.h b/src/3rdparty/certificates/p12topem.h new file mode 100644 index 0000000000..0798a907ff --- /dev/null +++ b/src/3rdparty/certificates/p12topem.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) by Pierre MOREAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program 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 General Public License + * for more details. + */ + +#ifndef P12TOPEM_H +#define P12TOPEM_H + +/** + * \file p12topem.h + * \brief Static library to convert p12 to pem + * \author Pierre MOREAU + * \version 1.0.0 + * \date 09 January 2014 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/** + * \struct resultP12ToPem p12topem.h + */ +struct resultP12ToPem { + bool ReturnCode; + int ErrorCode; + string Comment; + string PrivateKey; + string Certificate; +}; + +/** + * \brief Return string from BIO SSL + * \param BIO o PEM_write_BIO_... + * \return string PEM + */ +string x509ToString(BIO *o); + +/** + * \brief Convert P12 to PEM + * \param string p12File Path to P12 file + * \param string p12Passwd Password to open P12 file + * \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate) + */ +resultP12ToPem p12ToPem(string p12File, string p12Passwd); + +#endif /* P12TOPEM_H */ + diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index 8709e401f4..d3628c996e 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -112,7 +112,7 @@ QString queryPassword(const QString &user) class HttpCredentialsText : public HttpCredentials { public: HttpCredentialsText(const QString& user, const QString& password) - : HttpCredentials(user, password), + : HttpCredentials(user, password, "", "", ""), // FIXME: not working with client certs yet (qknight) _sslTrusted(false) {} diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index ce22667e42..4a4b6c465b 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -24,6 +24,8 @@ set(client_UI settingsdialog.ui sharedialog.ui sslerrordialog.ui + owncloudsetuppage.ui + addcertificatedialog.ui wizard/owncloudadvancedsetuppage.ui wizard/owncloudhttpcredspage.ui wizard/owncloudsetupnocredspage.ui @@ -59,6 +61,7 @@ set(client_SRCS accountmigrator.cpp quotainfo.cpp accountstate.cpp + addcertificatedialog.cpp wizard/abstractcredswizardpage.cpp wizard/owncloudadvancedsetuppage.cpp wizard/owncloudhttpcredspage.cpp @@ -106,6 +109,7 @@ set(3rdparty_SRC ../3rdparty/qtsingleapplication/qtlocalpeer.cpp ../3rdparty/qtsingleapplication/qtsingleapplication.cpp ../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp + ../3rdparty/certificates/p12topem.cpp ) if (APPLE) @@ -253,7 +257,7 @@ install(TARGETS ${APPLICATION_EXECUTABLE} BUNDLE DESTINATION "." ) -# FIXME: The following lines are dup in src/gui and src/cmd because it needs to be done after both are installed + #FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary # currently it needs to be done because the code right above needs to be executed no matter # if building a bundle or not and the install_qt4_executable needs to be called afterwards diff --git a/src/gui/addcertificatedialog.cpp b/src/gui/addcertificatedialog.cpp new file mode 100644 index 0000000000..39bf70b520 --- /dev/null +++ b/src/gui/addcertificatedialog.cpp @@ -0,0 +1,49 @@ +#include "ui_addcertificatedialog.h" +#include "addcertificatedialog.h" +#include +#include + + +namespace OCC { +AddCertificateDialog::AddCertificateDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddCertificateDialog) +{ + ui->setupUi(this); + ui->labelErrorCertif->setText(""); +} + +AddCertificateDialog::~AddCertificateDialog() +{ + delete ui; +} + +void AddCertificateDialog::on_pushButtonBrowseCertificate_clicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select a certificate"), "", tr("Certificate files (*.p12 *.pfx)")); + ui->lineEditCertificatePath->setText(fileName); +} + +QString AddCertificateDialog::getCertificatePath() +{ + return ui->lineEditCertificatePath->text(); +} + +QString AddCertificateDialog::getCertificatePasswd() +{ + return ui->lineEditPWDCertificate->text(); +} + +void AddCertificateDialog::showErrorMessage(QString message) +{ + ui->labelErrorCertif->setText(message); +} + +void AddCertificateDialog::Reinit() +{ + ui->labelErrorCertif->setText(""); + ui->lineEditCertificatePath->setText(""); + ui->lineEditPWDCertificate->setText(""); +} + +} diff --git a/src/gui/addcertificatedialog.h b/src/gui/addcertificatedialog.h new file mode 100644 index 0000000000..ba06da20f0 --- /dev/null +++ b/src/gui/addcertificatedialog.h @@ -0,0 +1,35 @@ +#ifndef ADDCERTIFICATEDIALOG_H +#define ADDCERTIFICATEDIALOG_H + +#include +#include + +namespace OCC { + +namespace Ui { +class AddCertificateDialog; +} + +class AddCertificateDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddCertificateDialog(QWidget *parent = 0); + ~AddCertificateDialog(); + QString getCertificatePath(); + QString getCertificatePasswd(); + void showErrorMessage(QString message); + void Reinit(); + +private slots: + void on_pushButtonBrowseCertificate_clicked(); + +private: + Ui::AddCertificateDialog *ui; + +}; + +}//End namespace OCC + +#endif // ADDCERTIFICATEDIALOG_H diff --git a/src/gui/addcertificatedialog.ui b/src/gui/addcertificatedialog.ui new file mode 100644 index 0000000000..341a2a6040 --- /dev/null +++ b/src/gui/addcertificatedialog.ui @@ -0,0 +1,220 @@ + + + OCC::AddCertificateDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 400 + 350 + + + + + 400 + 350 + + + + + 400 + 350 + + + + SSL client certificate authentication + + + + + 30 + 280 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 20 + 100 + 141 + 16 + + + + Certificate : + + + + + + 20 + 120 + 291 + 21 + + + + + + + + + + 320 + 120 + 71 + 21 + + + + Browse... + + + + + + 20 + 160 + 181 + 16 + + + + Certificate password : + + + + + + 20 + 180 + 291 + 21 + + + + + + + QLineEdit::Password + + + + + + 20 + 30 + 341 + 41 + + + + This server probably requires a SSL client certificate. + + + true + + + + + + 20 + 220 + 361 + 51 + + + + + + + + + 231 + 23 + 12 + + + + + + + + + 231 + 23 + 12 + + + + + + + + + 127 + 127 + 127 + + + + + + + + + + + true + + + + + + + buttonBox + accepted() + OCC::AddCertificateDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + OCC::AddCertificateDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 5938adb522..5f27789c3f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -299,6 +299,7 @@ void Folder::slotRunEtagJob() _requestEtagJob = new RequestEtagJob(account, remotePath(), this); // check if the etag is different QObject::connect(_requestEtagJob, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString))); + QObject::connect(_requestEtagJob, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotNetworkUnavailable())); FolderMan::instance()->slotScheduleETagJob(alias(), _requestEtagJob); // The _requestEtagJob is auto deleting itself on finish. Our guard pointer _requestEtagJob will then be null. } diff --git a/src/gui/wizard/owncloudhttpcredspage.cpp b/src/gui/wizard/owncloudhttpcredspage.cpp index c7d1d0264b..4bca38aaf6 100644 --- a/src/gui/wizard/owncloudhttpcredspage.cpp +++ b/src/gui/wizard/owncloudhttpcredspage.cpp @@ -26,7 +26,7 @@ namespace OCC { -OwncloudHttpCredsPage::OwncloudHttpCredsPage() +OwncloudHttpCredsPage::OwncloudHttpCredsPage(QWidget* parent) : AbstractCredentialsWizardPage(), _ui(), _connected(false), @@ -36,6 +36,10 @@ OwncloudHttpCredsPage::OwncloudHttpCredsPage() { _ui.setupUi(this); + if(parent){ + _ocWizard = qobject_cast(parent); + } + registerField( QLatin1String("OCUser*"), _ui.leUsername); registerField( QLatin1String("OCPasswd*"), _ui.lePassword); @@ -148,7 +152,8 @@ void OwncloudHttpCredsPage::setErrorString(const QString& err) AbstractCredentials* OwncloudHttpCredsPage::getCredentials() const { - return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text()); + QDateTime now = QDateTime::currentDateTime(); + return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->ownCloudCertificatePath, now.toString(QLatin1String("yyyy-MM-dd")), _ocWizard->ownCloudCertificatePasswd); } void OwncloudHttpCredsPage::setConfigExists(bool config) diff --git a/src/gui/wizard/owncloudhttpcredspage.h b/src/gui/wizard/owncloudhttpcredspage.h index 07e2d45a47..2a2de53540 100644 --- a/src/gui/wizard/owncloudhttpcredspage.h +++ b/src/gui/wizard/owncloudhttpcredspage.h @@ -17,6 +17,7 @@ #define MIRALL_OWNCLOUD_HTTP_CREDS_PAGE_H #include "wizard/abstractcredswizardpage.h" +#include "wizard/owncloudwizard.h" #include "ui_owncloudhttpcredspage.h" @@ -28,7 +29,7 @@ class OwncloudHttpCredsPage : public AbstractCredentialsWizardPage { Q_OBJECT public: - OwncloudHttpCredsPage(); + OwncloudHttpCredsPage(QWidget* parent); AbstractCredentials* getCredentials() const Q_DECL_OVERRIDE; @@ -53,6 +54,7 @@ private: bool _checking; bool _configExists; QProgressIndicator* _progressIndi; + OwncloudWizard* _ocWizard; }; } // namespace OCC diff --git a/src/gui/wizard/owncloudsetuppage.cpp b/src/gui/wizard/owncloudsetuppage.cpp index f7733cb2a8..25571032dc 100644 --- a/src/gui/wizard/owncloudsetuppage.cpp +++ b/src/gui/wizard/owncloudsetuppage.cpp @@ -19,17 +19,21 @@ #include #include #include +#include +#include #include "QProgressIndicator.h" #include "wizard/owncloudwizardcommon.h" #include "wizard/owncloudsetuppage.h" +#include "../3rdparty/certificates/p12topem.h" #include "theme.h" +#include "account.h" namespace OCC { -OwncloudSetupPage::OwncloudSetupPage() +OwncloudSetupPage::OwncloudSetupPage(QWidget *parent) : QWizardPage(), _ui(), _oCUrl(), @@ -58,6 +62,10 @@ OwncloudSetupPage::OwncloudSetupPage() connect(_ui.leUrl, SIGNAL(textChanged(QString)), SLOT(slotUrlChanged(QString))); connect(_ui.leUrl, SIGNAL(editingFinished()), SLOT(slotUrlEditFinished())); + + addCertDial = new AddCertificateDialog(this); + _ocWizard = qobject_cast(parent); + connect(_ocWizard,SIGNAL(needCertificate()),this,SLOT(slotAskSSLClientCertificate())); } void OwncloudSetupPage::setServerUrl( const QString& newUrl ) @@ -228,14 +236,18 @@ void OwncloudSetupPage::setErrorString( const QString& err, bool retryHTTPonly ) _ui.errorLabel->setVisible(false); } else { if (retryHTTPonly) { - QString msg = tr("

Could not connect securely:

%1

Do you want to connect unencrypted instead (not recommended)?

").arg(err); - QString title = tr("Connection failed"); - if (QMessageBox::question(this, title, msg, QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - QUrl url(_ui.leUrl->text()); - url.setScheme("http"); - _ui.leUrl->setText(url.toString()); - // skip ahead to next page, since the user would expect us to retry automatically - wizard()->next(); + if (err.contains("SSL handshake failed", Qt::CaseInsensitive)) { + slotAskSSLClientCertificate(); + } else { + QString msg = tr("

Could not connect securely:

%1

Do you want to connect unencrypted instead (not recommended)?

").arg(err); + QString title = tr("Connection failed"); + if (QMessageBox::question(this, title, msg, QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + QUrl url(_ui.leUrl->text()); + url.setScheme("http"); + _ui.leUrl->setText(url.toString()); + // skip ahead to next page, since the user would expect us to retry automatically + wizard()->next(); + } } } @@ -269,4 +281,76 @@ void OwncloudSetupPage::setConfigExists( bool config ) } } + +void OwncloudSetupPage::slotAskSSLClientCertificate() +{ + addCertDial->show(); + connect(addCertDial, SIGNAL(accepted()),this,SLOT(slotCertificateAccepted())); +} + +//called during the validation of the client certificate. +void OwncloudSetupPage::slotCertificateAccepted() +{ + QSslCertificate sslCertificate; + + resultP12ToPem certif = p12ToPem(addCertDial->getCertificatePath().toStdString() , addCertDial->getCertificatePasswd().toStdString()); + if(certif.ReturnCode){ + QString s = QString::fromStdString(certif.Certificate); + QByteArray ba = s.toLocal8Bit(); + + QList sslCertificateList = QSslCertificate::fromData(ba, QSsl::Pem); + sslCertificate = sslCertificateList.takeAt(0); + + this->_ocWizard->ownCloudCertificate = ba; + this->_ocWizard->ownCloudPrivateKey = certif.PrivateKey.c_str(); + this->_ocWizard->ownCloudCertificatePath = addCertDial->getCertificatePath(); + this->_ocWizard->ownCloudCertificatePasswd = addCertDial->getCertificatePasswd(); + + //FIXME qknight: hacky code ahead + AccountPtr acc = this->_ocWizard->account(); + acc->setCertificate(_ocWizard->ownCloudCertificate, _ocWizard->ownCloudPrivateKey); + + QList qba = sslCertificate.subjectInfoAttributes(); + QString _DN = ""; + QString _C,_ST, _L, _O, _OU, _CN, _emailAddress; + foreach(QByteArray qa, qba) + { + if(strcmp(qa.data(),"C")==0){ + _C="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"ST")==0){ + _ST="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"L")==0){ + _L="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"O")==0){ + _O="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"OU")==0){ + _OU="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"CN")==0){ + _CN="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + else if(strcmp(qa.data(),"emailAddress")==0){ + _emailAddress="/"+QString(qa)+"="+sslCertificate.subjectInfo(qa).join('/'); + } + } + _DN += _C+_ST+_L+_O+_OU+_CN+_emailAddress; + addCertDial->Reinit(); + validatePage(); + } else { + QString message; + message = certif.Comment.c_str(); + addCertDial->showErrorMessage(message); + addCertDial->show(); + } +} + +OwncloudSetupPage::~OwncloudSetupPage() +{ + delete addCertDial; +} + } // namespace OCC diff --git a/src/gui/wizard/owncloudsetuppage.h b/src/gui/wizard/owncloudsetuppage.h index 5385b2d09a..2576cf3621 100644 --- a/src/gui/wizard/owncloudsetuppage.h +++ b/src/gui/wizard/owncloudsetuppage.h @@ -19,6 +19,10 @@ #include #include "wizard/owncloudwizardcommon.h" +#include "wizard/owncloudwizard.h" + +#include "../addcertificatedialog.h" + #include "ui_owncloudsetupnocredspage.h" class QLabel; @@ -31,7 +35,8 @@ class OwncloudSetupPage: public QWizardPage { Q_OBJECT public: - OwncloudSetupPage(); + OwncloudSetupPage(QWidget *parent=0); + ~OwncloudSetupPage(); virtual bool isComplete() const Q_DECL_OVERRIDE; virtual void initializePage() Q_DECL_OVERRIDE; @@ -50,6 +55,8 @@ public slots: void setConfigExists( bool ); void startSpinner(); void stopSpinner(); + void slotAskSSLClientCertificate(); + void slotCertificateAccepted(); protected slots: void slotUrlChanged(const QString&); @@ -64,14 +71,20 @@ private: bool urlHasChanged(); Ui_OwncloudSetupPage _ui; + QString _oCUrl; QString _ocUser; bool _authTypeKnown; bool _checking; + bool _configExists; + bool _multipleFoldersExist; WizardCommon::AuthType _authType; QProgressIndicator* _progressIndi; + QButtonGroup* _selectiveSyncButtons; QString _remoteFolder; + AddCertificateDialog* addCertDial; + OwncloudWizard* _ocWizard; }; } // namespace OCC diff --git a/src/gui/wizard/owncloudwizard.cpp b/src/gui/wizard/owncloudwizard.cpp index 6e04c17e85..1964000987 100644 --- a/src/gui/wizard/owncloudwizard.cpp +++ b/src/gui/wizard/owncloudwizard.cpp @@ -37,8 +37,8 @@ namespace OCC OwncloudWizard::OwncloudWizard(QWidget *parent) : QWizard(parent), _account(0), - _setupPage(new OwncloudSetupPage), - _httpCredsPage(new OwncloudHttpCredsPage), + _setupPage(new OwncloudSetupPage(this)), + _httpCredsPage(new OwncloudHttpCredsPage(this)), _shibbolethCredsPage(new OwncloudShibbolethCredsPage), _advancedSetupPage(new OwncloudAdvancedSetupPage), _resultPage(new OwncloudWizardResultPage), @@ -238,5 +238,11 @@ AbstractCredentials* OwncloudWizard::getCredentials() const return 0; } +// outputs the signal needed to authenticate a certificate +void OwncloudWizard::raiseCertificatePopup() +{ + emit needCertificate(); +} + } // end namespace diff --git a/src/gui/wizard/owncloudwizard.h b/src/gui/wizard/owncloudwizard.h index ebfa356967..06515a2403 100644 --- a/src/gui/wizard/owncloudwizard.h +++ b/src/gui/wizard/owncloudwizard.h @@ -61,6 +61,12 @@ public: void successfulStep(); AbstractCredentials* getCredentials() const; + void raiseCertificatePopup(); + QByteArray ownCloudCertificate; + QString ownCloudPrivateKey; + QString ownCloudCertificatePath; + QString ownCloudCertificatePasswd; + public slots: void setAuthType(WizardCommon::AuthType type); void setRemoteFolder( const QString& ); @@ -75,6 +81,7 @@ signals: // make sure to connect to this, rather than finished(int)!! void basicSetupFinished( int ); void skipFolderConfiguration(); + void needCertificate(); private: AccountPtr _account; diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index 5c0da1739f..fbaca3e484 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -67,6 +67,7 @@ set(libsync_SRCS creds/http/httpconfigfile.cpp creds/credentialscommon.cpp ../3rdparty/qjson/json.cpp + ../3rdparty/certificates/p12topem.cpp ) if(TOKEN_AUTH_ONLY) set (libsync_SRCS diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 79fdb74a35..b9f0d92a0d 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -20,6 +20,7 @@ #include "owncloudtheme.h" #include "creds/abstractcredentials.h" #include "creds/credentialsfactory.h" +#include "../3rdparty/certificates/p12topem.h" #include #include @@ -29,8 +30,8 @@ #include #include #include - #include +#include namespace OCC { @@ -294,6 +295,7 @@ QNetworkReply *Account::getRequest(const QString &relPath) QNetworkReply *Account::getRequest(const QUrl &url) { QNetworkRequest request(url); + request.setSslConfiguration(this->createSslConfig()); return _am->get(request); } @@ -305,14 +307,61 @@ QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPat QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data) { req.setUrl(url); + req.setSslConfiguration(this->createSslConfig()); return _am->sendCustomRequest(req, verb, data); } +void Account::setCertificate(QByteArray certficate, QString privateKey) +{ + _pemCertificate=certficate; + _pemPrivateKey=privateKey; +} + void Account::setSslConfiguration(const QSslConfiguration &config) { _sslConfiguration = config; } +QSslConfiguration Account::createSslConfig() +{ + // if setting the client certificate fails, you will probably get an error similar to this: + // "An internal error number 1060 happened. SSL handshake failed, client certificate was requested: SSL error: sslv3 alert handshake failure" + + // maybe this code must not have to be reevaluated every request? + QSslConfiguration sslConfig; + QSslCertificate sslClientCertificate; + + // maybe move this code from createSslConfig to the Account constructor + ConfigFile cfgFile; + if(!cfgFile.certificatePath().isEmpty() && !cfgFile.certificatePasswd().isEmpty()) { + resultP12ToPem certif = p12ToPem(cfgFile.certificatePath().toStdString(), cfgFile.certificatePasswd().toStdString()); + QString s = QString::fromStdString(certif.Certificate); + QByteArray ba = s.toLocal8Bit(); + this->setCertificate(ba, QString::fromStdString(certif.PrivateKey)); + } + if((!_pemCertificate.isEmpty())&&(!_pemPrivateKey.isEmpty())) { + // Read certificates + QList sslCertificateList = QSslCertificate::fromData(_pemCertificate, QSsl::Pem); + if(sslCertificateList.length() != 0) { + sslClientCertificate = sslCertificateList.takeAt(0); + } + // Read key from file + QSslKey privateKey(_pemPrivateKey.toLocal8Bit(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey , ""); + + // SSL configuration + sslConfig.defaultConfiguration(); + sslConfig.setCaCertificates(QSslSocket::systemCaCertificates()); + QList caCertifs = sslConfig.caCertificates(); + sslConfig.setLocalCertificate(sslClientCertificate); + sslConfig.setPrivateKey(privateKey); + qDebug() << "Added SSL client certificate to the query"; + } else { + qDebug() << "Failed to add the SSL client certificate to the query!"; + } + + return sslConfig; +} + void Account::setApprovedCerts(const QList certs) { _approvedCerts = certs; @@ -387,32 +436,34 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value) void Account::slotHandleErrors(QNetworkReply *reply , QList errors) { NetworkJobTimeoutPauser pauser(reply); - qDebug() << "SSL-Errors happened for url " << reply->url().toString(); + QString out; + QDebug(&out) << "SSL-Errors happened for url " << reply->url().toString(); foreach(const QSslError &error, errors) { - qDebug() << "\tError in " << error.certificate() << ":" - << error.errorString() << "("<< error.error()<< ")"; + QDebug(&out) << "\tError in " << error.certificate() << ":" + << error.errorString() << "("<< error.error() << ")" << "\n"; } if( _treatSslErrorsAsFailure ) { // User decided once not to trust. Honor this decision. - qDebug() << "Certs not trusted by user decision, returning."; + qDebug() << out << "Certs not trusted by user decision, returning."; return; } QList approvedCerts; if (_sslErrorHandler.isNull() ) { - qDebug() << Q_FUNC_INFO << "called without valid SSL error handler for account" << url(); + qDebug() << out << Q_FUNC_INFO << "called without valid SSL error handler for account" << url(); + return; + } + + if (_sslErrorHandler->handleErrors(errors, &approvedCerts, sharedFromThis())) { + QSslSocket::addDefaultCaCertificates(approvedCerts); + addApprovedCerts(approvedCerts); + // all ssl certs are known and accepted. We can ignore the problems right away. +// qDebug() << out << "Certs are known and trusted! This is not an actual error."; + reply->ignoreSslErrors(); } else { - if (_sslErrorHandler->handleErrors(errors, &approvedCerts, sharedFromThis())) { - QSslSocket::addDefaultCaCertificates(approvedCerts); - addApprovedCerts(approvedCerts); - // all ssl certs are known and accepted. We can ignore the problems right away. - qDebug() << "Certs are already known and trusted, Errors are not valid."; - reply->ignoreSslErrors(); - } else { - _treatSslErrorsAsFailure = true; - return; - } + _treatSslErrorsAsFailure = true; + return; } } diff --git a/src/libsync/account.h b/src/libsync/account.h index 8b5b1dfce6..fec170ba74 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,7 @@ public: QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0); /** The ssl configuration during the first connection */ + QSslConfiguration createSslConfig(); QSslConfiguration sslConfiguration() const { return _sslConfiguration; } void setSslConfiguration(const QSslConfiguration &config); /** The certificates of the account */ @@ -144,6 +146,8 @@ public: QVariant credentialSetting(const QString& key) const; void setCredentialSetting(const QString& key, const QVariant &value); + void setCertificate(QByteArray certficate = QByteArray(), QString privateKey = QString()); + void clearCookieJar(); QNetworkAccessManager* networkAccessManager(); @@ -169,10 +173,14 @@ private: QList _approvedCerts; QSslConfiguration _sslConfiguration; QScopedPointer _sslErrorHandler; + QuotaInfo *_quotaInfo; QNetworkAccessManager *_am; AbstractCredentials* _credentials; bool _treatSslErrorsAsFailure; + int _state; static QString _configFileName; + QByteArray _pemCertificate; + QString _pemPrivateKey; QString _davPath; // default "remote.php/webdav/"; bool _wasMigrated; }; diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 01dc70b2fb..e26a5851c2 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -64,6 +64,9 @@ static const char downloadLimitC[] = "BWLimit/downloadLimit"; static const char maxLogLinesC[] = "Logging/maxLogLines"; +const char certPath[] = "http_certificatePath"; +const char certDate[] = "http_certificateDate"; +const char certPasswd[] = "http_certificatePasswd"; QString ConfigFile::_confDir = QString::null; bool ConfigFile::_askedUser = false; @@ -213,6 +216,8 @@ QString ConfigFile::excludeFile(Scope scope) const // prefer sync-exclude.lst, but if it does not exist, check for // exclude.lst for compatibility reasons in the user writeable // directories. + const QString exclFile("sync-exclude.lst"); + QFileInfo fi; if (scope != SystemScope) { QFileInfo fi; @@ -255,11 +260,15 @@ QString ConfigFile::excludeFileFromSystem() fi.setFile( QCoreApplication::applicationDirPath(), QLatin1String("../Resources/") + exclFile ); #endif + return fi.absoluteFilePath(); } QString ConfigFile::configFile() const { + if( qApp->applicationName().isEmpty() ) { + qApp->setApplicationName( Theme::instance()->appNameGUI() ); + } return configPath() + Theme::instance()->configFileName(); } @@ -567,4 +576,40 @@ void ConfigFile::setCrashReporter(bool enabled) settings.setValue(QLatin1String(crashReporterC), enabled); } +QString ConfigFile::certificatePath() const +{ + return retrieveData(QString(), QLatin1String(certPath)).toString(); +} + +void ConfigFile::setCertificatePath(const QString& cPath) +{ + QSettings settings(configFile(), QSettings::IniFormat); + settings.setValue( QLatin1String(certPath), cPath); + settings.sync(); +} + +QString ConfigFile::certificateDate() const +{ + return retrieveData(QString(), QLatin1String(certDate)).toString(); +} + +void ConfigFile::setCertificateDate(const QString& cDate) +{ + QSettings settings(configFile(), QSettings::IniFormat); + settings.setValue( QLatin1String(certDate), cDate); + settings.sync(); +} + +QString ConfigFile::certificatePasswd() const +{ + return retrieveData(QString(), QLatin1String(certPasswd)).toString(); +} + +void ConfigFile::setCertificatePasswd(const QString& cPasswd) +{ + QSettings settings(configFile(), QSettings::IniFormat); + settings.setValue( QLatin1String(certPasswd), cPasswd); + settings.sync(); +} + } diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index 64d98df462..4635a0561b 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -12,8 +12,8 @@ * for more details. */ -#ifndef MIRALLCONFIGFILE_H -#define MIRALLCONFIGFILE_H +#ifndef CONFIGFILE_H +#define CONFIGFILE_H #include "owncloudlib.h" #include @@ -116,6 +116,13 @@ public: void saveGeometryHeader(QHeaderView *header); void restoreGeometryHeader(QHeaderView *header); + QString certificatePath() const; + void setCertificatePath(const QString& cPath); + QString certificateDate() const; + void setCertificateDate(const QString& cDate); + QString certificatePasswd() const; + void setCertificatePasswd(const QString& cPasswd); + protected: QVariant getPolicySetting(const QString& policy, const QVariant& defaultValue = QVariant()) const; void storeData(const QString& group, const QString& key, const QVariant& value); @@ -137,4 +144,4 @@ private: }; } -#endif // MIRALLCONFIGFILE_H +#endif // CONFIGFILE_H diff --git a/src/libsync/creds/http/httpconfigfile.h b/src/libsync/creds/http/httpconfigfile.h index 6c0aabb024..3781d13315 100644 --- a/src/libsync/creds/http/httpconfigfile.h +++ b/src/libsync/creds/http/httpconfigfile.h @@ -30,6 +30,12 @@ public: bool passwordExists() const; void removePassword(); void fixupOldPassword(); + QString certificatePath() const; + void setCertificatePath(const QString& cPath); + QString certificateDate() const; + void setCertificateDate(const QString& cDate); + QString certificatePasswd() const; + void setCertificatePasswd(const QString& cPasswd); private: void removeOldPassword(); diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index 95be072f38..590dfa7f25 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -59,6 +59,9 @@ int getauth(const char *prompt, QString qPrompt = QString::fromLatin1( prompt ).trimmed(); QString user = http_credentials->user(); QString pwd = http_credentials->password(); + QString certificatePath = http_credentials->certificatePath(); + QString certificateDate = http_credentials->certificateDate(); + QString certificatePasswd = http_credentials->certificatePasswd(); if( qPrompt == QLatin1String("Enter your username:") ) { // qDebug() << "OOO Username requested!"; @@ -79,6 +82,9 @@ int getauth(const char *prompt, namespace { const char userC[] = "user"; +const char certifPathC[] = "certificatePath"; +const char certifPasswdC[] = "certificatePasswd"; +const char certifDateC[] = "certificateDate"; const char authenticationFailedC[] = "owncloud-authentication-failed"; } // ns @@ -101,15 +107,21 @@ private: HttpCredentials::HttpCredentials() : _user(), _password(), + _certificatePath(), + _certificateDate(), + _certificatePasswd(), _ready(false), _fetchJobInProgress(false), _readPwdFromDeprecatedPlace(false) { } -HttpCredentials::HttpCredentials(const QString& user, const QString& password) +HttpCredentials::HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificateDate, const QString& certificatePasswd) : _user(user), _password(password), + _certificatePath(certificatePath), + _certificateDate(certificateDate), + _certificatePasswd(certificatePasswd), _ready(true), _fetchJobInProgress(false) { @@ -118,6 +130,13 @@ HttpCredentials::HttpCredentials(const QString& user, const QString& password) void HttpCredentials::syncContextPreInit (CSYNC* ctx) { csync_set_auth_callback (ctx, getauth); + // create a SSL client certificate configuration in CSYNC* ctx + struct csync_client_certs_s clientCerts; + clientCerts.certificatePath = strdup(_certificatePath.toStdString().c_str()); + clientCerts.certificatePasswd = strdup(_certificatePasswd.toStdString().c_str()); + csync_set_module_property(ctx, "SSLClientCerts", &clientCerts); + free(clientCerts.certificatePath); + free(clientCerts.certificatePasswd); } void HttpCredentials::syncContextPreStart (CSYNC* ctx) @@ -167,6 +186,21 @@ QString HttpCredentials::password() const return _password; } +QString HttpCredentials::certificatePath() const +{ + return _certificatePath; +} + +QString HttpCredentials::certificateDate() const +{ + return _certificateDate; +} + +QString HttpCredentials::certificatePasswd() const +{ + return _certificatePasswd; +} + QNetworkAccessManager* HttpCredentials::getQNAM() const { AccessManager* qnam = new HttpCredentialsAccessManager(this); @@ -196,6 +230,9 @@ void HttpCredentials::fetch() // User must be fetched from config file fetchUser(); + _certificatePath = _account->credentialSetting(QLatin1String(certifPathC)).toString(); + _certificatePasswd = _account->credentialSetting(QLatin1String(certifPasswdC)).toString(); + _certificateDate = _account->credentialSetting(QLatin1String(certifDateC)).toString(); QSettings *settings = _account->settingsWithGroup(Theme::instance()->appName()); const QString kck = keychainKey(_account->url().toString(), _user ); @@ -322,6 +359,9 @@ void HttpCredentials::persist() return; } _account->setCredentialSetting(QLatin1String(userC), _user); + _account->setCredentialSetting(QLatin1String(certifPathC), _certificatePath); + _account->setCredentialSetting(QLatin1String(certifPasswdC), _certificatePasswd); + _account->setCredentialSetting(QLatin1String(certifDateC), _certificateDate); WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName()); QSettings *settings = _account->settingsWithGroup(Theme::instance()->appName()); settings->setParent(job); // make the job parent to make setting deleted properly diff --git a/src/libsync/creds/httpcredentials.h b/src/libsync/creds/httpcredentials.h index 1a2aa78b76..f476e78b67 100644 --- a/src/libsync/creds/httpcredentials.h +++ b/src/libsync/creds/httpcredentials.h @@ -36,7 +36,7 @@ class OWNCLOUDSYNC_EXPORT HttpCredentials : public AbstractCredentials public: explicit HttpCredentials(); - HttpCredentials(const QString& user, const QString& password); + HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificateDate, const QString& certificatePasswd); void syncContextPreInit(CSYNC* ctx) Q_DECL_OVERRIDE; void syncContextPreStart(CSYNC* ctx) Q_DECL_OVERRIDE; @@ -53,6 +53,9 @@ public: void invalidateToken() Q_DECL_OVERRIDE; QString fetchUser(); virtual bool sslIsTrusted() { return false; } + QString certificatePath() const; + QString certificateDate() const; + QString certificatePasswd() const; private Q_SLOTS: void slotAuthentication(QNetworkReply*, QAuthenticator*); @@ -64,6 +67,9 @@ protected: QString _password; private: + QString _certificatePath; + QString _certificateDate; + QString _certificatePasswd; bool _ready; bool _fetchJobInProgress; //True if the keychain job is in progress or the input dialog visible bool _readPwdFromDeprecatedPlace; @@ -72,7 +78,7 @@ private: class OWNCLOUDSYNC_EXPORT HttpCredentialsGui : public HttpCredentials { public: explicit HttpCredentialsGui() : HttpCredentials() {} - HttpCredentialsGui(const QString& user, const QString& password) : HttpCredentials(user, password) {} + HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificateDate, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificateDate, certificatePasswd) {} QString queryPassword(bool *ok) Q_DECL_OVERRIDE; }; diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index 041f02524c..5ff037c3af 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -154,6 +154,10 @@ void AbstractNetworkJob::slotFinished() { _timer.stop(); + if( _reply->error() == QNetworkReply::SslHandshakeFailedError ) { + qDebug() << "SslHandshakeFailedError: " << reply()->errorString() << " : can be caused by a webserver wanting SSL client certificates"; + } + if( _reply->error() != QNetworkReply::NoError ) { qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString(); if (_reply->error() == QNetworkReply::ProxyAuthenticationRequiredError) { From 9abc3e1333c44f94b2e0d5cc3fbf52a9cdfc0cf2 Mon Sep 17 00:00:00 2001 From: Joachim Schiele Date: Fri, 23 Jan 2015 19:08:14 +0000 Subject: [PATCH 2/3] fixes required by upstream --- csync/src/csync_owncloud.c | 1 - src/3rdparty/certificates/p12topem.cpp | 2 -- src/gui/CMakeLists.txt | 1 + src/gui/addcertificatedialog.cpp | 2 +- src/gui/addcertificatedialog.h | 2 +- src/libsync/account.cpp | 2 +- src/libsync/account.h | 2 +- src/libsync/configfile.cpp | 4 ---- 8 files changed, 5 insertions(+), 11 deletions(-) diff --git a/csync/src/csync_owncloud.c b/csync/src/csync_owncloud.c index a5e0644dec..b8f8172b74 100644 --- a/csync/src/csync_owncloud.c +++ b/csync/src/csync_owncloud.c @@ -805,7 +805,6 @@ int owncloud_commit(CSYNC* ctx) { SAFE_FREE( ctx->owncloud_context->dav_session.pwd ); SAFE_FREE( ctx->owncloud_context->dav_session.session_key); SAFE_FREE( ctx->owncloud_context->dav_session.error_string ); - return 0; } diff --git a/src/3rdparty/certificates/p12topem.cpp b/src/3rdparty/certificates/p12topem.cpp index aa6f7b42ba..75e736a7ab 100644 --- a/src/3rdparty/certificates/p12topem.cpp +++ b/src/3rdparty/certificates/p12topem.cpp @@ -21,8 +21,6 @@ #include "p12topem.h" -using namespace std; - /** * \fn string x509ToString (BIO) * \brief Return string from BIO SSL diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4a4b6c465b..80bd1ce747 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -258,6 +258,7 @@ install(TARGETS ${APPLICATION_EXECUTABLE} ) +# FIXME: The following lines are dup in src/gui and src/cmd because it needs to be done after both are installed #FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary # currently it needs to be done because the code right above needs to be executed no matter # if building a bundle or not and the install_qt4_executable needs to be called afterwards diff --git a/src/gui/addcertificatedialog.cpp b/src/gui/addcertificatedialog.cpp index 39bf70b520..030658af07 100644 --- a/src/gui/addcertificatedialog.cpp +++ b/src/gui/addcertificatedialog.cpp @@ -34,7 +34,7 @@ QString AddCertificateDialog::getCertificatePasswd() return ui->lineEditPWDCertificate->text(); } -void AddCertificateDialog::showErrorMessage(QString message) +void AddCertificateDialog::showErrorMessage(const QString message) { ui->labelErrorCertif->setText(message); } diff --git a/src/gui/addcertificatedialog.h b/src/gui/addcertificatedialog.h index ba06da20f0..9bcd57a917 100644 --- a/src/gui/addcertificatedialog.h +++ b/src/gui/addcertificatedialog.h @@ -19,7 +19,7 @@ public: ~AddCertificateDialog(); QString getCertificatePath(); QString getCertificatePasswd(); - void showErrorMessage(QString message); + void showErrorMessage(const QString message); void Reinit(); private slots: diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index b9f0d92a0d..d3c1a870e6 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -311,7 +311,7 @@ QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNet return _am->sendCustomRequest(req, verb, data); } -void Account::setCertificate(QByteArray certficate, QString privateKey) +void Account::setCertificate(const QByteArray certficate, const QString privateKey) { _pemCertificate=certficate; _pemPrivateKey=privateKey; diff --git a/src/libsync/account.h b/src/libsync/account.h index fec170ba74..3d677469a0 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -146,7 +146,7 @@ public: QVariant credentialSetting(const QString& key) const; void setCredentialSetting(const QString& key, const QVariant &value); - void setCertificate(QByteArray certficate = QByteArray(), QString privateKey = QString()); + void setCertificate(const QByteArray certficate = QByteArray(), const QString privateKey = QString()); void clearCookieJar(); diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index e26a5851c2..ceadbda67e 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -216,7 +216,6 @@ QString ConfigFile::excludeFile(Scope scope) const // prefer sync-exclude.lst, but if it does not exist, check for // exclude.lst for compatibility reasons in the user writeable // directories. - const QString exclFile("sync-exclude.lst"); QFileInfo fi; if (scope != SystemScope) { @@ -266,9 +265,6 @@ QString ConfigFile::excludeFileFromSystem() QString ConfigFile::configFile() const { - if( qApp->applicationName().isEmpty() ) { - qApp->setApplicationName( Theme::instance()->appNameGUI() ); - } return configPath() + Theme::instance()->configFileName(); } From 989005d616bba9293835dbdd358c8781256d44d2 Mon Sep 17 00:00:00 2001 From: Daniel Molkentin Date: Wed, 28 Jan 2015 13:31:17 +0100 Subject: [PATCH 3/3] Clientcert support: Explictly link libsync against openssl --- CMakeLists.txt | 1 + csync/src/CMakeLists.txt | 1 - src/libsync/CMakeLists.txt | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82912ebb9c..79bf08132e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ endif() # find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED ) #endif() find_package(Neon REQUIRED) +find_package(OpenSSL) if(NOT TOKEN_AUTH_ONLY) if (Qt5Core_DIR) diff --git a/csync/src/CMakeLists.txt b/csync/src/CMakeLists.txt index 3b2f5f058c..c14f777437 100644 --- a/csync/src/CMakeLists.txt +++ b/csync/src/CMakeLists.txt @@ -89,7 +89,6 @@ include_directories( ${CSYNC_PRIVATE_INCLUDE_DIRS} ) -find_package(OpenSSL) FIND_LIBRARY(CRYPTO_LIBRARY NAMES crypto) add_library(${CSYNC_LIBRARY} SHARED ${csync_SRCS}) diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index fbaca3e484..49d652ae29 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -119,6 +119,7 @@ list(APPEND libsync_LINK_TARGETS ocsync httpbf ${OS_SPECIFIC_LINK_LIBRARIES} + ${OPENSSL_LIBRARIES} ) if(QTKEYCHAIN_FOUND OR QT5KEYCHAIN_FOUND)