Commit f7d417e4 authored by Killian Lufau's avatar Killian Lufau

Implement HMAC for babel

HMAC is added in babel call to prevent babel communication between
nodes of different re6st networks.
This solves the problem of machines in different re6st networks but
on the same LAN that exchange routes through babel.
The key used to authenticate packets is randomly created on 16 bytes
by the registry and sent to nodes when they fetch network parameters.

This uses the WIP hmac branch of jech/babeld with Nexedi patches
and the added possibility to not check HMAC in incoming packets
for better HMAC integration on a HMAC-less network.
parent 73314e4d
......@@ -95,7 +95,10 @@ class Cache(object):
config = {}
for k, v in x.iteritems():
k = str(k)
if k in base64:
if k.startswith('babel_hmac'):
if v:
v = self._decrypt(v.decode('base64'))
elif k in base64:
v = v.decode('base64')
elif type(v) is unicode:
v = str(v)
......@@ -130,7 +133,8 @@ class Cache(object):
# BBB: Use buffer because of http://bugs.python.org/issue13676
# on Python 2.6
db.executemany("INSERT OR REPLACE INTO config VALUES(?,?)",
((k, buffer(v) if k in base64 else v)
((k, buffer(v) if k in base64 or
k.startswith('babel_hmac') else v)
for k, v in config.iteritems()))
self._loadConfig(config.iteritems())
return [k[:-5] if k.endswith(':json') else k
......
......@@ -395,6 +395,8 @@ def main():
os.path.join(config.state, 'babeld.state'),
os.path.join(config.run, 'babeld.pid'),
control_socket, cache.babel_default,
tuple(getattr(cache, k, None) for k in
('babel_hmac0', 'babel_hmac1', 'babel_hmac2')),
*config.babel_args).stop)
if config.up:
exit.release()
......
......@@ -61,9 +61,10 @@ def client(iface, address_list, encrypt, *args, **kw):
return openvpn(iface, encrypt, *remote, **kw)
def router(ip, ip4, src, hello_interval, log_path, state_path,
pidfile, control_socket, default, *args, **kw):
def router(ip, ip4, src, hello_interval, log_path, state_path, pidfile,
control_socket, default, hmacs, *args, **kw):
ip, n = ip
hmac0, hmac1, hmac2 = hmacs
if ip4:
ip4, n4 = ip4
cmd = ['babeld',
......@@ -79,9 +80,26 @@ def router(ip, ip4, src, hello_interval, log_path, state_path,
# is not equivalent, at least not the way we use babeld
# (and we don't need RTA_SRC for ipv4).
'-C', 'ipv6-subtrees true',
'-C', 'default ' + default,
'-C', 'redistribute local deny',
'-C', 'redistribute ip %s/%s eq %s' % (ip, n, n)]
if hmac0:
cmd += '-C', ('key type blake2s id %s value %s'
% ('hmac0', hmac0.encode('hex')))
cmd += '-C', 'default %s hmac %s' % (default, 'hmac0')
if hmac1:
cmd += '-C', ('key type blake2s id %s value %s'
% ('hmac1', hmac1.encode('hex')))
elif hmac1 and hmac2 is not None:
cmd += '-C', ('key type blake2s id %s value %s'
% ('hmac1', hmac1.encode('hex')))
cmd += '-C', 'default %s hmac %s' % (default, 'hmac1')
if hmac2 == '':
cmd += '-C', 'ignore_no_hmac'
else:
cmd += '-C', ('key type blake2s id %s value %s'
% ('hmac2', hmac2.encode('hex')))
else:
cmd += '-C', 'default ' + default
if ip4:
cmd += '-C', 'redistribute ip %s/%s eq %s' % (ip4, n4, n4)
if src:
......
......@@ -71,6 +71,8 @@ class RegistryServer(object):
"value")
self.prefix = self.getConfig("prefix", None)
self.version = str(self.getConfig("version", "\0")) # BBB: blob
# Initialize HMAC key
self.setConfig('babel_hmac0', buffer(os.urandom(16)))
utils.sqliteCreateTable(self.db, "token",
"token TEXT PRIMARY KEY NOT NULL",
"email TEXT NOT NULL",
......@@ -139,7 +141,7 @@ class RegistryServer(object):
# The following entry lists values that are base64-encoded.
kw[''] = 'version',
kw['version'] = self.version.encode('base64')
self.network_config = zlib.compress(json.dumps(kw), 9)
self.network_config = kw
# The 3 first bits code the number of bytes.
def encodeVersion(self, version):
......@@ -483,7 +485,15 @@ class RegistryServer(object):
@rpc
def getNetworkConfig(self, cn):
return self.network_config
with self.lock:
cert = self.getCert(cn)
config = self.network_config.copy()
for k in 'babel_hmac0', 'babel_hmac1', 'babel_hmac2':
v = self.getConfig(k, None)
if v is not None:
config[k] = '' if v == '' else x509.encrypt(
cert, v).encode('base64')
return zlib.compress(json.dumps(config), 9)
def _queryAddress(self, peer):
self.sendto(peer, 1)
......@@ -550,6 +560,36 @@ class RegistryServer(object):
q("INSERT INTO crl VALUES (?,?)", (serial, not_after))
self.updateNetworkConfig()
@rpc_private
def updateHMAC(self):
with self.lock:
with self.db:
hmac0 = self.getConfig('babel_hmac0', None)
hmac1 = self.getConfig('babel_hmac1', None)
hmac2 = self.getConfig('babel_hmac2', None)
if hmac0:
if hmac1:
self.setConfig('babel_hmac2', hmac0)
self.db.execute("DELETE FROM config WHERE name=?",
('babel_hmac0',))
else:
self.setConfig('babel_hmac1', buffer(os.urandom(16)))
elif hmac1 and hmac2 is not None:
self.setConfig('babel_hmac0', hmac1)
self.db.execute("DELETE FROM config WHERE name=?",
('babel_hmac1',))
self.db.execute("DELETE FROM config WHERE name=?",
('babel_hmac2',))
elif not (hmac0 or hmac1 or hmac2):
# Initialization of HMAC on the network
self.setConfig('babel_hmac1', buffer(os.urandom(16)))
self.setConfig('babel_hmac2', '')
self.version = self.encodeVersion(
1 + self.decodeVersion(self.version))
self.setConfig('version', buffer(self.version))
self.network_config['version'] = self.version.encode('base64')
self.sendto(self.prefix, 0)
@rpc_private
def getNodePrefix(self, email):
with self.lock:
......
......@@ -191,8 +191,9 @@ class BaseTunnelManager(object):
# TODO: To minimize downtime when network parameters change, we should do
# our best to not restart any process. Ideally, this list should be
# empty and the affected subprocesses reloaded.
NEED_RESTART = frozenset(('babel_default', 'encrypt', 'hello',
'ipv4', 'ipv4_sublen'))
NEED_RESTART = frozenset(('babel_default', 'babel_hmac0',
'babel_hmac1', 'babel_hmac2', 'encrypt',
'hello', 'ipv4', 'ipv4_sublen'))
_geoiplookup = None
_forward = None
......
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