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
117
118
119
120
121
122
123
124
125
126
127
|
#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(&pam_lock, "openrc-pam.%s", pw->pw_name);
setenv("EINFO_LOG", pam_lock, true);
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)) {
elog(LOG_INFO, "service started and not hotplugged, skipping session.");
goto out;
}
elog(LOG_INFO, opening ? "starting session" : "stopping session");
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) {
if (count == 0) {
pid = service_start(file);
rc_service_mark(svc_name, RC_SERVICE_HOTPLUGGED);
}
count++;
} else {
count--;
if (count == 0)
pid = service_stop(file);
}
elog(LOG_INFO, "%d sessions", 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);
unsetenv("EINFO_LOG");
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);
}
|