binaryninja/
enterprise.rs1use crate::rc::Array;
2use crate::string::{BnString, IntoCStr};
3use std::ffi::c_void;
4use std::marker::PhantomData;
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum EnterpriseCheckoutError {
10 #[error("enterprise server returned error: {0}")]
11 ServerError(String),
12 #[error("no credentials set for authentication")]
13 NoCredentials,
14 #[error("failed to authenticate with username and password")]
15 NotAuthenticated,
16 #[error("failed to refresh expired license: {0}")]
17 RefreshExpiredLicenseFailed(String),
18}
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
21pub enum EnterpriseCheckoutStatus {
22 AlreadyManaged,
24 Success(Option<Duration>),
26}
27
28pub fn checkout_license(
32 duration: Duration,
33) -> Result<EnterpriseCheckoutStatus, EnterpriseCheckoutError> {
34 if crate::is_ui_enabled() {
35 return Ok(EnterpriseCheckoutStatus::AlreadyManaged);
37 }
38
39 static CHECKOUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
41 let _mtx = CHECKOUT_MUTEX.lock().unwrap();
42
43 #[allow(clippy::collapsible_if)]
44 if !is_server_initialized() {
45 if !initialize_server() && is_server_floating_license() {
47 let last_error = server_last_error().to_string();
48 return Err(EnterpriseCheckoutError::ServerError(last_error));
49 }
50 }
51
52 if is_server_floating_license() {
53 if !is_server_connected() && !connect_server() {
54 let last_error = server_last_error().to_string();
55 return Err(EnterpriseCheckoutError::ServerError(last_error));
56 }
57
58 #[allow(clippy::collapsible_if)]
59 if !is_server_authenticated() {
60 'auth: {
61 if authenticate_server_with_method("Keychain", false) {
63 break 'auth;
64 }
65
66 let username = std::env::var("BN_ENTERPRISE_USERNAME");
68 let password = std::env::var("BN_ENTERPRISE_PASSWORD");
69 if let Ok(username) = username {
70 if let Ok(password) = password {
71 if !authenticate_server_with_credentials(&username, &password, true) {
73 return Err(EnterpriseCheckoutError::NotAuthenticated);
74 }
75 break 'auth;
77 }
78 }
79
80 let token = std::env::var("BN_ENTERPRISE_TOKEN");
81 if let Ok(token) = token {
82 if !authenticate_server_with_token(&token, true) {
83 return Err(EnterpriseCheckoutError::NotAuthenticated);
84 }
85 break 'auth;
86 }
87
88 return Err(EnterpriseCheckoutError::NoCredentials);
90 }
91 }
92 }
93
94 #[allow(clippy::collapsible_if)]
95 if !is_server_license_still_activated()
96 || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now())
97 {
98 if !update_server_license(duration) {
100 let last_error = server_last_error().to_string();
101 return Err(EnterpriseCheckoutError::RefreshExpiredLicenseFailed(
102 last_error,
103 ));
104 }
105 }
106
107 Ok(EnterpriseCheckoutStatus::Success(license_duration()))
108}
109
110pub fn release_license(release_floating: bool) {
111 if !crate::is_ui_enabled() {
112 if !is_server_initialized() {
117 initialize_server();
118 }
119 if !is_server_connected() {
120 connect_server();
121 }
122 if is_server_floating_license() && !release_floating {
124 return;
125 }
126 release_server_license();
128 }
129}
130
131pub fn server_username() -> String {
133 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUsername()) }
134}
135
136pub fn server_url() -> String {
138 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUrl()) }
139}
140
141pub fn set_server_url(url: &str) -> Result<(), ()> {
142 let url = url.to_cstr();
143 let result = unsafe {
144 binaryninjacore_sys::BNSetEnterpriseServerUrl(
145 url.as_ref().as_ptr() as *const std::os::raw::c_char
146 )
147 };
148 if result {
149 Ok(())
150 } else {
151 Err(())
152 }
153}
154
155pub fn server_name() -> String {
156 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerName()) }
157}
158
159pub fn server_id() -> String {
160 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerId()) }
161}
162
163pub fn server_version() -> u64 {
164 unsafe { binaryninjacore_sys::BNGetEnterpriseServerVersion() }
165}
166
167pub fn server_build_id() -> String {
168 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) }
169}
170
171pub fn server_token() -> String {
172 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerToken()) }
173}
174
175pub fn license_duration() -> Option<Duration> {
176 let duration =
177 Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() });
178 match duration {
179 Duration::ZERO => None,
181 _ => Some(duration),
182 }
183}
184
185pub fn license_expiration_time() -> SystemTime {
186 let m = Duration::from_secs(unsafe {
187 binaryninjacore_sys::BNGetEnterpriseServerLicenseExpirationTime()
188 });
189 UNIX_EPOCH + m
190}
191
192pub fn server_reservation_time_limit() -> Duration {
193 Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerReservationTimeLimit() })
194}
195
196pub fn is_server_floating_license() -> bool {
197 unsafe { binaryninjacore_sys::BNIsEnterpriseServerFloatingLicense() }
198}
199
200pub fn is_server_license_still_activated() -> bool {
201 unsafe { binaryninjacore_sys::BNIsEnterpriseServerLicenseStillActivated() }
202}
203
204pub fn authenticate_server_with_token(token: &str, remember: bool) -> bool {
205 let token = token.to_cstr();
206 unsafe {
207 binaryninjacore_sys::BNAuthenticateEnterpriseServerWithToken(
208 token.as_ref().as_ptr() as *const std::os::raw::c_char,
209 remember,
210 )
211 }
212}
213
214pub fn authenticate_server_with_credentials(
215 username: &str,
216 password: &str,
217 remember: bool,
218) -> bool {
219 let username = username.to_cstr();
220 let password = password.to_cstr();
221 unsafe {
222 binaryninjacore_sys::BNAuthenticateEnterpriseServerWithCredentials(
223 username.as_ref().as_ptr() as *const std::os::raw::c_char,
224 password.as_ref().as_ptr() as *const std::os::raw::c_char,
225 remember,
226 )
227 }
228}
229
230pub fn authenticate_server_with_method(method: &str, remember: bool) -> bool {
231 let method = method.to_cstr();
232 unsafe {
233 binaryninjacore_sys::BNAuthenticateEnterpriseServerWithMethod(
234 method.as_ref().as_ptr() as *const std::os::raw::c_char,
235 remember,
236 )
237 }
238}
239
240pub fn connect_server() -> bool {
241 unsafe { binaryninjacore_sys::BNConnectEnterpriseServer() }
242}
243
244pub fn deauthenticate_server() -> bool {
245 unsafe { binaryninjacore_sys::BNDeauthenticateEnterpriseServer() }
246}
247
248pub fn cancel_server_authentication() {
249 unsafe { binaryninjacore_sys::BNCancelEnterpriseServerAuthentication() }
250}
251
252pub fn update_server_license(timeout: Duration) -> bool {
253 unsafe { binaryninjacore_sys::BNUpdateEnterpriseServerLicense(timeout.as_secs()) }
254}
255
256pub fn release_server_license() -> bool {
257 unsafe { binaryninjacore_sys::BNReleaseEnterpriseServerLicense() }
258}
259
260pub fn is_server_connected() -> bool {
261 unsafe { binaryninjacore_sys::BNIsEnterpriseServerConnected() }
262}
263
264pub fn is_server_authenticated() -> bool {
265 unsafe { binaryninjacore_sys::BNIsEnterpriseServerAuthenticated() }
266}
267
268pub fn is_server_initialized() -> bool {
269 unsafe { binaryninjacore_sys::BNIsEnterpriseServerInitialized() }
270}
271
272pub fn initialize_server() -> bool {
273 unsafe { binaryninjacore_sys::BNInitializeEnterpriseServer() }
274}
275
276pub fn server_last_error() -> String {
277 unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerLastError()) }
278}
279
280pub fn server_authentication_methods() -> (Array<BnString>, Array<BnString>) {
281 let mut methods = core::ptr::null_mut();
282 let mut names = core::ptr::null_mut();
283 let count = unsafe {
284 binaryninjacore_sys::BNGetEnterpriseServerAuthenticationMethods(&mut methods, &mut names)
285 };
286 unsafe { (Array::new(methods, count, ()), Array::new(names, count, ())) }
287}
288
289#[repr(transparent)]
292#[derive(Debug)]
293pub struct EnterpriseServerCallback<'a> {
294 handle: binaryninjacore_sys::BNEnterpriseServerCallbacks,
295 lifetime: PhantomData<&'a ()>,
296}
297
298pub fn register_license_changed_callback<'a, F: FnMut(bool) + 'a>(
299 callback: F,
300) -> EnterpriseServerCallback<'a> {
301 unsafe extern "C" fn cb_license_status_changed<F: FnMut(bool)>(
302 ctxt: *mut c_void,
303 still_valid: bool,
304 ) {
305 let ctxt: &mut F = &mut *(ctxt as *mut F);
306 ctxt(still_valid)
307 }
308 let mut handle = binaryninjacore_sys::BNEnterpriseServerCallbacks {
309 context: Box::leak(Box::new(callback)) as *mut F as *mut c_void,
310 licenseStatusChanged: Some(cb_license_status_changed::<F>),
311 };
312 unsafe { binaryninjacore_sys::BNRegisterEnterpriseServerNotification(&mut handle) }
313 EnterpriseServerCallback {
314 handle,
315 lifetime: PhantomData,
316 }
317}
318
319pub fn unregister_license_changed_callback(mut callback_handle: EnterpriseServerCallback) {
320 unsafe {
321 binaryninjacore_sys::BNUnregisterEnterpriseServerNotification(&mut callback_handle.handle)
322 }
323}
324
325impl<'a> EnterpriseServerCallback<'a> {
326 pub fn register<F: FnMut(bool) + 'a>(callback: F) -> Self {
328 register_license_changed_callback(callback)
329 }
330
331 pub fn deregister(self) {
333 }
335}
336
337impl Drop for EnterpriseServerCallback<'_> {
338 fn drop(&mut self) {
339 unregister_license_changed_callback(EnterpriseServerCallback {
340 handle: self.handle,
341 lifetime: PhantomData,
342 })
343 }
344}