[Dev] Tizen 3.0 / multiuser: using PAM to maintain user environment inside AMD

Stéphane Desneux stephane.desneux at eurogiciel.fr
Wed Oct 16 17:45:44 GMT 2013


Hello,

Following the recent discussions on multiuser support in Tizen 3.0, I 
dug further on the steps involved in a user session creation.

NB: I used the latest IVI release 20131015.1 for investigation.

Actual situation
================

The default Systemd target is the graphical.target. In particular, this 
target will launch the following services in this order:
   * ac.service (AMD daemon)
   * launchpad-preload at app.service (launchpad for binary apps: OSP Apps, 
Core Apps and even usual apps like weston-terminal)
   * wrt_launchpad_daemon at app.service (launchpad for web apps)
   * user-session at 5000.service: opens the 'app' user session (starts 
systemd --user)

So before the user-session start, we have AMD and its launchpads running 
as root (despite the name 'xxx at app.service' for launchpads).

The user-session at .service is particular, as it contains:
- User=%I (i.e. User=app)
- PAMName=login
- ExecStart=-/usr/lib/systemd/systemd --user
- Environment=.... (static env variables)

The PAMName=login setting makes that systemd will open a PAM session 
with the pam service 'login' before starting systemd --user as 'app'. 
Respectively, when the user session terminates, the PAM session is 
closed too.

In the actual pam configuration, opening a 'login' session will call the 
pam_systemd module (included in systemd). This modules sends a DBus 
message to systemd login manager (systemd-logind) and gets some 
information in the response. This information is used to initialize some 
important environment variables. When the session terminates, the 
pam_systemd module also tells systemd-logind that the user session ended 
(and depending on its configuration, systemd-logind could kill any 
remaining user processes).

The following diagram tries to show what happens when systemd starts the 
user-session (bold=run as root, other run as user)

---------------------------- *systemd-logind*
--------*systemd*                  |
  |          |                      ^
  |          |                      |
  |          v                      |(un)register
  |     *PAM session*               |
  |     *pam_systemd*-<-(dbus msg)--
  |          |
  |          |
  |          v
  |       systemd --user -------------------
  |                                |
  |                                |
  |                                -> ... services inside user session
  |
  |
  |
  ---> *amd & launchpads* 
------------------------------------------------------

NB on IVI: actually, weston is started inside the user session using 
weston-launch. IMO this is not the right way to do things as 
weston-launch (setuid root) clears the actual environment, opens a new 
PAM session then forks a login shell which exec() weston (!). So there's 
a second PAM session opened but moreover, all the environment variables 
propagated from the top are erased: that's why we have to put 
workarounds for environment vars everywhere (in service files, in 
/etc/sysconfig, in /etc/profile.d and probably in some other places)...

Proposition for the future
==========================

As described previously on this list, we'll probably agree on having a 
global AMD daemon with its launchpads, running as root (or at least as a 
privileged user). The consequence is that we have to push information to 
AMD concerning the user sessions and appropriate environment within each 
session.

 From what I've seen in pam_systemd code (see here: 
https://review.tizen.org/gerrit/gitweb?p=platform/upstream/systemd.git;a=blob;f=src/login/pam-module.c;h=13290fd8ea6de3fcbb621e99dc7d92e7be50a030;hb=HEAD), 
we have the following vars initialized before running systemd --user:
- coming from pam_env.so (/etc/environment)
   * nothing (yet)
- coming from systemd-logind:
   * XDG_SESSION_ID
   * XDG_RUNTIME_DIR
   * XDG_SEAT
   * XDG_VTNR
- coming from user-session at .service (static vars in service):
   * DISPLAY
   * XDG_RUNTIME_DIR (useless...)
   * DBUS_SESSION_BUS_ADDRESS
- other usual variables set at login time:
   * HOME
   * USER
   * LOGNAME
   * LANG
   * PATH

So, to initialize a user-session environment in AMD, we could have a 
pam_amd module started *after* pam_systemd and responsible for sending 
the "init" request to AMD with the current environment. AMD would record 
the appropriate environment for the given user/session (uid, sid). This 
env would then be pushed to launchpads every time an application must be 
started.

Later during the user session, when the environment is complete (ex: 
desktop is started and we have WAYLAND_DISPLAY set), we could then 
update the environment in AMD, with some update policies: for example, 
an environment variable that was already set at init() time can't be 
updated by update() (we keep the initial value).

We'd get something like this:

---------------------------- *systemd-logind*
----------*systemd*                  |
  |            |                      ^
  |            |                      |
  |            v                      |(un)register
  |       *PAM session*               |
  |       *pam_systemd*-<-(dbus msg)--
  |    ---*pam_amd*
  |    |       |
  |    |       |
  |    |       v
  |    |    systemd --user -------------------
  |    |                    |
  |    |                    |
  |    |                    -> ......  services inside user session 
...........
  |    |                         |                        |               ^
  |    |                         |                        |               |
  |    |init(uid,sid,[env])      |update(uid,sid,[env])   |launch(app)    |
  |    |                         |                        |               |
  |    V                         v                        V               |
  -> *amd* -----------------------------------------------+------         |
  |                                                       |               |
  |                                                       |      apply env|
  |                                  launch(app,uid,[env])|   & launch app|
  |                                                       v               |
  ------> 
*launchpads*---------------------------------------------------------

Remark 1: Wether we have one environment per user or one environment per 
session is actually discussed... Given the previous diagram, do we know 
that a launch request comes from a given session ? It seems that we can 
handle both cases by adding the origin session id to the launch request. 
But I'm a bit wary about handling uid+session id actually because of 
extra error handling and added complexity. Do we have use cases for this ?

Remark 2: I didn't draw user session termination. Of course, it's the 
opposite: pam_amd would send an exit(uid,sid) to AMD to block any future 
launch for this session.


Discussion
==========

What do you think of this "pam_amd" module ?

Even later when some new mechanisms will be used to open a user session, 
I'm quite sure we'll still have a PAM session: this is just a standard 
behaviour on any linux distro. So using a PAM lib to propagate the 
session environment to AMD makes sense and would certainly allow a 
smoother transition to anything else in the future.

Hope you love ASCII art ;)
-- 
Stéphane Desneux
Intel OTC - Vannes/FR


More information about the Dev mailing list