b6b1e29706
- add exclusive login mode of operation to pam_selinux_permit (original patch by Dan Walsh)
319 lines
9.7 KiB
Diff
319 lines
9.7 KiB
Diff
diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c.kill-user 2008-01-28 18:34:18.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c 2008-01-28 18:34:18.000000000 +0100
|
|
@@ -1,7 +1,7 @@
|
|
/******************************************************************************
|
|
* A module for Linux-PAM that allows/denies acces based on SELinux state.
|
|
*
|
|
- * Copyright (c) 2007 Red Hat, Inc.
|
|
+ * Copyright (c) 2007, 2008 Red Hat, Inc.
|
|
* Written by Tomas Mraz <tmraz@redhat.com>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
@@ -46,6 +46,14 @@
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <ctype.h>
|
|
+#include <signal.h>
|
|
+#include <limits.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+#include <pwd.h>
|
|
+#include <dirent.h>
|
|
|
|
#define PAM_SM_AUTH
|
|
#define PAM_SM_ACCOUNT
|
|
@@ -57,6 +65,165 @@
|
|
|
|
#include <selinux/selinux.h>
|
|
|
|
+#define MODULE "pam_selinux_permit"
|
|
+#define OPT_DELIM ":"
|
|
+
|
|
+struct lockfd {
|
|
+ uid_t uid;
|
|
+ int fd;
|
|
+ int debug;
|
|
+};
|
|
+
|
|
+#define PROC_BASE "/proc"
|
|
+#define MAX_NAMES (int)(sizeof(unsigned long)*8)
|
|
+
|
|
+static int
|
|
+match_process_uid(pid_t pid, uid_t uid)
|
|
+{
|
|
+ char buf[128];
|
|
+ uid_t puid;
|
|
+ FILE *f;
|
|
+ int re = 0;
|
|
+
|
|
+ snprintf (buf, sizeof buf, PROC_BASE "/%d/status", pid);
|
|
+ if (!(f = fopen (buf, "r")))
|
|
+ return 0;
|
|
+
|
|
+ while (fgets(buf, sizeof buf, f)) {
|
|
+ if (sscanf (buf, "Uid:\t%d", &puid)) {
|
|
+ re = uid == puid;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ fclose(f);
|
|
+ return re;
|
|
+}
|
|
+
|
|
+static int
|
|
+check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
|
|
+{
|
|
+ DIR *dir;
|
|
+ struct dirent *de;
|
|
+ pid_t *pid_table, pid, self;
|
|
+ int i;
|
|
+ int pids, max_pids;
|
|
+ int running = 0;
|
|
+ self = getpid();
|
|
+ if (!(dir = opendir(PROC_BASE))) {
|
|
+ pam_syslog(pamh, LOG_ERR, "Failed to open proc directory file %s:", PROC_BASE);
|
|
+ return -1;
|
|
+ }
|
|
+ max_pids = 256;
|
|
+ pid_table = malloc(max_pids * sizeof (pid_t));
|
|
+ if (!pid_table) {
|
|
+ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
|
|
+ return -1;
|
|
+ }
|
|
+ pids = 0;
|
|
+ while ((de = readdir (dir)) != NULL) {
|
|
+ if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
|
|
+ continue;
|
|
+
|
|
+ if (pids == max_pids) {
|
|
+ if (!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
|
|
+ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
|
|
+ return -1;
|
|
+ }
|
|
+ max_pids *= 2;
|
|
+ }
|
|
+ pid_table[pids++] = pid;
|
|
+ }
|
|
+
|
|
+ (void)closedir(dir);
|
|
+
|
|
+ for (i = 0; i < pids; i++) {
|
|
+ pid_t id;
|
|
+
|
|
+ if (match_process_uid(pid_table[i], uid) == 0)
|
|
+ continue;
|
|
+ id = pid_table[i];
|
|
+
|
|
+ if (killall) {
|
|
+ if (debug)
|
|
+ pam_syslog(pamh, LOG_NOTICE, "Attempting to kill %d", id);
|
|
+ kill(id, SIGKILL);
|
|
+ }
|
|
+ running++;
|
|
+ }
|
|
+
|
|
+ free(pid_table);
|
|
+ return running;
|
|
+}
|
|
+
|
|
+static void
|
|
+sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED)
|
|
+{
|
|
+ struct lockfd *lockfd = plockfd;
|
|
+ struct flock fl;
|
|
+
|
|
+ memset(&fl, 0, sizeof(fl));
|
|
+ fl.l_type = F_UNLCK;
|
|
+ fl.l_whence = SEEK_SET;
|
|
+
|
|
+ if (lockfd->debug)
|
|
+ pam_syslog(pamh, LOG_ERR, "Unlocking fd: %d uid: %d", lockfd->fd, lockfd->uid);
|
|
+
|
|
+ /* Don't kill uid==0 */
|
|
+ if (lockfd->uid)
|
|
+ /* This is a DOS but it prevents an app from forking to prevent killing */
|
|
+ while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0)
|
|
+ continue;
|
|
+
|
|
+ fcntl(lockfd->fd, F_SETLK, &fl);
|
|
+ close(lockfd->fd);
|
|
+ free(lockfd);
|
|
+}
|
|
+
|
|
+static int
|
|
+sepermit_lock(pam_handle_t *pamh, const char *user, int debug)
|
|
+{
|
|
+ char buf[PATH_MAX];
|
|
+ struct flock fl;
|
|
+
|
|
+ memset(&fl, 0, sizeof(fl));
|
|
+ fl.l_type = F_WRLCK;
|
|
+ fl.l_whence = SEEK_SET;
|
|
+
|
|
+ struct passwd *pw = pam_modutil_getpwnam( pamh, user );
|
|
+ if (!pw) {
|
|
+ pam_syslog(pamh, LOG_ERR, "Unable to find uid for user %s", user);
|
|
+ return -1;
|
|
+ }
|
|
+ if (check_running(pamh, pw->pw_uid, 0, debug) > 0) {
|
|
+ pam_syslog(pamh, LOG_ERR, "User %s processes are running. Exclusive login not allowed", user);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ snprintf(buf, sizeof(buf), "%s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
|
|
+ int fd = open(buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
|
+ if (fd < 0) {
|
|
+ pam_syslog(pamh, LOG_ERR, "Unable to open lock file %s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
|
|
+ pam_syslog(pamh, LOG_ERR, "User %s with exclusive login already logged in", user);
|
|
+ close(fd);
|
|
+ return -1;
|
|
+ }
|
|
+ struct lockfd *lockfd=calloc(1, sizeof(struct lockfd));
|
|
+ if (!lockfd) {
|
|
+ close(fd);
|
|
+ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
|
|
+ return -1;
|
|
+ }
|
|
+ lockfd->uid = pw->pw_uid;
|
|
+ lockfd->debug = debug;
|
|
+ lockfd->fd=fd;
|
|
+ pam_set_data(pamh, "pam_selinux_permit", lockfd, sepermit_unlock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/* return 0 when matched, -1 when unmatched, pam error otherwise */
|
|
static int
|
|
sepermit_match(pam_handle_t *pamh, const char *cfgfile, const char *user,
|
|
@@ -67,6 +234,7 @@ sepermit_match(pam_handle_t *pamh, const
|
|
char *start;
|
|
size_t len = 0;
|
|
int matched = 0;
|
|
+ int exclusive = 0;
|
|
|
|
f = fopen(cfgfile, "r");
|
|
|
|
@@ -77,6 +245,8 @@ sepermit_match(pam_handle_t *pamh, const
|
|
|
|
while (!matched && getline(&line, &len, f) != -1) {
|
|
size_t n;
|
|
+ char *sptr;
|
|
+ char *opt;
|
|
|
|
if (line[0] == '#')
|
|
continue;
|
|
@@ -92,6 +262,7 @@ sepermit_match(pam_handle_t *pamh, const
|
|
continue;
|
|
|
|
start[n] = '\0';
|
|
+ start = strtok_r(start, OPT_DELIM, &sptr);
|
|
|
|
switch (start[0]) {
|
|
case '@':
|
|
@@ -117,11 +288,22 @@ sepermit_match(pam_handle_t *pamh, const
|
|
matched = 1;
|
|
}
|
|
}
|
|
+ if (matched)
|
|
+ while ((opt=strtok_r(NULL, OPT_DELIM, &sptr)) != NULL) {
|
|
+ if (strcmp(opt, "exclusive") == 0)
|
|
+ exclusive = 1;
|
|
+ else if (debug) {
|
|
+ pam_syslog(pamh, LOG_NOTICE, "Unknown user option: %s", opt);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
free(line);
|
|
fclose(f);
|
|
- return matched ? 0 : -1;
|
|
+ if (matched)
|
|
+ return exclusive ? sepermit_lock(pamh, user, debug) : 0;
|
|
+ else
|
|
+ return -1;
|
|
}
|
|
|
|
PAM_EXTERN int
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml
|
|
--- Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml.kill-user 2008-01-28 18:34:18.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml 2008-01-28 18:34:18.000000000 +0100
|
|
@@ -30,8 +30,8 @@
|
|
<refsect1 id="pam_selinux_permit-description">
|
|
<title>DESCRIPTION</title>
|
|
<para>
|
|
- The pam_selinux module allows or denies login depending on SELinux enforcement
|
|
- state.
|
|
+ The pam_selinux_permit module allows or denies login depending on SELinux
|
|
+ enforcement state.
|
|
</para>
|
|
<para>
|
|
When the user which is logging in matches an entry in the config file
|
|
@@ -41,14 +41,21 @@
|
|
</para>
|
|
<para>
|
|
The config file contains a simple list of user names one per line. If the
|
|
- <replaceable>name</replaceable> is prefixed with @ character it means that all
|
|
+ <replaceable>name</replaceable> is prefixed with <emphasis>@</emphasis> character it means that all
|
|
users in the group <replaceable>name</replaceable> match. If it is prefixed
|
|
- with a % character the SELinux user is used to match against the <replaceable>name</replaceable>
|
|
+ with a <emphasis>%</emphasis> character the SELinux user is used to match against the <replaceable>name</replaceable>
|
|
instead of the account name. Note that when SELinux is disabled the
|
|
SELinux user assigned to the account cannot be determined. This means that
|
|
such entries are never matched when SELinux is disabled and pam_selinux_permit
|
|
will return PAM_IGNORE.
|
|
</para>
|
|
+ <para>
|
|
+ Each user name in the configuration file can have optional arguments separated
|
|
+ by <emphasis>:</emphasis> character. The only currently recognized argument is <emphasis>exclusive</emphasis>.
|
|
+ The pam_selinux_permit module will allow only single concurrent user session for
|
|
+ the user with this argument specified and it will attempt to kill all processes
|
|
+ of the user after logout.
|
|
+ </para>
|
|
</refsect1>
|
|
|
|
<refsect1 id="pam_selinux_permit-options">
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am
|
|
--- Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am.kill-user 2008-01-28 18:34:18.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am 2008-01-28 18:35:01.000000000 +0100
|
|
@@ -16,10 +16,13 @@ XMLS = README.xml pam_selinux.8.xml pam_
|
|
|
|
securelibdir = $(SECUREDIR)
|
|
secureconfdir = $(SCONFIGDIR)
|
|
+sepermitlockdir = /var/run/sepermit
|
|
|
|
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
|
-I$(top_srcdir)/libpam_misc/include \
|
|
- -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\"
|
|
+ -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\" \
|
|
+ -D SEPERMIT_LOCKDIR=\"$(sepermitlockdir)\"
|
|
+
|
|
AM_LDFLAGS = -no-undefined \
|
|
-L$(top_builddir)/libpam -lpam @LIBSELINUX@
|
|
|
|
@@ -34,6 +37,7 @@ endif
|
|
pam_selinux_permit_la_LDFLAGS= $(pam_selinux_la_LDFLAGS)
|
|
|
|
secureconf_DATA = sepermit.conf
|
|
+sepermitlock_DATA =
|
|
|
|
if HAVE_LIBSELINUX
|
|
securelib_LTLIBRARIES = pam_selinux.la pam_selinux_permit.la
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf
|
|
--- Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf.kill-user 2008-01-28 18:34:18.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf 2008-01-28 18:34:18.000000000 +0100
|
|
@@ -4,3 +4,8 @@
|
|
# - an user name
|
|
# - a group name, with @group syntax
|
|
# - a SELinux user name, with %seuser syntax
|
|
+# Each line can contain optional arguments separated by :
|
|
+# The possible arguments are:
|
|
+# - exclusive - only single login session will
|
|
+# be allowed for the user and the user's processes
|
|
+# will be killed on logout
|