github-1: support for hashing functions other than sha1
authorJude Nagurney <jude@pwan.org>
Fri, 16 Oct 2015 05:04:40 +0000 (01:04 -0400)
committerJude Nagurney <jude@pwan.org>
Fri, 16 Oct 2015 05:04:40 +0000 (01:04 -0400)
certmaster/certmaster.py
certmaster/config.py
certmaster/utils.py
scripts/certmaster-request
test-it.sh [new file with mode: 0755]
tests/certmaster.conf.tst
tests/minion.conf.tst
tests/test-certmaster.sh

index 5947362..7f62a52 100644 (file)
@@ -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))
index c6e9174..bf9b87b 100644 (file)
@@ -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
index e348ec4..4dcddd7 100644 (file)
@@ -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)
index de4d047..0f11ba4 100755 (executable)
@@ -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 (executable)
index 0000000..f251154
--- /dev/null
@@ -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
+
index e4c294b..e95f400 100644 (file)
@@ -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
index f8fd330..66446d1 100644 (file)
@@ -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
 
index 11e7565..2d82c8c 100755 (executable)
@@ -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 <<EOF
+error: md5 hash function is unsupported: md5
+EOF
+)
+   assertEquals "MD5CA Attempt" "$actual" "$expected"
 }
 
 test_Sha1CA_Autosigning() {
 
-    # TODO:  Verify a deprecation warning was issued ?
-
-    certmaster-request --hostname testcert.pwan.co --ca sha1
-    openssl x509 -in /etc/pki/certmaster-sha1/testcert.pwan.co.cert  -text | grep Signature | grep sha1
+    actual=$(certmaster-request --hostname testcert.pwan.co --ca sha1 2>&1)
+    expected=$(cat <<EOF
+Deprecated hash function of sha1: sha1
+EOF
+)
+    assertEquals "deprecated sha1 warning" "$actual" "$expected"
+    openssl x509 -in /etc/pki/certmaster-sha1/testcert.pwan.co.cert  -text | grep Signature | grep sha1 > /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" $?
 
 }