Summary: The problem is that whenever an app is shutdown, and then reopened, the flipper dir gets reset when getting the CSR for connecting to flipper. This causes the first connection attempt to fail always, and it goes through the whole cert exchange, taking longer than necessary. Fixes it by loading the csr from disk if it's not loaded yet, without blowing away the whole certs state. A side effect of this would be that as long as some file exists where the csr lives, flipper state would never get reset, so it wouldn't be able to fix itself automatically anymore. To keep that working, I've made `resetFlipperDir()` public and am calling it explicitly when starting certificate exchange. This should ensure that we still reset when we need to, but not unnecessarily. The reason it went wrong is that getCSR used to be called only at cert exchange, when resetting and generating a new one was always desirable. However, when we shipped the fix for changeable android serials, it started to be used as a normal getter. Reviewed By: timur-valiev Differential Revision: D18834806 fbshipit-source-id: 56ca7e03e1aa9011f836bc9c021cf3048f7dc1e4
167 lines
5.1 KiB
C++
167 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "ConnectionContextStore.h"
|
|
#include <folly/json.h>
|
|
#include <folly/portability/SysStat.h>
|
|
#include <stdio.h>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include "CertificateUtils.h"
|
|
#include "Log.h"
|
|
|
|
using namespace facebook::flipper;
|
|
|
|
static constexpr auto CSR_FILE_NAME = "app.csr";
|
|
static constexpr auto FLIPPER_CA_FILE_NAME = "sonarCA.crt";
|
|
static constexpr auto CLIENT_CERT_FILE_NAME = "device.crt";
|
|
static constexpr auto PRIVATE_KEY_FILE = "privateKey.pem";
|
|
static constexpr auto CONNECTION_CONFIG_FILE = "connection_config.json";
|
|
|
|
bool fileExists(std::string fileName);
|
|
std::string loadStringFromFile(std::string fileName);
|
|
void writeStringToFile(std::string content, std::string fileName);
|
|
|
|
ConnectionContextStore::ConnectionContextStore(DeviceData deviceData)
|
|
: deviceData_(deviceData) {}
|
|
|
|
bool ConnectionContextStore::hasRequiredFiles() {
|
|
std::string caCert =
|
|
loadStringFromFile(absoluteFilePath(FLIPPER_CA_FILE_NAME));
|
|
std::string clientCert =
|
|
loadStringFromFile(absoluteFilePath(CLIENT_CERT_FILE_NAME));
|
|
std::string privateKey =
|
|
loadStringFromFile(absoluteFilePath(PRIVATE_KEY_FILE));
|
|
|
|
if (caCert == "" || clientCert == "" || privateKey == "") {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string ConnectionContextStore::getCertificateSigningRequest() {
|
|
// Use in-memory CSR if already loaded
|
|
if (csr != "") {
|
|
return csr;
|
|
}
|
|
|
|
// Attempt to load existing CSR from previous run of the app
|
|
csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME));
|
|
if (csr != "") {
|
|
return csr;
|
|
}
|
|
|
|
// Clean all state and generate a new one
|
|
resetState();
|
|
bool success = generateCertSigningRequest(
|
|
deviceData_.appId.c_str(),
|
|
absoluteFilePath(CSR_FILE_NAME).c_str(),
|
|
absoluteFilePath(PRIVATE_KEY_FILE).c_str());
|
|
if (!success) {
|
|
throw new std::runtime_error("Failed to generate CSR");
|
|
}
|
|
csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME));
|
|
|
|
return csr;
|
|
}
|
|
|
|
std::shared_ptr<SSLContext> ConnectionContextStore::getSSLContext() {
|
|
std::shared_ptr<folly::SSLContext> sslContext =
|
|
std::make_shared<folly::SSLContext>();
|
|
sslContext->loadTrustedCertificates(
|
|
absoluteFilePath(FLIPPER_CA_FILE_NAME).c_str());
|
|
sslContext->setVerificationOption(
|
|
folly::SSLContext::SSLVerifyPeerEnum::VERIFY);
|
|
sslContext->loadCertKeyPairFromFiles(
|
|
absoluteFilePath(CLIENT_CERT_FILE_NAME).c_str(),
|
|
absoluteFilePath(PRIVATE_KEY_FILE).c_str());
|
|
sslContext->authenticate(true, false);
|
|
return sslContext;
|
|
}
|
|
|
|
std::string ConnectionContextStore::getDeviceId() {
|
|
/* On android we can't reliably get the serial of the current device
|
|
So rely on our locally written config, which is provided by the
|
|
desktop app.
|
|
For backwards compatibility, when this isn't present, fall back to the
|
|
unreliable source. */
|
|
try {
|
|
std::string config =
|
|
loadStringFromFile(absoluteFilePath(CONNECTION_CONFIG_FILE));
|
|
auto maybeDeviceId = folly::parseJson(config)["deviceId"];
|
|
return maybeDeviceId.isString() ? maybeDeviceId.getString()
|
|
: deviceData_.deviceId;
|
|
} catch (std::exception&) {
|
|
return deviceData_.deviceId;
|
|
}
|
|
}
|
|
|
|
void ConnectionContextStore::storeConnectionConfig(folly::dynamic& config) {
|
|
std::string json = folly::toJson(config);
|
|
writeStringToFile(json, absoluteFilePath(CONNECTION_CONFIG_FILE));
|
|
}
|
|
|
|
std::string ConnectionContextStore::absoluteFilePath(const char* filename) {
|
|
return std::string(deviceData_.privateAppDirectory + "/sonar/" + filename);
|
|
}
|
|
|
|
std::string ConnectionContextStore::getCertificateDirectoryPath() {
|
|
return absoluteFilePath("");
|
|
}
|
|
|
|
bool ConnectionContextStore::resetState() {
|
|
// Clear in-memory state
|
|
csr = "";
|
|
|
|
// Delete state from disk
|
|
std::string dirPath = absoluteFilePath("");
|
|
struct stat info;
|
|
if (stat(dirPath.c_str(), &info) != 0) {
|
|
int ret = mkdir(dirPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
|
|
return ret == 0;
|
|
} else if (info.st_mode & S_IFDIR) {
|
|
for (auto file : {CSR_FILE_NAME,
|
|
FLIPPER_CA_FILE_NAME,
|
|
CLIENT_CERT_FILE_NAME,
|
|
PRIVATE_KEY_FILE,
|
|
CONNECTION_CONFIG_FILE}) {
|
|
std::remove(absoluteFilePath(file).c_str());
|
|
}
|
|
return true;
|
|
} else {
|
|
log("ERROR: Flipper path exists but is not a directory: " + dirPath);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string loadStringFromFile(std::string fileName) {
|
|
if (!fileExists(fileName)) {
|
|
return "";
|
|
}
|
|
std::stringstream buffer;
|
|
std::ifstream stream;
|
|
std::string line;
|
|
stream.open(fileName.c_str());
|
|
if (!stream) {
|
|
log("ERROR: Unable to open ifstream: " + fileName);
|
|
return "";
|
|
}
|
|
buffer << stream.rdbuf();
|
|
std::string s = buffer.str();
|
|
return s;
|
|
}
|
|
|
|
void writeStringToFile(std::string content, std::string fileName) {
|
|
std::ofstream out(fileName);
|
|
out << content;
|
|
}
|
|
|
|
bool fileExists(std::string fileName) {
|
|
struct stat buffer;
|
|
return stat(fileName.c_str(), &buffer) == 0;
|
|
}
|