From ad169aa7edc88091ff7bba3b66040873c28f6d81 Mon Sep 17 00:00:00 2001
From: "Anna (navi) Figueiredo Gomes" <navi@vlhl.dev>
Date: Fri, 12 Jul 2024 18:10:11 +0200
Subject: openrc-pam: auto-launch user services via pam

Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
---
 src/openrc-pam/meson.build  |  11 +++++
 src/openrc-pam/openrc-pam.c | 106 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+)
 create mode 100644 src/openrc-pam/meson.build
 create mode 100644 src/openrc-pam/openrc-pam.c

(limited to 'src/openrc-pam')

diff --git a/src/openrc-pam/meson.build b/src/openrc-pam/meson.build
new file mode 100644
index 00000000..fa9de471
--- /dev/null
+++ b/src/openrc-pam/meson.build
@@ -0,0 +1,11 @@
+if get_option('pam') and pam_dep.found()
+  shared_library('pam_openrc',
+    ['openrc-pam.c', misc_c, version_h],
+    c_args : [cc_branding_flags],
+    dependencies : [pam_dep],
+    name_prefix : '',
+    link_with : [libeinfo, librc],
+    include_directories : [incdir, einfo_incdir, rc_incdir],
+    install : true,
+    install_dir : libdir / 'security')
+endif
diff --git a/src/openrc-pam/openrc-pam.c b/src/openrc-pam/openrc-pam.c
new file mode 100644
index 00000000..241b123d
--- /dev/null
+++ b/src/openrc-pam/openrc-pam.c
@@ -0,0 +1,106 @@
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_modules.h>
+#include <security/pam_appl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "librc.h"
+#include "einfo.h"
+
+static int
+exec_openrc(pam_handle_t *pamh, bool opening)
+{
+	char *svc_name = NULL;
+	char *pam_lock = NULL;
+	char *logins, *rundir;
+	const char *username;
+	struct passwd *pw;
+	int count = 0, fd = -1;
+	int pid = -1, status;
+	int ret = PAM_SUCCESS;
+	RC_SERVICE service_status;
+
+	if (pam_get_user(pamh, &username, "username:") != PAM_SUCCESS)
+		return PAM_SESSION_ERR;
+
+	pw = getpwnam(username);
+	if (!pw)
+		return PAM_SESSION_ERR;
+
+	if (pw->pw_uid == 0)
+		return PAM_SUCCESS;
+
+	xasprintf(&svc_name, "user.%s", pw->pw_name);
+	service_status = rc_service_state(svc_name);
+	if (service_status & RC_SERVICE_STARTED && !(service_status & RC_SERVICE_HOTPLUGGED))
+		goto out;
+
+	xasprintf(&pam_lock, "openrc-pam.%s", pw->pw_name);
+	fd = svc_lock(pam_lock, false);
+
+	if (fd == -1) {
+		ret = PAM_SESSION_ERR;
+		goto out;
+	}
+
+	logins = rc_service_value_get(svc_name, "logins");
+	if (logins)
+		sscanf(logins, "%d", &count);
+	free(logins);
+
+	if (opening && count == 0) {
+		pid = service_start(svc_name);
+		rc_service_mark(svc_name, RC_SERVICE_HOTPLUGGED);
+		count++;
+	} else if (count > 0) {
+		pid = service_stop(svc_name);
+		count--;
+	}
+
+	if (pid > 0) {
+		waitpid(pid, &status, 0);
+		if (status != 0)
+			ret = PAM_SESSION_ERR;
+	}
+
+	xasprintf(&logins, "%d", count);
+	rc_service_value_set(svc_name, "logins", logins);
+	free(logins);
+
+	rundir = rc_service_value_get(svc_name, "xdg_runtime_dir");
+	if (rundir) {
+		char *rundir_env;
+		xasprintf(&rundir_env, "XDG_RUNTIME_DIR=%s", rundir);
+		pam_putenv(pamh, rundir_env);
+		free(rundir_env);
+		free(rundir);
+	}
+
+	svc_unlock(pam_lock, fd);
+
+out:
+	free(pam_lock);
+	free(svc_name);
+	return ret;
+}
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+	(void) argc;
+	(void) argv;
+	(void) flags;
+
+	return exec_openrc(pamh, true);
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+	(void) argc;
+	(void) argv;
+	(void) flags;
+
+	return exec_openrc(pamh, false);
+}
-- 
cgit v1.2.3