Skip to content

Commit 015bb4f

Browse files
CouleeAppsfuzyll
authored andcommitted
[Enterprise] Clean up initialization flow
1 parent 17435e0 commit 015bb4f

File tree

8 files changed

+157
-53
lines changed

8 files changed

+157
-53
lines changed

binaryninjacore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,6 +3359,7 @@ extern "C"
33593359
BINARYNINJACOREAPI void BNRegisterEnterpriseServerNotification(BNEnterpriseServerCallbacks* notify);
33603360
BINARYNINJACOREAPI void BNUnregisterEnterpriseServerNotification(BNEnterpriseServerCallbacks* notify);
33613361
BINARYNINJACOREAPI bool BNIsEnterpriseServerInitialized(void);
3362+
BINARYNINJACOREAPI bool BNInitializeEnterpriseServer(void);
33623363

33633364
BINARYNINJACOREAPI void BNRegisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks);
33643365
BINARYNINJACOREAPI void BNUnregisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks);

enterprise.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@
2323

2424
using namespace BinaryNinja::Enterprise;
2525

26+
27+
bool BinaryNinja::Enterprise::IsInitialized()
28+
{
29+
return BNIsEnterpriseServerInitialized();
30+
}
31+
32+
33+
bool BinaryNinja::Enterprise::Initialize()
34+
{
35+
return BNInitializeEnterpriseServer();
36+
}
37+
38+
2639
bool BinaryNinja::Enterprise::AuthenticateWithCredentials(const std::string& username, const std::string& password, bool remember)
2740
{
2841
return BNAuthenticateEnterpriseServerWithCredentials(username.c_str(), password.c_str(), remember);
@@ -99,6 +112,8 @@ bool BinaryNinja::Enterprise::IsAuthenticated()
99112
std::string BinaryNinja::Enterprise::GetUsername()
100113
{
101114
char* value = BNGetEnterpriseServerUsername();
115+
if (!value)
116+
return "";
102117
std::string result = value;
103118
BNFreeString(value);
104119
return result;
@@ -108,6 +123,8 @@ std::string BinaryNinja::Enterprise::GetUsername()
108123
std::string BinaryNinja::Enterprise::GetToken()
109124
{
110125
char* value = BNGetEnterpriseServerToken();
126+
if (!value)
127+
return "";
111128
std::string result = value;
112129
BNFreeString(value);
113130
return result;
@@ -117,6 +134,8 @@ std::string BinaryNinja::Enterprise::GetToken()
117134
std::string BinaryNinja::Enterprise::GetServerName()
118135
{
119136
char* value = BNGetEnterpriseServerName();
137+
if (!value)
138+
return "";
120139
std::string result = value;
121140
BNFreeString(value);
122141
return result;
@@ -126,6 +145,8 @@ std::string BinaryNinja::Enterprise::GetServerName()
126145
std::string BinaryNinja::Enterprise::GetServerUrl()
127146
{
128147
char* value = BNGetEnterpriseServerUrl();
148+
if (!value)
149+
return "";
129150
std::string result = value;
130151
BNFreeString(value);
131152
return result;
@@ -135,6 +156,8 @@ std::string BinaryNinja::Enterprise::GetServerUrl()
135156
std::string BinaryNinja::Enterprise::GetServerId()
136157
{
137158
char* value = BNGetEnterpriseServerId();
159+
if (!value)
160+
return "";
138161
std::string result = value;
139162
BNFreeString(value);
140163
return result;
@@ -150,6 +173,8 @@ uint64_t BinaryNinja::Enterprise::GetServerVersion()
150173
std::string BinaryNinja::Enterprise::GetServerBuildId()
151174
{
152175
char* value = BNGetEnterpriseServerBuildId();
176+
if (!value)
177+
return "";
153178
std::string result = value;
154179
BNFreeString(value);
155180
return result;
@@ -188,7 +213,6 @@ bool BinaryNinja::Enterprise::IsLicenseStillActivated()
188213

189214
std::string BinaryNinja::Enterprise::GetLastError()
190215
{
191-
return BNGetEnterpriseServerLastError();
192216
char* str = BNGetEnterpriseServerLastError();
193217
std::string value = str;
194218
BNFreeString(str);
@@ -208,14 +232,36 @@ void BinaryNinja::Enterprise::UnregisterNotification(BNEnterpriseServerCallbacks
208232
}
209233

210234

211-
BinaryNinja::Enterprise::LicenseCheckout::LicenseCheckout(int64_t duration)
235+
BinaryNinja::Enterprise::LicenseCheckout::LicenseCheckout(int64_t duration): m_acquiredLicense(false)
212236
{
213237
// This is a port of python's binaryninja.enterprise.LicenseCheckout
214238

215239
// UI builds have their own license manager
216240
if (BNIsUIEnabled())
217241
return;
218242

243+
if (!IsInitialized())
244+
{
245+
if (!Initialize())
246+
{
247+
// Named/computer licenses don't need this flow at all
248+
if (!IsFloatingLicense())
249+
{
250+
return;
251+
}
252+
253+
// Floating licenses though, this is an error. Probably the error
254+
// for needing to set enterprise.server.url in settings.json
255+
throw EnterpriseException(
256+
"Could not initialize Binary Ninja Enterprise: " + GetLastError()
257+
);
258+
}
259+
}
260+
261+
if (!IsFloatingLicense())
262+
{
263+
return;
264+
}
219265
if (!IsConnected())
220266
{
221267
Connect();

enterprise.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ namespace BinaryNinja
4040
EnterpriseException(const std::string& what): std::runtime_error(what) {}
4141
};
4242

43+
/*!
44+
Determine if the Enterprise Client has been initialized yet.
45+
\return True if Initialize() has been called successfully
46+
*/
47+
bool IsInitialized();
48+
49+
/*!
50+
Initialize the Enterprise Client
51+
\return True if successful
52+
*/
53+
bool Initialize();
54+
4355
/*!
4456
Authenticate to the Enterprise server with username and password
4557
\param username Username to authenticate with

python/__init__.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,18 @@ def destruct_function(self, ctxt, func):
216216

217217
_enable_default_log = True
218218
_plugin_init = False
219+
_enterprise_license_checkout = None
219220

220221

221222
def _init_plugins():
222223
global _enable_default_log
223224
global _plugin_init
225+
global _enterprise_license_checkout
224226

225227
if not core_ui_enabled() and core.BNGetProduct() == "Binary Ninja Enterprise Client":
226228
# Enterprise client needs to checkout a license reservation or else BNInitPlugins will fail
227-
if not enterprise.is_license_still_activated():
228-
try:
229-
enterprise.authenticate_with_method("Keychain")
230-
except RuntimeError:
231-
pass
232-
if not core.BNIsLicenseValidated() or not enterprise.is_license_still_activated():
233-
raise RuntimeError(
234-
"To use Binary Ninja Enterprise from a headless python script, you must check out a license first.\n"
235-
"You can either check out a license for an extended time with the UI, or use the binaryninja.enterprise module."
236-
)
229+
_enterprise_license_checkout = enterprise.LicenseCheckout()
230+
_enterprise_license_checkout.acquire()
237231

238232
if not _plugin_init:
239233
# The first call to BNInitCorePlugins returns True for successful initialization and True in this context indicates headless operation.

python/collaboration/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,15 @@ def enterprise_remote() -> Optional['Remote']:
7575
return None
7676

7777

78-
def add_known_remote(remote: 'Remote') -> None:
78+
def create_remote(name: str, address: str) -> 'Remote':
7979
"""
80-
Add a Remote to the list of known remotes (saved to Settings)
80+
Create a Remote and add it to the list of known remotes (saved to Settings)
8181
82-
:param remote: New Remote to add
82+
:param name: Identifier for remote
83+
:param address: Base address (HTTPS) for all api requests
8384
"""
8485
binaryninja._init_plugins()
85-
core.BNCollaborationAddRemote(remote._handle)
86+
return Remote(core.BNCollaborationCreateRemote(name, address))
8687

8788

8889
def remove_known_remote(remote: 'Remote') -> None:

python/collaboration/remote.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ctypes
22
import json
3+
import os
34
from typing import Dict, List, Optional, Tuple
45

56
import binaryninja
@@ -15,10 +16,6 @@ class Remote:
1516
"""
1617
def __init__(self, handle: core.BNRemoteHandle):
1718
"""
18-
Create a Remote object (but don't connect to it yet)
19-
20-
:param name: Identifier for remote
21-
:param address: Base address (HTTPS) for all api requests
2219
:param handle: FFI handle for internal use
2320
:raises: RuntimeError if there was an error
2421
"""
@@ -245,22 +242,37 @@ def connect(self, username: Optional[str] = None, token: Optional[str] = None):
245242
"""
246243
if not self.has_loaded_metadata:
247244
self.load_metadata()
248-
if username is None:
245+
got_auth = False
246+
if username is not None and token is not None:
247+
got_auth = True
248+
if not got_auth:
249249
# Try logging in with defaults
250-
if self.is_enterprise:
250+
if self.is_enterprise and enterprise.is_authenticated():
251251
username = enterprise.username()
252252
token = enterprise.token()
253-
else:
254-
# Load from default secrets provider
255-
secrets = binaryninja.SecretsProvider[
256-
binaryninja.Settings().get_string("enterprise.secretsProvider")]
257-
if not secrets.has_data(self.address):
258-
raise RuntimeError("No username and token provided, and none found "
259-
"in the default keychain.")
253+
if username is not None and token is not None:
254+
got_auth = True
255+
256+
if not got_auth:
257+
# Try to load from default secrets provider
258+
secrets = binaryninja.SecretsProvider[
259+
binaryninja.Settings().get_string("enterprise.secretsProvider")]
260+
if secrets.has_data(self.address):
260261
creds = json.loads(secrets.get_data(self.address))
261262
username = creds['username']
262263
token = creds['token']
263-
if username is None or token is None:
264+
got_auth = True
265+
266+
if not got_auth:
267+
# Try logging in with creds in the env
268+
if os.environ.get('BN_ENTERPRISE_USERNAME') is not None and \
269+
os.environ.get('BN_ENTERPRISE_PASSWORD') is not None:
270+
token = self.request_authentication_token(os.environ['BN_ENTERPRISE_USERNAME'], os.environ['BN_ENTERPRISE_PASSWORD'])
271+
if token is not None:
272+
username = os.environ['BN_ENTERPRISE_USERNAME']
273+
got_auth = True
274+
275+
if not got_auth or username is None or token is None:
264276
raise RuntimeError("Cannot connect without a username or token")
265277

266278
if not core.BNRemoteConnect(self._handle, username, token):

0 commit comments

Comments
 (0)