Skip to content

Commit

Permalink
fix review issues
Browse files Browse the repository at this point in the history
  • Loading branch information
irvingoujAtDevolution committed Oct 7, 2023
1 parent ac8805b commit 334dfec
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
- uses: actions/checkout@v3

- name: Test
run: cargo test --manifest-path ${{ matrix.manifest }} ${{ matrix.additional-args }} --test *
run: cargo test --manifest-path ${{ matrix.manifest }} ${{ matrix.additional-args }}

wasm:
name: WASM target
Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 11 additions & 26 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ authors = ["Devolutions Inc. <infos@devolutions.net>"]
description = "A Rust implementation of the Security Support Provider Interface (SSPI) API"
keywords = ["ntlm", "auth", "sspi", "windows", "kerberos"]

[[example]]
name = "kerberos"
path = "examples/kerberos.rs"
features = "network_client"

[workspace]
members = ["ffi", "ffi/symbol-rename-macro"]
exclude = ["tools/sspi-ffi-attacker", "tools/wasm-testcompile"]
Expand Down Expand Up @@ -41,11 +46,7 @@ lazy_static = "1.4"
serde = "1"
serde_derive = "1"
url = "2.4"
reqwest = { version = "0.11", features = [
"blocking",
"rustls-tls",
"rustls-tls-native-roots",
], optional = true, default-features = false }
reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false }

picky = { version = "7.0.0-rc.8", default-features = false }
picky-krb = "0.8"
Expand All @@ -59,32 +60,17 @@ trust-dns-resolver = { version = "0.23", optional = true }
portpicker = { version = "0.1", optional = true }
num-bigint-dig = "0.8"
tracing = "0.1"
rustls = { version = "0.21", features = [
"dangerous_configuration",
], optional = true }
rustls = { version = "0.21", features = ["dangerous_configuration"], optional = true }
zeroize = { version = "1.6", features = ["zeroize_derive"] }
tokio = { version = "1.32", features = ["time", "rt"], optional = true }
pcsc = { version = "2.8", optional = true }
async-recursion = "1.0.5"

[target.'cfg(windows)'.dependencies]
winreg = "0.51"
winapi = { version = "0.3", features = [
"std",
"sspi",
"rpcdce",
"impl-default",
"timezoneapi",
"wincrypt",
] }
windows = { version = "0.51", features = [
"Win32_Foundation",
"Win32_NetworkManagement_Dns",
] }
windows-sys = { version = "0.48", features = [
"Win32_Security_Cryptography",
"Win32_Foundation",
] }
winapi = { version = "0.3", features = ["std", "sspi", "rpcdce", "impl-default", "timezoneapi", "wincrypt"] }
windows = { version = "0.51", features = ["Win32_Foundation", "Win32_NetworkManagement_Dns"] }
windows-sys = { version = "0.48", features = ["Win32_Security_Cryptography", "Win32_Foundation"] }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
async-dnssd = "0.5"
Expand All @@ -97,5 +83,4 @@ whoami = "1.4"
picky = { version = "7.0.0-rc.8", features = ["x509"] }
base64 = "0.21.4"
hyper = "0.14.27"
tracing-log = "0.1.3"
tracing-subscriber = "0.3.17"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
217 changes: 115 additions & 102 deletions examples/kerberos.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
use base64::Engine;
use hyper::header::{
ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, AUTHORIZATION, CONNECTION, CONTENT_LENGTH, HOST, USER_AGENT,
};
use hyper::StatusCode;
use sspi::builders::EmptyInitializeSecurityContext;
use sspi::{
AcquireCredentialsHandleResult, ClientRequestFlags, CredentialsBuffers, DataRepresentation,
InitializeSecurityContextResult, KerberosConfig, Negotiate, SecurityBuffer, SecurityBufferType, SecurityStatus,
Sspi,
};
use sspi::{Kerberos, SspiImpl};
use std::error::Error;
use tracing::debug;
use tracing::info;

const TARGET_NAME: &'static str = "HTTP/your-server-name.your.domain";

fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
#[cfg(feature = "network_client")]
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
use hyper::StatusCode;
use sspi::{Kerberos, KerberosConfig, SecurityBuffer, SecurityBufferType, SecurityStatus};

tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG) // Adjust level as needed
.init();
Expand All @@ -25,23 +11,23 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let kerberos_config = KerberosConfig::new(&kdc_url, client_hostname.clone());
let mut kerberos = Kerberos::new_client_from_config(kerberos_config).unwrap();

let mut acq_creds_handle_result = get_cred_handle(&mut kerberos);
let mut acq_creds_handle_result = kerberos_example::get_cred_handle(&mut kerberos);

let mut input = vec![SecurityBuffer::new(Vec::new(), SecurityBufferType::Token)];
let mut output = vec![SecurityBuffer::new(Vec::new(), SecurityBufferType::Token)];

loop {
match step(
match kerberos_example::step(
&mut kerberos,
&mut acq_creds_handle_result.credentials_handle,
&mut input,
&mut output,
) {
Err(e) => panic!("error steping {:?}", e),
Ok(result) => {
let status_code = process_authentication(&mut input, &mut output)?;
if status_code == StatusCode::OK {
info!("connection authenticated");
let status_code = kerberos_example::process_authentication(&mut input, &mut output)?;
if status_code == hyper::StatusCode::OK {
tracing::info!("connection authenticated");
break Ok(());
}

Expand All @@ -53,89 +39,116 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
}

fn get_cred_handle(kerberos: &mut Kerberos) -> AcquireCredentialsHandleResult<Option<CredentialsBuffers>> {
let username = "user@your.domain".to_string();
let password = "user's_password".to_string();
let identity = sspi::AuthIdentity {
username,
password: password.into(),
domain: None,
#[cfg(feature = "network_client")]
mod kerberos_example {
use base64::Engine;
use hyper::header::{
ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, AUTHORIZATION, CONNECTION, CONTENT_LENGTH, HOST, USER_AGENT,
};
let acq_creds_handle_result = kerberos
.acquire_credentials_handle()
.with_credential_use(sspi::CredentialUse::Outbound)
.with_auth_data(&identity.into())
.execute()
.expect("AcquireCredentialsHandle resulted in error");
acq_creds_handle_result
}
use hyper::StatusCode;
use sspi::builders::EmptyInitializeSecurityContext;
use sspi::{
AcquireCredentialsHandleResult, ClientRequestFlags, CredentialsBuffers, DataRepresentation,
InitializeSecurityContextResult, Negotiate, SecurityBuffer, Sspi,
};
use sspi::{Kerberos, SspiImpl};
use std::error::Error;
use tracing::debug;

fn process_authentication(
input: &mut Vec<SecurityBuffer>,
output: &mut Vec<SecurityBuffer>,
) -> Result<StatusCode, Box<dyn std::error::Error + Send + Sync>> {
let output_token_in_binary = &output[0].buffer;
let base_64_token = base64::engine::general_purpose::STANDARD.encode(output_token_in_binary);
let server_result = send_http(base_64_token)?;
debug!("server responde = {:?}", server_result);
let www_authenticate = server_result
.headers()
.get("www-authenticate")
.ok_or("www-authentication header not found")?;
let server_token = www_authenticate.to_str().unwrap().replace("Negotiate ", "");
if server_token.len() <= 5 {
panic!("server token not found");
const TARGET_NAME: &'static str = "HTTP/your-server-name.your.domain";

pub(crate) fn get_cred_handle(
kerberos: &mut Kerberos,
) -> AcquireCredentialsHandleResult<Option<CredentialsBuffers>> {
let username = "user@your.domain".to_string();
let password = "user's_password".to_string();
let identity = sspi::AuthIdentity {
username,
password: password.into(),
domain: None,
};
let acq_creds_handle_result = kerberos
.acquire_credentials_handle()
.with_credential_use(sspi::CredentialUse::Outbound)
.with_auth_data(&identity.into())
.execute()
.expect("AcquireCredentialsHandle resulted in error");
acq_creds_handle_result
}
let decoded_new_token = base64::engine::general_purpose::STANDARD.decode(server_token).unwrap();
clear(input);
clear(output);
input[0].buffer = decoded_new_token;

Ok(server_result.status())
}
pub(crate) fn process_authentication(
input: &mut Vec<SecurityBuffer>,
output: &mut Vec<SecurityBuffer>,
) -> Result<StatusCode, Box<dyn std::error::Error + Send + Sync>> {
let output_token_in_binary = &output[0].buffer;
let base_64_token = base64::engine::general_purpose::STANDARD.encode(output_token_in_binary);
let server_result = send_http(base_64_token)?;
debug!("server responde = {:?}", server_result);
let www_authenticate = server_result
.headers()
.get("www-authenticate")
.ok_or("www-authentication header not found")?;
let server_token = www_authenticate.to_str().unwrap().replace("Negotiate ", "");
if server_token.len() <= 5 {
panic!("server token not found");
}
let decoded_new_token = base64::engine::general_purpose::STANDARD.decode(server_token).unwrap();
clear(input);
clear(output);
input[0].buffer = decoded_new_token;

fn clear(buf: &mut Vec<SecurityBuffer>) {
buf[0].buffer.clear();
}
Ok(server_result.status())
}

fn send_http(negotiate_token: String) -> Result<reqwest::blocking::Response, Box<dyn Error + Send + Sync>> {
let client = reqwest::blocking::Client::new();
let resp = client
.post("http://your-server-name.your.domain:5985/wsman")
.header(AUTHORIZATION, format!("Negotiate {}", negotiate_token))
.header(HOST, "our-server-name.your.domain:5985")
.header(CONNECTION, "keep-alive")
.header(CONTENT_LENGTH, "0")
.header(
USER_AGENT,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
)
.header(ACCEPT, "*/*")
.header(ACCEPT_ENCODING, "gzip, deflate")
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
.send()?;

Ok(resp)
}
pub(crate) fn clear(buf: &mut Vec<SecurityBuffer>) {
buf[0].buffer.clear();
}

pub(crate) fn send_http(
negotiate_token: String,
) -> Result<reqwest::blocking::Response, Box<dyn Error + Send + Sync>> {
let client = reqwest::blocking::Client::new();
let resp = client
.post("http://your-server-name.your.domain:5985/wsman")
.header(AUTHORIZATION, format!("Negotiate {}", negotiate_token))
.header(HOST, "our-server-name.your.domain:5985")
.header(CONNECTION, "keep-alive")
.header(CONTENT_LENGTH, "0")
.header(
USER_AGENT,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
)
.header(ACCEPT, "*/*")
.header(ACCEPT_ENCODING, "gzip, deflate")
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
.send()?;

Ok(resp)
}

fn step(
kerberos: &mut Kerberos,
credential_handle: &mut <Kerberos as SspiImpl>::CredentialsHandle,
input_buffer: &mut Vec<sspi::SecurityBuffer>,
output_buffer: &mut Vec<sspi::SecurityBuffer>,
) -> Result<InitializeSecurityContextResult, Box<dyn std::error::Error>> {
output_buffer[0].buffer.clear();
let mut builder = EmptyInitializeSecurityContext::<<Negotiate as SspiImpl>::CredentialsHandle>::new()
.with_credentials_handle(credential_handle)
.with_context_requirements(ClientRequestFlags::MUTUAL_AUTH)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name(TARGET_NAME)
.with_input(input_buffer)
.with_output(output_buffer);

let result = kerberos
.initialize_security_context_impl(&mut builder)
.resolve_with_default_network_client()?;

Ok(result)
pub(crate) fn step(
kerberos: &mut Kerberos,
credential_handle: &mut <Kerberos as SspiImpl>::CredentialsHandle,
input_buffer: &mut Vec<sspi::SecurityBuffer>,
output_buffer: &mut Vec<sspi::SecurityBuffer>,
) -> Result<InitializeSecurityContextResult, Box<dyn std::error::Error>> {
output_buffer[0].buffer.clear();
let mut builder = EmptyInitializeSecurityContext::<<Negotiate as SspiImpl>::CredentialsHandle>::new()
.with_credentials_handle(credential_handle)
.with_context_requirements(ClientRequestFlags::MUTUAL_AUTH)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name(TARGET_NAME)
.with_input(input_buffer)
.with_output(output_buffer);

let result = kerberos
.initialize_security_context_impl(&mut builder)
.resolve_with_default_network_client()?;

Ok(result)
}
}

// passes CI test compile
#[cfg(not(feature = "network_client"))]
fn main() {}

0 comments on commit 334dfec

Please sign in to comment.