Merge pull request #13188 from SimonWalton-HiFi/placename-crash

Initialize Qt's use of openssl lib before calling RSA code
This commit is contained in:
John Conklin II 2018-05-31 14:34:24 -07:00 committed by GitHub
commit 611159f65f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 84 deletions

View file

@ -22,6 +22,7 @@
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QUrlQuery> #include <QtCore/QUrlQuery>
#include <QtCore/QThreadPool>
#include <QtNetwork/QHttpMultiPart> #include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <qthread.h> #include <qthread.h>
@ -743,6 +744,9 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI
return; return;
} }
// Ensure openssl/Qt config is set up.
QSslConfiguration::defaultConfiguration();
// make sure we don't already have an outbound keypair generation request // make sure we don't already have an outbound keypair generation request
if (!_isWaitingForKeypairResponse) { if (!_isWaitingForKeypairResponse) {
_isWaitingForKeypairResponse = true; _isWaitingForKeypairResponse = true;
@ -751,94 +755,75 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI
qCDebug(networking) << "Clearing current private key in DataServerAccountInfo"; qCDebug(networking) << "Clearing current private key in DataServerAccountInfo";
_accountInfo.setPrivateKey(QByteArray()); _accountInfo.setPrivateKey(QByteArray());
// setup a new QThread to generate the keypair on, in case it takes a while // Create a runnable keypair generated to create an RSA pair and exit.
QThread* generateThread = new QThread(this);
generateThread->setObjectName("Account Manager Generator Thread");
// setup a keypair generator
RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator; RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator;
if (!isUserKeypair) { if (!isUserKeypair) {
keypairGenerator->setDomainID(domainID);
_accountInfo.setDomainID(domainID); _accountInfo.setDomainID(domainID);
} }
// start keypair generation when the thread starts
connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
// handle success or failure of keypair generation // handle success or failure of keypair generation
connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this,
connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, &AccountManager::processGeneratedKeypair);
this, &AccountManager::handleKeypairGenerationError); connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, this,
&AccountManager::handleKeypairGenerationError);
connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
keypairGenerator->moveToThread(generateThread);
qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair."; qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair.";
generateThread->start(); // Start on Qt's global thread pool.
QThreadPool::globalInstance()->start(keypairGenerator);
} }
} }
void AccountManager::processGeneratedKeypair() { void AccountManager::processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey) {
qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now."; qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now.";
RSAKeypairGenerator* keypairGenerator = qobject_cast<RSAKeypairGenerator*>(sender()); // hold the private key to later set our metaverse API account info if upload succeeds
_pendingPrivateKey = privateKey;
if (keypairGenerator) { // upload the public key so data-web has an up-to-date key
// hold the private key to later set our metaverse API account info if upload succeeds const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
_pendingPrivateKey = keypairGenerator->getPrivateKey(); const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
// upload the public key so data-web has an up-to-date key QString uploadPath;
const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; const auto& domainID = _accountInfo.getDomainID();
const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key"; if (domainID.isNull()) {
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
QString uploadPath;
const auto& domainID = keypairGenerator->getDomainID();
if (domainID.isNull()) {
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
} else {
uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
}
// setup a multipart upload to send up the public key
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart publicKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
publicKeyPart.setBody(keypairGenerator->getPublicKey());
requestMultiPart->append(publicKeyPart);
if (!domainID.isNull()) {
const auto& key = getTemporaryDomainKey(domainID);
QHttpPart apiKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"api_key\""));
apiKeyPart.setBody(key.toUtf8());
requestMultiPart->append(apiKeyPart);
}
// setup callback parameters so we know once the keypair upload has succeeded or failed
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
callbackParameters, QByteArray(), requestMultiPart);
keypairGenerator->deleteLater();
} else { } else {
qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator" uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
<< "but the casted sender is NULL. Will not process generated keypair.";
} }
// setup a multipart upload to send up the public key
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart publicKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
publicKeyPart.setBody(publicKey);
requestMultiPart->append(publicKeyPart);
// Currently broken? We don't have the temporary domain key.
if (!domainID.isNull()) {
const auto& key = getTemporaryDomainKey(domainID);
QHttpPart apiKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"api_key\""));
apiKeyPart.setBody(key.toUtf8());
requestMultiPart->append(apiKeyPart);
}
// setup callback parameters so we know once the keypair upload has succeeded or failed
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
callbackParameters, QByteArray(), requestMultiPart);
} }
void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) { void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
@ -877,6 +862,4 @@ void AccountManager::handleKeypairGenerationError() {
// reset our waiting state for keypair response // reset our waiting state for keypair response
_isWaitingForKeypairResponse = false; _isWaitingForKeypairResponse = false;
sender()->deleteLater();
} }

View file

@ -128,7 +128,7 @@ signals:
private slots: private slots:
void processReply(); void processReply();
void handleKeypairGenerationError(); void handleKeypairGenerationError();
void processGeneratedKeypair(); void processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey);
void publicKeyUploadSucceeded(QNetworkReply& reply); void publicKeyUploadSucceeded(QNetworkReply& reply);
void publicKeyUploadFailed(QNetworkReply& reply); void publicKeyUploadFailed(QNetworkReply& reply);
void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid()); void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());

View file

@ -25,7 +25,10 @@
RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) :
QObject(parent) QObject(parent)
{ {
}
void RSAKeypairGenerator::run() {
generateKeypair();
} }
void RSAKeypairGenerator::generateKeypair() { void RSAKeypairGenerator::generateKeypair() {
@ -92,5 +95,5 @@ void RSAKeypairGenerator::generateKeypair() {
OPENSSL_free(publicKeyDER); OPENSSL_free(publicKeyDER);
OPENSSL_free(privateKeyDER); OPENSSL_free(privateKeyDER);
emit generatedKeypair(); emit generatedKeypair(_publicKey, _privateKey);
} }

View file

@ -13,25 +13,20 @@
#define hifi_RSAKeypairGenerator_h #define hifi_RSAKeypairGenerator_h
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QRunnable>
#include <QtCore/QUuid> #include <QtCore/QUuid>
class RSAKeypairGenerator : public QObject { class RSAKeypairGenerator : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
RSAKeypairGenerator(QObject* parent = 0); RSAKeypairGenerator(QObject* parent = nullptr);
void setDomainID(const QUuid& domainID) { _domainID = domainID; } virtual void run() override;
const QUuid& getDomainID() const { return _domainID; }
const QByteArray& getPublicKey() const { return _publicKey; }
const QByteArray& getPrivateKey() const { return _privateKey; }
public slots:
void generateKeypair(); void generateKeypair();
signals: signals:
void errorGeneratingKeypair(); void errorGeneratingKeypair();
void generatedKeypair(); void generatedKeypair(QByteArray publicKey, QByteArray privateKey);
private: private:
QUuid _domainID; QUuid _domainID;