2017-11-25 22:19:25 +03:00
# include "webflowcredentials.h"
2017-11-28 22:06:27 +03:00
# include "creds/httpcredentials.h"
# include <QAuthenticator>
# include <QNetworkAccessManager>
# include <QNetworkReply>
# include <QPointer>
# include <QTimer>
2017-11-25 22:19:25 +03:00
# include <keychain.h>
2017-11-29 00:25:35 +03:00
# include <QDialog>
# include <QVBoxLayout>
# include <QLabel>
2017-11-25 22:19:25 +03:00
# include "accessmanager.h"
# include "account.h"
2019-08-27 04:32:21 +03:00
# include "configfile.h"
2017-11-25 22:19:25 +03:00
# include "theme.h"
2017-11-29 00:25:35 +03:00
# include "wizard/webview.h"
# include "webflowcredentialsdialog.h"
2017-11-25 22:19:25 +03:00
using namespace QKeychain ;
namespace OCC {
2017-11-28 22:06:27 +03:00
Q_LOGGING_CATEGORY ( lcWebFlowCredentials , " sync.credentials.webflow " , QtInfoMsg )
2019-08-27 04:32:21 +03:00
namespace {
const char userC [ ] = " user " ;
const char clientCertificatePEMC [ ] = " _clientCertificatePEM " ;
const char clientKeyPEMC [ ] = " _clientKeyPEM " ;
2019-08-30 05:56:01 +03:00
const char clientCaCertificatePEMC [ ] = " _clientCaCertificatePEM " ;
2019-08-27 04:32:21 +03:00
} // ns
2018-09-04 21:59:25 +03:00
class WebFlowCredentialsAccessManager : public AccessManager
{
public :
WebFlowCredentialsAccessManager ( const WebFlowCredentials * cred , QObject * parent = nullptr )
: AccessManager ( parent )
, _cred ( cred )
{
}
protected :
2018-11-11 13:09:29 +03:00
QNetworkReply * createRequest ( Operation op , const QNetworkRequest & request , QIODevice * outgoingData ) override
2018-09-04 21:59:25 +03:00
{
QNetworkRequest req ( request ) ;
2019-08-27 04:32:21 +03:00
if ( ! req . attribute ( WebFlowCredentials : : DontAddCredentialsAttribute ) . toBool ( ) ) {
2018-09-04 21:59:25 +03:00
if ( _cred & & ! _cred - > password ( ) . isEmpty ( ) ) {
QByteArray credHash = QByteArray ( _cred - > user ( ) . toUtf8 ( ) + " : " + _cred - > password ( ) . toUtf8 ( ) ) . toBase64 ( ) ;
req . setRawHeader ( " Authorization " , " Basic " + credHash ) ;
}
}
2019-08-27 04:32:21 +03:00
if ( _cred & & ! _cred - > _clientSslKey . isNull ( ) & & ! _cred - > _clientSslCertificate . isNull ( ) ) {
// SSL configuration
QSslConfiguration sslConfiguration = req . sslConfiguration ( ) ;
sslConfiguration . setLocalCertificate ( _cred - > _clientSslCertificate ) ;
sslConfiguration . setPrivateKey ( _cred - > _clientSslKey ) ;
2019-08-27 10:55:41 +03:00
// Merge client side CA with system CA
auto ca = sslConfiguration . systemCaCertificates ( ) ;
ca . append ( _cred - > _clientSslCaCertificates ) ;
sslConfiguration . setCaCertificates ( ca ) ;
2019-08-27 04:32:21 +03:00
req . setSslConfiguration ( sslConfiguration ) ;
}
2018-09-04 21:59:25 +03:00
return AccessManager : : createRequest ( op , req , outgoingData ) ;
}
private :
// The credentials object dies along with the account, while the QNAM might
// outlive both.
QPointer < const WebFlowCredentials > _cred ;
} ;
2019-08-27 04:32:21 +03:00
static void addSettingsToJob ( Account * account , QKeychain : : Job * job )
{
2019-08-27 10:55:41 +03:00
Q_UNUSED ( account )
2019-08-27 04:32:21 +03:00
auto settings = ConfigFile : : settingsWithGroup ( Theme : : instance ( ) - > appName ( ) ) ;
settings - > setParent ( job ) ; // make the job parent to make setting deleted properly
job - > setSettings ( settings . release ( ) ) ;
}
2017-11-25 22:19:25 +03:00
WebFlowCredentials : : WebFlowCredentials ( )
2018-11-02 11:49:43 +03:00
: _ready ( false )
, _credentialsValid ( false )
, _keychainMigration ( false )
2019-08-27 04:32:21 +03:00
, _retryOnKeyChainError ( false )
2017-11-25 22:19:25 +03:00
{
}
2019-08-27 10:55:41 +03:00
WebFlowCredentials : : WebFlowCredentials ( const QString & user , const QString & password , const QSslCertificate & certificate , const QSslKey & key , const QList < QSslCertificate > & caCertificates )
2017-11-25 22:19:25 +03:00
: _user ( user )
, _password ( password )
, _clientSslKey ( key )
, _clientSslCertificate ( certificate )
2019-08-27 10:55:41 +03:00
, _clientSslCaCertificates ( caCertificates )
2017-11-25 22:19:25 +03:00
, _ready ( true )
2017-11-28 22:06:27 +03:00
, _credentialsValid ( true )
2018-11-02 11:49:43 +03:00
, _keychainMigration ( false )
2019-08-27 04:32:21 +03:00
, _retryOnKeyChainError ( false )
2017-11-25 22:19:25 +03:00
{
}
QString WebFlowCredentials : : authType ( ) const {
return QString : : fromLatin1 ( " webflow " ) ;
}
QString WebFlowCredentials : : user ( ) const {
return _user ;
}
2017-11-28 22:06:27 +03:00
QString WebFlowCredentials : : password ( ) const {
return _password ;
}
2017-11-25 22:19:25 +03:00
QNetworkAccessManager * WebFlowCredentials : : createQNAM ( ) const {
2017-11-28 22:06:27 +03:00
qCInfo ( lcWebFlowCredentials ( ) ) < < " Get QNAM " ;
2018-09-04 21:59:25 +03:00
AccessManager * qnam = new WebFlowCredentialsAccessManager ( this ) ;
2017-11-28 22:06:27 +03:00
connect ( qnam , & AccessManager : : authenticationRequired , this , & WebFlowCredentials : : slotAuthentication ) ;
connect ( qnam , & AccessManager : : finished , this , & WebFlowCredentials : : slotFinished ) ;
2017-11-25 22:19:25 +03:00
return qnam ;
}
bool WebFlowCredentials : : ready ( ) const {
2017-11-28 22:06:27 +03:00
return _ready ;
2017-11-25 22:19:25 +03:00
}
void WebFlowCredentials : : fetchFromKeychain ( ) {
2017-11-28 22:06:27 +03:00
_wasFetched = true ;
2019-08-23 11:25:34 +03:00
// Make sure we get the user from the config file
2017-11-28 22:06:27 +03:00
fetchUser ( ) ;
2017-11-25 22:19:25 +03:00
2017-11-28 22:06:27 +03:00
if ( ready ( ) ) {
emit fetched ( ) ;
} else {
2019-04-28 12:03:38 +03:00
qCInfo ( lcWebFlowCredentials ( ) ) < < " Fetch from keychain! " ;
2017-11-28 22:06:27 +03:00
fetchFromKeychainHelper ( ) ;
}
2017-11-25 22:19:25 +03:00
}
2017-11-28 22:06:27 +03:00
void WebFlowCredentials : : askFromUser ( ) {
2019-08-24 17:21:44 +03:00
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
bool useFlow2 = ( _account - > serverVersionInt ( ) > = Account : : makeServerVersion ( 16 , 0 , 0 ) ) ;
2017-11-29 00:25:35 +03:00
2019-08-24 17:21:44 +03:00
_askDialog = new WebFlowCredentialsDialog ( _account , useFlow2 ) ;
if ( ! useFlow2 ) {
QUrl url = _account - > url ( ) ;
QString path = url . path ( ) + " /index.php/login/flow " ;
url . setPath ( path ) ;
_askDialog - > setUrl ( url ) ;
}
2017-11-29 00:25:35 +03:00
QString msg = tr ( " You have been logged out of %1 as user %2. Please login again " )
2019-08-30 06:35:36 +03:00
. arg ( _account - > displayName ( ) , _user ) ;
2017-11-29 00:25:35 +03:00
_askDialog - > setInfo ( msg ) ;
_askDialog - > show ( ) ;
connect ( _askDialog , & WebFlowCredentialsDialog : : urlCatched , this , & WebFlowCredentials : : slotAskFromUserCredentialsProvided ) ;
2019-02-15 22:23:24 +03:00
qCDebug ( lcWebFlowCredentials ( ) ) < < " User needs to reauth! " ;
2017-11-25 22:19:25 +03:00
}
2017-11-29 00:25:35 +03:00
void WebFlowCredentials : : slotAskFromUserCredentialsProvided ( const QString & user , const QString & pass , const QString & host ) {
2019-08-27 10:55:41 +03:00
Q_UNUSED ( host )
2018-07-02 14:02:15 +03:00
2017-11-29 00:25:35 +03:00
if ( _user ! = user ) {
qCInfo ( lcWebFlowCredentials ( ) ) < < " Authed with the wrong user! " ;
QString msg = tr ( " Please login with the user: %1 " )
2019-08-30 06:35:36 +03:00
. arg ( _user ) ;
2017-11-29 00:25:35 +03:00
_askDialog - > setError ( msg ) ;
2019-08-24 17:21:44 +03:00
if ( ! _askDialog - > isUsingFlow2 ( ) ) {
QUrl url = _account - > url ( ) ;
QString path = url . path ( ) + " /index.php/login/flow " ;
url . setPath ( path ) ;
_askDialog - > setUrl ( url ) ;
}
2017-11-29 00:25:35 +03:00
return ;
}
2018-04-23 22:28:03 +03:00
qCInfo ( lcWebFlowCredentials ( ) ) < < " Obtained a new password " ;
2017-11-29 00:25:35 +03:00
_password = pass ;
_ready = true ;
_credentialsValid = true ;
persist ( ) ;
emit asked ( ) ;
_askDialog - > close ( ) ;
delete _askDialog ;
2018-11-11 12:56:22 +03:00
_askDialog = nullptr ;
2017-11-29 00:25:35 +03:00
}
2017-11-25 22:19:25 +03:00
bool WebFlowCredentials : : stillValid ( QNetworkReply * reply ) {
2019-02-15 22:23:24 +03:00
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) {
qCWarning ( lcWebFlowCredentials ( ) ) < < reply - > error ( ) ;
qCWarning ( lcWebFlowCredentials ( ) ) < < reply - > errorString ( ) ;
}
2017-11-28 22:06:27 +03:00
return ( reply - > error ( ) ! = QNetworkReply : : AuthenticationRequiredError ) ;
2017-11-25 22:19:25 +03:00
}
void WebFlowCredentials : : persist ( ) {
2017-11-28 22:06:27 +03:00
if ( _user . isEmpty ( ) ) {
// We don't even have a user nothing to see here move along
return ;
}
2019-08-27 04:32:21 +03:00
_account - > setCredentialSetting ( userC , _user ) ;
2017-11-28 22:06:27 +03:00
_account - > wantsAccountSaved ( _account ) ;
2019-08-27 04:32:21 +03:00
// write cert if there is one
if ( ! _clientSslCertificate . isNull ( ) ) {
WritePasswordJob * job = new WritePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotWriteClientCertPEMJobDone ) ;
job - > setKey ( keychainKey ( _account - > url ( ) . toString ( ) , _user + clientCertificatePEMC , _account - > id ( ) ) ) ;
job - > setBinaryData ( _clientSslCertificate . toPem ( ) ) ;
job - > start ( ) ;
} else {
// no cert, just write credentials
slotWriteClientCertPEMJobDone ( ) ;
}
}
void WebFlowCredentials : : slotWriteClientCertPEMJobDone ( )
{
// write ssl key if there is one
if ( ! _clientSslKey . isNull ( ) ) {
WritePasswordJob * job = new WritePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotWriteClientKeyPEMJobDone ) ;
job - > setKey ( keychainKey ( _account - > url ( ) . toString ( ) , _user + clientKeyPEMC , _account - > id ( ) ) ) ;
job - > setBinaryData ( _clientSslKey . toPem ( ) ) ;
job - > start ( ) ;
} else {
// no key, just write credentials
slotWriteClientKeyPEMJobDone ( ) ;
}
}
2019-08-30 05:56:01 +03:00
void WebFlowCredentials : : writeSingleClientCaCertPEM ( )
2019-08-27 10:55:41 +03:00
{
2019-08-30 05:56:01 +03:00
// write a ca cert if there is any in the queue
if ( ! _clientSslCaCertificatesWriteQueue . isEmpty ( ) ) {
// grab and remove the first cert from the queue
auto cert = _clientSslCaCertificatesWriteQueue . dequeue ( ) ;
auto index = ( _clientSslCaCertificates . count ( ) - _clientSslCaCertificatesWriteQueue . count ( ) ) - 1 ;
// keep the limit
if ( index > ( _clientSslCaCertificatesMaxCount - 1 ) ) {
qCWarning ( lcWebFlowCredentials ) < < " Maximum client CA cert count exceeded while writing slot " < < QString : : number ( index ) < < " ), cutting off after " < < QString : : number ( _clientSslCaCertificatesMaxCount ) < < " certs " ;
_clientSslCaCertificatesWriteQueue . clear ( ) ;
slotWriteClientCaCertsPEMJobDone ( nullptr ) ;
return ;
}
2019-08-27 10:55:41 +03:00
WritePasswordJob * job = new WritePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotWriteClientCaCertsPEMJobDone ) ;
2019-08-30 05:56:01 +03:00
job - > setKey ( keychainKey ( _account - > url ( ) . toString ( ) , _user + clientCaCertificatePEMC + QString : : number ( index ) , _account - > id ( ) ) ) ;
job - > setBinaryData ( cert . toPem ( ) ) ;
job - > start ( ) ;
} else {
slotWriteClientCaCertsPEMJobDone ( nullptr ) ;
}
}
2019-08-27 10:55:41 +03:00
2019-08-30 05:56:01 +03:00
void WebFlowCredentials : : slotWriteClientKeyPEMJobDone ( )
{
_clientSslCaCertificatesWriteQueue . clear ( ) ;
2019-08-27 10:55:41 +03:00
2019-08-30 05:56:01 +03:00
// write ca certs if there are any
if ( ! _clientSslCaCertificates . isEmpty ( ) ) {
// queue the certs to avoid trouble on Windows (Workaround for CredWriteW used by QtKeychain)
_clientSslCaCertificatesWriteQueue . append ( _clientSslCaCertificates ) ;
// first ca cert
writeSingleClientCaCertPEM ( ) ;
2019-08-27 10:55:41 +03:00
} else {
2019-08-30 05:56:01 +03:00
slotWriteClientCaCertsPEMJobDone ( nullptr ) ;
2019-08-27 10:55:41 +03:00
}
}
2019-08-30 05:56:01 +03:00
void WebFlowCredentials : : slotWriteClientCaCertsPEMJobDone ( QKeychain : : Job * incomingJob )
2019-08-27 04:32:21 +03:00
{
2019-08-30 05:56:01 +03:00
// errors / next ca cert?
if ( incomingJob & & ! _clientSslCaCertificates . isEmpty ( ) ) {
WritePasswordJob * writeJob = static_cast < WritePasswordJob * > ( incomingJob ) ;
if ( writeJob - > error ( ) ! = NoError ) {
qCWarning ( lcWebFlowCredentials ) < < " Error while writing client CA cert " < < writeJob - > errorString ( ) ;
}
if ( ! _clientSslCaCertificatesWriteQueue . isEmpty ( ) ) {
// next ca cert
writeSingleClientCaCertPEM ( ) ;
return ;
}
}
// done storing ca certs, time for the password
2017-11-25 22:19:25 +03:00
WritePasswordJob * job = new WritePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
2019-08-27 04:32:21 +03:00
addSettingsToJob ( _account , job ) ;
2017-11-25 22:19:25 +03:00
job - > setInsecureFallback ( false ) ;
2019-08-27 04:32:21 +03:00
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotWriteJobDone ) ;
2017-11-25 22:19:25 +03:00
job - > setKey ( keychainKey ( _account - > url ( ) . toString ( ) , _user , _account - > id ( ) ) ) ;
job - > setTextData ( _password ) ;
job - > start ( ) ;
}
2019-08-27 04:32:21 +03:00
void WebFlowCredentials : : slotWriteJobDone ( QKeychain : : Job * job )
{
delete job - > settings ( ) ;
switch ( job - > error ( ) ) {
case NoError :
break ;
default :
qCWarning ( lcWebFlowCredentials ) < < " Error while writing password " < < job - > errorString ( ) ;
}
WritePasswordJob * wjob = qobject_cast < WritePasswordJob * > ( job ) ;
wjob - > deleteLater ( ) ;
}
2017-11-25 22:19:25 +03:00
void WebFlowCredentials : : invalidateToken ( ) {
2017-11-28 22:06:27 +03:00
// clear the session cookie.
_account - > clearCookieJar ( ) ;
// let QNAM forget about the password
// This needs to be done later in the event loop because we might be called (directly or
// indirectly) from QNetworkAccessManagerPrivate::authenticationRequired, which itself
// is a called from a BlockingQueuedConnection from the Qt HTTP thread. And clearing the
// cache needs to synchronize again with the HTTP thread.
QTimer : : singleShot ( 0 , _account , & Account : : clearQNAMCache ) ;
2017-11-25 22:19:25 +03:00
}
2019-08-30 05:56:01 +03:00
void WebFlowCredentials : : forgetSensitiveData ( ) {
2017-11-28 22:06:27 +03:00
_password = QString ( ) ;
_ready = false ;
fetchUser ( ) ;
2019-07-24 14:56:21 +03:00
_account - > deleteAppPassword ( ) ;
2017-11-28 22:06:27 +03:00
const QString kck = keychainKey ( _account - > url ( ) . toString ( ) , _user , _account - > id ( ) ) ;
if ( kck . isEmpty ( ) ) {
2019-02-15 22:23:24 +03:00
qCDebug ( lcWebFlowCredentials ( ) ) < < " InvalidateToken: User is empty, bailing out! " ;
2017-11-28 22:06:27 +03:00
return ;
}
DeletePasswordJob * job = new DeletePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
job - > setInsecureFallback ( false ) ;
job - > setKey ( kck ) ;
job - > start ( ) ;
invalidateToken ( ) ;
2019-08-30 05:56:01 +03:00
/* IMPORTANT
2019-09-09 19:46:25 +03:00
* TODO : For " Log out " & " Remove account " : Remove client CA certs and KEY !
2019-08-30 05:56:01 +03:00
*
* Disabled as long as selecting another cert is not supported by the UI .
*
* Being able to specify a new certificate is important anyway : expiry etc .
*/
//deleteKeychainEntries();
2017-11-28 22:06:27 +03:00
}
void WebFlowCredentials : : setAccount ( Account * account ) {
AbstractCredentials : : setAccount ( account ) ;
if ( _user . isEmpty ( ) ) {
fetchUser ( ) ;
}
}
QString WebFlowCredentials : : fetchUser ( ) {
2019-08-27 04:32:21 +03:00
_user = _account - > credentialSetting ( userC ) . toString ( ) ;
2017-11-28 22:06:27 +03:00
return _user ;
}
void WebFlowCredentials : : slotAuthentication ( QNetworkReply * reply , QAuthenticator * authenticator ) {
2019-08-27 10:55:41 +03:00
Q_UNUSED ( reply )
2017-11-28 22:06:27 +03:00
if ( ! _ready ) {
return ;
}
if ( _credentialsValid = = false ) {
return ;
}
2019-02-15 22:23:24 +03:00
qCDebug ( lcWebFlowCredentials ( ) ) < < " Requires authentication " ;
2017-11-28 22:06:27 +03:00
authenticator - > setUser ( _user ) ;
authenticator - > setPassword ( _password ) ;
_credentialsValid = false ;
}
void WebFlowCredentials : : slotFinished ( QNetworkReply * reply ) {
qCInfo ( lcWebFlowCredentials ( ) ) < < " request finished " ;
2018-05-01 23:07:16 +03:00
if ( reply - > error ( ) = = QNetworkReply : : NoError ) {
_credentialsValid = true ;
2019-07-24 14:56:21 +03:00
/// Used later for remote wipe
2019-11-29 06:28:50 +03:00
_account - > writeAppPasswordOnce ( _password ) ;
2018-05-01 23:07:16 +03:00
}
2017-11-28 22:06:27 +03:00
}
void WebFlowCredentials : : fetchFromKeychainHelper ( ) {
2019-08-27 04:32:21 +03:00
// Read client cert from keychain
const QString kck = keychainKey (
_account - > url ( ) . toString ( ) ,
_user + clientCertificatePEMC ,
_keychainMigration ? QString ( ) : _account - > id ( ) ) ;
ReadPasswordJob * job = new ReadPasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
job - > setKey ( kck ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotReadClientCertPEMJobDone ) ;
job - > start ( ) ;
}
void WebFlowCredentials : : slotReadClientCertPEMJobDone ( QKeychain : : Job * incomingJob )
{
# if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
Q_ASSERT ( ! incomingJob - > insecureFallback ( ) ) ; // If insecureFallback is set, the next test would be pointless
if ( _retryOnKeyChainError & & ( incomingJob - > error ( ) = = QKeychain : : NoBackendAvailable
2019-08-30 06:35:36 +03:00
| | incomingJob - > error ( ) = = QKeychain : : OtherError ) ) {
2019-08-27 04:32:21 +03:00
// Could be that the backend was not yet available. Wait some extra seconds.
// (Issues #4274 and #6522)
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
qCInfo ( lcWebFlowCredentials ) < < " Backend unavailable (yet?) Retrying in a few seconds. " < < incomingJob - > errorString ( ) ;
QTimer : : singleShot ( 10000 , this , & WebFlowCredentials : : fetchFromKeychainHelper ) ;
_retryOnKeyChainError = false ;
return ;
}
_retryOnKeyChainError = false ;
# endif
// Store PEM in memory
ReadPasswordJob * readJob = static_cast < ReadPasswordJob * > ( incomingJob ) ;
if ( readJob - > error ( ) = = NoError & & readJob - > binaryData ( ) . length ( ) > 0 ) {
QList < QSslCertificate > sslCertificateList = QSslCertificate : : fromData ( readJob - > binaryData ( ) , QSsl : : Pem ) ;
if ( sslCertificateList . length ( ) > = 1 ) {
_clientSslCertificate = sslCertificateList . at ( 0 ) ;
}
}
// Load key too
const QString kck = keychainKey (
_account - > url ( ) . toString ( ) ,
_user + clientKeyPEMC ,
_keychainMigration ? QString ( ) : _account - > id ( ) ) ;
ReadPasswordJob * job = new ReadPasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
job - > setKey ( kck ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotReadClientKeyPEMJobDone ) ;
job - > start ( ) ;
}
void WebFlowCredentials : : slotReadClientKeyPEMJobDone ( QKeychain : : Job * incomingJob )
{
// Store key in memory
ReadPasswordJob * readJob = static_cast < ReadPasswordJob * > ( incomingJob ) ;
if ( readJob - > error ( ) = = NoError & & readJob - > binaryData ( ) . length ( ) > 0 ) {
QByteArray clientKeyPEM = readJob - > binaryData ( ) ;
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
// load whatever we have. So we try until it works.
_clientSslKey = QSslKey ( clientKeyPEM , QSsl : : Rsa ) ;
if ( _clientSslKey . isNull ( ) ) {
_clientSslKey = QSslKey ( clientKeyPEM , QSsl : : Dsa ) ;
}
if ( _clientSslKey . isNull ( ) ) {
_clientSslKey = QSslKey ( clientKeyPEM , QSsl : : Ec ) ;
}
if ( _clientSslKey . isNull ( ) ) {
qCWarning ( lcWebFlowCredentials ) < < " Could not load SSL key into Qt! " ;
}
}
2019-08-30 05:56:01 +03:00
// Start fetching client CA certs
_clientSslCaCertificates . clear ( ) ;
2019-08-27 10:55:41 +03:00
2019-08-30 05:56:01 +03:00
readSingleClientCaCertPEM ( ) ;
}
void WebFlowCredentials : : readSingleClientCaCertPEM ( )
{
// try to fetch a client ca cert
if ( _clientSslCaCertificates . count ( ) < _clientSslCaCertificatesMaxCount ) {
const QString kck = keychainKey (
_account - > url ( ) . toString ( ) ,
_user + clientCaCertificatePEMC + QString : : number ( _clientSslCaCertificates . count ( ) ) ,
_keychainMigration ? QString ( ) : _account - > id ( ) ) ;
ReadPasswordJob * job = new ReadPasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( false ) ;
job - > setKey ( kck ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotReadClientCaCertsPEMJobDone ) ;
job - > start ( ) ;
} else {
qCWarning ( lcWebFlowCredentials ) < < " Maximum client CA cert count exceeded while reading, ignoring after " < < _clientSslCaCertificatesMaxCount ;
slotReadClientCaCertsPEMJobDone ( nullptr ) ;
}
2019-08-27 10:55:41 +03:00
}
void WebFlowCredentials : : slotReadClientCaCertsPEMJobDone ( QKeychain : : Job * incomingJob ) {
// Store key in memory
ReadPasswordJob * readJob = static_cast < ReadPasswordJob * > ( incomingJob ) ;
2019-08-30 05:56:01 +03:00
if ( readJob ) {
if ( readJob - > error ( ) = = NoError & & readJob - > binaryData ( ) . length ( ) > 0 ) {
QList < QSslCertificate > sslCertificateList = QSslCertificate : : fromData ( readJob - > binaryData ( ) , QSsl : : Pem ) ;
if ( sslCertificateList . length ( ) > = 1 ) {
_clientSslCaCertificates . append ( sslCertificateList . at ( 0 ) ) ;
}
// try next cert
readSingleClientCaCertPEM ( ) ;
return ;
} else {
if ( readJob - > error ( ) ! = QKeychain : : Error : : EntryNotFound | |
2019-09-09 19:52:36 +03:00
( ( readJob - > error ( ) = = QKeychain : : Error : : EntryNotFound ) & & _clientSslCaCertificates . count ( ) = = 0 ) ) {
2019-08-30 05:56:01 +03:00
qCWarning ( lcWebFlowCredentials ) < < " Unable to read client CA cert slot " < < QString : : number ( _clientSslCaCertificates . count ( ) ) < < readJob - > errorString ( ) ;
}
2019-08-27 10:55:41 +03:00
}
}
2019-08-27 04:32:21 +03:00
// Now fetch the actual server password
2017-11-28 22:06:27 +03:00
const QString kck = keychainKey (
_account - > url ( ) . toString ( ) ,
_user ,
2018-11-02 11:49:43 +03:00
_keychainMigration ? QString ( ) : _account - > id ( ) ) ;
2017-11-28 22:06:27 +03:00
ReadPasswordJob * job = new ReadPasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
2019-08-27 04:32:21 +03:00
addSettingsToJob ( _account , job ) ;
2017-11-28 22:06:27 +03:00
job - > setInsecureFallback ( false ) ;
job - > setKey ( kck ) ;
connect ( job , & Job : : finished , this , & WebFlowCredentials : : slotReadPasswordJobDone ) ;
job - > start ( ) ;
}
void WebFlowCredentials : : slotReadPasswordJobDone ( Job * incomingJob ) {
QKeychain : : ReadPasswordJob * job = static_cast < ReadPasswordJob * > ( incomingJob ) ;
QKeychain : : Error error = job - > error ( ) ;
2018-11-02 11:49:43 +03:00
// If we could not find the entry try the old entries
if ( ! _keychainMigration & & error = = QKeychain : : EntryNotFound ) {
_keychainMigration = true ;
fetchFromKeychainHelper ( ) ;
return ;
}
2019-08-27 04:32:21 +03:00
if ( _user . isEmpty ( ) ) {
qCWarning ( lcWebFlowCredentials ) < < " Strange: User is empty! " ;
}
2017-11-28 22:06:27 +03:00
if ( error = = QKeychain : : NoError ) {
_password = job - > textData ( ) ;
_ready = true ;
_credentialsValid = true ;
} else {
_ready = false ;
}
emit fetched ( ) ;
2018-11-02 11:49:43 +03:00
// If keychain data was read from legacy location, wipe these entries and store new ones
if ( _keychainMigration & & _ready ) {
_keychainMigration = false ;
persist ( ) ;
2019-08-30 06:35:36 +03:00
deleteKeychainEntries ( true ) ; // true: delete old entries
2019-02-15 22:23:24 +03:00
qCInfo ( lcWebFlowCredentials ) < < " Migrated old keychain entries " ;
2018-11-02 11:49:43 +03:00
}
}
2019-08-30 06:35:36 +03:00
void WebFlowCredentials : : deleteKeychainEntries ( bool oldKeychainEntries ) {
2019-08-30 05:56:01 +03:00
auto startDeleteJob = [ this , oldKeychainEntries ] ( QString user ) {
2019-08-27 04:32:21 +03:00
DeletePasswordJob * job = new DeletePasswordJob ( Theme : : instance ( ) - > appName ( ) ) ;
addSettingsToJob ( _account , job ) ;
job - > setInsecureFallback ( true ) ;
2019-08-30 05:56:01 +03:00
job - > setKey ( keychainKey ( _account - > url ( ) . toString ( ) ,
2019-08-30 06:35:36 +03:00
user ,
oldKeychainEntries ? QString ( ) : _account - > id ( ) ) ) ;
2019-08-27 04:32:21 +03:00
job - > start ( ) ;
} ;
startDeleteJob ( _user ) ;
startDeleteJob ( _user + clientKeyPEMC ) ;
startDeleteJob ( _user + clientCertificatePEMC ) ;
2019-08-30 05:56:01 +03:00
for ( auto i = 0 ; i < _clientSslCaCertificates . count ( ) ; i + + ) {
startDeleteJob ( _user + clientCaCertificatePEMC + QString : : number ( i ) ) ;
}
2017-11-25 22:19:25 +03:00
}
}