Make CA-less ipa-server-install option --root-ca-file optional.

The CA cert specified by --root-ca-file option must always be the CA cert of
the CA which issued the server certificates in the PKCS#12 files. As the cert
is not actually user selectable, use CA cert from the PKCS#12 files by default
if it is present.

Document --root-ca-file in ipa-server-install man page.

https://fedorahosted.org/freeipa/ticket/4457

Reviewed-By: Petr Viktorin <pviktori@redhat.com>
This commit is contained in:
Jan Cholasta 2014-08-05 09:06:39 +02:00 committed by Petr Viktorin
parent 418ce870bf
commit 6ad8c464a4
6 changed files with 59 additions and 44 deletions

View File

@ -69,7 +69,7 @@ from ipapython import sysrestore
from ipapython.ipautil import *
from ipapython import ipautil
from ipapython import dogtag
from ipalib import api, errors, util
from ipalib import api, errors, util, x509
from ipapython.config import IPAOptionParser
from ipalib.util import validate_domain_name
from ipalib.constants import CACERT
@ -223,7 +223,7 @@ def parse_options():
cert_group.add_option("--pkinit_pin", dest="pkinit_pin",
help="The password of the Kerberos KDC PKCS#12 file")
cert_group.add_option("--root-ca-file", dest="root_ca_file",
help="PEM file with root CA certificate(s) to trust")
help="PEM file containing the CA certificate for the PKCS#12 files")
cert_group.add_option("--subject", action="callback", callback=subject_callback,
type="string",
help="The certificate subject base (default O=<realm-name>)")
@ -316,10 +316,6 @@ def parse_options():
if options.pkinit_pkcs12 and options.pkinit_pin is None:
parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
if options.dirsrv_pkcs12 and not options.root_ca_file:
parser.error(
"--root-ca-file must be given with the PKCS#12 options.")
if (options.external_cert_file or options.external_ca_file) and options.dirsrv_pkcs12:
parser.error(
"PKCS#12 options cannot be used with the external CA options.")
@ -920,7 +916,7 @@ def main():
if options.http_pin is None:
sys.exit("%s unlock password required" % options.http_pkcs12)
http_pkcs12_info = (options.http_pkcs12, options.http_pin)
http_cert_name = installutils.check_pkcs12(
http_ca_cert = installutils.check_pkcs12(
http_pkcs12_info, ca_file, host_name)
if options.dirsrv_pkcs12:
@ -931,7 +927,7 @@ def main():
if options.dirsrv_pin is None:
sys.exit("%s unlock password required" % options.dirsrv_pkcs12)
dirsrv_pkcs12_info = (options.dirsrv_pkcs12, options.dirsrv_pin)
dirsrv_cert_name = installutils.check_pkcs12(
dirsrv_ca_cert = installutils.check_pkcs12(
dirsrv_pkcs12_info, ca_file, host_name)
if options.pkinit_pkcs12:
@ -943,6 +939,11 @@ def main():
sys.exit("%s unlock password required" % options.pkinit_pkcs12)
pkinit_pkcs12_info = (options.pkinit_pkcs12, options.pkinit_pin)
if (options.http_pkcs12 and options.dirsrv_pkcs12 and
http_ca_cert != dirsrv_ca_cert):
sys.exit("%s and %s are not signed by the same CA certificate" %
(options.http_pkcs12, options.dirsrv_pkcs12))
if not options.dm_password:
dm_password = read_dm_password()
@ -1073,8 +1074,7 @@ def main():
ntp.create_instance()
if options.dirsrv_pkcs12:
ds = dsinstance.DsInstance(fstore=fstore,
cert_nickname=dirsrv_cert_name)
ds = dsinstance.DsInstance(fstore=fstore)
ds.create_instance(realm_name, host_name, domain_name,
dm_password, dirsrv_pkcs12_info,
idstart=options.idstart, idmax=options.idmax,
@ -1128,7 +1128,7 @@ def main():
ca.publish_ca_cert(CACERT)
else:
# Put the CA cert where other instances expect it
shutil.copy(options.root_ca_file, CACERT)
x509.write_certificate(http_ca_cert, CACERT)
os.chmod(CACERT, 0444)
# we now need to enable ssl on the ds

View File

@ -118,6 +118,9 @@ The password of the Apache Server PKCS#12 file
\fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
The password of the Kerberos KDC PKCS#12 file
.TP
\fB\-\-root\-ca\-file\fR=\fIFILE\fR
PEM file containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC SSL certificates. Use this option if the CA certificate is not present in the PKCS#12 files.
.TP
\fB\-\-subject\fR=\fISUBJECT\fR
The certificate subject base (default O=REALM.NAME)

View File

@ -223,14 +223,14 @@ info: IPA V2.0
class DsInstance(service.Service):
def __init__(self, realm_name=None, domain_name=None, dm_password=None,
fstore=None, cert_nickname='Server-Cert'):
fstore=None):
service.Service.__init__(self, "dirsrv",
service_desc="directory server",
dm_password=dm_password,
ldapi=False,
autobind=service.DISABLED
)
self.nickname = cert_nickname
self.nickname = 'Server-Cert'
self.dm_password = dm_password
self.realm = realm_name
self.sub_dict = None
@ -641,24 +641,23 @@ class DsInstance(service.Service):
raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])
# We only handle one server cert
nickname = server_certs[0][0]
self.dercert = dsdb.get_cert_from_db(nickname, pem=False)
self.nickname = server_certs[0][0]
self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)
else:
nickname = self.nickname
cadb = certs.CertDB(self.realm, host_name=self.fqdn, subject_base=self.subject_base)
# FIXME, need to set this nickname in the RA plugin
cadb.export_ca_cert('ipaCert', False)
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
self.dercert = dsdb.create_server_cert(
nickname, self.fqdn, cadb)
self.nickname, self.fqdn, cadb)
dsdb.create_pin_file()
self.cacert_name = dsdb.cacert_name
if self.ca_is_configured:
dsdb.track_server_cert(
nickname, self.principal, dsdb.passwd_fname,
self.nickname, self.principal, dsdb.passwd_fname,
'restart_dirsrv %s' % self.serverid)
conn = ipaldap.IPAdmin(self.fqdn)
@ -679,7 +678,7 @@ class DsInstance(service.Service):
DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
objectclass=["top", "nsEncryptionModule"],
cn=["RSA"],
nsSSLPersonalitySSL=[nickname],
nsSSLPersonalitySSL=[self.nickname],
nsSSLToken=["internal (software)"],
nsSSLActivation=["on"],
)

View File

@ -800,8 +800,6 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
This is used for files given to --*_pkcs12 to ipa-server-install and
ipa-replica-prepare.
Return a (server cert name, CA cert names) tuple
"""
pkcs12_filename, pkcs12_passwd = pkcs12_info
root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
@ -812,13 +810,18 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
# Import the CA cert first so it has a known nickname
# (if it's present in the PKCS#12 it won't be overwritten)
ca_cert_name = 'The Root CA'
try:
nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
except (ValueError, RuntimeError) as e:
raise ScriptError(str(e))
if ca_file:
try:
nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
except (ValueError, RuntimeError) as e:
raise ScriptError(str(e))
# Import everything in the PKCS#12
nssdb.import_pkcs12(pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
try:
nssdb.import_pkcs12(
pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
except RuntimeError as e:
raise ScriptError(str(e))
# Check we have exactly one server cert (one with a private key)
server_certs = nssdb.find_server_certs()
@ -833,21 +836,23 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
# Check we have the whole cert chain & the CA is in it
trust_chain = nssdb.get_trust_chain(server_cert_name)
while trust_chain:
if trust_chain[0] == ca_cert_name:
break
trust_chain = trust_chain[1:]
else:
if len(trust_chain) < 2:
if ca_file:
raise ScriptError(
'%s is not signed by %s, or the full certificate chain is '
'not present in the PKCS#12 file' %
(pkcs12_filename, ca_file))
else:
raise ScriptError(
'The full certificate chain is not present in %s' %
pkcs12_filename)
if ca_file and trust_chain[-2] != ca_cert_name:
raise ScriptError(
'%s is not signed by %s, or the full certificate chain is not '
'present in the PKCS#12 file' % (pkcs12_filename, ca_file))
if len(trust_chain) != 2:
raise ScriptError(
'trust chain of the server certificate in %s contains %s '
'certificates, expected 2' %
(pkcs12_filename, len(trust_chain)))
'%s is not signed by %s' % (pkcs12_filename, ca_file))
ca_cert_name = trust_chain[-2]
# Check server validity
nssdb.trust_root_cert(ca_cert_name)
try:
nssdb.verify_server_cert_validity(server_cert_name, hostname)
except ValueError as e:
@ -855,8 +860,7 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
'The server certificate in %s is not valid: %s' %
(pkcs12_filename, e))
return server_cert_name
return nssdb.get_cert(ca_cert_name)
@contextmanager
def private_ccache(path=None):

View File

@ -139,7 +139,7 @@ class ReplicaPrepare(admintool.AdminTool):
"could not find directory instance: %s" % config_dir)
def check_pkcs12(self, pkcs12_file, pkcs12_pin):
installutils.check_pkcs12(
return installutils.check_pkcs12(
pkcs12_info=(pkcs12_file, pkcs12_pin),
ca_file=CACERT,
hostname=self.replica_fqdn)
@ -221,7 +221,8 @@ class ReplicaPrepare(admintool.AdminTool):
if options.http_pin is None:
raise admintool.ScriptError(
"%s unlock password required" % options.http_pkcs12)
self.check_pkcs12(options.http_pkcs12, options.http_pin)
http_ca_cert = self.check_pkcs12(
options.http_pkcs12, options.http_pin)
if options.dirsrv_pkcs12:
if options.dirsrv_pin is None:
@ -231,7 +232,8 @@ class ReplicaPrepare(admintool.AdminTool):
if options.dirsrv_pin is None:
raise admintool.ScriptError(
"%s unlock password required" % options.dirsrv_pkcs12)
self.check_pkcs12(options.dirsrv_pkcs12, options.dirsrv_pin)
dirsrv_ca_cert = self.check_pkcs12(
options.dirsrv_pkcs12, options.dirsrv_pin)
if options.pkinit_pkcs12:
if options.pkinit_pin is None:
@ -242,6 +244,12 @@ class ReplicaPrepare(admintool.AdminTool):
raise admintool.ScriptError(
"%s unlock password required" % options.pkinit_pkcs12)
if (options.http_pkcs12 and options.dirsrv_pkcs12 and
http_ca_cert != dirsrv_ca_cert):
raise admintool.ScriptError(
"%s and %s are not signed by the same CA certificate" %
(options.http_pkcs12, options.dirsrv_pkcs12))
if (not ipautil.file_exists(
dogtag.configured_constants().CS_CFG_PATH) and
options.dirsrv_pin is None):

View File

@ -154,7 +154,7 @@ class ServerCertInstall(admintool.AdminTool):
os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
server_cert = installutils.check_pkcs12(
installutils.check_pkcs12(
pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
ca_file=CACERT,
hostname=api.env.host)
@ -166,6 +166,7 @@ class ServerCertInstall(admintool.AdminTool):
cdb.delete_cert(old_cert)
cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
server_cert = cdb.find_server_certs()[0][0]
if api.env.enable_ra:
cdb.track_server_cert(server_cert, principal, cdb.passwd_fname,