New licensing system

By Wouter Spruit and Balder Viëtor with minor edits by me
This commit is contained in:
Thulinma 2016-09-27 11:18:42 +02:00
parent d0c81bb9a0
commit 2062d83858
10 changed files with 252 additions and 24 deletions

View file

@ -86,6 +86,9 @@ endif()
if (NOT DEFINED NOUPDATE ) if (NOT DEFINED NOUPDATE )
add_definitions(-DUPDATER=1) add_definitions(-DUPDATER=1)
endif() endif()
if (NOT DEFINED PERPETUAL )
add_definitions(-DLICENSING=1)
endif()
if (DEFINED NOAUTH ) if (DEFINED NOAUTH )
add_definitions(-DNOAUTH=1) add_definitions(-DNOAUTH=1)
endif() endif()
@ -498,6 +501,7 @@ set(controllerHeaders
${SOURCE_DIR}/src/controller/controller_capabilities.h ${SOURCE_DIR}/src/controller/controller_capabilities.h
${SOURCE_DIR}/src/controller/controller_streams.h ${SOURCE_DIR}/src/controller/controller_streams.h
${SOURCE_DIR}/src/controller/controller_push.h ${SOURCE_DIR}/src/controller/controller_push.h
${SOURCE_DIR}/src/controller/controller_license.h
) )
######################################## ########################################
@ -515,6 +519,7 @@ set(controllerSources
${SOURCE_DIR}/src/controller/controller_uplink.cpp ${SOURCE_DIR}/src/controller/controller_uplink.cpp
${SOURCE_DIR}/src/controller/controller_api.cpp ${SOURCE_DIR}/src/controller/controller_api.cpp
${SOURCE_DIR}/src/controller/controller_push.cpp ${SOURCE_DIR}/src/controller/controller_push.cpp
${SOURCE_DIR}/src/controller/controller_license.cpp
) )
######################################## ########################################
# MistController - Build # # MistController - Build #

View file

@ -55,6 +55,7 @@
#include "controller_updater.h" #include "controller_updater.h"
#include "controller_limits.h" #include "controller_limits.h"
#include "controller_uplink.h" #include "controller_uplink.h"
#include "controller_license.h"
/*LTS-END*/ /*LTS-END*/
#include "controller_api.h" #include "controller_api.h"
#include "controller_push.h" #include "controller_push.h"
@ -307,15 +308,28 @@ int main_loop(int argc, char ** argv){
Controller::Log("CONF", "Controller started"); Controller::Log("CONF", "Controller started");
Controller::conf.activate();//activate early, so threads aren't killed. Controller::conf.activate();//activate early, so threads aren't killed.
//Generate instanceId once per boot.
if (Controller::instanceId == ""){
srand(time(NULL));
do{
Controller::instanceId += (char)(64 + rand() % 62);
}while(Controller::instanceId.size() < 16);
}
/*LTS-START*/ /*LTS-START*/
#ifdef UPDATER #ifdef UPDATER
if (Controller::conf.getBool("update")){ if (Controller::conf.getBool("update")){
Controller::CheckUpdates(); Controller::CheckUpdates();
} }
#endif #endif
#ifdef LICENSING
Controller::initLicense();
Controller::checkLicense();
//start license checking thread
tthread::thread licenseThread(Controller::licenseLoop, 0);
#endif
/*LTS-END*/ /*LTS-END*/
//start stats thread //start stats thread
tthread::thread statsThread(Controller::SharedMemStats, &Controller::conf); tthread::thread statsThread(Controller::SharedMemStats, &Controller::conf);
//start monitoring thread //start monitoring thread
@ -324,6 +338,7 @@ int main_loop(int argc, char ** argv){
tthread::thread uplinkThread(Controller::uplinkConnection, 0);/*LTS*/ tthread::thread uplinkThread(Controller::uplinkConnection, 0);/*LTS*/
//start push checking thread //start push checking thread
tthread::thread pushThread(Controller::pushCheckLoop, 0); tthread::thread pushThread(Controller::pushCheckLoop, 0);
//start main loop //start main loop
while (Controller::conf.is_active){/*LTS*/ while (Controller::conf.is_active){/*LTS*/
@ -339,6 +354,11 @@ int main_loop(int argc, char ** argv){
shutdown_reason = "restart (on request)"; shutdown_reason = "restart (on request)";
} }
/*LTS-START*/ /*LTS-START*/
#ifdef LICENSING
if (!Controller::isLicensed()){
shutdown_reason = "no valid license";
}
#endif
if(Triggers::shouldTrigger("SYSTEM_STOP")){ if(Triggers::shouldTrigger("SYSTEM_STOP")){
if (!Triggers::doTrigger("SYSTEM_STOP", shutdown_reason)){ if (!Triggers::doTrigger("SYSTEM_STOP", shutdown_reason)){
Controller::conf.is_active = true; Controller::conf.is_active = true;
@ -359,8 +379,13 @@ int main_loop(int argc, char ** argv){
//join all joinable threads //join all joinable threads
statsThread.join(); statsThread.join();
monitorThread.join(); monitorThread.join();
uplinkThread.join();/*LTS*/ /*LTS-START*/
pushThread.join();/*LTS*/ uplinkThread.join();
pushThread.join();
#ifdef LICENSING
licenseThread.join();
#endif
/*LTS-END*/
//write config //write config
tthread::lock_guard<tthread::mutex> guard(Controller::logMutex); tthread::lock_guard<tthread::mutex> guard(Controller::logMutex);
Controller::Storage.removeMember("log"); Controller::Storage.removeMember("log");

View file

@ -15,6 +15,7 @@
#include "controller_updater.h" #include "controller_updater.h"
#include "controller_limits.h" #include "controller_limits.h"
#include "controller_push.h" #include "controller_push.h"
#include "controller_license.h"
/*LTS-END*/ /*LTS-END*/
///\brief Check the submitted configuration and handle things accordingly. ///\brief Check the submitted configuration and handle things accordingly.
@ -512,6 +513,11 @@ int Controller::handleAPIConnection(Socket::Connection & conn){
if (!Request.isMember("minimal") || Response.isMember("config")){ if (!Request.isMember("minimal") || Response.isMember("config")){
Response["config"] = Controller::Storage["config"]; Response["config"] = Controller::Storage["config"];
Response["config"]["version"] = PACKAGE_VERSION; Response["config"]["version"] = PACKAGE_VERSION;
/*LTS-START*/
#ifdef LICENSING
Response["config"]["license"] = getLicense();
#endif
/*LTS-END*/
//add required data to the current unix time to the config, for syncing reasons //add required data to the current unix time to the config, for syncing reasons
Response["config"]["time"] = Util::epoch(); Response["config"]["time"] = Util::epoch();
if ( !Response["config"].isMember("serverid")){ if ( !Response["config"].isMember("serverid")){

View file

@ -0,0 +1,189 @@
#include "controller_license.h"
#include "controller_storage.h"
#include <iostream>
#include <mist/defines.h>
#include <mist/http_parser.h>
#include <mist/socket.h>
#include <mist/auth.h>
#include <mist/timing.h>
#include <mist/config.h>
#include <mist/encryption.h>
#include <mist/encode.h>
namespace Controller{
static JSON::Value currentLicense;
const JSON::Value & getLicense(){
return currentLicense;
}
//PACKAGE_VERSION = MistServer version
//RELEASE = OS + user_ID
void initLicense(){
if (Storage.isMember("license") && Storage.isMember("license_id")){
INFO_MSG("Reading license from storage")
readLicense(Storage["license_id"].asInt(), Storage["license"].asStringRef());
}
}
bool isLicensed(){
uint64_t now = Util::epoch();
#if DEBUG >= DLVL_DEVEL
INFO_MSG("Verifying license against %llu:", now);
std::cout << currentLicense.toPrettyString() << std::endl;
#endif
//The loop below is timechecker loop
if (!currentLicense.isMember("valid_from") || !currentLicense.isMember("valid_till")){
#if DEBUG >= DLVL_DEVEL
INFO_MSG("Accepting license despite lack of date range, because early build");
#endif
return true;//temporary
}
if (now < currentLicense["valid_from"].asInt() || now > currentLicense["valid_till"].asInt()){
return false;//license is expired
}
if (RELEASE != currentLicense["release"].asStringRef() || PACKAGE_VERSION != currentLicense["version"].asStringRef()){
FAIL_MSG("Could not verify license");
return false;
}
//everything seems okay
return true;
}
bool checkLicense(){
updateLicense();
if (!currentLicense.isMember("interval")){
currentLicense["interval"] = 3600ll;
}
INFO_MSG("Checking license time");
if(!isLicensed()){
FAIL_MSG("License expired, shutting down");
kill(getpid(), SIGINT);
return false;
}
return true;
}
void parseKey(std::string key, char * newKey, unsigned int len){
memset(newKey, 0, len);
for (size_t i = 0; i < key.size() && i < (len << 1); ++i){
char c = key[i];
newKey[i>>1] |= ((c&15) + (((c&64)>>6) | ((c&64)>>3))) << ((~i&1) << 2);
}
}
void updateLicense(){
INFO_MSG("Running license updater");
JSON::Value response;
HTTP::Parser http;
Socket::Connection updrConn("releases.mistserver.org", 80, true);
if ( !updrConn){
WARN_MSG("Failed to reach licensing server");
return;
}
//Sending request to server.
//http.url = "/licensing.php"
//also see statics at start function.
http.url = "/license.php?release="+Encodings::URL::encode(RELEASE)+"&version="+Encodings::URL::encode(PACKAGE_VERSION)+"&iid="+Encodings::URL::encode(instanceId)+"&lid="+currentLicense["lic_id"].asString();
long long currID = currentLicense["lic_id"].asInt();
http.method = "GET";
http.SetHeader("Host", "releases.mistserver.org");
http.SetHeader("X-Version", PACKAGE_VERSION);
if (currID){
char aesKey[16];
if (strlen(SUPER_SECRET) >= 32){
parseKey(SUPER_SECRET SUPER_SECRET + 7,aesKey,16);
}else{
parseKey("4E56721C67306E1F473156F755FF5570",aesKey,16);
}
for (unsigned int i = 0; i < 8; ++i){
aesKey[15-i] = ((currID >> (i*8)) + aesKey[15-i]) & 0xFF;
}
char ivec[16];
memset(ivec, 0, 16);
http.SetHeader("X-IRDGAF", Encodings::Base64::encode(Encryption::AES_Crypt(RELEASE "|" PACKAGE_VERSION, sizeof(RELEASE "|" PACKAGE_VERSION), aesKey, ivec)));
}
updrConn.SendNow(http.BuildRequest());
http.Clean();
unsigned int startTime = Util::epoch();
while ((Util::epoch() - startTime < 10) && (updrConn || updrConn.Received().size())){
if (updrConn.spool() || updrConn.Received().size()){
if ( *(updrConn.Received().get().rbegin()) != '\n'){
std::string tmp = updrConn.Received().get();
updrConn.Received().get().clear();
if (updrConn.Received().size()){
updrConn.Received().get().insert(0, tmp);
}else{
updrConn.Received().append(tmp);
}
continue;
}
if (http.Read(updrConn.Received().get())){
response = JSON::fromString(http.body);
break; //break out of while loop
}
}
}
updrConn.close();
//read license
readLicense(response["lic_id"].asInt(), response["license"].asStringRef());
}
void readLicense(uint64_t licID, const std::string & input){
char aesKey[16];
if (strlen(SUPER_SECRET) >= 32){
parseKey(SUPER_SECRET SUPER_SECRET + 7,aesKey,16);
}else{
parseKey("4E56721C67306E1F473156F755FF5570",aesKey,16);
}
for (unsigned int i = 0; i < 8; ++i){
aesKey[15-i] = ((licID >> (i*8)) + aesKey[15-i]) & 0xFF;
}
std::string cipher = Encodings::Base64::decode(input);
std::string deCrypted;
//magic ivecs, they are empty. It's secretly 16 times \0.
char ivec[16];
memset(ivec, 0, 16);
deCrypted = Encryption::AES_Crypt(cipher.c_str(), cipher.size(), aesKey, ivec);
//get time stamps and license.
//verify checksum
if (deCrypted.size() < 33 || Secure::md5(deCrypted.substr(32)) != deCrypted.substr(0,32)){
FAIL_MSG("Could not decode license");
Storage.removeMember("license");
return;
}
currentLicense = JSON::fromString(deCrypted.substr(32));
if (RELEASE != currentLicense["release"].asStringRef() || PACKAGE_VERSION != currentLicense["version"].asStringRef()){
FAIL_MSG("Could not verify license");
return;
}
//Store license here.
if (currentLicense["store"].asBool()){
if (Storage["license"].asStringRef() != input){
Storage["license"] = input;
Storage["license_id"] = (long long)licID;
INFO_MSG("Stored license for offline use");
}
}
}
void licenseLoop(void * np){
unsigned long now = Util::epoch();
while (conf.is_active){
if (Util::epoch() - now > currentLicense["interval"].asInt()){
if (checkLicense()){
now = Util::epoch();
}
}
Util::wait(2000);//wait at least 2 seconds
}
}
}

View file

@ -0,0 +1,17 @@
#include <mist/json.h>
namespace Controller{
const JSON::Value & getLicense();
void initLicense();
bool isLicensed(); //checks/verifies license time
bool checkLicense(); //Call from Mainloop.
void updateLicense(); //retrieves update from license server
void licenseLoop(void * np);
void readLicense(uint64_t licId, const std::string & input); //checks/interprets license
}

View file

@ -11,7 +11,7 @@
///\brief Holds everything unique to the controller. ///\brief Holds everything unique to the controller.
namespace Controller { namespace Controller {
std::string instanceId; ///instanceId (previously uniqId) is first set in controller.cpp before licensing or update calls.
Util::Config conf; Util::Config conf;
JSON::Value Storage; ///< Global storage of data. JSON::Value Storage; ///< Global storage of data.
tthread::mutex configMutex; tthread::mutex configMutex;

View file

@ -4,6 +4,7 @@
#include <mist/tinythread.h> #include <mist/tinythread.h>
namespace Controller { namespace Controller {
extern std::string instanceId; ///global storage of instanceId (previously uniqID) for updater
extern Util::Config conf;///< Global storage of configuration. extern Util::Config conf;///< Global storage of configuration.
extern JSON::Value Storage; ///< Global storage of data. extern JSON::Value Storage; ///< Global storage of data.
extern tthread::mutex logMutex;///< Mutex for log thread. extern tthread::mutex logMutex;///< Mutex for log thread.

View file

@ -17,9 +17,10 @@
#include "controller_connectors.h" #include "controller_connectors.h"
#include "controller_updater.h" #include "controller_updater.h"
namespace Controller { namespace Controller {
JSON::Value updates; JSON::Value updates;
std::string uniqId;
std::string readFile(std::string filename){ std::string readFile(std::string filename){
std::ifstream file(filename.c_str()); std::ifstream file(filename.c_str());
@ -69,13 +70,6 @@ namespace Controller {
JSON::Value CheckUpdateInfo(){ JSON::Value CheckUpdateInfo(){
JSON::Value ret; JSON::Value ret;
if (uniqId == ""){
srand(time(NULL));
do{
char meh = 64 + rand() % 62;
uniqId += meh;
}while(uniqId.size() < 16);
}
//initialize connection //initialize connection
HTTP::Parser http; HTTP::Parser http;
@ -88,7 +82,7 @@ namespace Controller {
} }
//retrieve update information //retrieve update information
http.url = "/getsums.php?verinfo=1&rel=" RELEASE "&pass=" SHARED_SECRET "&uniqId=" + uniqId; http.url = "/getsums.php?verinfo=1&rel=" RELEASE "&pass=" SHARED_SECRET "&iid=" + instanceId;
http.method = "GET"; http.method = "GET";
http.SetHeader("Host", "releases.mistserver.org"); http.SetHeader("Host", "releases.mistserver.org");
http.SetHeader("X-Version", PACKAGE_VERSION); http.SetHeader("X-Version", PACKAGE_VERSION);

View file

@ -9,7 +9,6 @@
namespace Controller { namespace Controller {
extern JSON::Value updates; extern JSON::Value updates;
extern std::string uniqId;
std::string readFile(std::string filename); std::string readFile(std::string filename);
bool writeFile(std::string filename, std::string & contents); bool writeFile(std::string filename, std::string & contents);

View file

@ -33,14 +33,6 @@ void Controller::uplinkConnection(void * np) {
if (!uplink_port) { if (!uplink_port) {
return; return;
} }
if (uniqId == ""){
srand(time(NULL));
do{
char meh = 64 + rand() % 62;
uniqId += meh;
}while(uniqId.size() < 16);
}
unsigned long long lastSend = Util::epoch() - 5; unsigned long long lastSend = Util::epoch() - 5;
Socket::Connection uplink; Socket::Connection uplink;
@ -119,7 +111,7 @@ void Controller::uplinkConnection(void * np) {
it->removeMember("name"); it->removeMember("name");
} }
data["config"] = Controller::Storage["config"]; data["config"] = Controller::Storage["config"];
data["config"]["uniq"] = uniqId; data["config"]["uniq"] = instanceId;
data["config"]["version"] = PACKAGE_VERSION; data["config"]["version"] = PACKAGE_VERSION;
Controller::checkCapable(capabilities); Controller::checkCapable(capabilities);
data["capabilities"] = capabilities; data["capabilities"] = capabilities;