diff --git a/shadow-4.8.1-libsubid_creation.patch b/shadow-4.8.1-libsubid_creation.patch new file mode 100644 index 0000000..072ed2e --- /dev/null +++ b/shadow-4.8.1-libsubid_creation.patch @@ -0,0 +1,1522 @@ +From 0a7888b1fad613a052b988b01a71933b67296e68 Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Sat, 18 Apr 2020 18:03:54 -0500 +Subject: [PATCH] Create a new libsubid + +Closes #154 + +Currently this has three functions: one which returns the +list of subuid ranges for a user, one returning the subgids, +and one which frees the ranges lists. + +I might be mistaken about what -disable-man means; some of +the code suggests it means just don't re-generate them, but +not totally ignore them. But that doesn't seem to really work, +so let's just ignore man/ when -disable-man. + +Remove --disable-shared. I'm not sure why it was there, but it stems +from long, long ago, and I suspect it comes from some ancient +toolchain bug. + +Create a tests/run_some, a shorter version of run_all. I'll +slowly add tests to this as I verify they work, then I can +work on fixing the once which don't. + +Also, don't touch man/ if not -enable-man. + +Changelog: + Apr 22: change the subid list api as recomended by Dan Walsh. + Apr 23: implement get_subid_owner + Apr 24: implement range add/release + Apr 25: finish tests and rebase + May 10: make @owner const + +Signed-off-by: Serge Hallyn +--- + Makefile.am | 13 +- + configure.ac | 3 +- + lib/subordinateio.c | 204 +++++++++++++++- + lib/subordinateio.h | 7 + + libsubid/Makefile.am | 25 ++ + libsubid/api.c | 231 ++++++++++++++++++ + libsubid/api.h | 17 ++ + libsubid/subid.h | 17 ++ + src/Makefile.am | 60 +++++ + src/free_subid_range.c | 50 ++++ + src/get_subid_owners.c | 40 +++ + src/list_subid_ranges.c | 41 ++++ + src/new_subid_range.c | 57 +++++ + tests/libsubid/01_list_ranges/config.txt | 0 + .../libsubid/01_list_ranges/config/etc/subgid | 2 + + .../libsubid/01_list_ranges/config/etc/subuid | 3 + + .../libsubid/01_list_ranges/list_ranges.test | 38 +++ + tests/libsubid/02_get_subid_owners/config.txt | 0 + .../02_get_subid_owners/config/etc/passwd | 20 ++ + .../02_get_subid_owners/config/etc/subgid | 2 + + .../02_get_subid_owners/config/etc/subuid | 4 + + .../02_get_subid_owners/get_subid_owners.test | 52 ++++ + .../03_add_remove/add_remove_subids.test | 59 +++++ + tests/libsubid/03_add_remove/config.txt | 0 + .../libsubid/03_add_remove/config/etc/passwd | 20 ++ + .../libsubid/03_add_remove/config/etc/subgid | 2 + + .../libsubid/03_add_remove/config/etc/subuid | 1 + + tests/run_some | 136 +++++++++++ + 31 files changed, 1105 insertions(+), 17 deletions(-) + create mode 100644 libsubid/Makefile.am + create mode 100644 libsubid/api.c + create mode 100644 libsubid/api.h + create mode 100644 libsubid/subid.h + create mode 100644 src/free_subid_range.c + create mode 100644 src/get_subid_owners.c + create mode 100644 src/list_subid_ranges.c + create mode 100644 src/new_subid_range.c + create mode 100644 tests/libsubid/01_list_ranges/config.txt + create mode 100644 tests/libsubid/01_list_ranges/config/etc/subgid + create mode 100644 tests/libsubid/01_list_ranges/config/etc/subuid + create mode 100755 tests/libsubid/01_list_ranges/list_ranges.test + create mode 100644 tests/libsubid/02_get_subid_owners/config.txt + create mode 100644 tests/libsubid/02_get_subid_owners/config/etc/passwd + create mode 100644 tests/libsubid/02_get_subid_owners/config/etc/subgid + create mode 100644 tests/libsubid/02_get_subid_owners/config/etc/subuid + create mode 100755 tests/libsubid/02_get_subid_owners/get_subid_owners.test + create mode 100755 tests/libsubid/03_add_remove/add_remove_subids.test + create mode 100644 tests/libsubid/03_add_remove/config.txt + create mode 100644 tests/libsubid/03_add_remove/config/etc/passwd + create mode 100644 tests/libsubid/03_add_remove/config/etc/subgid + create mode 100644 tests/libsubid/03_add_remove/config/etc/subuid + create mode 100755 tests/run_some + +diff --git a/Makefile.am b/Makefile.am +index 8851f5d6..b6456cf9 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -2,5 +2,14 @@ + + EXTRA_DIST = NEWS README TODO shadow.spec.in + +-SUBDIRS = po man libmisc lib src \ +- contrib doc etc ++SUBDIRS = libmisc lib ++ ++if ENABLE_SUBIDS ++SUBDIRS += libsubid ++endif ++ ++SUBDIRS += src po contrib doc etc ++ ++if ENABLE_REGENERATE_MAN ++SUBDIRS += man ++endif +diff --git a/configure.ac b/configure.ac +index b8bc2b7a..b766eff0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -22,8 +22,8 @@ test "$prefix" = "/usr" && exec_prefix="" + + AC_GNU_SOURCE + +-AM_DISABLE_SHARED + AM_ENABLE_STATIC ++AM_ENABLE_SHARED + + AM_MAINTAINER_MODE + +@@ -725,6 +725,7 @@ AC_CONFIG_FILES([ + man/zh_TW/Makefile + libmisc/Makefile + lib/Makefile ++ libsubid/Makefile + src/Makefile + contrib/Makefile + etc/Makefile +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 0d89a64e..67202c98 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -13,14 +13,7 @@ + #include "subordinateio.h" + #include + #include +- +-struct subordinate_range { +- const char *owner; +- unsigned long start; +- unsigned long count; +-}; +- +-#define NFIELDS 3 ++#include + + /* + * subordinate_dup: create a duplicate range +@@ -78,7 +71,7 @@ static void *subordinate_parse (const char *line) + static char rangebuf[1024]; + int i; + char *cp; +- char *fields[NFIELDS]; ++ char *fields[SUBID_NFIELDS]; + + /* + * Copy the string to a temporary buffer so the substrings can +@@ -93,7 +86,7 @@ static void *subordinate_parse (const char *line) + * field. The fields are converted into NUL terminated strings. + */ + +- for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) { ++ for (cp = rangebuf, i = 0; (i < SUBID_NFIELDS) && (NULL != cp); i++) { + fields[i] = cp; + while (('\0' != *cp) && (':' != *cp)) { + cp++; +@@ -108,10 +101,10 @@ static void *subordinate_parse (const char *line) + } + + /* +- * There must be exactly NFIELDS colon separated fields or ++ * There must be exactly SUBID_NFIELDS colon separated fields or + * the entry is invalid. Also, fields must be non-blank. + */ +- if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0') ++ if (i != SUBID_NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0') + return NULL; + range.owner = fields[0]; + if (getulong (fields[1], &range.start) == 0) +@@ -314,6 +307,39 @@ static bool have_range(struct commonio_db *db, + return false; + } + ++static bool append_range(struct subordinate_range ***ranges, const struct subordinate_range *new, int n) ++{ ++ struct subordinate_range *tmp; ++ if (!*ranges) { ++ *ranges = malloc(2 * sizeof(struct subordinate_range **)); ++ if (!*ranges) ++ return false; ++ } else { ++ struct subordinate_range **new; ++ new = realloc(*ranges, (n + 2) * (sizeof(struct subordinate_range **))); ++ if (!new) ++ return false; ++ *ranges = new; ++ } ++ (*ranges)[n] = (*ranges)[n+1] = NULL; ++ tmp = subordinate_dup(new); ++ if (!tmp) ++ return false; ++ (*ranges)[n] = tmp; ++ return true; ++} ++ ++void free_subordinate_ranges(struct subordinate_range **ranges) ++{ ++ int i; ++ ++ if (!ranges) ++ return; ++ for (i = 0; ranges[i]; i++) ++ subordinate_free(ranges[i]); ++ free(ranges); ++} ++ + /* + * subordinate_range_cmp: compare uid ranges + * +@@ -692,6 +718,160 @@ gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) + start = find_free_range (&subordinate_gid_db, min, max, count); + return start == ULONG_MAX ? (gid_t) -1 : start; + } ++ ++/* ++ struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type) ++ * ++ * @owner: username ++ * @id_type: UID or GUID ++ * ++ * Returns the subuid or subgid ranges which are owned by the specified ++ * user. Username may be a username or a string representation of a ++ * UID number. If id_type is UID, then subuids are returned, else ++ * subgids are returned. If there is an error, < 0 is returned. ++ * ++ * The caller must free the subordinate range list. ++ */ ++struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type) ++{ ++ // TODO - need to handle owner being either uid or username ++ const struct subordinate_range *range; ++ struct subordinate_range **ranges = NULL; ++ struct commonio_db *db; ++ int size = 0; ++ ++ if (id_type == ID_TYPE_UID) ++ db = &subordinate_uid_db; ++ else ++ db = &subordinate_gid_db; ++ ++ commonio_rewind(db); ++ while ((range = commonio_next(db)) != NULL) { ++ if (0 == strcmp(range->owner, owner)) { ++ if (!append_range(&ranges, range, size++)) { ++ free_subordinate_ranges(ranges); ++ return NULL; ++ } ++ } ++ } ++ ++ return ranges; ++} ++ ++static bool all_digits(const char *str) ++{ ++ int i; ++ ++ for (i = 0; str[i] != '\0'; i++) ++ if (!isdigit(str[i])) ++ return false; ++ return true; ++} ++ ++static int append_uids(uid_t **uids, const char *owner, int n) ++{ ++ uid_t owner_uid; ++ uid_t *ret; ++ int i; ++ ++ if (all_digits(owner)) { ++ i = sscanf(owner, "%d", &owner_uid); ++ if (i != 1) { ++ // should not happen ++ free(*uids); ++ *uids = NULL; ++ return -1; ++ } ++ } else { ++ struct passwd *pwd = getpwnam(owner); ++ if (NULL == pwd) { ++ /* Username not defined in /etc/passwd, or error occured during lookup */ ++ free(*uids); ++ *uids = NULL; ++ return -1; ++ } ++ owner_uid = pwd->pw_uid; ++ } ++ ++ for (i = 0; i < n; i++) { ++ if (owner_uid == (*uids)[i]) ++ return n; ++ } ++ ++ ret = realloc(*uids, (n + 1) * sizeof(uid_t)); ++ if (!ret) { ++ free(*uids); ++ return -1; ++ } ++ ret[n] = owner_uid; ++ *uids = ret; ++ return n+1; ++} ++ ++int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type) ++{ ++ const struct subordinate_range *range; ++ struct commonio_db *db; ++ int n = 0; ++ ++ *uids = NULL; ++ if (id_type == ID_TYPE_UID) ++ db = &subordinate_uid_db; ++ else ++ db = &subordinate_gid_db; ++ ++ commonio_rewind(db); ++ while ((range = commonio_next(db)) != NULL) { ++ if (id >= range->start && id < range->start + range-> count) { ++ n = append_uids(uids, range->owner, n); ++ if (n < 0) ++ break; ++ } ++ } ++ ++ return n; ++} ++ ++bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse) ++{ ++ struct commonio_db *db; ++ const struct subordinate_range *r; ++ ++ if (id_type == ID_TYPE_UID) ++ db = &subordinate_uid_db; ++ else ++ db = &subordinate_gid_db; ++ commonio_rewind(db); ++ if (reuse) { ++ while ((r = commonio_next(db)) != NULL) { ++ // TODO account for username vs uid_t ++ if (0 != strcmp(r->owner, range->owner)) ++ continue; ++ if (r->count >= range->count) { ++ range->count = r->count; ++ range->start = r->start; ++ return true; ++ } ++ } ++ } ++ ++ range->start = find_free_range(db, range->start, ULONG_MAX, range->count); ++ if (range->start == ULONG_MAX) ++ return false; ++ ++ return add_range(db, range->owner, range->start, range->count) == 1; ++} ++ ++bool release_subid_range(struct subordinate_range *range, enum subid_type id_type) ++{ ++ struct commonio_db *db; ++ if (id_type == ID_TYPE_UID) ++ db = &subordinate_uid_db; ++ else ++ db = &subordinate_gid_db; ++ return remove_range(db, range->owner, range->start, range->count) == 1; ++} ++ + #else /* !ENABLE_SUBIDS */ + extern int errno; /* warning: ANSI C forbids an empty source file */ + #endif /* !ENABLE_SUBIDS */ +diff --git a/lib/subordinateio.h b/lib/subordinateio.h +index a21d72b8..13a21341 100644 +--- a/lib/subordinateio.h ++++ b/lib/subordinateio.h +@@ -11,6 +11,8 @@ + + #include + ++#include "../libsubid/subid.h" ++ + extern int sub_uid_close(void); + extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count); + extern bool sub_uid_file_present (void); +@@ -23,6 +25,11 @@ extern int sub_uid_unlock (void); + extern int sub_uid_add (const char *owner, uid_t start, unsigned long count); + extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count); + extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count); ++extern struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type); ++extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse); ++extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type); ++extern int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type); ++extern void free_subordinate_ranges(struct subordinate_range **ranges); + + extern int sub_gid_close(void); + extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count); +diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am +new file mode 100644 +index 00000000..bdbc52c3 +--- /dev/null ++++ b/libsubid/Makefile.am +@@ -0,0 +1,25 @@ ++lib_LTLIBRARIES = libsubid.la ++libsubid_la_LDFLAGS = -Wl,-soname,libsubid.so.@LIBSUBID_ABI@ \ ++ -shared -version-info @LIBSUBID_ABI_MAJOR@ ++libsubid_la_SOURCES = api.c ++ ++MISCLIBS = \ ++ $(LIBAUDIT) \ ++ $(LIBSELINUX) \ ++ $(LIBSEMANAGE) \ ++ $(LIBCRYPT_NOPAM) \ ++ $(LIBSKEY) \ ++ $(LIBMD) \ ++ $(LIBECONF) \ ++ $(LIBCRYPT) \ ++ $(LIBTCB) ++ ++libsubid_la_LIBADD = \ ++ $(top_srcdir)/lib/libshadow.la \ ++ $(top_srcdir)/libmisc/libmisc.a \ ++ $(MISCLIBS) ++ ++AM_CPPFLAGS = \ ++ -I${top_srcdir}/lib \ ++ -I${top_srcdir}/libmisc \ ++ -DLOCALEDIR=\"$(datadir)/locale\" +diff --git a/libsubid/api.c b/libsubid/api.c +new file mode 100644 +index 00000000..91d73bed +--- /dev/null ++++ b/libsubid/api.c +@@ -0,0 +1,231 @@ ++/* ++ * Copyright (c) 2020 Serge Hallyn ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The name of the copyright holders or contributors may not be used to ++ * endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "subordinateio.h" ++#include "idmapping.h" ++#include "api.h" ++ ++static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type) ++{ ++ struct subordinate_range **ranges = NULL; ++ ++ switch (id_type) { ++ case ID_TYPE_UID: ++ if (!sub_uid_open(O_RDONLY)) { ++ return NULL; ++ } ++ break; ++ case ID_TYPE_GID: ++ if (!sub_gid_open(O_RDONLY)) { ++ return NULL; ++ } ++ break; ++ default: ++ return NULL; ++ } ++ ++ ranges = list_owner_ranges(owner, id_type); ++ ++ if (id_type == ID_TYPE_UID) ++ sub_uid_close(); ++ else ++ sub_gid_close(); ++ ++ return ranges; ++} ++ ++struct subordinate_range **get_subuid_ranges(const char *owner) ++{ ++ return get_subid_ranges(owner, ID_TYPE_UID); ++} ++ ++struct subordinate_range **get_subgid_ranges(const char *owner) ++{ ++ return get_subid_ranges(owner, ID_TYPE_GID); ++} ++ ++void subid_free_ranges(struct subordinate_range **ranges) ++{ ++ return free_subordinate_ranges(ranges); ++} ++ ++int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type) ++{ ++ int ret = -1; ++ ++ switch (id_type) { ++ case ID_TYPE_UID: ++ if (!sub_uid_open(O_RDONLY)) { ++ return -1; ++ } ++ break; ++ case ID_TYPE_GID: ++ if (!sub_gid_open(O_RDONLY)) { ++ return -1; ++ } ++ break; ++ default: ++ return -1; ++ } ++ ++ ret = find_subid_owners(id, owner, id_type); ++ ++ if (id_type == ID_TYPE_UID) ++ sub_uid_close(); ++ else ++ sub_gid_close(); ++ ++ return ret; ++} ++ ++int get_subuid_owners(uid_t uid, uid_t **owner) ++{ ++ return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID); ++} ++ ++int get_subgid_owners(gid_t gid, uid_t **owner) ++{ ++ return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID); ++} ++ ++bool grant_subid_range(struct subordinate_range *range, bool reuse, ++ enum subid_type id_type) ++{ ++ bool ret; ++ ++ switch (id_type) { ++ case ID_TYPE_UID: ++ if (!sub_uid_lock()) { ++ printf("Failed loging subuids (errno %d)\n", errno); ++ return false; ++ } ++ if (!sub_uid_open(O_CREAT | O_RDWR)) { ++ printf("Failed opening subuids (errno %d)\n", errno); ++ sub_uid_unlock(); ++ return false; ++ } ++ break; ++ case ID_TYPE_GID: ++ if (!sub_gid_lock()) { ++ printf("Failed loging subgids (errno %d)\n", errno); ++ return false; ++ } ++ if (!sub_gid_open(O_CREAT | O_RDWR)) { ++ printf("Failed opening subgids (errno %d)\n", errno); ++ sub_gid_unlock(); ++ return false; ++ } ++ break; ++ default: ++ return false; ++ } ++ ++ ret = new_subid_range(range, id_type, reuse); ++ ++ if (id_type == ID_TYPE_UID) { ++ sub_uid_close(); ++ sub_uid_unlock(); ++ } else { ++ sub_gid_close(); ++ sub_gid_unlock(); ++ } ++ ++ return ret; ++} ++ ++bool grant_subuid_range(struct subordinate_range *range, bool reuse) ++{ ++ return grant_subid_range(range, reuse, ID_TYPE_UID); ++} ++ ++bool grant_subgid_range(struct subordinate_range *range, bool reuse) ++{ ++ return grant_subid_range(range, reuse, ID_TYPE_GID); ++} ++ ++bool free_subid_range(struct subordinate_range *range, enum subid_type id_type) ++{ ++ bool ret; ++ ++ switch (id_type) { ++ case ID_TYPE_UID: ++ if (!sub_uid_lock()) { ++ printf("Failed loging subuids (errno %d)\n", errno); ++ return false; ++ } ++ if (!sub_uid_open(O_CREAT | O_RDWR)) { ++ printf("Failed opening subuids (errno %d)\n", errno); ++ sub_uid_unlock(); ++ return false; ++ } ++ break; ++ case ID_TYPE_GID: ++ if (!sub_gid_lock()) { ++ printf("Failed loging subgids (errno %d)\n", errno); ++ return false; ++ } ++ if (!sub_gid_open(O_CREAT | O_RDWR)) { ++ printf("Failed opening subgids (errno %d)\n", errno); ++ sub_gid_unlock(); ++ return false; ++ } ++ break; ++ default: ++ return false; ++ } ++ ++ ret = release_subid_range(range, id_type); ++ ++ if (id_type == ID_TYPE_UID) { ++ sub_uid_close(); ++ sub_uid_unlock(); ++ } else { ++ sub_gid_close(); ++ sub_gid_unlock(); ++ } ++ ++ return ret; ++} ++ ++bool free_subuid_range(struct subordinate_range *range) ++{ ++ return free_subid_range(range, ID_TYPE_UID); ++} ++ ++bool free_subgid_range(struct subordinate_range *range) ++{ ++ return free_subid_range(range, ID_TYPE_GID); ++} +diff --git a/libsubid/api.h b/libsubid/api.h +new file mode 100644 +index 00000000..f65e3ec9 +--- /dev/null ++++ b/libsubid/api.h +@@ -0,0 +1,17 @@ ++#include "subid.h" ++#include ++ ++struct subordinate_range **get_subuid_ranges(const char *owner); ++struct subordinate_range **get_subgid_ranges(const char *owner); ++void subid_free_ranges(struct subordinate_range **ranges); ++ ++int get_subuid_owners(uid_t uid, uid_t **owner); ++int get_subgid_owners(uid_t uid, uid_t **owner); ++ ++/* range should be pre-allocated with owner and count filled in, start is ++ * ignored, can be 0 */ ++bool grant_subuid_range(struct subordinate_range *range, bool reuse); ++bool grant_subgid_range(struct subordinate_range *range, bool reuse); ++ ++bool free_subuid_range(struct subordinate_range *range); ++bool free_subgid_range(struct subordinate_range *range); +diff --git a/libsubid/subid.h b/libsubid/subid.h +new file mode 100644 +index 00000000..ba9a2f6f +--- /dev/null ++++ b/libsubid/subid.h +@@ -0,0 +1,17 @@ ++#include ++ ++#ifndef SUBID_RANGE_DEFINED ++#define SUBID_RANGE_DEFINED 1 ++struct subordinate_range { ++ const char *owner; ++ unsigned long start; ++ unsigned long count; ++}; ++ ++enum subid_type { ++ ID_TYPE_UID = 1, ++ ID_TYPE_GID = 2 ++}; ++ ++#define SUBID_NFIELDS 3 ++#endif +diff --git a/src/Makefile.am b/src/Makefile.am +index f175928a..8499ce08 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -156,4 +156,64 @@ if FCAPS + setcap cap_setuid+ep $(DESTDIR)$(ubindir)/newuidmap + setcap cap_setgid+ep $(DESTDIR)$(ubindir)/newgidmap + endif ++ ++noinst_PROGRAMS += list_subid_ranges \ ++ get_subid_owners \ ++ new_subid_range \ ++ free_subid_range ++ ++MISCLIBS = \ ++ $(LIBAUDIT) \ ++ $(LIBSELINUX) \ ++ $(LIBSEMANAGE) \ ++ $(LIBCRYPT_NOPAM) \ ++ $(LIBSKEY) \ ++ $(LIBMD) \ ++ $(LIBECONF) \ ++ $(LIBCRYPT) \ ++ $(LIBTCB) ++ ++list_subid_ranges_LDADD = \ ++ $(top_builddir)/lib/libshadow.la \ ++ $(top_builddir)/libmisc/libmisc.a \ ++ $(top_builddir)/libsubid/libsubid.la \ ++ $(MISCLIBS) ++ ++list_subid_ranges_CPPFLAGS = \ ++ -I$(top_srcdir)/lib \ ++ -I$(top_srcdir)/libmisc \ ++ -I$(top_srcdir)/libsubid ++ ++get_subid_owners_LDADD = \ ++ $(top_builddir)/lib/libshadow.la \ ++ $(top_builddir)/libmisc/libmisc.a \ ++ $(top_builddir)/libsubid/libsubid.la \ ++ $(MISCLIBS) ++ ++get_subid_owners_CPPFLAGS = \ ++ -I$(top_srcdir)/lib \ ++ -I$(top_srcdir)/libmisc \ ++ -I$(top_srcdir)/libsubid ++ ++new_subid_range_CPPFLAGS = \ ++ -I$(top_srcdir)/lib \ ++ -I$(top_srcdir)/libmisc \ ++ -I$(top_srcdir)/libsubid ++ ++new_subid_range_LDADD = \ ++ $(top_builddir)/lib/libshadow.la \ ++ $(top_builddir)/libmisc/libmisc.a \ ++ $(top_builddir)/libsubid/libsubid.la \ ++ $(MISCLIBS) ++ ++free_subid_range_CPPFLAGS = \ ++ -I$(top_srcdir)/lib \ ++ -I$(top_srcdir)/libmisc \ ++ -I$(top_srcdir)/libsubid ++ ++free_subid_range_LDADD = \ ++ $(top_builddir)/lib/libshadow.la \ ++ $(top_builddir)/libmisc/libmisc.a \ ++ $(top_builddir)/libsubid/libsubid.la \ ++ $(MISCLIBS) + endif +diff --git a/src/free_subid_range.c b/src/free_subid_range.c +new file mode 100644 +index 00000000..36858875 +--- /dev/null ++++ b/src/free_subid_range.c +@@ -0,0 +1,50 @@ ++#include ++#include ++#include "api.h" ++#include "stdlib.h" ++#include "prototypes.h" ++ ++/* Test program for the subid freeing routine */ ++ ++const char *Prog; ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: %s [-g] user start count\n", Prog); ++ fprintf(stderr, " Release a user's subuid (or with -g, subgid) range\n"); ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int c; ++ bool ok; ++ struct subordinate_range range; ++ bool group = false; // get subuids by default ++ ++ Prog = Basename (argv[0]); ++ while ((c = getopt(argc, argv, "g")) != EOF) { ++ switch(c) { ++ case 'g': group = true; break; ++ default: usage(); ++ } ++ } ++ argv = &argv[optind]; ++ argc = argc - optind; ++ if (argc < 3) ++ usage(); ++ range.owner = argv[0]; ++ range.start = atoi(argv[1]); ++ range.count = atoi(argv[2]); ++ if (group) ++ ok = free_subgid_range(&range); ++ else ++ ok = free_subuid_range(&range); ++ ++ if (!ok) { ++ fprintf(stderr, "Failed freeing id range\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ return 0; ++} +diff --git a/src/get_subid_owners.c b/src/get_subid_owners.c +new file mode 100644 +index 00000000..a4385540 +--- /dev/null ++++ b/src/get_subid_owners.c +@@ -0,0 +1,40 @@ ++#include ++#include "api.h" ++#include "stdlib.h" ++#include "prototypes.h" ++ ++const char *Prog; ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: [-g] %s subuid\n", Prog); ++ fprintf(stderr, " list uids who own the given subuid\n"); ++ fprintf(stderr, " pass -g to query a subgid\n"); ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int i, n; ++ uid_t *uids; ++ ++ Prog = Basename (argv[0]); ++ if (argc < 2) { ++ usage(); ++ } ++ if (argc == 3 && strcmp(argv[1], "-g") == 0) ++ n = get_subgid_owners(atoi(argv[2]), &uids); ++ else if (argc == 2 && strcmp(argv[1], "-h") == 0) ++ usage(); ++ else ++ n = get_subuid_owners(atoi(argv[1]), &uids); ++ if (n < 0) { ++ fprintf(stderr, "No owners found\n"); ++ exit(1); ++ } ++ for (i = 0; i < n; i++) { ++ printf("%d\n", uids[i]); ++ } ++ free(uids); ++ return 0; ++} +diff --git a/src/list_subid_ranges.c b/src/list_subid_ranges.c +new file mode 100644 +index 00000000..cdba610e +--- /dev/null ++++ b/src/list_subid_ranges.c +@@ -0,0 +1,41 @@ ++#include ++#include "api.h" ++#include "stdlib.h" ++#include "prototypes.h" ++ ++const char *Prog; ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: %s [-g] user\n", Prog); ++ fprintf(stderr, " list subuid ranges for user\n"); ++ fprintf(stderr, " pass -g to list subgid ranges\n"); ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int i; ++ struct subordinate_range **ranges; ++ ++ Prog = Basename (argv[0]); ++ if (argc < 2) { ++ usage(); ++ } ++ if (argc == 3 && strcmp(argv[1], "-g") == 0) ++ ranges = get_subgid_ranges(argv[2]); ++ else if (argc == 2 && strcmp(argv[1], "-h") == 0) ++ usage(); ++ else ++ ranges = get_subuid_ranges(argv[1]); ++ if (!ranges) { ++ fprintf(stderr, "Error fetching ranges\n"); ++ exit(1); ++ } ++ for (i = 0; ranges[i]; i++) { ++ printf("%d: %s %lu %lu\n", i, ranges[i]->owner, ++ ranges[i]->start, ranges[i]->count); ++ } ++ subid_free_ranges(ranges); ++ return 0; ++} +diff --git a/src/new_subid_range.c b/src/new_subid_range.c +new file mode 100644 +index 00000000..6d7b033b +--- /dev/null ++++ b/src/new_subid_range.c +@@ -0,0 +1,57 @@ ++#include ++#include ++#include "api.h" ++#include "stdlib.h" ++#include "prototypes.h" ++ ++/* Test program for the subid creation routine */ ++ ++const char *Prog; ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: %s [-g] [-n] user count\n", Prog); ++ fprintf(stderr, " Find a subuid (or with -g, subgid) range for user\n"); ++ fprintf(stderr, " If -n is given, a new range will be created even if one exists\n"); ++ fprintf(stderr, " count defaults to 65536\n"); ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int c; ++ struct subordinate_range range; ++ bool makenew = false; // reuse existing by default ++ bool group = false; // get subuids by default ++ bool ok; ++ ++ Prog = Basename (argv[0]); ++ while ((c = getopt(argc, argv, "gn")) != EOF) { ++ switch(c) { ++ case 'n': makenew = true; break; ++ case 'g': group = true; break; ++ default: usage(); ++ } ++ } ++ argv = &argv[optind]; ++ argc = argc - optind; ++ if (argc == 0) ++ usage(); ++ range.owner = argv[0]; ++ range.start = 0; ++ range.count = 65536; ++ if (argc > 1) ++ range.count = atoi(argv[1]); ++ if (group) ++ ok = grant_subgid_range(&range, !makenew); ++ else ++ ok = grant_subuid_range(&range, !makenew); ++ ++ if (!ok) { ++ fprintf(stderr, "Failed creating new id range\n"); ++ exit(EXIT_FAILURE); ++ } ++ printf("Subuid range %lu:%lu\n", range.start, range.count); ++ ++ return 0; ++} +diff --git a/tests/libsubid/01_list_ranges/config.txt b/tests/libsubid/01_list_ranges/config.txt +new file mode 100644 +index 00000000..e69de29b +diff --git a/tests/libsubid/01_list_ranges/config/etc/subgid b/tests/libsubid/01_list_ranges/config/etc/subgid +new file mode 100644 +index 00000000..b9495cfc +--- /dev/null ++++ b/tests/libsubid/01_list_ranges/config/etc/subgid +@@ -0,0 +1,2 @@ ++foo:200000:10000 ++root:500000:1000 +diff --git a/tests/libsubid/01_list_ranges/config/etc/subuid b/tests/libsubid/01_list_ranges/config/etc/subuid +new file mode 100644 +index 00000000..e5c537be +--- /dev/null ++++ b/tests/libsubid/01_list_ranges/config/etc/subuid +@@ -0,0 +1,3 @@ ++foo:300000:10000 ++foo:400000:10000 ++root:500000:1000 +diff --git a/tests/libsubid/01_list_ranges/list_ranges.test b/tests/libsubid/01_list_ranges/list_ranges.test +new file mode 100755 +index 00000000..b131303b +--- /dev/null ++++ b/tests/libsubid/01_list_ranges/list_ranges.test +@@ -0,0 +1,38 @@ ++#!/bin/sh ++ ++set -e ++ ++cd $(dirname $0) ++ ++. ../../common/config.sh ++. ../../common/log.sh ++ ++log_start "$0" "list_ranges shows subid ranges" ++ ++save_config ++ ++# restore the files on exit ++trap 'log_status "$0" "FAILURE"; restore_config' 0 ++ ++change_config ++ ++echo -n "list foo's ranges..." ++${build_path}/src/list_subid_ranges foo > /tmp/subuidlistout ++${build_path}/src/list_subid_ranges -g foo > /tmp/subgidlistout ++echo "OK" ++ ++echo -n "Check the subuid ranges..." ++[ $(wc -l /tmp/subuidlistout | awk '{ print $1 }') -eq 2 ] ++grep "0: foo 300000 10000" /tmp/subuidlistout ++grep "1: foo 400000 10000" /tmp/subuidlistout ++echo "OK" ++ ++echo -n "Check the subgid ranges..." ++[ $(wc -l /tmp/subgidlistout | awk '{ print $1 }') -eq 1 ] ++grep "0: foo 200000 10000" /tmp/subgidlistout ++echo "OK" ++ ++log_status "$0" "SUCCESS" ++restore_config ++trap '' 0 ++ +diff --git a/tests/libsubid/02_get_subid_owners/config.txt b/tests/libsubid/02_get_subid_owners/config.txt +new file mode 100644 +index 00000000..e69de29b +diff --git a/tests/libsubid/02_get_subid_owners/config/etc/passwd b/tests/libsubid/02_get_subid_owners/config/etc/passwd +new file mode 100644 +index 00000000..bf52df00 +--- /dev/null ++++ b/tests/libsubid/02_get_subid_owners/config/etc/passwd +@@ -0,0 +1,20 @@ ++root:x:0:0:root:/root:/bin/bash ++daemon:x:1:1:daemon:/usr/sbin:/bin/sh ++bin:x:2:2:bin:/bin:/bin/sh ++sys:x:3:3:sys:/dev:/bin/sh ++sync:x:4:65534:sync:/bin:/bin/sync ++games:x:5:60:games:/usr/games:/bin/sh ++man:x:6:12:man:/var/cache/man:/bin/sh ++lp:x:7:7:lp:/var/spool/lpd:/bin/sh ++mail:x:8:8:mail:/var/mail:/bin/sh ++news:x:9:9:news:/var/spool/news:/bin/sh ++uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh ++proxy:x:13:13:proxy:/bin:/bin/sh ++www-data:x:33:33:www-data:/var/www:/bin/sh ++backup:x:34:34:backup:/var/backups:/bin/sh ++list:x:38:38:Mailing List Manager:/var/list:/bin/sh ++irc:x:39:39:ircd:/var/run/ircd:/bin/sh ++gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh ++nobody:x:65534:65534:nobody:/nonexistent:/bin/sh ++Debian-exim:x:102:102::/var/spool/exim4:/bin/false ++foo:x:1000:1000::/home/foo:/bin/false +diff --git a/tests/libsubid/02_get_subid_owners/config/etc/subgid b/tests/libsubid/02_get_subid_owners/config/etc/subgid +new file mode 100644 +index 00000000..b9495cfc +--- /dev/null ++++ b/tests/libsubid/02_get_subid_owners/config/etc/subgid +@@ -0,0 +1,2 @@ ++foo:200000:10000 ++root:500000:1000 +diff --git a/tests/libsubid/02_get_subid_owners/config/etc/subuid b/tests/libsubid/02_get_subid_owners/config/etc/subuid +new file mode 100644 +index 00000000..dd11875a +--- /dev/null ++++ b/tests/libsubid/02_get_subid_owners/config/etc/subuid +@@ -0,0 +1,4 @@ ++foo:300000:10000 ++foo:400000:10000 ++foo:500000:10000 ++root:500000:1000 +diff --git a/tests/libsubid/02_get_subid_owners/get_subid_owners.test b/tests/libsubid/02_get_subid_owners/get_subid_owners.test +new file mode 100755 +index 00000000..145477cb +--- /dev/null ++++ b/tests/libsubid/02_get_subid_owners/get_subid_owners.test +@@ -0,0 +1,52 @@ ++#!/bin/sh ++ ++set -e ++ ++cd $(dirname $0) ++ ++. ../../common/config.sh ++. ../../common/log.sh ++ ++log_start "$0" "get subid owners" ++ ++save_config ++ ++# restore the files on exit ++trap 'log_status "$0" "FAILURE"; restore_config' 0 ++ ++change_config ++ ++echo -n "Noone owns 0 as a subid..." ++[ -z "$(${build_path}/src/get_subid_owners 0)" ] ++echo "OK" ++ ++echo -n "foo owns subuid 300000..." ++[ "$(${build_path}/src/get_subid_owners 300000)" = "1000" ] ++echo "OK" ++ ++echo -n "foo owns subgid 200000..." ++[ "$(${build_path}/src/get_subid_owners -g 200000)" = "1000" ] ++echo "OK" ++ ++echo -n "Noone owns subuid 200000..." ++[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ] ++echo "OK" ++ ++echo -n "Noone owns subgid 300000..." ++[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ] ++echo "OK" ++ ++echo -n "Both foo and root own subuid 500000..." ++cat > /tmp/expected << EOF ++1000 ++0 ++EOF ++${build_path}/src/get_subid_owners 500000 > /tmp/actual ++diff /tmp/expected /tmp/actual ++ ++echo "OK" ++ ++log_status "$0" "SUCCESS" ++restore_config ++trap '' 0 ++ +diff --git a/tests/libsubid/03_add_remove/add_remove_subids.test b/tests/libsubid/03_add_remove/add_remove_subids.test +new file mode 100755 +index 00000000..a24a9752 +--- /dev/null ++++ b/tests/libsubid/03_add_remove/add_remove_subids.test +@@ -0,0 +1,59 @@ ++#!/bin/sh ++ ++set -e ++ ++cd $(dirname $0) ++ ++. ../../common/config.sh ++. ../../common/log.sh ++ ++log_start "$0" "add and remove subid ranges" ++ ++save_config ++ ++# restore the files on exit ++trap 'log_status "$0" "FAILURE"; restore_config' 0 ++ ++change_config ++ ++echo -n "Existing ranges returned when possible..." ++res=$(${build_path}/src/new_subid_range foo 500) ++echo "debug" ++echo "res is $res" ++echo "wanted Subuid range 300000:10000" ++echo "end debug" ++[ "$res" = "Subuid range 300000:10000" ] ++[ $(grep -c foo /etc/subuid) -eq 1 ] ++echo "OK" ++ ++echo -n "New range returned if requested..." ++res=$(${build_path}/src/new_subid_range foo 500 -n) ++[ "$res" = "Subuid range 310000:500" ] ++[ $(grep -c foo /etc/subuid) -eq 2 ] ++echo "OK" ++ ++echo -n "Free works..." ++res=$(${build_path}/src/free_subid_range foo 310000 500) ++[ $(grep -c foo /etc/subuid) -eq 1 ] ++echo "OK" ++ ++echo -n "Subgids work too..." ++res=$(${build_path}/src/new_subid_range -g foo 100000) ++echo "DEBUG: res is ${res}" ++[ "$res" = "Subuid range 501000:100000" ] ++echo "DEBUG: subgid is:" ++cat /etc/subgid ++[ $(grep -c foo /etc/subgid) -eq 2 ] ++ ++echo -n "Subgid free works..." ++res=$(${build_path}/src/free_subid_range -g foo 501000 100000) ++echo "DEBUG: res is ${res}" ++echo "DEBUG: subgid is:" ++cat /etc/subgid ++[ $(grep -c foo /etc/subgid) -eq 1 ] ++echo "OK" ++ ++log_status "$0" "SUCCESS" ++restore_config ++trap '' 0 ++ +diff --git a/tests/libsubid/03_add_remove/config.txt b/tests/libsubid/03_add_remove/config.txt +new file mode 100644 +index 00000000..e69de29b +diff --git a/tests/libsubid/03_add_remove/config/etc/passwd b/tests/libsubid/03_add_remove/config/etc/passwd +new file mode 100644 +index 00000000..bf52df00 +--- /dev/null ++++ b/tests/libsubid/03_add_remove/config/etc/passwd +@@ -0,0 +1,20 @@ ++root:x:0:0:root:/root:/bin/bash ++daemon:x:1:1:daemon:/usr/sbin:/bin/sh ++bin:x:2:2:bin:/bin:/bin/sh ++sys:x:3:3:sys:/dev:/bin/sh ++sync:x:4:65534:sync:/bin:/bin/sync ++games:x:5:60:games:/usr/games:/bin/sh ++man:x:6:12:man:/var/cache/man:/bin/sh ++lp:x:7:7:lp:/var/spool/lpd:/bin/sh ++mail:x:8:8:mail:/var/mail:/bin/sh ++news:x:9:9:news:/var/spool/news:/bin/sh ++uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh ++proxy:x:13:13:proxy:/bin:/bin/sh ++www-data:x:33:33:www-data:/var/www:/bin/sh ++backup:x:34:34:backup:/var/backups:/bin/sh ++list:x:38:38:Mailing List Manager:/var/list:/bin/sh ++irc:x:39:39:ircd:/var/run/ircd:/bin/sh ++gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh ++nobody:x:65534:65534:nobody:/nonexistent:/bin/sh ++Debian-exim:x:102:102::/var/spool/exim4:/bin/false ++foo:x:1000:1000::/home/foo:/bin/false +diff --git a/tests/libsubid/03_add_remove/config/etc/subgid b/tests/libsubid/03_add_remove/config/etc/subgid +new file mode 100644 +index 00000000..b9495cfc +--- /dev/null ++++ b/tests/libsubid/03_add_remove/config/etc/subgid +@@ -0,0 +1,2 @@ ++foo:200000:10000 ++root:500000:1000 +diff --git a/tests/libsubid/03_add_remove/config/etc/subuid b/tests/libsubid/03_add_remove/config/etc/subuid +new file mode 100644 +index 00000000..cf80c2f7 +--- /dev/null ++++ b/tests/libsubid/03_add_remove/config/etc/subuid +@@ -0,0 +1 @@ ++foo:300000:10000 +diff --git a/tests/run_some b/tests/run_some +new file mode 100755 +index 00000000..2d085d59 +--- /dev/null ++++ b/tests/run_some +@@ -0,0 +1,136 @@ ++#!/bin/sh ++ ++set -e ++ ++export LC_ALL=C ++unset LANG ++unset LANGUAGE ++. common/config.sh ++ ++USE_PAM="yes" ++FAILURE_TESTS="yes" ++ ++succeeded=0 ++failed=0 ++failed_tests="" ++ ++run_test() ++{ ++ [ -f RUN_TEST.STOP ] && exit 1 ++ ++ if $1 > $1.log ++ then ++ succeeded=$((succeeded+1)) ++ echo -n "+" ++ else ++ failed=$((failed+1)) ++ failed_tests="$failed_tests $1" ++ echo -n "-" ++ fi ++ cat $1.log >> testsuite.log ++ [ -f /etc/passwd.lock ] && echo $1 /etc/passwd.lock || true ++ [ -f /etc/group.lock ] && echo $1 /etc/group.lock || true ++ [ -f /etc/shadow.lock ] && echo $1 /etc/shadow.lock || true ++ [ -f /etc/gshadow.lock ] && echo $1 /etc/gshadow.lock || true ++ if [ "$(stat -c"%G" /etc/shadow)" != "shadow" ] ++ then ++ echo $1 ++ ls -l /etc/shadow ++ chgrp shadow /etc/shadow ++ fi ++ if [ -d /nonexistent ] ++ then ++ echo $1 /nonexistent ++ rmdir /nonexistent ++ fi ++} ++ ++echo "+: test passed" ++echo "-: test failed" ++ ++# Empty the complete log. ++> testsuite.log ++ ++find ${build_path} -name "*.gcda" -delete ++run_test ./su/01/su_root.test ++run_test ./su/01/su_user.test ++find ${build_path} -name "*.gcda" -exec chmod a+rw {} \; ++run_test ./su/02/env_FOO-options_--login ++run_test ./su/02/env_FOO-options_--login_bash ++run_test ./su/02/env_FOO-options_--preserve-environment ++run_test ./su/02/env_FOO-options_--preserve-environment_bash ++run_test ./su/02/env_FOO-options_- ++run_test ./su/02/env_FOO-options_-_bash ++run_test ./su/02/env_FOO-options_-l-m ++run_test ./su/02/env_FOO-options_-l-m_bash ++run_test ./su/02/env_FOO-options_-l ++run_test ./su/02/env_FOO-options_-l_bash ++run_test ./su/02/env_FOO-options_-m_bash ++run_test ./su/02/env_FOO-options_-m ++run_test ./su/02/env_FOO-options_-p ++run_test ./su/02/env_FOO-options_-p_bash ++run_test ./su/02/env_FOO-options__bash ++run_test ./su/02/env_FOO-options_ ++run_test ./su/02/env_FOO-options_-p- ++run_test ./su/02/env_FOO-options_-p-_bash ++run_test ./su/02/env_special-options_-l-p ++run_test ./su/02/env_special-options_-l ++run_test ./su/02/env_special-options_-l-p_bash ++run_test ./su/02/env_special-options_-l_bash ++run_test ./su/02/env_special-options_-p ++run_test ./su/02/env_special-options_-p_bash ++run_test ./su/02/env_special-options_ ++run_test ./su/02/env_special-options__bash ++run_test ./su/02/env_special_root-options_-l-p ++run_test ./su/02/env_special_root-options_-l-p_bash ++run_test ./su/02/env_special_root-options_-l ++run_test ./su/02/env_special_root-options_-l_bash ++run_test ./su/02/env_special_root-options_-p ++run_test ./su/02/env_special_root-options_-p_bash ++run_test ./su/02/env_special_root-options_ ++run_test ./su/02/env_special_root-options__bash ++run_test ./su/03/su_run_command01.test ++run_test ./su/03/su_run_command02.test ++run_test ./su/03/su_run_command03.test ++run_test ./su/03/su_run_command04.test ++run_test ./su/03/su_run_command05.test ++run_test ./su/03/su_run_command06.test ++run_test ./su/03/su_run_command07.test ++run_test ./su/03/su_run_command08.test ++run_test ./su/03/su_run_command09.test ++run_test ./su/03/su_run_command10.test ++run_test ./su/03/su_run_command11.test ++run_test ./su/03/su_run_command12.test ++run_test ./su/03/su_run_command13.test ++run_test ./su/03/su_run_command14.test ++run_test ./su/03/su_run_command15.test ++run_test ./su/03/su_run_command16.test ++run_test ./su/03/su_run_command17.test ++run_test ./su/04/su_wrong_user.test ++run_test ./su/04/su_user_wrong_passwd.test ++run_test ./su/04/su_user_wrong_passwd_syslog.test ++run_test ./su/05/su_user_wrong_passwd_syslog.test ++run_test ./su/06/su_user_syslog.test ++run_test ./su/07/su_user_syslog.test ++run_test ./su/08/env_special-options_ ++run_test ./su/08/env_special_root-options_ ++run_test ./su/09/env_special-options_ ++run_test ./su/09/env_special_root-options_ ++run_test ./su/10_su_sulog_success/su.test ++run_test ./su/11_su_sulog_failure/su.test ++run_test ./su/12_su_child_failure/su.test ++run_test ./su/13_su_child_success/su.test ++run_test ./libsubid/01_list_ranges/list_ranges.test ++run_test ./libsubid/02_get_subid_owners/get_subid_owners.test ++run_test ./libsubid/03_add_remove/add_remove_subids.test ++ ++echo ++echo "$succeeded test(s) passed" ++echo "$failed test(s) failed" ++echo "log written in 'testsuite.log'" ++if [ "$failed" != "0" ] ++then ++ echo "the following tests failed:" ++ echo $failed_tests ++fi ++ +-- +2.30.2 + +From 607f1dd549cf9abc87af1cf29275f0d2d11eea29 Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Fri, 19 Jun 2020 22:09:20 -0500 +Subject: [PATCH] libsubid: fix a prototype in api.h + +Signed-off-by: Serge Hallyn +--- + libsubid/api.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libsubid/api.h b/libsubid/api.h +index f65e3ec9..fbdf0f9e 100644 +--- a/libsubid/api.h ++++ b/libsubid/api.h +@@ -6,7 +6,7 @@ struct subordinate_range **get_subgid_ranges(const char *owner); + void subid_free_ranges(struct subordinate_range **ranges); + + int get_subuid_owners(uid_t uid, uid_t **owner); +-int get_subgid_owners(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 */ +-- +2.30.2 + +From b5fb1b38eea2fb0489ed088c82daf6700e72363e Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Fri, 1 Jan 2021 13:01:54 -0600 +Subject: [PATCH] libsubid: move libmisc.a to last LIBADD entry + +Closes #297 + +Signed-off-by: Serge Hallyn +--- + libsubid/Makefile.am | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am +index bdbc52c3..8bef1ecc 100644 +--- a/libsubid/Makefile.am ++++ b/libsubid/Makefile.am +@@ -16,8 +16,8 @@ MISCLIBS = \ + + libsubid_la_LIBADD = \ + $(top_srcdir)/lib/libshadow.la \ +- $(top_srcdir)/libmisc/libmisc.a \ +- $(MISCLIBS) ++ $(MISCLIBS) \ ++ $(top_srcdir)/libmisc/libmisc.a + + AM_CPPFLAGS = \ + -I${top_srcdir}/lib \ +-- +2.30.2 + +From 43a917cce54019799a8de037fd63780a2b640afc Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Sat, 18 Apr 2020 14:57:56 -0500 +Subject: [PATCH] configure: define abi versions + +Signed-off-by: Serge Hallyn +--- + configure.ac | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index e4c6aaec..b8bc2b7a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1,11 +1,21 @@ + dnl Process this file with autoconf to produce a configure script. +-AC_PREREQ([2.64]) ++AC_PREREQ([2.69]) ++m4_define([libsubid_abi_major], 1) ++m4_define([libsubid_abi_minor], 0) ++m4_define([libsubid_abi_micro], 0) ++m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro]) + AC_INIT([shadow], [4.8.1], [pkg-shadow-devel@lists.alioth.debian.org], [], + [https://github.com/shadow-maint/shadow]) + AM_INIT_AUTOMAKE([1.11 foreign dist-xz]) ++AC_CONFIG_MACRO_DIRS([m4]) + AM_SILENT_RULES([yes]) + AC_CONFIG_HEADERS([config.h]) + ++AC_SUBST([LIBSUBID_ABI_MAJOR], [libsubid_abi_major]) ++AC_SUBST([LIBSUBID_ABI_MINOR], [libsubid_abi_minor]) ++AC_SUBST([LIBSUBID_ABI_MICRO], [libsubid_abi_micro]) ++AC_SUBST([LIBSUBID_ABI], [libsubid_abi]) ++ + dnl Some hacks... + test "$prefix" = "NONE" && prefix="/usr" + test "$prefix" = "/usr" && exec_prefix="" +-- +2.30.2 + diff --git a/shadow-4.8.1-libsubid_nsswitch_support.patch b/shadow-4.8.1-libsubid_nsswitch_support.patch new file mode 100644 index 0000000..eafa7c1 --- /dev/null +++ b/shadow-4.8.1-libsubid_nsswitch_support.patch @@ -0,0 +1,2107 @@ +From 514c1328b6c90d817ae0a9f7addfb3c9a11a275a Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +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 +--- + 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 +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "prototypes.h" ++#include "../libsubid/subid.h" ++ ++#define NSSWITCH "/etc/nsswitch.conf" ++ ++// NSS plugin handling for subids ++// If nsswitch has a line like ++// subid: sssd ++// then sssd will be consulted for subids. Unlike normal NSS dbs, ++// only one db is supported at a time. That's open to debate, but ++// the subids are a pretty limited resource, and local files seem ++// bound to step on any other allocations leading to insecure ++// conditions. ++static atomic_flag nss_init_started; ++static atomic_bool nss_init_completed; ++ ++static struct subid_nss_ops *subid_nss; ++ ++bool nss_is_initialized() { ++ return atomic_load(&nss_init_completed); ++} ++ ++void nss_exit() { ++ if (nss_is_initialized() && subid_nss) { ++ dlclose(subid_nss->handle); ++ free(subid_nss); ++ subid_nss = NULL; ++ } ++} ++ ++// nsswitch_path is an argument only to support testing. ++void nss_init(char *nsswitch_path) { ++ FILE *nssfp = NULL; ++ char *line = NULL, *p, *token, *saveptr; ++ size_t len = 0; ++ ++ if (atomic_flag_test_and_set(&nss_init_started)) { ++ // Another thread has started nss_init, wait for it to complete ++ while (!atomic_load(&nss_init_completed)) ++ usleep(100); ++ return; ++ } ++ ++ if (!nsswitch_path) ++ nsswitch_path = NSSWITCH; ++ ++ // read nsswitch.conf to check for a line like: ++ // subid: files ++ nssfp = fopen(nsswitch_path, "r"); ++ if (!nssfp) { ++ fprintf(stderr, "Failed opening %s: %m", nsswitch_path); ++ atomic_store(&nss_init_completed, true); ++ return; ++ } ++ while ((getline(&line, &len, nssfp)) != -1) { ++ if (line[0] == '\0' || line[0] == '#') ++ continue; ++ if (strlen(line) < 8) ++ continue; ++ if (strncasecmp(line, "subid:", 6) != 0) ++ continue; ++ p = &line[6]; ++ while ((*p) && isspace(*p)) ++ p++; ++ if (!*p) ++ continue; ++ for (token = strtok_r(p, " \n\t", &saveptr); ++ token; ++ token = strtok_r(NULL, " \n\t", &saveptr)) { ++ char libname[65]; ++ void *h; ++ if (strcmp(token, "files") == 0) { ++ subid_nss = NULL; ++ goto done; ++ } ++ if (strlen(token) > 50) { ++ fprintf(stderr, "Subid NSS module name too long (longer than 50 characters): %s\n", token); ++ fprintf(stderr, "Using files\n"); ++ subid_nss = NULL; ++ goto done; ++ } ++ snprintf(libname, 64, "libsubid_%s.so", token); ++ h = dlopen(libname, RTLD_LAZY); ++ if (!h) { ++ fprintf(stderr, "Error opening %s: %s\n", libname, dlerror()); ++ fprintf(stderr, "Using files\n"); ++ subid_nss = NULL; ++ goto done; ++ } ++ subid_nss = malloc(sizeof(*subid_nss)); ++ if (!subid_nss) { ++ dlclose(h); ++ goto done; ++ } ++ subid_nss->has_range = dlsym(h, "shadow_subid_has_range"); ++ if (!subid_nss->has_range) { ++ fprintf(stderr, "%s did not provide @has_range@\n", libname); ++ dlclose(h); ++ free(subid_nss); ++ subid_nss = NULL; ++ goto done; ++ } ++ subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges"); ++ if (!subid_nss->list_owner_ranges) { ++ fprintf(stderr, "%s did not provide @list_owner_ranges@\n", libname); ++ dlclose(h); ++ free(subid_nss); ++ subid_nss = NULL; ++ goto done; ++ } ++ subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range"); ++ if (!subid_nss->has_any_range) { ++ fprintf(stderr, "%s did not provide @has_any_range@\n", libname); ++ dlclose(h); ++ free(subid_nss); ++ subid_nss = NULL; ++ goto done; ++ } ++ subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners"); ++ if (!subid_nss->find_subid_owners) { ++ fprintf(stderr, "%s did not provide @find_subid_owners@\n", libname); ++ dlclose(h); ++ free(subid_nss); ++ subid_nss = NULL; ++ goto done; ++ } ++ subid_nss->handle = h; ++ goto done; ++ } ++ fprintf(stderr, "No usable subid NSS module found, using files\n"); ++ // subid_nss has to be null here, but to ease reviews: ++ free(subid_nss); ++ subid_nss = NULL; ++ goto done; ++ } ++ ++done: ++ atomic_store(&nss_init_completed, true); ++ free(line); ++ if (nssfp) { ++ atexit(nss_exit); ++ fclose(nssfp); ++ } ++} ++ ++struct subid_nss_ops *get_subid_nss_handle() { ++ nss_init(NULL); ++ return subid_nss; ++} +diff --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 ++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 + #include + #include ++#include + + /* + * 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 + +-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 ++#include + + #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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "defines.h" ++#include "prototypes.h" ++#include "subordinateio.h" ++#include "idmapping.h" ++ ++const char *Prog; ++ ++int main(int argc, char **argv) ++{ ++ char *owner; ++ unsigned long start, count; ++ bool check_uids; ++ Prog = Basename (argv[0]); ++ ++ if (argc != 5) ++ exit(1); ++ ++ owner = argv[1]; ++ check_uids = argv[2][0] == 'u'; ++ start = strtoul(argv[3], NULL, 10); ++ if (start == ULONG_MAX && errno == ERANGE) ++ exit(1); ++ count = strtoul(argv[4], NULL, 10); ++ if (count == ULONG_MAX && errno == ERANGE) ++ exit(1); ++ if (check_uids) { ++ if (have_sub_uids(owner, start, count)) ++ exit(0); ++ exit(1); ++ } ++ if (have_sub_gids(owner, start, count)) ++ exit(0); ++ exit(1); ++} +diff --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 ++#include ++#include ++#include ++#include ++#include ++ ++enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result) ++{ ++ if (strcmp(owner, "ubuntu") == 0) { ++ *result = true; ++ return SUBID_STATUS_SUCCESS; ++ } ++ if (strcmp(owner, "error") == 0) { ++ *result = false; ++ return SUBID_STATUS_ERROR; ++ } ++ if (strcmp(owner, "unknown") == 0) { ++ *result = false; ++ return SUBID_STATUS_UNKNOWN_USER; ++ } ++ if (strcmp(owner, "conn") == 0) { ++ *result = false; ++ return SUBID_STATUS_ERROR_CONN; ++ } ++ if (t == ID_TYPE_UID) { ++ *result = strcmp(owner, "user1") == 0; ++ return SUBID_STATUS_SUCCESS; ++ } ++ ++ *result = strcmp(owner, "group1") == 0; ++ return SUBID_STATUS_SUCCESS; ++} ++ ++enum subid_status shadow_subid_has_range(const char *owner, unsigned long start, unsigned long count, enum subid_type t, bool *result) ++{ ++ if (strcmp(owner, "ubuntu") == 0 && ++ start >= 200000 && ++ count <= 100000) { ++ *result = true; ++ return SUBID_STATUS_SUCCESS; ++ } ++ *result = false; ++ if (strcmp(owner, "error") == 0) ++ return SUBID_STATUS_ERROR; ++ if (strcmp(owner, "unknown") == 0) ++ return SUBID_STATUS_UNKNOWN_USER; ++ if (strcmp(owner, "conn") == 0) ++ return SUBID_STATUS_ERROR_CONN; ++ ++ if (t == ID_TYPE_UID && strcmp(owner, "user1") != 0) ++ return SUBID_STATUS_SUCCESS; ++ if (t == ID_TYPE_GID && strcmp(owner, "group1") != 0) ++ return SUBID_STATUS_SUCCESS; ++ ++ if (start < 100000) ++ return SUBID_STATUS_SUCCESS; ++ if (count >= 65536) ++ return SUBID_STATUS_SUCCESS; ++ *result = true; ++ return SUBID_STATUS_SUCCESS; ++} ++ ++// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values, ++// to ease manual testing. For automated testing, if you return those values, ++// we'll return 1000 for ubuntu and 1001 otherwise. ++static uid_t getnamuid(const char *name) { ++ struct passwd *pw; ++ ++ pw = getpwnam(name); ++ if (pw) ++ return pw->pw_uid; ++ ++ // For testing purposes ++ return strcmp(name, "ubuntu") == 0 ? (uid_t)1000 : (uid_t)1001; ++} ++ ++static int alloc_uid(uid_t **uids, uid_t id) { ++ *uids = malloc(sizeof(uid_t)); ++ if (!*uids) ++ return -1; ++ *uids[0] = id; ++ return 1; ++} ++ ++enum subid_status shadow_subid_find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids, int *count) ++{ ++ if (id >= 100000 && id < 165536) { ++ *count = alloc_uid(uids, getnamuid("user1")); ++ if (*count == 1) ++ return SUBID_STATUS_SUCCESS; ++ return SUBID_STATUS_ERROR; // out of memory ++ } ++ if (id >= 200000 && id < 300000) { ++ *count = alloc_uid(uids, getnamuid("ubuntu")); ++ if (*count == 1) ++ return SUBID_STATUS_SUCCESS; ++ return SUBID_STATUS_ERROR; // out of memory ++ } ++ *count = 0; // nothing found ++ return SUBID_STATUS_SUCCESS; ++} ++ ++enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges, int *count) ++{ ++ struct subordinate_range **ranges; ++ ++ *count = 0; ++ if (strcmp(owner, "error") == 0) ++ return SUBID_STATUS_ERROR; ++ if (strcmp(owner, "unknown") == 0) ++ return SUBID_STATUS_UNKNOWN_USER; ++ if (strcmp(owner, "conn") == 0) ++ return SUBID_STATUS_ERROR_CONN; ++ ++ *ranges = NULL; ++ if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 && ++ strcmp(owner, "group1") != 0) ++ return SUBID_STATUS_SUCCESS; ++ if (id_type == ID_TYPE_GID && strcmp(owner, "user1") == 0) ++ return SUBID_STATUS_SUCCESS; ++ if (id_type == ID_TYPE_UID && strcmp(owner, "group1") == 0) ++ return SUBID_STATUS_SUCCESS; ++ ranges = (struct subordinate_range **)malloc(sizeof(struct subordinate_range *)); ++ if (!*ranges) ++ return SUBID_STATUS_ERROR; ++ ranges[0] = (struct subordinate_range *)malloc(sizeof(struct subordinate_range)); ++ if (!ranges[0]) { ++ free(*ranges); ++ *ranges = NULL; ++ return SUBID_STATUS_ERROR; ++ } ++ ranges[0]->owner = strdup(owner); ++ if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) { ++ ranges[0]->start = 100000; ++ ranges[0]->count = 65536; ++ } else { ++ ranges[0]->start = 200000; ++ ranges[0]->count = 100000; ++ } ++ ++ *count = 1; ++ *in_ranges = ranges; ++ ++ return SUBID_STATUS_SUCCESS; ++} +diff --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 ++#include ++#include ++#include ++#include ++#include ++ ++extern bool nss_is_initialized(); ++extern struct subid_nss_ops *get_subid_nss_handle(); ++ ++void test1() { ++ // nsswitch1 has no subid: entry ++ setenv("LD_LIBRARY_PATH", ".", 1); ++ printf("Test with no subid entry\n"); ++ nss_init("./nsswitch1.conf"); ++ if (!nss_is_initialized() || get_subid_nss_handle()) ++ exit(1); ++ // second run should change nothing ++ printf("Test with no subid entry, second run\n"); ++ nss_init("./nsswitch1.conf"); ++ if (!nss_is_initialized() || get_subid_nss_handle()) ++ exit(1); ++} ++ ++void test2() { ++ // nsswitch2 has a subid: files entry ++ printf("test with 'files' subid entry\n"); ++ nss_init("./nsswitch2.conf"); ++ if (!nss_is_initialized() || get_subid_nss_handle()) ++ exit(1); ++ // second run should change nothing ++ printf("test with 'files' subid entry, second run\n"); ++ nss_init("./nsswitch2.conf"); ++ if (!nss_is_initialized() || get_subid_nss_handle()) ++ exit(1); ++} ++ ++void test3() { ++ // nsswitch3 has a subid: testnss entry ++ printf("test with 'test' subid entry\n"); ++ nss_init("./nsswitch3.conf"); ++ if (!nss_is_initialized() || !get_subid_nss_handle()) ++ exit(1); ++ // second run should change nothing ++ printf("test with 'test' subid entry, second run\n"); ++ nss_init("./nsswitch3.conf"); ++ if (!nss_is_initialized() || !get_subid_nss_handle()) ++ exit(1); ++} ++ ++const char *Prog; ++ ++int main(int argc, char *argv[]) ++{ ++ int which; ++ ++ Prog = Basename(argv[0]); ++ ++ if (argc < 1) ++ exit(1); ++ ++ which = atoi(argv[1]); ++ switch(which) { ++ case 1: test1(); break; ++ case 2: test2(); break; ++ case 3: test3(); break; ++ default: exit(1); ++ } ++ ++ printf("nss parsing tests done\n"); ++ exit(0); ++} +diff --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 +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 +--- + 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 + #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 +- +-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 + #include +-#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 +-#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 +-#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 + #include +-#include "api.h" ++#include "subid.h" + #include "stdlib.h" + #include "prototypes.h" + +-- +2.30.2 + diff --git a/shadow-utils.spec b/shadow-utils.spec index 8d1f5ea..551de7a 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.8.1 -Release: 8%{?dist} +Release: 9%{?dist} Epoch: 2 URL: https://github.com/shadow-maint/shadow Source0: https://github.com/shadow-maint/shadow/releases/download/%{version}/shadow-%{version}.tar.xz @@ -11,6 +11,11 @@ Source3: shadow-utils.login.defs Source4: shadow-bsd.txt Source5: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt Source6: shadow-utils.HOME_MODE.xml + +### Globals ### +%global includesubiddir %{_includedir}/subid + +### Patches ### # Misc small changes - most probably non-upstreamable Patch0: shadow-4.6-redhat.patch # Be more lenient with acceptable user/group names - non upstreamable @@ -62,6 +67,15 @@ Patch44: shadow-4.8.1-check-local-groups.patch Patch45: shadow-4.8.1-commonio-force-lock-file-sync.patch # https://github.com/shadow-maint/shadow/commit/df6ec1d1693c8c80c323b40d6fc82bb549363db3 Patch46: shadow-4.8.1-man-include-lastlog-file-caveat.patch +# https://github.com/shadow-maint/shadow/commit/0a7888b1fad613a052b988b01a71933b67296e68 +# https://github.com/shadow-maint/shadow/commit/607f1dd549cf9abc87af1cf29275f0d2d11eea29 +# https://github.com/shadow-maint/shadow/commit/b5fb1b38eea2fb0489ed088c82daf6700e72363e +# https://github.com/shadow-maint/shadow/commit/43a917cce54019799a8de037fd63780a2b640afc +Patch47: shadow-4.8.1-libsubid_creation.patch +# https://github.com/shadow-maint/shadow/commit/514c1328b6c90d817ae0a9f7addfb3c9a11a275a +# https://github.com/shadow-maint/shadow/commit/8492dee6632e340dee76eee895c3e30877bebf45 +# https://github.com/shadow-maint/shadow/commit/0f4347d1483191b2142546416a9eefe0c9459600 +Patch48: shadow-4.8.1-libsubid_nsswitch_support.patch License: BSD and GPLv2+ BuildRequires: make @@ -90,6 +104,23 @@ for all users. The useradd, userdel, and usermod commands are used for managing user accounts. The groupadd, groupdel, and groupmod commands are used for managing group accounts. + +### Subpackages ### +%package subid +Summary: A library to manage subordinate uid and gid ranges +License: BSD and GPLv2+ + +%description subid +Utility library that provides a way to manage subid ranges. + + +%package subid-devel +Summary: Development package for shadow-utils-subid +License: BSD and GPLv2+ + +%description subid-devel +Development files for shadow-utils-subid. + %prep %setup -q -n shadow-%{version} %patch0 -p1 -b .redhat @@ -116,6 +147,8 @@ are used for managing group accounts. %patch44 -p1 -b .check-local-groups %patch45 -p1 -b .commonio-force-lock-file-sync %patch46 -p1 -b .man-include-lastlog-file-caveat +%patch47 -p1 -b .libsubid_creation +%patch48 -p1 -b .libsubid_nsswitch_support iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -145,7 +178,7 @@ autoreconf --with-selinux \ --without-libcrack \ --without-libpam \ - --disable-shared \ + --enable-shared \ --with-group-name-max-length=32 %make_build @@ -220,6 +253,14 @@ for dir in $(ls -1d $RPM_BUILD_ROOT%{_mandir}/{??,??_??}) ; do echo "%%lang($lang) $dir/man*/*" >> shadow.lang done +# Move header files to its own folder +echo $(ls) +mkdir -p $RPM_BUILD_ROOT/%{includesubiddir} +install -m 644 libsubid/subid.h $RPM_BUILD_ROOT/%{includesubiddir}/ + +# Remove .la files created by libsubid +rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.la + %files -f shadow.lang %doc NEWS doc/HOWTO README %license gpl-2.0.txt shadow-bsd.txt @@ -268,7 +309,18 @@ done %{_mandir}/man8/vipw.8* %{_mandir}/man8/vigr.8* +%files subid +%{_libdir}/libsubid.so.* + +%files subid-devel +%{includesubiddir}/subid.h +%{_libdir}/libsubid.so + %changelog +* Fri Apr 16 2021 Iker Pedrosa - 2:4.8.1-9 +- libsubid: creation and nsswitch support +- Creation of subid and subid-devel subpackages + * Mon Mar 29 2021 Iker Pedrosa - 2:4.8.1-8 - man: include lastlog file caveat (#951564) - Upstream links to several patches