Pin server cert to host during pairing

This commit is contained in:
Cameron Gutman
2018-12-21 18:08:07 -08:00
parent b4d8e0e551
commit c6383f042c
9 changed files with 50 additions and 24 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ void BoxArtManager::handleBoxArtLoadComplete(NvComputer* computer, NvApp app, QU
QUrl BoxArtManager::loadBoxArtFromNetwork(NvComputer* computer, int appId) QUrl BoxArtManager::loadBoxArtFromNetwork(NvComputer* computer, int appId)
{ {
NvHTTP http(computer->activeAddress); NvHTTP http(computer->activeAddress, computer->serverCert);
QString cachePath = getFilePathForBoxArt(computer, appId); QString cachePath = getFilePathForBoxArt(computer, appId);
QImage image; QImage image;
+7 -7
View File
@@ -27,7 +27,7 @@ public:
private: private:
bool tryPollComputer(QString address, bool& changed) bool tryPollComputer(QString address, bool& changed)
{ {
NvHTTP http(address); NvHTTP http(address, m_Computer->serverCert);
QString serverInfo; QString serverInfo;
try { try {
@@ -36,7 +36,7 @@ private:
return false; return false;
} }
NvComputer newState(address, serverInfo); NvComputer newState(address, serverInfo, QSslCertificate());
// Ensure the machine that responded is the one we intended to contact // Ensure the machine that responded is the one we intended to contact
if (m_Computer->uuid != newState.uuid) { if (m_Computer->uuid != newState.uuid) {
@@ -52,7 +52,7 @@ private:
{ {
Q_ASSERT(m_Computer->activeAddress != nullptr); Q_ASSERT(m_Computer->activeAddress != nullptr);
NvHTTP http(m_Computer->activeAddress); NvHTTP http(m_Computer->activeAddress, m_Computer->serverCert);
QVector<NvApp> appList; QVector<NvApp> appList;
@@ -385,7 +385,7 @@ private:
NvPairingManager pairingManager(m_Computer->activeAddress); NvPairingManager pairingManager(m_Computer->activeAddress);
try { try {
NvPairingManager::PairState result = pairingManager.pair(m_Computer->appVersion, m_Pin); NvPairingManager::PairState result = pairingManager.pair(m_Computer->appVersion, m_Pin, m_Computer->serverCert);
switch (result) switch (result)
{ {
case NvPairingManager::PairState::PIN_WRONG: case NvPairingManager::PairState::PIN_WRONG:
@@ -438,7 +438,7 @@ signals:
private: private:
void run() void run()
{ {
NvHTTP http(m_Computer->activeAddress); NvHTTP http(m_Computer->activeAddress, m_Computer->serverCert);
try { try {
if (m_Computer->currentGameId != 0) { if (m_Computer->currentGameId != 0) {
@@ -540,7 +540,7 @@ signals:
private: private:
void run() void run()
{ {
NvHTTP http(m_Address); NvHTTP http(m_Address, QSslCertificate());
qInfo() << "Processing new PC at" << m_Address << "from" << (m_Mdns ? "mDNS" : "user"); qInfo() << "Processing new PC at" << m_Address << "from" << (m_Mdns ? "mDNS" : "user");
@@ -571,7 +571,7 @@ private:
return; return;
} }
NvComputer* newComputer = new NvComputer(m_Address, serverInfo); NvComputer* newComputer = new NvComputer(m_Address, serverInfo, QSslCertificate());
// Update addresses depending on the context // Update addresses depending on the context
if (m_Mdns) { if (m_Mdns) {
+14 -1
View File
@@ -10,6 +10,7 @@
#define SER_REMOTEADDR "remoteaddress" #define SER_REMOTEADDR "remoteaddress"
#define SER_MANUALADDR "manualaddress" #define SER_MANUALADDR "manualaddress"
#define SER_APPLIST "apps" #define SER_APPLIST "apps"
#define SER_SRVCERT "srvcert"
#define SER_APPNAME "name" #define SER_APPNAME "name"
#define SER_APPID "id" #define SER_APPID "id"
@@ -23,6 +24,7 @@ NvComputer::NvComputer(QSettings& settings)
this->localAddress = settings.value(SER_LOCALADDR).toString(); this->localAddress = settings.value(SER_LOCALADDR).toString();
this->remoteAddress = settings.value(SER_REMOTEADDR).toString(); this->remoteAddress = settings.value(SER_REMOTEADDR).toString();
this->manualAddress = settings.value(SER_MANUALADDR).toString(); this->manualAddress = settings.value(SER_MANUALADDR).toString();
this->serverCert = QSslCertificate(settings.value(SER_SRVCERT).toByteArray());
int appCount = settings.beginReadArray(SER_APPLIST); int appCount = settings.beginReadArray(SER_APPLIST);
for (int i = 0; i < appCount; i++) { for (int i = 0; i < appCount; i++) {
@@ -61,6 +63,7 @@ void NvComputer::serialize(QSettings& settings)
settings.setValue(SER_LOCALADDR, localAddress); settings.setValue(SER_LOCALADDR, localAddress);
settings.setValue(SER_REMOTEADDR, remoteAddress); settings.setValue(SER_REMOTEADDR, remoteAddress);
settings.setValue(SER_MANUALADDR, manualAddress); settings.setValue(SER_MANUALADDR, manualAddress);
settings.setValue(SER_SRVCERT, serverCert.toPem());
// Avoid deleting an existing applist if we couldn't get one // Avoid deleting an existing applist if we couldn't get one
if (!appList.isEmpty()) { if (!appList.isEmpty()) {
@@ -84,8 +87,10 @@ void NvComputer::sortAppList()
}); });
} }
NvComputer::NvComputer(QString address, QString serverInfo) NvComputer::NvComputer(QString address, QString serverInfo, QSslCertificate serverCert)
{ {
this->serverCert = serverCert;
this->name = NvHTTP::getXmlString(serverInfo, "hostname"); this->name = NvHTTP::getXmlString(serverInfo, "hostname");
if (this->name.isEmpty()) { if (this->name.isEmpty()) {
this->name = "UNKNOWN"; this->name = "UNKNOWN";
@@ -255,6 +260,13 @@ bool NvComputer::update(NvComputer& that)
changed = true; \ changed = true; \
} }
#define ASSIGN_IF_CHANGED_AND_NONNULL(field) \
if (!that.field.isNull() && \
this->field != that.field) { \
this->field = that.field; \
changed = true; \
}
ASSIGN_IF_CHANGED(name); ASSIGN_IF_CHANGED(name);
ASSIGN_IF_CHANGED_AND_NONEMPTY(macAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(macAddress);
ASSIGN_IF_CHANGED_AND_NONEMPTY(localAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(localAddress);
@@ -269,6 +281,7 @@ bool NvComputer::update(NvComputer& that)
ASSIGN_IF_CHANGED(appVersion); ASSIGN_IF_CHANGED(appVersion);
ASSIGN_IF_CHANGED(maxLumaPixelsHEVC); ASSIGN_IF_CHANGED(maxLumaPixelsHEVC);
ASSIGN_IF_CHANGED(gpuModel); ASSIGN_IF_CHANGED(gpuModel);
ASSIGN_IF_CHANGED_AND_NONNULL(serverCert);
ASSIGN_IF_CHANGED_AND_NONEMPTY(appList); ASSIGN_IF_CHANGED_AND_NONEMPTY(appList);
ASSIGN_IF_CHANGED_AND_NONEMPTY(displayModes); ASSIGN_IF_CHANGED_AND_NONEMPTY(displayModes);
return changed; return changed;
+2 -1
View File
@@ -19,7 +19,7 @@ private:
bool pendingQuit; bool pendingQuit;
public: public:
explicit NvComputer(QString address, QString serverInfo); explicit NvComputer(QString address, QString serverInfo, QSslCertificate serverCert);
explicit NvComputer(QSettings& settings); explicit NvComputer(QSettings& settings);
@@ -68,6 +68,7 @@ public:
QByteArray macAddress; QByteArray macAddress;
QString name; QString name;
QString uuid; QString uuid;
QSslCertificate serverCert;
QVector<NvApp> appList; QVector<NvApp> appList;
// Synchronization // Synchronization
+14 -4
View File
@@ -14,8 +14,9 @@
#define REQUEST_TIMEOUT_MS 5000 #define REQUEST_TIMEOUT_MS 5000
NvHTTP::NvHTTP(QString address) : NvHTTP::NvHTTP(QString address, QSslCertificate serverCert) :
m_Address(address) m_Address(address),
m_ServerCert(serverCert)
{ {
Q_ASSERT(!address.isEmpty()); Q_ASSERT(!address.isEmpty());
@@ -390,8 +391,17 @@ NvHTTP::openConnection(QUrl baseUrl,
QNetworkReply* reply = m_Nam.get(request); QNetworkReply* reply = m_Nam.get(request);
// Ignore self-signed certificate errors (since GFE uses them) if (m_ServerCert.isNull()) {
reply->ignoreSslErrors(); // No server cert yet
reply->ignoreSslErrors();
}
else {
// Pin the server certificate received during pairing
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(QSslError(QSslError::HostNameMismatch, m_ServerCert));
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, m_ServerCert));
reply->ignoreSslErrors(expectedSslErrors);
}
// Run the request with a timeout if requested // Run the request with a timeout if requested
QEventLoop loop; QEventLoop loop;
+2 -1
View File
@@ -122,7 +122,7 @@ public:
VERBOSE VERBOSE
}; };
explicit NvHTTP(QString address); explicit NvHTTP(QString address, QSslCertificate serverCert);
static static
int int
@@ -191,4 +191,5 @@ private:
QString m_Address; QString m_Address;
QNetworkAccessManager m_Nam; QNetworkAccessManager m_Nam;
QSslCertificate m_ServerCert;
}; };
+7 -6
View File
@@ -9,7 +9,7 @@
#include <openssl/evp.h> #include <openssl/evp.h>
NvPairingManager::NvPairingManager(QString address) : NvPairingManager::NvPairingManager(QString address) :
m_Http(address) m_Http(address, QSslCertificate())
{ {
QByteArray cert = IdentityManager::get()->getCertificate(); QByteArray cert = IdentityManager::get()->getCertificate();
BIO *bio = BIO_new_mem_buf(cert.data(), -1); BIO *bio = BIO_new_mem_buf(cert.data(), -1);
@@ -161,7 +161,7 @@ NvPairingManager::saltPin(QByteArray salt, QString pin)
} }
NvPairingManager::PairState NvPairingManager::PairState
NvPairingManager::pair(QString appVersion, QString pin) NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverCert)
{ {
int serverMajorVersion = NvHTTP::parseQuad(appVersion).at(0); int serverMajorVersion = NvHTTP::parseQuad(appVersion).at(0);
qInfo() << "Pairing with server generation:" << serverMajorVersion; qInfo() << "Pairing with server generation:" << serverMajorVersion;
@@ -200,8 +200,8 @@ NvPairingManager::pair(QString appVersion, QString pin)
return PairState::FAILED; return PairState::FAILED;
} }
QByteArray serverCert = NvHTTP::getXmlStringFromHex(getCert, "plaincert"); QByteArray serverCertStr = NvHTTP::getXmlStringFromHex(getCert, "plaincert");
if (serverCert == nullptr) if (serverCertStr == nullptr)
{ {
qCritical() << "Server likely already pairing"; qCritical() << "Server likely already pairing";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true);
@@ -262,7 +262,7 @@ NvPairingManager::pair(QString appVersion, QString pin)
if (!verifySignature(serverSecret, if (!verifySignature(serverSecret,
serverSignature, serverSignature,
serverCert)) serverCertStr))
{ {
qCritical() << "MITM detected"; qCritical() << "MITM detected";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true);
@@ -271,7 +271,7 @@ NvPairingManager::pair(QString appVersion, QString pin)
QByteArray expectedResponseData; QByteArray expectedResponseData;
expectedResponseData.append(randomChallenge); expectedResponseData.append(randomChallenge);
expectedResponseData.append(getSignatureFromPemCert(serverCert)); expectedResponseData.append(getSignatureFromPemCert(serverCertStr));
expectedResponseData.append(serverSecret); expectedResponseData.append(serverSecret);
if (QCryptographicHash::hash(expectedResponseData, hashAlgo) != serverResponse) if (QCryptographicHash::hash(expectedResponseData, hashAlgo) != serverResponse)
{ {
@@ -309,5 +309,6 @@ NvPairingManager::pair(QString appVersion, QString pin)
return PairState::FAILED; return PairState::FAILED;
} }
serverCert = QSslCertificate(serverCertStr);
return PairState::PAIRED; return PairState::PAIRED;
} }
+1 -1
View File
@@ -23,7 +23,7 @@ public:
~NvPairingManager(); ~NvPairingManager();
PairState PairState
pair(QString appVersion, QString pin); pair(QString appVersion, QString pin, QSslCertificate& serverCert);
private: private:
QByteArray QByteArray
+2 -2
View File
@@ -593,7 +593,7 @@ private:
// Perform a best-effort app quit // Perform a best-effort app quit
if (shouldQuit) { if (shouldQuit) {
NvHTTP http(m_Session->m_Computer->activeAddress); NvHTTP http(m_Session->m_Computer->activeAddress, m_Session->m_Computer->serverCert);
// Logging is already done inside NvHTTP // Logging is already done inside NvHTTP
try { try {
@@ -812,7 +812,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
m_Computer->currentGameId == m_App.id); m_Computer->currentGameId == m_App.id);
try { try {
NvHTTP http(m_Computer->activeAddress); NvHTTP http(m_Computer->activeAddress, m_Computer->serverCert);
if (m_Computer->currentGameId != 0) { if (m_Computer->currentGameId != 0) {
http.resumeApp(&m_StreamConfig); http.resumeApp(&m_StreamConfig);
} }