[PATCH] Add pam_nnp module to set no_new_privs
Andy Lutomirski
luto at amacapital.net
Sun Oct 6 19:08:45 UTC 2013
---
configure.in | 3 +-
modules/Makefile.am | 2 +-
modules/pam_nnp/Makefile.am | 40 +++++
modules/pam_nnp/README.xml | 39 +++++
modules/pam_nnp/nnp.conf | 16 ++
modules/pam_nnp/nnp.conf.5.xml | 142 ++++++++++++++++
modules/pam_nnp/pam_nnp.8.xml | 174 ++++++++++++++++++++
modules/pam_nnp/pam_nnp.c | 358 +++++++++++++++++++++++++++++++++++++++++
modules/pam_nnp/tst-pam_nnp | 2 +
9 files changed, 774 insertions(+), 2 deletions(-)
create mode 100644 modules/pam_nnp/Makefile.am
create mode 100644 modules/pam_nnp/README.xml
create mode 100644 modules/pam_nnp/nnp.conf
create mode 100644 modules/pam_nnp/nnp.conf.5.xml
create mode 100644 modules/pam_nnp/pam_nnp.8.xml
create mode 100644 modules/pam_nnp/pam_nnp.c
create mode 100755 modules/pam_nnp/tst-pam_nnp
diff --git a/configure.in b/configure.in
index 424a634..ce89edd 100644
--- a/configure.in
+++ b/configure.in
@@ -616,7 +616,8 @@ AC_CONFIG_FILES([Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile
modules/pam_loginuid/Makefile modules/pam_mail/Makefile \
modules/pam_mkhomedir/Makefile modules/pam_motd/Makefile \
modules/pam_namespace/Makefile \
- modules/pam_nologin/Makefile modules/pam_permit/Makefile \
+ modules/pam_nologin/Makefile modules/pam_nnp/Makefile \
+ modules/pam_permit/Makefile \
modules/pam_pwhistory/Makefile modules/pam_rhosts/Makefile \
modules/pam_rootok/Makefile modules/pam_exec/Makefile \
modules/pam_securetty/Makefile modules/pam_selinux/Makefile \
diff --git a/modules/Makefile.am b/modules/Makefile.am
index 0c80cea..8a32ed9 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -6,7 +6,7 @@ SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \
pam_env pam_exec pam_faildelay pam_filter pam_ftp \
pam_group pam_issue pam_keyinit pam_lastlog pam_limits \
pam_listfile pam_localuser pam_loginuid pam_mail \
- pam_mkhomedir pam_motd pam_namespace pam_nologin \
+ pam_mkhomedir pam_motd pam_namespace pam_nologin pam_nnp \
pam_permit pam_pwhistory pam_rhosts pam_rootok pam_securetty \
pam_selinux pam_sepermit pam_shells pam_stress \
pam_succeed_if pam_tally pam_tally2 pam_time pam_timestamp \
diff --git a/modules/pam_nnp/Makefile.am b/modules/pam_nnp/Makefile.am
new file mode 100644
index 0000000..9d98c32
--- /dev/null
+++ b/modules/pam_nnp/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2005, 2006, 2009 Thorsten Kukuk <kukuk at suse.de>
+# Copyright (c) 2013 Andy Lutomirski <luto at amacapital.net>
+#
+
+CLEANFILES = *~
+MAINTAINERCLEANFILES = $(MANS) README
+
+EXTRA_DIST = README $(MANS) $(XMLS) nnp.conf tst-pam_nnp
+
+man_MANS = nnp.conf.5 pam_nnp.8
+XMLS = README.xml nnp.conf.5.xml pam_nnp.8.xml
+
+TESTS = tst-pam_nnp
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+#nnp_conf_dir = $(SCONFIGDIR)/nnp.d
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+ -DNNP_FILE_DIR=\"$(nnp_conf_dir)/*.conf\" \
+ -DNNP_FILE=\"$(SCONFIGDIR)/nnp.conf\"
+AM_LDFLAGS = -no-undefined -avoid-version -module
+if HAVE_VERSIONING
+ AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+
+securelib_LTLIBRARIES = pam_nnp.la
+pam_nnp_la_LIBADD = $(top_builddir)/libpam/libpam.la
+
+secureconf_DATA = nnp.conf
+
+if ENABLE_REGENERATE_MAN
+noinst_DATA = README
+README: pam_nnp.8.xml nnp.conf.5.xml
+-include $(top_srcdir)/Make.xml.rules
+endif
+
+#install-data-local:
+# mkdir -p $(DESTDIR)$(nnp_conf_dir)
diff --git a/modules/pam_nnp/README.xml b/modules/pam_nnp/README.xml
new file mode 100644
index 0000000..92f26f0
--- /dev/null
+++ b/modules/pam_nnp/README.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.docbook.org/xml/4.3/docbookx.dtd"
+[
+<!--
+<!ENTITY pamnnp SYSTEM "pam_nnp.8.xml">
+-->
+<!--
+<!ENTITY nnpconf SYSTEM "nnp.conf.5.xml">
+-->
+]>
+
+<article>
+
+ <articleinfo>
+
+ <title>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_nnp.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_nnp-name"]/*)'/>
+ </title>
+
+ </articleinfo>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_nnp.8.xml" xpointer='xpointer(//refsect1[@id = "pam_nnp-description"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_nnp.8.xml" xpointer='xpointer(//refsect1[@id = "pam_nnp-options"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="nnp.conf.5.xml" xpointer='xpointer(//refsect1[@id = "nnp.conf-examples"]/*)'/>
+ </section>
+
+</article>
diff --git a/modules/pam_nnp/nnp.conf b/modules/pam_nnp/nnp.conf
new file mode 100644
index 0000000..38152fa
--- /dev/null
+++ b/modules/pam_nnp/nnp.conf
@@ -0,0 +1,16 @@
+# /etc/security/nnp.conf
+#
+#Each line describes a no_new_privs instruction for a user, group, or range.
+#
+#<domain> <action>
+#
+#Where:
+#<domain> can be:
+# - an user name
+# - a group name, with @group syntax
+# - the wildcard *, for default entry
+#
+#<action> can have the two values:
+# - "no_new_privs" to set no_new_privs
+# - "done" to stop processing further lines
+
diff --git a/modules/pam_nnp/nnp.conf.5.xml b/modules/pam_nnp/nnp.conf.5.xml
new file mode 100644
index 0000000..9297a43
--- /dev/null
+++ b/modules/pam_nnp/nnp.conf.5.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="nnp.conf">
+
+ <refmeta>
+ <refentrytitle>nnp.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>nnp.conf</refname>
+ <refpurpose>configuration file for the pam_nnp module</refpurpose>
+ </refnamediv>
+
+ <refsect1 id='nnp.conf-description'>
+ <title>DESCRIPTION</title>
+ <para>
+ The <emphasis>pam_nnp.so</emphasis> PAM module sets the
+ no_new_privs flag on sessions for selected users or groups.
+ Thae <filename>/etc/security/nnp.conf</filename> file determines
+ which users or groups are affected.
+ </para>
+ <para>
+ The syntax of the lines is as follows:
+ </para>
+ <para>
+ <replaceable><domain></replaceable> <replaceable><action></replaceable>
+ </para>
+ <para>
+ The fields listed above should be filled as follows:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option><domain></option>
+ </term>
+ <listitem>
+ <itemizedlist>
+ <listitem>
+ <para>
+ a username
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a groupname, with <emphasis remap='B'>@group</emphasis> syntax.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the wildcard <emphasis remap='B'>*</emphasis> matches all users.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ an uid range specified as <replaceable><min_uid></replaceable><emphasis
+ remap='B'>:</emphasis><replaceable><max_uid></replaceable>. If min_uid
+ is omitted, the match is exact for the max_uid. If max_uid is omitted, all
+ uids greater than or equal min_uid match.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a gid range specified as <emphasis
+ remap='B'>@</emphasis><replaceable><min_gid></replaceable><emphasis
+ remap='B'>:</emphasis><replaceable><max_gid></replaceable>. If min_gid
+ is omitted, the match is exact for the max_gid. If max_gid is omitted, all
+ gids greater than or equal min_gid match. For the exact match all groups including
+ the user's supplementary groups are examined. For the range matches only
+ the user's primary group is examined.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option><action></option>
+ </term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term><option>no_new_privs</option></term>
+ <listitem>
+ <para>Set the no_new_privs flag for the session.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>done</option></term>
+ <listitem>
+ <para>Stop processing rules.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ <para>
+ The pam_nnp module reports configuration problems
+ found in its configuration file and errors via <citerefentry>
+ <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1 id="nnp.conf-examples">
+ <title>EXAMPLES</title>
+ <para>
+ These are some example lines which might be specified in
+ <filename>/etc/security/nnp.conf</filename>.
+ </para>
+ <programlisting>
+ at student no_new_privs
+ at faculty done
+ftp no_new_privs
+:123 no_new_privs
+ at 650: done
+600:700 no_new_privs
+ </programlisting>
+ </refsect1>
+
+ <refsect1 id="nnp.conf-see_also">
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry><refentrytitle>pam_nnp</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id="nnp.conf-author">
+ <title>AUTHOR</title>
+ <para>
+ pam_nnp was initially written by Andy Lutomirski <luto at amacapital.net>.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/modules/pam_nnp/pam_nnp.8.xml b/modules/pam_nnp/pam_nnp.8.xml
new file mode 100644
index 0000000..11edcd8
--- /dev/null
+++ b/modules/pam_nnp/pam_nnp.8.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+
+<refentry id='pam_nnp'>
+
+ <refmeta>
+ <refentrytitle>pam_nnp</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class='setdesc'>Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id='pam_nnp-name'>
+ <refname>pam_nnp</refname>
+ <refpurpose>
+ PAM module to set no_new_privs
+ </refpurpose>
+ </refnamediv>
+
+<!-- body begins here -->
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="pam_nnp-cmdsynopsis">
+ <command>pam_nnp.so</command>
+ <arg choice="opt">
+ conf=<replaceable>/path/to/nnp.conf</replaceable>
+ </arg>
+ <arg choice="opt">
+ debug
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+
+ <refsect1 id="pam_nnp-description">
+ <title>DESCRIPTION</title>
+ <para>
+ The pam_nnp PAM module sets the no_new_privs flag on sessions for selected users or groups.
+ The no_new_privs flag prevents affected processes and their descendents from gaining privilege
+ via setuid programs, setgid programs, and file capabilities. This can reduce the attack
+ surface available to these processes.
+ </para>
+ <para>
+ By default settings are taken from the <filename>/etc/security/nnp.conf</filename> config
+ file. If a config file is explicitly specified with a module option then the files in the
+ above directory are not parsed.
+ </para>
+ <para>
+ The module must not be called by a multithreaded application.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-options">
+ <title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>conf=<replaceable>/path/to/nnp.conf</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Indicate an alternative nnp.conf style configuration file to
+ override the default.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>debug</option>
+ </term>
+ <listitem>
+ <para>
+ Print debug information.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-types">
+ <title>MODULE TYPES PROVIDED</title>
+ <para>
+ Only the <option>session</option> module type is provided.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-return_values">
+ <title>RETURN VALUES</title>
+ <variablelist>
+ <varlistentry>
+ <term>PAM_IGNORE</term>
+ <listitem>
+ <para>
+ no_new_privs was not configured for this user.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_SERVICE_ERR</term>
+ <listitem>
+ <para>
+ Cannot read config file.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_SESSION_ERR</term>
+ <listitem>
+ <para>
+ Error recovering account name.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_SYSTEM_ERR</term>
+ <listitem>
+ <para>
+ no_new_privs was configured, but the kernel rejected the attempt to set it.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_SUCCESS</term>
+ <listitem>
+ <para>
+ no_new_privs was set.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_USER_UNKNOWN</term>
+ <listitem>
+ <para>
+ The user is not known to the system.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-files">
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/security/nnp.conf</filename></term>
+ <listitem>
+ <para>Default configuration file</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-see_also">
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>nnp.conf</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_nnp-authors">
+ <title>AUTHORS</title>
+ <para>
+ pam_nnp was initially written by Andy Lutomirski <luto at amacapital.net>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/modules/pam_nnp/pam_nnp.c b/modules/pam_nnp/pam_nnp.c
new file mode 100644
index 0000000..9b43566
--- /dev/null
+++ b/modules/pam_nnp/pam_nnp.c
@@ -0,0 +1,358 @@
+/*
+ * pam_nnp: no_new_privs support for pam
+ *
+ * Loosely based on pam_limits from Linux-PAM.
+ *
+ *
+ * Copyright (c) 2012 Andrew Lutomirski
+ * Copyright (c) Cristian Gafton, 1996-1997, <gafton at redhat.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(linux) && !defined(__linux)
+#warning THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <syslog.h>
+#include <stdbool.h>
+#include <sys/prctl.h>
+
+#include <pwd.h>
+
+#define UNUSED __attribute__((unused))
+
+/* Module defines */
+#define LINE_LENGTH 1024
+
+#define NNP_RANGE_ERR -1 /* error in specified uid/gid range */
+#define NNP_RANGE_NONE 0 /* no range specified */
+#define NNP_RANGE_ONE 1 /* exact uid/gid specified (:max_uid)*/
+#define NNP_RANGE_MIN 2 /* only minimum uid/gid specified (min_uid:) */
+#define NNP_RANGE_MM 3 /* both min and max uid/gid specified (min_uid:max_uid) */
+
+#define PAM_DEBUG_ARG 1
+
+/* internal data */
+struct pam_nnp_s {
+ bool nnp;
+ const char *conf_file;
+};
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+
+static int
+_pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
+ struct pam_nnp_s *pl)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+ if (!strcmp(*argv,"debug")) {
+ ctrl |= PAM_DEBUG_ARG;
+ } else {
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+static int
+parse_uid_range(pam_handle_t *pamh, const char *domain,
+ uid_t *min_uid, uid_t *max_uid)
+{
+ const char *range = domain;
+ char *pmax;
+ char *endptr;
+ int rv = NNP_RANGE_MM;
+
+ if ((pmax=strchr(range, ':')) == NULL)
+ return NNP_RANGE_NONE;
+ ++pmax;
+
+ if (range[0] == '@')
+ ++range;
+
+ if (range[0] == ':')
+ rv = NNP_RANGE_ONE;
+ else {
+ errno = 0;
+ *min_uid = strtoul (range, &endptr, 10);
+ if (errno != 0 || (range == endptr) || *endptr != ':') {
+ pam_syslog(pamh, LOG_DEBUG,
+ "wrong min_uid/gid value in '%s'", domain);
+ return NNP_RANGE_ERR;
+ }
+ }
+
+ if (*pmax == '\0') {
+ if (rv == NNP_RANGE_ONE)
+ return NNP_RANGE_ERR;
+ else
+ return NNP_RANGE_MIN;
+ }
+
+ errno = 0;
+ *max_uid = strtoul (pmax, &endptr, 10);
+ if (errno != 0 || (pmax == endptr) || *endptr != '\0') {
+ pam_syslog(pamh, LOG_DEBUG,
+ "wrong max_uid/gid value in '%s'", domain);
+ return NNP_RANGE_ERR;
+ }
+
+ if (rv == NNP_RANGE_ONE)
+ *min_uid = *max_uid;
+ return rv;
+}
+
+static int
+parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid,
+ int ctrl, struct pam_nnp_s *pl)
+{
+ FILE *fil;
+ char buf[LINE_LENGTH];
+ const char *conf_file = pl->conf_file ? pl->conf_file : NNP_FILE;
+
+ /* check for the NNP_FILE */
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", conf_file);
+ fil = fopen(conf_file, "r");
+ if (fil == NULL) {
+ pam_syslog (pamh, LOG_WARNING,
+ "cannot read settings from %s: %m", conf_file);
+ return PAM_SERVICE_ERR;
+ }
+
+ enum nnp_mode { DONE, NNP } mode = DONE;
+
+ /* start the show */
+ while (fgets(buf, LINE_LENGTH, fil) != NULL) {
+ char domain[LINE_LENGTH];
+ char action[LINE_LENGTH];
+
+ int i;
+ int rngtype;
+ char *tptr,*line;
+ uid_t min_uid = (uid_t)-1, max_uid = (uid_t)-1;
+
+ line = buf;
+ /* skip the leading white space */
+ while (*line && isspace(*line))
+ line++;
+
+ /* Rip off the comments */
+ tptr = strchr(line,'#');
+ if (tptr)
+ *tptr = '\0';
+ /* Rip off the newline char */
+ tptr = strchr(line,'\n');
+ if (tptr)
+ *tptr = '\0';
+ /* Anything left ? */
+ if (!strlen(line))
+ continue;
+
+ domain[0] = action[0] = '\0';
+
+ i = sscanf(line, "%s%s", domain, action);
+ D(("scanned line[%d]: domain[%s], action[%s]", i, domain, action));
+
+ if ((rngtype=parse_uid_range(pamh, domain, &min_uid, &max_uid)) < 0) {
+ pam_syslog(pamh, LOG_WARNING, "invalid uid range '%s' - skipped", domain);
+ continue;
+ }
+
+ if (i == 2) { /* a complete line */
+ enum nnp_mode this_mode;
+ if (!strcasecmp(action, "no_new_privs") != 0) {
+ this_mode = NNP;
+ } else if (!strcasecmp(action, "done") != 0) {
+ this_mode = DONE;
+ } else {
+ pam_syslog(pamh, LOG_WARNING, "unknown action %s", action);
+ continue;
+ }
+
+ if (strcmp(uname, domain) == 0) { /* match for user name */
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "direct match for user %s", uname);
+ mode = this_mode;
+ goto parse_done;
+ } else if (domain[0]=='@') { /* some kind of group */
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "checking if %s is in group %s",
+ uname, domain + 1);
+ }
+ switch(rngtype) {
+ case NNP_RANGE_NONE:
+ if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+ mode = this_mode;
+ goto parse_done;
+ }
+ break;
+ case NNP_RANGE_ONE:
+ if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) {
+ mode = this_mode;
+ goto parse_done;
+ }
+ break;
+ case NNP_RANGE_MM:
+ if (gid > (gid_t)max_uid)
+ break;
+ /* fallthrough */
+ case NNP_RANGE_MIN:
+ if (gid >= (gid_t)min_uid) {
+ mode = this_mode;
+ goto parse_done;
+ }
+ }
+ } else {
+ switch(rngtype) { /* match against uid (not username) */
+ case NNP_RANGE_NONE:
+ if (strcmp(domain, "*") == 0) {
+ mode = this_mode;
+ goto parse_done;
+ }
+ break;
+ case NNP_RANGE_ONE:
+ if (uid != max_uid)
+ break;
+ /* fallthrough */
+ case NNP_RANGE_MM:
+ if (uid > max_uid)
+ break;
+ /* fallthrough */
+ case NNP_RANGE_MIN:
+ if (uid >= min_uid) {
+ mode = this_mode;
+ goto parse_done;
+ }
+ }
+ }
+ } else {
+ pam_syslog(pamh, LOG_WARNING, "invalid line '%s' - skipped", line);
+ }
+ }
+ parse_done:
+ fclose(fil);
+
+ pl->nnp = (mode == NNP);
+
+ return PAM_SUCCESS;
+}
+
+/* now the session stuff */
+PAM_EXTERN int
+pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ int retval;
+ char *user_name;
+ struct passwd *pwd;
+ int ctrl;
+ struct pam_nnp_s plstruct;
+ struct pam_nnp_s *pl = &plstruct;
+
+ D(("called."));
+
+ memset(pl, 0, sizeof(*pl));
+
+ ctrl = _pam_parse(pamh, argc, argv, pl);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ pam_syslog(pamh, LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ pwd = pam_modutil_getpwnam(pamh, user_name);
+ if (!pwd) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_WARNING,
+ "open_session username '%s' does not exist", user_name);
+ return PAM_USER_UNKNOWN;
+ }
+
+ retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
+
+ if (retval == PAM_SUCCESS && pl->nnp) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG,
+ "Setting no_new_privs for user '%s'", user_name);
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+ pam_error(pamh, "Failed to set no_new_privs for '%s'.",
+ pwd->pw_name);
+ return PAM_SYSTEM_ERR; /* This really has no business failing. */
+ }
+ return PAM_SUCCESS;
+ }
+
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN int
+pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED,
+ int argc UNUSED, const char **argv UNUSED)
+{
+ /* nothing to do */
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_nnp_modstruct = {
+ "pam_nnp",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL
+};
+#endif
+
diff --git a/modules/pam_nnp/tst-pam_nnp b/modules/pam_nnp/tst-pam_nnp
new file mode 100755
index 0000000..295821d
--- /dev/null
+++ b/modules/pam_nnp/tst-pam_nnp
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_nnp.so
--
1.8.3.1
More information about the Pam-list
mailing list