Re: Proposal for a new VPN DBUS interface

On Tue, 2007-05-08 at 10:55 +0200, Martin Willi wrote:
> Hi,
> I'd like to share some thoughts about an improved DBUS-API between
> NetworkManager and a VPN service. I think we need some changes here to
> support multiple active connections per service, and to allow multiple
> services to have active connections at the same time.
> We have to consider that we are targeting different kinds of services.
> Some launch a separate process for each tunnel, others use a long
> running daemon and handle multiple tunnels.
> VPN service:
> ------------
> I think we should define a generic DBUS interface for VPN services.
> Currently, all services are implementing their own interface. This
> doesn't make sense to me.


> A VPN service should handle multiple connections. In the
> process-per-tunnel case, a helper (the vpn.service interface
> implementer) could launch multiple processes and manage them. In the
> case of a multiple-tunnel-daemon, the daemon could implement the DBUS
> API on it's own (or using a helper).

Also right.

> A generic VPN service could look like this:
> org.freedesktop.networkmanager.vpn.service
>    startConnection(in str name, in array(struct{str key, str value}));
>    stopConnection(in str name);
>    getConnections(out array(struct{str name, int state));
> startConnections() has a list of implementation specific options (from
> the configuration dialog). The name uniquely defines the connections
> name. The getConnections() call lists all connections with its current
> state.
> To notify NetworkManager (and others) about the connections state, a
> service sends signals (as it currently does):
>    stateChange(str name, int state);
>    connectionEstablished(str name, str banner,
>                          array(struct{str key, str value}));
>    connectionFailed(str name, str reason);
> If an asynchronous call to startConnection() is successful,
> connectionEstablished() is signaled. It contains a list of configuration
> options (currently IP4Config). It may contain options for IPv4 and IPv6,
> as a tunnel may establish mixed routes.


> If connection setup fails, a single signal seems sufficient to me. The
> reason gives the user information about what has failed. We may consider
> to include a numeric failure code to handle them more generic.

Sounds good.

> Authentication service:
> -----------------------
> The current authentication mechanism seems to be too limited to me. The
> daemon should be able to request authentication data actively. This is
> needed to:
> - Do proper re-authentication
> - The VPN gateway may decide how to authenticate (e.g. EAP in IKEv2).
>   We can't authenticate in previous, as we don't know which
>   authentication method will be used.
> The nm-applet could provide a "authentication facility", which allows a
> VPN service to request authentication. This would allow us to reuse
> authentication dialogs and integrate them using shared libs.
> This authentication service could listen to signals emitted by the
> daemon:
>    authRequest(str name, str type, bool prompt, 
>                array(struct{str key, str value}));

The auth requests need to go through NetworkManager first, because NM
may have some system-wide admin-configured settings for the VPN client
that supersede any use input.  If NM determines that additional input is
necessary, it will ask for that input from the right source, which might
be the applet.

I'm inclined to think that we should have the VPN stuff be a private
dbus connection and not on the bus for security's sake.  There's really
no reason for the VPN stuff to be on a shared bus since NM gets to gate
the validity of VPN plugins anyway (ie, enforcing correctness of
the .service file, etc).

When a user-agent requests a VPN connection, it should be assigned a
transaction ID and a "cookie".  The transaction ID should be publicly
available (for example, sent out in signals on the shared bus).  The
cookie is known only to the initiator and is used to verify that an
authentication response comes from the appropriate source.

1) Applet requests "Work VPN" be activated
2) NM returns (transaction ID, cookie) as the D-Bus method return args
to the applet
3) NM calls the vpn daemon and requests the VPN activation
4) VPN daemon needs auth info; asks NM for it
5) NM determines it does not have the required auth info
6) NM broadcasts a signal on the system bus requesting auth info for the
transaction ID from step (2)
7) applet notices, pops up dialog, gets new auth info
8) applet calls NM's newAuthData() method, passing both the transaction
ID and the cookie
9) if the cookie does not match step (2), NM ignores the call
10) if the cookie does match step (2), NM passes the info back to the
VPN daemon

Essentially, only the entity requesting the VPN connection can/should
provide authentication information for that connection.

> If the authentication facility receives such a signal, it pops up the
> dialog using the key/value strings (containing challenges, ...), or, if
> prompt is not set, fetches them from the keyring. The type says which
> authentication dialog should be used. Then it calls the vpn.service's
> authData method.
>    authData(in str name, in array(struct{str key, str value}));
> Summary:
> --------
> The complete VPN service would look like this:
> org.freedesktop.networkmanager.vpn.service
>    /* 
>     * METHODS
>     */
>    /* start a connection called "name", using options in array */
>    startConnection(in str name, in array(struct{str key, str value}));
>    /* stop a previously started connection called "name" */
>    stopConnection(in str name);
>    /* get a list of active connections, and its state */
>    getConnections(out array(struct{str name, int state));
>    /* pass authentication data for connection "name" to the service */
>    authData(in str name, in array(struct{str key, str value}));
>    /*
>     */
>    /* the state of the connection "name" has changed to "state" */
>    stateChange(str name, int state);
>    /* the connection "name" has been established, options in array */
>    connectionEstablished(str name, str banner,
>                          array(struct{str key, str value}));
>    /* the connection "name" failed to establish, because of "reason" */
>    connectionFailed(str name, str reason);
>    /* the daemon needs authentication data for "name" of "type" */
>    authRequest(str name, str type, array(struct{str key, str value}));
> We could use "dicts" for the key/value arrays, but it seems a bit
> overkill. Every VPN service would need the somewhat complicated "dict"
> routines, so I would prefer simple array of strings. Or are/will be
> these dicts parts of DBUS?

Dicts are quite easy to use in D-Bus languages other than C, and
bindings to various toolkits like glib make it a lot easier too.  I
guess I'd prefer to use dicts for clear key/value separate (then we
don't have to care about specifying a separator and doing parsing

In python, dicts are essentially:

    myDict = {'key': 'value'}

String parsing would be more complex in some ways, simpler in others,
but I'd prefer dicts if we can use them.  Plus, we can specify argument
types, like having the IP address be a uint32_t rather than having to
call inet_aton() and such in NM.

> I'd like to hear what you think about a new DBUS interface. I'm
> currently implementing a NM interface into strongSwan (an IPsec
> solution). But the current interface limits its functionality, so I
> think we need a more powerful API.

No doubt, we certainly do.  Sorry for the lag.  The interface looks good
from what I can see.  The artificial restriction for one vpn at a time
wasn't intended to last this long.


> Awaiting your comments...
> Martin
> _______________________________________________
> NetworkManager-list mailing list
> NetworkManager-list gnome org

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]