# standard modules
import SimpleXMLRPCServer
+import string
import sys
+import traceback
import os
import os.path
from OpenSSL import crypto
-import sha
+
+try:
+ import hashlib
+except ImportError:
+ # Python-2.4.z ... gah! (or even 2.3!)
+ import sha
+ class hashlib:
+ @staticmethod
+ def new(algo):
+ if algo == 'sha1':
+ return sha.new()
+ raise ValueError, "Bad checksum type"
+
import glob
import socket
import exceptions
usename = utils.get_hostname(talk_to_certmaster=False)
- mycn = '%s-CA-KEY' % usename
- self.ca_key_file = '%s/certmaster.key' % self.cfg.cadir
- self.ca_cert_file = '%s/certmaster.crt' % self.cfg.cadir
-
self.logger = logger.Logger().logger
self.audit_logger = logger.AuditLogger()
- try:
- if not os.path.exists(self.cfg.cadir):
- os.makedirs(self.cfg.cadir)
- if not os.path.exists(self.ca_key_file) and not os.path.exists(self.ca_cert_file):
- certs.create_ca(CN=mycn, ca_key_file=self.ca_key_file, ca_cert_file=self.ca_cert_file)
- except (IOError, OSError), e:
- print 'Cannot make certmaster certificate authority keys/certs, aborting: %s' % e
- sys.exit(1)
-
-
- # open up the cakey and cacert so we have them available
- self.cakey = certs.retrieve_key_from_file(self.ca_key_file)
- self.cacert = certs.retrieve_cert_from_file(self.ca_cert_file)
-
- for dirpath in [self.cfg.cadir, self.cfg.certroot, self.cfg.csrroot]:
- if not os.path.exists(dirpath):
- os.makedirs(dirpath)
+ self.cakey = {}
+ self.cacert = {}
+
+ for (s_caname,a_ca) in self.cfg.ca.iteritems():
+ s_cadir = a_ca['cadir']
+
+ if s_caname == "":
+ mycn = '%s-CA-KEY' % usename
+ else:
+ mycn = '%s-%s-CA-KEY' % (s_caname.upper(),usename)
+
+ s_ca_key_file = '%s/certmaster.key' % s_cadir
+ s_ca_cert_file = '%s/certmaster.crt' % s_cadir
+
+ # if ca_key_file exists and ca_cert_file is missing == minion only setup
+ if os.path.exists(s_ca_key_file) and not os.path.exists(s_ca_cert_file):
+ continue
+
+ try:
+ if not os.path.exists(s_cadir):
+ os.makedirs(s_cadir)
+ if not os.path.exists(s_ca_key_file) and not os.path.exists(s_ca_cert_file):
+ certs.create_ca(CN=mycn, ca_key_file=s_ca_key_file, ca_cert_file=s_ca_cert_file)
+ except (IOError, OSError), e:
+ print 'Cannot make certmaster certificate authority keys/certs for CA %s, aborting: %s' % (s_caname, e)
+ sys.exit(1)
+
+ # open up the cakey and cacert so we have them available
+ self.cakey[s_caname] = certs.retrieve_key_from_file(s_ca_key_file)
+ self.cacert[s_caname] = certs.retrieve_cert_from_file(s_ca_cert_file)
+
+ for dirpath in [a_ca['cadir'], a_ca['certroot'], a_ca['csrroot'], a_ca['csrroot']]:
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
# setup handlers
self.handlers = {
'wait_for_cert': self.wait_for_cert,
}
-
+
def _dispatch(self, method, params):
if method == 'trait_names' or method == '_getAttributeNames':
return self.handlers.keys()
else:
self.logger.info("Unhandled method call for method: %s " % method)
raise codes.InvalidMethodException
-
+
def _sanitize_cn(self, commonname):
commonname = commonname.replace('/', '')
- commonname = commonname.replace('\\', '')
+ commonname = commonname.replace('\\', '')
return commonname
-
- def wait_for_cert(self, csrbuf, with_triggers=True):
+
+ def wait_for_cert(self, csrbuf, ca='', with_triggers=True):
"""
takes csr as a string
returns True, caller_cert, ca_cert
returns False, '', ''
"""
-
+
try:
csrreq = crypto.load_certificate_request(crypto.FILETYPE_PEM, csrbuf)
except crypto.Error, e:
#XXX need to raise a fault here and document it - but false is just as good
return False, '', ''
-
+
requesting_host = self._sanitize_cn(csrreq.get_subject().CN)
if with_triggers:
- self._run_triggers(requesting_host, '/var/lib/certmaster/triggers/request/pre/*')
+ self._run_triggers(requesting_host, '/var/lib/certmaster/triggers/request/pre/*')
self.logger.info("%s requested signing of cert %s" % (requesting_host,csrreq.get_subject().CN))
# get rid of dodgy characters in the filename we're about to make
-
- certfile = '%s/%s.cert' % (self.cfg.certroot, requesting_host)
- csrfile = '%s/%s.csr' % (self.cfg.csrroot, requesting_host)
+
+ certfile = '%s/%s.cert' % (self.cfg.ca[ca]['certroot'], requesting_host)
+ csrfile = '%s/%s.csr' % (self.cfg.ca[ca]['csrroot'], requesting_host)
# check for old csr on disk
# if we have it - compare the two - if they are not the same - raise a fault
self.logger.debug("csrfile: %s certfile: %s" % (csrfile, certfile))
-
+
if os.path.exists(csrfile):
oldfo = open(csrfile)
oldcsrbuf = oldfo.read()
- oldsha = sha.new()
+ oldsha = hashlib.new('sha1')
oldsha.update(oldcsrbuf)
olddig = oldsha.hexdigest()
- newsha = sha.new()
+ newsha = hashlib.new('sha1')
newsha.update(csrbuf)
newdig = newsha.hexdigest()
if not newdig == olddig:
self.logger.info("A cert for %s already exists and does not match the requesting cert" % (requesting_host))
# XXX raise a proper fault
return False, '', ''
-
+
# look for a cert:
# if we have it, then return True, etc, etc
if os.path.exists(certfile):
slavecert = certs.retrieve_cert_from_file(certfile)
cert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, slavecert)
- cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert)
+ cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert[ca])
if with_triggers:
self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/request/post/*')
return True, cert_buf, cacert_buf
-
+
# if we don't have a cert then:
# if we're autosign then sign it, write out the cert and return True, etc, etc
# else write out the csr
-
- if self.cfg.autosign:
- cert_fn = self.sign_this_csr(csrreq)
- cert = certs.retrieve_cert_from_file(cert_fn)
+
+ if self.cfg.ca[ca]['autosign']:
+ cert_fn = self.sign_this_csr(csrreq,ca=ca)
+ cert = certs.retrieve_cert_from_file(cert_fn)
cert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
- cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert)
- self.logger.info("cert for %s was autosigned" % (requesting_host))
+ cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert[ca])
+ self.logger.info("cert for %s for ca %s was autosigned" % (requesting_host,ca))
if with_triggers:
self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
return True, cert_buf, cacert_buf
-
+
else:
# write the csr out to a file to be dealt with by the admin
destfo = open(csrfile, 'w')
destfo.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, csrreq))
destfo.close()
del destfo
- self.logger.info("cert for %s created and ready to be signed" % (requesting_host))
+ self.logger.info("cert for %s for CA %s created and ready to be signed" % (requesting_host, ca))
if with_triggers:
self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
return False, '', ''
return False, '', ''
- def get_csrs_waiting(self):
- hosts = []
- csrglob = '%s/*.csr' % self.cfg.csrroot
+ def get_csrs_waiting(self, ca=''):
+ hosts = []
+ csrglob = '%s/*.csr' % self.cfg.ca[ca]['csrroot']
csr_list = glob.glob(csrglob)
for f in csr_list:
hn = os.path.basename(f)
hn = hn[:-4]
hosts.append(hn)
return hosts
-
- def remove_this_cert(self, hn, with_triggers=True):
+
+ def remove_this_cert(self, hn, with_triggers=True, ca=''):
""" removes cert for hostname using unlink """
cm = self
- csrglob = '%s/%s.csr' % (cm.cfg.csrroot, hn)
+ csrglob = '%s/%s.csr' % (cm.cfg.ca[ca]['csrroot'], hn)
csrs = glob.glob(csrglob)
- certglob = '%s/%s.cert' % (cm.cfg.certroot, hn)
+ certglob = '%s/%s.cert' % (cm.cfg.ca[ca]['certroot'], hn)
certs = glob.glob(certglob)
if not csrs and not certs:
# FIXME: should be an exception?
os.unlink(fn)
if with_triggers:
self._run_triggers(hn,'/var/lib/certmaster/triggers/remove/post/*')
-
- def sign_this_csr(self, csr, with_triggers=True):
+
+ def sign_this_csr(self, csr, with_triggers=True, ca=''):
"""returns the path to the signed cert file"""
csr_unlink_file = None
- if type(csr) is type(''):
+ if type(csr) is type(''):
if csr.startswith('/') and os.path.exists(csr): # we have a full path to the file
csrfo = open(csr)
csr_buf = csrfo.read()
csr_unlink_file = csr
-
- elif os.path.exists('%s/%s' % (self.cfg.csrroot, csr)): # we have a partial path?
- csrfo = open('%s/%s' % (self.cfg.csrroot, csr))
+
+ elif os.path.exists('%s/%s' % (self.cfg.ca[ca]['csrroot'], csr)): # we have a partial path?
+ csrfo = open('%s/%s' % (self.cfg.ca[ca]['csrroot'], csr))
csr_buf = csrfo.read()
- csr_unlink_file = '%s/%s' % (self.cfg.csrroot, csr)
-
+ csr_unlink_file = '%s/%s' % (self.cfg.ca[ca]['csrroot'], csr)
+
# we have a string of some kind
else:
csr_buf = csr
try:
- csrreq = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_buf)
+ csrreq = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_buf)
except crypto.Error, e:
self.logger.info("Unable to sign %s: Bad CSR" % (csr))
raise exceptions.Exception("Bad CSR: %s" % csr)
-
+
else: # assume we got a bare csr req
csrreq = csr
self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/pre/*')
- requesting_host = self._sanitize_cn(csrreq.get_subject().CN)
- certfile = '%s/%s.cert' % (self.cfg.certroot, requesting_host)
+ certfile = '%s/%s.cert' % (self.cfg.ca[ca]['certroot'], requesting_host)
self.logger.info("Signing for csr %s requested" % certfile)
- thiscert = certs.create_slave_certificate(csrreq, self.cakey, self.cacert, self.cfg.cadir)
+ thiscert = certs.create_slave_certificate(csrreq, self.cakey[ca], self.cacert[ca], self.cfg.ca[ca]['cadir'])
destfo = open(certfile, 'w')
destfo.write(crypto.dump_certificate(crypto.FILETYPE_PEM, thiscert))
if with_triggers:
self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/post/*')
-
+
if csr_unlink_file and os.path.exists(csr_unlink_file):
os.unlink(csr_unlink_file)
-
+
return certfile
# return a list of already signed certs
- def get_signed_certs(self, hostglobs=None):
- certglob = "%s/*.cert" % (self.cfg.certroot)
+ def get_signed_certs(self, hostglobs=None, ca=''):
+ certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
certs = []
globs = "*"
globs = hostglobs
for hostglob in globs:
- certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob)
+ certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
certs = certs + glob.glob(certglob)
signed_certs = []
return signed_certs
+ def get_peer_certs(self):
+ """
+ Returns a list of all certs under peerroot
+ """
+ myglob = os.path.join(self.cfg.peerroot, '*.%s' % self.cfg.cert_extension)
+ return glob.glob(myglob)
+
# return a list of the cert hash string we use to identify systems
- def get_cert_hashes(self, hostglobs=None):
- certglob = "%s/*.cert" % (self.cfg.certroot)
+ def get_cert_hashes(self, hostglobs=None,ca=''):
+ certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
certfiles = []
globs = "*"
globs = hostglobs
for hostglob in globs:
- certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob)
+ certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
certfiles = certfiles + glob.glob(certglob)
-
+
cert_hashes = []
for certfile in certfiles:
cert = certs.retrieve_cert_from_file(certfile)
cert_hashes.append("%s-%s" % (cert.get_subject().CN, cert.subject_name_hash()))
-
+
return cert_hashes
def _run_triggers(self, ref, globber):
def __init__(self, addr):
self.allow_reuse_address = True
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr)
-
+
def serve(xmlrpcinstance):
listen_addr = config.listen_addr
listen_port = config.listen_port
if listen_port == '':
- listen_port = CERTMASTER_LISTEN_PORT
+ listen_port = CERTMASTER_LISTEN_PORT
server = CertmasterXMLRPCServer((listen_addr,listen_port))
server.logRequests = 0 # don't print stuff to console
server.register_instance(xmlrpcinstance)
xmlrpcinstance.audit_logger.logger.info("certmaster started")
server.serve_forever()
+def excepthook(exctype, value, tracebackobj):
+ exctype_blurb = "Exception occured: %s" % exctype
+ excvalue_blurb = "Exception value: %s" % value
+ exctb_blurb = "Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tracebackobj)))
+
+ print exctype_blurb
+ print excvalue_blurb
+ print exctb_blurb
+
+ log = logger.Logger().logger
+ log.info(exctype_blurb)
+ log.info(excvalue_blurb)
+ log.info(exctb_blurb)
+
def main(argv):
-
+
+ sys.excepthook = excepthook
cm = CertMaster('/etc/certmaster/certmaster.conf')
+ if "--version" in sys.argv or "-v" in sys.argv:
+ print >> sys.stderr, file("/etc/certmaster/version").read().strip()
+ sys.exit(0)
+
if "daemon" in argv or "--daemon" in argv:
utils.daemonize("/var/run/certmaster.pid")
else:
print "serving...\n"
-
# just let exceptions bubble up for now
serve(cm)
-
+
if __name__ == "__main__":
#textdomain(I18N_DOMAIN)
main(sys.argv)