PAM module for individual authentication with role based authorization

James R. Leu jleu at inoc.com
Thu May 24 22:04:11 UTC 2007


I've implemented a PAM module that when used in a stack
allows for individual authentication, but then assigns
the user to a local "role" account.

First, is there any interest by the pam-linux community
for this module.
Second, if so how do I go about submitting the module
for formal review.
Third, regardless of interest in the pam-linux community
is there someone willing to review my code?

<patch against Linux-PAM "CVS from today" attached>
-- 
James R. Leu
jleu at inoc.com
INOC -> http://inoc.com/
DELIVERING UPTIME
-------------- next part --------------
diff -uNr Linux-PAM/configure.in Linux-PAM.jleu/configure.in
--- Linux-PAM/configure.in	2007-05-24 15:55:58.000000000 -0500
+++ Linux-PAM.jleu/configure.in	2007-05-24 16:24:25.000000000 -0500
@@ -501,7 +501,7 @@
 	modules/pam_mkhomedir/Makefile modules/pam_motd/Makefile \
 	modules/pam_namespace/Makefile \
 	modules/pam_nologin/Makefile modules/pam_permit/Makefile \
-	modules/pam_rhosts/Makefile \
+	modules/pam_rhosts/Makefile modules/pam_roles/Makefile \
 	modules/pam_rootok/Makefile modules/pam_exec/Makefile \
 	modules/pam_securetty/Makefile modules/pam_selinux/Makefile \
 	modules/pam_shells/Makefile modules/pam_stress/Makefile \
diff -uNr Linux-PAM/modules/Makefile.am Linux-PAM.jleu/modules/Makefile.am
--- Linux-PAM/modules/Makefile.am	2007-05-24 15:56:08.000000000 -0500
+++ Linux-PAM.jleu/modules/Makefile.am	2007-05-24 16:17:15.000000000 -0500
@@ -9,7 +9,7 @@
 	pam_securetty pam_selinux pam_shells pam_stress pam_succeed_if \
 	pam_tally pam_time pam_umask pam_unix pam_userdb pam_warn \
 	pam_wheel pam_xauth pam_exec pam_namespace pam_loginuid \
-	pam_faildelay
+	pam_faildelay pam_roles
 
 CLEANFILES = *~
 
diff -uNr Linux-PAM/modules/pam_roles/Makefile.am Linux-PAM.jleu/modules/pam_roles/Makefile.am
--- Linux-PAM/modules/pam_roles/Makefile.am	1969-12-31 18:00:00.000000000 -0600
+++ Linux-PAM.jleu/modules/pam_roles/Makefile.am	2007-05-24 16:28:21.000000000 -0500
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2007 James R. Leu <jleu at inoc.com>
+#
+
+CLEANFILES = *~
+
+EXTRA_DIST = README roles.conf tst-pam_roles
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+	-DPAM_ROLES_CONF=\"$(SCONFIGDIR)/roles.conf\"
+AM_LDFLAGS = -no-undefined -avoid-version -module \
+	-L$(top_builddir)/libpam -lpam
+if HAVE_VERSIONING
+  AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+
+securelib_LTLIBRARIES = pam_roles.la
+
+secureconf_DATA = roles.conf
+
+TESTS = tst-pam_roles
diff -uNr Linux-PAM/modules/pam_roles/pam_roles.c Linux-PAM.jleu/modules/pam_roles/pam_roles.c
--- Linux-PAM/modules/pam_roles/pam_roles.c	1969-12-31 18:00:00.000000000 -0600
+++ Linux-PAM.jleu/modules/pam_roles/pam_roles.c	2007-05-24 16:32:52.000000000 -0500
@@ -0,0 +1,261 @@
+/*
+ * Written by James R. Leu <jleu at inoc.com> 2007/5/24
+ */
+
+#define _BSD_SOURCE
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <strings.h>
+#include <grp.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define _PAM_EXTERN_FUNCTIONS
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#define LIST_SIZE 1024
+#ifndef PAM_ROLE_CONF
+#define PAM_ROLES_CONF "/etc/security/roles.conf"
+#endif
+
+static void
+_log_err(const char *format, ... )
+{
+	va_list args;
+
+	va_start(args, format);
+	openlog("pam_roles", LOG_CONS|LOG_PID, LOG_AUTH);
+	vsyslog(LOG_CRIT, format, args);
+	va_end(args);
+	closelog();
+}
+
+static int
+check_roles(char *list, char *result, int size)
+{
+	char *line = malloc(LIST_SIZE);
+	char *toks[LIST_SIZE];
+	char *tok;
+	int num = 0;
+	FILE *filep;
+	int i;
+	int retval = 0;
+
+	filep = fopen(PAM_ROLES_CONF, "r");
+
+	if (!line || !filep)
+		goto done;
+
+	while((tok = strtok(list, ":"))) {
+		toks[num++] = tok;
+		list = NULL;
+	}
+
+	while(fgets(line, LIST_SIZE, filep)) {
+		char *role;
+		char *name;
+
+		if (line[0] == '#')
+			continue;
+
+		line[strlen(line) - 1] = '\0';
+
+		role = strtok(line, ":");
+		name = strtok(NULL, ":");
+
+		if (!role || !name)
+			continue;
+
+		for (i = 0; i < num; i++) {
+			if (!strcmp(name, toks[i])) {
+				strncpy(result, role, size);
+				retval = 1;
+				goto done;
+			}
+		}
+	}
+
+done:
+	if (filep)
+		fclose(filep);
+	if (line)
+		free(line);
+
+	return retval;
+}
+
+static int
+pam_roles(const char *mode, pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	struct passwd *pw_entry;
+	struct group *grp;
+	const void *username;
+	gid_t	*grps;
+	int	no_grps;
+	int	retval = PAM_IGNORE;
+
+	int	debug = 0;
+	int	i;
+
+	char	*list = malloc(LIST_SIZE);
+	char	*role = malloc(LIST_SIZE);
+
+	(void) pam_get_item(pamh, PAM_USER, &username);
+
+	for (i = 0; i < argc; i++) {
+		if (strcmp(argv[i], "debug") == 0) {
+			debug = 1;
+		} else {
+			_log_err("pam_roles(%s): illegal module option %s\n",
+			    mode, argv[i]);
+		}
+	}
+
+	if (debug) {
+		const void *service;
+
+		(void) pam_get_item(pamh, PAM_SERVICE, &service);
+		_log_err("pam_roles(%s): service = %s, user = %s\n", mode,
+			(service) ? service : "not set",
+			(username) ? username : "not set");
+	}
+
+	if (username == NULL) {
+		retval = PAM_USER_UNKNOWN;
+		goto done;
+	}
+
+	/* stop masquerades by mapping username to uid to username */
+
+	if (!(pw_entry = getpwnam(username))) {
+		retval = PAM_USER_UNKNOWN;
+		goto done;
+	}
+
+	if (!(pw_entry = getpwuid(pw_entry->pw_uid))) {
+		retval = PAM_USER_UNKNOWN;
+		goto done;
+	}
+
+	if (strcmp(username, pw_entry->pw_name) != 0) {
+		_log_err("pam_roles(%s): username %s "
+		    "maps to userid %d which is username %s\n", mode,
+		    username, pw_entry->pw_uid, pw_entry->pw_name);
+	}
+
+	if (pw_entry->pw_uid == 0) {
+		/*
+		 * Root entry cannot have roles, ignore.
+		 */
+		retval = PAM_SUCCESS;
+		goto done;
+	}
+
+	if (initgroups(pw_entry->pw_name, pw_entry->pw_gid)) {
+		retval = PAM_USER_UNKNOWN;
+		goto done;
+	}
+
+	no_grps = getgroups(0, NULL);	/* find the current number of groups */
+	if (no_grps > 0) {
+		grps = calloc(no_grps, sizeof(gid_t));
+		(void) getgroups(no_grps, grps);
+	} else {
+		no_grps = 0;
+		grps = NULL;
+	}
+
+	list[0] = '\0';
+	strcat(list, pw_entry->pw_name);
+	if ((grp = getgrgid(pw_entry->pw_gid))) {
+		strcat(list, ":");
+		strcat(list, grp->gr_name);
+	}
+
+	for (i = 0; i < no_grps; i++) {
+		if ((grp = getgrgid(grps[i]))) {
+			strcat(list, ":");
+			strcat(list, grp->gr_name);
+		}
+	}
+
+	if (check_roles(list, role, LIST_SIZE)) {
+		int err = pam_set_item(pamh, PAM_USER, (const void *)role);
+		if (err != PAM_SUCCESS) {
+			_log_err("pam_roles(%s): failed to set user to %s\n",
+			    mode, role);
+		}
+		if (debug) {
+			const void *service;
+
+			(void) pam_get_item(pamh, PAM_SERVICE, &service);
+			_log_err("pam_roles(%s): service = %s, role = %s\n",
+			    mode, (service) ? service : "not set",
+			    (role) ? role : "not set");
+		}
+		retval = PAM_SUCCESS;
+	}
+
+done:
+	if (list)
+		free(list);
+	if (role)
+		free(role);
+
+	return retval;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return pam_roles("auth", pamh,flags,argc,argv);
+}
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return pam_roles("acct", pamh,flags,argc,argv);
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return pam_roles("open session", pamh,flags,argc,argv);
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return pam_roles("close session", pamh,flags,argc,argv);
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_roles_modstruct = {
+    "pam_roles_acct",
+    pam_sm_authenticate,
+    pam_sm_setcred,
+    pam_sm_acct_mgmt,
+    pam_sm_open_session,
+    pam_sm_close_session,
+    NULL,
+};
+#endif
diff -uNr Linux-PAM/modules/pam_roles/README Linux-PAM.jleu/modules/pam_roles/README
--- Linux-PAM/modules/pam_roles/README	1969-12-31 18:00:00.000000000 -0600
+++ Linux-PAM.jleu/modules/pam_roles/README	2007-05-24 15:56:22.000000000 -0500
@@ -0,0 +1,42 @@
+pam_roles PAM module for role account access
+
+DESCRIPTION
+
+The pam_roles PAM module does not authenticate the user, but instead it
+reassigns users to a local role account.
+
+By default rules for role assignments are taken from config file /etc/security
+/roles.conf.
+
+The pam_roles module can be used to allow individual credientials for
+autherntication (most likely non-local users) but then assigns
+the user to a local "role" account.  Criteria for assignment to a role
+accountis are limited to user and group name.  The processing of the file
+is linear (ie the first match is used).  If no match is found the
+modules does nothing, but will still return PAM_SUCCESS.
+
+This module implements "auth" (just authenticate, no creds), "session",
+and "account".
+
+EXAMPLES
+
+These are some example lines which might be specified in /etc/security/
+roles.conf.
+
+Users fred and bob login with there individual credientials, but
+are assigned to the "operator" role account.  User bill, is assigned to
+the "luser" role account.
+
+operator:bob
+operator:fred
+luser:bill
+
+Users in the group "admin" login with there individual credientials, but
+are assigned to the "root" role account.
+
+root:admin
+
+MODULE OPTIONS
+
+The only option supported by this module is "debug" which will send messages
+to syslog to aid in tracking what the module is doing.
diff -uNr Linux-PAM/modules/pam_roles/roles.conf Linux-PAM.jleu/modules/pam_roles/roles.conf
--- Linux-PAM/modules/pam_roles/roles.conf	1969-12-31 18:00:00.000000000 -0600
+++ Linux-PAM.jleu/modules/pam_roles/roles.conf	2007-05-24 15:56:22.000000000 -0500
@@ -0,0 +1,30 @@
+#
+# This is the configuration file for the pam_roles module. 
+#
+
+#
+# The syntax of the lines is as follows:
+#
+#       <role>:<group|user>
+#
+# white space is and lines begining with '#' are ignored.
+#
+# role
+#       is a local user name
+#
+# group
+#       is a group name available via the system call getgroups()
+#	if using the nscd system they can be from LDAP, RADIUS etc.
+#
+# user
+#       is the user name presented by the PAM system (aka PAM_USER)
+#
+
+#operator:fred
+#operator:bob
+#luser:bill
+#root:admin
+
+#
+# End of group.conf file
+#
diff -uNr Linux-PAM/modules/pam_roles/tst-pam_roles Linux-PAM.jleu/modules/pam_roles/tst-pam_roles
--- Linux-PAM/modules/pam_roles/tst-pam_roles	1969-12-31 18:00:00.000000000 -0600
+++ Linux-PAM.jleu/modules/pam_roles/tst-pam_roles	2007-05-24 16:10:07.000000000 -0500
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_roles.so
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/pam-list/attachments/20070524/78f64005/attachment.sig>


More information about the Pam-list mailing list