Commit 7977404a authored by Julien Muchembled's avatar Julien Muchembled

refactoring: move crypto code to a new file

parent 5be3cc90
#!/usr/bin/python
import sqlite3, sys
import os; sys.path[0] = os.path.dirname(sys.path[0])
from re6st import utils
from re6st import utils, x509
from OpenSSL import crypto
with open("/etc/re6stnet/ca.crt") as f:
ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
network = utils.networkFromCa(ca)
network = x509.networkFromCa(ca)
db = sqlite3.connect("/var/lib/re6stnet/registry.db")
for x in sys.argv[1:]:
......
#!/usr/bin/python
import argparse, atexit, errno, os, subprocess, sqlite3, sys, time
from OpenSSL import crypto
from re6st import registry, utils
from re6st import registry, utils, x509
def create(path, text=None, mode=0666):
fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, mode)
......@@ -54,7 +54,7 @@ def main():
# Get CA
ca = s.getCa()
network = utils.networkFromCa(loadCert(ca))
network = x509.networkFromCa(loadCert(ca))
if config.is_needed:
route, err = subprocess.Popen(('ip', '-6', '-o', 'route', 'get',
utils.ipFromBin(network)),
......@@ -143,7 +143,7 @@ def main():
os.close(cert_fd)
cert = loadCert(cert)
not_after = utils.notAfter(cert)
not_after = x509.notAfter(cert)
print("Setup complete. Certificate is valid until %s UTC"
" and will be automatically renewed after %s UTC.\n"
"Do not forget to backup to your private key (%s) or"
......@@ -169,7 +169,7 @@ dh %s
""" % (config.registry, ca_path, cert_path, key_path, dh_path))
print "Sample configuration file created."
cn = utils.subnetFromCert(cert)
cn = x509.subnetFromCert(cert)
subnet = network + utils.binFromSubnet(cn)
print "Your subnet: %s/%u (CN=%s)" \
% (utils.ipFromBin(subnet), len(subnet), cn)
......
import logging, sqlite3, socket, subprocess, time
from . import utils
from re6st.registry import RegistryClient
class PeerDB(object):
# internal ip = temp arg/attribute
def __init__(self, db_path, registry, key_path, network, prefix,
db_size=200):
self._prefix = prefix
def __init__(self, db_path, registry, cert, db_size=200):
self._prefix = cert.prefix
self._db_size = db_size
self._key_path = key_path
self._registry = registry
self._decrypt = cert.decrypt
self._registry = RegistryClient(registry, cert)
logging.info('Initialize cache ...')
self._db = sqlite3.connect(db_path, isolation_level=None)
......@@ -100,7 +98,7 @@ class PeerDB(object):
logging.info('Getting Boot peer...')
try:
bootpeer = self._registry.getBootstrapPeer(self._prefix)
prefix, address = utils.decrypt(self._key_path, bootpeer).split()
prefix, address = self._decrypt(bootpeer).split()
except (socket.error, subprocess.CalledProcessError, ValueError), e:
logging.warning('Failed to bootstrap (%s)',
e if bootpeer else 'no peer returned')
......
......@@ -27,7 +27,7 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from email.mime.text import MIMEText
from OpenSSL import crypto
from urllib import splittype, splithost, splitport, urlencode
from . import ctl, tunnel, utils, version
from . import ctl, tunnel, utils, version, x509
HMAC_HEADER = "Re6stHMAC"
RENEW_PERIOD = 30 * 86400
......@@ -79,16 +79,12 @@ class RegistryServer(object):
else:
self.db.execute("INSERT INTO cert VALUES ('',null,null)")
# Loading certificates
with open(self.config.ca) as f:
self.ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
with open(self.config.key) as f:
self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
self.cert = x509.Cert(self.config.ca, self.config.key)
# Get vpn network prefix
self.network = utils.networkFromCa(self.ca)
self.network = self.cert.network
logging.info("Network: %s/%u", utils.ipFromBin(self.network),
len(self.network))
self.email = self.ca.get_subject().emailAddress
self.email = self.cert.ca.get_subject().emailAddress
self.peers_lock = threading.Lock()
self.ctl = ctl.Babel(config.control_socket,
......@@ -141,7 +137,7 @@ class RegistryServer(object):
cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
except crypto.Error:
continue
x = utils.notAfter(cert)
x = x509.notAfter(cert)
if x <= old:
if prefix == self.prefix:
logging.critical("Refuse to delete certificate"
......@@ -205,8 +201,8 @@ class RegistryServer(object):
key = hashlib.sha1(struct.pack('Q',
random.getrandbits(64))).digest()
self.sessions.setdefault(client_prefix, [])[1:] = key,
key = utils.encrypt(cert, key)
sign = crypto.sign(self.key, key, 'sha1')
key = x509.encrypt(cert, key)
sign = self.cert.sign(key)
assert len(key) == len(sign)
return key + sign
......@@ -303,11 +299,11 @@ class RegistryServer(object):
cert.set_serial_number(0) # required for libssl < 1.0
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(self.cert_duration)
cert.set_issuer(self.ca.get_subject())
cert.set_issuer(self.cert.ca.get_subject())
subject.CN = "%u/%u" % (int(client_prefix, 2), len(client_prefix))
cert.set_subject(subject)
cert.set_pubkey(pubkey)
cert.sign(self.key, 'sha1')
cert.sign(self.cert.key, 'sha1')
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
self.db.execute("UPDATE cert SET cert = ? WHERE prefix = ?",
(cert, client_prefix))
......@@ -320,14 +316,14 @@ class RegistryServer(object):
with self.db:
pem = self.getCert(cn)
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
if utils.notAfter(cert) - RENEW_PERIOD < time.time():
if x509.notAfter(cert) - RENEW_PERIOD < time.time():
pem = self.createCertificate(cn, cert.get_subject(),
cert.get_pubkey())
return pem
@rpc
def getCa(self):
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.ca)
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert.ca)
@rpc
def getPrefix(self, cn):
......@@ -374,7 +370,7 @@ class RegistryServer(object):
return
cert = self.getCert(cn)
logging.info("Sending bootstrap peer: %s", msg)
return utils.encrypt(cert, msg)
return x509.encrypt(cert, msg)
@rpc
def versions(self):
......@@ -451,9 +447,8 @@ class RegistryClient(object):
_hmac = None
user_agent = "re6stnet/" + version.version
def __init__(self, url, key_path=None, ca=None, auto_close=True):
self.key_path = key_path
self.ca = ca
def __init__(self, url, cert=None, auto_close=True):
self.cert = cert
self.auto_close = auto_close
scheme, host = splittype(url)
host, path = splithost(host)
......@@ -483,8 +478,8 @@ class RegistryClient(object):
retry = False
h = self.hello(client_prefix)
n = len(h) // 2
crypto.verify(self.ca, h[n:], h[:n], 'sha1')
key = utils.decrypt(self.key_path, h[:n])
self.cert.verify(h[n:], h[:n])
key = self.cert.decrypt(h[:n])
h = hmac.HMAC(key, query, hashlib.sha1).digest()
key = hashlib.sha1(key).digest()
self._hmac = hashlib.sha1(key).digest()
......
......@@ -160,11 +160,12 @@ class TunnelKiller(object):
class TunnelManager(object):
def __init__(self, control_socket, peer_db, openvpn_args, timeout,
refresh, client_count, iface_list, network, prefix,
address, ip_changed, encrypt, remote_gateway, disable_proto,
neighbour_list=()):
self.ctl = ctl.Babel(control_socket, weakref.proxy(self), network)
def __init__(self, control_socket, peer_db, cert, openvpn_args, timeout,
refresh, client_count, iface_list, address, ip_changed,
encrypt, remote_gateway, disable_proto, neighbour_list=()):
self.cert = cert
self._network = cert.network
self.ctl = ctl.Babel(control_socket, weakref.proxy(self), self._network)
self.encrypt = encrypt
self.ovpn_args = openvpn_args
self.peer_db = peer_db
......@@ -178,9 +179,8 @@ class TunnelManager(object):
self._distant_peers = []
self._iface_to_prefix = {}
self._refresh_time = refresh
self._network = network
self._iface_list = iface_list
self._prefix = prefix
self._prefix = cert.prefix
address_dict = defaultdict(list)
for family, address in address:
address_dict[family] += address
......
import argparse, calendar, errno, logging, os, select as _select, shlex, signal
import argparse, errno, logging, os, select as _select, shlex, signal
import socket, struct, subprocess, sys, textwrap, threading, time, traceback
try:
subprocess.CalledProcessError(0, '', '')
......@@ -207,15 +207,6 @@ def ipFromBin(ip, suffix=''):
return socket.inet_ntop(socket.AF_INET6,
struct.pack('>QQ', int(ip[:64], 2), int(ip[64:], 2)))
def networkFromCa(ca):
return bin(ca.get_serial_number())[3:]
def subnetFromCert(cert):
return cert.get_subject().CN
def notAfter(cert):
return calendar.timegm(time.strptime(cert.get_notAfter(),'%Y%m%d%H%M%SZ'))
def dump_address(address):
return ';'.join(map(','.join, address))
......@@ -232,26 +223,3 @@ def parse_address(address_list):
def binFromSubnet(subnet):
p, l = subnet.split('/')
return bin(int(p))[2:].rjust(int(l), '0')
def decrypt(key_path, data):
p = Popen(('openssl', 'rsautl', '-decrypt', '-inkey', key_path),
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
out, err = p.communicate(data)
if p.returncode:
raise subprocess.CalledProcessError(p.returncode, 'openssl', err)
return out
def encrypt(cert, data):
r, w = os.pipe()
try:
threading.Thread(target=os.write, args=(w, cert)).start()
p = Popen(('openssl', 'rsautl', '-encrypt', '-certin',
'-inkey', '/proc/self/fd/%u' % r),
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
out, err = p.communicate(data)
finally:
os.close(r)
os.close(w)
if p.returncode:
raise subprocess.CalledProcessError(p.returncode, 'openssl', err)
return out
import calendar, logging, os, subprocess, threading, time
from OpenSSL import crypto
from . import utils
def networkFromCa(ca):
return bin(ca.get_serial_number())[3:]
def subnetFromCert(cert):
return cert.get_subject().CN
def notAfter(cert):
return calendar.timegm(time.strptime(cert.get_notAfter(),'%Y%m%d%H%M%SZ'))
def openssl(*args):
return utils.Popen(('openssl',) + args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def encrypt(cert, data):
r, w = os.pipe()
try:
threading.Thread(target=os.write, args=(w, cert)).start()
p = openssl('rsautl', '-encrypt', '-certin',
'-inkey', '/proc/self/fd/%u' % r)
out, err = p.communicate(data)
finally:
os.close(r)
os.close(w)
if p.returncode:
raise subprocess.CalledProcessError(p.returncode, 'openssl', err)
return out
def maybe_renew(path, cert, info, renew):
from .registry import RENEW_PERIOD
while True:
next_renew = notAfter(cert) - RENEW_PERIOD
if time.time() < next_renew:
return cert, next_renew
try:
pem = renew()
if not pem or pem == crypto.dump_certificate(
crypto.FILETYPE_PEM, cert):
exc_info = 0
break
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
except Exception:
exc_info = 1
break
new_path = path + '.new'
with open(new_path, 'w') as f:
f.write(pem)
try:
s = os.stat(path)
os.chown(new_path, s.st_uid, s.st_gid)
except OSError:
pass
os.rename(new_path, path)
logging.info("%s renewed until %s UTC",
info, time.asctime(time.gmtime(notAfter(cert))))
logging.error("%s not renewed. Will retry tomorrow.",
info, exc_info=exc_info)
return cert, time.time() + 86400
class VerifyError(Exception):
pass
class Cert(object):
def __init__(self, ca, key, cert=None):
self.ca_path = ca
self.cert_path = cert
self.key_path = key
with open(ca) as f:
self.ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
with open(key) as f:
self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
if cert:
with open(cert) as f:
cert = f.read()
self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
@property
def prefix(self):
return utils.binFromSubnet(subnetFromCert(self.cert))
@property
def network(self):
return networkFromCa(self.ca)
@property
def openvpn_args(self):
return ('--ca', self.ca_path,
'--cert', self.cert_path,
'--key', self.key_path)
def maybeRenew(self, registry):
from .registry import RegistryClient
registry = RegistryClient(registry, self)
self.cert, next_renew = maybe_renew(self.cert_path, self.cert,
"Certificate", lambda: registry.renewCertificate(self.prefix))
self.ca, ca_renew = maybe_renew(self.ca_path, self.ca,
"CA Certificate", registry.getCa)
return min(next_renew, ca_renew)
def verify(self, sign, data):
crypto.verify(self.ca, sign, data, 'sha1')
def sign(self, data):
return crypto.sign(self.key, data, 'sha1')
def decrypt(self, data):
p = openssl('rsautl', '-decrypt', '-inkey', self.key_path)
out, err = p.communicate(data)
if p.returncode:
raise subprocess.CalledProcessError(p.returncode, 'openssl', err)
return out
......@@ -2,9 +2,7 @@
import atexit, errno, logging, os, signal, socket
import sqlite3, subprocess, sys, time, threading
from collections import deque
from OpenSSL import crypto
from re6st import ctl, db, plib, tunnel, utils, version
from re6st.registry import RegistryClient, RENEW_PERIOD
from re6st import ctl, db, plib, tunnel, utils, version, x509
from re6st.utils import exit
class ReexecException(Exception):
......@@ -22,7 +20,7 @@ def getConfig():
"- any: ask peers our IP\n"
" (default: like 'upnp' if miniupnpc is installed,\n"
" otherwise like 'any')")
_('--registry', metavar='URL',
_('--registry', metavar='URL', required=True,
help="Public HTTP URL of the registry, for bootstrapping.")
_('-l', '--log', default='/var/log/re6stnet',
help="Path to the directory used for log files:\n"
......@@ -126,48 +124,11 @@ def getConfig():
return parser.parse_args()
def maybe_renew(path, cert, info, renew):
while True:
next_renew = utils.notAfter(cert) - RENEW_PERIOD
if time.time() < next_renew:
return cert, next_renew
try:
pem = renew()
if not pem or pem == crypto.dump_certificate(
crypto.FILETYPE_PEM, cert):
exc_info = 0
break
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
except Exception:
exc_info = 1
break
new_path = path + '.new'
with open(new_path, 'w') as f:
f.write(pem)
try:
s = os.stat(path)
os.chown(new_path, s.st_uid, s.st_gid)
except OSError:
pass
os.rename(new_path, path)
logging.info("%s renewed until %s UTC",
info, time.asctime(time.gmtime(utils.notAfter(cert))))
logging.error("%s not renewed. Will retry tomorrow.",
info, exc_info=exc_info)
return cert, time.time() + 86400
def main():
# Get arguments
config = getConfig()
with open(config.ca) as f:
ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
with open(config.cert) as f:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
prefix = utils.binFromSubnet(utils.subnetFromCert(cert))
config.openvpn_args += (
'--ca', config.ca,
'--cert', config.cert,
'--key', config.key)
cert = x509.Cert(config.ca, config.key, config.cert)
config.openvpn_args += cert.openvpn_args
# TODO: verify certificates (should we moved to M2Crypto ?)
if config.test:
......@@ -186,13 +147,8 @@ def main():
exit.signal(0, signal.SIGINT, signal.SIGTERM)
exit.signal(-1, signal.SIGHUP, signal.SIGUSR2)
registry = RegistryClient(config.registry, config.key, ca)
cert, next_renew = maybe_renew(config.cert, cert, "Certificate",
lambda: registry.renewCertificate(prefix))
ca, ca_renew = maybe_renew(config.ca, ca, "CA Certificate", registry.getCa)
if next_renew > ca_renew:
next_renew = ca_renew
network = utils.networkFromCa(ca)
next_renew = cert.maybeRenew(config.registry)
network = cert.network
if config.max_clients is None:
config.max_clients = config.client_count * 2
......@@ -288,7 +244,7 @@ def main():
cleanup.append(lambda: subprocess.call(args))
try:
subnet = network + prefix
subnet = network + cert.prefix
my_ip = utils.ipFromBin(subnet, '1')
my_subnet = '%s/%u' % (utils.ipFromBin(subnet), len(subnet))
my_network = "%s/%u" % (utils.ipFromBin(network), len(network))
......@@ -303,12 +259,11 @@ def main():
timeout = 4 * config.hello
cleanup = []
if config.client_count and not config.client:
required('registry')
peer_db = db.PeerDB(db_path, registry, config.key, network, prefix)
peer_db = db.PeerDB(db_path, config.registry, cert)
cleanup.append(lambda: peer_db.cacheMinimize(config.client_count))
tunnel_manager = tunnel.TunnelManager(config.control_socket,
peer_db, config.openvpn_args, timeout, config.tunnel_refresh,
config.client_count, config.iface_list, network, prefix,
peer_db, cert, config.openvpn_args, timeout,
config.tunnel_refresh, config.client_count, config.iface_list,
address, ip_changed, config.encrypt, remote_gateway,
config.disable_proto, config.neighbour)
cleanup.append(tunnel_manager.sock.close)
......
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