diff --git a/src/meson.build b/src/meson.build index 1dce3c0f..10d88c4e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -19,6 +19,7 @@ subdir('openrc-init') subdir('openrc-run') subdir('openrc-shutdown') subdir('openrc-user') +subdir('pam_openrc') subdir('poweroff') subdir('rc-abort') subdir('rc-depend') diff --git a/src/pam_openrc/meson.build b/src/pam_openrc/meson.build new file mode 100644 index 00000000..54fd1eca --- /dev/null +++ b/src/pam_openrc/meson.build @@ -0,0 +1,11 @@ +if get_option('pam') and pam_dep.found() + shared_module('pam_openrc', + ['pam_openrc.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/pam_openrc/pam_openrc.c b/src/pam_openrc/pam_openrc.c new file mode 100644 index 00000000..bea542f5 --- /dev/null +++ b/src/pam_openrc/pam_openrc.c @@ -0,0 +1,87 @@ +#include +#include +#ifndef LINUX_PAM +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include "helpers.h" + +static int lock_user(struct passwd *pw) { + char *file; + int fd; + + xasprintf(&file, "%s/users/%s", rc_svcdir(), pw->pw_name); + elog(LOG_INFO, "Opening %s.", file); + fd = open(file, O_RDWR | O_CREAT | O_CLOEXEC, 0664); + free(file); + + if (fd == -1 || flock(fd, LOCK_EX | LOCK_NB) == -1) { + elog(LOG_ERR, "Failed to lock lockfile."); + return -1; + } + + return fd; +} + +static int exec_openrc(pam_handle_t *pamh, bool start) { + const char *username, *session; + struct passwd *user; + char *elog_name; + int lockfile; + pid_t child; + + if (pam_get_item(pamh, PAM_SERVICE, (const void **)&session) != PAM_SUCCESS) + return PAM_SESSION_ERR; + /* noop if the current stack was started by openrc-user, to avoid looping */ + if (strcmp(session, "openrc-user") == 0) + return PAM_SUCCESS; + + if (pam_get_user(pamh, &username, "") != PAM_SUCCESS || !(user = getpwnam(username))) + return PAM_SESSION_ERR; + + if (user->pw_uid == 0) + return PAM_SUCCESS; + + xasprintf(&elog_name, "pam_openrc[%s]", user->pw_name); + setenv("EINFO_LOG", elog_name, true); + free(elog_name); + + if ((lockfile = lock_user(user)) == -1) { + unsetenv("EINFO_LOG"); + return PAM_SESSION_ERR; + } + + if ((child = fork()) == 0) { + setsid(); + if (fork() == 0) { + execl(RC_LIBEXECDIR "/bin/openrc-user", "openrc-user", user->pw_name, start ? "start" : "stop", NULL); + elog(LOG_ERR, "Failed to exec openrc-user"); + close(lockfile); + exit(1); + } + exit(0); + } + + waitpid(child, NULL, 0); + + close(lockfile); + unsetenv("EINFO_LOG"); + return PAM_SUCCESS; +} + +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { + (void) flags; (void) argc; (void) argv; + return exec_openrc(pamh, true); +} + +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { + (void) flags; (void) argc; (void) argv; + return exec_openrc(pamh, false); +}