diff options
author | Michael Stone <michael@laptop.org> | 2009-04-12 09:56:09 (GMT) |
---|---|---|
committer | Michael Stone <michael@laptop.org> | 2009-04-12 09:56:09 (GMT) |
commit | 485a84cba4284edd98c93385d9510605c2541fb7 (patch) | |
tree | 28dcd673f4f1afffd2d9d793d6ee5773f10190e6 /rainbow | |
parent | e81c6ce196116d6b6afe0efbfb559012820c88f2 (diff) |
Resume and multi-group now appear to function.
Diffstat (limited to 'rainbow')
-rwxr-xr-x | rainbow/bin/rainbow-run | 22 | ||||
-rw-r--r-- | rainbow/nss/nss-rainbow.c | 125 | ||||
-rw-r--r-- | rainbow/rainbow/inject.py | 35 |
3 files changed, 75 insertions, 107 deletions
diff --git a/rainbow/bin/rainbow-run b/rainbow/bin/rainbow-run index 2fa8d95..ca3b5cd 100755 --- a/rainbow/bin/rainbow-run +++ b/rainbow/bin/rainbow-run @@ -12,7 +12,7 @@ from pprint import pformat from rainbow.inject import inject from rainbow.permissions import PermissionSet -from rainbow.util import make_reporter, trace, unshare, CLONE_NEWNS, read_envdir +from rainbow.util import make_reporter, trace, unshare, CLONE_NEWNS, read_envdir sys.excepthook = trace @@ -32,7 +32,7 @@ def main(): help="Working directory.") parser.add_option('-f', '--fd', default=[], action='append', help="File descriptor number to leave open.") - parser.add_option('-i', '--id', default=None, + parser.add_option('-i', '--id', default=[], action='append', help="ID of shared-data group.") parser.add_option('-o', '--option', default=[], action='append', help="Options: video, audio, serial, constant-uid, xephyr.") @@ -40,6 +40,8 @@ def main(): help="Location of a permissions.info file.") parser.add_option('-u', '--user', default=None, help="Owning user.") + parser.add_option('-r', '--resume-user', default=None, + help="Resume <user>.") parser.add_option('-a', '--assistant', default=None, help="Task-specific assistant.") parser.add_option('-G', '--group', default=[], action='append', @@ -88,7 +90,14 @@ def main(): def check_serial(opts): return 'serial' in opts.option - def check_data_id(opts): + def check_resume_user(opts): + uid = None + if opts.resume_user: + uid = pwd.getpwnam(opts.resume_user).pw_uid + assert 10000 <= uid and uid < 60000 + return uid + + def check_data_ids(opts): return opts.id def check_assistant(opts): @@ -111,13 +120,14 @@ def main(): for perm in ('constant-uid', 'audio', 'video', 'serial'): pset._permissions.setdefault(perm, locals()['check_'+perm.replace('-','_')](opts)) - data_id = check_data_id(opts) + data_ids = check_data_ids(opts) assistant = check_assistant(opts) xephyr = check_xephyr(opts) - resume_uid = None + resume_uid = check_resume_user(opts) + if uid: report(1, "resuming uid (%d)", uid) - args = (report, spool, env, argv, cwd, pset, safe_fds, uid, gid, resume_uid, groups, [data_id], assistant, xephyr) + args = (report, spool, env, argv, cwd, pset, safe_fds, uid, gid, resume_uid, groups, data_ids, assistant, xephyr) report(1, 'rainbow:\n%s', pformat(args)) unshare(CLONE_NEWNS) diff --git a/rainbow/nss/nss-rainbow.c b/rainbow/nss/nss-rainbow.c index d043b67..21d746a 100644 --- a/rainbow/nss/nss-rainbow.c +++ b/rainbow/nss/nss-rainbow.c @@ -62,25 +62,42 @@ out_error_spool: return NSS_STATUS_UNAVAIL; } -enum nss_status -_nss_rainbow_getpwent_r(struct passwd *result, char* buf, size_t buflen, int *errnop) { - /* I'm going to interpret the reentrancy requirement on this function to mean - * that the sequence of all results returned by all calls to this function - * between calls to setpwent() should contain contain every entry exactly - * once. It is undefined whether writes to the underlying storage that - * interleave with calls to getpwent() will be visible to callers. */ - +int +read_gid_for_uid(unsigned long uid, unsigned long* gid) { char gid_path[NAME_MAX+1]; char gid_name[NAME_MAX+1]; size_t gid_path_len; ssize_t gid_name_len; char* gid_path_str; - unsigned long gid; + unsigned long val; -begin: gid_path_len = gid_name_len = NAME_MAX+1; gid_path_str = gid_path; + CHK(format_buf(&gid_path_str, &gid_path_len, SPOOL "/uid_to_gid/%d", uid) == 1, + "Unable to calculate gid-path.", out_err); + LET(gid_name_len = readlink(gid_path, gid_name, gid_name_len), gid_name_len == -1, + "Unable to read gid-path.", out_err); + CHK(parse_nat(&val, gid_name, gid_name_len) == 1, + "Unable to parse gid_buf into a gid.", out_err); + CHK(val < 10000 || val > 60000, + "gid not in valid range.", out_err); + + *gid = val; + return 0; +out_err: + return 1; +} + +enum nss_status +_nss_rainbow_getpwent_r(struct passwd *result, char* buf, size_t buflen, int *errnop) { + /* I'm going to interpret the reentrancy requirement on this function to mean + * that the sequence of all results returned by all calls to this function + * between calls to setpwent() should contain contain every entry exactly + * once. It is undefined whether writes to the underlying storage that + * interleave with calls to getpwent() will be visible to callers. */ + +begin: /* Locate the next reserved uid. */ while (true){ g_uid++; @@ -90,14 +107,9 @@ begin: result->pw_uid = g_uid; - CHK(format_buf(&gid_path_str, &gid_path_len, SPOOL "/uid_to_gid/%d", g_uid) == 1, - "Unable to calculate gid-path.", begin); - LET(gid_name_len = readlink(gid_path, gid_name, gid_name_len), gid_name_len == -1, - "Unable to read gid-path.", begin); - CHK(parse_nat(&gid, gid_name, gid_name_len) == 1, - "Unable to parse gid_buf into a gid.", begin); - CHK(gid < 10000 || gid > 60000, - "gid not in valid range.", begin); + unsigned long gid; + CHK(read_gid_for_uid(g_uid, &gid) == 1, + "Unable to calculate gid for uid.", begin); result->pw_gid = gid; result->pw_dir = buf; @@ -132,7 +144,11 @@ enum nss_status _nss_rainbow_getpwuid_r(uid_t uid, struct passwd *result, char* CHK(stat(result->pw_dir, &st) == -1, "Stat failed for homebuf.", out_error_errno); result->pw_uid = uid; - result->pw_gid = st.st_gid; + + unsigned long gid; + CHK(read_gid_for_uid(uid, &gid) == 1, + "Unable to read gid for uid.", out_error_errno); + result->pw_gid = gid; result->pw_gecos = result->pw_name = result->pw_passwd = buf; CHK(format_buf(&buf, &buflen, "%d", uid) == 1, @@ -213,67 +229,6 @@ struct gid_list { struct gid_list g_gids; struct gid_list* g_gid; -/* I'm going to assume that every appropriately named dirent represents a user. - * We may need to revise this, e.g. by checking for the existence of home dir. - * We're also going to be evil and use the scandir() traversal to directly - * record allocated uids. <MS> */ -int -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(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/%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); - CHK(parse_nat(&gid, gid_name, gid_name_len) == 1, - "Unable to parse gid_buf into a gid.", exit); - CHK(gid < 10000 || gid > 60000, - "gid not in valid range.", 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) - 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; - } - - /* 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 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; @@ -299,7 +254,7 @@ exit: } int -data_gid_to_members_scanner(const struct dirent* d) { +gid_to_members_scanner(const struct dirent* d) { unsigned long gid; struct dirent** ents; char path[NAME_MAX+1]; @@ -316,7 +271,7 @@ data_gid_to_members_scanner(const struct dirent* d) { 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, + CHK(format_buf(&path_str, &path_len, SPOOL "/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. @@ -341,7 +296,7 @@ data_gid_to_members_scanner(const struct dirent* d) { /* 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); + "Unable to scan members dir from $RAINBOW_SPOOL/gid_to_members/$GID.", exit); exit: return 0; } @@ -351,9 +306,7 @@ enum nss_status _nss_rainbow_setgrent(void) { INIT_LIST_HEAD(&g_gids.list); INIT_LIST_HEAD(&g_gids.members.list); g_gid = &g_gids; - 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, + CHK(scandir(SPOOL "/gid_to_members", &ents, 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; diff --git a/rainbow/rainbow/inject.py b/rainbow/rainbow/inject.py index 7dec051..4480114 100644 --- a/rainbow/rainbow/inject.py +++ b/rainbow/rainbow/inject.py @@ -43,24 +43,28 @@ def reserve_tag(log, spool, tag, tag_map, tag_type, tag_type_plural, min, max, s elt = int(basename(realpath(tag_path))) return elt -def reserve_uid(log, spool): - gid = reserve_elt(join(spool, 'gid_pool'), 10000, 65534, 1, 'gids') - log(1, 'reserved gid (%d) for new uid', gid) - - uid = reserve_tag(log, spool, str(gid), 'uid_to_gid', 'uid', 'uids', 10000, 65534, 1) - return (uid, gid) - -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)) +def add_uid_to_group(log, spool, owner_uid, uid, gid): + map_dir = join(spool, 'gid_to_members', str(gid)) make_dirs(map_dir, 0, 0, 0755) - owner_path = join(spool, 'data_gid_to_owner', str(gid)) + owner_path = join(spool, 'gid_to_owner', str(gid)) if not lexists(owner_path): - symlink(str(owner_uid), join(spool, 'data_gid_to_owner', str(gid))) + symlink(str(owner_uid), join(spool, '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() open(join(map_dir, str(getpwuid(owner_uid).pw_name)), 'w').close() + log(1, "added owner (%d) and uid (%d) to gid (%d)", owner_uid, uid, gid) + +def reserve_uid(log, spool, owner_uid): + gid = reserve_elt(join(spool, 'gid_pool'), 10000, 65534, 1, 'gids') + uid = reserve_elt(join(spool, 'uid_pool'), 10000, 65534, 1, 'uids') + symlink(str(gid), join(spool, 'uid_to_gid', str(uid))) + add_uid_to_group(log, spool, owner_uid, uid, gid) + return (uid, gid) + +def reserve_group(log, spool, owner_uid, uid, group): + gid = reserve_tag(log, spool, group, 'bundle_id_to_gid', 'gid', 'gids', 10000, 65534, 1) + add_uid_to_group(log, spool, owner_uid, uid, gid) return gid def grab_home(_, spool, uid, __, owner_gid): @@ -182,7 +186,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', 'data_gid_to_members', 'data_gid_to_owner') + 'uid_to_xephyr_auth', 'gid_to_members', '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) @@ -247,7 +251,7 @@ def configure_xephyr(_, spool, owner_gid, uid, env, safe_fds): def check_uid(_, spool, owner_uid, uid): assert 10000 <= uid and uid <= 65534 - assert readlink(join(spool, 'uid_to_owner_uid', str(uid))) == str(owner_uid) + assert getpwuid(owner_uid).pw_name in getgrgid(uid).gr_mem def inject(log, spool, env, argv, cwd, pset, safe_fds, owner_uid, owner_gid, uid, groups, data_groups, assistant, xephyr): @@ -260,12 +264,13 @@ def inject(log, spool, env, argv, cwd, pset, safe_fds, owner_uid, owner_gid, check_spool(spool, owner_uid, owner_gid) if not uid: - uid, gid = reserve_uid(log, spool) + uid, gid = reserve_uid(log, spool, owner_uid) home = grab_home(log, spool, uid, gid, owner_gid) else: check_uid(log, spool, owner_uid, uid) pw = getpwuid(uid) gid, home = pw.pw_gid, pw.pw_dir + log(1, "resuming uid (%d) for owner (%d) with gid (%d) and home (%s)", uid, owner_uid, gid, home) # XXX: Need to verify ownership and membership before joining data groups. data_group_to_gid = [(group, reserve_group(log, spool, owner_uid, uid, group)) for group in data_groups] |