URL: https://github.com/freeipa/freeipa/pull/694
Author: martbab
 Title: #694: RFC: implement local PKINIT deployment in server/replica install
Action: opened

PR body:
"""
This PR implements a basic local PKINIT functionality for server install with
'--no-pkinit' specified, and replica install against older masters or with
'--no-pkinit'.

These patches unblock WebUI logins/password auths on masters/replicas in the
cases proper PKINIT was not configured for whatever reasons.

Nevertheless, there are following things lacking in this PR that I will either
push on top of this one or create a new PR:

  -[ ] removal of anonymous keytab, asi it is now useless (and always was)
  -[ ] upgrade and transitions between PKINIT configurations
  -[ ] reporting PKINIT state in LDAP
  -[ ] API for querying the PKINIT status on all masters

http://www.freeipa.org/page/V4/Kerberos_PKINIT
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/694/head:pr694
git checkout pr694
From a3ad3a37972c81dec251c5ad7b1c9795d7ce4581 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 14:14:11 +0200
Subject: [PATCH 1/8] Use only anonymous PKINIT to fetch armor ccache

Since the anonymous principal can only use PKINIT to fetch credential
cache it makes no sense to try and use its kerberos key to establish
FAST channel.

We should also be able to use custom PKINIT anchor for the armoring.

https://pagure.io/freeipa/issue/6830
---
 ipalib/install/kinit.py | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py
index 1e4d1a8..fb6caee 100644
--- a/ipalib/install/kinit.py
+++ b/ipalib/install/kinit.py
@@ -7,7 +7,6 @@
 
 import gssapi
 
-from ipalib.constants import ANON_USER
 from ipaplatform.paths import paths
 from ipapython.ipa_log_manager import root_logger
 from ipapython.ipautil import run
@@ -97,29 +96,26 @@ def kinit_password(principal, password, ccache_name, config=None,
         raise RuntimeError(result.error_output)
 
 
-def kinit_armor(ccache_name):
+def kinit_armor(ccache_name, pkinit_anchor=None):
     """
-    perform kinit to obtain anonymous ticket to be used as armor for FAST.
+    perform anonymous pkinit to obtain anonymous ticket to be used as armor
+    for FAST.
+
+    :param ccache_name: location of the armor ccache
+    :param pkinit_anchor: if not None, the location of PKINIT anchor file to
+        use. Otherwise the value from Kerberos client library configuration is
+        used
+
+    :raises: CalledProcessError if the anonymous PKINIT fails
     """
     root_logger.debug("Initializing anonymous ccache")
 
     env = {'LC_ALL': 'C'}
-    # try with the keytab first and then again fallback to try with pkinit in
-    # case someone decided it is fun to remove Anonymous keys from the entry
-    # or in future pkinit enabled principal enforce the use of pkinit
-    try:
-        # Gssapi does not understand anonymous cred use kinit command instead
-        args = [paths.KINIT, '-k', '-t', paths.ANON_KEYTAB,
-                ANON_USER, '-c', ccache_name]
-        run(args, env=env, raiseonerr=True, capture_error=True)
-        return
-    except Exception as e:
-        root_logger.debug("Failed to init Anonymous keytab: %s", e,
-                          exc_info=True)
-
-    root_logger.debug("Fallback to slower Anonymous PKINIT")
     args = [paths.KINIT, '-n', '-c', ccache_name]
 
+    if pkinit_anchor is not None:
+        args.extend(['-X', 'X509_anchors=FILE:{}'.format(pkinit_anchor)])
+
     # this workaround enables us to capture stderr and put it
     # into the raised exception in case of unsuccessful authentication
     run(args, env=env, raiseonerr=True, capture_error=True)

From 4946b1f34dcc50cd46979ed249f308791a5cc397 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 14:44:29 +0200
Subject: [PATCH 2/8] krbinstance: add the ability to record and retrieve
 PKINIT status

An API was added to record the configured PKINIT status in the state
file during KDC configuration. The PKINIT feature can have the following
states:
   * full PKINIT: PKINIT certificate was issued by IPA CA and all
     clients with IPA CA configured as PKINIT trust anchor will be able
     to perform PKINIT and request anonymous TGT from this KDC
   * external PKINIT: the PKINIT certificate was provided by a 3rd party
     in a PKCS#12 bundle and all clients that have its root CA as anchor
     can request TGTs by PKINIT
   * local PKINIT: PKINIT certificate was self-signed by KDC's private
     key. This is a fallback mechanism usable only locally on the master
     hosting the KDC. Its intended use is to provide FAST armoring for
     password authenticated requests (e.g. WebUI logins)

See http://www.freeipa.org/page/V4/Kerberos_PKINIT#Configuration for
more details.

https://pagure.io/freeipa/issue/6830
---
 ipaserver/install/krbinstance.py | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 6c105f7..6f59210 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -28,7 +28,7 @@
 
 import dns.name
 
-from ipaserver.install import service
+from ipaserver.install import service, sysupgrade
 from ipaserver.install import installutils
 from ipapython import ipaldap
 from ipapython import ipautil
@@ -47,6 +47,23 @@
 from ipaplatform.tasks import tasks
 from ipaplatform.paths import paths
 
+PKINIT_STATUS = 'pkinit_status'
+PKINIT_LOCAL = 'local'
+PKINIT_EXTERNAL = 'external'
+PKINIT_FULL = 'full'
+
+
+def set_pkinit_state(state):
+    if state not in (PKINIT_LOCAL, PKINIT_EXTERNAL, PKINIT_FULL):
+        raise ValueError("Invalid PKINIT state: {}".format(state))
+
+    sysupgrade.set_upgrade_state('krb5kdc', PKINIT_STATUS, state)
+
+
+def get_pkinit_state():
+    return sysupgrade.get_upgrade_state('krb5kdc', PKINIT_STATUS)
+
+
 class KpasswdInstance(service.SimpleServiceInstance):
     def __init__(self):
         service.SimpleServiceInstance.__init__(self, "kadmin")

From 6bff23374ff5f85a68178f64e62a81a082f66c73 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 14:30:06 +0200
Subject: [PATCH 3/8] certmonger: Add a custom helper for PKINIT certificate
 issuance

This helper can either forward the signing of PKINIT CSR to IPA CA
helper if full PKINIT is required, or it can selfsign the CSR to have
local PKINIT working. The decision is made on the pkinit status written
to the state file.

https://pagure.io/freeipa/issue/6830
---
 freeipa.spec.in                      |   1 +
 install/certmonger/Makefile.am       |   1 +
 install/certmonger/ipa-pkinit-submit | 129 +++++++++++++++++++++++++++++++++++
 ipaplatform/base/paths.py            |   2 +
 4 files changed, 133 insertions(+)
 create mode 100644 install/certmonger/ipa-pkinit-submit

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 829c3f0..a83e141 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -1158,6 +1158,7 @@ fi
 %{_sbindir}/ipa-cacert-manage
 %{_sbindir}/ipa-winsync-migrate
 %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
+%{_libexecdir}/certmonger/ipa-pkinit-submit
 %{_libexecdir}/certmonger/ipa-server-guard
 %dir %{_libexecdir}/ipa
 %{_libexecdir}/ipa/ipa-custodia
diff --git a/install/certmonger/Makefile.am b/install/certmonger/Makefile.am
index 63fd577..bcaf2ef 100644
--- a/install/certmonger/Makefile.am
+++ b/install/certmonger/Makefile.am
@@ -3,6 +3,7 @@ NULL =
 appdir = $(libexecdir)/certmonger/
 app_SCRIPTS =					\
 	dogtag-ipa-ca-renew-agent-submit	\
+	ipa-pkinit-submit			\
 	ipa-server-guard			\
 	$(NULL)
 
diff --git a/install/certmonger/ipa-pkinit-submit b/install/certmonger/ipa-pkinit-submit
new file mode 100644
index 0000000..c9fe787
--- /dev/null
+++ b/install/certmonger/ipa-pkinit-submit
@@ -0,0 +1,129 @@
+#!/usr/bin/python2 -E
+#
+# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
+#
+from __future__ import print_function
+import os
+# Prevent garbage from readline on standard output
+# (see https://fedorahosted.org/freeipa/ticket/4064)
+if not os.isatty(1):
+    os.environ['TERM'] = 'dumb'
+from random import randint
+import sys
+import syslog
+import traceback
+
+from OpenSSL import crypto
+
+import six
+
+from ipapython import ipautil
+from ipaplatform.paths import paths
+from ipaserver.install import krbinstance
+
+# Return codes. Names of the constants are taken from
+# https://git.fedorahosted.org/cgit/certmonger.git/tree/src/submit-e.h
+ISSUED = 0
+UNREACHABLE = 3
+UNCONFIGURED = 4
+
+if six.PY3:
+    unicode = str
+
+
+IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
+KDC_EXT_USAGE_OIDS = "1.3.6.1.5.5.7.3.1,1.3.6.1.5.2.3.5"
+
+
+def request_pkinit_cert():
+    """
+    Try to request PKINIT certificate from the IPA CA.
+    """
+    helper = paths.IPA_SUBMIT
+    syslog.syslog(syslog.LOG_NOTICE, "Forwarding request to {}".format(helper))
+
+    args = [paths.IPA_SUBMIT]
+    args.extend(sys.argv[1:])
+
+    result = ipautil.run(args, raiseonerr=False, env=os.environ,
+                         capture_output=True)
+    rc = result.returncode
+
+    if six.PY2:
+        sys.stderr.write(result.raw_error_output)
+        sys.stdout.write(result.raw_output)
+    else:
+        # Write bytes directly
+        sys.stderr.buffer.write(  # pylint: disable=no-member
+            result.raw_error_output)
+        sys.stdout.buffer.write(  # pylint: disable=no-member
+            result.raw_output)
+
+    sys.stderr.flush()
+    sys.stdout.flush()
+
+    syslog.syslog(syslog.LOG_NOTICE,
+                  "{} returned {}".format(helper, rc))
+
+    return rc
+
+
+def self_sign_pkinit_cert():
+    """
+    Self-sign KDC certificate as a fallback mechanism when IPA CA is not
+    configured or rejects the CSR (e.g. on older IPA version)
+    """
+    cert_validity = 3600 * 24 * 365 * 2
+
+    with open(paths.KDC_KEY, 'rb') as kdc_key_file:
+        pkinit_pkey = crypto.load_privatekey(
+            crypto.FILETYPE_PEM, kdc_key_file.read())
+
+    pkinit_csr = crypto.load_certificate_request(
+        crypto.FILETYPE_PEM, os.environ.get('CERTMONGER_CSR'))
+
+    subject = pkinit_csr.get_subject()
+    pkinit_cert = crypto.X509()
+    pkinit_cert.set_version(2)
+    pkinit_cert.set_serial_number(randint(0, 65535))
+    pkinit_cert.set_issuer(subject)
+    pkinit_cert.set_subject(subject)
+    pkinit_cert.add_extensions(pkinit_csr.get_extensions())
+    pkinit_cert.set_pubkey(pkinit_csr.get_pubkey())
+
+    pkinit_cert.gmtime_adj_notBefore(0)
+    pkinit_cert.gmtime_adj_notAfter(cert_validity)
+
+    pkinit_cert.sign(pkinit_pkey, "sha256")
+    pkinit_cert_pem = crypto.dump_certificate(crypto.FILETYPE_PEM,
+                                              pkinit_cert)
+
+    print(pkinit_cert_pem)
+
+    return ISSUED
+
+
+def main():
+    # make sure that the correct extended usage is always used
+    os.environ['EKU'] = KDC_EXT_USAGE_OIDS
+
+    pkinit_state = krbinstance.get_pkinit_state()
+
+    if pkinit_state == krbinstance.PKINIT_LOCAL:
+        handler = self_sign_pkinit_cert
+    elif pkinit_state == krbinstance.PKINIT_FULL:
+        handler = request_pkinit_cert
+    else:
+        syslog.syslog(
+            syslog.LOG_ERR,
+            'Unrecognized pkinit state: {}'.format(pkinit_state))
+        return UNCONFIGURED
+
+    return handler()
+
+
+try:
+    sys.exit(main())
+except Exception as e:
+    syslog.syslog(syslog.LOG_ERR, traceback.format_exc())
+    sys.exit(UNREACHABLE)
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 9cf160f..09bee4b 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -197,7 +197,9 @@ class BasePathNamespace(object):
     DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-ca-renew-agent-submit"
     DOGTAG_IPA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-renew-agent-submit"
     CERTMONGER_DOGTAG_SUBMIT = "/usr/libexec/certmonger/dogtag-submit"
+    IPA_PKINIT_SUBMIT = "/usr/libexec/certmonger/ipa-pkinit-submit"
     IPA_SERVER_GUARD = "/usr/libexec/certmonger/ipa-server-guard"
+    IPA_SUBMIT = "/usr/libexec/certmonger/ipa-submit"
     GENERATE_RNDC_KEY = "/usr/libexec/generate-rndc-key.sh"
     IPA_DNSKEYSYNCD_REPLICA = "/usr/libexec/ipa/ipa-dnskeysync-replica"
     IPA_DNSKEYSYNCD = "/usr/libexec/ipa/ipa-dnskeysyncd"

From 9ed96e9c67510b7a5a4ef47d523783810f1e5b05 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 14:45:38 +0200
Subject: [PATCH 4/8] Configure custom CA for PKINIT certificate issuance

This is to reduce the complexity of PKINIT setup code in the installer.
The CA can forward the request to IPA CA or self-sign the CSR based on
the desired PKINIT status stored in state file.

https://pagure.io/freeipa/issue/6830
---
 ipalib/install/certmonger.py     | 21 +++++++++++++++++++++
 ipaserver/install/krbinstance.py |  7 +++++++
 2 files changed, 28 insertions(+)

diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
index 2a7876e..845dfe8 100644
--- a/ipalib/install/certmonger.py
+++ b/ipalib/install/certmonger.py
@@ -529,6 +529,27 @@ def _find_IPA_ca():
     return _cm_dbus_object(cm.bus, cm, ca_path, DBUS_CM_CA_IF, DBUS_CM_IF, True)
 
 
+def add_ca(nickname, helper):
+    """
+    Add a new CA to certmonger
+    :param nickname: new CA nickname
+    :param helper: location of the CA helper executable
+
+    :raises: ValueError if the CA with specified nickname is already configured
+    """
+    cm = _certmonger()
+    ca_path = cm.obj_if.find_ca_by_nickname(nickname)
+
+    if ca_path:
+        raise ValueError(
+            "CA with nickname '{}' is already configured".format(nickname))
+
+    cm.obj_if.add_known_ca(
+        nickname,
+        helper,
+        dbus.Array([], dbus.Signature('s')))
+
+
 def add_principal_to_cas(principal):
     """
     If the hostname we were passed to use in ipa-client-install doesn't
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 6f59210..8e712f6 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -47,6 +47,7 @@
 from ipaplatform.tasks import tasks
 from ipaplatform.paths import paths
 
+PKINIT_CA_NAME = 'ipa-pkinit-submit'
 PKINIT_STATUS = 'pkinit_status'
 PKINIT_LOCAL = 'local'
 PKINIT_EXTERNAL = 'external'
@@ -372,6 +373,12 @@ def _wait_for_replica_kdc_entry(self):
             replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60)
 
     def setup_pkinit(self):
+        try:
+            root_logger.debug("Adding '%s' CA to certmonger", PKINIT_CA_NAME)
+            certmonger.add_ca(PKINIT_CA_NAME, paths.IPA_PKINIT_SUBMIT)
+        except ValueError:
+            root_logger.debug("'%s' CA already configured", PKINIT_CA_NAME)
+
         if self.pkcs12_info:
             certs.install_pem_from_p12(self.pkcs12_info[0],
                                        self.pkcs12_info[1],

From a4c7a53162a1a94da89a843c644215cbf048328b Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 15:06:46 +0200
Subject: [PATCH 5/8] Allow for configuration of all three PKINIT variants when
 deploying KDC

The PKINIT setup code now can configure PKINIT using IPA CA signed
certificate, 3rd party certificate and local PKINIT with self-signed
keypair. The local PKINIT is also selected as a fallback mechanism if
the CSR is rejected by CA master. The exact nature of the configuration
is written into the state file and the future upgrade logic can consult
it for action.

http://www.freeipa.org/page/V4/Kerberos_PKINIT
https://pagure.io/freeipa/issue/6830
---
 ipaserver/install/krbinstance.py | 130 +++++++++++++++++++++++++--------------
 1 file changed, 84 insertions(+), 46 deletions(-)

diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 8e712f6..07600b6 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -38,6 +38,7 @@
 from ipalib.install import certmonger
 from ipapython.ipa_log_manager import root_logger
 from ipapython.dn import DN
+from ipapython.dogtag import KDC_PROFILE
 
 from ipaserver.install import replication
 from ipaserver.install import ldapupdate
@@ -372,6 +373,79 @@ def _wait_for_replica_kdc_entry(self):
             remote_ldap.gssapi_bind()
             replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60)
 
+    def _call_certmonger(self):
+        subject = str(DN(('cn', self.fqdn), self.subject_base))
+        krbtgt = "krbtgt/" + self.realm + "@" + self.realm
+        certpath = (paths.KDC_CERT, paths.KDC_KEY)
+        try:
+            prev_helper = None
+            # on the first CA-ful master without '--no-pkinit', we issue the
+            # certificate by contacting Dogtag directly
+            use_dogtag_submit = all(
+                [self.master_fqdn is None,
+                 self.pkcs12_info is None,
+                 self.config_pkinit])
+
+            if use_dogtag_submit:
+                ca_args = [
+                    paths.CERTMONGER_DOGTAG_SUBMIT,
+                    '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
+                    '--certfile', paths.RA_AGENT_PEM,
+                    '--keyfile', paths.RA_AGENT_KEY,
+                    '--cafile', paths.IPA_CA_CRT,
+                    '--agent-submit'
+                ]
+                helper = " ".join(ca_args)
+                prev_helper = certmonger.modify_ca_helper(
+                    PKINIT_CA_NAME, helper)
+
+            certmonger.request_and_wait_for_cert(
+                certpath,
+                subject,
+                krbtgt,
+                ca=PKINIT_CA_NAME,
+                dns=self.fqdn,
+                storage='FILE',
+                profile=KDC_PROFILE)
+        except dbus.DBusException as e:
+            # if the certificate is already tracked, ignore the error
+            name = e.get_dbus_name()
+            if name != 'org.fedorahosted.certmonger.duplicate':
+                root_logger.error("Failed to initiate the request: %s", e)
+            return
+        finally:
+            if prev_helper is not None:
+                certmonger.modify_ca_helper(PKINIT_CA_NAME, prev_helper)
+
+    def setup_local_pkinit(self):
+        set_pkinit_state(PKINIT_LOCAL)
+        self._call_certmonger()
+        # for self-signed certificate, the certificate is its own CA, copy it
+        # as CA cert
+        shutil.copyfile(paths.KDC_CERT, paths.CACERT_PEM)
+
+    def setup_full_pkinit(self):
+        try:
+            set_pkinit_state(PKINIT_FULL)
+            self._call_certmonger()
+            shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM)
+        except RuntimeError as e:
+            root_logger.error("PKINIT certificate request failed: %s", e)
+            root_logger.error("Falling back to local PKINIT with self-signed "
+                              "certificate")
+            self.stop_tracking_certs()
+            self.setup_local_pkinit()
+
+    def setup_external_pkinit(self):
+        set_pkinit_state(PKINIT_EXTERNAL)
+        certs.install_pem_from_p12(self.pkcs12_info[0],
+                                   self.pkcs12_info[1],
+                                   paths.KDC_CERT)
+        certs.install_key_from_p12(self.pkcs12_info[0],
+                                   self.pkcs12_info[1],
+                                   paths.KDC_KEY)
+        shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM)
+
     def setup_pkinit(self):
         try:
             root_logger.debug("Adding '%s' CA to certmonger", PKINIT_CA_NAME)
@@ -379,54 +453,15 @@ def setup_pkinit(self):
         except ValueError:
             root_logger.debug("'%s' CA already configured", PKINIT_CA_NAME)
 
+        if self.master_fqdn is not None:
+            self._wait_for_replica_kdc_entry()
+
         if self.pkcs12_info:
-            certs.install_pem_from_p12(self.pkcs12_info[0],
-                                       self.pkcs12_info[1],
-                                       paths.KDC_CERT)
-            certs.install_key_from_p12(self.pkcs12_info[0],
-                                       self.pkcs12_info[1],
-                                       paths.KDC_KEY)
+            self.setup_external_pkinit()
+        elif self.config_pkinit:
+            self.setup_full_pkinit()
         else:
-            subject = str(DN(('cn', self.fqdn), self.subject_base))
-            krbtgt = "krbtgt/" + self.realm + "@" + self.realm
-            certpath = (paths.KDC_CERT, paths.KDC_KEY)
-
-            try:
-                prev_helper = None
-                if self.master_fqdn is None:
-                    ca_args = [
-                        paths.CERTMONGER_DOGTAG_SUBMIT,
-                        '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
-                        '--certfile', paths.RA_AGENT_PEM,
-                        '--keyfile', paths.RA_AGENT_KEY,
-                        '--cafile', paths.IPA_CA_CRT,
-                        '--agent-submit'
-                    ]
-                    helper = " ".join(ca_args)
-                    prev_helper = certmonger.modify_ca_helper('IPA', helper)
-                else:
-                    self._wait_for_replica_kdc_entry()
-
-                certmonger.request_and_wait_for_cert(
-                    certpath,
-                    subject,
-                    krbtgt,
-                    dns=self.fqdn,
-                    storage='FILE',
-                    profile='KDCs_PKINIT_Certs')
-            except dbus.DBusException as e:
-                # if the certificate is already tracked, ignore the error
-                name = e.get_dbus_name()
-                if name != 'org.fedorahosted.certmonger.duplicate':
-                    root_logger.error("Failed to initiate the request: %s", e)
-                return
-            finally:
-                if prev_helper is not None:
-                    certmonger.modify_ca_helper('IPA', prev_helper)
-
-        # Finally copy the cacert in the krb directory so we don't
-        # have any selinux issues with the file context
-        shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM)
+            self.setup_local_pkinit()
 
         try:
             self.restart()
@@ -449,6 +484,9 @@ def enable_ssl(self):
             self.step("testing anonymous PKINIT", self.test_anonymous_pkinit)
 
             self.start_creation()
+        else:
+            self.setup_pkinit()
+            self.test_anonymous_pkinit()
 
     def get_anonymous_principal_name(self):
         return "%s@%s" % (ANON_USER, self.realm)

From 49ce056581728919fe69f25848b04493e86852d6 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Wed, 5 Apr 2017 15:59:24 +0200
Subject: [PATCH 6/8] test out anonymous PKINIT only when the master is fully
 configured

this is to ensure that we are talking only to our own KDC and not to KDC
on remote master that may not support (local) PKINIT functionality and
cause errors.

https://pagure.io/freeipa/issue/6817
---
 ipaserver/install/krbinstance.py           | 3 ---
 ipaserver/install/server/install.py        | 4 ++++
 ipaserver/install/server/replicainstall.py | 4 ++++
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 07600b6..ba3722d 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -481,12 +481,9 @@ def enable_ssl(self):
             self.steps = []
             self.step("installing X509 Certificate for PKINIT",
                       self.setup_pkinit)
-            self.step("testing anonymous PKINIT", self.test_anonymous_pkinit)
-
             self.start_creation()
         else:
             self.setup_pkinit()
-            self.test_anonymous_pkinit()
 
     def get_anonymous_principal_name(self):
         return "%s@%s" % (ANON_USER, self.realm)
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index d7eb0bf..2228d4d 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -892,6 +892,10 @@ def install(installer):
     # Make sure the files we crated in /var/run are recreated at startup
     tasks.configure_tmpfiles()
 
+    if not options.no_pkinit:
+        print("Testing anonymous PKINIT")
+    krb.test_anonymous_pkinit()
+
     # Everything installed properly, activate ipa service.
     services.knownservices.ipa.enable()
 
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index f489e69..03c2b58 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -1525,6 +1525,10 @@ def install(installer):
         # remove the extracted replica file
         remove_replica_info_dir(installer)
 
+    if not options.no_pkinit:
+        print("Testing anonymous PKINIT")
+    krb.test_anonymous_pkinit()
+
     # Everything installed properly, activate ipa service.
     services.knownservices.ipa.enable()
 

From 7f421244938182ad88924046da0918198e294e80 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 15:15:28 +0200
Subject: [PATCH 7/8] Use local anchor when testing out PKINIT configuration

The PKINIT configuration can use a different anchor than the one
specified in the Kerberos config file. We should always use KDC's anchor
on master to avoid false positive errors.

https://pagure.io/freeipa/issue/6830
---
 ipaserver/install/krbinstance.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index ba3722d..43908d6 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -36,6 +36,7 @@
 from ipalib import api, errors
 from ipalib.constants import ANON_USER
 from ipalib.install import certmonger
+from ipalib.install.kinit import kinit_armor
 from ipapython.ipa_log_manager import root_logger
 from ipapython.dn import DN
 from ipapython.dogtag import KDC_PROFILE
@@ -472,9 +473,10 @@ def setup_pkinit(self):
     def test_anonymous_pkinit(self):
         with ipautil.private_ccache() as anon_ccache:
             try:
-                ipautil.run([paths.KINIT, '-n', '-c', anon_ccache])
-            except ipautil.CalledProcessError:
-                raise RuntimeError("Failed to configure anonymous PKINIT")
+                kinit_armor(anon_ccache, pkinit_anchor=paths.CACERT_PEM)
+            except ipautil.CalledProcessError as e:
+                raise RuntimeError(
+                    "Failed to configure anonymous PKINIT: {}".format(e))
 
     def enable_ssl(self):
         if self.config_pkinit:

From 79c4efbc1287980213cfc06187f06f519d5fb556 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 31 Mar 2017 15:15:50 +0200
Subject: [PATCH 8/8] Use local anchor when armoring password requests

https://pagure.io/freeipa/issue/6830
---
 ipaserver/rpcserver.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 77ed7e1..1618724 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -944,7 +944,7 @@ def kinit(self, principal, password, ccache_name):
         self.debug('Obtaining armor in ccache %s', armor_path)
 
         try:
-            kinit_armor(armor_path)
+            kinit_armor(armor_path, pkinit_anchor=paths.CACERT_PEM)
         except RuntimeError as e:
             self.error("Failed to obtain armor cache")
             # We try to continue w/o armor, 2FA will be impacted
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to