diff --git a/Makefile b/Makefile index dd2c2695..03369342 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,6 @@ develop: update-submodules setup-git npm install pip install "setuptools>=0.9.8" # order matters here, base package must install first - # this is temporary until the version we need is released - pip install -e 'git+https://git@github.com/pyca/cryptography.git#egg=cryptography-1.0.dev1' pip install -e . pip install "file://`pwd`#egg=lemur[dev]" pip install "file://`pwd`#egg=lemur[tests]" diff --git a/lemur/certificates/verify.py b/lemur/certificates/verify.py index 1e0febec..79afdf50 100644 --- a/lemur/certificates/verify.py +++ b/lemur/certificates/verify.py @@ -6,14 +6,28 @@ .. moduleauthor:: Kevin Glisson """ import os -import re -import hashlib import requests import subprocess from OpenSSL import crypto +from cryptography import x509 +from cryptography.hazmat.backends import default_backend from flask import current_app +from contextlib import contextmanager +from tempfile import NamedTemporaryFile + + +@contextmanager +def mktempfile(): + with NamedTemporaryFile(delete=False) as f: + name = f.name + + try: + yield name + finally: + os.unlink(name) + def ocsp_verify(cert_path, issuer_chain_path): """ @@ -53,27 +67,18 @@ def crl_verify(cert_path): :return: True if certificate is valid, False otherwise :raise Exception: If certificate does not have CRL """ - s = "(http(s)?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}/\S*?$)" - regex = re.compile(s, re.MULTILINE) + with open(cert_path, 'rt') as c: + cert = x509.load_pem_x509_certificate(c.read(), default_backend()) - x509 = crypto.load_certificate(crypto.FILETYPE_PEM, open(cert_path, 'rt').read()) - for x in range(x509.get_extension_count()): - ext = x509.get_extension(x) - if ext.get_short_name() == 'crlDistributionPoints': - r = regex.search(ext.get_data()) - points = r.groups() - break - else: - raise Exception("Certificate does not have a CRL distribution point") - - for point in points: - if point: - response = requests.get(point) - crl = crypto.load_crl(crypto.FILETYPE_ASN1, response.content) - revoked = crl.get_revoked() - for r in revoked: - if x509.get_serial_number() == r.get_serial(): - return + distribution_points = cert.extensions.get_extension_for_oid(x509.OID_CRL_DISTRIBUTION_POINTS).value + for p in distribution_points: + point = p.full_name[0].value + response = requests.get(point) + crl = crypto.load_crl(crypto.FILETYPE_ASN1, response.content) # TODO this should be switched to cryptography when support exists + revoked = crl.get_revoked() + for r in revoked: + if cert.serial == r.get_serial(): + return return True @@ -99,22 +104,6 @@ def verify(cert_path, issuer_chain_path): raise Exception("Failed to verify") -def make_tmp_file(string): - """ - Creates a temporary file for a given string - - :param string: - :return: Full file path to created file - """ - m = hashlib.md5() - m.update(string) - hexdigest = m.hexdigest() - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), hexdigest) - with open(path, 'w') as f: - f.write(string) - return path - - def verify_string(cert_string, issuer_string): """ Verify a certificate given only it's string value @@ -123,13 +112,11 @@ def verify_string(cert_string, issuer_string): :param issuer_string: :return: True if valid, False otherwise """ - cert_path = make_tmp_file(cert_string) - issuer_path = make_tmp_file(issuer_string) - status = verify(cert_path, issuer_path) - remove_tmp_file(cert_path) - remove_tmp_file(issuer_path) + with mktempfile() as cert_tmp: + with open(cert_tmp, 'w') as f: + f.write(cert_string) + with mktempfile() as issuer_tmp: + with open(issuer_tmp, 'w') as f: + f.write(issuer_string) + status = verify(cert_tmp, issuer_tmp) return status - - -def remove_tmp_file(file_path): - os.remove(file_path) diff --git a/lemur/manage.py b/lemur/manage.py index 4b7ff6fb..42137576 100755 --- a/lemur/manage.py +++ b/lemur/manage.py @@ -4,6 +4,8 @@ import os import sys import base64 import time +import requests +import json from gunicorn.config import make_settings from cryptography.fernet import Fernet @@ -146,12 +148,15 @@ def check_revoked(): as `unknown`. """ for cert in cert_service.get_all_certs(): - if cert.chain: - status = verify_string(cert.body, cert.chain) - else: - status = verify_string(cert.body, "") + try: + if cert.chain: + status = verify_string(cert.body, cert.chain) + else: + status = verify_string(cert.body, "") - cert.status = 'valid' if status else "invalid" + cert.status = 'valid' if status else 'invalid' + except Exception as e: + cert.status = 'unknown' database.update(cert) @@ -181,7 +186,7 @@ def generate_settings(): return output -@manager.option('-s', '--sources', dest='labels', default='', required=False) +@manager.option('-s', '--sources', dest='labels') def sync_sources(labels): """ Attempts to run several methods Certificate discovery. This is @@ -207,13 +212,14 @@ def sync_sources(labels): try: sync_lock.acquire(timeout=10) # wait up to 10 seconds - if labels: - sys.stdout.write("[+] Staring to sync sources: {labels}!\n".format(labels=labels)) - labels = labels.split(",") - else: - sys.stdout.write("[+] Starting to sync ALL sources!\n") + sys.stdout.write("[+] Staring to sync sources: {labels}!\n".format(labels=labels)) + labels = labels.split(",") + + if labels[0] == 'all': + sync() + else: + sync(labels=labels) - sync(labels=labels) sys.stdout.write( "[+] Finished syncing sources. Run Time: {time}\n".format( time=(time.time() - start_time) @@ -680,6 +686,38 @@ class ProvisionELB(Command): done = True +@manager.command +def publish_verisign_units(): + """ + Simple function that queries verisign for API units and posts the mertics to + Atlas API for other teams to consume. + :return: + """ + from lemur.plugins import plugins + v = plugins.get('verisign-issuer') + units = v.get_available_units() + + metrics = {} + for item in units: + if item['@type'] in metrics.keys(): + metrics[item['@type']] += int(item['@remaining']) + else: + metrics.update({item['@type']: int(item['@remaining'])}) + + for name, value in metrics.items(): + metric = [ + { + "timestamp": 1321351651, + "type": "GAUGE", + "name": "Symantec {0} Unit Count".format(name), + "tags": {}, + "value": value + } + ] + + requests.post('http://localhost:8078/metrics', data=json.dumps(metric)) + + def main(): manager.add_command("start", LemurServer()) manager.add_command("runserver", Server(host='127.0.0.1')) diff --git a/setup.py b/setup.py index ea7ef326..0e15ea76 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ install_requires = [ 'six==1.9.0', 'gunicorn==19.3.0', 'pycrypto==2.6.1', - 'cryptography>=1.0dev', + 'cryptography==1.0', 'pyopenssl==0.15.1', 'pyjwt==1.0.1', 'xmltodict==0.9.2',