summaryrefslogtreecommitdiffstats
path: root/src/lib/Server/Plugins/SSLCA.py
blob: f18c18944f0602fbc9bcd86669a93c23784c9a00 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
import Bcfg2.Server.Plugin
from subprocess import Popen, PIPE
import lxml.etree
import posixpath
import logging
import pdb

class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
            #Bcfg2.Server.Plugin.Plugin,
            #Bcfg2.Server.Plugin.Generator,
            #Bcfg2.Server.Plugin.DirectoryBacked):

    """
    The SSLCA generator handles the creation and
    management of ssl certificates and their keys.
    """
    name = 'SSLCA'
    __version__ = '$Id:$'
    __author__ = 'g.hagger@gmail.com'
    __child__ = Bcfg2.Server.Plugin.FileBacked

    def __init__(self, core, datastore):
        Bcfg2.Server.Plugin.GroupSpool.__init__(self, core, datastore)
#        Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
#        Bcfg2.Server.Plugin.Generator.__init__(self)
#        try:
#            Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data,
#                                                         self.core.fam)
#        except OSError, ioerr:
#            self.logger.error("Failed to load SSHbase repository from %s" \
#                              % (self.data))
#            self.logger.error(ioerr)
#            raise Bcfg2.Server.Plugin.PluginInitError
#
    def HandleEvent(self, event=None):
        action = event.code2str()
        if event.filename[0] == '/':
            return
        epath = "".join([self.data, self.handles[event.requestID],
                         event.filename])
        if posixpath.isdir(epath):
            ident = self.handles[event.requestID] + event.filename
        else:
            ident = self.handles[event.requestID][:-1]
        
        self.logger.error('ACTION: %s, IDENT %s, FILENAME %s' % (action, ident, event.filename))

        if action in ['exists', 'created']:
            if posixpath.isdir(epath):
                self.AddDirectoryMonitor(epath[len(self.data):])
            if ident not in self.entries and posixpath.isfile(epath):
                if event.filename.endswith('key.xml'):
                    self.Entries['Path'][ident] = self.get_key
                elif event.filename.endswith('cert.xml'):
                    pass
#                    self.Entries['Path'][ident] = self.get_cert
                else:
                    fname = "".join([ident, '/', event.filename])
                    self.entries[fname] = self.__child__(epath)
                    self.entries[fname].HandleEvent(event)
        if action == 'changed':
            self.entries[ident].HandleEvent(event)
        elif action == 'deleted':
            fbase = self.handles[event.requestID] + event.filename
            if fbase in self.entries:
                # a directory was deleted
                del self.entries[fbase]
                del self.Entries['Path'][fbase]
            else:
                self.entries[ident].HandleEvent(event)

    def get_key(self, entry, metadata):
        path = entry.get('name')
        permdata = {'owner':'root',
                    'group':'root',
                    'type':'file',
                    'perms':'644'}
        [entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
        filename = "".join([path, '/', path.rsplit('/', 1)[1], '.H_', metadata.hostname])
        if filename not in self.entries.keys():
            key = self.build_key(filename, metadata)
            open(self.data + filename, 'w').write(key)
            entry.text = key
        else:
            entry.text = self.entries[filename].data

    def build_key(self, filename, metadata):
        # TODO read params
        type = 'rsa'
        bits = 2048
        if type == 'rsa':
            cmd = "openssl genrsa %s " % bits
        elif type == 'dsa':
            cmd = "openssl dsaparam -noout -genkey %s" % bits
        key = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
        return key

    def get_cert(self, entry, metadata):
        path = entry.get('name')
        permdata = {'owner':'root',
                    'group':'root',
                    'type':'file',
                    'perms':'644'}
        [entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
        filename = "".join([path, '/', path.rsplit('/', 1)[1], '.H_', metadata.hostname])
        if filename in self.entries.keys() and self.verify_cert(filename) :
            entry.text = self.entries[filename].data
        else:
            cert = self.build_cert(filename, metadata)
            open(self.data + filename, 'w').write(cert)
            entry.text = cert

    def verify_cert(self):
        # TODO
        # check cert matches key
        # check expiry
        pass

    def build_req(self):
        pass

    def build_cert(self):
        pass









































#class SSLCAFile:
#
#    def __init__(self, datastore, name, specific, encoding):
#        self.data = datastore
#        self.name = name
#        self.specific = specific
#        self.encoding = encoding
#        if name.endswith('.xml'):
#            self.xml = lxml.etree.parse(name)
#
#    def handle_event(self, event=None):
#        """Handle all fs events for this file."""
#        if event and event.code2str() == 'deleted':
#            return
#
#    def bind_entry(self, entry, metadata):
#        pdb.set_trace()
#
#
#class SSLCAKeyFile(SSLCAFile):
#
#    def __init__(self, datastore, name, specific, encoding):
#        SSLCAFile.__init__(self, datastore, name, specific, encoding)
#        key_attrs = self.xml.find('Key')
#        self.bits = key_attrs.get('bits')
#        self.type = key_attrs.get('type')
#
#    def bind_entry(self, entry, metadata):
#        """Build literal file information."""
#        if entry.tag == 'Path':
#            entry.set('type', 'file')
#        entry.text = self.get_key(entry, metadata)
#
#    def get_key(self, entry, metadata):
#        fname =  +dir '.H_' + metadata.hostname
#        # TODO add logic to get+verify key if hostfile exists & save if not
#        pdb.set_trace()
#        return self.build_key()
#
#    def build_key(self):
#        if self.type == 'rsa':
#            cmd = "openssl genrsa %s " % self.bits
#        elif self.type == 'dsa':
#            cmd = "openssl dsaparam -noout -genkey %s" % self.bits
#        key = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
#        return key
#
#
#class SSLCACertFile(SSLCAFile):
#
#    def __init__(self, datastore, name, specific, encoding):
#        SSLCAFile.__init__(self, datastore, name, specific, encoding)
#        cert_attrs = self.xml.find('Cert')
#        #self.format = cert_attrs.get('format')
#        #self.key = cert_attrs.get('key')
#        #self.ca = cert_attrs.get('ca')
#
#    def bind_entry(self, entry, metadata):
#        """Build literal file information."""
#        fname = entry.get('realname', entry.get('name'))
#        if entry.tag == 'Path':
#            entry.set('type', 'file')
#        entry.text = 'booya cert'
#
#
#class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet):
#    """
#    Handles host and group specific entries
#    """
#    def __init__(self, datastore, basename, path, entry_type, encoding):
#        self.data = datastore
#        Bcfg2.Server.Plugin.EntrySet.__init__(self, basename, path, entry_type, encoding)
#
#    def entry_init(self, event):
#        """Handle template and info file creation."""
#        logger = logging.getLogger('Bcfg2.Plugins.SSLCA')
#        if event.filename in self.entries:
#            logger.warn("Got duplicate add for %s" % event.filename)
#        else:
#            fpath = "%s/%s" % (self.path, event.filename)
#            try:
#                spec = self.specificity_from_filename(event.filename)
#            except Bcfg2.Server.Plugin.SpecificityError:
#                if not self.ignore.match(event.filename):
#                    logger.error("Could not process filename %s; ignoring" % fpath)
#                return
#            self.entries[event.filename] = self.entry_type(self.data, fpath,
#                                                           spec, self.encoding)
#        self.entries[event.filename].handle_event(event)
#
#
#class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
#    """
#    The SSLCA generator handles the creation and
#    management of ssl certificates and their keys.
#    """
#    name = 'SSLCA'
#    __version__ = '$Id:$'
#    __author__ = 'g.hagger@gmail.com'
#    filename_pattern = '(key|cert)\.xml'
#    es_cls = SSLCAEntrySet
#
#    def __init__(self, core, datastore):
#        Bcfg2.Server.Plugin.GroupSpool.__init__(self, core, datastore)
#
#    def HandleEvent(self, event):
#        action = event.code2str()
#        if event.filename[0] == '/':
#            return
#        epath = "".join([self.data, self.handles[event.requestID],
#                         event.filename])
#        if posixpath.isdir(epath):
#            ident = self.handles[event.requestID] + event.filename
#        else:
#            ident = self.handles[event.requestID][:-1]
#
#        if action in ['exists', 'created']:
#            if posixpath.isdir(epath):
#                self.AddDirectoryMonitor(epath[len(self.data):])
#            if ident not in self.entries and posixpath.isfile(epath):
#                if event.filename.endswith('key.xml'):
#                    es_child_cls = SSLCAKeyFile
#                elif event.filename.endswith('cert.xml'):
#                    es_child_cls = SSLCACertFile
#                else:
#                    return
#                dirpath = "".join([self.data, ident])
#                self.entries[ident] = self.es_cls(self.data,
#                                                  self.filename_pattern,
#                                                  dirpath,
#                                                  es_child_cls,
#                                                  self.encoding)
#                self.Entries['Path'][ident] = self.entries[ident].bind_entry
#            if not posixpath.isdir(epath):
#                # do not pass through directory events
#                self.entries[ident].handle_event(event)
#        if action == 'changed':
#            self.entries[ident].handle_event(event)
#        elif action == 'deleted':
#            fbase = self.handles[event.requestID] + event.filename
#            if fbase in self.entries:
#                # a directory was deleted
#                del self.entries[fbase]
#                del self.Entries['Path'][fbase]
#            else:
#                self.entries[ident].handle_event(event)










#import Bcfg2.Options
#import os
#from ConfigParser import ConfigParser, NoSectionError, NoOptionError
#from M2Crypto import RSA, EVP, X509, m2

#class SSLCA(Bcfg2.Server.Plugin.Plugin,
#              Bcfg2.Server.Plugin.Generator,
#              Bcfg2.Server.Plugin.DirectoryBacked):
#    """
#        The sslca generator manages ssl certificates
#        and keys
#    """
#
#    name = 'SSLbase'
#    __version__ = '0.00000000001'
#    __author__ = 'ghagger@wgen.net'
#
#    hostkey = 'localhost.key'
#    hostcert = 'localhost.crt'
#
#    def __init__(self, core, datastore):
#        Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
#        Bcfg2.Server.Plugin.Generator.__init__(self)
#        try:
#            Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data,
#                                                         self.core.fam)
#        except OSError, ioerr:
#            self.logger.error("Failed to load SSLbase repository from %s" \
#                              % (self.data))
#            self.logger.error(ioerr)
#            raise Bcfg2.Server.Plugin.PluginInitError
#        self.Entries = {'Path':
#                        {'/etc/pki/tls/private/localhost.key': self.get_key,
#                         '/etc/pki/tls/certs/localhost.crt': self.get_cert}}
#        # grab essential sslca configuration from bcfg2.conf
#        cp = ConfigParser()
#        cp.read(Bcfg2.Options.CFILE.value)
#        try:
#            ca_cert_filename = cp.get('sslca', 'ca_cert')
#            ca_key_filename = cp.get('sslca', 'ca_key')
#            self.ca_key_passphrase = cp.get('sslca', 'ca_key_passphrase')
#            self.cert_subject = cp.get('sslca', 'cert_subject')
#            self.cert_days = cp.get('sslca', 'cert_days')
#            self.pkey_bits = cp.get('sslca', 'pkey_bits')
#        except:
#            raise NoOptionError
#        self.ca_cert = X509.load_cert(ca_cert_filename)
#        self.ca_key = EVP.load_key(ca_key_filename, lambda x: self.ca_key_passphrase)
#        self._newkey = False
#
#    def get_key(self, entry, metadata):
#        filename = self.hostkey+".H_%s" % metadata.hostname
#        if filename in self.entries.keys():
#            entry.text = self.entries[filename].data
#            self.pkey = EVP.load_key_string(entry.text)
#        else:
#            (self.pkey, entry.text) = self.build_key(filename)
#            keyfile = open(self.data + '/' +filename, 'w')
#            keyfile.write(entry.text)
#            keyfile.close()
#            self._newkey = True
#
#    def build_key(self, filename):
#        """Generate new private key for client."""
#        rsa_key = RSA.gen_key(int(self.pkey_bits), m2.RSA_F4)
#        pkey = EVP.PKey()
#        pkey.assign_rsa(rsa_key)
#        keyfile = open(self.data + '/' +filename, 'w')
#        keyfile.write(pkey.as_pem(cipher=None))
#        keyfile.close()
#        self._newkey = True
#        return pkey, pkey.as_pem(cipher=None)
#
#    def get_cert(self, entry, metadata):
#        filename = self.hostcert + ".H_%s" % metadata.hostname
#        # load prexisting cert, if any
#        if filename in self.entries.keys() and self._newkey == False:
#            cert = X509.load_cert_string(self.entries[filename].data)
#            # check cert subjectAltNames match current aliases
#            cert_aliases = cert.get_ext('subjectAltName')
#            if cert_aliases:
#                if metadata.aliases != [alias.lstrip('DNS:') for alias in cert_aliases.get_value().split(', ')]:
#                    entry.text = self.build_cert(filename, metadata)
#                    return
#            entry.text = cert.as_text()+cert.as_string()
#        else:
#            entry.text = self.build_cert(filename, metadata)
#
#    def get_serial(self):
#        serialpath = self.data + '/serial'
#        serial = 0
#        if os.path.isfile(serialpath):
#            serialfile = open(serialpath, 'r')
#            serial = int(serialfile.read())
#            serialfile.close()
#        serialfile = open(serialpath, 'w')
#        serial += 1
#        serialfile.write(str(serial))
#        serialfile.close()
#        return serial           
#
#    def build_cert(self, filename, metadata):
#        req = self.make_request(self.pkey, metadata)
#        serial = self.get_serial()
#        cert = self.make_cert(req, serial, metadata.aliases)
#        cert_out = cert.as_text()+cert.as_pem()
#        certfile = open(self.data + '/' +filename, 'w')
#        certfile.write(cert_out)
#        certfile.close()
#        cert_store = self.data + '/certstore'
#        if not os.path.isdir(cert_store):
#            os.mkdir(cert_store)
#        storefile = open(cert_store + '/' + str(serial) + '.pem', 'w')
#        storefile.write(cert_out)
#        storefile.close()
#        return cert_out
#
#    def make_request(self, key, metadata):
#        req = X509.Request()
#        req.set_version(2)
#        req.set_pubkey(key)
#        name = X509.X509_Name()
#        parts = [a.split('=') for a in self.cert_subject.split(',')]
#        [setattr(name, k, v) for k,v in parts]
#        name.CN = metadata.hostname
#        req.set_subject_name(name)
#        req.sign(key, 'sha1')
#        return req
#        
#    def make_cert(self, req, serial, aliases):
#        pkey = req.get_pubkey()
#        if not req.verify(pkey):
#            raise ValueError, 'Error verifying request'
#        sub = req.get_subject()
#        cert = X509.X509()
#        cert.set_serial_number(serial)
#        cert.set_version(2)
#        cert.set_subject(sub)
#        cert.set_issuer(self.ca_cert)
#        cert.set_pubkey(pkey)
#        notBefore = m2.x509_get_not_before(cert.x509)
#        notAfter  = m2.x509_get_not_after(cert.x509)
#        m2.x509_gmtime_adj(notBefore, 0)
#        m2.x509_gmtime_adj(notAfter, 60*60*24*long(self.cert_days))
#        exts = [
#            ('basicConstraints','CA:FALSE'),
#            ('subjectKeyIdentifier','hash'),
#            ('authorityKeyIdentifier','keyid,issuer:always'),
#            ('nsCertType','SSL Server'),
#        ]
#        if aliases:
#            exts.append(('subjectAltName', ','.join(['DNS:'+alias for alias in aliases])))
#        for ext in exts:
#            cert.add_ext(X509.new_extension(ext[0],ext[1]))
#        cert.sign(self.ca_key, 'sha1')
#        return cert
#
#    def HandleEvent(self, event=None):
#        """Local event handler that does something...."""
#        Bcfg2.Server.Plugin.DirectoryBacked.HandleEvent(self, event)
#
#    def HandlesEntry(self, entry, _):
#        """Handle entries dynamically."""
#        return entry.tag == 'Path' and (entry.get('name').endswith(self.hostkey) or entry.get('name').endswith(self.hostcert))
#