use binaryninjacore_sys::*; use std::ffi::{c_char, c_void, CStr}; use std::fmt::Debug; use std::ptr::NonNull; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; use crate::string::{BnString, IntoCStr}; pub trait SecretsProvider { fn has_data(&mut self, key: &str) -> bool; fn get_data(&mut self, key: &str) -> Option; fn store_data(&mut self, key: &str, data: &str) -> bool; fn delete_data(&mut self, key: &str) -> bool; } /// Struct for storing secrets (e.g. tokens) in a system-specific manner #[repr(transparent)] pub struct CoreSecretsProvider { handle: NonNull, } impl CoreSecretsProvider { pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { Self { handle } } /// Register a new provider pub fn new(name: &str, callback: C) -> Self { // SAFETY: once create SecretsProvider is never dropped let name = name.to_cstr(); let callback = Box::leak(Box::new(callback)); let mut callbacks = BNSecretsProviderCallbacks { context: callback as *mut C as *mut c_void, hasData: Some(cb_has_data::), getData: Some(cb_get_data::), storeData: Some(cb_store_data::), deleteData: Some(cb_delete_data::), }; let result = unsafe { BNRegisterSecretsProvider(name.as_ptr(), &mut callbacks) }; unsafe { Self::from_raw(NonNull::new(result).unwrap()) } } /// Retrieve the list of providers pub fn all() -> Array { let mut count = 0; let result = unsafe { BNGetSecretsProviderList(&mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Retrieve a provider by name pub fn by_name(name: &str) -> Option { let name = name.to_cstr(); let result = unsafe { BNGetSecretsProviderByName(name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Self::from_raw(h) }) } pub fn name(&self) -> String { let result = unsafe { BNGetSecretsProviderName(self.handle.as_ptr()) }; assert!(!result.is_null()); unsafe { BnString::into_string(result) } } /// Check if data for a specific key exists, but do not retrieve it pub fn has_data(&self, key: &str) -> bool { let key = key.to_cstr(); unsafe { BNSecretsProviderHasData(self.handle.as_ptr(), key.as_ptr()) } } /// Retrieve data for the given key, if it exists pub fn get_data(&self, key: &str) -> String { let key = key.to_cstr(); let result = unsafe { BNGetSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) }; unsafe { BnString::into_string(result) } } /// Store data with the given key pub fn store_data(&self, key: &str, value: &str) -> bool { let key = key.to_cstr(); let value = value.to_cstr(); unsafe { BNStoreSecretsProviderData(self.handle.as_ptr(), key.as_ptr(), value.as_ptr()) } } /// Delete stored data with the given key pub fn delete_data(&self, key: &str) -> bool { let key = key.to_cstr(); unsafe { BNDeleteSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) } } } impl Debug for CoreSecretsProvider { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CoreSecretsProvider") .field("name", &self.name()) .finish() } } impl CoreArrayProvider for CoreSecretsProvider { type Raw = *mut BNSecretsProvider; type Context = (); type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for CoreSecretsProvider { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeSecretsProviderList(raw) } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { let raw_ptr = NonNull::new(*raw).unwrap(); Self::from_raw(raw_ptr) } } unsafe extern "C" fn cb_has_data( ctxt: *mut c_void, key: *const c_char, ) -> bool { let ctxt: &mut C = &mut *(ctxt as *mut C); ctxt.has_data(&CStr::from_ptr(key).to_string_lossy()) } unsafe extern "C" fn cb_get_data( ctxt: *mut c_void, key: *const c_char, ) -> *mut c_char { let ctxt: &mut C = &mut *(ctxt as *mut C); match ctxt.get_data(&CStr::from_ptr(key).to_string_lossy()) { Some(result) => BnString::into_raw(BnString::new(result)), None => std::ptr::null_mut(), } } unsafe extern "C" fn cb_store_data( ctxt: *mut c_void, key: *const c_char, data: *const c_char, ) -> bool { let ctxt: &mut C = &mut *(ctxt as *mut C); let key = CStr::from_ptr(key).to_string_lossy(); let data = CStr::from_ptr(data).to_string_lossy(); ctxt.store_data(&key, &data) } unsafe extern "C" fn cb_delete_data( ctxt: *mut c_void, key: *const c_char, ) -> bool { let ctxt: &mut C = &mut *(ctxt as *mut C); ctxt.delete_data(&CStr::from_ptr(key).to_string_lossy()) }