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