Remote Login Design

GNOME 46 introduced remote login. This post explores the architecture primarily through diagrams and tables for a clearer understanding.

Components overview


There are 4 components involved: the remote client, the GRD dispatcher daemon, the GRD handover daemon and the GDM daemon:

Component Type Responsibility
Remote Client Remote User Connects remotely via RDP. Supports RDP Server Redirection method.
Dispatcher GRD System-level daemon Handles initial connections, peeks routing token and orchestrates handovers.
Handover GRD User-level daemon Runs inside sessions (Greeter or User). Provides remote access of the session to the remote client.
GDM GDM System-level daemon Manages Displays and Sessions (Greeter or User).

API Overview


The components communicate with each other through dbus interfaces:

Exposed by GDM

org.gnome.DisplayManager.RemoteDisplayFactory

      • Method CreateRemoteDisplay
        Requests GDM to start a headless greeter. Accepts a RemoteId argument.

org.gnome.DisplayManager.RemoteDisplay

    • Property RemoteId
      The unique ID generated by the Dispatcher.
    • Property SessionId
      The session ID of the created session wrapped by this display.

 

Exposed by GRD Dispatcher

org.gnome.RemoteDesktop.Dispatcher

    • Method RequestHandover
      Returns the object path of the Handover interface matching the caller’s session ID.

org.gnome.RemoteDesktop.Handover

Dynamically created. One for each remote session.

    • Method StartHandover
      Initiates the handover process. Receives one-time username/password, returns certificate and key used by dispatcher.
    • Method TakeClient
      Gives the file descriptor of the remote client’s connection to the caller.
    • Signal TakeClientReady
      Informs that a file descriptor is ready to be taken.
    • Signal RedirectClient
      Instructs the source session to redirect the remote client to the destination session.

Flow Overview


Flow phase 1: Initial connection to greeter session

1. Connection:

    • Dispatcher receives a new connection from a Remote Client. Peeks the first bytes and doesn’t find a routing token. This means this is a new connection.

2. Authentication:

    • Dispatcher authenticates the Remote Client using system level credentials.

3. Session Request:

    • Dispatcher generates a unique remote_id (also known as routing token), and calls CreateRemoteDisplay() on GDM with this remote_id.

4. Registration:

    • GDM starts a headless greeter session.
    • GDM exposes RemoteDisplay object with RemoteId and SessionId.
    • Dispatcher detects new object. Matches RemoteId. Creates Handover D-Bus interface for this SessionId.

5. Handover Setup:

    • Handover is started in the headless greeter session.
    • Handover calls RequestHandover() to get its D-Bus object path with the Handover interface.
    • Handover calls StartHandover() with autogenerated one-time credentials. Gets from that call the certificate and key (to be used when Remote Client connects).

6. Redirection (The “Handover”):

    • Dispatcher performs RDP Server Redirection sending the one-time credentials, routing token (remote_id) and certificate.
    • Remote Client disconnects and reconnects.
    • Dispatcher peeks bytes; finds valid routing token.
    • Dispatcher emits TakeClientReady on the Handover interface.

7. Finalization:

    • Handover calls TakeClient() and gets the file descriptor of the Remote Client‘s connection.
    • Remote Client is connected to the headless greeter session.

Flow phase 2: Session transition (from greeter to user)

1. Session Creation:

    • User authenticates.
    • GDM starts a headless user session.

2. Registration:

    • GDM exposes a new RemoteDisplay with the same RemoteId and a new SessionId.
    • Dispatcher detects a RemoteId collision.
    • State Update: Dispatcher creates a new Handover D-Bus interface (dst) to be used by the New Handover in the headless user session.
    • The Existing Handover remains connected to its original Handover interface (src).

3. Handover Setup:

    • New Handover is started in the headless user session.
    • New Handover calls RequestHandover() to obtain its D-Bus object path with the Handover interface.
    • New Handover calls StartHandover() with new one-time credentials and receives the certificate and key.

4. Redirection Chain:

    • Dispatcher receives StartHandover() from dst.
    • Dispatcher emits RedirectClient on src (headless greeter session) with the new one-time credentials.
    • Existing Handover receives the signal and performs RDP Server Redirection.

5. Reconnection:

    • Remote Client disconnects and reconnects.
    • Dispatcher peeks bytes and finds a valid routing token (remote_id).
    • Dispatcher resolves the remote_id to the destination Handover (dst).
    • Dispatcher emits TakeClientReady on dst.

6. Finalization:

    • New Handover calls TakeClient() and receives the file descriptor of the Remote Client‘s connection.
    • Remote Client is connected to the headless user session.

 

Disclaimer

Please note that while this post outlines the basic architectural structure and logic, it may not guarantee a 100% match with the actual implementation at any given time. The codebase is subject to ongoing refactoring and potential improvements.