From 1993699e76fd3064b22271aa4a2cb000b988039a Mon Sep 17 00:00:00 2001 From: Thanh Nguyen Date: Mon, 16 Dec 2024 17:48:14 -0500 Subject: [PATCH] Add FFI call to fetch mTLS identities and to provision PIV slot (#56) * Add FFI call to fetch mTLS identities * Add PIV util call * Fix comment --- rustica-agent/src/ffi/enrollment.rs | 30 +++++++++++++++++ rustica-agent/src/ffi/mod.rs | 4 +++ rustica-agent/src/ffi/mtls.rs | 50 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 rustica-agent/src/ffi/mtls.rs diff --git a/rustica-agent/src/ffi/enrollment.rs b/rustica-agent/src/ffi/enrollment.rs index 278061b9..39232449 100644 --- a/rustica-agent/src/ffi/enrollment.rs +++ b/rustica-agent/src/ffi/enrollment.rs @@ -290,3 +290,33 @@ pub unsafe extern "C" fn generate_and_enroll( error!("All servers failed to register key"); false } + +#[no_mangle] +// Provision a new nistp384 key in the given slot +pub unsafe extern "C" fn provision_piv( + yubikey_serial: u32, + slot: u8, + subject: *const c_char, + pin: *const c_char, + management_key: *const c_char, +) -> bool { + let alg = AlgorithmId::EccP384; + let slot = SlotId::try_from(slot).unwrap(); + + println!("Provisioning new PIV key in slot {:?}", slot); + + let pin = CStr::from_ptr(pin); + let management_key = CStr::from_ptr(management_key); + let management_key = hex::decode(&management_key.to_str().unwrap()).unwrap(); + let subject = CStr::from_ptr(subject); + let policy = TouchPolicy::Always; + + let mut yk = Yubikey::open(yubikey_serial).unwrap(); + + if yk.unlock(pin.to_str().unwrap().as_bytes(), &management_key).is_err() { + println!("Could not unlock key"); + return false + } + + yk.provision(&slot, subject.to_str().unwrap(), alg, policy, PinPolicy::Never).is_ok() +} diff --git a/rustica-agent/src/ffi/mod.rs b/rustica-agent/src/ffi/mod.rs index cd740c3f..edc79f66 100644 --- a/rustica-agent/src/ffi/mod.rs +++ b/rustica-agent/src/ffi/mod.rs @@ -4,6 +4,7 @@ mod allowed_signer; mod signing; mod utils; mod yubikey_utils; +mod mtls; use std::ffi::{c_char, c_long, CStr}; @@ -25,6 +26,9 @@ pub use utils::*; /// For functions that handle YubiKey specific functionality (generally PIV) pub use yubikey_utils::*; +/// For functions that handle mTLS configs +pub use mtls::*; + use crate::config::UpdatableConfiguration; #[no_mangle] diff --git a/rustica-agent/src/ffi/mtls.rs b/rustica-agent/src/ffi/mtls.rs new file mode 100644 index 00000000..650cfc1a --- /dev/null +++ b/rustica-agent/src/ffi/mtls.rs @@ -0,0 +1,50 @@ +use crate::config::UpdatableConfiguration; +use std::{ffi::{c_char, CStr, CString}, ptr::null}; + +#[no_mangle] +/// Read the mTLS identities of the primary server (the first one) given a config path +pub unsafe extern "C" fn ffi_get_identities_of_primary_server(config_path: *const c_char) -> *const c_char { + let cf = CStr::from_ptr(config_path); + let config_path = match cf.to_str() { + Err(_) => return null(), + Ok(s) => s, + }; + + let updatable_configuration = match UpdatableConfiguration::new(config_path) { + + Ok(c) => c, + Err(e) => { + error!("Configuration was invalid: {e}"); + return null(); + } + }; + + let server = match updatable_configuration.get_configuration().servers.first() { + Some(s) => &s.mtls_cert, + None => return null(), + }; + + let cert = match x509_parser::pem::parse_x509_pem(server.as_bytes()) { + Err(e) => { + error!("Unable to parse mTLS cert PEM: {e}"); + return null(); + }, + Ok((_, s)) => s, + }; + + let subject = match cert.parse_x509() { + Err(e) => { + error!("Unable to parse mTLS cert: {e}"); + return null(); + }, + Ok(c) => c.tbs_certificate.subject().to_string(), + }; + + match CString::new(subject) { + Err(e) => { + error!("Unable to marshall subject to CString: {e}"); + return null(); + }, + Ok(s) => s.into_raw(), + } +}