After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 767872 - [review] lr/p11-forward: Use PKCS#11 remoting
[review] lr/p11-forward: Use PKCS#11 remoting
Status: RESOLVED OBSOLETE
Product: NetworkManager
Classification: Platform
Component: general
git master
Other Linux
: Normal normal
: ---
Assigned To: NetworkManager maintainer(s)
NetworkManager maintainer(s)
Depends on:
Blocks: nm-next
 
 
Reported: 2016-06-20 13:35 UTC by Lubomir Rintel
Modified: 2020-11-12 14:30 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Lubomir Rintel 2016-06-20 13:35:19 UTC
Problem scope
=============

Some connections (VPN, wpa_supplicant) need to access objects on PKCS#11 tokens
for authentication & encryption. The objects on PKCS#11 tokens are identified
by URIs (RFC 7512). p11-kit provides a mechanism to pick the correct token and
module for a given PKCS#11 URI.

The user can typically access a token from his user session, but
NetworkManager runs outside the user session. Some PKCS#11 URIs can identify
different objects in different user sessions (e.g. ones used by GNOME
Keyring software token). Luckily, p11-kit also provides a remoting protocol
that allows using a module from the different process than one that accesses
the actual token.

The user session typically runs one or more agents (GNOME Shell, nm-applet,
nmcli agent) that register with NetworkManager and service requests that need
to be done in the user session; notably requests for secrets. The secret agent
should be used to access the PKCS#11 token and provide an access channel for the
token user (VPN daemon, wpa_supplicant).

Currently, NetworkManager only supports key pairs in PKCS#12 or PEM files.
Apart from not being able to utilize the hardware tokens, this needs
the daemons to access the file in user home directory which causes all sorts
of trouble: It does not play well with SELinux and as the daemons are
privileged it's difficult to guard them against malicious users (e.g. users
can point the daemons to /dev/watchdog or /dev/zero and shut down the system
or consume memory and race-free validation is difficult if possible).


Design
======

A PKCS#11 URI that identifies a key pair should be a part of the connection
configuration.

The secret agent would spawn a p11-kit remoting agent in a user session when
requested. One option would be to use a "p11-kit remote" command and extend it
with a capability to pick the right module for a given URI. Other option would
be to link the agent with p11-kit, pick the right module and serve it via
p11_kit_remote_serve_module(). The first option is preferred, because in
either case we need to spawn a separate process, but in we wouldn't need the
agent to link against p11-kit keeping the dependency chain smaller.

NetworkManager APIs should be extended to allow passing the file descriptor
that is connected to the p11-kit remoting agent from the secret agent to the
PKCS#11 user.

The PKCS#11 user (a VPN daemon, wpa_supplicant...) that is able to use the
PKCS#11 URIs either uses the p11-kit-proxy.so module or the p11-kit API.
Currently the use of the remoting protocol is limited to a communication
with a command statically defined in p11-kit configuration. p11-kit doesn't
provide an API that would be able to utilize a remote module; nor is there
a way to make p11-kit-proxy.so use a remote module instead. Solving this need
cooperation with p11-kit upstream; current solution uses an environment
variable to pass the information about open file descriptor; there may be
a better way to do this.


Sequence diagram
================

Example of a VPN connection. All communication is on the system D-Bus.

    Runs in the
    user session              Runs outside the user session
 ---------------------     -------------------------------------------
/                     \   /                                           \

    Secret Agent              NetworkManager              VPN plugin
           |                        |                          |
           |                        |                          |
           |                        |  NeedP11Fd call (con)    |
           |                        |------------------------->|
           |                        |                          |
           |                        |                          |
           |                        |                  checks if the given
           |                        |                  connection uses a
           |                        |                  PKCS#11 URI
           |                        |                          |
           |                        |                          |
           |                        |  NeedP11Fd return (uri)  |
           |                        |<-------------------------|
           |                        |                          |
           |                        |                          |
           |             the agent manager does                |
           |             the usual procedure to                |
           |             pick the right agent                  |
           |                        |                          |
           |                        |                          |
           |  GetP11Fd call (uri)   |                          |
           |<-----------------------|                          |
           |                        |                          |
           |                        |                          |
spawns the "p11-kit remote          |                          |
--uri <uri> with stdin/stdout       |                          |
bound to a socketpair fd            |                          |
           |                        |                          |
           |                        |                          |
           |  GetP11Fd return (fd)  |                          |
           |----------------------->|                          |
           |                        |                          |
           |                        |  P11Fd call (fd)         |
           |                        |------------------------->|
           |                        |                          |
           |                        |                     remembers fd,
           |                        |                     keeps it open
           |                        |                          |
           |                        |  P11Fd return ()         |
           |                        |<-------------------------|
           |                        |                          |
           |                        |   Connect ...            |
           |                        |------------------------->|
           |                        |                          |
           |                        |             spawns the VPN daemon,
           |                        |             exports P11_REMOTE_FD=<fd>
           |                        |             into its environment


Issues
======

* p11-kit-remote requires a module name and what we have is a PKCS#11 URI
* Need a way to make p11-kit use file descriptor
* How do we tell if we have a p11-kit version that would be able to do the
  above and fail sensibly instead of using a local p11-kit?
* Do we need remoting for all PKCS#11 accesses? Probably not.
* UI changes are not done. Need a certificate picker and a way to preserve the
  file certificates for existing connections.
* Is there a file-backed PKCS#11 module that could be used with PEM files?
* When using a GNOME Keyring software token it always asks for a PIN. Storing
  the password to the keyring along with other secrets in NetworkManager is
  perhaps a bad idea.
* Some VPN daemons are bad at using PKCS#11. OpenVPN doesn't seem to use
  PKCS#11 for the CA certificate; Librewswan would need a way to pass the
  p11-kit remoting fd through the control socket.


Implementation
==============

p11-kit: https://github.com/NetworkManager/p11-kit/commits/lr/NM-p11
NetworkManager: https://cgit.freedesktop.org/NetworkManager/NetworkManager/log/?h=lr/p11-forward
network-manager-openvpn: https://git.gnome.org/browse/network-manager-openvpn/log/?h=lr/p11-forward
Comment 1 Lubomir Rintel 2016-06-20 13:57:45 UTC
A p11-kit mailing list thread: https://lists.freedesktop.org/archives/p11-glue/2016-June/000604.html
Comment 2 Beniamino Galvani 2016-06-24 11:39:54 UTC
If I understand correctly the mechanism to be used in p11-kit to pass the file descriptor is still under discussion. But, whatever the result is, the approach in NM and NM-openvpn branches looks good to me.
Comment 3 Dan Williams 2016-06-27 16:33:32 UTC
What would the wpa_supplicant case look like?  Something like this?

a) install engine_pkcs11 from the OpenSC project
b) install p11kit
c) verify that "p11tool --list-tokens" shows all the tokens that "p11-kit list-modules" shows

    Runs in the
    user session              Runs outside the user session
 ---------------------     -------------------------------------------
/                     \   /                                           \

    Secret Agent              NetworkManager            wpa_supplicant
           |                        |                          |
           |                        |                          |
           |                checks if the given                |
           |                 connection uses a                 |
           |                   PKCS#11 URI                     |
           |                        |                          |
           |             the agent manager does                |
           |             the usual procedure to                |
           |             pick the right agent                  |
           |                        |                          |
           |                        |                          |
           |  GetP11Fd call (uri)   |                          |
           |<-----------------------|                          |
           |                        |                          |
           |                        |                          |
spawns the "p11-kit remote          |                          |
--uri <uri> with stdin/stdout       |                          |
bound to a socketpair fd            |                          |
           |                        |                          |
           |                        |                          |
           |  GetP11Fd return (fd)  |                          |
           |----------------------->|                          |
           |                        |                          |
           |                        |                          |
           |                        | SetPKCS11EngineAndModulePath()
           |                        |   engine=/lib64/openssl/engines/libpkcs11.so
           |                        |   module=/lib64/p11-kit-proxy.so (????)
           |                        |                          |
                         <....................>
           |                        |                          |
           |                        |   SelectNetwork ...      |
           |                        |------------------------->|
           |                        |                          |
                         <????????????????????>
Comment 4 David Woodhouse 2016-06-27 18:51:27 UTC
Well, if you *spawn* wpa_supplicant then you pass in the fd you got from GetP11Fd as fd#3 or something. Then wpa_supplicant loads p11-kit-remote.so as its provider module, and p11-kit-remote.so uses the fd.

Note that your SetPKCS11EngineAndModulePath() in the above is redundant since that's the default now. If you tell wpa_supplicant your cert is 'pkcs11:...' then it'll do that automatically.

(In your case, it seems that p11-kit-remote.so is being loaded by p11-kit-proxy.so because it's part of the standard set of modules which is configured to be available to wpa_supplicant when run as the root user. Alternatively, we could explicitly request p11-kit-remote.so instead of letting it use p11-kit-proxy.so to load the standard set.)

That kind of works if you're spawning wpa_supplicant. We don't (per-connection) though; so we'd need to be able to *pass* a fd to it. Or a path to a UNIX socket which it could use for the remoting instead of a fd?
Comment 5 Lubomir Rintel 2016-06-28 14:15:07 UTC
(In reply to Dan Williams from comment #3)
> What would the wpa_supplicant case look like?  Something like this?

>     Runs in the
>     user session              Runs outside the user session
>  ---------------------     -------------------------------------------
> /                     \   /                                           \
....
>            |                        |                          |
>            |  GetP11Fd return (fd)  |                          |
>            |----------------------->|                          |
>            |                        |                          |

Until this point yes.

>            |                        |                          |
>            |                        | SetPKCS11EngineAndModulePath()
>            |                        |  
> engine=/lib64/openssl/engines/libpkcs11.so
>            |                        |   module=/lib64/p11-kit-proxy.so (????)
>            |                        |                          |
>                          <....................>

David suggests below that this is unneeded.

>            |                        |                          |
>            |                        |   SelectNetwork ...      |
>            |                        |------------------------->|
>            |                        |                          |
>                          <????????????????????>

Before we do select a Network, we need to set the PKCS#11 id. That is probably done with AddBlob? Maybe there should be an analogous call that would pass a Fd as well? AddBlobWithFd or just an extra AddFd?

(In reply to David Woodhouse from comment #4)
> Well, if you *spawn* wpa_supplicant then you pass in the fd you got from
> GetP11Fd as fd#3 or something. Then wpa_supplicant loads p11-kit-remote.so
> as its provider module, and p11-kit-remote.so uses the fd.

We don't spawn it. We'll need to extend the D-Bus API to allow passing the file descriptor. Then we need to figure out a way to pass it to the p11-kit-proxy.so module.
Comment 6 André Klapper 2020-11-12 14:30:27 UTC
bugzilla.gnome.org is being shut down in favor of a GitLab instance. 
We are closing all old bug reports and feature requests in GNOME Bugzilla which have not seen updates for a long time.

If you still use NetworkManager and if you still see this bug / want this feature in a recent and supported version of NetworkManager, then please feel free to report it at https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/

Thank you for creating this report and we are sorry it could not be implemented (workforce and time is unfortunately limited).