Commit e9fbc77c authored by Lennart Poettering's avatar Lennart Poettering

pam: introduce whitelist and blacklist user list feature

This is useful to exclude root from the session logout killings or to
limit killing to the selinux guest users.
parent 7fc01d33
......@@ -147,8 +147,6 @@ External:
* patch kernel for cpu feature modalias for autoloading aes/kvm/...
http://git.kernel.org/?p=linux/kernel/git/ak/linux-misc-2.6.git;a=shortlog;h=refs/heads/cpuid-match
* place /etc/inittab with explaining blurb.
* procps, psmisc, sysvinit-tools, hostname → util-linux-ng
https://bugzilla.redhat.com/show_bug.cgi?id=614245 -- plymouth
......
......@@ -202,17 +202,43 @@
</varlistentry>
<varlistentry>
<term><option>keep-root=</option></term>
<term><option>kill-only-users=</option></term>
<listitem><para>Takes a boolean
argument. If true, all processes
created by the root user (UID 0) during his
session and from his session will be
kept around after he logged out. This
option allows cancelling the effect of
<option>kill-session=1</option> and
<option>kill-user=1</option> for the
root user.</para></listitem>
<listitem><para>Takes a comma
separated list of user names or
numeric user ids as argument. If this
option is used the effect of the
<option>kill-session=</option> and
<option>kill-user=</option> options
will apply only to the listed
users. If this option is not used the
option applies to all local
users. Note that
<option>kill-exclude-users=</option>
takes precedence over this list and is
hence subtracted from the list
specified here.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>kill-exclude-users=</option></term>
<listitem><para>Takes a comma
separated list of user names or
numeric user ids as argument. Users
listed in this argument will not be
subject to the effect of
<option>kill-session=</option> or
<option>kill-user=</option>. Note
that that this option takes precedence
over
<option>kill-only-users=</option>, and
hence whatever is listed for
<option>kill-exclude-users=</option>
is guaranteed to never be killed by
this PAM module, independent of any
other configuration
setting.</para></listitem>
</varlistentry>
<varlistentry>
......@@ -259,7 +285,9 @@
<option>kill-session=0</option>,
<option>kill-user=0</option>,
<option>keep-root=1</option>,
<option>reset-controllers=cpu</option>.</para>
<option>reset-controllers=cpu</option>,
<option>kill-only-users=</option>,
<option>kill-exclude-users=root</option>.</para>
</refsect1>
<refsect1>
......
......@@ -42,12 +42,14 @@ static int parse_argv(pam_handle_t *handle,
bool *create_session,
bool *kill_session,
bool *kill_user,
bool *keep_root,
char ***controllers,
char ***reset_controllers) {
char ***reset_controllers,
char ***kill_only_users,
char ***kill_exclude_users) {
unsigned i;
bool reset_controller_set = false;
bool kill_exclude_users_set = false;
assert(argc >= 0);
assert(argc == 0 || argv);
......@@ -82,15 +84,6 @@ static int parse_argv(pam_handle_t *handle,
if (kill_user)
*kill_user = k;
} else if (startswith(argv[i], "keep-root=")) {
if ((k = parse_boolean(argv[i] + 10)) < 0) {
pam_syslog(handle, LOG_ERR, "Failed to parse keep-root= argument.");
return k;
}
if (keep_root)
*keep_root = k;
} else if (startswith(argv[i], "controllers=")) {
if (controllers) {
......@@ -121,6 +114,36 @@ static int parse_argv(pam_handle_t *handle,
reset_controller_set = true;
} else if (startswith(argv[i], "kill-only-users=")) {
if (kill_only_users) {
char **l;
if (!(l = strv_split(argv[i] + 16, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*kill_only_users);
*kill_only_users = l;
}
} else if (startswith(argv[i], "kill-exclude-users=")) {
if (kill_exclude_users) {
char **l;
if (!(l = strv_split(argv[i] + 19, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*kill_exclude_users);
*kill_exclude_users = l;
}
kill_exclude_users_set = true;
} else {
pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
return -EINVAL;
......@@ -147,6 +170,17 @@ static int parse_argv(pam_handle_t *handle,
if (kill_session && *kill_session && kill_user)
*kill_user = true;
if (!kill_exclude_users_set && kill_exclude_users) {
char **l;
if (!(l = strv_new("root", NULL))) {
pam_syslog(handle, LOG_ERR, "Out of memory");
return -ENOMEM;
}
*kill_exclude_users = l;
}
return 0;
}
......@@ -369,7 +403,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (sd_booted() <= 0)
return PAM_SUCCESS;
if (parse_argv(handle, argc, argv, &create_session, NULL, NULL, NULL, &controllers, &reset_controllers) < 0)
if (parse_argv(handle,
argc, argv,
&create_session, NULL, NULL,
&controllers, &reset_controllers,
NULL, NULL) < 0)
return PAM_SESSION_ERR;
if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
......@@ -500,6 +538,54 @@ static int session_remains(pam_handle_t *handle, const char *user_path) {
return !!remains;
}
static bool check_user_lists(
pam_handle_t *handle,
uid_t uid,
char **kill_only_users,
char **kill_exclude_users) {
const char *name = NULL;
char **l;
assert(handle);
if (uid == 0)
name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
else {
struct passwd *pw;
if ((pw = pam_modutil_getpwuid(handle, uid)))
name = pw->pw_name;
}
STRV_FOREACH(l, kill_exclude_users) {
uint32_t id;
if (safe_atou32(*l, &id) >= 0)
if ((uid_t) id == uid)
return false;
if (name && streq(name, *l))
return false;
}
if (strv_isempty(kill_only_users))
return true;
STRV_FOREACH(l, kill_only_users) {
uint32_t id;
if (safe_atou32(*l, &id) >= 0)
if ((uid_t) id == uid)
return true;
if (name && streq(name, *l))
return true;
}
return false;
}
_public_ PAM_EXTERN int pam_sm_close_session(
pam_handle_t *handle,
int flags,
......@@ -508,13 +594,12 @@ _public_ PAM_EXTERN int pam_sm_close_session(
const char *username = NULL;
bool kill_session = false;
bool kill_user = false;
bool keep_root = true;
int lock_fd = -1, r;
char *session_path = NULL, *nosession_path = NULL, *user_path = NULL;
const char *id;
struct passwd *pw;
const void *created = NULL;
char **controllers = NULL, **c;
char **controllers = NULL, **c, **kill_only_users = NULL, **kill_exclude_users = NULL;
assert(handle);
......@@ -522,7 +607,11 @@ _public_ PAM_EXTERN int pam_sm_close_session(
if (sd_booted() <= 0)
return PAM_SUCCESS;
if (parse_argv(handle, argc, argv, NULL, &kill_session, &kill_user, &keep_root, &controllers, NULL) < 0)
if (parse_argv(handle,
argc, argv,
NULL, &kill_session, &kill_user,
&controllers, NULL,
&kill_only_users, &kill_exclude_users) < 0)
return PAM_SESSION_ERR;
if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
......@@ -557,7 +646,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
goto finish;
}
if (kill_session && (pw->pw_uid != 0 || !keep_root)) {
if (kill_session && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users)) {
pam_syslog(handle, LOG_INFO, "Killing remaining processes of user session %s of %s.", id, username);
/* Kill processes in session cgroup, and delete it */
......@@ -591,7 +680,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
pam_syslog(handle, LOG_ERR, "Failed to determine whether a session remains: %s", strerror(-r));
/* Kill user processes not attached to any session */
if (kill_user && r == 0 && (pw->pw_uid != 0 || !keep_root)) {
if (kill_user && r == 0 && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users)) {
/* Kill user cgroup */
if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0)
......@@ -635,6 +724,8 @@ finish:
free(user_path);
strv_free(controllers);
strv_free(kill_exclude_users);
strv_free(kill_only_users);
return r;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment