Hi all, I'm packing for travel today, so unfortunately I'm not fully read up on the thread. However, I wanted to leave a comment about this so that the conversation is informed by it
There are ongoing conversations between the GDM devs, KDE/SDDM/PlasmaDM devs, and systemd upstream about "upstreaming" GDM (the server plumbing bits, not the gnome-shell UI) into systemd. We'd take GDM's dbus interface (the one used to communicate with gnome-shell) and represent it via Varlink instead. This way, systemd would act as the portal to authentication backends, similar to the way userdb works Rather than having separate methods for the PAM conversation and a separate GDM "send JSON messages via PAM" extension protocol, I was thinking we'd just make the classic PAM session one of those JSON extension protocols. Of course, since we're using Varlink, this is quite natural. And it makes implementing the UI easier, since there's only one type of authentication message to handle: JSON. The JSON-via-PAM messages are namespaced, so the UI can have special handling for different auth types, so if the UI needs to handle PAM messages in some specific way it still can As for PAM modules, we'd have a compatibility shim: systemd would run PAM conversations, then translate that into JSON messages for us (tunneling through existing JSON messages in the event that the PAM module knows the GDM protocol). But when I spoke to Lennart about this, he said he'd prefer that there was a native Varlink protocol for authentication services to implement. I think we'll need both And finally: by having all the greeter/display manager machinery in systemd, we could rework Polkit to use this mechanism. The benefit is then that Polkit can benefit from the same fancy authentication methods as the greeter Anyway, that's my info dump. Apologies for not proof reading or editing this. Hope it helps the conversation Best, Adrian On Tue, Jul 22, 2025, 10:03 Erin Shepherd <erin.sheph...@e43.eu> wrote: > > So you’ve brought something up that has certainly been ticking over in the > back > of my mind for a long time. I’m roughly 90% certain that the user DB > interface is > indeed the right extension point for hooking authentication systems into > the system > (And would be a better one than having to write a PAM module per > authentication system > - which often ends up just repeating the work of shuffling the username > and password > to the backend system over some custom protocol...) > > > > On 21 Jul 2025, at 12:39, Dominik George <n...@naturalnet.de> wrote: > > > > Hi, > > > > currently, the userdb system only allows querying for User Records and > > Group Records, hence providing a modern replacement for NSS. > > > > So note that currently there are only two ways that the User DB > generically > gets involved in user authentication: > > * pam_unix calls getpwnam and nss_systemd does the userdb lookups, or > * you’re for some reason using my nss module[1] that > I (initially) mostly created to solve problems I was having specific > to NixOS (specifically: plugins can never work in the NSS shadow stack > on NixOS) > > pam_systemd_home interacts with homed specifically and directly. > > (You probably know this, but its important to ensure everyone is on the > same page) > > Whatever such a protocol ends up looking like, I think “you could replace > pam_systemd_home with a generic UserDB PAM module and said generic > protocol” > is an important criterion > > > I would like to propose an addition to make it support authentication as > > well. The additions to the io.systemd.UserDatabase Varlink interface > > are: > > > I don’t think there’s any strict necessity for it to be in the > io.systemd.UserDatabase > Interface. In many ways I think it would be better for any such methods in > a separate > interface (because it means when you get an > `org.varlink.service.InterfaceNotFound` error > back for a given userdb service you know you can just skip all advanced > functionality > For that service for the rest of the authentication sequence) > > > > ```varlink > > # Start the authentication process for a user > > # > > # Requires a username, and an optional authentication authToken > > # (e.g. a password). > > # > > # The method can return: > > # > > # * a User Record object of the user that was authenticated > > # * a conversation token for the AuthenticateContinue method > > # * nothing at all (authentication finished and successful, but no > details provided) > > method Authenticate( > > userName : string, > > authToken : ?string, > > variables : []string, > > client : ?string, > > service : string > > ) -> ( > > convToken : ?int, > > user : ?object > > ) > > > > # Continue an ongogin conversation > > # > > # For example, the Authenticate method might have requested a > conversation > > # with the user, like in the OAuth Device Authorization Grant Flow where > the > > # user will need to open a web page on another device to log in. > > # In such a case, the Authenticate method will return a matching error > with > > # a conversation token, and the client requesting authentication can > later > > # continue the process using this conversation token. > > method AuthenticateContinue( > > convToken : int, > > authToken : ?string, > > variables : []string, > > client : ?string, > > service : string > > ) -> ( > > user : ?object > > ) > > > > # Cancel an ongogin authentication process for which a conversation token > > # has been issued > > method AuthenticateCancel( > > convToken : int, > > client : ?string, > > service : string > > ) > > > > # The authentication process was finished, but the authentication token > was invalid > > error InvalidAuthToken(message: ?string) > > > > # An authentication token was not provided, but is required > > # If a conversation token is issued, the client can use > AuthenticateContinue. > > # Otherwise, it has to restart the process. > > error AuthTokenRequired(convToken: ?int, message: ?string) > > > > # A conversation with the user is required (e.g. ask a non-auth-token > question, > > # open a website, etc.). > > # Once a reply was acquired, the client must continue the process using > > # AuthenticateContinue. > > error ConvRequired(convToken: int, message: string) > > > > # The authentication is ongoing, but cannot complete right now. > > # The service might be waiting for some backend to complete a task, > > # or for the user to acknowledge a second factor in some external app > > # or whatever. The client should ask for progress after the specified > > # interval, but does not need to provide any new information. > > error RetryRequired(convToken: int, interval: int, message: ?string) > > > > # An ongoging conversation timed out while waiting for the client to > > # continue. > > error ConvTimeout(message: ?string) > > ``` > > > > The protocol is designed with PAM compatibility in mind, so it borrows > > some of its terminology. However, it is tailored to asynchronous > > authentication mechanisms, like various OAuth / OIDC flows. > > So a minor thing here: If you’re doing conversation tokens, I would be > inclined to make them a string and exchange it at each step, so stateless > applications can be supported. > > But I’m not sure if (for the auth step in particular, which is very much > driven by the module) it would be better to use sd_varlink_push_fd to send > a file descriptor across for a “reversed” varlink socket that the other end > can use to drive the PAM conversation. A nice advantage here is that you > also > get automatic resource release by just closing the file descriptor. > > As described the interface proposed diverges a bunch from how > pam_sm_authenticate > and the conversation function work. As much as I dislike PAM, I don’t > think you > can get away form it; it's the lingua franca of Unix authentication after > all. > > I think trying to fit OAuth token flows into “normal” PAM like this is > probably > more trouble than its worth. If you’re looking to do things beyond the > basic text > messages and prompts that PAM understands then I think we need to look > towards GDM’s > extensions [2]. They’re definitely imperfect but they’re the closest thing > we have to > a “modern” login extension to PAM right now. > > Anything that slots in at this extension point likely needs the ability to > see any > PAM environment variables (perhaps you pass them across by calling > pam_getenvlist first) > and likely set its own; and also the ability to (when feasible) return a > token in > the PAM_AUTHTOK item that e.g. can be used to establish disk encryption or > Kerberos > credentials (where appropriate/separate from the UserDB backend) > > I don’t see a need to return the user record here. To figure out which > service you need > to call to authenticate you already need to be in possession of it. > > > > Concerning backwards compatibility, I propose that: > > > > * The userdbd multiplexer should try to call the Authenticate methods > > on downstreams, and watch out for any MethodNotImplemented errors. > > If a backend does not implement the Authenticate methods, the > > multiplexer can try to authenticate against a hashedPassword in > > the User Record itself. > > The multiplexer is completely uninvolved in login today (besides very > indirectly > i.e. through the nss_systemd compatibility shim). I don’t think this logic > belongs > in there. It belongs in whichever PAM module talks directly to the user DB. > > I agree about fallback to traditional behaviour on MethodNotImplemented > > Actually I think for the pam_acct extension point there’s a good > argument > for a backend being able to return a value which means “I have no > objections > - do lockout condition validation on the record as normal" > > > > * Implementations that have been done before will continue to work, > > because the proposal only contains additions, but no changes to > > existing methods. > > > > Instead of making a new interface for this, I propose to add it to the > > UserDatabase interface, so most code can jsut continue to work, and we > > can build on top of the existing multiplexer and socket infrastructure > > (however, this might also be possible while defining a second interface, > > but I also want to re-use the error types from UserDatabase). > > > > There’s really no reason why you can’t have multiple interfaces on a > single socket > and indeed Varlink is designed to support this (and I expect most UserDB > implementations > might supplement the normal interface with one of their own. My own > certainly does) > > > [1] https://github.com/erincandescent/pam_sduserdb/ > [2] https://gitlab.gnome.org/GNOME/gdm/-/tree/main/pam-extensions > >