How RDP smart card logon works

Pavlo Myroniuk November 24, 2025 #rust #freerdp #scard #kerberos #winscard #pcsclite

Intro

Around two years ago, FreeRDP got smart card logon support: Smartcard logon with FreeRDP. Theoretically (and sometimes practically), it is possible to use a smart card (scard) for RDP logon in FreeRDP. In this post, I will explain how RDP scard logon works and how to set it up.

This post has two main parts: theoretical and practical. I was thinking about two separate posts, but then I decided that I don't want to split theory and practice.

After reading this article, you should have enough understanding and skills to set up and troubleshoot your own RDP smart card logon.

Note: It is much easier to do smart card logon on Windows, so I assume we run FreeRDP on macOS and our target machine runs Windows.

You may be surprised (sarcasm), but it is not easy to do scard logon on macOS when connecting to the Windows machine. It became easier with recent improvement in sspi-rs. I have a lot to say, let's dive in.

How it works

Each section explains the theoretical aspects of RDP smart card logon and the software component we will use for it. Sometimes it is not trivial because not all Microsoft components have exact replacements on macOS.

RDP NLA

Now we need to understand how the scard authorization works. Without this knowledge, we can't move further. The RDP is very complicated. So, I'll focus my attention only on scard-related things.

So, the first thing you need to know is Network Level Authentication:

Network Level Authentication (NLA) is a feature of RDP Server or Remote Desktop Connection (RDP Client) that requires the connecting user to authenticate themselves before a session is established with the server.

...

Network Level Authentication delegates the user's credentials from the client through a client-side Security Support Provider and prompts the user to authenticate before establishing a session on the server.

Basically, NLA says: "Let's authenticate the user (and the server, if the Kerberos protocol is used) before opening any GUI windows at the beginning of the RDP connection". And only when the user passes the authentication, the NLA will transfer credentials to the target server. In turn, the RDP server will use these credentials to open the session.

NLA is not a concrete protocol but a general principle. In RDP, the CredSSP protocol is responsible for the NLA.

flowchart LR subgraph CREDSSP["CredSSP"] subgraph SPNEGO["SPNEGO"] subgraph ApplicationProtocol["Negotiated Application Protocol (NTLM, Kerberos, etc)"] end end end

The CredSSP protocol performs the user authentication (using NTLM/Kerberos/SPNEGO protocols inside) and transfers the user's credentials to the target server. The CredSSP protocol is responsible only for credential delegation. It doesn't authenticate clients or servers. An actual authentication is performed by the inner application protocol, like NTLM or Kerberos (preferably).

But Microsoft would not be Microsoft if it did not add another layer of abstraction. The CredSSP uses the SPNEGO framework: (quote src)

SPNEGO provides a framework for two parties that are engaged in authentication to select from a set of possible authentication mechanisms.

...The CredSSP Protocol uses SPNEGO to mutually authenticate the CredSSP client and CredSSP server. It then uses the encryption key that is established under SPNEGO to securely bind to the TLS session.

Let's summarize it.

The SPNEGO selects (negotiates) an appropriate authentication protocol and performs authentication using it. As a result, we'll have an established security context with a secure encryption key. In turn, the CredSSP will use this key to encrypt the credentials and pass (delegate) them to the target CredSSP (RDP) server.

FreeRDP implements the CredSSP protocol, so there are no pitfalls. Its CredSSP implementation can rely on either the built-in NTLM implementation, the MIT KRB5 implementation, or an external /sspi-module: — a dynamic library that implements the SSPI interface. Spoiler: We will use an external library.

Good. Now you have a brief overview of the NLA. Now it's time to move forward.

Kerberos

To log on with a smart card, we need SPNEGO to select Kerberos as the authentication protocol. Kerberos is the only authentication protocol that can be used for scard logon. To support password-less logon, Kerberos includes the PKINIT extension: Public Key Cryptography for Initial Authentication in Kerberos (PKINIT).

The only difference between password-based Kerberos and scard-based is in the AS exchange (The Authentication Service Exchange). During password-based logon, the AS exchange session key is derived from the user's password:

// https://github.com/Devolutions/picky-rs/blob/628bbcab3100a782971261022f0ec91b4f4549f5/picky-krb/src/crypto/aes/key_derivation.rs#L39-L51
pub fn derive_key_from_password<P: AsRef<[u8]>, S: AsRef<[u8]>>(
    password: P,
    salt: S,
    aes_size: &AesSize,
) -> KerberosCryptoResult<Vec<u8>> {
    let mut tmp = vec![0; aes_size.key_length()];

    pbkdf2_hmac::<Sha1>(password.as_ref(), salt.as_ref(), AES_ITERATION_COUNT, &mut tmp);

    // For AES encryption (https://www.rfc-editor.org/rfc/rfc3962.html#section-6):
    // > random-to-key function    ``    identity function
    let temp_key = random_to_key(tmp);

    // A Key Derivation Function (https://www.rfc-editor.org/rfc/rfc3961.html#section-5.1).
    derive_key(&temp_key, KERBEROS, aes_size)
}

Essentially, it means that if someone knows the user's password, they can decrypt the AsRep encrypted part, extract a session key, and decrypt-and-extract all other sequential keys in the Kerberos authentication (e.g., TGS exchange session key).

Thus, always enforce a strong password policy 🙃. However, I am not here to talk about Kerberos attack vectors and vulnerabilities. So, what is different in the scard-based logon?

During the scard-based logon, the AS exchange session key is derived using the the Diffie-Hellman Key Exchange. PKINIT also defines the Public Key Encryption, but I haven't seen it in action ever. I always see the Diffie-Hellman Key Exchange in the Wireshark capture of the mstsc.

Diffie–Hellman (DH) key exchange is a mathematical method of securely generating a symmetric cryptographic key over a public channel.

It means that two parties can securely establish an encryption key over a public network. There are tons of articles explaining how the Diffie-Hellman Key Agreement works. Let me paste only a small description and move on.

  1. The client generates DH parameters and a private key.
  2. Using the DH parameters and private key, the client generates the public key.
    // https://github.com/Devolutions/picky-rs/blob/628bbcab3100a782971261022f0ec91b4f4549f5/picky-krb/src/crypto/diffie_hellman.rs#L145-L149
    
    /// [Key and Parameter Requirements](https://www.rfc-editor.org/rfc/rfc2631#section-2.2)
    /// y is then computed by calculating g^x mod p.
    pub fn compute_public_key(private_key: &[u8], modulus: &[u8], base: &[u8]) -> DiffieHellmanResult<Vec<u8>> {
        generate_dh_shared_secret(base, private_key, modulus)
    }
    
  3. The client sends DH parameters and public key to the server (KDC in our case).
  4. The server generates its own private and public keys.
  5. Both parties can generate the shared secret (symmetric encryption key) using the DH algorithm:
    // https://github.com/Devolutions/picky-rs/blob/628bbcab3100a782971261022f0ec91b4f4549f5/picky-krb/src/crypto/diffie_hellman.rs#L94-L112
    
    /// [Using Diffie-Hellman Key Exchange](https://www.rfc-editor.org/rfc/rfc4556.html#section-3.2.3.1)
    /// let DHSharedSecret be the shared secret value. DHSharedSecret is the value ZZ
    ///
    /// [Generation of ZZ](https://www.rfc-editor.org/rfc/rfc2631#section-2.1.1)
    /// ZZ = g ^ (xb * xa) mod p
    /// ZZ = (yb ^ xa)  mod p  = (ya ^ xb)  mod p
    /// where ^ denotes exponentiation
    fn generate_dh_shared_secret(public_key: &[u8], private_key: &[u8], p: &[u8]) -> DiffieHellmanResult<Vec<u8>> {
        let public_key = BoxedUint::from_be_slice_vartime(public_key);
        let private_key = BoxedUint::from_be_slice_vartime(private_key);
        let p = Odd::new(BoxedUint::from_be_slice_vartime(p))
            .into_option()
            .ok_or(DiffieHellmanError::ModulusIsNotOdd)?;
        let p = BoxedMontyParams::new_vartime(p);
    
        // ZZ = (public_key ^ private_key) mod p
        let out = pow_mod_params(&public_key, &private_key, &p);
        Ok(out.to_be_bytes().to_vec())
    }
    
  6. Using the derived encryption key, the server encrypts all needed data and sends it to the client alongside the DH public key.

Of course, I've omitted many technical details, but I'm here to convey the overall idea, not to teach you cryptography. The astute reader may notice that the smart card is not required for the Diffie-Hellman exchange.

The smart card is needed ONLY for signing the digest of all these public authentication parameters: DH parameters, DH public key, nonce, etc (source code). The KDC will verify the signature using the client's certificate.

That's all 🙃. Someday I will write a detailed post about Kerberos scard auth, but today is not that day 😬.

The FreeRDP itself does not implement the Kerberos protocol. It uses the external library for that instead. FreeRDP's CredSSP implementation uses the SSPI interface to perform the authentication. Here we have a few options:

  1. Enable the WITH_KRB5 and WITH_KRB5_MIT flags and provide MIT KRB5 library for FreeRDP (see FreeRDP/wiki/Compilation). FreeRDP has a wrapper over MIT KRB5 to export its functionality via the SSPI interface: FreeRDP/winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c + FreeRDP/winpr/libwinpr/sspi/Kerberos/kerberos.c.
  2. Provide our own SSPI implementation using the /sspi-module arg. It can be any library that implements the SSPI interface.

Surprisingly, we will follow the second path. We will use the following open-source SSPI implementation: github/Devolutions/sspi-rs. It supports both Kerberos password-based and scard-based logon and has various other pros:

Scard credentials

During password-based logon, the CredSSP client transfers the user's username, password, and domain to the target server. In turn, the target server tries to log in the user using these credentials. But we cannot send the smart card to the target server 🙃. According to the specification, smart card credentials are defined as follows:

-- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cssp/94a1ab00-5500-42fd-8d3d-7a84e6c2cf03
TSCredentials ::= SEQUENCE {
        credType    [0] INTEGER,
        credentials [1] OCTET STRING
}
-- credType = 2. Credentials contains a TSSmartCardCreds structure that defines the user's smart card credentials. 

-- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cssp/4251d165-cf01-4513-a5d8-39ee4a98b7a4
TSSmartCardCreds ::= SEQUENCE {
        pin         [0] OCTET STRING,
        cspData     [1] TSCspDataDetail,
        userHint    [2] OCTET STRING OPTIONAL,
        domainHint  [3] OCTET STRING OPTIONAL
}
-- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cssp/34ee27b3-5791-43bb-9201-076054b58123
TSCspDataDetail ::= SEQUENCE {
        keySpec       [0] INTEGER,
        cardName      [1] OCTET STRING OPTIONAL,
        readerName    [2] OCTET STRING OPTIONAL,
        containerName [3] OCTET STRING OPTIONAL,
        cspName       [4] OCTET STRING OPTIONAL
}

I want to go through every field and explain its meaning:

Scard driver

As explained above, the Kerberos protocol does client and server authentication. And, as we can logically assume, it relies on some APIs to communicate with the smart card.

Let's first discuss how it works on Windows, then move on to macOS. Look at this diagram from the MSDN:

The Kerberos implementation uses the Windows high-level cryptographic API to communicate with the smart card. Using the certificate hash, it can get the user's certificate, container name, and all other smart card credentials. Windows allows using vendor-specific CSPs, but the default basecsp.dll is used during RDP smart card auth.

In turn, the basecsp.dll uses the smart card minidriver to perform cryptographic operations.

Smart Card Minidrivers:

The smart card minidriver provides a simpler alternative to developing a legacy cryptographic service provider (CSP) by encapsulating most of the complex cryptographic operations from the card minidriver developer.

Applications use CAPI for smart card–based cryptographic services. CAPI, in turn, routes these requests to the appropriate CSP to handle the cryptographic requirements. Although Base CSP can use the RSA capabilities of a smart card only by using the minidriver...

Smart Card Minidriver Overview:

The card-specific minidriver is the lowest logical interface layer in the Base CSP/KSP. This minidriver lets the Base CSP/KSP and applications interact directly with a specific type of card by using SCRM.

In simple words, the smart card minidriver is a small dll provided by the card vendor that implements the Smart Card Minidriver specification. This dll is used by BaseCSP to perform basic cryptography operations. This allows card vendors to reuse Microsoft's cryptography stack and not implement their own CSP:

I have the YubiKey 5 Nano smart card and will use it in this article. Of course, YubiKey has its own smart card minidriver implementation, which must be installed on the target machine: Deploying the YubiKey Smart Card Minidriver to workstations and servers.

But what about macOS? We do not have minidrivers here... FreeRDP (and sspi-rs) relies on the PKCS11 module (a dynamic library) for executing cryptographic operations.

In cryptography, PKCS #11 is a Public-Key Cryptography Standard that defines a C programming interface to create and manipulate cryptographic tokens that may contain secret cryptographic keys. It is often used to communicate with a Hardware Security Module or smart cards. (Wiki)

Fortunately, YubiKey also distributes its own PKCS11 library: libykcs11.2.7.2.dylib.

However, that's not the end: each smart card minidriver communicates with the smart card through a unified API: WinSCard API. ⬇️

WinSCard

The basic parts of the smart card subsystem are based on PC/SC standards (see the specifications at https://pcscworkgroup.com). (src)

The WinSCard API enables communications with smart card hardware. It is the lowest API from the software perspective.

The WinSCard API is based on the PC/SC (Personal Computer/Smart Card) specification - a globally implemented standard for cards and readers (https://www.smartcardbasics.com/pc-sc/).

The advantage of PC/SC is that applications do not have to acknowledge the details corresponding to the smart card reader when communicating with the smart card. This application can function with any reader that complies with the PC/SC standard. (src).

On Windows, we have WinSCard API, but on macOS, we have pcsc-lite:

PC/SC is the de facto cross-platform API for accessing smart card readers. It is published by PC/SC Workgroup, but the "reference implementation" is Windows. Linux and Mac OS X use the open source pcsc-lite package. (src)

Everything looks good at first sight: libykcs11 uses pcsc-lite, both of them are available on macOS. Seems like nothing stops us from scard logon. Unfortunately, there is one thing.

The target machine tries to open a new session using the transferred smart card credentials. Internally, the same high-level APIs are used except for the WinSCard API. All WinSCard API calls are redirected to the client machine (via [MS-RDPESC]: Remote Desktop Protocol: Smart Card Virtual Channel Extension). Soooo? Obviously, we will call the pcsc-lite API on macOS instead of WinSCard because we do not have WinSCard here. The problem is that the pcsc-lite API does not match the WinSCard API identically. There are differences:

A pcsc-lite wrapper needed to be implemented to match the original WinSCard API. FreeRDP's implementation is not complete. It lacks a proper WinSCard cache implementation. The target machine expects the client-side to have the required WinSCard smart card cache items.

It is a real problem: the target machine will not authenticate the client without a properly working smart card cache. Otherwise, the target machine will display a message such as "This smart card could not be used. Additional detail may be available in the system log. Please report this error to your administrator." or "The requested key container does not exist on the smart card.". If you are interested in smart card cache items, then check my old post: tbt.qkation.com/posts/win-scard-cache/.

To my knowledge, the sspi-rs's FFI module is the only open-source library that implements proper smart card cache items: github/Devolutions/sspi-rs/dea60b5e/ffi/src/winscard/system_scard/context.rs#L283-L564. Now, with this final software component, we can finally set up FreeRDP scard logon 🤩 🥵.

Let's put it all together

software componentmeaningWindowsmacOS
RDP clientno commentsmstsc.exeFreeRDP
CredSSP protocolPerforms NLA. Transfers user credentials to the target servercredssp.dllFreeRDP
Kerberos protocolAuthenticates the user and the server. Established the security contextImplemented inside Windowslibsspi.dylib - Kerberos client implementation with scard logon support
Smart card (mini)driverTransforms high-level crypto operations into low-level PCSC commandsYubiKey Smart Card Minidriverlibykcs11.2.7.2.dylib - a PKCS#11 module
PC/SCEnables communications with smart card hardwareWinSCardlibsspi.dylib - WinSCard-compatible pcsc-lite wrapper with proper smart card cache implementation

At this point, I hope it all makes sense to you 🤞.

Setting up

Finally! We've lived enough to start configuring this machinery 🤩.

If you would like to replicate the configuration below in your own environment, I have some prerequisites for you:

Smart card

I already have a ready-to-go smart card. The user certificate is enrolled and valid:

ykman piv info

You should have something like that:

PIV version:              5.4.3
PIN tries remaining:      3/3
PUK tries remaining:      0/3
Management key algorithm: TDES
WARNING: Using default PIN!
PUK is blocked
Management key is stored on the YubiKey, protected by PIN.
CHUID: 3019d4e739da739ced39ce739d836858210842108421c84210c3eb34101df30c281471d9a216caed2291f8e494350832303330303130313e00fe00
CCC:   No data available
Slot 9A (AUTHENTICATION):
  Private key type: RSA2048
  Public key type:  RSA2048
  Subject DN:       
  Issuer DN:        CN=tbt-WIN-956CQOSSJTF-CA,DC=tbt,DC=com
  Serial:           00:1c:00:00:00:11:a3:a9:8c:0c:7d:22:25:df:00:00:00:00:00:11
  Fingerprint:      bbb2ff52231e831d4d7b3c575c8f7bb8c50c941ac2121a0000d68d4d540255e5
  Not before:       2025-10-23T17:17:44+00:00
  Not after:        2027-10-23T17:27:44+00:00

Slot 9D (KEY_MANAGEMENT):
  Private key type: RSA2048

And here is the certificate itself: [email protected]. You can export it from your smart card using the following command:

ykman piv certificates export ${SLOT_ID} ${PATH_TO_FILE}
# Example:
ykman piv certificates export 9a ~/delete_it/[email protected]

And the last step in configuring the smart card is installing libykcs11.2.7.2.dylib.

YKCS11 is automatically built as part of yubico-piv-tool

git clone https://github.com/Yubico/yubico-piv-tool.git
cd yubico-piv-tool
mkdir build; cd build
cmake ..
make
sudo make install

When working with smart cards, I like to check if the smart scard works well using the following command:

echo "TheBestTvarynka" | pkcs11-tool --module "/usr/local/lib/libykcs11.2.7.2.dylib" -m RSA-PKCS -p 123456 -s --id 01 | base64
# You should get the base64-encoded signature:
# SRA+CTXAYJRO3Wz42K9W/4qeHlV2WmY/t6bDumdSOf7+KL1/32E7Cuq55GIdkDnZnCfHZp89Eyq/
# TEcV2GAnbyiMiUqlZb1iBEYyYOf5ovaJ7xy4dPIuEvkJLMVJPpCCJ7Cr9zolpTEmfZ7FMZVbtprS
# 04ho4P2lz60eJ00Bykrerwc4FcmIPs7tNX//1EJVNLrlXdEBYlxH8ZOFnYtMUggf80EJJlWxTHjT
# ZTz8/+KNZRSFwHR+lZikzxMDaVx4Kiq+2w5NMrSA0MQlFA7fvHOBRh4LBAdHPHzXmgCMWNM1cAbm
# h9PrXdMlo24Xdj+EHZRRLsD7HfeVYPWYNhCvoC0tW1RCVF0tPjogR2V0RnVuY3Rpb25MaXN0IHN0
# YXJ0IQotLVtUQlRdLT46IEdldEZ1bmN0aW9uTGlzdCBzdWNjZXNzIQo=

WinSCard

As I said above, we will use sspi-rs to provide FreeRDP with the custom WinSCard API implementation.

# The `sspi-rs` was in commit `ffd920b7b396739b60a6e1bae701f31e735deeef` at the time of writing this article.
git clone https://github.com/Devolutions/sspi-rs.git
cd sspi-rs/ffi
cargo build --features scard

file ../target/debug/libsspi.dylib
# You should see something like this:
# ../target/debug/libsspi.dylib: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=eb4b2d386823f59cbd4bd7ed08055f887db6c45b, with debug_info, not stripped

You can confirm that the libsspi.dylib library exports the WinSCard API by running:

readelf -Ws --dyn-syms ../target/debug/libsspi.dylib | grep -E " SCard"

To configure the WinSCard API implementation, we need to set the following environment variables:

namemeaningexample
WINSCARD_USE_SYSTEM_SCARDTells the sspi-rs to use the system-provided (real) smart card instead of an emulated onetrue

The last step in this section is to ensure that the pcsc-lite is installed on your system and running:

# I use Arch bwt
sudo pacman -S pcsclite
sudo systemctl enable pcscd
sudo systemctl start pcscd
sudo systemctl status pcscd
# ● pcscd.service - PC/SC Smart Card Daemon
#      Loaded: loaded (/usr/lib/systemd/system/pcscd.service; indirect; preset: disabled)
#      Active: active (running) since Sat 2025-11-08 12:28:03 EET; 5s ago
#  Invocation: e7d44e3ded3c4a19a388887891599d6b
# TriggeredBy: ● pcscd.socket
#        Docs: man:pcscd(8)
#    Main PID: 265031 (pcscd)
#       Tasks: 5 (limit: 18705)
#      Memory: 1.4M (peak: 2.7M)
#         CPU: 65ms
#      CGroup: /system.slice/pcscd.service
#              └─265031 /usr/bin/pcscd --foreground --auto-exit

# Nov 08 12:28:03 tbt systemd[1]: Started PC/SC Smart Card Daemon.

Kerberos

As I mentioned above, we will use sspi-rs to provide FreeRDP with a Kerberos implementation. sspi-rs exports both SSPI and WinSCard APIs from the same library file. Thus, at this point, we already have the Kerberos implementation exported via the SSPI interface inside libsspi.dylib from the previous step. You can confirm that by checking the exported functions. For example:

readelf -Ws --dyn-syms ../target/debug/libsspi.dylib | grep -E "SecurityContext(A|W)?$"
# You should see simething like this:
#    153: 00000000003066f0  2841 FUNC    GLOBAL DEFAULT   12 AcceptSecurityContext
#    197: 00000000003bfe30  2900 FUNC    GLOBAL DEFAULT   12 InitializeSecurityContextA
#    223: 0000000000308f60  2248 FUNC    GLOBAL DEFAULT   12 ImpersonateSecurityContext
#    233: 00000000003c2850  2272 FUNC    GLOBAL DEFAULT   12 ImportSecurityContextA
#    234: 00000000003c0990  2900 FUNC    GLOBAL DEFAULT   12 InitializeSecurityContextW
#    235: 000000000030bb40  2271 FUNC    GLOBAL DEFAULT   12 ExportSecurityContext
#    257: 00000000003c3130  2272 FUNC    GLOBAL DEFAULT   12 ImportSecurityContextW
#    269: 0000000000309830  2248 FUNC    GLOBAL DEFAULT   12 RevertSecurityContext
#    273: 0000000000307c60  2595 FUNC    GLOBAL DEFAULT   12 DeleteSecurityContext
# 105839: 00000000003c2850  2272 FUNC    GLOBAL DEFAULT   12 ImportSecurityContextA
# 105866: 00000000003c0990  2900 FUNC    GLOBAL DEFAULT   12 InitializeSecurityContextW
# 105872: 0000000000307c60  2595 FUNC    GLOBAL DEFAULT   12 DeleteSecurityContext
# 105878: 00000000003bfe30  2900 FUNC    GLOBAL DEFAULT   12 InitializeSecurityContextA
# 105887: 00000000003c3130  2272 FUNC    GLOBAL DEFAULT   12 ImportSecurityContextW
# 105896: 0000000000308f60  2248 FUNC    GLOBAL DEFAULT   12 ImpersonateSecurityContext
# 105929: 00000000003066f0  2841 FUNC    GLOBAL DEFAULT   12 AcceptSecurityContext
# 105957: 000000000030bb40  2271 FUNC    GLOBAL DEFAULT   12 ExportSecurityContext
# 105978: 0000000000309830  2248 FUNC    GLOBAL DEFAULT   12 RevertSecurityContext

We need to set the following environment variables to configure Kerberos properly:

namemeaningexample
SSPI_LOG_PATHPath to the logfile~/delete_it/sspi.log
SSPI_LOG_LEVELLogging leveltrace
SSPI_KDC_URLUrl to the KDC server or KDC Proxy server. You can omit this value if the DNS is properly configured.tcp://192.168.1.104:88
SSPI_SCARD_TYPEsspi-rs also implements the emulated smart card. We need to specify it to use the system-provided (real) smart cardsystem
SSPI_PKCS11_MODULE_PATHPath to the PKCS11 module/usr/local/lib/libykcs11.2.7.2.dylib

FreeRDP

Now to the hard part. Compiling the C/C++ project was always a nightmare for me. Time passes, and I become more skilled in it, but it remains unpleasant.

Fortunately, FreeRDP has an excellent compilation guide: FreeRDP/wiki/Compilation. Unfortunately, versions of some dependencies are outdated (at the moment), so I used the macOS bundling script (FreeRDP/scripts/bundle-mac-os.sh) to help me with compilation.

Demo

export SSPI_LOG_PATH=sspi.log
export SSPI_LOG_LEVEL=trace
export SSPI_KDC_URL="tcp://192.168.1.104:88"
export SSPI_SCARD_TYPE=system
export SSPI_PKCS11_MODULE_PATH=/usr/local/lib/libykcs11.2.7.2.dylib
export WINSCARD_USE_SYSTEM_SCARD=true

./sdl-freerdp /v:DESKTOP-QELPR32.tbt.com /u:t2 /d:tbt.com /p:123456 /smartcard-logon /sec:nla \
  /cert:ignore /log-level:TRACE \
  /sspi-module:/Users/user/delete_it/sspi-rs/target/debug/libsspi.dylib \
  /kerberos:pkcs11-module:/usr/local/lib/libykcs11.2.7.2.dylib \
  /winscard-module:/Users/user/delete_it/sspi-rs/target/debug/libsspi.dylib 

It was quite a short demo 😅. But it works!

Doc, references, code

Back to top