mirror of
https://github.com/moonlight-stream/moonlight-chrome.git
synced 2026-04-17 13:50:01 +00:00
Require cert pinning for HTTPS
This commit is contained in:
@@ -27,3 +27,4 @@
|
||||
#define GS_IO_ERROR -5
|
||||
#define GS_NOT_SUPPORTED_4K -6
|
||||
|
||||
#define GS_CERT_MISMATCH -100
|
||||
|
||||
@@ -59,7 +59,7 @@ static CURLcode sslctx_function(CURL * curl, void * sslctx, void * parm)
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
int http_request(char* url, PHTTP_DATA data) {
|
||||
int http_request(const char* url, const char* ppkstr, PHTTP_DATA data) {
|
||||
int ret;
|
||||
CURL *curl;
|
||||
|
||||
@@ -67,12 +67,10 @@ int http_request(char* url, PHTTP_DATA data) {
|
||||
if (!curl)
|
||||
return GS_FAILED;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,"PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_curl);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function);
|
||||
@@ -86,8 +84,12 @@ int http_request(char* url, PHTTP_DATA data) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
// HACK: Connecting with TLS v1.2 causes unexpected TLS alerts
|
||||
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);
|
||||
// Use the pinned certificate for HTTPS
|
||||
if (ppkstr != NULL) {
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, ppkstr);
|
||||
}
|
||||
|
||||
if (data->size > 0) {
|
||||
free(data->memory);
|
||||
@@ -102,7 +104,9 @@ int http_request(char* url, PHTTP_DATA data) {
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
if (res == CURLE_SSL_PINNEDPUBKEYNOTMATCH) {
|
||||
ret = GS_CERT_MISMATCH;
|
||||
} else if (res != CURLE_OK) {
|
||||
ret = GS_FAILED;
|
||||
} else if (data->memory == NULL) {
|
||||
ret = GS_OUT_OF_MEMORY;
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef struct _HTTP_DATA {
|
||||
} HTTP_DATA, *PHTTP_DATA;
|
||||
|
||||
PHTTP_DATA http_create_data();
|
||||
int http_request(char* url, PHTTP_DATA data);
|
||||
int http_request(const char* url, const char* ppkstr, PHTTP_DATA data);
|
||||
void http_free_data(PHTTP_DATA data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -54,11 +54,13 @@ static int xml_search(char* data, size_t len, char* node, char** result) {
|
||||
|
||||
startOffset = strstr(data, startTag);
|
||||
if (startOffset == NULL) {
|
||||
free(data);
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
endOffset = strstr(data, endTag);
|
||||
if (endOffset == NULL) {
|
||||
free(data);
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
@@ -67,6 +69,7 @@ static int xml_search(char* data, size_t len, char* node, char** result) {
|
||||
*result = malloc(strlen(startOffset + strlen(startTag)) + 1);
|
||||
strcpy(*result, startOffset + strlen(startTag));
|
||||
|
||||
free(data);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
@@ -141,24 +144,73 @@ static bool verifySignature(const unsigned char *data, int dataLength, unsigned
|
||||
}
|
||||
|
||||
X509* get_cert(PHTTP_DATA data) {
|
||||
char *result;
|
||||
char *pemcerthex;
|
||||
|
||||
if (xml_search(data->memory, data->size, "plaincert", &result) != GS_OK)
|
||||
if (xml_search(data->memory, data->size, "plaincert", &pemcerthex) != GS_OK)
|
||||
return NULL;
|
||||
|
||||
BIO* bio = BIO_new_mem_buf(result, -1);
|
||||
free(result);
|
||||
// Convert cert from hex string to the PEM string and null terminate
|
||||
int hexstrlen = strlen(pemcerthex);
|
||||
char *pemcert = malloc(hexstrlen / 2 + 1);
|
||||
for (int count = 0; count < hexstrlen; count += 2) {
|
||||
sscanf(&pemcerthex[count], "%2hhx", &pemcert[count / 2]);
|
||||
}
|
||||
pemcert[hexstrlen / 2] = 0;
|
||||
free(pemcerthex);
|
||||
|
||||
// pemcert is referenced, but NOT copied!
|
||||
BIO* bio = BIO_new_mem_buf(pemcert, -1);
|
||||
|
||||
if (bio) {
|
||||
X509* cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free_all(bio);
|
||||
free(pemcert);
|
||||
return cert;
|
||||
}
|
||||
else {
|
||||
free(pemcert);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char* x509_to_curl_ppk_string(X509* x509) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
// Get x509 public key alone in DER format
|
||||
EVP_PKEY* pubkey = X509_get_pubkey(x509);
|
||||
i2d_PUBKEY_bio(bio, pubkey);
|
||||
EVP_PKEY_free(pubkey);
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
|
||||
// SHA256 hash the resulting DER string
|
||||
unsigned char pubkeyhash[32];
|
||||
SHA256((unsigned char*)mem->data, mem->length, pubkeyhash);
|
||||
BIO_free(bio);
|
||||
|
||||
// Base64-encode the resulting SHA256 hash
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO* b64 = BIO_new(BIO_f_base64());
|
||||
bio = BIO_push(b64, bio);
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO_write(bio, pubkeyhash, sizeof(pubkeyhash));
|
||||
BIO_flush(bio);
|
||||
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
|
||||
// Assemble the final curl PPK string
|
||||
const char* prefix = "sha256//";
|
||||
char* ret = malloc(strlen(prefix) + mem->length + 1);
|
||||
memcpy(ret, prefix, strlen(prefix));
|
||||
memcpy(&ret[strlen(prefix)], mem->data, mem->length);
|
||||
ret[strlen(prefix) + mem->length] = 0;
|
||||
|
||||
BIO_free_all(bio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_unpair(const char* address) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
@@ -167,13 +219,13 @@ int gs_unpair(const char* address) {
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s", address, g_UniqueId);
|
||||
ret = http_request(url, data);
|
||||
ret = http_request(url, NULL, data);
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
int gs_pair(int serverMajorVersion, const char* address, const char* pin, char** curl_ppk_string) {
|
||||
int ret = GS_OK;
|
||||
char* result = NULL;
|
||||
X509* server_cert = NULL;
|
||||
@@ -188,7 +240,7 @@ int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
else if ((ret = http_request(url, data)) != GS_OK)
|
||||
else if ((ret = http_request(url, NULL, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
@@ -230,7 +282,7 @@ int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
bytes_to_hex(challenge_enc, challenge_hex, 16);
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientchallenge=%s", address, g_UniqueId, challenge_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
if ((ret = http_request(url, NULL, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
@@ -284,7 +336,7 @@ int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", address, g_UniqueId, challenge_response_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
if ((ret = http_request(url, NULL, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
@@ -328,7 +380,7 @@ int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", address, g_UniqueId, client_pairing_secret_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
if ((ret = http_request(url, NULL, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
@@ -341,6 +393,8 @@ int gs_pair(int serverMajorVersion, const char* address, const char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*curl_ppk_string = x509_to_curl_ppk_string(server_cert);
|
||||
|
||||
cleanup:
|
||||
if (ret != GS_OK)
|
||||
gs_unpair(address);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int gs_pair(int serverMajorVersion, const char* address, const char* pin);
|
||||
int gs_pair(int serverMajorVersion, const char* address, const char* pin, char** server_cert_der_string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user