-
Notifications
You must be signed in to change notification settings - Fork 3
Authentication architecture redesign
This page summarises some architectural changes that will enable these features:-
- full PAM conversation support
- a solution to https://github.com/neutrinolabs/xrdp/issues/1684
Date | Comment |
---|---|
23-Aug-2021 | Extended to cover xrdp-sesadmin dataflows |
24-Aug-2021 | Addressed review comments from @aquesnel and separated out connect and authentication phases |
26-Aug-2021 | Clarifications and typos identified by @aquesnel are fixed. |
6-May-2022 | Add UDS authentication and file descriptor detail |
18-May-2022 | PAM conversation extracted as separate dataflow, and implementation details added. |
6-Sep-2022 | Clarifications - no changes |
21-Nov-2022 | Use 'login' rather than 'authenticate'. Clarify multiple UDS logins are unsupported. Replace 'xrdp done' with 'logout' and 'close connection'. |
11-Jan-2023 | Specify file descriptor passing |
17-Jan-2023 | Replace sdriver with sesexec |
This page contains some embedded PlantUML diagrams, the source of which is in the dropdown below.
Diagram sources
/'============================================================================ Overview diagramThis has mostly been put together by @aquesnel =========================================================================='/
@startuml overview participant "Rdp Client" AS client participant "Xrdp" AS xrdpListner collections "xrdp per client" AS xrdp participant "Session Manager (xrdp-sesman)" AS sesman collections "Session Executive" AS sesexec participant "PAM" AS pam participant "Window Manager" AS wm participant "X Server" AS x participant "Channel Server (xrdp-chansrv)" AS chansrv
== Session Initialize ==
activate xrdpListner activate client client -> xrdpListner: connect xrdpListner -> xrdp: fork \n[xrdp_listen_fork(/xrdp/xrdp/xrdp_listen.c)] activate xrdp deactivate xrdpListner
== Connect ==
client -> xrdp: send credentials xrdp -> sesman: connect activate sesman
== Login ==
xrdp -> sesman : Login using PAM create sesexec activate sesexec sesman -> sesexec: fork and set up a communications link sesman -> sesexec: Login using PAM with this xrdp file descriptor sesman -X sesman : Sesman closes xrdp file descriptor copy.
ref over xrdp, sesexec, pam : PAM Conversation
sesman <- sesexec : Login result success, + xrdp file descriptor sesexec -X sesexec : sesexec closes file descriptor copy xrdp <- sesman : Login result success
=== New session ===
xrdp -> sesman : Connect to session sesman -> sesexec : Start session on display D + xrdp file descriptor sesman -X sesman : sesman closes xrdp file descriptor copy deactivate sesman
sesexec -> x: fork activate x sesexec -> chansrv: fork activate chansrv chansrv -> x: connect sesexec -> wm: fork activate wm wm -> x: connect
xrdp <- sesexec : Session started OK on display D xrdp -X xrdp : xrdp closes the file descriptor to sesman/sesexec sesexec -X sesexec : sesexec closes xrdp file descriptor copy sesexec -> sesexec: wait for window manager process to terminate deactivate sesexec
xrdp -> x: connect \n[lib_mod_connect(/xrdp/xup/xup.c)] xrdp -> chansrv: connect
== Session In Progress ==
loop until window manager process terminates x -> xrdp: display data xrdp -> client: display data chansrv -> xrdp: channel data xrdp -> client: channel data
client -> xrdp: input data xrdp -> x: input data client -> xrdp: channel data xrdp -> chansrv: channel data end
== Session End ==
note right of wm Some event (eg. user input) causes the Window Manager to terminate end note
destroy wm wm -> sesexec: window manager terminated activate sesexec
sesexec -> x: send SIGTERM sesexec -> x: wait for X server process to terminate xrdp <- x : X server closes connection xrdp -> client: disconnect destroy x destroy xrdp destroy client sesexec <- x: X server terminated
sesexec -> chansrv: send SIGTERM sesexec -> chansrv: wait for channel server to terminate destroy chansrv sesexec <- chansrv: Channel server terminated
sesexec -> pam: end PAM session destroy pam
sesexec -> sesexec: clean up sockets destroy sesexec sesexec -> sesman: Xrdp-Session terminated activate sesman sesman -> sesman: remove session from list of active sessions \n[sig_sesman_session_end(/xrdp/sesman/sig.c)] deactivate sesman
@enduml
'============================================================================
@startuml connect participant xrdp participant sesman
activate xrdp activate sesman
xrdp -> sesman : connect
@enduml
'============================================================================
@startuml uds-login-fail participant xrdp participant sesman
activate xrdp activate sesman
xrdp -> sesman : Login using UDS credentials xrdp <- sesman : Login result fail xrdp X- sesman : Sesman closes connection note over xrdp: No retries are allowed for UDS login attempts destroy xrdp
@enduml
'============================================================================
@startuml uds-login-ok participant xrdp participant sesman
activate xrdp activate sesman
xrdp -> sesman : Login using UDS credentials xrdp <- sesman : Login result success
@enduml
'============================================================================
@startuml pam-conversation participant xrdp participant sesexec participant "PAM" AS pam
activate xrdp activate sesexec activate pam sesexec -> pam: pam_start() sesexec <- pam : pam_conv() callback 1 xrdp <- sesexec : PAM prompt 1 xrdp -> sesexec : PAM response 1 ... Some messages ... sesexec <- pam : pam_conv() callback n xrdp <- sesexec : PAM prompt n xrdp -> sesexec : PAM response n sesexec <- pam : login result (success/fail) destroy pam
@enduml
'============================================================================
@startuml pam-login-fail participant xrdp participant sesman participant sesexec participant "PAM" AS pam
activate xrdp activate sesman
xrdp -> sesman : Login using PAM alt If sesexec not active for connection create sesexec activate sesexec sesman -> sesexec : fork and set up a communications link end sesman -> sesexec: Login using PAM with this xrdp file descriptor sesman -X sesman : Sesman closes xrdp file descriptor copy. deactivate sesman
ref over xrdp, sesexec, pam : PAM Conversation sesman <- sesexec : Login result fail activate sesman deactivate sesexec xrdp <- sesman : Login result fail note over sesman: Login result fail message contains a retry flag alt sesman detects MaxLoginRetryLimit reached (#1739)
xrdp X- sesman : Sesman closes connection note over xrdp: xrdp checks retry flag and exits destroy xrdp activate sesexec sesman -X sesexec : Closes sesexec connection sesexec -> sesexec : sesexec exits destroy sesexec
else limit not reached note over xrdp xrdp can issue another login message end note end
@enduml
'============================================================================
@startuml pam-login-ok participant xrdp participant sesman participant sesexec participant "PAM" AS pam
activate xrdp activate sesman
xrdp -> sesman : Login using PAM alt If sesexec not active for connection create sesexec activate sesexec sesman -> sesexec : fork and set up a communications link end sesman -> sesexec: Login using PAM with this xrdp file descriptor sesman -X sesman : Sesman closes xrdp file descriptor copy.
ref over xrdp, sesexec, pam : PAM Conversation sesman <- sesexec : Login result success, + xrdp file descriptor sesexec -X sesexec : sesexec closes xrdp file descriptor copy deactivate sesexec xrdp <- sesman : Login result success
@enduml
'============================================================================
@startuml newsession
participant xrdp participant sesman participant sesexec participant "PAM" AS pam participant "Session Processes" AS wmsession
ref over xrdp, sesman, sesexec, pam : This is preceded by a successful login sequence
activate xrdp activate sesman activate pam
xrdp -> sesman : Connect to session alt If sesexec not active for connection create sesexec activate sesexec sesman -> sesexec : fork and set up a communications link end note over sesman : sesman picks a suitable display number sesman -> sesexec : Start session on display D with this xrdp file descriptor sesman -X sesman : sesman closes xrdp file descriptor copy deactivate sesman sesexec -> pam : pam_open_session() sesexec -> wmsession : sesexec forks session processes on display D activate wmsession xrdp <- sesexec : Session started OK on display D xrdp -X xrdp : xrdp closes the file descriptor to sesman/sesexec sesexec -X sesexec : sesexec closes xrdp file descriptor copy note over sesman, sesexec : No more direct messages are exchanged with xrdp
@enduml
'============================================================================
@startuml samesession participant xrdp participant sesman participant sesexec
ref over xrdp, sesman, sesexec : This is preceded by a successful login sequence
activate xrdp activate sesexec activate sesman
xrdp -> sesman : Connect to session note over sesman: Sesman finds an already active session xrdp <- sesman: Connect to display D xrdp -X xrdp : xrdp closes the file descriptor to sesman sesman -X sesman : sesman closes xrdp file descriptor alt If sesexec was activated to authenticate user sesman -> sesexec : Closedown sesexec -X sesexec : sesexec exits destroy sesexec end
@enduml
'============================================================================
@startuml runsession
participant xrdp participant sesman participant sesexec participant "PAM" AS pam participant "Session Processes" AS wmsession
ref over xrdp, sesman, sesexec, pam, wmsession : This is preceded by connect session
activate xrdp activate sesexec activate pam activate wmsession
xrdp -> wmsession : xrdp connects to session ... Session runs ... sesexec <- wmsession : User closes session xrdp <- wmsession : X server exits destroy xrdp destroy wmsession sesexec -> pam : pam_close_session() / pam_end() destroy pam
activate sesman sesman X- sesexec : sesexec closes sesman connection and exits destroy sesexec
sesman -> sesman: remove session from list of active sessions
@enduml
'============================================================================
@startuml list-sessions participant xrdp participant sesman
ref over xrdp, sesman : This is preceded by a successful login sequence
activate xrdp activate sesman
xrdp -> sesman : List sessions loop for each session xrdp <- sesman : Session data note right : Contains end-of-list marker end
@enduml
'============================================================================
@startuml logout participant xrdp participant sesman participant sesexec participant "PAM" AS pam
activate xrdp activate sesman activate sesexec activate pam
ref over xrdp, sesman, sesexec, pam : This is preceded by a successful login sequence
xrdp -> sesman : xrdp sends logout message alt If sesexec was activated to authenticate user sesman -> sesexec : Closedown sesexec -> pam : pam_close_session() / pam_end() as appropriate sesexec -X sesexec : sesexec exits sesman X- sesexec : sesexec closes sesman connection and exits destroy sesexec destroy pam sesman -> sesman: remove any state data\nrelating to sesexec process
@enduml
'============================================================================
@startuml close-connection participant xrdp participant sesman
activate xrdp activate sesman
xrdp -> sesman : xrdp sends close connection message xrdp -> xrdp : xrdp closes connection destroy xrdp
sesman ->sesman : tidies up xrdp connection without\ngenerating errors in the log.
@enduml
To regenerate the diagrams, pull this library, run the generate_auth_images.sh
script, commit the changes and push the library.
See https://github.com/neutrinolabs/xrdp/discussions/1961
This encompasses both 'authentication' (who the user is) and 'authorization' (what the user is allowed to do).
Early versions of this page used 'authentication' or 'auth' in an unclear way. These uses have been replaced with 'login'.
Session is a word which has become somewhat overloaded in computing.
For the purposes of this document, 'session' encompasses the time from a successful login up until the time the user explicitly logs out of the desktop. It's more or less equivalent to a window manager session, a PAM session, or a systemd session.
During the exploration of #1684, it was determined that all the PAM calls relating to a PAM session need to be made from the same single thread of execution. The lifetime of this thread needs to encompass the lifetime of the session, as it is responsible for the lifecycle management of the session.
An superficially attractive idea is to incorporate this thread into the sesman process itself. There are many reasons why this isn't a great idea, but there is also at least one show-stopper; On systemd-based systems; when pam_systemd.so
is called to start a new session, the entire process which makes the call is added to a systemd session. So the lifecycle management thread for a session needs to be in a separate process.
It is proposed to call this process the session executive or sesexec for short. It's been difficult to find a clear term for this which won't get confused with "session manager" or "session leader", both of which are used in this name space. "Session driver" was used in earlier versions of this document but it doesn't abbreviate well.
This process is also a good place to incorporate other logic relating to a single session:-
- Starting the X server, window manager and chansrv processes.
- Stopping the above processes.
- (out-of-scope) User accounting (see #1408)
- (out-of-scope) Secret key refreshes for auto-reconnect (see #1443)
Since #2472 was merged, sesman supports UDS-based logins (regardless of whether or not PAM login is supported). Consequently alse logins are also considered in this document in separate sequence diagrams.
As well as PAM, xrdp supports login via *BSD auth_userokay()
,
direct password file access and raw kerberos. These will continue to be
supported as alternatives to PAM.
The xrdp-sesrun
utility will require updating to use these dataflows. In the diagrams below, simply substitute xrdp-sesman
for xrdp
. Additionally, code will need to be added to xrdp-sesman
to interact with the user at the terminal.
This utility can then be used as a diagnostic for the dataflows below.
This utility has been updated to use UDS login (#2472), so the user has a simple way to query sesman state.
While logging in the user via PAM, it will be necessary for both sesman and sesexec to communicate with xrdp at different times.
PR #2494 adds file descriptor passing to the libipm middleware used by xrdp. This allows the file descriptor from xrdp to be passed back and forth between these processes in a natural way.
Using this facility is assumed in this document.
There is a single instance of this process running on a system. It is responsible for:-
- Providing a single point-of-contact for xrdp and the xrdp-sesrun and xrdp-sesadmin utilities.
- Acting as a directory for existing xrdp sessions.
- Allocating X servers for new xrdp sessions.
There is an instance of this process running for each xrdp session on a system. It is responsible for:-
- Starting and stopping session executables (X server, window manager and chansrv)
- All interactions with the PAM stack
- All session lifecycle management operations.
There is a single master instance of this process running on a system, plus a separate forked process for each active xrdp connection.
It is responsible for:-
- User interation during the logging in phase
- Managing connections
- Fowarding/converting data between the RDP client, the backend X server and chansrv.
The following diagrams show the interactions between xrdp, sesman,the session executive (sesexec) and the PAM stack
Interactions will generally follow the following three stages:-
- A connection phase, where xrdp connects to sesman/sesexec
- A logging in phase where the user is authenticated and authorized.
- A command phase, where a command is executed for the logged in user.
Several of these dataflows contain a reference to a PAM conversation.
The conversation happens between the xrdp process (which is responsible for prompting the user) and the PAM stack which logs the user in to the system.
The sesexec process acts as a middle-man between these two actors as follows:-
Following either of the two preceding sequences, xrdp connects to the session as follows:-
This dataflow is currently exclusive to the xrdp-sesadmin
utility. In the diagram, read xrdp-sesadmin
for xrdp
.
It should be now clear how other commands apart from 'list sessions' can be dropped in to this model.
This also illustrates that SCP messages:-
- Should be relatively small (i.e. we don't send an array with megabytes of data)
- Should not be queued. If an SCP message cannot be sent in a timely fashion an error should be generated.
This dataflow is also by xrdp
(or xrdp-sesadmin
) to log a user out.
This can be used for simply authenticating a user without a session (i.e. when using xrdp as a VNC/RDP proxy)
This dataflow is also by xrdp
(or xrdp-sesadmin
) before closing a
connection.
'sesman' can use this as an indicator not to log errors when the connection closes.
This diagram shows a complete overview of an xrdp session, including the RDP client. The session has these characteristics:-
- Login is successful with no retries
- A new session is started
- the client remains connected for the whole of the session.
The current proposal is to implement the above architecture within the lifetime of the 1.x.x xrdp release series.
The initial release of the architecture will not contain an implementation of the PAM conversation - the existing non-interactive PAM handshake will be retained.
The PAM conversation requires interactive elements to be added to xrdp and xrdp-sesrun, and is likely to be complex. Implementing this aspect may delay availability of the initial release. Once the basic architecture is in place, implementation of the conversation can be added at a slower pace.