summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvieira <vieira@yubo.be>2016-03-02 00:26:29 +0000
committerBrian May <brian@linuxpenguins.xyz>2016-03-02 18:50:37 +1100
commitcedc8dc14665eaec78ec994c7fb06398600e15ad (patch)
tree1da67c2120b72075a9212f1a68c02e8d38c98304
parente8047ce3a9fd3476f1ada0b3bdd78b4016d622c7 (diff)
Add support for OpenBSD
-rw-r--r--sshuttle/methods/pf.py66
-rw-r--r--sshuttle/tests/test_methods_pf.py102
2 files changed, 167 insertions, 1 deletions
diff --git a/sshuttle/methods/pf.py b/sshuttle/methods/pf.py
index e017c12..fc238f8 100644
--- a/sshuttle/methods/pf.py
+++ b/sshuttle/methods/pf.py
@@ -198,6 +198,70 @@ class FreeBsd(Generic):
super(FreeBsd, self).add_rules(rules)
+class OpenBsd(Generic):
+ POOL_TICKET_OFFSET = 4
+ RULE_ACTION_OFFSET = 3324
+ ANCHOR_CALL_OFFSET = 1036
+
+ def __init__(self):
+ class pfioc_natlook(Structure):
+ pf_addr = Generic.pf_addr
+ _fields_ = [("saddr", pf_addr),
+ ("daddr", pf_addr),
+ ("rsaddr", pf_addr),
+ ("rdaddr", pf_addr),
+ ("rdomain", c_uint16),
+ ("rrdomain", c_uint16),
+ ("sxport", c_uint16),
+ ("dxport", c_uint16),
+ ("rsxport", c_uint16),
+ ("rdxport", c_uint16),
+ ("af", c_uint8), # sa_family_t
+ ("proto", c_uint8),
+ ("proto_variant", c_uint8),
+ ("direction", c_uint8)]
+
+ self.pfioc_rule = c_char * 3400
+ self.pfioc_natlook = pfioc_natlook
+ super(OpenBsd, self).__init__()
+
+ def add_anchors(self):
+ # before adding anchors and rules we must override the skip lo
+ # that comes by default in openbsd pf.conf so the rules we will add,
+ # which rely on translating/filtering packets on lo, can work
+ pfctl('-f /dev/stdin', b'match on lo\n')
+ super(OpenBsd, self).add_anchors()
+
+ def add_rules(self, includes, port, dnsport, nslist):
+ tables = [
+ b'table <forward_subnets> {%s}' % b','.join(includes)
+ ]
+ translating_rules = [
+ b'pass in on lo0 inet proto tcp '
+ b'divert-to 127.0.0.1 port %r' % port
+ ]
+ filtering_rules = [
+ b'pass out inet proto tcp '
+ b'to <forward_subnets> route-to lo0 keep state'
+ ]
+
+ if len(nslist) > 0:
+ tables.append(
+ b'table <dns_servers> {%s}' %
+ b','.join([ns[1].encode("ASCII") for ns in nslist]))
+ translating_rules.append(
+ b'pass in on lo0 inet proto udp to <dns_servers>'
+ b'port 53 rdr-to 127.0.0.1 port %r' % dnsport)
+ filtering_rules.append(
+ b'pass out inet proto udp to '
+ b'<dns_servers> port 53 route-to lo0 keep state')
+
+ rules = b'\n'.join(tables + translating_rules + filtering_rules) \
+ + b'\n'
+
+ super(OpenBsd, self).add_rules(rules)
+
+
class Darwin(FreeBsd):
RULE_ACTION_OFFSET = 3068
@@ -252,6 +316,8 @@ class Darwin(FreeBsd):
if sys.platform == 'darwin':
pf = Darwin()
+elif sys.platform.startswith('openbsd'):
+ pf = OpenBsd()
else:
pf = FreeBsd()
diff --git a/sshuttle/tests/test_methods_pf.py b/sshuttle/tests/test_methods_pf.py
index b12cdf5..cb7d5f1 100644
--- a/sshuttle/tests/test_methods_pf.py
+++ b/sshuttle/tests/test_methods_pf.py
@@ -4,7 +4,7 @@ import socket
from sshuttle.methods import get_method
from sshuttle.helpers import Fatal
-from sshuttle.methods.pf import FreeBsd, Darwin
+from sshuttle.methods.pf import FreeBsd, Darwin, OpenBsd
def test_get_supported_features():
@@ -131,6 +131,29 @@ def test_firewall_command_freebsd(mock_pf_get_dev, mock_ioctl, mock_stdout):
]
+@patch('sshuttle.methods.pf.pf', OpenBsd())
+@patch('sshuttle.methods.pf.sys.stdout')
+@patch('sshuttle.methods.pf.ioctl')
+@patch('sshuttle.methods.pf.pf_get_dev')
+def test_firewall_command_openbsd(mock_pf_get_dev, mock_ioctl, mock_stdout):
+ method = get_method('pf')
+ assert not method.firewall_command("somthing")
+
+ command = "QUERY_PF_NAT %d,%d,%s,%d,%s,%d\n" % (
+ socket.AF_INET, socket.IPPROTO_TCP,
+ "127.0.0.1", 1025, "127.0.0.2", 1024)
+ assert method.firewall_command(command)
+
+ assert mock_pf_get_dev.mock_calls == [call()]
+ assert mock_ioctl.mock_calls == [
+ call(mock_pf_get_dev(), 0xc0504417, ANY),
+ ]
+ assert mock_stdout.mock_calls == [
+ call.write('QUERY_PF_NAT_SUCCESS 0.0.0.0,0\n'),
+ call.flush(),
+ ]
+
+
def pfctl(args, stdin=None):
if args == '-s all':
return (b'INFO:\nStatus: Disabled\nanother mary had a little lamb\n',
@@ -301,3 +324,80 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
mock_pf_get_dev.reset_mock()
mock_pfctl.reset_mock()
mock_ioctl.reset_mock()
+
+
+@patch('sshuttle.helpers.verbose', new=3)
+@patch('sshuttle.methods.pf.pf', OpenBsd())
+@patch('sshuttle.methods.pf.pfctl')
+@patch('sshuttle.methods.pf.ioctl')
+@patch('sshuttle.methods.pf.pf_get_dev')
+def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
+ mock_pfctl.side_effect = pfctl
+
+ method = get_method('pf')
+ assert method.name == 'pf'
+
+ with pytest.raises(Exception) as excinfo:
+ method.setup_firewall(
+ 1024, 1026,
+ [(10, u'2404:6800:4004:80c::33')],
+ 10,
+ [(10, 64, False, u'2404:6800:4004:80c::'),
+ (10, 128, True, u'2404:6800:4004:80c::101f')],
+ True)
+ assert str(excinfo.value) \
+ == 'Address family "AF_INET6" unsupported by pf method_name'
+ assert mock_pf_get_dev.mock_calls == []
+ assert mock_ioctl.mock_calls == []
+ assert mock_pfctl.mock_calls == []
+
+ with pytest.raises(Exception) as excinfo:
+ method.setup_firewall(
+ 1025, 1027,
+ [(2, u'1.2.3.33')],
+ 2,
+ [(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
+ True)
+ assert str(excinfo.value) == 'UDP not supported by pf method_name'
+ assert mock_pf_get_dev.mock_calls == []
+ assert mock_ioctl.mock_calls == []
+ assert mock_pfctl.mock_calls == []
+
+ method.setup_firewall(
+ 1025, 1027,
+ [(2, u'1.2.3.33')],
+ 2,
+ [(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
+ False)
+ assert mock_ioctl.mock_calls == [
+ call(mock_pf_get_dev(), 0xcd48441a, ANY),
+ call(mock_pf_get_dev(), 0xcd48441a, ANY),
+ ]
+ assert mock_pfctl.mock_calls == [
+ call('-f /dev/stdin', b'match on lo\n'),
+ call('-s all'),
+ call('-a sshuttle -f /dev/stdin',
+ b'table <forward_subnets> {!1.2.3.66/32,1.2.3.0/24}\n'
+ b'table <dns_servers> {1.2.3.33}\n'
+ b'pass in on lo0 inet proto tcp divert-to 127.0.0.1 port 1025\n'
+ b'pass in on lo0 inet proto udp to '
+ b'<dns_servers>port 53 rdr-to 127.0.0.1 port 1027\n'
+ b'pass out inet proto tcp to '
+ b'<forward_subnets> route-to lo0 keep state\n'
+ b'pass out inet proto udp to '
+ b'<dns_servers> port 53 route-to lo0 keep state\n'),
+ call('-e'),
+ ]
+ mock_pf_get_dev.reset_mock()
+ mock_ioctl.reset_mock()
+ mock_pfctl.reset_mock()
+
+ method.restore_firewall(1025, 2, False)
+ assert mock_ioctl.mock_calls == []
+ assert mock_pfctl.mock_calls == [
+ call('-a sshuttle -F all'),
+ call("-d"),
+ ]
+ mock_pf_get_dev.reset_mock()
+ mock_pfctl.reset_mock()
+ mock_ioctl.reset_mock()