diff options
| author | Glenn Smith <glenn@vector35.com> | 2022-09-06 19:14:03 -0400 |
|---|---|---|
| committer | Alexander Taylor <alex@vector35.com> | 2022-10-12 01:40:27 -0400 |
| commit | c398bd1ea423f1f9a4fa9e6e3fcadc9143a99f99 (patch) | |
| tree | 6cd62115acaa6e18d9d136be28cfbd31435a01e2 | |
| parent | f68aa8ba0145f17a9ad76f59b1885c998f5fffa5 (diff) | |
[Enterprise] C++ api and example
| -rw-r--r-- | enterprise.cpp | 254 | ||||
| -rw-r--r-- | enterprise.h | 224 | ||||
| -rw-r--r-- | examples/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | examples/enterprise_test/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | examples/enterprise_test/src/enterprise_test.cpp | 101 |
5 files changed, 615 insertions, 0 deletions
diff --git a/enterprise.cpp b/enterprise.cpp new file mode 100644 index 00000000..446c4607 --- /dev/null +++ b/enterprise.cpp @@ -0,0 +1,254 @@ +// Copyright (c) 2015-2022 Vector 35 Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "enterprise.h" + +using namespace BinaryNinja::EnterpriseServer; + +bool BinaryNinja::EnterpriseServer::AuthenticateWithCredentials(const std::string& username, const std::string& password, bool remember) +{ + return BNAuthenticateEnterpriseServerWithCredentials(username.c_str(), password.c_str(), remember); +} + + +bool BinaryNinja::EnterpriseServer::AuthenticateWithMethod(const std::string& method, bool remember) +{ + return BNAuthenticateEnterpriseServerWithMethod(method.c_str(), remember); +} + + +std::vector<std::pair<std::string, std::string>> BinaryNinja::EnterpriseServer::GetAuthenticationMethods() +{ + char** methods; + char** names; + size_t count = BNGetEnterpriseServerAuthenticationMethods(&methods, &names); + + std::vector<std::pair<std::string, std::string>> results; + for (size_t i = 0; i < count; i++) + { + results.push_back({methods[i], names[i]}); + } + + BNFreeStringList(methods, count); + BNFreeStringList(names, count); + + return results; +} + + +bool BinaryNinja::EnterpriseServer::Deauthenticate() +{ + return BNDeauthenticateEnterpriseServer(); +} + + +void BinaryNinja::EnterpriseServer::CancelAuthentication() +{ + return BNCancelEnterpriseServerAuthentication(); +} + + +bool BinaryNinja::EnterpriseServer::Connect() +{ + return BNConnectEnterpriseServer(); +} + + +bool BinaryNinja::EnterpriseServer::AcquireLicense(uint64_t timeout) +{ + return BNAcquireEnterpriseServerLicense(timeout); +} + + +bool BinaryNinja::EnterpriseServer::ReleaseLicense() +{ + return BNReleaseEnterpriseServerLicense(); +} + + +bool BinaryNinja::EnterpriseServer::IsConnected() +{ + return BNIsEnterpriseServerConnected(); +} + + +bool BinaryNinja::EnterpriseServer::IsAuthenticated() +{ + return BNIsEnterpriseServerAuthenticated(); +} + + +std::string BinaryNinja::EnterpriseServer::GetUsername() +{ + char* value = BNGetEnterpriseServerUsername(); + std::string result = value; + BNFreeString(value); + return result; +} + + +std::string BinaryNinja::EnterpriseServer::GetToken() +{ + char* value = BNGetEnterpriseServerToken(); + std::string result = value; + BNFreeString(value); + return result; +} + + +std::string BinaryNinja::EnterpriseServer::GetServerName() +{ + char* value = BNGetEnterpriseServerName(); + std::string result = value; + BNFreeString(value); + return result; +} + + +std::string BinaryNinja::EnterpriseServer::GetServerId() +{ + char* value = BNGetEnterpriseServerId(); + std::string result = value; + BNFreeString(value); + return result; +} + + +uint64_t BinaryNinja::EnterpriseServer::GetServerVersion() +{ + return BNGetEnterpriseServerVersion(); +} + + +std::string BinaryNinja::EnterpriseServer::GetServerBuildId() +{ + char* value = BNGetEnterpriseServerBuildId(); + std::string result = value; + BNFreeString(value); + return result; +} + + +uint64_t BinaryNinja::EnterpriseServer::GetLicenseExpirationTime() +{ + return BNGetEnterpriseServerLicenseExpirationTime(); +} + + +uint64_t BinaryNinja::EnterpriseServer::GetLicenseDuration() +{ + return BNGetEnterpriseServerLicenseDuration(); +} + + +uint64_t BinaryNinja::EnterpriseServer::GetReservationTimeLimit() +{ + return BNGetEnterpriseServerReservationTimeLimit(); +} + + +bool BinaryNinja::EnterpriseServer::IsLicenseStillActivated() +{ + return BNIsEnterpriseServerLicenseStillActivated(); +} + + +std::string BinaryNinja::EnterpriseServer::GetLastError() +{ + return BNGetEnterpriseServerLastError(); + char* str = BNGetEnterpriseServerLastError(); + std::string value = str; + BNFreeString(str); + return value; +} + + +void BinaryNinja::EnterpriseServer::RegisterNotification(BNEnterpriseServerCallbacks* notify) +{ + BNRegisterEnterpriseServerNotification(notify); +} + + +void BinaryNinja::EnterpriseServer::UnregisterNotification(BNEnterpriseServerCallbacks* notify) +{ + BNUnregisterEnterpriseServerNotification(notify); +} + + +BinaryNinja::EnterpriseServer::LicenseCheckout::LicenseCheckout(int64_t duration) +{ + // This is a port of python's binaryninja.enterprise.LicenseCheckout + + // UI builds have their own license manager + if (BNIsUIEnabled()) + return; + + if (!IsConnected()) + { + Connect(); + } + if (!IsAuthenticated()) + { + // Try keychain + bool gotAuth = AuthenticateWithMethod("Keychain", false); + if (!gotAuth) + { + char* username = getenv("BN_ENTERPRISE_USERNAME"); + char* password = getenv("BN_ENTERPRISE_PASSWORD"); + if (username && password) + { + gotAuth = AuthenticateWithCredentials(username, password, true); + } + } + if (!gotAuth) + { + throw EnterpriseException( + "Could not checkout a license: Not authenticated. Try one of the following: \n" + " - Log in and check out a license for an extended time\n" + " - Set BN_ENTERPRISE_USERNAME and BN_ENTERPRISE_PASSWORD environment variables\n" + " - Use BinaryNinja::Enterprise::AuthenticateWithCredentials or AuthenticateWithMethod in your code" + ); + } + } + + // Keychain auth can activate a license if we have one in the keychain + if (!IsLicenseStillActivated()) + { + if (!EnterpriseServer::AcquireLicense(duration)) + { + throw EnterpriseException("Could not checkout a license: " + GetLastError()); + } + m_acquiredLicense = true; + } +} + + +BinaryNinja::EnterpriseServer::LicenseCheckout::~LicenseCheckout() +{ + // UI builds have their own license manager + if (BNIsUIEnabled()) + return; + + // Don't release if we got one from keychain + if (m_acquiredLicense) + { + EnterpriseServer::ReleaseLicense(); + } +} diff --git a/enterprise.h b/enterprise.h new file mode 100644 index 00000000..4d6b1fdd --- /dev/null +++ b/enterprise.h @@ -0,0 +1,224 @@ +// Copyright (c) 2015-2022 Vector 35 Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#pragma once + +#include <string> +#include <vector> +#include <stdexcept> +#include "binaryninjacore.h" + +namespace BinaryNinja +{ + /*! + API for interacting with an Enterprise Server. + These methods will only do anything on Enterprise editions of Binary Ninja. + */ + namespace EnterpriseServer + { + /*! + Custom exception class for all Enterprise functions that can throw exceptions + */ + struct EnterpriseException: std::runtime_error { + EnterpriseException(const std::string& what): std::runtime_error(what) {} + }; + + /*! + Authenticate to the server with username and password + \param username Username to authenticate with + \param password Password to authenticate with + \param remember Remember token in keychain + \return True if successful + */ + bool AuthenticateWithCredentials(const std::string& username, const std::string& password, bool remember); + + /*! + Authenticate with an external provider + \param method Provider method + \param remember Remember token in keychain + \return True if successful + */ + bool AuthenticateWithMethod(const std::string& method, bool remember); + + /*! + Get a list of accepted methods for authentication + \return List of (method, name) pairs + */ + std::vector<std::pair<std::string, std::string>> GetAuthenticationMethods(); + + /*! + Forget saved credentials + \return True if successful + */ + bool Deauthenticate(); + + /*! + Cancel a currently running authentication task + */ + void CancelAuthentication(); + + /*! + Perform initial connect to the server, pulling signing key and time limit + \return True if successful + */ + bool Connect(); + + /*! + Acquire or refresh a floating license + \param timeout Time (in minutes) + \return True if successful + */ + bool AcquireLicense(uint64_t timeout); + + /*! + Release the current hold on a license + \return True if successful + */ + bool ReleaseLicense(); + + /*! + Check if the server is connected + \return True if connected + */ + bool IsConnected(); + + /*! + Check if the user has authenticated with the server + \return True if authenticated + */ + bool IsAuthenticated(); + + /*! + Get currently connected username + \return Username of currently connected user + */ + std::string GetUsername(); + + /*! + Get token for current login session + \return Token for currently connected user + */ + std::string GetToken(); + + /*! + Get the display name of the currently connected server + \return Display name of the currently connected server + */ + std::string GetServerName(); + + /*! + Get the internal id of the currently connected server + \return Id of the currently connected server + */ + std::string GetServerId(); + + /*! + Get the version number of the currently connected server + \return Version of the currently connected server + */ + uint64_t GetServerVersion(); + + /*! + Get the build id string of the currently connected server + \return Build id of the currently connected server + */ + std::string GetServerBuildId(); + + /*! + Get the expiry time for the current license + \return Expiry time, in seconds from the epoch + */ + uint64_t GetLicenseExpirationTime(); + + /*! + Get the total length of the current license + \return Total time, in seconds + */ + uint64_t GetLicenseDuration(); + + /*! + Get the maximum time limit for reservations + \return Maximum reservation time, in seconds + */ + uint64_t GetReservationTimeLimit(); + + /*! + Check if the user's license is still activated + \return True if still activated + */ + bool IsLicenseStillActivated(); + + /*! + Get the last recorded error + \return Error text + */ + std::string GetLastError(); + + /*! + Register an object to receive callbacks when enterprise server events happen + \param notify Object to receive callbacks + */ + void RegisterNotification(BNEnterpriseServerCallbacks* notify); + + /*! + Un-register a previously registered notification handler object + \param notify Object to un-register + */ + void UnregisterNotification(BNEnterpriseServerCallbacks* notify); + + /*! + RAII object for holding an Enterprise license in a scope. Automatically + releases the license when destroyed. + + \b Example: + \code{.cpp} + + using namespace BinaryNinja; + assert(EnterpriseServer::Connect()); + assert(EnterpriseServer::AuthenticateWithCredentials("username", "password", true)); + { + EnterpriseServer::LicenseCheckout _{}; + Ref<BinaryView> bv = OpenView("/bin/ls", true, {}, options); + printf("%llx\n", bv->GetStart()); + // License is released at end of scope + } + + \endcode + */ + class LicenseCheckout + { + bool m_acquiredLicense; + public: + /*! + RAII constructor that checks out a license. License will be refreshed + automatically in a background thread while checked out, in intervals of `duration` + In the event of program crash, the license will expire `duration` seconds after + the most recent background refresh, so you may want a smaller value like 60 if + you expect your program to crash / be killed often. + See class docs for example usage. + \param duration Duration for refreshes and also length of each license checkout. + \throws EnterpriseException If license checkout fails + */ + explicit LicenseCheckout(int64_t duration = 900); + + ~LicenseCheckout(); + }; + } +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 96e13e00..8a831b46 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -11,4 +11,7 @@ if(NOT DEMO) add_subdirectory(workflows/inliner) add_subdirectory(workflows/tailcall) endif() +if(ENTERPRISE) + add_subdirectory(enterprise_test) +endif() add_subdirectory(x86_extension) diff --git a/examples/enterprise_test/CMakeLists.txt b/examples/enterprise_test/CMakeLists.txt new file mode 100644 index 00000000..9833817c --- /dev/null +++ b/examples/enterprise_test/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(enterprise_test CXX C) + +add_executable(${PROJECT_NAME} + src/enterprise_test.cpp) + +if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD) + # Out-of-tree build + find_path( + BN_API_PATH + NAMES binaryninjaapi.h + HINTS ../.. binaryninjaapi $ENV{BN_API_PATH} + REQUIRED + ) + add_subdirectory(${BN_API_PATH} api) +endif() + +target_link_libraries(${PROJECT_NAME} + binaryninjaapi) + +if (NOT WIN32) + target_link_libraries(${PROJECT_NAME} + dl) +endif() + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin) diff --git a/examples/enterprise_test/src/enterprise_test.cpp b/examples/enterprise_test/src/enterprise_test.cpp new file mode 100644 index 00000000..a0bff140 --- /dev/null +++ b/examples/enterprise_test/src/enterprise_test.cpp @@ -0,0 +1,101 @@ +// This is just bin-info but with an enterprise license checkout + +#include <sys/stat.h> + +#include <iostream> +#include <cstdlib> + +#include "binaryninjacore.h" +#include "binaryninjaapi.h" +#include "enterprise.h" + +using namespace BinaryNinja; +using namespace std; + +bool is_file(char* fname) +{ + struct stat buf; + if (stat(fname, &buf) == 0 && (buf.st_mode & S_IFREG) == S_IFREG) + return true; + + return false; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "USAGE: " << argv[0] << " <file_name>" << endl; + exit(-1); + } + + char* fname = argv[1]; + if (!is_file(fname)) + { + cerr << "Error: " << fname << " is not a regular file" << endl; + exit(-1); + } + + EnterpriseServer::LicenseCheckout _{}; + + /* In order to initiate the bundled plugins properly, the location + * of where bundled plugins directory is must be set. */ + SetBundledPluginDirectory(GetBundledPluginDirectory()); + InitPlugins(); + + Ref<BinaryData> bd = new BinaryData(new FileMetadata(), argv[1]); + Ref<BinaryView> bv; + for (auto type : BinaryViewType::GetViewTypes()) + { + if (type->IsTypeValidForData(bd) && type->GetName() != "Raw") + { + bv = type->Create(bd); + break; + } + } + + if (!bv || bv->GetTypeName() == "Raw") + { + fprintf(stderr, "Input file does not appear to be an exectuable\n"); + return -1; + } + + bv->UpdateAnalysisAndWait(); + + cout << "Target: " << fname << endl << endl; + cout << "TYPE: " << bv->GetTypeName() << endl; + cout << "START: 0x" << hex << bv->GetStart() << endl; + cout << "ENTRY: 0x" << hex << bv->GetEntryPoint() << endl; + cout << "PLATFORM: " << bv->GetDefaultPlatform()->GetName() << endl; + cout << endl; + + cout << "---------- 10 Functions ----------" << endl; + int x = 0; + for (auto func : bv->GetAnalysisFunctionList()) + { + cout << hex << func->GetStart() << " " << func->GetSymbol()->GetFullName() << endl; + if (++x >= 10) + break; + } + cout << endl; + + cout << "---------- 10 Strings ----------" << endl; + x = 0; + for (auto str_ref : bv->GetStrings()) + { + char* str = (char*)malloc(str_ref.length + 1); + bv->Read(str, str_ref.start, str_ref.length); + str[str_ref.length] = 0; + + cout << hex << str_ref.start << " (" << dec << str_ref.length << ") " << str << endl; + free(str); + + if (++x >= 10) + break; + } + + // Shutting down is required to allow for clean exit of the core + BNShutdown(); + + return 0; +} |
