Commit eb76397c authored by Martín Ferrari's avatar Martín Ferrari

Don't use uid and gid to run programs, ask for an user/uid and then use

initgroups to set up the groups. Changes in the protocol to allow this.
parent e84afa8c
......@@ -34,9 +34,9 @@ PROC KILL <pid> <signal> 200/500 kill(pid, signal)
(1) valid arguments: mtu <n>, state <up|down>, name <name>, lladdr <addr>
(2) After PROC CRTE, only secondary PROC cmds are accepted until finished.
Arguments are: uid gid argv[0] argv[1] ...
The argv parameters are parsed as base64-encoded strings if they start with a
'=' character.
Arguments are: user argv[0] argv[1] ...
The parameters are parsed as base64-encoded strings if they start with a '='
character.
(3) Secondary PROC commands, only valid after PROC CRTE. All parameters parsed
as base64-encoded strings. Arguments for PROC ENV are pairs of key-value to
......@@ -66,7 +66,7 @@ protocol exchanges occur through the socket.
<S> 200 Ok.
<C> ADDR DEL 10 192.168.1.1 24
<S> 500 Address does not exist.
<C> PROC CRTE 100 100 /bin/sh sh -c sleep 10
<C> PROC CRTE root /bin/sh sh -c sleep 10
<S> 200 Entering PROC mode.
<C> PROC CWD /
<S> 200 CWD set to /.
......
......@@ -6,22 +6,35 @@ from netns.node import Node
class __Config(object):
def __init__(self):
self._run_as = 65535
self._run_as = 65534
try:
self._run_as = pwd.getpwnam('nobody')[2]
pwd.getpwnam('nobody')
self._run_as = 'nobody'
except:
pass
def _set_run_as(self, uid):
if type(uid) != int:
uid = pwd.getpwnam(uid)[2]
def _set_run_as(self, user):
if str(user).isdigit():
uid = int(user)
try:
_user = pwd.getpwuid(uid)[0]
except:
raise AttributeError("UID %d does not exist" % int(user))
run_as = int(user)
else:
try:
uid = pwd.getpwnam(str(user))[2]
except:
raise AttributeError("User %s does not exist" % str(user))
run_as = str(user)
if uid == 0:
raise AttributeError("Cannot run as root by default")
self._run_as = uid
self._run_as = run_as
return run_as
def _get_run_as(self):
return self._run_as
run_as = property(_get_run_as, _set_run_as, None,
"Default uid to run applications as")
"Default user to run applications as")
config = __Config()
get_nodes = Node.get_nodes
......
......@@ -39,7 +39,7 @@ _proto_commands = {
"DEL": ("sisi", "")
},
"PROC": {
"CRTE": ("iib", "b*"),
"CRTE": ("bb", "b*"),
"POLL": ("i", ""),
"WAIT": ("i", ""),
"KILL": ("i", "i")
......@@ -246,8 +246,8 @@ class Server(object):
self.reply(221, "Sayounara.");
self.closed = True
def do_PROC_CRTE(self, cmdname, uid, gid, file, *argv):
self._proc = { 'uid': uid, 'gid': gid, 'file': file, 'argv': argv }
def do_PROC_CRTE(self, cmdname, user, file, *argv):
self._proc = { 'user': user, 'file': file, 'argv': argv }
self.commands = _proc_commands
self.reply(200, "Entering PROC mode.")
......@@ -416,13 +416,14 @@ class Client(object):
passfd.sendfd(self._fd, fd, "PROC " + type)
self._read_and_check_reply()
def popen(self, uid, gid, file, argv = None, cwd = None, env = None,
def popen(self, user, file, argv = None, cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Start a subprocess in the slave; the interface resembles
subprocess.Popen, but with less functionality. In particular
stdin/stdout/stderr can only be None or a open file descriptor."""
params = ["PROC", "CRTE", uid, gid, base64.b64encode(file)]
params = ["PROC", "CRTE", base64.b64encode(user),
base64.b64encode(file)]
if argv != None:
for i in argv:
params.append(base64.b64encode(i))
......
#!/usr/bin/env python
# vim:ts=4:sw=4:et:ai:sts=4
import unittest
import grp, pwd, unittest
import netns
class TestConfigure(unittest.TestCase):
def setUp(self):
# Default == nobody || (uid_t) -1
import pwd
try:
self.nobodyid = pwd.getpwnam('nobody')[2]
except:
self.nobodyid = None
def test_config_run_as_static(self):
# Not allow root as default user
# Don't allow root as default user
self.assertRaises(AttributeError, setattr, netns.config,
'run_as', 'root')
self.assertRaises(AttributeError, setattr, netns.config,
'run_as', 0)
self.assertEquals(netns.config.run_as, self.nobodyid or 65535)
# Don't allow invalid users
self.assertRaises(AttributeError, setattr, netns.config,
'run_as', 'foobarbaz') # hope nobody has this user!
self.assertRaises(AttributeError, setattr, netns.config,
'run_as', -1)
def test_config_run_as_runtime(self):
netns.config.run_as = (self.nobodyid or 65535)
user = netns.config.run_as = 'nobody'
uid = pwd.getpwnam(user)[2]
gid = pwd.getpwnam(user)[3]
groups = [x[2] for x in grp.getgrall() if user in x[3]]
node = netns.Node()
app = node.start_process(["sleep", "1000"])
pid = app.pid
......@@ -30,12 +31,14 @@ class TestConfigure(unittest.TestCase):
while True:
data = stat.readline()
fields = data.split()
if fields[0] != 'Uid:':
continue
uid = fields[1]
if fields[0] == 'Uid:':
self.assertEquals(fields[1:4], (uid,) * 4)
if fields[0] == 'Gid:':
self.assertEquals(fields[1:4], (gid,) * 4)
if fields[0] == 'Groups:':
self.assertEquals(set(fields[1:]), set(groups))
break
stat.close()
self.assertEquals(uid, (self.nobodyid or 65535))
if __name__ == '__main__':
unittest.main()
......@@ -74,19 +74,19 @@ class TestServer(unittest.TestCase):
check_error(self, "proc poll 1 2") # too many args
check_error(self, "proc poll a") # invalid type
check_ok(self, "proc crte 0 0 /bin/sh", srv.do_PROC_CRTE,
check_ok(self, "proc crte 0 /bin/sh", srv.do_PROC_CRTE,
[0, 0, '/bin/sh'])
# Commands that would fail, but the parsing is correct
check_ok(self, "proc poll 0", None, [0])
check_ok(self, "proc wait 0", None, [0])
check_ok(self, "proc kill 0", None, [0])
check_error(self, "proc crte 0 0 =") # empty b64
check_error(self, "proc crte 0 0 =a") # invalid b64
check_error(self, "proc crte 0 =") # empty b64
check_error(self, "proc crte 0 =a") # invalid b64
# simulate proc mode
srv.commands = netns.protocol._proc_commands
check_error(self, "proc crte 0 0 foo")
check_error(self, "proc crte 0 foo")
check_error(self, "proc poll 0")
check_error(self, "proc wait 0")
check_error(self, "proc kill 0")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment