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