diff options
Diffstat (limited to 'licenser.cpp')
-rw-r--r-- | licenser.cpp | 428 |
1 files changed, 0 insertions, 428 deletions
diff --git a/licenser.cpp b/licenser.cpp deleted file mode 100644 index ca52e4b..0000000 --- a/licenser.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* Copyright (C) 2022 The Qt Company Ltd. - * - * SPDX-License-Identifier: GPL-3.0-only WITH Qt-GPL-exception-1.0 -*/ - -#include "licenser.h" - -Licenser::Licenser(uint16_t tcpPort) -{ - // Init daemon settings - settings = new LicdSetup(e_set_type_daemon); - - if (tcpPort == 0) { - tcpPort = utils::strToInt(settings->get("tcp_listening_port")); - } - m_mocInterval = utils::strToInt(settings->get("moc_renewal_interval")) * 3600; // 3600 = secs in hour - // Start the HTTP client - m_http = new HttpClient(settings->get("server_addr"), - settings->get("reservation_access_point"), - settings->get("long-term_access_point"), - settings->get("version_query_access_point")); - // Start the TCP/IP m_server - m_server = new TcpServer(tcpPort); - - -} - -Licenser::~Licenser() -{ - delete(settings); - delete(m_server); - delete(m_http); - std::cout << "Daemon stopped." << std::endl; -} - -int Licenser::listen() -{ - - m_infoString = ""; - int socket = 0; // Placeholder for whatever socket gets active - std::string input = m_server->listenToClients(socket); - input = utils::trimStr(input); - if (input.empty()) { - return 0; //continue; - } - std::cout << "Got an request: " << input << std::endl; - RequestInfo request; - std::string reply = ""; - if (parseIncoming(input, request) == 0) { - std::string payload = ""; - if (request.type == RequestType::normal_license_renewal) { - std::cout << "Standard license renewal request\n"; - // See if it's MOC who's calling. Make sure app name is accepted, no matter of the case. - // request.appname is set in lowercase already in parseIncoming() method at this point - if (request.appName == utils::strToLower(MOCWRAPPER_APP_NAME)) { - // More than 24hrs (licd.ini default) from last successful request? - if (!isMocRenewalDue(request.licenseFile)) { - // No - Just send ok message and no further actions needed - reply = replyString[e_license_granted]; - m_server->sendResponse(socket, reply); - return 0; //continue; - } - } - if (buildRequestJson(request) == 0) { - if (sendLicensingRequest(reply, request) != 0) { - // Bad server connection: Check the existing license file for expiry date - checkLicenseExpiryDate(reply, request); - } else { - parseAndSaveJsonReply(reply, request); - } - } else { - reply = replyString[e_bad_request]; - } - } else if (request.type == RequestType::long_term_request) { - std::cout << "Long-term license request\n"; - if (buildRequestJson(request) == 0) { - if (sendLongTermRequest(reply, request) != 0) { - reply = replyString[e_bad_connection]; - reply += m_http->error(); - } else { - parseAndSaveJsonReply(reply, request); - } - } - } else if (request.type == RequestType::reservation_query) { - reply = checkReservations(); - } else if (request.type == RequestType::server_version) { - reply = askServerVersion(); - } - else if (request.type == RequestType::daemon_version) { - reply = getVersion(); - } - } else { - reply = replyString[e_bad_request] + '\n'; - } - reply += m_infoString; - reply +="\n"; - m_server->sendResponse(socket, reply); - std::cout << "Replied to socket " << socket << ": " << reply << std::endl; - return 0; -} - -std::string Licenser::getVersion() -{ - std::string version = "Qt License Daemon (licd) v"; - version += DAEMON_VERSION; - return version; -} - -int Licenser::parseIncoming(const std::string &incoming, RequestInfo &request) -{ - std::vector<std::string> req = utils::splitStr(incoming, '-'); - - // First find out the command (and operation for longterm case) - std::string cmd = utils::trimStr(req[0]); - if (cmd == LICENSE_REQUEST_CMD) { - request.type = RequestType::normal_license_renewal; - } else if (cmd.substr(0, 8) == LONGTERM_REQUEST_CMD) { - request.type = RequestType::long_term_request; - std::string op = utils::trimStr(cmd.substr(8, cmd.length() -1)); - if (op != LONGTERM_ADD_OP && op != LONGTERM_REMOVE_OP) { - m_infoString = "Invalid longterm operation: " + op; - std::cout << m_infoString; - return e_bad_request; - } - request.operation = op; - } else if (cmd == SERVER_VERSION_CMD) { - request.type = RequestType::server_version; - } else if (cmd == DAEMON_VERSION_CMD) { - request.type = RequestType::daemon_version; - return 0; - } else if (cmd == RESERVATION_QUERY_CMD) { - request.type = RequestType::reservation_query; - return 0; - } else { - std::cout << "Invalid command: " << cmd << std::endl; - return e_bad_request; - } - - // Then cycle through parameters. Start from [1] because [0] is the command - for (int i = 1; i < req.size(); i++) { - std::string item = req[i]; - char arg = (char)item[0]; - std::string val = utils::trimStr(item.substr(1, item.length() - 1)); - if (val.length() == 0) { - m_infoString = "No value for argument: -"; - m_infoString.push_back(arg); - std::cout << m_infoString << std::endl; - return e_bad_request; - } - - if (arg == 'a') request.appName = utils::strToLower(val); - else if (arg == 'v') request.appVersion = val; - else if (arg == 'u') request.userId = val; - else if (arg == 'i') request.licenseId = val; - else if (arg == 'l') request.serverAddr = val; - else { - m_infoString = "Invalid argument: -"; - m_infoString.push_back(arg); - return e_bad_request; - } - } - - // Sanity checks. May not be needed after all, if the clients are made correctly. - if (request.type == RequestType::server_version) { - return 0; // version query has no mandatory parameters, no checks - } - - bool fail = false; - if (request.userId.empty()) { - std::cout << "Missing argument: Username (-u)\n"; - fail = true; - } - if (request.licenseId.empty()) { - std::cout << "Missing argument: License ID (-i)\n"; - fail = true; - } - if (request.type == RequestType::normal_license_renewal) { - if (request.appName.empty()) { - std::cout << "Missing argument: Application name (-a)\n"; - fail = true; - } - if (request.appVersion.empty()) { - std::cout << "Missing argument: Application version (-v)\n"; - fail = true; - } - } else if (request.type == RequestType::long_term_request) { - if (request.operation.empty()) { - m_infoString = "Missing argument: Longterm operation (add/remove)\n"; - std::cout << m_infoString << std::endl; // For logging - fail = true; - } - } - if (fail) { - return e_bad_request; - } - - // Add user info to the license filename - std::stringstream ss; - ss << WORKING_DIR << DIR_SEPARATOR - << settings->get("license_file_base") << request.userId - << "_" << request.licenseId << settings->get("license_file_extension"); - request.licenseFile = ss.str(); - - return 0; -} - -int Licenser::buildRequestJson(RequestInfo &request) -{ - std::stringstream pay; - pay << "{"; - pay<< "\"license_number\":" << "\"" << request.licenseId << "\","; - pay << "\"user_id\":" << "\"" << request.userId << "\","; - if (request.type == RequestType::normal_license_renewal) { - pay << "\"hw_id\":" << "\"" << settings->get("hw_id") << "\","; - pay << "\"src\":" << "\"" << request.appName << "\","; - pay << "\"host_os\":" << "\"" << settings->get("host_os") << "\","; - pay << "\"src_version\":" << "\"" << request.appVersion << "\","; - pay << "\"email\":" << "\"" << request.email << "\""; - } else if (request.type == RequestType::long_term_request) { - pay << "\"operation\":" << "\"" << request.operation << "\""; - } - pay << "}"; - request.payload = pay.str(); - return 0; -} - -int Licenser::parseAndSaveJsonReply(std::string &reply, const RequestInfo &request) -{ - JsonHandler json(reply); - std::stringstream ss; - if (request.type == RequestType::long_term_request) { - // long-term request: Have "message" from response JSON directly as a reply to the user - reply = json.get("message"); - if (json.get("status") != "true" ) { - return 1; - } - // Nothing to add to the server message - } else { - if (json.get("status") == "true") { - ss << replyString[e_license_granted]; - ss << " expiry_date=" << json.get("expiry_date"); - ss << " license_id=" << json.get("license_number"); - ss << " reservation_id=" << json.get("reservation_id"); - reply = ss.str(); - } else { - if (json.get("message") == "License fully reserved") { - reply = replyString[e_license_pool_full]; - } else { - reply = replyString[e_license_rejected]; - } - // Do not save the response json, just return - return 1; - } - } - - // Add timestamp into license JSON - struct timeval tp; - gettimeofday(&tp, NULL); - uint64_t timeNow = tp.tv_sec; - - // Save the JSON, adding timestamp - json.add("last_timestamp", timeNow); - int result = utils::writeToFile(request.licenseFile, json.dump(4)); - if (result != 0) { - std::cout << "ERROR saving license file: '" << request.licenseFile << "': " << strerror(result) << std::endl; - } - return 0; -} - -bool Licenser::checkLicenseExpiryDate(std::string &reply, const RequestInfo &request) -{ - std::cout << "Offline - checking validity from license file " << request.licenseFile << std::endl; - // Open the license file - std::string data; - if (utils::readFile(data, request.licenseFile) != 0) { - std::cout << "No license file - rejecting license " << request.licenseFile << std::endl; - reply = replyString[e_bad_connection]; - return false; - } - JsonHandler license(data); - // Check license status - if (license.get("status") != "true") { - std::cout << "License status = false: Rejecting license\n"; - reply = replyString[e_bad_connection]; - return false; - } - // Expiry date. Add 1 day to expiry date to get the end actually at the beginning of the next day - std::string expDate = license.get("expiry_date"); - std::time_t expEpoch = utils::stringToEpoch(expDate.c_str()) + SECS_IN_DAY; - // Current date - std::time_t current = std::time(0); - // See if the time has expire - if (current > expEpoch) { - std::cout << "Expiry date " << expDate << " exceeded" << std::endl; - // License expired: Allow some leeway time for MOC and Plugin, but not for qtlicensetool - if (utils::strToLower(request.appName) != utils::strToLower(QTLICENSETOOL_APP_NAME)) { - int leewayTimeLeft = expEpoch + (license.getInt("leeway_hours") * SECS_IN_HOUR) - current; - if (leewayTimeLeft > 0) { - std::stringstream ss; - ss << replyString[RequestReply::e_no_conn_leeway] - << std::fixed << std::setprecision(1) - << (float)leewayTimeLeft / SECS_IN_DAY << " days"; - reply = ss.str(); - return true; - } else { - std::cout << "No leeway time left" << std::endl; - } - } - std::cout << "Rejecting license\n"; - reply = replyString[e_license_rejected]; - return false; - } - std::cout << "License granted, expires at " << expDate << std::endl; - reply = replyString[e_license_granted]; - return true; -} - -bool Licenser::isMocRenewalDue(const std::string &licenseFile) -{ - std::cout << "MOC calling, checking if renewal is needed\n"; - // Open the license file - std::string data; - if (utils::readFile(data, licenseFile) != 0) { - // If there is no license file yet, have to request anyway - std::cout << "No license file present (" << licenseFile << ") - requesting license\n"; - return true; - } - JsonHandler license(data); - if (license.get("status") != "true") { - // Failed license file shouldn't exist - just to be sure: - return true; - } - // Get current time - struct timeval tp; - gettimeofday(&tp, NULL); - uint64_t timeNow = tp.tv_sec; - if (timeNow > utils::strToInt(license.get("last_timestamp")) + m_mocInterval) { - // Renewal is due - std::cout << "Requesting license renewal\n"; - return true; - } - std::cout << "Renewal time not due, granting license\n"; - - return false; -} - -int Licenser::sendLicensingRequest(std::string &reply, const RequestInfo &request) -{ - // Generate auth hash - std::string authKey; - doHmacHashSha256(request.payload, settings->get("server_secret"), authKey); // 19755982232ff7b6f6d0f3c57ffc1c0e4f03060e7175d478f7b146fb1e000507"; - - // Send the request - if (m_http->sendRequest(reply, request.payload, request.serverAddr, authKey) != 0) { - return -1; - } - return 0; -} - -int Licenser::sendLongTermRequest(std::string &reply, const RequestInfo &request) -{ - if (m_http->sendRequest(reply, request.payload, request.serverAddr) != 0) { - return -1; - } - return 0; -} - -void Licenser::doHmacHashSha256(const std::string &payload, const std::string &secret, std::string &authKey) -{ - std::stringstream ss_result; - ss_result << "apikey "; - - // Allocate memory for the HMAC - std::vector<uint8_t> out(SHA256_HASH_SIZE); - - // Call hmac-sha256 function - hmac_sha256(secret.data(), secret.size(), payload.data(), payload.size(), - out.data(), out.size()); - - // Convert `out` to string with std::hex - for (uint8_t x : out) { - ss_result << std::hex << std::setfill('0') << std::setw(2) << (int)x; - } - - authKey = ss_result.str(); - JsonHandler pay(payload); - - // Print out the result - std::cout << "Message: " << pay.dump(4) << std::endl; - std::cout << "Key: " << secret << std::endl; - std::cout << "HMAC: " << authKey << std::endl; -} - -std::string Licenser::checkReservations() -{ - // Open all license files - std::string dir = WORKING_DIR; - std::string filter = settings->get("license_file_base"); - std::vector<std::string> files = utils::getDirListing(dir, filter); - std::stringstream reply; - reply << "Current reservations: \n"; - for (auto file : files) { - std::string data; - if (utils::readFile(data, file) != 0) { - std::cout << "Couldn't read license file: " << file << std::endl; - continue; - } - JsonHandler json(data); - reply << "\n--- License ID " << json.get("license_number") << " ---\n"; - reply << " User ID: " << json.get("user_id") << std::endl; - reply << " Expiry date: " << json.get("expiry_date") << std::endl; - reply << " Reservation ID: " << json.get("reservation_id") << std::endl; - } - return reply.str(); -} - -std::string Licenser::askServerVersion() -{ - std::string reply; - if (m_http->sendRequest(reply, "", "") != 0) { - reply = replyString[e_bad_connection]; - } else if (reply.find("Error") == std::string::npos) { - JsonHandler json(reply); - reply = "Qt License Server v"; - reply += json.get("version"); - } - return reply; -} |