summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAFormatCreator.py
blob: 3dadf0345d0b96b5d9d114efe53ee9740100a5d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
""" Cfg creator that creates SSL certs """

import lxml.etree
from Bcfg2.Utils import Executor
from Bcfg2.Server.FileMonitor import get_fam
from Bcfg2.Server.Plugin import PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import CfgCreationError, XMLCfgCreator, \
    CfgCreator


class CfgSSLCAFormatCreator(XMLCfgCreator):
    """ This class acts as both a Cfg creator that creates formatted
    SSL certs."""

    #: Different configurations for different clients/groups can be
    #: handled with Client and Group tags within pubkey.xml
    __specific__ = False

    #: Handle XML specifications of private keys
    __basenames__ = ['sslformat.xml']

    def __init__(self, fname):
        XMLCfgCreator.__init__(self, fname)
        self.cmd = Executor()

    def create_data(self, entry, metadata):
        """ generate a new formatted cert """
        self.logger.info("Cfg: Generating formatted SSL cert for %s" %
                         self.name)
        elem = self.XMLMatch(metadata).find("Format")
        certfile = None
        keyfile = None

        data = ''
        for part in elem:
            if part.tag == 'Key':
                if keyfile is None:
                    keyfile = self._get_keyfile(elem, metadata)
            
                cmd = ["openssl", "rsa", "-in", keyfile]
                if part.get('format') == 'der':
                    cmd.extend(['-outform', 'DER'])
                result = self.cmd.run(cmd)
                data += result.stdout
            elif part.tag == 'Cert':
                if certfile is None:
                    certfile = self._get_certfile(elem, metadata)
            
                cmd = ["openssl", "x509", "-in", certfile]
                if part.get('format') == 'der':
                    cmd.exend(['-outform', 'DER'])
                result = self.cmd.run(cmd)
                data += result.stdout
            else:
                raise CfgCreationError("Cfg: Unknown SSL Cert format "
                                       "%s for %s" % (part.tag, self.name))
        self.write_data(data, **self.get_specificity(metadata))
        return data

    def _get_keyfile(self, elem, metadata):
        """ Given a <Format/> element and client metadata, return the
        full path to the file on the filesystem that the key lives in."""
        keypath = elem.get("key", None)
        if keypath is not None:
            eset = self.cfg.entries[keypath]
            try:
                return eset.best_matching(metadata).name
            except PluginExecutionError:
                raise CfgCreationError("Cfg: No SSL Key found at %s" %
                                       keypath)
        else:
            # Get ssl key from cert creator
            certpath = elem.get("cert")
            eset = self.cfg.entries[certpath]
            try:
                creator = eset.best_matching(metadata,
                                             eset.get_handlers(metadata,
                                                               CfgCreator))
            except PluginExecutionError:
                raise CfgCreationError("Cfg: No SSL cert creator defined "
                                       "for %s" % certpath)

            cert = creator.XMLMatch(metadata).find("Cert")
            return creator._get_keyfile(cert, metadata)

    def _get_certfile(self, elem, metadata):
        """ Given a <Format/> element and client metadata, return the
        full path to the file on the filesystem that the cert lives in."""
        certpath = elem.get("cert")
        eset = self.cfg.entries[certpath]
        try:
            return eset.best_matching(metadata).name
        except PluginExecutionError:
            # SSL cert needs to be created
            try:
                creator = eset.best_matching(metadata,
                                             eset.get_handlers(metadata,
                                                               CfgCreator))
            except PluginExecutionError:
                raise CfgCreationError("Cfg: No SSL Cert or cert creator "
                                       "defined for %s" % certpath)

            certentry = lxml.etree.Element("Path", name=certpath)
            creator.create_data(certentry, metadata)

            tries = 0
            while True:
                if tries >= 10:
                    raise CfgCreationError("Cfg: Timed out waiting for event "
                                           "on SSL cert at %s" % certpath)
                get_fam().handle_events_in_interval(1)
                try:
                    return eset.best_matching(metadata).name
                except PluginExecutionError:
                    tries += 1
                    continue