From 8ab2977eeb79571ed6f976a12a51ed2b75cdc12b Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Wed, 29 Jul 2009 11:27:11 -0400 Subject: Backport certs.py patches from master. Fix deprecation warning for the sha library on Python 2.6 sha has been replaced by hashlib. We need to support Python 2.4 - 2.6 so this will use hashlib if available but fall back onto sha if not. Fortunately they use the same API for the function we need. 509042 Identify CAs to trust from an imported PKCS#12 file We used to use certutil -O to determine the cert chain to trust. This behavior changed in F-11 such that untrusted CAs are not displayed. This is only used when we import PKCS#12 files so use pk12util -l to display the list of certs and keys in the file to determine the nickname(s) of the CAs to trust. 509111 No need to trust NSS built-in CA's, more specific regex for finding CA nickname - Add some logging so we have a better idea of what happened if things fail - Default to self-signed CA to trust if one is not found. This will fix the self-signed CA case where certutil doesn't return untrusted CA's in -O output. - Remove unused httplib import --- ipa-server/ipaserver/certs.py | 73 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py index 8cb1d088..cc4211c2 100644 --- a/ipa-server/ipaserver/certs.py +++ b/ipa-server/ipaserver/certs.py @@ -18,14 +18,21 @@ # import os, stat, subprocess, re -import sha import errno import tempfile import shutil +import logging from ipa import sysrestore from ipa import ipautil +# The sha module is deprecated in Python 2.6, replaced by hashlib. Try +# that first and fall back to sha.sha if it isn't available. +try: + from hashlib import sha256 as sha +except ImportError: + from sha import sha + CA_SERIALNO="/var/lib/ipa/ca_serialno" class CertDB(object): @@ -118,7 +125,7 @@ class CertDB(object): os.chmod(fname, perms) def gen_password(self): - return sha.sha(ipautil.ipa_generate_password()).hexdigest() + return sha(ipautil.ipa_generate_password()).hexdigest() def run_certutil(self, args, stdin=None): new_args = ["/usr/bin/certutil", "-d", self.secdir] @@ -311,15 +318,55 @@ class CertDB(object): chain = p.stdout.read() chain = chain.split("\n") - root_nickname = re.match('\ *"(.*)".*', chain[0]).groups()[0] + root_nickname = re.match('\ *"(.*)" \[.*', chain[0]).groups()[0] return root_nickname - def trust_root_cert(self, nickname): - root_nickname = self.find_root_cert(nickname) - - self.run_certutil(["-M", "-n", root_nickname, - "-t", "CT,CT,"]) + def find_root_cert_from_pkcs12(self, pkcs12_fname, passwd_fname=None): + """Given a PKCS#12 file, try to find any certificates that do + not have a key. The assumption is that these are the root CAs. + """ + args = ["/usr/bin/pk12util", "-d", self.secdir, + "-l", pkcs12_fname, + "-k", passwd_fname] + if passwd_fname: + args = args + ["-w", passwd_fname] + try: + (stdout, stderr) = ipautil.run(args) + except ipautil.CalledProcessError, e: + if e.returncode == 17: + raise RuntimeError("incorrect password") + else: + raise RuntimeError("unknown error using pkcs#12 file") + + lines = stdout.split('\n') + + # A simple state machine. + # 1 = looking for "Certificate:" + # 2 = looking for the Friendly name (nickname) + nicknames = [] + state = 1 + for line in lines: + if state == 2: + m = re.match("\W+Friendly Name: (.*)", line) + if m: + nicknames.append( m.groups(0)[0]) + state = 1 + if line == "Certificate:": + state = 2 + + return nicknames + + def trust_root_cert(self, root_nickname): + if root_nickname is None: + logging.debug("Unable to identify root certificate to trust. Continuing but things are likely to fail.") + return + + if root_nickname[:7] == "Builtin": + logging.debug("No need to add trust for built-in root CA's, skipping %s" % root_nickname) + else: + self.run_certutil(["-M", "-n", root_nickname, + "-t", "CT,CT,"]) def find_server_certs(self): p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, @@ -399,8 +446,14 @@ class CertDB(object): # We only handle one server cert nickname = server_certs[0][0] - self.cacert_name = self.find_root_cert(nickname) - self.trust_root_cert(nickname) + ca_names = self.find_root_cert_from_pkcs12(pkcs12_fname, pkcs12_pwd_fname) + if len(ca_names) == 0: + raise RuntimeError("Could not find a CA cert in %s" % pkcs12_fname) + + self.cacert_name = ca_names[0] + for nickname in ca_names: + self.trust_root_cert(nickname) + self.create_pin_file() self.export_ca_cert(self.cacert_name, False) -- cgit