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 <serge@hallyn.com>
+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 <serge@hallyn.com>
+---
+ 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 <config.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <pwd.h>
++#include <stdbool.h>
++#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 <stdbool.h>
++
++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 <sys/types.h>
++
++#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 <sys/types.h>
+ #include <pwd.h>
+-
+-struct subordinate_range {
+-	const char *owner;
+-	unsigned long start;
+-	unsigned long count;
+-};
+-
+-#define NFIELDS 3
++#include <ctype.h>
+ 
+ /*
+  * 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 <sys/types.h>
+ 
++#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 <stdio.h>
++#include <unistd.h>
++#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 <stdio.h>
++#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 <stdio.h>
++#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 <stdio.h>
++#include <unistd.h>
++#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 <serge@hallyn.com>
+Date: Sun, 23 May 2021 08:03:10 -0500
+Subject: [PATCH] libsubid_init: don't print messages on error
+
+Signed-off-by: Serge Hallyn <serge@hallyn.com>
+---
+ 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 <serge@hallyn.com>
+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 <serge@hallyn.com>
+Cc: Alexey Tikhonov <atikhono@redhat.com>
+---
+ 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 <serge@hallyn.com>
+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 <serge@hallyn.com>
+---
+ 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 <sys/types.h>
++#include <stdio.h>
+ #include <stdbool.h>
+ 
+ #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 <stdio.h>
+ #include <errno.h>
+ #include <stdlib.h>
++#include <string.h>
+ #include <pwd.h>
+ #include <stdbool.h>
+ #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 <stdio.h>
++#include <stdlib.h>
++#include <dlfcn.h>
++#include <stdbool.h>
++#include <string.h>
++#include <strings.h>
++#include <ctype.h>
++#include <stdatomic.h>
++#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 <libsubid/subid.h>
++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 <stdbool.h>
+ #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 <sys/types.h>
++#include <stdbool.h>
+ 
+ #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 <sys/types.h>
+ #include <pwd.h>
+ #include <ctype.h>
++#include <fcntl.h>
+ 
+ /*
+  * 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 <config.h>
++#include <stdio.h>
++#include <string.h>
++#include <errno.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#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 <stdio.h>
+ #include <unistd.h>
+-#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 <stdio.h>
+-#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 <stdio.h>
+-#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 <stdio.h>
+ #include <unistd.h>
+-#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 <sys/types.h>
++#include <pwd.h>
++#include <stdlib.h>
++#include <stdbool.h>
++#include <subid.h>
++#include <string.h>
++
++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 <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <prototypes.h>
++#include <stdbool.h>
++#include <dlfcn.h>
++
++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 <stdio.h>
+ #include "commonio.h"
+ #include "subordinateio.h"
++#include "../libsubid/subid.h"
+ #include <sys/types.h>
+ #include <pwd.h>
+ #include <ctype.h>
+@@ -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 <serge@hallyn.com>
+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 <serge@hallyn.com>
+---
+ 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 @@
+     <title>DESCRIPTION</title>
+     <para>
+       The <command>newgidmap</command> sets <filename>/proc/[pid]/gid_map</filename> based on its
+-      command line arguments and the gids allowed in <filename>/etc/subgid</filename>.
++      command line arguments and the gids allowed (either in <filename>/etc/subgid</filename> or
++      through the configured NSS subid module).
+       Note that the root user is not exempted from the requirement for a valid
+       <filename>/etc/subgid</filename> entry.
+     </para>
+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 @@
+     <title>DESCRIPTION</title>
+     <para>
+       The <command>newuidmap</command> sets <filename>/proc/[pid]/uid_map</filename> based on its
+-      command line arguments and the uids allowed in <filename>/etc/subuid</filename>.
++      command line arguments and the uids allowed (either in <filename>/etc/subuid</filename> or
++      through the configured NSS subid module).
+       Note that the root user is not exempted from the requirement for a valid
+       <filename>/etc/subuid</filename> entry.
+     </para>
+-- 
+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 @@
+   <refsect1 id='description'>
+     <title>DESCRIPTION</title>
+     <para>
+-      The <command>newgidmap</command> sets <filename>/proc/[pid]/gid_map</filename> based on its
+-      command line arguments and the gids allowed (either in <filename>/etc/subgid</filename> or
+-      through the configured NSS subid module).
+-      Note that the root user is not exempted from the requirement for a valid
++      The <command>newgidmap</command> sets <filename>/proc/[pid]/gid_map</filename>
++      based on its command line arguments and the gids allowed. Subgid
++      delegation can either be managed via <filename>/etc/subgid</filename>
++      or through the configured NSS subid module. These options are mutually
++      exclusive.
++    </para>
++
++    <para>
++      Note that the root group is not exempted from the requirement for a valid
+       <filename>/etc/subgid</filename> entry.
+     </para>
+ 
+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 @@
+   <refsect1 id='description'>
+     <title>DESCRIPTION</title>
+     <para>
+-      The <command>newuidmap</command> sets <filename>/proc/[pid]/uid_map</filename> based on its
+-      command line arguments and the uids allowed (either in <filename>/etc/subuid</filename> or
+-      through the configured NSS subid module).
++      The <command>newuidmap</command> sets <filename>/proc/[pid]/uid_map</filename>
++      based on its command line arguments and the uids allowed. Subuid
++      delegation can either be managed via <filename>/etc/subuid</filename> or
++      through the configured NSS subid module. These options are mutually
++      exclusive.
++    </para>
++
++    <para>
+       Note that the root user is not exempted from the requirement for a valid
+       <filename>/etc/subuid</filename> entry.
+     </para>
+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 @@
+ <!-- SHADOW-CONFIG-HERE -->
+ ]>
+ <refentry id='subgid.5'>
++  <refentryinfo>
++    <author>
++      <firstname>Eric</firstname>
++      <surname>Biederman</surname>
++      <contrib>Creation, 2013</contrib>
++    </author>
++    <author>
++      <firstname>Iker</firstname>
++      <surname>Pedrosa</surname>
++      <contrib>Developer, 2021</contrib>
++    </author>
++  </refentryinfo>
+   <refmeta>
+     <refentrytitle>subgid</refentrytitle>
+     <manvolnum>5</manvolnum>
+@@ -41,12 +53,37 @@
+   </refmeta>
+   <refnamediv id='name'>
+     <refname>subgid</refname>
+-    <refpurpose>the subordinate gid file</refpurpose>
++    <refpurpose>the configuration for subordinate group ids</refpurpose>
+   </refnamediv>
+ 
+   <refsect1 id='description'>
+     <title>DESCRIPTION</title>
+     <para>
++      Subgid authorizes a group id to map ranges of group ids from its namespace
++      into child namespaces.
++    </para>
++    <para>
++      The delegation of the subordinate gids can be configured via the
++      <replaceable>subid</replaceable> field in
++      <filename>/etc/nsswitch.conf</filename> file. Only one value can be set
++      as the delegation source. Setting this field to
++      <replaceable>files</replaceable> configures the delegation of gids to
++      <filename>/etc/subgid</filename>. Setting any other value treats
++      the delegation as a plugin following with a name of the form
++      <replaceable>libsubid_$value.so</replaceable>. If the value or plugin is
++      missing, then the subordinate gid delegation falls back to
++      <replaceable>files</replaceable>.
++    </para>
++    <para>
++      Note, that <command>groupadd</command> will only create entries in
++      <filename>/etc/subgid</filename> if subid delegation is managed via subid
++      files.
++    </para>
++  </refsect1>
++
++  <refsect1 id='local-subordinate-delegation'>
++    <title>LOCAL SUBORDINATE DELEGATION</title>
++    <para>
+       Each line in <filename>/etc/subgid</filename> 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 @@
+ <!-- SHADOW-CONFIG-HERE -->
+ ]>
+ <refentry id='subuid.5'>
++  <refentryinfo>
++    <author>
++      <firstname>Eric</firstname>
++      <surname>Biederman</surname>
++      <contrib>Creation, 2013</contrib>
++    </author>
++    <author>
++      <firstname>Iker</firstname>
++      <surname>Pedrosa</surname>
++      <contrib>Developer, 2021</contrib>
++    </author>
++  </refentryinfo>
+   <refmeta>
+     <refentrytitle>subuid</refentrytitle>
+     <manvolnum>5</manvolnum>
+@@ -41,12 +53,37 @@
+   </refmeta>
+   <refnamediv id='name'>
+     <refname>subuid</refname>
+-    <refpurpose>the subordinate uid file</refpurpose>
++    <refpurpose>the configuration for subordinate user ids</refpurpose>
+   </refnamediv>
+ 
+   <refsect1 id='description'>
+     <title>DESCRIPTION</title>
+     <para>
++      Subuid authorizes a user id to map ranges of user ids from its namespace
++      into child namespaces.
++    </para>
++    <para>
++      The delegation of the subordinate uids can be configured via the
++      <replaceable>subid</replaceable> field in
++      <filename>/etc/nsswitch.conf</filename> file. Only one value can be set
++      as the delegation source. Setting this field to
++      <replaceable>files</replaceable> configures the delegation of uids to
++      <filename>/etc/subuid</filename>. Setting any other value treats
++      the delegation as a plugin following with a name of the form
++      <replaceable>libsubid_$value.so</replaceable>. If the value or plugin is
++      missing, then the subordinate uid delegation falls back to
++      <replaceable>files</replaceable>.
++    </para>
++    <para>
++      Note, that <command>useradd</command> will only create entries in
++      <filename>/etc/subuid</filename> if subid delegation is managed via subid
++      files.
++    </para>
++  </refsect1>
++
++  <refsect1 id='local-subordinate-delegation'>
++    <title>LOCAL SUBORDINATE DELEGATION</title>
++    <para>
+       Each line in <filename>/etc/subuid</filename> 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 <serge@hallyn.com>
+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 <serge@hallyn.com>
+---
+ 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..b07c1b2 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: 15%{?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,40 @@ 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
 
 License: BSD and GPLv2+
 Group: System Environment/Base
@@ -79,6 +118,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 +170,20 @@ 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
 
 iconv -f ISO88591 -t utf-8  doc/HOWTO > doc/HOWTO.utf8
 cp -f doc/HOWTO.utf8 doc/HOWTO
@@ -142,7 +212,7 @@ autoreconf
         --with-selinux \
         --without-libcrack \
         --without-libpam \
-        --disable-shared \
+        --enable-shared \
         --with-group-name-max-length=32
 %make_build
 
@@ -218,6 +288,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 +344,27 @@ done
 %{_mandir}/man8/vipw.8*
 %{_mandir}/man8/vigr.8*
 
+%files subid
+%{_libdir}/libsubid.so.*
+
+%files subid-devel
+%{includesubiddir}/subid.h
+%{_libdir}/libsubid.so
+
 %changelog
+* Tue Oct 19 2021 Iker Pedrosa <ipedrosa@redhat.com> - 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 <ipedrosa@redhat.com> - 2:4.6-14
 - usermod: allow all group types with -G option (#1967641)