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