SessionManagementArchitecture - neutrinolabs/xrdp GitHub Wiki

Overview

This page applies to versions of xrdp after v0.9.x

Session management is handled by two processes:-

  • xrdp-sesexec (or just sesexec) is a low-level process, responsible for the following:-

    • Authenticating and authorizing users (typically using username/password), and for any authentication dialogs.
    • Handling the PAM session for a single xrdp session.
    • Managing the top level processes for a single xrdp session.
  • xrdp-sesman (or just sesman) is a higher-level process responsible for the following:-

    • Single point-of-contact for all client requests (there is only one sesman in a system)
    • Answering user queries and requests for general session data.
    • Managing all the xrdp-sesexec instances in a system.

Process relationships

All session management client requests come in to sesman over a protocol called SCP (sesman control protocol).

Broadly speaking, sesman will do one of three things with a client request:-

  1. Answer it directly (e.g. 'list sessions for a user')
  2. Start an instance of sesexec to handle the request (e.g. 'log a user in using username/password').
  3. Use an existing instance of sesexec to handle the request (e.g. 'reconnect to an existing session').

Communications between sesman and sesexec happen over a protocol called ECP (executive control protocol). This protocol is split into two sub-protocols:-

  1. EICP (executive initialization control protocol) is used to handle initial requests from sesman, before an xrdp session is started.
  2. ERCP (executive runtime control protocol) deals exclusively with running xrdp sessions.

This split makes the code in sesman easier to follow. Clients have absolutely no visibility of EICP or ERCP.

sesman can pass an active SCP file descriptor to sesexec to handle a specific request. One example where this is required is where an authentication dialog needs to happen between the client and sesexec. There is no need for sesman to be involved with this, so it simply passes the SCP session to a sesexec instance and waits to be informed of the result.

This entity relationship diagram shows how the protocols fit in with the processes described so far:-

erDiagram

    Client }|..|| sesman : SCP
    sesman ||..|{ sesexec : "EICP/ERCP"
    Client }|..|{ sesexec : SCP

User authentication and authorization

All operations requested over SCP require a user to be authenticated and authorized. This is called a user login and happens right at the start of an SCP session.

Two sorts of user logins are provided by SCP:-

  1. A UDS login uses the UID and GID present on the other end of a Unix Domain Socket (UDS) to identify a user. The user is passed through a few authorization checks.
  2. A sys login authenticates and authorizes a user by using credentials supplied by a client. This is typically a username and password, although other credentials may be used if PAM is supported.

UDS logins are lightweight, and are typically used by clients to do such operations as listing the xrdp sessions created by a user. These are handled directly by sesman, as there is often no need to start a sesexec process for this.

Sys logins can be simple, but may involve a dialog with the client. For example, if a wrong password is provided, it may be necessary to reprompt the user. Sys logins always require a sesexec process to be started.

The sesman process

SCP interface

sesman listens for incoming connections on a Unix Domain Socket. The location of the socket is specified in the sesman.ini file.

General design

Broadly speaking, sesman consists of two lists of connections:-

  1. The pre-session list handles all SCP sessions which have not yet resulted in an xrdp session being started.
  2. The session list keeps track of all active xrdp sessions.

The reason for this split is the need to restart sesman (this is not yet implemented). The only important items tracked by sesman are long-running xrdp sessions. When sesman is restarted it can contact all the sesexec processes in the system and ask them for their session details. This allows the session list to be repopulated.

Pre-session list items may or may not have a sesexec process associated with them. If they do, sesman will communicate with these processes using EICP.

Session list items always have a sesexec process associated with them. Sesman will communicate with these processes using ERCP. When the sesexec process finishes, sesman will consider the session to be finished.

The sesexec executable

Design drivers

The design of sesexec is driven by the following:-

  • All PAM lifecycle functions are best called from a single process. This is not explicitly stated in the PAM specification. However not doing this can result in memory leaks and can also result in interoperability problems with frameworks that assume this is done (e.g. systemd on Linux). These lessons were learned with xrdp versions v0.9.x and earlier which split PAM authorization and PAM session management over two separate processes.
  • The relationship between sesman and a session is now incorporated in the ECP file descriptor rather than in a process parent/child relationship. This allows for sesman to be restarted, although this is not yet implemented. The parent/child model used in xrdp v0.9.x and earlier could never support a restart of sesman.
  • Using a clear process boundary allows for a clearer separation of responsibilities between sesman and sesexec.

Starting sesexec

Starting sesexec is quite complex, as it is necessary to create a Unix Domain socket between sesman and sesexec when this is done. This socket carries the EICP protocol initially used between the two.

A wrapper module sesexec_control.c within sesman is used to start sesexec instances.

Logging in users (sys login)

When sesexec is passed an EICP request to log a user in, it is also passed the SCP file descriptor from sesman. sesexec can use this file descriptor to communicate directly with the client when an authentication dialog is required.

After authentication is complete, the SCP file descriptor is passed back to sesman.

Session process management

Sesexec uses this process to start a session:-

  1. An X server is started.
  2. A small test program (see #2492) is then started to check the X server is fully operational.
  3. The window manager is started.
  4. The chansrv process is started.

These processes are all child processes of sesexec, and all run with the UID of the session user. They run in a separate process group from sesexec to avoid problems with signals being sent to the process group.

sesexec waits for the window manager process to exit. When it does, SIGTERM is sent to the other two processes. When these have also exited, sesexec itself cleans up the PAM session and finishes.

Debugging sesexec

While sesexec is running, a debugger can simply be attached to the running process. This enables some problems to be debugged. However, ther is often a need to start sesexec under the control of a debugger, or other program (e.g. valgrind).

To do this:-

  1. Find where sesexec is installed on the system. This is typically in a location like /usr/local/libexec/xrdp/xrdp-sesexec.

  2. Add a .prev extension to the executable, e.g.:-

    sudo mv /usr/local/libexec/xrdp/xrdp-sesexec /usr/local/libexec/xrdp/xrdp-sesexec.prev
  3. Create a new file /usr/local/libexec/xrdp/xrdp-sesexec (or whatever) with the following contents:-

    #!/bin/bash
    
    # To run sesexec under the debugger, uncomment this line, and
    # install `gdbserver`
    # When sesman starts sesexec, you can connect to it from gdb or a 
    # graphical debugger using something like:-
    #
    #   $ gdb /usr/local/libexec/xrdp/rdp-sesexec.prev
    #   (gdb) target remote localhost:2159
    #   (gdb) break <whatever>
    #   (gdb) run <params>
    #exec gdbserver :2159 $0.prev
    
    # To run sesexec under valgrind, uncomment this line, and install
    # 'valgrind':-
    
    #exec valgrind --leak-check=full $0.prev "$@" >/tmp/valgrind.log 2>&1
    
    exec -a xrdp-sesexec $0.prev "$@"

    You may need to change the initial shebang (#!) line if bash is not installed on your system as /bin/bash.

  4. Make sure the file is executable.

  5. Depending on what you are intending to do, uncomment one of the exec lines in the file, or event add a new section.

  6. Using an SCP client, cause sesman to start a new sesexec process.

  7. (If you want to debug sesexec). Attach a debugger to gdbserver as instructed in the file.

Example sequence diagram

As an example of the above concepts, consider a client request to authenticate a user and then start a session.

sequenceDiagram
client ->> sesman : [SCP] Connect
sesman ->> sesman : Create new entry on pre-session list
client ->> sesman : [SCP] Authenticate 'User1', password 'Password1'
sesman ->> sesman : Start new sesexec instance
sesman ->> sesexec : [EICP] Authenticate 'User1', password 'Password1'
sesexec ->> client : [SCP] Password incorrect
client ->> sesexec : [SCP] Authenticate 'User1', password 'Hunter2'
sesexec ->> client : [SCP] User 'User1' authenticated
sesexec ->> sesman : [EICP] Login OK
client ->> sesman : [SCP] Start xrdp session
sesman ->> sesexec : [EICP] Start xrdp session
sesman ->> sesman : Move session to from 'pre-session list' to 'session list'
sesexec ->> client : [SCP] Session started OK
sesexec ->> sesman : [ERCP] Session started event
⚠️ **GitHub.com Fallback** ⚠️