Commit 0e785b5c authored by Killian Lufau's avatar Killian Lufau

Implement HMAC for babel

HMAC is added to babel to make sure nodes from a given re6st network
don't talk to nodes from another re6st network. This is useful when
machines from separate re6st networks are on a LAN.
The key is the same for all nodes with the same registry: a random
part created by their registry and passed through network parameters,
combined with the prefix and prefix length of this re6st network.

This uses the WIP hmac branch of jech/babeld with Nexedi patches
and the added possibility to not check HMAC for better HMAC integration.
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