Blame SOURCES/pam-1.3.1-pam-usertype.patch

8c7ed8
From 926d7935edf35385e6c28bb97666aee443b71e46 Mon Sep 17 00:00:00 2001
8c7ed8
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
8c7ed8
Date: Fri, 10 Jan 2020 15:53:35 +0100
8c7ed8
Subject: [PATCH] pam_usertype: new module to tell if uid is in login.defs
8c7ed8
 ranges
8c7ed8
8c7ed8
This module will check if the user account type is system or regular based
8c7ed8
on its uid. To evaluate the condition it will use 0-99 reserved range
8c7ed8
together with `SYS_UID_MIN` and `SYS_UID_MAX` values from `/etc/login.defs`.
8c7ed8
8c7ed8
If these values are not set, it uses configure-time defaults
8c7ed8
`--with-sys-uid-min` and `--with-uid-min` (according to `login.defs` man page
8c7ed8
`SYS_UID_MAX` defaults to `UID_MIN - 1`.
8c7ed8
8c7ed8
This information can be used to skip specific module in pam stack
8c7ed8
based on the account type. `pam_succeed_if uid < 1000` is used at the moment
8c7ed8
however it does not reflect changes to `login.defs`.
8c7ed8
---
8c7ed8
 configure.ac                            |  22 ++
8c7ed8
 modules/Makefile.am                     |   2 +-
8c7ed8
 modules/pam_usertype/Makefile.am        |  34 +++
8c7ed8
 modules/pam_usertype/README.xml         |  41 +++
8c7ed8
 modules/pam_usertype/pam_usertype.8.xml | 170 +++++++++++++
8c7ed8
 modules/pam_usertype/pam_usertype.c     | 319 ++++++++++++++++++++++++
8c7ed8
 modules/pam_usertype/tst-pam_usertype   |   2 +
8c7ed8
 7 files changed, 589 insertions(+), 1 deletion(-)
8c7ed8
 create mode 100644 modules/pam_usertype/Makefile.am
8c7ed8
 create mode 100644 modules/pam_usertype/README.xml
8c7ed8
 create mode 100644 modules/pam_usertype/pam_usertype.8.xml
8c7ed8
 create mode 100644 modules/pam_usertype/pam_usertype.c
8c7ed8
 create mode 100755 modules/pam_usertype/tst-pam_usertype
8c7ed8
8c7ed8
diff --git a/configure.ac b/configure.ac
8c7ed8
index 90818683..2e7f131f 100644
8c7ed8
--- a/configure.ac
8c7ed8
+++ b/configure.ac
8c7ed8
@@ -649,6 +649,27 @@ AC_SUBST([HAVE_KEY_MANAGEMENT], $HAVE_KEY_MANAGEMENT)
8c7ed8
 
8c7ed8
 AM_CONDITIONAL([HAVE_KEY_MANAGEMENT], [test "$have_key_syscalls" = 1])
8c7ed8
 
8c7ed8
+dnl
8c7ed8
+dnl Get values for default uid ranges in login.defs used in pam_usertype
8c7ed8
+dnl
8c7ed8
+AC_ARG_WITH([uidmin], AS_HELP_STRING([--with-uidmin=<number>],[default value for regular user min uid (1000)]), opt_uidmin=$withval)
8c7ed8
+if test x"$opt_uidmin" == x; then
8c7ed8
+    opt_uidmin=1000
8c7ed8
+fi
8c7ed8
+AC_DEFINE_UNQUOTED(PAM_USERTYPE_UIDMIN, $opt_uidmin, [Minimum regular user uid.])
8c7ed8
+
8c7ed8
+AC_ARG_WITH([sysuidmin], AS_HELP_STRING([--with-sysuidmin=<number>],[default value for system user min uid (101)]), opt_sysuidmin=$withval)
8c7ed8
+if test x"$opt_sysuidmin" == x; then
8c7ed8
+    opt_sysuidmin=101
8c7ed8
+fi
8c7ed8
+AC_DEFINE_UNQUOTED(PAM_USERTYPE_SYSUIDMIN, $opt_sysuidmin, [Minimum system user uid.])
8c7ed8
+
8c7ed8
+AC_ARG_WITH([kerneloverflowuid], AS_HELP_STRING([--with-kernel-overflow-uid=<number>],[kernel overflow uid, default (uint16_t)-2=65534]), opt_kerneloverflowuid=$withval)
8c7ed8
+if test x"$opt_kerneloverflowuid" == x; then
8c7ed8
+    opt_kerneloverflowuid=65534
8c7ed8
+fi
8c7ed8
+AC_DEFINE_UNQUOTED(PAM_USERTYPE_OVERFLOW_UID, $opt_kerneloverflowuid, [Kernel overflow uid.])
8c7ed8
+
8c7ed8
 dnl Files to be created from when we run configure
8c7ed8
 AC_CONFIG_FILES([Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile \
8c7ed8
 	libpam_misc/Makefile conf/Makefile conf/pam_conv1/Makefile \
8c7ed8
@@ -677,6 +698,7 @@ AC_CONFIG_FILES([Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile
8c7ed8
 	modules/pam_timestamp/Makefile modules/pam_tty_audit/Makefile \
8c7ed8
 	modules/pam_umask/Makefile \
8c7ed8
 	modules/pam_unix/Makefile modules/pam_userdb/Makefile \
8c7ed8
+	modules/pam_usertype/Makefile \
8c7ed8
 	modules/pam_warn/Makefile modules/pam_wheel/Makefile \
8c7ed8
 	modules/pam_xauth/Makefile doc/Makefile doc/specs/Makefile \
8c7ed8
 	doc/man/Makefile doc/sag/Makefile doc/adg/Makefile \
8c7ed8
diff --git a/modules/Makefile.am b/modules/Makefile.am
8c7ed8
index 612fc740..d9659cb7 100644
8c7ed8
--- a/modules/Makefile.am
8c7ed8
+++ b/modules/Makefile.am
8c7ed8
@@ -19,7 +19,7 @@ SUBDIRS := pam_access pam_cracklib pam_debug pam_deny pam_echo \
8c7ed8
 	pam_selinux pam_sepermit pam_shells pam_stress \
8c7ed8
 	pam_succeed_if pam_time pam_timestamp \
8c7ed8
 	pam_tty_audit pam_umask \
8c7ed8
-	pam_unix pam_userdb pam_warn pam_wheel pam_xauth
8c7ed8
+	pam_unix pam_userdb pam_usertype pam_warn pam_wheel pam_xauth
8c7ed8
 
8c7ed8
 CLEANFILES = *~
8c7ed8
 
8c7ed8
diff --git a/modules/pam_usertype/Makefile.am b/modules/pam_usertype/Makefile.am
8c7ed8
new file mode 100644
8c7ed8
index 00000000..1646bc34
8c7ed8
--- /dev/null
8c7ed8
+++ b/modules/pam_usertype/Makefile.am
8c7ed8
@@ -0,0 +1,34 @@
8c7ed8
+#
8c7ed8
+# Copyright (c) 2005, 2006, 2009 Thorsten Kukuk <kukuk@suse.de>
8c7ed8
+# Copyright (c) 2020 Red Hat, Inc.
8c7ed8
+#
8c7ed8
+
8c7ed8
+CLEANFILES = *~
8c7ed8
+MAINTAINERCLEANFILES = $(MANS) README
8c7ed8
+
8c7ed8
+EXTRA_DIST = README ${MANS} ${XMLS} tst-pam_usertype
8c7ed8
+
8c7ed8
+TESTS = tst-pam_usertype
8c7ed8
+
8c7ed8
+man_MANS = pam_usertype.8
8c7ed8
+
8c7ed8
+XMLS = README.xml pam_usertype.8.xml
8c7ed8
+
8c7ed8
+securelibdir = $(SECUREDIR)
8c7ed8
+secureconfdir = $(SCONFIGDIR)
8c7ed8
+
8c7ed8
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
8c7ed8
+	$(WARN_CFLAGS)
8c7ed8
+AM_LDFLAGS = -no-undefined -avoid-version -module
8c7ed8
+if HAVE_VERSIONING
8c7ed8
+  AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
8c7ed8
+endif
8c7ed8
+
8c7ed8
+securelib_LTLIBRARIES = pam_usertype.la
8c7ed8
+pam_usertype_la_LIBADD = $(top_builddir)/libpam/libpam.la
8c7ed8
+
8c7ed8
+if ENABLE_REGENERATE_MAN
8c7ed8
+noinst_DATA = README
8c7ed8
+README: pam_usertype.8.xml
8c7ed8
+-include $(top_srcdir)/Make.xml.rules
8c7ed8
+endif
8c7ed8
diff --git a/modules/pam_usertype/README.xml b/modules/pam_usertype/README.xml
8c7ed8
new file mode 100644
8c7ed8
index 00000000..58550465
8c7ed8
--- /dev/null
8c7ed8
+++ b/modules/pam_usertype/README.xml
8c7ed8
@@ -0,0 +1,41 @@
8c7ed8
+
8c7ed8
+
8c7ed8
+"http://www.docbook.org/xml/4.3/docbookx.dtd"
8c7ed8
+[
8c7ed8
+
8c7ed8
+
8c7ed8
+-->
8c7ed8
+]>
8c7ed8
+
8c7ed8
+<article>
8c7ed8
+
8c7ed8
+  <articleinfo>
8c7ed8
+
8c7ed8
+    <title>
8c7ed8
+      
8c7ed8
+      href="pam_usertype.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_usertype-name"]/*)'/>
8c7ed8
+    </title>
8c7ed8
+
8c7ed8
+  </articleinfo>
8c7ed8
+
8c7ed8
+  <section>
8c7ed8
+    
8c7ed8
+      href="pam_usertype.8.xml" xpointer='xpointer(//refsect1[@id = "pam_usertype-description"]/*)'/>
8c7ed8
+  </section>
8c7ed8
+
8c7ed8
+  <section>
8c7ed8
+    
8c7ed8
+      href="pam_usertype.8.xml" xpointer='xpointer(//refsect1[@id = "pam_usertype-options"]/*)'/>
8c7ed8
+  </section>
8c7ed8
+
8c7ed8
+  <section>
8c7ed8
+    
8c7ed8
+      href="pam_usertype.8.xml" xpointer='xpointer(//refsect1[@id = "pam_usertype-examples"]/*)'/>
8c7ed8
+  </section>
8c7ed8
+
8c7ed8
+  <section>
8c7ed8
+    
8c7ed8
+      href="pam_usertype.8.xml" xpointer='xpointer(//refsect1[@id = "pam_usertype-author"]/*)'/>
8c7ed8
+  </section>
8c7ed8
+
8c7ed8
+</article>
8c7ed8
diff --git a/modules/pam_usertype/pam_usertype.8.xml b/modules/pam_usertype/pam_usertype.8.xml
8c7ed8
new file mode 100644
8c7ed8
index 00000000..1ba4ee71
8c7ed8
--- /dev/null
8c7ed8
+++ b/modules/pam_usertype/pam_usertype.8.xml
8c7ed8
@@ -0,0 +1,170 @@
8c7ed8
+
8c7ed8
+
8c7ed8
+        "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
8c7ed8
+
8c7ed8
+
8c7ed8
+<refentry id='pam_usertype'>
8c7ed8
+  <refmeta>
8c7ed8
+    <refentrytitle>pam_usertype</refentrytitle>
8c7ed8
+    <manvolnum>8</manvolnum>
8c7ed8
+    <refmiscinfo class='sectdesc'>Linux-PAM</refmiscinfo>
8c7ed8
+  </refmeta>
8c7ed8
+
8c7ed8
+  <refnamediv id='pam_usertype-name'>
8c7ed8
+    <refname>pam_usertype</refname>
8c7ed8
+    <refpurpose>check if the authenticated user is a system or regular account</refpurpose>
8c7ed8
+  </refnamediv>
8c7ed8
+
8c7ed8
+
8c7ed8
+  <refsynopsisdiv>
8c7ed8
+    <cmdsynopsis id='pam_usertype-cmdsynopsis'>
8c7ed8
+      <command>pam_usertype.so</command>
8c7ed8
+      <arg choice='opt' rep='repeat'><replaceable>flag</replaceable></arg>
8c7ed8
+      <arg choice='req'><replaceable>condition</replaceable></arg>
8c7ed8
+    </cmdsynopsis>
8c7ed8
+  </refsynopsisdiv>
8c7ed8
+
8c7ed8
+
8c7ed8
+  <refsect1 id='pam_usertype-description'>
8c7ed8
+    <title>DESCRIPTION</title>
8c7ed8
+    <para>
8c7ed8
+      pam_usertype.so is designed to succeed or fail authentication
8c7ed8
+      based on type of the account of the authenticated user.
8c7ed8
+      The type of the account is decided with help of
8c7ed8
+      <emphasis>SYS_UID_MIN</emphasis> and <emphasis>SYS_UID_MAX</emphasis>
8c7ed8
+      settings in <emphasis>/etc/login.defs</emphasis>. One use is to select
8c7ed8
+      whether to load other modules based on this test.
8c7ed8
+    </para>
8c7ed8
+
8c7ed8
+    <para>
8c7ed8
+      The module should be given only one condition as module argument.
8c7ed8
+      Authentication will succeed only if the condition is met.
8c7ed8
+    </para>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+  <refsect1 id="pam_usertype-options">
8c7ed8
+    <title>OPTIONS</title>
8c7ed8
+    <para>
8c7ed8
+      The following <emphasis>flag</emphasis>s are supported:
8c7ed8
+    </para>
8c7ed8
+
8c7ed8
+    <variablelist>
8c7ed8
+      <varlistentry>
8c7ed8
+        <term><option>use_uid</option></term>
8c7ed8
+        <listitem>
8c7ed8
+          <para>
8c7ed8
+            Evaluate conditions using the account of the user whose UID
8c7ed8
+            the application is running under instead of the user being
8c7ed8
+            authenticated.
8c7ed8
+          </para>
8c7ed8
+        </listitem>
8c7ed8
+      </varlistentry>
8c7ed8
+      <varlistentry>
8c7ed8
+        <term><option>audit</option></term>
8c7ed8
+        <listitem>
8c7ed8
+          <para>
8c7ed8
+            Log unknown users to the system log.
8c7ed8
+          </para>
8c7ed8
+        </listitem>
8c7ed8
+      </varlistentry>
8c7ed8
+    </variablelist>
8c7ed8
+
8c7ed8
+    <para>
8c7ed8
+      Available <emphasis>condition</emphasis>s are:
8c7ed8
+    </para>
8c7ed8
+
8c7ed8
+    <variablelist>
8c7ed8
+      <varlistentry>
8c7ed8
+        <term><option>issystem</option></term>
8c7ed8
+        <listitem>
8c7ed8
+          <para>Succeed if the user is a system user.</para>
8c7ed8
+        </listitem>
8c7ed8
+      </varlistentry>
8c7ed8
+      <varlistentry>
8c7ed8
+        <term><option>isregular</option></term>
8c7ed8
+        <listitem>
8c7ed8
+          <para>Succeed if the user is a regular user.</para>
8c7ed8
+        </listitem>
8c7ed8
+      </varlistentry>
8c7ed8
+    </variablelist>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+  <refsect1 id="pam_usertype-types">
8c7ed8
+    <title>MODULE TYPES PROVIDED</title>
8c7ed8
+    <para>
8c7ed8
+      All module types (<option>account</option>, <option>auth</option>,
8c7ed8
+      <option>password</option> and <option>session</option>) are provided.
8c7ed8
+    </para>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+  <refsect1 id='pam_usertype-return_values'>
8c7ed8
+    <title>RETURN VALUES</title>
8c7ed8
+     <variablelist>
8c7ed8
+
8c7ed8
+        <varlistentry>
8c7ed8
+          <term>PAM_SUCCESS</term>
8c7ed8
+          <listitem>
8c7ed8
+            <para>
8c7ed8
+              The condition was true.
8c7ed8
+            </para>
8c7ed8
+          </listitem>
8c7ed8
+        </varlistentry>
8c7ed8
+
8c7ed8
+        <varlistentry>
8c7ed8
+          <term>PAM_AUTH_ERR</term>
8c7ed8
+          <listitem>
8c7ed8
+            <para>
8c7ed8
+              The condition was false.
8c7ed8
+            </para>
8c7ed8
+          </listitem>
8c7ed8
+        </varlistentry>
8c7ed8
+
8c7ed8
+        <varlistentry>
8c7ed8
+          <term>PAM_SERVICE_ERR</term>
8c7ed8
+          <listitem>
8c7ed8
+            <para>
8c7ed8
+              A service error occurred or the arguments can't be
8c7ed8
+              parsed correctly.
8c7ed8
+            </para>
8c7ed8
+          </listitem>
8c7ed8
+        </varlistentry>
8c7ed8
+
8c7ed8
+        <varlistentry>
8c7ed8
+          <term>PAM_USER_UNKNOWN</term>
8c7ed8
+          <listitem>
8c7ed8
+            <para>
8c7ed8
+              User was not found.
8c7ed8
+            </para>
8c7ed8
+          </listitem>
8c7ed8
+        </varlistentry>
8c7ed8
+    </variablelist>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+
8c7ed8
+  <refsect1 id='pam_usertype-examples'>
8c7ed8
+    <title>EXAMPLES</title>
8c7ed8
+    <para>
8c7ed8
+      Skip remaining modules if the user is a system user:
8c7ed8
+    </para>
8c7ed8
+    <programlisting>
8c7ed8
+account sufficient pam_usertype.so issystem
8c7ed8
+    </programlisting>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+  <refsect1 id='pam_usertype-see_also'>
8c7ed8
+    <title>SEE ALSO</title>
8c7ed8
+    <para>
8c7ed8
+      <citerefentry>
8c7ed8
+        <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
8c7ed8
+      </citerefentry>,
8c7ed8
+      <citerefentry>
8c7ed8
+        <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
8c7ed8
+      </citerefentry>
8c7ed8
+    </para>
8c7ed8
+  </refsect1>
8c7ed8
+
8c7ed8
+  <refsect1 id='pam_usertype-author'>
8c7ed8
+    <title>AUTHOR</title>
8c7ed8
+    <para>Pavel Březina <pbrezina@redhat.com></para>
8c7ed8
+  </refsect1>
8c7ed8
+</refentry>
8c7ed8
diff --git a/modules/pam_usertype/pam_usertype.c b/modules/pam_usertype/pam_usertype.c
8c7ed8
new file mode 100644
8c7ed8
index 00000000..d3629c13
8c7ed8
--- /dev/null
8c7ed8
+++ b/modules/pam_usertype/pam_usertype.c
8c7ed8
@@ -0,0 +1,319 @@
8c7ed8
+/******************************************************************************
8c7ed8
+ * Check user type based on login.defs.
8c7ed8
+ *
8c7ed8
+ * Copyright (c) 2020 Red Hat, Inc.
8c7ed8
+ * Written by Pavel Březina <pbrezina@redhat.com>
8c7ed8
+ *
8c7ed8
+ * Redistribution and use in source and binary forms, with or without
8c7ed8
+ * modification, are permitted provided that the following conditions
8c7ed8
+ * are met:
8c7ed8
+ * 1. Redistributions of source code must retain the above copyright
8c7ed8
+ *    notice, and the entire permission notice in its entirety,
8c7ed8
+ *    including the disclaimer of warranties.
8c7ed8
+ * 2. Redistributions in binary form must reproduce the above copyright
8c7ed8
+ *    notice, this list of conditions and the following disclaimer in the
8c7ed8
+ *    documentation and/or other materials provided with the distribution.
8c7ed8
+ * 3. The name of the author may not be used to endorse or promote
8c7ed8
+ *    products derived from this software without specific prior
8c7ed8
+ *    written permission.
8c7ed8
+ *
8c7ed8
+ * ALTERNATIVELY, this product may be distributed under the terms of
8c7ed8
+ * the GNU Public License, in which case the provisions of the GPL are
8c7ed8
+ * required INSTEAD OF the above restrictions.  (This clause is
8c7ed8
+ * necessary due to a potential bad interaction between the GPL and
8c7ed8
+ * the restrictions contained in a BSD-style copyright.)
8c7ed8
+ *
8c7ed8
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
8c7ed8
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
8c7ed8
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
8c7ed8
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8c7ed8
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8c7ed8
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8c7ed8
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8c7ed8
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8c7ed8
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
8c7ed8
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
8c7ed8
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
8c7ed8
+ *
8c7ed8
+ */
8c7ed8
+
8c7ed8
+#include "config.h"
8c7ed8
+
8c7ed8
+#include <sys/types.h>
8c7ed8
+#include <stdlib.h>
8c7ed8
+#include <string.h>
8c7ed8
+#include <syslog.h>
8c7ed8
+#include <unistd.h>
8c7ed8
+#include <pwd.h>
8c7ed8
+#include <ctype.h>
8c7ed8
+#include <errno.h>
8c7ed8
+#include <stdio.h>
8c7ed8
+
8c7ed8
+#define PAM_SM_AUTH
8c7ed8
+#define PAM_SM_ACCOUNT
8c7ed8
+#define PAM_SM_SESSION
8c7ed8
+#define PAM_SM_PASSWORD
8c7ed8
+
8c7ed8
+#include <security/pam_modules.h>
8c7ed8
+#include <security/pam_modutil.h>
8c7ed8
+#include <security/pam_ext.h>
8c7ed8
+
8c7ed8
+#define LOGIN_DEFS "/etc/login.defs"
8c7ed8
+
8c7ed8
+enum pam_usertype_op {
8c7ed8
+    OP_IS_SYSTEM,
8c7ed8
+    OP_IS_REGULAR,
8c7ed8
+
8c7ed8
+    OP_SENTINEL
8c7ed8
+};
8c7ed8
+
8c7ed8
+struct pam_usertype_opts {
8c7ed8
+    enum pam_usertype_op op;
8c7ed8
+    int use_uid;
8c7ed8
+    int audit;
8c7ed8
+};
8c7ed8
+
8c7ed8
+static int
8c7ed8
+pam_usertype_parse_args(struct pam_usertype_opts *opts,
8c7ed8
+                        pam_handle_t *pamh,
8c7ed8
+                        int argc,
8c7ed8
+                        const char **argv)
8c7ed8
+{
8c7ed8
+    int i;
8c7ed8
+
8c7ed8
+    memset(opts, 0, sizeof(struct pam_usertype_opts));
8c7ed8
+    opts->op = OP_SENTINEL;
8c7ed8
+
8c7ed8
+    for (i = 0; i < argc; i++) {
8c7ed8
+        if (strcmp(argv[i], "use_uid") == 0) {
8c7ed8
+            opts->use_uid = 1;
8c7ed8
+        } else if (strcmp(argv[i], "audit") == 0) {
8c7ed8
+            opts->audit = 1;
8c7ed8
+        } else if (strcmp(argv[i], "issystem") == 0) {
8c7ed8
+            opts->op = OP_IS_SYSTEM;
8c7ed8
+        } else if (strcmp(argv[i], "isregular") == 0) {
8c7ed8
+            opts->op = OP_IS_REGULAR;
8c7ed8
+        } else {
8c7ed8
+            pam_syslog(pamh, LOG_WARNING, "Unknown argument: %s", argv[i]);
8c7ed8
+            /* Just continue. */
8c7ed8
+        }
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    if (opts->op == OP_SENTINEL) {
8c7ed8
+        pam_syslog(pamh, LOG_ERR, "Operation not specified");
8c7ed8
+        return PAM_SERVICE_ERR;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    return PAM_SUCCESS;
8c7ed8
+}
8c7ed8
+
8c7ed8
+static int
8c7ed8
+pam_usertype_get_uid(struct pam_usertype_opts *opts,
8c7ed8
+                     pam_handle_t *pamh,
8c7ed8
+                     uid_t *_uid)
8c7ed8
+{
8c7ed8
+    struct passwd *pwd;
8c7ed8
+    const void *prompt;
8c7ed8
+    const char *username;
8c7ed8
+    int ret;
8c7ed8
+
8c7ed8
+    /* Get uid of user that runs the application. */
8c7ed8
+    if (opts->use_uid) {
8c7ed8
+        pwd = pam_modutil_getpwuid(pamh, getuid());
8c7ed8
+        if (pwd == NULL) {
8c7ed8
+            pam_syslog(pamh, LOG_ERR,
8c7ed8
+                       "error retrieving information about user %lu",
8c7ed8
+                       (unsigned long)getuid());
8c7ed8
+            return PAM_USER_UNKNOWN;
8c7ed8
+        }
8c7ed8
+
8c7ed8
+        *_uid = pwd->pw_uid;
8c7ed8
+        return PAM_SUCCESS;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    /* Get uid of user that is being authenticated. */
8c7ed8
+    ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
8c7ed8
+    if (ret != PAM_SUCCESS || prompt == NULL || strlen(prompt) == 0) {
8c7ed8
+        prompt = "login: ";
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    ret = pam_get_user(pamh, &username, prompt);
8c7ed8
+    if (ret != PAM_SUCCESS || username == NULL) {
8c7ed8
+        pam_syslog(pamh, LOG_ERR, "error retrieving user name: %s",
8c7ed8
+                   pam_strerror(pamh, ret));
8c7ed8
+        return ret;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    pwd = pam_modutil_getpwnam(pamh, username);
8c7ed8
+    if (pwd == NULL) {
8c7ed8
+        if (opts->audit) {
8c7ed8
+            pam_syslog(pamh, LOG_NOTICE,
8c7ed8
+                       "error retrieving information about user %s", username);
8c7ed8
+        }
8c7ed8
+
8c7ed8
+        return PAM_USER_UNKNOWN;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    *_uid = pwd->pw_uid;
8c7ed8
+
8c7ed8
+    return PAM_SUCCESS;
8c7ed8
+}
8c7ed8
+
8c7ed8
+#define MAX_UID_VALUE 0xFFFFFFFFUL
8c7ed8
+
8c7ed8
+/* lookup a value for key in login.defs file or similar key value format */
8c7ed8
+char *
8c7ed8
+pam_usertype_search_key(pam_handle_t *pamh UNUSED,
8c7ed8
+		       const char *file_name,
8c7ed8
+		       const char *key)
8c7ed8
+{
8c7ed8
+	FILE *fp;
8c7ed8
+	char *buf = NULL;
8c7ed8
+	size_t buflen = 0;
8c7ed8
+	char *retval = NULL;
8c7ed8
+
8c7ed8
+	fp = fopen(file_name, "r");
8c7ed8
+	if (NULL == fp)
8c7ed8
+		return NULL;
8c7ed8
+
8c7ed8
+	while (!feof(fp)) {
8c7ed8
+		char *tmp, *cp;
8c7ed8
+#if defined(HAVE_GETLINE)
8c7ed8
+		ssize_t n = getline(&buf, &buflen, fp);
8c7ed8
+#elif defined (HAVE_GETDELIM)
8c7ed8
+		ssize_t n = getdelim(&buf, &buflen, '\n', fp);
8c7ed8
+#else
8c7ed8
+		ssize_t n;
8c7ed8
+
8c7ed8
+		if (buf == NULL) {
8c7ed8
+			buflen = BUF_SIZE;
8c7ed8
+			buf = malloc(buflen);
8c7ed8
+			if (buf == NULL) {
8c7ed8
+				fclose(fp);
8c7ed8
+				return NULL;
8c7ed8
+			}
8c7ed8
+		}
8c7ed8
+		buf[0] = '\0';
8c7ed8
+		if (fgets(buf, buflen - 1, fp) == NULL)
8c7ed8
+			break;
8c7ed8
+		else if (buf != NULL)
8c7ed8
+			n = strlen(buf);
8c7ed8
+		else
8c7ed8
+			n = 0;
8c7ed8
+#endif /* HAVE_GETLINE / HAVE_GETDELIM */
8c7ed8
+		cp = buf;
8c7ed8
+
8c7ed8
+		if (n < 1)
8c7ed8
+			break;
8c7ed8
+		if (cp[n - 1] == '\n')
8c7ed8
+			cp[n - 1] = '\0';
8c7ed8
+
8c7ed8
+		tmp = strchr(cp, '#');  /* remove comments */
8c7ed8
+		if (tmp)
8c7ed8
+			*tmp = '\0';
8c7ed8
+		while (isspace((int)*cp))    /* remove spaces and tabs */
8c7ed8
+			++cp;
8c7ed8
+		if (*cp == '\0')        /* ignore empty lines */
8c7ed8
+			continue;
8c7ed8
+
8c7ed8
+		tmp = strsep (&cp, " \t=");
8c7ed8
+		if (cp != NULL)
8c7ed8
+			while (isspace((int)*cp) || *cp == '=')
8c7ed8
+				++cp;
8c7ed8
+		else
8c7ed8
+			cp = "";
8c7ed8
+
8c7ed8
+		if (strcasecmp(tmp, key) == 0) {
8c7ed8
+			retval = strdup(cp);
8c7ed8
+			break;
8c7ed8
+		}
8c7ed8
+	}
8c7ed8
+	fclose(fp);
8c7ed8
+
8c7ed8
+	free(buf);
8c7ed8
+
8c7ed8
+	return retval;
8c7ed8
+}
8c7ed8
+
8c7ed8
+static uid_t
8c7ed8
+pam_usertype_get_id(pam_handle_t *pamh,
8c7ed8
+                    const char *key,
8c7ed8
+                    uid_t default_value)
8c7ed8
+{
8c7ed8
+    unsigned long ul;
8c7ed8
+    char *value;
8c7ed8
+    char *ep;
8c7ed8
+    uid_t uid;
8c7ed8
+
8c7ed8
+    value = pam_usertype_search_key(pamh, LOGIN_DEFS, key);
8c7ed8
+    if (value == NULL) {
8c7ed8
+        return default_value;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    /* taken from get_lastlog_uid_max() */
8c7ed8
+    ep = value + strlen(value);
8c7ed8
+    while (ep > value && isspace(*(--ep))) {
8c7ed8
+        *ep = '\0';
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    errno = 0;
8c7ed8
+    ul = strtoul(value, &ep, 10);
8c7ed8
+    if (!(ul >= MAX_UID_VALUE
8c7ed8
+        || (uid_t)ul >= MAX_UID_VALUE
8c7ed8
+        || (errno != 0 && ul == 0)
8c7ed8
+        || value == ep
8c7ed8
+        || *ep != '\0')) {
8c7ed8
+        uid = (uid_t)ul;
8c7ed8
+    } else {
8c7ed8
+        uid = default_value;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    free(value);
8c7ed8
+
8c7ed8
+    return uid;
8c7ed8
+}
8c7ed8
+
8c7ed8
+static int
8c7ed8
+pam_usertype_is_system(pam_handle_t *pamh, uid_t uid)
8c7ed8
+{
8c7ed8
+    uid_t uid_min;
8c7ed8
+    uid_t sys_min;
8c7ed8
+    uid_t sys_max;
8c7ed8
+
8c7ed8
+    if (uid == (uid_t)-1) {
8c7ed8
+        pam_syslog(pamh, LOG_WARNING, "invalid uid");
8c7ed8
+        return PAM_USER_UNKNOWN;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    if (uid <= 99) {
8c7ed8
+        /* Reserved. */
8c7ed8
+        return PAM_SUCCESS;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    if (uid == PAM_USERTYPE_OVERFLOW_UID) {
8c7ed8
+        /* nobody */
8c7ed8
+        return PAM_SUCCESS;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    uid_min = pam_usertype_get_id(pamh, "UID_MIN", PAM_USERTYPE_UIDMIN);
8c7ed8
+    sys_min = pam_usertype_get_id(pamh, "SYS_UID_MIN", PAM_USERTYPE_SYSUIDMIN);
8c7ed8
+    sys_max = pam_usertype_get_id(pamh, "SYS_UID_MAX", uid_min - 1);
8c7ed8
+
8c7ed8
+    return uid >= sys_min && uid <= sys_max ? PAM_SUCCESS : PAM_AUTH_ERR;
8c7ed8
+}
8c7ed8
+
8c7ed8
+static int
8c7ed8
+pam_usertype_is_regular(pam_handle_t *pamh, uid_t uid)
8c7ed8
+{
8c7ed8
+    int ret;
8c7ed8
+
8c7ed8
+    ret = pam_usertype_is_system(pamh, uid);
8c7ed8
+    switch (ret) {
8c7ed8
+    case PAM_SUCCESS:
8c7ed8
+        return PAM_AUTH_ERR;
8c7ed8
+    case PAM_USER_UNKNOWN:
8c7ed8
+        return PAM_USER_UNKNOWN;
8c7ed8
+    default:
8c7ed8
+        return PAM_SUCCESS;
8c7ed8
+    }
8c7ed8
+}
8c7ed8
+
8c7ed8
+static int
8c7ed8
+pam_usertype_evaluate(struct pam_usertype_opts *opts,
8c7ed8
+                      pam_handle_t *pamh,
8c7ed8
+                      uid_t uid)
8c7ed8
+{
8c7ed8
+    switch (opts->op) {
8c7ed8
+    case OP_IS_SYSTEM:
8c7ed8
+        return pam_usertype_is_system(pamh, uid);
8c7ed8
+    case OP_IS_REGULAR:
8c7ed8
+        return pam_usertype_is_regular(pamh, uid);
8c7ed8
+    default:
8c7ed8
+        pam_syslog(pamh, LOG_ERR, "Unknown operation: %d", opts->op);
8c7ed8
+        return PAM_SERVICE_ERR;
8c7ed8
+    }
8c7ed8
+}
8c7ed8
+
8c7ed8
+/**
8c7ed8
+ * Arguments:
8c7ed8
+ * - issystem: uid in <SYS_UID_MIN, SYS_UID_MAX>
8c7ed8
+ * - isregular: not issystem
8c7ed8
+ * - use_uid: use user that runs application not that is being authenticate (same as in pam_succeed_if)
8c7ed8
+ * - audit: log unknown users to syslog
8c7ed8
+ */
8c7ed8
+int
8c7ed8
+pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
8c7ed8
+                    int argc, const char **argv)
8c7ed8
+{
8c7ed8
+    struct pam_usertype_opts opts;
8c7ed8
+    uid_t uid;
8c7ed8
+    int ret;
8c7ed8
+
8c7ed8
+    ret = pam_usertype_parse_args(&opts, pamh, argc, argv);
8c7ed8
+    if (ret != PAM_SUCCESS) {
8c7ed8
+        return ret;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    ret = pam_usertype_get_uid(&opts, pamh, &uid);
8c7ed8
+    if (ret != PAM_SUCCESS) {
8c7ed8
+        return ret;
8c7ed8
+    }
8c7ed8
+
8c7ed8
+    return pam_usertype_evaluate(&opts, pamh, uid);
8c7ed8
+}
8c7ed8
+
8c7ed8
+int
8c7ed8
+pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
8c7ed8
+               int argc UNUSED, const char **argv UNUSED)
8c7ed8
+{
8c7ed8
+	return PAM_IGNORE;
8c7ed8
+}
8c7ed8
+
8c7ed8
+int
8c7ed8
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
8c7ed8
+{
8c7ed8
+	return pam_sm_authenticate(pamh, flags, argc, argv);
8c7ed8
+}
8c7ed8
+
8c7ed8
+int
8c7ed8
+pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
8c7ed8
+{
8c7ed8
+	return pam_sm_authenticate(pamh, flags, argc, argv);
8c7ed8
+}
8c7ed8
+
8c7ed8
+int
8c7ed8
+pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
8c7ed8
+{
8c7ed8
+	return pam_sm_authenticate(pamh, flags, argc, argv);
8c7ed8
+}
8c7ed8
+
8c7ed8
+int
8c7ed8
+pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
8c7ed8
+{
8c7ed8
+	return pam_sm_authenticate(pamh, flags, argc, argv);
8c7ed8
+}
8c7ed8
diff --git a/modules/pam_usertype/tst-pam_usertype b/modules/pam_usertype/tst-pam_usertype
8c7ed8
new file mode 100755
8c7ed8
index 00000000..a21f8fe7
8c7ed8
--- /dev/null
8c7ed8
+++ b/modules/pam_usertype/tst-pam_usertype
8c7ed8
@@ -0,0 +1,2 @@
8c7ed8
+#!/bin/sh
8c7ed8
+../../tests/tst-dlopen .libs/pam_usertype.so
8c7ed8
-- 
8c7ed8
2.25.2
8c7ed8