Commit 3266582f authored by Julien Muchembled's avatar Julien Muchembled

Use pyOpenSSL instead of spawning 'openssl' subprocesses

parent b55cc1c9
......@@ -32,6 +32,7 @@ setup(
keywords="slapos networkcache shadir shacache",
install_requires=[
'setuptools', # for namespace
'pyOpenSSL',
] + additional_install_requires,
classifiers=[
'Development Status :: 4 - Beta',
......
......@@ -17,11 +17,10 @@ import httplib
import json
import os
import socket
import subprocess
import tempfile
import traceback
import urllib2
import urlparse
from OpenSSL import crypto
# XXX: code between select/select_generic must be factored
......@@ -74,8 +73,8 @@ class NetworkcacheClient(object):
- SHADIR
- SHACACHE
'''
signature_private_key = None
openssl = 'openssl'
def parseUrl(self, url):
return_dict = {}
parsed_url = urlparse.urlparse(url)
......@@ -142,17 +141,20 @@ class NetworkcacheClient(object):
for k, v in self.parseUrl(shadir).iteritems():
setattr(self, 'shadir_%s' % k, v)
self.signature_private_key_file = signature_private_key_file
if signature_private_key_file:
with open(signature_private_key_file) as f:
self.signature_private_key = crypto.load_privatekey(crypto.FILETYPE_PEM,
f.read())
if type(signature_certificate_list) is str:
# If signature_certificate_list is a string, parse it to a list of
# certificates
cert_marker = "-----BEGIN CERTIFICATE-----"
parsed_signature_certificate_list = [cert_marker + '\n' + q.strip() \
signature_certificate_list = [cert_marker + '\n' + q.strip() \
for q in signature_certificate_list.split(cert_marker) \
if q.strip()]
self.signature_certificate_list = parsed_signature_certificate_list
else:
self.signature_certificate_list = signature_certificate_list
self.signature_certificate_list = [
crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
for certificate in signature_certificate_list or ()]
self.shacache_key_file = shacache_key_file
self.shacache_cert_file = shacache_cert_file
......@@ -205,11 +207,8 @@ class NetworkcacheClient(object):
def index(self, key, **kw):
data = json.dumps(kw)
try:
data = [data, self._getSignatureString(data)]
except Exception:
raise UploadError('Impossible to sign content, error:\n%s'
% traceback.format_exc())
data = [data, self._getSignatureString(data)]
if self.shadir_scheme == 'https':
shadir_connection = httplib.HTTPSConnection(self.shadir_host,
self.shadir_port, key_file=self.shadir_key_file,
......@@ -253,19 +252,14 @@ class NetworkcacheClient(object):
except Exception:
raise DirectoryNotFound('It was impossible to parse json response:\n%s'%
traceback.format_exc())
filtered_data_list = []
if self.signature_certificate_list is not None:
for data in data_list:
if len(data[1]):
if self._verifySignatureInCertificateList(data[0], data[1]):
filtered_data_list.append(data)
else:
filtered_data_list = data_list
if self.signature_certificate_list:
data_list = [data for data in data_list
if self._verifySignatureInCertificateList(*data)]
if len(filtered_data_list) == 0:
if not data_list:
raise DirectoryNotFound('Could not find a trustable entry.')
information_json, signature = filtered_data_list[0]
information_json, signature = data_list[0]
try:
information_dict = json.loads(information_json)
except Exception:
......@@ -291,52 +285,29 @@ class NetworkcacheClient(object):
except Exception:
raise DirectoryNotFound('It was impossible to parse json response:\n%s' %
traceback.format_exc())
filtered_data_list = []
for data in data_list:
if len(data[1]):
if self._verifySignatureInCertificateList(data[0], data[1]):
filtered_data_list.append(data)
return filtered_data_list
def _openssl(self, input, *args):
p = subprocess.Popen((self.openssl,) + args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = p.communicate(input)[0]
if p.returncode:
raise subprocess.CalledProcessError(p.returncode, self.openssl, output)
return output
return [data for data in data_list
if self._verifySignatureInCertificateList(*data)]
def _getSignatureString(self, content):
"""
Return the signature based on certification file.
"""
if self.signature_private_key_file is None:
return ''
return self._openssl(content, "dgst", "-sha1", "-sign",
self.signature_private_key_file).encode('base64')
k = self.signature_private_key
return '' if k is None else crypto.sign(k, content, 'sha1').encode('base64')
def _verifySignatureInCertificateList(self, content, signature_string):
"""
Returns true if it can find any valid certificate or false if it does not
find any.
"""
if self.signature_certificate_list:
with tempfile.NamedTemporaryFile() as signature_file:
signature_file.write(signature_string.decode('base64'))
signature_file.flush()
for certificate in self.signature_certificate_list:
try:
pubkey = self._openssl(certificate, "x509", "-pubkey", "-noout")
with tempfile.NamedTemporaryFile() as pubkey_file:
pubkey_file.write(pubkey)
pubkey_file.flush()
if self._openssl(content, "dgst", "-sha1", "-verify",
pubkey_file.name, "-signature", signature_file.name
).startswith('Verified OK'):
return True
except Exception:
# in case of failure, emit *anything*, but swallow all what possible
traceback.print_exc()
if signature_string:
signature = signature_string.decode('base64')
for certificate in self.signature_certificate_list:
try:
crypto.verify(certificate, signature, content, 'sha1')
return True
except crypto.Error:
pass
return False
......
......@@ -250,19 +250,19 @@ MYZmKV7A3nFehN9A+REz+WU3I7fE6vQRh9jKeuxnQLRv0TdP9CEdPcYcs/EQpIDb
-----END CERTIFICATE-----
"""
alternate_certificate = """ -----BEGIN CERTIFICATE-----
MIIB4DCCAUkCADANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJGUjEZMBcGA1UE
CBMQRGVmYXVsdCBQcm92aW5jZTEPMA0GA1UEChMGTmV4ZWRpMB4XDTExMDkxNTA5
MDAwMloXDTEyMDkxNTA5MDAwMlowOTELMAkGA1UEBhMCRlIxGTAXBgNVBAgTEERl
ZmF1bHQgUHJvdmluY2UxDzANBgNVBAoTBk5leGVkaTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEApYZv6OstoqNzxG1KI6iE5U4Ts2Xx9lgLeUGAMyfJLyMmRLhw
boKOyJ9Xke4dncoBAyNPokUR6iWOcnPHtMvNOsBFZ2f7VA28em3+E1JRYdeNUEtX
Z0s3HjcouaNAnPfjFTXHYj4um1wOw2cURSPuU5dpzKBbV+/QCb5DLheynisCAwEA
ATANBgkqhkiG9w0BAQsFAAOBgQBCZLbTVdrw3RZlVVMFezSHrhBYKAukTwZrNmJX
mHqi2tN8tNo6FX+wmxUUAf3e8R2Ymbdbn2bfbPpcKQ2fG7PuKGvhwMG3BlF9paEC
q7jdfWO18Zp/BG7tagz0jmmC4y/8akzHsVlruo2+2du2freE8dK746uoMlXlP93g
QUUGLQ==
-----END CERTIFICATE-----
alternate_certificate = """-----BEGIN CERTIFICATE-----
MIIB4DCCAUkCADANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJGUjEZMBcGA1UE
CBMQRGVmYXVsdCBQcm92aW5jZTEPMA0GA1UEChMGTmV4ZWRpMB4XDTExMDkxNTA5
MDAwMloXDTEyMDkxNTA5MDAwMlowOTELMAkGA1UEBhMCRlIxGTAXBgNVBAgTEERl
ZmF1bHQgUHJvdmluY2UxDzANBgNVBAoTBk5leGVkaTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEApYZv6OstoqNzxG1KI6iE5U4Ts2Xx9lgLeUGAMyfJLyMmRLhw
boKOyJ9Xke4dncoBAyNPokUR6iWOcnPHtMvNOsBFZ2f7VA28em3+E1JRYdeNUEtX
Z0s3HjcouaNAnPfjFTXHYj4um1wOw2cURSPuU5dpzKBbV+/QCb5DLheynisCAwEA
ATANBgkqhkiG9w0BAQsFAAOBgQBCZLbTVdrw3RZlVVMFezSHrhBYKAukTwZrNmJX
mHqi2tN8tNo6FX+wmxUUAf3e8R2Ymbdbn2bfbPpcKQ2fG7PuKGvhwMG3BlF9paEC
q7jdfWO18Zp/BG7tagz0jmmC4y/8akzHsVlruo2+2du2freE8dK746uoMlXlP93g
QUUGLQ==
-----END CERTIFICATE-----
"""
ca_cert = """-----BEGIN CERTIFICATE-----
......@@ -452,68 +452,6 @@ class OnlineTest(OnlineMixin, unittest.TestCase):
result = signed_nc.select(key)
self.assertEqual(result.read(), self.test_string)
def test_upload_signed_content_openssl_not_available(self):
key = 'somekey' + str(random.random())
urlmd5 = str(random.random())
file_name = 'my file'
key_file = tempfile.NamedTemporaryFile()
key_file.write(self.key)
key_file.flush()
signed_nc = slapos.libnetworkcache.NetworkcacheClient(
self.shacache, self.shadir, key_file.name, [self.certificate])
signed_nc.openssl = '/doesnotexists'
try:
signed_nc.upload(self.test_data, key, urlmd5=urlmd5, file_name=file_name)
self.fail("UploadError not raised")
except slapos.libnetworkcache.UploadError, e:
self.assertTrue(str(e).startswith("Impossible to sign content, error"))
def test_upload_signed_content_openssl_non_functional(self):
key = 'somekey' + str(random.random())
urlmd5 = str(random.random())
file_name = 'my file'
key_file = tempfile.NamedTemporaryFile()
key_file.write(self.key)
key_file.flush()
signed_nc = slapos.libnetworkcache.NetworkcacheClient(
self.shacache, self.shadir, key_file.name, [self.certificate])
signed_nc.openssl = sys.executable
try:
signed_nc.upload(self.test_data, key, urlmd5=urlmd5, file_name=file_name)
self.fail("UploadError not raised")
except slapos.libnetworkcache.UploadError, e:
self.assertTrue(str(e).startswith("Impossible to sign content, error"))
def test_select_signed_content_openssl_not_available(self):
key = 'somekey' + str(random.random())
urlmd5 = str(random.random())
file_name = 'my file'
key_file = tempfile.NamedTemporaryFile()
key_file.write(self.key)
key_file.flush()
signed_nc = slapos.libnetworkcache.NetworkcacheClient(
self.shacache, self.shadir, key_file.name, [self.certificate])
self.assertEqual(self.test_shasum, signed_nc.upload(self.test_data,
key, urlmd5=urlmd5, file_name=file_name))
signed_nc.openssl = '/doesnotexists'
self.assertDirectoryNotFound('Could not find a trustable entry.',
signed_nc.select, key)
def test_select_signed_content_openssl_non_functional(self):
key = 'somekey' + str(random.random())
urlmd5 = str(random.random())
file_name = 'my file'
key_file = tempfile.NamedTemporaryFile()
key_file.write(self.key)
key_file.flush()
signed_nc = slapos.libnetworkcache.NetworkcacheClient(
self.shacache, self.shadir, key_file.name, [self.certificate])
self.assertEqual(self.test_shasum, signed_nc.upload(self.test_data,
key, urlmd5=urlmd5, file_name=file_name))
signed_nc.openssl = sys.executable
self.assertDirectoryNotFound('Could not find a trustable entry.',
signed_nc.select, key)
def test_select_no_entries(self):
key = 'somekey' + str(random.random())
urlmd5 = str(random.random())
......
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