[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