diff options
author | Michael Stone <michael@laptop.org> | 2009-04-12 07:49:31 (GMT) |
---|---|---|
committer | Michael Stone <michael@laptop.org> | 2009-04-12 07:49:31 (GMT) |
commit | bf8af14d62c53e0307bd08e7023ddc9a7bb67ce7 (patch) | |
tree | c8dbe642948f39ddcc4083bd46195cad9bc449b7 | |
parent | 210be7b72d9a036586667f9a904073bac0b7b34a (diff) |
Improve nss-rainbow to produce adequate group membership data.
-rw-r--r-- | rainbow/nss/buf.c | 3 | ||||
-rw-r--r-- | rainbow/nss/cgen.h | 3 | ||||
-rw-r--r-- | rainbow/nss/gids.c | 2 | ||||
-rw-r--r-- | rainbow/nss/nat.c | 3 | ||||
-rw-r--r-- | rainbow/nss/nss-rainbow.c | 146 | ||||
-rw-r--r-- | rainbow/nss/test_nat.c | 3 | ||||
-rw-r--r-- | rainbow/rainbow/inject.py | 23 | ||||
-rw-r--r-- | rainbow/rainbow/util.py | 15 |
8 files changed, 152 insertions, 46 deletions
diff --git a/rainbow/nss/buf.c b/rainbow/nss/buf.c index 9ba8f64..5cf0fa4 100644 --- a/rainbow/nss/buf.c +++ b/rainbow/nss/buf.c @@ -4,10 +4,13 @@ #include <syslog.h> #include <string.h> #include <stdarg.h> +#include <stdlib.h> #include "cgen.h" #include "buf.h" +#include "config/debug.h" + int write_buf(char** buf, size_t * buflen, const char* val) { diff --git a/rainbow/nss/cgen.h b/rainbow/nss/cgen.h index 2172ad0..6d9f48e 100644 --- a/rainbow/nss/cgen.h +++ b/rainbow/nss/cgen.h @@ -2,7 +2,8 @@ #define SAVE_ERR(EXPR) {int __errno_save = errno; EXPR; errno = __errno_save;} #define __XSTRING(X) __STRING(X) /* #define PERROR(msg) {int __errno_cache = errno; dprintf(2, "%s|%d| %s: %s\nError %d: %s\n", __FILE__, __LINE__, __func__, msg, __errno_cache, strerror(__errno_cache));} */ -#define PERROR(msg) +#define PERROR(msg) {int __errno_cache = errno; if (getenv(DEBUG) != NULL) {dprintf(2, "%s|%d| %s: %s\n", __FILE__, __LINE__, __func__, msg); if (__errno_cache) {dprintf(2, "Error %d: %s\n", __errno_cache, strerror(__errno_cache));}};} +/* #define PERROR(msg) */ #define CHK(EXPR, MSG, ERR_LABEL) {if(EXPR) { PERROR(MSG); goto ERR_LABEL;}} #define LET(LETEXPR, CONDEXPR, MSG, ERR_LABEL) LETEXPR; if (CONDEXPR) { PERROR(MSG); goto ERR_LABEL;} #define TST(EXPR, ERRNO, MSG, ERR_LABEL) {if (EXPR) {errno = ERRNO; PERROR(MSG); goto ERR_LABEL;}} diff --git a/rainbow/nss/gids.c b/rainbow/nss/gids.c index 3fb93b2..a374dbe 100644 --- a/rainbow/nss/gids.c +++ b/rainbow/nss/gids.c @@ -13,7 +13,6 @@ int main() { setgrent(); while (1) { - printf("\n\n"); i = getgrent_r(&gr, buf, BUFLEN, &grp); if (i) { printf("status: %d\n", i); @@ -23,6 +22,7 @@ int main() { int num_members = strlen((char*)grp->gr_mem) / sizeof(char*); for (i = 0; i < num_members; i++) printf("%s ", grp->gr_mem[i]); + if (num_members) printf("\n"); cnt++; } endgrent(); diff --git a/rainbow/nss/nat.c b/rainbow/nss/nat.c index a5e70a1..dc6b648 100644 --- a/rainbow/nss/nat.c +++ b/rainbow/nss/nat.c @@ -3,10 +3,13 @@ #include <stdio.h> #include <syslog.h> #include <string.h> +#include <stdlib.h> #include "cgen.h" #include "nat.h" +#include "config/debug.h" + int nat_add(unsigned long* result, unsigned long arg) { unsigned long tmp = *result + arg; diff --git a/rainbow/nss/nss-rainbow.c b/rainbow/nss/nss-rainbow.c index bc51102..d043b67 100644 --- a/rainbow/nss/nss-rainbow.c +++ b/rainbow/nss/nss-rainbow.c @@ -35,7 +35,7 @@ static unsigned short g_uid = 0; * We're also going to be evil and use the scandir() traversal to directly * record allocated uids. <MS> */ int -uid_filter(const struct dirent* d) { +uid_scanner(const struct dirent* d) { unsigned long val; if(parse_nat(&val, d->d_name, strlen(d->d_name)) == 1) return 0; if(val < 10000 || val > 60000) return 0; @@ -54,8 +54,8 @@ _nss_rainbow_setpwent(void) { for (unsigned short i = 10000; i < 60000; i++) g_uids[i] = 0; g_uid = 0; - struct dirent** uids; - CHK(scandir(SPOOL "/uid_pool", &uids, uid_filter, versionsort) == -1, + struct dirent** ents; + CHK(scandir(SPOOL "/uid_pool", &ents, uid_scanner, versionsort) == -1, "Unable to extract uids from $RAINBOW_SPOOL.", out_error_spool); return NSS_STATUS_SUCCESS; out_error_spool: @@ -199,14 +199,14 @@ out_error_errno: #endif -struct uid_list { +struct member_list { struct list_head list; - uid_t uid; + char* name; }; struct gid_list { struct list_head list; - struct uid_list uids; + struct member_list members; gid_t gid; }; @@ -218,20 +218,22 @@ struct gid_list* g_gid; * We're also going to be evil and use the scandir() traversal to directly * record allocated uids. <MS> */ int -uid_to_gid_filter(const struct dirent* d) { - unsigned long uid, gid; +uid_to_gid_scanner(const struct dirent* d) { + unsigned long gid; char gid_path[NAME_MAX+1]; char gid_name[NAME_MAX+1]; size_t gid_path_len = NAME_MAX + 1; ssize_t gid_name_len = NAME_MAX + 1; char* gid_path_str = gid_path; + char* uid_str = d->d_name; + size_t uid_len = strlen(d->d_name); - CHK(parse_nat(&uid, d->d_name, strlen(d->d_name)) == 1, - "Unable to parse uid.", exit); - CHK(uid < 10000 || uid > 60000, - "uid not in valid range.", exit); + CHK(uid_len == 0 + || (uid_len == 1 && d->d_name[0] == '.') + || (uid_len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.'), + "Invalid path.", exit); - CHK(format_buf(&gid_path_str, &gid_path_len, SPOOL "/uid_to_gid/%d", uid) == 1, + CHK(format_buf(&gid_path_str, &gid_path_len, SPOOL "/uid_to_gid/%s", uid_str) == 1, "Unable to calculate uid-to-gid-path.", exit); LET(gid_name_len = readlink(gid_path, gid_name, gid_name_len), gid_name_len == -1, "Unable to read gid-path.", exit); @@ -240,7 +242,7 @@ uid_to_gid_filter(const struct dirent* d) { CHK(gid < 10000 || gid > 60000, "gid not in valid range.", exit); - /* We might have already allocated a uid_list for our gid. + /* We might have already allocated a gid_list for our gid. * Therefore, search for it. */ struct gid_list* gid_iter = NULL; list_for_each_entry(gid_iter, &g_gids.list, list) { @@ -248,12 +250,12 @@ uid_to_gid_filter(const struct dirent* d) { break; } - /* If we find no uid_list, make one and insert it. */ + /* If we find no gid_list, make one and insert it. */ if (&gid_iter->list == &g_gids.list) { LET(gid_iter = malloc(sizeof(struct gid_list)), !gid_iter, "Unable to allocate new gid_list node.", exit); gid_iter->gid = gid; - INIT_LIST_HEAD(&(gid_iter->uids.list)); + INIT_LIST_HEAD(&(gid_iter->members.list)); list_add_tail(&(gid_iter->list), &g_gids.list); g_gid = gid_iter; } @@ -261,23 +263,98 @@ uid_to_gid_filter(const struct dirent* d) { /* gid_iter now points to the appropriate gid_list node. * There's no way that we could already have our current (uid, gid) row. * Therefore, we'll immediately insert it. */ - struct uid_list* uid_iter; - LET(uid_iter = malloc(sizeof(struct uid_list)), !uid_iter, - "Unable to allocate new uid_list node.", exit); - uid_iter->uid = uid; - list_add_tail(&(uid_iter->list), &(gid_iter->uids.list)); + struct member_list* member_iter; + LET(member_iter = calloc(1, sizeof(struct member_list)), !member_iter, + "Unable to allocate new member_list node.", exit); + LET(member_iter->name = malloc(uid_len+1), !member_iter->name, + "Unable to allocate new member_list->name.", exit); + strncpy(member_iter->name, uid_str, uid_len+1); + list_add_tail(&(member_iter->list), &(gid_iter->members.list)); +exit: + return 0; +} + +int members_scanner(const struct dirent* d) { + size_t member_len = strlen(d->d_name); + char* member_str = d->d_name; + + CHK(member_len == 0 + || (member_len == 1 && d->d_name[0] == '.') + || (member_len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.'), + "Invalid data-gid-to-member member path.", exit); + + /* g_gid now points to the appropriate gid_list node. + * There's no way that we could already have our current (uid, gid) row. + * I think. :) + * Therefore, we'll immediately insert it. */ + struct member_list* member_iter; + LET(member_iter = calloc(1, sizeof(struct member_list)), !member_iter, + "Unable to allocate new member_list node.", exit); + LET(member_iter->name = malloc(member_len+1), !member_iter->name, + "Unable to allocate new member_list->name.", exit); + strncpy(member_iter->name, member_str, member_len+1); + list_add_tail(&(member_iter->list), &(g_gid->members.list)); exit: return 0; } +int +data_gid_to_members_scanner(const struct dirent* d) { + unsigned long gid; + struct dirent** ents; + char path[NAME_MAX+1]; + size_t path_len = NAME_MAX + 1; + char* path_str = path; + + CHK(path_len == 0 + || (path_len == 1 && d->d_name[0] == '.') + || (path_len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.'), + "Invalid data-gid-to-member gid dir.", exit); + + CHK(parse_nat(&gid, d->d_name, strlen(d->d_name)) == 1, + "Unable to parse d->d_name into a gid.", exit); + CHK(gid < 10000 || gid > 60000, + "gid not in valid range.", exit); + + CHK(format_buf(&path_str, &path_len, SPOOL "/data_gid_to_members/%d", gid) == 1, + "Unable to calculate data-gid-to-members path.", exit); + + /* We might have already allocated a gid_list for our gid. + * Therefore, search for it. */ + struct gid_list* gid_iter = NULL; + list_for_each_entry(gid_iter, &g_gids.list, list) { + if (gid_iter->gid == gid) { + g_gid = gid_iter; + break; + } + } + + /* If we find no gid_list, make one and insert it. */ + if (&gid_iter->list == &g_gids.list) { + LET(gid_iter = malloc(sizeof(struct gid_list)), !gid_iter, + "Unable to allocate new gid_list node.", exit); + gid_iter->gid = gid; + INIT_LIST_HEAD(&(gid_iter->members.list)); + list_add_tail(&(gid_iter->list), &g_gids.list); + g_gid = gid_iter; + } + + /* g_gid now points to an appropriate gid_list for us to fill in. */ + CHK(scandir(path, &ents, members_scanner, versionsort) == -1, + "Unable to scan members dir from $RAINBOW_SPOOL/data_gid_to_members/$GID.", exit); +exit: + return 0; +} enum nss_status _nss_rainbow_setgrent(void) { - struct dirent** uids; + struct dirent** ents; INIT_LIST_HEAD(&g_gids.list); - INIT_LIST_HEAD(&g_gids.uids.list); + INIT_LIST_HEAD(&g_gids.members.list); g_gid = &g_gids; - CHK(scandir(SPOOL "/uid_to_gid", &uids, uid_to_gid_filter, versionsort) == -1, + CHK(scandir(SPOOL "/uid_to_gid", &ents, uid_to_gid_scanner, versionsort) == -1, "Unable to extract uid->gids links from $RAINBOW_SPOOL.", out_error_spool); + CHK(scandir(SPOOL "/data_gid_to_members", &ents, data_gid_to_members_scanner, versionsort) == -1, + "Unable to extract gid->members dirs from $RAINBOW_SPOOL.", out_error_spool); g_gid = list_entry(g_gids.list.next, struct gid_list, list); return NSS_STATUS_SUCCESS; out_error_spool: @@ -286,13 +363,14 @@ out_error_spool: enum nss_status _nss_rainbow_endgrent(void) { struct gid_list* gid_iter; - struct uid_list* uid_iter; + struct member_list* member_iter; while(!list_empty(&g_gids.list)) { gid_iter = list_entry(g_gids.list.next, struct gid_list, list); - while(!list_empty(&(gid_iter->uids.list))) { - uid_iter = list_entry(gid_iter->uids.list.next, struct uid_list, list); - list_del(&(uid_iter->list)); - free(uid_iter); + while(!list_empty(&(gid_iter->members.list))) { + member_iter = list_entry(gid_iter->members.list.next, struct member_list, list); + list_del(&(member_iter->list)); + free(member_iter->name); + free(member_iter); } list_del(&(gid_iter->list)); free(gid_iter); @@ -318,10 +396,10 @@ fill_group(struct group* result, char* buf, size_t buflen, int *errnop, struct g "Unable to calculate group name.", out_error_errno); /* count the number of members */ - struct uid_list* uid_iter; - struct list_head* uid_node_iter; + struct member_list* member_iter; + struct list_head* member_node_iter; size_t member_count = 0; - list_for_each(uid_node_iter, &(gid_iter->uids.list)) { + list_for_each(member_node_iter, &(gid_iter->members.list)) { member_count++; } @@ -336,9 +414,9 @@ fill_group(struct group* result, char* buf, size_t buflen, int *errnop, struct g /* fill in the members and swing the pointers */ member_count = 0; - list_for_each_entry(uid_iter, &(gid_iter->uids.list), list) { + list_for_each_entry(member_iter, &(gid_iter->members.list), list) { result->gr_mem[member_count] = buf; - CHK(format_buf(&buf, &buflen, "%d", uid_iter->uid) == 1, + CHK(format_buf(&buf, &buflen, "%s", member_iter->name) == 1, "Unable to write a group member.", out_error_errno); member_count++; } diff --git a/rainbow/nss/test_nat.c b/rainbow/nss/test_nat.c index 99c7f84..b7a01cb 100644 --- a/rainbow/nss/test_nat.c +++ b/rainbow/nss/test_nat.c @@ -3,10 +3,13 @@ #include <stdio.h> #include <syslog.h> #include <string.h> +#include <stdlib.h> #include "cgen.h" #include "nat.h" +#include "config/debug.h" + int main() { openlog("nat", LOG_PERROR, LOG_LOCAL0); diff --git a/rainbow/rainbow/inject.py b/rainbow/rainbow/inject.py index 8ff9a8c..a8812f3 100644 --- a/rainbow/rainbow/inject.py +++ b/rainbow/rainbow/inject.py @@ -37,7 +37,7 @@ def reserve_tag(log, spool, tag, tag_map, tag_type, tag_type_plural, min, max, s tag_path = join(spool, tag_map, tag) elt_path = join(pool_dir, str(elt)) try: - symlink(elt_path, tag_path) + symlink(str(elt), tag_path) except OSError: unlink(elt_path) elt = int(basename(realpath(tag_path))) @@ -50,8 +50,17 @@ def reserve_uid(log, spool): uid = reserve_tag(log, spool, str(gid), 'uid_to_gid', 'uid', 'uids', 10000, 65534, 1) return (uid, gid) -def reserve_group(log, spool, group): - return reserve_tag(log, spool, group, 'bundle_id_to_gid', 'gid', 'gids', 10000, 65534, 1) +def reserve_group(log, spool, owner_uid, uid, group): + gid = reserve_tag(log, spool, group, 'bundle_id_to_gid', 'gid', 'gids', 10000, 65534, 1) + map_dir = join(spool, 'data_gid_to_members', str(gid)) + make_dirs(map_dir, 0, 0, 0755) + owner_path = join(spool, 'data_gid_to_owner', str(gid)) + if not lexists(owner_path): + symlink(str(owner_uid), join(spool, 'data_gid_to_owner', str(gid))) + # Only join groups that we own. + assert readlink(owner_path) == str(owner_uid) + open(join(map_dir, str(uid)), 'w').close() + return gid def grab_home(_, spool, uid, __, owner_gid): home = join(spool, 'uid_to_home_dir', str(uid)) @@ -173,7 +182,7 @@ def check_spool(spool, owner_uid, owner_gid): spool_dirs = ('uid_pool', 'gid_pool', 'uid_to_gid', 'bundle_id_to_gid', 'gid_to_data_dir', 'uid_to_home_dir', 'xephyr_display_pool', 'uid_to_xephyr_cookie', 'uid_to_xephyr_display', - 'uid_to_xephyr_auth') + 'uid_to_xephyr_auth', 'data_gid_to_members', 'data_gid_to_owner') for frag in spool_dirs: make_dirs(join(spool, frag), 0, 0, 0755) ck = Checker(join(spool, frag), owner_uid, owner_gid) @@ -184,7 +193,7 @@ def check_owner(_, __): def check_home_dirs(uid, gid, home, data_group_to_gid): for frag, _ in data_group_to_gid: - ck = Checker(join(home, frag), uid, gid) + ck = Checker(join(home, frag), uid, gid, [x for _, x in data_group_to_gid]) assert ck.positive(R_OK | W_OK | X_OK, S_IFDIR) def check_home(uid, gid, home): @@ -193,7 +202,7 @@ def check_home(uid, gid, home): def maybe_add_gid(owner_uid, gid): # rainbow should only let you drop privilege. - return getpwuid(owner_uid).pw_name in getgrgid(gid).mem + return getpwuid(owner_uid).pw_name in getgrgid(gid).gr_mem def configure_groups(log, owner_uid, groups, gid, data_group_to_gid, pset): groups.insert(0, gid) @@ -256,7 +265,7 @@ def inject(log, spool, env, argv, cwd, pset, safe_fds, owner_uid, owner_gid, gid, home = pw.pw_gid, pw.pw_dir # XXX: Need to verify ownership and membership before joining data groups. - data_group_to_gid = [(group, reserve_group(log, spool, group)) for group in data_groups] + data_group_to_gid = [(group, reserve_group(log, spool, owner_uid, uid, group)) for group in data_groups] configure_home(log, spool, home, owner_uid, owner_gid, uid, gid, data_group_to_gid) if cwd is None: diff --git a/rainbow/rainbow/util.py b/rainbow/rainbow/util.py index 5cd4029..3679c20 100644 --- a/rainbow/rainbow/util.py +++ b/rainbow/rainbow/util.py @@ -95,12 +95,21 @@ class Checker(object): In particular, this class does not know the special rules that adhere to uid 0. """ - def __init__(self, path, uid, gid): + def __init__(self, path, uid, gid, groups=None): self.path = path self.uid = uid self.gid = gid + self.groups = set(groups or []).union(set([gid])) self.observation = stat(self.path) + def __repr__(self): + return "Checker(%r, %r, %r, groups=%r)" % (self.path, self.uid, self.gid, self.groups) + + def __str__(self): + return ("Checker(%s, %s, %s, %s) -> mode: %o uid: %s, gid: %s" % + (self.path, self.uid, self.gid, self.groups, self.observation[ST_MODE], + self.observation[ST_UID], self.observation[ST_GID])) + def positive(self, needed, mask): # I'm going to try to turn off bits in mode as I verify that they can # be satisfied. @@ -108,7 +117,7 @@ class Checker(object): real_mode = o[ST_MODE] if self.uid == o[ST_UID]: needed &= ~((real_mode & 0700) >> 6) - if self.gid == o[ST_GID]: + if o[ST_GID] in self.groups: needed &= ~((real_mode & 0070) >> 3) needed &= ~(real_mode & 0007) # Make sure needed is empty and real_mode satisfies stat. @@ -122,7 +131,7 @@ class Checker(object): allowed = 0 if self.uid == o[ST_UID]: allowed |= (real_mode & 0700) >> 6 - if self.gid == o[ST_GID]: + if o[ST_GID] in self.groups: allowed |= (real_mode & 0070) >> 3 allowed |= (real_mode & 0007) # Did we avoid everything? |