shadow-utils/shadow-4.8.1-libsubid_nsswitch_support.patch
Iker Pedrosa fbf9d3a3ea libsubid initial implementation
Resolves: #1859252

- libsubid: creation and nsswitch support
- Creation of subid and subid-devel subpackages
- man: mention NSS in new[ug]idmap manpages
- libsubid: move development header to shadow folder
- libsubid: don't print error messages on stderr by default
- libsubid: libsubid_init return false if out of memory
- useradd: fix SUB_UID_COUNT=0
- 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
- man: clarify subid delegation
- libsubid: make shadow_logfd not extern
- login.defs: include HMAC_CRYPTO_ALGO key
2021-06-25 09:28:52 +02:00

2108 lines
61 KiB
Diff

From 514c1328b6c90d817ae0a9f7addfb3c9a11a275a Mon Sep 17 00:00:00 2001
From: Serge Hallyn <serge@hallyn.com>
Date: Sun, 31 Jan 2021 22:44:09 -0600
Subject: [PATCH 1/6] try again to fix libmisc sharing problem
Issue #297 reported seeing
*** Warning: Linking the shared library libsubid.la against the
*** static library ../libmisc/libmisc.a is not portable!
which commit b5fb1b38eea2fb0489ed088c82daf6700e72363e was supposed
to fix. But a few commits later it's back. So try to fix it
in the way the bug reporter suggested. This broke builds some
other ways, namely a few missing library specifications, so add
those.
Signed-off-by: Serge Hallyn <serge@hallyn.com>
---
configure.ac | 2 +-
libmisc/Makefile.am | 6 +++---
libsubid/Makefile.am | 6 ++++--
src/Makefile.am | 37 +++++++++++++++++++++++--------------
4 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/configure.ac b/configure.ac
index 6aaae6b7..7884bfb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,7 +55,7 @@ AC_CHECK_FUNCS(l64a fchmod fchown fsync futimes getgroups gethostname getspnam \
gettimeofday getusershell getutent initgroups lchown lckpwdf lstat \
lutimes memcpy memset setgroups sigaction strchr updwtmp updwtmpx innetgr \
getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getaddrinfo \
- ruserok)
+ ruserok dlopen)
AC_SYS_LARGEFILE
dnl Checks for typedefs, structures, and compiler characteristics.
diff --git a/libmisc/Makefile.am b/libmisc/Makefile.am
index 7f43161f..9766a7ec 100644
--- a/libmisc/Makefile.am
+++ b/libmisc/Makefile.am
@@ -3,9 +3,9 @@ EXTRA_DIST = .indent.pro xgetXXbyYY.c
AM_CPPFLAGS = -I$(top_srcdir)/lib $(ECONF_CPPFLAGS)
-noinst_LIBRARIES = libmisc.a
+noinst_LTLIBRARIES = libmisc.la
-libmisc_a_SOURCES = \
+libmisc_la_SOURCES = \
addgrps.c \
age.c \
audit_help.c \
@@ -74,6 +74,6 @@ libmisc_a_SOURCES = \
yesno.c
if WITH_BTRFS
-libmisc_a_SOURCES += btrfs.c
+libmisc_la_SOURCES += btrfs.c
endif
diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am
index 8bef1ecc..f24dbb94 100644
--- a/libsubid/Makefile.am
+++ b/libsubid/Makefile.am
@@ -12,12 +12,14 @@ MISCLIBS = \
$(LIBMD) \
$(LIBECONF) \
$(LIBCRYPT) \
+ $(LIBACL) \
+ $(LIBATTR) \
$(LIBTCB)
libsubid_la_LIBADD = \
$(top_srcdir)/lib/libshadow.la \
- $(MISCLIBS) \
- $(top_srcdir)/libmisc/libmisc.a
+ $(top_srcdir)/libmisc/libmisc.la \
+ $(MISCLIBS)
AM_CPPFLAGS = \
-I${top_srcdir}/lib \
diff --git a/src/Makefile.am b/src/Makefile.am
index 8499ce08..e9d354fd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -78,7 +78,7 @@ shadowsgidubins = passwd
endif
LDADD = $(INTLLIBS) \
- $(top_builddir)/libmisc/libmisc.a \
+ $(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/lib/libshadow.la \
$(LIBTCB)
@@ -95,28 +95,37 @@ LIBCRYPT_NOPAM = $(LIBCRYPT)
endif
chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
-newuidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP)
-newgidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP)
+newuidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP) -ldl
+newgidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP) -ldl
chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
expiry_LDADD = $(LDADD) $(LIBECONF)
gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
-groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
-groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
+groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF) -ldl
+groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF) -ldl
groupmems_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
-groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
+groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF) -ldl
grpck_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpunconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
lastlog_LDADD = $(LDADD) $(LIBAUDIT) $(LIBECONF)
+newuidmap_SOURCES = newuidmap.c ../libmisc/nss.c
+newgidmap_SOURCES = newgidmap.c ../libmisc/nss.c
+groupadd_SOURCES = groupadd.c ../libmisc/nss.c
+groupmod_SOURCES = groupmod.c ../libmisc/nss.c
+groupdel_SOURCES = groupdel.c ../libmisc/nss.c
+newusers_SOURCES = newusers.c ../libmisc/nss.c
+useradd_SOURCES = useradd.c ../libmisc/nss.c
+usermod_SOURCES = usermod.c ../libmisc/nss.c
+userdel_SOURCES = userdel.c ../libmisc/nss.c
login_SOURCES = \
login.c \
login_nopam.c
login_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
newgrp_LDADD = $(LDADD) $(LIBAUDIT) $(LIBCRYPT) $(LIBECONF)
-newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
+newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF) -ldl
nologin_LDADD =
passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBECONF)
pwck_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
@@ -127,9 +136,9 @@ su_SOURCES = \
suauth.c
su_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
sulogin_LDADD = $(LDADD) $(LIBCRYPT) $(LIBECONF)
-useradd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF)
-userdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBECONF)
-usermod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF)
+useradd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF) -ldl
+userdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBECONF) -ldl
+usermod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF) -ldl
vipw_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
install-am: all-am
@@ -175,7 +184,7 @@ 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)
@@ -186,7 +195,7 @@ 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)
@@ -202,7 +211,7 @@ 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)
@@ -213,7 +222,7 @@ 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)
endif
--
2.30.2
From 8492dee6632e340dee76eee895c3e30877bebf45 Mon Sep 17 00:00:00 2001
From: Serge Hallyn <serge@hallyn.com>
Date: Sun, 31 Jan 2021 17:38:20 -0600
Subject: [PATCH 2/6] subids: support nsswitch
Closes #154
When starting any operation to do with subuid delegation, check
nsswitch for a module to use. If none is specified, then use
the traditional /etc/subuid and /etc/subgid files.
Currently only one module is supported, and there is no fallback
to the files on errors. Several possibilities could be considered:
1. in case of connection error, fall back to files
2. in case of unknown user, also fall back to files
etc...
When non-files nss module is used, functions to edit the range
are not supported. It may make sense to support it, but it also
may make sense to require another tool to be used.
libsubordinateio also uses the nss_ helpers. This is how for instance
lxc could easily be converted to supporting nsswitch.
Add a set of test cases, including a dummy libsubid_zzz module. This
hardcodes values such that:
'ubuntu' gets 200000 - 300000
'user1' gets 100000 - 165536
'error' emulates an nss module error
'unknown' emulates a user unknown to the nss module
'conn' emulates a connection error ot the nss module
Changes to libsubid:
Change the list_owner_ranges api: return a count instead of making the array
null terminated.
This is a breaking change, so bump the libsubid abi major number.
Rename free_subuid_range and free_subgid_range to ungrant_subuid_range,
because otherwise it's confusing with free_subid_ranges which frees
memory.
Run libsubid tests in jenkins
Switch argument order in find_subid_owners
Move the db locking into subordinateio.c
Signed-off-by: Serge Hallyn <serge@hallyn.com>
---
configure.ac | 2 +-
lib/Makefile.am | 1 +
lib/nss.c | 157 ++++++++++++++++
lib/prototypes.h | 69 ++++++++
lib/subordinateio.c | 256 ++++++++++++++++++++++++---
lib/subordinateio.h | 6 +-
libmisc/idmapping.h | 2 +
libsubid/Makefile.am | 2 +-
libsubid/api.c | 168 +++---------------
libsubid/api.h | 10 +-
libsubid/subid.h | 8 +
src/Makefile.am | 29 +--
src/check_subid_range.c | 48 +++++
src/free_subid_range.c | 4 +-
src/list_subid_ranges.c | 10 +-
tests/libsubid/04_nss/Makefile | 12 ++
tests/libsubid/04_nss/empty | 0
tests/libsubid/04_nss/libsubid_zzz.c | 146 +++++++++++++++
tests/libsubid/04_nss/nsswitch1.conf | 20 +++
tests/libsubid/04_nss/nsswitch2.conf | 22 +++
tests/libsubid/04_nss/nsswitch3.conf | 22 +++
tests/libsubid/04_nss/subidnss.test | 22 +++
tests/libsubid/04_nss/test_nss.c | 72 ++++++++
tests/libsubid/04_nss/test_range | 50 ++++++
tests/run_some | 1 +
26 files changed, 935 insertions(+), 205 deletions(-)
create mode 100644 lib/nss.c
create mode 100644 src/check_subid_range.c
create mode 100644 tests/libsubid/04_nss/Makefile
create mode 100644 tests/libsubid/04_nss/empty
create mode 100644 tests/libsubid/04_nss/libsubid_zzz.c
create mode 100644 tests/libsubid/04_nss/nsswitch1.conf
create mode 100644 tests/libsubid/04_nss/nsswitch2.conf
create mode 100644 tests/libsubid/04_nss/nsswitch3.conf
create mode 100755 tests/libsubid/04_nss/subidnss.test
create mode 100644 tests/libsubid/04_nss/test_nss.c
create mode 100755 tests/libsubid/04_nss/test_range
diff --git a/configure.ac b/configure.ac
index 7884bfb6..7f7e8784 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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 --git a/lib/Makefile.am b/lib/Makefile.am
index bd9d6bfb..ecf3ee25 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -31,6 +31,7 @@ libshadow_la_SOURCES = \
groupio.h \
gshadow.c \
lockpw.c \
+ nss.c \
nscd.c \
nscd.h \
sssd.c \
diff --git a/lib/nss.c b/lib/nss.c
new file mode 100644
index 00000000..2f924740
--- /dev/null
+++ b/lib/nss.c
@@ -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 --git a/lib/prototypes.h b/lib/prototypes.h
index ac9ad274..0c42bcc2 100644
--- a/lib/prototypes.h
+++ b/lib/prototypes.h
@@ -262,6 +262,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 --git a/lib/subordinateio.c b/lib/subordinateio.c
index 67202c98..0bb29958 100644
--- a/lib/subordinateio.c
+++ b/lib/subordinateio.c
@@ -14,6 +14,7 @@
#include <sys/types.h>
#include <pwd.h>
#include <ctype.h>
+#include <fcntl.h>
/*
* subordinate_dup: create a duplicate range
@@ -311,17 +312,17 @@ static bool append_range(struct subordinate_range ***ranges, const struct subord
{
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;
@@ -329,13 +330,13 @@ static bool append_range(struct subordinate_range ***ranges, const struct subord
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);
}
@@ -602,21 +603,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);
}
@@ -684,21 +710,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);
}
@@ -720,42 +770,78 @@ gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
}
/*
- 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;
- if (id_type == ID_TYPE_UID)
+ *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;
+ }
+
+ 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)
@@ -808,17 +894,41 @@ static int append_uids(uid_t **uids, const char *owner, int n)
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) {
@@ -829,6 +939,11 @@ int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type)
}
}
+ if (id_type == ID_TYPE_UID)
+ sub_uid_close();
+ else
+ sub_gid_close();
+
return n;
}
@@ -836,11 +951,40 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b
{
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) {
@@ -856,20 +1000,74 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b
}
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 --git a/lib/subordinateio.h b/lib/subordinateio.h
index 13a21341..e4be482c 100644
--- a/lib/subordinateio.h
+++ b/lib/subordinateio.h
@@ -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 --git a/libmisc/idmapping.h b/libmisc/idmapping.h
index 3f32db68..1a8efe68 100644
--- a/libmisc/idmapping.h
+++ b/libmisc/idmapping.h
@@ -40,5 +40,7 @@ extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
extern void write_mapping(int proc_dir_fd, int ranges,
struct map_range *mappings, const char *map_file, uid_t ruid);
+extern void nss_init(char *nsswitch_path);
+
#endif /* _ID_MAPPING_H_ */
diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am
index f24dbb94..f543b5eb 100644
--- a/libsubid/Makefile.am
+++ b/libsubid/Makefile.am
@@ -19,7 +19,7 @@ MISCLIBS = \
libsubid_la_LIBADD = \
$(top_srcdir)/lib/libshadow.la \
$(top_srcdir)/libmisc/libmisc.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
AM_CPPFLAGS = \
-I${top_srcdir}/lib \
diff --git a/libsubid/api.c b/libsubid/api.c
index 91d73bed..737e1c8b 100644
--- a/libsubid/api.c
+++ b/libsubid/api.c
@@ -38,132 +38,48 @@
#include "idmapping.h"
#include "api.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 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)
+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 --git a/libsubid/api.h b/libsubid/api.h
index fbdf0f9e..97b04e25 100644
--- a/libsubid/api.h
+++ b/libsubid/api.h
@@ -1,9 +1,9 @@
#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_ranges(const char *owner, struct subordinate_range ***ranges);
+int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
+void subid_free_ranges(struct subordinate_range **ranges, int count);
int get_subuid_owners(uid_t uid, uid_t **owner);
int get_subgid_owners(gid_t gid, uid_t **owner);
@@ -13,5 +13,5 @@ int get_subgid_owners(gid_t gid, uid_t **owner);
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);
+bool ungrant_subuid_range(struct subordinate_range *range);
+bool ungrant_subgid_range(struct subordinate_range *range);
diff --git a/libsubid/subid.h b/libsubid/subid.h
index ba9a2f6f..2f27ad8a 100644
--- a/libsubid/subid.h
+++ b/libsubid/subid.h
@@ -1,4 +1,5 @@
#include <sys/types.h>
+#include <stdbool.h>
#ifndef SUBID_RANGE_DEFINED
#define SUBID_RANGE_DEFINED 1
@@ -13,5 +14,12 @@ 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,
+};
+
#define SUBID_NFIELDS 3
#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index e9d354fd..35027013 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,15 +111,6 @@ grpck_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpunconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
lastlog_LDADD = $(LDADD) $(LIBAUDIT) $(LIBECONF)
-newuidmap_SOURCES = newuidmap.c ../libmisc/nss.c
-newgidmap_SOURCES = newgidmap.c ../libmisc/nss.c
-groupadd_SOURCES = groupadd.c ../libmisc/nss.c
-groupmod_SOURCES = groupmod.c ../libmisc/nss.c
-groupdel_SOURCES = groupdel.c ../libmisc/nss.c
-newusers_SOURCES = newusers.c ../libmisc/nss.c
-useradd_SOURCES = useradd.c ../libmisc/nss.c
-usermod_SOURCES = usermod.c ../libmisc/nss.c
-userdel_SOURCES = userdel.c ../libmisc/nss.c
login_SOURCES = \
login.c \
login_nopam.c
@@ -169,7 +160,8 @@ endif
noinst_PROGRAMS += list_subid_ranges \
get_subid_owners \
new_subid_range \
- free_subid_range
+ free_subid_range \
+ check_subid_range
MISCLIBS = \
$(LIBAUDIT) \
@@ -186,7 +178,7 @@ list_subid_ranges_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
list_subid_ranges_CPPFLAGS = \
-I$(top_srcdir)/lib \
@@ -197,7 +189,7 @@ get_subid_owners_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
get_subid_owners_CPPFLAGS = \
-I$(top_srcdir)/lib \
@@ -213,7 +205,7 @@ new_subid_range_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
free_subid_range_CPPFLAGS = \
-I$(top_srcdir)/lib \
@@ -224,5 +216,14 @@ free_subid_range_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(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 --git a/src/check_subid_range.c b/src/check_subid_range.c
new file mode 100644
index 00000000..fb1c2cfc
--- /dev/null
+++ b/src/check_subid_range.c
@@ -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 --git a/src/free_subid_range.c b/src/free_subid_range.c
index 36858875..de6bc58f 100644
--- a/src/free_subid_range.c
+++ b/src/free_subid_range.c
@@ -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 --git a/src/list_subid_ranges.c b/src/list_subid_ranges.c
index cdba610e..440ef911 100644
--- a/src/list_subid_ranges.c
+++ b/src/list_subid_ranges.c
@@ -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 --git a/tests/libsubid/04_nss/Makefile b/tests/libsubid/04_nss/Makefile
new file mode 100644
index 00000000..6cd3225f
--- /dev/null
+++ b/tests/libsubid/04_nss/Makefile
@@ -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 --git a/tests/libsubid/04_nss/empty b/tests/libsubid/04_nss/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/libsubid/04_nss/libsubid_zzz.c b/tests/libsubid/04_nss/libsubid_zzz.c
new file mode 100644
index 00000000..b56a4bae
--- /dev/null
+++ b/tests/libsubid/04_nss/libsubid_zzz.c
@@ -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 --git a/tests/libsubid/04_nss/nsswitch1.conf b/tests/libsubid/04_nss/nsswitch1.conf
new file mode 100644
index 00000000..43764a39
--- /dev/null
+++ b/tests/libsubid/04_nss/nsswitch1.conf
@@ -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 --git a/tests/libsubid/04_nss/nsswitch2.conf b/tests/libsubid/04_nss/nsswitch2.conf
new file mode 100644
index 00000000..d371a36c
--- /dev/null
+++ b/tests/libsubid/04_nss/nsswitch2.conf
@@ -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 --git a/tests/libsubid/04_nss/nsswitch3.conf b/tests/libsubid/04_nss/nsswitch3.conf
new file mode 100644
index 00000000..19f2d934
--- /dev/null
+++ b/tests/libsubid/04_nss/nsswitch3.conf
@@ -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 --git a/tests/libsubid/04_nss/subidnss.test b/tests/libsubid/04_nss/subidnss.test
new file mode 100755
index 00000000..3d40dc8c
--- /dev/null
+++ b/tests/libsubid/04_nss/subidnss.test
@@ -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 --git a/tests/libsubid/04_nss/test_nss.c b/tests/libsubid/04_nss/test_nss.c
new file mode 100644
index 00000000..5d903ab4
--- /dev/null
+++ b/tests/libsubid/04_nss/test_nss.c
@@ -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 --git a/tests/libsubid/04_nss/test_range b/tests/libsubid/04_nss/test_range
new file mode 100755
index 00000000..356764fb
--- /dev/null
+++ b/tests/libsubid/04_nss/test_range
@@ -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 --git a/tests/run_some b/tests/run_some
index 15b4816a..e6f6eb5a 100755
--- a/tests/run_some
+++ b/tests/run_some
@@ -127,6 +127,7 @@ run_test ./newuidmap/01_newuidmap/newuidmap.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
+run_test ./libsubid/04_nss/subidnss.test
echo
echo "$succeeded test(s) passed"
--
2.30.2
From 0f4347d1483191b2142546416a9eefe0c9459600 Mon Sep 17 00:00:00 2001
From: Serge Hallyn <serge@hallyn.com>
Date: Thu, 15 Apr 2021 09:52:29 -0500
Subject: [PATCH 3/6] clean up libsubid headers
Move libsubid/api.h into libsubid/subid.h, and document the api in subid.h
Signed-off-by: Serge Hallyn <serge@hallyn.com>
---
libsubid/api.c | 2 +-
libsubid/api.h | 17 -------
libsubid/subid.h | 105 ++++++++++++++++++++++++++++++++++++++++
src/free_subid_range.c | 2 +-
src/get_subid_owners.c | 2 +-
src/list_subid_ranges.c | 2 +-
src/new_subid_range.c | 2 +-
7 files changed, 110 insertions(+), 22 deletions(-)
delete mode 100644 libsubid/api.h
diff --git a/libsubid/api.c b/libsubid/api.c
index 737e1c8b..a1b5bb3f 100644
--- a/libsubid/api.c
+++ b/libsubid/api.c
@@ -36,7 +36,7 @@
#include <stdbool.h>
#include "subordinateio.h"
#include "idmapping.h"
-#include "api.h"
+#include "subid.h"
static
int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
diff --git a/libsubid/api.h b/libsubid/api.h
deleted file mode 100644
index 97b04e25..00000000
--- a/libsubid/api.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "subid.h"
-#include <stdbool.h>
-
-int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges);
-int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
-void subid_free_ranges(struct subordinate_range **ranges, int count);
-
-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 ungrant_subuid_range(struct subordinate_range *range);
-bool ungrant_subgid_range(struct subordinate_range *range);
diff --git a/libsubid/subid.h b/libsubid/subid.h
index 2f27ad8a..769463f6 100644
--- a/libsubid/subid.h
+++ b/libsubid/subid.h
@@ -21,5 +21,110 @@ enum subid_status {
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 --git a/src/free_subid_range.c b/src/free_subid_range.c
index de6bc58f..3701a262 100644
--- a/src/free_subid_range.c
+++ b/src/free_subid_range.c
@@ -1,6 +1,6 @@
#include <stdio.h>
#include <unistd.h>
-#include "api.h"
+#include "subid.h"
#include "stdlib.h"
#include "prototypes.h"
diff --git a/src/get_subid_owners.c b/src/get_subid_owners.c
index a4385540..409e3fea 100644
--- a/src/get_subid_owners.c
+++ b/src/get_subid_owners.c
@@ -1,5 +1,5 @@
#include <stdio.h>
-#include "api.h"
+#include "subid.h"
#include "stdlib.h"
#include "prototypes.h"
diff --git a/src/list_subid_ranges.c b/src/list_subid_ranges.c
index 440ef911..21b2c192 100644
--- a/src/list_subid_ranges.c
+++ b/src/list_subid_ranges.c
@@ -1,5 +1,5 @@
#include <stdio.h>
-#include "api.h"
+#include "subid.h"
#include "stdlib.h"
#include "prototypes.h"
diff --git a/src/new_subid_range.c b/src/new_subid_range.c
index 6d7b033b..dde196b3 100644
--- a/src/new_subid_range.c
+++ b/src/new_subid_range.c
@@ -1,6 +1,6 @@
#include <stdio.h>
#include <unistd.h>
-#include "api.h"
+#include "subid.h"
#include "stdlib.h"
#include "prototypes.h"
--
2.30.2