diff options
29 files changed, 299 insertions, 163 deletions
diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index cb8f93b5f..750893dac 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -208,7 +208,9 @@ def request_cert(): "Forwarding request to dogtag-ipa-renew-agent") path = paths.DOGTAG_IPA_RENEW_AGENT_SUBMIT - args = [path] + sys.argv[1:] + ['--submit-option', "requestor_name=IPA"] + args = [path, '--dbdir', paths.IPA_RADB_DIR] + args.extend(sys.argv[1:]) + args.extend(['--submit-option', "requestor_name=IPA"]) if os.environ.get('CERTMONGER_CA_PROFILE') == 'caCACert': args += ['-N', '-O', 'bypassCAnotafter=true'] result = ipautil.run(args, raiseonerr=False, env=os.environ, diff --git a/install/tools/ipa-csreplica-manage b/install/tools/ipa-csreplica-manage index f494380e6..2d534d443 100755 --- a/install/tools/ipa-csreplica-manage +++ b/install/tools/ipa-csreplica-manage @@ -28,7 +28,7 @@ import os from ipaplatform.paths import paths from ipapython.ipa_log_manager import root_logger from ipaserver.install import (replication, installutils, bindinstance, - cainstance, certs) + cainstance) from ipalib import api, errors from ipalib.util import has_managed_topology from ipapython import ipautil, ipaldap, version @@ -275,7 +275,7 @@ def del_master(realm, hostname, options): sys.exit("There were issues removing a connection: %s" % e) # 6. Pick CA renewal master - ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(api.env.realm) if ca.is_renewal_master(hostname): ca.set_renewal_master(options.host) @@ -379,7 +379,7 @@ def set_renewal_master(realm, replica): if not replica: replica = installutils.get_fqdn() - ca = cainstance.CAInstance(realm, certs.NSS_DIR) + ca = cainstance.CAInstance(realm) if ca.is_renewal_master(replica): sys.exit("%s is already the renewal master" % replica) diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage index 56cb90bea..f802201b7 100755 --- a/install/tools/ipa-replica-manage +++ b/install/tools/ipa-replica-manage @@ -36,7 +36,7 @@ from six.moves.xmlrpc_client import MAXINT from ipaclient.install import ipadiscovery from ipapython import ipautil from ipaserver.install import replication, dsinstance, installutils -from ipaserver.install import bindinstance, cainstance, certs +from ipaserver.install import bindinstance, cainstance from ipaserver.install import opendnssecinstance, dnskeysyncinstance from ipapython import version, ipaldap from ipalib import api, errors @@ -890,7 +890,7 @@ def ensure_last_services(conn, hostname, masters, options): print("Please disable or replace DNSSEC key master first.") sys.exit("Deletion aborted") - ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(api.env.realm) if ca.is_renewal_master(hostname): try: ca.set_renewal_master(options.host) diff --git a/install/updates/05-pre_upgrade_plugins.update b/install/updates/05-pre_upgrade_plugins.update index d0e3eb7ce..19918efc6 100644 --- a/install/updates/05-pre_upgrade_plugins.update +++ b/install/updates/05-pre_upgrade_plugins.update @@ -8,3 +8,4 @@ plugin: update_referint plugin: update_uniqueness_plugins_to_new_syntax # last +plugin: update_ra_cert_store diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py index 75c5d97df..ec22594f8 100644 --- a/ipaclient/install/ipa_certupdate.py +++ b/ipaclient/install/ipa_certupdate.py @@ -139,6 +139,7 @@ class CertUpdate(admintool.AdminTool): services.knownservices.dirsrv.restart(instance) self.update_db(paths.HTTPD_ALIAS_DIR, certs) + self.update_db(paths.IPA_RADB_DIR, certs) if services.knownservices.httpd.is_running(): services.knownservices.httpd.restart() diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py index 3984147b6..dccb0e719 100644 --- a/ipaplatform/base/constants.py +++ b/ipaplatform/base/constants.py @@ -11,6 +11,7 @@ class BaseConstantsNamespace(object): DS_USER = 'dirsrv' DS_GROUP = 'dirsrv' HTTPD_USER = "apache" + HTTPD_GROUP = "apache" GSSPROXY_USER = "root" IPA_DNS_PACKAGE_NAME = "freeipa-server-dns" KDCPROXY_USER = "kdcproxy" diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 28db7f1fc..95f8b9050 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -39,8 +39,8 @@ class BasePathNamespace(object): HOSTS = "/etc/hosts" ETC_HTTPD_DIR = "/etc/httpd" HTTPD_ALIAS_DIR = "/etc/httpd/alias" - ALIAS_CACERT_ASC = "/etc/httpd/alias/cacert.asc" - ALIAS_PWDFILE_TXT = "/etc/httpd/alias/pwdfile.txt" + IPA_RADB_DIR = "/var/lib/ipa/radb" + ALIAS_CACERT_ASC = "/var/lib/ipa/radb/cacert.asc" HTTPD_CONF_D_DIR = "/etc/httpd/conf.d/" HTTPD_IPA_KDCPROXY_CONF = "/etc/ipa/kdcproxy/ipa-kdc-proxy.conf" HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/httpd/conf.d/ipa-kdc-proxy.conf" @@ -139,7 +139,7 @@ class BasePathNamespace(object): ROOT_IPA_CACHE = "/root/.ipa_cache" ROOT_PKI = "/root/.pki" DOGTAG_ADMIN_P12 = "/root/ca-agent.p12" - KRA_AGENT_PEM = "/etc/httpd/alias/kra-agent.pem" + KRA_AGENT_PEM = "/var/lib/ipa/radb/kra-agent.pem" CACERT_P12 = "/root/cacert.p12" ROOT_IPA_CSR = "/root/ipa.csr" NAMED_PID = "/run/named/named.pid" diff --git a/ipapython/certdb.py b/ipapython/certdb.py index 948132633..08b8391b1 100644 --- a/ipapython/certdb.py +++ b/ipapython/certdb.py @@ -17,7 +17,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +import binascii import os +import io +import pwd +import grp import re import tempfile import shutil @@ -26,6 +30,7 @@ from cryptography.hazmat.primitives import serialization from nss import nss from nss.error import NSPRError +from ipaplatform.tasks import tasks from ipapython.dn import DN from ipapython.ipa_log_manager import root_logger from ipapython import ipautil @@ -45,6 +50,8 @@ else: CA_NICKNAME_FMT = "%s IPA CA" +NSS_FILES = ("cert8.db", "key3.db", "secmod.db", "pwdfile.txt") + def get_ca_nickname(realm, format=CA_NICKNAME_FMT): return format % realm @@ -106,13 +113,63 @@ class NSSDatabase(object): new_args = new_args + args return ipautil.run(new_args, stdin, **kwargs) - def create_db(self, password_filename): + def create_db(self, password_filename=None, user=None, group=None, + mode=None, backup=False): """Create cert DB :param password_filename: Name of file containing the database password + :param user: User owner the secdir + :param group: Group owner of the secdir + :param mode: Mode of the secdir + :param backup: Backup the sedir files """ + dirmode = 0o750 + filemode = 0o640 + if mode is not None: + dirmode = mode + filemode = mode & 0o666 + + uid = -1 + gid = -1 + if user is not None: + uid = pwd.getpwnam(user).pw_uid + if group is not None: + gid = grp.getgrnam(group).gr_gid + + if backup: + for filename in NSS_FILES: + path = os.path.join(self.secdir, filename) + ipautil.backup_file(path) + + if not os.path.exists(self.secdir): + os.makedirs(self.secdir, dirmode) + + if password_filename is None: + password_filename = os.path.join(self.secdir, 'pwdfile.txt') + + if not os.path.exists(password_filename): + # Create the password file for this db + hex_str = binascii.hexlify(os.urandom(10)) + with io.open(os.open(password_filename, + os.O_CREAT | os.O_WRONLY, + filemode), 'wb', closefd=True) as f: + f.write(hex_str) + f.flush() + self.run_certutil(["-N", "-f", password_filename]) + # Finally fix up perms + os.chown(self.secdir, uid, gid) + os.chmod(self.secdir, dirmode) + tasks.restore_context(self.secdir) + for filename in NSS_FILES: + path = os.path.join(self.secdir, filename) + if os.path.exists(path): + if uid != -1 or gid != -1: + os.chown(path, uid, gid) + os.chmod(path, filemode) + tasks.restore_context(path) + def list_certs(self): """Return nicknames and cert flags for all certs in the database @@ -161,6 +218,31 @@ class NSSDatabase(object): return root_nicknames + def export_pkcs12(self, nickname, pkcs12_filename, db_password_filename, + pkcs12_passwd=None): + args = [PK12UTIL, "-d", self.secdir, + "-o", pkcs12_filename, + "-n", nickname, + "-k", db_password_filename] + pkcs12_password_file = None + if pkcs12_passwd is not None: + pkcs12_password_file = ipautil.write_tmp_file(pkcs12_passwd + '\n') + args = args + ["-w", pkcs12_password_file.name] + try: + ipautil.run(args) + except ipautil.CalledProcessError as e: + if e.returncode == 17: + raise RuntimeError("incorrect password for pkcs#12 file %s" % + pkcs12_filename) + elif e.returncode == 10: + raise RuntimeError("Failed to open %s" % pkcs12_filename) + else: + raise RuntimeError("unknown error exporting pkcs#12 file %s" % + pkcs12_filename) + finally: + if pkcs12_password_file is not None: + pkcs12_password_file.close() + def import_pkcs12(self, pkcs12_filename, db_password_filename, pkcs12_passwd=None): args = [PK12UTIL, "-d", self.secdir, @@ -508,3 +590,12 @@ class NSSDatabase(object): finally: del certdb, cert nss.nss_shutdown() + + def publish_ca_cert(self, canickname, location): + args = ["-L", "-n", canickname, "-a"] + result = self.run_certutil(args, capture_output=True) + cert = result.output + fd = open(location, "w+") + fd.write(cert) + fd.close() + os.chmod(location, 0o444) diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index af8cc53ae..8e92ef082 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -265,7 +265,7 @@ def install_step_0(standalone, replica_config, options): 'certmap.conf', 'subject_base', str(subject_base)) dsinstance.write_certmap_conf(realm_name, ca_subject) - ca = cainstance.CAInstance(realm_name, certs.NSS_DIR, + ca = cainstance.CAInstance(realm_name, paths.IPA_RADB_DIR, host_name=host_name) ca.configure_instance(host_name, dm_password, dm_password, subject_base=subject_base, @@ -293,7 +293,8 @@ def install_step_1(standalone, replica_config, options): subject_base = options._subject_base basedn = ipautil.realm_to_suffix(realm_name) - ca = cainstance.CAInstance(realm_name, certs.NSS_DIR, host_name=host_name) + ca = cainstance.CAInstance(realm_name, paths.IPA_RADB_DIR, + host_name=host_name) ca.stop('pki-tomcat') @@ -355,8 +356,7 @@ def install_step_1(standalone, replica_config, options): def uninstall(): - ca_instance = cainstance.CAInstance( - api.env.realm, certs.NSS_DIR) + ca_instance = cainstance.CAInstance(api.env.realm, paths.IPA_RADB_DIR) ca_instance.stop_tracking_certificates() if ca_instance.is_configured(): ca_instance.uninstall() diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index d86964127..1b7ada456 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -656,7 +656,7 @@ class CAInstance(DogtagInstance): Used when setting up replication """ - # Add the new RA cert to the database in /etc/httpd/alias + # Add the new RA cert into the RA database with tempfile.NamedTemporaryFile(mode="w") as agent_file: agent_file.write(self.dm_password) agent_file.flush() @@ -970,16 +970,6 @@ class CAInstance(DogtagInstance): self.log.warning("Error while removing CRL publish " "directory: %s", e) - def publish_ca_cert(self, location): - args = ["-L", "-n", self.canickname, "-a"] - result = self.__run_certutil( - args, capture_output=True) - cert = result.output - fd = open(location, "w+") - fd.write(cert) - fd.close() - os.chmod(location, 0o444) - def unconfigure_certmonger_renewal_guard(self): if not self.is_configured(): return @@ -1004,8 +994,8 @@ class CAInstance(DogtagInstance): ca='dogtag-ipa-ca-renew-agent', nickname='ipaCert', pin=None, - pinfile=paths.ALIAS_PWDFILE_TXT, - secdir=paths.HTTPD_ALIAS_DIR, + pinfile=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), + secdir=paths.IPA_RADB_DIR, pre_command='renew_ra_cert_pre', post_command='renew_ra_cert') except RuntimeError as e: @@ -1024,7 +1014,7 @@ class CAInstance(DogtagInstance): certmonger.stop_tracking(self.nss_db, nickname=nickname) try: - certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname='ipaCert') + certmonger.stop_tracking(paths.IPA_RADB_DIR, nickname='ipaCert') except RuntimeError as e: root_logger.error( "certmonger failed to stop tracking certificate: %s", e) diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index d484d8a2f..6d6523c1c 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -41,13 +41,8 @@ from ipapython.dn import DN from ipalib import pkcs10, x509, api from ipalib.errors import CertificateOperationError from ipalib.text import _ -from ipaplatform.constants import constants from ipaplatform.paths import paths -# Apache needs access to this database so we need to create it -# where apache can reach -NSS_DIR = paths.HTTPD_ALIAS_DIR - def get_cert_nickname(cert): """ @@ -80,9 +75,8 @@ class CertDB(object): """ # TODO: Remove all selfsign code - def __init__( - self, realm, nssdir=NSS_DIR, fstore=None, host_name=None, - subject_base=None, ca_subject=None): + def __init__(self, realm, nssdir=paths.IPA_RADB_DIR, fstore=None, + host_name=None, subject_base=None, ca_subject=None): self.nssdb = NSSDatabase(nssdir) self.secdir = nssdir @@ -93,10 +87,8 @@ class CertDB(object): self.certdb_fname = self.secdir + "/cert8.db" self.keydb_fname = self.secdir + "/key3.db" self.secmod_fname = self.secdir + "/secmod.db" - self.cacert_fname = self.secdir + "/cacert.asc" self.pk12_fname = self.secdir + "/cacert.p12" self.pin_fname = self.secdir + "/pin.txt" - self.pwd_conf = paths.HTTPD_PASSWORD_CONF self.reqdir = None self.certreq_fname = None self.certder_fname = None @@ -222,21 +214,22 @@ class CertDB(object): return False - def export_ca_cert(self, nickname, create_pkcs12=False): + def export_ca_cert(self, nickname, create_pkcs12=False, + cacert_fname=paths.ALIAS_CACERT_ASC): """create_pkcs12 tells us whether we should create a PKCS#12 file of the CA or not. If we are running on a replica then we won't have the private key to make a PKCS#12 file so we don't need to do that step.""" # export the CA cert for use with other apps - ipautil.backup_file(self.cacert_fname) + ipautil.backup_file(cacert_fname) root_nicknames = self.find_root_cert(nickname)[:-1] - fd = open(self.cacert_fname, "w") + fd = open(cacert_fname, "w") for root in root_nicknames: result = self.run_certutil(["-L", "-n", root, "-a"], capture_output=True) fd.write(result.output) fd.close() - os.chmod(self.cacert_fname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) + os.chmod(cacert_fname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) if create_pkcs12: ipautil.backup_file(self.pk12_fname) ipautil.run([paths.PK12UTIL, "-d", self.secdir, @@ -494,19 +487,6 @@ class CertDB(object): pwdfile.close() self.set_perms(self.pin_fname) - def create_password_conf(self): - """ - This is the format of mod_nss pin files. - """ - ipautil.backup_file(self.pwd_conf) - f = open(self.pwd_conf, "w") - f.write("internal:") - pwdfile = open(self.passwd_fname) - f.write(pwdfile.read()) - f.close() - pwdfile.close() - self.set_perms(self.pwd_conf, uid=constants.HTTPD_USER) - def find_root_cert(self, nickname): """ Given a nickname, return a list of the certificates that make up @@ -550,7 +530,8 @@ class CertDB(object): "-in", pem_fname, "-out", pkcs12_fname, "-passout", "file:" + pkcs12_pwd_fname]) - def create_from_cacert(self, cacert_fname, passwd=None): + def create_from_cacert(self, cacert_fname=paths.ALIAS_CACERT_ASC, + passwd=None): if ipautil.file_exists(self.certdb_fname): # We already have a cert db, see if it is for the same CA. # If it is we leave things as they are. @@ -646,15 +627,12 @@ class CertDB(object): "-passin", "file:" + pwd.name]) def publish_ca_cert(self, location): - shutil.copy(self.cacert_fname, location) - os.chmod(location, 0o444) + self.nssdb.publish_ca_cert(self.cacert_name, location) def export_pem_cert(self, nickname, location): return self.nssdb.export_pem_cert(nickname, location) - def request_service_cert(self, nickname, principal, host, pwdconf=False): - if pwdconf: - self.create_password_conf() + def request_service_cert(self, nickname, principal, host): certmonger.request_and_wait_for_cert(certpath=self.secdir, nickname=nickname, principal=principal, diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 2ebff6b09..32772db21 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -77,12 +77,12 @@ def export_kra_agent_pem(): """ Export ipaCert with private key for client authentication. """ - fd, filename = tempfile.mkstemp(dir=paths.HTTPD_ALIAS_DIR) + fd, filename = tempfile.mkstemp(dir=paths.IPA_RADB_DIR) os.close(fd) args = ["/usr/bin/pki", - "-d", paths.HTTPD_ALIAS_DIR, - "-C", paths.ALIAS_PWDFILE_TXT, + "-d", paths.IPA_RADB_DIR, + "-C", os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), "client-cert-show", "ipaCert", "--client-cert", filename] ipautil.run(args) diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 8e979a7aa..31358957b 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -814,12 +814,13 @@ class DsInstance(service.Service): # 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) + dsdb.create_from_cacert() ca_args = ['/usr/libexec/certmonger/dogtag-submit', '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, - '--dbdir', paths.HTTPD_ALIAS_DIR, + '--dbdir', paths.IPA_RADB_DIR, '--nickname', 'ipaCert', - '--sslpinfile', paths.ALIAS_PWDFILE_TXT, + '--sslpinfile', os.path.join(paths.IPA_RADB_DIR, + 'pwdfile.txt'), '--agent-submit'] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper('IPA', helper) diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index dddf097c0..f08bb68d1 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -19,7 +19,6 @@ from __future__ import print_function -import io import os import os.path import pwd @@ -35,6 +34,7 @@ from ipalib.install import certmonger from ipaserver.install import service from ipaserver.install import certs from ipaserver.install import installutils +from ipapython import certdb from ipapython import dogtag from ipapython import ipautil from ipapython.dn import DN @@ -70,8 +70,6 @@ NSS_CIPHER_SUITE = [ ] NSS_CIPHER_REVISION = '20160129' -NSS_FILES = ("cert8.db", "key3.db", "secmod.db", "pwdfile.txt") - def httpd_443_configured(): """ @@ -176,7 +174,6 @@ class HTTPInstance(service.Service): self.step("configure certmonger for renewals", self.configure_certmonger_renewal_guard) self.step("importing CA certificates from LDAP", self.__import_ca_certs) - self.step("publish CA cert", self.__publish_ca_cert) self.step("clean up any existing httpd ccaches", self.remove_httpd_ccaches) self.step("configuring SELinux for httpd", self.configure_selinux_for_httpd) @@ -316,31 +313,12 @@ class HTTPInstance(service.Service): if certmonger_stopped: certmonger.stop() - def create_cert_db(self): - database = certs.NSS_DIR - pwd_file = os.path.join(database, 'pwdfile.txt') - - for p in NSS_FILES: - nss_path = os.path.join(database, p) - ipautil.backup_file(nss_path) - - # Create the password file for this db - password = ipautil.ipa_generate_password() - with io.open(pwd_file, 'w') as f: - f.write(password) - - ipautil.run([paths.CERTUTIL, "-d", database, "-f", pwd_file, "-N"]) - - self.fix_cert_db_perms() - - def fix_cert_db_perms(self): - pent = pwd.getpwnam(self.service_user) - - for filename in NSS_FILES: - nss_path = os.path.join(certs.NSS_DIR, filename) - os.chmod(nss_path, 0o640) - os.chown(nss_path, 0, pent.pw_gid) - tasks.restore_context(nss_path) + def create_cert_dbs(self): + nssdb = certdb.NSSDatabase(nssdir=paths.HTTPD_ALIAS_DIR) + nssdb.create_db(user="root", group=constants.HTTPD_GROUP, backup=True) + nssdb = certdb.NSSDatabase(nssdir=paths.IPA_RADB_DIR) + nssdb.create_db(user=constants.HTTPD_USER, group=constants.HTTPD_GROUP, + mode=0o751, backup=True) def request_anon_keytab(self): parent = os.path.dirname(paths.ANON_KEYTAB) @@ -353,8 +331,26 @@ class HTTPInstance(service.Service): os.chown(parent, pent.pw_uid, pent.pw_gid) os.chown(paths.ANON_KEYTAB, pent.pw_uid, pent.pw_gid) + def create_password_conf(self): + """ + This is the format of mod_nss pin files. + """ + pwd_conf = paths.HTTPD_PASSWORD_CONF + + ipautil.backup_file(pwd_conf) + f = open(pwd_conf, "w") + f.write("internal:") + pwdfile = open(os.path.join(paths.HTTPD_ALIAS_DIR, 'pwdfile.txt')) + f.write(pwdfile.read()) + f.close() + pwdfile.close() + pent = pwd.getpwnam(constants.HTTPD_USER) + os.chown(pwd_conf, pent.pw_uid, pent.pw_gid) + os.chmod(pwd_conf, 0o400) + def __setup_ssl(self): - db = certs.CertDB(self.realm, subject_base=self.subject_base) + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base) if self.pkcs12_info: if self.ca_is_configured: trust_flags = 'CT,C,C' @@ -367,7 +363,7 @@ class HTTPInstance(service.Service): if len(server_certs) == 0: raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0]) - db.create_password_conf() + self.create_password_conf() # We only handle one server cert nickname = server_certs[0][0] @@ -383,13 +379,14 @@ class HTTPInstance(service.Service): else: if not self.promote: - db.create_password_conf() + self.create_password_conf() ca_args = [ '/usr/libexec/certmonger/dogtag-submit', '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, - '--dbdir', paths.HTTPD_ALIAS_DIR, + '--dbdir', paths.IPA_RADB_DIR, '--nickname', 'ipaCert', - '--sslpinfile', paths.ALIAS_PWDFILE_TXT, + '--sslpinfile', os.path.join(paths.IPA_RADB_DIR, + 'pwdfile.txt'), '--agent-submit' ] helper = " ".join(ca_args) @@ -413,21 +410,19 @@ class HTTPInstance(service.Service): self.add_cert_to_service() + # Verify we have a valid server cert server_certs = db.find_server_certs() if not server_certs: raise RuntimeError("Could not find a suitable server cert.") - # We only handle one server cert - nickname = server_certs[0][0] - db.export_ca_cert(nickname) - def __import_ca_certs(self): + # first for the RA DB db = certs.CertDB(self.realm, subject_base=self.subject_base) self.import_ca_certs(db, self.ca_is_configured) - - def __publish_ca_cert(self): - ca_db = certs.CertDB(self.realm) - ca_db.publish_ca_cert(paths.CA_CRT) + # and then also for the HTTPD DB + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base) + self.import_ca_certs(db, self.ca_is_configured) def is_kdcproxy_configured(self): """Check if KDC proxy has already been configured in the past""" @@ -574,10 +569,10 @@ class HTTPInstance(service.Service): self.enable() def stop_tracking_certificates(self): - db = certs.CertDB(api.env.realm) + db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) db.untrack_server_cert(self.cert_nickname) def start_tracking_certificates(self): - db = certs.CertDB(self.realm) + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR) db.track_server_cert(self.cert_nickname, self.principal, db.passwd_fname, 'restart_httpd') diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py index b2f2e2a9f..9cf0d4c5e 100644 --- a/ipaserver/install/ipa_backup.py +++ b/ipaserver/install/ipa_backup.py @@ -103,20 +103,21 @@ class Backup(admintool.AdminTool): description = "Back up IPA files and databases." dirs = (paths.IPA_HTML_DIR, - paths.ROOT_PKI, - paths.PKI_TOMCAT, - paths.SYSCONFIG_PKI, - paths.HTTPD_ALIAS_DIR, - paths.VAR_LIB_PKI_DIR, - paths.SYSRESTORE, - paths.IPA_CLIENT_SYSRESTORE, - paths.IPA_DNSSEC_DIR, - paths.SSSD_PUBCONF_KRB5_INCLUDE_D_DIR, - paths.AUTHCONFIG_LAST, - paths.VAR_LIB_CERTMONGER_DIR, - paths.VAR_LIB_IPA, - paths.VAR_RUN_DIRSRV_DIR, - paths.DIRSRV_LOCK_DIR, + paths.ROOT_PKI, + paths.PKI_TOMCAT, + paths.SYSCONFIG_PKI, + paths.HTTPD_ALIAS_DIR, + paths.IPA_RADB_DIR, + paths.VAR_LIB_PKI_DIR, + paths.SYSRESTORE, + paths.IPA_CLIENT_SYSRESTORE, + paths.IPA_DNSSEC_DIR, + paths.SSSD_PUBCONF_KRB5_INCLUDE_D_DIR, + paths.AUTHCONFIG_LAST, + paths.VAR_LIB_CERTMONGER_DIR, + paths.VAR_LIB_IPA, + paths.VAR_RUN_DIRSRV_DIR, + paths.DIRSRV_LOCK_DIR, ) files = ( diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py index 1d1ae2f21..e47d10467 100644 --- a/ipaserver/install/ipa_cacert_manage.py +++ b/ipaserver/install/ipa_cacert_manage.py @@ -131,7 +131,7 @@ class CACertManage(admintool.AdminTool): api.Backend.ldap2.connect(bind_pw=password) def renew(self): - ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(api.env.realm) if not ca.is_configured(): raise admintool.ScriptError("CA is not configured on this system") diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py index e7070b636..ece5f554b 100644 --- a/ipaserver/install/ipa_replica_prepare.py +++ b/ipaserver/install/ipa_replica_prepare.py @@ -603,7 +603,7 @@ class ReplicaPrepare(admintool.AdminTool): ca_db = certs.CertDB( api.env.realm, host_name=api.env.host, subject_base=subject_base) - db.create_from_cacert(ca_db.cacert_fname) + db.create_from_cacert() db.create_server_cert(nickname, hostname, ca_db) pkcs12_fname = os.path.join(self.dir, fname + ".p12") diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py index d07c7de7a..787f1f645 100644 --- a/ipaserver/install/ipa_server_certinstall.py +++ b/ipaserver/install/ipa_server_certinstall.py @@ -131,7 +131,7 @@ class ServerCertInstall(admintool.AdminTool): pass def install_http_cert(self): - dirname = certs.NSS_DIR + dirname = paths.HTTPD_ALIAS_DIR old_cert = installutils.get_directive(paths.HTTPD_NSS_CONF, 'NSSNickname') diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index f16a1748f..ec388011c 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -33,7 +33,6 @@ from ipaplatform.paths import paths from ipapython import certdb from ipapython import ipautil from ipapython.dn import DN -from ipaserver.install import certs from ipaserver.install import cainstance from ipaserver.install import installutils from ipaserver.install import ldapupdate @@ -107,7 +106,7 @@ class KRAInstance(DogtagInstance): raise RuntimeError( "KRA already installed.") # Confirm that a Dogtag 10 CA instance already exists - ca = cainstance.CAInstance(self.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(self.realm) if not ca.is_installed(): raise RuntimeError( "KRA configuration failed. " @@ -292,7 +291,7 @@ class KRAInstance(DogtagInstance): """ # get ipaCert certificate - with certdb.NSSDatabase(paths.HTTPD_ALIAS_DIR) as ipa_nssdb: + with certdb.NSSDatabase(paths.IPA_RADB_DIR) as ipa_nssdb: cert_data = ipa_nssdb.get_cert("ipaCert") cert = x509.load_certificate(cert_data, x509.DER) diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py index 3ddff596e..4fa4edb12 100644 --- a/ipaserver/install/plugins/ca_renewal_master.py +++ b/ipaserver/install/plugins/ca_renewal_master.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from ipaserver.install import installutils, certs, cainstance +from ipaserver.install import installutils, cainstance from ipalib import errors from ipalib import Updater from ipalib.install import certmonger @@ -34,7 +34,7 @@ class update_ca_renewal_master(Updater): """ def execute(self, **options): - ca = cainstance.CAInstance(self.api.env.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(self.api.env.realm) if not ca.is_configured(): self.debug("CA is not configured on this host") return False, [] @@ -74,7 +74,7 @@ class update_ca_renewal_master(Updater): return False, [] criteria = { - 'cert-database': paths.HTTPD_ALIAS_DIR, + 'cert-database': paths.IPA_RADB_DIR, 'cert-nickname': 'ipaCert', } request_id = certmonger.get_request_id(criteria) diff --git a/ipaserver/install/plugins/update_ca_topology.py b/ipaserver/install/plugins/update_ca_topology.py index f82926b19..e49f35883 100644 --- a/ipaserver/install/plugins/update_ca_topology.py +++ b/ipaserver/install/plugins/update_ca_topology.py @@ -6,7 +6,7 @@ from ipalib import errors from ipalib import Registry from ipalib import Updater from ipapython.dn import DN -from ipaserver.install import certs, cainstance +from ipaserver.install import cainstance from ipaserver.install import ldapupdate from ipaplatform.paths import paths @@ -21,7 +21,7 @@ class update_ca_topology(Updater): def execute(self, **options): - ca = cainstance.CAInstance(self.api.env.realm, certs.NSS_DIR) + ca = cainstance.CAInstance(self.api.env.realm) if not ca.is_configured(): self.log.debug("CA is not configured on this host") return False, [] diff --git a/ipaserver/install/plugins/update_ra_cert_store.py b/ipaserver/install/plugins/update_ra_cert_store.py new file mode 100644 index 000000000..84e556919 --- /dev/null +++ b/ipaserver/install/plugins/update_ra_cert_store.py @@ -0,0 +1,76 @@ +# +# Copyright (C) 2016 FreeIPA Contributors see COPYING for license +# + +import binascii +import os + +from ipalib import Registry +from ipalib import Updater +from ipalib.install import certmonger +from ipaplatform.constants import constants +from ipaplatform.paths import paths +from ipapython import certdb + +register = Registry() + + +@register() +class update_ra_cert_store(Updater): + """ + Moves the cert store from /etc/httpd/alias to /var/lib/ipa/radb + """ + + def execute(self, **options): + olddb = certdb.NSSDatabase(nssdir=paths.HTTPD_ALIAS_DIR) + if not olddb.has_nickname('ipaCert'): + # Nothign to do + return False, [] + + newdb = certdb.NSSDatabase(nssdir=paths.IPA_RADB_DIR) + if os.path.exists(paths.IPA_RADB_DIR): + if newdb.has_nickname('ipaCert'): + self.log.warning( + "An 'ipaCert' nickname exists in both the old {} and the " + "new {} NSS Databases!".format(paths.HTTPD_ALIAS_DIR, + paths.IPA_RADB_DIR)) + return False, [] + else: + # Create the DB + newdb.create_db(os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), + user=constants.HTTPD_USER, + group=constants.HTTPD_GROUP, + mode=0o751, backup=True) + + # Import cert chain (ignore errors, as certs may already be imported) + certlist = olddb.list_certs() + certflags = {} + for name, flags in certlist: + certflags[name] = flags + for name in olddb.get_trust_chain('ipaCert'): + if name == 'ipaCert': + continue + try: + cert = olddb.get_cert(name, pem=True) + newdb.add_cert(cert, name, certflags[name], pem=True) + except Exception as e: # pylint disable=broad-except + self.log.warning("Failed to import '{}' from trust " + "chain: {}".format(name, str(e))) + + # As the last step export/import/delete the RA Cert + ipa_httpd_pwdfile = os.path.join(paths.HTTPD_ALIAS_DIR, 'pwdfile.txt') + ipa_radb_pwdfile = os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt') + pw = binascii.hexlify(os.urandom(10)) + p12file = os.path.join(paths.IPA_RADB_DIR, 'ipaCert.p12') + olddb.export_pkcs12('ipaCert', p12file, ipa_httpd_pwdfile, pw) + newdb.import_pkcs12(p12file, ipa_radb_pwdfile, pw) + + certmonger.stop_tracking(secdir=olddb.secdir, + nickname='ipaCert') + certmonger.start_tracking(secdir=newdb.secdir, + nickname='ipaCert', + password_file=ipa_radb_pwdfile) + + olddb.delete_cert('ipaCert') + + return False, [] diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index c2bcc1c32..666e2a536 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -31,7 +31,7 @@ from ipalib.util import ( ) import ipaclient.install.ntpconf from ipaserver.install import ( - bindinstance, ca, cainstance, certs, dns, dsinstance, + bindinstance, ca, certs, dns, dsinstance, httpinstance, installutils, kra, krbinstance, ntpinstance, otpdinstance, custodiainstance, replication, service, sysupgrade) @@ -712,8 +712,9 @@ def install(installer): # Make sure tmpfiles dir exist before installing components tasks.create_tmpfiles_dirs() + # create NSS Databases http_instance = httpinstance.HTTPInstance() - http_instance.create_cert_db() + http_instance.create_cert_dbs() # Create DS user/group if it doesn't exist yet dsinstance.create_ds_user() @@ -778,8 +779,8 @@ def install(installer): ca.install_step_0(False, None, options) # Now put the CA cert where other instances exepct it - ca_instance = cainstance.CAInstance(realm_name, certs.NSS_DIR) - ca_instance.publish_ca_cert(paths.IPA_CA_CRT) + ca_db = certs.CertDB(realm_name) + ca_db.publish_ca_cert(paths.IPA_CA_CRT) else: # Put the CA cert where other instances expect it x509.write_certificate(http_ca_cert, paths.IPA_CA_CRT) @@ -830,11 +831,6 @@ def install(installer): ca_is_configured=setup_ca) tasks.restore_context(paths.CACHE_IPA_SESSIONS) - # Export full CA chain - ca_db = certs.CertDB(realm_name) - os.chmod(paths.IPA_CA_CRT, 0o644) - ca_db.publish_ca_cert(paths.IPA_CA_CRT) - ca.set_subject_base_in_config(options.subject_base) # Apply any LDAP updates. Needs to be done after the configuration file @@ -1103,7 +1099,8 @@ def uninstall(installer): # Note that this name will be wrong after the first uninstall. dirname = dsinstance.config_dirname( installutils.realm_to_serverid(api.env.realm)) - dirs = [dirname, paths.PKI_TOMCAT_ALIAS_DIR, certs.NSS_DIR] + dirs = [dirname, paths.PKI_TOMCAT_ALIAS_DIR, paths.HTTPD_ALIAS_DIR, + paths.IPA_RADB_DIR] ids = certmonger.check_state(dirs) if ids: root_logger.error('Some certificates may still be tracked by ' diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index ed7203d6f..f0b04523c 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -77,9 +77,12 @@ def make_pkcs12_info(directory, cert_name, password_name): def install_http_certs(host_name, realm_name, subject_base): principal = 'HTTP/%s@%s' % (host_name, realm_name) # Obtain certificate for the HTTP service - nssdir = certs.NSS_DIR + http = httpinstance.HTTPInstance() + http.create_password_conf() + nssdir = paths.HTTPD_ALIAS_DIR + subject = subject_base or DN(('O', realm_name)) db = certs.CertDB(realm_name, nssdir=nssdir, subject_base=subject_base) - db.request_service_cert('Server-Cert', principal, host_name, True) + db.request_service_cert('Server-Cert', principal, host_name) def install_replica_ds(config, options, ca_is_configured, remote_api, @@ -1332,9 +1335,9 @@ def install(installer): dsinstance.create_ds_user() - # create /etc/httpd/alias NSS Database + # create NSS Databases http_instance = httpinstance.HTTPInstance() - http_instance.create_cert_db() + http_instance.create_cert_dbs() try: conn.connect(ccache=ccache) diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 6919f521e..549158270 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -919,7 +919,7 @@ def certificate_renewal_update(ca, ds, http): 'ipaCACertRenewal', ), ( - paths.HTTPD_ALIAS_DIR, + paths.IPA_RADB_DIR, 'ipaCert', 'dogtag-ipa-ca-renew-agent', template % 'renew_ra_cert_pre', @@ -1374,7 +1374,7 @@ def fix_trust_flags(): root_logger.info("CA is not enabled") return - db = certs.CertDB(api.env.realm) + db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) nickname = certdb.get_ca_nickname(api.env.realm) cert = db.get_cert_from_db(nickname) if cert: @@ -1540,7 +1540,7 @@ def upgrade_configuration(): sub_dict['SUBJECT_BASE'] = subject_base ca = cainstance.CAInstance( - api.env.realm, certs.NSS_DIR, host_name=api.env.host) + api.env.realm, paths.IPA_RADB_DIR, host_name=api.env.host) ca_running = ca.is_running() with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'): diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 2f9fd4b37..6ff6d292c 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -1242,8 +1242,8 @@ class RestClient(Backend): self.sec_dir = api.env.dot_ipa + os.sep + 'alias' self.pwd_file = self.sec_dir + os.sep + '.pwd' else: - self.sec_dir = paths.HTTPD_ALIAS_DIR - self.pwd_file = paths.ALIAS_PWDFILE_TXT + self.sec_dir = paths.IPA_RADB_DIR + self.pwd_file = os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt') self.noise_file = self.sec_dir + os.sep + '.noise' self.ipa_key_size = "2048" self.ipa_certificate_nickname = "ipaCert" @@ -2015,8 +2015,8 @@ class kra(Backend): raise RuntimeError('KRA service is not enabled') crypto = cryptoutil.NSSCryptoProvider( - paths.HTTPD_ALIAS_DIR, - password_file=paths.ALIAS_PWDFILE_TXT) + paths.IPA_RADB_DIR, + password_file=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt')) # TODO: obtain KRA host & port from IPA service list or point to KRA load balancer # https://fedorahosted.org/freeipa/ticket/4557 diff --git a/ipaserver/plugins/rabase.py b/ipaserver/plugins/rabase.py index 736c16698..8f2c8c388 100644 --- a/ipaserver/plugins/rabase.py +++ b/ipaserver/plugins/rabase.py @@ -44,8 +44,8 @@ class rabase(Backend): self.sec_dir = api.env.dot_ipa + os.sep + 'alias' self.pwd_file = self.sec_dir + os.sep + '.pwd' else: - self.sec_dir = paths.HTTPD_ALIAS_DIR - self.pwd_file = paths.ALIAS_PWDFILE_TXT + self.sec_dir = paths.IPA_RADB_DIR + self.pwd_file = os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt') super(rabase, self).__init__(api) diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py index 2c58eeedb..b2d724d26 100644 --- a/ipaserver/secrets/store.py +++ b/ipaserver/secrets/store.py @@ -46,7 +46,7 @@ def PKI_TOMCAT_password_callback(): def HTTPD_password_callback(): - with open(paths.ALIAS_PWDFILE_TXT) as f: + with open(os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt')) as f: password = f.read() return password @@ -206,7 +206,7 @@ NAME_DB_MAP = { }, 'ra': { 'type': 'NSSDB', - 'path': paths.HTTPD_ALIAS_DIR, + 'path': paths.IPA_RADB_DIR, 'handler': NSSCertDB, 'pwcallback': HTTPD_password_callback, }, diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py index 206e0ef42..0b8277b8a 100644 --- a/ipatests/test_xmlrpc/test_cert_plugin.py +++ b/ipatests/test_xmlrpc/test_cert_plugin.py @@ -70,8 +70,8 @@ def is_db_configured(): # # To test against Dogtag CA in the lite-server: # -# - Copy the 3 NSS db files from /etc/httpd/alias to ~/.ipa/alias -# - Copy /etc/httpd/alias/pwdfile.txt to ~/.ipa/alias/.pwd. +# - Copy the 3 NSS db files from /var/lib/ipa/radb to ~/.ipa/alias +# - Copy /var/lib/ipa/radb/pwdfile.txt to ~/.ipa/alias/.pwd. # - Change ownership of these files to be readable by you. # # The API tested depends on the value of ~/.ipa/default/ra_plugin when |