diff --git a/Cargo.lock b/Cargo.lock index 453bd653..701d1cd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,9 +906,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown", @@ -1148,6 +1148,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.1" @@ -1792,9 +1798,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -2146,6 +2152,7 @@ dependencies = [ "ffi-types", "libc", "num-traits", + "picky-asn1-der", "sspi", "symbol-rename-macro", "tracing", @@ -2153,6 +2160,7 @@ dependencies = [ "whoami", "winapi", "windows-sys 0.48.0", + "winscard", ] [[package]] @@ -2260,13 +2268,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "fe80ced77cbfb4cb91a94bf72b378b4b6791a0d9b7f09d0be747d1bdff4e68bd" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -2282,10 +2291,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -2306,9 +2316,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", diff --git a/crates/ffi-types/src/winscard/functions.rs b/crates/ffi-types/src/winscard/functions.rs index 1d260f9d..60cd30a0 100644 --- a/crates/ffi-types/src/winscard/functions.rs +++ b/crates/ffi-types/src/winscard/functions.rs @@ -8,23 +8,26 @@ use crate::{ Handle, LpByte, LpCByte, LpCGuid, LpCStr, LpCVoid, LpCWStr, LpDword, LpGuid, LpStr, LpUuid, LpVoid, LpWStr, }; -pub type SCardEstablishContextFn = extern "system" fn(u32, *const c_void, *const c_void, LpScardContext) -> ScardStatus; -pub type SCardReleaseContextFn = extern "system" fn(ScardContext) -> ScardStatus; -pub type SCardIsValidContextFn = extern "system" fn(ScardContext) -> ScardStatus; +pub type SCardEstablishContextFn = + unsafe extern "system" fn(u32, *const c_void, *const c_void, LpScardContext) -> ScardStatus; +pub type SCardReleaseContextFn = unsafe extern "system" fn(ScardContext) -> ScardStatus; +pub type SCardIsValidContextFn = unsafe extern "system" fn(ScardContext) -> ScardStatus; pub type SCardListReaderGroupsAFn = extern "system" fn(ScardContext, LpStr, LpDword) -> ScardStatus; pub type SCardListReaderGroupsWFn = extern "system" fn(ScardContext, LpWStr, LpDword) -> ScardStatus; -pub type SCardListReadersAFn = extern "system" fn(ScardContext, LpCStr, LpStr, LpDword) -> ScardStatus; -pub type SCardListReadersWFn = extern "system" fn(ScardContext, LpCWStr, LpWStr, LpDword) -> ScardStatus; -pub type SCardListCardsAFn = extern "system" fn(ScardContext, LpCByte, LpCGuid, u32, *mut u8, LpDword) -> ScardStatus; -pub type SCardListCardsWFn = extern "system" fn(ScardContext, LpCByte, LpCGuid, u32, *mut u16, LpDword) -> ScardStatus; +pub type SCardListReadersAFn = unsafe extern "system" fn(ScardContext, LpCStr, LpStr, LpDword) -> ScardStatus; +pub type SCardListReadersWFn = unsafe extern "system" fn(ScardContext, LpCWStr, LpWStr, LpDword) -> ScardStatus; +pub type SCardListCardsAFn = + unsafe extern "system" fn(ScardContext, LpCByte, LpCGuid, u32, *mut u8, LpDword) -> ScardStatus; +pub type SCardListCardsWFn = + unsafe extern "system" fn(ScardContext, LpCByte, LpCGuid, u32, *mut u16, LpDword) -> ScardStatus; pub type SCardListInterfacesAFn = extern "system" fn(ScardContext, LpCStr, LpGuid, LpDword) -> ScardStatus; pub type SCardListInterfacesWFn = extern "system" fn(ScardContext, LpCWStr, LpGuid, LpDword) -> ScardStatus; pub type SCardGetProviderIdAFn = extern "system" fn(ScardContext, LpCStr, LpGuid) -> ScardStatus; pub type SCardGetProviderIdWFn = extern "system" fn(ScardContext, LpCWStr, LpGuid) -> ScardStatus; pub type SCardGetCardTypeProviderNameAFn = - extern "system" fn(ScardContext, LpCStr, u32, *mut u8, LpDword) -> ScardStatus; + unsafe extern "system" fn(ScardContext, LpCStr, u32, *mut u8, LpDword) -> ScardStatus; pub type SCardGetCardTypeProviderNameWFn = - extern "system" fn(ScardContext, LpCWStr, u32, *mut u16, LpDword) -> ScardStatus; + unsafe extern "system" fn(ScardContext, LpCWStr, u32, *mut u16, LpDword) -> ScardStatus; pub type SCardIntroduceReaderGroupAFn = extern "system" fn(ScardContext, LpCStr) -> ScardStatus; pub type SCardIntroduceReaderGroupWFn = extern "system" fn(ScardContext, LpCWStr) -> ScardStatus; pub type SCardForgetReaderGroupAFn = extern "system" fn(ScardContext, LpCStr) -> ScardStatus; @@ -45,24 +48,30 @@ pub type SCardSetCardTypeProviderNameAFn = extern "system" fn(ScardContext, LpCS pub type SCardSetCardTypeProviderNameWFn = extern "system" fn(ScardContext, LpCWStr, u32, LpCWStr) -> ScardStatus; pub type SCardForgetCardTypeAFn = extern "system" fn(ScardContext, LpCStr) -> ScardStatus; pub type SCardForgetCardTypeWFn = extern "system" fn(ScardContext, LpCWStr) -> ScardStatus; -pub type SCardFreeMemoryFn = extern "system" fn(ScardContext, LpCVoid) -> ScardStatus; +pub type SCardFreeMemoryFn = unsafe extern "system" fn(ScardContext, LpCVoid) -> ScardStatus; pub type SCardAccessStartedEventFn = extern "system" fn() -> Handle; -pub type SCardReleaseStartedEventFn = extern "system" fn() -> c_void; +pub type SCardReleaseStartedEventFn = extern "system" fn(); pub type SCardLocateCardsAFn = extern "system" fn(ScardContext, LpCStr, LpScardReaderStateA, u32) -> ScardStatus; pub type SCardLocateCardsWFn = extern "system" fn(ScardContext, LpCWStr, LpScardReaderStateW, u32) -> ScardStatus; pub type SCardLocateCardsByATRAFn = extern "system" fn(ScardContext, LpScardAtrMask, u32, LpScardReaderStateA, u32) -> ScardStatus; pub type SCardLocateCardsByATRWFn = extern "system" fn(ScardContext, LpScardAtrMask, u32, LpScardReaderStateW, u32) -> ScardStatus; -pub type SCardGetStatusChangeAFn = extern "system" fn(ScardContext, u32, LpScardReaderStateA, u32) -> ScardStatus; -pub type SCardGetStatusChangeWFn = extern "system" fn(ScardContext, u32, LpScardReaderStateW, u32) -> ScardStatus; +pub type SCardGetStatusChangeAFn = + unsafe extern "system" fn(ScardContext, u32, LpScardReaderStateA, u32) -> ScardStatus; +pub type SCardGetStatusChangeWFn = + unsafe extern "system" fn(ScardContext, u32, LpScardReaderStateW, u32) -> ScardStatus; pub type SCardCancelFn = extern "system" fn(ScardContext) -> ScardStatus; -pub type SCardReadCacheAFn = extern "system" fn(ScardContext, LpUuid, u32, LpStr, LpByte, LpDword) -> ScardStatus; -pub type SCardReadCacheWFn = extern "system" fn(ScardContext, LpUuid, u32, LpWStr, LpByte, LpDword) -> ScardStatus; -pub type SCardWriteCacheAFn = extern "system" fn(ScardContext, LpUuid, u32, LpStr, LpByte, u32) -> ScardStatus; -pub type SCardWriteCacheWFn = extern "system" fn(ScardContext, LpUuid, u32, LpWStr, LpByte, u32) -> ScardStatus; -pub type SCardGetReaderIconAFn = extern "system" fn(ScardContext, LpCStr, LpByte, LpDword) -> ScardStatus; -pub type SCardGetReaderIconWFn = extern "system" fn(ScardContext, LpCWStr, LpByte, LpDword) -> ScardStatus; +pub type SCardReadCacheAFn = + unsafe extern "system" fn(ScardContext, LpUuid, u32, LpStr, LpByte, LpDword) -> ScardStatus; +pub type SCardReadCacheWFn = + unsafe extern "system" fn(ScardContext, LpUuid, u32, LpWStr, LpByte, LpDword) -> ScardStatus; +pub type SCardWriteCacheAFn = unsafe extern "system" fn(ScardContext, LpUuid, u32, LpStr, LpByte, u32) -> ScardStatus; +pub type SCardWriteCacheWFn = unsafe extern "system" fn(ScardContext, LpUuid, u32, LpWStr, LpByte, u32) -> ScardStatus; +pub type SCardGetReaderIconAFn = unsafe extern "system" fn(ScardContext, LpCStr, LpByte, LpDword) -> ScardStatus; +pub type SCardGetReaderIconWFn = unsafe extern "system" fn(ScardContext, LpCWStr, LpByte, LpDword) -> ScardStatus; +pub type SCardGetDeviceTypeIdAFn = unsafe extern "system" fn(ScardContext, LpCStr, LpDword) -> ScardStatus; +pub type SCardGetDeviceTypeIdWFn = unsafe extern "system" fn(ScardContext, LpCWStr, LpDword) -> ScardStatus; pub type SCardGetReaderDeviceInstanceIdAFn = extern "system" fn(ScardContext, LpCStr, LpStr, LpDword) -> ScardStatus; pub type SCardGetReaderDeviceInstanceIdWFn = extern "system" fn(ScardContext, LpCWStr, LpWStr, LpDword) -> ScardStatus; pub type SCardListReadersWithDeviceInstanceIdAFn = @@ -70,29 +79,40 @@ pub type SCardListReadersWithDeviceInstanceIdAFn = pub type SCardListReadersWithDeviceInstanceIdWFn = extern "system" fn(ScardContext, LpCWStr, LpWStr, LpDword) -> ScardStatus; pub type SCardAuditFn = extern "system" fn(ScardContext, u32) -> ScardStatus; -pub type SCardConnectAFn = extern "system" fn(ScardContext, LpCStr, u32, u32, LpScardHandle, LpDword) -> ScardStatus; -pub type SCardConnectWFn = extern "system" fn(ScardContext, LpCWStr, u32, u32, LpScardHandle, LpDword) -> ScardStatus; +pub type SCardConnectAFn = + unsafe extern "system" fn(ScardContext, LpCStr, u32, u32, LpScardHandle, LpDword) -> ScardStatus; +pub type SCardConnectWFn = + unsafe extern "system" fn(ScardContext, LpCWStr, u32, u32, LpScardHandle, LpDword) -> ScardStatus; pub type SCardReconnectFn = extern "system" fn(ScardHandle, u32, u32, u32, LpDword) -> ScardStatus; -pub type SCardDisconnectFn = extern "system" fn(ScardHandle, u32) -> ScardStatus; -pub type SCardBeginTransactionFn = extern "system" fn(ScardHandle) -> ScardStatus; -pub type SCardEndTransactionFn = extern "system" fn(ScardHandle, u32) -> ScardStatus; +pub type SCardDisconnectFn = unsafe extern "system" fn(ScardHandle, u32) -> ScardStatus; +pub type SCardBeginTransactionFn = unsafe extern "system" fn(ScardHandle) -> ScardStatus; +pub type SCardEndTransactionFn = unsafe extern "system" fn(ScardHandle, u32) -> ScardStatus; pub type SCardCancelTransactionFn = extern "system" fn(ScardHandle) -> ScardStatus; pub type SCardStateFn = extern "system" fn(ScardHandle, LpDword, LpDword, LpByte, LpDword) -> ScardStatus; pub type SCardStatusAFn = - extern "system" fn(ScardHandle, LpStr, LpDword, LpDword, LpDword, LpByte, LpDword) -> ScardStatus; + unsafe extern "system" fn(ScardHandle, LpStr, LpDword, LpDword, LpDword, LpByte, LpDword) -> ScardStatus; pub type SCardStatusWFn = - extern "system" fn(ScardHandle, LpWStr, LpDword, LpDword, LpDword, LpByte, LpDword) -> ScardStatus; -pub type SCardTransmitFn = - extern "system" fn(ScardHandle, LpScardIoRequest, LpCByte, u32, LpScardIoRequest, LpByte, LpDword) -> ScardStatus; + unsafe extern "system" fn(ScardHandle, LpWStr, LpDword, LpDword, LpDword, LpByte, LpDword) -> ScardStatus; +pub type SCardTransmitFn = unsafe extern "system" fn( + ScardHandle, + LpScardIoRequest, + LpCByte, + u32, + LpScardIoRequest, + LpByte, + LpDword, +) -> ScardStatus; pub type SCardGetTransmitCountFn = extern "system" fn(ScardHandle, LpDword) -> ScardStatus; -pub type SCardControlFn = extern "system" fn(ScardHandle, u32, LpCVoid, u32, LpVoid, u32, LpDword) -> ScardStatus; +pub type SCardControlFn = + unsafe extern "system" fn(ScardHandle, u32, LpCVoid, u32, LpVoid, u32, LpDword) -> ScardStatus; pub type SCardGetAttribFn = extern "system" fn(ScardHandle, u32, LpByte, LpDword) -> ScardStatus; pub type SCardSetAttribFn = extern "system" fn(ScardHandle, u32, LpCByte, u32) -> ScardStatus; pub type SCardUIDlgSelectCardAFn = extern "system" fn(LpOpenCardNameExA) -> ScardStatus; pub type SCardUIDlgSelectCardWFn = extern "system" fn(LpOpenCardNameExW) -> ScardStatus; pub type GetOpenCardNameAFn = extern "system" fn(LpOpenCardNameA) -> ScardStatus; pub type GetOpenCardNameWFn = extern "system" fn(LpOpenCardNameW) -> ScardStatus; -pub type SCardDlgExtendedErrorFn = extern "system" fn() -> i32; +// Not a part of the standard winscard.h API +pub type GetSCardApiFunctionTableFn = extern "system" fn() -> PSCardApiFunctionTable; // https://github.com/FreeRDP/FreeRDP/blob/88f79c5748f4031cb50dfae3ebadcc6619b69f1c/winpr/include/winpr/smartcard.h#L1114 #[repr(C)] @@ -161,13 +181,14 @@ pub struct SCardApiFunctionTable { pub SCardUIDlgSelectCardW: SCardUIDlgSelectCardWFn, pub GetOpenCardNameA: GetOpenCardNameAFn, pub GetOpenCardNameW: GetOpenCardNameWFn, - pub SCardDlgExtendedError: SCardDlgExtendedErrorFn, pub SCardReadCacheA: SCardReadCacheAFn, pub SCardReadCacheW: SCardReadCacheWFn, pub SCardWriteCacheA: SCardWriteCacheAFn, pub SCardWriteCacheW: SCardWriteCacheWFn, pub SCardGetReaderIconA: SCardGetReaderIconAFn, pub SCardGetReaderIconW: SCardGetReaderIconWFn, + pub SCardGetDeviceTypeIdA: SCardGetDeviceTypeIdAFn, + pub SCardGetDeviceTypeIdW: SCardGetDeviceTypeIdWFn, pub SCardGetReaderDeviceInstanceIdA: SCardGetReaderDeviceInstanceIdAFn, pub SCardGetReaderDeviceInstanceIdW: SCardGetReaderDeviceInstanceIdWFn, pub SCardListReadersWithDeviceInstanceIdA: SCardListReadersWithDeviceInstanceIdAFn, diff --git a/crates/ffi-types/src/winscard/mod.rs b/crates/ffi-types/src/winscard/mod.rs index 1a282484..1028781c 100644 --- a/crates/ffi-types/src/winscard/mod.rs +++ b/crates/ffi-types/src/winscard/mod.rs @@ -36,12 +36,12 @@ pub type LpOcnDscProc = Option = result::Result; @@ -125,6 +132,15 @@ impl From for Error { } } +impl From for Error { + fn from(value: TryFromIntError) -> Self { + Error::new( + ErrorKind::InsufficientBuffer, + format!("error: can not convert integers: {}", value), + ) + } +} + /// [Smart Card Return Values](https://learn.microsoft.com/en-us/windows/win32/secauthn/authentication-return-values). #[derive(Debug, PartialEq)] #[repr(u32)] diff --git a/crates/winscard/src/macros.rs b/crates/winscard/src/macros.rs new file mode 100644 index 00000000..838922d1 --- /dev/null +++ b/crates/winscard/src/macros.rs @@ -0,0 +1,10 @@ +macro_rules! env { + ($name:expr) => {{ + std::env::var($name).map_err(|_| { + crate::Error::new( + crate::ErrorKind::InvalidParameter, + format!("The {} env var is not present or invalid", $name), + ) + }) + }}; +} diff --git a/crates/winscard/src/scard.rs b/crates/winscard/src/scard.rs index e1faba59..81ac7208 100644 --- a/crates/winscard/src/scard.rs +++ b/crates/winscard/src/scard.rs @@ -37,7 +37,29 @@ const PIN_LENGTH_RANGE_LOW_BOUND: usize = 6; // NIST.SP.800-73-4 part 2, section 2.4.3 const PIN_LENGTH_RANGE_HIGH_BOUND: usize = 8; -/// Represents an emulated smart card. +/// The original winscard ATR is not suitable because it contains AID bytes. +/// So we need to construct our own. Read more about our constructed ATR string: +/// https://smartcard-atr.apdu.fr/parse?ATR=3B+8D+01+80+FB+A0+00+00+03+08+00+00+10+00+01+00+4D +#[rustfmt::skip] +pub const ATR: [u8; 17] = [ + // TS. Direct Convention + 0x3b, + // T0. Y(1): b1000, K: 13 (historical bytes) + 0x8d, + // TD. Y(i+1) = b0000, Protocol T=1 + 0x01, + // Historical bytes + 0x80, + // Tag: 15, Len: 11. + 0xfb, + // PIV AID + 0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + // TCK (Checksum) + 0x4d, +]; + +/// Emulated smart card. +/// /// Currently, we support one key container per smart card. pub struct SmartCard<'a> { reader_name: Cow<'a, str>, @@ -58,12 +80,28 @@ impl SmartCard<'_> { /// Creates a smart card instance based on the provided data. pub fn new( reader_name: Cow, - mut pin: Vec, + pin: Vec, auth_cert_der: Vec, auth_pk: PrivateKey, ) -> WinScardResult> { let chuid = build_chuid()?; let auth_cert = build_auth_cert(auth_cert_der)?; + + Ok(SmartCard { + reader_name, + chuid, + ccc: build_ccc(), + pin: SmartCard::validate_and_pad_pin(pin)?, + auth_cert, + auth_pk, + state: SCardState::Ready, + transaction: false, + pending_command: None, + pending_response: None, + }) + } + + fn validate_and_pad_pin(mut pin: Vec) -> WinScardResult> { // All PIN requirements can be found here: NIST.SP.800-73-4 part 2, section 2.4.3 if !(PIN_LENGTH_RANGE_LOW_BOUND..=PIN_LENGTH_RANGE_HIGH_BOUND).contains(&pin.len()) { return Err(Error::new( @@ -77,23 +115,14 @@ impl SmartCard<'_> { "PIN should consist only of ASCII values representing decimal digits (0-9)", )); }; + if pin.len() < PIN_LENGTH_RANGE_HIGH_BOUND { // NIST.SP.800-73-4 part 2, section 2.4.3 const PIN_PAD_VALUE: u8 = 0xFF; pin.resize(PIN_LENGTH_RANGE_HIGH_BOUND, PIN_PAD_VALUE); } - Ok(SmartCard { - reader_name, - chuid, - ccc: build_ccc(), - pin, - auth_cert, - auth_pk, - state: SCardState::Ready, - transaction: false, - pending_command: None, - pending_response: None, - }) + + Ok(pin) } /// This functions handles one APDU command. @@ -425,27 +454,7 @@ impl<'a> WinScard for SmartCard<'a> { state: winscard::State::Specific, // We are always using the T1 protocol as the original Windows TPM smart card does protocol: winscard::Protocol::T1, - // The original winscard ATR is not suitable because it contains AID bytes. - // So we need to construct our own. Read more about our constructed ATR string: - // https://smartcard-atr.apdu.fr/parse?ATR=3B+8D+01+80+FB+A0+00+00+03+08+00+00+10+00+01+00+4D - #[rustfmt::skip] - atr: [ - // TS. Direct Convention - 0x3b, - // T0. Y(1): b1000, K: 13 (historical bytes) - 0x8d, - // TD. Y(i+1) = b0000, Protocol T=1 - 0x01, - // Historical bytes - 0x80, - // Tag: 15, Len: 11. - 0xfb, - // PIV AID - 0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - // TCK (Checksum) - 0x4d, - ] - .into(), + atr: ATR.into(), }) } diff --git a/crates/winscard/src/scard_context.rs b/crates/winscard/src/scard_context.rs index 439e0c93..d1d5b9bf 100644 --- a/crates/winscard/src/scard_context.rs +++ b/crates/winscard/src/scard_context.rs @@ -23,7 +23,7 @@ pub struct Reader<'a> { pub device_type_id: DeviceTypeId, } -/// Describes smart card info used for the smart card creation +/// Describes smart card info used for the smart card creation. pub struct SmartCardInfo<'a> { /// Container name which stores the certificate along with its private key. pub container_name: Cow<'a, str>, @@ -38,6 +38,67 @@ pub struct SmartCardInfo<'a> { } impl<'a> SmartCardInfo<'a> { + fn reader_icon() -> &'static [u8] { + include_bytes!("../assets/reader_icon.bmp") + } + + /// Tries to create [SmartCardInfo] structure based on environment variables. + /// Required environment variables are listed in the `env` module of this crate. + #[cfg(feature = "std")] + pub fn try_from_env() -> WinScardResult { + use std::fs; + + use crate::env::{ + WINSCARD_CERT_PATH_ENV, WINSCARD_CONTAINER_NAME_ENV, WINSCARD_PIN_ENV, WINSCARD_PK_PATH_ENV, + WINSCARD_READER_NAME_ENV, + }; + + let container_name = env!(WINSCARD_CONTAINER_NAME_ENV)?.into(); + let reader_name: Cow<'_, str> = env!(WINSCARD_READER_NAME_ENV)?.into(); + let pin = env!(WINSCARD_PIN_ENV)?.into(); + + let cert_path = env!(WINSCARD_CERT_PATH_ENV)?; + let raw_certificate = fs::read(cert_path).map_err(|e| { + Error::new( + ErrorKind::InvalidParameter, + format!("Unable to read certificate from the provided file: {}", e), + ) + })?; + let pk_path = env!(WINSCARD_PK_PATH_ENV)?; + let raw_private_key = fs::read_to_string(pk_path).map_err(|e| { + Error::new( + ErrorKind::InvalidParameter, + format!("Unable to read private key from the provided file: {}", e), + ) + })?; + let private_key = PrivateKey::from_pem_str(&raw_private_key).map_err(|e| { + Error::new( + ErrorKind::InvalidParameter, + format!( + "Error while trying to read a private key from a pem-encoded string: {}", + e + ), + ) + })?; + + // Standard Windows Reader Icon + let icon: &[u8] = Self::reader_icon(); + let reader: Reader<'_> = Reader { + name: reader_name, + icon: Icon::from(icon), + device_type_id: DeviceTypeId::Tpm, + }; + + Ok(Self { + container_name, + pin, + auth_cert_der: raw_certificate, + auth_pk: private_key, + reader, + }) + } + + /// Creates a new [ScardContext] based on the provided data. pub fn new( container_name: Cow<'a, str>, reader_name: Cow<'a, str>, @@ -46,7 +107,7 @@ impl<'a> SmartCardInfo<'a> { auth_pk: PrivateKey, ) -> Self { // Standard Windows Reader Icon - let icon: &[u8] = include_bytes!("../assets/reader_icon.bmp"); + let icon: &[u8] = Self::reader_icon(); let reader: Reader<'_> = Reader { name: reader_name, icon: Icon::from(icon), @@ -63,6 +124,7 @@ impl<'a> SmartCardInfo<'a> { } /// Represents the resource manager context (the scope). +/// /// Currently, we support only one smart card per smart card context. pub struct ScardContext<'a> { smart_card_info: SmartCardInfo<'a>, @@ -324,6 +386,11 @@ impl<'a> ScardContext<'a> { Ok(Self { smart_card_info, cache }) } + + /// Returns available smart card reader name. + pub fn reader_name(&self) -> &str { + self.smart_card_info.reader.name.as_ref() + } } impl<'a> WinScardContext for ScardContext<'a> { diff --git a/crates/winscard/src/winscard.rs b/crates/winscard/src/winscard.rs index aa5f55dd..7f6f312f 100644 --- a/crates/winscard/src/winscard.rs +++ b/crates/winscard/src/winscard.rs @@ -1,13 +1,14 @@ use alloc::borrow::Cow; use alloc::boxed::Box; +use alloc::format; use alloc::string::String; use alloc::vec::Vec; use bitflags::bitflags; -use crate::WinScardResult; +use crate::{Error, ErrorKind, WinScardResult}; -/// ATR string +/// ATR string. /// /// A sequence of bytes returned from a smart card when it is turned on. /// These bytes are used to identify the card to the system. @@ -66,6 +67,7 @@ impl From> for Icon<'_> { /// [SCARD_READER_CAPABILITIES](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/smclib/ns-smclib-_scard_reader_capabilities) /// `ReaderType` parameter: /// This member contains the reader type and is required. This member can have one of the values in the following table. +#[repr(u32)] #[derive(Debug, Copy, Clone)] pub enum DeviceTypeId { /// Serial reader @@ -88,6 +90,12 @@ pub enum DeviceTypeId { Vendor = 0xf0, } +impl From for u32 { + fn from(value: DeviceTypeId) -> Self { + value as u32 + } +} + /// [SCardConnectW](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardconnectw) /// /// `dwShareMode` parameter: @@ -104,6 +112,22 @@ pub enum ShareMode { Direct = 3, } +impl TryFrom for ShareMode { + type Error = Error; + + fn try_from(value: u32) -> Result { + match value { + 1 => Ok(Self::Exclusive), + 2 => Ok(Self::Shared), + 3 => Ok(Self::Direct), + _ => Err(Error::new( + ErrorKind::InvalidParameter, + format!("Invalid ShareMode value: {}", value), + )), + } + } +} + bitflags! { /// [SCardConnectW](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardconnectw) /// @@ -133,6 +157,7 @@ bitflags! { /// `pdwState` parameter: /// Current state of the smart card in the reader. Upon success, it receives one of the following state indicators. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u32)] pub enum State { /// Unknown smart card status. Unknown = 0, @@ -150,6 +175,12 @@ pub enum State { Specific = 6, } +impl From for u32 { + fn from(value: State) -> Self { + value as u32 + } +} + /// This structure described the current status and basic info about the smart card reader. #[derive(Debug, Clone)] pub struct Status<'a> { @@ -176,6 +207,20 @@ pub enum ControlCode { IoCtl = 0x00313520, } +impl TryFrom for ControlCode { + type Error = Error; + + fn try_from(value: u32) -> WinScardResult { + match value { + 0x00313520 => Ok(ControlCode::IoCtl), + _ => Err(Error::new( + ErrorKind::InvalidParameter, + format!("Unsupported control code: {:x?}", value), + )), + } + } +} + /// [SCARD_IO_REQUEST](https://learn.microsoft.com/en-us/windows/win32/secauthn/scard-io-request) /// /// The SCARD_IO_REQUEST structure begins a protocol control information structure. @@ -198,7 +243,8 @@ pub struct IoRequest { /// This structure represents the result of the `SCardTransmit` function. #[derive(Debug, Clone)] pub struct TransmitOutData { - /// Output APDU command. + /// Data returned from the card. If no data is returned from the card, + /// then this buffer will only contain the SW1 and SW2 status bytes. pub output_apdu: Vec, /// Returned protocol control information (PCI) specific to the protocol in use. pub receive_pci: Option, diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 9d6eaf56..bc22c62b 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib"] [features] default = [] tsssp = ["sspi/tsssp"] -scard = ["sspi/scard", "dep:ffi-types"] +scard = ["sspi/scard", "dep:ffi-types", "dep:winscard"] [dependencies] cfg-if = "1" @@ -24,6 +24,9 @@ num-traits = "0.2" whoami = "1.4" sspi = { path = "..", features = ["network_client", "dns_resolver"] } ffi-types = { path = "../crates/ffi-types", features = ["winscard"], optional = true } +picky-asn1-der = "0.4" + +winscard = { path = "../crates/winscard", features = ["std"], optional = true } # logging tracing = { version = "0.1" } diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 7eccba91..ef19f680 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -8,4 +8,5 @@ pub mod logging; pub mod sspi; mod utils; #[cfg(feature = "scard")] +#[deny(unsafe_op_in_unsafe_fn)] pub mod winscard; diff --git a/ffi/src/sspi/sec_pkg_info.rs b/ffi/src/sspi/sec_pkg_info.rs index 473f8fbe..74ba3ea6 100644 --- a/ffi/src/sspi/sec_pkg_info.rs +++ b/ffi/src/sspi/sec_pkg_info.rs @@ -7,7 +7,7 @@ use sspi::{enumerate_security_packages, PackageInfo, KERBEROS_VERSION}; use symbol_rename_macro::rename_symbol; use super::sspi_data_types::{SecChar, SecWChar, SecurityStatus}; -use crate::utils::c_w_str_to_string; +use crate::utils::{c_w_str_to_string, str_to_w_buff}; #[derive(Debug)] #[repr(C)] @@ -25,14 +25,10 @@ pub type PSecPkgInfoW = *mut SecPkgInfoW; #[allow(clippy::useless_conversion)] impl From for &mut SecPkgInfoW { fn from(pkg_info: PackageInfo) -> Self { - let mut pkg_name = pkg_info.name.to_string().encode_utf16().collect::>(); - // null-terminator - pkg_name.push(0); + let pkg_name = str_to_w_buff(pkg_info.name.as_ref()); let name_bytes_len = pkg_name.len() * 2; - let mut pkg_comment = pkg_info.comment.encode_utf16().collect::>(); - // null-terminator - pkg_comment.push(0); + let pkg_comment = str_to_w_buff(&pkg_info.comment); let comment_bytes_len = pkg_comment.len() * 2; let pkg_info_w_size = size_of::(); @@ -225,12 +221,8 @@ pub unsafe extern "system" fn EnumerateSecurityPackagesW( let mut comments = Vec::with_capacity(packages.len()); for package in &packages { - let mut name = package.name.to_string().encode_utf16().collect::>(); - // null-terminator - name.push(0); - let mut comment = package.comment.encode_utf16().collect::>(); - // null-terminator - comment.push(0); + let name = str_to_w_buff(package.name.as_ref()); + let comment = str_to_w_buff(&package.comment); size += (name.len() + comment.len()) * 2; diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 14334bb5..94a42890 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -9,13 +9,20 @@ pub fn into_raw_ptr(value: T) -> *mut T { pub unsafe fn c_w_str_to_string(s: *const u16) -> String { let mut len = 0; - while *(s.add(len)) != 0 { + while unsafe { *(s.add(len)) } != 0 { len += 1; } - String::from_utf16_lossy(from_raw_parts(s, len)) + String::from_utf16_lossy(unsafe { from_raw_parts(s, len) }) } pub unsafe fn raw_str_into_bytes(raw_buffer: *const c_char, len: usize) -> Vec { - from_raw_parts(raw_buffer, len).iter().map(|c| *c as u8).collect() + unsafe { from_raw_parts(raw_buffer, len) } + .iter() + .map(|c| *c as u8) + .collect() +} + +pub fn str_to_w_buff(data: &str) -> Vec { + data.encode_utf16().chain(std::iter::once(0)).collect() } diff --git a/ffi/src/winscard/buf_alloc.rs b/ffi/src/winscard/buf_alloc.rs new file mode 100644 index 00000000..a8184058 --- /dev/null +++ b/ffi/src/winscard/buf_alloc.rs @@ -0,0 +1,121 @@ +use std::iter::once; +use std::slice::from_raw_parts_mut; + +use ffi_types::{LpByte, LpDword, LpStr, LpWStr}; +use winscard::{Error, ErrorKind, WinScardResult}; + +use super::scard_handle::WinScardContextHandle; +use crate::utils::str_to_w_buff; + +pub const SCARD_AUTOALLOCATE: u32 = 0xffffffff; + +pub unsafe fn copy_buff( + context: &mut WinScardContextHandle, + raw_buff: LpByte, + raw_buff_len: LpDword, + buff_to_copy: &[u8], +) -> WinScardResult<()> { + let buff_to_copy_len = buff_to_copy.len().try_into()?; + + if raw_buff.is_null() { + unsafe { + *raw_buff_len = buff_to_copy_len; + } + return Ok(()); + } + + if unsafe { *raw_buff_len } == SCARD_AUTOALLOCATE { + // allocate a new buffer and write an address into raw_buff + let allocated = context.allocate_buffer(buff_to_copy.len())?; + unsafe { + *(raw_buff as *mut *mut u8) = allocated; + *raw_buff_len = buff_to_copy_len; + from_raw_parts_mut(allocated, buff_to_copy.len()).copy_from_slice(buff_to_copy); + } + } else { + if buff_to_copy_len > unsafe { *raw_buff_len } { + return Err(Error::new( + ErrorKind::InsufficientBuffer, + format!( + "expected at least {} bytes but got {}.", + buff_to_copy_len, *raw_buff_len + ), + )); + } + unsafe { + *raw_buff_len = buff_to_copy_len; + from_raw_parts_mut(raw_buff, buff_to_copy.len()).copy_from_slice(buff_to_copy); + } + } + + Ok(()) +} + +pub unsafe fn copy_w_buff( + context: &mut WinScardContextHandle, + raw_buf: LpWStr, + raw_buf_len: LpDword, + buff_to_copy: &[u16], +) -> WinScardResult<()> { + let buff_to_copy_len = buff_to_copy.len().try_into()?; + + if raw_buf.is_null() { + unsafe { + *raw_buf_len = buff_to_copy_len; + } + return Ok(()); + } + + if unsafe { *raw_buf_len } == SCARD_AUTOALLOCATE { + // allocate a new buffer and write an address into raw_buff + let allocated = context.allocate_buffer(buff_to_copy.len() * 2)? as *mut u16; + unsafe { + *(raw_buf as *mut *mut u16) = allocated; + *raw_buf_len = buff_to_copy_len; + from_raw_parts_mut(allocated, buff_to_copy.len()).copy_from_slice(buff_to_copy); + } + } else { + if buff_to_copy_len > unsafe { *raw_buf_len } { + return Err(Error::new( + ErrorKind::InsufficientBuffer, + format!("expected at least {} bytes but got {}.", buff_to_copy_len, *raw_buf_len), + )); + } + unsafe { + *raw_buf_len = buff_to_copy_len; + from_raw_parts_mut(raw_buf, buff_to_copy.len()).copy_from_slice(buff_to_copy); + } + } + + Ok(()) +} + +pub unsafe fn write_multistring_a( + context: &mut WinScardContextHandle, + strings: &[&str], + dest: LpStr, + dest_len: LpDword, +) -> WinScardResult<()> { + let buffer: Vec = strings + .iter() + .flat_map(|reader| reader.as_bytes().iter().cloned().chain(once(0))) + .chain(once(0)) + .collect(); + + unsafe { copy_buff(context, dest, dest_len, &buffer) } +} + +pub unsafe fn write_multistring_w( + context: &mut WinScardContextHandle, + strings: &[&str], + dest: LpWStr, + dest_len: LpDword, +) -> WinScardResult<()> { + let buffer: Vec = strings + .iter() + .flat_map(|reader| str_to_w_buff(reader)) + .chain(once(0)) + .collect(); + + unsafe { copy_w_buff(context, dest, dest_len, &buffer) } +} diff --git a/ffi/src/winscard/macros.rs b/ffi/src/winscard/macros.rs new file mode 100644 index 00000000..3dc07986 --- /dev/null +++ b/ffi/src/winscard/macros.rs @@ -0,0 +1,36 @@ +macro_rules! check_handle { + ($x:expr) => {{ + if $x == 0 { + return u32::from(winscard::ErrorKind::InvalidHandle); + } + }}; +} + +macro_rules! try_execute { + ($x:expr) => {{ + match $x { + Ok(value) => value, + Err(err) => { + error!(%err, "an error occurred"); + return u32::from(err.error_kind); + } + } + }}; + ($x:expr, $err_value:expr) => {{ + match $x { + Ok(val) => val, + Err(err) => { + error!(%err, "an error occurred"); + return $err_value.into(); + } + } + }}; +} + +macro_rules! check_null { + ($x:expr) => {{ + if $x.is_null() { + return u32::from(winscard::ErrorKind::InvalidParameter); + } + }}; +} diff --git a/ffi/src/winscard/mod.rs b/ffi/src/winscard/mod.rs index 0f402f20..05cca4aa 100644 --- a/ffi/src/winscard/mod.rs +++ b/ffi/src/winscard/mod.rs @@ -1,11 +1,37 @@ use ffi_types::winscard::functions::{PSCardApiFunctionTable, SCardApiFunctionTable}; +use ffi_types::winscard::ScardIoRequest; +use winscard::winscard::Protocol; use self::scard::*; use self::scard_context::*; use crate::utils::into_raw_ptr; +#[macro_use] +mod macros; +mod buf_alloc; pub mod scard; pub mod scard_context; +mod scard_handle; + +// The constants below are not documented anywhere and were discovered during debugging. +// Related example: https://github.com/bluetech/pcsc-rust/blob/b397cc8e3834a1dc791631105f37f34d321c8696/pcsc/src/lib.rs#L605-L613 +#[no_mangle] +pub static Rust_g_rgSCardT1Pci: ScardIoRequest = ScardIoRequest { + dw_protocol: Protocol::T1.bits(), + cb_pci_length: 8, +}; + +#[no_mangle] +pub static Rust_g_rgSCardT0Pci: ScardIoRequest = ScardIoRequest { + dw_protocol: Protocol::T0.bits(), + cb_pci_length: 8, +}; + +#[no_mangle] +pub static Rust_g_rgSCardRawPci: ScardIoRequest = ScardIoRequest { + dw_protocol: Protocol::Raw.bits(), + cb_pci_length: 8, +}; pub extern "system" fn GetSCardApiFunctionTable() -> PSCardApiFunctionTable { crate::logging::setup_logger(); @@ -74,13 +100,14 @@ pub extern "system" fn GetSCardApiFunctionTable() -> PSCardApiFunctionTable { SCardUIDlgSelectCardW, GetOpenCardNameA, GetOpenCardNameW, - SCardDlgExtendedError, SCardReadCacheA, SCardReadCacheW, SCardWriteCacheA, SCardWriteCacheW, SCardGetReaderIconA, SCardGetReaderIconW, + SCardGetDeviceTypeIdA, + SCardGetDeviceTypeIdW, SCardGetReaderDeviceInstanceIdA, SCardGetReaderDeviceInstanceIdW, SCardListReadersWithDeviceInstanceIdA, diff --git a/ffi/src/winscard/scard.rs b/ffi/src/winscard/scard.rs index 3d0adb02..7130afeb 100644 --- a/ffi/src/winscard/scard.rs +++ b/ffi/src/winscard/scard.rs @@ -1,37 +1,119 @@ +use std::ffi::CStr; +use std::slice::{from_raw_parts, from_raw_parts_mut}; + use ffi_types::winscard::{ LpOpenCardNameA, LpOpenCardNameExA, LpOpenCardNameExW, LpOpenCardNameW, LpScardHandle, LpScardIoRequest, ScardContext, ScardHandle, ScardStatus, }; use ffi_types::{LpByte, LpCByte, LpCStr, LpCVoid, LpCWStr, LpDword, LpStr, LpVoid, LpWStr}; use symbol_rename_macro::rename_symbol; +use winscard::winscard::Protocol; +use winscard::{ErrorKind, WinScardResult}; + +use super::buf_alloc::{copy_buff, write_multistring_a, write_multistring_w}; +use crate::utils::{c_w_str_to_string, into_raw_ptr}; +use crate::winscard::scard_handle::{ + copy_io_request_to_scard_io_request, scard_context_to_winscard_context, scard_handle_to_winscard, + scard_io_request_to_io_request, WinScardContextHandle, WinScardHandle, +}; + +unsafe fn connect( + context: ScardContext, + reader_name: &str, + dw_share_mode: u32, + dw_preferred_protocols: u32, + ph_card: LpScardHandle, + pdw_active_protocol: LpDword, +) -> WinScardResult<()> { + let share_mode = dw_share_mode.try_into()?; + let protocol = Protocol::from_bits(dw_preferred_protocols); + + let scard_context = unsafe { scard_context_to_winscard_context(context)? }; + let scard = scard_context.connect(reader_name, share_mode, protocol)?; + let protocol = scard.status()?.protocol.bits(); + + let scard = WinScardHandle::new(scard, context); + + let raw_card_handle = into_raw_ptr(scard) as ScardHandle; + + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + context.add_scard(raw_card_handle)?; + + unsafe { + *ph_card = raw_card_handle; + *pdw_active_protocol = protocol; + } + + Ok(()) +} #[cfg_attr(windows, rename_symbol(to = "Rust_SCardConnectA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardConnectA( - _context: ScardContext, - _sz_reader: LpCStr, - _dw_share_mode: u32, - _dw_preferred_protocols: u32, - _ph_card: LpScardHandle, - _pdw_active_protocol: LpDword, +pub unsafe extern "system" fn SCardConnectA( + context: ScardContext, + sz_reader: LpCStr, + dw_share_mode: u32, + dw_preferred_protocols: u32, + ph_card: LpScardHandle, + pdw_active_protocol: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(sz_reader); + check_null!(ph_card); + check_null!(pdw_active_protocol); + + let reader_name = try_execute!( + unsafe { CStr::from_ptr(sz_reader as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + + try_execute!(connect( + context, + &reader_name, + dw_share_mode, + dw_preferred_protocols, + ph_card, + pdw_active_protocol + )); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardConnectW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardConnectW( - _context: ScardContext, - _sz_reader: LpCWStr, - _dw_share_mode: u32, - _dw_preferred_protocols: u32, - _ph_card: LpScardHandle, - _pdw_active_protocol: LpDword, +pub unsafe extern "system" fn SCardConnectW( + context: ScardContext, + sz_reader: LpCWStr, + dw_share_mode: u32, + dw_preferred_protocols: u32, + ph_card: LpScardHandle, + pdw_active_protocol: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(sz_reader); + check_null!(ph_card); + check_null!(pdw_active_protocol); + + let reader_name = unsafe { c_w_str_to_string(sz_reader) }; + + try_execute!(unsafe { + connect( + context, + &reader_name, + dw_share_mode, + dw_preferred_protocols, + ph_card, + pdw_active_protocol, + ) + }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardReconnect"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardReconnect( _handle: ScardHandle, @@ -40,34 +122,60 @@ pub extern "system" fn SCardReconnect( _dw_initialization: u32, _pdw_active_protocol: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardDisconnect"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardDisconnect(_handle: ScardHandle, _dw_disposition: u32) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardDisconnect(handle: ScardHandle, _dw_disposition: u32) -> ScardStatus { + check_handle!(handle); + + let scard = unsafe { Box::from_raw(handle as *mut WinScardHandle) }; + if let Some(context) = unsafe { (scard.context() as *mut WinScardContextHandle).as_mut() } { + if context.remove_scard(handle) { + info!(?handle, "Successfully disconnected!"); + } else { + warn!("ScardHandle does not belong to the specified context.") + } + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardBeginTransaction"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardBeginTransaction(_handle: ScardHandle) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardBeginTransaction(handle: ScardHandle) -> ScardStatus { + check_handle!(handle); + let scard = try_execute!(unsafe { scard_handle_to_winscard(handle) }); + + try_execute!(scard.begin_transaction()); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardEndTransaction"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardEndTransaction(_handle: ScardHandle, _dw_disposition: u32) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardEndTransaction(handle: ScardHandle, _dw_disposition: u32) -> ScardStatus { + check_handle!(handle); + let scard = try_execute!(unsafe { scard_handle_to_winscard(handle) }); + + try_execute!(scard.end_transaction()); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardCancelTransaction"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardCancelTransaction(_handle: ScardHandle) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardState"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardState( _handle: ScardHandle, @@ -76,72 +184,190 @@ pub extern "system" fn SCardState( _pb_atr: LpByte, _pcb_atr_len: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardStatusA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardStatusA( - _handle: ScardHandle, - _msz_reader_names: LpStr, - _pcch_reader_len: LpDword, - _pdw_state: LpDword, - _pdw_protocol: LpDword, - _pb_atr: LpByte, - _pcb_atr_len: LpDword, +pub unsafe extern "system" fn SCardStatusA( + handle: ScardHandle, + msz_reader_names: LpStr, + pcch_reader_len: LpDword, + pdw_state: LpDword, + pdw_protocol: LpDword, + pb_atr: LpByte, + pcb_atr_len: LpDword, ) -> ScardStatus { - todo!() + check_handle!(handle); + check_null!(msz_reader_names); + check_null!(pcch_reader_len); + check_null!(pdw_state); + check_null!(pdw_protocol); + // pb_atr can be null. + // it's not specified in a docs, but `msclmd.dll` can invoke this function with pb_atr = 0. + check_null!(pcb_atr_len); + + let scard = unsafe { (handle as *mut WinScardHandle).as_ref().unwrap() }; + let status = try_execute!(scard.scard().status()); + check_handle!(scard.context()); + + let readers = status.readers.iter().map(|reader| reader.as_ref()).collect::>(); + let context = unsafe { (scard.context() as *mut WinScardContextHandle).as_mut() }.unwrap(); + try_execute!(unsafe { write_multistring_a(context, &readers, msz_reader_names, pcch_reader_len) }); + unsafe { + *pdw_state = status.state.into(); + *pdw_protocol = status.protocol.bits(); + } + + if !pb_atr.is_null() { + try_execute!(unsafe { copy_buff(context, pb_atr, pcb_atr_len, status.atr.as_ref()) }); + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardStatusW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardStatusW( - _handle: ScardHandle, - _msz_reader_names: LpWStr, - _pcch_reader_len: LpDword, - _pdw_state: LpDword, - _pdw_protocol: LpDword, - _pb_atr: LpByte, - _pcb_atr_len: LpDword, +pub unsafe extern "system" fn SCardStatusW( + handle: ScardHandle, + msz_reader_names: LpWStr, + pcch_reader_len: LpDword, + pdw_state: LpDword, + pdw_protocol: LpDword, + pb_atr: LpByte, + pcb_atr_len: LpDword, ) -> ScardStatus { - todo!() + check_handle!(handle); + check_null!(msz_reader_names); + check_null!(pcch_reader_len); + check_null!(pdw_state); + check_null!(pdw_protocol); + // pb_atr can be null. + // it's not specified in a docs, but `msclmd.dll` can invoke this function with pb_atr = 0. + check_null!(pcb_atr_len); + + let scard = unsafe { (handle as *mut WinScardHandle).as_ref() }.unwrap(); + let status = try_execute!(scard.scard().status()); + check_handle!(scard.context()); + + let readers = status.readers.iter().map(|reader| reader.as_ref()).collect::>(); + let context = unsafe { (scard.context() as *mut WinScardContextHandle).as_mut() }.unwrap(); + try_execute!(unsafe { write_multistring_w(context, &readers, msz_reader_names, pcch_reader_len) }); + unsafe { + *pdw_state = status.state.into(); + *pdw_protocol = status.protocol.bits(); + } + + if !pb_atr.is_null() { + try_execute!(unsafe { copy_buff(context, pb_atr, pcb_atr_len, status.atr.as_ref()) }); + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardTransmit"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardTransmit( - _handle: ScardHandle, - _pio_send_pci: LpScardIoRequest, - _pb_send_buffer: LpCByte, - _cb_send_length: u32, - _pio_recv_pci: LpScardIoRequest, - _pb_recv_buffer: LpByte, - _pcb_recv_length: LpDword, +pub unsafe extern "system" fn SCardTransmit( + handle: ScardHandle, + pio_send_pci: LpScardIoRequest, + pb_send_buffer: LpCByte, + cb_send_length: u32, + pio_recv_pci: LpScardIoRequest, + pb_recv_buffer: LpByte, + pcb_recv_length: LpDword, ) -> ScardStatus { - todo!() + check_handle!(handle); + check_null!(pio_send_pci); + let scard = try_execute!(unsafe { scard_handle_to_winscard(handle) }); + + let io_request = try_execute!(unsafe { scard_io_request_to_io_request(pio_send_pci) }); + let input_apdu = unsafe { + from_raw_parts( + pb_send_buffer, + try_execute!(cb_send_length.try_into(), ErrorKind::InsufficientBuffer), + ) + }; + + let out_data = try_execute!(scard.transmit(io_request, input_apdu)); + + let out_apdu_len = out_data.output_apdu.len(); + if out_apdu_len > try_execute!(unsafe { *pcb_recv_length }.try_into(), ErrorKind::InsufficientBuffer) + || pb_recv_buffer.is_null() + { + return ErrorKind::InsufficientBuffer.into(); + } + + let recv_buffer = unsafe { from_raw_parts_mut(pb_recv_buffer, out_apdu_len) }; + recv_buffer.copy_from_slice(&out_data.output_apdu); + + if !pio_recv_pci.is_null() && out_data.receive_pci.is_some() { + try_execute!(unsafe { + copy_io_request_to_scard_io_request(out_data.receive_pci.as_ref().unwrap(), pio_recv_pci) + }); + } + + unsafe { + *pcb_recv_length = try_execute!(out_apdu_len.try_into(), ErrorKind::InsufficientBuffer); + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetTransmitCount"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetTransmitCount(_handle: ScardHandle, pc_transmit_count: LpDword) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardControl"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardControl( - _handle: ScardHandle, - _dw_control_code: u32, - _lp_in_buffer: LpCVoid, - _cb_in_buffer_size: u32, - _lp_out_buffer: LpVoid, - _cb_out_buffer_size: u32, - _lp_bytes_returned: LpDword, +pub unsafe extern "system" fn SCardControl( + handle: ScardHandle, + dw_control_code: u32, + lp_in_buffer: LpCVoid, + cb_in_buffer_size: u32, + lp_out_buffer: LpVoid, + cb_out_buffer_size: u32, + lp_bytes_returned: LpDword, ) -> ScardStatus { - todo!() + check_handle!(handle); + let scard = try_execute!(unsafe { scard_handle_to_winscard(handle) }); + + let in_buffer = if !lp_in_buffer.is_null() { + unsafe { + from_raw_parts( + lp_in_buffer as *const u8, + try_execute!(cb_in_buffer_size.try_into(), ErrorKind::InsufficientBuffer), + ) + } + } else { + &[] + }; + let out_buffer = try_execute!(scard.control(try_execute!(dw_control_code.try_into()), in_buffer)); + let out_buffer_len = try_execute!(out_buffer.len().try_into(), ErrorKind::InsufficientBuffer); + + if !lp_out_buffer.is_null() { + if out_buffer_len > cb_out_buffer_size { + return ErrorKind::InsufficientBuffer.into(); + } + + let lp_out_buffer = unsafe { from_raw_parts_mut(lp_out_buffer as *mut u8, out_buffer.len()) }; + lp_out_buffer.copy_from_slice(&out_buffer); + unsafe { + *lp_bytes_returned = out_buffer_len; + } + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetAttrib"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetAttrib( _handle: ScardHandle, @@ -149,10 +375,11 @@ pub extern "system" fn SCardGetAttrib( _pb_attr: LpByte, _pcb_attrLen: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardSetAttrib"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardSetAttrib( _handle: ScardHandle, @@ -160,35 +387,33 @@ pub extern "system" fn SCardSetAttrib( _pb_attr: LpCByte, _cb_attrLen: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardUIDlgSelectCardA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardUIDlgSelectCardA(_p: LpOpenCardNameExA) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardUIDlgSelectCardW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardUIDlgSelectCardW(_p: LpOpenCardNameExW) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_GetOpenCardNameA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn GetOpenCardNameA(_p: LpOpenCardNameA) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_GetOpenCardNameW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn GetOpenCardNameW(_p: LpOpenCardNameW) -> ScardStatus { - todo!() -} - -#[cfg_attr(windows, rename_symbol(to = "Rust_SCardDlgExtendedError"))] -#[no_mangle] -pub extern "system" fn SCardDlgExtendedError() -> i32 { - todo!() + ErrorKind::UnsupportedFeature.into() } diff --git a/ffi/src/winscard/scard_context.rs b/ffi/src/winscard/scard_context.rs index 102537fc..31d836ef 100644 --- a/ffi/src/winscard/scard_context.rs +++ b/ffi/src/winscard/scard_context.rs @@ -1,31 +1,97 @@ +use std::borrow::Cow; +use std::ffi::CStr; +use std::slice::from_raw_parts_mut; + use ffi_types::winscard::{ LpScardAtrMask, LpScardContext, LpScardReaderStateA, LpScardReaderStateW, ScardContext, ScardStatus, }; use ffi_types::{Handle, LpByte, LpCByte, LpCGuid, LpCStr, LpCVoid, LpCWStr, LpDword, LpGuid, LpStr, LpUuid, LpWStr}; use libc::c_void; use symbol_rename_macro::rename_symbol; +use winscard::winscard::WinScardContext; +use winscard::{ErrorKind, ScardContext as PivCardContext, SmartCardInfo, WinScardResult, ATR}; + +use super::buf_alloc::{copy_w_buff, write_multistring_a, write_multistring_w}; +use crate::utils::{c_w_str_to_string, into_raw_ptr, str_to_w_buff}; +use crate::winscard::buf_alloc::copy_buff; +use crate::winscard::scard_handle::{scard_context_to_winscard_context, WinScardContextHandle}; + +const SCARD_STATE_CHANGED: u32 = 0x00000002; +const SCARD_STATE_INUSE: u32 = 0x00000100; +const SCARD_STATE_PRESENT: u32 = 0x00000020; +// Undocumented constant that appears in all API captures +const SCARD_STATE_UNNAMED_CONSTANT: u32 = 0x00010000; + +// https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardgetcardtypeprovidernamew +// `dwProviderId` function parameter:: +// The function retrieves the name of the smart card's primary service provider as a GUID string. +const SCARD_PROVIDER_PRIMARY: u32 = 1; +// The function retrieves the name of the cryptographic service provider. +const SCARD_PROVIDER_CSP: u32 = 2; +// The function retrieves the name of the smart card key storage provider (KSP). +const SCARD_PROVIDER_KSP: u32 = 3; +// The function retrieves the name of the card module. +const SCARD_PROVIDER_CARD_MODULE: u32 = 0x80000001; + +const MICROSOFT_DEFAULT_CSP: &str = "Microsoft Base Smart Card Crypto Provider"; +const MICROSOFT_DEFAULT_KSP: &str = "Microsoft Smart Card Key Storage Provider"; +const MICROSOFT_SCARD_DRIVER_LOCATION: &str = "C:\\Windows\\System32\\msclmd.dll"; + +const DEFAULT_CARD_NAME: &str = "Cool card"; + +// https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardgetstatuschangew +// To be notified of the arrival of a new smart card reader, +// set the szReader member of a SCARD_READERSTATE structure to "\\?PnP?\Notification", +const NEW_READER_NOTIFICATION: &str = "\\\\?PnP?\\Notification"; #[cfg_attr(windows, rename_symbol(to = "Rust_SCardEstablishContext"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardEstablishContext( +pub unsafe extern "system" fn SCardEstablishContext( _dw_scope: u32, _r1: *const c_void, _r2: *const c_void, - _context: LpScardContext, + context: LpScardContext, ) -> ScardStatus { - todo!() + crate::logging::setup_logger(); + check_null!(context); + + let scard_info = try_execute!(SmartCardInfo::try_from_env()); + // We have only one available reader + let scard_context: Box = Box::new(try_execute!(PivCardContext::new(scard_info))); + + let scard_context = WinScardContextHandle::with_scard_context(scard_context); + + let raw_ptr = into_raw_ptr(scard_context) as ScardContext; + info!(new_established_context = ?raw_ptr); + unsafe { + *context = raw_ptr; + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardReleaseContext"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardReleaseContext(_context: ScardContext) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardReleaseContext(context: ScardContext) -> ScardStatus { + check_handle!(context); + + let _ = unsafe { Box::from_raw(context as *mut WinScardContextHandle) }; + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIsValidContext"))] #[no_mangle] -pub extern "system" fn SCardIsValidContext(_context: ScardContext) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardIsValidContext(context: ScardContext) -> ScardStatus { + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + + if context.is_valid() { + ErrorKind::Success.into() + } else { + ErrorKind::InvalidHandle.into() + } } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReaderGroupsA"))] @@ -35,7 +101,7 @@ pub extern "system" fn SCardListReaderGroupsA( _gmsz_groups: LpStr, _pcch_groups: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReaderGroupsW"))] @@ -45,58 +111,104 @@ pub extern "system" fn SCardListReaderGroupsW( _gmsz_groups: LpWStr, _pcch_groups: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReadersA"))] #[no_mangle] -pub extern "system" fn SCardListReadersA( - _context: ScardContext, +pub unsafe extern "system" fn SCardListReadersA( + context: ScardContext, _msz_groups: LpCStr, - _msz_readers: LpStr, - _pcch_readers: LpDword, + msz_readers: LpStr, + pcch_readers: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(msz_readers); + check_null!(pcch_readers); + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + let readers = context.scard_context().list_readers(); + let readers = readers.iter().map(|reader| reader.to_string()).collect::>(); + let readers = readers.iter().map(|reader| reader.as_ref()).collect::>(); + + try_execute!(unsafe { write_multistring_a(context, &readers, msz_readers, pcch_readers) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReadersW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardListReadersW( - _context: ScardContext, +pub unsafe extern "system" fn SCardListReadersW( + context: ScardContext, _msz_groups: LpCWStr, - _msz_readers: LpWStr, - _pcch_readers: LpDword, + msz_readers: LpWStr, + pcch_readers: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(msz_readers); + check_null!(pcch_readers); + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + let readers = context.scard_context().list_readers(); + let readers = readers.iter().map(|reader| reader.to_string()).collect::>(); + let readers = readers.iter().map(|reader| reader.as_ref()).collect::>(); + + try_execute!(unsafe { write_multistring_w(context, &readers, msz_readers, pcch_readers) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListCardsA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardListCardsA( - _context: ScardContext, +pub unsafe extern "system" fn SCardListCardsA( + context: ScardContext, _pb_atr: LpCByte, _rgquid_nterfaces: LpCGuid, _cguid_interface_count: u32, - _msz_cards: *mut u8, - _pcch_cards: LpDword, + msz_cards: *mut u8, + pcch_cards: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(msz_cards); + check_null!(pcch_cards); + + // safe: checked above + let context = (context as *mut WinScardContextHandle).as_mut().unwrap(); + // we have only one smart card with only one default name + try_execute!(unsafe { write_multistring_a(context, &[DEFAULT_CARD_NAME], msz_cards, pcch_cards) }); + + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListCardsW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardListCardsW( - _context: ScardContext, +pub unsafe extern "system" fn SCardListCardsW( + context: ScardContext, _pb_atr: LpCByte, _rgquid_nterfaces: LpCGuid, _cguid_interface_count: u32, - _msz_cards: *mut u16, - _pcch_cards: LpDword, + msz_cards: *mut u16, + pcch_cards: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(msz_cards); + check_null!(pcch_cards); + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + // we have only one smart card with only one default name + try_execute!(unsafe { write_multistring_w(context, &[DEFAULT_CARD_NAME], msz_cards, pcch_cards) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListInterfacesA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardListInterfacesA( _context: ScardContext, @@ -104,10 +216,11 @@ pub extern "system" fn SCardListInterfacesA( _pguid_interfaces: LpGuid, _pcguid_interfaces: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListInterfacesW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardListInterfacesW( _context: ScardContext, @@ -115,150 +228,212 @@ pub extern "system" fn SCardListInterfacesW( _pguid_interfaces: LpGuid, _pcguid_interfaces: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetProviderIdA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetProviderIdA( _context: ScardContext, _sz_card: LpCStr, _pguid_provider_id: LpGuid, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetProviderIdW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetProviderIdW( _context: ScardContext, _sz_card: LpCWStr, _pguid_provider_id: LpGuid, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetCardTypeProviderNameA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetCardTypeProviderNameA( - _context: ScardContext, +pub unsafe extern "system" fn SCardGetCardTypeProviderNameA( + context: ScardContext, _sz_card_name: LpCStr, - _dw_provide_id: u32, - _szProvider: *mut u8, - _pcch_provider: LpDword, -) -> ScardStatus { - todo!() + dw_provide_id: u32, + szProvider: *mut u8, + pcch_provider: LpDword, +) -> ScardStatus { + check_handle!(context); + check_null!(szProvider); + check_null!(pcch_provider); + + let provider = match dw_provide_id { + SCARD_PROVIDER_PRIMARY => { + error!("Unsupported dw_provider_id: SCARD_PROVIDER_PRIMARY"); + return ErrorKind::UnsupportedFeature.into(); + } + SCARD_PROVIDER_CSP => MICROSOFT_DEFAULT_CSP, + SCARD_PROVIDER_KSP => MICROSOFT_DEFAULT_KSP, + SCARD_PROVIDER_CARD_MODULE => MICROSOFT_SCARD_DRIVER_LOCATION, + _ => { + error!(?dw_provide_id, "Unsupported dw_provider_id."); + return ErrorKind::InvalidParameter.into(); + } + }; + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + try_execute!(unsafe { copy_buff(context, szProvider, pcch_provider, provider.as_bytes()) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetCardTypeProviderNameW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetCardTypeProviderNameW( - _context: ScardContext, +pub unsafe extern "system" fn SCardGetCardTypeProviderNameW( + context: ScardContext, _sz_card_name: LpCWStr, - _dw_provide_id: u32, - _szProvider: *mut u16, - _pcch_provider: LpDword, -) -> ScardStatus { - todo!() + dw_provide_id: u32, + szProvider: *mut u16, + pcch_provider: LpDword, +) -> ScardStatus { + check_handle!(context); + check_null!(szProvider); + check_null!(pcch_provider); + + let provider = match dw_provide_id { + SCARD_PROVIDER_PRIMARY => { + error!("Unsupported dw_provider_id: SCARD_PROVIDER_PRIMARY"); + return ErrorKind::UnsupportedFeature.into(); + } + SCARD_PROVIDER_CSP => MICROSOFT_DEFAULT_CSP, + SCARD_PROVIDER_KSP => MICROSOFT_DEFAULT_KSP, + SCARD_PROVIDER_CARD_MODULE => MICROSOFT_SCARD_DRIVER_LOCATION, + _ => { + error!(?dw_provide_id, "Unsupported dw_provider_id."); + return ErrorKind::InvalidParameter.into(); + } + }; + let encoded = str_to_w_buff(provider); + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + try_execute!(unsafe { copy_w_buff(context, szProvider, pcch_provider, &encoded) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceReaderGroupA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceReaderGroupA(_context: ScardContext, _sz_group_name: LpCStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceReaderGroupW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceReaderGroupW(_context: ScardContext, _sz_group_name: LpCWStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetReaderGroupA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetReaderGroupA(_context: ScardContext, _sz_group_name: LpCStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetReaderGroupW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetReaderGroupW(_context: ScardContext, _sz_group_name: LpCWStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceReaderA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceReaderA( _context: ScardContext, _sz_reader_name: LpCStr, _sz_device_name: LpCStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceReaderW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceReaderW( _context: ScardContext, _sz_reader_name: LpCWStr, _sz_device_name: LpCWStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetReaderA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetReaderA(_context: ScardContext, _sz_reader_name: LpCStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetReaderW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetReaderW(_context: ScardContext, _sz_reader_name: LpCWStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardAddReaderToGroupA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardAddReaderToGroupA( _context: ScardContext, _sz_reader_name: LpCStr, _sz_group_name: LpCStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardAddReaderToGroupW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardAddReaderToGroupW( _context: ScardContext, _sz_reader_name: LpCWStr, _sz_group_name: LpCWStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardRemoveReaderFromGroupA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardRemoveReaderFromGroupA( _context: ScardContext, _sz_reader_name: LpCStr, _sz_group_name: LpCStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardRemoveReaderFromGroupW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardRemoveReaderFromGroupW( _context: ScardContext, _sz_reader_name: LpCWStr, _sz_group_name: LpCWStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceCardTypeA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceCardTypeA( _context: ScardContext, @@ -270,10 +445,11 @@ pub extern "system" fn SCardIntroduceCardTypeA( _pb_atr_mask: LpCByte, _cb_atr_len: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardIntroduceCardTypeW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardIntroduceCardTypeW( _context: ScardContext, @@ -285,10 +461,11 @@ pub extern "system" fn SCardIntroduceCardTypeW( _pb_atr_mask: LpCByte, _cb_atr_len: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardSetCardTypeProviderNameA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardSetCardTypeProviderNameA( _context: ScardContext, @@ -296,10 +473,11 @@ pub extern "system" fn SCardSetCardTypeProviderNameA( _dw_provider_id: u32, _sz_provider: LpCStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardSetCardTypeProviderNameW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardSetCardTypeProviderNameW( _context: ScardContext, @@ -307,40 +485,55 @@ pub extern "system" fn SCardSetCardTypeProviderNameW( _dw_provider_id: u32, _sz_provider: LpCWStr, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetCardTypeA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetCardTypeA(_context: ScardContext, _sz_card_name: LpCStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardForgetCardTypeW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardForgetCardTypeW(_context: ScardContext, _sz_card_name: LpCWStr) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardFreeMemory"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardFreeMemory(_context: ScardContext, _pv_mem: LpCVoid) -> ScardStatus { - todo!() +pub unsafe extern "system" fn SCardFreeMemory(context: ScardContext, pv_mem: LpCVoid) -> ScardStatus { + if let Some(context) = unsafe { (context as *mut WinScardContextHandle).as_mut() } { + if context.free_buffer(pv_mem) { + info!("Allocated buffer successfully freed."); + } else { + warn!(?pv_mem, "Attempt to free unknown buffer"); + } + + ErrorKind::Success.into() + } else { + ErrorKind::InvalidHandle.into() + } } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardAccessStartedEvent"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardAccessStartedEvent() -> Handle { - todo!() + // This value has been extracted from the original winscard SCardAccessStartedEvent call. + 0x0000000000000eb0 as Handle } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardReleaseStartedEvent"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardReleaseStartedEvent() -> c_void { - todo!() -} +pub extern "system" fn SCardReleaseStartedEvent() {} #[cfg_attr(windows, rename_symbol(to = "Rust_SCardLocateCardsA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardLocateCardsA( _context: ScardContext, @@ -348,10 +541,11 @@ pub extern "system" fn SCardLocateCardsA( _rg_reader_states: LpScardReaderStateA, _c_readers: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardLocateCardsW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardLocateCardsW( _context: ScardContext, @@ -359,10 +553,11 @@ pub extern "system" fn SCardLocateCardsW( _rg_reader_states: LpScardReaderStateW, _c_readers: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardLocateCardsByATRA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardLocateCardsByATRA( _context: ScardContext, @@ -371,10 +566,11 @@ pub extern "system" fn SCardLocateCardsByATRA( _rg_reader_states: LpScardReaderStateA, _c_readers: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardLocateCardsByATRW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardLocateCardsByATRW( _context: ScardContext, @@ -383,112 +579,320 @@ pub extern "system" fn SCardLocateCardsByATRW( _rg_reader_states: LpScardReaderStateW, _c_readers: u32, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetStatusChangeA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetStatusChangeA( - _context: ScardContext, +pub unsafe extern "system" fn SCardGetStatusChangeA( + context: ScardContext, _dw_timeout: u32, - _rg_reader_states: LpScardReaderStateA, - _c_readers: u32, -) -> ScardStatus { - todo!() + rg_reader_states: LpScardReaderStateA, + c_readers: u32, +) -> ScardStatus { + check_handle!(context); + check_null!(rg_reader_states); + + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let supported_readers = context.list_readers(); + + let reader_states = unsafe { + from_raw_parts_mut( + rg_reader_states, + try_execute!(c_readers.try_into(), ErrorKind::InsufficientBuffer), + ) + }; + + for reader_state in reader_states { + let reader = try_execute!( + unsafe { CStr::from_ptr(reader_state.sz_reader as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + + if supported_readers.contains(&Cow::Borrowed(&reader)) { + reader_state.dw_event_state = + SCARD_STATE_UNNAMED_CONSTANT | SCARD_STATE_INUSE | SCARD_STATE_PRESENT | SCARD_STATE_CHANGED; + reader_state.cb_atr = try_execute!(ATR.len().try_into(), ErrorKind::InsufficientBuffer); + reader_state.rgb_atr[0..ATR.len()].copy_from_slice(ATR.as_slice()); + } else if reader == NEW_READER_NOTIFICATION { + reader_state.dw_event_state = SCARD_STATE_UNNAMED_CONSTANT; + } else { + error!(?reader, "Unsupported reader"); + } + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetStatusChangeW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetStatusChangeW( - _context: ScardContext, +pub unsafe extern "system" fn SCardGetStatusChangeW( + context: ScardContext, _dw_timeout: u32, - _rg_reader_states: LpScardReaderStateW, - _c_readers: u32, -) -> ScardStatus { - todo!() + rg_reader_states: LpScardReaderStateW, + c_readers: u32, +) -> ScardStatus { + check_handle!(context); + check_null!(rg_reader_states); + + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let supported_readers = context.list_readers(); + + let reader_states = unsafe { + from_raw_parts_mut( + rg_reader_states, + try_execute!(c_readers.try_into(), ErrorKind::InsufficientBuffer), + ) + }; + for reader_state in reader_states { + let reader = unsafe { c_w_str_to_string(reader_state.sz_reader) }; + if supported_readers.contains(&Cow::Borrowed(&reader)) { + reader_state.dw_event_state = + SCARD_STATE_UNNAMED_CONSTANT | SCARD_STATE_INUSE | SCARD_STATE_PRESENT | SCARD_STATE_CHANGED; + reader_state.cb_atr = try_execute!(ATR.len().try_into(), ErrorKind::InsufficientBuffer); + reader_state.rgb_atr[0..ATR.len()].copy_from_slice(ATR.as_slice()); + } else if reader == NEW_READER_NOTIFICATION { + reader_state.dw_event_state = SCARD_STATE_UNNAMED_CONSTANT; + } else { + error!(?reader, "Unsupported reader"); + } + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardCancel"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardCancel(_context: ScardContext) -> ScardStatus { - todo!() + // https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardcancel + // The SCardCancel function terminates all outstanding actions within a specific resource manager context. + // + // We do not have such actions in an emulated scard context + ErrorKind::Success.into() +} + +unsafe fn read_cache(context: ScardContext, lookup_name: &str, data: LpByte, data_len: LpDword) -> WinScardResult<()> { + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + + if let Some(cached_value) = context.scard_context().read_cache(lookup_name) { + let cached_value = cached_value.to_vec(); + unsafe { copy_buff(context, data, data_len, &cached_value) } + } else { + warn!(cache = ?ErrorKind::CacheItemNotFound); + Ok(()) + } } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardReadCacheA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardReadCacheA( - _context: ScardContext, +pub unsafe extern "system" fn SCardReadCacheA( + context: ScardContext, _card_identifier: LpUuid, _freshness_counter: u32, - _lookup_lame: LpStr, - _data: LpByte, - _data_len: LpDword, + lookup_name: LpStr, + data: LpByte, + data_len: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(lookup_name); + check_null!(data_len); + + let lookup_name = try_execute!( + unsafe { CStr::from_ptr(lookup_name as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + + try_execute!(unsafe { read_cache(context, lookup_name, data, data_len) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardReadCacheW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardReadCacheW( - _context: ScardContext, +pub unsafe extern "system" fn SCardReadCacheW( + context: ScardContext, _card_identifier: LpUuid, _freshness_counter: u32, - _lookup_lame: LpWStr, - _data: LpByte, - _data_len: LpDword, + lookup_name: LpWStr, + data: LpByte, + data_len: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(lookup_name); + check_null!(data_len); + + let lookup_name = unsafe { c_w_str_to_string(lookup_name) }; + + try_execute!(unsafe { read_cache(context, &lookup_name, data, data_len) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardWriteCacheA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardWriteCacheA( - _context: ScardContext, +pub unsafe extern "system" fn SCardWriteCacheA( + context: ScardContext, _card_identifier: LpUuid, _freshness_counter: u32, - _lookup_lame: LpStr, - _data: LpByte, - _data_len: u32, + lookup_name: LpStr, + data: LpByte, + data_len: u32, ) -> ScardStatus { - todo!() + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let lookup_name = try_execute!( + unsafe { CStr::from_ptr(lookup_name as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + let data = from_raw_parts_mut(data, try_execute!(data_len.try_into(), ErrorKind::InsufficientBuffer)).to_vec(); + info!(write_lookup_name = lookup_name, ?data); + + context.write_cache(lookup_name.to_owned(), data); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardWriteCacheW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardWriteCacheW( - _context: ScardContext, +pub unsafe extern "system" fn SCardWriteCacheW( + context: ScardContext, _card_identifier: LpUuid, _freshness_counter: u32, - _lookup_lame: LpWStr, - _data: LpByte, - _data_len: u32, + lookup_name: LpWStr, + data: LpByte, + data_len: u32, ) -> ScardStatus { - todo!() + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let lookup_name = unsafe { c_w_str_to_string(lookup_name) }; + let data = from_raw_parts_mut(data, try_execute!(data_len.try_into(), ErrorKind::InsufficientBuffer)).to_vec(); + info!(write_lookup_name = lookup_name, ?data); + + context.write_cache(lookup_name, data); + + ErrorKind::Success.into() +} + +unsafe fn get_reader_icon( + context: &mut WinScardContextHandle, + reader_name: &str, + pb_icon: LpByte, + pcb_icon: LpDword, +) -> WinScardResult<()> { + let icon = context.scard_context().reader_icon(reader_name)?.as_ref().to_vec(); + + unsafe { copy_buff(context, pb_icon, pcb_icon, icon.as_ref()) } } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetReaderIconA"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetReaderIconA( - _context: ScardContext, - _sz_reader_name: LpCStr, - _pb_icon: LpByte, - _pcb_icon: LpDword, +pub unsafe extern "system" fn SCardGetReaderIconA( + context: ScardContext, + sz_reader_name: LpCStr, + pb_icon: LpByte, + pcb_icon: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(sz_reader_name); + // `pb_icon` can be null. + check_null!(pcb_icon); + + // safe: checked above + let context = unsafe { (context as *mut WinScardContextHandle).as_mut() }.unwrap(); + let reader_name = try_execute!( + unsafe { CStr::from_ptr(sz_reader_name as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + + try_execute!(unsafe { get_reader_icon(context, &reader_name, pb_icon, pcb_icon) }); + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetReaderIconW"))] +#[instrument(ret)] #[no_mangle] -pub extern "system" fn SCardGetReaderIconW( - _context: ScardContext, - _sz_reader_name: LpCWStr, - _pb_icon: LpByte, - _pcb_icon: LpDword, +pub unsafe extern "system" fn SCardGetReaderIconW( + context: ScardContext, + sz_reader_name: LpCWStr, + pb_icon: LpByte, + pcb_icon: LpDword, +) -> ScardStatus { + check_handle!(context); + check_null!(sz_reader_name); + // `pb_icon` can be null. + check_null!(pcb_icon); + + let (context, reader_name) = unsafe { + ( + // safe: checked above + (context as *mut WinScardContextHandle).as_mut().unwrap(), + c_w_str_to_string(sz_reader_name), + ) + }; + + try_execute!(get_reader_icon(context, &reader_name, pb_icon, pcb_icon)); + + ErrorKind::Success.into() +} + +#[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetDeviceTypeIdA"))] +#[instrument(ret)] +#[no_mangle] +pub unsafe extern "system" fn SCardGetDeviceTypeIdA( + context: ScardContext, + sz_reader_name: LpCStr, + pdw_device_type_id: LpDword, ) -> ScardStatus { - todo!() + check_handle!(context); + check_null!(sz_reader_name); + check_null!(pdw_device_type_id); + + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let reader_name = try_execute!( + unsafe { CStr::from_ptr(sz_reader_name as *const i8) }.to_str(), + ErrorKind::InvalidParameter + ); + + let type_id = try_execute!(context.device_type_id(&reader_name)); + unsafe { + *pdw_device_type_id = type_id.into(); + } + + ErrorKind::Success.into() +} + +#[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetDeviceTypeIdW"))] +#[instrument(ret)] +#[no_mangle] +pub unsafe extern "system" fn SCardGetDeviceTypeIdW( + context: ScardContext, + sz_reader_name: LpCWStr, + pdw_device_type_id: LpDword, +) -> ScardStatus { + check_handle!(context); + check_null!(sz_reader_name); + check_null!(pdw_device_type_id); + + let context = try_execute!(unsafe { scard_context_to_winscard_context(context) }); + let reader_name = unsafe { c_w_str_to_string(sz_reader_name) }; + + let type_id = try_execute!(context.device_type_id(&reader_name)); + unsafe { + *pdw_device_type_id = type_id.into(); + } + + ErrorKind::Success.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetReaderDeviceInstanceIdA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetReaderDeviceInstanceIdA( _context: ScardContext, @@ -496,10 +900,11 @@ pub extern "system" fn SCardGetReaderDeviceInstanceIdA( _sz_device_instance_id: LpStr, _pcch_device_instance_id: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardGetReaderDeviceInstanceIdW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardGetReaderDeviceInstanceIdW( _context: ScardContext, @@ -507,10 +912,11 @@ pub extern "system" fn SCardGetReaderDeviceInstanceIdW( _sz_device_instance_id: LpWStr, _pcch_device_instance_id: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReadersWithDeviceInstanceIdA"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardListReadersWithDeviceInstanceIdA( _context: ScardContext, @@ -518,10 +924,11 @@ pub extern "system" fn SCardListReadersWithDeviceInstanceIdA( _msz_readers: LpStr, _pcch_readers: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardListReadersWithDeviceInstanceIdW"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardListReadersWithDeviceInstanceIdW( _context: ScardContext, @@ -529,11 +936,12 @@ pub extern "system" fn SCardListReadersWithDeviceInstanceIdW( _msz_readers: LpWStr, _pcch_readers: LpDword, ) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } #[cfg_attr(windows, rename_symbol(to = "Rust_SCardAudit"))] +#[instrument(ret)] #[no_mangle] pub extern "system" fn SCardAudit(_context: ScardContext, _dw_event: u32) -> ScardStatus { - todo!() + ErrorKind::UnsupportedFeature.into() } diff --git a/ffi/src/winscard/scard_handle.rs b/ffi/src/winscard/scard_handle.rs new file mode 100644 index 00000000..9d64c199 --- /dev/null +++ b/ffi/src/winscard/scard_handle.rs @@ -0,0 +1,199 @@ +use std::mem::size_of; +use std::slice::{from_raw_parts, from_raw_parts_mut}; + +use ffi_types::winscard::{LpScardIoRequest, ScardContext, ScardHandle, ScardIoRequest}; +use ffi_types::LpCVoid; +use winscard::winscard::{IoRequest, Protocol, WinScard, WinScardContext}; +use winscard::{Error, ErrorKind, WinScardResult}; + +/// Scard context handle representation. +/// +/// Additionally, it holds allocated buffers and created smart card handles. +/// We need them because during the smart card context deletion, we need to free all allcated resources. +pub struct WinScardContextHandle { + /// Context of the emulated smart card. + scard_context: Box, + /// Created smart card handles during the API usage. + scards: Vec, + /// Allocated buffers in our smart card context. + /// All buffers are `[u8]`, so we need only pointer and don't need to remember its type. + allocations: Vec, +} + +impl WinScardContextHandle { + /// Creates a new [WinScardContextHandle] based on the provided inner scard context. + pub fn with_scard_context(scard_context: Box) -> Self { + Self { + scard_context, + scards: Vec::new(), + allocations: Vec::new(), + } + } + + /// Returns the shared reference to the inner [WinScardContext]. + pub fn scard_context(&self) -> &dyn WinScardContext { + self.scard_context.as_ref() + } + + /// Adds a new [ScardHandle] to the context handles. + pub fn add_scard(&mut self, scard: ScardHandle) -> WinScardResult<()> { + if scard == 0 { + return Err(Error::new(ErrorKind::InvalidHandle, "ScardHandle can not be NULL")); + } + + self.scards.push(scard); + + Ok(()) + } + + /// Removes the [ScardHandle] from the scard context. + pub fn remove_scard(&mut self, scard: ScardHandle) -> bool { + if let Some(index) = self.scards.iter().position(|x| *x == scard) { + self.scards.remove(index); + + true + } else { + false + } + } + + /// Allocated a new buffer inside the scard context. + pub fn allocate_buffer(&mut self, size: usize) -> WinScardResult<*mut u8> { + let buff = unsafe { libc::malloc(size) as *mut u8 }; + if buff.is_null() { + return Err(Error::new( + ErrorKind::NoMemory, + format!("Can not allocate {} bytes", size), + )); + } + self.allocations.push(buff as usize); + + Ok(buff) + } + + /// Deletes the buffer inside the scard context. + pub fn free_buffer(&mut self, buff: LpCVoid) -> bool { + let buff = buff as usize; + + if let Some(index) = self.allocations.iter().position(|x| *x == buff) { + self.allocations.remove(index); + + unsafe { + libc::free(buff as _); + } + + true + } else { + false + } + } +} + +impl Drop for WinScardContextHandle { + fn drop(&mut self) { + // [SCardReleaseContext](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardreleasecontext) + // ...freeing any resources allocated under that context, including SCARDHANDLE objects + unsafe { + for scard in &self.scards { + let _ = Box::from_raw(*scard as *mut WinScardHandle); + } + } + // ...and memory allocated using the SCARD_AUTOALLOCATE length designator. + unsafe { + for buff in &self.allocations { + libc::free(*buff as _); + } + } + } +} + +/// Scard handle representation. +/// +/// It also holds a pointer to the smart card context to which it belongs. +pub struct WinScardHandle { + /// The emulated smart card. + scard: Box, + /// Pointer to the smart card context to which it belongs. + context: ScardContext, +} + +impl WinScardHandle { + /// Creates a new [WinSCardHandle] based on the provided data. + pub fn new(scard: Box, context: ScardContext) -> Self { + Self { scard, context } + } + + /// Returns the [WinScard] handle. + pub fn scard(&self) -> &dyn WinScard { + self.scard.as_ref() + } + + /// Returns the parent [ScardContext] it belongs. + pub fn context(&self) -> ScardContext { + self.context + } +} + +pub unsafe fn scard_handle_to_winscard<'a>(handle: ScardHandle) -> WinScardResult<&'a mut dyn WinScard> { + if let Some(scard) = unsafe { (handle as *mut WinScardHandle).as_mut() } { + Ok(scard.scard.as_mut()) + } else { + Err(Error::new( + ErrorKind::InvalidHandle, + "Invalid smart card context handle.", + )) + } +} + +pub unsafe fn scard_context_to_winscard_context<'a>( + handle: ScardContext, +) -> WinScardResult<&'a mut dyn WinScardContext> { + if let Some(context) = unsafe { (handle as *mut WinScardContextHandle).as_mut() } { + Ok(context.scard_context.as_mut()) + } else { + Err(Error::new( + ErrorKind::InvalidHandle, + "Invalid smart card context handle.", + )) + } +} + +pub unsafe fn scard_io_request_to_io_request(pio_send_pci: LpScardIoRequest) -> WinScardResult { + let (cb_pci_length, dw_protocol) = unsafe { ((*pio_send_pci).cb_pci_length, (*pio_send_pci).dw_protocol) }; + let buffer_len = cb_pci_length.try_into()?; + let buffer = unsafe { (pio_send_pci as *const u8).add(size_of::()) }; + + Ok(IoRequest { + protocol: Protocol::from_bits(dw_protocol).unwrap_or(Protocol::empty()), + pci_info: unsafe { from_raw_parts(buffer, buffer_len) }.to_vec(), + }) +} + +pub unsafe fn copy_io_request_to_scard_io_request( + io_request: &IoRequest, + scard_io_request: LpScardIoRequest, +) -> WinScardResult<()> { + let pci_info_len = io_request.pci_info.len(); + let scard_pci_info_len = unsafe { (*scard_io_request).cb_pci_length }.try_into()?; + + if pci_info_len > scard_pci_info_len { + return Err(Error::new( + ErrorKind::InsufficientBuffer, + format!( + "ScardIoRequest::cb_pci_length is too small. Expected at least {} but got {}", + pci_info_len, scard_pci_info_len + ), + )); + } + + unsafe { + (*scard_io_request).dw_protocol = io_request.protocol.bits(); + (*scard_io_request).cb_pci_length = pci_info_len.try_into()?; + } + + let pci_buffer_ptr = unsafe { (scard_io_request as *mut u8).add(size_of::()) }; + let pci_buffer = unsafe { from_raw_parts_mut(pci_buffer_ptr, pci_info_len) }; + pci_buffer.copy_from_slice(&io_request.pci_info); + + Ok(()) +} diff --git a/ffi/sspi_winscard.def b/ffi/sspi_winscard.def index 83d0bfe2..c1fe472a 100644 --- a/ffi/sspi_winscard.def +++ b/ffi/sspi_winscard.def @@ -107,7 +107,6 @@ EXPORTS SCardUIDlgSelectCardW=Rust_SCardUIDlgSelectCardW GetOpenCardNameA=Rust_GetOpenCardNameA GetOpenCardNameW=Rust_GetOpenCardNameW - SCardDlgExtendedError=Rust_SCardDlgExtendedError SCardReadCacheA=Rust_SCardReadCacheA SCardReadCacheW=Rust_SCardReadCacheW SCardWriteCacheA=Rust_SCardWriteCacheA @@ -118,4 +117,9 @@ EXPORTS SCardGetReaderDeviceInstanceIdW=Rust_SCardGetReaderDeviceInstanceIdW SCardListReadersWithDeviceInstanceIdA=Rust_SCardListReadersWithDeviceInstanceIdA SCardListReadersWithDeviceInstanceIdW=Rust_SCardListReadersWithDeviceInstanceIdW - SCardAudit=Rust_SCardAudit \ No newline at end of file + SCardAudit=Rust_SCardAudit + SCardGetDeviceTypeIdA=Rust_SCardGetDeviceTypeIdA + SCardGetDeviceTypeIdW=Rust_SCardGetDeviceTypeIdW + g_rgSCardT1Pci=Rust_g_rgSCardT1Pci + g_rgSCardT0Pci=Rust_g_rgSCardT0Pci + g_rgSCardRawPci=Rust_g_rgSCardRawPci