Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: small fix for generator related changes and variable naming #180

Merged
merged 14 commits into from
Oct 24, 2023
Merged
6 changes: 4 additions & 2 deletions Cargo.lock

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

7 changes: 7 additions & 0 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"
features = ["network_client"]
required-features = ["network_client"]

[workspace]
members = [
"ffi",
Expand Down Expand Up @@ -79,6 +84,8 @@ futures = "0.3"
tokio = { version = "1.32", features = ["time", "rt"] }

[dev-dependencies]
hyper = "0.14.27"
base64 = "0.21.4"
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
static_assertions = "1"
whoami = "1.4"
picky = { version = "7.0.0-rc.8", features = ["x509"] }
149 changes: 149 additions & 0 deletions examples/kerberos.rs
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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, SecurityBuffer, SecurityBufferType, SecurityStatus, Sspi,
};
use sspi::{Kerberos, SspiImpl};
use std::error::Error;
static KDC_URL: &'static str = "tcp://computer_name.domain:88";
static HOSTNAME: &'static str = "computer_name.domain";
static USERNAME: &'static str = "user@domain";
static PASSWORD: &'static str = "Passoword";
static AUTH_METHOD: &'static str = "Negotiate"; // Negotiate or Kerberos

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let kerberos_config = KerberosConfig::new(&KDC_URL, HOSTNAME.to_string());
let mut kerberos = Kerberos::new_client_from_config(kerberos_config).unwrap();

let mut acq_creds_handle_result = get_cred_handle(&mut kerberos);

let mut input_token = String::new();
let mut client = reqwest::blocking::Client::new(); // super IMPORTANT, KEEP-ALIVE the http connection!
loop {
let (output_token, status) = step(
&mut kerberos,
&mut acq_creds_handle_result.credentials_handle,
&input_token,
);
if status == SecurityStatus::ContinueNeeded || status == SecurityStatus::Ok {
let (token_from_server, status_code) = process_authentication(&output_token, &mut client)?;
if status_code == hyper::StatusCode::OK {
println!("authenticated");
break Ok(());
}
input_token = token_from_server;
} else {
panic!("Having problem contiinue authentication");
}
}
}

pub(crate) fn get_cred_handle(kerberos: &mut Kerberos) -> AcquireCredentialsHandleResult<Option<CredentialsBuffers>> {
let identity = sspi::AuthIdentity {
username: crate::USERNAME.to_string(),
password: crate::PASSWORD.to_string().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
}

pub(crate) fn process_authentication(
token_neeeds_to_be_sent: &String,
client: &mut reqwest::blocking::Client,
) -> Result<(String, StatusCode), Box<dyn std::error::Error + Send + Sync>> {
let server_result = send_http(token_neeeds_to_be_sent, client)?;
if server_result.status() == StatusCode::OK {
return Ok((String::new(), StatusCode::OK));
}
let www_authenticate = server_result
.headers()
.get("www-authenticate")
.ok_or("expecting www-authentication header from server but not found")?;
let server_token = www_authenticate
.to_str()
.unwrap()
.replace(format!("{} ", AUTH_METHOD).as_str(), "");
Ok((server_token, server_result.status()))
}

pub(crate) fn send_http(
negotiate_token: &String,
client: &mut reqwest::blocking::Client,
) -> Result<reqwest::blocking::Response, Box<dyn Error + Send + Sync>> {
let resp = client
.post(format!("http://{}:5985/wsman?PSVersion=7.3.8", HOSTNAME))
.header(AUTHORIZATION, format!("{} {}", AUTH_METHOD, negotiate_token))
.header(HOST, format!("{}:5985", HOSTNAME))
.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_helper(
kerberos: &mut Kerberos,
cred_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>> {
let target_name = format!("HTTP/{}", crate::HOSTNAME);
let mut builder = EmptyInitializeSecurityContext::<<Kerberos as SspiImpl>::CredentialsHandle>::new()
.with_credentials_handle(cred_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 fn step(
kerberos: &mut Kerberos,
cred_handle: &mut <Kerberos as SspiImpl>::CredentialsHandle,
input_token: &String,
) -> (String, SecurityStatus) {
let input_buffer = base64::engine::general_purpose::STANDARD.decode(input_token).unwrap();
let mut secure_input_buffer = vec![SecurityBuffer::new(input_buffer, SecurityBufferType::Token)];
let mut secure_output_buffer = vec![SecurityBuffer::new(Vec::new(), SecurityBufferType::Token)];
match step_helper(
kerberos,
cred_handle,
&mut secure_input_buffer,
&mut secure_output_buffer,
) {
Ok(result) => {
let output_buffer = secure_output_buffer[0].to_owned();
let res = (
base64::engine::general_purpose::STANDARD.encode(output_buffer.buffer),
result.status,
);
res
}
Err(_) => {
panic!("error steping");
irvingoujAtDevolution marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
9 changes: 1 addition & 8 deletions src/kerberos/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,12 @@ use crate::kdc::detect_kdc_url;
use crate::negotiate::{NegotiatedProtocol, ProtocolConfig};
use crate::{Kerberos, Result};

#[derive(Debug)]
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
pub struct KerberosConfig {
pub url: Option<Url>,
pub hostname: Option<String>,
}

impl Debug for KerberosConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KerberosConfig")
.field("url", &self.url)
.finish_non_exhaustive()
}
}

impl ProtocolConfig for KerberosConfig {
fn new_client(&self) -> Result<NegotiatedProtocol> {
Ok(NegotiatedProtocol::Kerberos(Kerberos::new_client_from_config(
Expand Down
2 changes: 1 addition & 1 deletion src/kerberos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ impl<'a> Kerberos {
Ok(())
}

pub async fn initialize_security_context_impl(
pub(crate) async fn initialize_security_context_impl(
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
&'a mut self,
yield_point: &mut YieldPointLocal,
builder: &'a mut crate::builders::FilledInitializeSecurityContext<'_, <Self as SspiImpl>::CredentialsHandle>,
Expand Down
2 changes: 1 addition & 1 deletion src/negotiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl Negotiate {
match &negotiated_protocol {
NegotiatedProtocol::Pku2u(pku2u) => {
if !is_pku2u {
let ntlm_config = NtlmConfig::new(pku2u.config().hostname.clone());
let ntlm_config = NtlmConfig::new(pku2u.config().client_hostname.clone());
filtered_protocol = Some(NegotiatedProtocol::Ntlm(Ntlm::with_config(ntlm_config)));
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/pku2u/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ use crate::{Pku2u, Result};
pub struct Pku2uConfig {
pub p2p_certificate: Certificate,
pub private_key: SecretPrivateKey,
pub hostname: String,
pub client_hostname: String,
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
}

impl Pku2uConfig {
pub fn new(p2p_certificate: Certificate, private_key: PrivateKey, hostname: String) -> Self {
pub fn new(p2p_certificate: Certificate, private_key: PrivateKey, client_hostname: String) -> Self {
Self {
p2p_certificate,
private_key: private_key.into(),
hostname,
client_hostname,
}
}

#[cfg(target_os = "windows")]
pub fn default_client_config(hostname: String) -> Result<Self> {
pub fn default_client_config(client_hostname: String) -> Result<Self> {
use super::cert_utils::extraction::extract_client_p2p_cert_and_key;

let (p2p_certificate, private_key) = extract_client_p2p_cert_and_key()?;

Ok(Self {
p2p_certificate,
private_key: private_key.into(),
hostname,
client_hostname,
})
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/pku2u/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ impl Pku2u {
snames: &snames,
// we don't need the nonce in Pku2u
nonce: &[0],
hostname: &self.config.hostname,
hostname: &self.config.client_hostname,
context_requirements: builder.context_requirements,
})?;
let private_key = self.config.private_key.clone();
Expand Down Expand Up @@ -925,7 +925,7 @@ xFnLp2UBrhxA9GYrpJ5i0onRmexQnTVSl5DDq07s+3dbr9YAKjrg9IDZYqLbdwP1
config: Pku2uConfig {
p2p_certificate: p2p_certificate.clone(),
private_key: private_key.clone().into(),
hostname: "hostname".into(),
client_hostname: "hostname".into(),
},
state: Pku2uState::Final,
encryption_params: EncryptionParams {
Expand All @@ -950,7 +950,7 @@ xFnLp2UBrhxA9GYrpJ5i0onRmexQnTVSl5DDq07s+3dbr9YAKjrg9IDZYqLbdwP1
config: Pku2uConfig {
p2p_certificate,
private_key: private_key.into(),
hostname: "hostname".into(),
client_hostname: "hostname".into(),
},
state: Pku2uState::Final,
encryption_params: EncryptionParams {
Expand Down