Re: [systemd-devel] User authentication service isn't killed fully

2021-12-28 Thread Mantas Mikulėnas
On Tue, Dec 28, 2021, 16:39 beroal  wrote:

> I was not aware of `PAMName`. After reading its documentation, it's still
> not clear to me what it does and how it can be used. What's a PAM session?
> Do you have any references? Google search wasn't very helpful. AFAIK from
> the PAM documentation, session is not an entity, for example, it has no
> identifier. Is it a session stored in logind?
>

It's the abstract thing between pam_open_session() and pam_close_session().
Each module has its own definition of what a session really is –
pam_systemd makes it an entity that exists within systemd-logind,
pam_loginuid makes it an entity that exists within the kernel's audit
subsystem, pam_unix just writes "user foo logged in" to the syslog. I guess
you could call the entire child process tree (including reparented ones)
the session.

What PAMName= does is similar to your program: it initializes PAM with the
provided name, skips pam_authenticate but calls pam_acct_mgmt and
pam_open_session before starting the program. It's often used for
auto-login services.


> I would also like to know how systemd is supposed to handle authentication
> programs that can start a process for any user, not the one in the systemd
> unit file. I posted just a minimal example.


It doesn't get involved in those. If your program starts as root and "logs
in" arbitrary users (like sshd or getty/login or lightdm), then it doesn't
use PAMName= but continues calling PAM directly, like it always has.

>


Re: [systemd-devel] User authentication service isn't killed fully

2021-12-28 Thread beroal
I was not aware of `PAMName`. After reading its documentation, it's still not clear to me what it does and how it can be used. What's a PAM session? Do you have any references? Google search wasn't very helpful. AFAIK from the PAM documentation, session is not an entity, for example, it has no identifier. Is it a session stored in logind?I would also like to know how systemd is supposed to handle authentication programs that can start a process for any user, not the one in the systemd unit file. I posted just a minimal example. Original Message Subject: Re: [systemd-devel] User authentication service isn't killed fullyFrom: Mantas Mikulėnas To: beroal CC: systemd-devel@lists.freedesktop.orgOn Sun, Dec 26, 2021 at 3:03 PM beroal  wrote:Hi. I have an autologin program which authenticates a user without asking for a password and starts a child process executing a user shell (for example, Bash, Xorg, or a Wayland compositor).

This program is a systemd service. I discovered that systemd kills the autologin program, but does not kill the child of the autologin program. As I understand from the systemd documentation, systemd should kill both.Systemd doesn't kill child processes when stopping a service – it only kills processes found in the service's cgroup. As pam_systemd has intentionally moved your processes to a separate per-session .slice cgroup, they're no longer tied to the original .service's lifetime.(I'm not very familiar with Wayland's requirements, but does your autologin program do anything specific that the built-in [Service] PAMName= wouldn't do anyway?)-- Mantas Mikulėnas


Re: [systemd-devel] User authentication service isn't killed fully

2021-12-28 Thread Mantas Mikulėnas
On Sun, Dec 26, 2021 at 3:03 PM beroal  wrote:

> Hi. I have an autologin program which authenticates a user without asking
> for a password and starts a child process executing a user shell (for
> example, Bash, Xorg, or a Wayland compositor).
>
> This program is a systemd service. I discovered that systemd kills the
> autologin program, but does not kill the child of the autologin program. As
> I understand from the systemd documentation, systemd should kill both.
>

Systemd doesn't kill *child* processes when stopping a service – it only
kills processes found in the service's cgroup. As pam_systemd has
intentionally moved your processes to a separate per-session .slice cgroup,
they're no longer tied to the original .service's lifetime.

(I'm not very familiar with Wayland's requirements, but does your autologin
program do anything specific that the built-in [Service] PAMName= wouldn't
do anyway?)

-- 
Mantas Mikulėnas


[systemd-devel] User authentication service isn't killed fully

2021-12-26 Thread beroal
Hi. I have an autologin program which authenticates a user without asking for a 
password and starts a child process executing a user shell (for example, Bash, 
Xorg, or a Wayland compositor).

This program is a systemd service. I discovered that systemd kills the 
autologin program, but does not kill the child of the autologin program. As I 
understand from the systemd documentation, systemd should kill both.

I composed a minimal example in C at the end. It's lengthy. The service file 
`/etc/systemd/system/testa.service`:
```
[Unit]
Description=Autologin
After=systemd-user-sessions.service plymouth-quit-wait.service

[Service]
Type=simple
ExecStart=autologin

[Install]
Alias=display-manager.service
WantedBy=multi-user.target
```

I test it like this:
```
[root@beroal test-autologin]# ps --forest -e -o 
pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004
55809   55651 0::/user.slice/user-1002.sl   55808   11243 unconfined
  0  \_ grep 1004
[root@beroal test-autologin]# systemctl start testa
[root@beroal test-autologin]# ps --forest -e -o 
pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004
55824   55651 0::/user.slice/user-1002.sl   55823   11243 unconfined
  0  \_ grep 1004
55812   1 0::/user.slice/user-1004.sl   55812   55812 unconfined
  0 autologin
55822   55812 0::/user.slice/user-1004.sl   55812   55812 unconfined
  0  \_ sleep 600
55814   1 0::/user.slice/user-1004.sl   55814   55814 unconfined
   1004 /usr/lib/systemd/systemd --user
55815   55814 0::/user.slice/user-1004.sl   55814   55814 unconfined
   1004  \_ (sd-pam)
[root@beroal test-autologin]# systemctl stop testa
[root@beroal test-autologin]# ps --forest -e -o 
pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004
55828   55651 0::/user.slice/user-1002.sl   55827   11243 unconfined
  0  \_ grep 1004
55814   1 0::/user.slice/user-1004.sl   55814   55814 unconfined
   1004 /usr/lib/systemd/systemd --user
55815   55814 0::/user.slice/user-1004.sl   55814   55814 unconfined
   1004  \_ (sd-pam)
55822   1 0::/user.slice/user-1004.sl   55812   55812 unconfined
  0 sleep 600
[root@beroal test-autologin]# systemctl kill --kill-who=all --signal=SIGTERM 
user-1004.slice
[root@beroal test-autologin]# ps --forest -e -o 
pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004
55832   55651 0::/user.slice/user-1002.sl   55831   11243 unconfined
  0  \_ grep 1004
```
The child `sleep 600` process wasn't killed by stopping the service, but was 
killed by stopping the slice.

Is this the intended behavior? Do I handle user authentication wrongly? Is 
there a way to kill a service fully?

Arch Linux
systemd version: 249.7-2

`autologin`:

```c
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static int check_errno(char *operation, int error) {
if (error != -1)
return error;
fprintf(stderr, "%s failed: %s (%d)\n", operation, strerror(errno), errno);
exit(EXIT_FAILURE);
}

static int check_pam(struct pam_handle *handle, char *operation, int error) {
if (error == PAM_SUCCESS)
return 0;
fprintf(stderr, "%s failed: %s\n", operation, pam_strerror(handle, error));
if (handle != NULL)
pam_end(handle, error);
exit(EXIT_FAILURE);
}

static int pam_null_conv(int num_msg, const struct pam_message** msg, struct 
pam_response** resp, void* appdata_ptr) {
(void)msg;
(void)appdata_ptr;
*resp = calloc(num_msg, sizeof (struct pam_response));
return *resp == NULL ? PAM_BUF_ERR : PAM_SUCCESS;
}

static int pam_putenv_tuple(struct pam_handle* handle, char* key, char* value) {
char buf[256];
snprintf(buf, sizeof buf, "%s=%s", key, value);
return pam_putenv(handle, buf);
}

static char *getenv_or(char *key, char *or) {
char *var = getenv(key);
return var ? var : or;
}

int main(void) {
char *user_cmd[] = { "sleep", "600", NULL };

struct pam_conv conv = { pam_null_conv, NULL };
struct pam_handle* handle = NULL;

check_pam(handle, "pam_start", pam_start("autologin", "test", &conv, &handle));
check_pam(handle, "pam_acct_mgmt", pam_acct_mgmt(handle, PAM_SILENT));
check_pam(handle, "pam_setcred", pam_setcred(handle, PAM_ESTABLISH_CRED));

struct passwd* pwd = getpwnam("test");
if (pwd == NULL) {
fprintf(stderr, "getpwnam failed\n");
return 1;
}

check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "XDG_SEAT", 
"seat-guest"));
check_pam(handle, "pam_putenv", pam_putenv(handle, "XDG_SESSION_CLASS=user"));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "HOME", pwd->pw_dir));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "PWD", pwd->