aboutsummaryrefslogtreecommitdiff
path: root/src/openrc-pam/openrc-pam.c
blob: f0556ada81ddbf1266da7c2170f24c2d152945af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#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;
	char *file;
	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;
	}

	file = rc_service_resolve(svc_name);
	if (!file) {
		file = rc_service_dylink("user", svc_name);
		if (!file) {
			svc_unlock(pam_lock, fd);
			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(file);
		rc_service_mark(svc_name, RC_SERVICE_HOTPLUGGED);
		count++;
	} else if (count > 0) {
		pid = service_stop(file);
		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);
}