Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Delegate WinSCard calls to another library (for a RDP server for example) #161

Closed
LudovicRousseau opened this issue Nov 7, 2023 · 55 comments
Assignees

Comments

@LudovicRousseau
Copy link
Owner

LudovicRousseau commented Nov 7, 2023

Problem

In some cases it is required that the WinSCard API (implemented by libpcsclite.so.1) is redirected to another library.

Proposal

libpcsclite.so.1 reads an environment variable (like LIBPCSCLITE_DELEGATE) to redirect all the function calls to the other library defined by LIBPCSCLITE_DELEGATE.

Open questions

  • should this mechanism also be used by suid processes (could be a security issue)?
    • are environment variables propagated to suid programs? Maybe not. Need to check
  • what mechanism to use for the call redirection?
    • it should be fast
  • should this behaviour be enabled by default?
    • I would say yes otherwise it will not be used or usable on deployed systems

Please add your comments/questions/etc.

@boomer41
Copy link

boomer41 commented Nov 7, 2023

libpcsclite.so.1 reads an environment variable (like LIBPCSCLITE_DELEGATE) to redirect all the function calls to the other library defined by LIBPCSCLITE_DELEGATE.

Maybe another approach for discussion: A multi-"user" and/or client/server thing.
Say the PCSC daemon for that session provides a server (UNIX-Socket, like PCSC does already) on which other programs (RDP server) can register itself.
When the RDP session is started, the RDP server can dynamically register itself in the daemon and provide virtual readers.

The UNIX tun/tap networking driver is a great example.
Any userspace software may create a network interface transparent to other applications. The software chooses on what to do with the packets it gets. See OpenVPN for example.
All that is done by simply opening /dev/tun and writing some magic things to it.
And we can have as many as we sanely want.

With this way, we can not only have one singular "source of truth" for smartcards, but multiple at the same time.
Some possible usages, especially in combination:

  • RDP server idea above
  • Some test applications providing "real" devices to PCSC and therefore "real" devices for apps
    -> Integration tests for smartcard software on PCs
  • PKCS11 proxies for software that can only talk PCSC, e.g. for connecting to HSMs not having support for PCSC
    (Idea out of the blue, not sure about real word usage?)
  • Tunneling a smartcard with whatever software you like (see tun/tap example above)
    -> Forward a (NFC) smartcard touched by your mobile device
    -> KDE Connect integration maybe?
  • Have a smartcard running/connected on an external device, forward the PCSC socket via ssh, and use the card locally

Of course, when the connection between the host application and the PCSC daemon becomes closed/invalid, the smartcard is basically "unplugged".
When a new card shall be added, there just needs to be some program registering it dynamically, without affecting any configuration file whatsoever.

The LIBPCSCLITE_DELEGATE approach would be a problem when the delegate should change on a running session/process.
Windows for example can dynamically switch a session between RDP and Console. PCSC follows accordingly.
Maybe we can have this thing for Linux some day?
You'd have to write a separate delegate library for your setup that can dynamically switch between everything.

should this mechanism also be used by suid processes (could be a security issue)?

When done with any form of LD_LIBRARY_PATH/LD_PRELOAD'y thing, I'd follow the same strategy here.
LD_LIBRARY_PATH/LD_PRELOAD is AFAIK ignored, when running a suid binary.
At least it's set as a unsecure env variable for glibc [1].
It is unset [2] when "secure mode" is enabled.

When doing IPC over sockets (see below) there would not be any executable code that is being loaded in any way to the suid process, except the default libpcsclite.so.1 available in the system.

what mechanism to use for the call redirection?

Since PCSC does IPC over UNIX sockets, why not just the same thing again?
Even if the multi-user idea above is deemed not worth of time, we still can just pipe the delegate calls through a socket.
Maybe putting the path to a delegate-socket in the env variable is worth thinking about.

it should be fast

My opinion is, that when the current IPC is "fast enough", just reuse the existing approach.

should this behaviour be enabled by default?

IMO, Absolutely. Maybe we can reuse the already existing socket for normal client access, this would not need any configuration change whatsoever.
Maybe a configuration option to disable this may be useful.

References:
[1] https://codebrowser.dev/glibc/glibc/sysdeps/generic/unsecvars.h.html
[2] https://codebrowser.dev/glibc/glibc/elf/dl-support.c.html#305

@LudovicRousseau
Copy link
Owner Author

@boomer41 I don't know RDP details and I have a question: the session visible through RDP is the same as the session on the console?

On a Unix system you can have as many remote connections as you want and each connection is different on the server (for example using X11 remote connection or ssh connection).
On windows if two RDP clients connect to the same server they will see/share the same session?

@boomer41
Copy link

boomer41 commented Nov 7, 2023

On windows if two RDP clients connect to the same server they will see/share the same session?

Yes and no. It depends on the edition of Windows (Client/Server) and configured settings.
A RDP connection is essentially just a regular physical session on a long wire and behaves very similar as a physical console.

(Terminal-)Servers

Every user can have multiple concurrent sessions.
When connecting via RDP, the server picks a disconnected session first for that user to reconnect to.
If no such session is available, it starts a new session whilst the other users stay connected.
You can also switch between sessions via task manager.
The RDP connection itself doesn't change.
The screen just goes dark for a brief moment while the sessions are rerouted.

Clients / Non-Server-Edition

On clients (Windows 10/11), only one concurrent session is possible at one time.
Every scenario below also works in the symmetric opposite (e. g. connect via RDP first, then log in via Console)

Scenario A

When User A is connected via Console, and user B connects via RDP, User A's session is disconnected and A gets sent to the lock screen.

Scenario B

When User A is connected via Console, and the same user A connects via RDP, the console screen is sent to the login screen and the same session is now displayed via RDP.

Visual material on how RDP works on Windows Clients: https://youtu.be/LmnMRCixwLU?si=EkUM0XlSK-WLzbF0&t=341
He is logged in on the console of the VM, then connects via RDP to the VM as the same user.
His console session is now displayed via RDP.
If he logs back in on the console, he would get kicked out of RDP and the session is now display on console again.

@LudovicRousseau
Copy link
Owner Author

Thanks for the explanations @boomer41.

I asked because you proposed to inject the reader (connected on the client side) in the pcscd running in the server.
The problem with that is the pcscd process is global on the server. All the applications running on the server will see the same reader list.
Imagine you have 2 RDP clients, A & B, that connect to the server and inject their local readers A & B in pcscd. Now all the applications running on the server will see the readers A & B. It may be difficult for user on client A to know what reader to select. RDP clients would also see the readers locally connected to the server.
Now imagine your RDP server supports 100 remote users, each RDP client would see the 100 smart card readers.

Is this the expected behaviour?
Or each RDP client should only see (on the server) the reader locally connected on the RDP client computer?

@boomer41
Copy link

boomer41 commented Nov 7, 2023

IMO it should only see the readers connected to it's session or same uid. It would be a mess of confusion if it worked otherwise, and security is also problematic.

For each user session on terminal servers it would be better to start a PCSC server per session/user (with disabled physical smart cards), and have the PCSC socket be set as an env variable. Terminal servers are, imo, a special case where such a setup seems reasonable. systemd --user can do such things already.

We could also have some UID filtering for a global PCSC process.
Each user could only see such virtual smart cards created by the same UID (and some ones explicitly marked "public" by UID 0, for global things?)
We can deduce the UID of the calling process by checking the remote UID on the socket connection.
That's the way Windows does it. There is one global Smartcard service doing all this magic.

Physical cards would behave unchanged (or visibility can be controlled bei uid, gid, too? Configs are our friend.)

Sorry for the amount of edits. Mobile GitHub is trolling hard ATM.

@AndreyBarmaley
Copy link

That would be convenient. For example, replacement via LD_PRELOAD does not work with chromium sandbox.

@matt335672
Copy link

xrdp maintainer here.

LD_PRELOAD / LD_LIBRARY_PATH are a bit broken in general for terminal servers as there are privileged executables that can remove them when a session starts (see e.g. Debian BTS #711623). We can come up with workarounds, but you can see that this isn't ever going to be maintenance free.

One PCSC daemon per session/user is I think desirable from a security perspective as it provides strong separation between users without needing to explicitly test it.

For the particular user-case I'm concerned with, redirecting libpcsclite.so.1 will be fine, but I can see that LIBPCSCLITE_DELEGATE will also meet this use-case and maybe have other applications to. I especially like the tunnelling possibility as this could provide a remote smartcard facility for things like VNC sessions when the RDP protocol isn't required or appropriate.

I can't see any particular reason to make the daemon per-user rather than per-session (at least as far as the terminal server use-case goes). It might make systemd integration a little easier, but is that a good enough reason to add the extra complexity in to the daemon itself? Am I missing something?

@LudovicRousseau
Copy link
Owner Author

Thanks for your comments @matt335672 & @AndreyBarmaley

  • If my proposal to use LIBPCSCLITE_DELEGATE works for your use case it is fine. I will think more about that solution.
  • I do not plan to modify pcscd to add code specific to RDP as proposed by @boomer41. I don't want to maintain a code I do not use.

@matt335672
Copy link

Thanks @LudovicRousseau

To be clear, are you proposing LIBPCSCLITE_DELEGATE points to a UNIX domain socket, or to another library? I can see an argument for either approach.

@LudovicRousseau
Copy link
Owner Author

To be clear, are you proposing LIBPCSCLITE_DELEGATE points to a UNIX domain socket, or to another library? I can see an argument for either approach.

@matt335672 I was thinking about another library using the same WinSCard API. So no need to define a protocol to talk over a socket.
In the other library you can do whatever you want as long as you respect the WinSCard API.

@CendioOssman
Copy link

Perhaps rather than wholesale switching between PC/SC implementation, it could be possible to aggregate them? Something similar to how p11kit.

Different PC/SC providers would register themselves globally in the system, and it would be up to each library to decide when to provide actual readers to the multiplexer or not.

@LudovicRousseau
Copy link
Owner Author

@CendioOssman I don't think we need a multiplexer here. A redirector should be enough.

The readers that should be visible to an application are either:

  • local readers so use the standard pcsc-lite stack
  • remote readers (on a RDP client) available through RDP

Is there a use case where we need to mix these 2 options?

@matt335672
Copy link

I don't think there is myself. Also, it's not clear to me how the permissions would work in a case like this.

@CendioOssman
Copy link

It's probably not very common, but I can think of two scenarios:

  • Remote access to an otherwise local session. Depending on your security model, you might want to retain access to local resources when the session is accessed remotely. I think retaining access is how stuff mostly works on Linux at the moment. Unlike Windows, which disconnects the local access. Still, this is a policy that should ideally be left to the system IMO and not the PC/SC libraries.
  • Multiple remote connections to the same session, possibly over different protocols. E.g. one connection for the primary user, but a secondary collaboration or support connection.

@Maxhy
Copy link

Maxhy commented Nov 10, 2023

On Windows, such decision is the role of dwScope parameter on SCardEstablishContext which is currently unused on pcsclite afaik. Its probably implicit on the discussion for you guys, but I just wanted to highlight because it wasn't explicitly said when talking about use case scenarios here.

@LudovicRousseau
Copy link
Owner Author

On Windows, such decision is the role of dwScope parameter on SCardEstablishContext which is currently unused on pcsclite afaik. Its probably implicit on the discussion for you guys, but I just wanted to highlight because it wasn't explicitly said when talking about use case scenarios here.

Very good point.
To use SCardEstablishContext() dwScope parameter I guess you also need to use SCardAddReaderToGroup(), SCardForgetReader(), and friends.

@Maxhy
Copy link

Maxhy commented Nov 10, 2023

Not necessary, this is done by the system automatically (at least from a PC/SC user point of view). I guess that's why SCARD_LOCAL_READERS and SCARD_SYSTEM_READERS are considered internal lists by Microsoft.

EDIT: you were probably thinking from the RDP server point of view here and not the regular PC/SC application consumer. In that case SCardAddReaderToGroup and friends probably have to be used indeed to manage such internal lists, depending the implementation.

@jsorg71
Copy link

jsorg71 commented Nov 14, 2023

After reading through this, sure, I think LIBPCSCLITE_DELEGATE can work. I think most important here is to agree on something. I assume LIBPCSCLITE_DELEGATE would look like LIBPCSCLITE_DELEGATE=/usr/lib/xrdp/libpcsc_xrdp.so or the like. I guess libpcsclite would lookup all these functions on startup. This sounds a bit like libglvnd.
If a function is added to the libpcsclite API and it's not in the LIBPCSCLITE_DELEGATE, we would have to handle that.

@CendioOssman
Copy link

I'm still cautious about limiting us to just the one library, but if we're going the LIBPCSCLITE_DELEGATE then remember multi-arch systems. There needs to be a mechanism to allow multiple libraries depending on arch. Looking at how LD_PRELOAD works is probably a good idea.

@LudovicRousseau
Copy link
Owner Author

@jsorg71 you are right. It sounds like libglvnd (I did not know this project)

@CendioOssman multi-arch systems should be supported if you use LIBPCSCLITE_DELEGATE=libpcsc_xrdp.so. The correct version of the library will be selected by the dynamic loader.

@jsorg71
Copy link

jsorg71 commented Nov 15, 2023

Hey Pierre, I don't want to be dismissive of your concerns or anyone's concerns but I also feel if this gets too complicated nothing will get done. A multiplex design now seems complicated and it seems it could be done in the delegate anyway. We(xrdp) have had this crappy PCSC redirection problem since the beginning with users asking for status with no light ahead until now. Also important is that Ludovic seems to want to do this and finally to have pcsclite support for this use case is just miles ahead of what we had.

@matt335672
Copy link

I'd just like to pick up on the multi-arch conversation and the delegation. This comment is focussing on deployment rather than implementation, so I'm thinking it's mainly of interest to Pierre.

In particular, I'm thinking of RHEL and the derivatives. I don't have any great love for Red Hat after their actions this year, but we have a significant following using Alma and Rocky. When I say RHEL below, I'm including the derivatives.

On RHEL, PCSC-Lite is part of the BaseOS and xrdp is part of EPEL. Consequently, PCSC-Lite isn't updated for the lifetime of the system, but xrdp will be updated much more quickly. PCSC-Lite is on v1.9.4 for RHEL 9, and I believe it will stay on that version until 2032, if previous behaviour is anything to go by.

This means to support some operating systems, we need a way to delegate the other way round, at least until older versions of RHEL are no longer in use.

We can do this as follows:-

  1. xrdp ships libpcsclite.so.1, in a directory not normally in the shared library search path (/usr/lib64/xrdp-pcsc for example)
  2. xrdp ships the symlink /usr/lib64/libpcsc_xrdp.so which points to /usr/lib64/xrdp-pcsc/libpcsclite.so.1

On systems with a compatible version of PCSC-Lite we just set LIBPCSCLITE_DELEGATE=libpcsc_xrdp.so as Ludovic proposes above. This supports multi-arch provided a suitable 32-bit package is also provided.

On systems without a compatible version of PCSC-Lite :-

  • ldconfig is used to place /usr/lib64/xrdp-pcsc at the start of the library search path when the xrdp package is installed.
  • /etc/xrdp/xrdp.ini contains a line something like LibpcscliteLocal=/usr/lib64/libpcsclite.so.1

When any application using PCSC-Lite starts, the xrdp library will be preferentially loaded. This will detect whether it is running as part of a terminal session, and if not it will redirect to the standard library using the dlopen() mechanisms already discussed.

This approach won't support multi-arch without more work.

@CendioOssman
Copy link

Yes, whatever comes out of the discussion here will only be for new systems, and a fallback will always be needed.

It would be nice if dlopen() had a RTLD_NEXT flag like dlsym() has so we could let the system use the normal library search method. But nothing like that seems to be exposed from glibc. :/

@rhoog-ine
Copy link

This would be great for using the PC/SC services from windows inside WSL linux too!

@LudovicRousseau
Copy link
Owner Author

Good idea @rhoog-ine.
Someone will have to write the bridge between pcsc-lite and Windows WinSCard.

@LudovicRousseau
Copy link
Owner Author

Hello,
I implemented the idea of a delegate library. The code is available in the pcsc-debug project at https://github.com/ludovicRousseau/pcsc-debug
An archive is available at https://pcsclite.apdu.fr/files/pcsc-lite-2.0.1-fa8481a9.tar.bz2

A sample library is provided in https://github.com/LudovicRousseau/PCSC-debug/blob/master/src/libfake.c

What you have to do:

  • build and install the beta pcsc-lite
  • create a new library starting from libfake.c with your own implementation of the WinSCard functions
  • define the env variable LIBPCSCLITE_DELEGATE to point to your newly created library
  • run the application you want to redirect

Example:

$ LIBPCSCLITE_DELEGATE=/tmp/libpcsclite_fake.so.1 pcsc_scan 
PC/SC device scanner
V 1.7.1 (c) 2001-2022, Ludovic Rousseau <[email protected]>
SCardEstablishContext: Internal error.

The sample fake library returns SCARD_F_INTERNAL_ERROR for every function so the error for SCardEstablishContext() is expected.

@boomer41 @matt335672 @CendioOssman @Maxhy @jsorg71 @rhoog-ine tell me is this implementation works for you.
If everything is OK I will push my changes in the official pcsc-lite project and make a new release.

@rhoog-ine
Copy link

I think this is a great generic mechanism. It would also allow doing virtual card stuff like Windows Hello for Business 1. Can a single delegate impersonate multiple readers?
Anyway I'll give it a try when I have some time.

@LudovicRousseau
Copy link
Owner Author

@rhoog-ine the delegate library can return multiple readers for SCardListReaders(). You can simulate as many readers and cards as you want.

@LudovicRousseau
Copy link
Owner Author

The support of LIBPCSCLITE_DELEGATE is in the delegate branch of PCSC-debug at https://github.com/LudovicRousseau/PCSC-debug/tree/delegate

I will regularly rebase this branch over the master branch to keep it up-to-date.

@matt335672
Copy link

@LudovicRousseau - sorry it's taken me a few days to get here.

I've got a stub library in an xrdp build which implements the libpcsclite.so API. It's not complete yet, but with your pcsc-debug library I can use it to return status on a Yubikey with a couple of slots set up:-

$ export LIBPCSCLITE_DELEGATE=/usr/local/lib/xrdp-pcsc/libpcsclite.so.1
$ yubico-piv-tool -a status
Version:        5.2.4
Serial Number:  10664738
CHUID:  3019d4e739da739ced39ce739d836858210842108421c84210c3eb3410b7c842dcdb63f0b7746242695fe12856350832303330303130313e00fe00
CCC:    No data available
Slot 9a:
        Algorithm:      ECCP256
        Subject DN:     CN=mjb
        Issuer DN:      CN=mjb
        Fingerprint:    8b36bfac9aba036c1be05e066fbe065b6fc57324c63ac3e3bdc9212c6c0ca371
        Not Before:     Oct 22 11:38:45 2023 GMT
        Not After:      Oct 22 00:00:00 2024 GMT
Slot 9c:
        Algorithm:      RSA2048
        Subject DN:     CN=mjb
        Issuer DN:      CN=mjb
        Fingerprint:    9d36789cf72731134bad81086a49232eec3cde682572064d502e5007a3ec6f0f
        Not Before:     Oct 22 11:48:13 2023 GMT
        Not After:      Oct 22 00:00:00 2024 GMT
PIN tries left: 3

That's good enough for me at this stage. Looking at the code, I can see it's implemented as I expected, so I see no reason why this shouldn't work for us.

I can see how multilib can be supported for xrdp sessions:-

  1. I need to ensure my stub has a unique name (i.e. NOT libpcsclite.so.1 which is its current name!) and is installed to --libdir (as passed to the configure script).
  2. Packagers who need multilib will need to split the stub off from the main xrdp package.
  3. At runtime, LIBPCSCLITE_DELEGATE is set to an unqualified name, and the runtime linker will do the rest.

@LudovicRousseau
Copy link
Owner Author

Thanks @matt335672 for the feedback.
I think multilib for Gentoo is what is called multiarch for Debian: the ability to run 32-bits Intel code on a 64-bits Intel system.

You can name you library libpcsclite-xrdp.so.0 and it should work fine.

@LudovicRousseau
Copy link
Owner Author

Hello All,
Does xrdp, or the other RDP servers, run as root?
In the latest version of the code the use of LIBPCSCLITE_DELEGATE is disabled for root processes, to avoid security issues.
Maybe I should limit the restriction to suid root processes only?

You can get an archive of the source code from https://pcsclite.apdu.fr/files/pcsc-lite-2.0.3-delegate.tar.bz2

@matt335672
Copy link

That's a good idea, and thanks for thinking of us.

At the moment we're looking at using the redirector for user sessions only (i.e. after login), so that restriction won't affect us there. I don't think a lot of desktops work if you log in as root anyway.

An xrdp implementation of Windows hello or similar won't need libpcsclite.so as we can use the stub directly. That code won't be running as root anyway.

@LudovicRousseau LudovicRousseau changed the title Delegate WinSCard calls to another libray (for a RDP server for example) Delegate WinSCard calls to another library (for a RDP server for example) Mar 15, 2024
@frankmorgner
Copy link
Contributor

Do I understand correctly, that setting the environment variable LIBPCSCLITE_DELEGATE is enough to spy on and modify PIN/signature APDUs?

I have too little knowledge of Linux' security model, but I would think that the typical attack model asumes that simply modifying an environment variable by an attacker should not be sufficient to access critical user assets.

@LudovicRousseau
Copy link
Owner Author

Yes, you are right @frankmorgner. Using LIBPCSCLITE_DELEGATE will allow to spy/modify the APDUs.
It is already possible to replace the normal libpcsclite.so.1 using LD_PRELOAD as documented in PCSC API spy, update.
LIBPCSCLITE_DELEGATE will make this even easier.

That is one reason why this change is not yet in production. It has some "problematic" effects and we need to discuss.

If an attacker is able to inject an environment variable I think the security is already lost. So LIBPCSCLITE_DELEGATE may not really downgrade the security so much.

@boomer41
Copy link

If we're comparing LIBPCSCLITE_DELEGATE to LD_PRELOAD, maybe the same security should apply?
AFAIK, LD_PRELOAD is only used by the loader when certain conditions apply (UID = EUID, not just EUID == 0, ...).
Maybe we should copy the loader's behaviour then?

@LudovicRousseau
Copy link
Owner Author

Exact @boomer41. I already added this kind of test in LudovicRousseau/PCSC-debug@f3edd3f
LIBPCSCLITE_DELEGATE will be ignored for a root process, even if started by root, not just a suid process.

Do you think the code should do more checks? If yes then what?

@boomer41
Copy link

Do you think the code should do more checks? If yes then what?

glibc uses the auxiliary vector to see if it shall run in secure mode [1].
auxv_values[AT_SECURE] (or better getauxval(AT_SECURE) [2]) seems to be set by kernel security modules, too:

AT_SECURE
              Has a nonzero value if this executable should be treated
              securely.  Most commonly, a nonzero value indicates that
              the process is executing a set-user-ID or set-group-ID
              binary (so that its real and effective UIDs or GIDs differ
              from one another), or that it gained capabilities by
              executing a binary file that has capabilities (see
              [capabilities(7)](https://man7.org/linux/man-pages/man7/capabilities.7.html)).  Alternatively, a nonzero value may be
              triggered by a Linux Security Module.  When this value is
              nonzero, the dynamic linker disables the use of certain
              environment variables (see [ld-linux.so(8)](https://man7.org/linux/man-pages/man8/ld-linux.so.8.html)) and glibc
              changes other aspects of its behavior.  (See also
              [secure_getenv(3)](https://man7.org/linux/man-pages/man3/secure_getenv.3.html).)

If it is set, the "unsecure environment variables" are simply removed, effectively ignoring them [3].
Maybe this approach is better?

[1] https://elixir.bootlin.com/glibc/glibc-2.39.9000/source/sysdeps/unix/sysv/linux/dl-parse_auxv.h#L46
[2] https://man7.org/linux/man-pages/man3/getauxval.3.html
[3] https://elixir.bootlin.com/glibc/glibc-2.39.9000/source/elf/dl-support.c#L282

@boomer41
Copy link

boomer41 commented Mar 17, 2024

Alternatively, secure_getenv() [1] may be used.
When running in secure mode, the environment variable will be unset.

Edit: secure_getenv() source in [2]

[1] https://man7.org/linux/man-pages/man3/secure_getenv.3.html
[2] https://elixir.bootlin.com/glibc/glibc-2.21/source/stdlib/secure-getenv.c#L32

@matt335672
Copy link

I'd be happy with that @boomer41. It shouldn't affect normal uses of libpcsclite.so.1over RDP, and mimics existing behaviour. It's still a lot cleaner than using LD_PRELOAD or LD_LIBRARY_PATH which don't work for some sessions.

@matt335672
Copy link

@LudovicRousseau - can I PM/email you somehow about a potential security issue related to this discussion? I'm on Gitter if that helps.

@LudovicRousseau
Copy link
Owner Author

@matt335672 you email is available from https://blog.apdu.fr/articles/about_me/

@matt335672
Copy link

Thanks. Email sent from domain vocalistic.com

@frankmorgner
Copy link
Contributor

DLL Hijacking is known for quite some time and applications that don't want that, have taken counter measures against it. The concern I have, would be that LIBPCSCLITE_DELEGATE opens an other route of attack, that most applications are not aware of (and not protected against). Offering the delegation via a configuration file could be a little less risky, because changing the delegation library would require write permissions to /etc/default/pcscd, which is typically denied for the ordenary user.

BTW, would it be possible for an attacker running with user's rights to replace the system's pcscd, e.g. by running a process in the name of the user?

@LudovicRousseau
Copy link
Owner Author

BTW, would it be possible for an attacker running with user's rights to replace the system's pcscd, e.g. by running a process in the name of the user?

@frankmorgner If you define PCSCLITE_CSOCK_NAME before starting the application then libpcsclite.so.1 will use the socket name given by PCSCLITE_CSOCK_NAME instead of the default /var/run/pcscd.comm to talk to the pcscd daemon.

So it is already possible capture all the APDUs using a fake pcscd.

This mechanism was already used by some RDP servers but it is not working fine (the protocol is not defined, not everything is possible, etc.)

@frankmorgner
Copy link
Contributor

Hmm, OK, not sure if I like it...

@nathankidd
Copy link

Another way to consider the security is asking:

Does this add any capability the user didn't already have with LD_PRELOAD?

In general if a user can change their environment and write to disk then they can shoot themselves in the foot.

@boomer41
Copy link

Another way to consider the security is asking:

Does this add any capability the user didn't already have with LD_PRELOAD?

In general if a user can change their environment and write to disk then they can shoot themselves in the foot.

Because of that, my personal recommendation is to use secure_getenv (see above), so that we use the same "security policy" as LD_PRELOAD, as it basically is LD_PRELOAD with a nicer API.

@LudovicRousseau
Copy link
Owner Author

Because of that, my personal recommendation is to use secure_getenv (see above), so that we use the same "security policy" as LD_PRELOAD, as it basically is LD_PRELOAD with a nicer API.

Done in LudovicRousseau/PCSC-debug@721c264

pcsc-lite already uses secure_getenv() in https://github.com/LudovicRousseau/PCSC-debug/blob/master/src/sys_unix.c#L166 but I forgot about that.

@frankmorgner
Copy link
Contributor

As said above, DLL hijacking is known for a long time and in particular programs know how to prevent LD_PRELOAD to do any harm. I don't know if LIBPCSCLITE_DELEGATE is making matters worse with PCSCLITE_CSOCK_NAME already in place, but I can certainly say that I wasn't aware about the latter. Maybe this means that PCSC-Lite has already introduced unwanted side effects. However, as said above, I cannot evaluate the whole security model due to my lack of knowledge.

@nathankidd
Copy link

DLL hijacking is known for a long time and in particular programs know how to prevent LD_PRELOAD to do any harm

I appreciate your concern for security, so don't mean this to sound argumentative. Thinking out loud:

  • In a suid-type situation both LD_PRELOAD and secure_getenv have similar restrictions.
  • In a non-suid situation is there really anything you can do to prevent LD_PRELOAD besides a custom PT_INTERP in your ELF binary, or ensuring a static binary (no glibc)? If you are using the normal ld-linux.so as your loader then say your script sets LD_PRELOAD= before launching your binary? Too bad, ld-linux.so calls library global initializer functions before /bin/sh's main ever runs, so evil.so e.g. patches read() to skip the LD_PRELOAD=. If ld-preload-prevention.so is loaded first then the global init from evil.so can undo it. If it is loaded last how do you guarantee to undo every hook/trap evil.so left to resurrect itself later? That seems near impossible. The arms race is tilted to evil.so's favour since ld-preload-prevention.so has to be written ahead of time and evil.so can be changed by the attacker dynamically. Maybe you have knowledge about it that I haven't considered, or have you actually seen custom PT_INTERP or fully-static binaries in the real world?

@nathankidd
Copy link

Thinking about it a bit more this morning, I think if your threat model is the user account is already compromised (in order to set env) then LIBPCSCLITE_DELEGATE and LD_PRELOAD are red herrings. In that compromised environment the running process can be attached to with a debugger and read/write content intercepted.

You can't help a user that is already compromised. Show me I'm wrong. :)

@martinpaljak
Copy link
Sponsor Contributor

Maybe this development would make it easier to implement what is asked here: https://stackoverflow.com/questions/77112999/how-to-mount-pcsc-from-macos-to-linux-docker-container

@LudovicRousseau
Copy link
Owner Author

Good point @martinpaljak. LIBPCSCLITE_DELEGATE could help.

Someone would have to write a conversion between pcsc-lite and macOS PCSC framework since the APIs are not exactly the same (different types: LONGint32_t)

@LudovicRousseau
Copy link
Owner Author

LIBPCSCLITE_DELEGATE is now implemented in pcsc-lite 2.1.0
See https://blog.apdu.fr/posts/2024/04/how-to-use-libpcsclite_delegate/

Thanks everybody for your participation in the discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests