Merge remote-tracking branch 'origin/ssl_client_cert'

Conflicts:
	CMakeLists.txt
	csync/src/CMakeLists.txt
	csync/src/csync_owncloud.c
This commit is contained in:
Olivier Goffart 2015-01-28 14:11:29 +01:00
commit d70e146c1f
29 changed files with 869 additions and 40 deletions

View File

@ -142,6 +142,7 @@ endif()
if (USE_NEON)
find_package(Neon REQUIRED)
endif(USE_NEON)
find_package(OpenSSL)
if(NOT TOKEN_AUTH_ONLY)
if (Qt5Core_DIR)

View File

@ -68,6 +68,7 @@ if(USE_NEON)
)
list(APPEND CSYNC_LINK_LIBRARIES
${NEON_LIBRARIES}
${CRYPTO_LIBRARY}
)
add_definitions(-DUSE_NEON)
endif(USE_NEON)
@ -93,6 +94,8 @@ include_directories(
${CSYNC_PRIVATE_INCLUDE_DIRS}
)
FIND_LIBRARY(CRYPTO_LIBRARY NAMES crypto)
add_library(${CSYNC_LIBRARY} SHARED ${csync_SRCS})
#add_library(${CSYNC_LIBRARY}_static STATIC ${csync_SRCS})

View File

@ -46,6 +46,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.

View File

@ -385,6 +385,7 @@ int dav_connect(CSYNC *csyncCtx, const char *base_url) {
unsigned int port = 0;
int proxystate = -1;
csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context;
struct csync_client_certs_s* clientCerts = csyncCtx->clientCerts;
if (ctx->_connected) {
return 0;
@ -448,6 +449,29 @@ int dav_connect(CSYNC *csyncCtx, 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);
}
@ -478,6 +502,7 @@ out:
return rc;
}
char *owncloud_error_string(CSYNC* ctx)
{
return ctx->owncloud_context->dav_session.error_string;
@ -505,7 +530,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;
}
@ -513,6 +537,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;
ne_sock_exit();
}
@ -547,12 +577,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;
}
@ -567,3 +613,4 @@ void owncloud_init(CSYNC* ctx) {
}
/* vim: set ts=4 sw=4 et cindent: */

View File

@ -96,6 +96,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;

View File

@ -79,6 +79,7 @@ typedef struct csync_file_stat_s csync_file_stat_t;
struct csync_owncloud_ctx_s; // csync_owncloud.c
/**
* @brief csync public structure
*/
@ -96,6 +97,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;

114
src/3rdparty/certificates/p12topem.cpp vendored Normal file
View File

@ -0,0 +1,114 @@
/*
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
*
* 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 <p.moreau@agim.idshost.fr>
* \version 1.0.0
* \date 09 January 2014
*/
#include "p12topem.h"
/**
* \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;
}

62
src/3rdparty/certificates/p12topem.h vendored Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
*
* 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 <p.moreau@agim.idshost.fr>
* \version 1.0.0
* \date 09 January 2014
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
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 */

View File

@ -110,7 +110,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)
{}

View File

@ -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)
@ -251,6 +255,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

View File

@ -0,0 +1,49 @@
#include "ui_addcertificatedialog.h"
#include "addcertificatedialog.h"
#include <QFileDialog>
#include <QLineEdit>
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(const QString message)
{
ui->labelErrorCertif->setText(message);
}
void AddCertificateDialog::Reinit()
{
ui->labelErrorCertif->setText("");
ui->lineEditCertificatePath->setText("");
ui->lineEditPWDCertificate->setText("");
}
}

View File

@ -0,0 +1,35 @@
#ifndef ADDCERTIFICATEDIALOG_H
#define ADDCERTIFICATEDIALOG_H
#include <QDialog>
#include <QString>
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(const QString message);
void Reinit();
private slots:
void on_pushButtonBrowseCertificate_clicked();
private:
Ui::AddCertificateDialog *ui;
};
}//End namespace OCC
#endif // ADDCERTIFICATEDIALOG_H

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::AddCertificateDialog</class>
<widget class="QDialog" name="OCC::AddCertificateDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>350</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>SSL client certificate authentication</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>30</x>
<y>280</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="labelCertificateFile">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>141</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Certificate :</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEditCertificatePath">
<property name="geometry">
<rect>
<x>20</x>
<y>120</y>
<width>291</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButtonBrowseCertificate">
<property name="geometry">
<rect>
<x>320</x>
<y>120</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Browse...</string>
</property>
</widget>
<widget class="QLabel" name="labelPWDCertificate">
<property name="geometry">
<rect>
<x>20</x>
<y>160</y>
<width>181</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Certificate password :</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEditPWDCertificate">
<property name="geometry">
<rect>
<x>20</x>
<y>180</y>
<width>291</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
<widget class="QLabel" name="labelErrorMessage">
<property name="geometry">
<rect>
<x>20</x>
<y>30</y>
<width>341</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>This server probably requires a SSL client certificate.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="labelErrorCertif">
<property name="geometry">
<rect>
<x>20</x>
<y>220</y>
<width>361</width>
<height>51</height>
</rect>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>231</red>
<green>23</green>
<blue>12</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>231</red>
<green>23</green>
<blue>12</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OCC::AddCertificateDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OCC::AddCertificateDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -301,6 +301,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.
}

View File

@ -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<OwncloudWizard *>(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)

View File

@ -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

View File

@ -19,17 +19,21 @@
#include <QTimer>
#include <QPushButton>
#include <QMessageBox>
#include <QSsl>
#include <QSslCertificate>
#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<OwncloudWizard *>(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("<p>Could not connect securely:</p><p>%1</p><p>Do you want to connect unencrypted instead (not recommended)?</p>").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("<p>Could not connect securely:</p><p>%1</p><p>Do you want to connect unencrypted instead (not recommended)?</p>").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<QSslCertificate> 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<QByteArray> 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

View File

@ -19,6 +19,10 @@
#include <QWizard>
#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

View File

@ -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

View File

@ -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;

View File

@ -66,6 +66,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
@ -122,6 +123,7 @@ list(APPEND libsync_LINK_TARGETS
${QT_LIBRARIES}
ocsync
${OS_SPECIFIC_LINK_LIBRARIES}
${OPENSSL_LIBRARIES}
)
if(QTKEYCHAIN_FOUND OR QT5KEYCHAIN_FOUND)

View File

@ -20,6 +20,7 @@
#include "owncloudtheme.h"
#include "creds/abstractcredentials.h"
#include "creds/credentialsfactory.h"
#include "../3rdparty/certificates/p12topem.h"
#include <QSettings>
#include <QMutex>
@ -29,8 +30,8 @@
#include <QNetworkCookieJar>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QSslKey>
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(const QByteArray certficate, const 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<QSslCertificate> 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<QSslCertificate> 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<QSslCertificate> certs)
{
_approvedCerts = certs;
@ -387,32 +436,34 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value)
void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> 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<QSslCertificate> 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;
}
}

View File

@ -19,6 +19,7 @@
#include <QUrl>
#include <QNetworkCookie>
#include <QNetworkRequest>
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslConfiguration>
#include <QSslError>
@ -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(const QByteArray certficate = QByteArray(), const QString privateKey = QString());
void clearCookieJar();
QNetworkAccessManager* networkAccessManager();
@ -169,10 +173,14 @@ private:
QList<QSslCertificate> _approvedCerts;
QSslConfiguration _sslConfiguration;
QScopedPointer<AbstractSslErrorHandler> _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;
};

View File

@ -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,7 @@ 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.
QFileInfo fi;
if (scope != SystemScope) {
QFileInfo fi;
@ -255,6 +259,7 @@ QString ConfigFile::excludeFileFromSystem()
fi.setFile( QCoreApplication::applicationDirPath(),
QLatin1String("../Resources/") + exclFile );
#endif
return fi.absoluteFilePath();
}
@ -567,4 +572,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();
}
}

View File

@ -12,8 +12,8 @@
* for more details.
*/
#ifndef MIRALLCONFIGFILE_H
#define MIRALLCONFIGFILE_H
#ifndef CONFIGFILE_H
#define CONFIGFILE_H
#include "owncloudlib.h"
#include <QSharedPointer>
@ -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

View File

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

View File

@ -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

View File

@ -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;
};

View File

@ -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) {