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

beroal me at beroal.in.ua
Sun Dec 26 13:02:43 UTC 2021


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 at 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 at beroal test-autologin]# systemctl start testa
[root at 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 at beroal test-autologin]# systemctl stop testa
[root at 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 at beroal test-autologin]# systemctl kill --kill-who=all --signal=SIGTERM user-1004.slice
[root at 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 <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <security/pam_appl.h>

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->pw_dir));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "SHELL", pwd->pw_shell));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "USER", pwd->pw_name));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "LOGNAME", pwd->pw_name));
check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "TERM", getenv_or("TERM", "linux")));

check_pam(handle, "pam_open_session", pam_open_session(handle, PAM_SILENT));
char** env = pam_getenvlist(handle);

pid_t pid = check_errno("fork", fork());
if (pid == 0)
execvpe(user_cmd[0], user_cmd, env);

int res;
while ((res = waitpid(pid, NULL, 0)) <= 0) {
if (res == -1 && errno != EINTR) {
fprintf(stderr, "waitpid failed: %s (%d)\n", strerror(errno), errno);
break;
}
}

check_pam(handle, "pam_close_session", pam_close_session(handle, PAM_SILENT));
check_pam(handle, "pam_setcred", pam_setcred(handle, PAM_DELETE_CRED));
check_pam(handle, "pam_end", pam_end(handle, 0));

return 0;
}
```


More information about the systemd-devel mailing list