diff options
author | Michael Stone <michael@laptop.org> | 2009-04-11 08:40:36 (GMT) |
---|---|---|
committer | Michael Stone <michael@laptop.org> | 2009-04-11 08:40:36 (GMT) |
commit | 4c16e2af14a8c13d718f3505a18dfe51e187925e (patch) | |
tree | 85eeb68d8bbf1763dc87dcaeb99e3ee5dd7d6ad3 | |
parent | 21bc400eb8723e489916201f40243cdb3edbb587 (diff) |
Permit zero to many data groups.
-rw-r--r-- | rainbow/rainbow/inject.py | 154 |
1 files changed, 73 insertions, 81 deletions
diff --git a/rainbow/rainbow/inject.py b/rainbow/rainbow/inject.py index 2d15ada..9fd3ab8 100644 --- a/rainbow/rainbow/inject.py +++ b/rainbow/rainbow/inject.py @@ -1,26 +1,17 @@ import os -from os import R_OK, W_OK, X_OK, fork, symlink, unlink, O_CREAT, O_EXCL, readlink, chown, chmod +from os import R_OK, W_OK, X_OK, fork, symlink, unlink, O_CREAT, O_EXCL, chown, chmod from os import setgroups, setgid, setuid, chdir, umask, execvpe, waitpid, WEXITSTATUS from os import getpid, getuid, _exit, rename -from os.path import join, basename, realpath, lexists, islink +from os.path import join, basename, realpath, lexists from subprocess import check_call, Popen, PIPE from stat import S_IFDIR from tempfile import mkdtemp, mkstemp -from grp import getgrnam +from grp import getgrnam, getgrgid +from pwd import getpwuid import resource from rainbow.util import Checker, mount, make_dirs, get_fds, read_envdir -def strace(log, argv, env): - log(1, 'applying strace') - args = ['/usr/bin/strace', '-f'] - for k, v in env.iteritems(): - args.append('-E') - args.append('%s=%s' % (k, v)) - args.extend(argv) - - return args, {} - def reserve_elt(pool_dir, elt, max_elt, incr, elt_name): fd = None while elt < max_elt: @@ -34,66 +25,54 @@ def reserve_elt(pool_dir, elt, max_elt, incr, elt_name): raise RuntimeError("No " + elt_name + " available.") return elt -def reserve_credentials(log, spool, data_id, pset): - # Pick a gid for yourself. - gid = reserve_elt(join(spool, 'gid_pool'), 10001, 65534, 2, 'gids') - - # Then try to atomically symlink the gid you picked to 'name' in gid_dir. - # If you succeed, then you have the right gid. - # If you fail, someone else has the right gid so release yours and use theirs. - name_path = join(spool, 'bundle_id_to_gid', data_id) - gid_path = join(spool, 'gid_pool', str(gid)) +def reserve_tag(log, spool, tag, tag_map, tag_type, tag_type_plural, min, max, step): + # Pick an element for yourself. + pool_dir = join(spool, tag_type+'_pool') + elt = reserve_elt(pool_dir, min, max, step, tag_type_plural) + log(1, 'reserved %s (%d) for tag %s', tag_type, elt, tag) + + # Then try to atomically symlink the elt you picked to 'tag' in 'tag_map' dir. + # If you succeed, then you have the right elt. + # If you fail, someone else has the right elt so release yours and use theirs. + tag_path = join(spool, tag_map, tag) + elt_path = join(pool_dir, str(elt)) try: - symlink(gid_path, name_path) + symlink(elt_path, tag_path) except OSError: - unlink(gid_path) - gid = int(basename(realpath(name_path))) - - # Decide whether to reserve a new (even) uid or to set uid <- gid and reserve that. - uid = pset.has_permission('constant-uid') and gid or reserve_elt(join(spool, 'uid_pool'), 10000, 65534, 2, 'uids') - open(join(spool, 'uid_pool', str(gid)), 'w').close() - log(1, 'reserved credentials (%d, %d)', uid, gid) + unlink(elt_path) + elt = int(basename(realpath(tag_path))) + return elt - path = join(spool, 'uid_to_gid', str(uid)) - if not lexists(path): - symlink(str(gid), path) - else: - assert islink(path) and readlink(path) == str(gid) +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 grab_home(log, spool, data_id, uid, gid, owner_gid): +def reserve_group(log, spool, group): + return reserve_tag(log, spool, group, 'bundle_id_to_gid', 'gid', 'gids', 10000, 65534, 1) + +def grab_home(_, spool, uid, _, owner_gid): home = join(spool, 'uid_to_home_dir', str(uid)) make_dirs(home, uid, owner_gid, 0770) chown(home, uid, owner_gid) - # Per discussion with Bert Freudenberg, set the setgid bit on $SAR/instance + # Per discussion with Bert Freudenberg, set the setgid bit on $HOME # (i.e. $HOME) so that Sugar can better write inside it. <MS> chmod(home, 02770) return home -def configure_home(spool, owner_uid, owner_gid, uid, gid, home): - # Beware of CONSTANT_UID + instance==home: the instance dir may already exist. - path = join(spool, 'uid_to_instance_dir', str(uid)) - if not lexists(path): - symlink(home, path) - - if not lexists(join(home, 'instance')): - symlink('.', join(home, 'instance')) - - path = join(spool, 'gid_to_data_dir', str(gid)) - make_dirs(path, owner_uid, gid, 0770) - chown(path, owner_uid, gid) - chmod(path, 0770) - if not lexists(join(home, 'data')): - symlink(path, join(home, 'data')) - - #XXX: ARGH pippy. <MS> - make_dirs(join(home, 'tmp'), uid, gid, 0700) - mount('tmpfs', join(home, 'tmp'), 'tmpfs', 0, '') - -def mount_fsen(log, home): - log(1, 'Mounting tmpfsen on /tmp and /var/tmp.') - #XXX: Kills X socket. :( <MS> +def configure_home(_, spool, home, owner_uid, _, _, gid, data_group_to_gid): + for group, gid in data_group_to_gid: + path = join(spool, 'gid_to_data_dir', str(gid)) + make_dirs(path, owner_uid, gid, 0770) + chown(path, owner_uid, gid) + chmod(path, 02770) + if not lexists(join(home, group)): + symlink(path, join(home, group)) + +def mount_fsen(log, _): + log(1, 'Mounting tmpfsen on /var/tmp and... drat; /tmp kills the X socket. :( <MS>') #mount('tmpfs', '/tmp', 'tmpfs', 0, '') mount('tmpfs', '/var/tmp', 'tmpfs', 0, '') @@ -133,7 +112,7 @@ def run_assistant(log, assistant, env, owner_uid, owner_gid, uid, groups, safe_f log(1, 'pid %d uid %d', getpid(), getuid()) if envdir: check_call(['/bin/rm', '-rf', envdir]) -def launch(log, home, uid, gid, groups, argv, env, cwd, pset, safe_fds): +def launch(log, _, uid, gid, groups, argv, env, cwd, pset, safe_fds): # Set appropriate group membership(s), depending on requested permissions log(1, 'dropping privilege to (%d, %d, %r)', uid, gid, groups) setgroups(groups) @@ -167,9 +146,6 @@ def launch(log, home, uid, gid, groups, argv, env, cwd, pset, safe_fds): log(1, 'umask(0)') umask(0) - if pset.has_permission('strace'): - argv, env = strace(log, argv, env) - log(1, 'about to execve\nargv: %s\nenv: %s', argv, env) log(1, 'closing all fds but %s', safe_fds) for fd in get_fds(): @@ -179,10 +155,11 @@ def launch(log, home, uid, gid, groups, argv, env, cwd, pset, safe_fds): execvpe(argv[0], argv, env) -def check_data_id(data_id): +def check_data_groups(data_groups): # XXX: How do I figure out what MAX_PATH_LEN is in python? <MS> # XXX: How long are GECOS fields permitted to be? <MS> - assert data_id and '\0' not in data_id and len(data_id) < 128 + for data_id in data_groups: + assert data_id and '\0' not in data_id and len(data_id) < 128 def check_argv(argv): assert argv @@ -194,15 +171,15 @@ def check_cwd(uid, gid, cwd): def check_spool(spool, owner_uid, owner_gid): make_dirs(spool, 0, 0, 0755) spool_dirs = ('uid_pool', 'gid_pool', 'uid_to_gid', 'bundle_id_to_gid', - 'uid_to_instance_dir', 'gid_to_data_dir', 'uid_to_home_dir', - 'xephyr_display_pool', 'uid_to_xephyr_cookie', - 'uid_to_xephyr_display', 'uid_to_xephyr_auth') + 'gid_to_data_dir', 'uid_to_home_dir', 'xephyr_display_pool', + 'uid_to_xephyr_cookie', 'uid_to_xephyr_display', + 'uid_to_xephyr_auth') for frag in spool_dirs: make_dirs(join(spool, frag), 0, 0, 0755) ck = Checker(join(spool, frag), owner_uid, owner_gid) assert ck.positive(R_OK | X_OK, S_IFDIR) -def check_owner(owner_uid, owner_gid): +def check_owner(_, _): return True def check_home_dirs(uid, gid, home): @@ -214,14 +191,27 @@ def check_home(uid, gid, home): ck = Checker(home, uid, gid) assert ck.positive(R_OK | W_OK | X_OK, S_IFDIR) -def configure_groups(groups, gid, pset): +def maybe_add_gid(owner_uid, gid): + # rainbow should only let you drop privilege. + return getpwuid(owner_uid).pw_name in getgrgid(gid).mem + +def configure_groups(log, owner_uid, groups, gid, data_group_to_gid, pset): groups.insert(0, gid) - if pset.has_permission("audio"): groups.append(getgrnam("audio").gr_gid) - if pset.has_permission("video"): groups.append(getgrnam("video").gr_gid) - if pset.has_permission("serial"): groups.append(getgrnam("uucp").gr_gid) + + for _, data_gid in data_group_to_gid: + if maybe_add_gid(owner_uid, data_gid): + groups.insert(data_gid) + + for cap in ("audio", "video", "serial"): + try: + if pset.has_permission(cap): + cap_gid = getgrnam(cap).gr_gid + if maybe_add_gid(owner_uid, cap_gid): + groups.insert(cap_gid) + except Exception, e: log(1, "Skipping permission (%s) because of (%s).", cap, e) return list(set(groups)) -def configure_xephyr(log, spool, owner_gid, uid, env, safe_fds): +def configure_xephyr(_, spool, owner_gid, uid, env, safe_fds): # XXX: MUST CHECK RETURN VALUES on subprocesses!!!!! # XXX: I shouldn't be running these subprocesses as uid 0. # XXX: Must get env, fds right!!!! @@ -244,18 +234,20 @@ def configure_xephyr(log, spool, owner_gid, uid, env, safe_fds): return newenv def inject(log, spool, env, argv, cwd, pset, safe_fds, owner_uid, owner_gid, - groups, data_id, assistant): + groups, data_groups, assistant): # Note: exceptions are intended to bubble up to the caller and should # terminate execution. - check_data_id(data_id) + check_data_groups(data_groups) check_argv(argv) check_owner(owner_uid, owner_gid) check_spool(spool, owner_uid, owner_gid) - uid, gid = reserve_credentials(log, spool, data_id, pset) - home = grab_home(log, spool, data_id, uid, gid, owner_gid) - configure_home(spool, owner_uid, owner_gid, uid, gid, home) + uid, gid = reserve_uid(log, spool) + data_group_to_gid = [(group, reserve_group(log, spool, group)) for group in data_groups] + + home = grab_home(log, spool, uid, gid, owner_gid, ) + configure_home(log, spool, home, owner_uid, owner_gid, uid, gid, data_group_to_gid) if cwd is None: cwd = home @@ -264,7 +256,7 @@ def inject(log, spool, env, argv, cwd, pset, safe_fds, owner_uid, owner_gid, check_home_dirs(owner_uid, owner_gid, home) check_home(uid, gid, home) - groups = configure_groups(groups, gid, pset) + groups = configure_groups(log, owner_uid, groups, gid, data_group_to_gid, pset) env_updates = configure_xephyr(log, spool, owner_gid, uid, env, safe_fds) if env_updates: env.update(env_updates) env_updates = run_assistant(log, assistant, env, owner_uid, owner_gid, uid, groups, safe_fds) |