diff options
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins/SSLCA.py')
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/SSLCA.py | 104 |
1 files changed, 60 insertions, 44 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py index f111ffc60..74d8833f4 100644 --- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py +++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py @@ -3,20 +3,17 @@ certificates and their keys. """ import os import sys -import logging import tempfile import lxml.etree -from subprocess import Popen, PIPE, STDOUT -import Bcfg2.Options import Bcfg2.Server.Plugin +from Bcfg2.Utils import Executor from Bcfg2.Compat import ConfigParser from Bcfg2.Server.Plugin import PluginExecutionError -LOGGER = logging.getLogger(__name__) - class SSLCAXMLSpec(Bcfg2.Server.Plugin.StructFile): """ Base class to handle key.xml and cert.xml """ + encryption = False attrs = dict() tag = None @@ -30,10 +27,9 @@ class SSLCAXMLSpec(Bcfg2.Server.Plugin.StructFile): metadata.hostname, self.name)) elif len(entries) > 1: - LOGGER.warning("More than one matching %s entry found for %s in " - "%s; using first match" % (self.tag, - metadata.hostname, - self.name)) + self.logger.warning( + "More than one matching %s entry found for %s in %s; " + "using first match" % (self.tag, metadata.hostname, self.name)) rv = dict() for attr, default in self.attrs.items(): val = entries[0].get(attr.lower(), default) @@ -83,12 +79,13 @@ class SSLCADataFile(Bcfg2.Server.Plugin.SpecificData): class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): """ Entry set to handle SSLCA entries and XML files """ - def __init__(self, _, path, entry_type, encoding, parent=None): + def __init__(self, _, path, entry_type, parent=None): Bcfg2.Server.Plugin.EntrySet.__init__(self, os.path.basename(path), - path, entry_type, encoding) + path, entry_type) self.parent = parent self.key = None self.cert = None + self.cmd = Executor(timeout=120) def handle_event(self, event): action = event.code2str() @@ -122,14 +119,14 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): elif ktype == 'dsa': cmd = ["openssl", "dsaparam", "-noout", "-genkey", bits] self.debug_log("SSLCA: Generating new key: %s" % " ".join(cmd)) - proc = Popen(cmd, stdout=PIPE, stderr=PIPE) - key, err = proc.communicate() - if proc.wait(): + result = self.cmd.run(cmd) + if not result.success: raise PluginExecutionError("SSLCA: Failed to generate key %s for " "%s: %s" % (entry.get("name"), - metadata.hostname, err)) - open(os.path.join(self.path, filename), 'w').write(key) - return key + metadata.hostname, + result.error)) + open(os.path.join(self.path, filename), 'w').write(result.stdout) + return result.stdout def build_cert(self, entry, metadata, keyfile): """ generate a new cert """ @@ -162,13 +159,10 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): self.debug_log("SSLCA: Generating new certificate: %s" % " ".join(_scrub_pass(a) for a in cmd)) - proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) - (cert, err) = proc.communicate() - if proc.wait(): - # pylint: disable=E1103 + result = self.cmd.run(cmd) + if not result.success: raise PluginExecutionError("SSLCA: Failed to generate cert: %s" - % err.splitlines()[-1]) - # pylint: enable=E1103 + % result.error) finally: try: if req_config and os.path.exists(req_config): @@ -178,6 +172,7 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): except OSError: self.logger.error("SSLCA: Failed to unlink temporary files: %s" % sys.exc_info()[1]) + cert = result.stdout if cert_spec['append_chain'] and 'chaincert' in ca: cert += open(ca['chaincert']).read() @@ -241,11 +236,10 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): cmd = ["openssl", "req", "-new", "-config", req_config, "-days", days, "-key", keyfile, "-text", "-out", req] self.debug_log("SSLCA: Generating new CSR: %s" % " ".join(cmd)) - proc = Popen(cmd, stdout=PIPE, stderr=PIPE) - err = proc.communicate()[1] - if proc.wait(): + result = self.cmd.run(cmd) + if not result.success: raise PluginExecutionError("SSLCA: Failed to generate CSR: %s" % - err) + result.error) return req def verify_cert(self, filename, keyfile, entry, metadata): @@ -276,34 +270,34 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet): cmd.extend([chaincert, cert]) self.debug_log("SSLCA: Verifying %s against CA: %s" % (entry.get("name"), " ".join(cmd))) - res = Popen(cmd, stdout=PIPE, stderr=STDOUT).stdout.read() - if res == cert + ": OK\n": + result = self.cmd.run(cmd) + if result.stdout == cert + ": OK\n": self.debug_log("SSLCA: %s verified successfully against CA" % entry.get("name")) return True self.logger.warning("SSLCA: %s failed verification against CA: %s" % - (entry.get("name"), res)) + (entry.get("name"), result.error)) return False + def _get_modulus(self, fname, ftype="x509"): + """ get the modulus from the given file """ + cmd = ["openssl", ftype, "-noout", "-modulus", "-in", fname] + self.debug_log("SSLCA: Getting modulus of %s for verification: %s" % + (fname, " ".join(cmd))) + result = self.cmd.run(cmd) + if not result.success: + self.logger.warning("SSLCA: Failed to get modulus of %s: %s" % + (fname, result.error)) + return result.stdout.strip() + def verify_cert_against_key(self, filename, keyfile): """ check that a certificate validates against its private key. """ - def _modulus(fname, ftype="x509"): - """ get the modulus from the given file """ - cmd = ["openssl", ftype, "-noout", "-modulus", "-in", fname] - self.debug_log("SSLCA: Getting modulus of %s for verification: %s" - % (fname, " ".join(cmd))) - proc = Popen(cmd, stdout=PIPE, stderr=PIPE) - rv, err = proc.communicate() - if proc.wait(): - self.logger.warning("SSLCA: Failed to get modulus of %s: %s" % - (fname, err)) - return rv.strip() # pylint: disable=E1103 certfile = os.path.join(self.path, filename) - cert = _modulus(certfile) - key = _modulus(keyfile, ftype="rsa") + cert = self._get_modulus(certfile) + key = self._get_modulus(keyfile, ftype="rsa") if cert == key: self.debug_log("SSLCA: %s verified successfully against key %s" % (filename, keyfile)) @@ -362,10 +356,32 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool): """ The SSLCA generator handles the creation and management of ssl certificates and their keys. """ __author__ = 'g.hagger@gmail.com' + + options = Bcfg2.Server.Plugin.GroupSpool.options + [ + Bcfg2.Options.WildcardSectionGroup( + Bcfg2.Options.PathOption( + cf=("sslca_*", "config"), + help="Path to the openssl config for the CA"), + Bcfg2.Options.Option( + cf=("sslca_*", "passphrase"), + help="Passphrase for the CA private key"), + Bcfg2.Options.PathOption( + cf=("sslca_*", "chaincert"), + help="Path to the SSL chaining certificate for verification"), + Bcfg2.Options.BooleanOption( + cf=("sslca_*", "root_ca"), + help="Whether or not <chaincert> is a root CA (as opposed to " + "an intermediate cert"))] + # python 2.5 doesn't support mixing *magic and keyword arguments es_cls = lambda self, *args: SSLCAEntrySet(*args, **dict(parent=self)) es_child_cls = SSLCADataFile def get_ca(self, name): """ get a dict describing a CA from the config file """ - return dict(self.core.setup.cfp.items("sslca_%s" % name)) + rv = dict() + prefix = "sslca_%s_" % name + for attr in dir(Bcfg2.Options.setup): + if attr.startswith(prefix): + rv[attr[len(prefix):]] = getattr(Bcfg2.Options.setup, attr) + return rv |