Skip to content

Commit

Permalink
refactor(sspi): replace lazy_static! macro with LazyLock struct
Browse files Browse the repository at this point in the history
refactor(ffi): replace OnceLock with LazyLock for better code readability
  • Loading branch information
TheBestTvarynka committed Oct 9, 2024
1 parent 62632fa commit 3d30a6d
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 196 deletions.
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.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ cfg-if = "1"
time = { version = "0.3", default-features = false, features = ["std"] }
num-derive = "0.4"
num-traits = "0.2"
lazy_static = "1.5"
serde = "1"
serde_derive = "1"
url = "2.5"
Expand Down
87 changes: 31 additions & 56 deletions ffi/src/winscard/scard_context.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::ffi::CStr;
use std::slice::{from_raw_parts, from_raw_parts_mut};
use std::sync::{Mutex, OnceLock};
use std::sync::{LazyLock, Mutex};

#[cfg(target_os = "windows")]
use ffi_types::winscard::functions::SCardApiFunctionTable;
Expand All @@ -21,7 +21,7 @@ use crate::utils::{c_w_str_to_string, into_raw_ptr, str_encode_utf16};
use crate::winscard::scard_handle::{
raw_scard_context_handle_to_scard_context_handle, scard_context_to_winscard_context, WinScardContextHandle,
};
use crate::winscard::system_scard::SystemScardContext;
use crate::winscard::system_scard::{init_scard_api_table, SystemScardContext};

const ERROR_INVALID_HANDLE: u32 = 6;

Expand All @@ -35,23 +35,22 @@ const SMART_CARD_TYPE: &str = "WINSCARD_USE_SYSTEM_SCARD";
// of `SCARD_CONTEXTS` we can track all active contexts and correctly check is the passed context is valid.
// The same applies to the `SCardReleaseContext`. We need to ensure that the passed context handle was not
// released before.
static SCARD_CONTEXTS: OnceLock<Mutex<Vec<ScardContext>>> = OnceLock::new();
static SCARD_CONTEXTS: LazyLock<Mutex<Vec<ScardContext>>> = LazyLock::new(|| Mutex::new(Vec::new()));
// This API table instance is only needed for the `SCardAccessStartedEvent` function. This function
// doesn't accept any parameters, so we need a separate initialized API table to call the system API.
#[cfg(target_os = "windows")]
static WINSCARD_API: OnceLock<SCardApiFunctionTable> = OnceLock::new();
static WINSCARD_API: LazyLock<SCardApiFunctionTable> =
LazyLock::new(|| init_scard_api_table().expect("winscard module loading should not fail"));

fn save_context(context: ScardContext) {
SCARD_CONTEXTS
.get_or_init(|| Mutex::new(Vec::new()))
.lock()
.expect("SCARD_CONTEXTS mutex locking should not fail")
.push(context)
}

fn is_present(context: ScardContext) -> bool {
SCARD_CONTEXTS
.get_or_init(|| Mutex::new(Vec::new()))
.lock()
.expect("SCARD_CONTEXTS mutex locking should not fail")
.iter()
Expand All @@ -60,7 +59,6 @@ fn is_present(context: ScardContext) -> bool {

fn release_context(context: ScardContext) {
SCARD_CONTEXTS
.get_or_init(|| Mutex::new(Vec::new()))
.lock()
.expect("SCARD_CONTEXTS mutex locking should not fail")
.retain(|ctx| *ctx != context)
Expand Down Expand Up @@ -646,7 +644,23 @@ pub unsafe extern "system" fn SCardFreeMemory(context: ScardContext, pv_mem: LpC
// We use created event to return its handle from the `SCardAccessStartedEvent` function.
// Note. If the `SCardAccessStartedEvent` frunction is not be called, the event will not be created.
#[cfg(target_os = "windows")]
static START_EVENT_HANDLE: OnceLock<windows_sys::Win32::Foundation::HANDLE> = OnceLock::new();
static START_EVENT_HANDLE: LazyLock<windows_sys::Win32::Foundation::HANDLE> = LazyLock::new(|| {
use std::ptr::null;

use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Threading::CreateEventA;

// SAFETY: All parameters are correct.
let handle = unsafe { CreateEventA(null(), 1, 1, null()) };
if handle == 0 {
error!(
"Unable to create event: returned event handle is null. Last error: {}",
// SAFETY: it's safe to call this function.
unsafe { GetLastError() }
);
}
handle
});

#[cfg_attr(windows, rename_symbol(to = "Rust_SCardAccessStartedEvent"))]
#[instrument(ret)]
Expand All @@ -659,41 +673,21 @@ pub extern "system" fn SCardAccessStartedEvent() -> Handle {
// the smart card resource manager is started. The event-object handle can be specified in a call
// to one of the wait functions.

use crate::winscard::system_scard::init_scard_api_table;

if std::env::var(SMART_CARD_TYPE)
.and_then(|use_system_card| Ok(use_system_card == "true"))
.unwrap_or_default()
{
// Use system-provided smart card.
let api =
WINSCARD_API.get_or_init(|| init_scard_api_table().expect("winscard module loading should not fail"));

// SAFETY: The `api` is initialized, so it's safe to call this function.
unsafe { (api.SCardAccessStartedEvent)() }
//
// SAFETY: The `WINSCARD_API` is lazily initialized, so it's safe to call this function.
unsafe { (WINSCARD_API.SCardAccessStartedEvent)() }
} else {
// Use emulated smart card.
//
// We create the event once for the entire process and keep it like a singleton in the "signaled" state.
// We assume we're always ready for our virtual smart cards. Moreover, we don't use reference counters
// because we are always in a ready (signaled) state and have only one handle for the entire process.
*START_EVENT_HANDLE.get_or_init(|| {
use std::ptr::null;

use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Threading::CreateEventA;

// SAFETY: All parameters are correct.
let handle = unsafe { CreateEventA(null(), 1, 1, null()) };
if handle == 0 {
error!(
"Unable to create event: returned event handle is null. Last error: {}",
// SAFETY: it's safe to call this function.
unsafe { GetLastError() }
);
}
handle
})
*START_EVENT_HANDLE
}
}
#[cfg(not(target_os = "windows"))]
Expand All @@ -713,18 +707,14 @@ pub extern "system" fn SCardAccessStartedEvent() -> Handle {
pub extern "system" fn SCardReleaseStartedEvent() {
#[cfg(target_os = "windows")]
{
use crate::winscard::system_scard::init_scard_api_table;

if std::env::var(SMART_CARD_TYPE)
.and_then(|use_system_card| Ok(use_system_card == "true"))
.unwrap_or_default()
{
// Use system-provided smart card.
let api =
WINSCARD_API.get_or_init(|| init_scard_api_table().expect("winscard module loading should not fail"));

// SAFETY: The `api` is initialized, so it's safe to call this function.
unsafe { (api.SCardReleaseStartedEvent)() }
//
// SAFETY: The `WINSDCARD_API` is lazily initialized, so it's safe to call this function.
unsafe { (WINSCARD_API.SCardReleaseStartedEvent)() }
} else {
use windows_sys::Win32::Foundation::{CloseHandle, GetLastError};

Expand All @@ -733,24 +723,9 @@ pub extern "system" fn SCardReleaseStartedEvent() {
// We create the event once for the entire process and keep it like a singleton in the "signaled" state.
// We assume we're always ready for our virtual smart cards. Moreover, we don't use reference counters
// because we are always in a ready (signaled) state and have only one handle for the entire process.
let event_handle = *START_EVENT_HANDLE.get_or_init(|| {
use std::ptr::null;

use windows_sys::Win32::System::Threading::CreateEventA;

// SAFETY: All parameters are correct.
let handle = unsafe { CreateEventA(null(), 1, 1, null()) };
if handle == 0 {
error!(
"Unable to create event: returned event handle is null. Last error: {}",
// SAFETY: it's safe to call this function.
unsafe { GetLastError() }
);
}
handle
});
//
// SAFETY: It's safe to close the handle.
if unsafe { CloseHandle(event_handle) } == 0 {
if unsafe { CloseHandle(*START_EVENT_HANDLE) } == 0 {
error!(
"Cannot close the event handle. List error: {}",
// SAFETY: it's safe to call this function.
Expand Down
19 changes: 8 additions & 11 deletions src/credssp/sspi_cred_ssp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
mod cipher_block_size;
mod tls_connection;

use std::sync::Arc;
use std::sync::{Arc, LazyLock};

use async_recursion::async_recursion;
use lazy_static::lazy_static;
use picky_asn1_x509::Certificate;
use rand::rngs::OsRng;
use rand::Rng;
Expand All @@ -27,15 +26,13 @@ use crate::{

pub const PKG_NAME: &str = "CREDSSP";

lazy_static! {
pub static ref PACKAGE_INFO: PackageInfo = PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: negotiate::PACKAGE_INFO.max_token_len + 1,
name: SecurityPackageType::CredSsp,
comment: String::from("CredSsp security package"),
};
}
pub static PACKAGE_INFO: LazyLock<PackageInfo> = LazyLock::new(|| PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: negotiate::PACKAGE_INFO.max_token_len + 1,
name: SecurityPackageType::CredSsp,
comment: String::from("CredSsp security package"),
});

#[derive(Debug, Clone)]
enum CredSspState {
Expand Down
47 changes: 26 additions & 21 deletions src/credssp/ts_request/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lazy_static::lazy_static;
use std::sync::LazyLock;

use super::*;
use crate::credssp::CredSspMode;
Expand Down Expand Up @@ -196,40 +196,45 @@ const TS_CREDENTIALS_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: [u8; 25] = [
0x04, 0x00, 0xa2, 0x02, 0x04, 0x00,
];

lazy_static! {
static ref AUTH_IDENTITY_ONE_SYMBOL_USER_AND_PASSWORD: CredentialsBuffers = CredentialsBuffers::AuthIdentity(
static AUTH_IDENTITY_ONE_SYMBOL_USER_AND_PASSWORD: LazyLock<CredentialsBuffers> = LazyLock::new(|| {
CredentialsBuffers::AuthIdentity(
AuthIdentity {
username: Username::parse("a").unwrap(),
password: String::from("1").into(),
}
.into()
);
static ref AUTH_IDENTITY_STRONG_USERNAME_AND_PASSWORD: CredentialsBuffers = CredentialsBuffers::AuthIdentity(
.into(),
)
});
static AUTH_IDENTITY_STRONG_USERNAME_AND_PASSWORD: LazyLock<CredentialsBuffers> = LazyLock::new(|| {
CredentialsBuffers::AuthIdentity(
AuthIdentity {
username: Username::new("QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890", None).unwrap(),
password: String::from(
"@#$%^&*()_+1234567890-=QWERTYUIOP{}qwertyuiop[]asdfghjkl;ASDFGHJKL:\\\"|zxcvbnm,.ZXCVBNM<>?"
"@#$%^&*()_+1234567890-=QWERTYUIOP{}qwertyuiop[]asdfghjkl;ASDFGHJKL:\\\"|zxcvbnm,.ZXCVBNM<>?",
)
.into(),
}
.into()
);
static ref AUTH_IDENTITY_SIMPLE_WITH_USERNAME_AND_DOMAIN_AND_PASSWORD: CredentialsBuffers =
CredentialsBuffers::AuthIdentity(
AuthIdentity {
username: Username::new("Username", Some("Domain")).unwrap(),
password: String::from("Password").into(),
}
.into()
);
static ref AUTH_IDENTITY_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: CredentialsBuffers = CredentialsBuffers::AuthIdentity(
.into(),
)
});
static AUTH_IDENTITY_SIMPLE_WITH_USERNAME_AND_DOMAIN_AND_PASSWORD: LazyLock<CredentialsBuffers> = LazyLock::new(|| {
CredentialsBuffers::AuthIdentity(
AuthIdentity {
username: Username::new("Username", Some("Domain")).unwrap(),
password: String::from("Password").into(),
}
.into(),
)
});
static AUTH_IDENTITY_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: LazyLock<CredentialsBuffers> = LazyLock::new(|| {
CredentialsBuffers::AuthIdentity(
AuthIdentity {
username: Username::new("", Some("")).unwrap(),
password: String::from("").into(),
}
.into()
);
}
.into(),
)
});

#[test]
fn ntlm_decode_first_phase_with_nego_token_and_client_nonce() {
Expand Down
18 changes: 8 additions & 10 deletions src/kerberos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ mod utils;

use std::fmt::Debug;
use std::io::Write;
use std::sync::LazyLock;

pub use encryption_params::EncryptionParams;
use lazy_static::lazy_static;
use picky::key::PrivateKey;
use picky_asn1::restricted_string::IA5String;
use picky_asn1::wrapper::{ExplicitContextTag0, ExplicitContextTag1, OctetStringAsn1, Optional};
Expand Down Expand Up @@ -85,15 +85,13 @@ pub const SECURITY_TRAILER: usize = 60;
/// "The service accepts requests on UDP port 464 and TCP port 464 as well."
const KPASSWD_PORT: u16 = 464;

lazy_static! {
pub static ref PACKAGE_INFO: PackageInfo = PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: 0xbb80, // 48 000 bytes: default maximum token len in Windows
name: SecurityPackageType::Kerberos,
comment: String::from("Kerberos Security Package"),
};
}
pub static PACKAGE_INFO: LazyLock<PackageInfo> = LazyLock::new(|| PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: 0xbb80, // 48 000 bytes: default maximum token len in Windows
name: SecurityPackageType::Kerberos,
comment: String::from("Kerberos Security Package"),
});

#[derive(Debug, Clone)]
pub enum KerberosState {
Expand Down
19 changes: 8 additions & 11 deletions src/negotiate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::fmt::Debug;
use std::net::IpAddr;

use lazy_static::lazy_static;
use std::sync::LazyLock;

use crate::generator::{GeneratorChangePassword, GeneratorInitSecurityContext, YieldPointLocal};
use crate::kdc::detect_kdc_url;
Expand All @@ -19,15 +18,13 @@ use crate::{

pub const PKG_NAME: &str = "Negotiate";

lazy_static! {
pub static ref PACKAGE_INFO: PackageInfo = PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: 0xbb80, // 48 000 bytes: default maximum token len in Windows
name: SecurityPackageType::Negotiate,
comment: String::from("Microsoft Package Negotiator"),
};
}
pub static PACKAGE_INFO: LazyLock<PackageInfo> = LazyLock::new(|| PackageInfo {
capabilities: PackageCapabilities::empty(),
rpc_id: PACKAGE_ID_NONE,
max_token_len: 0xbb80, // 48 000 bytes: default maximum token len in Windows
name: SecurityPackageType::Negotiate,
comment: String::from("Microsoft Package Negotiator"),
});

pub trait ProtocolConfig: Debug + Send + Sync {
fn new_client(&self) -> Result<NegotiatedProtocol>;
Expand Down
Loading

0 comments on commit 3d30a6d

Please sign in to comment.