Use libcurl instead of curl
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "XWBSSOi"
|
name = "XWBSSOi"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -12,6 +12,7 @@ windows-sys = { version = "*", features = [
|
|||||||
"Win32_System_Registry",
|
"Win32_System_Registry",
|
||||||
"Win32_System_SystemInformation",
|
"Win32_System_SystemInformation",
|
||||||
] }
|
] }
|
||||||
|
curl = "*"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|||||||
137
src/common.rs
137
src/common.rs
@@ -7,16 +7,70 @@ const DEFAULT_COMPUTER_NAME: &str = "localhost";
|
|||||||
const DEFAULT_APP_NAME: &str = "CPRSChart.exe";
|
const DEFAULT_APP_NAME: &str = "CPRSChart.exe";
|
||||||
const DEFAULT_ISSUER: &str = "https://ssoi.sts.va.gov/Issuer/smtoken/SAML2";
|
const DEFAULT_ISSUER: &str = "https://ssoi.sts.va.gov/Issuer/smtoken/SAML2";
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct TokenConfig {
|
||||||
pub struct SystemError { pub code: windows_sys::Win32::Foundation::WIN32_ERROR }
|
pub iam: String,
|
||||||
impl std::fmt::Display for SystemError {
|
pub ua: String,
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
pub certificate: String,
|
||||||
write!(f, "System error: {:x}", self.code)
|
pub issuer: String,
|
||||||
|
pub hostname: String,
|
||||||
|
pub application: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TokenConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
iam: String::new(),
|
||||||
|
ua: String::new(),
|
||||||
|
certificate: String::new(),
|
||||||
|
issuer: String::new(),
|
||||||
|
hostname: String::new(),
|
||||||
|
application: String::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StringSystemError { String(std::string::FromUtf8Error), OsString(std::ffi::OsString), SystemError(SystemError) }
|
pub struct SystemError { pub code: windows_sys::Win32::Foundation::WIN32_ERROR }
|
||||||
|
impl std::fmt::Display for SystemError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "SystemError: {:x}", self.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for SystemError {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SSOError {
|
||||||
|
SystemError(SystemError),
|
||||||
|
FromUtf8Error(std::string::FromUtf8Error),
|
||||||
|
OsString(std::ffi::OsString),
|
||||||
|
CurlError(curl::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for SSOError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "SSOError: {:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for SSOError {}
|
||||||
|
impl From<SystemError> for SSOError {
|
||||||
|
fn from(err: SystemError) -> Self {
|
||||||
|
SSOError::SystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<std::string::FromUtf8Error> for SSOError {
|
||||||
|
fn from(err: std::string::FromUtf8Error) -> Self {
|
||||||
|
SSOError::FromUtf8Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<std::ffi::OsString> for SSOError {
|
||||||
|
fn from(err: std::ffi::OsString) -> Self {
|
||||||
|
SSOError::OsString(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<curl::Error> for SSOError {
|
||||||
|
fn from(err: curl::Error) -> Self {
|
||||||
|
SSOError::CurlError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ManagedCertificateStore(pub Cryptography::HCERTSTORE);
|
struct ManagedCertificateStore(pub Cryptography::HCERTSTORE);
|
||||||
impl Drop for ManagedCertificateStore {
|
impl Drop for ManagedCertificateStore {
|
||||||
@@ -25,15 +79,15 @@ impl Drop for ManagedCertificateStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_registry_reg_sz(key: Registry::HKEY, subkey: windows_sys::core::PCWSTR, value: windows_sys::core::PCWSTR) -> Result<String, StringSystemError> {
|
fn get_registry_reg_sz(key: Registry::HKEY, subkey: windows_sys::core::PCWSTR, value: windows_sys::core::PCWSTR) -> Result<String, SSOError> {
|
||||||
let mut bufsz: u32 = 0;
|
let mut bufsz: u32 = 0;
|
||||||
unsafe { Registry::RegGetValueW(key, subkey, value, Registry::RRF_RT_REG_SZ, std::ptr::null_mut(), std::ptr::null_mut(), &mut bufsz); }
|
unsafe { Registry::RegGetValueW(key, subkey, value, Registry::RRF_RT_REG_SZ, std::ptr::null_mut(), std::ptr::null_mut(), &mut bufsz); }
|
||||||
loop {
|
loop {
|
||||||
let mut buffer = vec![0 as u16; bufsz as usize >> 1];
|
let mut buffer = vec![0 as u16; bufsz as usize >> 1];
|
||||||
match unsafe { Registry::RegGetValueW(key, subkey, value, Registry::RRF_RT_REG_SZ, std::ptr::null_mut(), buffer.as_mut_ptr() as *mut std::ffi::c_void, &mut bufsz) } {
|
match unsafe { Registry::RegGetValueW(key, subkey, value, Registry::RRF_RT_REG_SZ, std::ptr::null_mut(), buffer.as_mut_ptr() as *mut std::ffi::c_void, &mut bufsz) } {
|
||||||
windows_sys::Win32::Foundation::ERROR_MORE_DATA => continue,
|
windows_sys::Win32::Foundation::ERROR_MORE_DATA => continue,
|
||||||
windows_sys::Win32::Foundation::ERROR_SUCCESS => return Ok(std::ffi::OsString::from_wide(&buffer[0 .. (bufsz as usize >> 1) - 1]).into_string().map_err(|err| StringSystemError::OsString(err))?),
|
windows_sys::Win32::Foundation::ERROR_SUCCESS => return Ok(std::ffi::OsString::from_wide(&buffer[0 .. (bufsz as usize >> 1) - 1]).into_string()?),
|
||||||
value => return Err(StringSystemError::SystemError(SystemError { code: value }))
|
value => return Err(SSOError::SystemError(SystemError { code: value })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,14 +127,14 @@ pub fn get_iam_request(application: &str, issuer: &str) -> String {
|
|||||||
</soapenv:Envelope>")
|
</soapenv:Envelope>")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_computer_name() -> Result<String, StringSystemError> {
|
pub fn get_local_computer_name() -> Result<String, SSOError> {
|
||||||
use windows_sys::Win32::System::SystemInformation;
|
use windows_sys::Win32::System::SystemInformation;
|
||||||
let mut bufsz: u32 = 0;
|
let mut bufsz: u32 = 0;
|
||||||
unsafe { SystemInformation::GetComputerNameExW(SystemInformation::ComputerNameDnsFullyQualified, std::ptr::null_mut() as windows_sys::core::PWSTR, &mut bufsz); }
|
unsafe { SystemInformation::GetComputerNameExW(SystemInformation::ComputerNameDnsFullyQualified, std::ptr::null_mut() as windows_sys::core::PWSTR, &mut bufsz); }
|
||||||
let mut buffer = vec![0 as u16; bufsz as usize];
|
let mut buffer = vec![0 as u16; bufsz as usize];
|
||||||
match unsafe { SystemInformation::GetComputerNameExW(SystemInformation::ComputerNameDnsFullyQualified, buffer.as_mut_ptr() as windows_sys::core::PWSTR, &mut bufsz) } {
|
match unsafe { SystemInformation::GetComputerNameExW(SystemInformation::ComputerNameDnsFullyQualified, buffer.as_mut_ptr() as windows_sys::core::PWSTR, &mut bufsz) } {
|
||||||
0 => unsafe { Err(StringSystemError::SystemError(SystemError { code: windows_sys::Win32::Foundation::GetLastError() })) },
|
0 => unsafe { Err(SSOError::SystemError(SystemError { code: windows_sys::Win32::Foundation::GetLastError() })) },
|
||||||
_ => std::ffi::OsString::from_wide(&buffer[0 .. bufsz as usize]).into_string().map_err(|err| StringSystemError::OsString(err))
|
_ => Ok(std::ffi::OsString::from_wide(&buffer[0 .. bufsz as usize]).into_string()?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +142,7 @@ pub fn get_app_name() -> Option<String> {
|
|||||||
std::env::current_exe().ok()?.file_name()?.to_str()?.to_owned().into()
|
std::env::current_exe().ok()?.file_name()?.to_str()?.to_owned().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vista_certificate(show_cert_dialog: bool) -> Result<*const Cryptography::CERT_CONTEXT, StringSystemError> {
|
pub fn get_vista_certificate(show_cert_dialog: bool) -> Result<*const Cryptography::CERT_CONTEXT, SSOError> {
|
||||||
let void_p_null = std::ptr::null_mut();
|
let void_p_null = std::ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
let store_system = ManagedCertificateStore(Cryptography::CertOpenSystemStoreW(0, windows_sys::w!("MY")));
|
let store_system = ManagedCertificateStore(Cryptography::CertOpenSystemStoreW(0, windows_sys::w!("MY")));
|
||||||
@@ -101,7 +155,7 @@ pub fn get_vista_certificate(show_cert_dialog: bool) -> Result<*const Cryptograp
|
|||||||
let name_bufsz = Cryptography::CertGetNameStringW(cert_iter, Cryptography::CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, void_p_null, void_p_null as windows_sys::core::PWSTR, 0);
|
let name_bufsz = Cryptography::CertGetNameStringW(cert_iter, Cryptography::CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, void_p_null, void_p_null as windows_sys::core::PWSTR, 0);
|
||||||
let mut name_buffer = vec![0 as u16; name_bufsz as usize];
|
let mut name_buffer = vec![0 as u16; name_bufsz as usize];
|
||||||
Cryptography::CertGetNameStringW(cert_iter, Cryptography::CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, void_p_null, name_buffer.as_mut_ptr() as windows_sys::core::PWSTR, name_bufsz);
|
Cryptography::CertGetNameStringW(cert_iter, Cryptography::CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, void_p_null, name_buffer.as_mut_ptr() as windows_sys::core::PWSTR, name_bufsz);
|
||||||
let name_string = std::ffi::OsString::from_wide(&name_buffer[0 .. name_bufsz as usize - 1]).into_string().map_err(|err| StringSystemError::OsString(err))?;
|
let name_string = std::ffi::OsString::from_wide(&name_buffer[0 .. name_bufsz as usize - 1]).into_string()?;
|
||||||
let mut key_usage_bits: u8 = 0;
|
let mut key_usage_bits: u8 = 0;
|
||||||
Cryptography::CertGetIntendedKeyUsage(Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING, (*cert_iter).pCertInfo, &mut key_usage_bits, std::mem::size_of_val(&key_usage_bits) as u32);
|
Cryptography::CertGetIntendedKeyUsage(Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING, (*cert_iter).pCertInfo, &mut key_usage_bits, std::mem::size_of_val(&key_usage_bits) as u32);
|
||||||
let valid_date = Cryptography::CertVerifyTimeValidity(void_p_null as *const windows_sys::Win32::Foundation::FILETIME, (*cert_iter).pCertInfo);
|
let valid_date = Cryptography::CertVerifyTimeValidity(void_p_null as *const windows_sys::Win32::Foundation::FILETIME, (*cert_iter).pCertInfo);
|
||||||
@@ -116,7 +170,7 @@ pub fn get_vista_certificate(show_cert_dialog: bool) -> Result<*const Cryptograp
|
|||||||
cert_selection = Cryptography::UI::CryptUIDlgSelectCertificateFromStore(store_memory.0, void_p_null, windows_sys::w!("VistA Logon - Certificate Selection"), windows_sys::w!("Select a certificate for VistA authentication"), 0, 0, void_p_null);
|
cert_selection = Cryptography::UI::CryptUIDlgSelectCertificateFromStore(store_memory.0, void_p_null, windows_sys::w!("VistA Logon - Certificate Selection"), windows_sys::w!("Select a certificate for VistA authentication"), 0, 0, void_p_null);
|
||||||
}
|
}
|
||||||
//Cryptography::CertCloseStore(store_memory.0, 0);
|
//Cryptography::CertCloseStore(store_memory.0, 0);
|
||||||
if cert_selection != void_p_null as *const Cryptography::CERT_CONTEXT { Ok(cert_selection) } else { Err(StringSystemError::SystemError(SystemError { code: windows_sys::Win32::Foundation::ERROR_REQUEST_REFUSED })) }
|
if cert_selection != void_p_null as *const Cryptography::CERT_CONTEXT { Ok(cert_selection) } else { Err(SSOError::SystemError(SystemError { code: windows_sys::Win32::Foundation::ERROR_REQUEST_REFUSED })) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,37 +182,30 @@ pub fn get_certificate_thumbprint(certificate: *const Cryptography::CERT_CONTEXT
|
|||||||
buffer.iter().map(|b| format!("{:02x}", b)).collect::<Vec<String>>().join("")
|
buffer.iter().map(|b| format!("{:02x}", b)).collect::<Vec<String>>().join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TokenConfig {
|
pub fn get_sso_token(config: TokenConfig) -> Result<String, SSOError> {
|
||||||
pub iam: String,
|
|
||||||
pub ua: String,
|
|
||||||
pub certificate: String,
|
|
||||||
pub issuer: String,
|
|
||||||
pub hostname: String,
|
|
||||||
pub application: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TokenConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
iam: String::new(),
|
|
||||||
ua: String::new(),
|
|
||||||
certificate: String::new(),
|
|
||||||
issuer: String::new(),
|
|
||||||
hostname: String::new(),
|
|
||||||
application: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_sso_token(config: TokenConfig) -> Result<String, StringSystemError> {
|
|
||||||
let config_certificate = match config.certificate.as_str() { "" => get_certificate_thumbprint(get_vista_certificate(true)?), _ => config.certificate };
|
let config_certificate = match config.certificate.as_str() { "" => get_certificate_thumbprint(get_vista_certificate(true)?), _ => config.certificate };
|
||||||
let config_hostname = match config.hostname.as_str() { "" => get_local_computer_name().unwrap_or_else(|_| DEFAULT_COMPUTER_NAME.to_owned()), _ => config.hostname };
|
let config_hostname = match config.hostname.as_str() { "" => get_local_computer_name().unwrap_or_else(|_| DEFAULT_COMPUTER_NAME.to_owned()), _ => config.hostname };
|
||||||
let config_application = match config.application.as_str() { "" => get_app_name().unwrap_or_else(|| DEFAULT_APP_NAME.to_owned()), _ => config.application };
|
let config_application = match config.application.as_str() { "" => get_app_name().unwrap_or_else(|| DEFAULT_APP_NAME.to_owned()), _ => config.application };
|
||||||
let mut req = std::process::Command::new("curl");
|
let config_iam = match config.iam.as_str() { "" => get_registry_iam(), _ => config.iam };
|
||||||
req.arg("-fsSL").arg("-X").arg("POST").arg(match config.iam.as_str() { "" => get_registry_iam(), _ => config.iam })
|
let mut easy = curl::easy::Easy::new();
|
||||||
.arg("--ca-native").arg("--cert").arg(format!("CurrentUser\\MY\\{config_certificate}"))
|
easy.url(&config_iam)?;
|
||||||
.arg("-A").arg(match config.ua.as_str() { "" => DEFAULT_USER_AGENT, v => v })
|
easy.ssl_cert(format!("CurrentUser\\MY\\{config_certificate}"))?;
|
||||||
.arg("-H").arg("Content-Type: application/xml").arg("-H").arg("Accept: application/xml")
|
easy.ssl_options(curl::easy::SslOpt::new().native_ca(true))?;
|
||||||
.arg("-d").arg(get_iam_request(format!("https://{config_hostname}/Delphi_RPC_Broker/{config_application}").as_str(), match config.issuer.as_str() { "" => DEFAULT_ISSUER, v => v }));
|
let mut headers = curl::easy::List::new();
|
||||||
String::from_utf8(req.stdin(std::process::Stdio::null()).stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::inherit()).output().unwrap().stdout).map_err(|err| StringSystemError::String(err))
|
headers.append("Content-Type: application/xml")?;
|
||||||
|
headers.append("Accept: application/xml")?;
|
||||||
|
easy.http_headers(headers)?;
|
||||||
|
easy.useragent(match config.ua.as_str() { "" => DEFAULT_USER_AGENT, v => v })?;
|
||||||
|
easy.post(true)?;
|
||||||
|
let request_body = get_iam_request(format!("https://{config_hostname}/Delphi_RPC_Broker/{config_application}").as_str(), match config.issuer.as_str() { "" => DEFAULT_ISSUER, v => v });
|
||||||
|
easy.post_field_size(request_body.len() as u64)?;
|
||||||
|
let mut response_body = Vec::new();
|
||||||
|
{
|
||||||
|
use std::io::Read;
|
||||||
|
let mut transfer = easy.transfer();
|
||||||
|
transfer.read_function(|buf| Ok(request_body.as_bytes().read(buf).unwrap_or(0)))?;
|
||||||
|
transfer.write_function(|buf| { response_body.extend_from_slice(buf); Ok(buf.len()) })?;
|
||||||
|
transfer.perform()?;
|
||||||
|
}
|
||||||
|
return Ok(String::from_utf8(response_body)?);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod common;
|
|||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match common::get_sso_token(common::TokenConfig::default()) {
|
match common::get_sso_token(common::TokenConfig::default()) {
|
||||||
Ok(value) => println!("{:?}", value),
|
Ok(value) => println!("{:?}", value),
|
||||||
Err(common::StringSystemError::SystemError(common::SystemError { code })) => { std::process::exit(code as i32) },
|
Err(common::SSOError::SystemError(common::SystemError { code })) => { std::process::exit(code as i32) },
|
||||||
_ => { std::process::exit(1) }
|
_ => { std::process::exit(1) }
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user