mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-17 22:23:31 +00:00
Pin server cert to host during pairing
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user