binaryninja/
enterprise.rsuse crate::rc::Array;
use crate::string::{BnString, IntoCStr};
use std::ffi::c_void;
use std::marker::PhantomData;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum EnterpriseCheckoutError {
#[error("enterprise server returned error: {0}")]
ServerError(String),
#[error("no username set for credential authentication")]
NoUsername,
#[error("no password set for credential authentication")]
NoPassword,
#[error("failed to authenticate with username and password")]
NotAuthenticated,
#[error("failed to refresh expired license: {0}")]
RefreshExpiredLicenseFailed(String),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum EnterpriseCheckoutStatus {
AlreadyManaged,
Success(Option<Duration>),
}
pub fn checkout_license(
duration: Duration,
) -> Result<EnterpriseCheckoutStatus, EnterpriseCheckoutError> {
if crate::is_ui_enabled() {
return Ok(EnterpriseCheckoutStatus::AlreadyManaged);
}
static CHECKOUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
let _mtx = CHECKOUT_MUTEX.lock().unwrap();
#[allow(clippy::collapsible_if)]
if !is_server_initialized() {
if !initialize_server() && is_server_floating_license() {
let last_error = server_last_error().to_string();
return Err(EnterpriseCheckoutError::ServerError(last_error));
}
}
if is_server_floating_license() {
if !is_server_connected() && !connect_server() {
let last_error = server_last_error().to_string();
return Err(EnterpriseCheckoutError::ServerError(last_error));
}
#[allow(clippy::collapsible_if)]
if !is_server_authenticated() {
if !authenticate_server_with_method("Keychain", false) {
let username = std::env::var("BN_ENTERPRISE_USERNAME")
.map_err(|_| EnterpriseCheckoutError::NoUsername)?;
let password = std::env::var("BN_ENTERPRISE_PASSWORD")
.map_err(|_| EnterpriseCheckoutError::NoPassword)?;
if !authenticate_server_with_credentials(&username, &password, true) {
return Err(EnterpriseCheckoutError::NotAuthenticated);
}
}
}
}
#[allow(clippy::collapsible_if)]
if !is_server_license_still_activated()
|| (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now())
{
if !update_server_license(duration) {
let last_error = server_last_error().to_string();
return Err(EnterpriseCheckoutError::RefreshExpiredLicenseFailed(
last_error,
));
}
}
Ok(EnterpriseCheckoutStatus::Success(license_duration()))
}
pub fn release_license(release_floating: bool) {
if !crate::is_ui_enabled() {
if !is_server_initialized() {
initialize_server();
}
if !is_server_connected() {
connect_server();
}
if is_server_floating_license() && !release_floating {
return;
}
release_server_license();
}
}
pub fn server_username() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUsername()) }
}
pub fn server_url() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUrl()) }
}
pub fn set_server_url(url: &str) -> Result<(), ()> {
let url = url.to_cstr();
let result = unsafe {
binaryninjacore_sys::BNSetEnterpriseServerUrl(
url.as_ref().as_ptr() as *const std::os::raw::c_char
)
};
if result {
Ok(())
} else {
Err(())
}
}
pub fn server_name() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerName()) }
}
pub fn server_id() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerId()) }
}
pub fn server_version() -> u64 {
unsafe { binaryninjacore_sys::BNGetEnterpriseServerVersion() }
}
pub fn server_build_id() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) }
}
pub fn server_token() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerToken()) }
}
pub fn license_duration() -> Option<Duration> {
let duration =
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() });
match duration {
Duration::ZERO => None,
_ => Some(duration),
}
}
pub fn license_expiration_time() -> SystemTime {
let m = Duration::from_secs(unsafe {
binaryninjacore_sys::BNGetEnterpriseServerLicenseExpirationTime()
});
UNIX_EPOCH + m
}
pub fn server_reservation_time_limit() -> Duration {
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerReservationTimeLimit() })
}
pub fn is_server_floating_license() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerFloatingLicense() }
}
pub fn is_server_license_still_activated() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerLicenseStillActivated() }
}
pub fn authenticate_server_with_credentials(
username: &str,
password: &str,
remember: bool,
) -> bool {
let username = username.to_cstr();
let password = password.to_cstr();
unsafe {
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithCredentials(
username.as_ref().as_ptr() as *const std::os::raw::c_char,
password.as_ref().as_ptr() as *const std::os::raw::c_char,
remember,
)
}
}
pub fn authenticate_server_with_method(method: &str, remember: bool) -> bool {
let method = method.to_cstr();
unsafe {
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithMethod(
method.as_ref().as_ptr() as *const std::os::raw::c_char,
remember,
)
}
}
pub fn connect_server() -> bool {
unsafe { binaryninjacore_sys::BNConnectEnterpriseServer() }
}
pub fn deauthenticate_server() -> bool {
unsafe { binaryninjacore_sys::BNDeauthenticateEnterpriseServer() }
}
pub fn cancel_server_authentication() {
unsafe { binaryninjacore_sys::BNCancelEnterpriseServerAuthentication() }
}
pub fn update_server_license(timeout: Duration) -> bool {
unsafe { binaryninjacore_sys::BNUpdateEnterpriseServerLicense(timeout.as_secs()) }
}
pub fn release_server_license() -> bool {
unsafe { binaryninjacore_sys::BNReleaseEnterpriseServerLicense() }
}
pub fn is_server_connected() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerConnected() }
}
pub fn is_server_authenticated() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerAuthenticated() }
}
pub fn is_server_initialized() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerInitialized() }
}
pub fn initialize_server() -> bool {
unsafe { binaryninjacore_sys::BNInitializeEnterpriseServer() }
}
pub fn server_last_error() -> String {
unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerLastError()) }
}
pub fn server_authentication_methods() -> (Array<BnString>, Array<BnString>) {
let mut methods = core::ptr::null_mut();
let mut names = core::ptr::null_mut();
let count = unsafe {
binaryninjacore_sys::BNGetEnterpriseServerAuthenticationMethods(&mut methods, &mut names)
};
unsafe { (Array::new(methods, count, ()), Array::new(names, count, ())) }
}
#[repr(transparent)]
#[derive(Debug)]
pub struct EnterpriseServerCallback<'a> {
handle: binaryninjacore_sys::BNEnterpriseServerCallbacks,
lifetime: PhantomData<&'a ()>,
}
pub fn register_license_changed_callback<'a, F: FnMut(bool) + 'a>(
callback: F,
) -> EnterpriseServerCallback<'a> {
unsafe extern "C" fn cb_license_status_changed<F: FnMut(bool)>(
ctxt: *mut c_void,
still_valid: bool,
) {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(still_valid)
}
let mut handle = binaryninjacore_sys::BNEnterpriseServerCallbacks {
context: Box::leak(Box::new(callback)) as *mut F as *mut c_void,
licenseStatusChanged: Some(cb_license_status_changed::<F>),
};
unsafe { binaryninjacore_sys::BNRegisterEnterpriseServerNotification(&mut handle) }
EnterpriseServerCallback {
handle,
lifetime: PhantomData,
}
}
pub fn unregister_license_changed_callback(mut callback_handle: EnterpriseServerCallback) {
unsafe {
binaryninjacore_sys::BNUnregisterEnterpriseServerNotification(&mut callback_handle.handle)
}
}
impl<'a> EnterpriseServerCallback<'a> {
pub fn register<F: FnMut(bool) + 'a>(callback: F) -> Self {
register_license_changed_callback(callback)
}
pub fn deregister(self) {
}
}
impl Drop for EnterpriseServerCallback<'_> {
fn drop(&mut self) {
unregister_license_changed_callback(EnterpriseServerCallback {
handle: self.handle,
lifetime: PhantomData,
})
}
}