add rootwrap filters to wrap ip netns exec

fixes bug 1044083

This patch adds specific filters for the ip command. The first filter
matches ip with any subcomand except netns exec.  The second filter
matches "ip netns exec" and then relies on the caller (match_filter) to
verify the sub-command against the other filters. Matching the
subcommand separately allows for a single set of filter definitions that
work with and without namespaces.

Change-Id: Ifd0378dc3461f84867efb3cb60396d9cfa9e582d
This commit is contained in:
Mark McClain 2012-09-04 19:50:00 -04:00
parent aaec26c7db
commit 28df8f6d8f
6 changed files with 102 additions and 7 deletions

View File

@ -20,3 +20,9 @@ kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
# dhcp-agent uses cat
cat: RegExpFilter, /bin/cat, root, cat, /proc/\d+/cmdline
# ip_lib
ip: IpFilter, /sbin/ip, root
ip_usr: IpFilter, /usr/sbin/ip, root
ip_exec: IpNetnsExecFilter, /sbin/ip, root
ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root

View File

@ -12,8 +12,10 @@
sysctl: CommandFilter, /sbin/sysctl, root
# ip_lib
ip: CommandFilter, /sbin/ip, root
ip_usr: CommandFilter, /usr/sbin/ip, root
ip: IpFilter, /sbin/ip, root
ip_usr: IpFilter, /usr/sbin/ip, root
ip_exec: IpNetnsExecFilter, /sbin/ip, root
ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root
# ovs_lib (if OVSInterfaceDriver is used)
ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root

View File

@ -13,5 +13,9 @@
# from the old mechanism
brctl: CommandFilter, /sbin/brctl, root
brctl_usr: CommandFilter, /usr/sbin/brctl, root
ip: CommandFilter, /sbin/ip, root
ip_usr: CommandFilter, /usr/sbin/ip, root
# ip_lib
ip: IpFilter, /sbin/ip, root
ip_usr: IpFilter, /usr/sbin/ip, root
ip_exec: IpNetnsExecFilter, /sbin/ip, root
ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root

View File

@ -44,6 +44,11 @@ class CommandFilter(object):
return None
class ExecCommandFilter(CommandFilter):
def exec_args(self, userargs):
return []
class RegExpFilter(CommandFilter):
"""Command filter doing regexp matching for every argument"""
@ -163,3 +168,29 @@ class ReadFileFilter(CommandFilter):
if len(userargs) != 2:
return False
return True
class IpFilter(CommandFilter):
"""Specific filter for the ip utility to that does not match exec."""
def match(self, userargs):
if userargs[0] == 'ip':
if userargs[1] == 'netns':
if userargs[2] in ('list', 'add', 'delete'):
return True
else:
return False
else:
return True
class IpNetnsExecFilter(ExecCommandFilter):
"""Specific filter for the ip utility to that does match exec."""
def match(self, userargs):
if userargs[:3] == ['ip', 'netns', 'exec']:
return True
else:
return False
def exec_args(self, userargs):
return userargs[4:]

View File

@ -54,7 +54,7 @@ def load_filters(filters_path):
return filterlist
def match_filter(filters, userargs):
def match_filter(filter_list, userargs):
"""
Checks user command and arguments through command filters and
returns the first matching filter, or None is none matched.
@ -62,8 +62,18 @@ def match_filter(filters, userargs):
found_filter = None
for f in filters:
for f in filter_list:
if f.match(userargs):
if isinstance(f, filters.ExecCommandFilter):
# This command calls exec verify that remaining args
# matches another filter.
leaf_filters = [fltr for fltr in filter_list
if not isinstance(fltr,
filters.ExecCommandFilter)]
args = f.exec_args(userargs)
if not args or not match_filter(leaf_filters, args):
continue
# Try other filters if executable is absent
if not os.access(f.exec_path, os.X_OK):
if not found_filter:

View File

@ -17,9 +17,10 @@
import os
import subprocess
import unittest2 as unittest
from quantum.rootwrap import filters
from quantum.rootwrap import wrapper
import unittest
class RootwrapTestCase(unittest.TestCase):
@ -108,6 +109,47 @@ class RootwrapTestCase(unittest.TestCase):
self.assertEqual(f.get_command(usercmd), ['/bin/cat', goodfn])
self.assertTrue(f.match(usercmd))
def test_IpFilter_non_netns(self):
f = filters.IpFilter('/sbin/ip', 'root')
self.assertTrue(f.match(['ip', 'link', 'list']))
def _test_IpFilter_netns_helper(self, action):
f = filters.IpFilter('/sbin/ip', 'root')
self.assertTrue(f.match(['ip', 'link', action]))
def test_IpFilter_netns_add(self):
self._test_IpFilter_netns_helper('add')
def test_IpFilter_netns_delete(self):
self._test_IpFilter_netns_helper('delete')
def test_IpFilter_netns_list(self):
self._test_IpFilter_netns_helper('list')
def test_IpNetnsExecFilter_match(self):
f = filters.IpNetnsExecFilter('/sbin/ip', 'root')
self.assertTrue(
f.match(['ip', 'netns', 'exec', 'foo', 'ip', 'link', 'list']))
def test_IpNetnsExecFilter_nomatch(self):
f = filters.IpNetnsExecFilter('/sbin/ip', 'root')
self.assertFalse(f.match(['ip', 'link', 'list']))
def test_match_filter_recurses_exec_command_filter(self):
filter_list = [filters.IpNetnsExecFilter('/sbin/ip', 'root'),
filters.IpFilter('/sbin/ip', 'root')]
args = ['ip', 'netns', 'exec', 'foo', 'ip', 'link', 'list']
self.assertIsNotNone(wrapper.match_filter(filter_list, args))
def test_match_filter_recurses_exec_command_filter(self):
filter_list = [filters.IpNetnsExecFilter('/sbin/ip', 'root'),
filters.IpFilter('/sbin/ip', 'root')]
args = ['ip', 'netns', 'exec', 'foo', 'ip', 'netns', 'exec', 'bar',
'ip', 'link', 'list']
self.assertIsNone(wrapper.match_filter(filter_list, args))
def test_skips(self):
# Check that all filters are skipped and that the last matches
usercmd = ["cat", "/"]