walters / rpms / pam

Forked from rpms/pam 5 years ago
Clone
Blob Blame History Raw
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