before_install:
- sudo apt-get update -qq
- sudo apt-get -y install -qq automake autopoint xsltproc libselinux1-dev gettext expect
+ - sudo apt-get -y install -qq byacc libtool
script:
- ./autogen.sh --without-selinux --disable-man
- grep ENABLE_ config.status
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])
groupio.h \
gshadow.c \
lockpw.c \
+ nss.c \
nscd.c \
nscd.h \
sssd.c \
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <stdatomic.h>
+#include "prototypes.h"
+#include "../libsubid/subid.h"
+
+#define NSSWITCH "/etc/nsswitch.conf"
+
+// NSS plugin handling for subids
+// If nsswitch has a line like
+// subid: sssd
+// then sssd will be consulted for subids. Unlike normal NSS dbs,
+// only one db is supported at a time. That's open to debate, but
+// the subids are a pretty limited resource, and local files seem
+// bound to step on any other allocations leading to insecure
+// conditions.
+static atomic_flag nss_init_started;
+static atomic_bool nss_init_completed;
+
+static struct subid_nss_ops *subid_nss;
+
+bool nss_is_initialized() {
+ return atomic_load(&nss_init_completed);
+}
+
+void nss_exit() {
+ if (nss_is_initialized() && subid_nss) {
+ dlclose(subid_nss->handle);
+ free(subid_nss);
+ subid_nss = NULL;
+ }
+}
+
+// nsswitch_path is an argument only to support testing.
+void nss_init(char *nsswitch_path) {
+ FILE *nssfp = NULL;
+ char *line = NULL, *p, *token, *saveptr;
+ size_t len = 0;
+
+ if (atomic_flag_test_and_set(&nss_init_started)) {
+ // Another thread has started nss_init, wait for it to complete
+ while (!atomic_load(&nss_init_completed))
+ usleep(100);
+ return;
+ }
+
+ if (!nsswitch_path)
+ nsswitch_path = NSSWITCH;
+
+ // read nsswitch.conf to check for a line like:
+ // subid: files
+ nssfp = fopen(nsswitch_path, "r");
+ if (!nssfp) {
+ fprintf(stderr, "Failed opening %s: %m", nsswitch_path);
+ atomic_store(&nss_init_completed, true);
+ return;
+ }
+ while ((getline(&line, &len, nssfp)) != -1) {
+ if (line[0] == '\0' || line[0] == '#')
+ continue;
+ if (strlen(line) < 8)
+ continue;
+ if (strncasecmp(line, "subid:", 6) != 0)
+ continue;
+ p = &line[6];
+ while ((*p) && isspace(*p))
+ p++;
+ if (!*p)
+ continue;
+ for (token = strtok_r(p, " \n\t", &saveptr);
+ token;
+ token = strtok_r(NULL, " \n\t", &saveptr)) {
+ char libname[65];
+ void *h;
+ if (strcmp(token, "files") == 0) {
+ subid_nss = NULL;
+ goto done;
+ }
+ if (strlen(token) > 50) {
+ fprintf(stderr, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
+ fprintf(stderr, "Using files\n");
+ subid_nss = NULL;
+ goto done;
+ }
+ snprintf(libname, 64, "libsubid_%s.so", token);
+ h = dlopen(libname, RTLD_LAZY);
+ if (!h) {
+ fprintf(stderr, "Error opening %s: %s\n", libname, dlerror());
+ fprintf(stderr, "Using files\n");
+ subid_nss = NULL;
+ goto done;
+ }
+ subid_nss = malloc(sizeof(*subid_nss));
+ if (!subid_nss) {
+ dlclose(h);
+ goto done;
+ }
+ subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
+ if (!subid_nss->has_range) {
+ fprintf(stderr, "%s did not provide @has_range@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+ goto done;
+ }
+ subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
+ if (!subid_nss->list_owner_ranges) {
+ fprintf(stderr, "%s did not provide @list_owner_ranges@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+ goto done;
+ }
+ subid_nss->has_any_range = dlsym(h, "shadow_subid_has_any_range");
+ if (!subid_nss->has_any_range) {
+ fprintf(stderr, "%s did not provide @has_any_range@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+ goto done;
+ }
+ subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
+ if (!subid_nss->find_subid_owners) {
+ fprintf(stderr, "%s did not provide @find_subid_owners@\n", libname);
+ dlclose(h);
+ free(subid_nss);
+ subid_nss = NULL;
+ goto done;
+ }
+ subid_nss->handle = h;
+ goto done;
+ }
+ fprintf(stderr, "No usable subid NSS module found, using files\n");
+ // subid_nss has to be null here, but to ease reviews:
+ free(subid_nss);
+ subid_nss = NULL;
+ goto done;
+ }
+
+done:
+ atomic_store(&nss_init_completed, true);
+ free(line);
+ if (nssfp) {
+ atexit(nss_exit);
+ fclose(nssfp);
+ }
+}
+
+struct subid_nss_ops *get_subid_nss_handle() {
+ nss_init(NULL);
+ return subid_nss;
+}
/* myname.c */
extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void);
+/* nss.c */
+#include <libsubid/subid.h>
+extern void nss_init(char *nsswitch_path);
+extern bool nss_is_initialized();
+
+struct subid_nss_ops {
+ /*
+ * nss_has_any_range: does a user own any subid range
+ *
+ * @owner: username
+ * @idtype: subuid or subgid
+ * @result: true if a subid allocation was found for @owner
+ *
+ * returns success if the module was able to determine an answer (true or false),
+ * else an error status.
+ */
+ enum subid_status (*has_any_range)(const char *owner, enum subid_type idtype, bool *result);
+
+ /*
+ * nss_has_range: does a user own a given subid range
+ *
+ * @owner: username
+ * @start: first subid in queried range
+ * @count: number of subids in queried range
+ * @idtype: subuid or subgid
+ * @result: true if @owner has been allocated the subid range.
+ *
+ * returns success if the module was able to determine an answer (true or false),
+ * else an error status.
+ */
+ enum subid_status (*has_range)(const char *owner, unsigned long start, unsigned long count, enum subid_type idtype, bool *result);
+
+ /*
+ * nss_list_owner_ranges: list the subid ranges delegated to a user.
+ *
+ * @owner - string representing username being queried
+ * @id_type - subuid or subgid
+ * @ranges - pointer to an array of struct subordinate_range pointers, or
+ * NULL. The returned array of struct subordinate_range and its
+ * members must be freed by the caller.
+ * @count - pointer to an integer into which the number of returned ranges
+ * is written.
+
+ * returns success if the module was able to determine an answer,
+ * else an error status.
+ */
+ enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges, int *count);
+
+ /*
+ * nss_find_subid_owners: find uids who own a given subuid or subgid.
+ *
+ * @id - the delegated id (subuid or subgid) being queried
+ * @id_type - subuid or subgid
+ * @uids - pointer to an array of uids which will be allocated by
+ * nss_find_subid_owners()
+ * @count - number of uids found
+ *
+ * returns success if the module was able to determine an answer,
+ * else an error status.
+ */
+ enum subid_status (*find_subid_owners)(unsigned long id, enum subid_type id_type, uid_t **uids, int *count);
+
+ /* The dlsym handle to close */
+ void *handle;
+};
+
+extern struct subid_nss_ops *get_subid_nss_handle();
+
+
/* pam_pass_non_interactive.c */
#ifdef USE_PAM
extern int do_pam_passwd_non_interactive (const char *pam_service,
#include <sys/types.h>
#include <pwd.h>
#include <ctype.h>
+#include <fcntl.h>
/*
* subordinate_dup: create a duplicate range
{
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;
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);
}
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);
}
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);
}
}
/*
- struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
+ * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
*
* @owner: username
* @id_type: UID or GUID
+ * @ranges: pointer to array of ranges into which results will be placed.
*
- * Returns the subuid or subgid ranges which are owned by the specified
+ * Fills in the subuid or subgid ranges which are owned by the specified
* user. Username may be a username or a string representation of a
* UID number. If id_type is UID, then subuids are returned, else
- * subgids are returned. If there is an error, < 0 is returned.
+ * subgids are given.
+
+ * Returns the number of ranges found, or < 0 on error.
*
* The caller must free the subordinate range list.
*/
-struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
+int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges)
{
// TODO - need to handle owner being either uid or username
- const struct subordinate_range *range;
struct subordinate_range **ranges = NULL;
+ const struct subordinate_range *range;
struct commonio_db *db;
- int size = 0;
+ enum subid_status status;
+ int count = 0;
+ struct subid_nss_ops *h;
- if (id_type == ID_TYPE_UID)
+ *in_ranges = NULL;
+
+ h = get_subid_nss_handle();
+ if (h) {
+ status = h->list_owner_ranges(owner, id_type, in_ranges, &count);
+ if (status == SUBID_STATUS_SUCCESS)
+ return count;
+ return -1;
+ }
+
+ switch (id_type) {
+ case ID_TYPE_UID:
+ if (!sub_uid_open(O_RDONLY)) {
+ return -1;
+ }
db = &subordinate_uid_db;
- else
+ break;
+ case ID_TYPE_GID:
+ if (!sub_gid_open(O_RDONLY)) {
+ return -1;
+ }
db = &subordinate_gid_db;
+ break;
+ default:
+ return -1;
+ }
commonio_rewind(db);
while ((range = commonio_next(db)) != NULL) {
if (0 == strcmp(range->owner, owner)) {
- if (!append_range(&ranges, range, size++)) {
- free_subordinate_ranges(ranges);
- return NULL;
+ if (!append_range(&ranges, range, count++)) {
+ free_subordinate_ranges(ranges, count-1);
+ ranges = NULL;
+ count = -1;
+ goto out;
}
}
}
- return ranges;
+out:
+ if (id_type == ID_TYPE_UID)
+ sub_uid_close();
+ else
+ sub_gid_close();
+
+ *in_ranges = ranges;
+ return count;
}
static bool all_digits(const char *str)
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) {
}
}
+ if (id_type == ID_TYPE_UID)
+ sub_uid_close();
+ else
+ sub_gid_close();
+
return n;
}
{
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) {
}
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 */
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);
extern void write_mapping(int proc_dir_fd, int ranges,
struct map_range *mappings, const char *map_file, uid_t ruid);
+extern void nss_init(char *nsswitch_path);
+
#endif /* _ID_MAPPING_H_ */
libsubid_la_LIBADD = \
$(top_srcdir)/lib/libshadow.la \
$(top_srcdir)/libmisc/libmisc.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
AM_CPPFLAGS = \
-I${top_srcdir}/lib \
#include "idmapping.h"
#include "api.h"
-static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type)
+static
+int get_subid_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges)
{
- struct subordinate_range **ranges = NULL;
-
- switch (id_type) {
- case ID_TYPE_UID:
- if (!sub_uid_open(O_RDONLY)) {
- return NULL;
- }
- break;
- case ID_TYPE_GID:
- if (!sub_gid_open(O_RDONLY)) {
- return NULL;
- }
- break;
- default:
- return NULL;
- }
-
- ranges = list_owner_ranges(owner, id_type);
-
- if (id_type == ID_TYPE_UID)
- sub_uid_close();
- else
- sub_gid_close();
-
- return ranges;
+ return list_owner_ranges(owner, id_type, ranges);
}
-struct subordinate_range **get_subuid_ranges(const char *owner)
+int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges)
{
- return get_subid_ranges(owner, ID_TYPE_UID);
+ return get_subid_ranges(owner, ID_TYPE_UID, ranges);
}
-struct subordinate_range **get_subgid_ranges(const char *owner)
+int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges)
{
- return get_subid_ranges(owner, ID_TYPE_GID);
+ return get_subid_ranges(owner, ID_TYPE_GID, ranges);
}
-void subid_free_ranges(struct subordinate_range **ranges)
+void subid_free_ranges(struct subordinate_range **ranges, int count)
{
- return free_subordinate_ranges(ranges);
+ return free_subordinate_ranges(ranges, count);
}
-int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type)
+static
+int get_subid_owner(unsigned long id, enum subid_type id_type, uid_t **owner)
{
- int ret = -1;
-
- switch (id_type) {
- case ID_TYPE_UID:
- if (!sub_uid_open(O_RDONLY)) {
- return -1;
- }
- break;
- case ID_TYPE_GID:
- if (!sub_gid_open(O_RDONLY)) {
- return -1;
- }
- break;
- default:
- return -1;
- }
-
- ret = find_subid_owners(id, owner, id_type);
-
- if (id_type == ID_TYPE_UID)
- sub_uid_close();
- else
- sub_gid_close();
-
- return ret;
+ return find_subid_owners(id, id_type, owner);
}
int get_subuid_owners(uid_t uid, uid_t **owner)
{
- return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID);
+ return get_subid_owner((unsigned long)uid, ID_TYPE_UID, owner);
}
int get_subgid_owners(gid_t gid, uid_t **owner)
{
- return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID);
+ return get_subid_owner((unsigned long)gid, ID_TYPE_GID, owner);
}
+static
bool grant_subid_range(struct subordinate_range *range, bool reuse,
enum subid_type id_type)
{
- bool ret;
-
- switch (id_type) {
- case ID_TYPE_UID:
- if (!sub_uid_lock()) {
- printf("Failed loging subuids (errno %d)\n", errno);
- return false;
- }
- if (!sub_uid_open(O_CREAT | O_RDWR)) {
- printf("Failed opening subuids (errno %d)\n", errno);
- sub_uid_unlock();
- return false;
- }
- break;
- case ID_TYPE_GID:
- if (!sub_gid_lock()) {
- printf("Failed loging subgids (errno %d)\n", errno);
- return false;
- }
- if (!sub_gid_open(O_CREAT | O_RDWR)) {
- printf("Failed opening subgids (errno %d)\n", errno);
- sub_gid_unlock();
- return false;
- }
- break;
- default:
- return false;
- }
-
- ret = new_subid_range(range, id_type, reuse);
-
- if (id_type == ID_TYPE_UID) {
- sub_uid_close();
- sub_uid_unlock();
- } else {
- sub_gid_close();
- sub_gid_unlock();
- }
-
- return ret;
+ return new_subid_range(range, id_type, reuse);
}
bool grant_subuid_range(struct subordinate_range *range, bool reuse)
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);
}
#include "subid.h"
#include <stdbool.h>
-struct subordinate_range **get_subuid_ranges(const char *owner);
-struct subordinate_range **get_subgid_ranges(const char *owner);
-void subid_free_ranges(struct subordinate_range **ranges);
+int get_subuid_ranges(const char *owner, struct subordinate_range ***ranges);
+int get_subgid_ranges(const char *owner, struct subordinate_range ***ranges);
+void subid_free_ranges(struct subordinate_range **ranges, int count);
int get_subuid_owners(uid_t uid, uid_t **owner);
int get_subgid_owners(gid_t gid, uid_t **owner);
bool grant_subuid_range(struct subordinate_range *range, bool reuse);
bool grant_subgid_range(struct subordinate_range *range, bool reuse);
-bool free_subuid_range(struct subordinate_range *range);
-bool free_subgid_range(struct subordinate_range *range);
+bool ungrant_subuid_range(struct subordinate_range *range);
+bool ungrant_subgid_range(struct subordinate_range *range);
#include <sys/types.h>
+#include <stdbool.h>
#ifndef SUBID_RANGE_DEFINED
#define SUBID_RANGE_DEFINED 1
ID_TYPE_GID = 2
};
+enum subid_status {
+ SUBID_STATUS_SUCCESS = 0,
+ SUBID_STATUS_UNKNOWN_USER = 1,
+ SUBID_STATUS_ERROR_CONN = 2,
+ SUBID_STATUS_ERROR = 3,
+};
+
#define SUBID_NFIELDS 3
#endif
grpconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpunconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
lastlog_LDADD = $(LDADD) $(LIBAUDIT) $(LIBECONF)
-newuidmap_SOURCES = newuidmap.c ../libmisc/nss.c
-newgidmap_SOURCES = newgidmap.c ../libmisc/nss.c
-groupadd_SOURCES = groupadd.c ../libmisc/nss.c
-groupmod_SOURCES = groupmod.c ../libmisc/nss.c
-groupdel_SOURCES = groupdel.c ../libmisc/nss.c
-newusers_SOURCES = newusers.c ../libmisc/nss.c
-useradd_SOURCES = useradd.c ../libmisc/nss.c
-usermod_SOURCES = usermod.c ../libmisc/nss.c
-userdel_SOURCES = userdel.c ../libmisc/nss.c
login_SOURCES = \
login.c \
login_nopam.c
noinst_PROGRAMS += list_subid_ranges \
get_subid_owners \
new_subid_range \
- free_subid_range
+ free_subid_range \
+ check_subid_range
MISCLIBS = \
$(LIBAUDIT) \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
list_subid_ranges_CPPFLAGS = \
-I$(top_srcdir)/lib \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
get_subid_owners_CPPFLAGS = \
-I$(top_srcdir)/lib \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
free_subid_range_CPPFLAGS = \
-I$(top_srcdir)/lib \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.la \
$(top_builddir)/libsubid/libsubid.la \
- $(MISCLIBS)
+ $(MISCLIBS) -ldl
+
+check_subid_range_CPPFLAGS = \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/libmisc
+
+check_subid_range_LDADD = \
+ $(top_builddir)/lib/libshadow.la \
+ $(top_builddir)/libmisc/libmisc.la \
+ $(MISCLIBS) -ldl
endif
--- /dev/null
+// This program is for testing purposes only.
+// usage is "[program] owner [u|g] start count
+// Exits 0 if owner has subid range starting start, of size count
+// Exits 1 otherwise.
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "prototypes.h"
+#include "subordinateio.h"
+#include "idmapping.h"
+
+const char *Prog;
+
+int main(int argc, char **argv)
+{
+ char *owner;
+ unsigned long start, count;
+ bool check_uids;
+ Prog = Basename (argv[0]);
+
+ if (argc != 5)
+ exit(1);
+
+ owner = argv[1];
+ check_uids = argv[2][0] == 'u';
+ start = strtoul(argv[3], NULL, 10);
+ if (start == ULONG_MAX && errno == ERANGE)
+ exit(1);
+ count = strtoul(argv[4], NULL, 10);
+ if (count == ULONG_MAX && errno == ERANGE)
+ exit(1);
+ if (check_uids) {
+ if (have_sub_uids(owner, start, count))
+ exit(0);
+ exit(1);
+ }
+ if (have_sub_gids(owner, start, count))
+ exit(0);
+ exit(1);
+}
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");
int main(int argc, char *argv[])
{
- int i;
+ int i, count=0;
struct subordinate_range **ranges;
Prog = Basename (argv[0]);
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;
}
--- /dev/null
+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
--- /dev/null
+#include <sys/types.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <subid.h>
+#include <string.h>
+
+enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result)
+{
+ if (strcmp(owner, "ubuntu") == 0) {
+ *result = true;
+ return SUBID_STATUS_SUCCESS;
+ }
+ if (strcmp(owner, "error") == 0) {
+ *result = false;
+ return SUBID_STATUS_ERROR;
+ }
+ if (strcmp(owner, "unknown") == 0) {
+ *result = false;
+ return SUBID_STATUS_UNKNOWN_USER;
+ }
+ if (strcmp(owner, "conn") == 0) {
+ *result = false;
+ return SUBID_STATUS_ERROR_CONN;
+ }
+ if (t == ID_TYPE_UID) {
+ *result = strcmp(owner, "user1") == 0;
+ return SUBID_STATUS_SUCCESS;
+ }
+
+ *result = strcmp(owner, "group1") == 0;
+ return SUBID_STATUS_SUCCESS;
+}
+
+enum subid_status shadow_subid_has_range(const char *owner, unsigned long start, unsigned long count, enum subid_type t, bool *result)
+{
+ if (strcmp(owner, "ubuntu") == 0 &&
+ start >= 200000 &&
+ count <= 100000) {
+ *result = true;
+ return SUBID_STATUS_SUCCESS;
+ }
+ *result = false;
+ if (strcmp(owner, "error") == 0)
+ return SUBID_STATUS_ERROR;
+ if (strcmp(owner, "unknown") == 0)
+ return SUBID_STATUS_UNKNOWN_USER;
+ if (strcmp(owner, "conn") == 0)
+ return SUBID_STATUS_ERROR_CONN;
+
+ if (t == ID_TYPE_UID && strcmp(owner, "user1") != 0)
+ return SUBID_STATUS_SUCCESS;
+ if (t == ID_TYPE_GID && strcmp(owner, "group1") != 0)
+ return SUBID_STATUS_SUCCESS;
+
+ if (start < 100000)
+ return SUBID_STATUS_SUCCESS;
+ if (count >= 65536)
+ return SUBID_STATUS_SUCCESS;
+ *result = true;
+ return SUBID_STATUS_SUCCESS;
+}
+
+// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values,
+// to ease manual testing. For automated testing, if you return those values,
+// we'll return 1000 for ubuntu and 1001 otherwise.
+static uid_t getnamuid(const char *name) {
+ struct passwd *pw;
+
+ pw = getpwnam(name);
+ if (pw)
+ return pw->pw_uid;
+
+ // For testing purposes
+ return strcmp(name, "ubuntu") == 0 ? (uid_t)1000 : (uid_t)1001;
+}
+
+static int alloc_uid(uid_t **uids, uid_t id) {
+ *uids = malloc(sizeof(uid_t));
+ if (!*uids)
+ return -1;
+ *uids[0] = id;
+ return 1;
+}
+
+enum subid_status shadow_subid_find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids, int *count)
+{
+ if (id >= 100000 && id < 165536) {
+ *count = alloc_uid(uids, getnamuid("user1"));
+ if (*count == 1)
+ return SUBID_STATUS_SUCCESS;
+ return SUBID_STATUS_ERROR; // out of memory
+ }
+ if (id >= 200000 && id < 300000) {
+ *count = alloc_uid(uids, getnamuid("ubuntu"));
+ if (*count == 1)
+ return SUBID_STATUS_SUCCESS;
+ return SUBID_STATUS_ERROR; // out of memory
+ }
+ *count = 0; // nothing found
+ return SUBID_STATUS_SUCCESS;
+}
+
+enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges, int *count)
+{
+ struct subordinate_range **ranges;
+
+ *count = 0;
+ if (strcmp(owner, "error") == 0)
+ return SUBID_STATUS_ERROR;
+ if (strcmp(owner, "unknown") == 0)
+ return SUBID_STATUS_UNKNOWN_USER;
+ if (strcmp(owner, "conn") == 0)
+ return SUBID_STATUS_ERROR_CONN;
+
+ *ranges = NULL;
+ if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 &&
+ strcmp(owner, "group1") != 0)
+ return SUBID_STATUS_SUCCESS;
+ if (id_type == ID_TYPE_GID && strcmp(owner, "user1") == 0)
+ return SUBID_STATUS_SUCCESS;
+ if (id_type == ID_TYPE_UID && strcmp(owner, "group1") == 0)
+ return SUBID_STATUS_SUCCESS;
+ ranges = (struct subordinate_range **)malloc(sizeof(struct subordinate_range *));
+ if (!*ranges)
+ return SUBID_STATUS_ERROR;
+ ranges[0] = (struct subordinate_range *)malloc(sizeof(struct subordinate_range));
+ if (!ranges[0]) {
+ free(*ranges);
+ *ranges = NULL;
+ return SUBID_STATUS_ERROR;
+ }
+ ranges[0]->owner = strdup(owner);
+ if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) {
+ ranges[0]->start = 100000;
+ ranges[0]->count = 65536;
+ } else {
+ ranges[0]->start = 200000;
+ ranges[0]->count = 100000;
+ }
+
+ *count = 1;
+ *in_ranges = ranges;
+
+ return SUBID_STATUS_SUCCESS;
+}
--- /dev/null
+# /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
--- /dev/null
+# /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
--- /dev/null
+# /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
--- /dev/null
+#!/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
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <prototypes.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+
+extern bool nss_is_initialized();
+extern struct subid_nss_ops *get_subid_nss_handle();
+
+void test1() {
+ // nsswitch1 has no subid: entry
+ setenv("LD_LIBRARY_PATH", ".", 1);
+ printf("Test with no subid entry\n");
+ nss_init("./nsswitch1.conf");
+ if (!nss_is_initialized() || get_subid_nss_handle())
+ exit(1);
+ // second run should change nothing
+ printf("Test with no subid entry, second run\n");
+ nss_init("./nsswitch1.conf");
+ if (!nss_is_initialized() || get_subid_nss_handle())
+ exit(1);
+}
+
+void test2() {
+ // nsswitch2 has a subid: files entry
+ printf("test with 'files' subid entry\n");
+ nss_init("./nsswitch2.conf");
+ if (!nss_is_initialized() || get_subid_nss_handle())
+ exit(1);
+ // second run should change nothing
+ printf("test with 'files' subid entry, second run\n");
+ nss_init("./nsswitch2.conf");
+ if (!nss_is_initialized() || get_subid_nss_handle())
+ exit(1);
+}
+
+void test3() {
+ // nsswitch3 has a subid: testnss entry
+ printf("test with 'test' subid entry\n");
+ nss_init("./nsswitch3.conf");
+ if (!nss_is_initialized() || !get_subid_nss_handle())
+ exit(1);
+ // second run should change nothing
+ printf("test with 'test' subid entry, second run\n");
+ nss_init("./nsswitch3.conf");
+ if (!nss_is_initialized() || !get_subid_nss_handle())
+ exit(1);
+}
+
+const char *Prog;
+
+int main(int argc, char *argv[])
+{
+ int which;
+
+ Prog = Basename(argv[0]);
+
+ if (argc < 1)
+ exit(1);
+
+ which = atoi(argv[1]);
+ switch(which) {
+ case 1: test1(); break;
+ case 2: test2(); break;
+ case 3: test3(); break;
+ default: exit(1);
+ }
+
+ printf("nss parsing tests done\n");
+ exit(0);
+}
--- /dev/null
+#!/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
run_test ./newuidmap/02_newuidmap_relaxed_gid_check/newuidmap.test
run_test ./newgidmap/01_newgidmap/newgidmap.test
run_test ./newgidmap/02_newgidmap_relaxed_gid_check/newgidmap.test
+run_test ./libsubid/04_nss/subidnss.test
echo
echo "$succeeded test(s) passed"