From 8c1e7ce1f1b146ba794779ccad8816baea16d64a Mon Sep 17 00:00:00 2001 From: Jude Nagurney Date: Fri, 16 Oct 2015 01:04:40 -0400 Subject: [PATCH] github-1: support for hashing functions other than sha1 --- certmaster/certmaster.py | 32 +++++++++++++++++++++++--------- certmaster/config.py | 15 +++------------ certmaster/utils.py | 18 ++++++++++++++---- scripts/certmaster-request | 4 ++++ test-it.sh | 10 ++++++++++ tests/certmaster.conf.tst | 11 ++++++++++- tests/minion.conf.tst | 6 +++--- tests/test-certmaster.sh | 30 ++++++++++++++++++++++-------- 8 files changed, 89 insertions(+), 37 deletions(-) create mode 100755 test-it.sh diff --git a/certmaster/certmaster.py b/certmaster/certmaster.py index 5947362..7f62a52 100644 --- a/certmaster/certmaster.py +++ b/certmaster/certmaster.py @@ -22,6 +22,7 @@ import traceback import os import os.path import warnings +import xmlrpclib from OpenSSL import crypto try: @@ -123,20 +124,33 @@ class CertMaster(object): def wait_for_cert(self, csrbuf, ca_name, with_triggers=True): """ takes csr as a string - returns True, caller_cert, ca_cert - returns False, '', '' + returns True, caller_cert, ca_cert, warning + returns False, '', '', '' """ try: certauth = self.cfg.ca[ca_name] except: + self.logger.info("Unknown cert authority: %s " % (ca_name)) raise codes.CMException("Unknown cert authority: %s" % ca_name) try: csrreq = crypto.load_certificate_request(crypto.FILETYPE_PEM, csrbuf) except crypto.Error, e: + self.logger.info("crypto error: %s " % (e)) #XXX need to raise a fault here and document it - but false is just as good - return False, '', '' + return False, '', '', '' + + ret_warning = '' + if certauth.hash_function == "md5": + ca_suffix = '' + if ca_name != '': + ca_suffix = ': ' + ca_name + fault = "md5 hash function is unsupported%s" % ca_suffix + self.logger.error(fault) + raise xmlrpclib.Fault(1001,fault) + elif certauth.hash_function == "sha1": + ret_warning = "Deprecated hash function of sha1: %s\n" % ca_name requesting_host = self._sanitize_cn(csrreq.get_subject().CN) @@ -165,7 +179,7 @@ class CertMaster(object): 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, '', '' + return False, '', '', ret_warning # look for a cert: @@ -176,7 +190,7 @@ class CertMaster(object): cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, certauth.cacert) if with_triggers: self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/request/post/*') - return True, cert_buf, cacert_buf + return True, cert_buf, cacert_buf, ret_warning # if we don't have a cert then: # if we're autosign then sign it, write out the cert and return True, etc, etc @@ -190,7 +204,7 @@ class CertMaster(object): self.logger.info("cert for %s for ca %s was autosigned" % (requesting_host,ca_name)) if with_triggers: self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*') - return True, cert_buf, cacert_buf + return True, cert_buf, cacert_buf, ret_warning else: # write the csr out to a file to be dealt with by the admin @@ -201,9 +215,9 @@ class CertMaster(object): self.logger.info("cert for %s for CA %s created and ready to be signed" % (requesting_host, ca_name)) if with_triggers: self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*') - return False, '', '' + return False, '', '', ret_warning - return False, '', '' + return False, '', '', ret_warning def get_csrs_waiting(self, certauth): hosts = [] @@ -271,7 +285,7 @@ class CertMaster(object): certfile = '%s/%s.cert' % (certauth.certroot, requesting_host) self.logger.info("Signing for csr %s requested" % certfile) - thiscert = certs.create_slave_certificate(csrreq, certauth.cakey, certauth.cacert, certauth.cadir, certauth.hash_function) + thiscert = certs.create_slave_certificate(csrreq, certauth.cakey, certauth.cacert, certauth.cadir, hash_function=certauth.hash_function) destfo = open(certfile, 'w') destfo.write(crypto.dump_certificate(crypto.FILETYPE_PEM, thiscert)) diff --git a/certmaster/config.py b/certmaster/config.py index c6e9174..bf9b87b 100644 --- a/certmaster/config.py +++ b/certmaster/config.py @@ -22,6 +22,7 @@ import urlparse from ConfigParser import NoSectionError, NoOptionError, ConfigParser from ConfigParser import ParsingError import exceptions +import warnings CONFIG_FILE = "/etc/certmaster/certmaster.conf" @@ -480,28 +481,18 @@ def read_config(config_file, BaseConfigDerived): ## Add the default items when just using a single ca opts.ca[''] = BaseConfigDerived() - opts.ca[''].hash_function = None + opts.ca[''].hash_function = "sha256" opts.ca[''].populate(confparser,'main') - if opts.ca[''].hash_function == 'sha1': - log.warning('hash_function value of sha1 is deprecated', DeprecationWarning) - elif opts.ca[''].hash_function == 'md5': - print >> sys.stderr, "Error: hash_function of md5 is not supported" - ## Add additonal ca sections sections = confparser.sections() for a_section in sections: if a_section.startswith('ca:'): ca_name = a_section[3:] opts.ca[ca_name] = BaseConfigDerived() - opts.ca[ca_name].hash_function = None + opts.ca[ca_name].hash_function = "sha256" opts.ca[ca_name].populate(confparser,a_section) opts.ca[ca_name].cakey = None opts.ca[ca_name].cacert = None - - if opts.ca[ca_name].hash_function == 'sha1': - warnings.warn('hash_function value of sha1 is deprecated in ca:%s section' % ca_name, DeprecationWarning) - elif opts.ca[ca_name].hash_function == 'md5': - print >> sys.stderr, "Error: hash_function of md5 is not supported in ca:% section" % ca_name return opts diff --git a/certmaster/utils.py b/certmaster/utils.py index e348ec4..4dcddd7 100644 --- a/certmaster/utils.py +++ b/certmaster/utils.py @@ -168,12 +168,15 @@ def create_minion_keys(hostname=None, ca_name=''): raise codes.CMException, "Could not create local keypair or csr for session" result = False + warning = ''; + cert_string = ''; + ca_cert_string = ''; while not result: try: # print "DEBUG: submitting CSR to certmaster: %s" % master_uri log.debug("submitting CSR: %s to certmaster %s" % (csr_file, master_uri)) - result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri, ca_name) + result, cert_string, ca_cert_string, warning = submit_csr_to_master(csr_file, master_uri, ca_name) except socket.error, e: log.warning("Could not locate certmaster at %s" % master_uri) @@ -183,6 +186,9 @@ def create_minion_keys(hostname=None, ca_name=''): log.warning("no response from certmaster %s, sleeping 10 seconds" % master_uri) time.sleep(10) + if warning != '': + log.warning(warning) + sys.stderr.write(warning) if result: # print "DEBUG: recieved certificate from certmaster" @@ -191,8 +197,12 @@ def create_minion_keys(hostname=None, ca_name=''): keypair = certs.retrieve_key_from_file(key_file) valid = certs.check_cert_key_match(cert_string, keypair) if not valid: - log.info("certificate does not match key (run certmaster-ca --clean first?)") - sys.stderr.write("certificate does not match key (run certmaster-ca --clean first?)\n") + if ca_name != "": + ca_suffix = "--ca " + ca_name + else: + ca_suffix = "" + log.info("certificate does not match key (run certmaster-ca --clean %s first on the certmaster ?)" % ca_suffix ) + sys.stderr.write("certificate does not match key (run certmaster-ca --clean %s first on the certmaster ?)\n" % ca_suffix) return cert_fd = os.open(cert_file, os.O_RDWR|os.O_CREAT, 0644) os.write(cert_fd, cert_string) @@ -237,7 +247,7 @@ def submit_csr_to_master(csr_file, master_uri, ca_name=''): """" gets us our cert back from the certmaster.wait_for_cert() method takes csr_file as path location and master_uri - returns Bool, str(cert), str(ca_cert) + returns Bool, str(cert), str(ca_cert), str(warning) """ fo = open(csr_file) diff --git a/scripts/certmaster-request b/scripts/certmaster-request index de4d047..0f11ba4 100755 --- a/scripts/certmaster-request +++ b/scripts/certmaster-request @@ -18,6 +18,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import distutils.sysconfig import optparse import sys +import xmlrpclib from certmaster import requester @@ -36,5 +37,8 @@ if __name__ == "__main__": try: requester.request_cert(hostname=opts.hostname, ca_name=opts.ca) + except xmlrpclib.Fault as f: + print >> sys.stderr, "error: %s" % str(f.faultString) except Exception as e: print >> sys.stderr, "error: %s" % str(e) + sys.exit(1) diff --git a/test-it.sh b/test-it.sh new file mode 100755 index 0000000..f251154 --- /dev/null +++ b/test-it.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +/etc/init.d/certmaster stop +rpm -e certmaster +rpm -i /root/certmaster.git/rpm-build/certmaster-0.28-1pwan.noarch.rpm +cp /etc/certmaster/certmaster.conf.rpmsave /etc/certmaster/certmaster.conf +cp /etc/certmaster/minion.conf.rpmsave /etc/certmaster/minion.conf +cd tests +./test-certmaster.sh + diff --git a/tests/certmaster.conf.tst b/tests/certmaster.conf.tst index e4c294b..e95f400 100644 --- a/tests/certmaster.conf.tst +++ b/tests/certmaster.conf.tst @@ -5,6 +5,7 @@ listen_addr = listen_port = 51235 cert_extension = cert sync_certs = False +log_level = debug # Use thse settings if no --ca flag provided autosign = no @@ -21,13 +22,21 @@ cert_dir = /etc/pki/certmaster/test certroot = /var/lib/certmaster/test/certs csrroot = /var/lib/certmaster/test/csrs +[ca:md5] +autosign = yes +cadir = /etc/pki/certmaster/md5-ca +cert_dir = /etc/pki/certmaster/md5 +certroot = /var/lib/certmaster/md5/certs +csrroot = /var/lib/certmaster/md5/csrs +hash_function = md5 + [ca:sha1] autosign = yes cadir = /etc/pki/certmaster/sha1-ca cert_dir = /etc/pki/certmaster/sha1 certroot = /var/lib/certmaster/sha1/certs csrroot = /var/lib/certmaster/sha1/csrs -hash_function = sha224 +hash_function = sha1 [ca:sha224] autosign = yes diff --git a/tests/minion.conf.tst b/tests/minion.conf.tst index f8fd330..66446d1 100644 --- a/tests/minion.conf.tst +++ b/tests/minion.conf.tst @@ -9,13 +9,13 @@ cert_dir = /etc/pki/certmaster [ca:test] cert_dir = /etc/pki/certmaster-test -hash_function = sha256 + +[ca:md5] +cert_dir = /etc/pki/certmaster-md5 [ca:sha1] cert_dir = /etc/pki/certmaster-sha1 -hash_function = sha1 [ca:sha224] cert_dir = /etc/pki/certmaster-sha224 -hash_function = sha224 diff --git a/tests/test-certmaster.sh b/tests/test-certmaster.sh index 11e7565..2d82c8c 100755 --- a/tests/test-certmaster.sh +++ b/tests/test-certmaster.sh @@ -10,8 +10,14 @@ setUp() cp minion.conf.tst /etc/certmaster/minion.conf rm -rf /var/lib/certmaster rm -rf /var/lib/certmaster/test + rm -rf /var/lib/certmaster/md5 + rm -rf /var/lib/certmaster/sha1 + rm -rf /var/lib/certmaster/sha224 rm -rf /etc/pki/certmaster rm -rf /etc/pki/certmaster-test + rm -rf /etc/pki/certmaster-md5 + rm -rf /etc/pki/certmaster-sha1 + rm -rf /etc/pki/certmaster-sha224 /etc/init.d/certmaster start >& /dev/null } @@ -178,7 +184,7 @@ test_TestCA_Autosigning() subject=`openssl x509 -in /etc/pki/certmaster-test/testcert.pwan.co.cert -subject -noout` [[ $subject == *"CN=testcert.pwan.co"* ]] - openssl x509 -in /etc/pki/certmaster-test/testcert.pwan.co.cert -text | grep Signature | grep sha256 + openssl x509 -in /etc/pki/certmaster-test/testcert.pwan.co.cert -text | grep Signature | grep sha256 > /dev/null 2>&1 assertTrue "testcert.pwan.co.cert has a sha256 hash" $? openssl rsa -in /etc/pki/certmaster-test/testcert.pwan.co.pem -check > /dev/null 2>&1 @@ -202,18 +208,26 @@ test_TestCA_Autosigning() } -test_MD5CA_Attempy() { +test_MD5CA_Attempt() { # TODO: Verify attempts to create MD5 certs fail - assertTrue "TODO" false + actual=$(certmaster-request --hostname badmd5req.pwan.co --ca md5 2>&1) + expected=$(cat <&1) + expected=$(cat < /dev/null 2>&1 assertTrue "testcert.pwan.co.cert has a sha1 hash" $? } @@ -222,7 +236,7 @@ test_Sha224CA_Autosigning() { # TODO: Verify /etc/pki/certmaster-test/testcert.pwan.co.cert is using sha224 certmaster-request --hostname testcert.pwan.co --ca sha224 - openssl x509 -in /etc/pki/certmaster-sha224/testcert.pwan.co.cert -text | grep Signature | grep sha224 + openssl x509 -in /etc/pki/certmaster-sha224/testcert.pwan.co.cert -text | grep Signature | grep sha224 > /dev/null 2>&1 assertTrue "testcert.pwan.co.cert has a sha224 hash" $? } -- 2.39.5