diff --git a/SOURCES/shadow-4.6-getsubids.patch b/SOURCES/shadow-4.6-getsubids.patch
new file mode 100644
index 0000000..12030cb
--- /dev/null
+++ b/SOURCES/shadow-4.6-getsubids.patch
@@ -0,0 +1,244 @@
+diff -up shadow-4.6/man/getsubids.1.xml.getsubids shadow-4.6/man/getsubids.1.xml
+--- shadow-4.6/man/getsubids.1.xml.getsubids 2021-12-09 10:40:50.730275761 +0100
++++ shadow-4.6/man/getsubids.1.xml 2021-12-09 10:40:50.730275761 +0100
+@@ -0,0 +1,141 @@
++
++
++
++]>
++
++
++
++
++ Iker
++ Pedrosa
++ Creation, 2021
++
++
++
++ getsubids
++ 1
++ User Commands
++ shadow-utils
++ &SHADOW_UTILS_VERSION;
++
++
++ getsubids
++ get the subordinate id ranges for a user
++
++
++
++
++ getsubids
++
++ options
++
++
++ USER
++
++
++
++
++
++ DESCRIPTION
++
++ The getsubids command lists the subordinate user ID
++ ranges for a given user. The subordinate group IDs can be listed using
++ the option.
++
++
++
++
++ OPTIONS
++
++ The options which apply to the getsubids command are:
++
++
++
++
++
++
++
++
++ List the subordinate group ID ranges.
++
++
++
++
++
++
++
++ EXAMPLE
++
++ For example, to obtain the subordinate UIDs of the testuser:
++
++
++
++$ getsubids testuser
++0: testuser 100000 65536
++
++
++
++ This command output provides (in order from left to right) the list
++ index, username, UID range start, and number of UIDs in range.
++
++
++
++
++ SEE ALSO
++
++
++ login.defs5
++ ,
++
++ newgidmap1
++ ,
++
++ newuidmap1
++ ,
++
++ subgid5
++ ,
++
++ subuid5
++ ,
++
++ useradd8
++ ,
++
++ userdel8
++ .
++
++ usermod8
++ ,
++
++
++
+diff -up shadow-4.6/man/Makefile.am.getsubids shadow-4.6/man/Makefile.am
+--- shadow-4.6/man/Makefile.am.getsubids 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/man/Makefile.am 2021-12-09 10:40:50.730275761 +0100
+@@ -59,6 +59,7 @@ man_MANS += $(man_nopam)
+ endif
+
+ man_subids = \
++ man1/getsubids.1 \
+ man1/newgidmap.1 \
+ man1/newuidmap.1 \
+ man5/subgid.5 \
+@@ -77,6 +78,7 @@ man_XMANS = \
+ expiry.1.xml \
+ faillog.5.xml \
+ faillog.8.xml \
++ getsubids.1.xml \
+ gpasswd.1.xml \
+ groupadd.8.xml \
+ groupdel.8.xml \
+diff -up shadow-4.6/src/getsubids.c.getsubids shadow-4.6/src/getsubids.c
+--- shadow-4.6/src/getsubids.c.getsubids 2021-12-09 10:40:50.730275761 +0100
++++ shadow-4.6/src/getsubids.c 2021-12-09 10:40:50.730275761 +0100
+@@ -0,0 +1,46 @@
++#include
++#include
++#include
++#include "subid.h"
++#include "prototypes.h"
++
++const char *Prog;
++FILE *shadow_logfd = NULL;
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: %s [-g] user\n", Prog);
++ fprintf(stderr, " list subuid ranges for user\n");
++ fprintf(stderr, " pass -g to list subgid ranges\n");
++ exit(EXIT_FAILURE);
++}
++
++int main(int argc, char *argv[])
++{
++ int i, count=0;
++ struct subid_range *ranges;
++ const char *owner;
++
++ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
++ if (argc < 2)
++ usage();
++ owner = argv[1];
++ if (argc == 3 && strcmp(argv[1], "-g") == 0) {
++ owner = argv[2];
++ count = get_subgid_ranges(owner, &ranges);
++ } else if (argc == 2 && strcmp(argv[1], "-h") == 0) {
++ usage();
++ } else {
++ count = get_subuid_ranges(owner, &ranges);
++ }
++ if (!ranges) {
++ fprintf(stderr, "Error fetching ranges\n");
++ exit(1);
++ }
++ for (i = 0; i < count; i++) {
++ printf("%d: %s %lu %lu\n", i, owner,
++ ranges[i].start, ranges[i].count);
++ }
++ return 0;
++}
+diff -up shadow-4.6/src/Makefile.am.getsubids shadow-4.6/src/Makefile.am
+--- shadow-4.6/src/Makefile.am.getsubids 2021-12-09 10:40:50.710275627 +0100
++++ shadow-4.6/src/Makefile.am 2021-12-09 10:45:04.465985510 +0100
+@@ -140,8 +140,8 @@ if WITH_TCB
+ endif
+
+ if ENABLE_SUBIDS
+-noinst_PROGRAMS += list_subid_ranges \
+- get_subid_owners \
++bin_PROGRAMS += getsubids
++noinst_PROGRAMS += get_subid_owners \
+ new_subid_range \
+ free_subid_range \
+ check_subid_range
+@@ -156,13 +156,13 @@ MISCLIBS = \
+ $(LIBCRYPT) \
+ $(LIBTCB)
+
+-list_subid_ranges_LDADD = \
++getsubids_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/libsubid/libsubid.la \
+ $(MISCLIBS) -ldl
+
+-list_subid_ranges_CPPFLAGS = \
++getsubids_CPPFLAGS = \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/libmisc \
+ -I$(top_srcdir)/libsubid
diff --git a/SOURCES/shadow-4.6-groupdel-fix-sigsegv-when-passwd-does-not-exist.patch b/SOURCES/shadow-4.6-groupdel-fix-sigsegv-when-passwd-does-not-exist.patch
new file mode 100644
index 0000000..658156a
--- /dev/null
+++ b/SOURCES/shadow-4.6-groupdel-fix-sigsegv-when-passwd-does-not-exist.patch
@@ -0,0 +1,13 @@
+diff -up shadow-4.9/libmisc/prefix_flag.c.groupdel-fix-sigsegv-when-passwd-does-not-exist shadow-4.9/libmisc/prefix_flag.c
+--- shadow-4.9/libmisc/prefix_flag.c.groupdel-fix-sigsegv-when-passwd-does-not-exist 2021-11-19 09:21:36.997091941 +0100
++++ shadow-4.9/libmisc/prefix_flag.c 2021-11-19 09:22:19.001341010 +0100
+@@ -288,6 +288,9 @@ extern struct passwd* prefix_getpwent()
+ if(!passwd_db_file) {
+ return getpwent();
+ }
++ if (!fp_pwent) {
++ return NULL;
++ }
+ return fgetpwent(fp_pwent);
+ }
+ extern void prefix_endpwent()
diff --git a/SOURCES/shadow-4.6-install_subid_h.patch b/SOURCES/shadow-4.6-install_subid_h.patch
new file mode 100644
index 0000000..cee2e03
--- /dev/null
+++ b/SOURCES/shadow-4.6-install_subid_h.patch
@@ -0,0 +1,28 @@
+From 77e39de1e6cbd6925f16bb260abb7d216296886b Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Tue, 4 May 2021 09:21:11 -0500
+Subject: [PATCH] Install subid.h
+
+Now subid.h gets installed under /usr/include/shadow/subid.h
+
+Signed-off-by: Serge Hallyn
+---
+ libsubid/Makefile.am | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am
+index f543b5eb..189165b0 100644
+--- a/libsubid/Makefile.am
++++ b/libsubid/Makefile.am
+@@ -3,6 +3,8 @@ libsubid_la_LDFLAGS = -Wl,-soname,libsubid.so.@LIBSUBID_ABI@ \
+ -shared -version-info @LIBSUBID_ABI_MAJOR@
+ libsubid_la_SOURCES = api.c
+
++pkginclude_HEADERS = subid.h
++
+ MISCLIBS = \
+ $(LIBAUDIT) \
+ $(LIBSELINUX) \
+--
+2.31.1
+
diff --git a/SOURCES/shadow-4.6-libsubid_creation.patch b/SOURCES/shadow-4.6-libsubid_creation.patch
new file mode 100644
index 0000000..bd4b5bc
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_creation.patch
@@ -0,0 +1,1306 @@
+diff -up shadow-4.6/configure.ac.libsubid_creation shadow-4.6/configure.ac
+--- shadow-4.6/configure.ac.libsubid_creation 2021-10-19 16:12:02.663748272 +0200
++++ shadow-4.6/configure.ac 2021-10-19 16:13:07.194697194 +0200
+@@ -1,11 +1,21 @@
+ dnl Process this file with autoconf to produce a configure script.
+-AC_PREREQ([2.64])
++AC_PREREQ([2.69])
++m4_define([libsubid_abi_major], 1)
++m4_define([libsubid_abi_minor], 0)
++m4_define([libsubid_abi_micro], 0)
++m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
+ AC_INIT([shadow], [4.6], [pkg-shadow-devel@lists.alioth.debian.org], [],
+ [https://github.com/shadow-maint/shadow])
+ AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
++AC_CONFIG_MACRO_DIRS([m4])
+ AM_SILENT_RULES([yes])
+ AC_CONFIG_HEADERS([config.h])
+
++AC_SUBST([LIBSUBID_ABI_MAJOR], [libsubid_abi_major])
++AC_SUBST([LIBSUBID_ABI_MINOR], [libsubid_abi_minor])
++AC_SUBST([LIBSUBID_ABI_MICRO], [libsubid_abi_micro])
++AC_SUBST([LIBSUBID_ABI], [libsubid_abi])
++
+ dnl Some hacks...
+ test "$prefix" = "NONE" && prefix="/usr"
+ test "$prefix" = "/usr" && exec_prefix=""
+@@ -22,8 +22,8 @@ test "$prefix" = "/usr" && exec_prefix=""
+
+ AC_GNU_SOURCE
+
+-AM_DISABLE_SHARED
+ AM_ENABLE_STATIC
++AM_ENABLE_SHARED
+
+ AM_MAINTAINER_MODE
+
+@@ -725,6 +725,7 @@ AC_CONFIG_FILES([
+ man/zh_TW/Makefile
+ libmisc/Makefile
+ lib/Makefile
++ libsubid/Makefile
+ src/Makefile
+ contrib/Makefile
+ etc/Makefile
+diff -up shadow-4.6/libsubid/api.c.libsubid_creation shadow-4.6/libsubid/api.c
+--- shadow-4.6/libsubid/api.c.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/libsubid/api.c 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,231 @@
++/*
++ * Copyright (c) 2020 Serge Hallyn
++ * 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, this list of conditions and the following disclaimer.
++ * 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 copyright holders or contributors may not be used to
++ * endorse or promote products derived from this software without
++ * specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * ``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 COPYRIGHT
++ * HOLDERS OR CONTRIBUTORS 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.
++ */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "subordinateio.h"
++#include "idmapping.h"
++#include "api.h"
++
++static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type)
++{
++ struct subordinate_range **ranges = NULL;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_open(O_RDONLY)) {
++ return NULL;
++ }
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_open(O_RDONLY)) {
++ return NULL;
++ }
++ break;
++ default:
++ return NULL;
++ }
++
++ ranges = list_owner_ranges(owner, id_type);
++
++ if (id_type == ID_TYPE_UID)
++ sub_uid_close();
++ else
++ sub_gid_close();
++
++ return ranges;
++}
++
++struct subordinate_range **get_subuid_ranges(const char *owner)
++{
++ return get_subid_ranges(owner, ID_TYPE_UID);
++}
++
++struct subordinate_range **get_subgid_ranges(const char *owner)
++{
++ return get_subid_ranges(owner, ID_TYPE_GID);
++}
++
++void subid_free_ranges(struct subordinate_range **ranges)
++{
++ return free_subordinate_ranges(ranges);
++}
++
++int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type)
++{
++ int ret = -1;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_open(O_RDONLY)) {
++ return -1;
++ }
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_open(O_RDONLY)) {
++ return -1;
++ }
++ break;
++ default:
++ return -1;
++ }
++
++ ret = find_subid_owners(id, owner, id_type);
++
++ if (id_type == ID_TYPE_UID)
++ sub_uid_close();
++ else
++ sub_gid_close();
++
++ return ret;
++}
++
++int get_subuid_owners(uid_t uid, uid_t **owner)
++{
++ return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID);
++}
++
++int get_subgid_owners(gid_t gid, uid_t **owner)
++{
++ return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID);
++}
++
++bool grant_subid_range(struct subordinate_range *range, bool reuse,
++ enum subid_type id_type)
++{
++ bool ret;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_lock()) {
++ printf("Failed loging subuids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_uid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subuids (errno %d)\n", errno);
++ sub_uid_unlock();
++ return false;
++ }
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_lock()) {
++ printf("Failed loging subgids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_gid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subgids (errno %d)\n", errno);
++ sub_gid_unlock();
++ return false;
++ }
++ break;
++ default:
++ return false;
++ }
++
++ ret = new_subid_range(range, id_type, reuse);
++
++ if (id_type == ID_TYPE_UID) {
++ sub_uid_close();
++ sub_uid_unlock();
++ } else {
++ sub_gid_close();
++ sub_gid_unlock();
++ }
++
++ return ret;
++}
++
++bool grant_subuid_range(struct subordinate_range *range, bool reuse)
++{
++ return grant_subid_range(range, reuse, ID_TYPE_UID);
++}
++
++bool grant_subgid_range(struct subordinate_range *range, bool reuse)
++{
++ return grant_subid_range(range, reuse, ID_TYPE_GID);
++}
++
++bool free_subid_range(struct subordinate_range *range, enum subid_type id_type)
++{
++ bool ret;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_lock()) {
++ printf("Failed loging subuids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_uid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subuids (errno %d)\n", errno);
++ sub_uid_unlock();
++ return false;
++ }
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_lock()) {
++ printf("Failed loging subgids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_gid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subgids (errno %d)\n", errno);
++ sub_gid_unlock();
++ return false;
++ }
++ break;
++ default:
++ return false;
++ }
++
++ ret = release_subid_range(range, id_type);
++
++ if (id_type == ID_TYPE_UID) {
++ sub_uid_close();
++ sub_uid_unlock();
++ } else {
++ sub_gid_close();
++ sub_gid_unlock();
++ }
++
++ return ret;
++}
++
++bool free_subuid_range(struct subordinate_range *range)
++{
++ return free_subid_range(range, ID_TYPE_UID);
++}
++
++bool free_subgid_range(struct subordinate_range *range)
++{
++ return free_subid_range(range, ID_TYPE_GID);
++}
+diff -up shadow-4.6/libsubid/api.h.libsubid_creation shadow-4.6/libsubid/api.h
+--- shadow-4.6/libsubid/api.h.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/libsubid/api.h 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,17 @@
++#include "subid.h"
++#include
++
++struct subordinate_range **get_subuid_ranges(const char *owner);
++struct subordinate_range **get_subgid_ranges(const char *owner);
++void subid_free_ranges(struct subordinate_range **ranges);
++
++int get_subuid_owners(uid_t uid, uid_t **owner);
++int get_subgid_owners(gid_t gid, uid_t **owner);
++
++/* range should be pre-allocated with owner and count filled in, start is
++ * ignored, can be 0 */
++bool grant_subuid_range(struct subordinate_range *range, bool reuse);
++bool grant_subgid_range(struct subordinate_range *range, bool reuse);
++
++bool free_subuid_range(struct subordinate_range *range);
++bool free_subgid_range(struct subordinate_range *range);
+diff -up shadow-4.6/libsubid/Makefile.am.libsubid_creation shadow-4.6/libsubid/Makefile.am
+--- shadow-4.6/libsubid/Makefile.am.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/libsubid/Makefile.am 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,24 @@
++lib_LTLIBRARIES = libsubid.la
++libsubid_la_LDFLAGS = -Wl,-soname,libsubid.so.@LIBSUBID_ABI@ \
++ -shared -version-info @LIBSUBID_ABI_MAJOR@
++libsubid_la_SOURCES = api.c
++
++MISCLIBS = \
++ $(LIBAUDIT) \
++ $(LIBSELINUX) \
++ $(LIBSEMANAGE) \
++ $(LIBCRYPT_NOPAM) \
++ $(LIBSKEY) \
++ $(LIBMD) \
++ $(LIBCRYPT) \
++ $(LIBTCB)
++
++libsubid_la_LIBADD = \
++ $(top_srcdir)/lib/libshadow.la \
++ $(MISCLIBS) \
++ $(top_srcdir)/libmisc/libmisc.a
++
++AM_CPPFLAGS = \
++ -I${top_srcdir}/lib \
++ -I${top_srcdir}/libmisc \
++ -DLOCALEDIR=\"$(datadir)/locale\"
+diff -up shadow-4.6/libsubid/subid.h.libsubid_creation shadow-4.6/libsubid/subid.h
+--- shadow-4.6/libsubid/subid.h.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/libsubid/subid.h 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,17 @@
++#include
++
++#ifndef SUBID_RANGE_DEFINED
++#define SUBID_RANGE_DEFINED 1
++struct subordinate_range {
++ const char *owner;
++ unsigned long start;
++ unsigned long count;
++};
++
++enum subid_type {
++ ID_TYPE_UID = 1,
++ ID_TYPE_GID = 2
++};
++
++#define SUBID_NFIELDS 3
++#endif
+diff -up shadow-4.6/lib/subordinateio.c.libsubid_creation shadow-4.6/lib/subordinateio.c
+--- shadow-4.6/lib/subordinateio.c.libsubid_creation 2021-10-19 16:12:02.654748139 +0200
++++ shadow-4.6/lib/subordinateio.c 2021-10-19 16:12:02.661748243 +0200
+@@ -13,14 +13,7 @@
+ #include "subordinateio.h"
+ #include
+ #include
+-
+-struct subordinate_range {
+- const char *owner;
+- unsigned long start;
+- unsigned long count;
+-};
+-
+-#define NFIELDS 3
++#include
+
+ /*
+ * subordinate_dup: create a duplicate range
+@@ -78,7 +71,7 @@ static void *subordinate_parse (const ch
+ static char rangebuf[1024];
+ int i;
+ char *cp;
+- char *fields[NFIELDS];
++ char *fields[SUBID_NFIELDS];
+
+ /*
+ * Copy the string to a temporary buffer so the substrings can
+@@ -93,7 +86,7 @@ static void *subordinate_parse (const ch
+ * field. The fields are converted into NUL terminated strings.
+ */
+
+- for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
++ for (cp = rangebuf, i = 0; (i < SUBID_NFIELDS) && (NULL != cp); i++) {
+ fields[i] = cp;
+ while (('\0' != *cp) && (':' != *cp)) {
+ cp++;
+@@ -108,10 +101,10 @@ static void *subordinate_parse (const ch
+ }
+
+ /*
+- * There must be exactly NFIELDS colon separated fields or
++ * There must be exactly SUBID_NFIELDS colon separated fields or
+ * the entry is invalid. Also, fields must be non-blank.
+ */
+- if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
++ if (i != SUBID_NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
+ return NULL;
+ range.owner = fields[0];
+ if (getulong (fields[1], &range.start) == 0)
+@@ -319,6 +312,39 @@ static bool have_range(struct commonio_d
+ return false;
+ }
+
++static bool append_range(struct subordinate_range ***ranges, const struct subordinate_range *new, int n)
++{
++ struct subordinate_range *tmp;
++ if (!*ranges) {
++ *ranges = malloc(2 * sizeof(struct subordinate_range **));
++ if (!*ranges)
++ return false;
++ } else {
++ struct subordinate_range **new;
++ new = realloc(*ranges, (n + 2) * (sizeof(struct subordinate_range **)));
++ if (!new)
++ return false;
++ *ranges = new;
++ }
++ (*ranges)[n] = (*ranges)[n+1] = NULL;
++ tmp = subordinate_dup(new);
++ if (!tmp)
++ return false;
++ (*ranges)[n] = tmp;
++ return true;
++}
++
++void free_subordinate_ranges(struct subordinate_range **ranges)
++{
++ int i;
++
++ if (!ranges)
++ return;
++ for (i = 0; ranges[i]; i++)
++ subordinate_free(ranges[i]);
++ free(ranges);
++}
++
+ /*
+ * subordinate_range_cmp: compare uid ranges
+ *
+@@ -697,6 +723,160 @@ gid_t sub_gid_find_free_range(gid_t min,
+ start = find_free_range (&subordinate_gid_db, min, max, count);
+ return start == ULONG_MAX ? (gid_t) -1 : start;
+ }
++
++/*
++ struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
++ *
++ * @owner: username
++ * @id_type: UID or GUID
++ *
++ * Returns the subuid or subgid ranges which are owned by the specified
++ * user. Username may be a username or a string representation of a
++ * UID number. If id_type is UID, then subuids are returned, else
++ * subgids are returned. If there is an error, < 0 is returned.
++ *
++ * The caller must free the subordinate range list.
++ */
++struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
++{
++ // TODO - need to handle owner being either uid or username
++ const struct subordinate_range *range;
++ struct subordinate_range **ranges = NULL;
++ struct commonio_db *db;
++ int size = 0;
++
++ if (id_type == ID_TYPE_UID)
++ db = &subordinate_uid_db;
++ else
++ db = &subordinate_gid_db;
++
++ commonio_rewind(db);
++ while ((range = commonio_next(db)) != NULL) {
++ if (0 == strcmp(range->owner, owner)) {
++ if (!append_range(&ranges, range, size++)) {
++ free_subordinate_ranges(ranges);
++ return NULL;
++ }
++ }
++ }
++
++ return ranges;
++}
++
++static bool all_digits(const char *str)
++{
++ int i;
++
++ for (i = 0; str[i] != '\0'; i++)
++ if (!isdigit(str[i]))
++ return false;
++ return true;
++}
++
++static int append_uids(uid_t **uids, const char *owner, int n)
++{
++ uid_t owner_uid;
++ uid_t *ret;
++ int i;
++
++ if (all_digits(owner)) {
++ i = sscanf(owner, "%d", &owner_uid);
++ if (i != 1) {
++ // should not happen
++ free(*uids);
++ *uids = NULL;
++ return -1;
++ }
++ } else {
++ struct passwd *pwd = getpwnam(owner);
++ if (NULL == pwd) {
++ /* Username not defined in /etc/passwd, or error occured during lookup */
++ free(*uids);
++ *uids = NULL;
++ return -1;
++ }
++ owner_uid = pwd->pw_uid;
++ }
++
++ for (i = 0; i < n; i++) {
++ if (owner_uid == (*uids)[i])
++ return n;
++ }
++
++ ret = realloc(*uids, (n + 1) * sizeof(uid_t));
++ if (!ret) {
++ free(*uids);
++ return -1;
++ }
++ ret[n] = owner_uid;
++ *uids = ret;
++ return n+1;
++}
++
++int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type)
++{
++ const struct subordinate_range *range;
++ struct commonio_db *db;
++ int n = 0;
++
++ *uids = NULL;
++ if (id_type == ID_TYPE_UID)
++ db = &subordinate_uid_db;
++ else
++ db = &subordinate_gid_db;
++
++ commonio_rewind(db);
++ while ((range = commonio_next(db)) != NULL) {
++ if (id >= range->start && id < range->start + range-> count) {
++ n = append_uids(uids, range->owner, n);
++ if (n < 0)
++ break;
++ }
++ }
++
++ return n;
++}
++
++bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse)
++{
++ struct commonio_db *db;
++ const struct subordinate_range *r;
++
++ if (id_type == ID_TYPE_UID)
++ db = &subordinate_uid_db;
++ else
++ db = &subordinate_gid_db;
++ commonio_rewind(db);
++ if (reuse) {
++ while ((r = commonio_next(db)) != NULL) {
++ // TODO account for username vs uid_t
++ if (0 != strcmp(r->owner, range->owner))
++ continue;
++ if (r->count >= range->count) {
++ range->count = r->count;
++ range->start = r->start;
++ return true;
++ }
++ }
++ }
++
++ range->start = find_free_range(db, range->start, ULONG_MAX, range->count);
++ if (range->start == ULONG_MAX)
++ return false;
++
++ return add_range(db, range->owner, range->start, range->count) == 1;
++}
++
++bool release_subid_range(struct subordinate_range *range, enum subid_type id_type)
++{
++ struct commonio_db *db;
++ if (id_type == ID_TYPE_UID)
++ db = &subordinate_uid_db;
++ else
++ db = &subordinate_gid_db;
++ return remove_range(db, range->owner, range->start, range->count) == 1;
++}
++
+ #else /* !ENABLE_SUBIDS */
+ extern int errno; /* warning: ANSI C forbids an empty source file */
+ #endif /* !ENABLE_SUBIDS */
+diff -up shadow-4.6/lib/subordinateio.h.libsubid_creation shadow-4.6/lib/subordinateio.h
+--- shadow-4.6/lib/subordinateio.h.libsubid_creation 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/lib/subordinateio.h 2021-10-19 16:12:02.661748243 +0200
+@@ -11,6 +11,8 @@
+
+ #include
+
++#include "../libsubid/subid.h"
++
+ extern int sub_uid_close(void);
+ extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count);
+ extern bool sub_uid_file_present (void);
+@@ -23,6 +25,11 @@ extern int sub_uid_unlock (void);
+ extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
+ extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
+ extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
++extern struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type);
++extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse);
++extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type);
++extern int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type);
++extern void free_subordinate_ranges(struct subordinate_range **ranges);
+
+ extern int sub_gid_close(void);
+ extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
+diff -up shadow-4.6/Makefile.am.libsubid_creation shadow-4.6/Makefile.am
+--- shadow-4.6/Makefile.am.libsubid_creation 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/Makefile.am 2021-10-19 16:12:02.660748228 +0200
+@@ -2,5 +2,14 @@
+
+ EXTRA_DIST = NEWS README TODO shadow.spec.in
+
+-SUBDIRS = po man libmisc lib src \
+- contrib doc etc
++SUBDIRS = libmisc lib
++
++if ENABLE_SUBIDS
++SUBDIRS += libsubid
++endif
++
++SUBDIRS += src po contrib doc etc
++
++if ENABLE_REGENERATE_MAN
++SUBDIRS += man
++endif
+diff -up shadow-4.6/src/free_subid_range.c.libsubid_creation shadow-4.6/src/free_subid_range.c
+--- shadow-4.6/src/free_subid_range.c.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/src/free_subid_range.c 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,50 @@
++#include
++#include
++#include "api.h"
++#include "stdlib.h"
++#include "prototypes.h"
++
++/* Test program for the subid freeing routine */
++
++const char *Prog;
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: %s [-g] user start count\n", Prog);
++ fprintf(stderr, " Release a user's subuid (or with -g, subgid) range\n");
++ exit(EXIT_FAILURE);
++}
++
++int main(int argc, char *argv[])
++{
++ int c;
++ bool ok;
++ struct subordinate_range range;
++ bool group = false; // get subuids by default
++
++ Prog = Basename (argv[0]);
++ while ((c = getopt(argc, argv, "g")) != EOF) {
++ switch(c) {
++ case 'g': group = true; break;
++ default: usage();
++ }
++ }
++ argv = &argv[optind];
++ argc = argc - optind;
++ if (argc < 3)
++ usage();
++ range.owner = argv[0];
++ range.start = atoi(argv[1]);
++ range.count = atoi(argv[2]);
++ if (group)
++ ok = free_subgid_range(&range);
++ else
++ ok = free_subuid_range(&range);
++
++ if (!ok) {
++ fprintf(stderr, "Failed freeing id range\n");
++ exit(EXIT_FAILURE);
++ }
++
++ return 0;
++}
+diff -up shadow-4.6/src/get_subid_owners.c.libsubid_creation shadow-4.6/src/get_subid_owners.c
+--- shadow-4.6/src/get_subid_owners.c.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/src/get_subid_owners.c 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,40 @@
++#include
++#include "api.h"
++#include "stdlib.h"
++#include "prototypes.h"
++
++const char *Prog;
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: [-g] %s subuid\n", Prog);
++ fprintf(stderr, " list uids who own the given subuid\n");
++ fprintf(stderr, " pass -g to query a subgid\n");
++ exit(EXIT_FAILURE);
++}
++
++int main(int argc, char *argv[])
++{
++ int i, n;
++ uid_t *uids;
++
++ Prog = Basename (argv[0]);
++ if (argc < 2) {
++ usage();
++ }
++ if (argc == 3 && strcmp(argv[1], "-g") == 0)
++ n = get_subgid_owners(atoi(argv[2]), &uids);
++ else if (argc == 2 && strcmp(argv[1], "-h") == 0)
++ usage();
++ else
++ n = get_subuid_owners(atoi(argv[1]), &uids);
++ if (n < 0) {
++ fprintf(stderr, "No owners found\n");
++ exit(1);
++ }
++ for (i = 0; i < n; i++) {
++ printf("%d\n", uids[i]);
++ }
++ free(uids);
++ return 0;
++}
+diff -up shadow-4.6/src/list_subid_ranges.c.libsubid_creation shadow-4.6/src/list_subid_ranges.c
+--- shadow-4.6/src/list_subid_ranges.c.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/src/list_subid_ranges.c 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,41 @@
++#include
++#include "api.h"
++#include "stdlib.h"
++#include "prototypes.h"
++
++const char *Prog;
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: %s [-g] user\n", Prog);
++ fprintf(stderr, " list subuid ranges for user\n");
++ fprintf(stderr, " pass -g to list subgid ranges\n");
++ exit(EXIT_FAILURE);
++}
++
++int main(int argc, char *argv[])
++{
++ int i;
++ struct subordinate_range **ranges;
++
++ Prog = Basename (argv[0]);
++ if (argc < 2) {
++ usage();
++ }
++ if (argc == 3 && strcmp(argv[1], "-g") == 0)
++ ranges = get_subgid_ranges(argv[2]);
++ else if (argc == 2 && strcmp(argv[1], "-h") == 0)
++ usage();
++ else
++ ranges = get_subuid_ranges(argv[1]);
++ if (!ranges) {
++ fprintf(stderr, "Error fetching ranges\n");
++ exit(1);
++ }
++ for (i = 0; ranges[i]; i++) {
++ printf("%d: %s %lu %lu\n", i, ranges[i]->owner,
++ ranges[i]->start, ranges[i]->count);
++ }
++ subid_free_ranges(ranges);
++ return 0;
++}
+diff -up shadow-4.6/src/new_subid_range.c.libsubid_creation shadow-4.6/src/new_subid_range.c
+--- shadow-4.6/src/new_subid_range.c.libsubid_creation 2021-10-19 16:12:02.661748243 +0200
++++ shadow-4.6/src/new_subid_range.c 2021-10-19 16:12:02.661748243 +0200
+@@ -0,0 +1,57 @@
++#include
++#include
++#include "api.h"
++#include "stdlib.h"
++#include "prototypes.h"
++
++/* Test program for the subid creation routine */
++
++const char *Prog;
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: %s [-g] [-n] user count\n", Prog);
++ fprintf(stderr, " Find a subuid (or with -g, subgid) range for user\n");
++ fprintf(stderr, " If -n is given, a new range will be created even if one exists\n");
++ fprintf(stderr, " count defaults to 65536\n");
++ exit(EXIT_FAILURE);
++}
++
++int main(int argc, char *argv[])
++{
++ int c;
++ struct subordinate_range range;
++ bool makenew = false; // reuse existing by default
++ bool group = false; // get subuids by default
++ bool ok;
++
++ Prog = Basename (argv[0]);
++ while ((c = getopt(argc, argv, "gn")) != EOF) {
++ switch(c) {
++ case 'n': makenew = true; break;
++ case 'g': group = true; break;
++ default: usage();
++ }
++ }
++ argv = &argv[optind];
++ argc = argc - optind;
++ if (argc == 0)
++ usage();
++ range.owner = argv[0];
++ range.start = 0;
++ range.count = 65536;
++ if (argc > 1)
++ range.count = atoi(argv[1]);
++ if (group)
++ ok = grant_subgid_range(&range, !makenew);
++ else
++ ok = grant_subuid_range(&range, !makenew);
++
++ if (!ok) {
++ fprintf(stderr, "Failed creating new id range\n");
++ exit(EXIT_FAILURE);
++ }
++ printf("Subuid range %lu:%lu\n", range.start, range.count);
++
++ return 0;
++}
+diff -up shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subgid.libsubid_creation shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subgid
+--- shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subgid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subgid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,2 @@
++foo:200000:10000
++root:500000:1000
+diff -up shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subuid.libsubid_creation shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subuid
+--- shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subuid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/01_list_ranges/config/etc/subuid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,3 @@
++foo:300000:10000
++foo:400000:10000
++root:500000:1000
+diff -up shadow-4.6/tests/libsubid/01_list_ranges/config.txt.libsubid_creation shadow-4.6/tests/libsubid/01_list_ranges/config.txt
+diff -up shadow-4.6/tests/libsubid/01_list_ranges/list_ranges.test.libsubid_creation shadow-4.6/tests/libsubid/01_list_ranges/list_ranges.test
+--- shadow-4.6/tests/libsubid/01_list_ranges/list_ranges.test.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/01_list_ranges/list_ranges.test 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,38 @@
++#!/bin/sh
++
++set -e
++
++cd $(dirname $0)
++
++. ../../common/config.sh
++. ../../common/log.sh
++
++log_start "$0" "list_ranges shows subid ranges"
++
++save_config
++
++# restore the files on exit
++trap 'log_status "$0" "FAILURE"; restore_config' 0
++
++change_config
++
++echo -n "list foo's ranges..."
++${build_path}/src/list_subid_ranges foo > /tmp/subuidlistout
++${build_path}/src/list_subid_ranges -g foo > /tmp/subgidlistout
++echo "OK"
++
++echo -n "Check the subuid ranges..."
++[ $(wc -l /tmp/subuidlistout | awk '{ print $1 }') -eq 2 ]
++grep "0: foo 300000 10000" /tmp/subuidlistout
++grep "1: foo 400000 10000" /tmp/subuidlistout
++echo "OK"
++
++echo -n "Check the subgid ranges..."
++[ $(wc -l /tmp/subgidlistout | awk '{ print $1 }') -eq 1 ]
++grep "0: foo 200000 10000" /tmp/subgidlistout
++echo "OK"
++
++log_status "$0" "SUCCESS"
++restore_config
++trap '' 0
++
+diff -up shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/passwd.libsubid_creation shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/passwd
+--- shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/passwd.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/passwd 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,20 @@
++root:x:0:0:root:/root:/bin/bash
++daemon:x:1:1:daemon:/usr/sbin:/bin/sh
++bin:x:2:2:bin:/bin:/bin/sh
++sys:x:3:3:sys:/dev:/bin/sh
++sync:x:4:65534:sync:/bin:/bin/sync
++games:x:5:60:games:/usr/games:/bin/sh
++man:x:6:12:man:/var/cache/man:/bin/sh
++lp:x:7:7:lp:/var/spool/lpd:/bin/sh
++mail:x:8:8:mail:/var/mail:/bin/sh
++news:x:9:9:news:/var/spool/news:/bin/sh
++uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
++proxy:x:13:13:proxy:/bin:/bin/sh
++www-data:x:33:33:www-data:/var/www:/bin/sh
++backup:x:34:34:backup:/var/backups:/bin/sh
++list:x:38:38:Mailing List Manager:/var/list:/bin/sh
++irc:x:39:39:ircd:/var/run/ircd:/bin/sh
++gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
++nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
++Debian-exim:x:102:102::/var/spool/exim4:/bin/false
++foo:x:1000:1000::/home/foo:/bin/false
+diff -up shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subgid.libsubid_creation shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subgid
+--- shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subgid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subgid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,2 @@
++foo:200000:10000
++root:500000:1000
+diff -up shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subuid.libsubid_creation shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subuid
+--- shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subuid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/02_get_subid_owners/config/etc/subuid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,4 @@
++foo:300000:10000
++foo:400000:10000
++foo:500000:10000
++root:500000:1000
+diff -up shadow-4.6/tests/libsubid/02_get_subid_owners/config.txt.libsubid_creation shadow-4.6/tests/libsubid/02_get_subid_owners/config.txt
+diff -up shadow-4.6/tests/libsubid/02_get_subid_owners/get_subid_owners.test.libsubid_creation shadow-4.6/tests/libsubid/02_get_subid_owners/get_subid_owners.test
+--- shadow-4.6/tests/libsubid/02_get_subid_owners/get_subid_owners.test.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/02_get_subid_owners/get_subid_owners.test 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,52 @@
++#!/bin/sh
++
++set -e
++
++cd $(dirname $0)
++
++. ../../common/config.sh
++. ../../common/log.sh
++
++log_start "$0" "get subid owners"
++
++save_config
++
++# restore the files on exit
++trap 'log_status "$0" "FAILURE"; restore_config' 0
++
++change_config
++
++echo -n "Noone owns 0 as a subid..."
++[ -z "$(${build_path}/src/get_subid_owners 0)" ]
++echo "OK"
++
++echo -n "foo owns subuid 300000..."
++[ "$(${build_path}/src/get_subid_owners 300000)" = "1000" ]
++echo "OK"
++
++echo -n "foo owns subgid 200000..."
++[ "$(${build_path}/src/get_subid_owners -g 200000)" = "1000" ]
++echo "OK"
++
++echo -n "Noone owns subuid 200000..."
++[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ]
++echo "OK"
++
++echo -n "Noone owns subgid 300000..."
++[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ]
++echo "OK"
++
++echo -n "Both foo and root own subuid 500000..."
++cat > /tmp/expected << EOF
++1000
++0
++EOF
++${build_path}/src/get_subid_owners 500000 > /tmp/actual
++diff /tmp/expected /tmp/actual
++
++echo "OK"
++
++log_status "$0" "SUCCESS"
++restore_config
++trap '' 0
++
+diff -up shadow-4.6/tests/libsubid/03_add_remove/add_remove_subids.test.libsubid_creation shadow-4.6/tests/libsubid/03_add_remove/add_remove_subids.test
+--- shadow-4.6/tests/libsubid/03_add_remove/add_remove_subids.test.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/03_add_remove/add_remove_subids.test 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,59 @@
++#!/bin/sh
++
++set -e
++
++cd $(dirname $0)
++
++. ../../common/config.sh
++. ../../common/log.sh
++
++log_start "$0" "add and remove subid ranges"
++
++save_config
++
++# restore the files on exit
++trap 'log_status "$0" "FAILURE"; restore_config' 0
++
++change_config
++
++echo -n "Existing ranges returned when possible..."
++res=$(${build_path}/src/new_subid_range foo 500)
++echo "debug"
++echo "res is $res"
++echo "wanted Subuid range 300000:10000"
++echo "end debug"
++[ "$res" = "Subuid range 300000:10000" ]
++[ $(grep -c foo /etc/subuid) -eq 1 ]
++echo "OK"
++
++echo -n "New range returned if requested..."
++res=$(${build_path}/src/new_subid_range foo 500 -n)
++[ "$res" = "Subuid range 310000:500" ]
++[ $(grep -c foo /etc/subuid) -eq 2 ]
++echo "OK"
++
++echo -n "Free works..."
++res=$(${build_path}/src/free_subid_range foo 310000 500)
++[ $(grep -c foo /etc/subuid) -eq 1 ]
++echo "OK"
++
++echo -n "Subgids work too..."
++res=$(${build_path}/src/new_subid_range -g foo 100000)
++echo "DEBUG: res is ${res}"
++[ "$res" = "Subuid range 501000:100000" ]
++echo "DEBUG: subgid is:"
++cat /etc/subgid
++[ $(grep -c foo /etc/subgid) -eq 2 ]
++
++echo -n "Subgid free works..."
++res=$(${build_path}/src/free_subid_range -g foo 501000 100000)
++echo "DEBUG: res is ${res}"
++echo "DEBUG: subgid is:"
++cat /etc/subgid
++[ $(grep -c foo /etc/subgid) -eq 1 ]
++echo "OK"
++
++log_status "$0" "SUCCESS"
++restore_config
++trap '' 0
++
+diff -up shadow-4.6/tests/libsubid/03_add_remove/config/etc/passwd.libsubid_creation shadow-4.6/tests/libsubid/03_add_remove/config/etc/passwd
+--- shadow-4.6/tests/libsubid/03_add_remove/config/etc/passwd.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/03_add_remove/config/etc/passwd 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,20 @@
++root:x:0:0:root:/root:/bin/bash
++daemon:x:1:1:daemon:/usr/sbin:/bin/sh
++bin:x:2:2:bin:/bin:/bin/sh
++sys:x:3:3:sys:/dev:/bin/sh
++sync:x:4:65534:sync:/bin:/bin/sync
++games:x:5:60:games:/usr/games:/bin/sh
++man:x:6:12:man:/var/cache/man:/bin/sh
++lp:x:7:7:lp:/var/spool/lpd:/bin/sh
++mail:x:8:8:mail:/var/mail:/bin/sh
++news:x:9:9:news:/var/spool/news:/bin/sh
++uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
++proxy:x:13:13:proxy:/bin:/bin/sh
++www-data:x:33:33:www-data:/var/www:/bin/sh
++backup:x:34:34:backup:/var/backups:/bin/sh
++list:x:38:38:Mailing List Manager:/var/list:/bin/sh
++irc:x:39:39:ircd:/var/run/ircd:/bin/sh
++gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
++nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
++Debian-exim:x:102:102::/var/spool/exim4:/bin/false
++foo:x:1000:1000::/home/foo:/bin/false
+diff -up shadow-4.6/tests/libsubid/03_add_remove/config/etc/subgid.libsubid_creation shadow-4.6/tests/libsubid/03_add_remove/config/etc/subgid
+--- shadow-4.6/tests/libsubid/03_add_remove/config/etc/subgid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/03_add_remove/config/etc/subgid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,2 @@
++foo:200000:10000
++root:500000:1000
+diff -up shadow-4.6/tests/libsubid/03_add_remove/config/etc/subuid.libsubid_creation shadow-4.6/tests/libsubid/03_add_remove/config/etc/subuid
+--- shadow-4.6/tests/libsubid/03_add_remove/config/etc/subuid.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/libsubid/03_add_remove/config/etc/subuid 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1 @@
++foo:300000:10000
+diff -up shadow-4.6/tests/libsubid/03_add_remove/config.txt.libsubid_creation shadow-4.6/tests/libsubid/03_add_remove/config.txt
+diff -up shadow-4.6/tests/run_some.libsubid_creation shadow-4.6/tests/run_some
+--- shadow-4.6/tests/run_some.libsubid_creation 2021-10-19 16:12:02.662748257 +0200
++++ shadow-4.6/tests/run_some 2021-10-19 16:12:02.662748257 +0200
+@@ -0,0 +1,136 @@
++#!/bin/sh
++
++set -e
++
++export LC_ALL=C
++unset LANG
++unset LANGUAGE
++. common/config.sh
++
++USE_PAM="yes"
++FAILURE_TESTS="yes"
++
++succeeded=0
++failed=0
++failed_tests=""
++
++run_test()
++{
++ [ -f RUN_TEST.STOP ] && exit 1
++
++ if $1 > $1.log
++ then
++ succeeded=$((succeeded+1))
++ echo -n "+"
++ else
++ failed=$((failed+1))
++ failed_tests="$failed_tests $1"
++ echo -n "-"
++ fi
++ cat $1.log >> testsuite.log
++ [ -f /etc/passwd.lock ] && echo $1 /etc/passwd.lock || true
++ [ -f /etc/group.lock ] && echo $1 /etc/group.lock || true
++ [ -f /etc/shadow.lock ] && echo $1 /etc/shadow.lock || true
++ [ -f /etc/gshadow.lock ] && echo $1 /etc/gshadow.lock || true
++ if [ "$(stat -c"%G" /etc/shadow)" != "shadow" ]
++ then
++ echo $1
++ ls -l /etc/shadow
++ chgrp shadow /etc/shadow
++ fi
++ if [ -d /nonexistent ]
++ then
++ echo $1 /nonexistent
++ rmdir /nonexistent
++ fi
++}
++
++echo "+: test passed"
++echo "-: test failed"
++
++# Empty the complete log.
++> testsuite.log
++
++find ${build_path} -name "*.gcda" -delete
++run_test ./su/01/su_root.test
++run_test ./su/01/su_user.test
++find ${build_path} -name "*.gcda" -exec chmod a+rw {} \;
++run_test ./su/02/env_FOO-options_--login
++run_test ./su/02/env_FOO-options_--login_bash
++run_test ./su/02/env_FOO-options_--preserve-environment
++run_test ./su/02/env_FOO-options_--preserve-environment_bash
++run_test ./su/02/env_FOO-options_-
++run_test ./su/02/env_FOO-options_-_bash
++run_test ./su/02/env_FOO-options_-l-m
++run_test ./su/02/env_FOO-options_-l-m_bash
++run_test ./su/02/env_FOO-options_-l
++run_test ./su/02/env_FOO-options_-l_bash
++run_test ./su/02/env_FOO-options_-m_bash
++run_test ./su/02/env_FOO-options_-m
++run_test ./su/02/env_FOO-options_-p
++run_test ./su/02/env_FOO-options_-p_bash
++run_test ./su/02/env_FOO-options__bash
++run_test ./su/02/env_FOO-options_
++run_test ./su/02/env_FOO-options_-p-
++run_test ./su/02/env_FOO-options_-p-_bash
++run_test ./su/02/env_special-options_-l-p
++run_test ./su/02/env_special-options_-l
++run_test ./su/02/env_special-options_-l-p_bash
++run_test ./su/02/env_special-options_-l_bash
++run_test ./su/02/env_special-options_-p
++run_test ./su/02/env_special-options_-p_bash
++run_test ./su/02/env_special-options_
++run_test ./su/02/env_special-options__bash
++run_test ./su/02/env_special_root-options_-l-p
++run_test ./su/02/env_special_root-options_-l-p_bash
++run_test ./su/02/env_special_root-options_-l
++run_test ./su/02/env_special_root-options_-l_bash
++run_test ./su/02/env_special_root-options_-p
++run_test ./su/02/env_special_root-options_-p_bash
++run_test ./su/02/env_special_root-options_
++run_test ./su/02/env_special_root-options__bash
++run_test ./su/03/su_run_command01.test
++run_test ./su/03/su_run_command02.test
++run_test ./su/03/su_run_command03.test
++run_test ./su/03/su_run_command04.test
++run_test ./su/03/su_run_command05.test
++run_test ./su/03/su_run_command06.test
++run_test ./su/03/su_run_command07.test
++run_test ./su/03/su_run_command08.test
++run_test ./su/03/su_run_command09.test
++run_test ./su/03/su_run_command10.test
++run_test ./su/03/su_run_command11.test
++run_test ./su/03/su_run_command12.test
++run_test ./su/03/su_run_command13.test
++run_test ./su/03/su_run_command14.test
++run_test ./su/03/su_run_command15.test
++run_test ./su/03/su_run_command16.test
++run_test ./su/03/su_run_command17.test
++run_test ./su/04/su_wrong_user.test
++run_test ./su/04/su_user_wrong_passwd.test
++run_test ./su/04/su_user_wrong_passwd_syslog.test
++run_test ./su/05/su_user_wrong_passwd_syslog.test
++run_test ./su/06/su_user_syslog.test
++run_test ./su/07/su_user_syslog.test
++run_test ./su/08/env_special-options_
++run_test ./su/08/env_special_root-options_
++run_test ./su/09/env_special-options_
++run_test ./su/09/env_special_root-options_
++run_test ./su/10_su_sulog_success/su.test
++run_test ./su/11_su_sulog_failure/su.test
++run_test ./su/12_su_child_failure/su.test
++run_test ./su/13_su_child_success/su.test
++run_test ./libsubid/01_list_ranges/list_ranges.test
++run_test ./libsubid/02_get_subid_owners/get_subid_owners.test
++run_test ./libsubid/03_add_remove/add_remove_subids.test
++
++echo
++echo "$succeeded test(s) passed"
++echo "$failed test(s) failed"
++echo "log written in 'testsuite.log'"
++if [ "$failed" != "0" ]
++then
++ echo "the following tests failed:"
++ echo $failed_tests
++fi
++
+diff -up shadow-4.6/src/Makefile.am.libsubid_creation shadow-4.6/src/Makefile.am
+--- shadow-4.6/src/Makefile.am.bp 2021-10-19 13:13:14.132503541 +0200
++++ shadow-4.6/src/Makefile.am 2021-10-19 13:14:40.055871030 +0200
+@@ -138,3 +138,64 @@
+ chmod $(sgidperms) $(DESTDIR)$(ubindir)/$$i; \
+ done
+ endif
++
++if ENABLE_SUBIDS
++noinst_PROGRAMS += list_subid_ranges \
++ get_subid_owners \
++ new_subid_range \
++ free_subid_range
++
++MISCLIBS = \
++ $(LIBAUDIT) \
++ $(LIBSELINUX) \
++ $(LIBSEMANAGE) \
++ $(LIBCRYPT_NOPAM) \
++ $(LIBSKEY) \
++ $(LIBMD) \
++ $(LIBCRYPT) \
++ $(LIBTCB)
++
++list_subid_ranges_LDADD = \
++ $(top_builddir)/lib/libshadow.la \
++ $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libsubid/libsubid.la \
++ $(MISCLIBS)
++
++list_subid_ranges_CPPFLAGS = \
++ -I$(top_srcdir)/lib \
++ -I$(top_srcdir)/libmisc \
++ -I$(top_srcdir)/libsubid
++
++get_subid_owners_LDADD = \
++ $(top_builddir)/lib/libshadow.la \
++ $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libsubid/libsubid.la \
++ $(MISCLIBS)
++
++get_subid_owners_CPPFLAGS = \
++ -I$(top_srcdir)/lib \
++ -I$(top_srcdir)/libmisc \
++ -I$(top_srcdir)/libsubid
++
++new_subid_range_CPPFLAGS = \
++ -I$(top_srcdir)/lib \
++ -I$(top_srcdir)/libmisc \
++ -I$(top_srcdir)/libsubid
++
++new_subid_range_LDADD = \
++ $(top_builddir)/lib/libshadow.la \
++ $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libsubid/libsubid.la \
++ $(MISCLIBS)
++
++free_subid_range_CPPFLAGS = \
++ -I$(top_srcdir)/lib \
++ -I$(top_srcdir)/libmisc \
++ -I$(top_srcdir)/libsubid
++
++free_subid_range_LDADD = \
++ $(top_builddir)/lib/libshadow.la \
++ $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libsubid/libsubid.la \
++ $(MISCLIBS)
++endif
diff --git a/SOURCES/shadow-4.6-libsubid_fix_newusers_nss_provides_subids.patch b/SOURCES/shadow-4.6-libsubid_fix_newusers_nss_provides_subids.patch
new file mode 100644
index 0000000..c0ca905
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_fix_newusers_nss_provides_subids.patch
@@ -0,0 +1,151 @@
+diff -up shadow-4.8.1/lib/nss.c.libsubid_fix_newusers_nss_provides_subids shadow-4.8.1/lib/nss.c
+--- shadow-4.8.1/lib/nss.c.libsubid_fix_newusers_nss_provides_subids 2021-05-25 09:37:14.772741048 +0200
++++ shadow-4.8.1/lib/nss.c 2021-05-25 09:37:14.782741188 +0200
+@@ -116,14 +116,6 @@ void nss_init(char *nsswitch_path) {
+ subid_nss = NULL;
+ goto done;
+ }
+- subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range");
+- if (!subid_nss->has_any_range) {
+- fprintf(shadow_logfd, "%s did not provide @has_any_range@\n", libname);
+- dlclose(h);
+- free(subid_nss);
+- subid_nss = NULL;
+- goto done;
+- }
+ subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
+ if (!subid_nss->find_subid_owners) {
+ fprintf(shadow_logfd, "%s did not provide @find_subid_owners@\n", libname);
+diff -up shadow-4.8.1/lib/prototypes.h.libsubid_fix_newusers_nss_provides_subids shadow-4.8.1/lib/prototypes.h
+--- shadow-4.8.1/lib/prototypes.h.libsubid_fix_newusers_nss_provides_subids 2021-05-25 09:37:14.780741160 +0200
++++ shadow-4.8.1/lib/prototypes.h 2021-05-25 09:37:14.782741188 +0200
+@@ -279,18 +279,6 @@ extern bool nss_is_initialized();
+
+ struct subid_nss_ops {
+ /*
+- * nss_has_any_range: does a user own any subid range
+- *
+- * @owner: username
+- * @idtype: subuid or subgid
+- * @result: true if a subid allocation was found for @owner
+- *
+- * returns success if the module was able to determine an answer (true or false),
+- * else an error status.
+- */
+- enum subid_status (*has_any_range)(const char *owner, enum subid_type idtype, bool *result);
+-
+- /*
+ * nss_has_range: does a user own a given subid range
+ *
+ * @owner: username
+diff -up shadow-4.8.1/lib/subordinateio.c.libsubid_fix_newusers_nss_provides_subids shadow-4.8.1/lib/subordinateio.c
+--- shadow-4.8.1/lib/subordinateio.c.libsubid_fix_newusers_nss_provides_subids 2021-05-25 09:37:14.780741160 +0200
++++ shadow-4.8.1/lib/subordinateio.c 2021-05-25 09:37:14.782741188 +0200
+@@ -598,19 +598,8 @@ int sub_uid_open (int mode)
+ return commonio_open (&subordinate_uid_db, mode);
+ }
+
+-bool sub_uid_assigned(const char *owner)
++bool local_sub_uid_assigned(const char *owner)
+ {
+- struct subid_nss_ops *h;
+- bool found;
+- enum subid_status status;
+- h = get_subid_nss_handle();
+- if (h) {
+- status = h->has_any_range(owner, ID_TYPE_UID, &found);
+- if (status == SUBID_STATUS_SUCCESS && found)
+- return true;
+- return false;
+- }
+-
+ return range_exists (&subordinate_uid_db, owner);
+ }
+
+@@ -720,18 +709,8 @@ bool have_sub_gids(const char *owner, gi
+ return have_range(&subordinate_gid_db, owner, start, count);
+ }
+
+-bool sub_gid_assigned(const char *owner)
++bool local_sub_gid_assigned(const char *owner)
+ {
+- struct subid_nss_ops *h;
+- bool found;
+- enum subid_status status;
+- h = get_subid_nss_handle();
+- if (h) {
+- status = h->has_any_range(owner, ID_TYPE_GID, &found);
+- if (status == SUBID_STATUS_SUCCESS && found)
+- return true;
+- return false;
+- }
+ return range_exists (&subordinate_gid_db, owner);
+ }
+
+diff -up shadow-4.8.1/lib/subordinateio.h.libsubid_fix_newusers_nss_provides_subids shadow-4.8.1/lib/subordinateio.h
+--- shadow-4.8.1/lib/subordinateio.h.libsubid_fix_newusers_nss_provides_subids 2021-05-25 09:37:14.780741160 +0200
++++ shadow-4.8.1/lib/subordinateio.h 2021-05-25 09:37:14.782741188 +0200
+@@ -16,7 +16,7 @@
+ extern int sub_uid_close(void);
+ extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count);
+ extern bool sub_uid_file_present (void);
+-extern bool sub_uid_assigned(const char *owner);
++extern bool local_sub_uid_assigned(const char *owner);
+ extern int sub_uid_lock (void);
+ extern int sub_uid_setdbname (const char *filename);
+ extern /*@observer@*/const char *sub_uid_dbname (void);
+@@ -34,7 +34,7 @@ extern void free_subordinate_ranges(stru
+ extern int sub_gid_close(void);
+ extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
+ extern bool sub_gid_file_present (void);
+-extern bool sub_gid_assigned(const char *owner);
++extern bool local_sub_gid_assigned(const char *owner);
+ extern int sub_gid_lock (void);
+ extern int sub_gid_setdbname (const char *filename);
+ extern /*@observer@*/const char *sub_gid_dbname (void);
+diff -up shadow-4.8.1/src/newusers.c.libsubid_fix_newusers_nss_provides_subids shadow-4.8.1/src/newusers.c
+--- shadow-4.8.1/src/newusers.c.libsubid_fix_newusers_nss_provides_subids 2021-05-25 09:37:14.776741104 +0200
++++ shadow-4.8.1/src/newusers.c 2021-05-25 09:37:25.955897160 +0200
+@@ -1021,6 +1021,24 @@ static void close_files (void)
+ #endif /* ENABLE_SUBIDS */
+ }
+
++static bool want_subuids(void)
++{
++ if (get_subid_nss_handle() != NULL)
++ return false;
++ if (getdef_ulong ("SUB_UID_COUNT", 65536) == 0)
++ return false;
++ return true;
++}
++
++static bool want_subgids(void)
++{
++ if (get_subid_nss_handle() != NULL)
++ return false;
++ if (getdef_ulong ("SUB_GID_COUNT", 65536) == 0)
++ return false;
++ return true;
++}
++
+ int main (int argc, char **argv)
+ {
+ char buf[BUFSIZ];
+@@ -1250,7 +1268,7 @@ int main (int argc, char **argv)
+ /*
+ * Add subordinate uids if the user does not have them.
+ */
+- if (is_sub_uid && !sub_uid_assigned(fields[0])) {
++ if (is_sub_uid && want_subuids() && !local_sub_uid_assigned(fields[0])) {
+ uid_t sub_uid_start = 0;
+ unsigned long sub_uid_count = 0;
+ if (find_new_sub_uids(fields[0], &sub_uid_start, &sub_uid_count) == 0) {
+@@ -1270,7 +1288,7 @@ int main (int argc, char **argv)
+ /*
+ * Add subordinate gids if the user does not have them.
+ */
+- if (is_sub_gid && !sub_gid_assigned(fields[0])) {
++ if (is_sub_gid && want_subgids() && !local_sub_gid_assigned(fields[0])) {
+ gid_t sub_gid_start = 0;
+ unsigned long sub_gid_count = 0;
+ if (find_new_sub_gids(fields[0], &sub_gid_start, &sub_gid_count) == 0) {
diff --git a/SOURCES/shadow-4.6-libsubid_init_not_print_error_messages.patch b/SOURCES/shadow-4.6-libsubid_init_not_print_error_messages.patch
new file mode 100644
index 0000000..820a043
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_init_not_print_error_messages.patch
@@ -0,0 +1,40 @@
+From b0e86b959fe5c086ffb5e7eaf3c1b1e9219411e9 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Sun, 23 May 2021 08:03:10 -0500
+Subject: [PATCH] libsubid_init: don't print messages on error
+
+Signed-off-by: Serge Hallyn
+---
+ libsubid/api.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/libsubid/api.c b/libsubid/api.c
+index c4848142..b477b271 100644
+--- a/libsubid/api.c
++++ b/libsubid/api.c
+@@ -46,12 +46,10 @@ bool libsubid_init(const char *progname, FILE * logfd)
+ {
+ if (progname) {
+ progname = strdup(progname);
+- if (progname) {
++ if (progname)
+ Prog = progname;
+- } else {
+- fprintf(stderr, "Out of memory");
++ else
+ return false;
+- }
+ }
+
+ if (logfd) {
+@@ -60,7 +58,6 @@ bool libsubid_init(const char *progname, FILE * logfd)
+ }
+ shadow_logfd = fopen("/dev/null", "w");
+ if (!shadow_logfd) {
+- fprintf(stderr, "ERROR opening /dev/null for error messages. Using stderr.");
+ shadow_logfd = stderr;
+ return false;
+ }
+--
+2.30.2
+
diff --git a/SOURCES/shadow-4.6-libsubid_init_return_false.patch b/SOURCES/shadow-4.6-libsubid_init_return_false.patch
new file mode 100644
index 0000000..4d02d0d
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_init_return_false.patch
@@ -0,0 +1,37 @@
+From e34f49c1966fcaa9390a544a0136ec189a3c870e Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Mon, 17 May 2021 08:48:03 -0500
+Subject: [PATCH] libsubid_init: return false if out of memory
+
+The rest of the run isn't likely to get much better, is it?
+
+Thanks to Alexey for pointing this out.
+
+Signed-off-by: Serge Hallyn
+Cc: Alexey Tikhonov
+---
+ libsubid/api.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/libsubid/api.c b/libsubid/api.c
+index 8ca09859..8618e500 100644
+--- a/libsubid/api.c
++++ b/libsubid/api.c
+@@ -46,10 +46,12 @@ bool libsubid_init(const char *progname, FILE * logfd)
+ {
+ if (progname) {
+ progname = strdup(progname);
+- if (progname)
++ if (progname) {
+ Prog = progname;
+- else
++ } else {
+ fprintf(stderr, "Out of memory");
++ return false;
++ }
+ }
+
+ if (logfd) {
+--
+2.30.2
+
diff --git a/SOURCES/shadow-4.6-libsubid_make_logfd_not_extern.patch b/SOURCES/shadow-4.6-libsubid_make_logfd_not_extern.patch
new file mode 100644
index 0000000..2994442
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_make_logfd_not_extern.patch
@@ -0,0 +1,41 @@
+From 1d767fb779d7b203ad609540d1dc605cf62d1050 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Fri, 28 May 2021 22:02:16 -0500
+Subject: [PATCH] libsubid/api.c: make shadow_logfd not extern
+
+Closes #346
+
+Also #include stdio.h
+
+Signed-off-by: Serge Hallyn
+---
+ libsubid/api.c | 2 +-
+ libsubid/subid.h | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/libsubid/api.c b/libsubid/api.c
+index b477b271..a7b904d0 100644
+--- a/libsubid/api.c
++++ b/libsubid/api.c
+@@ -40,7 +40,7 @@
+ #include "subid.h"
+
+ const char *Prog = "(libsubid)";
+-extern FILE * shadow_logfd;
++FILE *shadow_logfd;
+
+ bool libsubid_init(const char *progname, FILE * logfd)
+ {
+diff --git a/libsubid/subid.h b/libsubid/subid.h
+index 5fef2572..eabafe4d 100644
+--- a/libsubid/subid.h
++++ b/libsubid/subid.h
+@@ -1,4 +1,5 @@
+ #include
++#include
+ #include
+
+ #ifndef SUBID_RANGE_DEFINED
+--
+2.31.1
+
diff --git a/SOURCES/shadow-4.6-libsubid_not_print_error_messages.patch b/SOURCES/shadow-4.6-libsubid_not_print_error_messages.patch
new file mode 100644
index 0000000..2d4faa3
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_not_print_error_messages.patch
@@ -0,0 +1,2382 @@
+diff -up shadow-4.6/lib/commonio.c.libsubid_not_print_error_messages shadow-4.6/lib/commonio.c
+--- shadow-4.6/lib/commonio.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.668172672 +0100
++++ shadow-4.6/lib/commonio.c 2021-11-03 09:28:20.444277611 +0100
+@@ -147,7 +147,7 @@ static int do_lock_file (const char *fil
+ fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ if (-1 == fd) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: %s: %s\n",
+ Prog, file, strerror (errno));
+ }
+@@ -159,7 +159,7 @@ static int do_lock_file (const char *fil
+ len = (ssize_t) strlen (buf) + 1;
+ if (write (fd, buf, (size_t) len) != len) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: %s: %s\n",
+ Prog, file, strerror (errno));
+ }
+@@ -172,7 +172,7 @@ static int do_lock_file (const char *fil
+ if (link (file, lock) == 0) {
+ retval = check_link_count (file);
+ if ((0==retval) && log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: %s: lock file already used\n",
+ Prog, file);
+ }
+@@ -183,7 +183,7 @@ static int do_lock_file (const char *fil
+ fd = open (lock, O_RDWR);
+ if (-1 == fd) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+@@ -195,7 +195,7 @@ static int do_lock_file (const char *fil
+ close (fd);
+ if (len <= 0) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: existing lock file %s without a PID\n",
+ Prog, lock);
+ }
+@@ -206,7 +206,7 @@ static int do_lock_file (const char *fil
+ buf[len] = '\0';
+ if (get_pid (buf, &pid) == 0) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: existing lock file %s with an invalid PID '%s'\n",
+ Prog, lock, buf);
+ }
+@@ -216,7 +216,7 @@ static int do_lock_file (const char *fil
+ }
+ if (kill (pid, 0) == 0) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: lock %s already used by PID %lu\n",
+ Prog, lock, (unsigned long) pid);
+ }
+@@ -226,7 +226,7 @@ static int do_lock_file (const char *fil
+ }
+ if (unlink (lock) != 0) {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: cannot get lock %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+@@ -238,13 +238,13 @@ static int do_lock_file (const char *fil
+ if (link (file, lock) == 0) {
+ retval = check_link_count (file);
+ if ((0==retval) && log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: %s: lock file already used\n",
+ Prog, file);
+ }
+ } else {
+ if (log) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: cannot get lock %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+@@ -435,7 +435,7 @@ int commonio_lock (struct commonio_db *d
+ if (0 == lock_count) {
+ if (lckpwdf () == -1) {
+ if (geteuid () != 0) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ "%s: Permission denied.\n",
+ Prog);
+ }
+@@ -471,7 +471,7 @@ int commonio_lock (struct commonio_db *d
+ }
+ /* no unnecessary retries on "permission denied" errors */
+ if (geteuid () != 0) {
+- (void) fprintf (stderr, "%s: Permission denied.\n",
++ (void) fprintf (shadow_logfd, "%s: Permission denied.\n",
+ Prog);
+ return 0;
+ }
+@@ -1101,7 +1101,7 @@ int commonio_update (struct commonio_db
+ p = find_entry_by_name (db, db->ops->getname (eptr));
+ if (NULL != p) {
+ if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) {
+- fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
++ fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
+ db->ops->free (nentry);
+ return 0;
+ }
+@@ -1206,7 +1206,7 @@ int commonio_remove (struct commonio_db
+ return 0;
+ }
+ if (next_entry_by_name (db, p->next, name) != NULL) {
+- fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
++ fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
+ return 0;
+ }
+
+diff -up shadow-4.6/lib/encrypt.c.libsubid_not_print_error_messages shadow-4.6/lib/encrypt.c
+--- shadow-4.6/lib/encrypt.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/lib/encrypt.c 2021-11-03 09:24:05.681172775 +0100
+@@ -78,7 +78,7 @@
+ method = &nummethod[0];
+ }
+ }
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("crypt method not supported by libcrypt? (%s)\n"),
+ method);
+ exit (EXIT_FAILURE);
+diff -up shadow-4.6/lib/getdef.c.libsubid_not_print_error_messages shadow-4.6/lib/getdef.c
+--- shadow-4.6/lib/getdef.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.670172687 +0100
++++ shadow-4.6/lib/getdef.c 2021-11-03 09:24:05.681172775 +0100
+@@ -233,7 +233,7 @@ int getdef_num (const char *item, int df
+ if ( (getlong (d->value, &val) == 0)
+ || (val > INT_MAX)
+ || (val < INT_MIN)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+@@ -268,7 +268,7 @@ unsigned int getdef_unum (const char *it
+ if ( (getlong (d->value, &val) == 0)
+ || (val < 0)
+ || (val > INT_MAX)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+@@ -301,7 +301,7 @@ long getdef_long (const char *item, long
+ }
+
+ if (getlong (d->value, &val) == 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+@@ -334,7 +334,7 @@ unsigned long getdef_ulong (const char *
+
+ if (getulong (d->value, &val) == 0) {
+ /* FIXME: we should have a getulong */
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+@@ -372,7 +372,7 @@ int putdef_str (const char *name, const
+ cp = strdup (value);
+ if (NULL == cp) {
+ (void) fputs (_("Could not allocate space for config info.\n"),
+- stderr);
++ shadow_logfd);
+ SYSLOG ((LOG_ERR, "could not allocate space for config info"));
+ return -1;
+ }
+@@ -417,7 +417,7 @@ static /*@observer@*/ /*@null@*/struct i
+ goto out;
+ }
+ }
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("configuration error - unknown item '%s' (notify administrator)\n"),
+ name);
+ SYSLOG ((LOG_CRIT, "unknown configuration item `%s'", name));
+diff -up shadow-4.6/libmisc/addgrps.c.libsubid_not_print_error_messages shadow-4.6/libmisc/addgrps.c
+--- shadow-4.6/libmisc/addgrps.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/addgrps.c 2021-11-03 09:24:05.681172775 +0100
+@@ -93,7 +93,7 @@ int add_groups (const char *list)
+
+ grp = getgrnam (token); /* local, no need for xgetgrnam */
+ if (NULL == grp) {
+- fprintf (stderr, _("Warning: unknown group %s\n"),
++ fprintf (shadow_logfd, _("Warning: unknown group %s\n"),
+ token);
+ continue;
+ }
+@@ -105,7 +105,7 @@ int add_groups (const char *list)
+ }
+
+ if (ngroups >= sysconf (_SC_NGROUPS_MAX)) {
+- fputs (_("Warning: too many groups\n"), stderr);
++ fputs (_("Warning: too many groups\n"), shadow_logfd);
+ break;
+ }
+ tmp = (gid_t *) realloc (grouplist, (size_t)(ngroups + 1) * sizeof (GETGROUPS_T));
+diff -up shadow-4.6/libmisc/audit_help.c.libsubid_not_print_error_messages shadow-4.6/libmisc/audit_help.c
+--- shadow-4.6/libmisc/audit_help.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.649172520 +0100
++++ shadow-4.6/libmisc/audit_help.c 2021-11-03 09:24:05.681172775 +0100
+@@ -59,7 +59,7 @@ void audit_help_open (void)
+ return;
+ }
+ (void) fputs (_("Cannot open audit interface - aborting.\n"),
+- stderr);
++ shadow_logfd);
+ exit (EXIT_FAILURE);
+ }
+ }
+diff -up shadow-4.6/libmisc/chowntty.c.libsubid_not_print_error_messages shadow-4.6/libmisc/chowntty.c
+--- shadow-4.6/libmisc/chowntty.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/chowntty.c 2021-11-03 09:24:05.681172775 +0100
+@@ -75,7 +75,7 @@ void chown_tty (const struct passwd *inf
+ || (fchmod (STDIN_FILENO, (mode_t)getdef_num ("TTYPERM", 0600)) != 0)) {
+ int err = errno;
+
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Unable to change owner or mode of tty stdin: %s"),
+ strerror (err));
+ SYSLOG ((LOG_WARN,
+diff -up shadow-4.6/libmisc/cleanup_group.c.libsubid_not_print_error_messages shadow-4.6/libmisc/cleanup_group.c
+--- shadow-4.6/libmisc/cleanup_group.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.649172520 +0100
++++ shadow-4.6/libmisc/cleanup_group.c 2021-11-03 09:24:05.681172775 +0100
+@@ -203,7 +203,7 @@ void cleanup_report_del_group_gshadow (v
+ void cleanup_unlock_group (unused void *arg)
+ {
+ if (gr_unlock () == 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to unlock %s\n"),
+ Prog, gr_dbname ());
+ SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+@@ -223,7 +223,7 @@ void cleanup_unlock_group (unused void *
+ void cleanup_unlock_gshadow (unused void *arg)
+ {
+ if (sgr_unlock () == 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to unlock %s\n"),
+ Prog, sgr_dbname ());
+ SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+diff -up shadow-4.6/libmisc/cleanup_user.c.libsubid_not_print_error_messages shadow-4.6/libmisc/cleanup_user.c
+--- shadow-4.6/libmisc/cleanup_user.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.649172520 +0100
++++ shadow-4.6/libmisc/cleanup_user.c 2021-11-03 09:24:05.682172783 +0100
+@@ -120,7 +120,7 @@ void cleanup_report_add_user_shadow (voi
+ void cleanup_unlock_passwd (unused void *arg)
+ {
+ if (pw_unlock () == 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to unlock %s\n"),
+ Prog, pw_dbname ());
+ SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+@@ -139,7 +139,7 @@ void cleanup_unlock_passwd (unused void
+ void cleanup_unlock_shadow (unused void *arg)
+ {
+ if (spw_unlock () == 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to unlock %s\n"),
+ Prog, spw_dbname ());
+ SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+diff -up shadow-4.6/libmisc/copydir.c.libsubid_not_print_error_messages shadow-4.6/libmisc/copydir.c
+--- shadow-4.6/libmisc/copydir.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.642172465 +0100
++++ shadow-4.6/libmisc/copydir.c 2021-11-03 09:24:05.682172783 +0100
+@@ -125,11 +125,11 @@ static void error_acl (struct error_cont
+ }
+
+ va_start (ap, fmt);
+- (void) fprintf (stderr, _("%s: "), Prog);
+- if (vfprintf (stderr, fmt, ap) != 0) {
+- (void) fputs (_(": "), stderr);
++ (void) fprintf (shadow_logfd, _("%s: "), Prog);
++ if (vfprintf (shadow_logfd, fmt, ap) != 0) {
++ (void) fputs (_(": "), shadow_logfd);
+ }
+- (void) fprintf (stderr, "%s\n", strerror (errno));
++ (void) fprintf (shadow_logfd, "%s\n", strerror (errno));
+ va_end (ap);
+ }
+
+@@ -248,7 +248,7 @@ int copy_tree (const char *src_root, con
+ }
+
+ if (!S_ISDIR (sb.st_mode)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ "%s: %s is not a directory",
+ Prog, src_root);
+ return -1;
+diff -up shadow-4.6/libmisc/env.c.libsubid_not_print_error_messages shadow-4.6/libmisc/env.c
+--- shadow-4.6/libmisc/env.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/env.c 2021-11-03 09:24:05.682172783 +0100
+@@ -171,7 +171,7 @@ void addenv (const char *string, /*@null
+ }
+ newenvp = __newenvp;
+ } else {
+- (void) fputs (_("Environment overflow\n"), stderr);
++ (void) fputs (_("Environment overflow\n"), shadow_logfd);
+ newenvc--;
+ free (newenvp[newenvc]);
+ }
+diff -up shadow-4.6/libmisc/find_new_gid.c.libsubid_not_print_error_messages shadow-4.6/libmisc/find_new_gid.c
+--- shadow-4.6/libmisc/find_new_gid.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.665172648 +0100
++++ shadow-4.6/libmisc/find_new_gid.c 2021-11-03 09:24:05.682172783 +0100
+@@ -74,7 +74,7 @@ static int get_ranges (bool sys_group, g
+
+ /* Check that the ranges make sense */
+ if (*max_id < *min_id) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: SYS_GID_MIN (%lu), "
+ "GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
+ Prog, (unsigned long) *min_id,
+@@ -104,7 +104,7 @@ static int get_ranges (bool sys_group, g
+
+ /* Check that the ranges make sense */
+ if (*max_id < *min_id) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: GID_MIN (%lu), "
+ "GID_MAX (%lu)\n"),
+ Prog, (unsigned long) *min_id,
+@@ -220,7 +220,7 @@ int find_new_gid (bool sys_group,
+ * more likely to want to stop and address the
+ * issue.
+ */
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Encountered error attempting to use "
+ "preferred GID: %s\n"),
+ Prog, strerror (result));
+@@ -250,7 +250,7 @@ int find_new_gid (bool sys_group,
+ /* Create an array to hold all of the discovered GIDs */
+ used_gids = malloc (sizeof (bool) * (gid_max +1));
+ if (NULL == used_gids) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to allocate memory: %s\n"),
+ Prog, strerror (errno));
+ return -1;
+@@ -330,7 +330,7 @@ int find_new_gid (bool sys_group,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique system GID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -373,7 +373,7 @@ int find_new_gid (bool sys_group,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique system GID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -433,7 +433,7 @@ int find_new_gid (bool sys_group,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique GID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -476,7 +476,7 @@ int find_new_gid (bool sys_group,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique GID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -495,7 +495,7 @@ int find_new_gid (bool sys_group,
+ }
+
+ /* The code reached here and found no available IDs in the range */
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique GID (no more available GIDs)\n"),
+ Prog);
+ SYSLOG ((LOG_WARN, "no more available GIDs on the system"));
+diff -up shadow-4.6/libmisc/find_new_sub_gids.c.libsubid_not_print_error_messages shadow-4.6/libmisc/find_new_sub_gids.c
+--- shadow-4.6/libmisc/find_new_sub_gids.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/find_new_sub_gids.c 2021-11-03 09:24:05.682172783 +0100
+@@ -61,7 +61,7 @@ int find_new_sub_gids (const char *owner
+ count = getdef_ulong ("SUB_GID_COUNT", 65536);
+
+ if (min > max || count >= max || (min + count - 1) > max) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: SUB_GID_MIN (%lu),"
+ " SUB_GID_MAX (%lu), SUB_GID_COUNT (%lu)\n"),
+ Prog, min, max, count);
+@@ -70,7 +70,7 @@ int find_new_sub_gids (const char *owner
+
+ start = sub_gid_find_free_range(min, max, count);
+ if (start == (gid_t)-1) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique subordinate GID range\n"),
+ Prog);
+ SYSLOG ((LOG_WARN, "no more available subordinate GIDs on the system"));
+diff -up shadow-4.6/libmisc/find_new_sub_uids.c.libsubid_not_print_error_messages shadow-4.6/libmisc/find_new_sub_uids.c
+--- shadow-4.6/libmisc/find_new_sub_uids.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/find_new_sub_uids.c 2021-11-03 09:24:05.682172783 +0100
+@@ -61,7 +61,7 @@ int find_new_sub_uids (const char *owner
+ count = getdef_ulong ("SUB_UID_COUNT", 65536);
+
+ if (min > max || count >= max || (min + count - 1) > max) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: SUB_UID_MIN (%lu),"
+ " SUB_UID_MAX (%lu), SUB_UID_COUNT (%lu)\n"),
+ Prog, min, max, count);
+@@ -70,7 +70,7 @@ int find_new_sub_uids (const char *owner
+
+ start = sub_uid_find_free_range(min, max, count);
+ if (start == (uid_t)-1) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique subordinate UID range\n"),
+ Prog);
+ SYSLOG ((LOG_WARN, "no more available subordinate UIDs on the system"));
+diff -up shadow-4.6/libmisc/find_new_uid.c.libsubid_not_print_error_messages shadow-4.6/libmisc/find_new_uid.c
+--- shadow-4.6/libmisc/find_new_uid.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.665172648 +0100
++++ shadow-4.6/libmisc/find_new_uid.c 2021-11-03 09:24:05.682172783 +0100
+@@ -74,7 +74,7 @@ static int get_ranges (bool sys_user, ui
+
+ /* Check that the ranges make sense */
+ if (*max_id < *min_id) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: SYS_UID_MIN (%lu), "
+ "UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
+ Prog, (unsigned long) *min_id,
+@@ -104,7 +104,7 @@ static int get_ranges (bool sys_user, ui
+
+ /* Check that the ranges make sense */
+ if (*max_id < *min_id) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: Invalid configuration: UID_MIN (%lu), "
+ "UID_MAX (%lu)\n"),
+ Prog, (unsigned long) *min_id,
+@@ -220,7 +220,7 @@ int find_new_uid(bool sys_user,
+ * more likely to want to stop and address the
+ * issue.
+ */
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Encountered error attempting to use "
+ "preferred UID: %s\n"),
+ Prog, strerror (result));
+@@ -250,7 +250,7 @@ int find_new_uid(bool sys_user,
+ /* Create an array to hold all of the discovered UIDs */
+ used_uids = malloc (sizeof (bool) * (uid_max +1));
+ if (NULL == used_uids) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: failed to allocate memory: %s\n"),
+ Prog, strerror (errno));
+ return -1;
+@@ -330,7 +330,7 @@ int find_new_uid(bool sys_user,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique system UID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -373,7 +373,7 @@ int find_new_uid(bool sys_user,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique system UID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -433,7 +433,7 @@ int find_new_uid(bool sys_user,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique UID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -476,7 +476,7 @@ int find_new_uid(bool sys_user,
+ *
+ */
+ if (!nospam) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique UID (%s). "
+ "Suppressing additional messages.\n"),
+ Prog, strerror (result));
+@@ -495,7 +495,7 @@ int find_new_uid(bool sys_user,
+ }
+
+ /* The code reached here and found no available IDs in the range */
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Can't get unique UID (no more available UIDs)\n"),
+ Prog);
+ SYSLOG ((LOG_WARN, "no more available UIDs on the system"));
+diff -up shadow-4.6/libmisc/gettime.c.libsubid_not_print_error_messages shadow-4.6/libmisc/gettime.c
+--- shadow-4.6/libmisc/gettime.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/gettime.c 2021-11-03 09:24:05.682172783 +0100
+@@ -61,23 +61,23 @@
+ epoch = strtoull (source_date_epoch, &endptr, 10);
+ if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+ || (errno != 0 && epoch == 0)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"),
+ strerror(errno));
+ } else if (endptr == source_date_epoch) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"),
+ endptr);
+ } else if (*endptr != '\0') {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"),
+ endptr);
+ } else if (epoch > ULONG_MAX) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to %lu but was found to be: %llu\n"),
+ ULONG_MAX, epoch);
+ } else if (epoch > fallback) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to the current time (%lu) but was found to be: %llu\n"),
+ fallback, epoch);
+ } else {
+diff -up shadow-4.6/libmisc/idmapping.c.libsubid_not_print_error_messages shadow-4.6/libmisc/idmapping.c
+--- shadow-4.6/libmisc/idmapping.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/idmapping.c 2021-11-03 09:24:05.682172783 +0100
+@@ -43,19 +43,19 @@ struct map_range *get_map_ranges(int ran
+ int idx, argidx;
+
+ if (ranges < 0 || argc < 0) {
+- fprintf(stderr, "%s: error calculating number of arguments\n", Prog);
++ fprintf(shadow_logfd, "%s: error calculating number of arguments\n", Prog);
+ return NULL;
+ }
+
+ if (ranges != ((argc + 2) / 3)) {
+- fprintf(stderr, "%s: ranges: %u is wrong for argc: %d\n", Prog, ranges, argc);
++ fprintf(shadow_logfd, "%s: ranges: %u is wrong for argc: %d\n", Prog, ranges, argc);
+ return NULL;
+ }
+
+ if ((ranges * 3) > argc) {
+- fprintf(stderr, "ranges: %u argc: %d\n",
++ fprintf(shadow_logfd, "ranges: %u argc: %d\n",
+ ranges, argc);
+- fprintf(stderr,
++ fprintf(shadow_logfd,
+ _( "%s: Not enough arguments to form %u mappings\n"),
+ Prog, ranges);
+ return NULL;
+@@ -63,7 +63,7 @@ struct map_range *get_map_ranges(int ran
+
+ mappings = calloc(ranges, sizeof(*mappings));
+ if (!mappings) {
+- fprintf(stderr, _( "%s: Memory allocation failure\n"),
++ fprintf(shadow_logfd, _( "%s: Memory allocation failure\n"),
+ Prog);
+ exit(EXIT_FAILURE);
+ }
+@@ -84,24 +84,24 @@ struct map_range *get_map_ranges(int ran
+ return NULL;
+ }
+ if (ULONG_MAX - mapping->upper <= mapping->count || ULONG_MAX - mapping->lower <= mapping->count) {
+- fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog);
++ fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog);
+ exit(EXIT_FAILURE);
+ }
+ if (mapping->upper > UINT_MAX ||
+ mapping->lower > UINT_MAX ||
+ mapping->count > UINT_MAX) {
+- fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog);
++ fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog);
+ exit(EXIT_FAILURE);
+ }
+ if (mapping->lower + mapping->count > UINT_MAX ||
+ mapping->upper + mapping->count > UINT_MAX) {
+- fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog);
++ fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog);
+ exit(EXIT_FAILURE);
+ }
+ if (mapping->lower + mapping->count < mapping->lower ||
+ mapping->upper + mapping->count < mapping->upper) {
+ /* this one really shouldn't be possible given previous checks */
+- fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog);
++ fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog);
+ exit(EXIT_FAILURE);
+ }
+ }
+@@ -142,7 +142,7 @@ void write_mapping(int proc_dir_fd, int
+ mapping->lower,
+ mapping->count);
+ if ((written <= 0) || (written >= (bufsize - (pos - buf)))) {
+- fprintf(stderr, _("%s: snprintf failed!\n"), Prog);
++ fprintf(shadow_logfd, _("%s: snprintf failed!\n"), Prog);
+ exit(EXIT_FAILURE);
+ }
+ pos += written;
+@@ -151,12 +151,12 @@ void write_mapping(int proc_dir_fd, int
+ /* Write the mapping to the mapping file */
+ fd = openat(proc_dir_fd, map_file, O_WRONLY);
+ if (fd < 0) {
+- fprintf(stderr, _("%s: open of %s failed: %s\n"),
++ fprintf(shadow_logfd, _("%s: open of %s failed: %s\n"),
+ Prog, map_file, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (write(fd, buf, pos - buf) != (pos - buf)) {
+- fprintf(stderr, _("%s: write to %s failed: %s\n"),
++ fprintf(shadow_logfd, _("%s: write to %s failed: %s\n"),
+ Prog, map_file, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+diff -up shadow-4.6/libmisc/limits.c.libsubid_not_print_error_messages shadow-4.6/libmisc/limits.c
+--- shadow-4.6/libmisc/limits.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/limits.c 2021-11-03 09:24:05.682172783 +0100
+@@ -548,7 +548,7 @@ void setup_limits (const struct passwd *
+ #ifdef LIMITS
+ if (info->pw_uid != 0) {
+ if ((setup_user_limits (info->pw_name) & LOGIN_ERROR_LOGIN) != 0) {
+- (void) fputs (_("Too many logins.\n"), stderr);
++ (void) fputs (_("Too many logins.\n"), shadow_logfd);
+ (void) sleep (2); /* XXX: Should be FAIL_DELAY */
+ exit (EXIT_FAILURE);
+ }
+diff -up shadow-4.6/libmisc/pam_pass.c.libsubid_not_print_error_messages shadow-4.6/libmisc/pam_pass.c
+--- shadow-4.6/libmisc/pam_pass.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/pam_pass.c 2021-11-03 09:24:05.682172783 +0100
+@@ -59,20 +59,20 @@ void do_pam_passwd (const char *user, bo
+
+ ret = pam_start ("passwd", user, &conv, &pamh);
+ if (ret != PAM_SUCCESS) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("passwd: pam_start() failed, error %d\n"), ret);
+ exit (10); /* XXX */
+ }
+
+ ret = pam_chauthtok (pamh, flags);
+ if (ret != PAM_SUCCESS) {
+- fprintf (stderr, _("passwd: %s\n"), pam_strerror (pamh, ret));
+- fputs (_("passwd: password unchanged\n"), stderr);
++ fprintf (shadow_logfd, _("passwd: %s\n"), pam_strerror (pamh, ret));
++ fputs (_("passwd: password unchanged\n"), shadow_logfd);
+ pam_end (pamh, ret);
+ exit (10); /* XXX */
+ }
+
+- fputs (_("passwd: password updated successfully\n"), stderr);
++ fputs (_("passwd: password updated successfully\n"), shadow_logfd);
+ (void) pam_end (pamh, PAM_SUCCESS);
+ }
+ #else /* !USE_PAM */
+diff -up shadow-4.6/libmisc/pam_pass_non_interactive.c.libsubid_not_print_error_messages shadow-4.6/libmisc/pam_pass_non_interactive.c
+--- shadow-4.6/libmisc/pam_pass_non_interactive.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/pam_pass_non_interactive.c 2021-11-03 09:24:05.683172791 +0100
+@@ -76,7 +76,7 @@ static int ni_conv (int num_msg,
+
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: PAM modules requesting echoing are not supported.\n"),
+ Prog);
+ goto failed_conversation;
+@@ -88,7 +88,7 @@ static int ni_conv (int num_msg,
+ break;
+ case PAM_ERROR_MSG:
+ if ( (NULL == msg[count]->msg)
+- || (fprintf (stderr, "%s\n", msg[count]->msg) <0)) {
++ || (fprintf (shadow_logfd, "%s\n", msg[count]->msg) <0)) {
+ goto failed_conversation;
+ }
+ responses[count].resp = NULL;
+@@ -101,7 +101,7 @@ static int ni_conv (int num_msg,
+ responses[count].resp = NULL;
+ break;
+ default:
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: conversation type %d not supported.\n"),
+ Prog, msg[count]->msg_style);
+ goto failed_conversation;
+@@ -143,7 +143,7 @@ int do_pam_passwd_non_interactive (const
+
+ ret = pam_start (pam_service, username, &non_interactive_pam_conv, &pamh);
+ if (ret != PAM_SUCCESS) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: (user %s) pam_start failure %d\n"),
+ Prog, username, ret);
+ return 1;
+@@ -152,7 +152,7 @@ int do_pam_passwd_non_interactive (const
+ non_interactive_password = password;
+ ret = pam_chauthtok (pamh, 0);
+ if (ret != PAM_SUCCESS) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: (user %s) pam_chauthtok() failed, error:\n"
+ "%s\n"),
+ Prog, username, pam_strerror (pamh, ret));
+diff -up shadow-4.6/libmisc/prefix_flag.c.libsubid_not_print_error_messages shadow-4.6/libmisc/prefix_flag.c
+--- shadow-4.6/libmisc/prefix_flag.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.658172592 +0100
++++ shadow-4.6/libmisc/prefix_flag.c 2021-11-03 09:24:05.683172791 +0100
+@@ -80,14 +80,14 @@ extern const char* process_prefix_flag (
+ if ( (strcmp (argv[i], "--prefix") == 0)
+ || (strcmp (argv[i], short_opt) == 0)) {
+ if (NULL != prefix) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: multiple --prefix options\n"),
+ Prog);
+ exit (E_BAD_ARG);
+ }
+
+ if (i + 1 == argc) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: option '%s' requires an argument\n"),
+ Prog, argv[i]);
+ exit (E_BAD_ARG);
+diff -up shadow-4.6/libmisc/pwdcheck.c.libsubid_not_print_error_messages shadow-4.6/libmisc/pwdcheck.c
+--- shadow-4.6/libmisc/pwdcheck.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/pwdcheck.c 2021-11-03 09:24:05.683172791 +0100
+@@ -51,7 +51,7 @@ void passwd_check (const char *user, con
+ if (pw_auth (passwd, user, PW_LOGIN, (char *) 0) != 0) {
+ SYSLOG ((LOG_WARN, "incorrect password for `%s'", user));
+ (void) sleep (1);
+- fprintf (stderr, _("Incorrect password for %s.\n"), user);
++ fprintf (shadow_logfd, _("Incorrect password for %s.\n"), user);
+ exit (EXIT_FAILURE);
+ }
+ }
+diff -up shadow-4.6/libmisc/root_flag.c.libsubid_not_print_error_messages shadow-4.6/libmisc/root_flag.c
+--- shadow-4.6/libmisc/root_flag.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/root_flag.c 2021-11-03 09:24:05.683172791 +0100
+@@ -62,14 +62,14 @@ extern void process_root_flag (const cha
+ if ( (strcmp (argv[i], "--root") == 0)
+ || (strcmp (argv[i], short_opt) == 0)) {
+ if (NULL != newroot) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: multiple --root options\n"),
+ Prog);
+ exit (E_BAD_ARG);
+ }
+
+ if (i + 1 == argc) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: option '%s' requires an argument\n"),
+ Prog, argv[i]);
+ exit (E_BAD_ARG);
+@@ -88,34 +88,34 @@ static void change_root (const char* new
+ /* Drop privileges */
+ if ( (setregid (getgid (), getgid ()) != 0)
+ || (setreuid (getuid (), getuid ()) != 0)) {
+- fprintf (stderr, _("%s: failed to drop privileges (%s)\n"),
++ fprintf (shadow_logfd, _("%s: failed to drop privileges (%s)\n"),
+ Prog, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ if ('/' != newroot[0]) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: invalid chroot path '%s'\n"),
+ Prog, newroot);
+ exit (E_BAD_ARG);
+ }
+
+ if (access (newroot, F_OK) != 0) {
+- fprintf(stderr,
++ fprintf(shadow_logfd,
+ _("%s: cannot access chroot directory %s: %s\n"),
+ Prog, newroot, strerror (errno));
+ exit (E_BAD_ARG);
+ }
+
+ if (chdir (newroot) != 0) {
+- fprintf(stderr,
++ fprintf(shadow_logfd,
+ _("%s: cannot chdir to chroot directory %s: %s\n"),
+ Prog, newroot, strerror (errno));
+ exit (E_BAD_ARG);
+ }
+
+ if (chroot (newroot) != 0) {
+- fprintf(stderr,
++ fprintf(shadow_logfd,
+ _("%s: unable to chroot to directory %s: %s\n"),
+ Prog, newroot, strerror (errno));
+ exit (E_BAD_ARG);
+diff -up shadow-4.6/libmisc/salt.c.libsubid_not_print_error_messages shadow-4.6/libmisc/salt.c
+--- shadow-4.6/libmisc/salt.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/salt.c 2021-11-03 09:24:05.683172791 +0100
+@@ -241,7 +241,7 @@ static /*@observer@*/const char *gensalt
+ salt_len = (size_t) shadow_random (8, 16);
+ #endif /* USE_SHA_CRYPT */
+ } else if (0 != strcmp (method, "DES")) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Invalid ENCRYPT_METHOD value: '%s'.\n"
+ "Defaulting to DES.\n"),
+ method);
+diff -up shadow-4.6/libmisc/setupenv.c.libsubid_not_print_error_messages shadow-4.6/libmisc/setupenv.c
+--- shadow-4.6/libmisc/setupenv.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/setupenv.c 2021-11-03 09:24:05.683172791 +0100
+@@ -219,7 +219,7 @@ void setup_env (struct passwd *info)
+ static char temp_pw_dir[] = "/";
+
+ if (!getdef_bool ("DEFAULT_HOME") || chdir ("/") == -1) {
+- fprintf (stderr, _("Unable to cd to '%s'\n"),
++ fprintf (shadow_logfd, _("Unable to cd to '%s'\n"),
+ info->pw_dir);
+ SYSLOG ((LOG_WARN,
+ "unable to cd to `%s' for user `%s'\n",
+diff -up shadow-4.6/libmisc/user_busy.c.libsubid_not_print_error_messages shadow-4.6/libmisc/user_busy.c
+--- shadow-4.6/libmisc/user_busy.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.669172680 +0100
++++ shadow-4.6/libmisc/user_busy.c 2021-11-03 09:24:05.683172791 +0100
+@@ -96,7 +96,7 @@ static int user_busy_utmp (const char *n
+ continue;
+ }
+
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: user %s is currently logged in\n"),
+ Prog, name);
+ return 1;
+@@ -249,7 +249,7 @@ static int user_busy_processes (const ch
+ #ifdef ENABLE_SUBIDS
+ sub_uid_close();
+ #endif
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: user %s is currently used by process %d\n"),
+ Prog, name, pid);
+ return 1;
+@@ -272,7 +272,7 @@ static int user_busy_processes (const ch
+ #ifdef ENABLE_SUBIDS
+ sub_uid_close();
+ #endif
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: user %s is currently used by process %d\n"),
+ Prog, name, pid);
+ return 1;
+diff -up shadow-4.6/libmisc/xgetXXbyYY.c.libsubid_not_print_error_messages shadow-4.6/libmisc/xgetXXbyYY.c
+--- shadow-4.6/libmisc/xgetXXbyYY.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/xgetXXbyYY.c 2021-11-03 09:24:05.683172791 +0100
+@@ -74,7 +74,7 @@
+
+ result = malloc(sizeof(LOOKUP_TYPE));
+ if (NULL == result) {
+- fprintf (stderr, _("%s: out of memory\n"),
++ fprintf (shadow_logfd, _("%s: out of memory\n"),
+ "x" STRINGIZE(FUNCTION_NAME));
+ exit (13);
+ }
+@@ -84,7 +84,7 @@
+ LOOKUP_TYPE *resbuf = NULL;
+ buffer = (char *)realloc (buffer, length);
+ if (NULL == buffer) {
+- fprintf (stderr, _("%s: out of memory\n"),
++ fprintf (shadow_logfd, _("%s: out of memory\n"),
+ "x" STRINGIZE(FUNCTION_NAME));
+ exit (13);
+ }
+@@ -132,7 +132,7 @@
+ if (result) {
+ result = DUP_FUNCTION(result);
+ if (NULL == result) {
+- fprintf (stderr, _("%s: out of memory\n"),
++ fprintf (shadow_logfd, _("%s: out of memory\n"),
+ "x" STRINGIZE(FUNCTION_NAME));
+ exit (13);
+ }
+diff -up shadow-4.6/libmisc/xmalloc.c.libsubid_not_print_error_messages shadow-4.6/libmisc/xmalloc.c
+--- shadow-4.6/libmisc/xmalloc.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/libmisc/xmalloc.c 2021-11-03 09:24:05.683172791 +0100
+@@ -54,7 +54,7 @@
+
+ ptr = (char *) malloc (size);
+ if (NULL == ptr) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: failed to allocate memory: %s\n"),
+ Prog, strerror (errno));
+ exit (13);
+diff -up shadow-4.6/lib/nscd.c.libsubid_not_print_error_messages shadow-4.6/lib/nscd.c
+--- shadow-4.6/lib/nscd.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/lib/nscd.c 2021-11-03 09:24:05.683172791 +0100
+@@ -25,13 +25,13 @@ int nscd_flush_cache (const char *servic
+
+ if (run_command (cmd, spawnedArgs, spawnedEnv, &status) != 0) {
+ /* run_command writes its own more detailed message. */
+- (void) fprintf (stderr, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
++ (void) fprintf (shadow_logfd, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
+ return -1;
+ }
+
+ code = WEXITSTATUS (status);
+ if (!WIFEXITED (status)) {
+- (void) fprintf (stderr,
++ (void) fprintf (shadow_logfd,
+ _("%s: nscd did not terminate normally (signal %d)\n"),
+ Prog, WTERMSIG (status));
+ return -1;
+@@ -43,9 +43,9 @@ int nscd_flush_cache (const char *servic
+ /* nscd is installed, but it isn't active. */
+ return 0;
+ } else if (code != 0) {
+- (void) fprintf (stderr, _("%s: nscd exited with status %d\n"),
++ (void) fprintf (shadow_logfd, _("%s: nscd exited with status %d\n"),
+ Prog, code);
+- (void) fprintf (stderr, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
++ (void) fprintf (shadow_logfd, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
+ return -1;
+ }
+
+diff -up shadow-4.6/lib/nss.c.libsubid_not_print_error_messages shadow-4.6/lib/nss.c
+--- shadow-4.6/lib/nss.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/lib/nss.c 2021-11-03 09:24:05.683172791 +0100
+@@ -56,7 +56,7 @@ void nss_init(char *nsswitch_path) {
+ // subid: files
+ nssfp = fopen(nsswitch_path, "r");
+ if (!nssfp) {
+- fprintf(stderr, "Failed opening %s: %m", nsswitch_path);
++ fprintf(shadow_logfd, "Failed opening %s: %m", nsswitch_path);
+ atomic_store(&nss_init_completed, true);
+ return;
+ }
+@@ -82,16 +82,16 @@ void nss_init(char *nsswitch_path) {
+ goto done;
+ }
+ if (strlen(token) > 50) {
+- fprintf(stderr, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
+- fprintf(stderr, "Using files\n");
++ fprintf(shadow_logfd, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
++ fprintf(shadow_logfd, "Using files\n");
+ subid_nss = NULL;
+ goto done;
+ }
+ snprintf(libname, 64, "libsubid_%s.so", token);
+ h = dlopen(libname, RTLD_LAZY);
+ if (!h) {
+- fprintf(stderr, "Error opening %s: %s\n", libname, dlerror());
+- fprintf(stderr, "Using files\n");
++ fprintf(shadow_logfd, "Error opening %s: %s\n", libname, dlerror());
++ fprintf(shadow_logfd, "Using files\n");
+ subid_nss = NULL;
+ goto done;
+ }
+@@ -102,7 +102,7 @@ void nss_init(char *nsswitch_path) {
+ }
+ subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
+ if (!subid_nss->has_range) {
+- fprintf(stderr, "%s did not provide @has_range@\n", libname);
++ fprintf(shadow_logfd, "%s did not provide @has_range@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+@@ -110,7 +110,7 @@ void nss_init(char *nsswitch_path) {
+ }
+ subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
+ if (!subid_nss->list_owner_ranges) {
+- fprintf(stderr, "%s did not provide @list_owner_ranges@\n", libname);
++ fprintf(shadow_logfd, "%s did not provide @list_owner_ranges@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+@@ -118,7 +118,7 @@ void nss_init(char *nsswitch_path) {
+ }
+ subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range");
+ if (!subid_nss->has_any_range) {
+- fprintf(stderr, "%s did not provide @has_any_range@\n", libname);
++ fprintf(shadow_logfd, "%s did not provide @has_any_range@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+@@ -126,7 +126,7 @@ void nss_init(char *nsswitch_path) {
+ }
+ subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
+ if (!subid_nss->find_subid_owners) {
+- fprintf(stderr, "%s did not provide @find_subid_owners@\n", libname);
++ fprintf(shadow_logfd, "%s did not provide @find_subid_owners@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+@@ -135,7 +135,7 @@ void nss_init(char *nsswitch_path) {
+ subid_nss->handle = h;
+ goto done;
+ }
+- fprintf(stderr, "No usable subid NSS module found, using files\n");
++ fprintf(shadow_logfd, "No usable subid NSS module found, using files\n");
+ // subid_nss has to be null here, but to ease reviews:
+ free(subid_nss);
+ subid_nss = NULL;
+diff -up shadow-4.6/lib/prototypes.h.libsubid_not_print_error_messages shadow-4.6/lib/prototypes.h
+--- shadow-4.6/lib/prototypes.h.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/lib/prototypes.h 2021-11-03 09:24:05.683172791 +0100
+@@ -59,7 +59,8 @@
+ #include "defines.h"
+ #include "commonio.h"
+
+-extern /*@observer@*/ const char *Prog;
++extern /*@observer@*/ const char *Prog; /* Program name showed in error messages */
++extern FILE *shadow_logfd; /* file descripter to which error messages are printed */
+
+ /* addgrps.c */
+ #if defined (HAVE_SETGROUPS) && ! defined (USE_PAM)
+diff -up shadow-4.6/lib/selinux.c.libsubid_not_print_error_messages shadow-4.6/lib/selinux.c
+diff -up shadow-4.6/lib/semanage.c.libsubid_not_print_error_messages shadow-4.6/lib/semanage.c
+--- shadow-4.6/lib/semanage.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.644172481 +0100
++++ shadow-4.6/lib/semanage.c 2021-11-03 09:24:05.691172854 +0100
+@@ -69,7 +69,7 @@ static void semanage_error_callback (unu
+ switch (semanage_msg_get_level (handle)) {
+ case SEMANAGE_MSG_ERR:
+ case SEMANAGE_MSG_WARN:
+- fprintf (stderr, _("[libsemanage]: %s\n"), message);
++ fprintf (shadow_logfd, _("[libsemanage]: %s\n"), message);
+ break;
+ case SEMANAGE_MSG_INFO:
+ /* nop */
+@@ -87,7 +87,7 @@ static semanage_handle_t *semanage_init
+
+ handle = semanage_handle_create ();
+ if (NULL == handle) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Cannot create SELinux management handle\n"));
+ return NULL;
+ }
+@@ -96,26 +96,26 @@ static semanage_handle_t *semanage_init
+
+ ret = semanage_is_managed (handle);
+ if (ret != 1) {
+- fprintf (stderr, _("SELinux policy not managed\n"));
++ fprintf (shadow_logfd, _("SELinux policy not managed\n"));
+ goto fail;
+ }
+
+ ret = semanage_access_check (handle);
+ if (ret < SEMANAGE_CAN_READ) {
+- fprintf (stderr, _("Cannot read SELinux policy store\n"));
++ fprintf (shadow_logfd, _("Cannot read SELinux policy store\n"));
+ goto fail;
+ }
+
+ ret = semanage_connect (handle);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Cannot establish SELinux management connection\n"));
+ goto fail;
+ }
+
+ ret = semanage_begin_transaction (handle);
+ if (ret != 0) {
+- fprintf (stderr, _("Cannot begin SELinux transaction\n"));
++ fprintf (shadow_logfd, _("Cannot begin SELinux transaction\n"));
+ goto fail;
+ }
+
+@@ -137,7 +137,7 @@ static int semanage_user_mod (semanage_h
+
+ semanage_seuser_query (handle, key, &seuser);
+ if (NULL == seuser) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not query seuser for %s\n"), login_name);
+ ret = 1;
+ goto done;
+@@ -146,7 +146,7 @@ static int semanage_user_mod (semanage_h
+ #if 0
+ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not set serange for %s\n"), login_name);
+ ret = 1;
+ goto done;
+@@ -155,7 +155,7 @@ static int semanage_user_mod (semanage_h
+
+ ret = semanage_seuser_set_sename (handle, seuser, seuser_name);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not set sename for %s\n"),
+ login_name);
+ ret = 1;
+@@ -164,7 +164,7 @@ static int semanage_user_mod (semanage_h
+
+ ret = semanage_seuser_modify_local (handle, key, seuser);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not modify login mapping for %s\n"),
+ login_name);
+ ret = 1;
+@@ -188,7 +188,7 @@ static int semanage_user_add (semanage_h
+
+ ret = semanage_seuser_create (handle, &seuser);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Cannot create SELinux login mapping for %s\n"),
+ login_name);
+ ret = 1;
+@@ -197,7 +197,7 @@ static int semanage_user_add (semanage_h
+
+ ret = semanage_seuser_set_name (handle, seuser, login_name);
+ if (ret != 0) {
+- fprintf (stderr, _("Could not set name for %s\n"), login_name);
++ fprintf (shadow_logfd, _("Could not set name for %s\n"), login_name);
+ ret = 1;
+ goto done;
+ }
+@@ -205,7 +205,7 @@ static int semanage_user_add (semanage_h
+ #if 0
+ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not set serange for %s\n"),
+ login_name);
+ ret = 1;
+@@ -215,7 +215,7 @@ static int semanage_user_add (semanage_h
+
+ ret = semanage_seuser_set_sename (handle, seuser, seuser_name);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not set SELinux user for %s\n"),
+ login_name);
+ ret = 1;
+@@ -224,7 +224,7 @@ static int semanage_user_add (semanage_h
+
+ ret = semanage_seuser_modify_local (handle, key, seuser);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not add login mapping for %s\n"),
+ login_name);
+ ret = 1;
+@@ -252,21 +252,21 @@ int set_seuser (const char *login_name,
+
+ handle = semanage_init ();
+ if (NULL == handle) {
+- fprintf (stderr, _("Cannot init SELinux management\n"));
++ fprintf (shadow_logfd, _("Cannot init SELinux management\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create (handle, login_name, &key);
+ if (ret != 0) {
+- fprintf (stderr, _("Cannot create SELinux user key\n"));
++ fprintf (shadow_logfd, _("Cannot create SELinux user key\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists (handle, key, &seuser_exists);
+ if (ret < 0) {
+- fprintf (stderr, _("Cannot verify the SELinux user\n"));
++ fprintf (shadow_logfd, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+@@ -274,7 +274,7 @@ int set_seuser (const char *login_name,
+ if (0 != seuser_exists) {
+ ret = semanage_user_mod (handle, key, login_name, seuser_name);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Cannot modify SELinux user mapping\n"));
+ ret = 1;
+ goto done;
+@@ -282,7 +282,7 @@ int set_seuser (const char *login_name,
+ } else {
+ ret = semanage_user_add (handle, key, login_name, seuser_name);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Cannot add SELinux user mapping\n"));
+ ret = 1;
+ goto done;
+@@ -291,7 +291,7 @@ int set_seuser (const char *login_name,
+
+ ret = semanage_commit (handle);
+ if (ret < 0) {
+- fprintf (stderr, _("Cannot commit SELinux transaction\n"));
++ fprintf (shadow_logfd, _("Cannot commit SELinux transaction\n"));
+ ret = 1;
+ goto done;
+ }
+@@ -317,27 +317,27 @@ int del_seuser (const char *login_name)
+
+ handle = semanage_init ();
+ if (NULL == handle) {
+- fprintf (stderr, _("Cannot init SELinux management\n"));
++ fprintf (shadow_logfd, _("Cannot init SELinux management\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create (handle, login_name, &key);
+ if (ret != 0) {
+- fprintf (stderr, _("Cannot create SELinux user key\n"));
++ fprintf (shadow_logfd, _("Cannot create SELinux user key\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists (handle, key, &exists);
+ if (ret < 0) {
+- fprintf (stderr, _("Cannot verify the SELinux user\n"));
++ fprintf (shadow_logfd, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (0 == exists) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Login mapping for %s is not defined, OK if default mapping was used\n"),
+ login_name);
+ ret = 0; /* probably default mapping */
+@@ -346,13 +346,13 @@ int del_seuser (const char *login_name)
+
+ ret = semanage_seuser_exists_local (handle, key, &exists);
+ if (ret < 0) {
+- fprintf (stderr, _("Cannot verify the SELinux user\n"));
++ fprintf (shadow_logfd, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (0 == exists) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Login mapping for %s is defined in policy, cannot be deleted\n"),
+ login_name);
+ ret = 0; /* Login mapping defined in policy can't be deleted */
+@@ -361,7 +361,7 @@ int del_seuser (const char *login_name)
+
+ ret = semanage_seuser_del_local (handle, key);
+ if (ret != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("Could not delete login mapping for %s"),
+ login_name);
+ ret = 1;
+@@ -370,7 +370,7 @@ int del_seuser (const char *login_name)
+
+ ret = semanage_commit (handle);
+ if (ret < 0) {
+- fprintf (stderr, _("Cannot commit SELinux transaction\n"));
++ fprintf (shadow_logfd, _("Cannot commit SELinux transaction\n"));
+ ret = 1;
+ goto done;
+ }
+diff -up shadow-4.6/lib/spawn.c.libsubid_not_print_error_messages shadow-4.6/lib/spawn.c
+--- shadow-4.6/lib/spawn.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.660172608 +0100
++++ shadow-4.6/lib/spawn.c 2021-11-03 09:24:05.692172863 +0100
+@@ -48,7 +48,7 @@ int run_command (const char *cmd, const
+ }
+
+ (void) fflush (stdout);
+- (void) fflush (stderr);
++ (void) fflush (shadow_logfd);
+
+ pid = fork ();
+ if (0 == pid) {
+@@ -57,11 +57,11 @@ int run_command (const char *cmd, const
+ if (ENOENT == errno) {
+ exit (E_CMD_NOTFOUND);
+ }
+- fprintf (stderr, "%s: cannot execute %s: %s\n",
++ fprintf (shadow_logfd, "%s: cannot execute %s: %s\n",
+ Prog, cmd, strerror (errno));
+ exit (E_CMD_NOEXEC);
+ } else if ((pid_t)-1 == pid) {
+- fprintf (stderr, "%s: cannot execute %s: %s\n",
++ fprintf (shadow_logfd, "%s: cannot execute %s: %s\n",
+ Prog, cmd, strerror (errno));
+ return -1;
+ }
+@@ -72,7 +72,7 @@ int run_command (const char *cmd, const
+ || ((pid_t)-1 != wpid && wpid != pid));
+
+ if ((pid_t)-1 == wpid) {
+- fprintf (stderr, "%s: waitpid (status: %d): %s\n",
++ fprintf (shadow_logfd, "%s: waitpid (status: %d): %s\n",
+ Prog, *status, strerror (errno));
+ return -1;
+ }
+diff -up shadow-4.6/libsubid/api.c.libsubid_not_print_error_messages shadow-4.6/libsubid/api.c
+--- shadow-4.6/libsubid/api.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/libsubid/api.c 2021-11-03 09:24:05.692172863 +0100
+@@ -32,12 +32,39 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
+ #include "subordinateio.h"
+ #include "idmapping.h"
+ #include "subid.h"
+
++const char *Prog = "(libsubid)";
++extern FILE * shadow_logfd;
++
++bool libsubid_init(const char *progname, FILE * logfd)
++{
++ if (progname) {
++ progname = strdup(progname);
++ if (progname)
++ Prog = progname;
++ else
++ fprintf(stderr, "Out of memory");
++ }
++
++ if (logfd) {
++ shadow_logfd = logfd;
++ return true;
++ }
++ shadow_logfd = fopen("/dev/null", "w");
++ if (!shadow_logfd) {
++ fprintf(stderr, "ERROR opening /dev/null for error messages. Using stderr.");
++ shadow_logfd = stderr;
++ return false;
++ }
++ return true;
++}
++
+ static
+ int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
+ {
+diff -up shadow-4.6/libsubid/subid.h.libsubid_not_print_error_messages shadow-4.6/libsubid/subid.h
+--- shadow-4.6/libsubid/subid.h.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/libsubid/subid.h 2021-11-03 09:24:05.692172863 +0100
+@@ -22,6 +22,22 @@ enum subid_status {
+ };
+
+ /*
++ * libsubid_init: initialize libsubid
++ *
++ * @progname: Name to display as program. If NULL, then "(libsubid)" will be
++ * shown in error messages.
++ * @logfd: Open file pointer to pass error messages to. If NULL, then
++ * /dev/null will be opened and messages will be sent there. The
++ * default if libsubid_init() is not called is stderr (2).
++ *
++ * This function does not need to be called. If not called, then the defaults
++ * will be used.
++ *
++ * Returns false if an error occurred.
++ */
++bool libsubid_init(const char *progname, FILE *logfd);
++
++/*
+ * get_subuid_ranges: return a list of UID ranges for a user
+ *
+ * @owner: username being queried
+diff -up shadow-4.6/lib/tcbfuncs.c.libsubid_not_print_error_messages shadow-4.6/lib/tcbfuncs.c
+--- shadow-4.6/lib/tcbfuncs.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/lib/tcbfuncs.c 2021-11-03 09:24:05.693172870 +0100
+@@ -72,8 +72,8 @@ shadowtcb_status shadowtcb_gain_priv (vo
+ * to exit soon.
+ */
+ #define OUT_OF_MEMORY do { \
+- fprintf (stderr, _("%s: out of memory\n"), Prog); \
+- (void) fflush (stderr); \
++ fprintf (shadow_logfd, _("%s: out of memory\n"), Prog); \
++ (void) fflush (shadow_logfd); \
+ } while (false)
+
+ /* Returns user's tcb directory path relative to TCB_DIR. */
+@@ -116,7 +116,7 @@ static /*@null@*/ char *shadowtcb_path_r
+ return NULL;
+ }
+ if (lstat (path, &st) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, path, strerror (errno));
+ free (path);
+@@ -132,7 +132,7 @@ static /*@null@*/ char *shadowtcb_path_r
+ return rval;
+ }
+ if (!S_ISLNK (st.st_mode)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: %s is neither a directory, nor a symlink.\n"),
+ Prog, path);
+ free (path);
+@@ -140,7 +140,7 @@ static /*@null@*/ char *shadowtcb_path_r
+ }
+ ret = readlink (path, link, sizeof (link) - 1);
+ if (-1 == ret) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot read symbolic link %s: %s\n"),
+ Prog, path, strerror (errno));
+ free (path);
+@@ -149,7 +149,7 @@ static /*@null@*/ char *shadowtcb_path_r
+ free (path);
+ if ((size_t)ret >= sizeof(link) - 1) {
+ link[sizeof(link) - 1] = '\0';
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Suspiciously long symlink: %s\n"),
+ Prog, link);
+ return NULL;
+@@ -207,7 +207,7 @@ static shadowtcb_status mkdir_leading (c
+ }
+ ptr = path;
+ if (stat (TCB_DIR, &st) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, TCB_DIR, strerror (errno));
+ goto out_free_path;
+@@ -219,19 +219,19 @@ static shadowtcb_status mkdir_leading (c
+ return SHADOWTCB_FAILURE;
+ }
+ if ((mkdir (dir, 0700) != 0) && (errno != EEXIST)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot create directory %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+ }
+ if (chown (dir, 0, st.st_gid) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+ }
+ if (chmod (dir, 0711) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+@@ -261,7 +261,7 @@ static shadowtcb_status unlink_suffs (co
+ return SHADOWTCB_FAILURE;
+ }
+ if ((unlink (tmp) != 0) && (errno != ENOENT)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: unlink: %s: %s\n"),
+ Prog, tmp, strerror (errno));
+ free (tmp);
+@@ -286,7 +286,7 @@ static shadowtcb_status rmdir_leading (c
+ }
+ if (rmdir (dir) != 0) {
+ if (errno != ENOTEMPTY) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot remove directory %s: %s\n"),
+ Prog, dir, strerror (errno));
+ ret = SHADOWTCB_FAILURE;
+@@ -315,7 +315,7 @@ static shadowtcb_status move_dir (const
+ goto out_free_nomem;
+ }
+ if (stat (olddir, &oldmode) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, olddir, strerror (errno));
+ goto out_free;
+@@ -342,7 +342,7 @@ static shadowtcb_status move_dir (const
+ goto out_free;
+ }
+ if (rename (real_old_dir, real_new_dir) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot rename %s to %s: %s\n"),
+ Prog, real_old_dir, real_new_dir, strerror (errno));
+ goto out_free;
+@@ -351,7 +351,7 @@ static shadowtcb_status move_dir (const
+ goto out_free;
+ }
+ if ((unlink (olddir) != 0) && (errno != ENOENT)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot remove %s: %s\n"),
+ Prog, olddir, strerror (errno));
+ goto out_free;
+@@ -365,7 +365,7 @@ static shadowtcb_status move_dir (const
+ }
+ if ( (strcmp (real_new_dir, newdir) != 0)
+ && (symlink (real_new_dir_rel, newdir) != 0)) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot create symbolic link %s: %s\n"),
+ Prog, real_new_dir_rel, strerror (errno));
+ goto out_free;
+@@ -464,37 +464,37 @@ shadowtcb_status shadowtcb_move (/*@NULL
+ return SHADOWTCB_FAILURE;
+ }
+ if (stat (tcbdir, &dirmode) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (chown (tcbdir, 0, 0) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owners of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (tcbdir, 0700) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (lstat (shadow, &filemode) != 0) {
+ if (errno != ENOENT) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot lstat %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Warning, user %s has no tcb shadow file.\n"),
+ Prog, user_newname);
+ } else {
+ if (!S_ISREG (filemode.st_mode) ||
+ filemode.st_nlink != 1) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Emergency: %s's tcb shadow is not a "
+ "regular file with st_nlink=1.\n"
+ "The account is left locked.\n"),
+@@ -502,13 +502,13 @@ shadowtcb_status shadowtcb_move (/*@NULL
+ goto out_free;
+ }
+ if (chown (shadow, user_newid, filemode.st_gid) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (shadow, filemode.st_mode & 07777) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+@@ -518,7 +518,7 @@ shadowtcb_status shadowtcb_move (/*@NULL
+ goto out_free;
+ }
+ if (chown (tcbdir, user_newid, dirmode.st_gid) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+@@ -543,7 +543,7 @@ shadowtcb_status shadowtcb_create (const
+ return SHADOWTCB_SUCCESS;
+ }
+ if (stat (TCB_DIR, &tcbdir_stat) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, TCB_DIR, strerror (errno));
+ return SHADOWTCB_FAILURE;
+@@ -563,39 +563,39 @@ shadowtcb_status shadowtcb_create (const
+ return SHADOWTCB_FAILURE;
+ }
+ if (mkdir (dir, 0700) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: mkdir: %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+ }
+ fd = open (shadow, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot open %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ close (fd);
+ if (chown (shadow, 0, authgid) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (shadow, (mode_t) ((authgid == shadowgid) ? 0600 : 0640)) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chown (dir, 0, authgid) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (dir, (mode_t) ((authgid == shadowgid) ? 02700 : 02710)) != 0) {
+- fprintf (stderr,
++ fprintf (shadow_logfd,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+diff -up shadow-4.6/src/chage.c.libsubid_not_print_error_messages shadow-4.6/src/chage.c
+--- shadow-4.6/src/chage.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.655172568 +0100
++++ shadow-4.6/src/chage.c 2021-11-03 09:24:05.694172878 +0100
+@@ -66,6 +66,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool
+ dflg = false, /* set last password change date */
+@@ -806,6 +807,7 @@ int main (int argc, char **argv)
+ * Get the program name so that error messages can use it.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ sanitize_env ();
+ (void) setlocale (LC_ALL, "");
+diff -up shadow-4.6/src/check_subid_range.c.libsubid_not_print_error_messages shadow-4.6/src/check_subid_range.c
+--- shadow-4.6/src/check_subid_range.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/src/check_subid_range.c 2021-11-03 09:24:05.694172878 +0100
+@@ -18,6 +18,7 @@
+ #include "idmapping.h"
+
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ int main(int argc, char **argv)
+ {
+@@ -25,6 +26,7 @@ int main(int argc, char **argv)
+ unsigned long start, count;
+ bool check_uids;
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ if (argc != 5)
+ exit(1);
+diff -up shadow-4.6/src/chfn.c.libsubid_not_print_error_messages shadow-4.6/src/chfn.c
+--- shadow-4.6/src/chfn.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/chfn.c 2021-11-03 09:24:05.695172886 +0100
+@@ -61,6 +61,7 @@
+ * Global variables.
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+ static char fullnm[BUFSIZ];
+ static char roomno[BUFSIZ];
+ static char workph[BUFSIZ];
+@@ -639,6 +640,7 @@ int main (int argc, char **argv)
+ * prefix to most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ sanitize_env ();
+ (void) setlocale (LC_ALL, "");
+diff -up shadow-4.6/src/chgpasswd.c.libsubid_not_print_error_messages shadow-4.6/src/chgpasswd.c
+--- shadow-4.6/src/chgpasswd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/chgpasswd.c 2021-11-03 09:32:53.937617545 +0100
+@@ -66,6 +66,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+ static bool eflg = false;
+ static bool md5flg = false;
+ #ifdef USE_SHA_CRYPT
+@@ -466,6 +467,7 @@ int main (int argc, char **argv)
+ int line = 0;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/chpasswd.c.libsubid_not_print_error_messages shadow-4.6/src/chpasswd.c
+--- shadow-4.6/src/chpasswd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/chpasswd.c 2021-11-03 09:33:19.029832153 +0100
+@@ -63,6 +63,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+ static bool eflg = false;
+ static bool md5flg = false;
+ #ifdef USE_SHA_CRYPT
+@@ -453,6 +454,7 @@ int main (int argc, char **argv)
+ int line = 0;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/chsh.c.libsubid_not_print_error_messages shadow-4.6/src/chsh.c
+--- shadow-4.6/src/chsh.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/chsh.c 2021-11-03 09:24:05.697172902 +0100
+@@ -63,6 +63,7 @@
+ * Global variables
+ */
+ const char *Prog; /* Program name */
++FILE *shadow_logfd = NULL;
+ static bool amroot; /* Real UID is root */
+ static char loginsh[BUFSIZ]; /* Name of new login shell */
+ /* command line options */
+@@ -446,6 +447,7 @@ int main (int argc, char **argv)
+ * most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/expiry.c.libsubid_not_print_error_messages shadow-4.6/src/expiry.c
+--- shadow-4.6/src/expiry.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/expiry.c 2021-11-03 09:24:05.698172910 +0100
+@@ -46,6 +46,7 @@
+
+ /* Global variables */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+ static bool cflg = false;
+
+ /* local function prototypes */
+@@ -144,6 +145,7 @@ int main (int argc, char **argv)
+ struct spwd *spwd;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ sanitize_env ();
+
+diff -up shadow-4.6/src/faillog.c.libsubid_not_print_error_messages shadow-4.6/src/faillog.c
+--- shadow-4.6/src/faillog.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.655172568 +0100
++++ shadow-4.6/src/faillog.c 2021-11-03 09:24:05.698172910 +0100
+@@ -62,6 +62,7 @@ static void reset (void);
+ * Global variables
+ */
+ const char *Prog; /* Program name */
++FILE *shadow_logfd = NULL;
+ static FILE *fail; /* failure file stream */
+ static time_t seconds; /* that number of days in seconds */
+ static unsigned long umin; /* if uflg and has_umin, only display users with uid >= umin */
+@@ -573,6 +574,7 @@ int main (int argc, char **argv)
+ * most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/free_subid_range.c.libsubid_not_print_error_messages shadow-4.6/src/free_subid_range.c
+--- shadow-4.6/src/free_subid_range.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/src/free_subid_range.c 2021-11-03 09:24:05.698172910 +0100
+@@ -7,6 +7,7 @@
+ /* Test program for the subid freeing routine */
+
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ void usage(void)
+ {
+@@ -23,6 +24,7 @@ int main(int argc, char *argv[])
+ bool group = false; // get subuids by default
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ while ((c = getopt(argc, argv, "g")) != EOF) {
+ switch(c) {
+ case 'g': group = true; break;
+diff -up shadow-4.6/src/get_subid_owners.c.libsubid_not_print_error_messages shadow-4.6/src/get_subid_owners.c
+--- shadow-4.6/src/get_subid_owners.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.678172751 +0100
++++ shadow-4.6/src/get_subid_owners.c 2021-11-03 09:24:05.699172918 +0100
+@@ -4,6 +4,7 @@
+ #include "prototypes.h"
+
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ void usage(void)
+ {
+@@ -19,6 +20,7 @@ int main(int argc, char *argv[])
+ uid_t *uids;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ if (argc < 2) {
+ usage();
+ }
+diff -up shadow-4.6/src/gpasswd.c.libsubid_not_print_error_messages shadow-4.6/src/gpasswd.c
+--- shadow-4.6/src/gpasswd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/gpasswd.c 2021-11-03 09:24:05.699172918 +0100
+@@ -58,6 +58,7 @@
+ */
+ /* The name of this command, as it is invoked */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ #ifdef SHADOWGRP
+ /* Indicate if shadow groups are enabled on the system
+@@ -926,6 +927,7 @@ int main (int argc, char **argv)
+ */
+ bywho = getuid ();
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ OPENLOG ("gpasswd");
+ setbuf (stdout, NULL);
+diff -up shadow-4.6/src/groupadd.c.libsubid_not_print_error_messages shadow-4.6/src/groupadd.c
+--- shadow-4.6/src/groupadd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/groupadd.c 2021-11-03 09:24:05.700172926 +0100
+@@ -72,6 +72,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static /*@null@*/char *group_name;
+ static gid_t group_id;
+@@ -582,6 +583,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/groupdel.c.libsubid_not_print_error_messages shadow-4.6/src/groupdel.c
+--- shadow-4.6/src/groupdel.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/groupdel.c 2021-11-03 09:24:05.700172926 +0100
+@@ -58,6 +58,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static char *group_name;
+ static gid_t group_id = -1;
+@@ -377,6 +378,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/groupmems.c.libsubid_not_print_error_messages shadow-4.6/src/groupmems.c
+--- shadow-4.6/src/groupmems.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/groupmems.c 2021-11-03 09:24:05.701172934 +0100
+@@ -65,6 +65,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static char *adduser = NULL;
+ static char *deluser = NULL;
+@@ -595,6 +596,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/groupmod.c.libsubid_not_print_error_messages shadow-4.6/src/groupmod.c
+--- shadow-4.6/src/groupmod.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/groupmod.c 2021-11-03 09:24:05.702172942 +0100
+@@ -76,6 +76,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ #ifdef SHADOWGRP
+ static bool is_shadow_grp;
+@@ -799,6 +800,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/groups.c.libsubid_not_print_error_messages shadow-4.6/src/groups.c
+--- shadow-4.6/src/groups.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/groups.c 2021-11-03 09:24:05.702172942 +0100
+@@ -43,6 +43,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ /* local function prototypes */
+ static void print_groups (const char *member);
+@@ -126,6 +127,7 @@ int main (int argc, char **argv)
+ * Get the program name so that error messages can use it.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ if (argc == 1) {
+
+diff -up shadow-4.6/src/grpck.c.libsubid_not_print_error_messages shadow-4.6/src/grpck.c
+--- shadow-4.6/src/grpck.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.662172624 +0100
++++ shadow-4.6/src/grpck.c 2021-11-03 09:24:05.703172950 +0100
+@@ -68,6 +68,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static const char *grp_file = GROUP_FILE;
+ static bool use_system_grp_file = true;
+@@ -836,6 +837,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/grpconv.c.libsubid_not_print_error_messages shadow-4.6/src/grpconv.c
+--- shadow-4.6/src/grpconv.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/grpconv.c 2021-11-03 09:24:05.703172950 +0100
+@@ -59,6 +59,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool gr_locked = false;
+ static bool sgr_locked = false;
+@@ -146,6 +147,7 @@ int main (int argc, char **argv)
+ struct sgrp sgent;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/grpunconv.c.libsubid_not_print_error_messages shadow-4.6/src/grpunconv.c
+--- shadow-4.6/src/grpunconv.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/grpunconv.c 2021-11-03 09:24:05.704172958 +0100
+@@ -59,6 +59,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool gr_locked = false;
+ static bool sgr_locked = false;
+@@ -145,6 +146,7 @@ int main (int argc, char **argv)
+ const struct sgrp *sg;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/lastlog.c.libsubid_not_print_error_messages shadow-4.6/src/lastlog.c
+--- shadow-4.6/src/lastlog.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.655172568 +0100
++++ shadow-4.6/src/lastlog.c 2021-11-03 09:24:05.704172958 +0100
+@@ -58,6 +58,7 @@
+ * Global variables
+ */
+ const char *Prog; /* Program name */
++FILE *shadow_logfd = NULL;
+ static FILE *lastlogfile; /* lastlog file stream */
+ static unsigned long umin; /* if uflg and has_umin, only display users with uid >= umin */
+ static bool has_umin = false;
+@@ -283,6 +284,7 @@ int main (int argc, char **argv)
+ * most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/list_subid_ranges.c.libsubid_not_print_error_messages shadow-4.6/src/list_subid_ranges.c
+--- shadow-4.6/src/list_subid_ranges.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.679172759 +0100
++++ shadow-4.6/src/list_subid_ranges.c 2021-11-03 09:24:05.704172958 +0100
+@@ -4,6 +4,7 @@
+ #include "prototypes.h"
+
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ void usage(void)
+ {
+@@ -19,6 +20,7 @@ int main(int argc, char *argv[])
+ struct subordinate_range **ranges;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ if (argc < 2) {
+ usage();
+ }
+diff -up shadow-4.6/src/login.c.libsubid_not_print_error_messages shadow-4.6/src/login.c
+--- shadow-4.6/src/login.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/login.c 2021-11-03 09:24:05.705172966 +0100
+@@ -83,6 +83,7 @@ static pam_handle_t *pamh = NULL;
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static const char *hostname = "";
+ static /*@null@*/ /*@only@*/char *username = NULL;
+@@ -562,6 +563,7 @@ int main (int argc, char **argv)
+
+ amroot = (getuid () == 0);
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ if (geteuid() != 0) {
+ fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog);
+diff -up shadow-4.6/src/logoutd.c.libsubid_not_print_error_messages shadow-4.6/src/logoutd.c
+--- shadow-4.6/src/logoutd.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/logoutd.c 2021-11-03 09:24:05.706172974 +0100
+@@ -44,6 +44,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ #ifndef DEFAULT_HUP_MESG
+ #define DEFAULT_HUP_MESG _("login time exceeded\n\n")
+@@ -187,6 +188,7 @@ int main (int argc, char **argv)
+ * Start syslogging everything
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ OPENLOG ("logoutd");
+
+diff -up shadow-4.6/src/newgidmap.c.libsubid_not_print_error_messages shadow-4.6/src/newgidmap.c
+--- shadow-4.6/src/newgidmap.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/newgidmap.c 2021-11-03 09:24:05.706172974 +0100
+@@ -45,6 +45,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+
+ static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow_setgroups)
+@@ -175,6 +176,7 @@ int main(int argc, char **argv)
+ bool allow_setgroups = false;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ /*
+ * The valid syntax are
+diff -up shadow-4.6/src/newgrp.c.libsubid_not_print_error_messages shadow-4.6/src/newgrp.c
+--- shadow-4.6/src/newgrp.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.650172528 +0100
++++ shadow-4.6/src/newgrp.c 2021-11-03 09:24:05.707172982 +0100
+@@ -49,6 +49,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ extern char **newenvp;
+ extern char **environ;
+@@ -443,6 +444,7 @@ int main (int argc, char **argv)
+ * don't need to re-exec anything. -- JWP
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ is_newgrp = (strcmp (Prog, "newgrp") == 0);
+ OPENLOG (is_newgrp ? "newgrp" : "sg");
+ gid = getgid ();
+diff -up shadow-4.6/src/new_subid_range.c.libsubid_not_print_error_messages shadow-4.6/src/new_subid_range.c
+--- shadow-4.6/src/new_subid_range.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.679172759 +0100
++++ shadow-4.6/src/new_subid_range.c 2021-11-03 09:24:05.707172982 +0100
+@@ -7,6 +7,7 @@
+ /* Test program for the subid creation routine */
+
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ void usage(void)
+ {
+@@ -26,6 +27,7 @@ int main(int argc, char *argv[])
+ bool ok;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ while ((c = getopt(argc, argv, "gn")) != EOF) {
+ switch(c) {
+ case 'n': makenew = true; break;
+diff -up shadow-4.6/src/newuidmap.c.libsubid_not_print_error_messages shadow-4.6/src/newuidmap.c
+--- shadow-4.6/src/newuidmap.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/newuidmap.c 2021-11-03 09:24:05.707172982 +0100
+@@ -45,6 +45,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool verify_range(struct passwd *pw, struct map_range *range)
+ {
+@@ -105,6 +106,7 @@ int main(int argc, char **argv)
+ int written;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ /*
+ * The valid syntax are
+diff -up shadow-4.6/src/newusers.c.libsubid_not_print_error_messages shadow-4.6/src/newusers.c
+--- shadow-4.6/src/newusers.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.670172687 +0100
++++ shadow-4.6/src/newusers.c 2021-11-03 09:24:05.708172990 +0100
+@@ -75,6 +75,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool rflg = false; /* create a system account */
+ #ifndef USE_PAM
+@@ -970,6 +971,7 @@ int main (int argc, char **argv)
+ #endif /* USE_PAM */
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/passwd.c.libsubid_not_print_error_messages shadow-4.6/src/passwd.c
+--- shadow-4.6/src/passwd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/passwd.c 2021-11-03 09:24:05.709172998 +0100
+@@ -72,6 +72,7 @@
+ * Global variables
+ */
+ const char *Prog; /* Program name */
++FILE *shadow_logfd = NULL;
+
+ static char *name; /* The name of user whose password is being changed */
+ static char *myname; /* The current user's name */
+@@ -808,6 +809,7 @@ int main (int argc, char **argv)
+ * most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/pwck.c.libsubid_not_print_error_messages shadow-4.6/src/pwck.c
+--- shadow-4.6/src/pwck.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/pwck.c 2021-11-03 09:24:05.709172998 +0100
+@@ -70,6 +70,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool use_system_pw_file = true;
+ static bool use_system_spw_file = true;
+diff -up shadow-4.6/src/pwconv.c.libsubid_not_print_error_messages shadow-4.6/src/pwconv.c
+--- shadow-4.6/src/pwconv.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/pwconv.c 2021-11-03 09:24:05.709172998 +0100
+@@ -89,6 +89,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool spw_locked = false;
+ static bool pw_locked = false;
+@@ -176,6 +177,7 @@ int main (int argc, char **argv)
+ struct spwd spent;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/pwunconv.c.libsubid_not_print_error_messages shadow-4.6/src/pwunconv.c
+--- shadow-4.6/src/pwunconv.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/pwunconv.c 2021-11-03 09:24:05.710173006 +0100
+@@ -53,6 +53,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static bool spw_locked = false;
+ static bool pw_locked = false;
+@@ -137,6 +138,7 @@ int main (int argc, char **argv)
+ const struct spwd *spwd;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/su.c.libsubid_not_print_error_messages shadow-4.6/src/su.c
+--- shadow-4.6/src/su.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/su.c 2021-11-03 09:24:05.710173006 +0100
+@@ -82,6 +82,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+ static /*@observer@*/const char *caller_tty = NULL; /* Name of tty SU is run from */
+ static bool caller_is_root = false;
+ static uid_t caller_uid;
+@@ -699,6 +700,7 @@ static void save_caller_context (char **
+ * most error messages.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ caller_uid = getuid ();
+ caller_is_root = (caller_uid == 0);
+diff -up shadow-4.6/src/sulogin.c.libsubid_not_print_error_messages shadow-4.6/src/sulogin.c
+--- shadow-4.6/src/sulogin.c.libsubid_not_print_error_messages 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/src/sulogin.c 2021-11-03 09:24:05.710173006 +0100
+@@ -50,6 +50,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static char name[BUFSIZ];
+ static char pass[BUFSIZ];
+@@ -106,6 +107,7 @@ static RETSIGTYPE catch_signals (unused
+ #endif
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+ (void) textdomain (PACKAGE);
+diff -up shadow-4.6/src/useradd.c.libsubid_not_print_error_messages shadow-4.6/src/useradd.c
+--- shadow-4.6/src/useradd.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.672172703 +0100
++++ shadow-4.6/src/useradd.c 2021-11-03 09:24:05.711173014 +0100
+@@ -92,6 +92,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ /*
+ * These defaults are used if there is no defaults file.
+@@ -2176,6 +2177,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/userdel.c.libsubid_not_print_error_messages shadow-4.6/src/userdel.c
+--- shadow-4.6/src/userdel.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.663172632 +0100
++++ shadow-4.6/src/userdel.c 2021-11-03 09:24:05.712173022 +0100
+@@ -89,6 +89,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static char *user_name;
+ static uid_t user_id;
+@@ -939,6 +940,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+ (void) textdomain (PACKAGE);
+diff -up shadow-4.6/src/usermod.c.libsubid_not_print_error_messages shadow-4.6/src/usermod.c
+--- shadow-4.6/src/usermod.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.674172719 +0100
++++ shadow-4.6/src/usermod.c 2021-11-03 09:24:05.712173022 +0100
+@@ -102,6 +102,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static char *user_name;
+ static char *user_newname;
+@@ -2125,6 +2126,7 @@ int main (int argc, char **argv)
+ * Get my name so that I can use it to report errors.
+ */
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+diff -up shadow-4.6/src/vipw.c.libsubid_not_print_error_messages shadow-4.6/src/vipw.c
+--- shadow-4.6/src/vipw.c.libsubid_not_print_error_messages 2021-11-03 09:24:05.664172640 +0100
++++ shadow-4.6/src/vipw.c 2021-11-03 09:24:05.713173030 +0100
+@@ -63,6 +63,7 @@
+ * Global variables
+ */
+ const char *Prog;
++FILE *shadow_logfd = NULL;
+
+ static const char *filename, *fileeditname;
+ static bool filelocked = false;
+@@ -438,6 +439,7 @@ int main (int argc, char **argv)
+ bool do_vipw;
+
+ Prog = Basename (argv[0]);
++ shadow_logfd = stderr;
+
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
diff --git a/SOURCES/shadow-4.6-libsubid_nsswitch_support.patch b/SOURCES/shadow-4.6-libsubid_nsswitch_support.patch
new file mode 100644
index 0000000..4348d0e
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_nsswitch_support.patch
@@ -0,0 +1,1758 @@
+diff -up shadow-4.6/configure.ac.libsubid_nsswitch_support shadow-4.6/configure.ac
+--- shadow-4.6/configure.ac.libsubid_nsswitch_support 2021-10-19 13:16:21.989493315 +0200
++++ shadow-4.6/configure.ac 2021-10-19 13:19:07.743131310 +0200
+@@ -1,6 +1,6 @@
+ dnl Process this file with autoconf to produce a configure script.
+ AC_PREREQ([2.69])
+-m4_define([libsubid_abi_major], 1)
++m4_define([libsubid_abi_major], 2)
+ m4_define([libsubid_abi_minor], 0)
+ m4_define([libsubid_abi_micro], 0)
+ m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
+diff -up shadow-4.6/lib/Makefile.am.libsubid_nsswitch_support shadow-4.6/lib/Makefile.am
+--- shadow-4.6/lib/Makefile.am.libsubid_nsswitch_support 2021-10-19 13:16:21.973493060 +0200
++++ shadow-4.6/lib/Makefile.am 2021-10-19 13:16:21.989493315 +0200
+@@ -28,6 +28,7 @@ libshadow_la_SOURCES = \
+ groupio.h \
+ gshadow.c \
+ lockpw.c \
++ nss.c \
+ nscd.c \
+ nscd.h \
+ sssd.c \
+diff -up shadow-4.6/libmisc/idmapping.h.libsubid_nsswitch_support shadow-4.6/libmisc/idmapping.h
+--- shadow-4.6/libmisc/idmapping.h.libsubid_nsswitch_support 2021-10-19 13:16:21.989493315 +0200
++++ shadow-4.6/libmisc/idmapping.h 2021-10-19 13:19:50.629813857 +0200
+@@ -40,5 +40,7 @@ extern struct map_range *get_map_ranges(
+ extern void write_mapping(int proc_dir_fd, int ranges,
+ struct map_range *mappings, const char *map_file);
+
++extern void nss_init(char *nsswitch_path);
++
+ #endif /* _ID_MAPPING_H_ */
+
+diff -up shadow-4.6/libmisc/Makefile.am.libsubid_nsswitch_support shadow-4.6/libmisc/Makefile.am
+--- shadow-4.6/libmisc/Makefile.am.libsubid_nsswitch_support 2021-10-19 13:16:21.988493299 +0200
++++ shadow-4.6/libmisc/Makefile.am 2021-10-19 13:17:03.356151673 +0200
+@@ -3,9 +3,9 @@ EXTRA_DIST = .indent.pro xgetXXbyYY.c
+
+ AM_CPPFLAGS = -I$(top_srcdir)/lib
+
+-noinst_LIBRARIES = libmisc.a
++noinst_LTLIBRARIES = libmisc.la
+
+-libmisc_a_SOURCES = \
++libmisc_la_SOURCES = \
+ addgrps.c \
+ age.c \
+ audit_help.c \
+diff -up shadow-4.6/lib/nss.c.libsubid_nsswitch_support shadow-4.6/lib/nss.c
+--- shadow-4.6/lib/nss.c.libsubid_nsswitch_support 2021-10-19 13:16:21.989493315 +0200
++++ shadow-4.6/lib/nss.c 2021-10-19 13:16:21.989493315 +0200
+@@ -0,0 +1,157 @@
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "prototypes.h"
++#include "../libsubid/subid.h"
++
++#define NSSWITCH "/etc/nsswitch.conf"
++
++// NSS plugin handling for subids
++// If nsswitch has a line like
++// subid: sssd
++// then sssd will be consulted for subids. Unlike normal NSS dbs,
++// only one db is supported at a time. That's open to debate, but
++// the subids are a pretty limited resource, and local files seem
++// bound to step on any other allocations leading to insecure
++// conditions.
++static atomic_flag nss_init_started;
++static atomic_bool nss_init_completed;
++
++static struct subid_nss_ops *subid_nss;
++
++bool nss_is_initialized() {
++ return atomic_load(&nss_init_completed);
++}
++
++void nss_exit() {
++ if (nss_is_initialized() && subid_nss) {
++ dlclose(subid_nss->handle);
++ free(subid_nss);
++ subid_nss = NULL;
++ }
++}
++
++// nsswitch_path is an argument only to support testing.
++void nss_init(char *nsswitch_path) {
++ FILE *nssfp = NULL;
++ char *line = NULL, *p, *token, *saveptr;
++ size_t len = 0;
++
++ if (atomic_flag_test_and_set(&nss_init_started)) {
++ // Another thread has started nss_init, wait for it to complete
++ while (!atomic_load(&nss_init_completed))
++ usleep(100);
++ return;
++ }
++
++ if (!nsswitch_path)
++ nsswitch_path = NSSWITCH;
++
++ // read nsswitch.conf to check for a line like:
++ // subid: files
++ nssfp = fopen(nsswitch_path, "r");
++ if (!nssfp) {
++ fprintf(stderr, "Failed opening %s: %m", nsswitch_path);
++ atomic_store(&nss_init_completed, true);
++ return;
++ }
++ while ((getline(&line, &len, nssfp)) != -1) {
++ if (line[0] == '\0' || line[0] == '#')
++ continue;
++ if (strlen(line) < 8)
++ continue;
++ if (strncasecmp(line, "subid:", 6) != 0)
++ continue;
++ p = &line[6];
++ while ((*p) && isspace(*p))
++ p++;
++ if (!*p)
++ continue;
++ for (token = strtok_r(p, " \n\t", &saveptr);
++ token;
++ token = strtok_r(NULL, " \n\t", &saveptr)) {
++ char libname[65];
++ void *h;
++ if (strcmp(token, "files") == 0) {
++ subid_nss = NULL;
++ goto done;
++ }
++ if (strlen(token) > 50) {
++ fprintf(stderr, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
++ fprintf(stderr, "Using files\n");
++ subid_nss = NULL;
++ goto done;
++ }
++ snprintf(libname, 64, "libsubid_%s.so", token);
++ h = dlopen(libname, RTLD_LAZY);
++ if (!h) {
++ fprintf(stderr, "Error opening %s: %s\n", libname, dlerror());
++ fprintf(stderr, "Using files\n");
++ subid_nss = NULL;
++ goto done;
++ }
++ subid_nss = malloc(sizeof(*subid_nss));
++ if (!subid_nss) {
++ dlclose(h);
++ goto done;
++ }
++ subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
++ if (!subid_nss->has_range) {
++ fprintf(stderr, "%s did not provide @has_range@\n", libname);
++ dlclose(h);
++ free(subid_nss);
++ subid_nss = NULL;
++ goto done;
++ }
++ subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
++ if (!subid_nss->list_owner_ranges) {
++ fprintf(stderr, "%s did not provide @list_owner_ranges@\n", libname);
++ dlclose(h);
++ free(subid_nss);
++ subid_nss = NULL;
++ goto done;
++ }
++ subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range");
++ if (!subid_nss->has_any_range) {
++ fprintf(stderr, "%s did not provide @has_any_range@\n", libname);
++ dlclose(h);
++ free(subid_nss);
++ subid_nss = NULL;
++ goto done;
++ }
++ subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
++ if (!subid_nss->find_subid_owners) {
++ fprintf(stderr, "%s did not provide @find_subid_owners@\n", libname);
++ dlclose(h);
++ free(subid_nss);
++ subid_nss = NULL;
++ goto done;
++ }
++ subid_nss->handle = h;
++ goto done;
++ }
++ fprintf(stderr, "No usable subid NSS module found, using files\n");
++ // subid_nss has to be null here, but to ease reviews:
++ free(subid_nss);
++ subid_nss = NULL;
++ goto done;
++ }
++
++done:
++ atomic_store(&nss_init_completed, true);
++ free(line);
++ if (nssfp) {
++ atexit(nss_exit);
++ fclose(nssfp);
++ }
++}
++
++struct subid_nss_ops *get_subid_nss_handle() {
++ nss_init(NULL);
++ return subid_nss;
++}
+diff -up shadow-4.6/lib/prototypes.h.libsubid_nsswitch_support shadow-4.6/lib/prototypes.h
+--- shadow-4.6/lib/prototypes.h.libsubid_nsswitch_support 2021-10-19 13:16:21.961492869 +0200
++++ shadow-4.6/lib/prototypes.h 2021-10-19 13:16:21.989493315 +0200
+@@ -263,6 +263,75 @@ extern void motd (void);
+ /* myname.c */
+ extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void);
+
++/* nss.c */
++#include
++extern void nss_init(char *nsswitch_path);
++extern bool nss_is_initialized();
++
++struct subid_nss_ops {
++ /*
++ * nss_has_any_range: does a user own any subid range
++ *
++ * @owner: username
++ * @idtype: subuid or subgid
++ * @result: true if a subid allocation was found for @owner
++ *
++ * returns success if the module was able to determine an answer (true or false),
++ * else an error status.
++ */
++ enum subid_status (*has_any_range)(const char *owner, enum subid_type idtype, bool *result);
++
++ /*
++ * nss_has_range: does a user own a given subid range
++ *
++ * @owner: username
++ * @start: first subid in queried range
++ * @count: number of subids in queried range
++ * @idtype: subuid or subgid
++ * @result: true if @owner has been allocated the subid range.
++ *
++ * returns success if the module was able to determine an answer (true or false),
++ * else an error status.
++ */
++ enum subid_status (*has_range)(const char *owner, unsigned long start, unsigned long count, enum subid_type idtype, bool *result);
++
++ /*
++ * nss_list_owner_ranges: list the subid ranges delegated to a user.
++ *
++ * @owner - string representing username being queried
++ * @id_type - subuid or subgid
++ * @ranges - pointer to an array of struct subordinate_range pointers, or
++ * NULL. The returned array of struct subordinate_range and its
++ * members must be freed by the caller.
++ * @count - pointer to an integer into which the number of returned ranges
++ * is written.
++
++ * returns success if the module was able to determine an answer,
++ * else an error status.
++ */
++ enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges, int *count);
++
++ /*
++ * nss_find_subid_owners: find uids who own a given subuid or subgid.
++ *
++ * @id - the delegated id (subuid or subgid) being queried
++ * @id_type - subuid or subgid
++ * @uids - pointer to an array of uids which will be allocated by
++ * nss_find_subid_owners()
++ * @count - number of uids found
++ *
++ * returns success if the module was able to determine an answer,
++ * else an error status.
++ */
++ enum subid_status (*find_subid_owners)(unsigned long id, enum subid_type id_type, uid_t **uids, int *count);
++
++ /* The dlsym handle to close */
++ void *handle;
++};
++
++extern struct subid_nss_ops *get_subid_nss_handle();
++
++
+ /* pam_pass_non_interactive.c */
+ #ifdef USE_PAM
+ extern int do_pam_passwd_non_interactive (const char *pam_service,
+diff -up shadow-4.6/libsubid/api.c.libsubid_nsswitch_support shadow-4.6/libsubid/api.c
+--- shadow-4.6/libsubid/api.c.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/libsubid/api.c 2021-10-19 13:16:21.991493347 +0200
+@@ -36,134 +36,50 @@
+ #include
+ #include "subordinateio.h"
+ #include "idmapping.h"
+-#include "api.h"
++#include "subid.h"
+
+-static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type)
++static
++int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
+ {
+- struct subordinate_range **ranges = NULL;
+-
+- switch (id_type) {
+- case ID_TYPE_UID:
+- if (!sub_uid_open(O_RDONLY)) {
+- return NULL;
+- }
+- break;
+- case ID_TYPE_GID:
+- if (!sub_gid_open(O_RDONLY)) {
+- return NULL;
+- }
+- break;
+- default:
+- return NULL;
+- }
+-
+- ranges = list_owner_ranges(owner, id_type);
+-
+- if (id_type == ID_TYPE_UID)
+- sub_uid_close();
+- else
+- sub_gid_close();
+-
+- return ranges;
++ return list_owner_ranges(owner, id_type, ranges);
+ }
+
+-struct subordinate_range **get_subuid_ranges(const char *owner)
++int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges)
+ {
+- return get_subid_ranges(owner, ID_TYPE_UID);
++ return get_subid_ranges(owner, ID_TYPE_UID, ranges);
+ }
+
+-struct subordinate_range **get_subgid_ranges(const char *owner)
++int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges)
+ {
+- return get_subid_ranges(owner, ID_TYPE_GID);
++ return get_subid_ranges(owner, ID_TYPE_GID, ranges);
+ }
+
+-void subid_free_ranges(struct subordinate_range **ranges)
++void subid_free_ranges(struct subordinate_range **ranges, int count)
+ {
+- return free_subordinate_ranges(ranges);
++ return free_subordinate_ranges(ranges, count);
+ }
+
+-int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type)
++static
++int get_subid_owner(unsigned long id, enum subid_type id_type, uid_t **owner)
+ {
+- int ret = -1;
+-
+- switch (id_type) {
+- case ID_TYPE_UID:
+- if (!sub_uid_open(O_RDONLY)) {
+- return -1;
+- }
+- break;
+- case ID_TYPE_GID:
+- if (!sub_gid_open(O_RDONLY)) {
+- return -1;
+- }
+- break;
+- default:
+- return -1;
+- }
+-
+- ret = find_subid_owners(id, owner, id_type);
+-
+- if (id_type == ID_TYPE_UID)
+- sub_uid_close();
+- else
+- sub_gid_close();
+-
+- return ret;
++ return find_subid_owners(id, id_type, owner);
+ }
+
+ int get_subuid_owners(uid_t uid, uid_t **owner)
+ {
+- return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID);
++ return get_subid_owner((unsigned long)uid, ID_TYPE_UID, owner);
+ }
+
+ int get_subgid_owners(gid_t gid, uid_t **owner)
+ {
+- return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID);
++ return get_subid_owner((unsigned long)gid, ID_TYPE_GID, owner);
+ }
+
++static
+ bool grant_subid_range(struct subordinate_range *range, bool reuse,
+ enum subid_type id_type)
+ {
+- bool ret;
+-
+- switch (id_type) {
+- case ID_TYPE_UID:
+- if (!sub_uid_lock()) {
+- printf("Failed loging subuids (errno %d)\n", errno);
+- return false;
+- }
+- if (!sub_uid_open(O_CREAT | O_RDWR)) {
+- printf("Failed opening subuids (errno %d)\n", errno);
+- sub_uid_unlock();
+- return false;
+- }
+- break;
+- case ID_TYPE_GID:
+- if (!sub_gid_lock()) {
+- printf("Failed loging subgids (errno %d)\n", errno);
+- return false;
+- }
+- if (!sub_gid_open(O_CREAT | O_RDWR)) {
+- printf("Failed opening subgids (errno %d)\n", errno);
+- sub_gid_unlock();
+- return false;
+- }
+- break;
+- default:
+- return false;
+- }
+-
+- ret = new_subid_range(range, id_type, reuse);
+-
+- if (id_type == ID_TYPE_UID) {
+- sub_uid_close();
+- sub_uid_unlock();
+- } else {
+- sub_gid_close();
+- sub_gid_unlock();
+- }
+-
+- return ret;
++ return new_subid_range(range, id_type, reuse);
+ }
+
+ bool grant_subuid_range(struct subordinate_range *range, bool reuse)
+@@ -176,56 +92,18 @@ bool grant_subgid_range(struct subordina
+ return grant_subid_range(range, reuse, ID_TYPE_GID);
+ }
+
+-bool free_subid_range(struct subordinate_range *range, enum subid_type id_type)
++static
++bool ungrant_subid_range(struct subordinate_range *range, enum subid_type id_type)
+ {
+- bool ret;
+-
+- switch (id_type) {
+- case ID_TYPE_UID:
+- if (!sub_uid_lock()) {
+- printf("Failed loging subuids (errno %d)\n", errno);
+- return false;
+- }
+- if (!sub_uid_open(O_CREAT | O_RDWR)) {
+- printf("Failed opening subuids (errno %d)\n", errno);
+- sub_uid_unlock();
+- return false;
+- }
+- break;
+- case ID_TYPE_GID:
+- if (!sub_gid_lock()) {
+- printf("Failed loging subgids (errno %d)\n", errno);
+- return false;
+- }
+- if (!sub_gid_open(O_CREAT | O_RDWR)) {
+- printf("Failed opening subgids (errno %d)\n", errno);
+- sub_gid_unlock();
+- return false;
+- }
+- break;
+- default:
+- return false;
+- }
+-
+- ret = release_subid_range(range, id_type);
+-
+- if (id_type == ID_TYPE_UID) {
+- sub_uid_close();
+- sub_uid_unlock();
+- } else {
+- sub_gid_close();
+- sub_gid_unlock();
+- }
+-
+- return ret;
++ return release_subid_range(range, id_type);
+ }
+
+-bool free_subuid_range(struct subordinate_range *range)
++bool ungrant_subuid_range(struct subordinate_range *range)
+ {
+- return free_subid_range(range, ID_TYPE_UID);
++ return ungrant_subid_range(range, ID_TYPE_UID);
+ }
+
+-bool free_subgid_range(struct subordinate_range *range)
++bool ungrant_subgid_range(struct subordinate_range *range)
+ {
+- return free_subid_range(range, ID_TYPE_GID);
++ return ungrant_subid_range(range, ID_TYPE_GID);
+ }
+diff -up shadow-4.6/libsubid/api.h.libsubid_nsswitch_support shadow-4.6/libsubid/api.h
+diff -up shadow-4.6/libsubid/Makefile.am.libsubid_nsswitch_support shadow-4.6/libsubid/Makefile.am
+--- shadow-4.6/libsubid/Makefile.am.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/libsubid/Makefile.am 2021-10-19 13:16:21.989493315 +0200
+@@ -12,12 +12,14 @@ MISCLIBS = \
+ $(LIBSKEY) \
+ $(LIBMD) \
+ $(LIBCRYPT) \
++ $(LIBACL) \
++ $(LIBATTR) \
+ $(LIBTCB)
+
+ libsubid_la_LIBADD = \
+ $(top_srcdir)/lib/libshadow.la \
+- $(MISCLIBS) \
+- $(top_srcdir)/libmisc/libmisc.a
++ $(top_srcdir)/libmisc/libmisc.la \
++ $(MISCLIBS) -ldl
+
+ AM_CPPFLAGS = \
+ -I${top_srcdir}/lib \
+diff -up shadow-4.6/libsubid/subid.h.libsubid_nsswitch_support shadow-4.6/libsubid/subid.h
+--- shadow-4.6/libsubid/subid.h.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/libsubid/subid.h 2021-10-19 13:16:21.991493347 +0200
+@@ -1,4 +1,5 @@
+ #include
++#include
+
+ #ifndef SUBID_RANGE_DEFINED
+ #define SUBID_RANGE_DEFINED 1
+@@ -13,5 +14,117 @@ enum subid_type {
+ ID_TYPE_GID = 2
+ };
+
++enum subid_status {
++ SUBID_STATUS_SUCCESS = 0,
++ SUBID_STATUS_UNKNOWN_USER = 1,
++ SUBID_STATUS_ERROR_CONN = 2,
++ SUBID_STATUS_ERROR = 3,
++};
++
++/*
++ * get_subuid_ranges: return a list of UID ranges for a user
++ *
++ * @owner: username being queried
++ * @ranges: a pointer to a subordinate range ** in which the result will be
++ * returned.
++ *
++ * returns: number of ranges found, ir < 0 on error.
++ */
++int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges);
++
++/*
++ * get_subgid_ranges: return a list of GID ranges for a user
++ *
++ * @owner: username being queried
++ * @ranges: a pointer to a subordinate range ** in which the result will be
++ * returned.
++ *
++ * returns: number of ranges found, ir < 0 on error.
++ */
++int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
++
++/*
++ * subid_free_ranges: free an array of subordinate_ranges returned by either
++ * get_subuid_ranges() or get_subgid_ranges().
++ *
++ * @ranges: the ranges to free
++ * @count: the number of ranges in @ranges
++ */
++void subid_free_ranges(struct subordinate_range **ranges, int count);
++
++/*
++ * get_subuid_owners: return a list of uids to which the given uid has been
++ * delegated.
++ *
++ * @uid: The subuid being queried
++ * @owners: a pointer to an array of uids into which the results are placed.
++ * The returned array must be freed by the caller.
++ *
++ * Returns the number of uids returned, or < 0 on error.
++ */
++int get_subuid_owners(uid_t uid, uid_t **owner);
++
++/*
++ * get_subgid_owners: return a list of uids to which the given gid has been
++ * delegated.
++ *
++ * @uid: The subgid being queried
++ * @owners: a pointer to an array of uids into which the results are placed.
++ * The returned array must be freed by the caller.
++ *
++ * Returns the number of uids returned, or < 0 on error.
++ */
++int get_subgid_owners(gid_t gid, uid_t **owner);
++
++/*
++ * grant_subuid_range: assign a subuid range to a user
++ *
++ * @range: pointer to a struct subordinate_range detailing the UID range
++ * to allocate. ->owner must be the username, and ->count must be
++ * filled in. ->start is ignored, and will contain the start
++ * of the newly allocated range, upon success.
++ *
++ * Returns true if the delegation succeeded, false otherwise. If true,
++ * then the range from (range->start, range->start + range->count) will
++ * be delegated to range->owner.
++ */
++bool grant_subuid_range(struct subordinate_range *range, bool reuse);
++
++/*
++ * grant_subsid_range: assign a subgid range to a user
++ *
++ * @range: pointer to a struct subordinate_range detailing the GID range
++ * to allocate. ->owner must be the username, and ->count must be
++ * filled in. ->start is ignored, and will contain the start
++ * of the newly allocated range, upon success.
++ *
++ * Returns true if the delegation succeeded, false otherwise. If true,
++ * then the range from (range->start, range->start + range->count) will
++ * be delegated to range->owner.
++ */
++bool grant_subgid_range(struct subordinate_range *range, bool reuse);
++
++/*
++ * ungrant_subuid_range: remove a subuid allocation.
++ *
++ * @range: pointer to a struct subordinate_range detailing the UID allocation
++ * to remove.
++ *
++ * Returns true if successful, false if it failed, for instance if the
++ * delegation did not exist.
++ */
++bool ungrant_subuid_range(struct subordinate_range *range);
++
++/*
++ * ungrant_subuid_range: remove a subgid allocation.
++ *
++ * @range: pointer to a struct subordinate_range detailing the GID allocation
++ * to remove.
++ *
++ * Returns true if successful, false if it failed, for instance if the
++ * delegation did not exist.
++ */
++bool ungrant_subgid_range(struct subordinate_range *range);
++
+ #define SUBID_NFIELDS 3
+ #endif
+diff -up shadow-4.6/lib/subordinateio.c.libsubid_nsswitch_support shadow-4.6/lib/subordinateio.c
+--- shadow-4.6/lib/subordinateio.c.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/lib/subordinateio.c 2021-10-19 13:16:21.989493315 +0200
+@@ -14,6 +14,7 @@
+ #include
+ #include
+ #include
++#include
+
+ /*
+ * subordinate_dup: create a duplicate range
+@@ -316,17 +317,17 @@ static bool append_range(struct subordin
+ {
+ struct subordinate_range *tmp;
+ if (!*ranges) {
+- *ranges = malloc(2 * sizeof(struct subordinate_range **));
++ *ranges = malloc(sizeof(struct subordinate_range *));
+ if (!*ranges)
+ return false;
+ } else {
+ struct subordinate_range **new;
+- new = realloc(*ranges, (n + 2) * (sizeof(struct subordinate_range **)));
++ new = realloc(*ranges, (n + 1) * (sizeof(struct subordinate_range *)));
+ if (!new)
+ return false;
+ *ranges = new;
+ }
+- (*ranges)[n] = (*ranges)[n+1] = NULL;
++ (*ranges)[n] = NULL;
+ tmp = subordinate_dup(new);
+ if (!tmp)
+ return false;
+@@ -334,13 +335,13 @@ static bool append_range(struct subordin
+ return true;
+ }
+
+-void free_subordinate_ranges(struct subordinate_range **ranges)
++void free_subordinate_ranges(struct subordinate_range **ranges, int count)
+ {
+ int i;
+
+ if (!ranges)
+ return;
+- for (i = 0; ranges[i]; i++)
++ for (i = 0; i < count; i++)
+ subordinate_free(ranges[i]);
+ free(ranges);
+ }
+@@ -607,21 +608,46 @@ int sub_uid_open (int mode)
+
+ bool sub_uid_assigned(const char *owner)
+ {
++ struct subid_nss_ops *h;
++ bool found;
++ enum subid_status status;
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->has_any_range(owner, ID_TYPE_UID, &found);
++ if (status == SUBID_STATUS_SUCCESS && found)
++ return true;
++ return false;
++ }
++
+ return range_exists (&subordinate_uid_db, owner);
+ }
+
+ bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
+ {
++ struct subid_nss_ops *h;
++ bool found;
++ enum subid_status status;
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->has_range(owner, start, count, ID_TYPE_UID, &found);
++ if (status == SUBID_STATUS_SUCCESS && found)
++ return true;
++ return false;
++ }
+ return have_range (&subordinate_uid_db, owner, start, count);
+ }
+
+ int sub_uid_add (const char *owner, uid_t start, unsigned long count)
+ {
++ if (get_subid_nss_handle())
++ return -EOPNOTSUPP;
+ return add_range (&subordinate_uid_db, owner, start, count);
+ }
+
+ int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
+ {
++ if (get_subid_nss_handle())
++ return -EOPNOTSUPP;
+ return remove_range (&subordinate_uid_db, owner, start, count);
+ }
+
+@@ -689,21 +715,45 @@ int sub_gid_open (int mode)
+
+ bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
+ {
++ struct subid_nss_ops *h;
++ bool found;
++ enum subid_status status;
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->has_range(owner, start, count, ID_TYPE_GID, &found);
++ if (status == SUBID_STATUS_SUCCESS && found)
++ return true;
++ return false;
++ }
+ return have_range(&subordinate_gid_db, owner, start, count);
+ }
+
+ bool sub_gid_assigned(const char *owner)
+ {
++ struct subid_nss_ops *h;
++ bool found;
++ enum subid_status status;
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->has_any_range(owner, ID_TYPE_GID, &found);
++ if (status == SUBID_STATUS_SUCCESS && found)
++ return true;
++ return false;
++ }
+ return range_exists (&subordinate_gid_db, owner);
+ }
+
+ int sub_gid_add (const char *owner, gid_t start, unsigned long count)
+ {
++ if (get_subid_nss_handle())
++ return -EOPNOTSUPP;
+ return add_range (&subordinate_gid_db, owner, start, count);
+ }
+
+ int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
+ {
++ if (get_subid_nss_handle())
++ return -EOPNOTSUPP;
+ return remove_range (&subordinate_gid_db, owner, start, count);
+ }
+
+@@ -725,42 +775,78 @@ gid_t sub_gid_find_free_range(gid_t min,
+ }
+
+ /*
+- struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
++ * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
+ *
+ * @owner: username
+ * @id_type: UID or GUID
++ * @ranges: pointer to array of ranges into which results will be placed.
+ *
+- * Returns the subuid or subgid ranges which are owned by the specified
++ * Fills in the subuid or subgid ranges which are owned by the specified
+ * user. Username may be a username or a string representation of a
+ * UID number. If id_type is UID, then subuids are returned, else
+- * subgids are returned. If there is an error, < 0 is returned.
++ * subgids are given.
++
++ * Returns the number of ranges found, or < 0 on error.
+ *
+ * The caller must free the subordinate range list.
+ */
+-struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
++int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges)
+ {
+ // TODO - need to handle owner being either uid or username
+- const struct subordinate_range *range;
+ struct subordinate_range **ranges = NULL;
++ const struct subordinate_range *range;
+ struct commonio_db *db;
+- int size = 0;
++ enum subid_status status;
++ int count = 0;
++ struct subid_nss_ops *h;
++
++ *in_ranges = NULL;
++
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->list_owner_ranges(owner, id_type, in_ranges, &count);
++ if (status == SUBID_STATUS_SUCCESS)
++ return count;
++ return -1;
++ }
+
+- if (id_type == ID_TYPE_UID)
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_open(O_RDONLY)) {
++ return -1;
++ }
+ db = &subordinate_uid_db;
+- else
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_open(O_RDONLY)) {
++ return -1;
++ }
+ db = &subordinate_gid_db;
++ break;
++ default:
++ return -1;
++ }
+
+ commonio_rewind(db);
+ while ((range = commonio_next(db)) != NULL) {
+ if (0 == strcmp(range->owner, owner)) {
+- if (!append_range(&ranges, range, size++)) {
+- free_subordinate_ranges(ranges);
+- return NULL;
++ if (!append_range(&ranges, range, count++)) {
++ free_subordinate_ranges(ranges, count-1);
++ ranges = NULL;
++ count = -1;
++ goto out;
+ }
+ }
+ }
+
+- return ranges;
++out:
++ if (id_type == ID_TYPE_UID)
++ sub_uid_close();
++ else
++ sub_gid_close();
++
++ *in_ranges = ranges;
++ return count;
+ }
+
+ static bool all_digits(const char *str)
+@@ -813,17 +899,41 @@ static int append_uids(uid_t **uids, con
+ return n+1;
+ }
+
+-int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type)
++int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids)
+ {
+ const struct subordinate_range *range;
++ struct subid_nss_ops *h;
++ enum subid_status status;
+ struct commonio_db *db;
+ int n = 0;
+
+- *uids = NULL;
+- if (id_type == ID_TYPE_UID)
++ h = get_subid_nss_handle();
++ if (h) {
++ status = h->find_subid_owners(id, id_type, uids, &n);
++ // Several ways we could handle the error cases here.
++ if (status != SUBID_STATUS_SUCCESS)
++ return -1;
++ return n;
++ }
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_open(O_RDONLY)) {
++ return -1;
++ }
+ db = &subordinate_uid_db;
+- else
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_open(O_RDONLY)) {
++ return -1;
++ }
+ db = &subordinate_gid_db;
++ break;
++ default:
++ return -1;
++ }
++
++ *uids = NULL;
+
+ commonio_rewind(db);
+ while ((range = commonio_next(db)) != NULL) {
+@@ -834,6 +944,11 @@ int find_subid_owners(unsigned long id,
+ }
+ }
+
++ if (id_type == ID_TYPE_UID)
++ sub_uid_close();
++ else
++ sub_gid_close();
++
+ return n;
+ }
+
+@@ -841,11 +956,40 @@ bool new_subid_range(struct subordinate_
+ {
+ struct commonio_db *db;
+ const struct subordinate_range *r;
++ bool ret;
+
+- if (id_type == ID_TYPE_UID)
++ if (get_subid_nss_handle())
++ return false;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_lock()) {
++ printf("Failed loging subuids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_uid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subuids (errno %d)\n", errno);
++ sub_uid_unlock();
++ return false;
++ }
+ db = &subordinate_uid_db;
+- else
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_lock()) {
++ printf("Failed loging subgids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_gid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subgids (errno %d)\n", errno);
++ sub_gid_unlock();
++ return false;
++ }
+ db = &subordinate_gid_db;
++ break;
++ default:
++ return false;
++ }
++
+ commonio_rewind(db);
+ if (reuse) {
+ while ((r = commonio_next(db)) != NULL) {
+@@ -861,20 +1005,74 @@ bool new_subid_range(struct subordinate_
+ }
+
+ range->start = find_free_range(db, range->start, ULONG_MAX, range->count);
+- if (range->start == ULONG_MAX)
+- return false;
+
+- return add_range(db, range->owner, range->start, range->count) == 1;
++ if (range->start == ULONG_MAX) {
++ ret = false;
++ goto out;
++ }
++
++ ret = add_range(db, range->owner, range->start, range->count) == 1;
++
++out:
++ if (id_type == ID_TYPE_UID) {
++ sub_uid_close();
++ sub_uid_unlock();
++ } else {
++ sub_gid_close();
++ sub_gid_unlock();
++ }
++
++ return ret;
+ }
+
+ bool release_subid_range(struct subordinate_range *range, enum subid_type id_type)
+ {
+ struct commonio_db *db;
+- if (id_type == ID_TYPE_UID)
++ bool ret;
++
++ if (get_subid_nss_handle())
++ return false;
++
++ switch (id_type) {
++ case ID_TYPE_UID:
++ if (!sub_uid_lock()) {
++ printf("Failed loging subuids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_uid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subuids (errno %d)\n", errno);
++ sub_uid_unlock();
++ return false;
++ }
+ db = &subordinate_uid_db;
+- else
++ break;
++ case ID_TYPE_GID:
++ if (!sub_gid_lock()) {
++ printf("Failed loging subgids (errno %d)\n", errno);
++ return false;
++ }
++ if (!sub_gid_open(O_CREAT | O_RDWR)) {
++ printf("Failed opening subgids (errno %d)\n", errno);
++ sub_gid_unlock();
++ return false;
++ }
+ db = &subordinate_gid_db;
+- return remove_range(db, range->owner, range->start, range->count) == 1;
++ break;
++ default:
++ return false;
++ }
++
++ ret = remove_range(db, range->owner, range->start, range->count) == 1;
++
++ if (id_type == ID_TYPE_UID) {
++ sub_uid_close();
++ sub_uid_unlock();
++ } else {
++ sub_gid_close();
++ sub_gid_unlock();
++ }
++
++ return ret;
+ }
+
+ #else /* !ENABLE_SUBIDS */
+diff -up shadow-4.6/lib/subordinateio.h.libsubid_nsswitch_support shadow-4.6/lib/subordinateio.h
+--- shadow-4.6/lib/subordinateio.h.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/lib/subordinateio.h 2021-10-19 13:16:21.989493315 +0200
+@@ -25,11 +25,11 @@ extern int sub_uid_unlock (void);
+ extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
+ extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
+ extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
+-extern struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type);
++extern int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges);
+ extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse);
+ extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type);
+-extern int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type);
+-extern void free_subordinate_ranges(struct subordinate_range **ranges);
++extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids);
++extern void free_subordinate_ranges(struct subordinate_range **ranges, int count);
+
+ extern int sub_gid_close(void);
+ extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
+diff -up shadow-4.6/src/check_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/check_subid_range.c
+--- shadow-4.6/src/check_subid_range.c.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/src/check_subid_range.c 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,48 @@
++// This program is for testing purposes only.
++// usage is "[program] owner [u|g] start count
++// Exits 0 if owner has subid range starting start, of size count
++// Exits 1 otherwise.
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "defines.h"
++#include "prototypes.h"
++#include "subordinateio.h"
++#include "idmapping.h"
++
++const char *Prog;
++
++int main(int argc, char **argv)
++{
++ char *owner;
++ unsigned long start, count;
++ bool check_uids;
++ Prog = Basename (argv[0]);
++
++ if (argc != 5)
++ exit(1);
++
++ owner = argv[1];
++ check_uids = argv[2][0] == 'u';
++ start = strtoul(argv[3], NULL, 10);
++ if (start == ULONG_MAX && errno == ERANGE)
++ exit(1);
++ count = strtoul(argv[4], NULL, 10);
++ if (count == ULONG_MAX && errno == ERANGE)
++ exit(1);
++ if (check_uids) {
++ if (have_sub_uids(owner, start, count))
++ exit(0);
++ exit(1);
++ }
++ if (have_sub_gids(owner, start, count))
++ exit(0);
++ exit(1);
++}
+diff -up shadow-4.6/src/free_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/free_subid_range.c
+--- shadow-4.6/src/free_subid_range.c.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/src/free_subid_range.c 2021-10-19 13:16:21.991493347 +0200
+@@ -1,6 +1,6 @@
+ #include
+ #include
+-#include "api.h"
++#include "subid.h"
+ #include "stdlib.h"
+ #include "prototypes.h"
+
+@@ -37,9 +37,9 @@ int main(int argc, char *argv[])
+ range.start = atoi(argv[1]);
+ range.count = atoi(argv[2]);
+ if (group)
+- ok = free_subgid_range(&range);
++ ok = ungrant_subgid_range(&range);
+ else
+- ok = free_subuid_range(&range);
++ ok = ungrant_subuid_range(&range);
+
+ if (!ok) {
+ fprintf(stderr, "Failed freeing id range\n");
+diff -up shadow-4.6/src/get_subid_owners.c.libsubid_nsswitch_support shadow-4.6/src/get_subid_owners.c
+--- shadow-4.6/src/get_subid_owners.c.libsubid_nsswitch_support 2021-10-19 13:16:21.986493267 +0200
++++ shadow-4.6/src/get_subid_owners.c 2021-10-19 13:16:21.991493347 +0200
+@@ -1,5 +1,5 @@
+ #include
+-#include "api.h"
++#include "subid.h"
+ #include "stdlib.h"
+ #include "prototypes.h"
+
+diff -up shadow-4.6/src/list_subid_ranges.c.libsubid_nsswitch_support shadow-4.6/src/list_subid_ranges.c
+--- shadow-4.6/src/list_subid_ranges.c.libsubid_nsswitch_support 2021-10-19 13:16:21.987493283 +0200
++++ shadow-4.6/src/list_subid_ranges.c 2021-10-19 13:16:21.991493347 +0200
+@@ -1,5 +1,5 @@
+ #include
+-#include "api.h"
++#include "subid.h"
+ #include "stdlib.h"
+ #include "prototypes.h"
+
+@@ -15,7 +15,7 @@ void usage(void)
+
+ int main(int argc, char *argv[])
+ {
+- int i;
++ int i, count=0;
+ struct subordinate_range **ranges;
+
+ Prog = Basename (argv[0]);
+@@ -23,19 +23,19 @@ int main(int argc, char *argv[])
+ usage();
+ }
+ if (argc == 3 && strcmp(argv[1], "-g") == 0)
+- ranges = get_subgid_ranges(argv[2]);
++ count = get_subgid_ranges(argv[2], &ranges);
+ else if (argc == 2 && strcmp(argv[1], "-h") == 0)
+ usage();
+ else
+- ranges = get_subuid_ranges(argv[1]);
++ count = get_subuid_ranges(argv[1], &ranges);
+ if (!ranges) {
+ fprintf(stderr, "Error fetching ranges\n");
+ exit(1);
+ }
+- for (i = 0; ranges[i]; i++) {
++ for (i = 0; i < count; i++) {
+ printf("%d: %s %lu %lu\n", i, ranges[i]->owner,
+ ranges[i]->start, ranges[i]->count);
+ }
+- subid_free_ranges(ranges);
++ subid_free_ranges(ranges, count);
+ return 0;
+ }
+diff -up shadow-4.6/src/Makefile.am.libsubid_nsswitch_support shadow-4.6/src/Makefile.am
+--- shadow-4.6/src/Makefile.am.libsubid_nsswitch_support 2021-10-19 13:16:21.988493299 +0200
++++ shadow-4.6/src/Makefile.am 2021-10-19 13:20:47.920725652 +0200
+@@ -69,7 +69,7 @@ shadowsgidubins = passwd
+ endif
+
+ LDADD = $(INTLLIBS) \
+- $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/lib/libshadow.la \
+ $(LIBTCB)
+
+@@ -86,17 +86,17 @@ LIBCRYPT_NOPAM = $(LIBCRYPT)
+ endif
+
+ chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
+-newuidmap_LDADD = $(LDADD) $(LIBSELINUX)
+-newgidmap_LDADD = $(LDADD) $(LIBSELINUX)
++newuidmap_LDADD = $(LDADD) $(LIBSELINUX) -ldl
++newgidmap_LDADD = $(LDADD) $(LIBSELINUX) -ldl
+ chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
+ chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT)
+ chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
+ chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT)
+ gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT)
+-groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
+-groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
++groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
++groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
+ groupmems_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX)
+-groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
++groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
+ grpck_LDADD = $(LDADD) $(LIBSELINUX)
+ grpconv_LDADD = $(LDADD) $(LIBSELINUX)
+ grpunconv_LDADD = $(LDADD) $(LIBSELINUX)
+@@ -106,7 +106,7 @@ login_SOURCES = \
+ login_nopam.c
+ login_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
+ newgrp_LDADD = $(LDADD) $(LIBAUDIT) $(LIBCRYPT)
+-newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT)
++newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) -ldl
+ nologin_LDADD =
+ passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM)
+ pwck_LDADD = $(LDADD) $(LIBSELINUX)
+@@ -117,9 +117,9 @@ su_SOURCES = \
+ suauth.c
+ su_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
+ sulogin_LDADD = $(LDADD) $(LIBCRYPT)
+-useradd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR)
+-userdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE)
+-usermod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR)
++useradd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) -ldl
++userdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) -ldl
++usermod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) -ldl
+ vipw_LDADD = $(LDADD) $(LIBSELINUX)
+
+ install-am: all-am
+@@ -143,7 +143,8 @@ if ENABLE_SUBIDS
+ noinst_PROGRAMS += list_subid_ranges \
+ get_subid_owners \
+ new_subid_range \
+- free_subid_range
++ free_subid_range \
++ check_subid_range
+
+ MISCLIBS = \
+ $(LIBAUDIT) \
+@@ -158,9 +159,9 @@ MISCLIBS = \
+
+ list_subid_ranges_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+- $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/libsubid/libsubid.la \
+- $(MISCLIBS)
++ $(MISCLIBS) -ldl
+
+ list_subid_ranges_CPPFLAGS = \
+ -I$(top_srcdir)/lib \
+@@ -169,9 +170,9 @@ list_subid_ranges_CPPFLAGS = \
+
+ get_subid_owners_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+- $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/libsubid/libsubid.la \
+- $(MISCLIBS)
++ $(MISCLIBS) -ldl
+
+ get_subid_owners_CPPFLAGS = \
+ -I$(top_srcdir)/lib \
+@@ -185,9 +186,9 @@ new_subid_range_CPPFLAGS = \
+
+ new_subid_range_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+- $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/libsubid/libsubid.la \
+- $(MISCLIBS)
++ $(MISCLIBS) -ldl
+
+ free_subid_range_CPPFLAGS = \
+ -I$(top_srcdir)/lib \
+@@ -196,7 +197,16 @@ free_subid_range_CPPFLAGS = \
+
+ free_subid_range_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+- $(top_builddir)/libmisc/libmisc.a \
++ $(top_builddir)/libmisc/libmisc.la \
+ $(top_builddir)/libsubid/libsubid.la \
+- $(MISCLIBS)
++ $(MISCLIBS) -ldl
++
++check_subid_range_CPPFLAGS = \
++ -I$(top_srcdir)/lib \
++ -I$(top_srcdir)/libmisc
++
++check_subid_range_LDADD = \
++ $(top_builddir)/lib/libshadow.la \
++ $(top_builddir)/libmisc/libmisc.la \
++ $(MISCLIBS) -ldl
+ endif
+diff -up shadow-4.6/src/new_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/new_subid_range.c
+--- shadow-4.6/src/new_subid_range.c.libsubid_nsswitch_support 2021-10-19 13:16:21.987493283 +0200
++++ shadow-4.6/src/new_subid_range.c 2021-10-19 13:16:21.991493347 +0200
+@@ -1,6 +1,6 @@
+ #include
+ #include
+-#include "api.h"
++#include "subid.h"
+ #include "stdlib.h"
+ #include "prototypes.h"
+
+diff -up shadow-4.6/tests/libsubid/04_nss/empty.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/empty
+diff -up shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c
+--- shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,146 @@
++#include
++#include
++#include
++#include
++#include
++#include
++
++enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result)
++{
++ if (strcmp(owner, "ubuntu") == 0) {
++ *result = true;
++ return SUBID_STATUS_SUCCESS;
++ }
++ if (strcmp(owner, "error") == 0) {
++ *result = false;
++ return SUBID_STATUS_ERROR;
++ }
++ if (strcmp(owner, "unknown") == 0) {
++ *result = false;
++ return SUBID_STATUS_UNKNOWN_USER;
++ }
++ if (strcmp(owner, "conn") == 0) {
++ *result = false;
++ return SUBID_STATUS_ERROR_CONN;
++ }
++ if (t == ID_TYPE_UID) {
++ *result = strcmp(owner, "user1") == 0;
++ return SUBID_STATUS_SUCCESS;
++ }
++
++ *result = strcmp(owner, "group1") == 0;
++ return SUBID_STATUS_SUCCESS;
++}
++
++enum subid_status shadow_subid_has_range(const char *owner, unsigned long start, unsigned long count, enum subid_type t, bool *result)
++{
++ if (strcmp(owner, "ubuntu") == 0 &&
++ start >= 200000 &&
++ count <= 100000) {
++ *result = true;
++ return SUBID_STATUS_SUCCESS;
++ }
++ *result = false;
++ if (strcmp(owner, "error") == 0)
++ return SUBID_STATUS_ERROR;
++ if (strcmp(owner, "unknown") == 0)
++ return SUBID_STATUS_UNKNOWN_USER;
++ if (strcmp(owner, "conn") == 0)
++ return SUBID_STATUS_ERROR_CONN;
++
++ if (t == ID_TYPE_UID && strcmp(owner, "user1") != 0)
++ return SUBID_STATUS_SUCCESS;
++ if (t == ID_TYPE_GID && strcmp(owner, "group1") != 0)
++ return SUBID_STATUS_SUCCESS;
++
++ if (start < 100000)
++ return SUBID_STATUS_SUCCESS;
++ if (count >= 65536)
++ return SUBID_STATUS_SUCCESS;
++ *result = true;
++ return SUBID_STATUS_SUCCESS;
++}
++
++// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values,
++// to ease manual testing. For automated testing, if you return those values,
++// we'll return 1000 for ubuntu and 1001 otherwise.
++static uid_t getnamuid(const char *name) {
++ struct passwd *pw;
++
++ pw = getpwnam(name);
++ if (pw)
++ return pw->pw_uid;
++
++ // For testing purposes
++ return strcmp(name, "ubuntu") == 0 ? (uid_t)1000 : (uid_t)1001;
++}
++
++static int alloc_uid(uid_t **uids, uid_t id) {
++ *uids = malloc(sizeof(uid_t));
++ if (!*uids)
++ return -1;
++ *uids[0] = id;
++ return 1;
++}
++
++enum subid_status shadow_subid_find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids, int *count)
++{
++ if (id >= 100000 && id < 165536) {
++ *count = alloc_uid(uids, getnamuid("user1"));
++ if (*count == 1)
++ return SUBID_STATUS_SUCCESS;
++ return SUBID_STATUS_ERROR; // out of memory
++ }
++ if (id >= 200000 && id < 300000) {
++ *count = alloc_uid(uids, getnamuid("ubuntu"));
++ if (*count == 1)
++ return SUBID_STATUS_SUCCESS;
++ return SUBID_STATUS_ERROR; // out of memory
++ }
++ *count = 0; // nothing found
++ return SUBID_STATUS_SUCCESS;
++}
++
++enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges, int *count)
++{
++ struct subordinate_range **ranges;
++
++ *count = 0;
++ if (strcmp(owner, "error") == 0)
++ return SUBID_STATUS_ERROR;
++ if (strcmp(owner, "unknown") == 0)
++ return SUBID_STATUS_UNKNOWN_USER;
++ if (strcmp(owner, "conn") == 0)
++ return SUBID_STATUS_ERROR_CONN;
++
++ *ranges = NULL;
++ if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 &&
++ strcmp(owner, "group1") != 0)
++ return SUBID_STATUS_SUCCESS;
++ if (id_type == ID_TYPE_GID && strcmp(owner, "user1") == 0)
++ return SUBID_STATUS_SUCCESS;
++ if (id_type == ID_TYPE_UID && strcmp(owner, "group1") == 0)
++ return SUBID_STATUS_SUCCESS;
++ ranges = (struct subordinate_range **)malloc(sizeof(struct subordinate_range *));
++ if (!*ranges)
++ return SUBID_STATUS_ERROR;
++ ranges[0] = (struct subordinate_range *)malloc(sizeof(struct subordinate_range));
++ if (!ranges[0]) {
++ free(*ranges);
++ *ranges = NULL;
++ return SUBID_STATUS_ERROR;
++ }
++ ranges[0]->owner = strdup(owner);
++ if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) {
++ ranges[0]->start = 100000;
++ ranges[0]->count = 65536;
++ } else {
++ ranges[0]->start = 200000;
++ ranges[0]->count = 100000;
++ }
++
++ *count = 1;
++ *in_ranges = ranges;
++
++ return SUBID_STATUS_SUCCESS;
++}
+diff -up shadow-4.6/tests/libsubid/04_nss/Makefile.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/Makefile
+--- shadow-4.6/tests/libsubid/04_nss/Makefile.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/Makefile 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,12 @@
++all: test_nss libsubid_zzz.so
++
++test_nss: test_nss.c ../../../lib/nss.c
++ gcc -c -I../../../lib/ -I../../.. -o test_nss.o test_nss.c
++ gcc -o test_nss test_nss.o ../../../libmisc/.libs/libmisc.a ../../../lib/.libs/libshadow.a -ldl
++
++libsubid_zzz.so: libsubid_zzz.c
++ gcc -c -I../../../lib/ -I../../.. -I../../../libmisc -I../../../libsubid libsubid_zzz.c
++ gcc -L../../../libsubid -shared -o libsubid_zzz.so libsubid_zzz.o ../../../lib/.libs/libshadow.a -ldl
++
++clean:
++ rm -f *.o *.so test_nss
+diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf
+--- shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,20 @@
++# /etc/nsswitch.conf
++#
++# Example configuration of GNU Name Service Switch functionality.
++# If you have the `glibc-doc-reference' and `info' packages installed, try:
++# `info libc "Name Service Switch"' for information about this file.
++
++passwd: files systemd
++group: files systemd
++shadow: files
++gshadow: files
++
++hosts: files mdns4_minimal [NOTFOUND=return] dns
++networks: files
++
++protocols: db files
++services: db files
++ethers: db files
++rpc: db files
++
++netgroup: nis
+diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf
+--- shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,22 @@
++# /etc/nsswitch.conf
++#
++# Example configuration of GNU Name Service Switch functionality.
++# If you have the `glibc-doc-reference' and `info' packages installed, try:
++# `info libc "Name Service Switch"' for information about this file.
++
++passwd: files systemd
++group: files systemd
++shadow: files
++gshadow: files
++
++hosts: files mdns4_minimal [NOTFOUND=return] dns
++networks: files
++
++protocols: db files
++services: db files
++ethers: db files
++rpc: db files
++
++netgroup: nis
++
++subid: files
+diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf
+--- shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,22 @@
++# /etc/nsswitch.conf
++#
++# Example configuration of GNU Name Service Switch functionality.
++# If you have the `glibc-doc-reference' and `info' packages installed, try:
++# `info libc "Name Service Switch"' for information about this file.
++
++passwd: files systemd
++group: files systemd
++shadow: files
++gshadow: files
++
++hosts: files mdns4_minimal [NOTFOUND=return] dns
++networks: files
++
++protocols: db files
++services: db files
++ethers: db files
++rpc: db files
++
++netgroup: nis
++
++subid: zzz
+diff -up shadow-4.6/tests/libsubid/04_nss/subidnss.test.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/subidnss.test
+--- shadow-4.6/tests/libsubid/04_nss/subidnss.test.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/subidnss.test 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,22 @@
++#!/bin/sh
++
++set -e
++
++cd $(dirname $0)
++
++. ../../common/config.sh
++. ../../common/log.sh
++
++make
++
++export LD_LIBRARY_PATH=.:../../../lib/.libs:$LD_LIBRARY_PATH
++
++./test_nss 1
++./test_nss 2
++./test_nss 3
++
++unshare -Urm ./test_range
++
++log_status "$0" "SUCCESS"
++
++trap '' 0
+diff -up shadow-4.6/tests/libsubid/04_nss/test_nss.c.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/test_nss.c
+--- shadow-4.6/tests/libsubid/04_nss/test_nss.c.libsubid_nsswitch_support 2021-10-19 13:16:21.990493331 +0200
++++ shadow-4.6/tests/libsubid/04_nss/test_nss.c 2021-10-19 13:16:21.990493331 +0200
+@@ -0,0 +1,72 @@
++#include
++#include
++#include
++#include
++#include
++#include
++
++extern bool nss_is_initialized();
++extern struct subid_nss_ops *get_subid_nss_handle();
++
++void test1() {
++ // nsswitch1 has no subid: entry
++ setenv("LD_LIBRARY_PATH", ".", 1);
++ printf("Test with no subid entry\n");
++ nss_init("./nsswitch1.conf");
++ if (!nss_is_initialized() || get_subid_nss_handle())
++ exit(1);
++ // second run should change nothing
++ printf("Test with no subid entry, second run\n");
++ nss_init("./nsswitch1.conf");
++ if (!nss_is_initialized() || get_subid_nss_handle())
++ exit(1);
++}
++
++void test2() {
++ // nsswitch2 has a subid: files entry
++ printf("test with 'files' subid entry\n");
++ nss_init("./nsswitch2.conf");
++ if (!nss_is_initialized() || get_subid_nss_handle())
++ exit(1);
++ // second run should change nothing
++ printf("test with 'files' subid entry, second run\n");
++ nss_init("./nsswitch2.conf");
++ if (!nss_is_initialized() || get_subid_nss_handle())
++ exit(1);
++}
++
++void test3() {
++ // nsswitch3 has a subid: testnss entry
++ printf("test with 'test' subid entry\n");
++ nss_init("./nsswitch3.conf");
++ if (!nss_is_initialized() || !get_subid_nss_handle())
++ exit(1);
++ // second run should change nothing
++ printf("test with 'test' subid entry, second run\n");
++ nss_init("./nsswitch3.conf");
++ if (!nss_is_initialized() || !get_subid_nss_handle())
++ exit(1);
++}
++
++const char *Prog;
++
++int main(int argc, char *argv[])
++{
++ int which;
++
++ Prog = Basename(argv[0]);
++
++ if (argc < 1)
++ exit(1);
++
++ which = atoi(argv[1]);
++ switch(which) {
++ case 1: test1(); break;
++ case 2: test2(); break;
++ case 3: test3(); break;
++ default: exit(1);
++ }
++
++ printf("nss parsing tests done\n");
++ exit(0);
++}
+diff -up shadow-4.6/tests/libsubid/04_nss/test_range.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/test_range
+--- shadow-4.6/tests/libsubid/04_nss/test_range.libsubid_nsswitch_support 2021-10-19 13:16:21.991493347 +0200
++++ shadow-4.6/tests/libsubid/04_nss/test_range 2021-10-19 13:16:21.991493347 +0200
+@@ -0,0 +1,50 @@
++#!/bin/sh
++
++set -x
++
++echo "starting check_range tests"
++
++export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
++mount --bind ./nsswitch3.conf /etc/nsswitch.conf
++cleanup1() {
++ umount /etc/nsswitch.conf
++}
++trap cleanup1 EXIT HUP INT TERM
++../../../src/check_subid_range user1 u 100000 65535
++if [ $? -ne 0 ]; then
++ exit 1
++fi
++../../../src/check_subid_range user2 u 100000 65535
++if [ $? -eq 0 ]; then
++ exit 1
++fi
++../../../src/check_subid_range unknown u 100000 65535
++if [ $? -eq 0 ]; then
++ exit 1
++fi
++../../../src/check_subid_range error u 100000 65535
++if [ $? -eq 0 ]; then
++ exit 1
++fi
++../../../src/check_subid_range user1 u 1000 65535
++if [ $? -eq 0 ]; then
++ exit 1
++fi
++
++umount /etc/nsswitch.conf
++
++mount --bind ./nsswitch1.conf /etc/nsswitch.conf
++mount --bind ./empty /etc/subuid
++
++cleanup2() {
++ umount /etc/subuid
++ umount /etc/nsswitch.conf
++}
++trap cleanup2 EXIT HUP INT TERM
++../../../src/check_subid_range user1 u 100000 65535
++if [ $? -eq 0 ]; then
++ exit 1
++fi
++
++echo "check_range tests complete"
++exit 0
+diff -up shadow-4.6/tests/run_some.libsubid_nsswitch_support shadow-4.6/tests/run_some
+--- shadow-4.6/tests/run_some.libsubid_nsswitch_support 2021-10-19 13:16:21.987493283 +0200
++++ shadow-4.6/tests/run_some 2021-10-19 13:16:21.991493347 +0200
+@@ -123,6 +123,7 @@ run_test ./su/13_su_child_success/su.tes
+ run_test ./libsubid/01_list_ranges/list_ranges.test
+ run_test ./libsubid/02_get_subid_owners/get_subid_owners.test
+ run_test ./libsubid/03_add_remove/add_remove_subids.test
++run_test ./libsubid/04_nss/subidnss.test
+
+ echo
+ echo "$succeeded test(s) passed"
diff --git a/SOURCES/shadow-4.6-libsubid_simplify_ranges_variable.patch b/SOURCES/shadow-4.6-libsubid_simplify_ranges_variable.patch
new file mode 100644
index 0000000..4cd848b
--- /dev/null
+++ b/SOURCES/shadow-4.6-libsubid_simplify_ranges_variable.patch
@@ -0,0 +1,264 @@
+diff -up shadow-4.8.1/configure.ac.libsubid_simplify_ranges_variable shadow-4.8.1/configure.ac
+--- shadow-4.8.1/configure.ac.libsubid_simplify_ranges_variable 2021-05-24 15:02:56.165917066 +0200
++++ shadow-4.8.1/configure.ac 2021-05-24 15:02:56.184917324 +0200
+@@ -1,6 +1,6 @@
+ dnl Process this file with autoconf to produce a configure script.
+ AC_PREREQ([2.69])
+-m4_define([libsubid_abi_major], 2)
++m4_define([libsubid_abi_major], 3)
+ m4_define([libsubid_abi_minor], 0)
+ m4_define([libsubid_abi_micro], 0)
+ m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
+diff -up shadow-4.8.1/lib/prototypes.h.libsubid_simplify_ranges_variable shadow-4.8.1/lib/prototypes.h
+--- shadow-4.8.1/lib/prototypes.h.libsubid_simplify_ranges_variable 2021-05-24 15:02:56.184917324 +0200
++++ shadow-4.8.1/lib/prototypes.h 2021-05-24 16:38:57.610619467 +0200
+@@ -309,16 +309,15 @@ struct subid_nss_ops {
+ *
+ * @owner - string representing username being queried
+ * @id_type - subuid or subgid
+- * @ranges - pointer to an array of struct subordinate_range pointers, or
+- * NULL. The returned array of struct subordinate_range and its
+- * members must be freed by the caller.
++ * @ranges - pointer to an array of struct subid_range, or NULL. The
++ * returned array must be freed by the caller.
+ * @count - pointer to an integer into which the number of returned ranges
+ * is written.
+
+ * returns success if the module was able to determine an answer,
+ * else an error status.
+ */
+- enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges, int *count);
++ enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subid_range **ranges, int *count);
+
+ /*
+ * nss_find_subid_owners: find uids who own a given subuid or subgid.
+diff -up shadow-4.8.1/libsubid/api.c.libsubid_simplify_ranges_variable shadow-4.8.1/libsubid/api.c
+--- shadow-4.8.1/libsubid/api.c.libsubid_simplify_ranges_variable 2021-05-24 15:03:01.467989079 +0200
++++ shadow-4.8.1/libsubid/api.c 2021-05-24 16:42:32.091584531 +0200
+@@ -68,26 +68,21 @@ bool libsubid_init(const char *progname,
+ }
+
+ static
+-int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
++int get_subid_ranges(const char *owner, enum subid_type id_type, struct subid_range **ranges)
+ {
+ return list_owner_ranges(owner, id_type, ranges);
+ }
+
+-int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges)
++int get_subuid_ranges(const char *owner, struct subid_range **ranges)
+ {
+ return get_subid_ranges(owner, ID_TYPE_UID, ranges);
+ }
+
+-int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges)
++int get_subgid_ranges(const char *owner, struct subid_range **ranges)
+ {
+ return get_subid_ranges(owner, ID_TYPE_GID, ranges);
+ }
+
+-void subid_free_ranges(struct subordinate_range **ranges, int count)
+-{
+- return free_subordinate_ranges(ranges, count);
+-}
+-
+ static
+ int get_subid_owner(unsigned long id, enum subid_type id_type, uid_t **owner)
+ {
+diff -up shadow-4.8.1/libsubid/subid.h.libsubid_simplify_ranges_variable shadow-4.8.1/libsubid/subid.h
+--- shadow-4.8.1/libsubid/subid.h.libsubid_simplify_ranges_variable 2021-05-24 15:03:01.468989093 +0200
++++ shadow-4.8.1/libsubid/subid.h 2021-05-24 16:43:49.697657383 +0200
+@@ -3,6 +3,15 @@
+
+ #ifndef SUBID_RANGE_DEFINED
+ #define SUBID_RANGE_DEFINED 1
++
++/* subid_range is just a starting point and size of a range */
++struct subid_range {
++ unsigned long start;
++ unsigned long count;
++};
++
++/* subordinage_range is a subid_range plus an owner, representing
++ * a range in /etc/subuid or /etc/subgid */
+ struct subordinate_range {
+ const char *owner;
+ unsigned long start;
+@@ -41,32 +50,27 @@ bool libsubid_init(const char *progname,
+ * get_subuid_ranges: return a list of UID ranges for a user
+ *
+ * @owner: username being queried
+- * @ranges: a pointer to a subordinate range ** in which the result will be
+- * returned.
++ * @ranges: a pointer to an array of subid_range structs in which the result
++ * will be returned.
++ *
++ * The caller must free(ranges) when done.
+ *
+ * returns: number of ranges found, ir < 0 on error.
+ */
+-int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges);
++int get_subuid_ranges(const char *owner, struct subid_range **ranges);
+
+ /*
+ * get_subgid_ranges: return a list of GID ranges for a user
+ *
+ * @owner: username being queried
+- * @ranges: a pointer to a subordinate range ** in which the result will be
+- * returned.
++ * @ranges: a pointer to an array of subid_range structs in which the result
++ * will be returned.
+ *
+- * returns: number of ranges found, ir < 0 on error.
+- */
+-int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
+-
+-/*
+- * subid_free_ranges: free an array of subordinate_ranges returned by either
+- * get_subuid_ranges() or get_subgid_ranges().
++ * The caller must free(ranges) when done.
+ *
+- * @ranges: the ranges to free
+- * @count: the number of ranges in @ranges
++ * returns: number of ranges found, ir < 0 on error.
+ */
+-void subid_free_ranges(struct subordinate_range **ranges, int count);
++int get_subgid_ranges(const char *owner, struct subid_range **ranges);
+
+ /*
+ * get_subuid_owners: return a list of uids to which the given uid has been
+diff -up shadow-4.8.1/lib/subordinateio.c.libsubid-simplify shadow-4.8.1/lib/subordinateio.c
+--- shadow-4.8.1/lib/subordinateio.c.libsubid-simplify 2021-05-24 17:27:38.721035241 +0200
++++ shadow-4.8.1/lib/subordinateio.c 2021-05-24 17:28:06.481420946 +0200
+@@ -11,6 +11,7 @@
+ #include
+ #include "commonio.h"
+ #include "subordinateio.h"
++#include "../libsubid/subid.h"
+ #include
+ #include
+ #include
+@@ -308,25 +309,21 @@ static bool have_range(struct commonio_d
+ return false;
+ }
+
+-static bool append_range(struct subordinate_range ***ranges, const struct subordinate_range *new, int n)
++static bool append_range(struct subid_range **ranges, const struct subordinate_range *new, int n)
+ {
+- struct subordinate_range *tmp;
+ if (!*ranges) {
+- *ranges = malloc(sizeof(struct subordinate_range *));
++ *ranges = malloc(sizeof(struct subid_range));
+ if (!*ranges)
+ return false;
+ } else {
+- struct subordinate_range **new;
+- new = realloc(*ranges, (n + 1) * (sizeof(struct subordinate_range *)));
+- if (!new)
++ struct subid_range *alloced;
++ alloced = realloc(*ranges, (n + 1) * (sizeof(struct subid_range)));
++ if (!alloced)
+ return false;
+- *ranges = new;
++ *ranges = alloced;
+ }
+- (*ranges)[n] = NULL;
+- tmp = subordinate_dup(new);
+- if (!tmp)
+- return false;
+- (*ranges)[n] = tmp;
++ (*ranges)[n].start = new->start;
++ (*ranges)[n].count = new->count;
+ return true;
+ }
+
+@@ -785,10 +782,10 @@ gid_t sub_gid_find_free_range(gid_t min,
+ *
+ * The caller must free the subordinate range list.
+ */
+-int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges)
++int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **in_ranges)
+ {
+ // TODO - need to handle owner being either uid or username
+- struct subordinate_range **ranges = NULL;
++ struct subid_range *ranges = NULL;
+ const struct subordinate_range *range;
+ struct commonio_db *db;
+ enum subid_status status;
+@@ -826,7 +823,7 @@ int list_owner_ranges(const char *owner,
+ while ((range = commonio_next(db)) != NULL) {
+ if (0 == strcmp(range->owner, owner)) {
+ if (!append_range(&ranges, range, count++)) {
+- free_subordinate_ranges(ranges, count-1);
++ free(ranges);
+ ranges = NULL;
+ count = -1;
+ goto out;
+diff -up shadow-4.8.1/lib/subordinateio.h.libsubid_simplify_ranges_variable shadow-4.8.1/lib/subordinateio.h
+--- shadow-4.8.1/lib/subordinateio.h.libsubid_simplify_ranges_variable 2021-05-24 15:03:01.467989079 +0200
++++ shadow-4.8.1/lib/subordinateio.h 2021-05-24 16:40:56.978269647 +0200
+@@ -25,7 +25,7 @@ extern int sub_uid_unlock (void);
+ extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
+ extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
+ extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
+-extern int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges);
++extern int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **ranges);
+ extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse);
+ extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type);
+ extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids);
+diff -up shadow-4.8.1/src/list_subid_ranges.c.libsubid_simplify_ranges_variable shadow-4.8.1/src/list_subid_ranges.c
+--- shadow-4.8.1/src/list_subid_ranges.c.libsubid_simplify_ranges_variable 2021-05-24 15:03:01.468989093 +0200
++++ shadow-4.8.1/src/list_subid_ranges.c 2021-05-24 16:45:10.884779740 +0200
+@@ -17,27 +17,29 @@ void usage(void)
+ int main(int argc, char *argv[])
+ {
+ int i, count=0;
+- struct subordinate_range **ranges;
++ struct subid_range *ranges;
++ const char *owner;
+
+ Prog = Basename (argv[0]);
+ shadow_logfd = stderr;
+- if (argc < 2) {
++ if (argc < 2)
+ usage();
+- }
+- if (argc == 3 && strcmp(argv[1], "-g") == 0)
+- count = get_subgid_ranges(argv[2], &ranges);
+- else if (argc == 2 && strcmp(argv[1], "-h") == 0)
++ owner = argv[1];
++ if (argc == 3 && strcmp(argv[1], "-g") == 0) {
++ owner = argv[2];
++ count = get_subgid_ranges(owner, &ranges);
++ } else if (argc == 2 && strcmp(argv[1], "-h") == 0) {
+ usage();
+- else
+- count = get_subuid_ranges(argv[1], &ranges);
++ } else {
++ count = get_subuid_ranges(owner, &ranges);
++ }
+ if (!ranges) {
+ fprintf(stderr, "Error fetching ranges\n");
+ exit(1);
+ }
+ for (i = 0; i < count; i++) {
+- printf("%d: %s %lu %lu\n", i, ranges[i]->owner,
+- ranges[i]->start, ranges[i]->count);
++ printf("%d: %s %lu %lu\n", i, owner,
++ ranges[i].start, ranges[i].count);
+ }
+- subid_free_ranges(ranges, count);
+ return 0;
+ }
+diff -up shadow-4.8.1/tests/libsubid/04_nss/libsubid_zzz.c.libsubid_simplify_ranges_variable shadow-4.8.1/tests/libsubid/04_nss/libsubid_zzz.c
+--- shadow-4.8.1/tests/libsubid/04_nss/libsubid_zzz.c.libsubid_simplify_ranges_variable 2021-05-24 15:02:56.166917079 +0200
++++ shadow-4.8.1/tests/libsubid/04_nss/libsubid_zzz.c 2021-05-24 15:03:01.469989106 +0200
+@@ -113,7 +113,7 @@ enum subid_status shadow_subid_list_owne
+ if (strcmp(owner, "conn") == 0)
+ return SUBID_STATUS_ERROR_CONN;
+
+- *ranges = NULL;
++ *in_ranges = NULL;
+ if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 &&
+ strcmp(owner, "group1") != 0)
+ return SUBID_STATUS_SUCCESS;
diff --git a/SOURCES/shadow-4.6-man-mention-nss-in-newuidmap.patch b/SOURCES/shadow-4.6-man-mention-nss-in-newuidmap.patch
new file mode 100644
index 0000000..e26cfa7
--- /dev/null
+++ b/SOURCES/shadow-4.6-man-mention-nss-in-newuidmap.patch
@@ -0,0 +1,44 @@
+From 186b1b7ac1a68d0fcc618a22da1a99232b420911 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Tue, 4 May 2021 14:39:26 -0500
+Subject: [PATCH] manpages: mention NSS in new[ug]idmap manpages
+
+Closes #328
+
+Signed-off-by: Serge Hallyn
+---
+ man/newgidmap.1.xml | 3 ++-
+ man/newuidmap.1.xml | 3 ++-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/man/newgidmap.1.xml b/man/newgidmap.1.xml
+index 71b03e56..76fc1e30 100644
+--- a/man/newgidmap.1.xml
++++ b/man/newgidmap.1.xml
+@@ -88,7 +88,8 @@
+ DESCRIPTION
+
+ The newgidmap sets /proc/[pid]/gid_map based on its
+- command line arguments and the gids allowed in /etc/subgid.
++ command line arguments and the gids allowed (either in /etc/subgid or
++ through the configured NSS subid module).
+ Note that the root user is not exempted from the requirement for a valid
+ /etc/subgid entry.
+
+diff --git a/man/newuidmap.1.xml b/man/newuidmap.1.xml
+index a6f1f085..44eca50a 100644
+--- a/man/newuidmap.1.xml
++++ b/man/newuidmap.1.xml
+@@ -88,7 +88,8 @@
+ DESCRIPTION
+
+ The newuidmap sets /proc/[pid]/uid_map based on its
+- command line arguments and the uids allowed in /etc/subuid.
++ command line arguments and the uids allowed (either in /etc/subuid or
++ through the configured NSS subid module).
+ Note that the root user is not exempted from the requirement for a valid
+ /etc/subuid entry.
+
+--
+2.30.2
+
diff --git a/SOURCES/shadow-4.6-man_clarify_subid_delegation.patch b/SOURCES/shadow-4.6-man_clarify_subid_delegation.patch
new file mode 100644
index 0000000..47d4d46
--- /dev/null
+++ b/SOURCES/shadow-4.6-man_clarify_subid_delegation.patch
@@ -0,0 +1,166 @@
+diff -up shadow-4.6/man/newgidmap.1.xml.man_clarify_subid_delegation shadow-4.6/man/newgidmap.1.xml
+--- shadow-4.6/man/newgidmap.1.xml.man_clarify_subid_delegation 2021-11-03 09:58:34.176484342 +0100
++++ shadow-4.6/man/newgidmap.1.xml 2021-11-03 09:58:34.191484452 +0100
+@@ -80,10 +80,15 @@
+
+ DESCRIPTION
+
+- The newgidmap sets /proc/[pid]/gid_map based on its
+- command line arguments and the gids allowed (either in /etc/subgid or
+- through the configured NSS subid module).
+- Note that the root user is not exempted from the requirement for a valid
++ The newgidmap sets /proc/[pid]/gid_map
++ based on its command line arguments and the gids allowed. Subgid
++ delegation can either be managed via /etc/subgid
++ or through the configured NSS subid module. These options are mutually
++ exclusive.
++
++
++
++ Note that the root group is not exempted from the requirement for a valid
+ /etc/subgid entry.
+
+
+diff -up shadow-4.6/man/newuidmap.1.xml.man_clarify_subid_delegation shadow-4.6/man/newuidmap.1.xml
+--- shadow-4.6/man/newuidmap.1.xml.man_clarify_subid_delegation 2021-11-03 09:58:34.176484342 +0100
++++ shadow-4.6/man/newuidmap.1.xml 2021-11-03 09:58:34.191484452 +0100
+@@ -80,9 +80,14 @@
+
+ DESCRIPTION
+
+- The newuidmap sets /proc/[pid]/uid_map based on its
+- command line arguments and the uids allowed (either in /etc/subuid or
+- through the configured NSS subid module).
++ The newuidmap sets /proc/[pid]/uid_map
++ based on its command line arguments and the uids allowed. Subuid
++ delegation can either be managed via /etc/subuid or
++ through the configured NSS subid module. These options are mutually
++ exclusive.
++
++
++
+ Note that the root user is not exempted from the requirement for a valid
+ /etc/subuid entry.
+
+diff -up shadow-4.6/man/subgid.5.xml.man_clarify_subid_delegation shadow-4.6/man/subgid.5.xml
+--- shadow-4.6/man/subgid.5.xml.man_clarify_subid_delegation 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/man/subgid.5.xml 2021-11-03 09:59:55.752084920 +0100
+@@ -32,6 +32,18 @@
+
+ ]>
+
++
++
++ Eric
++ Biederman
++ Creation, 2013
++
++
++ Iker
++ Pedrosa
++ Developer, 2021
++
++
+
+ subgid
+ 5
+@@ -41,12 +53,37 @@
+
+
+ subgid
+- the subordinate gid file
++ the configuration for subordinate group ids
+
+
+
+ DESCRIPTION
+
++ Subgid authorizes a group id to map ranges of group ids from its namespace
++ into child namespaces.
++
++
++ The delegation of the subordinate gids can be configured via the
++ subid field in
++ /etc/nsswitch.conf file. Only one value can be set
++ as the delegation source. Setting this field to
++ files configures the delegation of gids to
++ /etc/subgid. Setting any other value treats
++ the delegation as a plugin following with a name of the form
++ libsubid_$value.so. If the value or plugin is
++ missing, then the subordinate gid delegation falls back to
++ files.
++
++
++ Note, that groupadd will only create entries in
++ /etc/subgid if subid delegation is managed via subid
++ files.
++
++
++
++
++ LOCAL SUBORDINATE DELEGATION
++
+ Each line in /etc/subgid contains
+ a user name and a range of subordinate group ids that user
+ is allowed to use.
+diff -up shadow-4.6/man/subuid.5.xml.man_clarify_subid_delegation shadow-4.6/man/subuid.5.xml
+--- shadow-4.6/man/subuid.5.xml.man_clarify_subid_delegation 2018-04-29 18:42:37.000000000 +0200
++++ shadow-4.6/man/subuid.5.xml 2021-11-03 10:00:18.888255255 +0100
+@@ -32,6 +32,18 @@
+
+ ]>
+
++
++
++ Eric
++ Biederman
++ Creation, 2013
++
++
++ Iker
++ Pedrosa
++ Developer, 2021
++
++
+
+ subuid
+ 5
+@@ -41,12 +53,37 @@
+
+
+ subuid
+- the subordinate uid file
++ the configuration for subordinate user ids
+
+
+
+ DESCRIPTION
+
++ Subuid authorizes a user id to map ranges of user ids from its namespace
++ into child namespaces.
++
++
++ The delegation of the subordinate uids can be configured via the
++ subid field in
++ /etc/nsswitch.conf file. Only one value can be set
++ as the delegation source. Setting this field to
++ files configures the delegation of uids to
++ /etc/subuid. Setting any other value treats
++ the delegation as a plugin following with a name of the form
++ libsubid_$value.so. If the value or plugin is
++ missing, then the subordinate uid delegation falls back to
++ files.
++
++
++ Note, that useradd will only create entries in
++ /etc/subuid if subid delegation is managed via subid
++ files.
++
++
++
++
++ LOCAL SUBORDINATE DELEGATION
++
+ Each line in /etc/subuid contains
+ a user name and a range of subordinate user ids that user
+ is allowed to use.
diff --git a/SOURCES/shadow-4.6-respect_enable_static_no.patch b/SOURCES/shadow-4.6-respect_enable_static_no.patch
new file mode 100644
index 0000000..ed62a08
--- /dev/null
+++ b/SOURCES/shadow-4.6-respect_enable_static_no.patch
@@ -0,0 +1,24 @@
+diff -up shadow-4.6/configure.ac.respect_enable_static_no shadow-4.6/configure.ac
+--- shadow-4.6/configure.ac.respect_enable_static_no 2021-11-03 12:09:39.852829632 +0100
++++ shadow-4.6/configure.ac 2021-11-03 12:10:32.447203434 +0100
+@@ -311,6 +311,8 @@ if test "$with_sha_crypt" = "yes"; then
+ AC_DEFINE(USE_SHA_CRYPT, 1, [Define to allow the SHA256 and SHA512 password encryption algorithms])
+ fi
+
++AM_CONDITIONAL(ENABLE_SHARED, test "x$enable_shared" = "xyes")
++
+ if test "$with_nscd" = "yes"; then
+ AC_CHECK_FUNC(posix_spawn,
+ [AC_DEFINE(USE_NSCD, 1, [Define to support flushing of nscd caches])],
+diff -up shadow-4.6/libsubid/Makefile.am.respect_enable_static_no shadow-4.6/libsubid/Makefile.am
+--- shadow-4.6/libsubid/Makefile.am.respect_enable_static_no 2021-11-03 12:09:39.851829625 +0100
++++ shadow-4.6/libsubid/Makefile.am 2021-11-03 12:09:39.852829632 +0100
+@@ -1,6 +1,8 @@
+ lib_LTLIBRARIES = libsubid.la
++if ENABLE_SHARED
+ libsubid_la_LDFLAGS = -Wl,-soname,libsubid.so.@LIBSUBID_ABI@ \
+ -shared -version-info @LIBSUBID_ABI_MAJOR@
++endif
+ libsubid_la_SOURCES = api.c
+
+ pkginclude_HEADERS = subid.h
diff --git a/SOURCES/shadow-4.6-useradd_SUB_UID_COUNT-0.patch b/SOURCES/shadow-4.6-useradd_SUB_UID_COUNT-0.patch
new file mode 100644
index 0000000..f393368
--- /dev/null
+++ b/SOURCES/shadow-4.6-useradd_SUB_UID_COUNT-0.patch
@@ -0,0 +1,44 @@
+From 663824ef4ca927aa2b4319b69e0bfa68282ec719 Mon Sep 17 00:00:00 2001
+From: Serge Hallyn
+Date: Sat, 22 May 2021 11:42:02 -0500
+Subject: [PATCH] Fix useradd with SUB_UID_COUNT=0
+
+Closes #298
+
+Fix useradd when SUB_UID_COUNT=0 in login.defs.
+
+Signed-off-by: Serge Hallyn
+---
+ src/useradd.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/src/useradd.c b/src/useradd.c
+index 06accb2f..9862ae55 100644
+--- a/src/useradd.c
++++ b/src/useradd.c
+@@ -2386,6 +2386,8 @@ int main (int argc, char **argv)
+ #ifdef ENABLE_SUBIDS
+ uid_t uid_min;
+ uid_t uid_max;
++ unsigned long subuid_count;
++ unsigned long subgid_count;
+ #endif
+
+ /*
+@@ -2427,9 +2429,11 @@ int main (int argc, char **argv)
+ #ifdef ENABLE_SUBIDS
+ uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL);
+ uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL);
+- is_sub_uid = sub_uid_file_present () && !rflg &&
++ subuid_count = getdef_ulong ("SUB_UID_COUNT", 65536);
++ subgid_count = getdef_ulong ("SUB_GID_COUNT", 65536);
++ is_sub_uid = subuid_count > 0 && sub_uid_file_present () && !rflg &&
+ (!user_id || (user_id <= uid_max && user_id >= uid_min));
+- is_sub_gid = sub_gid_file_present () && !rflg &&
++ is_sub_gid = subgid_count > 0 && sub_gid_file_present () && !rflg &&
+ (!user_id || (user_id <= uid_max && user_id >= uid_min));
+ #endif /* ENABLE_SUBIDS */
+
+--
+2.30.2
+
diff --git a/SOURCES/shadow-4.6-useradd_dont_try_to_create_0_subuids.patch b/SOURCES/shadow-4.6-useradd_dont_try_to_create_0_subuids.patch
new file mode 100644
index 0000000..a53d724
--- /dev/null
+++ b/SOURCES/shadow-4.6-useradd_dont_try_to_create_0_subuids.patch
@@ -0,0 +1,21 @@
+diff -up shadow-4.6/src/useradd.c.useradd_dont_try_to_create_0_subuids shadow-4.6/src/useradd.c
+--- shadow-4.6/src/useradd.c.useradd_dont_try_to_create_0_subuids 2021-11-03 11:55:00.189562187 +0100
++++ shadow-4.6/src/useradd.c 2021-11-03 11:57:34.128658978 +0100
+@@ -2350,7 +2350,7 @@ int main (int argc, char **argv)
+ }
+
+ #ifdef ENABLE_SUBIDS
+- if (is_sub_uid) {
++ if (is_sub_uid && subuid_count != 0) {
+ if (find_new_sub_uids(user_name, &sub_uid_start, &sub_uid_count) < 0) {
+ fprintf (stderr,
+ _("%s: can't create subordinate user IDs\n"),
+@@ -2358,7 +2358,7 @@ int main (int argc, char **argv)
+ fail_exit(E_SUB_UID_UPDATE);
+ }
+ }
+- if (is_sub_gid) {
++ if (is_sub_gid && subgid_count != 0) {
+ if (find_new_sub_gids(user_name, &sub_gid_start, &sub_gid_count) < 0) {
+ fprintf (stderr,
+ _("%s: can't create subordinate group IDs\n"),
diff --git a/SPECS/shadow-utils.spec b/SPECS/shadow-utils.spec
index 4a66b18..8426b53 100644
--- a/SPECS/shadow-utils.spec
+++ b/SPECS/shadow-utils.spec
@@ -1,7 +1,7 @@
Summary: Utilities for managing accounts and shadow password files
Name: shadow-utils
Version: 4.6
-Release: 14%{?dist}
+Release: 16%{?dist}
Epoch: 2
URL: http://pkg-shadow.alioth.debian.org/
Source0: https://github.com/shadow-maint/shadow/releases/download/%{version}/shadow-%{version}.tar.xz
@@ -10,6 +10,11 @@ Source2: shadow-utils.useradd
Source3: shadow-utils.login.defs
Source4: shadow-bsd.txt
Source5: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+### Globals ###
+%global includesubiddir %{_includedir}/shadow
+
+### Patches ###
Patch0: shadow-4.6-redhat.patch
Patch1: shadow-4.6-goodname.patch
Patch2: shadow-4.1.5.1-info-parent-dir.patch
@@ -49,6 +54,44 @@ Patch45: shadow-4.6-sssd-redirect-warning.patch
Patch46: shadow-4.6-remove-login-string-references.patch
# https://github.com/shadow-maint/shadow/commit/e481437ab9ebe9a8bf8fbaabe986d42b2f765991
Patch47: shadow-4.6-usermod-allow-all-group-types.patch
+# https://github.com/shadow-maint/shadow/commit/0a7888b1fad613a052b988b01a71933b67296e68
+# https://github.com/shadow-maint/shadow/commit/607f1dd549cf9abc87af1cf29275f0d2d11eea29
+# https://github.com/shadow-maint/shadow/commit/b5fb1b38eea2fb0489ed088c82daf6700e72363e
+# https://github.com/shadow-maint/shadow/commit/43a917cce54019799a8de037fd63780a2b640afc
+Patch48: shadow-4.6-libsubid_creation.patch
+# https://github.com/shadow-maint/shadow/commit/514c1328b6c90d817ae0a9f7addfb3c9a11a275a
+# https://github.com/shadow-maint/shadow/commit/8492dee6632e340dee76eee895c3e30877bebf45
+# https://github.com/shadow-maint/shadow/commit/0f4347d1483191b2142546416a9eefe0c9459600
+Patch49: shadow-4.6-libsubid_nsswitch_support.patch
+# https://github.com/shadow-maint/shadow/commit/186b1b7ac1a68d0fcc618a22da1a99232b420911
+Patch50: shadow-4.6-man-mention-nss-in-newuidmap.patch
+# https://github.com/shadow-maint/shadow/commit/f9831a4a1a20b0e8fe47cc72ec20018ec04dbb90
+Patch51: shadow-4.6-libsubid_not_print_error_messages.patch
+# https://github.com/shadow-maint/shadow/commit/c6cab4a7bafa18d9d65a333cac1261e7b5e32bc9
+Patch52: shadow-4.6-libsubid_init_return_false.patch
+# https://github.com/shadow-maint/shadow/commit/2f1f45d64fc7c10e7a3cbe00e89f63714343e526
+Patch53: shadow-4.6-useradd_SUB_UID_COUNT-0.patch
+# https://github.com/shadow-maint/shadow/commit/ea7af4e1543c63590d4107ae075fea385028997d
+Patch54: shadow-4.6-libsubid_simplify_ranges_variable.patch
+# https://github.com/shadow-maint/shadow/commit/0fe42f571c69f0105d31305f995c9887aeb9525e
+Patch55: shadow-4.6-libsubid_init_not_print_error_messages.patch
+# https://github.com/shadow-maint/shadow/commit/ec1951c181faed188464396b2cfdd2efb726c7f3
+Patch56: shadow-4.6-libsubid_fix_newusers_nss_provides_subids.patch
+# https://github.com/shadow-maint/shadow/commit/087112244327be50abc24f9ec8afbf60ae8b2dec
+# https://github.com/shadow-maint/shadow/pull/353
+Patch57: shadow-4.6-man_clarify_subid_delegation.patch
+# https://github.com/shadow-maint/shadow/commit/bd920ab36a6c641e4a8769f8c7f8ca738ec61820
+Patch58: shadow-4.6-libsubid_make_logfd_not_extern.patch
+# https://github.com/shadow-maint/shadow/commit/0dffc7c61200f492eeac03c29fa7e93b62d3cead
+Patch59: shadow-4.6-useradd_dont_try_to_create_0_subuids.patch
+# https://github.com/shadow-maint/shadow/commit/77e39de1e6cbd6925f16bb260abb7d216296886b
+Patch60: shadow-4.6-install_subid_h.patch
+# https://github.com/shadow-maint/shadow/commit/fa986b1d73605ecca54a4f19249227aeab827bf6
+Patch61: shadow-4.6-respect_enable_static_no.patch
+# https://github.com/shadow-maint/shadow/commit/3b6ccf642c6bb2b7db087f09ee563ae9318af734
+Patch62: shadow-4.6-getsubids.patch
+# https://github.com/shadow-maint/shadow/commit/a757b458ffb4fb9a40bcbb4f7869449431c67f83
+Patch63: shadow-4.6-groupdel-fix-sigsegv-when-passwd-does-not-exist.patch
License: BSD and GPLv2+
Group: System Environment/Base
@@ -79,6 +122,23 @@ for all users. The useradd, userdel, and usermod commands are used for
managing user accounts. The groupadd, groupdel, and groupmod commands
are used for managing group accounts.
+
+### Subpackages ###
+%package subid
+Summary: A library to manage subordinate uid and gid ranges
+License: BSD and GPLv2+
+
+%description subid
+Utility library that provides a way to manage subid ranges.
+
+
+%package subid-devel
+Summary: Development package for shadow-utils-subid
+License: BSD and GPLv2+
+
+%description subid-devel
+Development files for shadow-utils-subid.
+
%prep
%setup -q -n shadow-%{version}
%patch0 -p1 -b .redhat
@@ -114,6 +174,22 @@ are used for managing group accounts.
%patch45 -p1 -b .sssd-redirect-warning
%patch46 -p1 -b .remove-login-string-references
%patch47 -p1 -b .usermod-allow-all-group-types
+%patch48 -p1 -b .libsubid_creation
+%patch49 -p1 -b .libsubid_nsswitch_support
+%patch50 -p1 -b .man-mention-nss-in-newuidmap
+%patch51 -p1 -b .libsubid_not_print_error_messages
+%patch52 -p1 -b .libsubid_init_return_false
+%patch53 -p1 -b .useradd_SUB_UID_COUNT-0
+%patch54 -p1 -b .libsubid_simplify_ranges_variable
+%patch55 -p1 -b .libsubid_init_not_print_error_messages
+%patch56 -p1 -b .libsubid_fix_newusers_nss_provides_subids
+%patch57 -p1 -b .man_clarify_subid_delegation
+%patch58 -p1 -b .libsubid_make_logfd_not_extern
+%patch59 -p1 -b .useradd_dont_try_to_create_0_subuids
+%patch60 -p1 -b .install_subid_h
+%patch61 -p1 -b .respect_enable_static_no
+%patch62 -p1 -b .getsubids
+%patch63 -p1 -b .groupdel-fix-sigsegv-when-passwd-does-not-exist
iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8
cp -f doc/HOWTO.utf8 doc/HOWTO
@@ -142,7 +218,7 @@ autoreconf
--with-selinux \
--without-libcrack \
--without-libpam \
- --disable-shared \
+ --enable-shared \
--with-group-name-max-length=32
%make_build
@@ -218,6 +294,13 @@ for dir in $(ls -1d $RPM_BUILD_ROOT%{_mandir}/{??,??_??}) ; do
echo "%%lang($lang) $dir/man*/*" >> shadow.lang
done
+# Move header files to its own folder
+mkdir -p $RPM_BUILD_ROOT/%{includesubiddir}
+install -m 644 libsubid/subid.h $RPM_BUILD_ROOT/%{includesubiddir}/
+
+# Remove .la files created by libsubid
+rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.la
+
%files -f shadow.lang
%doc NEWS doc/HOWTO README
%{!?_licensedir:%global license %%doc}
@@ -267,7 +350,33 @@ done
%{_mandir}/man8/vipw.8*
%{_mandir}/man8/vigr.8*
+%files subid
+%{_libdir}/libsubid.so.*
+%{_bindir}/getsubids
+%{_mandir}/man1/getsubids.1*
+
+%files subid-devel
+%{includesubiddir}/subid.h
+%{_libdir}/libsubid.so
+
%changelog
+* Thu Dec 9 2021 Iker Pedrosa - 2:4.6-16
+- getsubids: provide system binary and man page. Resolves: #2013016
+- groupdel: fix SIGSEGV when passwd does not exist. Resolves: #1986782
+
+* Tue Oct 19 2021 Iker Pedrosa - 2:4.6-15
+- Creation of subid and subid-devel subpackages (#2013009)
+- libsubid: creation and nsswitch support
+- libsubid: don't print error messages on stderr by default
+- libsubid: libsubid_init return false if out of memory
+- libsubid: don't return owner in list_owner_ranges API call
+- libsubid: libsubid_init don't print messages on error
+- libsubid: fix newusers when nss provides subids
+- libsubid: make shadow_logfd not extern
+- useradd: fix SUB_UID_COUNT=0
+- man: mention NSS in new[ug]idmap manpages
+- man: clarify subid delegation
+
* Thu Aug 12 2021 Iker Pedrosa - 2:4.6-14
- usermod: allow all group types with -G option (#1967641)