binaryninja/
secrets_provider.rs

1use binaryninjacore_sys::*;
2use std::ffi::{c_char, c_void, CStr};
3use std::fmt::Debug;
4use std::ptr::NonNull;
5
6use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
7use crate::string::{BnString, IntoCStr};
8
9pub trait SecretsProvider {
10    fn has_data(&mut self, key: &str) -> bool;
11    fn get_data(&mut self, key: &str) -> Option<String>;
12    fn store_data(&mut self, key: &str, data: &str) -> bool;
13    fn delete_data(&mut self, key: &str) -> bool;
14}
15
16/// Struct for storing secrets (e.g. tokens) in a system-specific manner
17#[repr(transparent)]
18pub struct CoreSecretsProvider {
19    handle: NonNull<BNSecretsProvider>,
20}
21
22impl CoreSecretsProvider {
23    pub(crate) unsafe fn from_raw(handle: NonNull<BNSecretsProvider>) -> Self {
24        Self { handle }
25    }
26
27    /// Register a new provider
28    pub fn new<C: SecretsProvider>(name: &str, callback: C) -> Self {
29        // SAFETY: once create SecretsProvider is never dropped
30        let name = name.to_cstr();
31        let callback = Box::leak(Box::new(callback));
32        let mut callbacks = BNSecretsProviderCallbacks {
33            context: callback as *mut C as *mut c_void,
34            hasData: Some(cb_has_data::<C>),
35            getData: Some(cb_get_data::<C>),
36            storeData: Some(cb_store_data::<C>),
37            deleteData: Some(cb_delete_data::<C>),
38        };
39        let result = unsafe { BNRegisterSecretsProvider(name.as_ptr(), &mut callbacks) };
40        unsafe { Self::from_raw(NonNull::new(result).unwrap()) }
41    }
42
43    /// Retrieve the list of providers
44    pub fn all() -> Array<CoreSecretsProvider> {
45        let mut count = 0;
46        let result = unsafe { BNGetSecretsProviderList(&mut count) };
47        assert!(!result.is_null());
48        unsafe { Array::new(result, count, ()) }
49    }
50
51    /// Retrieve a provider by name
52    pub fn by_name(name: &str) -> Option<CoreSecretsProvider> {
53        let name = name.to_cstr();
54        let result = unsafe { BNGetSecretsProviderByName(name.as_ptr()) };
55        NonNull::new(result).map(|h| unsafe { Self::from_raw(h) })
56    }
57
58    pub fn name(&self) -> String {
59        let result = unsafe { BNGetSecretsProviderName(self.handle.as_ptr()) };
60        assert!(!result.is_null());
61        unsafe { BnString::into_string(result) }
62    }
63
64    /// Check if data for a specific key exists, but do not retrieve it
65    pub fn has_data(&self, key: &str) -> bool {
66        let key = key.to_cstr();
67        unsafe { BNSecretsProviderHasData(self.handle.as_ptr(), key.as_ptr()) }
68    }
69
70    /// Retrieve data for the given key, if it exists
71    pub fn get_data(&self, key: &str) -> String {
72        let key = key.to_cstr();
73        let result = unsafe { BNGetSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) };
74        unsafe { BnString::into_string(result) }
75    }
76
77    /// Store data with the given key
78    pub fn store_data(&self, key: &str, value: &str) -> bool {
79        let key = key.to_cstr();
80        let value = value.to_cstr();
81        unsafe { BNStoreSecretsProviderData(self.handle.as_ptr(), key.as_ptr(), value.as_ptr()) }
82    }
83
84    /// Delete stored data with the given key
85    pub fn delete_data(&self, key: &str) -> bool {
86        let key = key.to_cstr();
87        unsafe { BNDeleteSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) }
88    }
89}
90
91impl Debug for CoreSecretsProvider {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.debug_struct("CoreSecretsProvider")
94            .field("name", &self.name())
95            .finish()
96    }
97}
98
99impl CoreArrayProvider for CoreSecretsProvider {
100    type Raw = *mut BNSecretsProvider;
101    type Context = ();
102    type Wrapped<'a> = Self;
103}
104
105unsafe impl CoreArrayProviderInner for CoreSecretsProvider {
106    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
107        BNFreeSecretsProviderList(raw)
108    }
109
110    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
111        let raw_ptr = NonNull::new(*raw).unwrap();
112        Self::from_raw(raw_ptr)
113    }
114}
115
116unsafe extern "C" fn cb_has_data<C: SecretsProvider>(
117    ctxt: *mut c_void,
118    key: *const c_char,
119) -> bool {
120    let ctxt: &mut C = &mut *(ctxt as *mut C);
121    ctxt.has_data(&CStr::from_ptr(key).to_string_lossy())
122}
123
124unsafe extern "C" fn cb_get_data<C: SecretsProvider>(
125    ctxt: *mut c_void,
126    key: *const c_char,
127) -> *mut c_char {
128    let ctxt: &mut C = &mut *(ctxt as *mut C);
129    match ctxt.get_data(&CStr::from_ptr(key).to_string_lossy()) {
130        Some(result) => BnString::into_raw(BnString::new(result)),
131        None => std::ptr::null_mut(),
132    }
133}
134
135unsafe extern "C" fn cb_store_data<C: SecretsProvider>(
136    ctxt: *mut c_void,
137    key: *const c_char,
138    data: *const c_char,
139) -> bool {
140    let ctxt: &mut C = &mut *(ctxt as *mut C);
141    let key = CStr::from_ptr(key).to_string_lossy();
142    let data = CStr::from_ptr(data).to_string_lossy();
143    ctxt.store_data(&key, &data)
144}
145
146unsafe extern "C" fn cb_delete_data<C: SecretsProvider>(
147    ctxt: *mut c_void,
148    key: *const c_char,
149) -> bool {
150    let ctxt: &mut C = &mut *(ctxt as *mut C);
151    ctxt.delete_data(&CStr::from_ptr(key).to_string_lossy())
152}