diff --git a/lib/nss.c b/lib/nss.c
index 5957390741..8e71879ed9 100644
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -25,37 +25,113 @@
// 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.
+// subid: sss
+// then the sss module (libsubid_sss.so) will be consulted for subids.
+// If nsswitch has a line specifying multiple databases, like:
+// subid: sss files
+// then databases will be consulted in the specified order. The search
+// stops as soon as the user is found in a database, even if no subids
+// are defined there. For example, if 'sss' knows the user but provides
+// no subids, 'files' will not be consulted.
+//
+// While multiple databases are now supported, the subids are a pretty
+// limited resource. Mixing local files with network allocations
+// (like sssd) requires careful management. Misconfigurations would
+// lead to overlapping ID mappings. Use with caution.
+
static atomic_flag nss_init_started;
static atomic_bool nss_init_completed;
-static struct subid_nss_ops *subid_nss;
+static struct subid_nss_db *subid_nss_db_head;
-bool nss_is_initialized() {
+bool
+nss_is_initialized(void) {
return atomic_load(&nss_init_completed);
}
-static void nss_exit(void) {
- if (nss_is_initialized() && subid_nss) {
- dlclose(subid_nss->handle);
- free(subid_nss);
- subid_nss = NULL;
+static void
+nss_exit(void) {
+ struct subid_nss_db *current;
+ struct subid_nss_db *next;
+
+ if (nss_is_initialized() && subid_nss_db_head) {
+ current = subid_nss_db_head;
+ while (current) {
+ next = current->next;
+ if (current->ops) {
+ dlclose(current->ops->handle);
+ free(current->ops);
+ }
+ free(current);
+ current = next;
+ }
+
+ subid_nss_db_head = NULL;
}
}
+static struct subid_nss_ops *
+open_and_check_nss_module(const char *libname) {
+ void *h;
+ struct subid_nss_ops *nss_ops;
+
+ h = dlopen(libname, RTLD_LAZY);
+ if (!h) {
+ fprintf(log_get_logfd(), "Error opening %s: %s\n", libname, dlerror());
+ fprintf(log_get_logfd(), "Using files\n");
+ return NULL;
+ }
+
+ nss_ops = malloc_T(1, struct subid_nss_ops);
+ if (!nss_ops) {
+ fprintf(log_get_logfd(), "Failed to allocate memory for subid NSS module %s\n", libname);
+ dlclose(h);
+ return NULL;
+ }
+
+ nss_ops->has_range = dlsym(h, "shadow_subid_has_range");
+ if (!nss_ops->has_range) {
+ fprintf(log_get_logfd(), "%s did not provide @has_range@\n", libname);
+ goto close_lib;
+ }
+ nss_ops->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
+ if (!nss_ops->list_owner_ranges) {
+ fprintf(log_get_logfd(), "%s did not provide @list_owner_ranges@\n", libname);
+ goto close_lib;
+ }
+ nss_ops->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
+ if (!nss_ops->find_subid_owners) {
+ fprintf(log_get_logfd(), "%s did not provide @find_subid_owners@\n", libname);
+ goto close_lib;
+ }
+ nss_ops->free = dlsym(h, "shadow_subid_free");
+ if (!nss_ops->free) {
+ fprintf(log_get_logfd(), "%s did not provide @free@\n", libname);
+ goto close_lib;
+ }
+
+ nss_ops->handle = h;
+ return nss_ops;
+
+close_lib:
+ dlclose(h);
+ free(nss_ops);
+ return NULL;
+}
+
// nsswitch_path is an argument only to support testing.
void
nss_init(const char *nsswitch_path) {
- char *line = NULL, *p;
- char libname[64];
- FILE *nssfp = NULL;
- void *h;
- size_t len = 0;
+ char libname[64];
+ char *line = NULL;
+ char *p;
+ char *token;
+ FILE *nssfp = NULL;
+ size_t len = 0;
+ const char *delimiters = " \t\n";
+ struct subid_nss_db *new_db;
+ struct subid_nss_db **tail = &subid_nss_db_head;
+ struct subid_nss_ops *ops;
if (atomic_flag_test_and_set(&nss_init_started)) {
// Another thread has started nss_init, wait for it to complete
@@ -68,7 +144,7 @@ nss_init(const char *nsswitch_path) {
nsswitch_path = NSSWITCH;
// read nsswitch.conf to check for a line like:
- // subid: files
+ // subid: sss files
nssfp = fopen(nsswitch_path, "r");
if (!nssfp) {
if (errno != ENOENT)
@@ -86,66 +162,61 @@ nss_init(const char *nsswitch_path) {
if (!strcaseprefix(line, "subid:"))
continue;
p = &line[6];
- p = stpspn(p, " \t\n");
+ p = stpspn(p, delimiters);
if (!streq(p, ""))
break;
p = NULL;
}
+
if (p == NULL) {
- goto null_subid;
+ // Use NULL to indicate the built-in "files" database
+ subid_nss_db_head = NULL;
+ goto done;
}
- if (stpsep(p, " \t\n") == NULL) {
- fprintf(log_get_logfd(), "No usable subid NSS module found, using files\n");
- // subid_nss has to be null here, but to ease reviews:
- goto null_subid;
- }
- if (streq(p, "files")) {
- goto null_subid;
- }
- if (strlen(p) > 50) {
- fprintf(log_get_logfd(), "Subid NSS module name too long (longer than 50 characters): %s\n", p);
- fprintf(log_get_logfd(), "Using files\n");
- goto null_subid;
- }
- stprintf_a(libname, "libsubid_%s.so", p);
- h = dlopen(libname, RTLD_LAZY);
- if (!h) {
- fprintf(log_get_logfd(), "Error opening %s: %s\n", libname, dlerror());
- fprintf(log_get_logfd(), "Using files\n");
- goto null_subid;
- }
- subid_nss = malloc_T(1, struct subid_nss_ops);
- if (!subid_nss) {
- goto close_lib;
- }
- subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
- if (!subid_nss->has_range) {
- fprintf(log_get_logfd(), "%s did not provide @has_range@\n", libname);
- goto close_lib;
- }
- subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
- if (!subid_nss->list_owner_ranges) {
- fprintf(log_get_logfd(), "%s did not provide @list_owner_ranges@\n", libname);
- goto close_lib;
- }
- subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
- if (!subid_nss->find_subid_owners) {
- fprintf(log_get_logfd(), "%s did not provide @find_subid_owners@\n", libname);
- goto close_lib;
- }
- subid_nss->free = dlsym(h, "shadow_subid_free");
- if (!subid_nss->free) {
- fprintf(log_get_logfd(), "%s did not provide @subid_free@\n", libname);
- goto close_lib;
+
+ while (NULL != (token = strsep(&p, delimiters))) {
+ if (*token == '\0') {
+ continue;
+ }
+
+ if (streq(token, "files")) {
+ // Use NULL to indicate the built-in "files" database
+ ops = NULL;
+ } else {
+ if (stprintf_a(libname, "libsubid_%s.so", token) == -1) {
+ fprintf(log_get_logfd(), "Subid NSS module name too long: %s\n", token);
+ continue;
+ }
+
+ ops = open_and_check_nss_module(libname);
+ if (!ops) {
+ continue;
+ }
+ }
+
+ new_db = malloc_T(1, struct subid_nss_db);
+ if (!new_db) {
+ if (ops) {
+ dlclose(ops->handle);
+ free(ops);
+ ops = NULL;
+ }
+
+ fprintf(log_get_logfd(), "Failed to allocate memory for subid NSS module %s, skipping\n", token);
+ continue;
+ }
+
+ new_db->ops = ops;
+ new_db->next = NULL;
+ *tail = new_db;
+ tail = &new_db->next;
}
- subid_nss->handle = h;
- goto done;
-close_lib:
- dlclose(h);
- free(subid_nss);
-null_subid:
- subid_nss = NULL;
+ if (subid_nss_db_head == NULL) {
+ // No vaild NSS database loaded, using "files" only.
+ // NULL indicates the built-in "files" database, so we can continue, but log a warning.
+ fprintf(log_get_logfd(), "No usable subid NSS module found, using files\n");
+ }
done:
atomic_store(&nss_init_completed, true);
@@ -156,7 +227,8 @@ nss_init(const char *nsswitch_path) {
}
}
-struct subid_nss_ops *get_subid_nss_handle() {
+struct subid_nss_db *
+get_subid_nss_db(void) {
nss_init(NULL);
- return subid_nss;
+ return subid_nss_db_head;
}
diff --git a/lib/prototypes.h b/lib/prototypes.h
index 411f8d559c..17841483ca 100644
--- a/lib/prototypes.h
+++ b/lib/prototypes.h
@@ -228,6 +228,11 @@ extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void);
extern void nss_init(const char *nsswitch_path);
extern bool nss_is_initialized(void);
+struct subid_nss_db {
+ struct subid_nss_ops *ops;
+ struct subid_nss_db *next;
+};
+
struct subid_nss_ops {
/*
* nss_has_range: does a user own a given subid range
@@ -289,7 +294,7 @@ struct subid_nss_ops {
void *handle;
};
-extern struct subid_nss_ops *get_subid_nss_handle(void);
+extern struct subid_nss_db *get_subid_nss_db(void);
/* pam_pass_non_interactive.c */
diff --git a/lib/subordinateio.c b/lib/subordinateio.c
index acd3f1ffdc..c9b9bf1c3b 100644
--- a/lib/subordinateio.c
+++ b/lib/subordinateio.c
@@ -564,7 +564,7 @@ static bool have_range(struct commonio_db *db,
rc = sub_uid_open(O_RDONLY);
else
rc = sub_gid_open(O_RDONLY);
- if (rc < 0)
+ if (!rc)
return false;
}
@@ -626,17 +626,53 @@ bool local_sub_uid_assigned(const char *owner)
bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
{
+ enum subid_status status;
+ struct subid_nss_db *db;
struct subid_nss_ops *h;
+ bool close_db = false;
+ bool exists;
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;
+
+ db = get_subid_nss_db();
+ if (!db) {
+ // No NSS module configured, search local files only.
+ return have_range(&subordinate_uid_db, owner, start, count);
+ }
+
+ for (; db; db = db->next) {
+ h = db->ops;
+ if (h) {
+ status = h->has_range(owner, start, count, ID_TYPE_UID, &found);
+ if (status == SUBID_STATUS_SUCCESS)
+ return found;
+ if (status == SUBID_STATUS_UNKNOWN_USER)
+ continue; // User not found in this database, try the next one.
+
+ return false; // Error occurred.
+ } else {
+ // Local "files" database
+ if (!subordinate_uid_db.isopen) {
+ if (sub_uid_open(O_RDONLY) == 0)
+ return false;
+ close_db = true;
+ }
+
+ found = have_range(&subordinate_uid_db, owner, start, count);
+ exists = range_exists(&subordinate_uid_db, owner);
+
+ if (close_db)
+ sub_uid_close(true);
+
+ if (found)
+ return true;
+ if (!exists)
+ continue; // User does not have any ranges; try the next database.
+ return false; // User has ranges but does not own the requested range.
+ }
}
- return have_range (&subordinate_uid_db, owner, start, count);
+
+ // Searched all databases and didn't find the range.
+ return false;
}
/*
@@ -647,7 +683,11 @@ bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
*/
int sub_uid_add (const char *owner, uid_t start, unsigned long count)
{
- if (get_subid_nss_handle()) {
+ struct subid_nss_db *db;
+
+ db = get_subid_nss_db();
+ if (db && db->ops) {
+ // NSS module configured and the first database is not "files".
errno = EOPNOTSUPP;
return 0;
}
@@ -657,7 +697,11 @@ int sub_uid_add (const char *owner, uid_t start, unsigned long count)
/* Return 1 on success. on failure, return 0 and set errno appropriately */
int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
{
- if (get_subid_nss_handle()) {
+ struct subid_nss_db *db;
+
+ db = get_subid_nss_db();
+ if (db && db->ops) {
+ // NSS module configured and the first database is not "files".
errno = EOPNOTSUPP;
return 0;
}
@@ -690,11 +734,23 @@ uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
*/
bool want_subuid_file(void)
{
- if (get_subid_nss_handle() != NULL)
- return false;
+ struct subid_nss_db *db;
+
if (getdef_ulong("SUB_UID_COUNT", 65536) == 0)
return false;
- return true;
+
+ db = get_subid_nss_db();
+ if (db == NULL)
+ return true;
+
+ for (; db; db = db->next) {
+ if (db->ops == NULL) {
+ // Local "files" database is used.
+ return true;
+ }
+ }
+
+ return false;
}
/*
@@ -705,11 +761,23 @@ bool want_subuid_file(void)
*/
bool want_subgid_file(void)
{
- if (get_subid_nss_handle() != NULL)
- return false;
+ struct subid_nss_db *db;
+
if (getdef_ulong("SUB_GID_COUNT", 65536) == 0)
return false;
- return true;
+
+ db = get_subid_nss_db();
+ if (db == NULL)
+ return true;
+
+ for (; db; db = db->next) {
+ if (db->ops == NULL) {
+ // Local "files" database is used.
+ return true;
+ }
+ }
+
+ return false;
}
static struct commonio_db subordinate_gid_db = {
@@ -759,17 +827,53 @@ int sub_gid_open (int mode)
bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
{
+ enum subid_status status;
+ struct subid_nss_db *db;
struct subid_nss_ops *h;
+ bool close_db = false;
+ bool exists;
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;
+
+ db = get_subid_nss_db();
+ if (!db) {
+ // No NSS module configured, search local files only.
+ return have_range(&subordinate_gid_db, owner, start, count);
+ }
+
+ for (; db; db = db->next) {
+ h = db->ops;
+ if (h) {
+ status = h->has_range(owner, start, count, ID_TYPE_GID, &found);
+ if (status == SUBID_STATUS_SUCCESS)
+ return found;
+ if (status == SUBID_STATUS_UNKNOWN_USER)
+ continue; // Group not found in this database, try the next one.
+
+ return false; // Error occurred.
+ } else {
+ // Local "files" database
+ if (!subordinate_gid_db.isopen) {
+ if (sub_gid_open(O_RDONLY) == 0)
+ return false;
+ close_db = true;
+ }
+
+ found = have_range(&subordinate_gid_db, owner, start, count);
+ exists = range_exists(&subordinate_gid_db, owner);
+
+ if (close_db)
+ sub_gid_close(true);
+
+ if (found)
+ return true;
+ if (!exists)
+ continue; // Group does not have any ranges; try the next database.
+ return false; // Group has ranges but does not own the requested range.
+ }
}
- return have_range(&subordinate_gid_db, owner, start, count);
+
+ // Searched all databases and didn't find the range.
+ return false;
}
bool local_sub_gid_assigned(const char *owner)
@@ -785,7 +889,11 @@ bool local_sub_gid_assigned(const char *owner)
*/
int sub_gid_add (const char *owner, gid_t start, unsigned long count)
{
- if (get_subid_nss_handle()) {
+ struct subid_nss_db *db;
+
+ db = get_subid_nss_db();
+ if (db && db->ops) {
+ // NSS module configured and the first database is not "files".
errno = EOPNOTSUPP;
return 0;
}
@@ -795,7 +903,11 @@ int sub_gid_add (const char *owner, gid_t start, unsigned long count)
/* Return 1 on success. on failure, return 0 and set errno appropriately */
int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
{
- if (get_subid_nss_handle()) {
+ struct subid_nss_db *db;
+
+ db = get_subid_nss_db();
+ if (db && db->ops) {
+ // NSS module configured and the first database is not "files".
errno = EOPNOTSUPP;
return 0;
}
@@ -854,7 +966,7 @@ static bool get_owner_id(const char *owner, enum subid_type id_type, char *id)
}
/*
- * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
+ * static int list_local_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **in_ranges)
*
* @owner: username
* @id_type: UID or GUID
@@ -865,32 +977,23 @@ static bool get_owner_id(const char *owner, enum subid_type id_type, char *id)
* UID number. If id_type is UID, then subuids are returned, else
* subgids are given.
- * Returns the number of ranges found, or < 0 on error.
+ * Returns the number of ranges found in local files only, or < 0 on error.
+ * NSS modules are not consulted by this function.
*
* The caller must free the subordinate range list.
*/
-int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **in_ranges)
+static int list_local_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **in_ranges)
{
// TODO - need to handle owner being either uid or username
struct subid_range *ranges = NULL;
const struct subordinate_range *range;
struct commonio_db *db;
- enum subid_status status;
int count = 0;
- struct subid_nss_ops *h;
char id[ID_SIZE];
bool have_owner_id;
*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)) {
@@ -933,6 +1036,91 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r
return count;
}
+/*
+ * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range ***ranges)
+ *
+ * @owner: username
+ * @id_type: UID or GUID
+ * @ranges: pointer to array of ranges into which results will be placed.
+ *
+ * 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 given.
+
+ * Returns the number of ranges found, or < 0 on error.
+ *
+ * The caller must free the subordinate range list.
+ */
+int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **ranges)
+{
+ enum subid_status status;
+ int count = 0;
+ struct subid_nss_db *db;
+ struct subid_nss_ops *h = NULL;
+ struct subid_range *our_ranges;
+ bool error = false;
+
+ *ranges = NULL;
+
+ db = get_subid_nss_db();
+ if (!db) {
+ // No NSS module configured, search local files only.
+ return list_local_owner_ranges(owner, id_type, ranges);
+ }
+
+ for (; db; db = db->next) {
+ h = db->ops;
+ if (h) {
+ status = h->list_owner_ranges(owner, id_type, ranges, &count);
+
+ if (status == SUBID_STATUS_SUCCESS) {
+ if (count > 0) {
+ our_ranges = malloc_T(count, struct subid_range);
+ if (!our_ranges)
+ goto fail;
+
+ memcpy(our_ranges, *ranges, count * sizeof(struct subid_range));
+ h->free(*ranges);
+ *ranges = our_ranges;
+ }
+ return count;
+ }
+ if (status == SUBID_STATUS_UNKNOWN_USER)
+ goto next;
+ if (status == SUBID_STATUS_ERROR || status == SUBID_STATUS_ERROR_CONN)
+ goto fail;
+ } else {
+ // Local "files" database.
+ count = list_local_owner_ranges(owner, id_type, ranges);
+ if (count > 0)
+ return count;
+ if (count == 0)
+ goto next;
+ if (count < 0)
+ goto fail;
+ }
+
+ fail:
+ error = true;
+
+ next:
+ if (*ranges) {
+ if (h)
+ h->free(*ranges);
+ else
+ free(*ranges);
+ *ranges = NULL;
+ }
+
+ if (error)
+ return -1;
+ }
+
+ // Searched all databases but 0 ranges found.
+ return 0;
+}
+
static int append_uids(uid_t **uids, const char *owner, int n)
{
int i;
@@ -970,23 +1158,12 @@ static int append_uids(uid_t **uids, const char *owner, int n)
return n+1;
}
-int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids)
+static int find_local_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;
- 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)) {
@@ -1023,14 +1200,84 @@ int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids)
return n;
}
+int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids)
+{
+ struct subid_nss_db *db;
+ struct subid_nss_ops *h;
+ enum subid_status status;
+ int n = 0;
+ int count = 0;
+ bool error = false;
+ uid_t *new_uids = NULL;
+ uid_t *all_uids = NULL;
+
+ *uids = NULL;
+
+ db = get_subid_nss_db();
+ if (!db) {
+ // No NSS module configured, search local files only.
+ return find_local_subid_owners(id, id_type, uids);
+ }
+
+ // Search for all databases and aggregate results.
+ for (; db; db = db->next) {
+ h = db->ops;
+ if (h) {
+ status = h->find_subid_owners(id, id_type, &new_uids, &count);
+ if (status != SUBID_STATUS_SUCCESS)
+ count = -1;
+ } else {
+ count = find_local_subid_owners(id, id_type, &new_uids);
+ }
+
+ if (count <= 0)
+ goto next;
+
+ if (count > 0) {
+ all_uids = reallocf_T(all_uids, n + count, uid_t);
+ if (!all_uids)
+ goto fail;
+ memcpy(all_uids + n, new_uids, count * sizeof(uid_t));
+ n += count;
+ goto next;
+ }
+
+ fail:
+ error = true;
+
+ next:
+ if (new_uids) {
+ if (h)
+ h->free(new_uids);
+ else
+ free(new_uids);
+ new_uids = NULL;
+ }
+
+ if (error) {
+ if (all_uids)
+ free(all_uids);
+ return -1;
+ }
+ }
+
+ *uids = all_uids;
+ return n;
+}
+
bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse)
{
struct commonio_db *db;
+ struct subid_nss_db *nss_db;
const struct subordinate_range *r;
bool ret;
- if (get_subid_nss_handle())
+ nss_db = get_subid_nss_db();
+ if (nss_db && nss_db->ops) {
+ // NSS module configured and the first database is not "files".
+ errno = EOPNOTSUPP;
return false;
+ }
switch (id_type) {
case ID_TYPE_UID:
@@ -1099,10 +1346,15 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b
bool release_subid_range(struct subordinate_range *range, enum subid_type id_type)
{
struct commonio_db *db;
+ struct subid_nss_db *nss_db;
bool ret;
- if (get_subid_nss_handle())
+ nss_db = get_subid_nss_db();
+ if (nss_db && nss_db->ops) {
+ // NSS module configured and the first database is not "files".
+ errno = EOPNOTSUPP;
return false;
+ }
switch (id_type) {
case ID_TYPE_UID:
@@ -1148,12 +1400,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ
void free_subid_pointer(void *ptr)
{
- struct subid_nss_ops *h = get_subid_nss_handle();
- if (h) {
- h->free(ptr);
- } else {
- free(ptr);
- }
+ free(ptr);
}
#else /* !ENABLE_SUBIDS */
diff --git a/man/subgid.5.xml b/man/subgid.5.xml
index 09a866fca6..2bfcd62a5c 100644
--- a/man/subgid.5.xml
+++ b/man/subgid.5.xml
@@ -41,20 +41,26 @@
The delegation of the subordinate gids can be configured via the
subid field in
- /etc/nsswitch.conf file. Only one value can be set
- as the delegation source. Setting this field to
- files configures the delegation of gids to
- /etc/subgid. Setting any other value treats
- the delegation as a plugin following with a name of the form
- libsubid_$value.so. If the value or plugin is
- missing, then the subordinate gid delegation falls back to
+ /etc/nsswitch.conf file. Multiple values can be
+ specified, separated by whitespace. These values will be consulted in order.
+ Setting this field to files configures the
+ delegation of gids to /etc/subgid. Setting any other
+ value treats the delegation as a plugin following with a name of the form
+ libsubid_$value.so. If the plugin is missing
+ or fails to load, it is ignored. If no plugin configures successfully,
+ the subordinate gid delegation falls back to
files.
- Note, that newusers, useradd, and
+ Note that the search stops as soon as the group exists in a delegation
+ database, even if no subids are defined for that group there.
+
+
+ Also note, that newusers, useradd, and
usermod will only create entries in
- /etc/subgid if subid delegation is managed via subid
- files.
+ /etc/subgid if the subid
+ delegation is not configured or files is the
+ first configured value.
diff --git a/man/subuid.5.xml b/man/subuid.5.xml
index eb28eed7ff..7aed0df792 100644
--- a/man/subuid.5.xml
+++ b/man/subuid.5.xml
@@ -41,20 +41,26 @@
The delegation of the subordinate uids can be configured via the
subid field in
- /etc/nsswitch.conf file. Only one value can be set
- as the delegation source. Setting this field to
- files configures the delegation of uids to
- /etc/subuid. Setting any other value treats
- the delegation as a plugin following with a name of the form
- libsubid_$value.so. If the value or plugin is
- missing, then the subordinate uid delegation falls back to
+ /etc/nsswitch.conf file. Multiple values can be
+ specified, separated by whitespace. These values will be consulted in order.
+ Setting this field to files configures the
+ delegation of uids to /etc/subuid. Setting any other
+ value treats the delegation as a plugin following with a name of the form
+ libsubid_$value.so. If the plugin is missing
+ or fails to load, it is ignored. If no plugin configures successfully,
+ the subordinate uid delegation falls back to
files.
- Note, that newusers, useradd, and
+ Note that the search stops as soon as the user exists in a delegation
+ database, even if no subids are defined for that user there.
+
+
+ Also note, that newusers, useradd, and
usermod will only create entries in
- /etc/subuid if subid delegation is managed via subid
- files.
+ /etc/subuid if the subid
+ delegation is not configured or files is the
+ first configured value.
diff --git a/tests/libsubid/04_nss/libsubid_zzz.c b/tests/libsubid/04_nss/libsubid_zzz.c
index 2e929687ec..0a76fe107f 100644
--- a/tests/libsubid/04_nss/libsubid_zzz.c
+++ b/tests/libsubid/04_nss/libsubid_zzz.c
@@ -6,60 +6,30 @@
#include
#include "alloc/malloc.h"
-enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result)
+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) {
- *result = true;
+ *result = start >= 200000 && start + count <= 200000 + 100000;
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;
+
+ if (strcmp(owner, "user1") == 0 && t == ID_TYPE_UID) {
+ *result = start >= 100000 && start + count <= 100000 + 65536;
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;
+ if (strcmp(owner, "group1") == 0 && t == ID_TYPE_GID) {
+ *result = start >= 100000 && start + count <= 100000 + 65536;
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;
+ return SUBID_STATUS_UNKNOWN_USER;
}
// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values,
@@ -109,34 +79,38 @@ enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_t
*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;
*in_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 = malloc_T(1, struct subid_range);
if (!ranges)
return SUBID_STATUS_ERROR;
- if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) {
+
+ *count = 1;
+ *in_ranges = ranges;
+
+ if (strcmp(owner, "user1") == 0 && id_type == ID_TYPE_UID) {
+ ranges[0].start = 100000;
+ ranges[0].count = 65536;
+ return SUBID_STATUS_SUCCESS;
+ }
+
+ if (strcmp(owner, "group1") == 0 && id_type == ID_TYPE_GID) {
ranges[0].start = 100000;
ranges[0].count = 65536;
- } else {
+ return SUBID_STATUS_SUCCESS;
+ }
+
+ if (strcmp(owner, "ubuntu") == 0) {
ranges[0].start = 200000;
ranges[0].count = 100000;
+ return SUBID_STATUS_SUCCESS;
}
- *count = 1;
- *in_ranges = ranges;
-
- return SUBID_STATUS_SUCCESS;
+ free(ranges);
+ *in_ranges = NULL;
+ return SUBID_STATUS_UNKNOWN_USER;
}
void shadow_subid_free(void *ptr)
diff --git a/tests/libsubid/04_nss/nsswitch4.conf b/tests/libsubid/04_nss/nsswitch4.conf
new file mode 100644
index 0000000000..b824e89ed9
--- /dev/null
+++ b/tests/libsubid/04_nss/nsswitch4.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 zzz
diff --git a/tests/libsubid/04_nss/nsswitch5.conf b/tests/libsubid/04_nss/nsswitch5.conf
new file mode 100644
index 0000000000..e474dd9e92
--- /dev/null
+++ b/tests/libsubid/04_nss/nsswitch5.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 files
diff --git a/tests/libsubid/04_nss/passwd b/tests/libsubid/04_nss/passwd
new file mode 100644
index 0000000000..fb5a6fe443
--- /dev/null
+++ b/tests/libsubid/04_nss/passwd
@@ -0,0 +1,24 @@
+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
+ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
+user1:x:1001:1001::/home/user1:/bin/bash
+user2:x:1002:1002::/home/user2:/bin/bash
+error:x:1003:1003::/home/error:/bin/bash
+conn:x:1004:1004::/home/conn:/bin/bash
\ No newline at end of file
diff --git a/tests/libsubid/04_nss/subidnss.test b/tests/libsubid/04_nss/subidnss.test
index 400171fa88..3e49418645 100755
--- a/tests/libsubid/04_nss/subidnss.test
+++ b/tests/libsubid/04_nss/subidnss.test
@@ -14,8 +14,11 @@ export LD_LIBRARY_PATH=.:${build_path}/lib/.libs:$LD_LIBRARY_PATH
./test_nss 1
./test_nss 2
./test_nss 3
+./test_nss 4
+./test_nss 5
unshare -Urm ./test_range
+unshare -Urm ./test_list
log_status "$0" "SUCCESS"
diff --git a/tests/libsubid/04_nss/subuid b/tests/libsubid/04_nss/subuid
new file mode 100644
index 0000000000..30eacbff6b
--- /dev/null
+++ b/tests/libsubid/04_nss/subuid
@@ -0,0 +1,4 @@
+user2:300000:65536
+ubuntu:400000:65536
+error:500000:65536
+conn:600000:65536
diff --git a/tests/libsubid/04_nss/test_list b/tests/libsubid/04_nss/test_list
new file mode 100755
index 0000000000..2e72fee421
--- /dev/null
+++ b/tests/libsubid/04_nss/test_list
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+set -x
+
+echo "starting getsubids tests"
+
+check_val() {
+ expected="$1"
+ shift
+ out=$("$@" 2>&1)
+ if [ "$out" != "$expected" ]; then
+ echo "FAIL: $*"
+ echo "Expected: $expected"
+ echo "Got: $out"
+ exit 1
+ fi
+}
+
+touch /etc/passwd
+mount --bind ./passwd /etc/passwd
+
+export LD_LIBRARY_PATH=.:${build_path}/libsubid/.libs:$LD_LIBRARY_PATH
+touch /etc/nsswitch.conf
+mount --bind ./nsswitch3.conf /etc/nsswitch.conf
+cleanup1() {
+ umount /etc/nsswitch.conf
+}
+trap cleanup1 EXIT HUP INT TERM
+check_val "0: user1 100000 65536" ${build_path}/src/.libs/getsubids user1
+check_val "Error fetching ranges" ${build_path}/src/.libs/getsubids user2
+check_val "0: group1 100000 65536" ${build_path}/src/.libs/getsubids -g group1
+check_val "0: ubuntu 200000 100000" ${build_path}/src/.libs/getsubids ubuntu
+check_val "Error fetching ranges" ${build_path}/src/.libs/getsubids error
+
+umount /etc/nsswitch.conf
+
+mount --bind ./nsswitch1.conf /etc/nsswitch.conf
+touch /etc/subuid /etc/subgid
+mount --bind ./subuid /etc/subuid
+mount --bind ./subuid /etc/subgid
+
+cleanup2() {
+ umount /etc/subuid
+ umount /etc/subgid
+ umount /etc/nsswitch.conf
+}
+trap cleanup2 EXIT HUP INT TERM
+check_val "Error fetching ranges" ${build_path}/src/.libs/getsubids user1
+check_val "0: user2 300000 65536" ${build_path}/src/.libs/getsubids user2
+check_val "0: user2 300000 65536" ${build_path}/src/.libs/getsubids -g user2
+check_val "0: ubuntu 400000 65536" ${build_path}/src/.libs/getsubids ubuntu
+
+umount /etc/subuid
+umount /etc/nsswitch.conf
+
+# nsswitch4: files zzz
+# subuid has user2, ubuntu, error, conn
+# zzz has user1, ubuntu (different range)
+mount --bind ./subuid /etc/subuid
+mount --bind ./nsswitch4.conf /etc/nsswitch.conf
+
+cleanup3() {
+ umount /etc/subuid
+ umount /etc/nsswitch.conf
+}
+trap cleanup3 EXIT HUP INT TERM
+
+check_val "0: user1 100000 65536" ${build_path}/src/.libs/getsubids user1
+check_val "0: user2 300000 65536" ${build_path}/src/.libs/getsubids user2
+check_val "0: ubuntu 400000 65536" ${build_path}/src/.libs/getsubids ubuntu
+
+umount /etc/nsswitch.conf
+
+# nsswitch5: zzz files
+mount --bind ./nsswitch5.conf /etc/nsswitch.conf
+
+cleanup4() {
+ umount /etc/subuid
+ umount /etc/nsswitch.conf
+}
+trap cleanup4 EXIT HUP INT TERM
+
+check_val "0: user1 100000 65536" ${build_path}/src/.libs/getsubids user1
+check_val "0: user2 300000 65536" ${build_path}/src/.libs/getsubids user2
+check_val "0: ubuntu 200000 100000" ${build_path}/src/.libs/getsubids ubuntu
+check_val "Error fetching ranges" ${build_path}/src/.libs/getsubids error
+
+echo "check_range tests complete"
+exit 0
diff --git a/tests/libsubid/04_nss/test_nss.c b/tests/libsubid/04_nss/test_nss.c
index 5d903ab41a..e92f1c6525 100644
--- a/tests/libsubid/04_nss/test_nss.c
+++ b/tests/libsubid/04_nss/test_nss.c
@@ -6,45 +6,118 @@
#include
extern bool nss_is_initialized();
-extern struct subid_nss_ops *get_subid_nss_handle();
+
+extern struct subid_nss_db *get_subid_nss_db();
+
+void check_files(struct subid_nss_db *h) {
+ if (!h) {
+ exit(1);
+ }
+ if (h->ops) {
+ exit(1);
+ }
+}
+
+void check_zzz(struct subid_nss_db *h) {
+ if (!h) {
+ exit(1);
+ }
+ if (!h->ops) {
+ exit(1);
+ }
+}
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())
+ if (!nss_is_initialized() || get_subid_nss_db())
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())
+ if (!nss_is_initialized() || get_subid_nss_db())
exit(1);
}
void test2() {
+ struct subid_nss_db *h;
// 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())
+ if (!nss_is_initialized())
+ exit(1);
+ h = get_subid_nss_db();
+ check_files(h);
+ if (h->next) {
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())
+ if (!nss_is_initialized())
exit(1);
+ h = get_subid_nss_db();
+ check_files(h);
+ if (h->next) {
+ exit(1);
+ }
}
void test3() {
- // nsswitch3 has a subid: testnss entry
- printf("test with 'test' subid entry\n");
+ struct subid_nss_db *h;
+ // nsswitch3 has a subid: zzz entry
+ printf("test with 'zzz' subid entry\n");
nss_init("./nsswitch3.conf");
- if (!nss_is_initialized() || !get_subid_nss_handle())
+ if (!nss_is_initialized())
exit(1);
+ h = get_subid_nss_db();
+ check_zzz(h);
+ if (h->next)
+ exit(1);
+
// second run should change nothing
- printf("test with 'test' subid entry, second run\n");
+ printf("test with 'zzz' subid entry, second run\n");
nss_init("./nsswitch3.conf");
- if (!nss_is_initialized() || !get_subid_nss_handle())
+ if (!nss_is_initialized())
+ exit(1);
+ h = get_subid_nss_db();
+ check_zzz(h);
+ if (h->next)
+ exit(1);
+}
+
+void test4() {
+ struct subid_nss_db *h;
+ // nsswitch4 has a subid: files zzz
+ printf("test with 'files zzz' subid entry\n");
+ nss_init("./nsswitch4.conf");
+ if (!nss_is_initialized())
+ exit(1);
+ h = get_subid_nss_db();
+ check_files(h);
+ if (!h->next)
+ exit(1);
+ check_zzz(h->next);
+ if (h->next->next)
+ exit(1);
+}
+
+void test5() {
+ struct subid_nss_db *h;
+ // nsswitch5 has a subid: zzz files
+ printf("test with 'zzz files' subid entry\n");
+ nss_init("./nsswitch5.conf");
+ if (!nss_is_initialized())
+ exit(1);
+ h = get_subid_nss_db();
+ check_zzz(h);
+ if (!h->next)
+ exit(1);
+ check_files(h->next);
+ if (h->next->next)
exit(1);
}
@@ -64,6 +137,8 @@ int main(int argc, char *argv[])
case 1: test1(); break;
case 2: test2(); break;
case 3: test3(); break;
+ case 4: test4(); break;
+ case 5: test5(); break;
default: exit(1);
}
diff --git a/tests/libsubid/04_nss/test_range b/tests/libsubid/04_nss/test_range
index 45a791c746..1fee910af1 100755
--- a/tests/libsubid/04_nss/test_range
+++ b/tests/libsubid/04_nss/test_range
@@ -4,6 +4,9 @@ set -x
echo "starting check_range tests"
+touch /etc/passwd
+mount --bind ./passwd /etc/passwd
+
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
touch /etc/nsswitch.conf
mount --bind ./nsswitch3.conf /etc/nsswitch.conf
@@ -48,5 +51,129 @@ if [ $? -eq 0 ]; then
exit 1
fi
+umount /etc/subuid
+umount /etc/nsswitch.conf
+
+# nsswitch4: files zzz
+# subuid has user2, ubuntu, error, conn
+# zzz has user1, ubuntu (different range)
+mount --bind ./subuid /etc/subuid
+mount --bind ./nsswitch4.conf /etc/nsswitch.conf
+
+cleanup3() {
+ umount /etc/subuid
+ umount /etc/nsswitch.conf
+}
+trap cleanup3 EXIT HUP INT TERM
+
+# user1 100000 -> not in files -> in zzz -> yes
+${build_path}/src/check_subid_range user1 u 100000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# user1 200000 -> not in files -> in zzz -> no
+${build_path}/src/check_subid_range user1 u 200000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# user2 300000 -> in files -> yes
+${build_path}/src/check_subid_range user2 u 300000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# user2 400000 -> in files -> no
+${build_path}/src/check_subid_range user2 u 400000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# unknown 500000 -> not in files -> not in zzz -> no
+${build_path}/src/check_subid_range unknown u 500000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# ubuntu 200000 -> in files -> no
+# Even though ubuntu 200000 is in zzz, it should stop at files because user is in files.
+${build_path}/src/check_subid_range ubuntu u 200000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# ubuntu 400000 -> in files -> yes
+${build_path}/src/check_subid_range ubuntu u 400000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+umount /etc/nsswitch.conf
+
+# nsswitch5: zzz files
+mount --bind ./nsswitch5.conf /etc/nsswitch.conf
+
+cleanup4() {
+ umount /etc/subuid
+ umount /etc/nsswitch.conf
+}
+trap cleanup4 EXIT HUP INT TERM
+
+# user1 100000 -> in zzz -> yes
+${build_path}/src/check_subid_range user1 u 100000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# user1 200000 -> in zzz -> no
+${build_path}/src/check_subid_range user1 u 200000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# user2 300000 -> not in zzz -> in files -> yes
+${build_path}/src/check_subid_range user2 u 300000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# user2 400000 -> not in zzz -> in files -> no
+${build_path}/src/check_subid_range user2 u 400000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# unknown 500000 -> not in zzz -> not in files -> no
+${build_path}/src/check_subid_range unknown u 500000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# ubuntu 200000 -> in zzz -> yes
+${build_path}/src/check_subid_range ubuntu u 200000 65535
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# ubuntu 400000 -> in zzz -> no
+# zzz has the user ubuntu. So it won't lookup files
+${build_path}/src/check_subid_range ubuntu u 400000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# error 500000 -> zzz errors -> no
+${build_path}/src/check_subid_range error u 500000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+# conn 600000 -> zzz errors -> no
+${build_path}/src/check_subid_range conn u 600000 65535
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
echo "check_range tests complete"
exit 0