Blame SOURCES/shadow-4.6-libsubid_nsswitch_support.patch

9ed61c
diff -up shadow-4.6/configure.ac.libsubid_nsswitch_support shadow-4.6/configure.ac
9ed61c
--- shadow-4.6/configure.ac.libsubid_nsswitch_support	2021-10-19 13:16:21.989493315 +0200
9ed61c
+++ shadow-4.6/configure.ac	2021-10-19 13:19:07.743131310 +0200
9ed61c
@@ -1,6 +1,6 @@
9ed61c
 dnl Process this file with autoconf to produce a configure script.
9ed61c
 AC_PREREQ([2.69])
9ed61c
-m4_define([libsubid_abi_major], 1)
9ed61c
+m4_define([libsubid_abi_major], 2)
9ed61c
 m4_define([libsubid_abi_minor], 0)
9ed61c
 m4_define([libsubid_abi_micro], 0)
9ed61c
 m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
9ed61c
diff -up shadow-4.6/lib/Makefile.am.libsubid_nsswitch_support shadow-4.6/lib/Makefile.am
9ed61c
--- shadow-4.6/lib/Makefile.am.libsubid_nsswitch_support	2021-10-19 13:16:21.973493060 +0200
9ed61c
+++ shadow-4.6/lib/Makefile.am	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -28,6 +28,7 @@ libshadow_la_SOURCES = \
9ed61c
 	groupio.h \
9ed61c
 	gshadow.c \
9ed61c
 	lockpw.c \
9ed61c
+	nss.c \
9ed61c
 	nscd.c \
9ed61c
 	nscd.h \
9ed61c
 	sssd.c \
9ed61c
diff -up shadow-4.6/libmisc/idmapping.h.libsubid_nsswitch_support shadow-4.6/libmisc/idmapping.h
9ed61c
--- shadow-4.6/libmisc/idmapping.h.libsubid_nsswitch_support	2021-10-19 13:16:21.989493315 +0200
9ed61c
+++ shadow-4.6/libmisc/idmapping.h	2021-10-19 13:19:50.629813857 +0200
9ed61c
@@ -40,5 +40,7 @@ extern struct map_range *get_map_ranges(
9ed61c
 extern void write_mapping(int proc_dir_fd, int ranges,
9ed61c
 	struct map_range *mappings, const char *map_file);
9ed61c
 
9ed61c
+extern void nss_init(char *nsswitch_path);
9ed61c
+
9ed61c
 #endif /* _ID_MAPPING_H_ */
9ed61c
 
9ed61c
diff -up shadow-4.6/libmisc/Makefile.am.libsubid_nsswitch_support shadow-4.6/libmisc/Makefile.am
9ed61c
--- shadow-4.6/libmisc/Makefile.am.libsubid_nsswitch_support	2021-10-19 13:16:21.988493299 +0200
9ed61c
+++ shadow-4.6/libmisc/Makefile.am	2021-10-19 13:17:03.356151673 +0200
9ed61c
@@ -3,9 +3,9 @@ EXTRA_DIST = .indent.pro xgetXXbyYY.c
9ed61c
 
9ed61c
 AM_CPPFLAGS = -I$(top_srcdir)/lib
9ed61c
 
9ed61c
-noinst_LIBRARIES = libmisc.a
9ed61c
+noinst_LTLIBRARIES = libmisc.la
9ed61c
 
9ed61c
-libmisc_a_SOURCES = \
9ed61c
+libmisc_la_SOURCES = \
9ed61c
 	addgrps.c \
9ed61c
 	age.c \
9ed61c
 	audit_help.c \
9ed61c
diff -up shadow-4.6/lib/nss.c.libsubid_nsswitch_support shadow-4.6/lib/nss.c
9ed61c
--- shadow-4.6/lib/nss.c.libsubid_nsswitch_support	2021-10-19 13:16:21.989493315 +0200
9ed61c
+++ shadow-4.6/lib/nss.c	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -0,0 +1,157 @@
9ed61c
+#include <stdio.h>
9ed61c
+#include <stdlib.h>
9ed61c
+#include <dlfcn.h>
9ed61c
+#include <stdbool.h>
9ed61c
+#include <string.h>
9ed61c
+#include <strings.h>
9ed61c
+#include <ctype.h>
9ed61c
+#include <stdatomic.h>
9ed61c
+#include "prototypes.h"
9ed61c
+#include "../libsubid/subid.h"
9ed61c
+
9ed61c
+#define NSSWITCH "/etc/nsswitch.conf"
9ed61c
+
9ed61c
+// NSS plugin handling for subids
9ed61c
+// If nsswitch has a line like
9ed61c
+//    subid: sssd
9ed61c
+// then sssd will be consulted for subids.  Unlike normal NSS dbs,
9ed61c
+// only one db is supported at a time.  That's open to debate, but
9ed61c
+// the subids are a pretty limited resource, and local files seem
9ed61c
+// bound to step on any other allocations leading to insecure
9ed61c
+// conditions.
9ed61c
+static atomic_flag nss_init_started;
9ed61c
+static atomic_bool nss_init_completed;
9ed61c
+
9ed61c
+static struct subid_nss_ops *subid_nss;
9ed61c
+
9ed61c
+bool nss_is_initialized() {
9ed61c
+	return atomic_load(&nss_init_completed);
9ed61c
+}
9ed61c
+
9ed61c
+void nss_exit() {
9ed61c
+	if (nss_is_initialized() && subid_nss) {
9ed61c
+		dlclose(subid_nss->handle);
9ed61c
+		free(subid_nss);
9ed61c
+		subid_nss = NULL;
9ed61c
+	}
9ed61c
+}
9ed61c
+
9ed61c
+// nsswitch_path is an argument only to support testing.
9ed61c
+void nss_init(char *nsswitch_path) {
9ed61c
+	FILE *nssfp = NULL;
9ed61c
+	char *line = NULL, *p, *token, *saveptr;
9ed61c
+	size_t len = 0;
9ed61c
+
9ed61c
+	if (atomic_flag_test_and_set(&nss_init_started)) {
9ed61c
+		// Another thread has started nss_init, wait for it to complete
9ed61c
+		while (!atomic_load(&nss_init_completed))
9ed61c
+			usleep(100);
9ed61c
+		return;
9ed61c
+	}
9ed61c
+
9ed61c
+	if (!nsswitch_path)
9ed61c
+		nsswitch_path = NSSWITCH;
9ed61c
+
9ed61c
+	// read nsswitch.conf to check for a line like:
9ed61c
+	//   subid:	files
9ed61c
+	nssfp = fopen(nsswitch_path, "r");
9ed61c
+	if (!nssfp) {
9ed61c
+		fprintf(stderr, "Failed opening %s: %m", nsswitch_path);
9ed61c
+		atomic_store(&nss_init_completed, true);
9ed61c
+		return;
9ed61c
+	}
9ed61c
+	while ((getline(&line, &len, nssfp)) != -1) {
9ed61c
+		if (line[0] == '\0' || line[0] == '#')
9ed61c
+			continue;
9ed61c
+		if (strlen(line) < 8)
9ed61c
+			continue;
9ed61c
+		if (strncasecmp(line, "subid:", 6) != 0)
9ed61c
+			continue;
9ed61c
+		p = &line[6];
9ed61c
+		while ((*p) && isspace(*p))
9ed61c
+			p++;
9ed61c
+		if (!*p)
9ed61c
+			continue;
9ed61c
+		for (token = strtok_r(p, " \n\t", &saveptr);
9ed61c
+		     token;
9ed61c
+		     token = strtok_r(NULL, " \n\t", &saveptr)) {
9ed61c
+			char libname[65];
9ed61c
+			void *h;
9ed61c
+			if (strcmp(token, "files") == 0) {
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			if (strlen(token) > 50) {
9ed61c
+				fprintf(stderr, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
9ed61c
+				fprintf(stderr, "Using files\n");
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			snprintf(libname, 64,  "libsubid_%s.so", token);
9ed61c
+			h = dlopen(libname, RTLD_LAZY);
9ed61c
+			if (!h) {
9ed61c
+				fprintf(stderr, "Error opening %s: %s\n", libname, dlerror());
9ed61c
+				fprintf(stderr, "Using files\n");
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss = malloc(sizeof(*subid_nss));
9ed61c
+			if (!subid_nss) {
9ed61c
+				dlclose(h);
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
9ed61c
+			if (!subid_nss->has_range) {
9ed61c
+				fprintf(stderr, "%s did not provide @has_range@\n", libname);
9ed61c
+				dlclose(h);
9ed61c
+				free(subid_nss);
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
9ed61c
+			if (!subid_nss->list_owner_ranges) {
9ed61c
+				fprintf(stderr, "%s did not provide @list_owner_ranges@\n", libname);
9ed61c
+				dlclose(h);
9ed61c
+				free(subid_nss);
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range");
9ed61c
+			if (!subid_nss->has_any_range) {
9ed61c
+				fprintf(stderr, "%s did not provide @has_any_range@\n", libname);
9ed61c
+				dlclose(h);
9ed61c
+				free(subid_nss);
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
9ed61c
+			if (!subid_nss->find_subid_owners) {
9ed61c
+				fprintf(stderr, "%s did not provide @find_subid_owners@\n", libname);
9ed61c
+				dlclose(h);
9ed61c
+				free(subid_nss);
9ed61c
+				subid_nss = NULL;
9ed61c
+				goto done;
9ed61c
+			}
9ed61c
+			subid_nss->handle = h;
9ed61c
+			goto done;
9ed61c
+		}
9ed61c
+		fprintf(stderr, "No usable subid NSS module found, using files\n");
9ed61c
+		// subid_nss has to be null here, but to ease reviews:
9ed61c
+		free(subid_nss);
9ed61c
+		subid_nss = NULL;
9ed61c
+		goto done;
9ed61c
+	}
9ed61c
+
9ed61c
+done:
9ed61c
+	atomic_store(&nss_init_completed, true);
9ed61c
+	free(line);
9ed61c
+	if (nssfp) {
9ed61c
+		atexit(nss_exit);
9ed61c
+		fclose(nssfp);
9ed61c
+	}
9ed61c
+}
9ed61c
+
9ed61c
+struct subid_nss_ops *get_subid_nss_handle() {
9ed61c
+	nss_init(NULL);
9ed61c
+	return subid_nss;
9ed61c
+}
9ed61c
diff -up shadow-4.6/lib/prototypes.h.libsubid_nsswitch_support shadow-4.6/lib/prototypes.h
9ed61c
--- shadow-4.6/lib/prototypes.h.libsubid_nsswitch_support	2021-10-19 13:16:21.961492869 +0200
9ed61c
+++ shadow-4.6/lib/prototypes.h	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -263,6 +263,75 @@ extern void motd (void);
9ed61c
 /* myname.c */
9ed61c
 extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void);
9ed61c
 
9ed61c
+/* nss.c */
9ed61c
+#include <libsubid/subid.h>
9ed61c
+extern void nss_init(char *nsswitch_path);
9ed61c
+extern bool nss_is_initialized();
9ed61c
+
9ed61c
+struct subid_nss_ops {
9ed61c
+	/*
9ed61c
+	 * nss_has_any_range: does a user own any subid range
9ed61c
+	 *
9ed61c
+	 * @owner: username
9ed61c
+	 * @idtype: subuid or subgid
9ed61c
+	 * @result: true if a subid allocation was found for @owner
9ed61c
+	 *
9ed61c
+	 * returns success if the module was able to determine an answer (true or false),
9ed61c
+	 * else an error status.
9ed61c
+	 */
9ed61c
+	enum subid_status (*has_any_range)(const char *owner, enum subid_type idtype, bool *result);
9ed61c
+
9ed61c
+	/*
9ed61c
+	 * nss_has_range: does a user own a given subid range
9ed61c
+	 *
9ed61c
+	 * @owner: username
9ed61c
+	 * @start: first subid in queried range
9ed61c
+	 * @count: number of subids in queried range
9ed61c
+	 * @idtype: subuid or subgid
9ed61c
+	 * @result: true if @owner has been allocated the subid range.
9ed61c
+	 *
9ed61c
+	 * returns success if the module was able to determine an answer (true or false),
9ed61c
+	 * else an error status.
9ed61c
+	 */
9ed61c
+	enum subid_status (*has_range)(const char *owner, unsigned long start, unsigned long count, enum subid_type idtype, bool *result);
9ed61c
+
9ed61c
+	/*
9ed61c
+	 * nss_list_owner_ranges: list the subid ranges delegated to a user.
9ed61c
+	 *
9ed61c
+	 * @owner - string representing username being queried
9ed61c
+	 * @id_type - subuid or subgid
9ed61c
+	 * @ranges - pointer to an array of struct subordinate_range pointers, or
9ed61c
+	 *           NULL.  The returned array of struct subordinate_range and its
9ed61c
+	 *           members must be freed by the caller.
9ed61c
+	 * @count - pointer to an integer into which the number of returned ranges
9ed61c
+	 *          is written.
9ed61c
+
9ed61c
+	 * returns success if the module was able to determine an answer,
9ed61c
+	 * else an error status.
9ed61c
+	 */
9ed61c
+	enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges, int *count);
9ed61c
+
9ed61c
+	/*
9ed61c
+	 * nss_find_subid_owners: find uids who own a given subuid or subgid.
9ed61c
+	 *
9ed61c
+	 * @id - the delegated id (subuid or subgid) being queried
9ed61c
+	 * @id_type - subuid or subgid
9ed61c
+	 * @uids - pointer to an array of uids which will be allocated by
9ed61c
+	 *         nss_find_subid_owners()
9ed61c
+	 * @count - number of uids found
9ed61c
+	 *
9ed61c
+	 * returns success if the module was able to determine an answer,
9ed61c
+	 * else an error status.
9ed61c
+	 */
9ed61c
+	enum subid_status (*find_subid_owners)(unsigned long id, enum subid_type id_type, uid_t **uids, int *count);
9ed61c
+
9ed61c
+	/* The dlsym handle to close */
9ed61c
+	void *handle;
9ed61c
+};
9ed61c
+
9ed61c
+extern struct subid_nss_ops *get_subid_nss_handle();
9ed61c
+
9ed61c
+
9ed61c
 /* pam_pass_non_interactive.c */
9ed61c
 #ifdef USE_PAM
9ed61c
 extern int do_pam_passwd_non_interactive (const char *pam_service,
9ed61c
diff -up shadow-4.6/libsubid/api.c.libsubid_nsswitch_support shadow-4.6/libsubid/api.c
9ed61c
--- shadow-4.6/libsubid/api.c.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/libsubid/api.c	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -36,134 +36,50 @@
9ed61c
 #include <stdbool.h>
9ed61c
 #include "subordinateio.h"
9ed61c
 #include "idmapping.h"
9ed61c
-#include "api.h"
9ed61c
+#include "subid.h"
9ed61c
 
9ed61c
-static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type)
9ed61c
+static
9ed61c
+int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
9ed61c
 {
9ed61c
-	struct subordinate_range **ranges = NULL;
9ed61c
-
9ed61c
-	switch (id_type) {
9ed61c
-	case ID_TYPE_UID:
9ed61c
-		if (!sub_uid_open(O_RDONLY)) {
9ed61c
-			return NULL;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	case ID_TYPE_GID:
9ed61c
-		if (!sub_gid_open(O_RDONLY)) {
9ed61c
-			return NULL;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	default:
9ed61c
-		return NULL;
9ed61c
-	}
9ed61c
-
9ed61c
-	ranges = list_owner_ranges(owner, id_type);
9ed61c
-
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
-		sub_uid_close();
9ed61c
-	else
9ed61c
-		sub_gid_close();
9ed61c
-
9ed61c
-	return ranges;
9ed61c
+	return list_owner_ranges(owner, id_type, ranges);
9ed61c
 }
9ed61c
 
9ed61c
-struct subordinate_range **get_subuid_ranges(const char *owner)
9ed61c
+int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges)
9ed61c
 {
9ed61c
-	return get_subid_ranges(owner, ID_TYPE_UID);
9ed61c
+	return get_subid_ranges(owner, ID_TYPE_UID, ranges);
9ed61c
 }
9ed61c
 
9ed61c
-struct subordinate_range **get_subgid_ranges(const char *owner)
9ed61c
+int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges)
9ed61c
 {
9ed61c
-	return get_subid_ranges(owner, ID_TYPE_GID);
9ed61c
+	return get_subid_ranges(owner, ID_TYPE_GID, ranges);
9ed61c
 }
9ed61c
 
9ed61c
-void subid_free_ranges(struct subordinate_range **ranges)
9ed61c
+void subid_free_ranges(struct subordinate_range **ranges, int count)
9ed61c
 {
9ed61c
-	return free_subordinate_ranges(ranges);
9ed61c
+	return free_subordinate_ranges(ranges, count);
9ed61c
 }
9ed61c
 
9ed61c
-int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type)
9ed61c
+static
9ed61c
+int get_subid_owner(unsigned long id, enum subid_type id_type, uid_t **owner)
9ed61c
 {
9ed61c
-	int ret = -1;
9ed61c
-
9ed61c
-	switch (id_type) {
9ed61c
-	case ID_TYPE_UID:
9ed61c
-		if (!sub_uid_open(O_RDONLY)) {
9ed61c
-			return -1;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	case ID_TYPE_GID:
9ed61c
-		if (!sub_gid_open(O_RDONLY)) {
9ed61c
-			return -1;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	default:
9ed61c
-		return -1;
9ed61c
-	}
9ed61c
-
9ed61c
-	ret = find_subid_owners(id, owner, id_type);
9ed61c
-
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
-		sub_uid_close();
9ed61c
-	else
9ed61c
-		sub_gid_close();
9ed61c
-
9ed61c
-	return ret;
9ed61c
+	return find_subid_owners(id, id_type, owner);
9ed61c
 }
9ed61c
 
9ed61c
 int get_subuid_owners(uid_t uid, uid_t **owner)
9ed61c
 {
9ed61c
-	return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID);
9ed61c
+	return get_subid_owner((unsigned long)uid, ID_TYPE_UID, owner);
9ed61c
 }
9ed61c
 
9ed61c
 int get_subgid_owners(gid_t gid, uid_t **owner)
9ed61c
 {
9ed61c
-	return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID);
9ed61c
+	return get_subid_owner((unsigned long)gid, ID_TYPE_GID, owner);
9ed61c
 }
9ed61c
 
9ed61c
+static
9ed61c
 bool grant_subid_range(struct subordinate_range *range, bool reuse,
9ed61c
 		       enum subid_type id_type)
9ed61c
 {
9ed61c
-	bool ret;
9ed61c
-
9ed61c
-	switch (id_type) {
9ed61c
-	case ID_TYPE_UID:
9ed61c
-		if (!sub_uid_lock()) {
9ed61c
-			printf("Failed loging subuids (errno %d)\n", errno);
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		if (!sub_uid_open(O_CREAT | O_RDWR)) {
9ed61c
-			printf("Failed opening subuids (errno %d)\n", errno);
9ed61c
-			sub_uid_unlock();
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	case ID_TYPE_GID:
9ed61c
-		if (!sub_gid_lock()) {
9ed61c
-			printf("Failed loging subgids (errno %d)\n", errno);
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		if (!sub_gid_open(O_CREAT | O_RDWR)) {
9ed61c
-			printf("Failed opening subgids (errno %d)\n", errno);
9ed61c
-			sub_gid_unlock();
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	default:
9ed61c
-		return false;
9ed61c
-	}
9ed61c
-
9ed61c
-	ret = new_subid_range(range, id_type, reuse);
9ed61c
-
9ed61c
-	if (id_type == ID_TYPE_UID) {
9ed61c
-		sub_uid_close();
9ed61c
-		sub_uid_unlock();
9ed61c
-	} else {
9ed61c
-		sub_gid_close();
9ed61c
-		sub_gid_unlock();
9ed61c
-	}
9ed61c
-
9ed61c
-	return ret;
9ed61c
+	return new_subid_range(range, id_type, reuse);
9ed61c
 }
9ed61c
 
9ed61c
 bool grant_subuid_range(struct subordinate_range *range, bool reuse)
9ed61c
@@ -176,56 +92,18 @@ bool grant_subgid_range(struct subordina
9ed61c
 	return grant_subid_range(range, reuse, ID_TYPE_GID);
9ed61c
 }
9ed61c
 
9ed61c
-bool free_subid_range(struct subordinate_range *range, enum subid_type id_type)
9ed61c
+static
9ed61c
+bool ungrant_subid_range(struct subordinate_range *range, enum subid_type id_type)
9ed61c
 {
9ed61c
-	bool ret;
9ed61c
-
9ed61c
-	switch (id_type) {
9ed61c
-	case ID_TYPE_UID:
9ed61c
-		if (!sub_uid_lock()) {
9ed61c
-			printf("Failed loging subuids (errno %d)\n", errno);
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		if (!sub_uid_open(O_CREAT | O_RDWR)) {
9ed61c
-			printf("Failed opening subuids (errno %d)\n", errno);
9ed61c
-			sub_uid_unlock();
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	case ID_TYPE_GID:
9ed61c
-		if (!sub_gid_lock()) {
9ed61c
-			printf("Failed loging subgids (errno %d)\n", errno);
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		if (!sub_gid_open(O_CREAT | O_RDWR)) {
9ed61c
-			printf("Failed opening subgids (errno %d)\n", errno);
9ed61c
-			sub_gid_unlock();
9ed61c
-			return false;
9ed61c
-		}
9ed61c
-		break;
9ed61c
-	default:
9ed61c
-		return false;
9ed61c
-	}
9ed61c
-
9ed61c
-	ret = release_subid_range(range, id_type);
9ed61c
-
9ed61c
-	if (id_type == ID_TYPE_UID) {
9ed61c
-		sub_uid_close();
9ed61c
-		sub_uid_unlock();
9ed61c
-	} else {
9ed61c
-		sub_gid_close();
9ed61c
-		sub_gid_unlock();
9ed61c
-	}
9ed61c
-
9ed61c
-	return ret;
9ed61c
+	return release_subid_range(range, id_type);
9ed61c
 }
9ed61c
 
9ed61c
-bool free_subuid_range(struct subordinate_range *range)
9ed61c
+bool ungrant_subuid_range(struct subordinate_range *range)
9ed61c
 {
9ed61c
-	return free_subid_range(range, ID_TYPE_UID);
9ed61c
+	return ungrant_subid_range(range, ID_TYPE_UID);
9ed61c
 }
9ed61c
 
9ed61c
-bool free_subgid_range(struct subordinate_range *range)
9ed61c
+bool ungrant_subgid_range(struct subordinate_range *range)
9ed61c
 {
9ed61c
-	return free_subid_range(range, ID_TYPE_GID);
9ed61c
+	return ungrant_subid_range(range, ID_TYPE_GID);
9ed61c
 }
9ed61c
diff -up shadow-4.6/libsubid/api.h.libsubid_nsswitch_support shadow-4.6/libsubid/api.h
9ed61c
diff -up shadow-4.6/libsubid/Makefile.am.libsubid_nsswitch_support shadow-4.6/libsubid/Makefile.am
9ed61c
--- shadow-4.6/libsubid/Makefile.am.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/libsubid/Makefile.am	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -12,12 +12,14 @@ MISCLIBS = \
9ed61c
 	$(LIBSKEY) \
9ed61c
 	$(LIBMD) \
9ed61c
 	$(LIBCRYPT) \
9ed61c
+	$(LIBACL) \
9ed61c
+	$(LIBATTR) \
9ed61c
 	$(LIBTCB)
9ed61c
 
9ed61c
 libsubid_la_LIBADD = \
9ed61c
 	$(top_srcdir)/lib/libshadow.la \
9ed61c
-	$(MISCLIBS) \
9ed61c
-	$(top_srcdir)/libmisc/libmisc.a
9ed61c
+	$(top_srcdir)/libmisc/libmisc.la \
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
 
9ed61c
 AM_CPPFLAGS = \
9ed61c
 	-I${top_srcdir}/lib \
9ed61c
diff -up shadow-4.6/libsubid/subid.h.libsubid_nsswitch_support shadow-4.6/libsubid/subid.h
9ed61c
--- shadow-4.6/libsubid/subid.h.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/libsubid/subid.h	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -1,4 +1,5 @@
9ed61c
 #include <sys/types.h>
9ed61c
+#include <stdbool.h>
9ed61c
 
9ed61c
 #ifndef SUBID_RANGE_DEFINED
9ed61c
 #define SUBID_RANGE_DEFINED 1
9ed61c
@@ -13,5 +14,117 @@ enum subid_type {
9ed61c
 	ID_TYPE_GID = 2
9ed61c
 };
9ed61c
 
9ed61c
+enum subid_status {
9ed61c
+	SUBID_STATUS_SUCCESS = 0,
9ed61c
+	SUBID_STATUS_UNKNOWN_USER = 1,
9ed61c
+	SUBID_STATUS_ERROR_CONN = 2,
9ed61c
+	SUBID_STATUS_ERROR = 3,
9ed61c
+};
9ed61c
+
9ed61c
+/*
9ed61c
+ * get_subuid_ranges: return a list of UID ranges for a user
9ed61c
+ *
9ed61c
+ * @owner: username being queried
9ed61c
+ * @ranges: a pointer to a subordinate range ** in which the result will be
9ed61c
+ *          returned.
9ed61c
+ *
9ed61c
+ * returns: number of ranges found, ir < 0 on error.
9ed61c
+ */
9ed61c
+int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges);
9ed61c
+
9ed61c
+/*
9ed61c
+ * get_subgid_ranges: return a list of GID ranges for a user
9ed61c
+ *
9ed61c
+ * @owner: username being queried
9ed61c
+ * @ranges: a pointer to a subordinate range ** in which the result will be
9ed61c
+ *          returned.
9ed61c
+ *
9ed61c
+ * returns: number of ranges found, ir < 0 on error.
9ed61c
+ */
9ed61c
+int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
9ed61c
+
9ed61c
+/*
9ed61c
+ * subid_free_ranges: free an array of subordinate_ranges returned by either
9ed61c
+ *                    get_subuid_ranges() or get_subgid_ranges().
9ed61c
+ *
9ed61c
+ * @ranges: the ranges to free
9ed61c
+ * @count: the number of ranges in @ranges
9ed61c
+ */
9ed61c
+void subid_free_ranges(struct subordinate_range **ranges, int count);
9ed61c
+
9ed61c
+/*
9ed61c
+ * get_subuid_owners: return a list of uids to which the given uid has been
9ed61c
+ *                    delegated.
9ed61c
+ *
9ed61c
+ * @uid: The subuid being queried
9ed61c
+ * @owners: a pointer to an array of uids into which the results are placed.
9ed61c
+ *          The returned array must be freed by the caller.
9ed61c
+ *
9ed61c
+ * Returns the number of uids returned, or < 0 on error.
9ed61c
+ */
9ed61c
+int get_subuid_owners(uid_t uid, uid_t **owner);
9ed61c
+
9ed61c
+/*
9ed61c
+ * get_subgid_owners: return a list of uids to which the given gid has been
9ed61c
+ *                    delegated.
9ed61c
+ *
9ed61c
+ * @uid: The subgid being queried
9ed61c
+ * @owners: a pointer to an array of uids into which the results are placed.
9ed61c
+ *          The returned array must be freed by the caller.
9ed61c
+ *
9ed61c
+ * Returns the number of uids returned, or < 0 on error.
9ed61c
+ */
9ed61c
+int get_subgid_owners(gid_t gid, uid_t **owner);
9ed61c
+
9ed61c
+/*
9ed61c
+ * grant_subuid_range: assign a subuid range to a user
9ed61c
+ *
9ed61c
+ * @range: pointer to a struct subordinate_range detailing the UID range
9ed61c
+ *         to allocate.  ->owner must be the username, and ->count must be
9ed61c
+ *         filled in.  ->start is ignored, and will contain the start
9ed61c
+ *         of the newly allocated range, upon success.
9ed61c
+ *
9ed61c
+ * Returns true if the delegation succeeded, false otherwise.  If true,
9ed61c
+ * then the range from (range->start, range->start + range->count) will
9ed61c
+ * be delegated to range->owner.
9ed61c
+ */
9ed61c
+bool grant_subuid_range(struct subordinate_range *range, bool reuse);
9ed61c
+
9ed61c
+/*
9ed61c
+ * grant_subsid_range: assign a subgid range to a user
9ed61c
+ *
9ed61c
+ * @range: pointer to a struct subordinate_range detailing the GID range
9ed61c
+ *         to allocate.  ->owner must be the username, and ->count must be
9ed61c
+ *         filled in.  ->start is ignored, and will contain the start
9ed61c
+ *         of the newly allocated range, upon success.
9ed61c
+ *
9ed61c
+ * Returns true if the delegation succeeded, false otherwise.  If true,
9ed61c
+ * then the range from (range->start, range->start + range->count) will
9ed61c
+ * be delegated to range->owner.
9ed61c
+ */
9ed61c
+bool grant_subgid_range(struct subordinate_range *range, bool reuse);
9ed61c
+
9ed61c
+/*
9ed61c
+ * ungrant_subuid_range: remove a subuid allocation.
9ed61c
+ *
9ed61c
+ * @range: pointer to a struct subordinate_range detailing the UID allocation
9ed61c
+ *         to remove.
9ed61c
+ *
9ed61c
+ * Returns true if successful, false if it failed, for instance if the
9ed61c
+ * delegation did not exist.
9ed61c
+ */
9ed61c
+bool ungrant_subuid_range(struct subordinate_range *range);
9ed61c
+
9ed61c
+/*
9ed61c
+ * ungrant_subuid_range: remove a subgid allocation.
9ed61c
+ *
9ed61c
+ * @range: pointer to a struct subordinate_range detailing the GID allocation
9ed61c
+ *         to remove.
9ed61c
+ *
9ed61c
+ * Returns true if successful, false if it failed, for instance if the
9ed61c
+ * delegation did not exist.
9ed61c
+ */
9ed61c
+bool ungrant_subgid_range(struct subordinate_range *range);
9ed61c
+
9ed61c
 #define SUBID_NFIELDS 3
9ed61c
 #endif
9ed61c
diff -up shadow-4.6/lib/subordinateio.c.libsubid_nsswitch_support shadow-4.6/lib/subordinateio.c
9ed61c
--- shadow-4.6/lib/subordinateio.c.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/lib/subordinateio.c	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -14,6 +14,7 @@
9ed61c
 #include <sys/types.h>
9ed61c
 #include <pwd.h>
9ed61c
 #include <ctype.h>
9ed61c
+#include <fcntl.h>
9ed61c
 
9ed61c
 /*
9ed61c
  * subordinate_dup: create a duplicate range
9ed61c
@@ -316,17 +317,17 @@ static bool append_range(struct subordin
9ed61c
 {
9ed61c
 	struct subordinate_range *tmp;
9ed61c
 	if (!*ranges) {
9ed61c
-		*ranges = malloc(2 * sizeof(struct subordinate_range **));
9ed61c
+		*ranges = malloc(sizeof(struct subordinate_range *));
9ed61c
 		if (!*ranges)
9ed61c
 			return false;
9ed61c
 	} else {
9ed61c
 		struct subordinate_range **new;
9ed61c
-		new = realloc(*ranges, (n + 2) * (sizeof(struct subordinate_range **)));
9ed61c
+		new = realloc(*ranges, (n + 1) * (sizeof(struct subordinate_range *)));
9ed61c
 		if (!new)
9ed61c
 			return false;
9ed61c
 		*ranges = new;
9ed61c
 	}
9ed61c
-	(*ranges)[n] = (*ranges)[n+1] = NULL;
9ed61c
+	(*ranges)[n] = NULL;
9ed61c
 	tmp = subordinate_dup(new);
9ed61c
 	if (!tmp)
9ed61c
 		return false;
9ed61c
@@ -334,13 +335,13 @@ static bool append_range(struct subordin
9ed61c
 	return true;
9ed61c
 }
9ed61c
 
9ed61c
-void free_subordinate_ranges(struct subordinate_range **ranges)
9ed61c
+void free_subordinate_ranges(struct subordinate_range **ranges, int count)
9ed61c
 {
9ed61c
 	int i;
9ed61c
 
9ed61c
 	if (!ranges)
9ed61c
 		return;
9ed61c
-	for (i = 0; ranges[i]; i++)
9ed61c
+	for (i = 0; i < count; i++)
9ed61c
 		subordinate_free(ranges[i]);
9ed61c
 	free(ranges);
9ed61c
 }
9ed61c
@@ -607,21 +608,46 @@ int sub_uid_open (int mode)
9ed61c
 
9ed61c
 bool sub_uid_assigned(const char *owner)
9ed61c
 {
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+	bool found;
9ed61c
+	enum subid_status status;
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->has_any_range(owner, ID_TYPE_UID, &found);
9ed61c
+		if (status == SUBID_STATUS_SUCCESS && found)
9ed61c
+			return true;
9ed61c
+		return false;
9ed61c
+	}
9ed61c
+
9ed61c
 	return range_exists (&subordinate_uid_db, owner);
9ed61c
 }
9ed61c
 
9ed61c
 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+	bool found;
9ed61c
+	enum subid_status status;
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->has_range(owner, start, count, ID_TYPE_UID, &found);
9ed61c
+		if (status == SUBID_STATUS_SUCCESS && found)
9ed61c
+			return true;
9ed61c
+		return false;
9ed61c
+	}
9ed61c
 	return have_range (&subordinate_uid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return -EOPNOTSUPP;
9ed61c
 	return add_range (&subordinate_uid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return -EOPNOTSUPP;
9ed61c
 	return remove_range (&subordinate_uid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
@@ -689,21 +715,45 @@ int sub_gid_open (int mode)
9ed61c
 
9ed61c
 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+	bool found;
9ed61c
+	enum subid_status status;
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->has_range(owner, start, count, ID_TYPE_GID, &found);
9ed61c
+		if (status == SUBID_STATUS_SUCCESS && found)
9ed61c
+			return true;
9ed61c
+		return false;
9ed61c
+	}
9ed61c
 	return have_range(&subordinate_gid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
 bool sub_gid_assigned(const char *owner)
9ed61c
 {
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+	bool found;
9ed61c
+	enum subid_status status;
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->has_any_range(owner, ID_TYPE_GID, &found);
9ed61c
+		if (status == SUBID_STATUS_SUCCESS && found)
9ed61c
+			return true;
9ed61c
+		return false;
9ed61c
+	}
9ed61c
 	return range_exists (&subordinate_gid_db, owner);
9ed61c
 }
9ed61c
 
9ed61c
 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return -EOPNOTSUPP;
9ed61c
 	return add_range (&subordinate_gid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
9ed61c
 {
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return -EOPNOTSUPP;
9ed61c
 	return remove_range (&subordinate_gid_db, owner, start, count);
9ed61c
 }
9ed61c
 
9ed61c
@@ -725,42 +775,78 @@ gid_t sub_gid_find_free_range(gid_t min,
9ed61c
 }
9ed61c
 
9ed61c
 /*
9ed61c
- struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
9ed61c
+ * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
9ed61c
  *
9ed61c
  * @owner: username
9ed61c
  * @id_type: UID or GUID
9ed61c
+ * @ranges: pointer to array of ranges into which results will be placed.
9ed61c
  *
9ed61c
- * Returns the subuid or subgid ranges which are owned by the specified
9ed61c
+ * Fills in the subuid or subgid ranges which are owned by the specified
9ed61c
  * user.  Username may be a username or a string representation of a
9ed61c
  * UID number.  If id_type is UID, then subuids are returned, else
9ed61c
- * subgids are returned.  If there is an error, < 0 is returned.
9ed61c
+ * subgids are given.
9ed61c
+
9ed61c
+ * Returns the number of ranges found, or < 0 on error.
9ed61c
  *
9ed61c
  * The caller must free the subordinate range list.
9ed61c
  */
9ed61c
-struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
9ed61c
+int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges)
9ed61c
 {
9ed61c
 	// TODO - need to handle owner being either uid or username
9ed61c
-	const struct subordinate_range *range;
9ed61c
 	struct subordinate_range **ranges = NULL;
9ed61c
+	const struct subordinate_range *range;
9ed61c
 	struct commonio_db *db;
9ed61c
-	int size = 0;
9ed61c
+	enum subid_status status;
9ed61c
+	int count = 0;
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+
9ed61c
+	*in_ranges = NULL;
9ed61c
+
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->list_owner_ranges(owner, id_type, in_ranges, &count);
9ed61c
+		if (status == SUBID_STATUS_SUCCESS)
9ed61c
+			return count;
9ed61c
+		return -1;
9ed61c
+	}
9ed61c
 
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
+	switch (id_type) {
9ed61c
+	case ID_TYPE_UID:
9ed61c
+		if (!sub_uid_open(O_RDONLY)) {
9ed61c
+			return -1;
9ed61c
+		}
9ed61c
 		db = &subordinate_uid_db;
9ed61c
-	else
9ed61c
+		break;
9ed61c
+	case ID_TYPE_GID:
9ed61c
+		if (!sub_gid_open(O_RDONLY)) {
9ed61c
+			return -1;
9ed61c
+		}
9ed61c
 		db = &subordinate_gid_db;
9ed61c
+		break;
9ed61c
+	default:
9ed61c
+		return -1;
9ed61c
+	}
9ed61c
 
9ed61c
 	commonio_rewind(db);
9ed61c
 	while ((range = commonio_next(db)) != NULL) {
9ed61c
 		if (0 == strcmp(range->owner, owner)) {
9ed61c
-			if (!append_range(&ranges, range, size++)) {
9ed61c
-				free_subordinate_ranges(ranges);
9ed61c
-				return NULL;
9ed61c
+			if (!append_range(&ranges, range, count++)) {
9ed61c
+				free_subordinate_ranges(ranges, count-1);
9ed61c
+				ranges = NULL;
9ed61c
+				count = -1;
9ed61c
+				goto out;
9ed61c
 			}
9ed61c
 		}
9ed61c
 	}
9ed61c
 
9ed61c
-	return ranges;
9ed61c
+out:
9ed61c
+	if (id_type == ID_TYPE_UID)
9ed61c
+		sub_uid_close();
9ed61c
+	else
9ed61c
+		sub_gid_close();
9ed61c
+
9ed61c
+	*in_ranges = ranges;
9ed61c
+	return count;
9ed61c
 }
9ed61c
 
9ed61c
 static bool all_digits(const char *str)
9ed61c
@@ -813,17 +899,41 @@ static int append_uids(uid_t **uids, con
9ed61c
 	return n+1;
9ed61c
 }
9ed61c
 
9ed61c
-int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type)
9ed61c
+int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids)
9ed61c
 {
9ed61c
 	const struct subordinate_range *range;
9ed61c
+	struct subid_nss_ops *h;
9ed61c
+	enum subid_status status;
9ed61c
 	struct commonio_db *db;
9ed61c
 	int n = 0;
9ed61c
 
9ed61c
-	*uids = NULL;
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
+	h = get_subid_nss_handle();
9ed61c
+	if (h) {
9ed61c
+		status = h->find_subid_owners(id, id_type, uids, &n);
9ed61c
+		// Several ways we could handle the error cases here.
9ed61c
+		if (status != SUBID_STATUS_SUCCESS)
9ed61c
+			return -1;
9ed61c
+		return n;
9ed61c
+	}
9ed61c
+
9ed61c
+	switch (id_type) {
9ed61c
+	case ID_TYPE_UID:
9ed61c
+		if (!sub_uid_open(O_RDONLY)) {
9ed61c
+			return -1;
9ed61c
+		}
9ed61c
 		db = &subordinate_uid_db;
9ed61c
-	else
9ed61c
+		break;
9ed61c
+	case ID_TYPE_GID:
9ed61c
+		if (!sub_gid_open(O_RDONLY)) {
9ed61c
+			return -1;
9ed61c
+		}
9ed61c
 		db = &subordinate_gid_db;
9ed61c
+		break;
9ed61c
+	default:
9ed61c
+		return -1;
9ed61c
+	}
9ed61c
+
9ed61c
+	*uids = NULL;
9ed61c
 
9ed61c
 	commonio_rewind(db);
9ed61c
 	while ((range = commonio_next(db)) != NULL) {
9ed61c
@@ -834,6 +944,11 @@ int find_subid_owners(unsigned long id,
9ed61c
 		}
9ed61c
 	}
9ed61c
 
9ed61c
+	if (id_type == ID_TYPE_UID)
9ed61c
+		sub_uid_close();
9ed61c
+	else
9ed61c
+		sub_gid_close();
9ed61c
+
9ed61c
 	return n;
9ed61c
 }
9ed61c
 
9ed61c
@@ -841,11 +956,40 @@ bool new_subid_range(struct subordinate_
9ed61c
 {
9ed61c
 	struct commonio_db *db;
9ed61c
 	const struct subordinate_range *r;
9ed61c
+	bool ret;
9ed61c
 
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return false;
9ed61c
+
9ed61c
+	switch (id_type) {
9ed61c
+	case ID_TYPE_UID:
9ed61c
+		if (!sub_uid_lock()) {
9ed61c
+			printf("Failed loging subuids (errno %d)\n", errno);
9ed61c
+			return false;
9ed61c
+		}
9ed61c
+		if (!sub_uid_open(O_CREAT | O_RDWR)) {
9ed61c
+			printf("Failed opening subuids (errno %d)\n", errno);
9ed61c
+			sub_uid_unlock();
9ed61c
+			return false;
9ed61c
+		}
9ed61c
 		db = &subordinate_uid_db;
9ed61c
-	else
9ed61c
+		break;
9ed61c
+	case ID_TYPE_GID:
9ed61c
+		if (!sub_gid_lock()) {
9ed61c
+			printf("Failed loging subgids (errno %d)\n", errno);
9ed61c
+			return false;
9ed61c
+		}
9ed61c
+		if (!sub_gid_open(O_CREAT | O_RDWR)) {
9ed61c
+			printf("Failed opening subgids (errno %d)\n", errno);
9ed61c
+			sub_gid_unlock();
9ed61c
+			return false;
9ed61c
+		}
9ed61c
 		db = &subordinate_gid_db;
9ed61c
+		break;
9ed61c
+	default:
9ed61c
+		return false;
9ed61c
+	}
9ed61c
+
9ed61c
 	commonio_rewind(db);
9ed61c
 	if (reuse) {
9ed61c
 		while ((r = commonio_next(db)) != NULL) {
9ed61c
@@ -861,20 +1005,74 @@ bool new_subid_range(struct subordinate_
9ed61c
 	}
9ed61c
 
9ed61c
 	range->start = find_free_range(db, range->start, ULONG_MAX, range->count);
9ed61c
-	if (range->start == ULONG_MAX)
9ed61c
-		return false;
9ed61c
 
9ed61c
-	return add_range(db, range->owner, range->start, range->count) == 1;
9ed61c
+	if (range->start == ULONG_MAX) {
9ed61c
+		ret = false;
9ed61c
+		goto out;
9ed61c
+	}
9ed61c
+
9ed61c
+	ret = add_range(db, range->owner, range->start, range->count) == 1;
9ed61c
+
9ed61c
+out:
9ed61c
+	if (id_type == ID_TYPE_UID) {
9ed61c
+		sub_uid_close();
9ed61c
+		sub_uid_unlock();
9ed61c
+	} else {
9ed61c
+		sub_gid_close();
9ed61c
+		sub_gid_unlock();
9ed61c
+	}
9ed61c
+
9ed61c
+	return ret;
9ed61c
 }
9ed61c
 
9ed61c
 bool release_subid_range(struct subordinate_range *range, enum subid_type id_type)
9ed61c
 {
9ed61c
 	struct commonio_db *db;
9ed61c
-	if (id_type == ID_TYPE_UID)
9ed61c
+	bool ret;
9ed61c
+
9ed61c
+	if (get_subid_nss_handle())
9ed61c
+		return false;
9ed61c
+
9ed61c
+	switch (id_type) {
9ed61c
+	case ID_TYPE_UID:
9ed61c
+		if (!sub_uid_lock()) {
9ed61c
+			printf("Failed loging subuids (errno %d)\n", errno);
9ed61c
+			return false;
9ed61c
+		}
9ed61c
+		if (!sub_uid_open(O_CREAT | O_RDWR)) {
9ed61c
+			printf("Failed opening subuids (errno %d)\n", errno);
9ed61c
+			sub_uid_unlock();
9ed61c
+			return false;
9ed61c
+		}
9ed61c
 		db = &subordinate_uid_db;
9ed61c
-	else
9ed61c
+		break;
9ed61c
+	case ID_TYPE_GID:
9ed61c
+		if (!sub_gid_lock()) {
9ed61c
+			printf("Failed loging subgids (errno %d)\n", errno);
9ed61c
+			return false;
9ed61c
+		}
9ed61c
+		if (!sub_gid_open(O_CREAT | O_RDWR)) {
9ed61c
+			printf("Failed opening subgids (errno %d)\n", errno);
9ed61c
+			sub_gid_unlock();
9ed61c
+			return false;
9ed61c
+		}
9ed61c
 		db = &subordinate_gid_db;
9ed61c
-	return remove_range(db, range->owner, range->start, range->count) == 1;
9ed61c
+		break;
9ed61c
+	default:
9ed61c
+		return false;
9ed61c
+	}
9ed61c
+
9ed61c
+	ret = remove_range(db, range->owner, range->start, range->count) == 1;
9ed61c
+
9ed61c
+	if (id_type == ID_TYPE_UID) {
9ed61c
+		sub_uid_close();
9ed61c
+		sub_uid_unlock();
9ed61c
+	} else {
9ed61c
+		sub_gid_close();
9ed61c
+		sub_gid_unlock();
9ed61c
+	}
9ed61c
+
9ed61c
+	return ret;
9ed61c
 }
9ed61c
 
9ed61c
 #else				/* !ENABLE_SUBIDS */
9ed61c
diff -up shadow-4.6/lib/subordinateio.h.libsubid_nsswitch_support shadow-4.6/lib/subordinateio.h
9ed61c
--- shadow-4.6/lib/subordinateio.h.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/lib/subordinateio.h	2021-10-19 13:16:21.989493315 +0200
9ed61c
@@ -25,11 +25,11 @@ extern int sub_uid_unlock (void);
9ed61c
 extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
9ed61c
 extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
9ed61c
 extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
9ed61c
-extern struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type);
9ed61c
+extern int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges);
9ed61c
 extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse);
9ed61c
 extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type);
9ed61c
-extern int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type);
9ed61c
-extern void free_subordinate_ranges(struct subordinate_range **ranges);
9ed61c
+extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids);
9ed61c
+extern void free_subordinate_ranges(struct subordinate_range **ranges, int count);
9ed61c
 
9ed61c
 extern int sub_gid_close(void);
9ed61c
 extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
9ed61c
diff -up shadow-4.6/src/check_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/check_subid_range.c
9ed61c
--- shadow-4.6/src/check_subid_range.c.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/src/check_subid_range.c	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,48 @@
9ed61c
+// This program is for testing purposes only.
9ed61c
+// usage is "[program] owner [u|g] start count
9ed61c
+// Exits 0 if owner has subid range starting start, of size count
9ed61c
+// Exits 1 otherwise.
9ed61c
+
9ed61c
+#include <config.h>
9ed61c
+#include <stdio.h>
9ed61c
+#include <string.h>
9ed61c
+#include <errno.h>
9ed61c
+#include <stdbool.h>
9ed61c
+#include <stdlib.h>
9ed61c
+#include <sys/types.h>
9ed61c
+#include <sys/stat.h>
9ed61c
+#include <fcntl.h>
9ed61c
+#include "defines.h"
9ed61c
+#include "prototypes.h"
9ed61c
+#include "subordinateio.h"
9ed61c
+#include "idmapping.h"
9ed61c
+
9ed61c
+const char *Prog;
9ed61c
+
9ed61c
+int main(int argc, char **argv)
9ed61c
+{
9ed61c
+	char *owner;
9ed61c
+	unsigned long start, count;
9ed61c
+	bool check_uids;
9ed61c
+	Prog = Basename (argv[0]);
9ed61c
+
9ed61c
+	if (argc != 5)
9ed61c
+		exit(1);
9ed61c
+
9ed61c
+	owner = argv[1];
9ed61c
+	check_uids = argv[2][0] == 'u';
9ed61c
+	start = strtoul(argv[3], NULL, 10);
9ed61c
+	if (start == ULONG_MAX && errno == ERANGE)
9ed61c
+		exit(1);
9ed61c
+	count = strtoul(argv[4], NULL, 10);
9ed61c
+	if (count == ULONG_MAX && errno == ERANGE)
9ed61c
+		exit(1);
9ed61c
+	if (check_uids) {
9ed61c
+		if (have_sub_uids(owner, start, count))
9ed61c
+			exit(0);
9ed61c
+		exit(1);
9ed61c
+	}
9ed61c
+	if (have_sub_gids(owner, start, count))
9ed61c
+		exit(0);
9ed61c
+	exit(1);
9ed61c
+}
9ed61c
diff -up shadow-4.6/src/free_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/free_subid_range.c
9ed61c
--- shadow-4.6/src/free_subid_range.c.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/src/free_subid_range.c	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -1,6 +1,6 @@
9ed61c
 #include <stdio.h>
9ed61c
 #include <unistd.h>
9ed61c
-#include "api.h"
9ed61c
+#include "subid.h"
9ed61c
 #include "stdlib.h"
9ed61c
 #include "prototypes.h"
9ed61c
 
9ed61c
@@ -37,9 +37,9 @@ int main(int argc, char *argv[])
9ed61c
 	range.start = atoi(argv[1]);
9ed61c
 	range.count = atoi(argv[2]);
9ed61c
 	if (group)
9ed61c
-		ok = free_subgid_range(&range);
9ed61c
+		ok = ungrant_subgid_range(&range);
9ed61c
 	else
9ed61c
-		ok = free_subuid_range(&range);
9ed61c
+		ok = ungrant_subuid_range(&range);
9ed61c
 
9ed61c
 	if (!ok) {
9ed61c
 		fprintf(stderr, "Failed freeing id range\n");
9ed61c
diff -up shadow-4.6/src/get_subid_owners.c.libsubid_nsswitch_support shadow-4.6/src/get_subid_owners.c
9ed61c
--- shadow-4.6/src/get_subid_owners.c.libsubid_nsswitch_support	2021-10-19 13:16:21.986493267 +0200
9ed61c
+++ shadow-4.6/src/get_subid_owners.c	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -1,5 +1,5 @@
9ed61c
 #include <stdio.h>
9ed61c
-#include "api.h"
9ed61c
+#include "subid.h"
9ed61c
 #include "stdlib.h"
9ed61c
 #include "prototypes.h"
9ed61c
 
9ed61c
diff -up shadow-4.6/src/list_subid_ranges.c.libsubid_nsswitch_support shadow-4.6/src/list_subid_ranges.c
9ed61c
--- shadow-4.6/src/list_subid_ranges.c.libsubid_nsswitch_support	2021-10-19 13:16:21.987493283 +0200
9ed61c
+++ shadow-4.6/src/list_subid_ranges.c	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -1,5 +1,5 @@
9ed61c
 #include <stdio.h>
9ed61c
-#include "api.h"
9ed61c
+#include "subid.h"
9ed61c
 #include "stdlib.h"
9ed61c
 #include "prototypes.h"
9ed61c
 
9ed61c
@@ -15,7 +15,7 @@ void usage(void)
9ed61c
 
9ed61c
 int main(int argc, char *argv[])
9ed61c
 {
9ed61c
-	int i;
9ed61c
+	int i, count=0;
9ed61c
 	struct subordinate_range **ranges;
9ed61c
 
9ed61c
 	Prog = Basename (argv[0]);
9ed61c
@@ -23,19 +23,19 @@ int main(int argc, char *argv[])
9ed61c
 		usage();
9ed61c
 	}
9ed61c
 	if (argc == 3 && strcmp(argv[1], "-g") == 0)
9ed61c
-		ranges = get_subgid_ranges(argv[2]);
9ed61c
+		count = get_subgid_ranges(argv[2], &ranges;;
9ed61c
 	else if (argc == 2 && strcmp(argv[1], "-h") == 0)
9ed61c
 		usage();
9ed61c
 	else
9ed61c
-		ranges = get_subuid_ranges(argv[1]);
9ed61c
+		count = get_subuid_ranges(argv[1], &ranges;;
9ed61c
 	if (!ranges) {
9ed61c
 		fprintf(stderr, "Error fetching ranges\n");
9ed61c
 		exit(1);
9ed61c
 	}
9ed61c
-	for (i = 0; ranges[i]; i++) {
9ed61c
+	for (i = 0; i < count; i++) {
9ed61c
 		printf("%d: %s %lu %lu\n", i, ranges[i]->owner,
9ed61c
 			ranges[i]->start, ranges[i]->count);
9ed61c
 	}
9ed61c
-	subid_free_ranges(ranges);
9ed61c
+	subid_free_ranges(ranges, count);
9ed61c
 	return 0;
9ed61c
 }
9ed61c
diff -up shadow-4.6/src/Makefile.am.libsubid_nsswitch_support shadow-4.6/src/Makefile.am
9ed61c
--- shadow-4.6/src/Makefile.am.libsubid_nsswitch_support	2021-10-19 13:16:21.988493299 +0200
9ed61c
+++ shadow-4.6/src/Makefile.am	2021-10-19 13:20:47.920725652 +0200
9ed61c
@@ -69,7 +69,7 @@ shadowsgidubins = passwd
9ed61c
 endif
9ed61c
 
9ed61c
 LDADD          = $(INTLLIBS) \
9ed61c
-		 $(top_builddir)/libmisc/libmisc.a \
9ed61c
+		 $(top_builddir)/libmisc/libmisc.la \
9ed61c
 		 $(top_builddir)/lib/libshadow.la \
9ed61c
 		 $(LIBTCB)
9ed61c
 
9ed61c
@@ -86,17 +86,17 @@ LIBCRYPT_NOPAM = $(LIBCRYPT)
9ed61c
 endif
9ed61c
 
9ed61c
 chage_LDADD    = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
9ed61c
-newuidmap_LDADD    = $(LDADD) $(LIBSELINUX)
9ed61c
-newgidmap_LDADD    = $(LDADD) $(LIBSELINUX)
9ed61c
+newuidmap_LDADD    = $(LDADD) $(LIBSELINUX) -ldl
9ed61c
+newgidmap_LDADD    = $(LDADD) $(LIBSELINUX) -ldl
9ed61c
 chfn_LDADD     = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
9ed61c
 chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT)
9ed61c
 chsh_LDADD     = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
9ed61c
 chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT)
9ed61c
 gpasswd_LDADD  = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT)
9ed61c
-groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
9ed61c
-groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
9ed61c
+groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
9ed61c
+groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
9ed61c
 groupmems_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX)
9ed61c
-groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
9ed61c
+groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) -ldl
9ed61c
 grpck_LDADD    = $(LDADD) $(LIBSELINUX)
9ed61c
 grpconv_LDADD  = $(LDADD) $(LIBSELINUX)
9ed61c
 grpunconv_LDADD = $(LDADD) $(LIBSELINUX)
9ed61c
@@ -106,7 +106,7 @@ login_SOURCES  = \
9ed61c
 	login_nopam.c
9ed61c
 login_LDADD    = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
9ed61c
 newgrp_LDADD   = $(LDADD) $(LIBAUDIT) $(LIBCRYPT)
9ed61c
-newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT)
9ed61c
+newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) -ldl
9ed61c
 nologin_LDADD  =
9ed61c
 passwd_LDADD   = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM)
9ed61c
 pwck_LDADD     = $(LDADD) $(LIBSELINUX)
9ed61c
@@ -117,9 +117,9 @@ su_SOURCES     = \
9ed61c
 	suauth.c
9ed61c
 su_LDADD       = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
9ed61c
 sulogin_LDADD  = $(LDADD) $(LIBCRYPT)
9ed61c
-useradd_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR)
9ed61c
-userdel_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE)
9ed61c
-usermod_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR)
9ed61c
+useradd_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) -ldl
9ed61c
+userdel_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) -ldl
9ed61c
+usermod_LDADD  = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) -ldl
9ed61c
 vipw_LDADD     = $(LDADD) $(LIBSELINUX)
9ed61c
 
9ed61c
 install-am: all-am
9ed61c
@@ -143,7 +143,8 @@ if ENABLE_SUBIDS
9ed61c
 noinst_PROGRAMS += list_subid_ranges  \
9ed61c
 					get_subid_owners \
9ed61c
 					new_subid_range \
9ed61c
-					free_subid_range
9ed61c
+					free_subid_range \
9ed61c
+					check_subid_range
9ed61c
 
9ed61c
 MISCLIBS = \
9ed61c
 	$(LIBAUDIT) \
9ed61c
@@ -158,9 +159,9 @@ MISCLIBS = \
9ed61c
 
9ed61c
 list_subid_ranges_LDADD = \
9ed61c
 	$(top_builddir)/lib/libshadow.la \
9ed61c
-	$(top_builddir)/libmisc/libmisc.a \
9ed61c
+	$(top_builddir)/libmisc/libmisc.la \
9ed61c
 	$(top_builddir)/libsubid/libsubid.la \
9ed61c
-	$(MISCLIBS)
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
 
9ed61c
 list_subid_ranges_CPPFLAGS = \
9ed61c
 	-I$(top_srcdir)/lib \
9ed61c
@@ -169,9 +170,9 @@ list_subid_ranges_CPPFLAGS = \
9ed61c
 
9ed61c
 get_subid_owners_LDADD = \
9ed61c
 	$(top_builddir)/lib/libshadow.la \
9ed61c
-	$(top_builddir)/libmisc/libmisc.a \
9ed61c
+	$(top_builddir)/libmisc/libmisc.la \
9ed61c
 	$(top_builddir)/libsubid/libsubid.la \
9ed61c
-	$(MISCLIBS)
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
 
9ed61c
 get_subid_owners_CPPFLAGS = \
9ed61c
 	-I$(top_srcdir)/lib \
9ed61c
@@ -185,9 +186,9 @@ new_subid_range_CPPFLAGS = \
9ed61c
 
9ed61c
 new_subid_range_LDADD = \
9ed61c
 	$(top_builddir)/lib/libshadow.la \
9ed61c
-	$(top_builddir)/libmisc/libmisc.a \
9ed61c
+	$(top_builddir)/libmisc/libmisc.la \
9ed61c
 	$(top_builddir)/libsubid/libsubid.la \
9ed61c
-	$(MISCLIBS)
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
 
9ed61c
 free_subid_range_CPPFLAGS = \
9ed61c
 	-I$(top_srcdir)/lib \
9ed61c
@@ -196,7 +197,16 @@ free_subid_range_CPPFLAGS = \
9ed61c
 
9ed61c
 free_subid_range_LDADD = \
9ed61c
 	$(top_builddir)/lib/libshadow.la \
9ed61c
-	$(top_builddir)/libmisc/libmisc.a \
9ed61c
+	$(top_builddir)/libmisc/libmisc.la \
9ed61c
 	$(top_builddir)/libsubid/libsubid.la \
9ed61c
-	$(MISCLIBS)
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
+
9ed61c
+check_subid_range_CPPFLAGS = \
9ed61c
+	-I$(top_srcdir)/lib \
9ed61c
+	-I$(top_srcdir)/libmisc
9ed61c
+
9ed61c
+check_subid_range_LDADD = \
9ed61c
+	$(top_builddir)/lib/libshadow.la \
9ed61c
+	$(top_builddir)/libmisc/libmisc.la \
9ed61c
+	$(MISCLIBS) -ldl
9ed61c
 endif
9ed61c
diff -up shadow-4.6/src/new_subid_range.c.libsubid_nsswitch_support shadow-4.6/src/new_subid_range.c
9ed61c
--- shadow-4.6/src/new_subid_range.c.libsubid_nsswitch_support	2021-10-19 13:16:21.987493283 +0200
9ed61c
+++ shadow-4.6/src/new_subid_range.c	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -1,6 +1,6 @@
9ed61c
 #include <stdio.h>
9ed61c
 #include <unistd.h>
9ed61c
-#include "api.h"
9ed61c
+#include "subid.h"
9ed61c
 #include "stdlib.h"
9ed61c
 #include "prototypes.h"
9ed61c
 
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/empty.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/empty
9ed61c
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
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/libsubid_zzz.c	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,146 @@
9ed61c
+#include <sys/types.h>
9ed61c
+#include <pwd.h>
9ed61c
+#include <stdlib.h>
9ed61c
+#include <stdbool.h>
9ed61c
+#include <subid.h>
9ed61c
+#include <string.h>
9ed61c
+
9ed61c
+enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result)
9ed61c
+{
9ed61c
+	if (strcmp(owner, "ubuntu") == 0) {
9ed61c
+		*result = true;
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	}
9ed61c
+	if (strcmp(owner, "error") == 0) {
9ed61c
+		*result = false;
9ed61c
+		return SUBID_STATUS_ERROR;
9ed61c
+	}
9ed61c
+	if (strcmp(owner, "unknown") == 0) {
9ed61c
+		*result = false;
9ed61c
+		return SUBID_STATUS_UNKNOWN_USER;
9ed61c
+	}
9ed61c
+	if (strcmp(owner, "conn") == 0) {
9ed61c
+		*result = false;
9ed61c
+		return SUBID_STATUS_ERROR_CONN;
9ed61c
+	}
9ed61c
+	if (t == ID_TYPE_UID) {
9ed61c
+		*result = strcmp(owner, "user1") == 0;
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	}
9ed61c
+
9ed61c
+	*result = strcmp(owner, "group1") == 0;
9ed61c
+	return SUBID_STATUS_SUCCESS;
9ed61c
+}
9ed61c
+
9ed61c
+enum subid_status shadow_subid_has_range(const char *owner, unsigned long start, unsigned long count, enum subid_type t, bool *result)
9ed61c
+{
9ed61c
+	if (strcmp(owner, "ubuntu") == 0 &&
9ed61c
+	    start >= 200000 &&
9ed61c
+	    count <= 100000) {
9ed61c
+		*result = true;
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	}
9ed61c
+	*result = false;
9ed61c
+	if (strcmp(owner, "error") == 0)
9ed61c
+		return SUBID_STATUS_ERROR;
9ed61c
+	if (strcmp(owner, "unknown") == 0)
9ed61c
+		return SUBID_STATUS_UNKNOWN_USER;
9ed61c
+	if (strcmp(owner, "conn") == 0)
9ed61c
+		return SUBID_STATUS_ERROR_CONN;
9ed61c
+
9ed61c
+	if (t == ID_TYPE_UID && strcmp(owner, "user1") != 0)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	if (t == ID_TYPE_GID && strcmp(owner, "group1") != 0)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+
9ed61c
+	if (start < 100000)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	if (count >= 65536)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	*result = true;
9ed61c
+	return SUBID_STATUS_SUCCESS;
9ed61c
+}
9ed61c
+
9ed61c
+// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values,
9ed61c
+// to ease manual testing.  For automated testing, if you return those values,
9ed61c
+// we'll return 1000 for ubuntu and 1001 otherwise.
9ed61c
+static uid_t getnamuid(const char *name) {
9ed61c
+	struct passwd *pw;
9ed61c
+
9ed61c
+	pw = getpwnam(name);
9ed61c
+	if (pw)
9ed61c
+		return pw->pw_uid;
9ed61c
+
9ed61c
+	// For testing purposes
9ed61c
+	return strcmp(name, "ubuntu") == 0 ? (uid_t)1000 : (uid_t)1001;
9ed61c
+}
9ed61c
+
9ed61c
+static int alloc_uid(uid_t **uids, uid_t id) {
9ed61c
+	*uids = malloc(sizeof(uid_t));
9ed61c
+	if (!*uids)
9ed61c
+		return -1;
9ed61c
+	*uids[0] = id;
9ed61c
+	return 1;
9ed61c
+}
9ed61c
+
9ed61c
+enum subid_status shadow_subid_find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids, int *count)
9ed61c
+{
9ed61c
+	if (id >= 100000 && id < 165536) {
9ed61c
+		*count = alloc_uid(uids, getnamuid("user1"));
9ed61c
+		if (*count == 1)
9ed61c
+			return SUBID_STATUS_SUCCESS;
9ed61c
+		return SUBID_STATUS_ERROR; // out of memory
9ed61c
+	}
9ed61c
+	if (id >= 200000 && id < 300000) {
9ed61c
+		*count = alloc_uid(uids, getnamuid("ubuntu"));
9ed61c
+		if (*count == 1)
9ed61c
+			return SUBID_STATUS_SUCCESS;
9ed61c
+		return SUBID_STATUS_ERROR; // out of memory
9ed61c
+	}
9ed61c
+	*count = 0; // nothing found
9ed61c
+	return SUBID_STATUS_SUCCESS;
9ed61c
+}
9ed61c
+
9ed61c
+enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges, int *count)
9ed61c
+{
9ed61c
+	struct subordinate_range **ranges;
9ed61c
+
9ed61c
+	*count = 0;
9ed61c
+	if (strcmp(owner, "error") == 0)
9ed61c
+		return SUBID_STATUS_ERROR;
9ed61c
+	if (strcmp(owner, "unknown") == 0)
9ed61c
+		return SUBID_STATUS_UNKNOWN_USER;
9ed61c
+	if (strcmp(owner, "conn") == 0)
9ed61c
+		return SUBID_STATUS_ERROR_CONN;
9ed61c
+
9ed61c
+	*ranges = NULL;
9ed61c
+	if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 &&
9ed61c
+	    strcmp(owner, "group1") != 0)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	if (id_type == ID_TYPE_GID && strcmp(owner, "user1") == 0)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	if (id_type == ID_TYPE_UID && strcmp(owner, "group1") == 0)
9ed61c
+		return SUBID_STATUS_SUCCESS;
9ed61c
+	ranges = (struct subordinate_range **)malloc(sizeof(struct subordinate_range *));
9ed61c
+	if (!*ranges)
9ed61c
+		return SUBID_STATUS_ERROR;
9ed61c
+	ranges[0] = (struct subordinate_range *)malloc(sizeof(struct subordinate_range));
9ed61c
+	if (!ranges[0]) {
9ed61c
+		free(*ranges);
9ed61c
+		*ranges = NULL;
9ed61c
+		return SUBID_STATUS_ERROR;
9ed61c
+	}
9ed61c
+	ranges[0]->owner = strdup(owner);
9ed61c
+	if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) {
9ed61c
+		ranges[0]->start = 100000;
9ed61c
+		ranges[0]->count = 65536;
9ed61c
+	} else {
9ed61c
+		ranges[0]->start = 200000;
9ed61c
+		ranges[0]->count = 100000;
9ed61c
+	}
9ed61c
+
9ed61c
+	*count = 1;
9ed61c
+	*in_ranges = ranges;
9ed61c
+
9ed61c
+	return SUBID_STATUS_SUCCESS;
9ed61c
+}
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/Makefile.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/Makefile
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/Makefile.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/Makefile	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,12 @@
9ed61c
+all: test_nss libsubid_zzz.so
9ed61c
+
9ed61c
+test_nss: test_nss.c ../../../lib/nss.c
9ed61c
+	gcc -c -I../../../lib/ -I../../.. -o test_nss.o test_nss.c
9ed61c
+	gcc -o test_nss test_nss.o ../../../libmisc/.libs/libmisc.a ../../../lib/.libs/libshadow.a -ldl
9ed61c
+
9ed61c
+libsubid_zzz.so: libsubid_zzz.c
9ed61c
+	gcc -c -I../../../lib/ -I../../.. -I../../../libmisc -I../../../libsubid libsubid_zzz.c
9ed61c
+	gcc -L../../../libsubid -shared -o libsubid_zzz.so libsubid_zzz.o ../../../lib/.libs/libshadow.a -ldl
9ed61c
+
9ed61c
+clean:
9ed61c
+	rm -f *.o *.so test_nss
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/nsswitch1.conf	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,20 @@
9ed61c
+# /etc/nsswitch.conf
9ed61c
+#
9ed61c
+# Example configuration of GNU Name Service Switch functionality.
9ed61c
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
9ed61c
+# `info libc "Name Service Switch"' for information about this file.
9ed61c
+
9ed61c
+passwd:         files systemd
9ed61c
+group:          files systemd
9ed61c
+shadow:         files
9ed61c
+gshadow:        files
9ed61c
+
9ed61c
+hosts:          files mdns4_minimal [NOTFOUND=return] dns
9ed61c
+networks:       files
9ed61c
+
9ed61c
+protocols:      db files
9ed61c
+services:       db files
9ed61c
+ethers:         db files
9ed61c
+rpc:            db files
9ed61c
+
9ed61c
+netgroup:       nis
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/nsswitch2.conf	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,22 @@
9ed61c
+# /etc/nsswitch.conf
9ed61c
+#
9ed61c
+# Example configuration of GNU Name Service Switch functionality.
9ed61c
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
9ed61c
+# `info libc "Name Service Switch"' for information about this file.
9ed61c
+
9ed61c
+passwd:         files systemd
9ed61c
+group:          files systemd
9ed61c
+shadow:         files
9ed61c
+gshadow:        files
9ed61c
+
9ed61c
+hosts:          files mdns4_minimal [NOTFOUND=return] dns
9ed61c
+networks:       files
9ed61c
+
9ed61c
+protocols:      db files
9ed61c
+services:       db files
9ed61c
+ethers:         db files
9ed61c
+rpc:            db files
9ed61c
+
9ed61c
+netgroup:       nis
9ed61c
+
9ed61c
+subid:          files
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/nsswitch3.conf	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,22 @@
9ed61c
+# /etc/nsswitch.conf
9ed61c
+#
9ed61c
+# Example configuration of GNU Name Service Switch functionality.
9ed61c
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
9ed61c
+# `info libc "Name Service Switch"' for information about this file.
9ed61c
+
9ed61c
+passwd:         files systemd
9ed61c
+group:          files systemd
9ed61c
+shadow:         files
9ed61c
+gshadow:        files
9ed61c
+
9ed61c
+hosts:          files mdns4_minimal [NOTFOUND=return] dns
9ed61c
+networks:       files
9ed61c
+
9ed61c
+protocols:      db files
9ed61c
+services:       db files
9ed61c
+ethers:         db files
9ed61c
+rpc:            db files
9ed61c
+
9ed61c
+netgroup:       nis
9ed61c
+
9ed61c
+subid:          zzz
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/subidnss.test.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/subidnss.test
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/subidnss.test.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/subidnss.test	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,22 @@
9ed61c
+#!/bin/sh
9ed61c
+
9ed61c
+set -e
9ed61c
+
9ed61c
+cd $(dirname $0)
9ed61c
+
9ed61c
+. ../../common/config.sh
9ed61c
+. ../../common/log.sh
9ed61c
+
9ed61c
+make
9ed61c
+
9ed61c
+export LD_LIBRARY_PATH=.:../../../lib/.libs:$LD_LIBRARY_PATH
9ed61c
+
9ed61c
+./test_nss 1
9ed61c
+./test_nss 2
9ed61c
+./test_nss 3
9ed61c
+
9ed61c
+unshare -Urm ./test_range
9ed61c
+
9ed61c
+log_status "$0" "SUCCESS"
9ed61c
+
9ed61c
+trap '' 0
9ed61c
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
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/test_nss.c.libsubid_nsswitch_support	2021-10-19 13:16:21.990493331 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/test_nss.c	2021-10-19 13:16:21.990493331 +0200
9ed61c
@@ -0,0 +1,72 @@
9ed61c
+#include <stdio.h>
9ed61c
+#include <stdlib.h>
9ed61c
+#include <unistd.h>
9ed61c
+#include <prototypes.h>
9ed61c
+#include <stdbool.h>
9ed61c
+#include <dlfcn.h>
9ed61c
+
9ed61c
+extern bool nss_is_initialized();
9ed61c
+extern struct subid_nss_ops *get_subid_nss_handle();
9ed61c
+
9ed61c
+void test1() {
9ed61c
+	// nsswitch1 has no subid: entry
9ed61c
+	setenv("LD_LIBRARY_PATH", ".", 1);
9ed61c
+	printf("Test with no subid entry\n");
9ed61c
+	nss_init("./nsswitch1.conf");
9ed61c
+	if (!nss_is_initialized() || get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+	// second run should change nothing
9ed61c
+	printf("Test with no subid entry, second run\n");
9ed61c
+	nss_init("./nsswitch1.conf");
9ed61c
+	if (!nss_is_initialized() || get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+}
9ed61c
+
9ed61c
+void test2() {
9ed61c
+	// nsswitch2 has a subid: files entry
9ed61c
+	printf("test with 'files' subid entry\n");
9ed61c
+	nss_init("./nsswitch2.conf");
9ed61c
+	if (!nss_is_initialized() || get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+	// second run should change nothing
9ed61c
+	printf("test with 'files' subid entry, second run\n");
9ed61c
+	nss_init("./nsswitch2.conf");
9ed61c
+	if (!nss_is_initialized() || get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+}
9ed61c
+
9ed61c
+void test3() {
9ed61c
+	// nsswitch3 has a subid: testnss entry
9ed61c
+	printf("test with 'test' subid entry\n");
9ed61c
+	nss_init("./nsswitch3.conf");
9ed61c
+	if (!nss_is_initialized() || !get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+	// second run should change nothing
9ed61c
+	printf("test with 'test' subid entry, second run\n");
9ed61c
+	nss_init("./nsswitch3.conf");
9ed61c
+	if (!nss_is_initialized() || !get_subid_nss_handle())
9ed61c
+		exit(1);
9ed61c
+}
9ed61c
+
9ed61c
+const char *Prog;
9ed61c
+
9ed61c
+int main(int argc, char *argv[])
9ed61c
+{
9ed61c
+	int which;
9ed61c
+
9ed61c
+	Prog = Basename(argv[0]);
9ed61c
+
9ed61c
+	if (argc < 1)
9ed61c
+		exit(1);
9ed61c
+
9ed61c
+	which = atoi(argv[1]);
9ed61c
+	switch(which) {
9ed61c
+	case 1: test1(); break;
9ed61c
+	case 2: test2(); break;
9ed61c
+	case 3: test3(); break;
9ed61c
+	default: exit(1);
9ed61c
+	}
9ed61c
+
9ed61c
+	printf("nss parsing tests done\n");
9ed61c
+	exit(0);
9ed61c
+}
9ed61c
diff -up shadow-4.6/tests/libsubid/04_nss/test_range.libsubid_nsswitch_support shadow-4.6/tests/libsubid/04_nss/test_range
9ed61c
--- shadow-4.6/tests/libsubid/04_nss/test_range.libsubid_nsswitch_support	2021-10-19 13:16:21.991493347 +0200
9ed61c
+++ shadow-4.6/tests/libsubid/04_nss/test_range	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -0,0 +1,50 @@
9ed61c
+#!/bin/sh
9ed61c
+
9ed61c
+set -x
9ed61c
+
9ed61c
+echo "starting check_range tests"
9ed61c
+
9ed61c
+export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
9ed61c
+mount --bind ./nsswitch3.conf /etc/nsswitch.conf
9ed61c
+cleanup1() {
9ed61c
+    umount /etc/nsswitch.conf
9ed61c
+}
9ed61c
+trap cleanup1 EXIT HUP INT TERM
9ed61c
+../../../src/check_subid_range user1 u 100000 65535
9ed61c
+if [ $? -ne 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+../../../src/check_subid_range user2 u 100000 65535
9ed61c
+if [ $? -eq 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+../../../src/check_subid_range unknown u 100000 65535
9ed61c
+if [ $? -eq 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+../../../src/check_subid_range error u 100000 65535
9ed61c
+if [ $? -eq 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+../../../src/check_subid_range user1 u 1000 65535
9ed61c
+if [ $? -eq 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+
9ed61c
+umount /etc/nsswitch.conf
9ed61c
+
9ed61c
+mount --bind ./nsswitch1.conf /etc/nsswitch.conf
9ed61c
+mount --bind ./empty /etc/subuid
9ed61c
+
9ed61c
+cleanup2() {
9ed61c
+    umount /etc/subuid
9ed61c
+    umount /etc/nsswitch.conf
9ed61c
+}
9ed61c
+trap cleanup2 EXIT HUP INT TERM
9ed61c
+../../../src/check_subid_range user1 u 100000 65535
9ed61c
+if [ $? -eq 0 ]; then
9ed61c
+    exit 1
9ed61c
+fi
9ed61c
+
9ed61c
+echo "check_range tests complete"
9ed61c
+exit 0
9ed61c
diff -up shadow-4.6/tests/run_some.libsubid_nsswitch_support shadow-4.6/tests/run_some
9ed61c
--- shadow-4.6/tests/run_some.libsubid_nsswitch_support	2021-10-19 13:16:21.987493283 +0200
9ed61c
+++ shadow-4.6/tests/run_some	2021-10-19 13:16:21.991493347 +0200
9ed61c
@@ -123,6 +123,7 @@ run_test ./su/13_su_child_success/su.tes
9ed61c
 run_test ./libsubid/01_list_ranges/list_ranges.test
9ed61c
 run_test ./libsubid/02_get_subid_owners/get_subid_owners.test
9ed61c
 run_test ./libsubid/03_add_remove/add_remove_subids.test
9ed61c
+run_test ./libsubid/04_nss/subidnss.test
9ed61c
 
9ed61c
 echo
9ed61c
 echo "$succeeded test(s) passed"