From 483b06d68cbb3facb2f1fcff4d857d3e3233a2e0 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 30 2017 13:45:50 +0000 Subject: import ipa-4.5.0-22.el7_4 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a43c0f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +SOURCES/freeipa-4.5.0.tar.gz +SOURCES/header-logo.png +SOURCES/login-screen-background.jpg +SOURCES/login-screen-logo.png +SOURCES/product-name.png diff --git a/.ipa.metadata b/.ipa.metadata new file mode 100644 index 0000000..61ded07 --- /dev/null +++ b/.ipa.metadata @@ -0,0 +1,5 @@ +686e9b1375659524de83e1b78df66b355715438e SOURCES/freeipa-4.5.0.tar.gz +77c318cf1f4fc25cf847de0692a77859a767c0e3 SOURCES/header-logo.png +8727245558422bf966d60677568925f081b8e299 SOURCES/login-screen-background.jpg +24a29d79efbd0906777be4639957abda111fca4b SOURCES/login-screen-logo.png +af82b7b7d327bd683c7d062a6f15713ea91ebedf SOURCES/product-name.png diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/0001-Add-options-to-allow-ticket-caching.patch b/SOURCES/0001-Add-options-to-allow-ticket-caching.patch new file mode 100644 index 0000000..1c3b8e0 --- /dev/null +++ b/SOURCES/0001-Add-options-to-allow-ticket-caching.patch @@ -0,0 +1,39 @@ +From 6c4d53f843575d5e69a0c310cdb2e5026751faa4 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Mon, 6 Mar 2017 13:46:44 -0500 +Subject: [PATCH] Add options to allow ticket caching + +This new option (planned to land in gssproxy 0.7) we cache the ldap +ticket properly and avoid a ticket lookup to the KDC on each and every +ldap connection. (Also requires krb5 libs 1.15.1 to benefit from caching). + +Ticket: https://pagure.io/freeipa/issue/6771 + +Signed-off-by: Simo Sorce +Reviewed-By: Martin Babinsky +--- + install/share/gssproxy.conf.template | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/install/share/gssproxy.conf.template b/install/share/gssproxy.conf.template +index fbb158a689a430168ea9841d59cb558755371968..9d111009f5a5ba24dd474be336bf0cb27ab59aab 100644 +--- a/install/share/gssproxy.conf.template ++++ b/install/share/gssproxy.conf.template +@@ -4,6 +4,7 @@ + cred_store = keytab:$HTTP_KEYTAB + cred_store = client_keytab:$HTTP_KEYTAB + allow_protocol_transition = true ++ allow_client_ccache_sync = true + cred_usage = both + euid = $HTTPD_USER + +@@ -12,5 +13,6 @@ + cred_store = keytab:$HTTP_KEYTAB + cred_store = client_keytab:$HTTP_KEYTAB + allow_constrained_delegation = true ++ allow_client_ccache_sync = true + cred_usage = initiate + euid = $IPAAPI_USER +-- +2.12.0 + diff --git a/SOURCES/0002-Use-connection-keep-alive.patch b/SOURCES/0002-Use-connection-keep-alive.patch new file mode 100644 index 0000000..c7320ea --- /dev/null +++ b/SOURCES/0002-Use-connection-keep-alive.patch @@ -0,0 +1,35 @@ +From 1216aaa3c5f5e3dc3a81de81633eaade15df1129 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Mon, 20 Mar 2017 08:47:41 +0100 +Subject: [PATCH] Use connection keep-alive + +Do not forcefully close the connection after every request. This enables +HTTP connection keep-alive, also known as persistent TCP and TLS/SSL +connection. Keep-alive speed up consecutive HTTP requests by 15% (for +local, low-latency network connections to a fast server) to multiple +times (high latency connections or remote peers). + +https://pagure.io/freeipa/issue/6641 + +Signed-off-by: Christian Heimes +Reviewed-By: Tomas Krizek +--- + ipalib/rpc.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index 16ffb8b541107e3ce1a84d143db007a1105b49b5..8d587180a65bd06b644c6df23ac9fb26eb7e97dd 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -686,7 +686,7 @@ class KerbTransport(SSLTransport): + return self.parse_response(response) + except gssapi.exceptions.GSSError as e: + self._handle_exception(e) +- finally: ++ except BaseException: + self.close() + + if six.PY3: +-- +2.12.1 + diff --git a/SOURCES/0003-Add-debug-logging-for-keep-alive.patch b/SOURCES/0003-Add-debug-logging-for-keep-alive.patch new file mode 100644 index 0000000..4fc4f1d --- /dev/null +++ b/SOURCES/0003-Add-debug-logging-for-keep-alive.patch @@ -0,0 +1,68 @@ +From bdaf584ef5ebcae08e86142ceb80ebe56ac11fa3 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Mon, 20 Mar 2017 08:47:51 +0100 +Subject: [PATCH] Add debug logging for keep-alive + +Signed-off-by: Christian Heimes +Reviewed-By: Tomas Krizek +--- + ipalib/rpc.py | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index 8d587180a65bd06b644c6df23ac9fb26eb7e97dd..38321d17cf2c9529738aa45cc44bbd38b08b032b 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -79,6 +79,13 @@ except ImportError: + from xmlrpc.client import (Binary, Fault, DateTime, dumps, loads, ServerProxy, + Transport, ProtocolError, MININT, MAXINT) + ++# pylint: disable=import-error ++if six.PY3: ++ from http.client import RemoteDisconnected ++else: ++ from httplib import BadStatusLine as RemoteDisconnected ++# pylint: enable=import-error ++ + + if six.PY3: + unicode = str +@@ -531,6 +538,7 @@ class SSLTransport(LanguageAwareTransport): + host, self._extra_headers, _x509 = self.get_host_info(host) + + if self._connection and host == self._connection[0]: ++ root_logger.debug("HTTP connection keep-alive (%s)", host) + return self._connection[1] + + conn = create_https_connection( +@@ -540,6 +548,7 @@ class SSLTransport(LanguageAwareTransport): + tls_version_max=api.env.tls_version_max) + + conn.connect() ++ root_logger.debug("New HTTP connection (%s)", host) + + self._connection = host, conn + return self._connection[1] +@@ -686,8 +695,18 @@ class KerbTransport(SSLTransport): + return self.parse_response(response) + except gssapi.exceptions.GSSError as e: + self._handle_exception(e) +- except BaseException: ++ except RemoteDisconnected: ++ # keep-alive connection was terminated by remote peer, close ++ # connection and let transport handle reconnect for us. ++ self.close() ++ root_logger.debug("HTTP server has closed connection (%s)", host) ++ raise ++ except BaseException as e: ++ # Unexpected exception may leave connections in a bad state. + self.close() ++ root_logger.debug("HTTP connection destroyed (%s)", ++ host, exc_info=True) ++ raise + + if six.PY3: + def __send_request(self, connection, host, handler, request_body, debug): +-- +2.12.1 + diff --git a/SOURCES/0004-Increase-Apache-HTTPD-s-default-keep-alive-timeout.patch b/SOURCES/0004-Increase-Apache-HTTPD-s-default-keep-alive-timeout.patch new file mode 100644 index 0000000..4750bad --- /dev/null +++ b/SOURCES/0004-Increase-Apache-HTTPD-s-default-keep-alive-timeout.patch @@ -0,0 +1,41 @@ +From 2905ebdb1a6c668da1a12d79824c6710e3f0eb94 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Mon, 20 Mar 2017 08:47:56 +0100 +Subject: [PATCH] Increase Apache HTTPD's default keep alive timeout + +Apache has a default keep alive timeout of 5 seconds. That's too low for +interactive commands, e.g. password prompts. 30 seconds sounds like a +good compromise. + +Signed-off-by: Christian Heimes +Reviewed-By: Tomas Krizek +--- + install/conf/ipa.conf | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf +index 164231c729a8b3d64982ea0d9592e949635d7418..e1f1a581b4e8a91b899bcf165ca81f266fa9e516 100644 +--- a/install/conf/ipa.conf ++++ b/install/conf/ipa.conf +@@ -1,5 +1,5 @@ + # +-# VERSION 24 - DO NOT REMOVE THIS LINE ++# VERSION 25 - DO NOT REMOVE THIS LINE + # + # This file may be overwritten on upgrades. + # +@@ -20,6 +20,11 @@ DirectoryIndex index.html + # requests, ticket #2767. This should easily support a 64KiB PAC. + LimitRequestFieldSize 100000 + ++# Increase connection keep alive time. Default value is 5 seconds, which is too ++# short for interactive ipa commands. 30 seconds is a good compromise. ++KeepAlive On ++KeepAliveTimeout 30 ++ + # ipa-rewrite.conf is loaded separately + + # This is required so the auto-configuration works with Firefox 2+ +-- +2.12.1 + diff --git a/SOURCES/0005-ipapython.ipautil.nolog_replace-Do-not-replace-empty.patch b/SOURCES/0005-ipapython.ipautil.nolog_replace-Do-not-replace-empty.patch new file mode 100644 index 0000000..188c635 --- /dev/null +++ b/SOURCES/0005-ipapython.ipautil.nolog_replace-Do-not-replace-empty.patch @@ -0,0 +1,32 @@ +From fef78a011c148f63a08014bbe7ed2d63fe3380bd Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Mon, 20 Mar 2017 12:48:14 +0100 +Subject: [PATCH] ipapython.ipautil.nolog_replace: Do not replace empty value + +When provided empty value in nolog parameter nolog_replace added 'XXXXXXXX' +three (once for plain value, once for http quoted value and last time for shell +quoted value) times before every character (including terminating '\0') in the string. + +https://pagure.io/freeipa/issue/6738 + +Reviewed-By: Pavel Vomacka +--- + ipapython/ipautil.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 60b4a37fe247624e826d0f6516cb9a25d30ae75d..cd66328e6c9a0f69e6f83582a9d288ac239c5be3 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -505,7 +505,7 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, + def nolog_replace(string, nolog): + """Replace occurences of strings given in `nolog` with XXXXXXXX""" + for value in nolog: +- if not isinstance(value, six.string_types): ++ if not value or not isinstance(value, six.string_types): + continue + + quoted = urllib.parse.quote(value) +-- +2.12.1 + diff --git a/SOURCES/0006-tasks-run-systemctl-daemon-reload-after-httpd.servic.patch b/SOURCES/0006-tasks-run-systemctl-daemon-reload-after-httpd.servic.patch new file mode 100644 index 0000000..15450ff --- /dev/null +++ b/SOURCES/0006-tasks-run-systemctl-daemon-reload-after-httpd.servic.patch @@ -0,0 +1,49 @@ +From d8a9ed4e2fc164962d76773b57277f97bca84270 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 16 Mar 2017 12:51:29 +0000 +Subject: [PATCH] tasks: run `systemctl daemon-reload` after httpd.service.d + updates + +Run `systemctl daemon-reload` after +`/etc/systemd/system/httpd.service.d/ipa.conf` is created or deleted, +otherwise systemd will not merge the file into httpd.service and therefore +required environment variables will not be set for httpd. + +This fixes authentication failures ("No valid Negotiate header in server +response") due to missing `GSS_USE_PROXY=yes` in httpd environment. + +https://pagure.io/freeipa/issue/6773 + +Reviewed-By: Martin Babinsky +--- + ipaplatform/redhat/tasks.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py +index c1b574e06fc52839b684cbe96587365fa107b2eb..d0ef5fbd1ceb8110dd417dda44a74dc63898456a 100644 +--- a/ipaplatform/redhat/tasks.py ++++ b/ipaplatform/redhat/tasks.py +@@ -483,6 +483,9 @@ class RedHatTaskNamespace(BaseTaskNamespace): + os.chmod(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, 0o644) + self.restore_context(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) + ++ ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"], ++ raiseonerr=False) ++ + def configure_http_gssproxy_conf(self): + ipautil.copy_template_file( + os.path.join(paths.USR_SHARE_IPA_DIR, 'gssproxy.conf.template'), +@@ -513,6 +516,10 @@ class RedHatTaskNamespace(BaseTaskNamespace): + 'Error removing %s: %s', + paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e + ) ++ return ++ ++ ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"], ++ raiseonerr=False) + + def set_hostname(self, hostname): + ipautil.run([paths.BIN_HOSTNAMECTL, 'set-hostname', hostname]) +-- +2.12.1 + diff --git a/SOURCES/0007-man-ipa-cacert-manage-install-needs-clarification.patch b/SOURCES/0007-man-ipa-cacert-manage-install-needs-clarification.patch new file mode 100644 index 0000000..def7c31 --- /dev/null +++ b/SOURCES/0007-man-ipa-cacert-manage-install-needs-clarification.patch @@ -0,0 +1,31 @@ +From 9d5e8f44210f661850ec67f92909534dd52c2ee8 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 22 Mar 2017 08:49:39 +0100 +Subject: [PATCH] man ipa-cacert-manage install needs clarification + +The customers are often confused by ipa-cacert-manage install. The man page +should make it clear that IPA CA is not modified in any way by this command. + +https://pagure.io/freeipa/issue/6795 + +Reviewed-By: Tomas Krizek +--- + install/tools/man/ipa-cacert-manage.1 | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1 +index 4515d7c404054139725fd47f366706cb1e222be5..128edd8bd2500a09f406da8dc01a53b269007ab0 100644 +--- a/install/tools/man/ipa-cacert-manage.1 ++++ b/install/tools/man/ipa-cacert-manage.1 +@@ -46,6 +46,8 @@ When the IPA CA is not configured, this command is not available. + .RS + This command can be used to install the certificate contained in \fICERTFILE\fR as an additional CA certificate to IPA. + .sp ++Important: this does not replace IPA CA but adds the provided certificate as a known CA. This is useful for instance when using ipa-server-certinstall to replace HTTP/LDAP certificates with third-party certificates signed by this additional CA. ++.sp + Please do not forget to run ipa-certupdate on the master, all the replicas and all the clients after this command in order to update IPA certificates databases. + .RE + .SH "COMMON OPTIONS" +-- +2.12.1 + diff --git a/SOURCES/0008-certs-do-not-implicitly-create-DS-pin.txt.patch b/SOURCES/0008-certs-do-not-implicitly-create-DS-pin.txt.patch new file mode 100644 index 0000000..caea419 --- /dev/null +++ b/SOURCES/0008-certs-do-not-implicitly-create-DS-pin.txt.patch @@ -0,0 +1,48 @@ +From 846b1c9b72f539cbe4b8d6e23de81e03b1afec9e Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 14 Mar 2017 09:32:17 +0100 +Subject: [PATCH] certs: do not implicitly create DS pin.txt + +Do not implicitly create DS pin.txt in `CertDB.init_from_pkcs12()`, create +it explicitly in `DSInstance.__enable_ssl()`. + +This stops the file from being created in /etc/httpd/alias during classic +replica install. + +https://pagure.io/freeipa/issue/4639 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/certs.py | 1 - + ipaserver/install/dsinstance.py | 3 ++- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 63e7887c4e73a8346d4eb5d865ddc89c07247573..9f340b8678c55cffe2872df97c643c34857cfaa9 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -635,7 +635,6 @@ class CertDB(object): + self.cacert_name = ca_names[-1] + self.trust_root_cert(self.cacert_name, trust_flags) + +- self.create_pin_file() + self.export_ca_cert(nickname, False) + + def publish_ca_cert(self, location): +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 91cc180e62b9532e716c07c493b359567b20c749..79dc90e92cac49a2b64ff6645f75dc3a8cbcc104 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -838,7 +838,8 @@ class DsInstance(service.Service): + certmonger.modify_ca_helper('IPA', prev_helper) + + self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False) +- dsdb.create_pin_file() ++ ++ dsdb.create_pin_file() + + self.cacert_name = dsdb.cacert_name + +-- +2.12.1 + diff --git a/SOURCES/0009-httpinstance-clean-up-etc-httpd-alias-on-uninstall.patch b/SOURCES/0009-httpinstance-clean-up-etc-httpd-alias-on-uninstall.patch new file mode 100644 index 0000000..c26e495 --- /dev/null +++ b/SOURCES/0009-httpinstance-clean-up-etc-httpd-alias-on-uninstall.patch @@ -0,0 +1,74 @@ +From 10e74165a827377ed3318d4d2b974fdbf0fab9db Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 8 Mar 2017 14:24:15 +0000 +Subject: [PATCH] httpinstance: clean up /etc/httpd/alias on uninstall + +Restore cert8.db, key3.db, pwdfile.txt and secmod.db in /etc/httpd/alias +from backup on uninstall. + +Files modified by IPA are kept with .ipasave suffix. + +https://pagure.io/freeipa/issue/4639 + +Reviewed-By: Martin Babinsky +--- + ipapython/certdb.py | 13 +++++++++++++ + ipaserver/install/certs.py | 3 +++ + ipaserver/install/httpinstance.py | 3 +++ + 3 files changed, 19 insertions(+) + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 6c89e778068d9ed1e9939077f7114463776e3516..f1410e5ae4290263573e9554ab4e66873d4344a1 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -169,6 +169,19 @@ class NSSDatabase(object): + new_mode = filemode + os.chmod(path, new_mode) + ++ def restore(self): ++ for filename in NSS_FILES: ++ path = os.path.join(self.secdir, filename) ++ backup_path = path + '.orig' ++ save_path = path + '.ipasave' ++ try: ++ if os.path.exists(path): ++ os.rename(path, save_path) ++ if os.path.exists(backup_path): ++ os.rename(backup_path, path) ++ except OSError as e: ++ root_logger.debug(e) ++ + def list_certs(self): + """Return nicknames and cert flags for all certs in the database + +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 9f340b8678c55cffe2872df97c643c34857cfaa9..0ca971358030db6a6e7e410e58a984675bcf53ac 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -234,6 +234,9 @@ class CertDB(object): + backup=True) + self.set_perms(self.passwd_fname, write=True) + ++ def restore(self): ++ self.nssdb.restore() ++ + def list_certs(self): + """ + Return a tuple of tuples containing (nickname, trust) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index ca3bcc87eec2c93a664db517df3eddecaaf565c2..f6f0b0c4f6acd648aa9f6f5d7400617613245473 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -555,6 +555,9 @@ class HTTPInstance(service.Service): + ca_iface.Set('org.fedorahosted.certmonger.ca', + 'external-helper', helper) + ++ db = certs.CertDB(self.realm, paths.HTTPD_ALIAS_DIR) ++ db.restore() ++ + for f in [paths.HTTPD_IPA_CONF, paths.HTTPD_SSL_CONF, paths.HTTPD_NSS_CONF]: + try: + self.fstore.restore_file(f) +-- +2.12.1 + diff --git a/SOURCES/0010-Fixing-replica-install-fix-ldap-connection-in-domlvl.patch b/SOURCES/0010-Fixing-replica-install-fix-ldap-connection-in-domlvl.patch new file mode 100644 index 0000000..0664301 --- /dev/null +++ b/SOURCES/0010-Fixing-replica-install-fix-ldap-connection-in-domlvl.patch @@ -0,0 +1,43 @@ +From 175c29c7b57a0ab48d1371c199e70f3435a0ead7 Mon Sep 17 00:00:00 2001 +From: felipe +Date: Tue, 21 Mar 2017 09:05:56 -0300 +Subject: [PATCH] Fixing replica install: fix ldap connection in domlvl 0 + +Now, at the domain level 0, the replica install always uses +Directory Manager credentials to create the LDAP connection. +Since ACIs permitting hosts to manage their own services were +added in 4.2 release, the old master denies this operations. + +https://pagure.io/freeipa/issue/6549 + +Reviewed-By: Martin Basti +Reviewed-By: Jan Cholasta +--- + ipaserver/install/server/replicainstall.py | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index b4463fd4066efbc68f22e4f8f3175b59cb20b103..f489e691999fd9d6e82879341922510e56eac47d 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1391,7 +1391,16 @@ def install(installer): + dsinstance.create_ds_user() + + try: +- conn.connect(ccache=ccache) ++ if promote: ++ conn.connect(ccache=ccache) ++ else: ++ # dmlvl 0 replica install should always use DM credentials ++ # to create remote LDAP connection. Since ACIs permitting hosts ++ # to manage their own services were added in 4.2 release, ++ # the master denies this operations. ++ conn.connect(bind_dn=ipaldap.DIRMAN_DN, cacert=cafile, ++ bind_pw=config.dirman_password) ++ + # Update and istall updated CA file + cafile = install_ca_cert(conn, api.env.basedn, api.env.realm, cafile) + +-- +2.12.1 + diff --git a/SOURCES/0011-replica-prepare-fix-wrong-IPA-CA-nickname-in-replica.patch b/SOURCES/0011-replica-prepare-fix-wrong-IPA-CA-nickname-in-replica.patch new file mode 100644 index 0000000..7b96d06 --- /dev/null +++ b/SOURCES/0011-replica-prepare-fix-wrong-IPA-CA-nickname-in-replica.patch @@ -0,0 +1,51 @@ +From c34fa1891b774e98de6a1787001f2215ea85c0f3 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 17 Mar 2017 09:34:08 +0000 +Subject: [PATCH] replica prepare: fix wrong IPA CA nickname in replica file + +Lookup IPA CA subject and pass it to CertDB when creating dscert.p12 and +httpcert.p12, otherwise a generic nickname will be used for the IPA CA +certificate instead of "$REALM IPA CA". + +This fixes replica install on domain level 0 from a replica file created +using ipa-replica-install on IPA 4.5. + +https://pagure.io/freeipa/issue/6777 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/ipa_replica_prepare.py | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py +index f4925a6c46b6714362545ee5e8194b7b02de5091..95c3818a9fc34c937f8b418e91a1bfc28352b02e 100644 +--- a/ipaserver/install/ipa_replica_prepare.py ++++ b/ipaserver/install/ipa_replica_prepare.py +@@ -34,7 +34,7 @@ import dns.resolver + from six.moves.configparser import SafeConfigParser + # pylint: enable=import-error + +-from ipaserver.install import certs, installutils, bindinstance, dsinstance ++from ipaserver.install import certs, installutils, bindinstance, dsinstance, ca + from ipaserver.install.replication import enable_replication_version_checking + from ipaserver.install.server.replicainstall import install_ca_cert + from ipaserver.install.bindinstance import ( +@@ -537,12 +537,13 @@ class ReplicaPrepare(admintool.AdminTool): + """ + hostname = self.replica_fqdn + subject_base = self.subject_base ++ ca_subject = ca.lookup_ca_subject(api, subject_base) + nickname = "Server-Cert" + + try: + db = certs.CertDB( +- api.env.realm, nssdir=self.dir, subject_base=subject_base, +- host_name=api.env.host) ++ api.env.realm, nssdir=self.dir, host_name=api.env.host, ++ subject_base=subject_base, ca_subject=ca_subject) + db.create_passwd_file() + db.create_from_cacert() + db.create_server_cert(nickname, hostname) +-- +2.12.1 + diff --git a/SOURCES/0012-ldap2-use-LDAP-whoami-operation-to-retrieve-bind-DN-.patch b/SOURCES/0012-ldap2-use-LDAP-whoami-operation-to-retrieve-bind-DN-.patch new file mode 100644 index 0000000..2b4be55 --- /dev/null +++ b/SOURCES/0012-ldap2-use-LDAP-whoami-operation-to-retrieve-bind-DN-.patch @@ -0,0 +1,43 @@ +From 1288763da61ba9e0c9bd345487a3e645c58284df Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 22 Mar 2017 13:00:22 +0200 +Subject: [PATCH] ldap2: use LDAP whoami operation to retrieve bind DN for + current connection + +For external users which are mapped to some DN in LDAP server, we +wouldn't neccesary be able to find a kerberos data in their LDAP entry. +Instead of searching for Kerberos principal use actual DN we are bound +to because for get_effective_rights LDAP control we only need the DN +itself. + +Fixes https://pagure.io/freeipa/issue/6797 + +Reviewed-By: Martin Babinsky +Reviewed-By: Pavel Vomacka +--- + ipaserver/plugins/ldap2.py | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py +index def124530cc863e6924c7b6f1f48c236323019a9..3b1e4da57a8e16e3d9b27eea24025de2caa53216 100644 +--- a/ipaserver/plugins/ldap2.py ++++ b/ipaserver/plugins/ldap2.py +@@ -286,12 +286,11 @@ class ldap2(CrudBackend, LDAPClient): + + assert isinstance(dn, DN) + +- principal = getattr(context, 'principal') +- entry = self.find_entry_by_attr("krbprincipalname", principal, +- "krbPrincipalAux", base_dn=self.api.env.basedn) ++ bind_dn = self.conn.whoami_s()[4:] ++ + sctrl = [ + GetEffectiveRightsControl( +- True, "dn: {0}".format(entry.dn).encode('utf-8')) ++ True, "dn: {0}".format(bind_dn).encode('utf-8')) + ] + self.conn.set_option(_ldap.OPT_SERVER_CONTROLS, sctrl) + try: +-- +2.12.1 + diff --git a/SOURCES/0013-Backup-ipa-specific-httpd-unit-file.patch b/SOURCES/0013-Backup-ipa-specific-httpd-unit-file.patch new file mode 100644 index 0000000..bf500ae --- /dev/null +++ b/SOURCES/0013-Backup-ipa-specific-httpd-unit-file.patch @@ -0,0 +1,47 @@ +From 7b57dd770bbb4861f46805adaa9597445dff142c Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Thu, 16 Mar 2017 10:22:59 +0100 +Subject: [PATCH] Backup ipa-specific httpd unit-file + +On backup-restore, the ipa unit file for httpd was not backed up. +This file however contains setting for httpd to communicate with +gssproxy so not backing it up will result in httpd not knowing +how to get credentials. + +https://pagure.io/freeipa/issue/6748 + +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +--- + ipaserver/install/ipa_backup.py | 1 + + ipaserver/install/ipa_restore.py | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py +index 07c50c8364c313b9aeb6d73540b541f55d98a44d..56583c01b1677a48c103d79123e3fbe106222f38 100644 +--- a/ipaserver/install/ipa_backup.py ++++ b/ipaserver/install/ipa_backup.py +@@ -166,6 +166,7 @@ class Backup(admintool.AdminTool): + paths.KDC_CERT, + paths.KDC_KEY, + paths.SYSTEMD_IPA_SERVICE, ++ paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, + paths.SYSTEMD_SSSD_SERVICE, + paths.SYSTEMD_CERTMONGER_SERVICE, + paths.SYSTEMD_PKI_TOMCAT_SERVICE, +diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py +index d798654ea7464f66e461936e41e3747a16acb21d..2552bbdef36f653f1c377ea096ca227d09e5f3e6 100644 +--- a/ipaserver/install/ipa_restore.py ++++ b/ipaserver/install/ipa_restore.py +@@ -414,6 +414,8 @@ class Restore(admintool.AdminTool): + sssd = services.service('sssd', api) + sssd.restart() + http.remove_httpd_ccaches() ++ # have the daemons pick up their restored configs ++ run([paths.SYSTEMCTL, "--system", "daemon-reload"]) + finally: + try: + os.chdir(cwd) +-- +2.12.1 + diff --git a/SOURCES/0014-WebUI-check-principals-in-lowercase.patch b/SOURCES/0014-WebUI-check-principals-in-lowercase.patch new file mode 100644 index 0000000..ebaff11 --- /dev/null +++ b/SOURCES/0014-WebUI-check-principals-in-lowercase.patch @@ -0,0 +1,36 @@ +From 4ffc29d45ff1121f76b39ac7acaee824b4d04aaf Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Wed, 22 Mar 2017 16:39:21 +0100 +Subject: [PATCH] WebUI: check principals in lowercase + +WebUI checks whether principal name of logged user and principal name +in each command is equal. As KDC for our principals is case insensitive +- it does make sense to switch this check also into case insensitive. +So both principals are reformated to lower case and then +compared. + +Part of: https://pagure.io/freeipa/issue/3242 + +Reviewed-By: Petr Vobornik +Reviewed-By: Alexander Bokovoy +--- + install/ui/src/freeipa/rpc.js | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/install/ui/src/freeipa/rpc.js b/install/ui/src/freeipa/rpc.js +index 7ae1b64291a4530137e0fb8d72ff5a8491cb10b4..1880f8d5732f982c25924b787b273c9e56636b20 100644 +--- a/install/ui/src/freeipa/rpc.js ++++ b/install/ui/src/freeipa/rpc.js +@@ -389,7 +389,8 @@ rpc.command = function(spec) { + } else if (IPA.version && data.version && IPA.version !== data.version) { + window.location.reload(); + +- } else if (IPA.principal && data.principal && IPA.principal !== data.principal) { ++ } else if (IPA.principal && data.principal && ++ IPA.principal.toLowerCase() !== data.principal.toLowerCase()) { + window.location.reload(); + + } else if (data.error) { +-- +2.12.1 + diff --git a/SOURCES/0015-WebUI-add-method-for-disabling-item-in-user-dropdown.patch b/SOURCES/0015-WebUI-add-method-for-disabling-item-in-user-dropdown.patch new file mode 100644 index 0000000..e8389c4 --- /dev/null +++ b/SOURCES/0015-WebUI-add-method-for-disabling-item-in-user-dropdown.patch @@ -0,0 +1,105 @@ +From 894fdd8c10552ef0b90363db985bb25e398d99e1 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Wed, 22 Mar 2017 16:48:36 +0100 +Subject: [PATCH] WebUI: add method for disabling item in user dropdown menu + +AD user can do only several things. One of those which are not +allowed is to reset password to itself. Therefore we need to be +able to turn of a item in dropdown menu. In our case +'Password reset' item. Function which disable menu item and detach +the listener on click from the item specified by its name was added. + +Part of: https://pagure.io/freeipa/issue/3242 + +Reviewed-By: Petr Vobornik +Reviewed-By: Alexander Bokovoy +--- + install/ui/src/freeipa/Application_controller.js | 42 ++++++++++++++++++++---- + install/ui/src/freeipa/widgets/App.js | 4 +++ + 2 files changed, 40 insertions(+), 6 deletions(-) + +diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js +index 32add5f8f3d6874c1c555bf28d2b70cd54af5956..d809c1f2662609e390609270ef3ddc42f0727936 100644 +--- a/install/ui/src/freeipa/Application_controller.js ++++ b/install/ui/src/freeipa/Application_controller.js +@@ -69,6 +69,16 @@ define([ + facet_changing: false, + + /** ++ * Listeners for user menu items ++ */ ++ on_profile_listener: null, ++ on_passwd_reset_listener: null, ++ on_logout_listener: null, ++ on_item_select_listener: null, ++ on_configuration_listerer: null, ++ on_about_listener: null, ++ ++ /** + * Currently displayed facet + * + */ +@@ -109,12 +119,7 @@ define([ + } + }; + +- on(this.app_widget.menu_widget, 'item-select', this.on_menu_click.bind(this)); +- on(this.app_widget, 'profile-click', this.on_profile.bind(this)); +- on(this.app_widget, 'logout-click', this.on_logout.bind(this)); +- on(this.app_widget, 'password-reset-click', this.on_password_reset.bind(this)); +- on(this.app_widget, 'configuration-click', this.on_configuration.bind(this)); +- on(this.app_widget, 'about-click', this.on_about.bind(this)); ++ this.register_user_menu_listeners(); + + on(this.router, 'facet-show', this.on_facet_show.bind(this)); + on(this.router, 'facet-change', this.on_facet_change.bind(this)); +@@ -133,6 +138,31 @@ define([ + IPA.opened_dialogs.start_handling(this); + }, + ++ register_user_menu_listeners: function() { ++ this.on_profile_listener = on(this.app_widget, 'profile-click', ++ this.on_profile.bind(this)); ++ this.on_passwd_reset_listener = on(this.app_widget, ++ 'password-reset-click', this.on_password_reset.bind(this)); ++ this.on_logout_listener = on(this.app_widget, 'logout-click', ++ this.on_logout.bind(this)); ++ this.on_item_select_listener = on(this.app_widget.menu_widget, ++ 'item-select', this.on_menu_click.bind(this)); ++ this.on_configuration_listerer = on(this.app_widget, ++ 'configuration-click', this.on_configuration.bind(this)); ++ this.on_about_listener = on(this.app_widget, ++ 'about-click', this.on_about.bind(this)); ++ }, ++ ++ /** ++ * Turns off one item in user dropdown menu and remove its listener. ++ * @param {string} name of the user menu item which should be disabled ++ * @param {Object} listener disable this listener ++ */ ++ disable_user_menu_item: function(name, listener) { ++ this.app_widget.disable_user_menu_item(name); ++ listener.remove(); ++ }, ++ + /** + * Gets: + * * metadata +diff --git a/install/ui/src/freeipa/widgets/App.js b/install/ui/src/freeipa/widgets/App.js +index 68b78c7c4be44f5a1f658fed6b6b75d1beda22c5..95bc9b2cf3bcf40cd3a4cab47e9043e05331e019 100644 +--- a/install/ui/src/freeipa/widgets/App.js ++++ b/install/ui/src/freeipa/widgets/App.js +@@ -222,6 +222,10 @@ define(['dojo/_base/declare', + } + }, + ++ disable_user_menu_item: function(name) { ++ this.user_menu.disable_item(name); ++ }, ++ + on_menu_item_click: function(item) { + this.collapse_menu(); + }, +-- +2.12.1 + diff --git a/SOURCES/0016-WebUI-Add-support-for-login-for-AD-users.patch b/SOURCES/0016-WebUI-Add-support-for-login-for-AD-users.patch new file mode 100644 index 0000000..4912fd1 --- /dev/null +++ b/SOURCES/0016-WebUI-Add-support-for-login-for-AD-users.patch @@ -0,0 +1,341 @@ +From 18e9bc2399af788399e66c3f4b28e6a7f0378b78 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Wed, 22 Mar 2017 16:54:33 +0100 +Subject: [PATCH] WebUI: Add support for login for AD users + +After login, method user-find --whoami was called which cannot be +called for AD users. That method was replaced by ipa whoami command +and sequential command according to result of ipa whoami. AD user +can now be logged in. + +AD users have new menu definition which contains only list of IPA +users and profile page of AD user - "User ID Override". + +This commit also fixes several places where IPA.whoami object was +used, because its structure was also changed. It now contains two +objects. First one is stored in 'metadata' property and stores +result from ipa whoami (type of object, command which should be +called for showing detailed data about currently logged entity, etc). +The second one is stored in 'data' property which stores result of +_show command for currently logged entity. + +https://pagure.io/freeipa/issue/3242 + +Reviewed-By: Petr Vobornik +Reviewed-By: Alexander Bokovoy +--- + install/ui/src/freeipa/Application_controller.js | 52 +++++++++++++++++++----- + install/ui/src/freeipa/idviews.js | 21 +++++++++- + install/ui/src/freeipa/ipa.js | 47 +++++++++++++-------- + install/ui/src/freeipa/navigation/menu_spec.js | 10 +++++ + install/ui/src/freeipa/otptoken.js | 2 +- + install/ui/src/freeipa/user.js | 5 ++- + ipaserver/plugins/internal.py | 1 + + 7 files changed, 108 insertions(+), 30 deletions(-) + +diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js +index d809c1f2662609e390609270ef3ddc42f0727936..5eb4e7a5104b780761b9a5179dbfd1501a8d1478 100644 +--- a/install/ui/src/freeipa/Application_controller.js ++++ b/install/ui/src/freeipa/Application_controller.js +@@ -31,6 +31,7 @@ define([ + './widgets/App', + './widgets/FacetContainer', + './ipa', ++ './rpc', + './reg', + './config', + './widget', +@@ -41,7 +42,7 @@ define([ + './plugins/load_page' + ], + function(declare, array, Deferred, on, topic, query, dom_class, auth, +- JSON, App_widget, FacetContainer, IPA, reg, config, widget_mod, ++ JSON, App_widget, FacetContainer, IPA, rpc, reg, config, widget_mod, + Menu, Router, routing, menu_spec) { + + /** +@@ -156,7 +157,7 @@ define([ + /** + * Turns off one item in user dropdown menu and remove its listener. + * @param {string} name of the user menu item which should be disabled +- * @param {Object} listener disable this listener ++ * @param {Object} listener disable di + */ + disable_user_menu_item: function(name, listener) { + this.app_widget.disable_user_menu_item(name); +@@ -179,16 +180,22 @@ define([ + */ + choose_profile: function() { + +- // TODO: change IPA.whoami.cn[0] to something readable +- this.update_logged_in(true, IPA.whoami.cn[0]); ++ this.update_logged_in(true); + var selfservice = this.is_selfservice(); + + + this.app_widget.menu_widget.ignore_changes = true; + + if (selfservice) { +- this.menu.name = menu_spec.self_service.name; +- this.menu.add_items(menu_spec.self_service.items); ++ if (this.is_aduser_selfservice()) { ++ this.menu.name = menu_spec.ad_self_service.name; ++ this.menu.add_items(menu_spec.ad_self_service.items); ++ this.disable_user_menu_item('password_reset', ++ this.on_passwd_reset_listener); ++ } else { ++ this.menu.name = menu_spec.self_service.name; ++ this.menu.add_items(menu_spec.self_service.items); ++ } + } else { + this.menu.name = menu_spec.admin.name; + this.menu.add_items(menu_spec.admin.items); +@@ -232,10 +239,9 @@ define([ + }, + + is_selfservice: function() { +- var whoami = IPA.whoami; ++ var whoami = IPA.whoami.data; + var self_service = true; + +- + if (whoami.hasOwnProperty('memberof_group') && + whoami.memberof_group.indexOf('admins') !== -1) { + self_service = false; +@@ -255,13 +261,39 @@ define([ + return self_service; + }, + +- update_logged_in: function(logged_in, fullname) { ++ is_aduser_selfservice: function() { ++ var selfservice = IPA.whoami.metadata.object === 'idoverrideuser'; ++ // quite ugly, needed for users and iduseroverride to hide breadcrumb ++ IPA.is_aduser_selfservice = selfservice; ++ ++ return selfservice; ++ }, ++ ++ update_logged_in: function(logged_in) { + this.app_widget.set('logged', logged_in); ++ ++ var whoami = IPA.whoami; ++ var fullname = ''; ++ var entity = whoami.metadata.object; ++ ++ if (whoami.data.cn) { ++ fullname = whoami.data.cn[0]; ++ } else if (whoami.data.displayname) { ++ fullname = whoami.data.displayname[0]; ++ } else if (whoami.data.gecos) { ++ fullname = whoami.data.gecos[0]; ++ } else if (whoami.data.krbprincipalname) { ++ fullname = whoami.data.krbprincipalname[0]; ++ } else if (whoami.data.ipaoriginaluid) { ++ fullname = whoami.data.ipaoriginaluid[0]; ++ } ++ + this.app_widget.set('fullname', fullname); + }, + + on_profile: function() { +- routing.navigate(['entity', 'user', 'details', [IPA.whoami.uid[0]]]); ++ routing.navigate(['entity', IPA.whoami.metadata.object, 'details', ++ IPA.whoami.metadata.arguments]); + }, + + on_logout: function(event) { +diff --git a/install/ui/src/freeipa/idviews.js b/install/ui/src/freeipa/idviews.js +index f383ab3be4c4ed997fb209da2da4d04835236d8a..d9133a13c2ed8970e7919bb28d06f818d832170a 100644 +--- a/install/ui/src/freeipa/idviews.js ++++ b/install/ui/src/freeipa/idviews.js +@@ -452,6 +452,21 @@ idviews.id_override_user_details_facet = function(spec) { + return that; + }; + ++ ++idviews.aduser_idoverrideuser_pre_op = function(spec, context) { ++ spec = spec || []; ++ ++ if (!IPA.is_aduser_selfservice) return spec; ++ ++ var facet = spec.facets[0]; ++ facet.label = '@i18n:objects.idoverrideuser.profile'; ++ facet.actions = []; ++ facet.header_actions = []; ++ facet.disable_breadcrumb = true; ++ ++ return spec; ++}; ++ + /** + * @extends IPA.cert.certs_widget + */ +@@ -948,7 +963,11 @@ idviews.register = function() { + var w = reg.widget; + + e.register({type: 'idview', spec: idviews.spec}); +- e.register({type: 'idoverrideuser', spec: idviews.idoverrideuser_spec}); ++ e.register({ ++ type: 'idoverrideuser', ++ spec: idviews.idoverrideuser_spec, ++ pre_ops: [idviews.aduser_idoverrideuser_pre_op] ++ }); + e.register({type: 'idoverridegroup', spec: idviews.idoverridegroup_spec}); + f.copy('attribute', 'idview_appliedtohosts', { + factory: idviews.appliedtohosts_facet +diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js +index 0ddbd0744d699cacddb3970e5ec7cb72b9dbf4f4..2538001c94141b823d634ca63327a66fd148129f 100644 +--- a/install/ui/src/freeipa/ipa.js ++++ b/install/ui/src/freeipa/ipa.js +@@ -86,7 +86,8 @@ var IPA = function () { + /** + * User information + * +- * - output of ipa user-find --whoami ++ * - output of ipa whoami in that.whoami.metadata and then object_show method ++ * in that.whoami.data + */ + that.whoami = {}; + +@@ -263,19 +264,33 @@ var IPA = function () { + */ + that.get_whoami_command = function(batch) { + return rpc.command({ +- entity: 'user', +- method: 'find', +- options: { +- whoami: true, +- all: true +- }, ++ method: 'whoami', + on_success: function(data, text_status, xhr) { +- that.whoami = batch ? data.result[0] : data.result.result[0]; +- var cn = that.whoami.krbcanonicalname; +- if (cn) that.principal = cn[0]; +- if (!that.principal) { +- that.principal = that.whoami.krbprincipalname[0]; +- } ++ that.whoami.metadata = data; ++ ++ rpc.command({ ++ method: data.details || data.command, ++ args: data.arguments, ++ options: function() { ++ var options = data.options || []; ++ $.extend(options, {all: true}); ++ return options; ++ }(), ++ on_success: function(data, text_status, xhr) { ++ that.whoami.data = false ? data.result[0] : data.result.result; ++ var entity = that.whoami.metadata.object; ++ ++ if (entity === 'user') { ++ var cn = that.whoami.data.krbcanonicalname; ++ if (cn) that.principal = cn[0]; ++ if (!that.principal) { ++ that.principal = that.whoami.data.krbprincipalname[0]; ++ } ++ } else if (entity === 'idoverrideuser') { ++ that.principal = that.whoami.data.ipaoriginaluid[0]; ++ } ++ } ++ }).execute(); + } + }); + }; +@@ -616,7 +631,7 @@ IPA.update_password_expiration = function() { + + var now, expires, notify_days, diff, message, container, notify; + +- expires = rpc.extract_objects(IPA.whoami.krbpasswordexpiration); ++ expires = rpc.extract_objects(IPA.whoami.data.krbpasswordexpiration); + expires = expires ? datetime.parse(expires[0]) : null; + + notify_days = IPA.server_config.ipapwdexpadvnotify; +@@ -650,13 +665,13 @@ IPA.update_password_expiration = function() { + IPA.password_selfservice = function() { + var reset_dialog = builder.build('dialog', { + $type: 'user_password', +- args: [IPA.whoami.uid[0]] ++ args: [IPA.whoami.data.uid[0]] + }); + reset_dialog.succeeded.attach(function() { + var command = IPA.get_whoami_command(); + var orig_on_success = command.on_success; + command.on_success = function(data, text_status, xhr) { +- orig_on_success.call(this, data, text_status, xhr); ++ orig_on_success.call(this, data.result, text_status, xhr); + IPA.update_password_expiration(); + }; + command.execute(); +diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js +index 4f78e4bf9ba25bf2f7585e38086b1cbc6db34026..9329694c14a47cbe1ec244554327b40743044d7b 100644 +--- a/install/ui/src/freeipa/navigation/menu_spec.js ++++ b/install/ui/src/freeipa/navigation/menu_spec.js +@@ -353,5 +353,15 @@ nav.self_service = { + ] + }; + ++nav.ad_self_service = { ++ name: 'ad_self_service', ++ items: [ ++ { ++ entity: 'idoverrideuser', ++ label: 'Profile' ++ } ++ ] ++}; ++ + return nav; + }); +diff --git a/install/ui/src/freeipa/otptoken.js b/install/ui/src/freeipa/otptoken.js +index caa7a85523d6e63db629a3a518e8611d511f7952..1f6f20d801042a5424ecf5894658df9411723bcc 100644 +--- a/install/ui/src/freeipa/otptoken.js ++++ b/install/ui/src/freeipa/otptoken.js +@@ -361,7 +361,7 @@ otptoken.adder_dialog = function(spec) { + + var command = that.entity_adder_dialog_create_add_command(record); + if (that.self_service) { +- command.set_option('ipatokenowner', IPA.whoami.uid[0]); ++ command.set_option('ipatokenowner', IPA.whoami.data.uid[0]); + } + return command; + }; +diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js +index 4bb04488b51dd43a437ab3759eb3f530afe62550..6b2bf196c31e7891d3389eb2e2774f56d88ac2ba 100644 +--- a/install/ui/src/freeipa/user.js ++++ b/install/ui/src/freeipa/user.js +@@ -735,7 +735,7 @@ IPA.user.password_dialog = function(spec) { + var that = dialogs.command_dialog(spec); + + that.is_self_service = function() { +- var self_service = that.args[0] === IPA.whoami.uid[0]; ++ var self_service = that.args[0] === IPA.whoami.data.uid[0]; + return self_service; + }; + +@@ -895,7 +895,8 @@ IPA.user.self_service_other_user_evaluator = function(spec) { + that.state = []; + + var value = that.adapter.load(data); +- if (IPA.is_selfservice && IPA.whoami.uid[0] !== value[0]) { ++ if (IPA.is_aduser_selfservice || ++ (IPA.is_selfservice && IPA.whoami.data.uid[0] !== value[0])) { + that.state.push('self-service-other'); + } + +diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py +index 9fa1b6de857cf7e21210f557befabff32da0d4ff..6feefa5941506f38f01b8016a22cad14a831e3fc 100644 +--- a/ipaserver/plugins/internal.py ++++ b/ipaserver/plugins/internal.py +@@ -625,6 +625,7 @@ class i18n_messages(Command): + "anchor_label": _("User to override"), + "anchor_tooltip": _("Enter trusted or IPA user login. Note: search doesn't list users from trusted domains."), + "anchor_tooltip_ad": _("Enter trusted user login."), ++ "profile": _("Profile"), + }, + "idoverridegroup": { + "anchor_label": _("Group to override"), +-- +2.12.1 + diff --git a/SOURCES/0017-cert-do-not-limit-internal-searches-in-cert-find.patch b/SOURCES/0017-cert-do-not-limit-internal-searches-in-cert-find.patch new file mode 100644 index 0000000..7166516 --- /dev/null +++ b/SOURCES/0017-cert-do-not-limit-internal-searches-in-cert-find.patch @@ -0,0 +1,105 @@ +From ca26e32beb77fbd8fcc66e6eea07c6eeeb9261c9 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 22 Mar 2017 06:58:25 +0000 +Subject: [PATCH] cert: do not limit internal searches in cert-find + +Instead, apply the limits on the combined result. + +This fixes (absence of) `--sizelimit` leading to strange behavior, such as +`cert-find --users user` returning a non-empty result only with +`--sizelimit 0`. + +https://pagure.io/freeipa/issue/6716 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/cert.py | 28 ++++++++++------------------ + 1 file changed, 10 insertions(+), 18 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 9f901076075809592ad5ddeec8d71c273d4853c9..1a6d04533cebb2eb00022981dae9ffe5b785ba8b 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -1324,7 +1324,7 @@ class cert_find(Search, CertMethod): + + return result, False, True + +- def _ca_search(self, all, raw, pkey_only, sizelimit, exactly, **options): ++ def _ca_search(self, all, raw, pkey_only, exactly, **options): + ra_options = {} + for name in ('revocation_reason', + 'issuer', +@@ -1343,10 +1343,6 @@ class cert_find(Search, CertMethod): + elif isinstance(value, DN): + value = unicode(value) + ra_options[name] = value +- if sizelimit > 0: +- # Dogtag doesn't tell that the size limit was exceeded +- # search for one more entry so that we can tell ourselves +- ra_options['sizelimit'] = sizelimit + 1 + if exactly: + ra_options['exactly'] = True + +@@ -1369,11 +1365,6 @@ class cert_find(Search, CertMethod): + + ra = self.api.Backend.ra + for ra_obj in ra.find(ra_options): +- if sizelimit > 0 and len(result) >= sizelimit: +- self.add_message(messages.SearchResultTruncated( +- reason=errors.SizeLimitExceeded())) +- break +- + issuer = DN(ra_obj['issuer']) + serial_number = ra_obj['serial_number'] + +@@ -1411,8 +1402,7 @@ class cert_find(Search, CertMethod): + + return result, False, complete + +- def _ldap_search(self, all, raw, pkey_only, no_members, timelimit, +- sizelimit, **options): ++ def _ldap_search(self, all, raw, pkey_only, no_members, **options): + ldap = self.api.Backend.ldap2 + + filters = [] +@@ -1453,8 +1443,8 @@ class cert_find(Search, CertMethod): + base_dn=self.api.env.basedn, + filter=filter, + attrs_list=['usercertificate'], +- time_limit=timelimit, +- size_limit=sizelimit, ++ time_limit=0, ++ size_limit=0, + ) + except errors.EmptyResult: + entries = [] +@@ -1527,13 +1517,9 @@ class cert_find(Search, CertMethod): + raw=raw, + pkey_only=pkey_only, + no_members=no_members, +- timelimit=timelimit, +- sizelimit=sizelimit, + **options) + + if sub_complete: +- sizelimit = 0 +- + for key in tuple(result): + if key not in sub_result: + del result[key] +@@ -1552,6 +1538,12 @@ class cert_find(Search, CertMethod): + complete = complete or sub_complete + + result = list(six.itervalues(result)) ++ if sizelimit > 0 and len(result) > sizelimit: ++ if not truncated: ++ self.add_message(messages.SearchResultTruncated( ++ reason=errors.SizeLimitExceeded())) ++ result = result[:sizelimit] ++ truncated = True + + ret = dict( + result=result +-- +2.12.1 + diff --git a/SOURCES/0018-ipa-kdb-add-ipadb_fetch_principals_with_extra_filter.patch b/SOURCES/0018-ipa-kdb-add-ipadb_fetch_principals_with_extra_filter.patch new file mode 100644 index 0000000..3a5a47a --- /dev/null +++ b/SOURCES/0018-ipa-kdb-add-ipadb_fetch_principals_with_extra_filter.patch @@ -0,0 +1,132 @@ +From 7a115884d370d8e9b2c7b110a0565fe5b78446a9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 15 Feb 2017 12:09:20 +0100 +Subject: [PATCH] ipa-kdb: add ipadb_fetch_principals_with_extra_filter() + +Additionally make ipadb_find_principal public. + +Related to https://pagure.io/freeipa/issue/4905 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: David Kupka +--- + daemons/ipa-kdb/ipa_kdb.h | 11 +++++++ + daemons/ipa-kdb/ipa_kdb_principals.c | 58 ++++++++++++++++++++++++++++-------- + 2 files changed, 56 insertions(+), 13 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h +index 8a3f7d3c012186fd73b27abef09602b0d0e96e8d..72f2675809a3267cce30bc06c77335697c7287ad 100644 +--- a/daemons/ipa-kdb/ipa_kdb.h ++++ b/daemons/ipa-kdb/ipa_kdb.h +@@ -198,6 +198,17 @@ krb5_error_code ipadb_put_principal(krb5_context kcontext, + char **db_args); + krb5_error_code ipadb_delete_principal(krb5_context kcontext, + krb5_const_principal search_for); ++krb5_error_code ++ipadb_fetch_principals_with_extra_filter(struct ipadb_context *ipactx, ++ unsigned int flags, ++ const char *principal, ++ const char *filter, ++ LDAPMessage **result); ++krb5_error_code ipadb_find_principal(krb5_context kcontext, ++ unsigned int flags, ++ LDAPMessage *res, ++ char **principal, ++ LDAPMessage **entry); + #if KRB5_KDB_API_VERSION < 8 + krb5_error_code ipadb_iterate(krb5_context kcontext, + char *match_entry, +diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c +index 3bd8fb8c70c61b056a714bc0a8149bd8524beb1d..82c857430b11279b4029fa72a6d430610524ba43 100644 +--- a/daemons/ipa-kdb/ipa_kdb_principals.c ++++ b/daemons/ipa-kdb/ipa_kdb_principals.c +@@ -37,6 +37,17 @@ + "(objectclass=krbprincipal))" \ + "(krbprincipalname=%s))" + ++#define PRINC_TGS_SEARCH_FILTER_EXTRA "(&(|(objectclass=krbprincipalaux)" \ ++ "(objectclass=krbprincipal)" \ ++ "(objectclass=ipakrbprincipal))" \ ++ "(|(ipakrbprincipalalias=%s)" \ ++ "(krbprincipalname:caseIgnoreIA5Match:=%s))" \ ++ "%s)" ++ ++#define PRINC_SEARCH_FILTER_EXTRA "(&(|(objectclass=krbprincipalaux)" \ ++ "(objectclass=krbprincipal))" \ ++ "(krbprincipalname=%s)" \ ++ "%s)" + static char *std_principal_attrs[] = { + "krbPrincipalName", + "krbCanonicalName", +@@ -864,10 +875,12 @@ done: + return kerr; + } + +-static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx, +- unsigned int flags, +- char *principal, +- LDAPMessage **result) ++krb5_error_code ++ipadb_fetch_principals_with_extra_filter(struct ipadb_context *ipactx, ++ unsigned int flags, ++ const char *principal, ++ const char *filter, ++ LDAPMessage **result) + { + krb5_error_code kerr; + char *src_filter = NULL; +@@ -890,11 +903,21 @@ static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx, + goto done; + } + +- if (flags & KRB5_KDB_FLAG_ALIAS_OK) { +- ret = asprintf(&src_filter, PRINC_TGS_SEARCH_FILTER, +- esc_original_princ, esc_original_princ); ++ if (filter == NULL) { ++ if (flags & KRB5_KDB_FLAG_ALIAS_OK) { ++ ret = asprintf(&src_filter, PRINC_TGS_SEARCH_FILTER, ++ esc_original_princ, esc_original_princ); ++ } else { ++ ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_original_princ); ++ } + } else { +- ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_original_princ); ++ if (flags & KRB5_KDB_FLAG_ALIAS_OK) { ++ ret = asprintf(&src_filter, PRINC_TGS_SEARCH_FILTER_EXTRA, ++ esc_original_princ, esc_original_princ, filter); ++ } else { ++ ret = asprintf(&src_filter, PRINC_SEARCH_FILTER_EXTRA, ++ esc_original_princ, filter); ++ } + } + + if (ret == -1) { +@@ -913,11 +936,20 @@ done: + return kerr; + } + +-static krb5_error_code ipadb_find_principal(krb5_context kcontext, +- unsigned int flags, +- LDAPMessage *res, +- char **principal, +- LDAPMessage **entry) ++static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx, ++ unsigned int flags, ++ char *principal, ++ LDAPMessage **result) ++{ ++ return ipadb_fetch_principals_with_extra_filter(ipactx, flags, principal, ++ NULL, result); ++} ++ ++krb5_error_code ipadb_find_principal(krb5_context kcontext, ++ unsigned int flags, ++ LDAPMessage *res, ++ char **principal, ++ LDAPMessage **entry) + { + struct ipadb_context *ipactx; + bool found = false; +-- +2.12.1 + diff --git a/SOURCES/0019-IPA-certauth-plugin.patch b/SOURCES/0019-IPA-certauth-plugin.patch new file mode 100644 index 0000000..b600531 --- /dev/null +++ b/SOURCES/0019-IPA-certauth-plugin.patch @@ -0,0 +1,609 @@ +From 0956c8149f11921ed427d67b10bb9b6c4b97df48 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 2 Feb 2017 12:32:13 +0100 +Subject: [PATCH] IPA certauth plugin + +This patch add a certauth plugin which allows the IPA server to support +PKINIT for certificates which do not include a special SAN extension +which contains a Kerberos principal but allow other mappings with the +help of SSSD's certmap library. + +Related to https://pagure.io/freeipa/issue/4905 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: David Kupka +--- + daemons/ipa-kdb/Makefile.am | 24 ++- + daemons/ipa-kdb/ipa-certauth.in | 5 + + daemons/ipa-kdb/ipa_kdb.c | 2 + + daemons/ipa-kdb/ipa_kdb.exports | 1 + + daemons/ipa-kdb/ipa_kdb.h | 5 + + daemons/ipa-kdb/ipa_kdb_certauth.c | 398 +++++++++++++++++++++++++++++++++++++ + freeipa.spec.in | 2 + + server.m4 | 13 ++ + 8 files changed, 449 insertions(+), 1 deletion(-) + create mode 100644 daemons/ipa-kdb/ipa-certauth.in + create mode 100644 daemons/ipa-kdb/ipa_kdb_certauth.c + +diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am +index 6a2caa0637bf076c796b50efc92412062524f35f..715666e779a4fa64c2c0f71767f09efb19b5f908 100644 +--- a/daemons/ipa-kdb/Makefile.am ++++ b/daemons/ipa-kdb/Makefile.am +@@ -18,6 +18,7 @@ AM_CPPFLAGS = \ + $(WARN_CFLAGS) \ + $(NDRPAC_CFLAGS) \ + $(NSS_CFLAGS) \ ++ $(SSSCERTMAP_CFLAGS) \ + $(NULL) + + plugindir = $(libdir)/krb5/plugins/kdb +@@ -39,6 +40,20 @@ ipadb_la_SOURCES = \ + ipa_kdb_audit_as.c \ + $(NULL) + ++if BUILD_IPA_CERTAUTH_PLUGIN ++ipadb_la_SOURCES += ipa_kdb_certauth.c ++ ++ ++%: %.in ++ sed \ ++ -e 's|@plugindir@|$(plugindir)|g' \ ++ '$(srcdir)/$@.in' >$@ ++ ++krb5confdir = $(sysconfdir)/krb5.conf.d ++krb5conf_DATA = ipa-certauth ++CLEANFILES = $(krb5conf_DATA) ++endif ++ + ipadb_la_LDFLAGS = \ + -avoid-version \ + -module \ +@@ -50,6 +65,7 @@ ipadb_la_LIBADD = \ + $(NDRPAC_LIBS) \ + $(UNISTRING_LIBS) \ + $(NSS_LIBS) \ ++ $(SSSCERTMAP_LIBS) \ + $(top_builddir)/util/libutil.la \ + $(NULL) + +@@ -70,6 +86,11 @@ ipa_kdb_tests_SOURCES = \ + ipa_kdb_delegation.c \ + ipa_kdb_audit_as.c \ + $(NULL) ++ ++if BUILD_IPA_CERTAUTH_PLUGIN ++ipa_kdb_tests_SOURCES += ipa_kdb_certauth.c ++endif ++ + ipa_kdb_tests_CFLAGS = $(CMOCKA_CFLAGS) + ipa_kdb_tests_LDADD = \ + $(CMOCKA_LIBS) \ +@@ -78,12 +99,13 @@ ipa_kdb_tests_LDADD = \ + $(NDRPAC_LIBS) \ + $(UNISTRING_LIBS) \ + $(NSS_LIBS) \ ++ $(SSSCERTMAP_LIBS) \ + $(top_builddir)/util/libutil.la \ + -lkdb5 \ + -lsss_idmap \ + $(NULL) + +-dist_noinst_DATA = ipa_kdb.exports ++dist_noinst_DATA = ipa_kdb.exports ipa-certauth.in + + clean-local: + rm -f tests/.dirstamp +diff --git a/daemons/ipa-kdb/ipa-certauth.in b/daemons/ipa-kdb/ipa-certauth.in +new file mode 100644 +index 0000000000000000000000000000000000000000..eda89a26f02fbea449eb754b232b8115904acd21 +--- /dev/null ++++ b/daemons/ipa-kdb/ipa-certauth.in +@@ -0,0 +1,5 @@ ++[plugins] ++ certauth = { ++ module = ipakdb:@plugindir@/ipadb.so ++ enable_only = ipakdb ++ } +diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c +index c19b7c40e2e88173ab8367a3ef1d7f46245fd174..a961e4e57cf5379eb237551d56e3bc8dc82d952d 100644 +--- a/daemons/ipa-kdb/ipa_kdb.c ++++ b/daemons/ipa-kdb/ipa_kdb.c +@@ -67,6 +67,8 @@ static void ipadb_context_free(krb5_context kcontext, + } + free(cfg->authz_data); + ++ ipa_certauth_free_moddata(&((*ctx)->certauth_moddata)); ++ + free(*ctx); + *ctx = NULL; + } +diff --git a/daemons/ipa-kdb/ipa_kdb.exports b/daemons/ipa-kdb/ipa_kdb.exports +index d2c3f30246cc7ebcba02a9ec5d134e604fa0dbb9..27ce92d2edd741245061a5f4ee9275169440c932 100644 +--- a/daemons/ipa-kdb/ipa_kdb.exports ++++ b/daemons/ipa-kdb/ipa_kdb.exports +@@ -3,6 +3,7 @@ EXPORTED { + # public symbols + global: + kdb_function_table; ++ certauth_ipakdb_initvt; + + # everything else is local + local: +diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h +index 72f2675809a3267cce30bc06c77335697c7287ad..632c1979d15e88aec86d5e408ed6c7017d8362b8 100644 +--- a/daemons/ipa-kdb/ipa_kdb.h ++++ b/daemons/ipa-kdb/ipa_kdb.h +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + #include "ipa_krb5.h" + #include "ipa_pwd.h" +@@ -111,6 +112,7 @@ struct ipadb_context { + krb5_key_salt_tuple *def_encs; + int n_def_encs; + struct ipadb_mspac *mspac; ++ krb5_certauth_moddata certauth_moddata; + + /* Don't access this directly, use ipadb_get_global_config(). */ + struct ipadb_global_config config; +@@ -331,3 +333,6 @@ ipadb_get_global_config(struct ipadb_context *ipactx); + int ipadb_get_enc_salt_types(struct ipadb_context *ipactx, LDAPMessage *entry, + char *attr, krb5_key_salt_tuple **enc_salt_types, + int *n_enc_salt_types); ++ ++/* CERTAUTH PLUGIN */ ++void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata); +diff --git a/daemons/ipa-kdb/ipa_kdb_certauth.c b/daemons/ipa-kdb/ipa_kdb_certauth.c +new file mode 100644 +index 0000000000000000000000000000000000000000..a53a2ce4e7ceb06ec8de117cdbca2666fdb5a97a +--- /dev/null ++++ b/daemons/ipa-kdb/ipa_kdb_certauth.c +@@ -0,0 +1,398 @@ ++/** BEGIN COPYRIGHT BLOCK ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Additional permission under GPLv3 section 7: ++ * ++ * In the following paragraph, "GPL" means the GNU General Public ++ * License, version 3 or any later version, and "Non-GPL Code" means ++ * code that is governed neither by the GPL nor a license ++ * compatible with the GPL. ++ * ++ * You may link the code of this Program with Non-GPL Code and convey ++ * linked combinations including the two, provided that such Non-GPL ++ * Code only links to the code of this Program through those well ++ * defined interfaces identified in the file named EXCEPTION found in ++ * the source code files (the "Approved Interfaces"). The files of ++ * Non-GPL Code may instantiate templates or use macros or inline ++ * functions from the Approved Interfaces without causing the resulting ++ * work to be covered by the GPL. Only the copyright holders of this ++ * Program may make changes or additions to the list of Approved ++ * Interfaces. ++ * ++ * Authors: ++ * Sumit Bose ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * All rights reserved. ++ * END COPYRIGHT BLOCK **/ ++ ++#include ++//#include ++#include ++#include ++ ++#include "util/ipa_krb5.h" ++#include "ipa_kdb.h" ++ ++#define IPA_OC_CERTMAP_RULE "ipaCertMapRule" ++#define IPA_CERTMAP_MAPRULE "ipaCertMapMapRule" ++#define IPA_CERTMAP_MATCHRULE "ipaCertMapMatchRule" ++#define IPA_CERTMAP_PRIORITY "ipaCertMapPriority" ++#define IPA_ENABLED_FLAG "ipaEnabledFlag" ++#define IPA_TRUE_VALUE "TRUE" ++#define IPA_ASSOCIATED_DOMAIN "associatedDomain" ++ ++#define OBJECTCLASS "objectClass" ++ ++#define CERTMAP_FILTER "(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \ ++ "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" ++ ++#ifndef discard_const ++#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) ++#endif ++ ++ ++struct krb5_certauth_moddata_st { ++ char *local_domain; ++ struct sss_certmap_ctx *sss_certmap_ctx; ++ struct ipadb_context *ipactx; ++}; ++ ++void ipa_certmap_debug(void *private, ++ const char *file, long line, ++ const char *function, ++ const char *format, ...) ++{ ++ va_list ap; ++ char str[255] = { 0 }; ++ ++ va_start(ap, format); ++ vsnprintf(str, sizeof(str)-1, format, ap); ++ va_end(ap); ++ krb5_klog_syslog(LOG_INFO, str); ++} ++ ++void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata) ++{ ++ if (moddata == NULL || *moddata == NULL) { ++ return; ++ } ++ ++ free((*moddata)->local_domain); ++ (*moddata)->local_domain = NULL; ++ sss_certmap_free_ctx((*moddata)->sss_certmap_ctx); ++ (*moddata)->sss_certmap_ctx = NULL; ++ ++ free(*moddata); ++ ++ return; ++} ++ ++static krb5_error_code ipa_get_init_data(krb5_context kcontext, ++ krb5_certauth_moddata moddata_out) ++{ ++ int ret; ++ struct sss_certmap_ctx *ctx = NULL; ++ struct ipadb_context *ipactx; ++ krb5_error_code kerr; ++ char *basedn = NULL; ++ LDAPMessage *result = NULL; ++ LDAPMessage *le; ++ LDAP *lc; ++ size_t c; ++ uint32_t prio; ++ char *map_rule = NULL; ++ char *match_rule = NULL; ++ char **domains = NULL; ++ ++ const char *certmap_attrs[] = { OBJECTCLASS, ++ IPA_CERTMAP_PRIORITY, ++ IPA_CERTMAP_MATCHRULE, ++ IPA_CERTMAP_MAPRULE, ++ IPA_ASSOCIATED_DOMAIN, ++ IPA_ENABLED_FLAG, ++ NULL}; ++ ++ ++ krb5_klog_syslog(LOG_INFO, "Initializing IPA certauth plugin."); ++ ++ ipactx = ipadb_get_context(kcontext); ++ if (ipactx == NULL) { ++ return KRB5_KDB_DBNOTINITED; ++ } ++ ++ if (ipactx->certauth_moddata == NULL) { ++ ret = asprintf(&basedn, "cn=certmap,%s", ipactx->base); ++ if (ret == -1) { ++ return ENOMEM; ++ } ++ ++ kerr = ipadb_simple_search(ipactx,basedn, LDAP_SCOPE_SUBTREE, ++ CERTMAP_FILTER, discard_const(certmap_attrs), ++ &result); ++ if (kerr != 0 && kerr != KRB5_KDB_NOENTRY) { ++ goto done; ++ } ++ ++ ret = sss_certmap_init(NULL, ipa_certmap_debug, NULL, &ctx); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ if (kerr == KRB5_KDB_NOENTRY) { ++ ret = sss_certmap_add_rule(ctx, SSS_CERTMAP_MIN_PRIO, ++ NULL, NULL, NULL); ++ if (ret != 0) { ++ goto done; ++ } ++ } else { ++ lc = ipactx->lcontext; ++ ++ for (le = ldap_first_entry(lc, result); le; ++ le = ldap_next_entry(lc, le)) { ++ prio = SSS_CERTMAP_MIN_PRIO; ++ ret = ipadb_ldap_attr_to_uint32(lc, le, IPA_CERTMAP_PRIORITY, ++ &prio); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } ++ ++ free(map_rule); ++ map_rule = NULL; ++ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MAPRULE, ++ &map_rule); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } ++ ++ free(match_rule); ++ match_rule = NULL; ++ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MATCHRULE, ++ &match_rule); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } ++ ++ if (domains != NULL) { ++ for (c = 0; domains[c] != NULL; c++) { ++ free(domains[c]); ++ } ++ free(domains); ++ domains = NULL; ++ } ++ ret = ipadb_ldap_attr_to_strlist(lc, le, IPA_ASSOCIATED_DOMAIN, ++ &domains); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } ++ ++ ret = sss_certmap_add_rule(ctx, prio, match_rule, map_rule, ++ (const char **) domains); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ } ++ ++ ipactx->certauth_moddata = moddata_out; ++ ++ if (ipactx->realm != NULL) { ++ ipactx->certauth_moddata->local_domain = strdup(ipactx->realm); ++ if (ipactx->certauth_moddata->local_domain == NULL) { ++ free(ipactx->certauth_moddata); ++ ipactx->certauth_moddata = NULL; ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ipactx->certauth_moddata->sss_certmap_ctx = ctx; ++ ipactx->certauth_moddata->ipactx = ipactx; ++ ++ } ++ ++ ret = 0; ++ ++done: ++ ldap_msgfree(result); ++ free(basedn); ++ free(map_rule); ++ free(match_rule); ++ if (domains != NULL) { ++ for (c = 0; domains[c] != NULL; c++) { ++ free(domains[c]); ++ } ++ free(domains); ++ domains = NULL; ++ } ++ ++ if (ret != 0) { ++ sss_certmap_free_ctx(ctx); ++ } ++ ++ return ret; ++} ++ ++static krb5_error_code ipa_certauth_authorize(krb5_context context, ++ krb5_certauth_moddata moddata, ++ const uint8_t *cert, ++ size_t cert_len, ++ krb5_const_principal princ, ++ const void *opts, ++ const krb5_db_entry *db_entry, ++ char ***authinds_out) ++{ ++ char *cert_filter = NULL; ++ char **domains = NULL; ++ int ret; ++ size_t c; ++ char *principal = NULL; ++ LDAPMessage *res = NULL; ++ krb5_error_code kerr; ++ LDAPMessage *lentry; ++ ++ if (moddata == NULL) { ++ return KRB5_PLUGIN_NO_HANDLE; ++ } ++ ++ if (moddata->sss_certmap_ctx == NULL) { ++ kerr = ipa_get_init_data(context, moddata); ++ if (kerr != 0) { ++ krb5_klog_syslog(LOG_ERR, "Failed to init certmapping data"); ++ return KRB5_PLUGIN_NO_HANDLE; ++ } ++ } ++ ++ ret = krb5_unparse_name(context, princ, &principal); ++ if (ret != 0) { ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } ++ krb5_klog_syslog(LOG_INFO, "Doing certauth authorize for [%s]", principal); ++ ++ ret = sss_certmap_get_search_filter(moddata->sss_certmap_ctx, ++ cert, cert_len, ++ &cert_filter, &domains); ++ if (ret != 0) { ++ if (ret == ENOENT) { ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ } ++ goto done; ++ } ++ krb5_klog_syslog(LOG_INFO, "Got cert filter [%s]", cert_filter); ++ ++ /* If there are no domains assigned the rule will apply to the local ++ * domain only. */ ++ if (domains != NULL) { ++ ++ if (moddata->local_domain == NULL) { ++ /* We don't know our own domain name, in general this should not ++ * happen. But to be fault tolerant we allow matching rule which ++ * do not have a domain assigned. */ ++ ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } ++ ++ for (c = 0; domains[c] != NULL; c++) { ++ if (strcasecmp(domains[c], moddata->local_domain) == 0) { ++ break; ++ } ++ } ++ ++ /* Our domain was not in the list */ ++ if (domains[c] == NULL) { ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } ++ } ++ ++ kerr = ipadb_fetch_principals_with_extra_filter(moddata->ipactx, ++ KRB5_KDB_FLAG_ALIAS_OK, ++ principal, ++ cert_filter, ++ &res); ++ if (kerr != 0) { ++ krb5_klog_syslog(LOG_ERR, "Search failed [%d]", kerr); ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } ++ ++ kerr = ipadb_find_principal(context, KRB5_KDB_FLAG_ALIAS_OK, res, ++ &principal, &lentry); ++ if (kerr == KRB5_KDB_NOENTRY) { ++ krb5_klog_syslog(LOG_INFO, "No matching entry found"); ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } else if (kerr != 0) { ++ krb5_klog_syslog(LOG_ERR, "ipadb_find_principal failed [%d]", kerr); ++ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; ++ goto done; ++ } ++ ++ /* TODO: add more tests ? */ ++ ++ ret = 0; ++ ++done: ++ sss_certmap_free_filter_and_domains(cert_filter, domains); ++ krb5_free_unparsed_name(context, principal); ++ ldap_msgfree(res); ++ ++ return ret; ++} ++ ++static krb5_error_code ipa_certauth_init(krb5_context kcontext, ++ krb5_certauth_moddata *moddata_out) ++{ ++ struct krb5_certauth_moddata_st *certauth_moddata; ++ ++ certauth_moddata = calloc(1, sizeof(struct krb5_certauth_moddata_st)); ++ if (certauth_moddata == NULL) { ++ return ENOMEM; ++ } ++ ++ *moddata_out = certauth_moddata; ++ ++ return 0; ++} ++ ++static void ipa_certauth_fini(krb5_context context, ++ krb5_certauth_moddata moddata_out) ++{ ++ krb5_klog_syslog(LOG_INFO, "IPA certauth plugin un-loaded."); ++ return; ++} ++ ++ ++krb5_error_code certauth_ipakdb_initvt(krb5_context context, ++ int maj_ver, int min_ver, ++ krb5_plugin_vtable vtable) ++{ ++ krb5_certauth_vtable vt; ++ ++ if (maj_ver != 1) { ++ return KRB5_PLUGIN_VER_NOTSUPP; ++ } ++ ++ vt = (krb5_certauth_vtable) vtable; ++ ++ vt->name = "ipakdb"; ++ vt->authorize = ipa_certauth_authorize; ++ vt->init = ipa_certauth_init; ++ vt->fini = ipa_certauth_fini; ++ /* currently we do not return authentication indicators */ ++ vt->free_ind = NULL; ++ return 0; ++} +diff --git a/freeipa.spec.in b/freeipa.spec.in +index f776b34af88cc8ccd02da0713cb6eaca161c99f5..18291a5793a6b69dcd719f42e80e1652169e5e1d 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -120,6 +120,7 @@ BuildRequires: libtalloc-devel + BuildRequires: libtevent-devel + BuildRequires: libuuid-devel + BuildRequires: libsss_idmap-devel ++BuildRequires: libsss_certmap-devel + # 1.14.0: sss_nss_getnamebycert (https://fedorahosted.org/sssd/ticket/2897) + BuildRequires: libsss_nss_idmap-devel >= 1.14.0 + BuildRequires: rhino +@@ -1164,6 +1165,7 @@ fi + %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck + %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf + %config(noreplace) %{_sysconfdir}/oddjobd.conf.d/ipa-server.conf ++%config(noreplace) %{_sysconfdir}/krb5.conf.d/ipa-certauth + %dir %{_libexecdir}/ipa/certmonger + %attr(755,root,root) %{_libexecdir}/ipa/certmonger/* + # NOTE: systemd specific section +diff --git a/server.m4 b/server.m4 +index 92b5cdd3a6ff90b70a9002360ff3d3aec5053392..7b2e94df91a4803849e496142788a4ed87ef487d 100644 +--- a/server.m4 ++++ b/server.m4 +@@ -30,6 +30,19 @@ dnl -- sss_idmap is needed by the extdom exop -- + PKG_CHECK_MODULES([SSSIDMAP], [sss_idmap]) + PKG_CHECK_MODULES([SSSNSSIDMAP], [sss_nss_idmap >= 1.13.90]) + ++dnl -- sss_certmap and certauth.h are needed by the IPA KDB certauth plugin -- ++PKG_CHECK_EXISTS([sss_certmap], ++ [PKG_CHECK_MODULES([SSSCERTMAP], [sss_certmap])], ++ [AC_MSG_NOTICE([sss_certmap not found])]) ++AC_CHECK_HEADER([krb5/certauth_plugin.h], ++ [have_certauth_plugin=yes], ++ [have_certauth_plugin=no]) ++AM_CONDITIONAL([BUILD_IPA_CERTAUTH_PLUGIN], ++ [test x$have_certauth_plugin = xyes -a x"$SSSCERTMAP_LIBS" != x]) ++AM_COND_IF([BUILD_IPA_CERTAUTH_PLUGIN], ++ [AC_MSG_NOTICE([Build IPA KDB certauth plugin])], ++ [AC_MSG_WARN([Cannot build IPA KDB certauth plugin])]) ++ + dnl --------------------------------------------------------------------------- + dnl - Check for KRB5 krad + dnl --------------------------------------------------------------------------- +-- +2.12.1 + diff --git a/SOURCES/0020-configure-fix-disable-server-with-certauth-plugin.patch b/SOURCES/0020-configure-fix-disable-server-with-certauth-plugin.patch new file mode 100644 index 0000000..67fec06 --- /dev/null +++ b/SOURCES/0020-configure-fix-disable-server-with-certauth-plugin.patch @@ -0,0 +1,55 @@ +From 1dca2667b1e43540c377a45b0f653b0e9bc8840d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 27 Mar 2017 12:18:53 +0200 +Subject: [PATCH] configure: fix --disable-server with certauth plugin + +Resolves https://pagure.io/freeipa/issue/6816 + +Reviewed-By: Christian Heimes +--- + configure.ac | 12 ++++++++++++ + server.m4 | 5 ----- + 2 files changed, 12 insertions(+), 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 2d84426d1039e822fa3ee53410c819274e763e32..8d4b82e4590e9e122f7aa5684fd78834c4b6a204 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -225,6 +225,18 @@ AM_COND_IF([ENABLE_SERVER], [ + ]) + + dnl --------------------------------------------------------------------------- ++dnl - Check if IPA certauth plugin can be build ++dnl --------------------------------------------------------------------------- ++ ++AM_CONDITIONAL([BUILD_IPA_CERTAUTH_PLUGIN], ++ [test x$have_certauth_plugin = xyes -a x"$SSSCERTMAP_LIBS" != x]) ++AM_COND_IF([BUILD_IPA_CERTAUTH_PLUGIN], [ ++ AM_COND_IF([ENABLE_SERVER], ++ [AC_MSG_NOTICE([Build IPA KDB certauth plugin])], ++ [AC_MSG_WARN([Cannot build IPA KDB certauth plugin])]) ++]) ++ ++dnl --------------------------------------------------------------------------- + dnl - Check for program paths + dnl --------------------------------------------------------------------------- + AC_PATH_PROG(UNLINK, unlink, [AC_MSG_ERROR([unlink not found])]) +diff --git a/server.m4 b/server.m4 +index 7b2e94df91a4803849e496142788a4ed87ef487d..a4c99195ae535e586445cf5bbe9fef457d224531 100644 +--- a/server.m4 ++++ b/server.m4 +@@ -37,11 +37,6 @@ PKG_CHECK_EXISTS([sss_certmap], + AC_CHECK_HEADER([krb5/certauth_plugin.h], + [have_certauth_plugin=yes], + [have_certauth_plugin=no]) +-AM_CONDITIONAL([BUILD_IPA_CERTAUTH_PLUGIN], +- [test x$have_certauth_plugin = xyes -a x"$SSSCERTMAP_LIBS" != x]) +-AM_COND_IF([BUILD_IPA_CERTAUTH_PLUGIN], +- [AC_MSG_NOTICE([Build IPA KDB certauth plugin])], +- [AC_MSG_WARN([Cannot build IPA KDB certauth plugin])]) + + dnl --------------------------------------------------------------------------- + dnl - Check for KRB5 krad +-- +2.12.1 + diff --git a/SOURCES/0021-ipa-kdb-do-not-depend-on-certauth_plugin.h.patch b/SOURCES/0021-ipa-kdb-do-not-depend-on-certauth_plugin.h.patch new file mode 100644 index 0000000..fcbc083 --- /dev/null +++ b/SOURCES/0021-ipa-kdb-do-not-depend-on-certauth_plugin.h.patch @@ -0,0 +1,85 @@ +From 1c421b3874488c0021a5e0d344be31c84c2b4bd0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 27 Mar 2017 13:19:57 +0200 +Subject: [PATCH] ipa-kdb: do not depend on certauth_plugin.h + +Related to https://pagure.io/freeipa/issue/4905 + +Reviewed-By: Christian Heimes +--- + configure.ac | 2 ++ + daemons/ipa-kdb/ipa_kdb.c | 2 ++ + daemons/ipa-kdb/ipa_kdb.h | 8 ++++++++ + 3 files changed, 12 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 8d4b82e4590e9e122f7aa5684fd78834c4b6a204..ded1d71fd079a5f6947ef0627fb699783c8cc109 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -231,6 +231,8 @@ dnl --------------------------------------------------------------------------- + AM_CONDITIONAL([BUILD_IPA_CERTAUTH_PLUGIN], + [test x$have_certauth_plugin = xyes -a x"$SSSCERTMAP_LIBS" != x]) + AM_COND_IF([BUILD_IPA_CERTAUTH_PLUGIN], [ ++ AC_DEFINE([HAVE_KRB5_CERTAUTH_PLUGIN], [1], ++ [MIT Kerberos version supports certauth plugin]) + AM_COND_IF([ENABLE_SERVER], + [AC_MSG_NOTICE([Build IPA KDB certauth plugin])], + [AC_MSG_WARN([Cannot build IPA KDB certauth plugin])]) +diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c +index a961e4e57cf5379eb237551d56e3bc8dc82d952d..050bfc90cef1bce4c932f54bb6050438c60ca79f 100644 +--- a/daemons/ipa-kdb/ipa_kdb.c ++++ b/daemons/ipa-kdb/ipa_kdb.c +@@ -67,7 +67,9 @@ static void ipadb_context_free(krb5_context kcontext, + } + free(cfg->authz_data); + ++#ifdef HAVE_KRB5_CERTAUTH_PLUGIN + ipa_certauth_free_moddata(&((*ctx)->certauth_moddata)); ++#endif + + free(*ctx); + *ctx = NULL; +diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h +index 632c1979d15e88aec86d5e408ed6c7017d8362b8..72573a61adecfae152796d61b88b6c43b3a975a3 100644 +--- a/daemons/ipa-kdb/ipa_kdb.h ++++ b/daemons/ipa-kdb/ipa_kdb.h +@@ -30,6 +30,8 @@ + * filtering purposes */ + #define SECURID 1 + ++#include "config.h" ++ + #include + #include + #include +@@ -40,7 +42,9 @@ + #include + #include + #include ++#ifdef HAVE_KRB5_CERTAUTH_PLUGIN + #include ++#endif + + #include "ipa_krb5.h" + #include "ipa_pwd.h" +@@ -112,7 +116,9 @@ struct ipadb_context { + krb5_key_salt_tuple *def_encs; + int n_def_encs; + struct ipadb_mspac *mspac; ++#ifdef HAVE_KRB5_CERTAUTH_PLUGIN + krb5_certauth_moddata certauth_moddata; ++#endif + + /* Don't access this directly, use ipadb_get_global_config(). */ + struct ipadb_global_config config; +@@ -334,5 +340,7 @@ int ipadb_get_enc_salt_types(struct ipadb_context *ipactx, LDAPMessage *entry, + char *attr, krb5_key_salt_tuple **enc_salt_types, + int *n_enc_salt_types); + ++#ifdef HAVE_KRB5_CERTAUTH_PLUGIN + /* CERTAUTH PLUGIN */ + void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata); ++#endif +-- +2.12.1 + diff --git a/SOURCES/0022-WebUI-Add-support-for-suppressing-warnings.patch b/SOURCES/0022-WebUI-Add-support-for-suppressing-warnings.patch new file mode 100644 index 0000000..8a4d26c --- /dev/null +++ b/SOURCES/0022-WebUI-Add-support-for-suppressing-warnings.patch @@ -0,0 +1,46 @@ +From 3d8b42ac1e532168c2dae96ab0de3d83df0268d0 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Fri, 17 Mar 2017 15:10:42 +0100 +Subject: [PATCH] WebUI: Add support for suppressing warnings + +Each command can have specified an array of warning codes which will +be suppressed and won't be shown. + +For specifying this it is necessary to set command property +'supressed_warnings: [codes_of_warning]' + +Part of: https://pagure.io/freeipa/issue/6618 + +Reviewed-By: Petr Vobornik +--- + install/ui/src/freeipa/rpc.js | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/install/ui/src/freeipa/rpc.js b/install/ui/src/freeipa/rpc.js +index 1880f8d5732f982c25924b787b273c9e56636b20..84282f78d940ac2d18d00df92a7430ca51bbf389 100644 +--- a/install/ui/src/freeipa/rpc.js ++++ b/install/ui/src/freeipa/rpc.js +@@ -72,6 +72,12 @@ rpc.command = function(spec) { + that.options = $.extend({}, spec.options || {}); + + /** ++ * @property {Array} suppress_warnings array of message codes which ++ * are suppressed ++ */ ++ that.suppress_warnings = spec.suppress_warnings || []; ++ ++ /** + * Success handler + * @property {Function} + * @param {Object} data +@@ -219,6 +225,7 @@ rpc.command = function(spec) { + + for (var i=0,l=msgs.length; i -1) continue; + // escape and reformat message + msg.message = util.beautify_message(msg.message); + IPA.notify(msg.message, msg.type); +-- +2.12.1 + diff --git a/SOURCES/0023-WebUI-suppress-truncation-warning-in-select-widget.patch b/SOURCES/0023-WebUI-suppress-truncation-warning-in-select-widget.patch new file mode 100644 index 0000000..b36a576 --- /dev/null +++ b/SOURCES/0023-WebUI-suppress-truncation-warning-in-select-widget.patch @@ -0,0 +1,37 @@ +From 66ea6269d7ad401e5f89b1ab33f8e827efb25dd8 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Fri, 17 Mar 2017 15:10:49 +0100 +Subject: [PATCH] WebUI: suppress truncation warning in select widget + +This widget is used on details pages and dialogs. When the size limit +is set to lower number the warning about truncation was shown every time +the details page was open. + +Now, with support for suppressing warning messages from server according +to its code, we are able to disable warning with 13017 code (truncation +warning) + +https://pagure.io/freeipa/issue/6618 + +Reviewed-By: Petr Vobornik +--- + install/ui/src/freeipa/widget.js | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js +index 223b449962fabb47cf72b0443e39c295c783ab7f..b7a6504cf4af942c99ee217a2b47718af9e40f86 100644 +--- a/install/ui/src/freeipa/widget.js ++++ b/install/ui/src/freeipa/widget.js +@@ -5012,7 +5012,8 @@ IPA.entity_select_widget = function(spec) { + entity: that.other_entity.name, + method: 'find', + args: [filter], +- options: that.filter_options ++ options: that.filter_options, ++ suppress_warnings: [13017] + }); + var no_members = metadata.get('@mc-opt:' + cmd.get_command() + ':no_members'); + if (no_members) { +-- +2.12.1 + diff --git a/SOURCES/0024-WebUI-Fix-showing-vault-in-selfservice-view.patch b/SOURCES/0024-WebUI-Fix-showing-vault-in-selfservice-view.patch new file mode 100644 index 0000000..d729a24 --- /dev/null +++ b/SOURCES/0024-WebUI-Fix-showing-vault-in-selfservice-view.patch @@ -0,0 +1,54 @@ +From 5ccffb9ca109d820c5535140713a5b6672aa4f71 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Fri, 24 Mar 2017 10:19:21 +0100 +Subject: [PATCH] WebUI: Fix showing vault in selfservice view + +Vaults menu item was shown even when the KRA service was not installed. +That was caused by different path to the menu item in admin's view +and in selfservice view. + +The path is now set correctly for both situations. 'network_service/vault' +for admin's view and 'vault' for selfservice view. + +https://pagure.io/freeipa/issue/6812 + +Reviewed-By: Petr Vobornik +--- + install/ui/src/freeipa/navigation/menu_spec.js | 1 + + install/ui/src/freeipa/vault.js | 8 +++++--- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js +index 9329694c14a47cbe1ec244554327b40743044d7b..0c30459691d8f652dc35ccf74ed27fae7654020d 100644 +--- a/install/ui/src/freeipa/navigation/menu_spec.js ++++ b/install/ui/src/freeipa/navigation/menu_spec.js +@@ -326,6 +326,7 @@ nav.self_service = { + { entity: 'user' }, + { entity: 'otptoken' }, + { ++ name: 'vault', + entity: 'vault', + facet: 'search', + children: [ +diff --git a/install/ui/src/freeipa/vault.js b/install/ui/src/freeipa/vault.js +index b5cdc810adea9b521df77eb328b55475a707580a..36a4838ee108020cf6ad7a20c59e4ab5403f3528 100644 +--- a/install/ui/src/freeipa/vault.js ++++ b/install/ui/src/freeipa/vault.js +@@ -809,9 +809,11 @@ vault.config_sidebar_policy = function(spec) { + + + vault.remove_vault_menu_item = function() { +- if (!IPA.vault_enabled) { +- menu.remove_item('network_services/vault'); +- } ++ if (IPA.vault_enabled) return; ++ ++ var menu_location = IPA.is_selfservice ? 'vault' : 'network_services/vault'; ++ ++ menu.remove_item(menu_location); + }; + + vault.my_vault_spec = make_my_vault_spec(); +-- +2.12.1 + diff --git a/SOURCES/0025-Set-KDC-Disable-Last-Success-by-default.patch b/SOURCES/0025-Set-KDC-Disable-Last-Success-by-default.patch new file mode 100644 index 0000000..f5abe43 --- /dev/null +++ b/SOURCES/0025-Set-KDC-Disable-Last-Success-by-default.patch @@ -0,0 +1,35 @@ +From ac3c0d46d947c59aa25f4c9268ef17023c87b4b2 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 22 Mar 2017 17:47:04 +0100 +Subject: [PATCH] Set "KDC:Disable Last Success" by default + +In big deployments enabled recording of the last sucesfull login +this creates a huge changelog on DS side and cause performance +issues even if this is excluded from replication. + +Actually this is not used directly by FreeIPA so it is safe to remove +in new installations. User who need this must manually remove +"KDC:Disable Last Success" using `ipa config-mod` command or WebUI. + +https://pagure.io/freeipa/issue/5313 + +Reviewed-By: Stanislav Laznicka +--- + install/share/bootstrap-template.ldif | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif +index da12ddf0ca887e8305402048ceed5d5b28816164..ea1e5b222e7af5ed7c5d80bbaf9282735e425e18 100644 +--- a/install/share/bootstrap-template.ldif ++++ b/install/share/bootstrap-template.ldif +@@ -410,6 +410,7 @@ ipaUserObjectClasses: ipasshuser + ipaDefaultEmailDomain: $DOMAIN + ipaMigrationEnabled: FALSE + ipaConfigString: AllowNThash ++ipaConfigString: KDC:Disable Last Success + ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023 + ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023 + +-- +2.12.1 + diff --git a/SOURCES/0026-WebUI-Allow-to-add-certs-to-certmapping-with-CERT-LI.patch b/SOURCES/0026-WebUI-Allow-to-add-certs-to-certmapping-with-CERT-LI.patch new file mode 100644 index 0000000..eb02c8d --- /dev/null +++ b/SOURCES/0026-WebUI-Allow-to-add-certs-to-certmapping-with-CERT-LI.patch @@ -0,0 +1,69 @@ +From be6eedde5a5aaf7ad1b527c0cfb9699ccb98a6b5 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Mon, 27 Mar 2017 14:14:32 +0200 +Subject: [PATCH] WebUI: Allow to add certs to certmapping with CERT LINES + around + +The certificate to the certmapping might be inserted as +base64 encoded blob. This patch allows to also insert the certificate +blob with surrounding "-----BEGIN CERTIFICATE-----" and +"-----END CERTIFICATE-----" lines. This behavior is the same in +widget for assigning certificates to users, so the change helps +WebUI to be more consistent. + +https://pagure.io/freeipa/issue/6772 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Petr Vobornik +--- + install/ui/src/freeipa/plugins/certmap.js | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/install/ui/src/freeipa/plugins/certmap.js b/install/ui/src/freeipa/plugins/certmap.js +index ecbe095b9ead5c3dad70380202836608d564cd58..c613601e989f065a3d6289b02b60563020acf978 100644 +--- a/install/ui/src/freeipa/plugins/certmap.js ++++ b/install/ui/src/freeipa/plugins/certmap.js +@@ -8,6 +8,7 @@ define([ + 'dojo/_base/declare', + 'dojo/Evented', + 'dojo/on', ++ '../certificate', + '../navigation', + '../field', + '../ipa', +@@ -19,8 +20,8 @@ define([ + // plain imports + '../search', + '../entity'], +- function(lang, declare, Evented, on, navigation, mod_field, IPA, +- phases, reg, widget_mod, text, util) { ++ function(lang, declare, Evented, on, certificate, navigation, ++ mod_field, IPA, phases, reg, widget_mod, text, util) { + /** + * Certificate map module + * @class +@@ -312,6 +313,12 @@ certmap.certmap_multivalued_widget = function (spec) { + var widget = widgets[0]; + var inner_widgets = widget.widgets.get_widgets(); + ++ var normalize_certs = function(certs) { ++ for (var k = 0, l = certs.length; k +Date: Fri, 24 Mar 2017 14:47:38 +0100 +Subject: [PATCH] Bump samba version for FIPS and priv. separation + +With the latest Samba, adding trusts to AD under FIPS should now work +as well as adding trusts as a whole after the privilege separation +rework. + +https://pagure.io/freeipa/issue/6671 +https://pagure.io/freeipa/issue/6697 + +Reviewed-By: Martin Basti +--- + freeipa.spec.in | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 18291a5793a6b69dcd719f42e80e1652169e5e1d..5419ed10723fc7aa3ecc1b3f66b3ef1c8b38b12f 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -36,11 +36,13 @@ + + %global alt_name ipa + %if 0%{?rhel} +-%global samba_version 4.0.5-1 ++# Require 4.6.0-4 which brings RC4 for FIPS + trust fixes to priv. separation ++%global samba_version 4.6.0-4 + %global selinux_policy_version 3.12.1-153 + %global slapi_nis_version 0.56.0-4 + %else +-%global samba_version 2:4.0.5-1 ++# Require 4.6.0-4 which brings RC4 for FIPS + trust fixes to priv. separation ++%global samba_version 2:4.6.0-4 + %global selinux_policy_version 3.13.1-158.4 + %global slapi_nis_version 0.56.1 + %endif +-- +2.12.1 + diff --git a/SOURCES/0028-Reworked-the-renaming-mechanism.patch b/SOURCES/0028-Reworked-the-renaming-mechanism.patch new file mode 100644 index 0000000..36f0a8d --- /dev/null +++ b/SOURCES/0028-Reworked-the-renaming-mechanism.patch @@ -0,0 +1,296 @@ +From bd2a0a8d363af6c8b1491314d5da5f3c146e4ce6 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 27 Mar 2017 08:18:29 +0200 +Subject: [PATCH] Reworked the renaming mechanism + +The rename operation on *_mod commands was only allowed when +the primary key of an entry was also its RDN. With these changes, +it should be possible to rename the rest of the entries as well. + +An attribute to the base LDAPObject was added to whitelist the +objects we want to allow to be renamed. It replaced an old +attribute rdn_is_primary_key which was used for the very same +purpose but the name was confusing because it was not set +correctly for certain objects. + +https://pagure.io/freeipa/issue/2466 +https://pagure.io/freeipa/issue/6784 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +--- + ipaserver/plugins/automount.py | 2 +- + ipaserver/plugins/baseldap.py | 32 ++++++++++++++++++++------------ + ipaserver/plugins/baseuser.py | 2 +- + ipaserver/plugins/ca.py | 2 +- + ipaserver/plugins/dns.py | 2 +- + ipaserver/plugins/group.py | 2 +- + ipaserver/plugins/idviews.py | 6 +++--- + ipaserver/plugins/otptoken.py | 2 +- + ipaserver/plugins/permission.py | 2 +- + ipaserver/plugins/privilege.py | 2 +- + ipaserver/plugins/radiusproxy.py | 2 +- + ipaserver/plugins/role.py | 2 +- + ipaserver/plugins/servicedelegation.py | 2 +- + 13 files changed, 34 insertions(+), 26 deletions(-) + +diff --git a/ipaserver/plugins/automount.py b/ipaserver/plugins/automount.py +index c4cf2d6db876e13c78ecd73fc53bb356bf190e17..03f994c65832e7b6099739e951105c4b5a897391 100644 +--- a/ipaserver/plugins/automount.py ++++ b/ipaserver/plugins/automount.py +@@ -456,7 +456,7 @@ class automountkey(LDAPObject): + default_attributes = [ + 'automountkey', 'automountinformation', 'description' + ] +- rdn_is_primary_key = True ++ allow_rename = True + rdn_separator = ' ' + + takes_params = ( +diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py +index 79ba7fc4a14f8105cda481e1599b2acbd8394e45..dbe3cbd28c85ebc3d9254e24e14c5701adc673ab 100644 +--- a/ipaserver/plugins/baseldap.py ++++ b/ipaserver/plugins/baseldap.py +@@ -36,7 +36,7 @@ from ipalib.text import _ + from ipalib.util import json_serialize, validate_hostname + from ipalib.capabilities import client_has_capability + from ipalib.messages import add_message, SearchResultTruncated +-from ipapython.dn import DN ++from ipapython.dn import DN, RDN + from ipapython.version import API_VERSION + + if six.PY3: +@@ -549,7 +549,7 @@ class LDAPObject(Object): + rdn_attribute = '' + uuid_attribute = '' + attribute_members = {} +- rdn_is_primary_key = False # Do we need RDN change to do a rename? ++ allow_rename = False + password_attributes = [] + # Can bind as this entry (has userPassword or krbPrincipalKey) + bindable = False +@@ -1384,7 +1384,7 @@ class LDAPUpdate(LDAPQuery, crud.Update): + def get_options(self): + for option in super(LDAPUpdate, self).get_options(): + yield option +- if self.obj.rdn_is_primary_key: ++ if self.obj.allow_rename: + yield self._get_rename_option() + + def execute(self, *keys, **options): +@@ -1419,15 +1419,19 @@ class LDAPUpdate(LDAPQuery, crud.Update): + _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), list(entry_attrs), allow_only=False) + + rdnupdate = False +- try: +- if self.obj.rdn_is_primary_key and 'rename' in options: +- if not options['rename']: +- raise errors.ValidationError(name='rename', error=u'can\'t be empty') +- entry_attrs[self.obj.primary_key.name] = options['rename'] +- +- if self.obj.rdn_is_primary_key and self.obj.primary_key.name in entry_attrs: ++ if 'rename' in options: ++ if not options['rename']: ++ raise errors.ValidationError( ++ name='rename', error=u'can\'t be empty') ++ entry_attrs[self.obj.primary_key.name] = options['rename'] ++ ++ # if setattr was used to change the RDN, the primary_key.name is ++ # already in entry_attrs ++ if self.obj.allow_rename and self.obj.primary_key.name in entry_attrs: ++ # perform RDN change if the primary key is also RDN ++ if (RDN((self.obj.primary_key.name, keys[-1])) == ++ entry_attrs.dn[0]): + try: +- # RDN change + new_dn = DN((self.obj.primary_key.name, + entry_attrs[self.obj.primary_key.name]), + *entry_attrs.dn[1:]) +@@ -1435,17 +1439,21 @@ class LDAPUpdate(LDAPQuery, crud.Update): + entry_attrs.dn, + new_dn) + +- rdnkeys = keys[:-1] + (entry_attrs[self.obj.primary_key.name], ) ++ rdnkeys = (keys[:-1] + ++ (entry_attrs[self.obj.primary_key.name], )) + entry_attrs.dn = self.obj.get_dn(*rdnkeys) + options['rdnupdate'] = True + rdnupdate = True + except errors.EmptyModlist: + # Attempt to rename to the current name, ignore + pass ++ except errors.NotFound: ++ self.obj.handle_not_found(*keys) + finally: + # Delete the primary_key from entry_attrs either way + del entry_attrs[self.obj.primary_key.name] + ++ try: + # Exception callbacks will need to test for options['rdnupdate'] + # to decide what to do. An EmptyModlist in this context doesn't + # mean an error occurred, just that there were no other updates to +diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py +index 44adc76ec854dadbe0d8a4e8ca03e71c30df526c..bf24dbf542d3b481671dfe4e8cee14a2edcc26e0 100644 +--- a/ipaserver/plugins/baseuser.py ++++ b/ipaserver/plugins/baseuser.py +@@ -164,7 +164,7 @@ class baseuser(LDAPObject): + 'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], + 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], + } +- rdn_is_primary_key = True ++ allow_rename = True + bindable = True + password_attributes = [('userpassword', 'has_password'), + ('krbprincipalkey', 'has_keytab')] +diff --git a/ipaserver/plugins/ca.py b/ipaserver/plugins/ca.py +index f774f78bd6d4ad236b37d06b8b267e8dd78f93b7..9bb163dffa645c1cbb10976e62cbd4a714139319 100644 +--- a/ipaserver/plugins/ca.py ++++ b/ipaserver/plugins/ca.py +@@ -68,7 +68,7 @@ class ca(LDAPObject): + 'cn', 'description', 'ipacaid', 'ipacaissuerdn', 'ipacasubjectdn', + ] + rdn_attribute = 'cn' +- rdn_is_primary_key = True ++ allow_rename = True + label = _('Certificate Authorities') + label_singular = _('Certificate Authority') + +diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py +index 7007928f3b4b2fd863077193671a03ae46119dc5..47ac963a0ae26fcaa81e70a8143bd7d0c172d20e 100644 +--- a/ipaserver/plugins/dns.py ++++ b/ipaserver/plugins/dns.py +@@ -3000,7 +3000,7 @@ class dnsrecord(LDAPObject): + possible_objectclasses = ['idnsTemplateObject'] + permission_filter_objectclasses = ['idnsrecord'] + default_attributes = ['idnsname'] + _record_attributes +- rdn_is_primary_key = True ++ allow_rename = True + + label = _('DNS Resource Records') + label_singular = _('DNS Resource Record') +diff --git a/ipaserver/plugins/group.py b/ipaserver/plugins/group.py +index 218da3c94d95bb399761acf9414182eff566c63b..1fb092d5f049e86f12681e5eb2397f98f1001697 100644 +--- a/ipaserver/plugins/group.py ++++ b/ipaserver/plugins/group.py +@@ -173,7 +173,7 @@ class group(LDAPObject): + 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', + 'sudorule'], + } +- rdn_is_primary_key = True ++ allow_rename = True + managed_permissions = { + 'System: Read Groups': { + 'replaces_global_anonymous_aci': True, +diff --git a/ipaserver/plugins/idviews.py b/ipaserver/plugins/idviews.py +index 6d4ac75209ea08e3c2969d53e1ae5372c3a535ac..b5ee32cf138677874497e2f345c932c352e20054 100644 +--- a/ipaserver/plugins/idviews.py ++++ b/ipaserver/plugins/idviews.py +@@ -97,7 +97,7 @@ class idview(LDAPObject): + object_class = ['ipaIDView', 'top'] + possible_objectclasses = ['ipaNameResolutionData'] + default_attributes = ['cn', 'description', 'ipadomainresolutionorder'] +- rdn_is_primary_key = True ++ allow_rename = True + + label = _('ID Views') + label_singular = _('ID View') +@@ -848,7 +848,7 @@ class idoverrideuser(baseidoverride): + + label = _('User ID overrides') + label_singular = _('User ID override') +- rdn_is_primary_key = True ++ allow_rename = True + + # ID user overrides are bindable because we map SASL GSSAPI + # authentication of trusted users to ID user overrides in the +@@ -964,7 +964,7 @@ class idoverridegroup(baseidoverride): + + label = _('Group ID overrides') + label_singular = _('Group ID override') +- rdn_is_primary_key = True ++ allow_rename = True + + permission_filter_objectclasses = ['ipaGroupOverride'] + managed_permissions = { +diff --git a/ipaserver/plugins/otptoken.py b/ipaserver/plugins/otptoken.py +index 98ecbe58b84622d7937a7e6eff77c5e41624bf4f..c66f0980f0fc2ed49b4224be40a18ce528a6da7b 100644 +--- a/ipaserver/plugins/otptoken.py ++++ b/ipaserver/plugins/otptoken.py +@@ -143,7 +143,7 @@ class otptoken(LDAPObject): + relationships = { + 'managedby': ('Managed by', 'man_by_', 'not_man_by_'), + } +- rdn_is_primary_key = True ++ allow_rename = True + + label = _('OTP Tokens') + label_singular = _('OTP Token') +diff --git a/ipaserver/plugins/permission.py b/ipaserver/plugins/permission.py +index dd2a0183e90ed6da9e55fb0590ea0bd81bf0bd67..977c6fe363c501f820aa82ae5b2ea00d8c78c7ae 100644 +--- a/ipaserver/plugins/permission.py ++++ b/ipaserver/plugins/permission.py +@@ -188,7 +188,7 @@ class permission(baseldap.LDAPObject): + 'member': ['privilege'], + 'memberindirect': ['role'], + } +- rdn_is_primary_key = True ++ allow_rename = True + managed_permissions = { + 'System: Read Permissions': { + 'replaces_global_anonymous_aci': True, +diff --git a/ipaserver/plugins/privilege.py b/ipaserver/plugins/privilege.py +index b3afbd289ac2e82d5569b5d5306be398a560413e..01d5396902d482eb5a9f21e7ece730a0a35157d6 100644 +--- a/ipaserver/plugins/privilege.py ++++ b/ipaserver/plugins/privilege.py +@@ -101,7 +101,7 @@ class privilege(LDAPObject): + reverse_members = { + 'member': ['permission'], + } +- rdn_is_primary_key = True ++ allow_rename = True + managed_permissions = { + 'System: Read Privileges': { + 'replaces_global_anonymous_aci': True, +diff --git a/ipaserver/plugins/radiusproxy.py b/ipaserver/plugins/radiusproxy.py +index 3391b8aed77205fb1a586d5472d8cfdbc9fd1cd5..be77c62432066beec951e5f50afe689e1d6debce 100644 +--- a/ipaserver/plugins/radiusproxy.py ++++ b/ipaserver/plugins/radiusproxy.py +@@ -101,7 +101,7 @@ class radiusproxy(LDAPObject): + 'ipatokenradiustimeout', 'ipatokenradiusretries', 'ipatokenusermapattribute' + ] + search_attributes = ['cn', 'description', 'ipatokenradiusserver'] +- rdn_is_primary_key = True ++ allow_rename = True + label = _('RADIUS Servers') + label_singular = _('RADIUS Server') + +diff --git a/ipaserver/plugins/role.py b/ipaserver/plugins/role.py +index 5d0d1f8c657b8d840762135f5ff16db90fb4893f..e7f115c461a6a0421f9c43d0410daaf9d4307e76 100644 +--- a/ipaserver/plugins/role.py ++++ b/ipaserver/plugins/role.py +@@ -92,7 +92,7 @@ class role(LDAPObject): + reverse_members = { + 'member': ['privilege'], + } +- rdn_is_primary_key = True ++ allow_rename = True + managed_permissions = { + 'System: Read Roles': { + 'replaces_global_anonymous_aci': True, +diff --git a/ipaserver/plugins/servicedelegation.py b/ipaserver/plugins/servicedelegation.py +index c8052e957cc5f8d24f6a8d0621ca93422052e35b..4f94924fa76691bcd6c6fc2cef9eb7fb30fce48c 100644 +--- a/ipaserver/plugins/servicedelegation.py ++++ b/ipaserver/plugins/servicedelegation.py +@@ -138,7 +138,7 @@ class servicedelegation(LDAPObject): + }, + } + +- rdn_is_primary_key = True ++ allow_rename = True + + takes_params = ( + Str( +-- +2.12.1 + diff --git a/SOURCES/0029-Allow-renaming-of-the-HBAC-rule-objects.patch b/SOURCES/0029-Allow-renaming-of-the-HBAC-rule-objects.patch new file mode 100644 index 0000000..2247186 --- /dev/null +++ b/SOURCES/0029-Allow-renaming-of-the-HBAC-rule-objects.patch @@ -0,0 +1,103 @@ +From 496255286bdf83c11deeba08755de56e639de000 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 27 Mar 2017 08:25:04 +0200 +Subject: [PATCH] Allow renaming of the HBAC rule objects + +The recent changes allow HBAC rule objects to be renamed. + +https://pagure.io/freeipa/issue/6784 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +--- + API.txt | 3 ++- + VERSION.m4 | 4 ++-- + ipaserver/plugins/hbacrule.py | 1 + + ipatests/test_xmlrpc/test_hbac_plugin.py | 15 +++++++++++++++ + 4 files changed, 20 insertions(+), 3 deletions(-) + +diff --git a/API.txt b/API.txt +index f0bd1b6495854decf4470efdcf1f2d915ce71c52..2a63c983a343b07ec7928bc774c6443a84b7c64c 100644 +--- a/API.txt ++++ b/API.txt +@@ -2163,7 +2163,7 @@ output: ListOfEntries('result') + output: Output('summary', type=[, ]) + output: Output('truncated', type=[]) + command: hbacrule_mod/1 +-args: 1,16,3 ++args: 1,17,3 + arg: Str('cn', cli_name='name') + option: StrEnum('accessruletype?', autofill=False, cli_name='type', default=u'allow', values=[u'allow', u'deny']) + option: Str('addattr*', cli_name='addattr') +@@ -2175,6 +2175,7 @@ option: StrEnum('hostcategory?', autofill=False, cli_name='hostcat', values=[u'a + option: Bool('ipaenabledflag?', autofill=False) + option: Flag('no_members', autofill=True, default=False) + option: Flag('raw', autofill=True, cli_name='raw', default=False) ++option: Str('rename?', cli_name='rename') + option: Flag('rights', autofill=True, default=False) + option: StrEnum('servicecategory?', autofill=False, cli_name='servicecat', values=[u'all']) + option: Str('setattr*', cli_name='setattr') +diff --git a/VERSION.m4 b/VERSION.m4 +index 743f2dbe0d05126f11c67574c5a9b712cb1f112d..bbb5212e5b8cd9604b6ec90d4a0bd4c3276b1856 100644 +--- a/VERSION.m4 ++++ b/VERSION.m4 +@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000) + # # + ######################################################## + define(IPA_API_VERSION_MAJOR, 2) +-define(IPA_API_VERSION_MINOR, 223) +-# Last change: Add domain resolution order to ID views ++define(IPA_API_VERSION_MINOR, 224) ++# Last change: Add rename option to HBAC rule objects + + + ######################################################## +diff --git a/ipaserver/plugins/hbacrule.py b/ipaserver/plugins/hbacrule.py +index 60e5e606fff6d2ffb93db608328c5987b91d1fa8..2495702e87accaf60eb38dae0fb122ac0764452f 100644 +--- a/ipaserver/plugins/hbacrule.py ++++ b/ipaserver/plugins/hbacrule.py +@@ -141,6 +141,7 @@ class hbacrule(LDAPObject): + ] + uuid_attribute = 'ipauniqueid' + rdn_attribute = 'ipauniqueid' ++ allow_rename = True + attribute_members = { + 'memberuser': ['user', 'group'], + 'memberhost': ['host', 'hostgroup'], +diff --git a/ipatests/test_xmlrpc/test_hbac_plugin.py b/ipatests/test_xmlrpc/test_hbac_plugin.py +index 75c15c5abe472d975f0c2bc78eb9dd5fda8af45e..b495fe3341f8d0682f65b4fc1d408734d130a7cd 100644 +--- a/ipatests/test_xmlrpc/test_hbac_plugin.py ++++ b/ipatests/test_xmlrpc/test_hbac_plugin.py +@@ -34,6 +34,7 @@ class test_hbac(XMLRPC_test): + Test the `hbacrule` plugin. + """ + rule_name = u'testing_rule1234' ++ rule_renamed = u'mega_testing_rule' + rule_type = u'allow' + rule_type_fail = u'value not allowed' + rule_service = u'ssh' +@@ -459,6 +460,20 @@ class test_hbac(XMLRPC_test): + assert_attr_equal(entry, 'cn', self.rule_name) + assert_attr_equal(entry, 'memberservice_hbacsvc', self.test_service) + ++ def test_o_hbacrule_rename(self): ++ """ ++ Test renaming an HBAC rule, rename it back afterwards ++ """ ++ api.Command['hbacrule_mod']( ++ self.rule_name, rename=self.rule_renamed ++ ) ++ entry = api.Command['hbacrule_show'](self.rule_renamed)['result'] ++ assert_attr_equal(entry, 'cn', self.rule_renamed) ++ # clean up by renaming the rule back ++ api.Command['hbacrule_mod']( ++ self.rule_renamed, rename=self.rule_name ++ ) ++ + def test_y_hbacrule_zap_testing_data(self): + """ + Clear data for HBAC plugin testing. +-- +2.12.1 + diff --git a/SOURCES/0030-Allow-renaming-of-the-sudorule-objects.patch b/SOURCES/0030-Allow-renaming-of-the-sudorule-objects.patch new file mode 100644 index 0000000..7198e0f --- /dev/null +++ b/SOURCES/0030-Allow-renaming-of-the-sudorule-objects.patch @@ -0,0 +1,100 @@ +From f868a2016bdad996fac9cc9d3b3e9b4228ab1dd4 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 27 Mar 2017 08:26:03 +0200 +Subject: [PATCH] Allow renaming of the sudorule objects + +The recent changes allow the sudorule objects to be renamed. + +https://pagure.io/freeipa/issue/2466 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +--- + API.txt | 3 ++- + VERSION.m4 | 2 +- + ipaserver/plugins/sudorule.py | 1 + + ipatests/test_xmlrpc/test_sudorule_plugin.py | 14 ++++++++++++++ + 4 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/API.txt b/API.txt +index 2a63c983a343b07ec7928bc774c6443a84b7c64c..7594157384511c1317738dafb41361676a2a0fd7 100644 +--- a/API.txt ++++ b/API.txt +@@ -5403,7 +5403,7 @@ output: ListOfEntries('result') + output: Output('summary', type=[, ]) + output: Output('truncated', type=[]) + command: sudorule_mod/1 +-args: 1,20,3 ++args: 1,21,3 + arg: Str('cn', cli_name='sudorule_name') + option: Str('addattr*', cli_name='addattr') + option: Flag('all', autofill=True, cli_name='all', default=False) +@@ -5420,6 +5420,7 @@ option: StrEnum('ipasudorunasgroupcategory?', autofill=False, cli_name='runasgro + option: StrEnum('ipasudorunasusercategory?', autofill=False, cli_name='runasusercat', values=[u'all']) + option: Flag('no_members', autofill=True, default=False) + option: Flag('raw', autofill=True, cli_name='raw', default=False) ++option: Str('rename?', cli_name='rename') + option: Flag('rights', autofill=True, default=False) + option: Str('setattr*', cli_name='setattr') + option: Int('sudoorder?', autofill=False, cli_name='order', default=0) +diff --git a/VERSION.m4 b/VERSION.m4 +index bbb5212e5b8cd9604b6ec90d4a0bd4c3276b1856..31e7c1d6e7054d3b4ef1d9dfaf349d2959f8330a 100644 +--- a/VERSION.m4 ++++ b/VERSION.m4 +@@ -74,7 +74,7 @@ define(IPA_DATA_VERSION, 20100614120000) + ######################################################## + define(IPA_API_VERSION_MAJOR, 2) + define(IPA_API_VERSION_MINOR, 224) +-# Last change: Add rename option to HBAC rule objects ++# Last change: Add rename option to sudorule objects + + + ######################################################## +diff --git a/ipaserver/plugins/sudorule.py b/ipaserver/plugins/sudorule.py +index 90771072ac8645863e77e88b2500c47c0bb2c8df..28c3f21f113fd14160abd518663f2d582f8653fd 100644 +--- a/ipaserver/plugins/sudorule.py ++++ b/ipaserver/plugins/sudorule.py +@@ -145,6 +145,7 @@ class sudorule(LDAPObject): + ] + uuid_attribute = 'ipauniqueid' + rdn_attribute = 'ipauniqueid' ++ allow_rename = True + attribute_members = { + 'memberuser': ['user', 'group'], + 'memberhost': ['host', 'hostgroup'], +diff --git a/ipatests/test_xmlrpc/test_sudorule_plugin.py b/ipatests/test_xmlrpc/test_sudorule_plugin.py +index c37262a43cc3805913688be4bda0318d67b364c4..75dbfbe67202a57299330d21200fe7ca1bb3f77e 100644 +--- a/ipatests/test_xmlrpc/test_sudorule_plugin.py ++++ b/ipatests/test_xmlrpc/test_sudorule_plugin.py +@@ -42,6 +42,7 @@ class test_sudorule(XMLRPC_test): + """ + rule_name = u'testing_sudorule1' + rule_name2 = u'testing_sudorule2' ++ rule_renamed = u'testing_mega_sudorule' + rule_command = u'/usr/bin/testsudocmd1' + rule_desc = u'description' + rule_desc_mod = u'description modified' +@@ -782,6 +783,19 @@ class test_sudorule(XMLRPC_test): + api.Command['sudorule_mod'](self.rule_name, sudoorder=None) + api.Command['sudorule_mod'](self.rule_name2, sudoorder=None) + ++ def test_l_1_sudorule_rename(self): ++ """ ++ Test renaming an HBAC rule, rename it back afterwards ++ """ ++ api.Command['sudorule_mod']( ++ self.rule_name, rename=self.rule_renamed ++ ) ++ entry = api.Command['sudorule_show'](self.rule_renamed)['result'] ++ assert_attr_equal(entry, 'cn', self.rule_renamed) ++ # clean up by renaming the rule back ++ api.Command['sudorule_mod']( ++ self.rule_renamed, rename=self.rule_name ++ ) + + def test_m_sudorule_del(self): + """ +-- +2.12.1 + diff --git a/SOURCES/0031-Create-temporaty-directories-at-the-begining-of-unin.patch b/SOURCES/0031-Create-temporaty-directories-at-the-begining-of-unin.patch new file mode 100644 index 0000000..3c73e87 --- /dev/null +++ b/SOURCES/0031-Create-temporaty-directories-at-the-begining-of-unin.patch @@ -0,0 +1,36 @@ +From e344a42bfff8c9d124b13ae43baec72c5329e29f Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Thu, 23 Mar 2017 12:48:06 +0100 +Subject: [PATCH] Create temporaty directories at the begining of uninstall + +Since commit 38c6689 temporary directories are no longer created at package +install time. Instead they're created at server install time. +Some steps in uninstall also assume that temporary direcories exist. Creating +the directories in the begining of server uninstall ensure that the uninstall +will go through. + +https://pagure.io/freeipa/issue/6715 + +Reviewed-By: Martin Basti +--- + ipaserver/install/server/install.py | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index de6b5b31274c87ceca5d98bcf8e80230ec6ae1f7..d7eb0bfacd0815026c82f59d76962f527e2b7dad 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -1042,6 +1042,10 @@ def uninstall(installer): + + rv = 0 + ++ # further steps assumes that temporary directories exists so rather ++ # ensure they are created ++ tasks.create_tmpfiles_dirs() ++ + print("Shutting down all IPA services") + try: + services.knownservices.ipa.stop() +-- +2.12.1 + diff --git a/SOURCES/0032-dogtag-ipa-ca-renew-agent-submit-fix-the-is_replicat.patch b/SOURCES/0032-dogtag-ipa-ca-renew-agent-submit-fix-the-is_replicat.patch new file mode 100644 index 0000000..cd3f5ba --- /dev/null +++ b/SOURCES/0032-dogtag-ipa-ca-renew-agent-submit-fix-the-is_replicat.patch @@ -0,0 +1,38 @@ +From c7d19fca09f7398af63ceffb915afc9b5d507e1e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 24 Mar 2017 11:02:33 +0100 +Subject: [PATCH] dogtag-ipa-ca-renew-agent-submit: fix the is_replicated() + function + +dogtag-ipa-ca-renew-agent-submit behaves differently depending on the +certificate it needs to renew. For instance, some certificates (such as IPA RA) +are the same on all the hosts and the renewal is actually done only on +the renewal master. On other nodes, the new cert is downloaded from LDAP. + +The function is_replicated() is returning the opposite as what it should. If +the cert nickname is IPA RA, it should return that the cert is replicated but +it doesn't, and this leads to a wrong code path to renew the cert. + +https://pagure.io/freeipa/issue/6813 + +Reviewed-By: Jan Cholasta +--- + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index cc690b8fa26854a5ab683915a5ba6a8d3c0d4ae4..5782db703c49d7c2e92c806e24e9925e8e7d710a 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -119,7 +119,7 @@ def is_renewable(): + + + def is_replicated(): +- return not get_nickname() ++ return bool(get_nickname()) + + + def is_renewal_master(): +-- +2.12.1 + diff --git a/SOURCES/0033-Simplify-KRA-transport-cert-cache.patch b/SOURCES/0033-Simplify-KRA-transport-cert-cache.patch new file mode 100644 index 0000000..2e3441a --- /dev/null +++ b/SOURCES/0033-Simplify-KRA-transport-cert-cache.patch @@ -0,0 +1,195 @@ +From 1190a1b41d436de4dab7a622d78217baba44a9ef Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 17 Mar 2017 10:44:38 +0100 +Subject: [PATCH] Simplify KRA transport cert cache + +In-memory cache causes problem in forking servers. A file based cache is +good enough. It's easier to understand and avoids performance regression +and synchronization issues when cert becomes out-of-date. + +https://pagure.io/freeipa/issue/6787 +Signed-off-by: Christian Heimes +Reviewed-By: Jan Cholasta +--- + ipaclient/plugins/vault.py | 103 ++++++++++++++++++++++++--------------------- + 1 file changed, 55 insertions(+), 48 deletions(-) + +diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py +index d677ec0287d6b37cfd63820a919c0726d3a4ae9f..3fb4900d9cf90e6902c40e1c3d8cfdafec2e28b8 100644 +--- a/ipaclient/plugins/vault.py ++++ b/ipaclient/plugins/vault.py +@@ -20,7 +20,6 @@ + from __future__ import print_function + + import base64 +-import collections + import errno + import getpass + import io +@@ -558,74 +557,79 @@ class vault_mod(Local): + return response + + +-class _TransportCertCache(collections.MutableMapping): ++class _TransportCertCache(object): + def __init__(self): + self._dirname = os.path.join( +- USER_CACHE_PATH, 'ipa', 'kra-transport-certs') +- self._transport_certs = {} ++ USER_CACHE_PATH, 'ipa', 'kra-transport-certs' ++ ) + + def _get_filename(self, domain): + basename = DNSName(domain).ToASCII() + '.pem' + return os.path.join(self._dirname, basename) + +- def __getitem__(self, domain): +- try: +- transport_cert = self._transport_certs[domain] +- except KeyError: +- transport_cert = None ++ def load_cert(self, domain): ++ """Load cert from cache + +- filename = self._get_filename(domain) ++ :param domain: IPA domain ++ :return: cryptography.x509.Certificate or None ++ """ ++ filename = self._get_filename(domain) ++ try: + try: +- try: +- transport_cert = x509.load_certificate_from_file(filename) +- except EnvironmentError as e: +- if e.errno != errno.ENOENT: +- raise +- except Exception: +- logger.warning("Failed to load %s: %s", filename, +- exc_info=True) +- +- if transport_cert is None: +- raise KeyError(domain) +- +- self._transport_certs[domain] = transport_cert ++ return x509.load_certificate_from_file(filename) ++ except EnvironmentError as e: ++ if e.errno != errno.ENOENT: ++ raise ++ except Exception: ++ logger.warning("Failed to load %s", filename, exc_info=True) + +- return transport_cert ++ def store_cert(self, domain, transport_cert): ++ """Store a new cert or override existing cert + +- def __setitem__(self, domain, transport_cert): ++ :param domain: IPA domain ++ :param transport_cert: cryptography.x509.Certificate ++ :return: True if cert was stored successfully ++ """ + filename = self._get_filename(domain) +- transport_cert_der = ( +- transport_cert.public_bytes(serialization.Encoding.DER)) ++ pem = transport_cert.public_bytes(serialization.Encoding.PEM) + try: + try: + os.makedirs(self._dirname) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise +- fd, tmpfilename = tempfile.mkstemp(dir=self._dirname) +- os.close(fd) +- x509.write_certificate(transport_cert_der, tmpfilename) +- os.rename(tmpfilename, filename) ++ with tempfile.NamedTemporaryFile(dir=self._dirname, delete=False, ++ mode='wb') as f: ++ try: ++ f.write(pem) ++ f.flush() ++ os.fdatasync(f.fileno()) ++ f.close() ++ os.rename(f.name, filename) ++ except Exception: ++ os.unlink(f.name) ++ raise + except Exception: + logger.warning("Failed to save %s", filename, exc_info=True) ++ return False ++ else: ++ return True + +- self._transport_certs[domain] = transport_cert ++ def remove_cert(self, domain): ++ """Remove a cert from cache, ignores errors + +- def __delitem__(self, domain): ++ :param domain: IPA domain ++ :return: True if cert was found and removed ++ """ + filename = self._get_filename(domain) + try: + os.unlink(filename) + except EnvironmentError as e: + if e.errno != errno.ENOENT: + logger.warning("Failed to remove %s", filename, exc_info=True) +- +- del self._transport_certs[domain] +- +- def __len__(self): +- return len(self._transport_certs) +- +- def __iter__(self): +- return iter(self._transport_certs) ++ return False ++ else: ++ return True + + + _transport_cert_cache = _TransportCertCache() +@@ -646,7 +650,10 @@ class vaultconfig_show(MethodOverride): + # cache transport certificate + transport_cert = x509.load_certificate( + response['result']['transport_cert'], x509.DER) +- _transport_cert_cache[self.api.env.domain] = transport_cert ++ ++ _transport_cert_cache.store_cert( ++ self.api.env.domain, transport_cert ++ ) + + if file: + with open(file, 'w') as f: +@@ -680,7 +687,7 @@ class ModVaultData(Local): + except (errors.InternalError, + errors.ExecutionError, + errors.GenericError): +- _transport_cert_cache.pop(self.api.env.domain, None) ++ _transport_cert_cache.remove_cert(self.api.env.domain) + if raise_unexpected: + raise + +@@ -691,17 +698,17 @@ class ModVaultData(Local): + domain = self.api.env.domain + + # try call with cached transport certificate +- transport_cert = _transport_cert_cache.get(domain) ++ transport_cert = _transport_cert_cache.load_cert(domain) + if transport_cert is not None: + result = self._do_internal(algo, transport_cert, False, + *args, **options) + if result is not None: + return result + +- # retrieve and cache transport certificate +- self.api.Command.vaultconfig_show() +- transport_cert = _transport_cert_cache[domain] +- ++ # retrieve transport certificate (cached by vaultconfig_show) ++ response = self.api.Command.vaultconfig_show() ++ transport_cert = x509.load_certificate( ++ response['result']['transport_cert'], x509.DER) + # call with the retrieved transport certificate + return self._do_internal(algo, transport_cert, True, + *args, **options) +-- +2.12.1 + diff --git a/SOURCES/0034-rpcserver.login_x509-Actually-return-reply-from-__ca.patch b/SOURCES/0034-rpcserver.login_x509-Actually-return-reply-from-__ca.patch new file mode 100644 index 0000000..7a5c3b4 --- /dev/null +++ b/SOURCES/0034-rpcserver.login_x509-Actually-return-reply-from-__ca.patch @@ -0,0 +1,33 @@ +From c9eefa180576e7218d6aef063ea52915c0ce18a6 Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Mon, 27 Mar 2017 16:09:09 +0200 +Subject: [PATCH] rpcserver.login_x509: Actually return reply from __call__ + method + +__call__ didn't return causing internal error in wsgi application. Previously +this bug was hidden by some other error and the code worked even though it +shouldn't. + +https://pagure.io/freeipa/issue/6819 + +Reviewed-By: Pavel Vomacka +--- + ipaserver/rpcserver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index be4e3916b6011dd2b6c90a0267990bf1e370dfb9..77ed7e124c2ca3dcb49d3a68269d6fa9875d4da0 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -842,7 +842,7 @@ class login_x509(KerberosLogin): + environ, start_response, 'KRB5CCNAME not set', + 'Authentication failed') + +- super(login_x509, self).__call__(environ, start_response) ++ return super(login_x509, self).__call__(environ, start_response) + + + class login_password(Backend, KerberosSession): +-- +2.12.1 + diff --git a/SOURCES/0035-Backup-CA-cert-from-kerberos-folder.patch b/SOURCES/0035-Backup-CA-cert-from-kerberos-folder.patch new file mode 100644 index 0000000..1aebce7 --- /dev/null +++ b/SOURCES/0035-Backup-CA-cert-from-kerberos-folder.patch @@ -0,0 +1,27 @@ +From 32b4dac59052bb48ba4862573d617c35e137e4b7 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 27 Mar 2017 10:44:56 +0200 +Subject: [PATCH] Backup CA cert from kerberos folder + +https://pagure.io/freeipa/issue/6748 + +Reviewed-By: Martin Basti +--- + ipaserver/install/ipa_backup.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py +index 56583c01b1677a48c103d79123e3fbe106222f38..f71a40bb06545c8d89d1e3fdbc37d5e6e1fe8d58 100644 +--- a/ipaserver/install/ipa_backup.py ++++ b/ipaserver/install/ipa_backup.py +@@ -165,6 +165,7 @@ class Backup(admintool.AdminTool): + paths.KRB5KDC_KDC_CONF, + paths.KDC_CERT, + paths.KDC_KEY, ++ paths.CACERT_PEM, + paths.SYSTEMD_IPA_SERVICE, + paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, + paths.SYSTEMD_SSSD_SERVICE, +-- +2.12.1 + diff --git a/SOURCES/0036-spec-file-Bump-requires-to-make-Certificate-Login-in.patch b/SOURCES/0036-spec-file-Bump-requires-to-make-Certificate-Login-in.patch new file mode 100644 index 0000000..1869391 --- /dev/null +++ b/SOURCES/0036-spec-file-Bump-requires-to-make-Certificate-Login-in.patch @@ -0,0 +1,54 @@ +From 03d0b25c585e9202ff0fa7fba96df5c9f0a1a337 Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Thu, 23 Mar 2017 08:43:51 +0100 +Subject: [PATCH] spec file: Bump requires to make Certificate Login in WebUI + work + +gssproxy >= 0.7.0-2 - fixes impersonator checking +mod_lookup_identity >= 0.9.9 - adds support for single certificate assigned to multiple users +mod_nss >= 1.0.14-3 - no longer sets remote user in fixup hook +sssd-dbus >= 1.15.2 - adds FindByNameAndCertificate DBus method + +https://pagure.io/freeipa/issue/6823 + +Reviewed-By: Pavel Vomacka +Reviewed-By: Jan Cholasta +--- + freeipa.spec.in | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 5419ed10723fc7aa3ecc1b3f66b3ef1c8b38b12f..9c8a14a580ad80ed10e797bef9661e7b1feb81b3 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -270,9 +270,11 @@ Requires: ntp + Requires: httpd >= 2.4.6-31 + Requires: mod_wsgi + Requires: mod_auth_gssapi >= 1.5.0 +-Requires: mod_nss >= 1.0.8-26 ++# 1.0.14-3: https://bugzilla.redhat.com/show_bug.cgi?id=1431206 ++Requires: mod_nss >= 1.0.14-3 + Requires: mod_session +-Requires: mod_lookup_identity ++# 0.9.9: https://github.com/adelton/mod_lookup_identity/pull/3 ++Requires: mod_lookup_identity >= 0.9.9 + Requires: python-ldap >= 2.4.15 + Requires: python-gssapi >= 1.2.0 + Requires: acl +@@ -300,9 +302,10 @@ Requires: systemd-python + Requires: %{etc_systemd_dir} + Requires: gzip + Requires: oddjob +-Requires: gssproxy >= 0.7.0 +-# Require 1.15.1 for the certificate identity mapping feature +-Requires: sssd-dbus >= 1.15.1 ++# 0.7.0-2: https://pagure.io/gssproxy/pull-request/172 ++Requires: gssproxy >= 0.7.0-2 ++# 1.15.2: FindByNameAndCertificate (https://pagure.io/SSSD/sssd/issue/3050) ++Requires: sssd-dbus >= 1.15.2 + + Provides: %{alt_name}-server = %{version} + Conflicts: %{alt_name}-server +-- +2.12.1 + diff --git a/SOURCES/0037-Use-Custodia-0.3.1-features.patch b/SOURCES/0037-Use-Custodia-0.3.1-features.patch new file mode 100644 index 0000000..44b805d --- /dev/null +++ b/SOURCES/0037-Use-Custodia-0.3.1-features.patch @@ -0,0 +1,236 @@ +From a93e6040fdadd41dc7d1c46c09110b7321ed333c Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 28 Feb 2017 12:07:19 +0100 +Subject: [PATCH] Use Custodia 0.3.1 features + +* Use sd-notify in ipa-custodia.service +* Introduce libexec/ipa/ipa-custodia script. It comes with correct + default setting for IPA's config file. The new file also makes it + simpler to run IPA's custodia instance with its own SELinux context. +* ipapython no longer depends on custodia + +The patch addresses three issues: + +* https://bugzilla.redhat.com/show_bug.cgi?id=1430247 + Forward compatibility with Custodia 0.3 in Fedora rawhide +* https://pagure.io/freeipa/issue/5825 + Use sd-notify +* https://pagure.io/freeipa/issue/6788 + Prepare for separate SELinux context + +Signed-off-by: Christian Heimes +Reviewed-By: Martin Basti +Reviewed-By: Jan Cholasta +--- + freeipa.spec.in | 13 ++++++++----- + init/systemd/Makefile.am | 1 + + init/systemd/ipa-custodia.service.in | 5 ++--- + install/tools/Makefile.am | 1 + + install/tools/ipa-custodia | 6 ++++++ + ipapython/setup.py | 1 - + ipaserver/secrets/service.py | 30 ++++++++++++++++++++++++++++++ + ipaserver/setup.py | 1 + + ipasetup.py.in | 1 + + 9 files changed, 50 insertions(+), 9 deletions(-) + create mode 100755 install/tools/ipa-custodia + create mode 100644 ipaserver/secrets/service.py + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 9c8a14a580ad80ed10e797bef9661e7b1feb81b3..91fca6ea974bd70847feb1e3b6db8ae3cbda061c 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -181,7 +181,8 @@ BuildRequires: pki-base-python2 + BuildRequires: python-pytest-multihost + BuildRequires: python-pytest-sourceorder + BuildRequires: python-jwcrypto +-BuildRequires: python-custodia ++# 0.3: sd_notify (https://pagure.io/freeipa/issue/5825) ++BuildRequires: python-custodia >= 0.3.1 + BuildRequires: dbus-python + BuildRequires: python-dateutil + BuildRequires: python-enum34 +@@ -216,7 +217,8 @@ BuildRequires: pki-base-python3 + BuildRequires: python3-pytest-multihost + BuildRequires: python3-pytest-sourceorder + BuildRequires: python3-jwcrypto +-BuildRequires: python3-custodia ++# 0.3: sd_notify (https://pagure.io/freeipa/issue/5825) ++BuildRequires: python3-custodia >= 0.3.1 + BuildRequires: python3-dbus + BuildRequires: python3-dateutil + BuildRequires: python3-enum34 +@@ -340,6 +342,7 @@ BuildArch: noarch + Requires: %{name}-server-common = %{version}-%{release} + Requires: %{name}-common = %{version}-%{release} + Requires: python2-ipaclient = %{version}-%{release} ++Requires: python-custodia >= 0.3.1 + Requires: python-ldap >= 2.4.15 + Requires: python-lxml + Requires: python-gssapi >= 1.2.0 +@@ -370,6 +373,7 @@ BuildArch: noarch + Requires: %{name}-server-common = %{version}-%{release} + Requires: %{name}-common = %{version}-%{release} + Requires: python3-ipaclient = %{version}-%{release} ++Requires: python3-custodia >= 0.3.1 + Requires: python3-pyldap >= 2.4.15 + Requires: python3-lxml + Requires: python3-gssapi >= 1.2.0 +@@ -399,7 +403,7 @@ BuildArch: noarch + Requires: %{name}-client-common = %{version}-%{release} + Requires: httpd >= 2.4.6-31 + Requires: systemd-units >= 38 +-Requires: custodia ++Requires: custodia >= 0.3.1 + + Provides: %{alt_name}-server-common = %{version} + Conflicts: %{alt_name}-server-common +@@ -650,7 +654,6 @@ Requires: python-jwcrypto + Requires: python-cffi + Requires: python-ldap >= 2.4.15 + Requires: python-requests +-Requires: python-custodia + Requires: python-dns >= 1.15 + Requires: python-enum34 + Requires: python-netifaces >= 0.10.4 +@@ -699,7 +702,6 @@ Requires: python3-six + Requires: python3-jwcrypto + Requires: python3-cffi + Requires: python3-pyldap >= 2.4.15 +-Requires: python3-custodia + Requires: python3-requests + Requires: python3-dns >= 1.15 + Requires: python3-netifaces >= 0.10.4 +@@ -1160,6 +1162,7 @@ fi + %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit + %{_libexecdir}/certmonger/ipa-server-guard + %dir %{_libexecdir}/ipa ++%{_libexecdir}/ipa/ipa-custodia + %{_libexecdir}/ipa/ipa-dnskeysyncd + %{_libexecdir}/ipa/ipa-dnskeysync-replica + %{_libexecdir}/ipa/ipa-ods-exporter +diff --git a/init/systemd/Makefile.am b/init/systemd/Makefile.am +index 325e8574812a2ec507911128dbac0315070d2897..945f6ac22a050f393990cad27156e092ce4f7a29 100644 +--- a/init/systemd/Makefile.am ++++ b/init/systemd/Makefile.am +@@ -18,5 +18,6 @@ CLEANFILES = $(systemdsystemunit_DATA) + -e 's|@IPA_SYSCONF_DIR[@]|$(IPA_SYSCONF_DIR)|g' \ + -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@sbindir[@]|$(sbindir)|g' \ ++ -e 's|@libexecdir[@]|$(libexecdir)|g' \ + -e 's|@sysconfenvdir[@]|$(sysconfenvdir)|g' \ + '$(srcdir)/$@.in' >$@ +diff --git a/init/systemd/ipa-custodia.service.in b/init/systemd/ipa-custodia.service.in +index 3f9b128aa1b7ee373c52e1e3566048ec6028c826..0247bd8826529d638c692d827ae31393db292b4a 100644 +--- a/init/systemd/ipa-custodia.service.in ++++ b/init/systemd/ipa-custodia.service.in +@@ -2,9 +2,8 @@ + Description=IPA Custodia Service + + [Service] +-Type=simple +- +-ExecStart=@sbindir@/custodia @IPA_SYSCONF_DIR@/custodia/custodia.conf ++Type=notify ++ExecStart=@libexecdir@/ipa/ipa-custodia @IPA_SYSCONF_DIR@/custodia/custodia.conf + PrivateTmp=yes + Restart=on-failure + RestartSec=60s +diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am +index f2c2ce2953c3ac146a80f7e4515769683a01f843..493e5ff4a8290be8ef076135104a85f8315b7842 100644 +--- a/install/tools/Makefile.am ++++ b/install/tools/Makefile.am +@@ -32,6 +32,7 @@ dist_sbin_SCRIPTS = \ + + appdir = $(libexecdir)/ipa/ + dist_app_SCRIPTS = \ ++ ipa-custodia \ + ipa-httpd-kdcproxy \ + ipa-pki-retrieve-key \ + $(NULL) +diff --git a/install/tools/ipa-custodia b/install/tools/ipa-custodia +new file mode 100755 +index 0000000000000000000000000000000000000000..5deeeffdd78db323b6534934065772bb0ae67438 +--- /dev/null ++++ b/install/tools/ipa-custodia +@@ -0,0 +1,6 @@ ++#!/usr/bin/python2 ++# Copyright (C) 2017 IPA Project Contributors, see COPYING for license ++from ipaserver.secrets.service import main ++ ++if __name__ == '__main__': ++ main() +diff --git a/ipapython/setup.py b/ipapython/setup.py +index 86e4131e5f9cfc106393875018d6ac2645a38be1..2fc039fe7bb673add17404d13bf477c5b8bb0606 100755 +--- a/ipapython/setup.py ++++ b/ipapython/setup.py +@@ -38,7 +38,6 @@ if __name__ == '__main__': + ], + install_requires=[ + "cffi", +- "custodia", + "cryptography", + "dnspython", + "gssapi", +diff --git a/ipaserver/secrets/service.py b/ipaserver/secrets/service.py +new file mode 100644 +index 0000000000000000000000000000000000000000..f51c46a30e4caf76e38659c2f0a6a2c645376978 +--- /dev/null ++++ b/ipaserver/secrets/service.py +@@ -0,0 +1,30 @@ ++# Copyright (C) 2017 IPA Project Contributors, see COPYING for license ++import argparse ++ ++import custodia.server ++ ++ ++argparser = argparse.ArgumentParser( ++ prog='ipa-custodia', ++ description='IPA Custodia service' ++) ++argparser.add_argument( ++ '--debug', ++ action='store_true', ++ help='Debug mode' ++) ++argparser.add_argument( ++ 'configfile', ++ nargs='?', ++ type=argparse.FileType('r'), ++ help="Path to IPA's custodia server config", ++ default='/etc/ipa/custodia/custodia.conf' ++) ++ ++ ++def main(): ++ return custodia.server.main(argparser) ++ ++ ++if __name__ == '__main__': ++ main() +diff --git a/ipaserver/setup.py b/ipaserver/setup.py +index d3c735c0f9e604512d6ccd14dcd16a186c6ecad4..42b0c1b0618ef9867acb1fe2add5702a756cf2d2 100755 +--- a/ipaserver/setup.py ++++ b/ipaserver/setup.py +@@ -47,6 +47,7 @@ if __name__ == '__main__': + ], + install_requires=[ + "cryptography", ++ "custodia", + "dbus-python", + "dnspython", + "dogtag-pki", +diff --git a/ipasetup.py.in b/ipasetup.py.in +index 915f0edee7ca291cc4921f6b8e4d38498253b372..7f9b2c918c0cd582706edee087ed5944451aaf2e 100644 +--- a/ipasetup.py.in ++++ b/ipasetup.py.in +@@ -64,6 +64,7 @@ if SETUPTOOLS_VERSION < (8, 0, 0): + + PACKAGE_VERSION = { + 'cryptography': 'cryptography >= 1.4', ++ 'custodia': 'custodia >= 0.3.1', + 'dnspython': 'dnspython >= 1.15', + 'gssapi': 'gssapi >= 1.2.0', + 'ipaclient': 'ipaclient == {}'.format(VERSION), +-- +2.12.1 + diff --git a/SOURCES/0038-spec-file-bump-krb5-devel-BuildRequires-for-certauth.patch b/SOURCES/0038-spec-file-bump-krb5-devel-BuildRequires-for-certauth.patch new file mode 100644 index 0000000..06d302d --- /dev/null +++ b/SOURCES/0038-spec-file-bump-krb5-devel-BuildRequires-for-certauth.patch @@ -0,0 +1,40 @@ +From 93e0cc4aaf84e7a988a11d13674c294294b8498a Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 28 Mar 2017 10:43:31 +0000 +Subject: [PATCH] spec file: bump krb5-devel BuildRequires for certauth + +Bump BuildRequires on krb5-devel to the version which introduces the +certauth pluggable interface. + +This fixes RPM build failure when an older version of krb5-devel was +installed. + +https://pagure.io/freeipa/issue/4905 + +Reviewed-By: David Kupka +--- + freeipa.spec.in | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 91fca6ea974bd70847feb1e3b6db8ae3cbda061c..e7e39e87bef39653d660a345793750f59c8dd715 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -80,12 +80,10 @@ BuildRequires: openldap-devel + # will cause the build to fail due to unsatisfied dependencies. + # DAL version change may cause code crash or memory leaks, it is better to fail early. + %if 0%{?fedora} > 25 +-BuildRequires: krb5-devel >= 1.15-5 + BuildRequires: krb5-kdb-version = 6.1 +-%else +-# 1.12: libkrad (http://krbdev.mit.edu/rt/Ticket/Display.html?id=7678) +-BuildRequires: krb5-devel >= 1.12 + %endif ++# 1.15.1-3: certauth (http://krbdev.mit.edu/rt/Ticket/Display.html?id=8561) ++BuildRequires: krb5-devel >= 1.15.1-3 + # 1.27.4: xmlrpc_curl_xportparms.gssapi_delegation + BuildRequires: xmlrpc-c-devel >= 1.27.4 + BuildRequires: popt-devel +-- +2.12.1 + diff --git a/SOURCES/0039-Avoid-growing-FILE-ccaches-unnecessarily.patch b/SOURCES/0039-Avoid-growing-FILE-ccaches-unnecessarily.patch new file mode 100644 index 0000000..25803da --- /dev/null +++ b/SOURCES/0039-Avoid-growing-FILE-ccaches-unnecessarily.patch @@ -0,0 +1,34 @@ +From 5c84f945e0fe5e41d706fd7f700392214178b6aa Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Wed, 22 Mar 2017 18:25:38 -0400 +Subject: [PATCH] Avoid growing FILE ccaches unnecessarily + +Related https://pagure.io/freeipa/issue/6775 + +Signed-off-by: Simo Sorce +Reviewed-By: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipapython/session_storage.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py +index 7fe17fb235614e687a88c090336d3c59a7a24aac..a88f9f7a75c73d4dc753183a100350d197d02199 100644 +--- a/ipapython/session_storage.py ++++ b/ipapython/session_storage.py +@@ -104,6 +104,12 @@ def store_data(princ_name, key, value): + """ + Stores the session cookie in a hidden ccache entry. + """ ++ # FILE ccaches grow every time an entry is stored, so we need ++ # to avoid storing the same entry multiple times. ++ oldvalue = get_data(princ_name, key) ++ if oldvalue == value: ++ return ++ + context = krb5_context() + principal = krb5_principal() + ccache = krb5_ccache() +-- +2.12.1 + diff --git a/SOURCES/0040-Handle-failed-authentication-via-cookie.patch b/SOURCES/0040-Handle-failed-authentication-via-cookie.patch new file mode 100644 index 0000000..7d32ccc --- /dev/null +++ b/SOURCES/0040-Handle-failed-authentication-via-cookie.patch @@ -0,0 +1,120 @@ +From d1a482316296d32551470de698a1bdd6a7efed1a Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Wed, 22 Mar 2017 18:38:22 -0400 +Subject: [PATCH] Handle failed authentication via cookie + +If cookie authentication fails and we get back a 401 see if we +tried a SPNEGO auth by checking if we had a GSSAPI context. If not +it means our session cookie was invalid or expired or some other +error happened on the server that requires us to try a full SPNEGO +handshake, so go ahead and try it. + +Fixes https://pagure.io/freeipa/issue/6775 + +Signed-off-by: Simo Sorce +Reviewed-By: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipalib/rpc.py | 52 ++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 32 insertions(+), 20 deletions(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index 38321d17cf2c9529738aa45cc44bbd38b08b032b..c1ceeec197c4a9c55f303f0fd431e86adb389598 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -586,22 +586,33 @@ class KerbTransport(SSLTransport): + else: + raise errors.KerberosError(message=unicode(e)) + +- def get_host_info(self, host): ++ def _get_host(self): ++ return self._connection[0] ++ ++ def _remove_extra_header(self, name): ++ for (h, v) in self._extra_headers: ++ if h == name: ++ self._extra_headers.remove((h, v)) ++ break ++ ++ def get_auth_info(self, use_cookie=True): + """ + Two things can happen here. If we have a session we will add + a cookie for that. If not we will set an Authorization header. + """ +- (host, extra_headers, x509) = SSLTransport.get_host_info(self, host) +- +- if not isinstance(extra_headers, list): +- extra_headers = [] ++ if not isinstance(self._extra_headers, list): ++ self._extra_headers = [] + +- session_cookie = getattr(context, 'session_cookie', None) +- if session_cookie: +- extra_headers.append(('Cookie', session_cookie)) +- return (host, extra_headers, x509) ++ # Remove any existing Cookie first ++ self._remove_extra_header('Cookie') ++ if use_cookie: ++ session_cookie = getattr(context, 'session_cookie', None) ++ if session_cookie: ++ self._extra_headers.append(('Cookie', session_cookie)) ++ return + + # Set the remote host principal ++ host = self._get_host() + service = self.service + "@" + host.split(':')[0] + + try: +@@ -616,18 +627,14 @@ class KerbTransport(SSLTransport): + except gssapi.exceptions.GSSError as e: + self._handle_exception(e, service=service) + +- self._set_auth_header(extra_headers, response) +- +- return (host, extra_headers, x509) ++ self._set_auth_header(response) + +- def _set_auth_header(self, extra_headers, token): +- for (h, v) in extra_headers: +- if h == 'Authorization': +- extra_headers.remove((h, v)) +- break ++ def _set_auth_header(self, token): ++ # Remove any existing authorization header first ++ self._remove_extra_header('Authorization') + + if token: +- extra_headers.append( ++ self._extra_headers.append( + ('Authorization', 'negotiate %s' % base64.b64encode(token).decode('ascii')) + ) + +@@ -651,18 +658,23 @@ class KerbTransport(SSLTransport): + if self._sec_context.complete: + self._sec_context = None + return True +- self._set_auth_header(self._extra_headers, token) ++ self._set_auth_header(token) ++ return False ++ elif response.status == 401: ++ self.get_auth_info(use_cookie=False) + return False + return True + + def single_request(self, host, handler, request_body, verbose=0): + # Based on Python 2.7's xmllib.Transport.single_request + try: +- h = SSLTransport.make_connection(self, host) ++ h = self.make_connection(host) + + if verbose: + h.set_debuglevel(1) + ++ self.get_auth_info() ++ + while True: + if six.PY2: + # pylint: disable=no-value-for-parameter +-- +2.12.1 + diff --git a/SOURCES/0041-Work-around-issues-fetching-session-data.patch b/SOURCES/0041-Work-around-issues-fetching-session-data.patch new file mode 100644 index 0000000..5be8812 --- /dev/null +++ b/SOURCES/0041-Work-around-issues-fetching-session-data.patch @@ -0,0 +1,331 @@ +From 2f8033174775f55cb2377baf524fc36914aa38fa Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Thu, 23 Mar 2017 13:02:00 -0400 +Subject: [PATCH] Work around issues fetching session data + +Unfortunately the MIT krb5 library has a severe limitation with FILE +ccaches when retrieving config data. It will always only search until +the first entry is found and return that one. + +For FILE caches MIT krb5 does not support removing old entries when a +new one is stored, and storage happens only in append mode, so the end +result is that even if an update is stored it is never returned with the +standard krb5_cc_get_config() call. + +To work around this issue we simply implement what krb5_cc_get_config() +does under the hood with the difference that we do not stop at the first +match but keep going until all ccache entries have been checked. + +Related https://pagure.io/freeipa/issue/6775 + +Signed-off-by: Simo Sorce +Reviewed-By: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipapython/session_storage.py | 213 ++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 190 insertions(+), 23 deletions(-) + +diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py +index a88f9f7a75c73d4dc753183a100350d197d02199..f3094f60000aa6e3f4b27fe91092c4214936f651 100644 +--- a/ipapython/session_storage.py ++++ b/ipapython/session_storage.py +@@ -13,6 +13,12 @@ try: + except OSError as e: # pragma: no cover + raise ImportError(str(e)) + ++krb5_int32 = ctypes.c_int32 ++krb5_error_code = krb5_int32 ++krb5_magic = krb5_error_code ++krb5_enctype = krb5_int32 ++krb5_octet = ctypes.c_uint8 ++krb5_timestamp = krb5_int32 + + class _krb5_context(ctypes.Structure): # noqa + """krb5/krb5.h struct _krb5_context""" +@@ -27,7 +33,7 @@ class _krb5_ccache(ctypes.Structure): # noqa + class _krb5_data(ctypes.Structure): # noqa + """krb5/krb5.h struct _krb5_data""" + _fields_ = [ +- ("magic", ctypes.c_int32), ++ ("magic", krb5_magic), + ("length", ctypes.c_uint), + ("data", ctypes.c_char_p), + ] +@@ -38,6 +44,63 @@ class krb5_principal_data(ctypes.Structure): # noqa + _fields_ = [] + + ++class _krb5_keyblock(ctypes.Structure): # noqa ++ """krb5/krb5.h struct _krb5_keyblock""" ++ _fields_ = [ ++ ("magic", krb5_magic), ++ ("enctype", krb5_enctype), ++ ("length", ctypes.c_uint), ++ ("contents", ctypes.POINTER(krb5_octet)) ++ ] ++ ++ ++class _krb5_ticket_times(ctypes.Structure): # noqa ++ """krb5/krb5.h struct _krb5_ticket_times""" ++ _fields_ = [ ++ ("authtime", krb5_timestamp), ++ ("starttime", krb5_timestamp), ++ ("endtime", krb5_timestamp), ++ ("renew_till", krb5_timestamp), ++ ] ++ ++ ++class _krb5_address(ctypes.Structure): # noqa ++ """krb5/krb5.h struct _krb5_address""" ++ _fields_ = [] ++ ++ ++class _krb5_authdata(ctypes.Structure): # noqa ++ """krb5/krb5.h struct _krb5_authdata""" ++ _fields_ = [] ++ ++ ++krb5_principal = ctypes.POINTER(krb5_principal_data) ++krb5_keyblock = _krb5_keyblock ++krb5_ticket_times = _krb5_ticket_times ++krb5_boolean = ctypes.c_uint ++krb5_flags = krb5_int32 ++krb5_data = _krb5_data ++krb5_address_p = ctypes.POINTER(_krb5_address) ++krb5_authdata_p = ctypes.POINTER(_krb5_authdata) ++ ++ ++class _krb5_creds(ctypes.Structure): # noqa ++ """krb5/krb5.h struct _krb5_creds""" ++ _fields_ = [ ++ ("magic", krb5_magic), ++ ("client", krb5_principal), ++ ("server", krb5_principal), ++ ("keyblock", krb5_keyblock), ++ ("times", krb5_ticket_times), ++ ("is_skey", krb5_boolean), ++ ("ticket_flags", krb5_flags), ++ ("addresses", ctypes.POINTER(krb5_address_p)), ++ ("ticket", krb5_data), ++ ("second_ticket", krb5_data), ++ ("authdata", ctypes.POINTER(krb5_authdata_p)) ++ ] ++ ++ + class KRB5Error(Exception): + pass + +@@ -48,11 +111,13 @@ def krb5_errcheck(result, func, arguments): + raise KRB5Error(result, func.__name__, arguments) + + +-krb5_principal = ctypes.POINTER(krb5_principal_data) + krb5_context = ctypes.POINTER(_krb5_context) + krb5_ccache = ctypes.POINTER(_krb5_ccache) + krb5_data_p = ctypes.POINTER(_krb5_data) + krb5_error = ctypes.c_int32 ++krb5_creds = _krb5_creds ++krb5_pointer = ctypes.c_void_p ++krb5_cc_cursor = krb5_pointer + + krb5_init_context = LIBKRB5.krb5_init_context + krb5_init_context.argtypes = (ctypes.POINTER(krb5_context), ) +@@ -61,15 +126,15 @@ krb5_init_context.errcheck = krb5_errcheck + + krb5_free_context = LIBKRB5.krb5_free_context + krb5_free_context.argtypes = (krb5_context, ) +-krb5_free_context.retval = None ++krb5_free_context.restype = None + + krb5_free_principal = LIBKRB5.krb5_free_principal + krb5_free_principal.argtypes = (krb5_context, krb5_principal) +-krb5_free_principal.retval = None ++krb5_free_principal.restype = None + + krb5_free_data_contents = LIBKRB5.krb5_free_data_contents + krb5_free_data_contents.argtypes = (krb5_context, krb5_data_p) +-krb5_free_data_contents.retval = None ++krb5_free_data_contents.restype = None + + krb5_cc_default = LIBKRB5.krb5_cc_default + krb5_cc_default.argtypes = (krb5_context, ctypes.POINTER(krb5_ccache), ) +@@ -78,26 +143,79 @@ krb5_cc_default.errcheck = krb5_errcheck + + krb5_cc_close = LIBKRB5.krb5_cc_close + krb5_cc_close.argtypes = (krb5_context, krb5_ccache, ) +-krb5_cc_close.retval = krb5_error ++krb5_cc_close.restype = krb5_error + krb5_cc_close.errcheck = krb5_errcheck + + krb5_parse_name = LIBKRB5.krb5_parse_name + krb5_parse_name.argtypes = (krb5_context, ctypes.c_char_p, + ctypes.POINTER(krb5_principal), ) +-krb5_parse_name.retval = krb5_error ++krb5_parse_name.restype = krb5_error + krb5_parse_name.errcheck = krb5_errcheck + + krb5_cc_set_config = LIBKRB5.krb5_cc_set_config + krb5_cc_set_config.argtypes = (krb5_context, krb5_ccache, krb5_principal, + ctypes.c_char_p, krb5_data_p, ) +-krb5_cc_set_config.retval = krb5_error ++krb5_cc_set_config.restype = krb5_error + krb5_cc_set_config.errcheck = krb5_errcheck + +-krb5_cc_get_config = LIBKRB5.krb5_cc_get_config +-krb5_cc_get_config.argtypes = (krb5_context, krb5_ccache, krb5_principal, +- ctypes.c_char_p, krb5_data_p, ) +-krb5_cc_get_config.retval = krb5_error +-krb5_cc_get_config.errcheck = krb5_errcheck ++krb5_cc_get_principal = LIBKRB5.krb5_cc_get_principal ++krb5_cc_get_principal.argtypes = (krb5_context, krb5_ccache, ++ ctypes.POINTER(krb5_principal), ) ++krb5_cc_get_principal.restype = krb5_error ++krb5_cc_get_principal.errcheck = krb5_errcheck ++ ++# krb5_build_principal is a variadic function but that can't be expressed ++# in a ctypes argtypes definition, so I explicitly listed the number of ++# arguments we actually use through the code for type checking purposes ++krb5_build_principal = LIBKRB5.krb5_build_principal ++krb5_build_principal.argtypes = (krb5_context, ctypes.POINTER(krb5_principal), ++ ctypes.c_uint, ctypes.c_char_p, ++ ctypes.c_char_p, ctypes.c_char_p, ++ ctypes.c_char_p, ctypes.c_char_p, ) ++krb5_build_principal.restype = krb5_error ++krb5_build_principal.errcheck = krb5_errcheck ++ ++krb5_cc_start_seq_get = LIBKRB5.krb5_cc_start_seq_get ++krb5_cc_start_seq_get.argtypes = (krb5_context, krb5_ccache, ++ ctypes.POINTER(krb5_cc_cursor), ) ++krb5_cc_start_seq_get.restype = krb5_error ++krb5_cc_start_seq_get.errcheck = krb5_errcheck ++ ++krb5_cc_next_cred = LIBKRB5.krb5_cc_next_cred ++krb5_cc_next_cred.argtypes = (krb5_context, krb5_ccache, ++ ctypes.POINTER(krb5_cc_cursor), ++ ctypes.POINTER(krb5_creds), ) ++krb5_cc_next_cred.restype = krb5_error ++krb5_cc_next_cred.errcheck = krb5_errcheck ++ ++krb5_cc_end_seq_get = LIBKRB5.krb5_cc_end_seq_get ++krb5_cc_end_seq_get.argtypes = (krb5_context, krb5_ccache, ++ ctypes.POINTER(krb5_cc_cursor), ) ++krb5_cc_end_seq_get.restype = krb5_error ++krb5_cc_end_seq_get.errcheck = krb5_errcheck ++ ++krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents ++krb5_free_cred_contents.argtypes = (krb5_context, ctypes.POINTER(krb5_creds)) ++krb5_free_cred_contents.restype = krb5_error ++krb5_free_cred_contents.errcheck = krb5_errcheck ++ ++krb5_principal_compare = LIBKRB5.krb5_principal_compare ++krb5_principal_compare.argtypes = (krb5_context, krb5_principal, ++ krb5_principal, ) ++krb5_principal_compare.restype = krb5_boolean ++ ++krb5_unparse_name = LIBKRB5.krb5_unparse_name ++krb5_unparse_name.argtypes = (krb5_context, krb5_principal, ++ ctypes.POINTER(ctypes.c_char_p), ) ++krb5_unparse_name.restype = krb5_error ++krb5_unparse_name.errcheck = krb5_errcheck ++ ++krb5_free_unparsed_name = LIBKRB5.krb5_free_unparsed_name ++krb5_free_unparsed_name.argtypes = (krb5_context, ctypes.c_char_p, ) ++krb5_free_unparsed_name.restype = None ++ ++CONF_REALM = "X-CACHECONF:" ++CONF_NAME = "krb5_ccache_conf_data" + + + def store_data(princ_name, key, value): +@@ -144,29 +262,78 @@ def get_data(princ_name, key): + """ + context = krb5_context() + principal = krb5_principal() ++ srv_princ = krb5_principal() + ccache = krb5_ccache() +- data = _krb5_data() ++ pname_princ = krb5_principal() ++ pname = ctypes.c_char_p() + + try: + krb5_init_context(ctypes.byref(context)) + +- krb5_parse_name(context, ctypes.c_char_p(princ_name), +- ctypes.byref(principal)) +- + krb5_cc_default(context, ctypes.byref(ccache)) ++ krb5_cc_get_principal(context, ccache, ctypes.byref(principal)) + +- krb5_cc_get_config(context, ccache, principal, key, +- ctypes.byref(data)) +- +- return str(data.data) ++ # We need to parse and then unparse the name in case the pric_name ++ # passed in comes w/o a realm attached ++ krb5_parse_name(context, ctypes.c_char_p(princ_name), ++ ctypes.byref(pname_princ)) ++ krb5_unparse_name(context, pname_princ, ctypes.byref(pname)) ++ ++ krb5_build_principal(context, ctypes.byref(srv_princ), ++ len(CONF_REALM), ctypes.c_char_p(CONF_REALM), ++ ctypes.c_char_p(CONF_NAME), ctypes.c_char_p(key), ++ pname, ctypes.c_char_p(None)) ++ ++ # Unfortunately we can't just use krb5_cc_get_config() ++ # because of bugs in some ccache handling code in krb5 ++ # libraries that would always return the first entry ++ # stored and not the last one, which is the one we want. ++ cursor = krb5_cc_cursor() ++ creds = krb5_creds() ++ got_creds = False ++ krb5_cc_start_seq_get(context, ccache, ctypes.byref(cursor)) ++ try: ++ while True: ++ checkcreds = krb5_creds() ++ # the next function will throw an error and break out of the ++ # while loop when we try to access past the last cred ++ krb5_cc_next_cred(context, ccache, ctypes.byref(cursor), ++ ctypes.byref(checkcreds)) ++ if (krb5_principal_compare(context, principal, ++ checkcreds.client) == 1 and ++ krb5_principal_compare(context, srv_princ, ++ checkcreds.server) == 1): ++ if got_creds: ++ krb5_free_cred_contents(context, ctypes.byref(creds)) ++ creds = checkcreds ++ got_creds = True ++ # We do not stop here, as we want the LAST entry ++ # in the ccache for those ccaches that cannot delete ++ # but only always append, like FILE ++ else: ++ krb5_free_cred_contents(context, ++ ctypes.byref(checkcreds)) ++ except KRB5Error: ++ pass ++ finally: ++ krb5_cc_end_seq_get(context, ccache, ctypes.byref(cursor)) ++ ++ if got_creds: ++ data = creds.ticket.data.decode('utf-8') ++ krb5_free_cred_contents(context, ctypes.byref(creds)) ++ return data + + finally: + if principal: + krb5_free_principal(context, principal) ++ if srv_princ: ++ krb5_free_principal(context, srv_princ) ++ if pname_princ: ++ krb5_free_principal(context, pname_princ) ++ if pname: ++ krb5_free_unparsed_name(context, pname) + if ccache: + krb5_cc_close(context, ccache) +- if data: +- krb5_free_data_contents(context, data) + if context: + krb5_free_context(context) + +-- +2.12.1 + diff --git a/SOURCES/0042-Prevent-churn-on-ccaches.patch b/SOURCES/0042-Prevent-churn-on-ccaches.patch new file mode 100644 index 0000000..1912a24 --- /dev/null +++ b/SOURCES/0042-Prevent-churn-on-ccaches.patch @@ -0,0 +1,71 @@ +From df5600dc012465f2f18a54aa451353f0fd9d5453 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Thu, 23 Mar 2017 17:49:27 -0400 +Subject: [PATCH] Prevent churn on ccaches + +We slice down the received cookie so that just the content that matter +is preserved. Thi is ok because servers can't trust anything else anyway +and will accept a cookie with the ancillary data missing. + +By removing variable parts like the expiry component added by +mod_session or the Expiration or Max-Age metadata we keep only the part +of the cookie that changes only when a new session is generated. + +This way when storing the cookie we actually add a new entry in the +ccache only when the session actually changes, and this prevents churn +on FILE based ccaches. + +Related https://pagure.io/freeipa/issue/6775 + +Signed-off-by: Simo Sorce +Reviewed-By: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipalib/rpc.py | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index c1ceeec197c4a9c55f303f0fd431e86adb389598..5c49bd2456b7e564043a886c840fa2678060f9e3 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -38,6 +38,7 @@ import os + import locale + import base64 + import json ++import re + import socket + import gzip + +@@ -737,6 +738,20 @@ class KerbTransport(SSLTransport): + self.send_content(connection, request_body) + return connection + ++ # Find all occurrences of the expiry component ++ expiry_re = re.compile(r'.*?(&expiry=\d+).*?') ++ ++ def _slice_session_cookie(self, session_cookie): ++ # Keep only the cookie value and strip away all other info. ++ # This is to reduce the churn on FILE ccaches which grow every time we ++ # set new data. The expiration time for the cookie is set in the ++ # encrypted data anyway and will be enforced by the server ++ http_cookie = session_cookie.http_cookie() ++ # We also remove the "expiry" part from the data which is not required ++ for exp in self.expiry_re.findall(http_cookie): ++ http_cookie = http_cookie.replace(exp, '') ++ return http_cookie ++ + def store_session_cookie(self, cookie_header): + ''' + Given the contents of a Set-Cookie header scan the header and +@@ -787,7 +802,7 @@ class KerbTransport(SSLTransport): + if session_cookie is None: + return + +- cookie_string = str(session_cookie) ++ cookie_string = self._slice_session_cookie(session_cookie) + root_logger.debug("storing cookie '%s' for principal %s", cookie_string, principal) + try: + update_persistent_client_session_data(principal, cookie_string) +-- +2.12.1 + diff --git a/SOURCES/0043-Generate-PIN-for-PKI-to-help-Dogtag-in-FIPS.patch b/SOURCES/0043-Generate-PIN-for-PKI-to-help-Dogtag-in-FIPS.patch new file mode 100644 index 0000000..4d1370c --- /dev/null +++ b/SOURCES/0043-Generate-PIN-for-PKI-to-help-Dogtag-in-FIPS.patch @@ -0,0 +1,68 @@ +From 036605789d6b34f5592d2ef38723eeb87e6ae21a Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 28 Mar 2017 13:54:16 +0200 +Subject: [PATCH] Generate PIN for PKI to help Dogtag in FIPS + +Dogtag is currently unable to generate a PIN it could use for +an NSS database creation in FIPS. Generate it for them so that +we don't fail. + +https://pagure.io/freeipa/issue/6824 + +Reviewed-By: Tomas Krizek +--- + ipaserver/install/cainstance.py | 6 +++++- + ipaserver/install/krainstance.py | 6 +++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index f0d3c236810d01f08192b239c0edb362ed78e071..92bb760d39d23fedb40b7e3c5bea53381f1c87ad 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -541,6 +541,10 @@ class CAInstance(DogtagInstance): + # CA key algorithm + config.set("CA", "pki_ca_signing_key_algorithm", self.ca_signing_algorithm) + ++ # generate pin which we know can be used for FIPS NSS database ++ pki_pin = ipautil.ipa_generate_password() ++ config.set("CA", "pki_pin", pki_pin) ++ + if self.clone: + + if self.no_db_setup: +@@ -613,7 +617,7 @@ class CAInstance(DogtagInstance): + try: + DogtagInstance.spawn_instance( + self, cfg_file, +- nolog_list=(self.dm_password, self.admin_password) ++ nolog_list=(self.dm_password, self.admin_password, pki_pin) + ) + finally: + os.remove(cfg_file) +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index b41ccb6fa6517f53ad1f83389b45795f0cd135bc..34d667857a8055752e258a591af983190f33daa5 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -235,6 +235,10 @@ class KRAInstance(DogtagInstance): + "KRA", "pki_share_dbuser_dn", + str(DN(('uid', 'pkidbuser'), ('ou', 'people'), ('o', 'ipaca')))) + ++ # generate pin which we know can be used for FIPS NSS database ++ pki_pin = ipautil.ipa_generate_password() ++ config.set("KRA", "pki_pin", pki_pin) ++ + _p12_tmpfile_handle, p12_tmpfile_name = tempfile.mkstemp(dir=paths.TMP) + + if self.clone: +@@ -275,7 +279,7 @@ class KRAInstance(DogtagInstance): + try: + DogtagInstance.spawn_instance( + self, cfg_file, +- nolog_list=(self.dm_password, self.admin_password) ++ nolog_list=(self.dm_password, self.admin_password, pki_pin) + ) + finally: + os.remove(p12_tmpfile_name) +-- +2.12.1 + diff --git a/SOURCES/0044-httpinstance.disable_system_trust-Don-t-fail-if-modu.patch b/SOURCES/0044-httpinstance.disable_system_trust-Don-t-fail-if-modu.patch new file mode 100644 index 0000000..f8356de --- /dev/null +++ b/SOURCES/0044-httpinstance.disable_system_trust-Don-t-fail-if-modu.patch @@ -0,0 +1,47 @@ +From 9cacddeb763c3e07ee31ac5bc2528cfb274b57b1 Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Mon, 27 Mar 2017 09:30:53 +0200 +Subject: [PATCH] httpinstance.disable_system_trust: Don't fail if module 'Root + Certs' is not available + +Server installation failed when attmpting to disable module 'Root Certs' and +the module was not available in HTTP_ALIAS_DIR. When the module is not +available there's no need to disable it and the error may be treated as +success. + +https://pagure.io/freeipa/issue/6803 + +Reviewed-By: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/httpinstance.py | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index f6f0b0c4f6acd648aa9f6f5d7400617613245473..01b55e7a7b00d020b7745c419267ad4f0ba86804 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -355,9 +355,17 @@ class HTTPInstance(service.Service): + name = 'Root Certs' + args = [paths.MODUTIL, '-dbdir', paths.HTTPD_ALIAS_DIR, '-force'] + +- result = ipautil.run(args + ['-list', name], +- env={}, +- capture_output=True) ++ try: ++ result = ipautil.run(args + ['-list', name], ++ env={}, ++ capture_output=True) ++ except ipautil.CalledProcessError as e: ++ if e.returncode == 29: # ERROR: Module not found in database. ++ root_logger.debug( ++ 'Module %s not available, treating as disabled', name) ++ return False ++ raise ++ + if 'Status: Enabled' in result.output: + ipautil.run(args + ['-disable', name], env={}) + return True +-- +2.12.1 + diff --git a/SOURCES/0045-extdom-do-reverse-search-for-domain-separator.patch b/SOURCES/0045-extdom-do-reverse-search-for-domain-separator.patch new file mode 100644 index 0000000..c422b4d --- /dev/null +++ b/SOURCES/0045-extdom-do-reverse-search-for-domain-separator.patch @@ -0,0 +1,49 @@ +From e795c094ac6ccc8a33e247ba56b93c35ed9cf57d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 17 Mar 2017 14:10:52 +0100 +Subject: [PATCH] extdom: do reverse search for domain separator + +To avoid issues which @-signs in the short user or group names it is +better to search for the domain separator starting at the end of the +fully-qualified name. + +Reviewed-By: Alexander Bokovoy +Reviewed-By: David Kupka +--- + daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c +index e629247fd771e374d50486d836cd3b0d8d32a78a..aa1ff10dfbf51b87a367261202b39d1346bd337a 100644 +--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c ++++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c +@@ -515,7 +515,7 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx, + char *short_user_name = NULL; + + short_user_name = strdup(user_name); +- if ((locat = strchr(short_user_name, SSSD_DOMAIN_SEPARATOR)) != NULL) { ++ if ((locat = strrchr(short_user_name, SSSD_DOMAIN_SEPARATOR)) != NULL) { + if (strcasecmp(locat+1, domain_name) == 0 ) { + locat[0] = '\0'; + } else { +@@ -626,7 +626,7 @@ int pack_ber_group(enum response_types response_type, + char *short_group_name = NULL; + + short_group_name = strdup(group_name); +- if ((locat = strchr(short_group_name, SSSD_DOMAIN_SEPARATOR)) != NULL) { ++ if ((locat = strrchr(short_group_name, SSSD_DOMAIN_SEPARATOR)) != NULL) { + if (strcasecmp(locat+1, domain_name) == 0 ) { + locat[0] = '\0'; + } else { +@@ -901,7 +901,7 @@ static int handle_sid_or_cert_request(struct ipa_extdom_ctx *ctx, + goto done; + } + +- sep = strchr(fq_name, SSSD_DOMAIN_SEPARATOR); ++ sep = strrchr(fq_name, SSSD_DOMAIN_SEPARATOR); + if (sep == NULL) { + set_err_msg(req, "Failed to split fully qualified name"); + ret = LDAP_OPERATIONS_ERROR; +-- +2.12.1 + diff --git a/SOURCES/0046-extdom-improve-cert-request.patch b/SOURCES/0046-extdom-improve-cert-request.patch new file mode 100644 index 0000000..bbdf685 --- /dev/null +++ b/SOURCES/0046-extdom-improve-cert-request.patch @@ -0,0 +1,243 @@ +From b2af54f9e327763783f482b3d5b7b3819ce75f82 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 17 Mar 2017 14:48:50 +0100 +Subject: [PATCH] extdom: improve cert request + +Certificates can be assigned to multiple user so the extdom plugin must +use sss_nss_getlistbycert() instead of sss_nss_getnamebycert() and +return a list of fully-qualified user names. + +Due to issues on the SSSD side the current version of lookups by +certificates didn't work at all and the changes here won't break +existing clients. + +Related to https://pagure.io/freeipa/issue/6826 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: David Kupka +--- + .../ipa-extdom-extop/ipa_extdom.h | 3 +- + .../ipa-extdom-extop/ipa_extdom_common.c | 157 ++++++++++++++++++--- + server.m4 | 2 +- + 3 files changed, 143 insertions(+), 19 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h +index 34e2d3c795e33bbeb9552910d80ddcc828751b93..bc29f069816b0ce13578c9ae14c61edb832d44e4 100644 +--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h ++++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h +@@ -95,7 +95,8 @@ enum response_types { + RESP_USER, + RESP_GROUP, + RESP_USER_GROUPLIST, +- RESP_GROUP_MEMBERS ++ RESP_GROUP_MEMBERS, ++ RESP_NAME_LIST + }; + + struct extdom_req { +diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c +index aa1ff10dfbf51b87a367261202b39d1346bd337a..fe225fa86669a6728bec5014be41d80275f10717 100644 +--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c ++++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c +@@ -698,6 +698,90 @@ done: + return ret; + } + ++int pack_ber_name_list(struct extdom_req *req, char **fq_name_list, ++ struct berval **berval) ++{ ++ BerElement *ber = NULL; ++ int ret; ++ char *sep; ++ size_t c; ++ size_t len; ++ size_t name_len; ++ ++ /* count the names */ ++ for (c = 0; fq_name_list[c] != NULL; c++); ++ if (c == 0) { ++ set_err_msg(req, "Empty name list"); ++ return LDAP_NO_SUCH_OBJECT; ++ } ++ ++ ber = ber_alloc_t( LBER_USE_DER ); ++ if (ber == NULL) { ++ set_err_msg(req, "BER alloc failed"); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ ++ ret = ber_printf(ber,"{e{", RESP_NAME_LIST); ++ if (ret == -1) { ++ set_err_msg(req, "BER start failed"); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ for (c = 0; fq_name_list[c] != NULL; c++) { ++ len = strlen(fq_name_list[c]); ++ if (len < 3) { ++ set_err_msg(req, "Fully qualified name too short"); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ sep = strrchr(fq_name_list[c], SSSD_DOMAIN_SEPARATOR); ++ if (sep == NULL) { ++ set_err_msg(req, "Failed to split fully qualified name"); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ name_len = sep - fq_name_list[c]; ++ if (name_len == 0) { ++ set_err_msg(req, "Missing name."); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ if (name_len + 1 == len) { ++ set_err_msg(req, "Missing domain."); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ ret = ber_printf(ber,"{oo}", (sep + 1), len - name_len -1, ++ fq_name_list[c], name_len); ++ if (ret == -1) { ++ set_err_msg(req, "BER list item failed"); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ } ++ ++ ret = ber_printf(ber,"}}"); ++ if (ret == -1) { ++ set_err_msg(req, "BER end failed"); ++ ber_free(ber, 1); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ ret = ber_flatten(ber, berval); ++ ber_free(ber, 1); ++ if (ret == -1) { ++ set_err_msg(req, "BER flatten failed"); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ ++ return LDAP_SUCCESS; ++} ++ + int pack_ber_name(const char *domain_name, const char *name, + struct berval **berval) + { +@@ -867,12 +951,56 @@ done: + return ret; + } + +-static int handle_sid_or_cert_request(struct ipa_extdom_ctx *ctx, +- struct extdom_req *req, +- enum request_types request_type, +- enum input_types input_type, +- const char *input, +- struct berval **berval) ++static int handle_cert_request(struct ipa_extdom_ctx *ctx, ++ struct extdom_req *req, ++ enum request_types request_type, ++ enum input_types input_type, ++ const char *input, ++ struct berval **berval) ++{ ++ int ret; ++ char **fq_names = NULL; ++ enum sss_id_type *id_types = NULL; ++ size_t c; ++ ++ if (request_type != REQ_SIMPLE) { ++ set_err_msg(req, "Only simple request type allowed " ++ "for lookups by certificate"); ++ ret = LDAP_PROTOCOL_ERROR; ++ goto done; ++ } ++ ++ ret = sss_nss_getlistbycert(input, &fq_names, &id_types); ++ if (ret != 0) { ++ if (ret == ENOENT) { ++ ret = LDAP_NO_SUCH_OBJECT; ++ } else { ++ set_err_msg(req, "Failed to lookup name by certificate"); ++ ret = LDAP_OPERATIONS_ERROR; ++ } ++ goto done; ++ } ++ ++ ret = pack_ber_name_list(req, fq_names, berval); ++ ++done: ++ if (fq_names != NULL) { ++ for (c = 0; fq_names[c] != NULL; c++) { ++ free(fq_names[c]); ++ } ++ free(fq_names); ++ } ++ free(id_types); ++ ++ return ret; ++} ++ ++static int handle_sid_request(struct ipa_extdom_ctx *ctx, ++ struct extdom_req *req, ++ enum request_types request_type, ++ enum input_types input_type, ++ const char *input, ++ struct berval **berval) + { + int ret; + struct passwd pwd; +@@ -886,11 +1014,7 @@ static int handle_sid_or_cert_request(struct ipa_extdom_ctx *ctx, + enum sss_id_type id_type; + struct sss_nss_kv *kv_list = NULL; + +- if (input_type == INP_SID) { +- ret = sss_nss_getnamebysid(input, &fq_name, &id_type); +- } else { +- ret = sss_nss_getnamebycert(input, &fq_name, &id_type); +- } ++ ret = sss_nss_getnamebysid(input, &fq_name, &id_type); + if (ret != 0) { + if (ret == ENOENT) { + ret = LDAP_NO_SUCH_OBJECT; +@@ -1147,13 +1271,12 @@ int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req, + + break; + case INP_SID: ++ ret = handle_sid_request(ctx, req, req->request_type, ++ req->input_type, req->data.sid, berval); ++ break; + case INP_CERT: +- ret = handle_sid_or_cert_request(ctx, req, req->request_type, +- req->input_type, +- req->input_type == INP_SID ? +- req->data.sid : +- req->data.cert, +- berval); ++ ret = handle_cert_request(ctx, req, req->request_type, ++ req->input_type, req->data.cert, berval); + break; + case INP_NAME: + ret = handle_name_request(ctx, req, req->request_type, +diff --git a/server.m4 b/server.m4 +index a4c99195ae535e586445cf5bbe9fef457d224531..5d5333e194cb339d31576f54a70d96becadf9a87 100644 +--- a/server.m4 ++++ b/server.m4 +@@ -28,7 +28,7 @@ DIRSRV_CFLAGS="$DIRSRV_CFLAGS $NSPR_CFLAGS" + + dnl -- sss_idmap is needed by the extdom exop -- + PKG_CHECK_MODULES([SSSIDMAP], [sss_idmap]) +-PKG_CHECK_MODULES([SSSNSSIDMAP], [sss_nss_idmap >= 1.13.90]) ++PKG_CHECK_MODULES([SSSNSSIDMAP], [sss_nss_idmap >= 1.15.2]) + + dnl -- sss_certmap and certauth.h are needed by the IPA KDB certauth plugin -- + PKG_CHECK_EXISTS([sss_certmap], +-- +2.12.1 + diff --git a/SOURCES/0047-spec-file-bump-libsss_nss_idmap-devel-BuildRequires.patch b/SOURCES/0047-spec-file-bump-libsss_nss_idmap-devel-BuildRequires.patch new file mode 100644 index 0000000..a7fd0f5 --- /dev/null +++ b/SOURCES/0047-spec-file-bump-libsss_nss_idmap-devel-BuildRequires.patch @@ -0,0 +1,37 @@ +From e7091fc939b7b7a1c23f4e95220e343ca21958ad Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 29 Mar 2017 07:14:24 +0000 +Subject: [PATCH] spec file: bump libsss_nss_idmap-devel BuildRequires + +Bump BuildRequires on libsss_nss_idmap-devel to the version which +introduces the sss_nss_getlistbycert function. + +This fixes RPM build failure when an older version of +libsss_nss_idmap-devel was installed. + +https://pagure.io/freeipa/issue/6828 + +Reviewed-By: Tomas Krizek +Reviewed-By: Alexander Bokovoy +--- + freeipa.spec.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index e7e39e87bef39653d660a345793750f59c8dd715..829c3f0b2898de1ecbf0cfb769fde5cd978c241c 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -121,8 +121,8 @@ BuildRequires: libtevent-devel + BuildRequires: libuuid-devel + BuildRequires: libsss_idmap-devel + BuildRequires: libsss_certmap-devel +-# 1.14.0: sss_nss_getnamebycert (https://fedorahosted.org/sssd/ticket/2897) +-BuildRequires: libsss_nss_idmap-devel >= 1.14.0 ++# 1.15.3: sss_nss_getlistbycert (https://pagure.io/SSSD/sssd/issue/3050) ++BuildRequires: libsss_nss_idmap-devel >= 1.15.3 + BuildRequires: rhino + BuildRequires: libverto-devel + BuildRequires: libunistring-devel +-- +2.12.2 + diff --git a/SOURCES/0048-server-make-sure-we-test-for-sss_nss_getlistbycert.patch b/SOURCES/0048-server-make-sure-we-test-for-sss_nss_getlistbycert.patch new file mode 100644 index 0000000..1da5471 --- /dev/null +++ b/SOURCES/0048-server-make-sure-we-test-for-sss_nss_getlistbycert.patch @@ -0,0 +1,32 @@ +From 6c76be7f9d5c25d940b026310e2efec3d46b5d23 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 29 Mar 2017 10:43:11 +0300 +Subject: [PATCH] server: make sure we test for sss_nss_getlistbycert + +Fixes https://pagure.io/freeipa/issue/6828 + +Reviewed-By: Christian Heimes +Reviewed-By: Tomas Krizek +--- + server.m4 | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/server.m4 b/server.m4 +index 5d5333e194cb339d31576f54a70d96becadf9a87..346d73e906c5d0499e46fcc4da070007b2ff5973 100644 +--- a/server.m4 ++++ b/server.m4 +@@ -29,6 +29,11 @@ DIRSRV_CFLAGS="$DIRSRV_CFLAGS $NSPR_CFLAGS" + dnl -- sss_idmap is needed by the extdom exop -- + PKG_CHECK_MODULES([SSSIDMAP], [sss_idmap]) + PKG_CHECK_MODULES([SSSNSSIDMAP], [sss_nss_idmap >= 1.15.2]) ++AC_CHECK_LIB([sss_nss_idmap], ++ [sss_nss_getlistbycert], ++ [], ++ [AC_MSG_ERROR([Required sss_nss_getlistbycert symbol in sss_nss_idmap not found])], ++ []) + + dnl -- sss_certmap and certauth.h are needed by the IPA KDB certauth plugin -- + PKG_CHECK_EXISTS([sss_certmap], +-- +2.12.2 + diff --git a/SOURCES/0049-Upgrade-configure-PKINIT-after-adding-anonymous-prin.patch b/SOURCES/0049-Upgrade-configure-PKINIT-after-adding-anonymous-prin.patch new file mode 100644 index 0000000..9ce9eec --- /dev/null +++ b/SOURCES/0049-Upgrade-configure-PKINIT-after-adding-anonymous-prin.patch @@ -0,0 +1,34 @@ +From a4140595a3fcb42d9666aea823d3d8cd9ae0c7c3 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 21 Mar 2017 17:03:35 +0100 +Subject: [PATCH] Upgrade: configure PKINIT after adding anonymous principal + +In order to set up PKINIT, the anonymous principal must already be +created, otherwise the upgrade with fail when trying out anonymous +PKINIT. Switch the order of steps so that this issue does not occur. + +https://pagure.io/freeipa/issue/6792 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/server/upgrade.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 1706079da86d9ba9066f71f02b170c161c1f2963..be07d78585d4772eb6dd0aaa8fb4ccb588c42c65 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1809,9 +1809,9 @@ def upgrade_configuration(): + KDC_CERT=paths.KDC_CERT, + KDC_KEY=paths.KDC_KEY, + CACERT_PEM=paths.CACERT_PEM) +- setup_pkinit(krb) + enable_anonymous_principal(krb) + http.request_anon_keytab() ++ setup_pkinit(krb) + + if not ds_running: + ds.stop(ds_serverid) +-- +2.12.2 + diff --git a/SOURCES/0050-Remove-unused-variable-from-failed-anonymous-PKINIT-.patch b/SOURCES/0050-Remove-unused-variable-from-failed-anonymous-PKINIT-.patch new file mode 100644 index 0000000..bdc94ba --- /dev/null +++ b/SOURCES/0050-Remove-unused-variable-from-failed-anonymous-PKINIT-.patch @@ -0,0 +1,28 @@ +From 7b4ef6d23fb335d99b38347f1c4516a21222231e Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 22 Mar 2017 10:01:34 +0100 +Subject: [PATCH] Remove unused variable from failed anonymous PKINIT handling + +https://pagure.io/freeipa/issue/6792 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/krbinstance.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index d936cc5f4f47e0e641a2d9ba4f943aab0301045c..c817076249a224347421b1bf18088eecb8eb345f 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -413,7 +413,7 @@ class KrbInstance(service.Service): + with ipautil.private_ccache() as anon_ccache: + try: + ipautil.run([paths.KINIT, '-n', '-c', anon_ccache]) +- except ipautil.CalledProcessError as e: ++ except ipautil.CalledProcessError: + raise RuntimeError("Failed to configure anonymous PKINIT") + + def enable_ssl(self): +-- +2.12.2 + diff --git a/SOURCES/0051-Split-out-anonymous-PKINIT-test-to-a-separate-method.patch b/SOURCES/0051-Split-out-anonymous-PKINIT-test-to-a-separate-method.patch new file mode 100644 index 0000000..f509598 --- /dev/null +++ b/SOURCES/0051-Split-out-anonymous-PKINIT-test-to-a-separate-method.patch @@ -0,0 +1,37 @@ +From b12c465ae8b8ffb1e34741daf8c0dea6525e5fcf Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 22 Mar 2017 10:04:52 +0100 +Subject: [PATCH] Split out anonymous PKINIT test to a separate method + +This allows for more flexibility in the whole PKINIT setup process. + +https://pagure.io/freeipa/issue/6792 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/krbinstance.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index c817076249a224347421b1bf18088eecb8eb345f..5f4b5282f54234c15b1a8d8273eff69e134e665b 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -410,6 +410,7 @@ class KrbInstance(service.Service): + root_logger.critical("krb5kdc service failed to restart") + raise + ++ def test_anonymous_pkinit(self): + with ipautil.private_ccache() as anon_ccache: + try: + ipautil.run([paths.KINIT, '-n', '-c', anon_ccache]) +@@ -421,6 +422,7 @@ class KrbInstance(service.Service): + self.steps = [] + self.step("installing X509 Certificate for PKINIT", + self.setup_pkinit) ++ self.step("testing anonymous PKINIT", self.test_anonymous_pkinit) + + self.start_creation() + +-- +2.12.2 + diff --git a/SOURCES/0052-Ensure-KDC-is-propery-configured-after-upgrade.patch b/SOURCES/0052-Ensure-KDC-is-propery-configured-after-upgrade.patch new file mode 100644 index 0000000..4be94d5 --- /dev/null +++ b/SOURCES/0052-Ensure-KDC-is-propery-configured-after-upgrade.patch @@ -0,0 +1,47 @@ +From 73ed5d59d0777329450cb8d6dce78f8ee862068b Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 22 Mar 2017 11:56:18 +0100 +Subject: [PATCH] Ensure KDC is propery configured after upgrade + +https://pagure.io/freeipa/issue/6792 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/server/upgrade.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index be07d78585d4772eb6dd0aaa8fb4ccb588c42c65..0db764cb80f6d0fb22f00719dadf1f921f97bf62 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1499,15 +1499,14 @@ def enable_anonymous_principal(krb): + def setup_pkinit(krb): + root_logger.info("[Setup PKINIT]") + +- if os.path.exists(paths.KDC_CERT): +- root_logger.info("PKINIT already set up") +- return +- + if not api.Command.ca_is_enabled()['result']: + root_logger.info("CA is not enabled") + return + +- krb.setup_pkinit() ++ if not os.path.exists(paths.KDC_CERT): ++ root_logger.info("Requesting PKINIT certificate") ++ krb.setup_pkinit() ++ + replacevars = dict() + replacevars['pkinit_identity'] = 'FILE:{},{}'.format( + paths.KDC_CERT,paths.KDC_KEY) +@@ -1519,6 +1518,7 @@ def setup_pkinit(krb): + if krb.is_running(): + krb.stop() + krb.start() ++ krb.test_anonymous_pkinit() + + + def disable_httpd_system_trust(http): +-- +2.12.2 + diff --git a/SOURCES/0053-adtrust-make-sure-that-runtime-hostname-result-is-co.patch b/SOURCES/0053-adtrust-make-sure-that-runtime-hostname-result-is-co.patch new file mode 100644 index 0000000..d1feda9 --- /dev/null +++ b/SOURCES/0053-adtrust-make-sure-that-runtime-hostname-result-is-co.patch @@ -0,0 +1,77 @@ +From dd4ae3da2d341a25b63936b689e53fdbc8e93f65 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Mon, 20 Mar 2017 13:23:44 +0200 +Subject: [PATCH] adtrust: make sure that runtime hostname result is consistent + with the configuration + +FreeIPA's `ipasam` module to Samba uses gethostname() call to identify +own server's host name. This value is then used in multiple places, +including construction of cifs/host.name principal. `ipasam` module +always uses GSSAPI authentication when talking to LDAP, so Kerberos +keys must be available in the /etc/samba/samba.keytab. However, if +the principal was created using non-FQDN name but system reports +FQDN name, `ipasam` will fail to acquire Kerberos credentials. +Same with FQDN principal and non-FQDN hostname. + +Also host name and principal name must have the same case. + +Report an error when configuring ADTrust instance with inconsistent +runtime hostname and configuration. This prevents errors like this: + + [20/21]: starting CIFS services + ipa : CRITICAL CIFS services failed to start + + where samba logs have this: + + [2017/03/20 06:34:27.385307, 0] ipa_sam.c:4193(bind_callback_cleanup) + kerberos error: code=-1765328203, message=Keytab contains no suitable keys for cifs/ipatrust@EXAMPLE.COM + [2017/03/20 06:34:27.385476, 1] ../source3/lib/smbldap.c:1206(get_cached_ldap_connect) + Connection to LDAP server failed for the 16 try! + +Fixes https://pagure.io/freeipa/issue/6786 + +Reviewed-By: Martin Basti +--- + ipaserver/install/adtrustinstance.py | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py +index 0b189854f568ea5d8c0e68077255939887ff0cc3..b4db055045823ce8ae7e3b264e1442a085f81b2d 100644 +--- a/ipaserver/install/adtrustinstance.py ++++ b/ipaserver/install/adtrustinstance.py +@@ -27,6 +27,7 @@ import uuid + import string + import struct + import re ++import socket + + import six + +@@ -689,6 +690,15 @@ class ADTRUSTInstance(service.Service): + except Exception as e: + root_logger.critical("Enabling nsswitch support in slapi-nis failed with error '%s'" % e) + ++ def __validate_server_hostname(self): ++ hostname = socket.gethostname() ++ if hostname != self.fqdn: ++ raise ValueError("Host reports different name than configured: " ++ "'%s' versus '%s'. Samba requires to have " ++ "the same hostname or Kerberos principal " ++ "'cifs/%s' will not be found in Samba keytab." % ++ (hostname, self.fqdn, self.fqdn)) ++ + def __start(self): + try: + self.start() +@@ -804,6 +814,8 @@ class ADTRUSTInstance(service.Service): + api.Backend.ldap2.add_entry(entry) + + def create_instance(self): ++ self.step("validate server hostname", ++ self.__validate_server_hostname) + self.step("stopping smbd", self.__stop) + self.step("creating samba domain object", \ + self.__create_samba_domain_object) +-- +2.12.2 + diff --git a/SOURCES/0054-Allow-erasing-ipaDomainResolutionOrder-attribute.patch b/SOURCES/0054-Allow-erasing-ipaDomainResolutionOrder-attribute.patch new file mode 100644 index 0000000..2fe04fe --- /dev/null +++ b/SOURCES/0054-Allow-erasing-ipaDomainResolutionOrder-attribute.patch @@ -0,0 +1,42 @@ +From 5b9fe9df34d0eff697adefa171d35a57e561c17e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 28 Mar 2017 16:15:21 +0200 +Subject: [PATCH] Allow erasing ipaDomainResolutionOrder attribute +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently when trying to erase the ipaDomainResolutionOrder attribute we +hit an internal error as the split() method is called on a None object. + +By returning early in case of empty string we now allow removing the +ipaDomainResolutionOrder attribute by both calling delattr or setting +its value to an empty string. + +https://pagure.io/freeipa/issue/6825 + +Signed-off-by: Fabiano Fidêncio +Reviewed-By: Martin Basti +--- + ipaserver/plugins/config.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py +index 232c88121fd2c938f77a1e76e1d7c9f0ad8e7550..b50e7a4691bd76bfaf7c332cd89b0f1bf55bac46 100644 +--- a/ipaserver/plugins/config.py ++++ b/ipaserver/plugins/config.py +@@ -359,6 +359,11 @@ class config(LDAPObject): + + domain_resolution_order = entry_attrs[attr_name] + ++ # setting up an empty string means that the previous configuration has ++ # to be cleaned up/removed. So, do nothing and let it pass ++ if not domain_resolution_order: ++ return ++ + # empty resolution order is signalized by single separator, do nothing + # and let it pass + if domain_resolution_order == DOMAIN_RESOLUTION_ORDER_SEPARATOR: +-- +2.12.2 + diff --git a/SOURCES/0055-Always-check-and-create-anonymous-principal-during-K.patch b/SOURCES/0055-Always-check-and-create-anonymous-principal-during-K.patch new file mode 100644 index 0000000..4a67768 --- /dev/null +++ b/SOURCES/0055-Always-check-and-create-anonymous-principal-during-K.patch @@ -0,0 +1,70 @@ +From 6602dffc7ab8e9bdc7fefd02f9ed11e5575f5f7b Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 22 Mar 2017 16:41:59 +0100 +Subject: [PATCH] Always check and create anonymous principal during KDC + install + +The anonymous principal will now be checked for presence and created on +both server and replica install. This fixes errors caused during replica +installation against older master that do not have anonymous principal +present. + +https://pagure.io/freeipa/issue/6799 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/krbinstance.py | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 5f4b5282f54234c15b1a8d8273eff69e134e665b..6c105f74c8da2bfd34ace607b13170bc96a8ff1d 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -33,7 +33,7 @@ from ipaserver.install import installutils + from ipapython import ipaldap + from ipapython import ipautil + from ipapython import kernel_keyring +-from ipalib import api ++from ipalib import api, errors + from ipalib.constants import ANON_USER + from ipalib.install import certmonger + from ipapython.ipa_log_manager import root_logger +@@ -142,6 +142,7 @@ class KrbInstance(service.Service): + pass + + def __common_post_setup(self): ++ self.step("creating anonymous principal", self.add_anonymous_principal) + self.step("starting the KDC", self.__start_instance) + self.step("configuring KDC to start on boot", self.__enable) + +@@ -160,7 +161,6 @@ class KrbInstance(service.Service): + self.step("creating a keytab for the directory", self.__create_ds_keytab) + self.step("creating a keytab for the machine", self.__create_host_keytab) + self.step("adding the password extension to the directory", self.__add_pwd_extop_module) +- self.step("creating anonymous principal", self.add_anonymous_principal) + + self.__common_post_setup() + +@@ -432,8 +432,17 @@ class KrbInstance(service.Service): + def add_anonymous_principal(self): + # Create the special anonymous principal + princ_realm = self.get_anonymous_principal_name() +- installutils.kadmin_addprinc(princ_realm) +- self._ldap_mod("anon-princ-aci.ldif", self.sub_dict) ++ dn = DN(('krbprincipalname', princ_realm), self.get_realm_suffix()) ++ try: ++ self.api.Backend.ldap2.get_entry(dn) ++ except errors.NotFound: ++ installutils.kadmin_addprinc(princ_realm) ++ self._ldap_mod("anon-princ-aci.ldif", self.sub_dict) ++ ++ try: ++ self.api.Backend.ldap2.set_entry_active(dn, True) ++ except errors.AlreadyActive: ++ pass + + def __convert_to_gssapi_replication(self): + repl = replication.ReplicationManager(self.realm, +-- +2.12.2 + diff --git a/SOURCES/0056-Remove-duplicate-functionality-in-upgrade.patch b/SOURCES/0056-Remove-duplicate-functionality-in-upgrade.patch new file mode 100644 index 0000000..2b9d354 --- /dev/null +++ b/SOURCES/0056-Remove-duplicate-functionality-in-upgrade.patch @@ -0,0 +1,53 @@ +From dd300d7db884db2d0aa228c08d2447539ce14c1c Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 22 Mar 2017 16:52:14 +0100 +Subject: [PATCH] Remove duplicate functionality in upgrade + +Since krbinstance code can now handle all operations of the +`enabled_anonymous_principal` function from upgrade we can remove +extraneous function altogether. + +https://pagure.io/freeipa/issue/6799 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/server/upgrade.py | 16 +--------------- + 1 file changed, 1 insertion(+), 15 deletions(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 0db764cb80f6d0fb22f00719dadf1f921f97bf62..25b86297af3ae9d5f21cebb93f493b90670dcfc3 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1482,20 +1482,6 @@ def add_default_caacl(ca): + sysupgrade.set_upgrade_state('caacl', 'add_default_caacl', True) + + +-def enable_anonymous_principal(krb): +- princ_realm = krb.get_anonymous_principal_name() +- dn = DN(('krbprincipalname', princ_realm), krb.get_realm_suffix()) +- try: +- _ = api.Backend.ldap2.get_entry(dn) # pylint: disable=unused-variable +- except ipalib.errors.NotFound: +- krb.add_anonymous_principal() +- +- try: +- api.Backend.ldap2.set_entry_active(dn, True) +- except ipalib.errors.AlreadyActive: +- pass +- +- + def setup_pkinit(krb): + root_logger.info("[Setup PKINIT]") + +@@ -1809,7 +1795,7 @@ def upgrade_configuration(): + KDC_CERT=paths.KDC_CERT, + KDC_KEY=paths.KDC_KEY, + CACERT_PEM=paths.CACERT_PEM) +- enable_anonymous_principal(krb) ++ krb.add_anonymous_principal() + http.request_anon_keytab() + setup_pkinit(krb) + +-- +2.12.2 + diff --git a/SOURCES/0057-Fix-the-order-of-cert-files-check.patch b/SOURCES/0057-Fix-the-order-of-cert-files-check.patch new file mode 100644 index 0000000..d1c9c42 --- /dev/null +++ b/SOURCES/0057-Fix-the-order-of-cert-files-check.patch @@ -0,0 +1,47 @@ +From 9bc2481d6669906e105e1035a10cd81374464e5b Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 22 Mar 2017 17:10:56 +0100 +Subject: [PATCH] Fix the order of cert-files check + +Without this patch, if either of dirsrv_cert_files, http_cert_files +or pkinit_cert_files is set along with no-pkinit, the user is first +requested to add the remaining options and when they do that, +they are told that they are using 'no-pkinit' along with +'pkinit-cert-file'. + +https://pagure.io/freeipa/issue/6801 + +Reviewed-By: Martin Basti +--- + ipaserver/install/server/__init__.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py +index 14f1ec48a1b8c7a520db69ffad378d488efa29cc..117f51c4ebfaeba51d3c85625cda0d0eee305696 100644 +--- a/ipaserver/install/server/__init__.py ++++ b/ipaserver/install/server/__init__.py +@@ -340,16 +340,16 @@ class ServerInstallInterface(ServerCertificateInstallInterface, + cert_file_opt = (self.pkinit_cert_files,) + if not self.no_pkinit: + cert_file_req += cert_file_opt +- if any(cert_file_req + cert_file_opt) and not all(cert_file_req): +- raise RuntimeError( +- "--dirsrv-cert-file, --http-cert-file, and --pkinit-cert-file " +- "or --no-pkinit are required if any key file options are used." +- ) + if self.no_pkinit and self.pkinit_cert_files: + raise RuntimeError( + "--no-pkinit and --pkinit-cert-file cannot be specified " + "together" + ) ++ if any(cert_file_req + cert_file_opt) and not all(cert_file_req): ++ raise RuntimeError( ++ "--dirsrv-cert-file, --http-cert-file, and --pkinit-cert-file " ++ "or --no-pkinit are required if any key file options are used." ++ ) + + if not self.interactive: + if self.dirsrv_cert_files and self.dirsrv_pin is None: +-- +2.12.2 + diff --git a/SOURCES/0058-Don-t-allow-setting-pkinit-related-options-on-DL0.patch b/SOURCES/0058-Don-t-allow-setting-pkinit-related-options-on-DL0.patch new file mode 100644 index 0000000..1675a78 --- /dev/null +++ b/SOURCES/0058-Don-t-allow-setting-pkinit-related-options-on-DL0.patch @@ -0,0 +1,95 @@ +From 60b57639295ab94949986ec59de3c8e6c92bee7d Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 22 Mar 2017 17:26:51 +0100 +Subject: [PATCH] Don't allow setting pkinit-related options on DL0 + +pkinit is not supported on DL0, remove options that allow to set it +from ipa-{server,replica}-install. + +https://pagure.io/freeipa/issue/6801 + +Reviewed-By: Martin Basti +--- + install/tools/man/ipa-replica-install.1 | 2 +- + install/tools/man/ipa-server-install.1 | 2 +- + ipaserver/install/server/__init__.py | 21 +++++++++++++++++++++ + 3 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1 +index d63912c7018bd09a8567688a1f8d4db0c698ac3f..7d241324818dd3a5294da5e84b67a19d0d9a31b6 100644 +--- a/install/tools/man/ipa-replica-install.1 ++++ b/install/tools/man/ipa-replica-install.1 +@@ -114,7 +114,7 @@ Install and configure a CA on this replica. If a CA is not configured then + certificate operations will be forwarded to a master with a CA installed. + .TP + \fB\-\-no\-pkinit\fR +-Disables pkinit setup steps ++Disables pkinit setup steps. This is the default and only allowed behavior on domain level 0. + .TP + \fB\-\-dirsrv\-cert\-file\fR=FILE + File containing the Directory Server SSL certificate and private key +diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1 +index c48bdae7485a34d72381188191d6423ca2d16044..d5d28df8e72295296a9ac321623ead49fe4692a3 100644 +--- a/install/tools/man/ipa-server-install.1 ++++ b/install/tools/man/ipa-server-install.1 +@@ -93,7 +93,7 @@ Type of the external CA. Possible values are "generic", "ms-cs". Default value i + File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. + .TP + \fB\-\-no\-pkinit\fR +-Disables pkinit setup steps ++Disables pkinit setup steps. This is the default and only allowed behavior on domain level 0. + .TP + \fB\-\-dirsrv\-cert\-file\fR=\fIFILE\fR + File containing the Directory Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times. +diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py +index 117f51c4ebfaeba51d3c85625cda0d0eee305696..096cb0142fc7fe70fdc3d2ad1e5caedf0f65b643 100644 +--- a/ipaserver/install/server/__init__.py ++++ b/ipaserver/install/server/__init__.py +@@ -332,9 +332,24 @@ class ServerInstallInterface(ServerCertificateInstallInterface, + if not os.path.exists(value): + raise ValueError("File %s does not exist." % value) + ++ def _is_promote(self): ++ """ ++ :returns: True if domain level options correspond to domain level > 0 ++ """ ++ raise NotImplementedError() ++ + def __init__(self, **kwargs): + super(ServerInstallInterface, self).__init__(**kwargs) + ++ # pkinit is not supported on DL0, don't allow related options ++ if not self._is_promote(): ++ if (self.no_pkinit or self.pkinit_cert_files is not None or ++ self.pkinit_pin is not None): ++ raise RuntimeError( ++ "pkinit on domain level 0 is not supported. Please " ++ "don't use any pkinit-related options.") ++ self.no_pkinit = True ++ + # If any of the key file options are selected, all are required. + cert_file_req = (self.dirsrv_cert_files, self.http_cert_files) + cert_file_opt = (self.pkinit_cert_files,) +@@ -557,6 +572,9 @@ class ServerMasterInstall(ServerMasterInstallInterface): + add_sids = True + add_agents = False + ++ def _is_promote(self): ++ return self.domain_level > constants.DOMAIN_LEVEL_0 ++ + def __init__(self, **kwargs): + super(ServerMasterInstall, self).__init__(**kwargs) + master_init(self) +@@ -590,6 +608,9 @@ class ServerReplicaInstall(ServerReplicaInstallInterface): + description="Kerberos password for the specified admin principal", + ) + ++ def _is_promote(self): ++ return self.replica_file is None ++ + def __init__(self, **kwargs): + super(ServerReplicaInstall, self).__init__(**kwargs) + replica_init(self) +-- +2.12.2 + diff --git a/SOURCES/0059-replica-prepare-man-remove-pkinit-option-refs.patch b/SOURCES/0059-replica-prepare-man-remove-pkinit-option-refs.patch new file mode 100644 index 0000000..ac24ad6 --- /dev/null +++ b/SOURCES/0059-replica-prepare-man-remove-pkinit-option-refs.patch @@ -0,0 +1,60 @@ +From a0b479ef9f9be97cc170734c0af5330d9fd702ce Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 24 Mar 2017 12:29:53 +0100 +Subject: [PATCH] replica-prepare man: remove pkinit option refs + +Remove the references to the pkinit options which was forgotten +about in 46d4d534c0 + +https://pagure.io/freeipa/issue/6801 + +Reviewed-By: Martin Basti +--- + install/tools/man/ipa-replica-prepare.1 | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/install/tools/man/ipa-replica-prepare.1 b/install/tools/man/ipa-replica-prepare.1 +index 2063657f8eb4e97fc11b1abb95a892e26b4344e6..afc5408ef87ec5cf967d00dd21aa848584c7eb1e 100644 +--- a/install/tools/man/ipa-replica-prepare.1 ++++ b/install/tools/man/ipa-replica-prepare.1 +@@ -43,27 +43,18 @@ File containing the Directory Server SSL certificate and private key. The files + \fB\-\-http\-cert\-file\fR=\fIFILE\fR + File containing the Apache Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times. + .TP +-\fB\-\-pkinit\-cert\-file\fR=\fIFILE\fR +-File containing the Kerberos KDC SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times. +-.TP + \fB\-\-dirsrv\-pin\fR=\fIPIN\fR + The password to unlock the Directory Server private key + .TP + \fB\-\-http\-pin\fR=\fIPIN\fR + The password to unlock the Apache Server private key + .TP +-\fB\-\-pkinit\-pin\fR=\fIPIN\fR +-The password to unlock the Kerberos KDC private key +-.TP + \fB\-\-dirsrv\-cert\-name\fR=\fINAME\fR + Name of the Directory Server SSL certificate to install + .TP + \fB\-\-http\-cert\-name\fR=\fINAME\fR + Name of the Apache Server SSL certificate to install + .TP +-\fB\-\-pkinit\-cert\-name\fR=\fINAME\fR +-Name of the Kerberos KDC SSL certificate to install +-.TP + \fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR + Directory Manager (existing master) password + .TP +@@ -81,9 +72,6 @@ Do not create reverse DNS zone + \fB\-\-ca\fR=\fICA_FILE\fR + Location of CA PKCS#12 file, default /root/cacert.p12 + .TP +-\fB\-\-no\-pkinit\fR +-Disables pkinit setup steps +-.TP + \fB\-\-debug\fR + Prints info log messages to the output + .SH "EXIT STATUS" +-- +2.12.2 + diff --git a/SOURCES/0060-Remove-redundant-option-check-for-cert-files.patch b/SOURCES/0060-Remove-redundant-option-check-for-cert-files.patch new file mode 100644 index 0000000..09e8115 --- /dev/null +++ b/SOURCES/0060-Remove-redundant-option-check-for-cert-files.patch @@ -0,0 +1,41 @@ +From abb400e3fbb7c607b6ec40cfd155aa14175d35d7 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 29 Mar 2017 09:00:09 +0200 +Subject: [PATCH] Remove redundant option check for cert files + +There was a redundant check for CA-less install certificate files +for replicas but the same check is done for all installers before +that. + +https://pagure.io/freeipa/issue/6801 + +Reviewed-By: Martin Basti +--- + ipaserver/install/server/__init__.py | 10 +--------- + 1 file changed, 1 insertion(+), 9 deletions(-) + +diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py +index 096cb0142fc7fe70fdc3d2ad1e5caedf0f65b643..89444f21fefc902931b7ecfaba861a18ecc28dbe 100644 +--- a/ipaserver/install/server/__init__.py ++++ b/ipaserver/install/server/__init__.py +@@ -470,16 +470,8 @@ class ServerInstallInterface(ServerCertificateInstallInterface, + "idmax (%s) cannot be smaller than idstart (%s)" % + (self.idmax, self.idstart)) + else: +- cert_file_req = (self.dirsrv_cert_files, self.http_cert_files) +- cert_file_opt = (self.pkinit_cert_files,) +- ++ # replica installers + if self.replica_file is None: +- # If any of the PKCS#12 options are selected, all are required. +- if any(cert_file_req + cert_file_opt) and not all(cert_file_req): +- raise RuntimeError( +- "--dirsrv-cert-file and --http-cert-file are required " +- "if any PKCS#12 options are used") +- + if self.servers and not self.domain_name: + raise RuntimeError( + "The --server option cannot be used without providing " +-- +2.12.2 + diff --git a/SOURCES/0061-Hide-request_type-doc-string-in-cert-request-help.patch b/SOURCES/0061-Hide-request_type-doc-string-in-cert-request-help.patch new file mode 100644 index 0000000..75c7a38 --- /dev/null +++ b/SOURCES/0061-Hide-request_type-doc-string-in-cert-request-help.patch @@ -0,0 +1,34 @@ +From 9a6731643f1b0e3c47df13866109323ddd4c5db0 Mon Sep 17 00:00:00 2001 +From: Abhijeet Kasurde +Date: Sat, 18 Feb 2017 16:31:07 +0530 +Subject: [PATCH] Hide request_type doc string in cert-request help + +Fix hides description of request_type argument in cert-request +command help + +Fixes https://pagure.io/freeipa/issue/6494 +Fixes https://pagure.io/freeipa/issue/5734 + +Signed-off-by: Abhijeet Kasurde +Reviewed-By: Martin Basti +Reviewed-By: Fraser Tweedale +--- + ipaserver/plugins/cert.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 1a6d04533cebb2eb00022981dae9ffe5b785ba8b..dfc7444ddbf31ac3c194e050af28220fc2a87a92 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -491,7 +491,7 @@ class certreq(BaseCertObject): + 'request_type', + default=u'pkcs10', + autofill=True, +- flags={'no_update', 'no_update', 'no_search'}, ++ flags={'no_option', 'no_update', 'no_update', 'no_search'}, + ), + Str( + 'profile_id?', validate_profile_id, +-- +2.12.2 + diff --git a/SOURCES/0062-Get-correct-CA-cert-nickname-in-CA-less.patch b/SOURCES/0062-Get-correct-CA-cert-nickname-in-CA-less.patch new file mode 100644 index 0000000..cfd0cb5 --- /dev/null +++ b/SOURCES/0062-Get-correct-CA-cert-nickname-in-CA-less.patch @@ -0,0 +1,60 @@ +From f57c0fbd46d0cca82b45c2f16fab316aa2554a08 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 24 Mar 2017 09:52:18 +0100 +Subject: [PATCH] Get correct CA cert nickname in CA-less + +During CA-less installation, we initialize the HTTPD alias +database from a pkcs12 file. This means there's going to +be different nicknames to the added certificates. Store +the CA certificate nickname in HTTPInstance__setup_ssl() +to be able to correctly export it later. + +https://pagure.io/freeipa/issue/6806 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/httpinstance.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 01b55e7a7b00d020b7745c419267ad4f0ba86804..3e4252cb1e907618d4aa15f7381caff5e4e868e3 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -118,6 +118,7 @@ class WebGuiInstance(service.SimpleServiceInstance): + def __init__(self): + service.SimpleServiceInstance.__init__(self, "ipa_webgui") + ++ + class HTTPInstance(service.Service): + def __init__(self, fstore=None, cert_nickname='Server-Cert', + api=api): +@@ -130,6 +131,7 @@ class HTTPInstance(service.Service): + service_user=HTTPD_USER, + keytab=paths.HTTP_KEYTAB) + ++ self.cacert_nickname = None + self.cert_nickname = cert_nickname + self.ca_is_configured = True + self.keytab_user = constants.GSSPROXY_USER +@@ -441,6 +443,9 @@ class HTTPInstance(service.Service): + if not server_certs: + raise RuntimeError("Could not find a suitable server cert.") + ++ # store the CA cert nickname so that we can publish it later on ++ self.cacert_nickname = db.cacert_name ++ + def __import_ca_certs(self): + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base) +@@ -449,7 +454,7 @@ class HTTPInstance(service.Service): + def __publish_ca_cert(self): + ca_db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base) +- ca_db.publish_ca_cert(paths.CA_CRT) ++ ca_db.export_pem_cert(self.cacert_nickname, paths.CA_CRT) + + def is_kdcproxy_configured(self): + """Check if KDC proxy has already been configured in the past""" +-- +2.12.2 + diff --git a/SOURCES/0063-Remove-publish_ca_cert-method-from-NSSDatabase.patch b/SOURCES/0063-Remove-publish_ca_cert-method-from-NSSDatabase.patch new file mode 100644 index 0000000..2ac172a --- /dev/null +++ b/SOURCES/0063-Remove-publish_ca_cert-method-from-NSSDatabase.patch @@ -0,0 +1,49 @@ +From caa2c2b6e4b6ac29774dc7f8ec7c3f5210e2f828 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 27 Mar 2017 10:31:36 +0200 +Subject: [PATCH] Remove publish_ca_cert() method from NSSDatabase + +NSSDatabase.publish_ca_cert() is not used anymore, remove it. + +https://pagure.io/freeipa/issue/6806 + +Reviewed-By: Jan Cholasta +--- + ipapython/certdb.py | 9 --------- + ipaserver/install/certs.py | 3 --- + 2 files changed, 12 deletions(-) + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index f1410e5ae4290263573e9554ab4e66873d4344a1..0665f944457fb09820eb244c742cb1782e515ad1 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -596,12 +596,3 @@ 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/certs.py b/ipaserver/install/certs.py +index 0ca971358030db6a6e7e410e58a984675bcf53ac..16139f81f0d0bd6889a9f38948204bb5bc018028 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -640,9 +640,6 @@ class CertDB(object): + + self.export_ca_cert(nickname, False) + +- def publish_ca_cert(self, location): +- self.nssdb.publish_ca_cert(self.cacert_name, location) +- + def export_pem_cert(self, nickname, location): + return self.nssdb.export_pem_cert(nickname, location) + +-- +2.12.2 + diff --git a/SOURCES/0064-httpinstance-make-sure-NSS-database-is-backed-up.patch b/SOURCES/0064-httpinstance-make-sure-NSS-database-is-backed-up.patch new file mode 100644 index 0000000..4e78d20 --- /dev/null +++ b/SOURCES/0064-httpinstance-make-sure-NSS-database-is-backed-up.patch @@ -0,0 +1,39 @@ +From 27360b29b510d5ae92469b079569973676efd26c Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 3 Apr 2017 10:49:26 +0000 +Subject: [PATCH] httpinstance: make sure NSS database is backed up + +The NSS database at /etc/httpd/alias is not properly initialized and backed +up in CA-less replica promotion. This might cause the install to fail after +previous install and uninstall. + +Make sure the NSS database is initialized and backed up even in CA-less +replica promotion to fix the issue. + +https://pagure.io/freeipa/issue/4639 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/httpinstance.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 3e4252cb1e907618d4aa15f7381caff5e4e868e3..079ea92606cc53f98beca1759a7e24db64bfd3f4 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -375,10 +375,11 @@ class HTTPInstance(service.Service): + return False + + def __setup_ssl(self): ++ truncate = not self.promote or not self.ca_is_configured + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base, user="root", + group=constants.HTTPD_GROUP, +- truncate=(not self.promote)) ++ truncate=truncate) + self.disable_system_trust() + if self.pkcs12_info: + if self.ca_is_configured: +-- +2.12.2 + diff --git a/SOURCES/0065-IPA-KDB-use-relative-path-in-ipa-certmap-config-snip.patch b/SOURCES/0065-IPA-KDB-use-relative-path-in-ipa-certmap-config-snip.patch new file mode 100644 index 0000000..544e546 --- /dev/null +++ b/SOURCES/0065-IPA-KDB-use-relative-path-in-ipa-certmap-config-snip.patch @@ -0,0 +1,72 @@ +From cae66046bb31205283341cd3b19af799c5fe6a30 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 29 Mar 2017 15:46:50 +0200 +Subject: [PATCH] IPA-KDB: use relative path in ipa-certmap config snippet + +Architecture specific paths should be avoided in the global Kerberos +configuration because it is read e.g. by 32bit and 64bit libraries they +are installed in parallel. + +Resolves https://pagure.io/freeipa/issue/6833 + +Reviewed-By: Christian Heimes +Reviewed-By: Jan Cholasta +--- + daemons/ipa-kdb/Makefile.am | 12 ++++-------- + daemons/ipa-kdb/{ipa-certauth.in => ipa-certauth} | 2 +- + 2 files changed, 5 insertions(+), 9 deletions(-) + rename daemons/ipa-kdb/{ipa-certauth.in => ipa-certauth} (56%) + +diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am +index 715666e779a4fa64c2c0f71767f09efb19b5f908..259bc3b20fa96cadff43c3acdce1bd3ba49cdb31 100644 +--- a/daemons/ipa-kdb/Makefile.am ++++ b/daemons/ipa-kdb/Makefile.am +@@ -40,18 +40,16 @@ ipadb_la_SOURCES = \ + ipa_kdb_audit_as.c \ + $(NULL) + ++dist_noinst_DATA = ipa_kdb.exports ++ + if BUILD_IPA_CERTAUTH_PLUGIN + ipadb_la_SOURCES += ipa_kdb_certauth.c + + +-%: %.in +- sed \ +- -e 's|@plugindir@|$(plugindir)|g' \ +- '$(srcdir)/$@.in' >$@ +- + krb5confdir = $(sysconfdir)/krb5.conf.d + krb5conf_DATA = ipa-certauth +-CLEANFILES = $(krb5conf_DATA) ++else ++dist_noinst_DATA += ipa-certauth + endif + + ipadb_la_LDFLAGS = \ +@@ -105,8 +103,6 @@ ipa_kdb_tests_LDADD = \ + -lsss_idmap \ + $(NULL) + +-dist_noinst_DATA = ipa_kdb.exports ipa-certauth.in +- + clean-local: + rm -f tests/.dirstamp + +diff --git a/daemons/ipa-kdb/ipa-certauth.in b/daemons/ipa-kdb/ipa-certauth +similarity index 56% +rename from daemons/ipa-kdb/ipa-certauth.in +rename to daemons/ipa-kdb/ipa-certauth +index eda89a26f02fbea449eb754b232b8115904acd21..6fde08284da22161a97df675d15392f80ffcc6fb 100644 +--- a/daemons/ipa-kdb/ipa-certauth.in ++++ b/daemons/ipa-kdb/ipa-certauth +@@ -1,5 +1,5 @@ + [plugins] + certauth = { +- module = ipakdb:@plugindir@/ipadb.so ++ module = ipakdb:kdb/ipadb.so + enable_only = ipakdb + } +-- +2.12.2 + diff --git a/SOURCES/0066-Add-pki_pin-only-when-needed.patch b/SOURCES/0066-Add-pki_pin-only-when-needed.patch new file mode 100644 index 0000000..880c676 --- /dev/null +++ b/SOURCES/0066-Add-pki_pin-only-when-needed.patch @@ -0,0 +1,62 @@ +From 71061059a6c56bad818cb379070ef742bbe517a3 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Mon, 3 Apr 2017 14:08:46 +0200 +Subject: [PATCH] Add pki_pin only when needed + +If both the pki-tomcat NSS database and its password.conf have been +created, don't try to override the password.conf file. + +https://pagure.io/freeipa/issue/6839 + +Reviewed-By: Tomas Krizek +Reviewed-By: Christian Heimes +--- + ipaserver/install/cainstance.py | 10 +++++++--- + ipaserver/install/krainstance.py | 10 +++++++--- + 2 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 92bb760d39d23fedb40b7e3c5bea53381f1c87ad..3980e412603437b0db5804623f6626d11e52c009 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -541,9 +541,13 @@ class CAInstance(DogtagInstance): + # CA key algorithm + config.set("CA", "pki_ca_signing_key_algorithm", self.ca_signing_algorithm) + +- # generate pin which we know can be used for FIPS NSS database +- pki_pin = ipautil.ipa_generate_password() +- config.set("CA", "pki_pin", pki_pin) ++ if not (os.path.isdir(paths.PKI_TOMCAT_ALIAS_DIR) and ++ os.path.isfile(paths.PKI_TOMCAT_PASSWORD_CONF)): ++ # generate pin which we know can be used for FIPS NSS database ++ pki_pin = ipautil.ipa_generate_password() ++ config.set("CA", "pki_pin", pki_pin) ++ else: ++ pki_pin = None + + if self.clone: + +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index 34d667857a8055752e258a591af983190f33daa5..fc25ac72b0dc593f06a8b070b67b5d54a0ab8bce 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -235,9 +235,13 @@ class KRAInstance(DogtagInstance): + "KRA", "pki_share_dbuser_dn", + str(DN(('uid', 'pkidbuser'), ('ou', 'people'), ('o', 'ipaca')))) + +- # generate pin which we know can be used for FIPS NSS database +- pki_pin = ipautil.ipa_generate_password() +- config.set("KRA", "pki_pin", pki_pin) ++ if not (os.path.isdir(paths.PKI_TOMCAT_ALIAS_DIR) and ++ os.path.isfile(paths.PKI_TOMCAT_PASSWORD_CONF)): ++ # generate pin which we know can be used for FIPS NSS database ++ pki_pin = ipautil.ipa_generate_password() ++ config.set("KRA", "pki_pin", pki_pin) ++ else: ++ pki_pin = None + + _p12_tmpfile_handle, p12_tmpfile_name = tempfile.mkstemp(dir=paths.TMP) + +-- +2.9.3 + diff --git a/SOURCES/0067-idrange-add-properly-handle-empty-dom-name-option.patch b/SOURCES/0067-idrange-add-properly-handle-empty-dom-name-option.patch new file mode 100644 index 0000000..3ef7ec8 --- /dev/null +++ b/SOURCES/0067-idrange-add-properly-handle-empty-dom-name-option.patch @@ -0,0 +1,35 @@ +From 1f9d9de5dd0e5935bb71060d867b46f675977163 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 28 Mar 2017 16:02:45 +0200 +Subject: [PATCH] idrange-add: properly handle empty --dom-name option + +When idrange-add is called with --dom-name=, the CLI exits with +ipa: ERROR: an internal error has occurred +This happens because the code checks if the option is provided but does not +check if the value is None. + +We need to handle empty dom-name as if the option was not specified. + +https://pagure.io/freeipa/issue/6404 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/idrange.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py +index 5b88a6b0fd91271eaeb19c8315e23299a13ff7e4..c8ea95af801a86dd0180497964ddbd1b2741f279 100644 +--- a/ipaserver/plugins/idrange.py ++++ b/ipaserver/plugins/idrange.py +@@ -411,7 +411,7 @@ class idrange_add(LDAPCreate): + + # This needs to stay in options since there is no + # ipanttrusteddomainname attribute in LDAP +- if 'ipanttrusteddomainname' in options: ++ if options.get('ipanttrusteddomainname'): + if is_set('ipanttrusteddomainsid'): + raise errors.ValidationError(name='ID Range setup', + error=_('Options dom-sid and dom-name ' +-- +2.9.3 + diff --git a/SOURCES/0068-ipa-sam-create-the-gidNumber-attribute-in-the-truste.patch b/SOURCES/0068-ipa-sam-create-the-gidNumber-attribute-in-the-truste.patch new file mode 100644 index 0000000..462657b --- /dev/null +++ b/SOURCES/0068-ipa-sam-create-the-gidNumber-attribute-in-the-truste.patch @@ -0,0 +1,145 @@ +From 376a1fbfe97624116d8fb10f26d97ef15fd3b917 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 21 Mar 2017 17:33:20 +0100 +Subject: [PATCH] ipa-sam: create the gidNumber attribute in the trusted domain + entry + +When a trusted domain entry is created, the uidNumber attribute is created +but not the gidNumber attribute. This causes samba to log + Failed to find a Unix account for DOM-AD$ +because the samu structure does not contain a group_sid and is not put +in the cache. +The fix creates the gidNumber attribute in the trusted domain entry, +and initialises the group_sid field in the samu structure returned +by ldapsam_getsampwnam. This ensures that the entry is put in the cache. + +Note that this is only a partial fix for 6660 as it does not prevent +_netr_ServerAuthenticate3 from failing with the log + _netr_ServerAuthenticate3: netlogon_creds_server_check failed. Rejecting auth request from client VM-AD machine account dom-ad.example.com. + +https://pagure.io/freeipa/issue/6827 + +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-sam/ipa_sam.c | 40 +++++++++++++++++++++++++++++++++++++--- + 1 file changed, 37 insertions(+), 3 deletions(-) + +diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c +index 4c1fda5f82b43f69929613f9938410b32cff31e7..6a29e8e10b4299356b9ead76276eecc8083791a3 100644 +--- a/daemons/ipa-sam/ipa_sam.c ++++ b/daemons/ipa-sam/ipa_sam.c +@@ -195,6 +195,7 @@ struct ipasam_privates { + char *trust_dn; + char *flat_name; + struct dom_sid fallback_primary_group; ++ char *fallback_primary_group_gid_str; + char *server_princ; + char *client_princ; + struct sss_idmap_ctx *idmap_ctx; +@@ -2419,6 +2420,9 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods, + if (entry == NULL || sid == NULL) { + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_UIDNUMBER, IPA_MAGIC_ID_STR); ++ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, ++ LDAP_ATTRIBUTE_GIDNUMBER, ++ ldap_state->ipasam_privates->fallback_primary_group_gid_str); + } + + if (td->netbios_name != NULL) { +@@ -2829,6 +2833,7 @@ static bool init_sam_from_td(struct samu *user, struct pdb_trusted_domain *td, + { + NTSTATUS status; + struct dom_sid *u_sid; ++ struct dom_sid *g_sid; + char *name; + char *trustpw = NULL; + char *trustpw_utf8 = NULL; +@@ -2884,6 +2889,11 @@ static bool init_sam_from_td(struct samu *user, struct pdb_trusted_domain *td, + } + talloc_free(u_sid); + ++ g_sid = &ldap_state->ipasam_privates->fallback_primary_group; ++ if (!pdb_set_group_sid(user, g_sid, PDB_SET)) { ++ return false; ++ } ++ + status = get_trust_pwd(user, &td->trust_auth_incoming, &trustpw, NULL); + if (!NT_STATUS_IS_OK(status)) { + return false; +@@ -3594,14 +3604,17 @@ static void ipasam_free_private_data(void **vp) + static struct dom_sid *get_fallback_group_sid(TALLOC_CTX *mem_ctx, + struct smbldap_state *ldap_state, + struct sss_idmap_ctx *idmap_ctx, +- LDAPMessage *dom_entry) ++ LDAPMessage *dom_entry, ++ char **fallback_group_gid_str) + { + char *dn; + char *sid; ++ char *gidnumber; + int ret; + const char *filter = "objectClass=*"; + const char *attr_list[] = { + LDAP_ATTRIBUTE_SID, ++ LDAP_ATTRIBUTE_GIDNUMBER, + NULL}; + LDAPMessage *result; + LDAPMessage *entry; +@@ -3648,9 +3661,20 @@ static struct dom_sid *get_fallback_group_sid(TALLOC_CTX *mem_ctx, + talloc_free(sid); + return NULL; + } ++ talloc_free(sid); ++ ++ gidnumber = get_single_attribute(mem_ctx, ldap_state->ldap_struct, ++ entry, LDAP_ATTRIBUTE_GIDNUMBER); ++ if (gidnumber == NULL) { ++ DEBUG(0, ("Missing mandatory attribute %s.\n", ++ LDAP_ATTRIBUTE_GIDNUMBER)); ++ ldap_msgfree(result); ++ return NULL; ++ } ++ ++ *fallback_group_gid_str = gidnumber; + + ldap_msgfree(result); +- talloc_free(sid); + + return fallback_group_sid; + } +@@ -4443,6 +4467,7 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + char *domain_sid_string = NULL; + struct dom_sid *ldap_domain_sid = NULL; + struct dom_sid *fallback_group_sid = NULL; ++ char *fallback_group_gid_str = NULL; + + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; +@@ -4586,7 +4611,8 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + fallback_group_sid = get_fallback_group_sid(ldap_state, + ldap_state->smbldap_state, + ldap_state->ipasam_privates->idmap_ctx, +- result); ++ result, ++ &fallback_group_gid_str); + if (fallback_group_sid == NULL) { + DEBUG(0, ("Cannot find SID of fallback group.\n")); + ldap_msgfree(result); +@@ -4596,6 +4622,14 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + fallback_group_sid); + talloc_free(fallback_group_sid); + ++ if (fallback_group_gid_str == NULL) { ++ DEBUG(0, ("Cannot find gidNumber of fallback group.\n")); ++ ldap_msgfree(result); ++ return NT_STATUS_INVALID_PARAMETER; ++ } ++ ldap_state->ipasam_privates->fallback_primary_group_gid_str = ++ fallback_group_gid_str; ++ + domain_sid_string = get_single_attribute( + ldap_state, + ldap_state->smbldap_state->ldap_struct, +-- +2.9.3 + diff --git a/SOURCES/0069-Upgrade-add-gidnumber-to-trusted-domain-entry.patch b/SOURCES/0069-Upgrade-add-gidnumber-to-trusted-domain-entry.patch new file mode 100644 index 0000000..3a887bf --- /dev/null +++ b/SOURCES/0069-Upgrade-add-gidnumber-to-trusted-domain-entry.patch @@ -0,0 +1,104 @@ +From 54422d3c58ace8496b0bd2fc536365159e6666e6 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 3 Apr 2017 15:57:47 +0200 +Subject: [PATCH] Upgrade: add gidnumber to trusted domain entry + +The trusted domain entries created in earlier versions are missing gidnumber. +During upgrade, a new plugin will read the gidnumber of the fallback group +cn=Default SMB Group and add this value to trusted domain entries which do +not have a gidNumber. + +https://pagure.io/freeipa/issue/6827 + +Reviewed-By: Alexander Bokovoy +--- + install/updates/90-post_upgrade_plugins.update | 1 + + ipaserver/install/plugins/adtrust.py | 56 ++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+) + +diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update +index 34069e7457dd9690a14c5c055c6d05ad76004d16..8477199e07d6729d5847e58bfa67d061bd1410c2 100644 +--- a/install/updates/90-post_upgrade_plugins.update ++++ b/install/updates/90-post_upgrade_plugins.update +@@ -10,6 +10,7 @@ plugin: update_sigden_extdom_broken_config + plugin: update_sids + plugin: update_default_range + plugin: update_default_trust_view ++plugin: update_tdo_gidnumber + plugin: update_ca_renewal_master + plugin: update_idrange_type + plugin: update_pacs +diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py +index 42968089f547f61edd2f1223d088a22762a33b70..075f197780edc2aadf42fa82b71e9e2b29e66ea9 100644 +--- a/ipaserver/install/plugins/adtrust.py ++++ b/ipaserver/install/plugins/adtrust.py +@@ -22,6 +22,7 @@ from ipalib import Updater + from ipapython.dn import DN + from ipapython.ipa_log_manager import root_logger + from ipaserver.install import sysupgrade ++from ipaserver.install.adtrustinstance import ADTRUSTInstance + + register = Registry() + +@@ -316,3 +317,58 @@ class update_sids(Updater): + + sysupgrade.set_upgrade_state('sidgen', 'update_sids', False) + return False, () ++ ++ ++@register() ++class update_tdo_gidnumber(Updater): ++ """ ++ Create a gidNumber attribute for Trusted Domain Objects. ++ ++ The value is taken from the fallback group defined in cn=Default SMB Group. ++ """ ++ def execute(self, **options): ++ ldap = self.api.Backend.ldap2 ++ ++ # Read the gidnumber of the fallback group ++ dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME), ++ self.api.env.container_group, ++ self.api.env.basedn) ++ ++ try: ++ entry = ldap.get_entry(dn, ['gidnumber']) ++ gidNumber = entry.get('gidnumber') ++ except errors.NotFound: ++ self.log.error("{0} not found".format( ++ ADTRUSTInstance.FALLBACK_GROUP_NAME)) ++ return False, () ++ ++ if not gidNumber: ++ self.log.error("{0} does not have a gidnumber".format( ++ ADTRUSTInstance.FALLBACK_GROUP_NAME)) ++ return False, () ++ ++ # For each trusted domain object, add gidNumber ++ try: ++ tdos = ldap.get_entries( ++ DN(self.api.env.container_adtrusts, self.api.env.basedn), ++ scope=ldap.SCOPE_ONELEVEL, ++ filter="(objectclass=ipaNTTrustedDomain)", ++ attrs_list=['gidnumber']) ++ for tdo in tdos: ++ # if the trusted domain object does not contain gidnumber, ++ # add the default fallback group gidnumber ++ if not tdo.get('gidnumber'): ++ try: ++ tdo['gidnumber'] = gidNumber ++ ldap.update_entry(tdo) ++ self.log.debug("Added gidnumber {0} to {1}".format( ++ gidNumber, tdo.dn)) ++ except Exception: ++ self.log.warning( ++ "Failed to add gidnumber to {0}".format(tdo.dn)) ++ ++ except errors.NotFound: ++ self.log.debug("No trusted domain object to update") ++ return False, () ++ ++ return False, () +-- +2.9.3 + diff --git a/SOURCES/0070-dsinstance-reconnect-ldap2-after-DS-is-restarted-by-.patch b/SOURCES/0070-dsinstance-reconnect-ldap2-after-DS-is-restarted-by-.patch new file mode 100644 index 0000000..0e7a34c --- /dev/null +++ b/SOURCES/0070-dsinstance-reconnect-ldap2-after-DS-is-restarted-by-.patch @@ -0,0 +1,53 @@ +From f6ecef4bdf8f5f99c89c0649232a230c28191869 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:40:19 +0200 +Subject: [PATCH] dsinstance: reconnect ldap2 after DS is restarted by + certmonger + +DS is restarted by certmonger in the restart_dirsrv script after the DS +certificate is saved. This breaks the ldap2 backend and makes any operation +fail with NetworkError until it is reconnected. + +Reconnect ldap2 after the DS certificate request is finished to fix the +issue. Make sure restart_dirsrv waits for the ldapi socket so that the +reconnect does not fail. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + install/restart_scripts/restart_dirsrv | 2 +- + ipaserver/install/dsinstance.py | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/install/restart_scripts/restart_dirsrv b/install/restart_scripts/restart_dirsrv +index b4c9490c10506aba60eee16c3f46ee7cb0474f50..ff476cac46f76d4964d39b12c04401dfc19c2d3a 100644 +--- a/install/restart_scripts/restart_dirsrv ++++ b/install/restart_scripts/restart_dirsrv +@@ -41,7 +41,7 @@ def _main(): + + try: + if services.knownservices.dirsrv.is_running(): +- services.knownservices.dirsrv.restart(instance) ++ services.knownservices.dirsrv.restart(instance, ldapi=True) + except Exception as e: + syslog.syslog(syslog.LOG_ERR, "Cannot restart dirsrv (instance: '%s'): %s" % (instance, str(e))) + +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 79dc90e92cac49a2b64ff6645f75dc3a8cbcc104..fb5f925de8e658dca9370714413012527f00c39d 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -837,6 +837,10 @@ class DsInstance(service.Service): + finally: + certmonger.modify_ca_helper('IPA', prev_helper) + ++ # restart_dirsrv in the request above restarts DS, reconnect ldap2 ++ api.Backend.ldap2.disconnect() ++ api.Backend.ldap2.connect() ++ + self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False) + + dsdb.create_pin_file() +-- +2.9.3 + diff --git a/SOURCES/0071-httpinstance-avoid-httpd-restart-during-certificate-.patch b/SOURCES/0071-httpinstance-avoid-httpd-restart-during-certificate-.patch new file mode 100644 index 0000000..a9b55ba --- /dev/null +++ b/SOURCES/0071-httpinstance-avoid-httpd-restart-during-certificate-.patch @@ -0,0 +1,52 @@ +From 19738b56604f50e3806b220e74ac4a5ab52f71a4 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:40:41 +0200 +Subject: [PATCH] httpinstance: avoid httpd restart during certificate request + +httpd is restarted by certmonger in the restart_httpd script after the +httpd certificate is saved if it was previously running. The restart will +fail because httpd is not properly configured at this point. + +Stop httpd at the beginning of httpd install to avoid the restart. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/httpinstance.py | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 079ea92606cc53f98beca1759a7e24db64bfd3f4..d7cd776ab9831b5408797ae41b7c7fbb10707b18 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -160,6 +160,7 @@ class HTTPInstance(service.Service): + self.ca_is_configured = ca_is_configured + self.promote = promote + ++ self.step("stopping httpd", self.__stop) + self.step("setting mod_nss port to 443", self.__set_mod_nss_port) + self.step("setting mod_nss cipher suite", + self.set_mod_nss_cipher_suite) +@@ -185,15 +186,15 @@ class HTTPInstance(service.Service): + self.step("create KDC proxy user", create_kdcproxy_user) + self.step("create KDC proxy config", self.create_kdcproxy_conf) + self.step("enable KDC proxy", self.enable_kdcproxy) +- self.step("restarting httpd", self.__start) ++ self.step("starting httpd", self.start) + self.step("configuring httpd to start on boot", self.__enable) + self.step("enabling oddjobd", self.enable_and_start_oddjobd) + + self.start_creation() + +- def __start(self): ++ def __stop(self): + self.backup_state("running", self.is_running()) +- self.restart() ++ self.stop() + + def __enable(self): + self.backup_state("enabled", self.is_enabled()) +-- +2.9.3 + diff --git a/SOURCES/0072-dsinstance-httpinstance-consolidate-certificate-requ.patch b/SOURCES/0072-dsinstance-httpinstance-consolidate-certificate-requ.patch new file mode 100644 index 0000000..9c7a67f --- /dev/null +++ b/SOURCES/0072-dsinstance-httpinstance-consolidate-certificate-requ.patch @@ -0,0 +1,289 @@ +From 2409b5204101cceafb28289db0d99c1474ee2430 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:43:09 +0200 +Subject: [PATCH] dsinstance, httpinstance: consolidate certificate request + code + +A different code path is used for DS and httpd certificate requests in +replica promotion. This is rather unnecessary and makes the certificate +request code not easy to follow. + +Consolidate the non-promotion and promotion code paths into one. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/dsinstance.py | 76 +++++++++--------------------- + ipaserver/install/httpinstance.py | 40 ++++++++-------- + ipaserver/install/server/install.py | 4 -- + ipaserver/install/server/replicainstall.py | 22 +-------- + 4 files changed, 43 insertions(+), 99 deletions(-) + +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index fb5f925de8e658dca9370714413012527f00c39d..31dbd4ec8bcaf4a7545b4f9f316fe609b845cb75 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -396,10 +396,7 @@ class DsInstance(service.Service): + + self.step("creating DS keytab", self.request_service_keytab) + if self.promote: +- if self.pkcs12_info: +- self.step("configuring TLS for DS instance", self.__enable_ssl) +- else: +- self.step("retrieving DS Certificate", self.__get_ds_cert) ++ self.step("configuring TLS for DS instance", self.__enable_ssl) + self.step("restarting directory server", self.__restart_instance) + + self.step("setting up initial replication", self.__setup_replica) +@@ -810,18 +807,23 @@ class DsInstance(service.Service): + dsdb.track_server_cert( + self.nickname, self.principal, dsdb.passwd_fname, + 'restart_dirsrv %s' % self.serverid) ++ ++ self.add_cert_to_service() + else: + dsdb.create_from_cacert() +- 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) ++ 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: ++ prev_helper = None + try: + cmd = 'restart_dirsrv %s' % self.serverid + certmonger.request_and_wait_for_cert( +@@ -835,7 +837,8 @@ class DsInstance(service.Service): + dns=[self.fqdn], + post_command=cmd) + finally: +- certmonger.modify_ca_helper('IPA', prev_helper) ++ if prev_helper is not None: ++ certmonger.modify_ca_helper('IPA', prev_helper) + + # restart_dirsrv in the request above restarts DS, reconnect ldap2 + api.Backend.ldap2.disconnect() +@@ -843,6 +846,9 @@ class DsInstance(service.Service): + + self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False) + ++ if prev_helper is not None: ++ self.add_cert_to_service() ++ + dsdb.create_pin_file() + + self.cacert_name = dsdb.cacert_name +@@ -1236,46 +1242,6 @@ class DsInstance(service.Service): + ipautil.config_replace_variables(paths.SYSCONFIG_DIRSRV, + replacevars=vardict) + +- def __get_ds_cert(self): +- nssdb_dir = config_dirname(self.serverid) +- db = certs.CertDB( +- self.realm, +- nssdir=nssdb_dir, +- subject_base=self.subject_base, +- ca_subject=self.ca_subject, +- ) +- db.create_from_cacert() +- db.request_service_cert(self.nickname, self.principal, self.fqdn) +- db.create_pin_file() +- +- # Connect to self over ldapi as Directory Manager and configure SSL +- ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) +- conn = ipaldap.LDAPClient(ldap_uri) +- conn.external_bind() +- +- mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"), +- (ldap.MOD_REPLACE, "nsSSL3Ciphers", "default"), +- (ldap.MOD_REPLACE, "allowWeakCipher", "off")] +- conn.modify_s(DN(('cn', 'encryption'), ('cn', 'config')), mod) +- +- mod = [(ldap.MOD_ADD, "nsslapd-security", "on")] +- conn.modify_s(DN(('cn', 'config')), mod) +- +- entry = conn.make_entry( +- DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')), +- objectclass=["top", "nsEncryptionModule"], +- cn=["RSA"], +- nsSSLPersonalitySSL=[self.nickname], +- nsSSLToken=["internal (software)"], +- nsSSLActivation=["on"], +- ) +- conn.add_entry(entry) +- +- conn.unbind() +- +- # check for open secure port 636 from now on +- self.open_ports.append(636) +- + + def write_certmap_conf(realm, ca_subject): + """(Re)write certmap.conf with given CA subject DN.""" +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index d7cd776ab9831b5408797ae41b7c7fbb10707b18..45bf479d1088c3b3396d955bf2592c4bce1e886f 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -376,12 +376,12 @@ class HTTPInstance(service.Service): + return False + + def __setup_ssl(self): +- truncate = not self.promote or not self.ca_is_configured + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base, user="root", + group=constants.HTTPD_GROUP, +- truncate=truncate) ++ truncate=True) + self.disable_system_trust() ++ self.create_password_conf() + if self.pkcs12_info: + if self.ca_is_configured: + trust_flags = 'CT,C,C' +@@ -394,8 +394,6 @@ 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]) + +- self.create_password_conf() +- + # We only handle one server cert + nickname = server_certs[0][0] + if nickname == 'ipaCert': +@@ -410,7 +408,6 @@ class HTTPInstance(service.Service): + + else: + if not self.promote: +- self.create_password_conf() + ca_args = [ + paths.CERTMONGER_DOGTAG_SUBMIT, + '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, +@@ -421,23 +418,26 @@ class HTTPInstance(service.Service): + ] + helper = " ".join(ca_args) + prev_helper = certmonger.modify_ca_helper('IPA', helper) +- +- try: +- certmonger.request_and_wait_for_cert( +- certpath=db.secdir, +- nickname=self.cert_nickname, +- principal=self.principal, +- passwd_fname=db.passwd_fname, +- subject=str(DN(('CN', self.fqdn), self.subject_base)), +- ca='IPA', +- profile=dogtag.DEFAULT_PROFILE, +- dns=[self.fqdn], +- post_command='restart_httpd') +- self.dercert = db.get_cert_from_db( +- self.cert_nickname, pem=False) +- finally: ++ else: ++ prev_helper = None ++ try: ++ certmonger.request_and_wait_for_cert( ++ certpath=db.secdir, ++ nickname=self.cert_nickname, ++ principal=self.principal, ++ passwd_fname=db.passwd_fname, ++ subject=str(DN(('CN', self.fqdn), self.subject_base)), ++ ca='IPA', ++ profile=dogtag.DEFAULT_PROFILE, ++ dns=[self.fqdn], ++ post_command='restart_httpd') ++ finally: ++ if prev_helper is not None: + certmonger.modify_ca_helper('IPA', prev_helper) + ++ self.dercert = db.get_cert_from_db(self.cert_nickname, pem=False) ++ ++ if prev_helper is not None: + self.add_cert_to_service() + + # Verify we have a valid server cert +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index d7eb0bfacd0815026c82f59d76962f527e2b7dad..f8e64ec26e85bbc6218018eec8f403a0567b45a2 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -807,10 +807,6 @@ def install(installer): + if setup_ca: + ca.install_step_1(False, None, options) + +- # The DS instance is created before the keytab, add the SSL cert we +- # generated +- ds.add_cert_to_service() +- + otpd = otpdinstance.OtpdInstance() + otpd.create_instance('OTPD', host_name, + ipautil.realm_to_suffix(realm_name)) +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index f489e691999fd9d6e82879341922510e56eac47d..cd6a62f9540f4a46da70e0cc5686eff5f54e7dfe 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -27,7 +27,6 @@ from ipapython.dn import DN + from ipapython.ipa_log_manager import root_logger + from ipapython.admintool import ScriptError + from ipaplatform import services +-from ipaplatform.constants import constants as pconstants + from ipaplatform.tasks import tasks + from ipaplatform.paths import paths + from ipalib import api, constants, create_api, errors, rpc, x509 +@@ -77,18 +76,6 @@ def make_pkcs12_info(directory, cert_name, password_name): + return None + + +-def install_http_certs(host_name, realm_name, subject_base): +- principal = 'HTTP/%s@%s' % (host_name, realm_name) +- subject = subject_base or DN(('O', realm_name)) +- db = certs.CertDB(realm_name, nssdir=paths.HTTPD_ALIAS_DIR, +- subject_base=subject, user="root", +- group=pconstants.HTTPD_GROUP, truncate=True) +- db.request_service_cert('Server-Cert', principal, host_name) +- # Obtain certificate for the HTTP service +- http = httpinstance.HTTPInstance() +- http.create_password_conf() +- +- + def install_replica_ds(config, options, ca_is_configured, remote_api, + ca_file, promote=False, pkcs12_info=None): + dsinstance.check_ports() +@@ -175,7 +162,8 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file, + http.create_instance( + config.realm_name, config.host_name, config.domain_name, + pkcs12_info, auto_redirect=auto_redirect, ca_file=ca_file, +- ca_is_configured=ca_is_configured, promote=promote) ++ ca_is_configured=ca_is_configured, promote=promote, ++ subject_base=config.subject_base) + + return http + +@@ -1414,12 +1402,6 @@ def install(installer): + # Always try to install DNS records + install_dns_records(config, options, remote_api) + +- if promote and ca_enabled: +- # we need to install http certs to setup ssl for httpd +- install_http_certs(config.host_name, +- config.realm_name, +- config.subject_base) +- + ntpinstance.ntp_ldap_enable(config.host_name, ds.suffix, + remote_api.env.realm) + finally: +-- +2.9.3 + diff --git a/SOURCES/0073-install-request-service-certs-after-host-keytab-is-s.patch b/SOURCES/0073-install-request-service-certs-after-host-keytab-is-s.patch new file mode 100644 index 0000000..45e18e6 --- /dev/null +++ b/SOURCES/0073-install-request-service-certs-after-host-keytab-is-s.patch @@ -0,0 +1,135 @@ +From acb04249a77f62f72179899223bfeacdd2292883 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:44:21 +0200 +Subject: [PATCH] install: request service certs after host keytab is set up + +The certmonger renew agent and restart scripts use host keytab for +authentication. When they are executed during a certmonger request before +the host keytab is set up, the authentication will fail. + +Make sure all certmonger requests in the installer are done after the host +keytab is set up. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/dsinstance.py | 17 +++++++---------- + ipaserver/install/server/install.py | 18 +++++++----------- + ipaserver/install/server/replicainstall.py | 5 ++--- + 3 files changed, 16 insertions(+), 24 deletions(-) + +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 31dbd4ec8bcaf4a7545b4f9f316fe609b845cb75..72fcb65f2eb699d0077d3c5cc02a3fcaaad9b8e5 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -256,7 +256,7 @@ class DsInstance(service.Service): + + subject_base = ipautil.dn_attribute_property('_subject_base') + +- def __common_setup(self, enable_ssl=False): ++ def __common_setup(self): + + self.step("creating directory server user", create_ds_user) + self.step("creating directory server instance", self.__create_instance) +@@ -279,8 +279,6 @@ class DsInstance(service.Service): + self.step("configuring topology plugin", self.__config_topology_module) + self.step("creating indices", self.__create_indices) + self.step("enabling referential integrity plugin", self.__add_referint_module) +- if enable_ssl: +- self.step("configuring TLS for DS instance", self.__enable_ssl) + self.step("configuring certmap.conf", self.__certmap_conf) + self.step("configure new location for managed entries", self.__repoint_managed_entries) + self.step("configure dirsrv ccache", self.configure_dirsrv_ccache) +@@ -356,8 +354,12 @@ class DsInstance(service.Service): + self.steps = [] + + self.step("configuring TLS for DS instance", self.__enable_ssl) ++ if self.master_fqdn is None: ++ self.step("adding CA certificate entry", self.__upload_ca_cert) ++ else: ++ self.step("importing CA certificates from LDAP", ++ self.__import_ca_certs) + self.step("restarting directory server", self.__restart_instance) +- self.step("adding CA certificate entry", self.__upload_ca_cert) + + self.start_creation() + +@@ -391,21 +393,16 @@ class DsInstance(service.Service): + self.promote = promote + self.api = api + +- self.__common_setup(enable_ssl=(not self.promote)) ++ self.__common_setup() + self.step("restarting directory server", self.__restart_instance) + + self.step("creating DS keytab", self.request_service_keytab) +- if self.promote: +- self.step("configuring TLS for DS instance", self.__enable_ssl) +- self.step("restarting directory server", self.__restart_instance) +- + self.step("setting up initial replication", self.__setup_replica) + self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings) + self.step("updating schema", self.__update_schema) + # See LDIFs for automember configuration during replica install + self.step("setting Auto Member configuration", self.__add_replica_automember_config) + self.step("enabling S4U2Proxy delegation", self.__setup_s4u2proxy) +- self.step("importing CA certificates from LDAP", self.__import_ca_certs) + + self.__common_post_setup() + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index f8e64ec26e85bbc6218018eec8f403a0567b45a2..bf2e248dceaae36ba0030d3eaa47976f51ce60ba 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -770,6 +770,13 @@ def install(installer): + realm_name, host_name, domain_name, dm_password, + options.subject_base, options.ca_subject, 1101, 1100, None) + ++ krb = krbinstance.KrbInstance(fstore) ++ krb.create_instance(realm_name, host_name, domain_name, ++ dm_password, master_password, ++ setup_pkinit=not options.no_pkinit, ++ pkcs12_info=pkinit_pkcs12_info, ++ subject_base=options.subject_base) ++ + if setup_ca: + if not options.external_cert_files and options.external_ca: + # stage 1 of external CA installation +@@ -793,17 +800,6 @@ def install(installer): + # we now need to enable ssl on the ds + ds.enable_ssl() + +- krb = krbinstance.KrbInstance(fstore) +- krb.create_instance(realm_name, host_name, domain_name, +- dm_password, master_password, +- setup_pkinit=not options.no_pkinit, +- pkcs12_info=pkinit_pkcs12_info, +- subject_base=options.subject_base) +- +- # restart DS to enable ipa-pwd-extop plugin +- print("Restarting directory server to enable password extension plugin") +- ds.restart() +- + if setup_ca: + ca.install_step_1(False, None, options) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index cd6a62f9540f4a46da70e0cc5686eff5f54e7dfe..6f1a0d6d29b20d53986205a63382a385e75f80ea 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1422,9 +1422,8 @@ def install(installer): + setup_pkinit=not options.no_pkinit, + promote=promote) + +- # restart DS to enable ipa-pwd-extop plugin +- print("Restarting directory server to enable password extension plugin") +- ds.restart() ++ # we now need to enable ssl on the ds ++ ds.enable_ssl() + + install_http( + config, +-- +2.9.3 + diff --git a/SOURCES/0074-renew-agent-revert-to-host-keytab-authentication.patch b/SOURCES/0074-renew-agent-revert-to-host-keytab-authentication.patch new file mode 100644 index 0000000..3dbde6d --- /dev/null +++ b/SOURCES/0074-renew-agent-revert-to-host-keytab-authentication.patch @@ -0,0 +1,53 @@ +From 37ddd26bc4b2f99dfa27b2ad45219290a2f44ec5 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:46:58 +0200 +Subject: [PATCH] renew agent: revert to host keytab authentication + +Fixes an issue where the renew agent uses GSSAPI for LDAP connection but +fails because it is not authenticated. + +This reverts commit 7462adec13c5b25b6868d2863dc38062c97d0ff7. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 5782db703c49d7c2e92c806e24e9925e8e7d710a..3389447a99d9ab9dac159b0d57ca02f60698ce0c 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -40,6 +40,7 @@ from cryptography.hazmat.backends import default_backend + + import six + ++from ipalib.install.kinit import kinit_keytab + from ipapython import ipautil + from ipapython.dn import DN + from ipalib import api, errors, x509 +@@ -132,7 +133,7 @@ def ldap_connect(): + conn = None + try: + conn = ldap2(api) +- conn.connect(autobind=True) ++ conn.connect(ccache=os.environ['KRB5CCNAME']) + yield conn + finally: + if conn is not None and conn.isconnected(): +@@ -526,6 +527,11 @@ def main(): + tmpdir = tempfile.mkdtemp(prefix="tmp-") + certs.renewal_lock.acquire() + try: ++ principal = str('host/%s@%s' % (api.env.host, api.env.realm)) ++ ccache_filename = os.path.join(tmpdir, 'ccache') ++ os.environ['KRB5CCNAME'] = ccache_filename ++ kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename) ++ + profile = os.environ.get('CERTMONGER_CA_PROFILE') + if is_replicated(): + if profile or is_renewal_master(): +-- +2.9.3 + diff --git a/SOURCES/0075-renew-agent-restart-scripts-connect-to-LDAP-after-ki.patch b/SOURCES/0075-renew-agent-restart-scripts-connect-to-LDAP-after-ki.patch new file mode 100644 index 0000000..baf91f4 --- /dev/null +++ b/SOURCES/0075-renew-agent-restart-scripts-connect-to-LDAP-after-ki.patch @@ -0,0 +1,117 @@ +From 429f07426014c51025d136b505165a43f5e0df21 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 7 Apr 2017 07:51:01 +0200 +Subject: [PATCH] renew agent, restart scripts: connect to LDAP after kinit + +Connect to LDAP after kinit is done, otherwise GSSAPI authentication will +fail. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Martin Babinsky +--- + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 6 ++++-- + install/restart_scripts/renew_ca_cert | 6 ++++-- + install/restart_scripts/renew_ra_cert | 6 ++++-- + 3 files changed, 12 insertions(+), 6 deletions(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 3389447a99d9ab9dac159b0d57ca02f60698ce0c..7a3d9551884c0fe43566dd9012699211a39294eb 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -518,7 +518,6 @@ def main(): + + api.bootstrap(in_server=True, context='renew', confdir=paths.ETC_IPA) + api.finalize() +- api.Backend.ldap2.connect() + + operation = os.environ.get('CERTMONGER_OPERATION') + if operation not in ('SUBMIT', 'POLL'): +@@ -532,6 +531,8 @@ def main(): + os.environ['KRB5CCNAME'] = ccache_filename + kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename) + ++ api.Backend.ldap2.connect() ++ + profile = os.environ.get('CERTMONGER_CA_PROFILE') + if is_replicated(): + if profile or is_renewal_master(): +@@ -547,9 +548,10 @@ def main(): + print(item) + return res[0] + finally: ++ if api.Backend.ldap2.isconnected(): ++ api.Backend.ldap2.disconnect() + certs.renewal_lock.release() + shutil.rmtree(tmpdir) +- api.Backend.ldap2.disconnect() + + + try: +diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert +index bbeae1ae1da5a230f3de1c2569c2324606ae9789..7a54b4c7e05a35b40b17e46b75ff8d47db1b2d23 100644 +--- a/install/restart_scripts/renew_ca_cert ++++ b/install/restart_scripts/renew_ca_cert +@@ -42,7 +42,6 @@ def _main(): + + api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) + api.finalize() +- api.Backend.ldap2.connect() + + dogtag_service = services.knownservices['pki_tomcatd'] + +@@ -77,6 +76,8 @@ def _main(): + kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename) + os.environ['KRB5CCNAME'] = ccache_filename + ++ api.Backend.ldap2.connect() ++ + ca = cainstance.CAInstance(host_name=api.env.host) + ca.update_cert_config(nickname, cert) + if ca.is_renewal_master(): +@@ -184,8 +185,9 @@ def _main(): + if conn is not None and conn.isconnected(): + conn.disconnect() + finally: ++ if api.Backend.ldap2.isconnected(): ++ api.Backend.ldap2.disconnect() + shutil.rmtree(tmpdir) +- api.Backend.ldap2.disconnect() + + # Now we can start the CA. Using the services start should fire + # off the servlet to verify that the CA is actually up and responding so +diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert +index 5c71d5791fa8254de686d1c3a8d01e2cda4d493b..486ee786629076687864f6ef9c3a69b8e389dc28 100644 +--- a/install/restart_scripts/renew_ra_cert ++++ b/install/restart_scripts/renew_ra_cert +@@ -38,7 +38,6 @@ from ipaplatform.paths import paths + def _main(): + api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) + api.finalize() +- api.Backend.ldap2.connect() + + tmpdir = tempfile.mkdtemp(prefix="tmp-") + try: +@@ -47,6 +46,8 @@ def _main(): + kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename) + os.environ['KRB5CCNAME'] = ccache_filename + ++ api.Backend.ldap2.connect() ++ + ca = cainstance.CAInstance(host_name=api.env.host) + ra_certpath = paths.RA_AGENT_PEM + if ca.is_renewal_master(): +@@ -71,8 +72,9 @@ def _main(): + # Load it into dogtag + cainstance.update_people_entry(dercert) + finally: ++ if api.Backend.ldap2.isconnected(): ++ api.Backend.ldap2.disconnect() + shutil.rmtree(tmpdir) +- api.Backend.ldap2.disconnect() + + + def main(): +-- +2.9.3 + diff --git a/SOURCES/0076-ipaserver-dcerpc-unify-error-processing.patch b/SOURCES/0076-ipaserver-dcerpc-unify-error-processing.patch new file mode 100644 index 0000000..40b8580 --- /dev/null +++ b/SOURCES/0076-ipaserver-dcerpc-unify-error-processing.patch @@ -0,0 +1,93 @@ +From 65579492d3d545d6acabaedc019c457551c32063 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Mon, 3 Apr 2017 10:29:21 +0300 +Subject: [PATCH] ipaserver/dcerpc: unify error processing + +Samba error code reporting changes from version to version but we also +did not provide proper input into DCE RPC error processing method we +have. + +Unify error processing and add few more fallback entries. + +With Samba 4.7 we'll have to change it again because error code +processing for Samba Python modules will change with introduction of +samba.ntstatus and samba.werror modules. + +Note that this commit also changes a message returned for error code +-1073741772 (NT_STATUS_OBJECT_NOT_FOUND) because it is more general one. + +Fixes https://pagure.io/freeipa/issue/6859 + +Reviewed-By: Martin Basti +--- + ipaserver/dcerpc.py | 23 +++++++++++++++++------ + 1 file changed, 17 insertions(+), 6 deletions(-) + +diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py +index 2d9d7e5577f1cac6f35701dd277199f9a37387f8..d684a17cabe43bbbd43d29f75f534b6e50fccd12 100644 +--- a/ipaserver/dcerpc.py ++++ b/ipaserver/dcerpc.py +@@ -117,19 +117,27 @@ dcerpc_error_codes = { + # we simply will skip the binding + access_denied_error, + -1073741772: # NT_STATUS_OBJECT_NAME_NOT_FOUND +- errors.RemoteRetrieveError( +- reason=_('CIFS server configuration does not allow ' +- 'access to \\\\pipe\\lsarpc')), ++ errors.NotFound( ++ reason=_('Cannot find specified domain or server name')), + } + + dcerpc_error_messages = { + "NT_STATUS_OBJECT_NAME_NOT_FOUND": + errors.NotFound( + reason=_('Cannot find specified domain or server name')), ++ "The object name is not found.": ++ errors.NotFound( ++ reason=_('Cannot find specified domain or server name')), + "WERR_NO_LOGON_SERVERS": + errors.RemoteRetrieveError( + reason=_('AD DC was unable to reach any IPA domain controller. ' + 'Most likely it is a DNS or firewall issue')), ++ # This is a very long key, don't change it ++ "There are currently no logon servers available to " ++ "service the logon request.": ++ errors.RemoteRetrieveError( ++ reason=_('AD DC was unable to reach any IPA domain controller. ' ++ 'Most likely it is a DNS or firewall issue')), + "NT_STATUS_INVALID_PARAMETER_MIX": + errors.RequirementError( + name=_('At least the domain or IP address should be specified')), +@@ -802,7 +810,8 @@ class DomainValidator(object): + + # Both methods should not fail at the same time + if finddc_error and len(info['gc']) == 0: +- raise assess_dcerpc_exception(message=str(finddc_error)) ++ num, message = e.args # pylint: disable=unpacking-non-sequence ++ raise assess_dcerpc_exception(num=num, message=message) + + self._info[domain] = info + return info +@@ -908,7 +917,8 @@ class TrustDomainInstance(object): + else: + result = netrc.finddc(address=remote_host, flags=flags) + except RuntimeError as e: +- raise assess_dcerpc_exception(message=str(e)) ++ num, message = e.args # pylint: disable=unpacking-non-sequence ++ raise assess_dcerpc_exception(num=num, message=message) + + if not result: + return False +@@ -1408,7 +1418,8 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None): + result = netrc.finddc(domain=trustdomain, + flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS) + except RuntimeError as e: +- raise assess_dcerpc_exception(message=str(e)) ++ num, message = e.args # pylint: disable=unpacking-non-sequence ++ raise assess_dcerpc_exception(num=num, message=message) + + td.info['dc'] = unicode(result.pdc_dns_name) + td.info['name'] = unicode(result.dns_domain) +-- +2.9.3 + diff --git a/SOURCES/0077-trust-always-use-oddjobd-helper-for-fetching-trust-i.patch b/SOURCES/0077-trust-always-use-oddjobd-helper-for-fetching-trust-i.patch new file mode 100644 index 0000000..db6b720 --- /dev/null +++ b/SOURCES/0077-trust-always-use-oddjobd-helper-for-fetching-trust-i.patch @@ -0,0 +1,86 @@ +From 3353a259bb8ace57efcfd784f2a0c0c6884d9966 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 5 Apr 2017 12:37:10 +0300 +Subject: [PATCH] trust: always use oddjobd helper for fetching trust + information + +Since introduction of privilege separation in IPA framework none of the +operations that require direct access to the framework's credentials can +be done. All authentication has to be performed with GSSAPI. + +As result, we cannot obtain TGT for HTTP/.. principal with kinit +anymore, so it is better to re-route all types of trust to oddjobd +helper and get rid of casing out two-way trust. + +Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1438366 + +Reviewed-By: Martin Basti +--- + ipaserver/plugins/trust.py | 43 ++++++++++--------------------------------- + 1 file changed, 10 insertions(+), 33 deletions(-) + +diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py +index 3de2458466214044f6b1b5d8560a2a7ac53ede57..0829f8c714f15c4384a89e18ba29e417405c249c 100644 +--- a/ipaserver/plugins/trust.py ++++ b/ipaserver/plugins/trust.py +@@ -1742,47 +1742,24 @@ class trust_fetch_domains(LDAPRetrieve): + ldap = self.api.Backend.ldap2 + verify_samba_component_presence(ldap, self.api) + +- trust = self.api.Command.trust_show( +- keys[0], all=True, raw=True)['result'] ++ # Check first that the trust actually exists ++ result = self.api.Command.trust_show(keys[0], all=True, raw=True) ++ self.obj.warning_if_ad_trust_dom_have_missing_SID(result, **options) + + result = dict() + result['result'] = [] + result['count'] = 0 + result['truncated'] = False + +- trust_direction = int(trust['ipanttrustdirection'][0]) +- is_nontransitive = int(trust.get('ipanttrustattributes', +- [0])[0]) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE + # For one-way trust and external trust fetch over DBus. + # We don't get the list in this case. +- if trust_direction != TRUST_BIDIRECTIONAL or is_nontransitive: +- fetch_trusted_domains_over_dbus(self.api, self.log, keys[0]) +- result['summary'] = unicode(_('List of trust domains successfully refreshed. Use trustdomain-find command to list them.')) +- return result +- +- trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api) +- if not trustinstance.configured: +- raise errors.NotFound( +- name=_('AD Trust setup'), +- reason=_( +- 'Cannot perform join operation without own domain ' +- 'configured. Make sure you have run ipa-adtrust-install ' +- 'on the IPA server first' +- ) +- ) +- +- trustinstance.populate_remote_domain(keys[0]) +- +- res = fetch_domains_from_trust(self.api, trustinstance, **options) +- domains = add_new_domains_from_trust(self.api, trustinstance, trust, res, **options) +- +- if len(domains) > 0: +- result['summary'] = unicode(_('List of trust domains successfully refreshed')) +- else: +- result['summary'] = unicode(_('No new trust domains were found')) +- +- result['result'] = domains +- result['count'] = len(domains) ++ # With privilege separation we also cannot authenticate as ++ # HTTP/ principal because we have no access to its key material. ++ # Thus, we'll use DBus call out to oddjobd helper in all cases ++ fetch_trusted_domains_over_dbus(self.api, self.log, keys[0]) ++ result['summary'] = unicode(_('List of trust domains successfully ' ++ 'refreshed. Use trustdomain-find ' ++ 'command to list them.')) + return result + + +-- +2.9.3 + diff --git a/SOURCES/0078-WebUI-cert-login-Configure-name-of-parameter-used-to.patch b/SOURCES/0078-WebUI-cert-login-Configure-name-of-parameter-used-to.patch new file mode 100644 index 0000000..d547750 --- /dev/null +++ b/SOURCES/0078-WebUI-cert-login-Configure-name-of-parameter-used-to.patch @@ -0,0 +1,34 @@ +From 5ac1c55462297d4458d07a6ff9941170056216ef Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Mon, 10 Apr 2017 13:11:13 +0200 +Subject: [PATCH] WebUI: cert login: Configure name of parameter used to pass + username + +Directive LookupUserByCertificateParamName tells mod_lookup_identity module the +name of GET parameter that is used to provide username in case certificate is +mapped to multiple user accounts. +Without this directive login with certificate that's mapped to multiple users +doesn't work. + +https://pagure.io/freeipa/issue/6860 + +Reviewed-By: Florence Blanc-Renaud +--- + install/conf/ipa.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf +index e1f1a581b4e8a91b899bcf165ca81f266fa9e516..75c122e6c94b941c278d724add84315753082531 100644 +--- a/install/conf/ipa.conf ++++ b/install/conf/ipa.conf +@@ -117,6 +117,7 @@ Alias /ipa/session/cookie "/usr/share/ipa/gssapi.login" + NSSVerifyClient require + NSSUserName SSL_CLIENT_CERT + LookupUserByCertificate On ++ LookupUserByCertificateParamName "username" + WSGIProcessGroup ipa + WSGIApplicationGroup ipa + GssapiImpersonate On +-- +2.9.3 + diff --git a/SOURCES/0079-Create-system-users-for-FreeIPA-services-during-pack.patch b/SOURCES/0079-Create-system-users-for-FreeIPA-services-during-pack.patch new file mode 100644 index 0000000..586cc85 --- /dev/null +++ b/SOURCES/0079-Create-system-users-for-FreeIPA-services-during-pack.patch @@ -0,0 +1,414 @@ +From a4a85c69a945b023b4017ecf4285f9f5e97d5f20 Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Tue, 11 Apr 2017 11:43:40 +0200 +Subject: [PATCH] Create system users for FreeIPA services during package + installation + +Previously system users needed by FreeIPA server services was created during +ipa-server-install. This led to problem when DBus policy was configured during +package installation but the user specified in the policy didn't exist yet +(and potentionally similar ones). Now the users will be created in package %pre +section so all users freeipa-server package needs exist before any installation +or configuration begins. +Another possibility would be using systemd-sysusers(8) for this purpose but +given that systemd is not available during container build the traditional +approach is superior. +Also dirsrv and pkiuser users are no longer created by FreeIPA instead it +depends on 389ds and dogtag to create those users. + +https://pagure.io/freeipa/issue/6743 + +Reviewed-By: Jan Cholasta +Reviewed-By: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + freeipa.spec.in | 9 +++++ + ipaplatform/base/tasks.py | 53 ------------------------------ + ipaplatform/redhat/tasks.py | 26 --------------- + ipaserver/install/cainstance.py | 12 ------- + ipaserver/install/dsinstance.py | 11 ------- + ipaserver/install/httpinstance.py | 13 -------- + ipaserver/install/installutils.py | 13 -------- + ipaserver/install/ipa_restore.py | 7 ---- + ipaserver/install/server/install.py | 6 +--- + ipaserver/install/server/replicainstall.py | 6 +--- + ipaserver/install/server/upgrade.py | 2 -- + 11 files changed, 11 insertions(+), 147 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 829c3f0b2898de1ecbf0cfb769fde5cd978c241c..978ebb184f7d051b303940560f44c7a094b071a1 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1030,6 +1030,15 @@ if [ -e /usr/sbin/ipa_kpasswd ]; then + # END + fi + ++# create users and groups ++# create kdcproxy group and user ++getent group kdcproxy >/dev/null || groupadd -f -r kdcproxy ++getent passwd kdcproxy >/dev/null || useradd -r -g kdcproxy -s /sbin/nologin -d / -c "IPA KDC Proxy User" kdcproxy ++# create ipaapi group and user ++getent group ipaapi >/dev/null || groupadd -f -r ipaapi ++getent passwd ipaapi >/dev/null || useradd -r -g ipaapi -s /sbin/nologin -d / -c "IPA Framework User" ipaapi ++# add apache to ipaaapi group ++id -Gn apache | grep '\bipaapi\b' >/dev/null || usermod apache -a -G ipaapi + + %postun server-trust-ad + if [ "$1" -ge "1" ]; then +diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py +index 9f91fef2b572a29bf876641fd9ad879604054a2f..3358b7d257cc60ceaecfbbac5155d79b0e63de2e 100644 +--- a/ipaplatform/base/tasks.py ++++ b/ipaplatform/base/tasks.py +@@ -22,9 +22,6 @@ + This module contains default platform-specific implementations of system tasks. + ''' + +-import pwd +-import grp +- + from pkg_resources import parse_version + + from ipaplatform.paths import paths +@@ -186,56 +183,6 @@ class BaseTaskNamespace(object): + + raise NotImplementedError() + +- def create_system_user(self, name, group, homedir, shell, +- uid=None, gid=None, comment=None, +- create_homedir=False, groups=None): +- """Create a system user with a corresponding group""" +- try: +- grp.getgrnam(group) +- except KeyError: +- log.debug('Adding group %s', group) +- args = [paths.GROUPADD, '-r', group] +- if gid: +- args += ['-g', str(gid)] +- try: +- ipautil.run(args) +- log.debug('Done adding group') +- except ipautil.CalledProcessError as e: +- log.critical('Failed to add group: %s', e) +- raise +- else: +- log.debug('group %s exists', group) +- +- try: +- pwd.getpwnam(name) +- except KeyError: +- log.debug('Adding user %s', name) +- args = [ +- paths.USERADD, +- '-g', group, +- '-d', homedir, +- '-s', shell, +- '-r', name, +- ] +- if uid: +- args += ['-u', str(uid)] +- if comment: +- args += ['-c', comment] +- if create_homedir: +- args += ['-m'] +- else: +- args += ['-M'] +- if groups is not None: +- args += ['-G', groups.join(',')] +- try: +- ipautil.run(args) +- log.debug('Done adding user') +- except ipautil.CalledProcessError as e: +- log.critical('Failed to add user: %s', e) +- raise +- else: +- log.debug('user %s exists', name) +- + @staticmethod + def parse_ipa_version(version): + """ +diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py +index d0ef5fbd1ceb8110dd417dda44a74dc63898456a..07efebab97eabcf2dc39bd345920a1c7be56e9f5 100644 +--- a/ipaplatform/redhat/tasks.py ++++ b/ipaplatform/redhat/tasks.py +@@ -431,32 +431,6 @@ class RedHatTaskNamespace(BaseTaskNamespace): + + return True + +- def create_system_user(self, name, group, homedir, shell, +- uid=None, gid=None, comment=None, +- create_homedir=False, groups=None): +- """ +- Create a system user with a corresponding group +- +- According to https://fedoraproject.org/wiki/Packaging:UsersAndGroups?rd=Packaging/UsersAndGroups#Soft_static_allocation +- some system users should have fixed UID, GID and other parameters set. +- This values should be constant and may be hardcoded. +- Add other values for other users when needed. +- """ +- if name == constants.PKI_USER: +- if uid is None: +- uid = 17 +- if gid is None: +- gid = 17 +- if comment is None: +- comment = 'CA System User' +- if name == constants.DS_USER: +- if comment is None: +- comment = 'DS System User' +- +- super(RedHatTaskNamespace, self).create_system_user( +- name, group, homedir, shell, uid, gid, comment, create_homedir, +- groups) +- + def parse_ipa_version(self, version): + """ + :param version: textual version +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 3980e412603437b0db5804623f6626d11e52c009..ac5d9e2fc633c5ad732670245b72bee0f03268a6 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -46,7 +46,6 @@ from ipalib import errors + import ipalib.constants + from ipalib.install import certmonger + from ipaplatform import services +-from ipaplatform.constants import constants + from ipaplatform.paths import paths + from ipaplatform.tasks import tasks + +@@ -263,16 +262,6 @@ def is_ca_installed_locally(): + return os.path.exists(paths.CA_CS_CFG_PATH) + + +-def create_ca_user(): +- """Create PKI user/group if it doesn't exist yet.""" +- tasks.create_system_user( +- name=constants.PKI_USER, +- group=constants.PKI_GROUP, +- homedir=paths.VAR_LIB, +- shell=paths.NOLOGIN, +- ) +- +- + class CAInstance(DogtagInstance): + """ + When using a dogtag CA the DS database contains just the +@@ -382,7 +371,6 @@ class CAInstance(DogtagInstance): + has_ra_cert = False + + if not ra_only: +- self.step("creating certificate server user", create_ca_user) + if promote: + # Setup Database + self.step("creating certificate server db", self.__create_ds_db) +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 72fcb65f2eb699d0077d3c5cc02a3fcaaad9b8e5..99a1781ca4475805e9bf3b2bac3f26b5fb107a43 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -158,16 +158,6 @@ def is_ds_running(server_id=''): + return services.knownservices.dirsrv.is_running(instance_name=server_id) + + +-def create_ds_user(): +- """Create DS user/group if it doesn't exist yet.""" +- tasks.create_system_user( +- name=DS_USER, +- group=DS_USER, +- homedir=paths.VAR_LIB_DIRSRV, +- shell=paths.NOLOGIN, +- ) +- +- + def get_domain_level(api=api): + ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=api.env.realm) + conn = ipaldap.LDAPClient(ldap_uri) +@@ -258,7 +248,6 @@ class DsInstance(service.Service): + + def __common_setup(self): + +- self.step("creating directory server user", create_ds_user) + self.step("creating directory server instance", self.__create_instance) + self.step("enabling ldapi", self.__enable_ldapi) + self.step("configure autobind for root", self.__root_autobind) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 45bf479d1088c3b3396d955bf2592c4bce1e886f..8e444be2d23ec5e7890d221508bc866de2854c89 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -102,18 +102,6 @@ def httpd_443_configured(): + return False + + +-def create_kdcproxy_user(): +- """Create KDC proxy user/group if it doesn't exist yet.""" +- tasks.create_system_user( +- name=KDCPROXY_USER, +- group=KDCPROXY_USER, +- homedir=paths.VAR_LIB_KDCPROXY, +- shell=paths.NOLOGIN, +- comment="IPA KDC Proxy User", +- create_homedir=True, +- ) +- +- + class WebGuiInstance(service.SimpleServiceInstance): + def __init__(self): + service.SimpleServiceInstance.__init__(self, "ipa_webgui") +@@ -183,7 +171,6 @@ class HTTPInstance(service.Service): + self.remove_httpd_ccaches) + self.step("configuring SELinux for httpd", self.configure_selinux_for_httpd) + if not self.is_kdcproxy_configured(): +- self.step("create KDC proxy user", create_kdcproxy_user) + self.step("create KDC proxy config", self.create_kdcproxy_conf) + self.step("enable KDC proxy", self.enable_kdcproxy) + self.step("starting httpd", self.start) +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index ef6a399ad28ae8b8646864baea9965f762050484..9230e70056b1a773246a0d95e6ecb943cada953c 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -44,7 +44,6 @@ import six + from six.moves.configparser import SafeConfigParser, NoOptionError + # pylint: enable=import-error + +-from ipalib.constants import IPAAPI_USER, IPAAPI_GROUP + from ipalib.install import sysrestore + from ipalib.install.kinit import kinit_password + import ipaplatform +@@ -56,7 +55,6 @@ from ipalib import api, errors, x509 + from ipapython.dn import DN + from ipaserver.install import certs, service, sysupgrade + from ipaplatform import services +-from ipaplatform.constants import constants + from ipaplatform.paths import paths + from ipaplatform.tasks import tasks + +@@ -1515,14 +1513,3 @@ def default_subject_base(realm_name): + + def default_ca_subject_dn(subject_base): + return DN(('CN', 'Certificate Authority'), subject_base) +- +- +-def create_ipaapi_user(): +- """Create IPA API user/group if it doesn't exist yet.""" +- tasks.create_system_user( +- name=IPAAPI_USER, +- group=IPAAPI_GROUP, +- homedir=paths.VAR_LIB, +- shell=paths.NOLOGIN +- ) +- tasks.add_user_to_group(constants.HTTPD_USER, IPAAPI_GROUP) +diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py +index 2552bbdef36f653f1c377ea096ca227d09e5f3e6..378c013b6f4a4656768d7a484d2014a0f9eef3c0 100644 +--- a/ipaserver/install/ipa_restore.py ++++ b/ipaserver/install/ipa_restore.py +@@ -36,8 +36,6 @@ from ipapython import version, ipautil + from ipapython.ipautil import run, user_input + from ipapython import admintool + from ipapython.dn import DN +-from ipaserver.install.dsinstance import create_ds_user +-from ipaserver.install.cainstance import create_ca_user + from ipaserver.install.replication import (wait_for_task, ReplicationManager, + get_cs_replication_manager) + from ipaserver.install import installutils +@@ -296,7 +294,6 @@ class Restore(admintool.AdminTool): + not user_input("Continue to restore?", False)): + raise admintool.ScriptError("Aborted") + +- create_ds_user() + pent = pwd.getpwnam(constants.DS_USER) + + # Temporary directory for decrypting files before restoring +@@ -379,15 +376,11 @@ class Restore(admintool.AdminTool): + # We do either a full file restore or we restore data. + if restore_type == 'FULL': + self.remove_old_files() +- if 'CA' in self.backup_services: +- create_ca_user() + self.cert_restore_prepare() + self.file_restore(options.no_logs) + self.cert_restore() + if 'CA' in self.backup_services: + self.__create_dogtag_log_dirs() +- if http.is_kdcproxy_configured(): +- httpinstance.create_kdcproxy_user() + + # Always restore the data from ldif + # We need to restore both userRoot and ipaca. +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index bf2e248dceaae36ba0030d3eaa47976f51ce60ba..197f01ccef58bb3564eb4c6b5b4d615bff1e523d 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -39,7 +39,7 @@ from ipaserver.install import ( + from ipaserver.install.installutils import ( + IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address, + is_ipa_configured, load_pkcs12, read_password, verify_fqdn, +- update_hosts_file, create_ipaapi_user) ++ update_hosts_file) + + if six.PY3: + unicode = str +@@ -721,12 +721,8 @@ def install(installer): + update_hosts_file(ip_addresses, host_name, fstore) + + # Make sure tmpfiles dir exist before installing components +- create_ipaapi_user() + tasks.create_tmpfiles_dirs() + +- # Create DS user/group if it doesn't exist yet +- dsinstance.create_ds_user() +- + # Create a directory server instance + if not options.external_cert_files: + # Configure ntpd +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 6f1a0d6d29b20d53986205a63382a385e75f80ea..b82d7b474640e24da7d978e9546ebd7a8e602c29 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -41,8 +41,7 @@ from ipaserver.install import ( + installutils, kra, krbinstance, + ntpinstance, otpdinstance, custodiainstance, service) + from ipaserver.install.installutils import ( +- create_replica_config, ReplicaConfig, load_pkcs12, is_ipa_configured, +- create_ipaapi_user) ++ create_replica_config, ReplicaConfig, load_pkcs12, is_ipa_configured) + from ipaserver.install.replication import ( + ReplicationManager, replica_conn_check) + import SSSDConfig +@@ -1347,7 +1346,6 @@ def install(installer): + ccache = os.environ['KRB5CCNAME'] + + # Make sure tmpfiles dir exist before installing components +- create_ipaapi_user() + tasks.create_tmpfiles_dirs() + + if promote: +@@ -1376,8 +1374,6 @@ def install(installer): + ntp = ntpinstance.NTPInstance() + ntp.create_instance() + +- dsinstance.create_ds_user() +- + try: + if promote: + conn.connect(ccache=ccache) +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 25b86297af3ae9d5f21cebb93f493b90670dcfc3..927acb011172de926773196eb1d032af8376f3d9 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1652,7 +1652,6 @@ def upgrade_configuration(): + + if not http.is_kdcproxy_configured(): + root_logger.info('[Enabling KDC Proxy]') +- httpinstance.create_kdcproxy_user() + http.create_kdcproxy_conf() + http.enable_kdcproxy() + +@@ -1837,7 +1836,6 @@ def upgrade_check(options): + + def upgrade(): + # Do this early so that any code depending on these dirs will not fail +- installutils.create_ipaapi_user() + tasks.create_tmpfiles_dirs() + tasks.configure_tmpfiles() + +-- +2.9.3 + diff --git a/SOURCES/0080-Fix-s4u2self-with-adtrust.patch b/SOURCES/0080-Fix-s4u2self-with-adtrust.patch new file mode 100644 index 0000000..af64e74 --- /dev/null +++ b/SOURCES/0080-Fix-s4u2self-with-adtrust.patch @@ -0,0 +1,60 @@ +From 542c31e057cbd4bd6261abcc883ace14f69719d6 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Mon, 10 Apr 2017 15:32:54 -0400 +Subject: [PATCH] Fix s4u2self with adtrust + +When ADtrust is installed we add a PAC to all tickets, during protocol +transition we need to generate a new PAC for the requested user ticket, +not check the existing PAC on the requestor ticket. + +https://pagure.io/freeipa/issue/6862 + +Signed-off-by: Simo Sorce +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-kdb/ipa_kdb_mspac.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c +index cf1bd5b4eaf6ac8eba92639cc48cb7c333a6e836..00cc19ca1e757e28530eafcd38ebf73003e251e3 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -2117,6 +2117,7 @@ krb5_error_code ipadb_sign_authdata(krb5_context context, + struct ipadb_context *ipactx; + bool with_pac; + bool with_pad; ++ bool make_ad = false; + int result; + krb5_db_entry *client_entry = NULL; + krb5_boolean is_equal; +@@ -2165,7 +2166,14 @@ krb5_error_code ipadb_sign_authdata(krb5_context context, + "currently not supported."); + } + +- if (is_as_req && with_pac && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) { ++ /* we need to create a PAC if we are requested one and this is an AS REQ, ++ * or we are doing protocol transition (s4u2self) */ ++ if ((is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) || ++ (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { ++ make_ad = true; ++ } ++ ++ if (with_pac && make_ad) { + /* Be aggressive here: special case for discovering range type + * immediately after establishing the trust by IPA framework */ + if ((krb5_princ_size(context, ks_client_princ) == 2) && +@@ -2188,9 +2196,7 @@ krb5_error_code ipadb_sign_authdata(krb5_context context, + if (kerr != 0 && kerr != ENOENT) { + goto done; + } +- } +- +- if (!is_as_req && with_pac) { ++ } else if (with_pac && !is_as_req) { + /* find the existing PAC, if present */ + kerr = krb5_find_authdata(context, tgt_auth_data, NULL, + KRB5_AUTHDATA_WIN2K_PAC, &pac_auth_data); +-- +2.9.3 + diff --git a/SOURCES/0081-Add-debug-log-in-case-cookie-retrieval-went-wrong.patch b/SOURCES/0081-Add-debug-log-in-case-cookie-retrieval-went-wrong.patch new file mode 100644 index 0000000..eef3ee2 --- /dev/null +++ b/SOURCES/0081-Add-debug-log-in-case-cookie-retrieval-went-wrong.patch @@ -0,0 +1,31 @@ +From 6a66a69f4500fc8b324f3f3f0f0a4d79ea3fbe1e Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 17 Mar 2017 08:55:30 +0100 +Subject: [PATCH] Add debug log in case cookie retrieval went wrong + +https://pagure.io/freeipa/issue/6774 + +Reviewed-By: Martin Basti +--- + ipalib/rpc.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index 5c49bd2456b7e564043a886c840fa2678060f9e3..e23ca3d061645b2695a9e0deaa0b7d666f986e0e 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -895,7 +895,10 @@ class RPCClient(Connectible): + session_cookie = Cookie.get_named_cookie_from_string( + cookie_string, COOKIE_NAME, + timestamp=datetime.datetime.utcnow()) +- except Exception: ++ except Exception as e: ++ self.log.debug( ++ 'Error retrieving cookie from the persistent storage: {err}' ++ .format(err=e)) + return None + + return session_cookie +-- +2.12.2 + diff --git a/SOURCES/0082-server-install-remove-broken-no-pkinit-check.patch b/SOURCES/0082-server-install-remove-broken-no-pkinit-check.patch new file mode 100644 index 0000000..bb4e410 --- /dev/null +++ b/SOURCES/0082-server-install-remove-broken-no-pkinit-check.patch @@ -0,0 +1,36 @@ +From e4f21a17762e3dcdbe05d9d62255fff9a7e2c8fa Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 4 Apr 2017 10:41:23 +0200 +Subject: [PATCH] server-install: remove broken no-pkinit check + +Don't check for no-pkinit option in case pkinit cert file was +provided. Setting no-pkinit is prohibited in this case, so without +this fix we have an impossible option-check if we want to provide +an own pkinit certificate and private key. + +https://pagure.io/freeipa/issue/6807 + +Reviewed-By: Martin Basti +--- + ipaserver/install/server/install.py | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 197f01ccef58bb3564eb4c6b5b4d615bff1e523d..b899b4be4028e6cdfd95bb9868fba8be25a07b65 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -513,11 +513,6 @@ def install_check(installer): + dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin) + + if options.pkinit_cert_files: +- if not options.no_pkinit: +- raise ScriptError("Cannot create KDC PKINIT certificate and use " +- "provided external PKINIT certificate at the " +- "same time. Please choose one of them.") +- + if options.pkinit_pin is None: + options.pkinit_pin = read_password( + "Enter Kerberos KDC private key unlock", +-- +2.12.2 + diff --git a/SOURCES/0083-Add-the-force-join-option-to-replica-install.patch b/SOURCES/0083-Add-the-force-join-option-to-replica-install.patch new file mode 100644 index 0000000..db317df --- /dev/null +++ b/SOURCES/0083-Add-the-force-join-option-to-replica-install.patch @@ -0,0 +1,55 @@ +From 8af160743817054289d1fff9aa904168e9606061 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 5 Apr 2017 09:49:57 +0200 +Subject: [PATCH] Add the force-join option to replica install + +When installing client from inside replica installation on DL1, +it's possible that the client installation would fail and recommend +using --force-join option which is not available in replica installer. +Add the option there. + +https://pagure.io/freeipa/issue/6183 + +Reviewed-By: Tomas Krizek +Reviewed-By: Jan Cholasta +--- + ipaserver/install/server/__init__.py | 2 +- + ipaserver/install/server/replicainstall.py | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py +index 89444f21fefc902931b7ecfaba861a18ecc28dbe..028a4aa60feccf5af85f76d443dcb42b01684406 100644 +--- a/ipaserver/install/server/__init__.py ++++ b/ipaserver/install/server/__init__.py +@@ -166,7 +166,6 @@ class ServerInstallInterface(ServerCertificateInstallInterface, + """ + description = "Server" + +- force_join = False + kinit_attempts = 1 + fixed_primary = True + ntp_servers = None +@@ -526,6 +525,7 @@ class ServerMasterInstall(ServerMasterInstallInterface): + Server master installer + """ + ++ force_join = False + servers = None + no_wait_for_dns = True + host_password = None +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index b82d7b474640e24da7d978e9546ebd7a8e602c29..383932b39b9ee99a7a5ce3275a5a7e02581b85b7 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -935,6 +935,8 @@ def ensure_enrolled(installer): + args.append("--no-sshd") + if installer.mkhomedir: + args.append("--mkhomedir") ++ if installer.force_join: ++ args.append("--force-join") + + ipautil.run(args, stdin=stdin, nolog=nolog, redirect_output=True) + print() +-- +2.12.2 + diff --git a/SOURCES/0084-replicainstall-better-client-install-exception-handl.patch b/SOURCES/0084-replicainstall-better-client-install-exception-handl.patch new file mode 100644 index 0000000..6e8ac10 --- /dev/null +++ b/SOURCES/0084-replicainstall-better-client-install-exception-handl.patch @@ -0,0 +1,120 @@ +From 9303e3d0912e60b2069f7c5bad6b816ed8b033ef Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 5 Apr 2017 09:57:44 +0200 +Subject: [PATCH] replicainstall: better client install exception handling + +The exception handling of client install inside replica installation +was rather promiscuous, hungrily eating any possible exception thrown +at it. Scoped down the try-except block and reduced its promiscuity. +This change should improve the future development experience debugging +this part of the code. + +https://pagure.io/freeipa/issue/6183 + +Reviewed-By: Tomas Krizek +Reviewed-By: Jan Cholasta +--- + ipaserver/install/server/replicainstall.py | 83 +++++++++++++++--------------- + 1 file changed, 41 insertions(+), 42 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 383932b39b9ee99a7a5ce3275a5a7e02581b85b7..aa8e67f60b8abe591d55a907c409b584c74d4541 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -895,52 +895,51 @@ def install_check(installer): + + + def ensure_enrolled(installer): +- # Call client install script +- service.print_msg("Configuring client side components") ++ args = [paths.IPA_CLIENT_INSTALL, "--unattended", "--no-ntp"] ++ stdin = None ++ nolog = [] ++ ++ if installer.domain_name: ++ args.extend(["--domain", installer.domain_name]) ++ if installer.server: ++ args.extend(["--server", installer.server]) ++ if installer.realm_name: ++ args.extend(["--realm", installer.realm_name]) ++ if installer.host_name: ++ args.extend(["--hostname", installer.host_name]) ++ ++ if installer.password: ++ args.extend(["--password", installer.password]) ++ nolog.append(installer.password) ++ else: ++ if installer.admin_password: ++ # Always set principal if password was set explicitly, ++ # the password itself gets passed directly via stdin ++ args.extend(["--principal", installer.principal or "admin"]) ++ stdin = installer.admin_password ++ if installer.keytab: ++ args.extend(["--keytab", installer.keytab]) ++ ++ if installer.no_dns_sshfp: ++ args.append("--no-dns-sshfp") ++ if installer.ssh_trust_dns: ++ args.append("--ssh-trust-dns") ++ if installer.no_ssh: ++ args.append("--no-ssh") ++ if installer.no_sshd: ++ args.append("--no-sshd") ++ if installer.mkhomedir: ++ args.append("--mkhomedir") ++ if installer.force_join: ++ args.append("--force-join") ++ + try: ++ # Call client install script ++ service.print_msg("Configuring client side components") + installer._enrollment_performed = True +- +- args = [paths.IPA_CLIENT_INSTALL, "--unattended", "--no-ntp"] +- stdin = None +- nolog = [] +- +- if installer.domain_name: +- args.extend(["--domain", installer.domain_name]) +- if installer.server: +- args.extend(["--server", installer.server]) +- if installer.realm_name: +- args.extend(["--realm", installer.realm_name]) +- if installer.host_name: +- args.extend(["--hostname", installer.host_name]) +- +- if installer.password: +- args.extend(["--password", installer.password]) +- nolog.append(installer.password) +- else: +- if installer.admin_password: +- # Always set principal if password was set explicitly, +- # the password itself gets passed directly via stdin +- args.extend(["--principal", installer.principal or "admin"]) +- stdin = installer.admin_password +- if installer.keytab: +- args.extend(["--keytab", installer.keytab]) +- +- if installer.no_dns_sshfp: +- args.append("--no-dns-sshfp") +- if installer.ssh_trust_dns: +- args.append("--ssh-trust-dns") +- if installer.no_ssh: +- args.append("--no-ssh") +- if installer.no_sshd: +- args.append("--no-sshd") +- if installer.mkhomedir: +- args.append("--mkhomedir") +- if installer.force_join: +- args.append("--force-join") +- + ipautil.run(args, stdin=stdin, nolog=nolog, redirect_output=True) + print() +- except Exception: ++ except ipautil.CalledProcessError: + raise ScriptError("Configuration of client side components failed!") + + +-- +2.12.2 + diff --git a/SOURCES/0085-Fix-CA-less-to-CA-full-upgrade.patch b/SOURCES/0085-Fix-CA-less-to-CA-full-upgrade.patch new file mode 100644 index 0000000..18f9500 --- /dev/null +++ b/SOURCES/0085-Fix-CA-less-to-CA-full-upgrade.patch @@ -0,0 +1,110 @@ +From c3ee037c2dd92ccb277523919e991471c9caa3c6 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 11 Apr 2017 10:21:15 +0200 +Subject: [PATCH] Fix CA-less to CA-full upgrade + +CertDB would have always created a directory on initialization. This +behavior changes here by replacing the truncate argument with create +which will only create the database when really required. + +https://pagure.io/freeipa/issue/6853 + +Reviewed-By: Tomas Krizek +--- + ipaserver/install/ca.py | 2 ++ + ipaserver/install/certs.py | 38 ++++++++++++++++++++++++++++---------- + ipaserver/install/httpinstance.py | 2 +- + 3 files changed, 31 insertions(+), 11 deletions(-) + +diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py +index db3b744a51b0ae2ba12f79c155a1bb0698d94bec..8ee0fda23411563c70b7db5f39f43c2869c108b5 100644 +--- a/ipaserver/install/ca.py ++++ b/ipaserver/install/ca.py +@@ -183,6 +183,8 @@ def install_check(standalone, replica_config, options): + realm_name, nssdir=dirname, subject_base=options._subject_base) + + for db in (cadb, dsdb): ++ if not db.exists(): ++ continue + for nickname, _trust_flags in db.list_certs(): + if nickname == certdb.get_ca_nickname(realm_name): + raise ScriptError( +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 16139f81f0d0bd6889a9f38948204bb5bc018028..89e57134f24c505d669057eefffb7862b3b8179a 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -99,7 +99,7 @@ class CertDB(object): + # TODO: Remove all selfsign code + def __init__(self, realm, nssdir, fstore=None, + host_name=None, subject_base=None, ca_subject=None, +- user=None, group=None, mode=None, truncate=False): ++ user=None, group=None, mode=None, create=False): + self.nssdb = NSSDatabase(nssdir) + + self.secdir = nssdir +@@ -132,15 +132,16 @@ class CertDB(object): + self.uid = 0 + self.gid = 0 + +- if not truncate and os.path.exists(self.secdir): +- # We are going to set the owner of all of the cert +- # files to the owner of the containing directory +- # instead of that of the process. This works when +- # this is called by root for a daemon that runs as +- # a normal user +- mode = os.stat(self.secdir) +- self.uid = mode[stat.ST_UID] +- self.gid = mode[stat.ST_GID] ++ if not create: ++ if os.path.isdir(self.secdir): ++ # We are going to set the owner of all of the cert ++ # files to the owner of the containing directory ++ # instead of that of the process. This works when ++ # this is called by root for a daemon that runs as ++ # a normal user ++ mode = os.stat(self.secdir) ++ self.uid = mode[stat.ST_UID] ++ self.gid = mode[stat.ST_GID] + else: + if user is not None: + pu = pwd.getpwnam(user) +@@ -162,6 +163,23 @@ class CertDB(object): + def passwd_fname(self): + return self.nssdb.pwd_file + ++ def exists(self): ++ """ ++ Checks whether all NSS database files + our pwd_file exist ++ """ ++ db_files = ( ++ self.secdir, ++ self.certdb_fname, ++ self.keydb_fname, ++ self.secmod_fname, ++ self.nssdb.pwd_file, ++ ) ++ ++ for f in db_files: ++ if not os.path.exists(f): ++ return False ++ return True ++ + def __del__(self): + if self.reqdir is not None: + shutil.rmtree(self.reqdir, ignore_errors=True) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 8e444be2d23ec5e7890d221508bc866de2854c89..aeb5c5e450813469e1b6cd374b30cd4aab338537 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -366,7 +366,7 @@ class HTTPInstance(service.Service): + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR, + subject_base=self.subject_base, user="root", + group=constants.HTTPD_GROUP, +- truncate=True) ++ create=True) + self.disable_system_trust() + self.create_password_conf() + if self.pkcs12_info: +-- +2.12.2 + diff --git a/SOURCES/0086-cert-defer-cert-find-result-post-processing.patch b/SOURCES/0086-cert-defer-cert-find-result-post-processing.patch new file mode 100644 index 0000000..044f33a --- /dev/null +++ b/SOURCES/0086-cert-defer-cert-find-result-post-processing.patch @@ -0,0 +1,221 @@ +From eb9d14debc4276f422ae55d141e30246d5943067 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 30 Mar 2017 08:33:30 +0000 +Subject: [PATCH] cert: defer cert-find result post-processing + +Rather than post-processing the results of each internal search, +post-process the combined result. + +This avoids expensive per-certificate searches when cert-find is executed +with the --all option on certificates which won't even be included in the +combined result. + +https://pagure.io/freeipa/issue/6808 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/cert.py | 93 +++++++++++++++++++++++++++------------------ + ipaserver/plugins/dogtag.py | 10 +++++ + 2 files changed, 66 insertions(+), 37 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index dfc7444ddbf31ac3c194e050af28220fc2a87a92..68402679cf0320e9c664ea89276f6c4332730a15 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -162,6 +162,11 @@ def normalize_pkidate(value): + return datetime.datetime.strptime(value, PKIDATE_FORMAT) + + ++def convert_pkidatetime(value): ++ value = datetime.datetime.fromtimestamp(int(value) // 1000) ++ return x509.format_datetime(value) ++ ++ + def validate_csr(ugettext, csr): + """ + Ensure the CSR is base64-encoded and can be decoded by our PKCS#10 +@@ -1296,18 +1301,7 @@ class cert_find(Search, CertMethod): + + return (DN(cert_obj.issuer), cert_obj.serial_number) + +- def _get_cert_obj(self, cert, all, raw, pkey_only): +- obj = {'certificate': base64.b64encode(cert).decode('ascii')} +- +- full = not pkey_only and all +- if not raw: +- self.obj._parse(obj, full) +- if not full: +- del obj['certificate'] +- +- return obj +- +- def _cert_search(self, all, raw, pkey_only, **options): ++ def _cert_search(self, pkey_only, **options): + result = collections.OrderedDict() + + try: +@@ -1316,15 +1310,19 @@ class cert_find(Search, CertMethod): + return result, False, False + + try: +- key = self._get_cert_key(cert) ++ issuer, serial_number = self._get_cert_key(cert) + except ValueError: + return result, True, True + +- result[key] = self._get_cert_obj(cert, all, raw, pkey_only) ++ obj = {'serial_number': serial_number} ++ if not pkey_only: ++ obj['certificate'] = base64.b64encode(cert).decode('ascii') ++ ++ result[issuer, serial_number] = obj + + return result, False, True + +- def _ca_search(self, all, raw, pkey_only, exactly, **options): ++ def _ca_search(self, raw, pkey_only, exactly, **options): + ra_options = {} + for name in ('revocation_reason', + 'issuer', +@@ -1357,7 +1355,6 @@ class cert_find(Search, CertMethod): + return result, False, complete + + ca_objs = self.api.Command.ca_find( +- all=all, + timelimit=0, + sizelimit=0, + )['result'] +@@ -1377,24 +1374,16 @@ class cert_find(Search, CertMethod): + obj = {'serial_number': serial_number} + else: + obj = ra_obj +- if all: +- obj.update(ra.get_certificate(str(serial_number))) + + if not raw: + obj['issuer'] = issuer + obj['subject'] = DN(ra_obj['subject']) ++ obj['valid_not_before'] = ( ++ convert_pkidatetime(obj['valid_not_before'])) ++ obj['valid_not_after'] = ( ++ convert_pkidatetime(obj['valid_not_after'])) + obj['revoked'] = ( + ra_obj['status'] in (u'REVOKED', u'REVOKED_EXPIRED')) +- if all: +- obj['certificate'] = ( +- obj['certificate'].replace('\r\n', '')) +- self.obj._parse(obj) +- +- if 'certificate_chain' in ca_obj: +- cert = x509.load_certificate(obj['certificate']) +- cert_der = cert.public_bytes(serialization.Encoding.DER) +- obj['certificate_chain'] = ( +- [cert_der] + ca_obj['certificate_chain']) + + obj['cacn'] = ca_obj['cn'][0] + +@@ -1402,7 +1391,7 @@ class cert_find(Search, CertMethod): + + return result, False, complete + +- def _ldap_search(self, all, raw, pkey_only, no_members, **options): ++ def _ldap_search(self, all, pkey_only, no_members, **options): + ldap = self.api.Backend.ldap2 + + filters = [] +@@ -1461,26 +1450,25 @@ class cert_find(Search, CertMethod): + for attr in ('usercertificate', 'usercertificate;binary'): + for cert in entry.get(attr, []): + try: +- key = self._get_cert_key(cert) ++ issuer, serial_number = self._get_cert_key(cert) + except ValueError: + truncated = True + continue + + try: +- obj = result[key] ++ obj = result[issuer, serial_number] + except KeyError: +- obj = self._get_cert_obj(cert, all, raw, pkey_only) +- result[key] = obj ++ obj = {'serial_number': serial_number} ++ if not pkey_only and all: ++ obj['certificate'] = ( ++ base64.b64encode(cert).decode('ascii')) ++ result[issuer, serial_number] = obj + + if not pkey_only and (all or not no_members): + owners = obj.setdefault('owner', []) + if entry.dn not in owners: + owners.append(entry.dn) + +- if not raw: +- for obj in six.itervalues(result): +- self.obj._fill_owners(obj) +- + return result, truncated, complete + + def execute(self, criteria=None, all=False, raw=False, pkey_only=False, +@@ -1537,6 +1525,37 @@ class cert_find(Search, CertMethod): + truncated = truncated or sub_truncated + complete = complete or sub_complete + ++ if not pkey_only: ++ ca_objs = {} ++ ra = self.api.Backend.ra ++ ++ for key, obj in six.iteritems(result): ++ if all and 'cacn' in obj: ++ _issuer, serial_number = key ++ cacn = obj['cacn'] ++ ++ try: ++ ca_obj = ca_objs[cacn] ++ except KeyError: ++ ca_obj = ca_objs[cacn] = ( ++ self.api.Command.ca_show(cacn, all=True)['result']) ++ ++ obj.update(ra.get_certificate(str(serial_number))) ++ if not raw: ++ obj['certificate'] = ( ++ obj['certificate'].replace('\r\n', '')) ++ ++ if 'certificate_chain' in ca_obj: ++ cert = x509.load_certificate(obj['certificate']) ++ cert_der = ( ++ cert.public_bytes(serialization.Encoding.DER)) ++ obj['certificate_chain'] = ( ++ [cert_der] + ca_obj['certificate_chain']) ++ ++ if not raw: ++ self.obj._parse(obj, all) ++ self.obj._fill_owners(obj) ++ + result = list(six.itervalues(result)) + if sizelimit > 0 and len(result) > sizelimit: + if not truncated: +diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py +index d1dd707f145e0f58bfa721df55513d46c14358f2..3997531032746a22243a4219250af4172e9ae5b3 100644 +--- a/ipaserver/plugins/dogtag.py ++++ b/ipaserver/plugins/dogtag.py +@@ -1945,6 +1945,16 @@ class ra(rabase.rabase, RestClient): + if len(issuer_dn) == 1: + response_request['issuer'] = unicode(issuer_dn[0].text) + ++ not_valid_before = cert.xpath('NotValidBefore') ++ if len(not_valid_before) == 1: ++ response_request['valid_not_before'] = ( ++ unicode(not_valid_before[0].text)) ++ ++ not_valid_after = cert.xpath('NotValidAfter') ++ if len(not_valid_after) == 1: ++ response_request['valid_not_after'] = ( ++ unicode(not_valid_after[0].text)) ++ + status = cert.xpath('Status') + if len(status) == 1: + response_request['status'] = unicode(status[0].text) +-- +2.12.2 + diff --git a/SOURCES/0087-server-install-No-double-Kerberos-install.patch b/SOURCES/0087-server-install-No-double-Kerberos-install.patch new file mode 100644 index 0000000..9750dd7 --- /dev/null +++ b/SOURCES/0087-server-install-No-double-Kerberos-install.patch @@ -0,0 +1,42 @@ +From fabf804e7351b546310cc1f50164785099ff1811 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 18 Apr 2017 17:14:27 +0200 +Subject: [PATCH] server-install: No double Kerberos install + +When we're installing server with an external CA, the installation +would have failed in the second step where it's passed the required +CA cert file because it would have tried to perform the Kerberos +installation for the second time. + +https://pagure.io/freeipa/issue/6757 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/server/install.py | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index b899b4be4028e6cdfd95bb9868fba8be25a07b65..b360e0532ce1b9b729be1cc2398cb2b46620901c 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -762,11 +762,12 @@ def install(installer): + options.subject_base, options.ca_subject, 1101, 1100, None) + + krb = krbinstance.KrbInstance(fstore) +- krb.create_instance(realm_name, host_name, domain_name, +- dm_password, master_password, +- setup_pkinit=not options.no_pkinit, +- pkcs12_info=pkinit_pkcs12_info, +- subject_base=options.subject_base) ++ if not options.external_cert_files: ++ krb.create_instance(realm_name, host_name, domain_name, ++ dm_password, master_password, ++ setup_pkinit=not options.no_pkinit, ++ pkcs12_info=pkinit_pkcs12_info, ++ subject_base=options.subject_base) + + if setup_ca: + if not options.external_cert_files and options.external_ca: +-- +2.12.2 + diff --git a/SOURCES/0088-ext.-CA-correctly-write-the-cert-chain.patch b/SOURCES/0088-ext.-CA-correctly-write-the-cert-chain.patch new file mode 100644 index 0000000..92d8a1c --- /dev/null +++ b/SOURCES/0088-ext.-CA-correctly-write-the-cert-chain.patch @@ -0,0 +1,35 @@ +From 3ee73ed6d739a9d89dadd78f37388e8cfdba143b Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 18 Apr 2017 17:17:48 +0200 +Subject: [PATCH] ext. CA: correctly write the cert chain + +The cert file would have been rewritten all over again with +any of the cert in the CA cert chain without this patch. + +https://pagure.io/freeipa/issue/6872 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/cainstance.py | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index ac5d9e2fc633c5ad732670245b72bee0f03268a6..e2070e39f7e162fcff6e1f8cca41218e440b5f58 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -783,9 +783,10 @@ class CAInstance(DogtagInstance): + certlist = x509.pkcs7_to_pems(data, x509.DER) + + # We have all the certificates in certlist, write them to a PEM file +- for cert in certlist: +- with open(paths.IPA_CA_CRT, 'w') as ipaca_pem: ++ with open(paths.IPA_CA_CRT, 'w') as ipaca_pem: ++ for cert in certlist: + ipaca_pem.write(cert) ++ ipaca_pem.write('\n') + + def __request_ra_certificate(self): + # create a temp file storing the pwd +-- +2.12.2 + diff --git a/SOURCES/0089-Fix-RA-cert-import-during-DL0-replication.patch b/SOURCES/0089-Fix-RA-cert-import-during-DL0-replication.patch new file mode 100644 index 0000000..0804309 --- /dev/null +++ b/SOURCES/0089-Fix-RA-cert-import-during-DL0-replication.patch @@ -0,0 +1,122 @@ +From 899f9b980afba02cfdf80155905354a7371ad871 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 19 Apr 2017 11:42:40 +0200 +Subject: [PATCH] Fix RA cert import during DL0 replication + +Previous versions of FreeIPA add password to the ra.p12 file +contained in the password-protected tarball. This was forgotten +about in the recent changes and fixed now. + +https://pagure.io/freeipa/issue/6878 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/cainstance.py | 43 +++++++++++++++++++------------- + ipaserver/install/ipa_replica_prepare.py | 17 +++++++------ + 2 files changed, 35 insertions(+), 25 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index e2070e39f7e162fcff6e1f8cca41218e440b5f58..640d2884130dd152012e50dde45514f5ca26a523 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -338,6 +338,7 @@ class CAInstance(DogtagInstance): + self.clone = True + self.master_host = master_host + self.master_replication_port = master_replication_port ++ self.ra_p12 = ra_p12 + + self.subject_base = \ + subject_base or installutils.default_subject_base(self.realm) +@@ -400,7 +401,7 @@ class CAInstance(DogtagInstance): + self.step("Importing RA key", self.__import_ra_key) + else: + self.step("importing RA certificate from PKCS #12 file", +- lambda: self.import_ra_cert(ra_p12)) ++ self.__import_ra_cert) + + if not ra_only: + self.step("setting up signing cert profile", self.__setup_sign_profile) +@@ -673,28 +674,36 @@ class CAInstance(DogtagInstance): + 'NSS_ENABLE_PKIX_VERIFY', '1', + quotes=False, separator='=') + +- def import_ra_cert(self, rafile): ++ def __import_ra_cert(self): ++ """ ++ Helper method for IPA domain level 0 replica install ++ """ ++ self.import_ra_cert(self.ra_p12, self.dm_password) ++ ++ def import_ra_cert(self, rafile, password=''): + """ + Cloned RAs will use the same RA agent cert as the master so we + need to import from a PKCS#12 file. + + Used when setting up replication + """ +- # get the private key from the file +- ipautil.run([paths.OPENSSL, +- "pkcs12", +- "-in", rafile, +- "-nocerts", "-nodes", +- "-out", paths.RA_AGENT_KEY, +- "-passin", "pass:"]) +- +- # get the certificate from the pkcs12 file +- ipautil.run([paths.OPENSSL, +- "pkcs12", +- "-in", rafile, +- "-clcerts", "-nokeys", +- "-out", paths.RA_AGENT_PEM, +- "-passin", "pass:"]) ++ with ipautil.write_tmp_file(password) as f: ++ pwdarg = 'file:{file}'.format(file=f.name) ++ # get the private key from the file ++ ipautil.run([paths.OPENSSL, ++ "pkcs12", ++ "-in", rafile, ++ "-nocerts", "-nodes", ++ "-out", paths.RA_AGENT_KEY, ++ "-passin", pwdarg]) ++ ++ # get the certificate from the pkcs12 file ++ ipautil.run([paths.OPENSSL, ++ "pkcs12", ++ "-in", rafile, ++ "-clcerts", "-nokeys", ++ "-out", paths.RA_AGENT_PEM, ++ "-passin", pwdarg]) + self.__set_ra_cert_perms() + + self.configure_agent_renewal() +diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py +index 95c3818a9fc34c937f8b418e91a1bfc28352b02e..d4456dd796167c3717be013d2378413519a3b366 100644 +--- a/ipaserver/install/ipa_replica_prepare.py ++++ b/ipaserver/install/ipa_replica_prepare.py +@@ -571,14 +571,15 @@ class ReplicaPrepare(admintool.AdminTool): + def export_ra_pkcs12(self): + if (os.path.exists(paths.RA_AGENT_PEM) and + os.path.exists(paths.RA_AGENT_KEY)): +- ipautil.run([ +- paths.OPENSSL, +- "pkcs12", "-export", +- "-inkey", paths.RA_AGENT_KEY, +- "-in", paths.RA_AGENT_PEM, +- "-out", os.path.join(self.dir, "ra.p12"), +- "-passout", "pass:" +- ]) ++ with ipautil.write_tmp_file(self.dirman_password) as f: ++ ipautil.run([ ++ paths.OPENSSL, ++ "pkcs12", "-export", ++ "-inkey", paths.RA_AGENT_KEY, ++ "-in", paths.RA_AGENT_PEM, ++ "-out", os.path.join(self.dir, "ra.p12"), ++ "-passout", "file:{pwfile}".format(pwfile=f.name) ++ ]) + + def update_pki_admin_password(self): + dn = DN('uid=admin', 'ou=people', 'o=ipaca') +-- +2.12.2 + diff --git a/SOURCES/0090-configure-fix-AC_CHECK_LIB-usage.patch b/SOURCES/0090-configure-fix-AC_CHECK_LIB-usage.patch new file mode 100644 index 0000000..edc2836 --- /dev/null +++ b/SOURCES/0090-configure-fix-AC_CHECK_LIB-usage.patch @@ -0,0 +1,64 @@ +From 03750b1c1ba8ed670691e4e464d110b9329d85be Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 5 Apr 2017 10:24:17 +0000 +Subject: [PATCH] configure: fix AC_CHECK_LIB usage + +Replace empty string with a single space in the third argument of +`AC_CHECK_LIB` (`action-if-found`) where applicable. + +Empty string in the argument causes `AC_CHECK_LIB` to use the default +action when a library is found which includes adding the library to `LIBS`, +which specifies libraries to be linked in every binary and library in the +project. + +This fixes libkrad, liblber, libldap_r and libsss_nss_idmap being linked to +every binary and library in IPA, even where unused. + +https://pagure.io/freeipa/issue/6846 + +Reviewed-By: Stanislav Laznicka +--- + configure.ac | 4 ++-- + server.m4 | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/configure.ac b/configure.ac +index ded1d71fd079a5f6947ef0627fb699783c8cc109..e31a9849c4c0556833f5cd47104381d006c96eef 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -86,8 +86,8 @@ dnl --------------------------------------------------------------------------- + + SAVE_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$NSPR_CFLAGS $NSS_CFLAGS" +-AC_CHECK_LIB([ldap_r], [ldap_search], [], AC_MSG_ERROR([libldap_r not found])) +-AC_CHECK_LIB([lber], [ber_peek_tag], [], AC_MSG_ERROR([liblber not found])) ++AC_CHECK_LIB([ldap_r], [ldap_search], [ ], AC_MSG_ERROR([libldap_r not found])) ++AC_CHECK_LIB([lber], [ber_peek_tag], [ ], AC_MSG_ERROR([liblber not found])) + LDAP_LIBS="-lldap_r -llber" + LDAP_CFLAGS="" + AC_SUBST(LDAP_LIBS) +diff --git a/server.m4 b/server.m4 +index 346d73e906c5d0499e46fcc4da070007b2ff5973..aa784e096299d5a6cc599a0d6a0652168f9bafbc 100644 +--- a/server.m4 ++++ b/server.m4 +@@ -31,7 +31,7 @@ PKG_CHECK_MODULES([SSSIDMAP], [sss_idmap]) + PKG_CHECK_MODULES([SSSNSSIDMAP], [sss_nss_idmap >= 1.15.2]) + AC_CHECK_LIB([sss_nss_idmap], + [sss_nss_getlistbycert], +- [], ++ [ ], + [AC_MSG_ERROR([Required sss_nss_getlistbycert symbol in sss_nss_idmap not found])], + []) + +@@ -48,7 +48,7 @@ dnl - Check for KRB5 krad + dnl --------------------------------------------------------------------------- + + AC_CHECK_HEADER(krad.h, [], [AC_MSG_ERROR([krad.h not found])]) +-AC_CHECK_LIB(krad, main, [], [AC_MSG_ERROR([libkrad not found])]) ++AC_CHECK_LIB(krad, main, [ ], [AC_MSG_ERROR([libkrad not found])]) + KRAD_LIBS="-lkrad" + krb5rundir="${localstatedir}/run/krb5kdc" + AC_SUBST(KRAD_LIBS) +-- +2.12.2 + diff --git a/SOURCES/0091-Fix-CAInstance.import_ra_cert-for-empty-passwords.patch b/SOURCES/0091-Fix-CAInstance.import_ra_cert-for-empty-passwords.patch new file mode 100644 index 0000000..cc63d38 --- /dev/null +++ b/SOURCES/0091-Fix-CAInstance.import_ra_cert-for-empty-passwords.patch @@ -0,0 +1,30 @@ +From 61b5a76bcd856d679f05c5f5f12f770cc6826783 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Thu, 20 Apr 2017 10:09:05 +0200 +Subject: [PATCH] Fix CAInstance.import_ra_cert for empty passwords + +OpenSSL can't cope with empty files, add a newline after each password + +https://pagure.io/freeipa/issue/6878 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/cainstance.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 640d2884130dd152012e50dde45514f5ca26a523..0672bccf79d7cc6133fdb20f0854366306bfc2e0 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -687,7 +687,7 @@ class CAInstance(DogtagInstance): + + Used when setting up replication + """ +- with ipautil.write_tmp_file(password) as f: ++ with ipautil.write_tmp_file(password + '\n') as f: + pwdarg = 'file:{file}'.format(file=f.name) + # get the private key from the file + ipautil.run([paths.OPENSSL, +-- +2.12.2 + diff --git a/SOURCES/0092-upgrade-adtrust-update_tdo_gidnumber-plugin-must-che.patch b/SOURCES/0092-upgrade-adtrust-update_tdo_gidnumber-plugin-must-che.patch new file mode 100644 index 0000000..8d8b44a --- /dev/null +++ b/SOURCES/0092-upgrade-adtrust-update_tdo_gidnumber-plugin-must-che.patch @@ -0,0 +1,37 @@ +From f50331d2f9f34ae17a3d5323e74982ca87eba12e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 20 Apr 2017 16:31:53 +0200 +Subject: [PATCH] upgrade: adtrust update_tdo_gidnumber plugin must check if + adtrust is installed + +During upgrade, the plugin update_tdo_gidnumber is launched in order to +add a gidnumber to the Trusted Domain Object. +This plugin should not be run when ad trust is not installed, otherwise an +error message is displayed. + +https://pagure.io/freeipa/issue/6881 + +Reviewed-By: Alexander Bokovoy +--- + ipaserver/install/plugins/adtrust.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py +index 075f197780edc2aadf42fa82b71e9e2b29e66ea9..a72af00635649ddf54640738c2f28cb09c7e91bb 100644 +--- a/ipaserver/install/plugins/adtrust.py ++++ b/ipaserver/install/plugins/adtrust.py +@@ -329,6 +329,11 @@ class update_tdo_gidnumber(Updater): + def execute(self, **options): + ldap = self.api.Backend.ldap2 + ++ # First, see if trusts are enabled on the server ++ if not self.api.Command.adtrust_is_enabled()['result']: ++ self.log.debug('AD Trusts are not enabled on this server') ++ return False, [] ++ + # Read the gidnumber of the fallback group + dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME), + self.api.env.container_group, +-- +2.12.2 + diff --git a/SOURCES/0093-compat-manage-behave-the-same-for-all-users.patch b/SOURCES/0093-compat-manage-behave-the-same-for-all-users.patch new file mode 100644 index 0000000..0729953 --- /dev/null +++ b/SOURCES/0093-compat-manage-behave-the-same-for-all-users.patch @@ -0,0 +1,34 @@ +From 03b605a70c8f5a6def7a572ceb302051934c78e7 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 21 Apr 2017 09:32:34 +0200 +Subject: [PATCH] compat-manage: behave the same for all users + +Due to LDAP connection refactoring, compat-manage would have behaved +differently for root and for other users even though it requires +the directory manager password. This is caused by it trying to do +external bind when it does not have the DIRMAN password which was +previously not supplied. + +https://pagure.io/freeipa/issue/6821 + +Reviewed-By: Martin Basti +--- + install/tools/ipa-compat-manage | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/install/tools/ipa-compat-manage b/install/tools/ipa-compat-manage +index a29a92fabd653bb983778420cca0983048ee39ef..6dd259d2aad870e575093d6732157de743502ac2 100755 +--- a/install/tools/ipa-compat-manage ++++ b/install/tools/ipa-compat-manage +@@ -105,7 +105,7 @@ def main(): + debug=options.debug, + confdir=paths.ETC_IPA) + api.finalize() +- api.Backend.ldap2.connect() ++ api.Backend.ldap2.connect(bind_pw=dirman_password) + + if args[0] == "status": + entry = None +-- +2.12.2 + diff --git a/SOURCES/0094-Move-the-compat-plugin-setup-at-the-end-of-install.patch b/SOURCES/0094-Move-the-compat-plugin-setup-at-the-end-of-install.patch new file mode 100644 index 0000000..2a1fcb9 --- /dev/null +++ b/SOURCES/0094-Move-the-compat-plugin-setup-at-the-end-of-install.patch @@ -0,0 +1,317 @@ +From f0bd45fb0c1071006887dc10abac233d2756d951 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Thu, 13 Apr 2017 09:15:47 +0200 +Subject: [PATCH] Move the compat plugin setup at the end of install + +The compat plugin was causing deadlocks with the topology plugin. Move +its setup at the end of the installation and remove the +cn=topology,cn=ipa,cn=etc subtree from its scope. + +https://pagure.io/freeipa/issue/6821 + +Reviewed-By: Martin Basti +--- + install/share/Makefile.am | 1 - + install/updates/10-schema_compat.update | 93 --------------------- + .../80-schema_compat.update} | 96 +++++++++++++++++++++- + install/updates/Makefile.am | 2 +- + ipaplatform/base/paths.py | 3 +- + ipaserver/install/dsinstance.py | 9 -- + 6 files changed, 98 insertions(+), 106 deletions(-) + delete mode 100644 install/updates/10-schema_compat.update + rename install/{share/schema_compat.uldif => updates/80-schema_compat.update} (55%) + +diff --git a/install/share/Makefile.am b/install/share/Makefile.am +index 9e539a3f30c2979de26575ba66bbb23fecd03a88..b27861da37153d77d693ce6e46340525bbd50173 100644 +--- a/install/share/Makefile.am ++++ b/install/share/Makefile.am +@@ -65,7 +65,6 @@ dist_app_DATA = \ + opendnssec_conf.template \ + opendnssec_kasp.template \ + unique-attributes.ldif \ +- schema_compat.uldif \ + ldapi.ldif \ + wsgi.py \ + repoint-managed-entries.ldif \ +diff --git a/install/updates/10-schema_compat.update b/install/updates/10-schema_compat.update +deleted file mode 100644 +index fbe8703407aacd75baf160630c20835a1b4ddc65..0000000000000000000000000000000000000000 +--- a/install/updates/10-schema_compat.update ++++ /dev/null +@@ -1,93 +0,0 @@ +-dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config +-only:schema-compat-entry-rdn:%ifeq("ipaEnabledFlag", "FALSE", "DISABLED", "cn=%{cn}") +-add:schema-compat-entry-attribute: sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}") +-add:schema-compat-entry-attribute: sudoRunAsUser=%%%{ipaSudoRunAsExtUserGroup} +-# Fix for #4324 (regression of #1309) +-remove:schema-compat-entry-attribute:sudoRunAsGroup=%deref("ipaSudoRunAs","cn") +-remove:schema-compat-entry-attribute:sudoRunAsUser=%{ipaSudoRunAsExtUser} +-remove:schema-compat-entry-attribute:sudoRunAsUser=%%%{ipaSudoRunAsExtUserGroup} +-remove:schema-compat-entry-attribute:sudoRunAsUser=%deref("ipaSudoRunAs","uid") +-remove:schema-compat-entry-attribute:sudoRunAsGroup=%{ipaSudoRunAsExtGroup} +-remove:schema-compat-entry-attribute:sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn") +- +-# We need to add the value in a separate transaction +-dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-entry-attribute: sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn") +-add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%{ipaSudoRunAsExtUser}") +-add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%%%{ipaSudoRunAsExtUserGroup}") +-add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%deref_f(\"ipaSudoRunAs\",\"(objectclass=posixAccount)\",\"uid\")") +-add: schema-compat-entry-attribute: sudoRunAsGroup=%ifeq("ipaSudoRunAsGroupCategory","all","ALL","%{ipaSudoRunAsExtGroup}") +-add: schema-compat-entry-attribute: sudoRunAsGroup=%ifeq("ipaSudoRunAsGroupCategory","all","ALL","%deref_f(\"ipaSudoRunAsGroup\",\"(objectclass=posixGroup)\",\"cn\")") +-remove: schema-compat-ignore-subtree: cn=changelog +-remove: schema-compat-ignore-subtree: o=ipaca +-add: schema-compat-restrict-subtree: $SUFFIX +-add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX +- +-# Change padding for host and userCategory so the pad returns the same value +-# as the original, '' or -. +-dn: cn=ng,cn=Schema Compatibility,cn=plugins,cn=config +-replace: schema-compat-entry-attribute:nisNetgroupTriple=(%link("%ifeq(\"hostCategory\",\"all\",\"\",\"%collect(\\\"%{externalHost}\\\",\\\"%deref(\\\\\\\"memberHost\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberHost\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\")\")","-",",","%ifeq(\"userCategory\",\"all\",\"\",\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\")","-"),%{nisDomainName:-})::nisNetgroupTriple=(%link("%ifeq(\"hostCategory\",\"all\",\"\",\"%collect(\\\"%{externalHost}\\\",\\\"%deref(\\\\\\\"memberHost\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberHost\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\")\")","%ifeq(\"hostCategory\",\"all\",\"\",\"-\")",",","%ifeq(\"userCategory\",\"all\",\"\",\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\")","%ifeq(\"userCategory\",\"all\",\"\",\"-\")"),%{nisDomainName:-}) +-remove: schema-compat-ignore-subtree: cn=changelog +-remove: schema-compat-ignore-subtree: o=ipaca +-add: schema-compat-restrict-subtree: $SUFFIX +-add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX +- +-dn: cn=computers, cn=Schema Compatibility, cn=plugins, cn=config +-default:objectClass: top +-default:objectClass: extensibleObject +-default:cn: computers +-default:schema-compat-container-group: cn=compat, $SUFFIX +-default:schema-compat-container-rdn: cn=computers +-default:schema-compat-search-base: cn=computers, cn=accounts, $SUFFIX +-default:schema-compat-search-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost)) +-default:schema-compat-entry-rdn: cn=%first("%{fqdn}") +-default:schema-compat-entry-attribute: objectclass=device +-default:schema-compat-entry-attribute: objectclass=ieee802Device +-default:schema-compat-entry-attribute: cn=%{fqdn} +-default:schema-compat-entry-attribute: macAddress=%{macAddress} +-remove: schema-compat-ignore-subtree: cn=changelog +-remove: schema-compat-ignore-subtree: o=ipaca +-add: schema-compat-restrict-subtree: $SUFFIX +-add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX +- +-dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config +-add:schema-compat-entry-attribute: sudoOrder=%{sudoOrder} +- +-dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config +-remove: schema-compat-ignore-subtree: cn=changelog +-remove: schema-compat-ignore-subtree: o=ipaca +-add: schema-compat-restrict-subtree: $SUFFIX +-add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX +- +-dn: cn=groups,cn=Schema Compatibility,cn=plugins,cn=config +-remove: schema-compat-ignore-subtree: cn=changelog +-remove: schema-compat-ignore-subtree: o=ipaca +-add: schema-compat-restrict-subtree: $SUFFIX +-add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config +-add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX +- +-dn: cn=Schema Compatibility,cn=plugins,cn=config +-# We need to run schema-compat pre-bind callback before +-# other IPA pre-bind callbacks to make sure bind DN is +-# rewritten to the original entry if needed +-add:nsslapd-pluginprecedence: 40 +- +-dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config +-add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","objectclass=ipaOverrideTarget","") +-add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","ipaanchoruuid=:IPA:$DOMAIN:%{ipauniqueid}","") +-add:schema-compat-entry-attribute: ipaanchoruuid=%{ipaanchoruuid} +-add:schema-compat-entry-attribute: %ifeq("ipaanchoruuid","%{ipaanchoruuid}","objectclass=ipaOverrideTarget","") +- +-dn: cn=groups,cn=Schema Compatibility,cn=plugins,cn=config +-add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","objectclass=ipaOverrideTarget","") +-add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","ipaanchoruuid=:IPA:$DOMAIN:%{ipauniqueid}","") +-add:schema-compat-entry-attribute: ipaanchoruuid=%{ipaanchoruuid} +-add:schema-compat-entry-attribute: %ifeq("ipaanchoruuid","%{ipaanchoruuid}","objectclass=ipaOverrideTarget","") +- +-dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config +-add:schema-compat-entry-attribute: uid=%{uid} +-replace:schema-compat-entry-rdn: uid=%{uid}::uid=%first("%{uid}") +diff --git a/install/share/schema_compat.uldif b/install/updates/80-schema_compat.update +similarity index 55% +rename from install/share/schema_compat.uldif +rename to install/updates/80-schema_compat.update +index 66f8ea1c31bc534b3ee134c6df6132f4318c81fc..06cbcab8ad809d95a907c161044ff91df827ebf3 100644 +--- a/install/share/schema_compat.uldif ++++ b/install/updates/80-schema_compat.update +@@ -1,5 +1,6 @@ + # +-# Enable the Schema Compatibility plugin provided by slapi-nis. ++# Setup the Schema Compatibility plugin provided by slapi-nis. ++# This should be done after all other updates have been applied + # + # http://slapi-nis.fedorahosted.org/ + # +@@ -126,3 +127,96 @@ default:schema-compat-entry-attribute: macAddress=%{macAddress} + dn: oid=2.16.840.1.113730.3.4.9,cn=features,cn=config + only:aci: (targetattr !="aci")(version 3.0; acl "VLV Request Control"; allow (read, search, compare, proxy) userdn = "ldap:///anyone"; ) + ++dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config ++only:schema-compat-entry-rdn:%ifeq("ipaEnabledFlag", "FALSE", "DISABLED", "cn=%{cn}") ++add:schema-compat-entry-attribute: sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}") ++add:schema-compat-entry-attribute: sudoRunAsUser=%%%{ipaSudoRunAsExtUserGroup} ++# Fix for #4324 (regression of #1309) ++remove:schema-compat-entry-attribute:sudoRunAsGroup=%deref("ipaSudoRunAs","cn") ++remove:schema-compat-entry-attribute:sudoRunAsUser=%{ipaSudoRunAsExtUser} ++remove:schema-compat-entry-attribute:sudoRunAsUser=%%%{ipaSudoRunAsExtUserGroup} ++remove:schema-compat-entry-attribute:sudoRunAsUser=%deref("ipaSudoRunAs","uid") ++remove:schema-compat-entry-attribute:sudoRunAsGroup=%{ipaSudoRunAsExtGroup} ++remove:schema-compat-entry-attribute:sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn") ++ ++# We need to add the value in a separate transaction ++dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-entry-attribute: sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn") ++add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%{ipaSudoRunAsExtUser}") ++add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%%%{ipaSudoRunAsExtUserGroup}") ++add: schema-compat-entry-attribute: sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%deref_f(\"ipaSudoRunAs\",\"(objectclass=posixAccount)\",\"uid\")") ++add: schema-compat-entry-attribute: sudoRunAsGroup=%ifeq("ipaSudoRunAsGroupCategory","all","ALL","%{ipaSudoRunAsExtGroup}") ++add: schema-compat-entry-attribute: sudoRunAsGroup=%ifeq("ipaSudoRunAsGroupCategory","all","ALL","%deref_f(\"ipaSudoRunAsGroup\",\"(objectclass=posixGroup)\",\"cn\")") ++remove: schema-compat-ignore-subtree: cn=changelog ++remove: schema-compat-ignore-subtree: o=ipaca ++add: schema-compat-restrict-subtree: $SUFFIX ++add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++ ++# Change padding for host and userCategory so the pad returns the same value ++# as the original, '' or -. ++dn: cn=ng,cn=Schema Compatibility,cn=plugins,cn=config ++replace: schema-compat-entry-attribute:nisNetgroupTriple=(%link("%ifeq(\"hostCategory\",\"all\",\"\",\"%collect(\\\"%{externalHost}\\\",\\\"%deref(\\\\\\\"memberHost\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberHost\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\")\")","-",",","%ifeq(\"userCategory\",\"all\",\"\",\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\")","-"),%{nisDomainName:-})::nisNetgroupTriple=(%link("%ifeq(\"hostCategory\",\"all\",\"\",\"%collect(\\\"%{externalHost}\\\",\\\"%deref(\\\\\\\"memberHost\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberHost\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\")\")","%ifeq(\"hostCategory\",\"all\",\"\",\"-\")",",","%ifeq(\"userCategory\",\"all\",\"\",\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\")","%ifeq(\"userCategory\",\"all\",\"\",\"-\")"),%{nisDomainName:-}) ++remove: schema-compat-ignore-subtree: cn=changelog ++remove: schema-compat-ignore-subtree: o=ipaca ++add: schema-compat-restrict-subtree: $SUFFIX ++add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++ ++dn: cn=computers, cn=Schema Compatibility, cn=plugins, cn=config ++default:objectClass: top ++default:objectClass: extensibleObject ++default:cn: computers ++default:schema-compat-container-group: cn=compat, $SUFFIX ++default:schema-compat-container-rdn: cn=computers ++default:schema-compat-search-base: cn=computers, cn=accounts, $SUFFIX ++default:schema-compat-search-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost)) ++default:schema-compat-entry-rdn: cn=%first("%{fqdn}") ++default:schema-compat-entry-attribute: objectclass=device ++default:schema-compat-entry-attribute: objectclass=ieee802Device ++default:schema-compat-entry-attribute: cn=%{fqdn} ++default:schema-compat-entry-attribute: macAddress=%{macAddress} ++remove: schema-compat-ignore-subtree: cn=changelog ++remove: schema-compat-ignore-subtree: o=ipaca ++add: schema-compat-restrict-subtree: $SUFFIX ++add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++ ++dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config ++add:schema-compat-entry-attribute: sudoOrder=%{sudoOrder} ++ ++dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config ++remove: schema-compat-ignore-subtree: cn=changelog ++remove: schema-compat-ignore-subtree: o=ipaca ++add: schema-compat-restrict-subtree: $SUFFIX ++add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++ ++dn: cn=groups,cn=Schema Compatibility,cn=plugins,cn=config ++remove: schema-compat-ignore-subtree: cn=changelog ++remove: schema-compat-ignore-subtree: o=ipaca ++add: schema-compat-restrict-subtree: $SUFFIX ++add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config ++add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++ ++dn: cn=Schema Compatibility,cn=plugins,cn=config ++# We need to run schema-compat pre-bind callback before ++# other IPA pre-bind callbacks to make sure bind DN is ++# rewritten to the original entry if needed ++add:nsslapd-pluginprecedence: 40 ++ ++dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config ++add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","objectclass=ipaOverrideTarget","") ++add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","ipaanchoruuid=:IPA:$DOMAIN:%{ipauniqueid}","") ++add:schema-compat-entry-attribute: ipaanchoruuid=%{ipaanchoruuid} ++add:schema-compat-entry-attribute: %ifeq("ipaanchoruuid","%{ipaanchoruuid}","objectclass=ipaOverrideTarget","") ++ ++dn: cn=groups,cn=Schema Compatibility,cn=plugins,cn=config ++add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","objectclass=ipaOverrideTarget","") ++add:schema-compat-entry-attribute: %ifeq("ipauniqueid","%{ipauniqueid}","ipaanchoruuid=:IPA:$DOMAIN:%{ipauniqueid}","") ++add:schema-compat-entry-attribute: ipaanchoruuid=%{ipaanchoruuid} ++add:schema-compat-entry-attribute: %ifeq("ipaanchoruuid","%{ipaanchoruuid}","objectclass=ipaOverrideTarget","") ++ ++dn: cn=users,cn=Schema Compatibility,cn=plugins,cn=config ++add:schema-compat-entry-attribute: uid=%{uid} ++replace:schema-compat-entry-rdn: uid=%{uid}::uid=%first("%{uid}") +diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am +index 0ff0edb93abf4c4656b7504bd9ce8f774918fc2d..e18d01127b592a6c7941729d6160d10fb2d3e76c 100644 +--- a/install/updates/Makefile.am ++++ b/install/updates/Makefile.am +@@ -9,7 +9,6 @@ app_DATA = \ + 10-selinuxusermap.update \ + 10-rootdse.update \ + 10-uniqueness.update \ +- 10-schema_compat.update \ + 19-managed-entries.update \ + 20-aci.update \ + 20-dna.update \ +@@ -62,6 +61,7 @@ app_DATA = \ + 73-custodia.update \ + 73-winsync.update \ + 73-certmap.update \ ++ 80-schema_compat.update \ + 90-post_upgrade_plugins.update \ + $(NULL) + +diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py +index 9cf160fac483157b508dedac7a5fc26cb12c63a4..dbdd71ed0b4d69c1101db4aeb7d93152ab8aa730 100644 +--- a/ipaplatform/base/paths.py ++++ b/ipaplatform/base/paths.py +@@ -236,7 +236,8 @@ class BasePathNamespace(object): + HTML_KRBREALM_CON = "/usr/share/ipa/html/krbrealm.con" + NIS_ULDIF = "/usr/share/ipa/nis.uldif" + NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif" +- SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/schema_compat.uldif" ++ SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/updates/91-schema_compat.update" ++ SCHEMA_COMPAT_POST_ULDIF = "/usr/share/ipa/schema_compat_post.uldif" + IPA_JS_PLUGINS_DIR = "/usr/share/ipa/ui/js/plugins" + UPDATES_DIR = "/usr/share/ipa/updates/" + DICT_WORDS = "/usr/share/dict/words" +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 99a1781ca4475805e9bf3b2bac3f26b5fb107a43..403fe8489fdd9e0dbf40dd4df3794b51185d45b9 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -38,7 +38,6 @@ from ipapython import dogtag + from ipaserver.install import service + from ipaserver.install import installutils + from ipaserver.install import certs +-from ipaserver.install import ldapupdate + from ipaserver.install import replication + from ipaserver.install import sysupgrade + from ipaserver.install import upgradeinstance +@@ -281,8 +280,6 @@ class DsInstance(service.Service): + self.step("configuring Posix uid/gid generation", + self.__config_uidgid_gen) + self.step("adding replication acis", self.__add_replication_acis) +- self.step("enabling compatibility plugin", +- self.__enable_compat_plugin) + self.step("activating sidgen plugin", self._add_sidgen_plugin) + self.step("activating extdom plugin", self._add_extdom_plugin) + self.step("tuning directory server", self.__tuning) +@@ -706,12 +703,6 @@ class DsInstance(service.Service): + def __add_winsync_module(self): + self._ldap_mod("ipa-winsync-conf.ldif") + +- def __enable_compat_plugin(self): +- ld = ldapupdate.LDAPUpdate(dm_password=self.dm_password, sub_dict=self.sub_dict) +- rv = ld.update([paths.SCHEMA_COMPAT_ULDIF]) +- if not rv: +- raise RuntimeError("Enabling compatibility plugin failed") +- + def __config_version_module(self): + self._ldap_mod("version-conf.ldif") + +-- +2.12.2 + diff --git a/SOURCES/0095-compat-ignore-cn-topology-cn-ipa-cn-etc-subtree.patch b/SOURCES/0095-compat-ignore-cn-topology-cn-ipa-cn-etc-subtree.patch new file mode 100644 index 0000000..43b196f --- /dev/null +++ b/SOURCES/0095-compat-ignore-cn-topology-cn-ipa-cn-etc-subtree.patch @@ -0,0 +1,62 @@ +From 08c5150d76971ab17eb02ceef0e65e993b7732ff Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 21 Apr 2017 09:39:56 +0200 +Subject: [PATCH] compat: ignore cn=topology,cn=ipa,cn=etc subtree + +The entries in cn=topology,cn=ipa,cn=etc should not be taken in +account for the compat plugin. + +https://pagure.io/freeipa/issue/6821 + +Reviewed-By: Martin Basti +--- + install/updates/80-schema_compat.update | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/install/updates/80-schema_compat.update b/install/updates/80-schema_compat.update +index 06cbcab8ad809d95a907c161044ff91df827ebf3..7483518134aad47195ab136626acb5130c0d536d 100644 +--- a/install/updates/80-schema_compat.update ++++ b/install/updates/80-schema_compat.update +@@ -152,6 +152,7 @@ remove: schema-compat-ignore-subtree: o=ipaca + add: schema-compat-restrict-subtree: $SUFFIX + add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config + add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++add: schema-compat-ignore-subtree: cn=topology,cn=ipa,cn=etc,$SUFFIX + + # Change padding for host and userCategory so the pad returns the same value + # as the original, '' or -. +@@ -162,6 +163,7 @@ remove: schema-compat-ignore-subtree: o=ipaca + add: schema-compat-restrict-subtree: $SUFFIX + add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config + add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++add: schema-compat-ignore-subtree: cn=topology,cn=ipa,cn=etc,$SUFFIX + + dn: cn=computers, cn=Schema Compatibility, cn=plugins, cn=config + default:objectClass: top +@@ -181,6 +183,7 @@ remove: schema-compat-ignore-subtree: o=ipaca + add: schema-compat-restrict-subtree: $SUFFIX + add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config + add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++add: schema-compat-ignore-subtree: cn=topology,cn=ipa,cn=etc,$SUFFIX + + dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config + add:schema-compat-entry-attribute: sudoOrder=%{sudoOrder} +@@ -191,6 +194,7 @@ remove: schema-compat-ignore-subtree: o=ipaca + add: schema-compat-restrict-subtree: $SUFFIX + add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config + add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++add: schema-compat-ignore-subtree: cn=topology,cn=ipa,cn=etc,$SUFFIX + + dn: cn=groups,cn=Schema Compatibility,cn=plugins,cn=config + remove: schema-compat-ignore-subtree: cn=changelog +@@ -198,6 +202,7 @@ remove: schema-compat-ignore-subtree: o=ipaca + add: schema-compat-restrict-subtree: $SUFFIX + add: schema-compat-restrict-subtree: cn=Schema Compatibility,cn=plugins,cn=config + add: schema-compat-ignore-subtree: cn=dna,cn=ipa,cn=etc,$SUFFIX ++add: schema-compat-ignore-subtree: cn=topology,cn=ipa,cn=etc,$SUFFIX + + dn: cn=Schema Compatibility,cn=plugins,cn=config + # We need to run schema-compat pre-bind callback before +-- +2.12.2 + diff --git a/SOURCES/0096-spec-file-bump-krb5-Requires-for-certauth-fixes.patch b/SOURCES/0096-spec-file-bump-krb5-Requires-for-certauth-fixes.patch new file mode 100644 index 0000000..2f0c64d --- /dev/null +++ b/SOURCES/0096-spec-file-bump-krb5-Requires-for-certauth-fixes.patch @@ -0,0 +1,68 @@ +From c8a4e5d946d8261748d632361a016950ab42f4ba Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 25 Apr 2017 12:35:34 +0000 +Subject: [PATCH] spec file: bump krb5 Requires for certauth fixes + +Bump krb5-* Requires to the version which includes the final version of +certauth support. + +https://pagure.io/freeipa/issue/4905 + +Reviewed-By: Stanislav Laznicka +--- + freeipa.spec.in | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 978ebb184f7d051b303940560f44c7a094b071a1..3b7410b6bda3afc877d928b4df21529ae2faf0aa 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -36,11 +36,15 @@ + + %global alt_name ipa + %if 0%{?rhel} ++# 1.15.1-7: certauth (http://krbdev.mit.edu/rt/Ticket/Display.html?id=8561) ++%global krb5_version 1.15.1-4 + # Require 4.6.0-4 which brings RC4 for FIPS + trust fixes to priv. separation + %global samba_version 4.6.0-4 + %global selinux_policy_version 3.12.1-153 + %global slapi_nis_version 0.56.0-4 + %else ++# 1.15.1-7: certauth (http://krbdev.mit.edu/rt/Ticket/Display.html?id=8561) ++%global krb5_version 1.15.1-7 + # Require 4.6.0-4 which brings RC4 for FIPS + trust fixes to priv. separation + %global samba_version 2:4.6.0-4 + %global selinux_policy_version 3.13.1-158.4 +@@ -82,8 +86,7 @@ BuildRequires: openldap-devel + %if 0%{?fedora} > 25 + BuildRequires: krb5-kdb-version = 6.1 + %endif +-# 1.15.1-3: certauth (http://krbdev.mit.edu/rt/Ticket/Display.html?id=8561) +-BuildRequires: krb5-devel >= 1.15.1-3 ++BuildRequires: krb5-devel >= %{krb5_version} + # 1.27.4: xmlrpc_curl_xportparms.gssapi_delegation + BuildRequires: xmlrpc-c-devel >= 1.27.4 + BuildRequires: popt-devel +@@ -263,8 +266,9 @@ Requires: 389-ds-base >= 1.3.5.14 + Requires: openldap-clients > 2.4.35-4 + Requires: nss >= 3.14.3-12.0 + Requires: nss-tools >= 3.14.3-12.0 ++Requires(post): krb5-server >= %{krb5_version} + Requires(post): krb5-server >= %{krb5_base_version}, krb5-server < %{krb5_base_version}.100 +-Requires: krb5-pkinit-openssl ++Requires: krb5-pkinit-openssl >= %{krb5_version} + Requires: cyrus-sasl-gssapi%{?_isa} + Requires: ntp + Requires: httpd >= 2.4.6-31 +@@ -481,7 +485,7 @@ Requires: python2-ipaclient = %{version}-%{release} + Requires: python-ldap + Requires: cyrus-sasl-gssapi%{?_isa} + Requires: ntp +-Requires: krb5-workstation ++Requires: krb5-workstation >= %{krb5_version} + Requires: authconfig + Requires: curl + # NIS domain name config: /usr/lib/systemd/system/*-domainname.service +-- +2.12.2 + diff --git a/SOURCES/0097-Hide-PKI-Client-database-password-in-log-file.patch b/SOURCES/0097-Hide-PKI-Client-database-password-in-log-file.patch new file mode 100644 index 0000000..bb0c79f --- /dev/null +++ b/SOURCES/0097-Hide-PKI-Client-database-password-in-log-file.patch @@ -0,0 +1,70 @@ +From 4106c7dcfc685580eeb0f2074872036cd5faaaae Mon Sep 17 00:00:00 2001 +From: Abhijeet Kasurde +Date: Thu, 27 Apr 2017 16:23:41 +0530 +Subject: [PATCH] Hide PKI Client database password in log file + +This fix masks PKI client database password from showing +in CA/KRA installer log file + +Fixes https://pagure.io/freeipa/issue/6904 + +Signed-off-by: Abhijeet Kasurde +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/cainstance.py | 5 ++++- + ipaserver/install/krainstance.py | 9 ++++++--- + 2 files changed, 10 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 0672bccf79d7cc6133fdb20f0854366306bfc2e0..84d60bfddc0fb968f31706e54e36557e9543846e 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -610,7 +610,10 @@ class CAInstance(DogtagInstance): + try: + DogtagInstance.spawn_instance( + self, cfg_file, +- nolog_list=(self.dm_password, self.admin_password, pki_pin) ++ nolog_list=(self.dm_password, ++ self.admin_password, ++ pki_pin, ++ self.tmp_agent_pwd) + ) + finally: + os.remove(cfg_file) +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index fc25ac72b0dc593f06a8b070b67b5d54a0ab8bce..c39d6874a9d685f91b5d30ea1954320b8ee0c1ed 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -150,6 +150,7 @@ class KRAInstance(DogtagInstance): + os.chown(cfg_file, pent.pw_uid, pent.pw_gid) + self.tmp_agent_db = tempfile.mkdtemp( + prefix="tmp-", dir=paths.VAR_LIB_IPA) ++ tmp_agent_pwd = ipautil.ipa_generate_password() + + # Create KRA configuration + config = ConfigParser() +@@ -173,8 +174,7 @@ class KRAInstance(DogtagInstance): + + # Client security database + config.set("KRA", "pki_client_database_dir", self.tmp_agent_db) +- config.set("KRA", "pki_client_database_password", +- ipautil.ipa_generate_password()) ++ config.set("KRA", "pki_client_database_password", tmp_agent_pwd) + config.set("KRA", "pki_client_database_purge", "True") + config.set("KRA", "pki_client_pkcs12_password", self.admin_password) + +@@ -283,7 +283,10 @@ class KRAInstance(DogtagInstance): + try: + DogtagInstance.spawn_instance( + self, cfg_file, +- nolog_list=(self.dm_password, self.admin_password, pki_pin) ++ nolog_list=(self.dm_password, ++ self.admin_password, ++ pki_pin, ++ tmp_agent_pwd) + ) + finally: + os.remove(p12_tmpfile_name) +-- +2.12.2 + diff --git a/SOURCES/0098-Vault-Explicitly-default-to-3DES-CBC.patch b/SOURCES/0098-Vault-Explicitly-default-to-3DES-CBC.patch new file mode 100644 index 0000000..b6cf014 --- /dev/null +++ b/SOURCES/0098-Vault-Explicitly-default-to-3DES-CBC.patch @@ -0,0 +1,50 @@ +From c6b9b76307faa001670bc990fbe88aeb23bad403 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Wed, 26 Apr 2017 18:15:40 +0200 +Subject: [PATCH] Vault: Explicitly default to 3DES CBC + +The server-side plugin for IPA Vault relied on the fact that the default +oid for encryption algorithm is 3DES in CBC mode (DES-EDE3-CBC). Dogtag +10.4 has changed the default from 3DES to AES. Pass the correct +algorithm OID to KeyClient.archive_encrypted_data(). + +Closes: https://pagure.io/freeipa/issue/6899 +Signed-off-by: Christian Heimes +Reviewed-By: Fraser Tweedale +--- + ipaserver/plugins/vault.py | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py +index 57e1ed7800063822f87da5a71f0f3a0df4d8dd33..d46aca821d2ec94a38dd7cc930f26038d5d80a90 100644 +--- a/ipaserver/plugins/vault.py ++++ b/ipaserver/plugins/vault.py +@@ -38,6 +38,14 @@ from ipapython.dn import DN + if api.env.in_server: + import pki.account + import pki.key ++ # pylint: disable=no-member ++ try: ++ # pki >= 10.4.0 ++ from pki.crypto import DES_EDE3_CBC_OID ++ except ImportError: ++ DES_EDE3_CBC_OID = pki.key.KeyClient.DES_EDE3_CBC_OID ++ # pylint: enable=no-member ++ + + if six.PY3: + unicode = str +@@ -1059,8 +1067,8 @@ class vault_archive_internal(PKQuery): + pki.key.KeyClient.PASS_PHRASE_TYPE, + wrapped_vault_data, + wrapped_session_key, +- None, +- nonce, ++ algorithm_oid=DES_EDE3_CBC_OID, ++ nonce_iv=nonce, + ) + + kra_account.logout() +-- +2.12.2 + diff --git a/SOURCES/0099-separate-function-to-set-ipaConfigString-values-on-s.patch b/SOURCES/0099-separate-function-to-set-ipaConfigString-values-on-s.patch new file mode 100644 index 0000000..2ca4e1b --- /dev/null +++ b/SOURCES/0099-separate-function-to-set-ipaConfigString-values-on-s.patch @@ -0,0 +1,244 @@ +From fd6873ad33493b5f395a92f03d54cd184b90d2a2 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 25 Apr 2017 18:55:59 +0200 +Subject: [PATCH] separate function to set ipaConfigString values on service + entry + +There is some code duplication regarding setting ipaConfigString values +when: + * LDAP-enabling a service entry + * advertising enabled KDCProxy in LDAP + +We can delegate the common work to a single re-usable function and thus +expose it to future use-cases (like PKINIT advertising). + +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/install/httpinstance.py | 43 +----------- + ipaserver/install/service.py | 135 ++++++++++++++++++++++++++------------ + 2 files changed, 94 insertions(+), 84 deletions(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index aeb5c5e450813469e1b6cd374b30cd4aab338537..f0a477e0bf16b03ed8b937279dad88e6e2b3aab6 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -42,7 +42,6 @@ from ipapython.ipa_log_manager import root_logger + import ipapython.errors + from ipaserver.install import sysupgrade + from ipalib import api +-from ipalib import errors + from ipalib.constants import ANON_USER + from ipaplatform.constants import constants + from ipaplatform.tasks import tasks +@@ -451,46 +450,8 @@ class HTTPInstance(service.Service): + + def enable_kdcproxy(self): + """Add ipaConfigString=kdcProxyEnabled to cn=KDC""" +- entry_name = DN(('cn', 'KDC'), ('cn', self.fqdn), ('cn', 'masters'), +- ('cn', 'ipa'), ('cn', 'etc'), self.suffix) +- attr_name = 'kdcProxyEnabled' +- +- try: +- entry = api.Backend.ldap2.get_entry( +- entry_name, ['ipaConfigString']) +- except errors.NotFound: +- pass +- else: +- if any(attr_name.lower() == val.lower() +- for val in entry.get('ipaConfigString', [])): +- root_logger.debug("service KDCPROXY already enabled") +- return +- +- entry.setdefault('ipaConfigString', []).append(attr_name) +- try: +- api.Backend.ldap2.update_entry(entry) +- except errors.EmptyModlist: +- root_logger.debug("service KDCPROXY already enabled") +- return +- except: +- root_logger.debug("failed to enable service KDCPROXY") +- raise +- +- root_logger.debug("service KDCPROXY enabled") +- return +- +- entry = api.Backend.ldap2.make_entry( +- entry_name, +- objectclass=["nsContainer", "ipaConfigObject"], +- cn=['KDC'], +- ipaconfigstring=[attr_name] +- ) +- +- try: +- api.Backend.ldap2.add_entry(entry) +- except errors.DuplicateEntry: +- root_logger.debug("failed to add service KDCPROXY entry") +- raise ++ service.set_service_entry_config( ++ 'KDC', self.fqdn, [u'kdcProxyEnabled'], self.suffix) + + def create_kdcproxy_conf(self): + """Create ipa-kdc-proxy.conf in /etc/ipa/kdcproxy""" +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 9533a887ca41b8d9f9480ec30b908b213807ca7e..6b5e69ccd08444c591f15eb680b4cbdf5b6f4de1 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -136,6 +136,87 @@ def find_providing_server(svcname, conn, host_name=None, api=api): + return None + + ++def case_insensitive_attr_has_value(attr, value): ++ """ ++ Helper function to find value in an attribute having case-insensitive ++ matching rules ++ ++ :param attr: attribute values ++ :param value: value to find ++ ++ :returns: True if the case-insensitive match succeeds, false otherwise ++ ++ """ ++ if any(value.lower() == val.lower() ++ for val in attr): ++ return True ++ ++ return False ++ ++ ++def set_service_entry_config(name, fqdn, config_values, ++ ldap_suffix='', ++ post_add_config=()): ++ """ ++ Sets the 'ipaConfigString' values on the entry. If the entry is not present ++ already, create a new one with desired 'ipaConfigString' ++ ++ :param name: service entry name ++ :param config_values: configuration values to store ++ :param fqdn: master fqdn ++ :param ldap_suffix: LDAP backend suffix ++ :param post_add_config: additional configuration to add when adding a ++ non-existent entry ++ """ ++ assert isinstance(ldap_suffix, DN) ++ ++ entry_name = DN( ++ ('cn', name), ('cn', fqdn), ('cn', 'masters'), ++ ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix) ++ ++ # enable disabled service ++ try: ++ entry = api.Backend.ldap2.get_entry( ++ entry_name, ['ipaConfigString']) ++ except errors.NotFound: ++ pass ++ else: ++ existing_values = entry.get('ipaConnfigString', []) ++ for value in config_values: ++ if case_insensitive_attr_has_value(existing_values, value): ++ root_logger.debug( ++ "service %s: config string %s already set", name, value) ++ ++ entry.setdefault('ipaConfigString', []).append(value) ++ ++ try: ++ api.Backend.ldap2.update_entry(entry) ++ except errors.EmptyModlist: ++ root_logger.debug( ++ "service %s has already enabled config values %s", name, ++ config_values) ++ return ++ except: ++ root_logger.debug("failed to set service %s config values", name) ++ raise ++ ++ root_logger.debug("service %s has all config values set", name) ++ return ++ ++ entry = api.Backend.ldap2.make_entry( ++ entry_name, ++ objectclass=["nsContainer", "ipaConfigObject"], ++ cn=[name], ++ ipaconfigstring=config_values + list(post_add_config), ++ ) ++ ++ try: ++ api.Backend.ldap2.add_entry(entry) ++ except (errors.DuplicateEntry) as e: ++ root_logger.debug("failed to add service entry %s", name) ++ raise e ++ ++ + class Service(object): + def __init__(self, service_name, service_desc=None, sstore=None, + fstore=None, api=api, realm_name=None, +@@ -442,51 +523,19 @@ class Service(object): + + def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='', + config=[]): +- assert isinstance(ldap_suffix, DN) +- self.disable() ++ extra_config_opts = [ ++ ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])]) ++ ] ++ extra_config_opts.extend(config) + +- entry_name = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix) +- +- # enable disabled service +- try: +- entry = api.Backend.ldap2.get_entry( +- entry_name, ['ipaConfigString']) +- except errors.NotFound: +- pass +- else: +- if any(u'enabledservice' == val.lower() +- for val in entry.get('ipaConfigString', [])): +- root_logger.debug("service %s startup entry already enabled", name) +- return +- +- entry.setdefault('ipaConfigString', []).append(u'enabledService') +- +- try: +- api.Backend.ldap2.update_entry(entry) +- except errors.EmptyModlist: +- root_logger.debug("service %s startup entry already enabled", name) +- return +- except: +- root_logger.debug("failed to enable service %s startup entry", name) +- raise +- +- root_logger.debug("service %s startup entry enabled", name) +- return +- +- order = SERVICE_LIST[name][1] +- entry = api.Backend.ldap2.make_entry( +- entry_name, +- objectclass=["nsContainer", "ipaConfigObject"], +- cn=[name], +- ipaconfigstring=[ +- "enabledService", "startOrder " + str(order)] + config, +- ) ++ self.disable() + +- try: +- api.Backend.ldap2.add_entry(entry) +- except (errors.DuplicateEntry) as e: +- root_logger.debug("failed to add service %s startup entry", name) +- raise e ++ set_service_entry_config( ++ name, ++ fqdn, ++ [u'enabledService'], ++ ldap_suffix=ldap_suffix, ++ post_add_config=extra_config_opts) + + def ldap_disable(self, name, fqdn, ldap_suffix): + assert isinstance(ldap_suffix, DN) +-- +2.12.2 + diff --git a/SOURCES/0100-Allow-for-configuration-of-all-three-PKINIT-variants.patch b/SOURCES/0100-Allow-for-configuration-of-all-three-PKINIT-variants.patch new file mode 100644 index 0000000..0106656 --- /dev/null +++ b/SOURCES/0100-Allow-for-configuration-of-all-three-PKINIT-variants.patch @@ -0,0 +1,205 @@ +From 152715b8514b1b94e1c353baedff12d24efaacb7 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 31 Mar 2017 15:06:46 +0200 +Subject: [PATCH] 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 or `--no-pkinit` is used. + +http://www.freeipa.org/page/V4/Kerberos_PKINIT +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/install/krbinstance.py | 145 +++++++++++++++++++++++++-------------- + 1 file changed, 93 insertions(+), 52 deletions(-) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 6c105f74c8da2bfd34ace607b13170bc96a8ff1d..80215788cf4031ef82e9ec7e08bde6cfc4390303 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -38,6 +38,7 @@ from ipalib.constants import ANON_USER + 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 +@@ -354,61 +355,84 @@ class KrbInstance(service.Service): + remote_ldap.gssapi_bind() + replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60) + +- def setup_pkinit(self): +- 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) +- else: +- subject = str(DN(('cn', self.fqdn), self.subject_base)) +- krbtgt = "krbtgt/" + self.realm + "@" + self.realm +- certpath = (paths.KDC_CERT, paths.KDC_KEY) ++ def _call_certmonger(self, certmonger_ca='IPA'): ++ 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 ++ 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(certmonger_ca, helper) ++ ++ certmonger.request_and_wait_for_cert( ++ certpath, ++ subject, ++ krbtgt, ++ ca=certmonger_ca, ++ 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(certmonger_ca, prev_helper) ++ ++ def issue_selfsigned_pkinit_certs(self): ++ self._call_certmonger(certmonger_ca="SelfSign") ++ # for self-signed certificate, the certificate is its own CA, copy it ++ # as CA cert ++ shutil.copyfile(paths.KDC_CERT, paths.CACERT_PEM) ++ ++ def issue_ipa_ca_signed_pkinit_certs(self): ++ try: ++ self._call_certmonger() ++ # copy IPA CA bundle to the KDC's CA cert bundle ++ 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("Failed to configure PKINIT") ++ self.stop_tracking_certs() ++ self.issue_selfsigned_pkinit_certs() ++ ++ def install_external_pkinit_certs(self): ++ 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) ++ # copy IPA CA bundle to the KDC's CA cert bundle ++ # NOTE: this may not be the same set of CA certificates trusted by ++ # externally provided PKINIT cert. + shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) + +- try: +- self.restart() +- except Exception: +- root_logger.critical("krb5kdc service failed to restart") +- raise ++ def setup_pkinit(self): ++ if self.pkcs12_info: ++ self.install_external_pkinit_certs() ++ elif self.config_pkinit: ++ self.issue_ipa_ca_signed_pkinit_certs() + + def test_anonymous_pkinit(self): + with ipautil.private_ccache() as anon_ccache: +@@ -418,6 +442,15 @@ class KrbInstance(service.Service): + raise RuntimeError("Failed to configure anonymous PKINIT") + + def enable_ssl(self): ++ """ ++ generate PKINIT certificate for KDC. If `--no-pkinit` was specified, ++ only configure local self-signed KDC certificate for use as a FAST ++ channel generator for WebUI. Do not advertise the installation steps in ++ this case. ++ """ ++ if self.master_fqdn is not None: ++ self._wait_for_replica_kdc_entry() ++ + if self.config_pkinit: + self.steps = [] + self.step("installing X509 Certificate for PKINIT", +@@ -425,6 +458,14 @@ class KrbInstance(service.Service): + self.step("testing anonymous PKINIT", self.test_anonymous_pkinit) + + self.start_creation() ++ else: ++ self.issue_selfsigned_pkinit_certs() ++ ++ try: ++ self.restart() ++ except Exception: ++ root_logger.critical("krb5kdc service failed to restart") ++ raise + + def get_anonymous_principal_name(self): + return "%s@%s" % (ANON_USER, self.realm) +-- +2.12.2 + diff --git a/SOURCES/0101-API-for-retrieval-of-master-s-PKINIT-status-and-publ.patch b/SOURCES/0101-API-for-retrieval-of-master-s-PKINIT-status-and-publ.patch new file mode 100644 index 0000000..8c7687e --- /dev/null +++ b/SOURCES/0101-API-for-retrieval-of-master-s-PKINIT-status-and-publ.patch @@ -0,0 +1,99 @@ +From a6f958875f3b42a8ea5856b672f5e8416c0aad90 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 31 Mar 2017 14:44:29 +0200 +Subject: [PATCH] API for retrieval of master's PKINIT status and publishing it + in LDAP + +An API was provided to report whether PKINIT is enabled for clients or +not. If yes, the pkinitEnabled value will be added to the +ipaConfigString attribute of master's KDC entry. + +See http://www.freeipa.org/page/V4/Kerberos_PKINIT#Configuration for +more details. + +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/install/krbinstance.py | 41 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 80215788cf4031ef82e9ec7e08bde6cfc4390303..ad3475f95371c9ae17c8b0ac082039c041d5c64c 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -48,6 +48,38 @@ from ipaplatform.constants import constants + from ipaplatform.tasks import tasks + from ipaplatform.paths import paths + ++PKINIT_ENABLED = 'pkinitEnabled' ++ ++ ++def get_pkinit_request_ca(): ++ """ ++ Return the certmonger CA name which is serving the PKINIT certificate ++ request. If the certificate is not tracked by Certmonger, return None ++ """ ++ pkinit_request_id = certmonger.get_request_id( ++ {'cert-file': paths.KDC_CERT}) ++ ++ if pkinit_request_id is None: ++ return ++ ++ return certmonger.get_request_value(pkinit_request_id, 'ca-name') ++ ++ ++def is_pkinit_enabled(): ++ """ ++ check whether PKINIT is enabled on the master by checking for the presence ++ of KDC certificate and it's tracking CA ++ """ ++ ++ if os.path.exists(paths.KDC_CERT): ++ pkinit_request_ca = get_pkinit_request_ca() ++ ++ if pkinit_request_ca != "SelfSign": ++ return True ++ ++ return False ++ ++ + class KpasswdInstance(service.SimpleServiceInstance): + def __init__(self): + service.SimpleServiceInstance.__init__(self, "kadmin") +@@ -399,6 +431,13 @@ class KrbInstance(service.Service): + if prev_helper is not None: + certmonger.modify_ca_helper(certmonger_ca, prev_helper) + ++ def pkinit_enable(self): ++ """ ++ advertise enabled PKINIT feature in master's KDC entry in LDAP ++ """ ++ service.set_service_entry_config( ++ 'KDC', self.fqdn, [PKINIT_ENABLED], self.suffix) ++ + def issue_selfsigned_pkinit_certs(self): + self._call_certmonger(certmonger_ca="SelfSign") + # for self-signed certificate, the certificate is its own CA, copy it +@@ -410,6 +449,7 @@ class KrbInstance(service.Service): + self._call_certmonger() + # copy IPA CA bundle to the KDC's CA cert bundle + shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) ++ self.pkinit_enable() + except RuntimeError as e: + root_logger.error("PKINIT certificate request failed: %s", e) + root_logger.error("Failed to configure PKINIT") +@@ -427,6 +467,7 @@ class KrbInstance(service.Service): + # NOTE: this may not be the same set of CA certificates trusted by + # externally provided PKINIT cert. + shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) ++ self.pkinit_enable() + + def setup_pkinit(self): + if self.pkcs12_info: +-- +2.12.2 + diff --git a/SOURCES/0102-Use-only-anonymous-PKINIT-to-fetch-armor-ccache.patch b/SOURCES/0102-Use-only-anonymous-PKINIT-to-fetch-armor-ccache.patch new file mode 100644 index 0000000..0588dee --- /dev/null +++ b/SOURCES/0102-Use-only-anonymous-PKINIT-to-fetch-armor-ccache.patch @@ -0,0 +1,79 @@ +From 15da0d16e99f5c6956f1ed687cc3cffdade83cb5 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 31 Mar 2017 14:14:11 +0200 +Subject: [PATCH] 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 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + 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 1e4d1a82fdefe968db13c3847b9b37b3b3f75d6f..fb6caee4d6b5fef27b53753b21ad83572da31ac4 100644 +--- a/ipalib/install/kinit.py ++++ b/ipalib/install/kinit.py +@@ -7,7 +7,6 @@ import time + + 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) +-- +2.12.2 + diff --git a/SOURCES/0103-Stop-requesting-anonymous-keytab-and-purge-all-refer.patch b/SOURCES/0103-Stop-requesting-anonymous-keytab-and-purge-all-refer.patch new file mode 100644 index 0000000..0b0431a --- /dev/null +++ b/SOURCES/0103-Stop-requesting-anonymous-keytab-and-purge-all-refer.patch @@ -0,0 +1,110 @@ +From 2bd0e49b7a7ba98a8ee6872cc7c3e619578c4431 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 5 Apr 2017 17:29:26 +0200 +Subject: [PATCH] Stop requesting anonymous keytab and purge all references of + it + +anonymous kinit using keytab never worked so we may safely remove all +code that requests/uses it. + +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaplatform/base/paths.py | 1 - + ipaserver/install/httpinstance.py | 17 ----------------- + ipaserver/install/ipa_backup.py | 1 - + ipaserver/install/server/upgrade.py | 1 - + 4 files changed, 20 deletions(-) + +diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py +index dbdd71ed0b4d69c1101db4aeb7d93152ab8aa730..f80c9e95ab875222887e3692ab80151f84345469 100644 +--- a/ipaplatform/base/paths.py ++++ b/ipaplatform/base/paths.py +@@ -50,7 +50,6 @@ class BasePathNamespace(object): + HTTPD_SSL_CONF = "/etc/httpd/conf.d/ssl.conf" + OLD_IPA_KEYTAB = "/etc/httpd/conf/ipa.keytab" + HTTP_KEYTAB = "/var/lib/ipa/gssproxy/http.keytab" +- ANON_KEYTAB = "/var/lib/ipa/api/anon.keytab" + HTTPD_PASSWORD_CONF = "/etc/httpd/conf/password.conf" + IDMAPD_CONF = "/etc/idmapd.conf" + ETC_IPA = "/etc/ipa" +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index f0a477e0bf16b03ed8b937279dad88e6e2b3aab6..7898c53bc02785e2750dba61a5696f079355c9d7 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -30,7 +30,6 @@ import locale + + import six + +-from ipalib.constants import IPAAPI_USER + from ipalib.install import certmonger + from ipaserver.install import service + from ipaserver.install import certs +@@ -42,7 +41,6 @@ from ipapython.ipa_log_manager import root_logger + import ipapython.errors + from ipaserver.install import sysupgrade + from ipalib import api +-from ipalib.constants import ANON_USER + from ipaplatform.constants import constants + from ipaplatform.tasks import tasks + from ipaplatform.paths import paths +@@ -158,7 +156,6 @@ class HTTPInstance(service.Service): + self.step("adding URL rewriting rules", self.__add_include) + self.step("configuring httpd", self.__configure_http) + self.step("setting up httpd keytab", self.request_service_keytab) +- self.step("retrieving anonymous keytab", self.request_anon_keytab) + self.step("configuring Gssproxy", self.configure_gssproxy) + self.step("setting up ssl", self.__setup_ssl) + if self.ca_is_configured: +@@ -304,20 +301,6 @@ class HTTPInstance(service.Service): + if certmonger_stopped: + certmonger.stop() + +- def request_anon_keytab(self): +- parent = os.path.dirname(paths.ANON_KEYTAB) +- if not os.path.exists(parent): +- os.makedirs(parent, 0o755) +- +- self.clean_previous_keytab(keytab=paths.ANON_KEYTAB) +- self.run_getkeytab(self.api.env.ldap_uri, paths.ANON_KEYTAB, ANON_USER) +- +- pent = pwd.getpwnam(IPAAPI_USER) +- os.chmod(parent, 0o700) +- os.chown(parent, pent.pw_uid, pent.pw_gid) +- +- self.set_keytab_owner(keytab=paths.ANON_KEYTAB, owner=IPAAPI_USER) +- + def create_password_conf(self): + """ + This is the format of mod_nss pin files. +diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py +index f71a40bb06545c8d89d1e3fdbc37d5e6e1fe8d58..40f08d7d727a8b97b5996f15d27c1e20788e1473 100644 +--- a/ipaserver/install/ipa_backup.py ++++ b/ipaserver/install/ipa_backup.py +@@ -120,7 +120,6 @@ class Backup(admintool.AdminTool): + ) + + files = ( +- paths.ANON_KEYTAB, + paths.NAMED_CONF, + paths.NAMED_KEYTAB, + paths.RESOLV_CONF, +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 927acb011172de926773196eb1d032af8376f3d9..ea2918f5037898b6b8dc601441a439b6150d54e5 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1795,7 +1795,6 @@ def upgrade_configuration(): + KDC_KEY=paths.KDC_KEY, + CACERT_PEM=paths.CACERT_PEM) + krb.add_anonymous_principal() +- http.request_anon_keytab() + setup_pkinit(krb) + + if not ds_running: +-- +2.12.2 + diff --git a/SOURCES/0104-Use-local-anchor-when-armoring-password-requests.patch b/SOURCES/0104-Use-local-anchor-when-armoring-password-requests.patch new file mode 100644 index 0000000..eb161d5 --- /dev/null +++ b/SOURCES/0104-Use-local-anchor-when-armoring-password-requests.patch @@ -0,0 +1,31 @@ +From b5992ea987f6d8d49c988a9ab42463655b3d8e05 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 31 Mar 2017 15:15:50 +0200 +Subject: [PATCH] Use local anchor when armoring password requests + +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/rpcserver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 77ed7e124c2ca3dcb49d3a68269d6fa9875d4da0..161872450d141a61af4345a20e278db728fe2aac 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -944,7 +944,7 @@ class login_password(Backend, KerberosSession): + 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 +-- +2.12.2 + diff --git a/SOURCES/0105-Upgrade-configure-local-full-PKINIT-depending-on-the.patch b/SOURCES/0105-Upgrade-configure-local-full-PKINIT-depending-on-the.patch new file mode 100644 index 0000000..b679de0 --- /dev/null +++ b/SOURCES/0105-Upgrade-configure-local-full-PKINIT-depending-on-the.patch @@ -0,0 +1,52 @@ +From c40683f85776f401b3e6bb0a3a69a48a206ab633 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 6 Apr 2017 18:52:05 +0200 +Subject: [PATCH] Upgrade: configure local/full PKINIT depending on the master + status + +The upgrader has been modified to configure either local or full PKINIT +depending on the CA status. Additionally, the new PKINIT configuration +will be written to the master's KDC entry. + +https://pagure.io/freeipa/issue/6830 +http://www.freeipa.org/page/V4/Kerberos_PKINIT + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/install/server/upgrade.py | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index ea2918f5037898b6b8dc601441a439b6150d54e5..8da918114066598ec5a74098d85dfef06d22bf86 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1485,14 +1485,17 @@ def add_default_caacl(ca): + def setup_pkinit(krb): + root_logger.info("[Setup PKINIT]") + +- if not api.Command.ca_is_enabled()['result']: +- root_logger.info("CA is not enabled") +- return ++ pkinit_is_enabled = krbinstance.is_pkinit_enabled() ++ ca_is_enabled = api.Command.ca_is_enabled()['result'] + +- if not os.path.exists(paths.KDC_CERT): +- root_logger.info("Requesting PKINIT certificate") +- krb.setup_pkinit() ++ if not pkinit_is_enabled: ++ if ca_is_enabled: ++ krb.issue_ipa_ca_signed_pkinit_certs() ++ else: ++ krb.issue_selfsigned_pkinit_certs() + ++ # reconfigure KDC just in case in order to handle potentially broken ++ # 4.5.0 -> 4.5.1 upgrade path + replacevars = dict() + replacevars['pkinit_identity'] = 'FILE:{},{}'.format( + paths.KDC_CERT,paths.KDC_KEY) +-- +2.12.2 + diff --git a/SOURCES/0106-Do-not-test-anonymous-PKINIT-after-install-upgrade.patch b/SOURCES/0106-Do-not-test-anonymous-PKINIT-after-install-upgrade.patch new file mode 100644 index 0000000..34a1b07 --- /dev/null +++ b/SOURCES/0106-Do-not-test-anonymous-PKINIT-after-install-upgrade.patch @@ -0,0 +1,62 @@ +From 60412d08baa5a6836e505428a8b9bc73bdce0353 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 25 Apr 2017 19:12:51 +0200 +Subject: [PATCH] Do not test anonymous PKINIT after install/upgrade + +Local FAST armoring will now work regardless of PKINIT status so there +is no need to explicitly test for working PKINIT. If there is, there +should be a test case for that. + +https://pagure.io/freeipa/issue/6830 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +Reviewed-By: Simo Sorce +--- + ipaserver/install/krbinstance.py | 9 --------- + ipaserver/install/server/upgrade.py | 1 - + 2 files changed, 10 deletions(-) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index ad3475f95371c9ae17c8b0ac082039c041d5c64c..76ac3029ca6d1cbdd85c6ced6272c6f9a21f04a1 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -475,13 +475,6 @@ class KrbInstance(service.Service): + elif self.config_pkinit: + self.issue_ipa_ca_signed_pkinit_certs() + +- 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") +- + def enable_ssl(self): + """ + generate PKINIT certificate for KDC. If `--no-pkinit` was specified, +@@ -496,8 +489,6 @@ class KrbInstance(service.Service): + 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.issue_selfsigned_pkinit_certs() +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 8da918114066598ec5a74098d85dfef06d22bf86..0f27428dd492bb44dd8c69a7e7f47abb531843f5 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1507,7 +1507,6 @@ def setup_pkinit(krb): + if krb.is_running(): + krb.stop() + krb.start() +- krb.test_anonymous_pkinit() + + + def disable_httpd_system_trust(http): +-- +2.12.2 + diff --git a/SOURCES/0107-vault-piped-input-for-ipa-vault-add-fails.patch b/SOURCES/0107-vault-piped-input-for-ipa-vault-add-fails.patch new file mode 100644 index 0000000..545fb7f --- /dev/null +++ b/SOURCES/0107-vault-piped-input-for-ipa-vault-add-fails.patch @@ -0,0 +1,104 @@ +From a0ea8706fddb0459982c2ae276679cea6b0a812e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 27 Apr 2017 18:20:06 +0200 +Subject: [PATCH] vault: piped input for ipa vault-add fails + +An exception is raised when using echo "Secret123\n" | ipa vault-add myvault + +This happens because the code is using (string).decode(sys.stdin.encoding) +and sys.stdin.encoding is None when the input is read from a pipe. +The fix is using the prompt_password method defined by Backend.textui, +which gracefully handles this issue. + +https://pagure.io/freeipa/issue/6907 + +Reviewed-By: Christian Heimes +Reviewed-By: Abhijeet Kasurde +Reviewed-By: Stanislav Laznicka +--- + ipaclient/plugins/vault.py | 37 ++++++++----------------------------- + 1 file changed, 8 insertions(+), 29 deletions(-) + +diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py +index 3fb4900d9cf90e6902c40e1c3d8cfdafec2e28b8..f21dc4dbb6579c0f92ae9ab94d76a6396b26b233 100644 +--- a/ipaclient/plugins/vault.py ++++ b/ipaclient/plugins/vault.py +@@ -21,11 +21,9 @@ from __future__ import print_function + + import base64 + import errno +-import getpass + import io + import json + import os +-import sys + import tempfile + + from cryptography.fernet import Fernet, InvalidToken +@@ -84,29 +82,6 @@ register = Registry() + MAX_VAULT_DATA_SIZE = 2**20 # = 1 MB + + +-def get_new_password(): +- """ +- Gets new password from user and verify it. +- """ +- while True: +- password = getpass.getpass('New password: ').decode( +- sys.stdin.encoding) +- password2 = getpass.getpass('Verify password: ').decode( +- sys.stdin.encoding) +- +- if password == password2: +- return password +- +- print(' ** Passwords do not match! **') +- +- +-def get_existing_password(): +- """ +- Gets existing password from user. +- """ +- return getpass.getpass('Password: ').decode(sys.stdin.encoding) +- +- + def generate_symmetric_key(password, salt): + """ + Generates symmetric key from password and salt. +@@ -304,7 +279,8 @@ class vault_add(Local): + password = password.rstrip('\n') + + else: +- password = get_new_password() ++ password = self.api.Backend.textui.prompt_password( ++ 'New password') + + # generate vault salt + options['ipavaultsalt'] = os.urandom(16) +@@ -887,9 +863,11 @@ class vault_archive(ModVaultData): + + else: + if override_password: +- password = get_new_password() ++ password = self.api.Backend.textui.prompt_password( ++ 'New password') + else: +- password = get_existing_password() ++ password = self.api.Backend.textui.prompt_password( ++ 'Password', confirm=False) + + if not override_password: + # verify password by retrieving existing data +@@ -1112,7 +1090,8 @@ class vault_retrieve(ModVaultData): + password = password.rstrip('\n') + + else: +- password = get_existing_password() ++ password = self.api.Backend.textui.prompt_password( ++ 'Password', confirm=False) + + # generate encryption key from password + encryption_key = generate_symmetric_key(password, salt) +-- +2.12.2 + diff --git a/SOURCES/0108-automount-install-fix-checking-of-SSSD-functionality.patch b/SOURCES/0108-automount-install-fix-checking-of-SSSD-functionality.patch new file mode 100644 index 0000000..524993f --- /dev/null +++ b/SOURCES/0108-automount-install-fix-checking-of-SSSD-functionality.patch @@ -0,0 +1,83 @@ +From 5a96db72e6bb7597217c5fbbcaa1b29836a9c8c0 Mon Sep 17 00:00:00 2001 +From: Petr Vobornik +Date: Tue, 25 Apr 2017 18:19:21 +0200 +Subject: [PATCH] automount install: fix checking of SSSD functionality on + uninstall + +Change in 2d4d1a9dc0ef2bbe86751768d6e6b009a52c0dc9 no longer initializes +api in `ipa-client-automount --uninstallation` Which caused error in +wait_for_sssd which gets realm from initialized API. + +This patch initializes the API in a way that it doesn't download schema +on uninstallation and on installation it uses host keytab for it so it +no longer requires user's Kerberos credentials. + +Also fix call of xxx_service_class_factory which requires api as param. + +https://pagure.io/freeipa/issue/6861 + +Reviewed-By: Rob Crittenden +Reviewed-By: Tomas Krizek +--- + client/ipa-client-automount | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/client/ipa-client-automount b/client/ipa-client-automount +index 18914bd74932180f300fcbc7b7db0ba1505881bd..2b1d8b9a8ca14d5403635fb20cee37984fe4a101 100755 +--- a/client/ipa-client-automount ++++ b/client/ipa-client-automount +@@ -193,7 +193,7 @@ def configure_autofs_sssd(fstore, statestore, autodiscover, options): + sssdconfig.write(paths.SSSD_CONF) + statestore.backup_state('autofs', 'sssd', True) + +- sssd = services.service('sssd') ++ sssd = services.service('sssd', api) + sssd.restart() + print("Restarting sssd, waiting for it to become available.") + wait_for_sssd() +@@ -281,7 +281,7 @@ def uninstall(fstore, statestore): + break + sssdconfig.save_domain(domain) + sssdconfig.write(paths.SSSD_CONF) +- sssd = services.service('sssd') ++ sssd = services.service('sssd', api) + sssd.restart() + wait_for_sssd() + except Exception as e: +@@ -379,9 +379,6 @@ def main(): + paths.IPACLIENT_INSTALL_LOG, verbose=False, debug=options.debug, + filemode='a', console_format='%(message)s') + +- if options.uninstall: +- return uninstall(fstore, statestore) +- + cfg = dict( + context='cli_installer', + confdir=paths.ETC_IPA, +@@ -390,8 +387,11 @@ def main(): + verbose=0, + ) + ++ # Bootstrap API early so that env object is available + api.bootstrap(**cfg) +- api.finalize() ++ ++ if options.uninstall: ++ return uninstall(fstore, statestore) + + ca_cert_path = None + if os.path.exists(paths.IPA_CA_CRT): +@@ -449,6 +449,10 @@ def main(): + os.environ['KRB5CCNAME'] = ccache_name + except gssapi.exceptions.GSSError as e: + sys.exit("Failed to obtain host TGT: %s" % e) ++ ++ # Finalize API when TGT obtained using host keytab exists ++ api.finalize() ++ + # Now we have a TGT, connect to IPA + try: + api.Backend.rpcclient.connect() +-- +2.12.2 + diff --git a/SOURCES/0109-Fix-CA-server-cert-validation-in-FIPS.patch b/SOURCES/0109-Fix-CA-server-cert-validation-in-FIPS.patch new file mode 100644 index 0000000..f10a3a1 --- /dev/null +++ b/SOURCES/0109-Fix-CA-server-cert-validation-in-FIPS.patch @@ -0,0 +1,72 @@ +From 83fe9a4eb7b96d9d02066a73fe1894fb8b797753 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 26 Apr 2017 08:19:27 +0200 +Subject: [PATCH] Fix CA/server cert validation in FIPS + +In FIPS, the NSS library needs to be passed passwords to perform +certificate validation. Should we not have passed it and the NSS +guys have not fixed this yet, we would get SEC_ERROR_BAD_SIGNATURE +which is completely different error than one would expect but +that's just how things are with NSS right now. + +https://pagure.io/freeipa/issue/6897 + +Reviewed-By: Christian Heimes +Reviewed-By: Abhijeet Kasurde +--- + ipapython/certdb.py | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 0665f944457fb09820eb244c742cb1782e515ad1..ea73ec139df9013b860df447fcffd9038cf7c8f2 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -77,6 +77,11 @@ def find_cert_from_txt(cert, start=0): + return (cert, e) + + ++def get_file_cont(slot, token, filename): ++ with open(filename) as f: ++ return f.read() ++ ++ + class NSSDatabase(object): + """A general-purpose wrapper around a NSS cert database + +@@ -547,12 +552,14 @@ class NSSDatabase(object): + if nss.nss_is_initialized(): + nss.nss_shutdown() + nss.nss_init(self.secdir) ++ nss.set_password_callback(get_file_cont) + try: + certdb = nss.get_default_certdb() + cert = nss.find_cert_from_nickname(nickname) + intended_usage = nss.certificateUsageSSLServer + try: +- approved_usage = cert.verify_now(certdb, True, intended_usage) ++ approved_usage = cert.verify_now(certdb, True, intended_usage, ++ self.pwd_file) + except NSPRError as e: + if e.errno != -8102: + raise ValueError(e.strerror) +@@ -572,6 +579,7 @@ class NSSDatabase(object): + if nss.nss_is_initialized(): + nss.nss_shutdown() + nss.nss_init(self.secdir) ++ nss.set_password_callback(get_file_cont) + try: + certdb = nss.get_default_certdb() + cert = nss.find_cert_from_nickname(nickname) +@@ -586,7 +594,8 @@ class NSSDatabase(object): + raise ValueError("not a CA certificate") + intended_usage = nss.certificateUsageSSLCA + try: +- approved_usage = cert.verify_now(certdb, True, intended_usage) ++ approved_usage = cert.verify_now(certdb, True, intended_usage, ++ self.pwd_file) + except NSPRError as e: + if e.errno != -8102: # SEC_ERROR_INADEQUATE_KEY_USAGE + raise ValueError(e.strerror) +-- +2.12.2 + diff --git a/SOURCES/0110-restore-restart-reload-gssproxy-after-restore.patch b/SOURCES/0110-restore-restart-reload-gssproxy-after-restore.patch new file mode 100644 index 0000000..5905a68 --- /dev/null +++ b/SOURCES/0110-restore-restart-reload-gssproxy-after-restore.patch @@ -0,0 +1,77 @@ +From 5a2424fc07c931be1788bca19726df41f02479e7 Mon Sep 17 00:00:00 2001 +From: Petr Vobornik +Date: Wed, 26 Apr 2017 18:47:53 +0200 +Subject: [PATCH] restore: restart/reload gssproxy after restore + +So that gssproxy picks up new configuration and therefore related +usages like authentication of CLI against server works + +https://pagure.io/freeipa/issue/6902 + +Reviewed-By: Tomas Krizek +--- + ipaplatform/base/services.py | 21 ++++++++++++++++++--- + ipaserver/install/ipa_restore.py | 3 +++ + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/ipaplatform/base/services.py b/ipaplatform/base/services.py +index 068b9723cab2773ca2e274e1734723371410675a..fca6298fc07e03ab4ceeba0b51286e33ae168fcc 100644 +--- a/ipaplatform/base/services.py ++++ b/ipaplatform/base/services.py +@@ -154,6 +154,10 @@ class PlatformService(object): + + return + ++ def reload_or_restart(self, instance_name="", capture_output=True, ++ wait=True): ++ return ++ + def restart(self, instance_name="", capture_output=True, wait=True): + return + +@@ -298,14 +302,25 @@ class SystemdService(PlatformService): + instance_name, + update_service_list=update_service_list) + +- def restart(self, instance_name="", capture_output=True, wait=True): +- ipautil.run([paths.SYSTEMCTL, "restart", +- self.service_instance(instance_name)], ++ def _restart_base(self, instance_name, operation, capture_output=True, ++ wait=False): ++ ++ ipautil.run([paths.SYSTEMCTL, operation, ++ self.service_instance(instance_name)], + skip_output=not capture_output) + + if wait and self.is_running(instance_name): + self.wait_for_open_ports(self.service_instance(instance_name)) + ++ def reload_or_restart(self, instance_name="", capture_output=True, ++ wait=True): ++ self._restart_base(instance_name, "reload-or-restart", ++ capture_output, wait) ++ ++ def restart(self, instance_name="", capture_output=True, wait=True): ++ self._restart_base(instance_name, "restart", ++ capture_output, wait) ++ + def is_running(self, instance_name="", wait=True): + instance = self.service_instance(instance_name, 'is-active') + +diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py +index 378c013b6f4a4656768d7a484d2014a0f9eef3c0..96fc493c774f5de4c8149bf477cb66ec4960de4f 100644 +--- a/ipaserver/install/ipa_restore.py ++++ b/ipaserver/install/ipa_restore.py +@@ -401,6 +401,9 @@ class Restore(admintool.AdminTool): + services.knownservices.pki_tomcatd.enable() + services.knownservices.pki_tomcatd.disable() + ++ self.log.info('Restarting GSS-proxy') ++ gssproxy = services.service('gssproxy', api) ++ gssproxy.reload_or_restart() + self.log.info('Starting IPA services') + run(['ipactl', 'start']) + self.log.info('Restarting SSSD') +-- +2.12.2 + diff --git a/SOURCES/0111-kerberos-session-use-CA-cert-with-full-cert-chain-fo.patch b/SOURCES/0111-kerberos-session-use-CA-cert-with-full-cert-chain-fo.patch new file mode 100644 index 0000000..2b6cf22 --- /dev/null +++ b/SOURCES/0111-kerberos-session-use-CA-cert-with-full-cert-chain-fo.patch @@ -0,0 +1,39 @@ +From 1815435956746814362ddafca4f7a967e8886d90 Mon Sep 17 00:00:00 2001 +From: Petr Vobornik +Date: Tue, 25 Apr 2017 17:19:36 +0200 +Subject: [PATCH] kerberos session: use CA cert with full cert chain for + obtaining cookie + +Http request performed in finalize_kerberos_acquisition doesn't use +CA certificate/certificate store with full certificate chain of IPA server. +So it might happen that in case that IPA is installed with externally signed +CA certificate, the call can fail because of certificate validation +and e.g. prevent session acquisition. + +If it will fail for sure is not known - the use case was not discovered, +but it is faster and safer to fix preemptively. + +https://pagure.io/freeipa/issue/6876 + +Reviewed-By: Martin Basti +--- + ipaserver/rpcserver.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 161872450d141a61af4345a20e278db728fe2aac..996a3d29884ca0180c39841f6986abf9b23ff13a 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -602,7 +602,8 @@ class KerberosSession(HTTP_Status): + try: + target = self.api.env.host + r = requests.get('http://{0}/ipa/session/cookie'.format(target), +- auth=NegotiateAuth(target, ccache_name)) ++ auth=NegotiateAuth(target, ccache_name), ++ verify=paths.IPA_CA_CRT) + session_cookie = r.cookies.get("ipa_session") + if not session_cookie: + raise ValueError('No session cookie found') +-- +2.12.2 + diff --git a/SOURCES/0112-ipa-client-install-remove-extra-space-in-pkinit_anch.patch b/SOURCES/0112-ipa-client-install-remove-extra-space-in-pkinit_anch.patch new file mode 100644 index 0000000..05ccb90 --- /dev/null +++ b/SOURCES/0112-ipa-client-install-remove-extra-space-in-pkinit_anch.patch @@ -0,0 +1,34 @@ +From 7066dcf1154f9538e9da6bcbcedb69b973509b3c Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 2 May 2017 10:22:22 +0200 +Subject: [PATCH] ipa-client-install: remove extra space in pkinit_anchors + definition + +ipa-client-install modifies /etc/krb5.conf and defines the following line: + pkinit_anchors = FILE: /etc/ipa/ca.crt + +The extra space between FILE: and /etc/ipa/ca.crt break pkinit. + +https://pagure.io/freeipa/issue/6916 + +Reviewed-By: Alexander Bokovoy +--- + ipaclient/install/client.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 549c9b8199255ec57cde3624d34e98d2a9be8d69..abca692fd61be4a9f35a1398fb2af4b1d9e8689b 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -710,7 +710,7 @@ def configure_krb5_conf( + kropts.append(krbconf.setOption('default_domain', cli_domain)) + + kropts.append( +- krbconf.setOption('pkinit_anchors', 'FILE: %s' % paths.IPA_CA_CRT)) ++ krbconf.setOption('pkinit_anchors', 'FILE:%s' % paths.IPA_CA_CRT)) + ropts = [{ + 'name': cli_realm, + 'type': 'subsection', +-- +2.12.2 + diff --git a/SOURCES/0113-Refresh-Dogtag-RestClient.ca_host-property.patch b/SOURCES/0113-Refresh-Dogtag-RestClient.ca_host-property.patch new file mode 100644 index 0000000..509618f --- /dev/null +++ b/SOURCES/0113-Refresh-Dogtag-RestClient.ca_host-property.patch @@ -0,0 +1,114 @@ +From 103d784865c4ebab9085e8edda34f9cb47d70150 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Thu, 27 Apr 2017 12:51:30 +0200 +Subject: [PATCH] Refresh Dogtag RestClient.ca_host property + +Refresh the ca_host property of the Dogtag's RestClient class when +it's requested as a context manager. + +This solves the problem which would occur on DL0 when installing +CA which needs to perform a set of steps against itself accessing +8443 port. This port should however only be available locally so +trying to connect to remote master would fail. We need to make +sure the right CA host is accessed. + +https://pagure.io/freeipa/issue/6878 + +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +--- + ipaserver/install/cainstance.py | 5 ++--- + ipaserver/plugins/dogtag.py | 30 ++++++++++++++++++------------ + 2 files changed, 20 insertions(+), 15 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 84d60bfddc0fb968f31706e54e36557e9543846e..d72feb884964ecf49fe0166cbfeb3cb2c10737fe 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -425,6 +425,8 @@ class CAInstance(DogtagInstance): + self.step("Configure HTTP to proxy connections", + self.http_proxy) + self.step("restarting certificate server", self.restart_instance) ++ self.step("updating IPA configuration", update_ipa_conf) ++ self.step("enabling CA instance", self.__enable_instance) + if not promote: + self.step("migrating certificate profiles to LDAP", + migrate_profiles_to_ldap) +@@ -432,9 +434,6 @@ class CAInstance(DogtagInstance): + import_included_profiles) + self.step("adding default CA ACL", ensure_default_caacl) + self.step("adding 'ipa' CA entry", ensure_ipa_authority_entry) +- self.step("updating IPA configuration", update_ipa_conf) +- +- self.step("enabling CA instance", self.__enable_instance) + + self.step("configuring certmonger renewal for lightweight CAs", + self.__add_lightweight_ca_tracking_requests) +diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py +index 3997531032746a22243a4219250af4172e9ae5b3..bddaab58a546196958811f10bb4d049db4aea524 100644 +--- a/ipaserver/plugins/dogtag.py ++++ b/ipaserver/plugins/dogtag.py +@@ -1202,7 +1202,6 @@ import os + import random + from ipaserver.plugins import rabase + from ipalib.constants import TYPE_ERROR +-from ipalib.util import cachedproperty + from ipalib import _ + from ipaplatform.paths import paths + +@@ -1250,34 +1249,41 @@ class RestClient(Backend): + self.client_keyfile = paths.RA_AGENT_KEY + super(RestClient, self).__init__(api) + ++ self._ca_host = None + # session cookie + self.override_port = None + self.cookie = None + +- @cachedproperty ++ @property + def ca_host(self): + """ +- :return: host +- as str ++ :returns: FQDN of a host hopefully providing a CA service + +- Select our CA host. ++ Select our CA host, cache it for the first time. + """ ++ if self._ca_host is not None: ++ return self._ca_host ++ + ldap2 = self.api.Backend.ldap2 + if host_has_service(api.env.ca_host, ldap2, "CA"): +- return api.env.ca_host +- if api.env.host != api.env.ca_host: ++ object.__setattr__(self, '_ca_host', api.env.ca_host) ++ elif api.env.host != api.env.ca_host: + if host_has_service(api.env.host, ldap2, "CA"): +- return api.env.host +- host = select_any_master(ldap2) +- if host: +- return host ++ object.__setattr__(self, '_ca_host', api.env.host) + else: +- return api.env.ca_host ++ object.__setattr__(self, '_ca_host', select_any_master(ldap2)) ++ if self._ca_host is None: ++ object.__setattr__(self, '_ca_host', api.env.ca_host) ++ return self._ca_host + + def __enter__(self): + """Log into the REST API""" + if self.cookie is not None: + return ++ ++ # Refresh the ca_host property ++ object.__setattr__(self, '_ca_host', None) ++ + status, resp_headers, _resp_body = dogtag.https_request( + self.ca_host, self.override_port or self.env.ca_agent_port, + url='/ca/rest/account/login', +-- +2.12.2 + diff --git a/SOURCES/0114-Remove-the-cachedproperty-class.patch b/SOURCES/0114-Remove-the-cachedproperty-class.patch new file mode 100644 index 0000000..727c632 --- /dev/null +++ b/SOURCES/0114-Remove-the-cachedproperty-class.patch @@ -0,0 +1,71 @@ +From 4fe9684ccd97f0c6cd32d858f681f98fb97162dc Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 28 Apr 2017 09:31:45 +0200 +Subject: [PATCH] Remove the cachedproperty class + +The cachedproperty class was used in one special use-case where it only +caused issues. Let's get rid of it. + +https://pagure.io/freeipa/issue/6878 + +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +--- + ipalib/util.py | 34 ---------------------------------- + 1 file changed, 34 deletions(-) + +diff --git a/ipalib/util.py b/ipalib/util.py +index e9d4105775a2c9096b1718a604d31034b44bf0bd..8973a19abf56d1d1c5ba04f6edb4228dd2329e65 100644 +--- a/ipalib/util.py ++++ b/ipalib/util.py +@@ -34,7 +34,6 @@ import dns + import encodings + import sys + import ssl +-from weakref import WeakKeyDictionary + + import netaddr + from dns import resolver, rdatatype +@@ -492,39 +491,6 @@ def remove_sshpubkey_from_output_list_post(context, entries): + delattr(context, 'ipasshpubkey_added') + + +-class cachedproperty(object): +- """ +- A property-like attribute that caches the return value of a method call. +- +- When the attribute is first read, the method is called and its return +- value is saved and returned. On subsequent reads, the saved value is +- returned. +- +- Typical usage: +- class C(object): +- @cachedproperty +- def attr(self): +- return 'value' +- """ +- __slots__ = ('getter', 'store') +- +- def __init__(self, getter): +- self.getter = getter +- self.store = WeakKeyDictionary() +- +- def __get__(self, obj, cls): +- if obj is None: +- return None +- if obj not in self.store: +- self.store[obj] = self.getter(obj) +- return self.store[obj] +- +- def __set__(self, obj, value): +- raise AttributeError("can't set attribute") +- +- def __delete__(self, obj): +- raise AttributeError("can't delete attribute") +- + # regexp matching signed floating point number (group 1) followed by + # optional whitespace followed by time unit, e.g. day, hour (group 7) + time_duration_re = re.compile(r'([-+]?((\d+)|(\d+\.\d+)|(\.\d+)|(\d+\.)))\s*([a-z]+)', re.IGNORECASE) +-- +2.12.2 + diff --git a/SOURCES/0115-ipa-server-install-with-external-CA-fix-pkinit-cert-.patch b/SOURCES/0115-ipa-server-install-with-external-CA-fix-pkinit-cert-.patch new file mode 100644 index 0000000..74bfec8 --- /dev/null +++ b/SOURCES/0115-ipa-server-install-with-external-CA-fix-pkinit-cert-.patch @@ -0,0 +1,62 @@ +From 23030ef4f4faa9bf3ee13d13dedb2e0a21da1f2a Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 3 May 2017 10:21:12 +0200 +Subject: [PATCH] ipa-server-install with external CA: fix pkinit cert issuance + +ipa-server-install with external CA fails to issue pkinit certs. +This happens because the installer calls +krb = krbinstance.KrbInstance(fstore) +then +krb.enable_ssl() +and in this code path self.config_pkinit is set to None, leading to a wrong +code path. + +The fix initializes the required fields of the krbinstance before calling +krb.enable_ssl. + +https://pagure.io/freeipa/issue/6921 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Abhijeet Kasurde +--- + ipaserver/install/krbinstance.py | 8 ++++++++ + ipaserver/install/server/install.py | 4 ++++ + 2 files changed, 12 insertions(+) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 76ac3029ca6d1cbdd85c6ced6272c6f9a21f04a1..2f14ff592064d3446f73b31e615b2de88d6d786c 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -108,6 +108,14 @@ class KrbInstance(service.Service): + suffix = ipautil.dn_attribute_property('_suffix') + subject_base = ipautil.dn_attribute_property('_subject_base') + ++ def init_info(self, realm_name, host_name, setup_pkinit=False, ++ subject_base=None): ++ self.fqdn = host_name ++ self.realm = realm_name ++ self.suffix = ipautil.realm_to_suffix(realm_name) ++ self.subject_base = subject_base ++ self.config_pkinit = setup_pkinit ++ + def get_realm_suffix(self): + return DN(('cn', self.realm), ('cn', 'kerberos'), self.suffix) + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index b360e0532ce1b9b729be1cc2398cb2b46620901c..0ce60e964cb210708e56fb43a5b70f8e3405caf2 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -768,6 +768,10 @@ def install(installer): + setup_pkinit=not options.no_pkinit, + pkcs12_info=pkinit_pkcs12_info, + subject_base=options.subject_base) ++ else: ++ krb.init_info(realm_name, host_name, ++ setup_pkinit=not options.no_pkinit, ++ subject_base=options.subject_base) + + if setup_ca: + if not options.external_cert_files and options.external_ca: +-- +2.12.2 + diff --git a/SOURCES/0116-kra-install-update-installation-failure-message.patch b/SOURCES/0116-kra-install-update-installation-failure-message.patch new file mode 100644 index 0000000..086a672 --- /dev/null +++ b/SOURCES/0116-kra-install-update-installation-failure-message.patch @@ -0,0 +1,32 @@ +From 17a0ed476c206cea4e72a262da6392f9c2ad2eff Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Wed, 3 May 2017 15:29:55 +0200 +Subject: [PATCH] kra install: update installation failure message + +When installation fails, do not advise the user to use the +obsoleted --uninstall option. + +Signed-off-by: Tomas Krizek +Fixes https://pagure.io/freeipa/issue/6923 + +Reviewed-By: Martin Basti +--- + ipaserver/install/ipa_kra_install.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py +index 25766541df53f034a813487321a3ad6a0ae43e57..b06d49c834d0ffa4f2e35c3241a83e42c4c9c337 100644 +--- a/ipaserver/install/ipa_kra_install.py ++++ b/ipaserver/install/ipa_kra_install.py +@@ -103,7 +103,7 @@ class KRAInstaller(KRAInstall): + + FAIL_MESSAGE = ''' + Your system may be partly configured. +- Run ipa-kra-install --uninstall to clean up. ++ If you run into issues, you may have to re-install IPA on this server. + ''' + + def validate_options(self, needs_root=True): +-- +2.12.2 + diff --git a/SOURCES/0117-Make-sure-remote-hosts-have-our-keys.patch b/SOURCES/0117-Make-sure-remote-hosts-have-our-keys.patch new file mode 100644 index 0000000..37fc42f --- /dev/null +++ b/SOURCES/0117-Make-sure-remote-hosts-have-our-keys.patch @@ -0,0 +1,114 @@ +From 948ab2a1f44676769e1e8c9be439606d05672c9b Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 31 Mar 2017 11:22:45 -0400 +Subject: [PATCH] Make sure remote hosts have our keys + +In complex replication setups a replica may try to obtain CA keys from a +host that is not the master we initially create the keys against. +In this case race conditions may happen due to replication. So we need +to make sure the server we are contacting to get the CA keys has our +keys in LDAP. We do this by waiting to positively fetch our encryption +public key (the last one we create) from the target host LDAP server. + +Fixes: https://pagure.io/freeipa/issue/6838 + +Signed-off-by: Simo Sorce +Reviewed-By: Stanislav Laznicka +Reviewed-By: Christian Heimes +--- + ipaserver/install/custodiainstance.py | 28 +++++++++++++++++++++++++++- + ipaserver/secrets/kem.py | 12 ++++++++++++ + 2 files changed, 39 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py +index 6a613923163bccd1b59e0c3b3672905715a8de7c..390576bc0c0edfb7d8f8895eca9df30079526aa8 100644 +--- a/ipaserver/install/custodiainstance.py ++++ b/ipaserver/install/custodiainstance.py +@@ -1,6 +1,6 @@ + # Copyright (C) 2015 FreeIPa Project Contributors, see 'COPYING' for license. + +-from ipaserver.secrets.kem import IPAKEMKeys ++from ipaserver.secrets.kem import IPAKEMKeys, KEMLdap + from ipaserver.secrets.client import CustodiaClient + from ipaplatform.paths import paths + from ipaplatform.constants import constants +@@ -18,6 +18,7 @@ import shutil + import os + import stat + import tempfile ++import time + import pwd + + +@@ -122,6 +123,27 @@ class CustodiaInstance(SimpleServiceInstance): + cli = self.__CustodiaClient(server=master_host_name) + cli.fetch_key('dm/DMHash') + ++ def __wait_keys(self, host, timeout=300): ++ ldap_uri = 'ldap://%s' % host ++ deadline = int(time.time()) + timeout ++ root_logger.info("Waiting up to {} seconds to see our keys " ++ "appear on host: {}".format(timeout, host)) ++ ++ konn = KEMLdap(ldap_uri) ++ saved_e = None ++ while True: ++ try: ++ return konn.check_host_keys(self.fqdn) ++ except Exception as e: ++ # log only once for the same error ++ if not isinstance(e, type(saved_e)): ++ root_logger.debug( ++ "Transient error getting keys: '{err}'".format(err=e)) ++ saved_e = e ++ if int(time.time()) > deadline: ++ raise RuntimeError("Timed out trying to obtain keys.") ++ time.sleep(1) ++ + def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): + # Fecth all needed certs one by one, then combine them in a single + # p12 file +@@ -129,6 +151,10 @@ class CustodiaInstance(SimpleServiceInstance): + prefix = data['prefix'] + certlist = data['list'] + ++ # Before we attempt to fetch keys from this host, make sure our public ++ # keys have been replicated there. ++ self.__wait_keys(ca_host) ++ + cli = self.__CustodiaClient(server=ca_host) + + # Temporary nssdb +diff --git a/ipaserver/secrets/kem.py b/ipaserver/secrets/kem.py +index 28fb4d31b35fc96c77ddd3f09cb3927efb4000fa..c1991c6b2ae00ed7147b2ec18389e463784b9f98 100644 +--- a/ipaserver/secrets/kem.py ++++ b/ipaserver/secrets/kem.py +@@ -24,6 +24,7 @@ import ldap + + IPA_REL_BASE_DN = 'cn=custodia,cn=ipa,cn=etc' + IPA_KEYS_QUERY = '(&(ipaKeyUsage={usage:s})(memberPrincipal={princ:s}))' ++IPA_CHECK_QUERY = '(cn=enc/{host:s})' + RFC5280_USAGE_MAP = {KEY_USAGE_SIG: 'digitalSignature', + KEY_USAGE_ENC: 'dataEncipherment'} + +@@ -78,6 +79,17 @@ class KEMLdap(iSecLdap): + jwk['use'] = KEY_USAGE_MAP[usage] + return json_encode(jwk) + ++ def check_host_keys(self, host): ++ conn = self.connect() ++ scope = ldap.SCOPE_SUBTREE ++ ++ ldap_filter = self.build_filter(IPA_CHECK_QUERY, {'host': host}) ++ r = conn.search_s(self.keysbase, scope, ldap_filter) ++ if len(r) != 1: ++ raise ValueError("Incorrect number of results (%d) searching for" ++ "public key for %s" % (len(r), host)) ++ return True ++ + def _format_public_key(self, key): + if isinstance(key, str): + jwkey = json_decode(key) +-- +2.12.2 + diff --git a/SOURCES/0118-Use-proper-SELinux-context-with-http.keytab.patch b/SOURCES/0118-Use-proper-SELinux-context-with-http.keytab.patch new file mode 100644 index 0000000..d5c36af --- /dev/null +++ b/SOURCES/0118-Use-proper-SELinux-context-with-http.keytab.patch @@ -0,0 +1,40 @@ +From 488c433c369bfcd13e95d910b500c455a01715b6 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 3 May 2017 13:51:02 +0200 +Subject: [PATCH] Use proper SELinux context with http.keytab + +During upgrade keytab is moved to a new location using "move" operation. +This commit replaces move operation with "copy" and "remove" that +ensures a proper selinux context. + +https://pagure.io/freeipa/issue/6924 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/server/upgrade.py | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 0f27428dd492bb44dd8c69a7e7f47abb531843f5..4d8fd666dfd4e918103b449d4c31bb7661727115 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1427,7 +1427,15 @@ def update_ipa_httpd_service_conf(http): + def update_http_keytab(http): + root_logger.info('[Moving HTTPD service keytab to gssproxy]') + if os.path.exists(paths.OLD_IPA_KEYTAB): +- shutil.move(paths.OLD_IPA_KEYTAB, http.keytab) ++ # ensure proper SELinux context by using copy operation ++ shutil.copy(paths.OLD_IPA_KEYTAB, http.keytab) ++ try: ++ os.remove(paths.OLD_IPA_KEYTAB) ++ except OSError as e: ++ root_logger.error( ++ 'Cannot remove file %s (%s). Please remove the file manually.', ++ paths.OLD_IPA_KEYTAB, e ++ ) + pent = pwd.getpwnam(http.keytab_user) + os.chown(http.keytab, pent.pw_uid, pent.pw_gid) + +-- +2.12.2 + diff --git a/SOURCES/0119-ipa-kra-install-fix-check_host_keys.patch b/SOURCES/0119-ipa-kra-install-fix-check_host_keys.patch new file mode 100644 index 0000000..50522f5 --- /dev/null +++ b/SOURCES/0119-ipa-kra-install-fix-check_host_keys.patch @@ -0,0 +1,47 @@ +From 85cd84580f45c99b6ab49814ead7eb2f259ca444 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 5 May 2017 17:06:09 +0200 +Subject: [PATCH] ipa-kra-install: fix check_host_keys + +ipa-kra-install on a replica checks that the keys are available before +going further to avoid race condition due to replication. The issue is +that the check_host_keys method expects to find exactly one key for +cn=env/host but 2 may exist: one below cn=custodia and one below +cn=dogtag,cn=custodia. +The fix is to check that at least one key exist (not exactly one key). + +https://pagure.io/freeipa/issue/6934 + +Reviewed-By: Martin Basti +--- + ipaserver/secrets/kem.py | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/secrets/kem.py b/ipaserver/secrets/kem.py +index c1991c6b2ae00ed7147b2ec18389e463784b9f98..3363d82fef54c463f498edeb95ddee454b20d31d 100644 +--- a/ipaserver/secrets/kem.py ++++ b/ipaserver/secrets/kem.py +@@ -72,7 +72,7 @@ class KEMLdap(iSecLdap): + 'princ': principal}) + r = conn.search_s(self.keysbase, scope, ldap_filter) + if len(r) != 1: +- raise ValueError("Incorrect number of results (%d) searching for" ++ raise ValueError("Incorrect number of results (%d) searching for " + "public key for %s" % (len(r), principal)) + ipa_public_key = r[0][1]['ipaPublicKey'][0] + jwk = self._parse_public_key(ipa_public_key) +@@ -85,9 +85,8 @@ class KEMLdap(iSecLdap): + + ldap_filter = self.build_filter(IPA_CHECK_QUERY, {'host': host}) + r = conn.search_s(self.keysbase, scope, ldap_filter) +- if len(r) != 1: +- raise ValueError("Incorrect number of results (%d) searching for" +- "public key for %s" % (len(r), host)) ++ if not r: ++ raise ValueError("No public keys were found for %s" % host) + return True + + def _format_public_key(self, key): +-- +2.12.2 + diff --git a/SOURCES/0120-python2-ipalib-add-missing-python-dependency.patch b/SOURCES/0120-python2-ipalib-add-missing-python-dependency.patch new file mode 100644 index 0000000..ef73fef --- /dev/null +++ b/SOURCES/0120-python2-ipalib-add-missing-python-dependency.patch @@ -0,0 +1,30 @@ +From d0df144c6411bc0966dfd475c5d781ac5d44f476 Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Tue, 2 May 2017 18:32:34 +0200 +Subject: [PATCH] python2-ipalib: add missing python dependency + +Commit dfd560a190cb2ab13f34ed9e21c5fb5c6e793f18 started to use +ssl symbols like ssl.OP_NO_SSLv2 that were introduced in Python 2.7.9. + +Related https://pagure.io/freeipa/issue/6920 + +Reviewed-By: Martin Babinsky +--- + freeipa.spec.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 3b7410b6bda3afc877d928b4df21529ae2faf0aa..1dd550bd39fd14349ede58bde337783aa5c0ea04 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -639,6 +639,7 @@ Requires: python-gssapi >= 1.2.0 + Requires: gnupg + Requires: keyutils + Requires: pyOpenSSL ++Requires: python >= 2.7.9 + Requires: python-nss >= 0.16 + Requires: python-cryptography >= 1.4 + Requires: python-netaddr +-- +2.9.3 + diff --git a/SOURCES/0121-installer-service-fix-typo-in-service-entry.patch b/SOURCES/0121-installer-service-fix-typo-in-service-entry.patch new file mode 100644 index 0000000..4bb270e --- /dev/null +++ b/SOURCES/0121-installer-service-fix-typo-in-service-entry.patch @@ -0,0 +1,31 @@ +From b7c0c372c3d78e60f5b2889c88f4f3b4a5abdcad Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Tue, 2 May 2017 18:42:13 +0200 +Subject: [PATCH] installer service: fix typo in service entry + +The typo would result in incorrect resolution of existing keys and +their existence wasn't properly logged as intended. + +Related https://pagure.io/freeipa/issue/6920 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/service.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 6b5e69ccd08444c591f15eb680b4cbdf5b6f4de1..1aa49ed25b25366afd2bb17073b4b214c231d54b 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -181,7 +181,7 @@ def set_service_entry_config(name, fqdn, config_values, + except errors.NotFound: + pass + else: +- existing_values = entry.get('ipaConnfigString', []) ++ existing_values = entry.get('ipaConfigString', []) + for value in config_values: + if case_insensitive_attr_has_value(existing_values, value): + root_logger.debug( +-- +2.9.3 + diff --git a/SOURCES/0122-upgrade-add-missing-suffix-to-http-instance.patch b/SOURCES/0122-upgrade-add-missing-suffix-to-http-instance.patch new file mode 100644 index 0000000..2dbb567 --- /dev/null +++ b/SOURCES/0122-upgrade-add-missing-suffix-to-http-instance.patch @@ -0,0 +1,31 @@ +From b16fba6f07455cc62284f0a225e2cd6aa6253efb Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Tue, 2 May 2017 19:26:04 +0200 +Subject: [PATCH] upgrade: add missing suffix to http instance + +During an upgrade, http.suffix is used to identify ldap entry when +configuring kdc proxy. When the suffix is missing, the script crashed +when enabling KDC proxy, because it used invalid DN. + +Fixes https://pagure.io/freeipa/issue/6920 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/server/upgrade.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 4d8fd666dfd4e918103b449d4c31bb7661727115..9aec2d857aee1a601f351218e253d44b14f6d4ec 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1638,6 +1638,7 @@ def upgrade_configuration(): + http = httpinstance.HTTPInstance(fstore) + http.fqdn = fqdn + http.realm = api.env.realm ++ http.suffix = ipautil.realm_to_suffix(api.env.realm) + http.configure_selinux_for_httpd() + http.change_mod_nss_port_from_http() + +-- +2.9.3 + diff --git a/SOURCES/0123-Turn-on-NSSOCSP-check-in-mod_nss-conf.patch b/SOURCES/0123-Turn-on-NSSOCSP-check-in-mod_nss-conf.patch new file mode 100644 index 0000000..8d4caa9 --- /dev/null +++ b/SOURCES/0123-Turn-on-NSSOCSP-check-in-mod_nss-conf.patch @@ -0,0 +1,227 @@ +From e8f329dd4340d5216d86160a8065e0530b981b47 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Thu, 6 Apr 2017 16:15:47 +0200 +Subject: [PATCH] Turn on NSSOCSP check in mod_nss conf + +Turn on NSSOCSP directive during install/replica install/upgrade. +That check whether the certificate which is used for login is +revoked or not using OSCP. + +Marks the server cert in httpd NSS DB as trusted peer ('P,,') +to avoid chicken and egg problem when it is needed to contact +the OCSP responder when httpd is starting. + +https://pagure.io/freeipa/issue/6370 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +Reviewed-By: Jan Cholasta +Reviewed-By: Martin Basti +--- + freeipa.spec.in | 4 ++++ + install/restart_scripts/restart_httpd | 14 +++++++++++++- + ipaserver/install/httpinstance.py | 30 ++++++++++++++++++++++++++++++ + ipaserver/install/server/upgrade.py | 25 +++++++++++++++++++++++++ + ipaserver/setup.py | 1 + + 5 files changed, 73 insertions(+), 1 deletion(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 1dd550bd39fd14349ede58bde337783aa5c0ea04..1b3ed15036eab6262b144d970cbdfdad31ac13ea 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -195,6 +195,7 @@ BuildRequires: python-nose + BuildRequires: python-paste + BuildRequires: systemd-python + BuildRequires: python2-jinja2 ++BuildRequires: python-augeas + + %if 0%{?with_python3} + # FIXME: this depedency is missing - server will not work +@@ -232,6 +233,7 @@ BuildRequires: python3-nose + BuildRequires: python3-paste + BuildRequires: python3-systemd + BuildRequires: python3-jinja2 ++BuildRequires: python3-augeas + %endif # with_python3 + %endif # with_lint + +@@ -355,6 +357,7 @@ Requires: python-dns >= 1.15 + Requires: python-kdcproxy >= 0.3 + Requires: rpm-libs + Requires: pki-base-python2 ++Requires: python-augeas + + %description -n python2-ipaserver + IPA is an integrated solution to provide centrally managed Identity (users, +@@ -384,6 +387,7 @@ Requires: python3-pyasn1 + Requires: python3-dbus + Requires: python3-dns >= 1.15 + Requires: python3-kdcproxy >= 0.3 ++Requires: python3-augeas + Requires: rpm-libs + Requires: pki-base-python3 + +diff --git a/install/restart_scripts/restart_httpd b/install/restart_scripts/restart_httpd +index d1684812904a9d32842a0ca548ec6b9df5a5a0b7..b661b82b896b109c3859ac82c2d84ab27b839f72 100644 +--- a/install/restart_scripts/restart_httpd ++++ b/install/restart_scripts/restart_httpd +@@ -21,11 +21,23 @@ + + import syslog + import traceback ++from ipalib import api + from ipaplatform import services +-from ipaserver.install import certs ++from ipaplatform.paths import paths ++from ipaserver.install import certs, installutils + + + def _main(): ++ ++ api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) ++ api.finalize() ++ ++ db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) ++ nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, "NSSNickname") ++ ++ # Add trust flag which set certificate trusted for SSL connections. ++ db.trust_root_cert(nickname, "P,,") ++ + syslog.syslog(syslog.LOG_NOTICE, 'certmonger restarted httpd') + + try: +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 7898c53bc02785e2750dba61a5696f079355c9d7..ab688a85f157b1886842a91bb7d22f9ea99e3615 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -29,6 +29,7 @@ import pipes + import locale + + import six ++from augeas import Augeas + + from ipalib.install import certmonger + from ipaserver.install import service +@@ -153,6 +154,7 @@ class HTTPInstance(service.Service): + self.set_mod_nss_protocol) + self.step("setting mod_nss password file", self.__set_mod_nss_passwordfile) + self.step("enabling mod_nss renegotiate", self.enable_mod_nss_renegotiate) ++ self.step("enabling mod_nss OCSP", self.enable_mod_nss_ocsp) + self.step("adding URL rewriting rules", self.__add_include) + self.step("configuring httpd", self.__configure_http) + self.step("setting up httpd keytab", self.request_service_keytab) +@@ -259,6 +261,31 @@ class HTTPInstance(service.Service): + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRenegotiation', 'on', False) + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRequireSafeNegotiation', 'on', False) + ++ def enable_mod_nss_ocsp(self): ++ aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD) ++ ++ aug.set('/augeas/load/Httpd/lens', 'Httpd.lns') ++ aug.set('/augeas/load/Httpd/incl', paths.HTTPD_NSS_CONF) ++ aug.load() ++ ++ path = '/files{}/VirtualHost'.format(paths.HTTPD_NSS_CONF) ++ ++ ocsp_comment = aug.get( ++ '{}/#comment[.=~regexp("NSSOCSP .*")]'.format(path)) ++ ocsp_dir = aug.get('{}/directive[.="NSSOCSP"]'.format(path)) ++ ++ if ocsp_dir is None and ocsp_comment is not None: ++ # Directive is missing, comment is present ++ aug.set('{}/#comment[.=~regexp("NSSOCSP .*")]'.format(path), ++ 'NSSOCSP') ++ aug.rename('{}/#comment[.="NSSOCSP"]'.format(path), 'directive') ++ elif ocsp_dir is None: ++ # Directive is missing and comment is missing ++ aug.set('{}/directive[last()+1]'.format(path), "NSSOCSP") ++ ++ aug.set('{}/directive[. = "NSSOCSP"]/arg'.format(path), 'on') ++ aug.save() ++ + def set_mod_nss_cipher_suite(self): + ciphers = ','.join(NSS_CIPHER_SUITE) + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSCipherSuite', ciphers, False) +@@ -351,6 +378,7 @@ class HTTPInstance(service.Service): + create=True) + self.disable_system_trust() + self.create_password_conf() ++ + if self.pkcs12_info: + if self.ca_is_configured: + trust_flags = 'CT,C,C' +@@ -375,6 +403,8 @@ class HTTPInstance(service.Service): + self.__set_mod_nss_nickname(nickname) + self.add_cert_to_service() + ++ db.trust_root_cert(nickname, "P,,") ++ + else: + if not self.promote: + ca_args = [ +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 9aec2d857aee1a601f351218e253d44b14f6d4ec..7b0476d442902f2c3dc65819d54953e820f5e560 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1392,6 +1392,24 @@ def fix_trust_flags(): + sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) + + ++def fix_server_cert_trust_flags(): ++ root_logger.info( ++ '[Fixing server certificate trust flags in %s]' % ++ paths.HTTPD_ALIAS_DIR) ++ ++ if sysupgrade.get_upgrade_state('http', 'fix_serv_cert_trust_flags'): ++ root_logger.info("Trust flags already processed") ++ return ++ ++ db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) ++ sc_nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, ++ "NSSNickname") ++ # Add trust flag which set certificate trusted for SSL connections. ++ db.trust_root_cert(sc_nickname, "P,,") ++ ++ sysupgrade.set_upgrade_state('http', 'fix_serv_cert_trust_flags', True) ++ ++ + def update_mod_nss_protocol(http): + root_logger.info('[Updating mod_nss protocol versions]') + +@@ -1404,6 +1422,11 @@ def update_mod_nss_protocol(http): + sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True) + + ++def enable_mod_nss_ocsp(http): ++ root_logger.info('[Updating mod_nss enabling OCSP]') ++ http.enable_mod_nss_ocsp() ++ ++ + def update_mod_nss_cipher_suite(http): + root_logger.info('[Updating mod_nss cipher suite]') + +@@ -1671,7 +1694,9 @@ def upgrade_configuration(): + update_ipa_httpd_service_conf(http) + update_mod_nss_protocol(http) + update_mod_nss_cipher_suite(http) ++ enable_mod_nss_ocsp(http) + fix_trust_flags() ++ fix_server_cert_trust_flags() + update_http_keytab(http) + http.configure_gssproxy() + http.start() +diff --git a/ipaserver/setup.py b/ipaserver/setup.py +index 42b0c1b0618ef9867acb1fe2add5702a756cf2d2..e0b69e547ef8c2b76ce14ab27c1c29260e33f57f 100755 +--- a/ipaserver/setup.py ++++ b/ipaserver/setup.py +@@ -60,6 +60,7 @@ if __name__ == '__main__': + "pyasn1", + "pyldap", + "six", ++ "python-augeas", + # not available on PyPI + # "python-libipa_hbac", + # "python-sss", +-- +2.9.3 + diff --git a/SOURCES/0124-cert-show-writable-files-does-not-mean-dirs.patch b/SOURCES/0124-cert-show-writable-files-does-not-mean-dirs.patch new file mode 100644 index 0000000..70b071f --- /dev/null +++ b/SOURCES/0124-cert-show-writable-files-does-not-mean-dirs.patch @@ -0,0 +1,31 @@ +From 348fdfd66d9b3ab0214af91d193ae93b9969610e Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 9 May 2017 17:49:56 +0200 +Subject: [PATCH] cert-show: writable files does not mean dirs + +ipalib.util.check_writable_file didn't check whether the argument +is an actual file which is now fixed. + +https://pagure.io/freeipa/issue/6883 + +Reviewed-By: Fraser Tweedale +--- + ipalib/util.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipalib/util.py b/ipalib/util.py +index 8973a19abf56d1d1c5ba04f6edb4228dd2329e65..713fc107e9374eefe7805bc4e1abc40b6d150c32 100644 +--- a/ipalib/util.py ++++ b/ipalib/util.py +@@ -170,7 +170,7 @@ def check_writable_file(filename): + if filename is None: + raise errors.FileError(reason=_('Filename is empty')) + try: +- if os.path.exists(filename): ++ if os.path.isfile(filename): + if not os.access(filename, os.W_OK): + raise errors.FileError(reason=_('Permission denied: %(file)s') % dict(file=filename)) + else: +-- +2.9.3 + diff --git a/SOURCES/0125-Bump-version-of-ipa.conf-file.patch b/SOURCES/0125-Bump-version-of-ipa.conf-file.patch new file mode 100644 index 0000000..d45ab9c --- /dev/null +++ b/SOURCES/0125-Bump-version-of-ipa.conf-file.patch @@ -0,0 +1,30 @@ +From dd433bd402b847b651ba2aa722e4b37c3235984b Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Thu, 11 May 2017 10:17:33 +0200 +Subject: [PATCH] Bump version of ipa.conf file + +In commit 157831a287c64106eed4 the version bump was forgotten and therefore the +ipa.conf file is not replaced during upgrade and login using certificate when +single certificate is mapped to multiple users doesn't work. + +https://pagure.io/freeipa/issue/6860 + +Reviewed-By: Martin Basti +--- + install/conf/ipa.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf +index 75c122e6c94b941c278d724add84315753082531..a7ca5ce715e55960b8edd307cdbe41dcbd6b29ca 100644 +--- a/install/conf/ipa.conf ++++ b/install/conf/ipa.conf +@@ -1,5 +1,5 @@ + # +-# VERSION 25 - DO NOT REMOVE THIS LINE ++# VERSION 26 - DO NOT REMOVE THIS LINE + # + # This file may be overwritten on upgrades. + # +-- +2.9.3 + diff --git a/SOURCES/0126-ipa-kra-install-manpage-document-domain-level-1.patch b/SOURCES/0126-ipa-kra-install-manpage-document-domain-level-1.patch new file mode 100644 index 0000000..35e0738 --- /dev/null +++ b/SOURCES/0126-ipa-kra-install-manpage-document-domain-level-1.patch @@ -0,0 +1,72 @@ +From 6534557508ba2ae7bda4ec2f2508d80dcef8297f Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 10 May 2017 18:04:52 +0200 +Subject: [PATCH] ipa-kra-install manpage: document domain-level 1 + +ipa-kra-install man page was missing a specific section for domain level 1. +This commits also fixes a wrong option short name (for --log-file) and +indents the text corresponding to -p DM_PASSWORD + +https://pagure.io/freeipa/issue/6922 + +Reviewed-By: Tomas Krizek +--- + install/tools/man/ipa-kra-install.1 | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +diff --git a/install/tools/man/ipa-kra-install.1 b/install/tools/man/ipa-kra-install.1 +index 0aa9073c3303bd852627e430102ceb40575decc4..51afaac6474a9483e8c02bae605fe95994cce1f2 100644 +--- a/install/tools/man/ipa-kra-install.1 ++++ b/install/tools/man/ipa-kra-install.1 +@@ -16,26 +16,37 @@ + .\" + .\" Author: Ade Lee + .\" +-.TH "ipa-kra-install" "1" "Aug 24 2014" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-kra-install" "1" "May 10 2017" "FreeIPA" "FreeIPA Manual Pages" + .SH "NAME" + ipa\-kra\-install \- Install a KRA on a server + .SH "SYNOPSIS" ++.SS "DOMAIN LEVEL 0" ++.TP + ipa\-kra\-install [\fIOPTION\fR]... [replica_file] ++.SS "DOMAIN LEVEL 1" ++.TP ++ipa\-kra\-install [\fIOPTION\fR]... + .SH "DESCRIPTION" + Adds a KRA as an IPA\-managed service. This requires that the IPA server is already installed and configured, including a CA. + + The KRA (Key Recovery Authority) is a component used to securely store secrets such as passwords, symmetric keys and private asymmetric keys. It is used as the back-end repository for the IPA Password Vault. + +-ipa\-kra\-install can be run without replica_file to add KRA to the existing CA. ++In a domain at domain level 0, ipa\-kra\-install can be run without replica_file to add KRA to the existing CA, or with replica_file to install the KRA service on the replica. + ipa\-kra\-install will contact the CA to determine if a KRA has already been installed on another replica, and if so, will exit indicating that a replica_file is required. + + The replica_file is created using the ipa\-replica\-prepare utility. A new replica_file should be generated on the master IPA server after the KRA has been installed and configured, so that the replica_file will contain the master KRA configuration and system certificates. + ++In a domain at domain level 1, ipa\-kra\-install can be used to add KRA to the existing CA, or to install the KRA service on a replica, and does not require any replica file. ++ + KRA can only be removed along with the entire server using ipa\-server\-install \-\-uninstall. + .SH "OPTIONS" ++.TP + \fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR + Directory Manager (existing master) password + .TP ++\fB\-\-no-host-dns\fR ++Do not use DNS for hostname lookup during installation ++.TP + \fB\-U\fR, \fB\-\-unattended\fR + An unattended installation that will never prompt for user input + .TP +@@ -45,7 +56,7 @@ Enable debug output when more verbose output is needed + \fB\-q\fR, \fB\-\-quiet\fR + Output only errors + .TP +-\fB\-v\fR, \fB\-\-log-file\fR=\fFILE\fR ++\fB\-\-log-file\fR=\fRFILE\fR + Log to the given file + .SH "EXIT STATUS" + 0 if the command was successful +-- +2.9.3 + diff --git a/SOURCES/0127-renew-agent-respect-CA-renewal-master-setting.patch b/SOURCES/0127-renew-agent-respect-CA-renewal-master-setting.patch new file mode 100644 index 0000000..a1aafa9 --- /dev/null +++ b/SOURCES/0127-renew-agent-respect-CA-renewal-master-setting.patch @@ -0,0 +1,54 @@ +From 55e779b19714532744c8b22e514e9e49563350e3 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 24 Apr 2017 05:24:24 +0000 +Subject: [PATCH] renew agent: respect CA renewal master setting + +Do not bypass the renewal master check when a non-virtual profile is used +in dogtag-ipa-ca-renew-agent-submit. + +This fixes dogtag-ipa-ca-renew-agent not respecting the CA renewal master +setting for certificates tracked with a real profile. (Note that there +currently aren't any such certificates tracked by us.) + +Request the RA certificate using dogtag-submit rather than +dogtag-ipa-ca-renew-agent-submit as the CA renewal master setting is not +available so early in the install process. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 2 +- + ipaserver/install/cainstance.py | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 7a3d9551884c0fe43566dd9012699211a39294eb..f253fd9587ac1ef3ece712ca9999c1ea4f3d55d8 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -535,7 +535,7 @@ def main(): + + profile = os.environ.get('CERTMONGER_CA_PROFILE') + if is_replicated(): +- if profile or is_renewal_master(): ++ if is_renewal_master(): + handler = request_and_store_cert + else: + handler = retrieve_cert_continuous +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index d72feb884964ecf49fe0166cbfeb3cb2c10737fe..97baa606c960806376e025b5654eea816da207ed 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -822,7 +822,7 @@ class CAInstance(DogtagInstance): + "-out", chain_file.name, + ], stdin=data, capture_output=False) + +- agent_args = [paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT, ++ agent_args = [paths.CERTMONGER_DOGTAG_SUBMIT, + "--dbdir", self.tmp_agent_db, + "--nickname", "ipa-ca-agent", + "--cafile", chain_file.name, +-- +2.9.3 + diff --git a/SOURCES/0128-server-upgrade-always-fix-certmonger-tracking-reques.patch b/SOURCES/0128-server-upgrade-always-fix-certmonger-tracking-reques.patch new file mode 100644 index 0000000..8e8a48d --- /dev/null +++ b/SOURCES/0128-server-upgrade-always-fix-certmonger-tracking-reques.patch @@ -0,0 +1,93 @@ +From ba42557e2acb526587b07956e75a2a1394882771 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 28 Feb 2017 10:55:54 +0000 +Subject: [PATCH] server upgrade: always fix certmonger tracking request + +Fix certmonger tracking requests on every run of ipa-server-upgrade rather +than only when the tracking configuration has changed and the requests have +not yet been updated. + +This allows fixing broken tracking requests just by re-running +ipa-server-upgrade. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/server/upgrade.py | 28 +++++++--------------------- + 1 file changed, 7 insertions(+), 21 deletions(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 7b0476d442902f2c3dc65819d54953e820f5e560..855056dc1fa20e813d82ecc5090a14cfc4f91831 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -905,8 +905,6 @@ def certificate_renewal_update(ca, ds, http): + template = paths.CERTMONGER_COMMAND_TEMPLATE + serverid = installutils.realm_to_serverid(api.env.realm) + +- # bump version when requests is changed +- version = 6 + requests = [ + { + 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, +@@ -971,25 +969,17 @@ def certificate_renewal_update(ca, ds, http): + } + ] + +- root_logger.info("[Update certmonger certificate renewal configuration to " +- "version %d]" % version) ++ root_logger.info("[Update certmonger certificate renewal configuration]") + if not ca.is_configured(): + root_logger.info('CA is not configured') + return False + +- state = 'certificate_renewal_update_%d' % version +- if sysupgrade.get_upgrade_state('dogtag', state): +- return False +- + # State not set, lets see if we are already configured + for request in requests: + request_id = certmonger.get_request_id(request) + if request_id is None: + break + else: +- sysupgrade.set_upgrade_state('dogtag', state, True) +- root_logger.info("Certmonger certificate renewal configuration is " +- "already at version %d" % version) + return False + + # Ok, now we need to stop tracking, then we can start tracking them +@@ -998,13 +988,11 @@ def certificate_renewal_update(ca, ds, http): + ds.stop_tracking_certificates(serverid) + http.stop_tracking_certificates() + +- if not sysupgrade.get_upgrade_state('dogtag', +- 'certificate_renewal_update_1'): +- filename = paths.CERTMONGER_CAS_CA_RENEWAL +- if os.path.exists(filename): +- with installutils.stopped_service('certmonger'): +- root_logger.info("Removing %s" % filename) +- installutils.remove_file(filename) ++ filename = paths.CERTMONGER_CAS_CA_RENEWAL ++ if os.path.exists(filename): ++ with installutils.stopped_service('certmonger'): ++ root_logger.info("Removing %s" % filename) ++ installutils.remove_file(filename) + + ca.configure_certmonger_renewal() + ca.configure_renewal() +@@ -1013,9 +1001,7 @@ def certificate_renewal_update(ca, ds, http): + ds.start_tracking_certificates(serverid) + http.start_tracking_certificates() + +- sysupgrade.set_upgrade_state('dogtag', state, True) +- root_logger.info("Certmonger certificate renewal configuration updated to " +- "version %d" % version) ++ root_logger.info("Certmonger certificate renewal configuration updated") + return True + + def copy_crl_file(old_path, new_path=None): +-- +2.9.3 + diff --git a/SOURCES/0129-cainstance-use-correct-profile-for-lightweight-CA-ce.patch b/SOURCES/0129-cainstance-use-correct-profile-for-lightweight-CA-ce.patch new file mode 100644 index 0000000..2865b5c --- /dev/null +++ b/SOURCES/0129-cainstance-use-correct-profile-for-lightweight-CA-ce.patch @@ -0,0 +1,182 @@ +From 1b17dfa9fb62215eeb1ceadc7902785a7ed384e9 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 28 Feb 2017 10:58:28 +0000 +Subject: [PATCH] cainstance: use correct profile for lightweight CA + certificates + +Use Dogtag's `caCACert` CA certificate profile rather than the +`ipaCACertRenewal` virtual profile for lightweight CA certificates. + +The `ipaCACertRenewal` virtual profile adds special handling of externally +signed CA certificates and LDAP replication of issued certificates on top +of `caCACert`, neither of which is relevant for lightweight CA +certificates. + +Remove all of the special casing of lightweight CA certificates from +dogtag-ipa-ca-renew-agent-submit. + +Make sure existing lightweight CA certmonger tracking requests are updated +on server upgrade. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + .../certmonger/dogtag-ipa-ca-renew-agent-submit | 36 +++------------------- + ipaserver/install/cainstance.py | 7 ++--- + ipaserver/install/server/upgrade.py | 16 ++++++++++ + 3 files changed, 23 insertions(+), 36 deletions(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index f253fd9587ac1ef3ece712ca9999c1ea4f3d55d8..51b0880c5b57758845e2ffa0c9545bbca7e8c751 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -98,25 +98,7 @@ def get_nickname(): + DN('CN=IPA RA', subject_base): 'ipaCert', + } + +- try: +- return nickname_by_subject_dn[DN(subject)] +- except KeyError: +- cas = api.Command.ca_find(ipacasubjectdn=DN(subject))['result'] +- if len(cas) == 0: +- return None +- return 'caSigningCert cert-pki-ca {}'.format(cas[0]['ipacaid'][0]) +- +- +-def is_lightweight_ca(): +- nickname = get_nickname() or '' +- return nickname != IPA_CA_NICKNAME and nickname.startswith(IPA_CA_NICKNAME) +- +-def is_renewable(): +- cert = os.environ.get('CERTMONGER_CERTIFICATE') +- if not cert: +- return False +- else: +- return x509.is_self_signed(cert) or is_lightweight_ca() ++ return nickname_by_subject_dn.get(DN(subject)) + + + def is_replicated(): +@@ -276,11 +258,6 @@ def store_cert(): + if not cert: + return (REJECTED, "New certificate requests not supported") + +- if is_lightweight_ca(): +- # Lightweight CAs are updated in Dogtag's NSSDB +- # by Dogtag itself, so do not store it +- return (ISSUED, cert) +- + dercert = x509.normalize_certificate(cert) + + dn = DN(('cn', nickname), ('cn', 'ca_renewal'), +@@ -405,12 +382,6 @@ def retrieve_cert_continuous(): + if old_cert: + old_cert = x509.normalize_certificate(old_cert) + +- if is_lightweight_ca(): +- # Lightweight CAs are updated in Dogtag's NSSDB +- # by Dogtag itself, so do not try to retrieve it. +- # Everything is fine as is. +- return (ISSUED, os.environ.get('CERTMONGER_CERTIFICATE')) +- + result = call_handler(retrieve_or_reuse_cert) + if result[0] != ISSUED: + return result +@@ -466,12 +437,13 @@ def renew_ca_cert(): + cert = os.environ.get('CERTMONGER_CERTIFICATE') + if not cert: + return (REJECTED, "New certificate requests not supported") ++ is_self_signed = x509.is_self_signed(cert) + + operation = os.environ.get('CERTMONGER_OPERATION') + if operation == 'SUBMIT': + state = 'retrieve' + +- if is_renewable() and is_renewal_master(): ++ if is_self_signed and is_renewal_master(): + state = 'request' + elif operation == 'POLL': + cookie = os.environ.get('CERTMONGER_CA_COOKIE') +@@ -489,7 +461,7 @@ def renew_ca_cert(): + + if state == 'retrieve': + result = call_handler(retrieve_cert) +- if result[0] == REJECTED and not is_renewable(): ++ if result[0] == REJECTED and not is_self_signed: + syslog.syslog(syslog.LOG_ALERT, + "Certificate with subject '%s' is about to expire, " + "use ipa-cacert-manage to renew it" +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 97baa606c960806376e025b5654eea816da207ed..546e1b7b39b323bbaeae3fb57d31ea4152d5e418 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -436,7 +436,7 @@ class CAInstance(DogtagInstance): + self.step("adding 'ipa' CA entry", ensure_ipa_authority_entry) + + self.step("configuring certmonger renewal for lightweight CAs", +- self.__add_lightweight_ca_tracking_requests) ++ self.add_lightweight_ca_tracking_requests) + + if ra_only: + runtime = None +@@ -1246,7 +1246,7 @@ class CAInstance(DogtagInstance): + os.chmod(keyfile, 0o600) + os.chown(keyfile, pent.pw_uid, pent.pw_gid) + +- def __add_lightweight_ca_tracking_requests(self): ++ def add_lightweight_ca_tracking_requests(self): + try: + lwcas = api.Backend.ldap2.get_entries( + base_dn=api.env.basedn, +@@ -1810,11 +1810,10 @@ def add_lightweight_ca_tracking_requests(logger, lwcas): + pin=certmonger.get_pin('internal'), + nickname=nickname, + ca=ipalib.constants.RENEWAL_CA_NAME, ++ profile='caCACert', + pre_command='stop_pkicad', + post_command='renew_ca_cert "%s"' % nickname, + ) +- request_id = certmonger.get_request_id(criteria) +- certmonger.modify(request_id, profile='ipaCACertRenewal') + logger.debug( + 'Lightweight CA renewal: ' + 'added tracking request for "%s"', nickname) +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 855056dc1fa20e813d82ecc5090a14cfc4f91831..96fdadf751ef619e198a861d9f62440c98f3abae 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -974,6 +974,21 @@ def certificate_renewal_update(ca, ds, http): + root_logger.info('CA is not configured') + return False + ++ db = certs.CertDB(api.env.realm, paths.PKI_TOMCAT_ALIAS_DIR) ++ for nickname, _trust_flags in db.list_certs(): ++ if nickname.startswith('caSigningCert cert-pki-ca '): ++ requests.append( ++ { ++ 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, ++ 'cert-nickname': nickname, ++ 'ca': 'dogtag-ipa-ca-renew-agent', ++ 'cert-presave-command': template % 'stop_pkicad', ++ 'cert-postsave-command': ++ (template % ('renew_ca_cert "%s"' % nickname)), ++ 'template-profile': 'caCACert', ++ } ++ ) ++ + # State not set, lets see if we are already configured + for request in requests: + request_id = certmonger.get_request_id(request) +@@ -998,6 +1013,7 @@ def certificate_renewal_update(ca, ds, http): + ca.configure_renewal() + ca.configure_agent_renewal() + ca.track_servercert() ++ ca.add_lightweight_ca_tracking_requests() + ds.start_tracking_certificates(serverid) + http.start_tracking_certificates() + +-- +2.9.3 + diff --git a/SOURCES/0130-renew-agent-allow-reusing-existing-certs.patch b/SOURCES/0130-renew-agent-allow-reusing-existing-certs.patch new file mode 100644 index 0000000..e1db085 --- /dev/null +++ b/SOURCES/0130-renew-agent-allow-reusing-existing-certs.patch @@ -0,0 +1,260 @@ +From 854ceb13a72630ba357ca5c1ec8ac5b320a4c9c5 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 19 Apr 2017 12:55:47 +0000 +Subject: [PATCH] renew agent: allow reusing existing certs + +Add a switch which makes `dogtag-ipa-ca-renew-agent-submit` reuse the +existing certificate rather than request a new one from the CA while +maintaining LDAP replication of the certificate. + +Make this available as a new `dogtag-ipa-ca-renew-agent-reuse` certmonger +CA. + +This allows redoing the LDAP replication and reexecuting pre- and post-save +commands of a tracking request without reissuing the certificate. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + .../certmonger/dogtag-ipa-ca-renew-agent-submit | 67 ++++++++++++++++------ + ipaserver/install/cainstance.py | 8 ++- + ipaserver/install/dogtaginstance.py | 15 +++-- + 3 files changed, 63 insertions(+), 27 deletions(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 51b0880c5b57758845e2ffa0c9545bbca7e8c751..7b5489555d069856a6da7a21b5ab2b0f4dd4a41c 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -193,10 +193,18 @@ def call_handler(_handler, *args, **kwargs): + + return result + +-def request_cert(): ++ ++def request_cert(reuse_existing, **kwargs): + """ + Request certificate from IPA CA. + """ ++ if reuse_existing: ++ cert = os.environ.get('CERTMONGER_CERTIFICATE') ++ if cert: ++ return (ISSUED, cert) ++ else: ++ return (REJECTED, "New certificate requests not supported") ++ + syslog.syslog(syslog.LOG_NOTICE, + "Forwarding request to dogtag-ipa-renew-agent") + +@@ -231,7 +239,8 @@ def request_cert(): + else: + return (rc, stdout) + +-def store_cert(): ++ ++def store_cert(**kwargs): + """ + Store certificate in LDAP. + """ +@@ -292,7 +301,8 @@ def store_cert(): + + return (ISSUED, cert) + +-def request_and_store_cert(): ++ ++def request_and_store_cert(**kwargs): + """ + Request certificate from IPA CA and store it in LDAP. + """ +@@ -318,7 +328,7 @@ def request_and_store_cert(): + else: + os.environ['CERTMONGER_CA_COOKIE'] = cookie + +- result = call_handler(request_cert) ++ result = call_handler(request_cert, **kwargs) + if result[0] == WAIT: + return (result[0], 'request:%s' % result[1]) + elif result[0] == WAIT_WITH_DELAY: +@@ -337,7 +347,7 @@ def request_and_store_cert(): + os.environ['CERTMONGER_CA_COOKIE'] = cookie + os.environ['CERTMONGER_CERTIFICATE'] = cert + +- result = call_handler(store_cert) ++ result = call_handler(store_cert, **kwargs) + if result[0] == WAIT: + return (result[0], 'store:%s:%s' % (cert, result[1])) + elif result[0] == WAIT_WITH_DELAY: +@@ -345,7 +355,8 @@ def request_and_store_cert(): + else: + return result + +-def retrieve_or_reuse_cert(): ++ ++def retrieve_or_reuse_cert(**kwargs): + """ + Retrieve certificate from LDAP. If the certificate is not available, reuse + the old certificate. +@@ -373,7 +384,8 @@ def retrieve_or_reuse_cert(): + + return (ISSUED, cert) + +-def retrieve_cert_continuous(): ++ ++def retrieve_cert_continuous(reuse_existing, **kwargs): + """ + Retrieve new certificate from LDAP. Repeat every eight hours until the + certificate is available. +@@ -382,8 +394,10 @@ def retrieve_cert_continuous(): + if old_cert: + old_cert = x509.normalize_certificate(old_cert) + +- result = call_handler(retrieve_or_reuse_cert) +- if result[0] != ISSUED: ++ result = call_handler(retrieve_or_reuse_cert, ++ reuse_existing=reuse_existing, ++ **kwargs) ++ if result[0] != ISSUED or reuse_existing: + return result + + new_cert = x509.normalize_certificate(result[1]) +@@ -394,17 +408,19 @@ def retrieve_cert_continuous(): + + return result + +-def retrieve_cert(): ++ ++def retrieve_cert(**kwargs): + """ + Retrieve new certificate from LDAP. + """ +- result = call_handler(retrieve_cert_continuous) ++ result = call_handler(retrieve_cert_continuous, **kwargs) + if result[0] == WAIT_WITH_DELAY: + return (REJECTED, "Updated certificate not available") + + return result + +-def export_csr(): ++ ++def export_csr(**kwargs): + """ + This does not actually renew the cert, it just writes the CSR provided + by certmonger to /var/lib/ipa/ca.csr and returns the existing cert. +@@ -430,7 +446,8 @@ def export_csr(): + + return (ISSUED, cert) + +-def renew_ca_cert(): ++ ++def renew_ca_cert(reuse_existing, **kwargs): + """ + This is used for automatic CA certificate renewal. + """ +@@ -443,7 +460,7 @@ def renew_ca_cert(): + if operation == 'SUBMIT': + state = 'retrieve' + +- if is_self_signed and is_renewal_master(): ++ if is_self_signed and not reuse_existing and is_renewal_master(): + state = 'request' + elif operation == 'POLL': + cookie = os.environ.get('CERTMONGER_CA_COOKIE') +@@ -460,8 +477,10 @@ def renew_ca_cert(): + return (OPERATION_NOT_SUPPORTED_BY_HELPER,) + + if state == 'retrieve': +- result = call_handler(retrieve_cert) +- if result[0] == REJECTED and not is_self_signed: ++ result = call_handler(retrieve_cert, ++ reuse_existing=reuse_existing, ++ **kwargs) ++ if result[0] == REJECTED and not is_self_signed and not reuse_existing: + syslog.syslog(syslog.LOG_ALERT, + "Certificate with subject '%s' is about to expire, " + "use ipa-cacert-manage to renew it" +@@ -469,7 +488,9 @@ def renew_ca_cert(): + elif state == 'request': + profile = os.environ['CERTMONGER_CA_PROFILE'] + os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert' +- result = call_handler(request_and_store_cert) ++ result = call_handler(request_and_store_cert, ++ reuse_existing=reuse_existing, ++ **kwargs) + os.environ['CERTMONGER_CA_PROFILE'] = profile + + if result[0] == WAIT: +@@ -480,6 +501,16 @@ def renew_ca_cert(): + return result + + def main(): ++ kwargs = { ++ 'reuse_existing': False, ++ } ++ try: ++ sys.argv.remove('--reuse-existing') ++ except ValueError: ++ pass ++ else: ++ kwargs['reuse_existing'] = True ++ + handlers = { + 'ipaStorage': store_cert, + 'ipaRetrievalOrReuse': retrieve_or_reuse_cert, +@@ -515,7 +546,7 @@ def main(): + handler = request_cert + handler = handlers.get(profile, handler) + +- res = call_handler(handler) ++ res = call_handler(handler, **kwargs) + for item in res[1:]: + print(item) + return res[0] +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 546e1b7b39b323bbaeae3fb57d31ea4152d5e418..ff5432d1a7460f1b853c7cf7490b3604c82cd1f7 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -964,9 +964,11 @@ class CAInstance(DogtagInstance): + obj = bus.get_object('org.fedorahosted.certmonger', + '/org/fedorahosted/certmonger') + iface = dbus.Interface(obj, 'org.fedorahosted.certmonger') +- path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent') +- if path: +- iface.remove_known_ca(path) ++ for suffix in ['', '-reuse']: ++ name = 'dogtag-ipa-ca-renew-agent' + suffix ++ path = iface.find_ca_by_nickname(name) ++ if path: ++ iface.remove_known_ca(path) + + cmonger.stop() + +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index 356358adf1b60e236ce821fb44a77ca5f8c1942f..e0515973f7a598b30c6f12675b9ebdbfd0cf3423 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -265,12 +265,15 @@ class DogtagInstance(service.Service): + obj = bus.get_object('org.fedorahosted.certmonger', + '/org/fedorahosted/certmonger') + iface = dbus.Interface(obj, 'org.fedorahosted.certmonger') +- path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent') +- if not path: +- iface.add_known_ca( +- 'dogtag-ipa-ca-renew-agent', +- paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT, +- dbus.Array([], dbus.Signature('s'))) ++ for suffix, args in [('', ''), ('-reuse', ' --reuse-existing')]: ++ name = 'dogtag-ipa-ca-renew-agent' + suffix ++ path = iface.find_ca_by_nickname(name) ++ if not path: ++ command = paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT + args ++ iface.add_known_ca( ++ name, ++ command, ++ dbus.Array([], dbus.Signature('s'))) + + def __get_pin(self): + try: +-- +2.9.3 + diff --git a/SOURCES/0131-renew-agent-always-export-CSR-on-IPA-CA-certificate-.patch b/SOURCES/0131-renew-agent-always-export-CSR-on-IPA-CA-certificate-.patch new file mode 100644 index 0000000..c13f5f9 --- /dev/null +++ b/SOURCES/0131-renew-agent-always-export-CSR-on-IPA-CA-certificate-.patch @@ -0,0 +1,51 @@ +From da3e6ab68f4f40b2851770fcc928b5bb93831c42 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 24 Apr 2017 06:20:07 +0000 +Subject: [PATCH] renew agent: always export CSR on IPA CA certificate renewal + +Make sure a CSR is exported for the IPA CA whenever certmonger detects that +the CA certificate is about to expire. + +This is a pre-requisite for using the `dogtag-ipa-ca-renew-agent-reuse` CA +instead of the `ipaCSRExport` virtual profile to export the CSR. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 7b5489555d069856a6da7a21b5ab2b0f4dd4a41c..657a1bc638e1da680522c638e92914098fc6ab4b 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -451,6 +451,10 @@ def renew_ca_cert(reuse_existing, **kwargs): + """ + This is used for automatic CA certificate renewal. + """ ++ csr = os.environ.get('CERTMONGER_CSR') ++ if not csr: ++ return (UNCONFIGURED, "Certificate request not provided") ++ + cert = os.environ.get('CERTMONGER_CERTIFICATE') + if not cert: + return (REJECTED, "New certificate requests not supported") +@@ -462,6 +466,13 @@ def renew_ca_cert(reuse_existing, **kwargs): + + if is_self_signed and not reuse_existing and is_renewal_master(): + state = 'request' ++ ++ csr_file = paths.IPA_CA_CSR ++ try: ++ with open(csr_file, 'wb') as f: ++ f.write(csr) ++ except Exception as e: ++ return (UNREACHABLE, "Failed to write %s: %s" % (csr_file, e)) + elif operation == 'POLL': + cookie = os.environ.get('CERTMONGER_CA_COOKIE') + if not cookie: +-- +2.9.3 + diff --git a/SOURCES/0132-renew-agent-get-rid-of-virtual-profiles.patch b/SOURCES/0132-renew-agent-get-rid-of-virtual-profiles.patch new file mode 100644 index 0000000..3088979 --- /dev/null +++ b/SOURCES/0132-renew-agent-get-rid-of-virtual-profiles.patch @@ -0,0 +1,321 @@ +From 6a30753c5a437240e4678ef4acae3255ad6d15ee Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 24 Apr 2017 06:40:11 +0000 +Subject: [PATCH] renew agent: get rid of virtual profiles + +Replace all uses of virtual profiles with `dogtag-ipa-ca-renew-agent-reuse` +and remove profile from the IPA CA certificate tracking request. + +This prevents virtual profiles from making their way into CSRs and in turn +being rejected by certain CAs. This affected the IPA CA CSR with Microsoft +CS in particular. + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + .../certmonger/dogtag-ipa-ca-renew-agent-submit | 50 ++++------------------ + ipaclient/install/ipa_certupdate.py | 4 +- + ipalib/install/certmonger.py | 25 ++++++++--- + ipaserver/install/cainstance.py | 8 ++-- + ipaserver/install/dogtaginstance.py | 6 +-- + ipaserver/install/ipa_cacert_manage.py | 12 +++--- + ipaserver/install/krainstance.py | 6 +-- + ipaserver/install/server/upgrade.py | 2 +- + 8 files changed, 46 insertions(+), 67 deletions(-) + +diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +index 657a1bc638e1da680522c638e92914098fc6ab4b..3d3e791449014082060ecdc50118a28a9ef315b8 100755 +--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit ++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit +@@ -297,7 +297,7 @@ def store_cert(**kwargs): + syslog.syslog( + syslog.LOG_ERR, + "Giving up. To retry storing the certificate, resubmit the " +- "request with profile \"ipaStorage\"") ++ "request with CA \"dogtag-ipa-ca-renew-agent-reuse\"") + + return (ISSUED, cert) + +@@ -420,33 +420,6 @@ def retrieve_cert(**kwargs): + return result + + +-def export_csr(**kwargs): +- """ +- This does not actually renew the cert, it just writes the CSR provided +- by certmonger to /var/lib/ipa/ca.csr and returns the existing cert. +- """ +- operation = os.environ.get('CERTMONGER_OPERATION') +- if operation != 'SUBMIT': +- return (OPERATION_NOT_SUPPORTED_BY_HELPER,) +- +- csr = os.environ.get('CERTMONGER_CSR') +- if not csr: +- return (UNCONFIGURED, "Certificate request not provided") +- +- cert = os.environ.get('CERTMONGER_CERTIFICATE') +- if not cert: +- return (REJECTED, "New certificate requests not supported") +- +- csr_file = paths.IPA_CA_CSR +- try: +- with open(csr_file, 'wb') as f: +- f.write(csr) +- except Exception as e: +- return (UNREACHABLE, "Failed to write %s: %s" % (csr_file, e)) +- +- return (ISSUED, cert) +- +- + def renew_ca_cert(reuse_existing, **kwargs): + """ + This is used for automatic CA certificate renewal. +@@ -497,12 +470,15 @@ def renew_ca_cert(reuse_existing, **kwargs): + "use ipa-cacert-manage to renew it" + % (os.environ.get("CERTMONGER_REQ_SUBJECT"),)) + elif state == 'request': +- profile = os.environ['CERTMONGER_CA_PROFILE'] ++ profile = os.environ.get('CERTMONGER_CA_PROFILE') + os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert' + result = call_handler(request_and_store_cert, + reuse_existing=reuse_existing, + **kwargs) +- os.environ['CERTMONGER_CA_PROFILE'] = profile ++ if profile is not None: ++ os.environ['CERTMONGER_CA_PROFILE'] = profile ++ else: ++ del os.environ['CERTMONGER_CA_PROFILE'] + + if result[0] == WAIT: + return (result[0], '%s:%s' % (state, result[1])) +@@ -522,14 +498,6 @@ def main(): + else: + kwargs['reuse_existing'] = True + +- handlers = { +- 'ipaStorage': store_cert, +- 'ipaRetrievalOrReuse': retrieve_or_reuse_cert, +- 'ipaRetrieval': retrieve_cert, +- 'ipaCSRExport': export_csr, +- 'ipaCACertRenewal': renew_ca_cert, +- } +- + api.bootstrap(in_server=True, context='renew', confdir=paths.ETC_IPA) + api.finalize() + +@@ -547,15 +515,15 @@ def main(): + + api.Backend.ldap2.connect() + +- profile = os.environ.get('CERTMONGER_CA_PROFILE') +- if is_replicated(): ++ if get_nickname() == IPA_CA_NICKNAME: ++ handler = renew_ca_cert ++ elif is_replicated(): + if is_renewal_master(): + handler = request_and_store_cert + else: + handler = retrieve_cert_continuous + else: + handler = request_cert +- handler = handlers.get(profile, handler) + + res = call_handler(handler, **kwargs) + for item in res[1:]: +diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py +index d6ffbde1900280b548877752726e4f066632877a..7dc88f07ae14e5416f6fe3dc8400b7d4bcabef72 100644 +--- a/ipaclient/install/ipa_certupdate.py ++++ b/ipaclient/install/ipa_certupdate.py +@@ -153,7 +153,7 @@ class CertUpdate(admintool.AdminTool): + + self.log.debug("resubmitting certmonger request '%s'", request_id) + certmonger.resubmit_request( +- request_id, profile='ipaRetrievalOrReuse') ++ request_id, ca='dogtag-ipa-ca-renew-agent-reuse', profile='') + try: + state = certmonger.wait_for_request(request_id, timeout) + except RuntimeError: +@@ -167,7 +167,7 @@ class CertUpdate(admintool.AdminTool): + "please check the request manually" % request_id) + + self.log.debug("modifying certmonger request '%s'", request_id) +- certmonger.modify(request_id, profile='ipaCACertRenewal') ++ certmonger.modify(request_id, ca='dogtag-ipa-ca-renew-agent') + + self.update_file(paths.CA_CRT, certs) + +diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py +index 2a7876ea9ba986f57c00fc7ad61d10fe91894f55..5709853ffebdbf58929b9a935e906ae67341bea8 100644 +--- a/ipalib/install/certmonger.py ++++ b/ipalib/install/certmonger.py +@@ -501,18 +501,29 @@ def stop_tracking(secdir=None, request_id=None, nickname=None, certfile=None): + request.parent.obj_if.remove_request(request.path) + + +-def modify(request_id, profile=None): +- if profile: ++def modify(request_id, ca=None, profile=None): ++ if ca or profile: + request = _get_request({'nickname': request_id}) +- if request: +- request.obj_if.modify({'template-profile': profile}) ++ update = {} ++ if ca is not None: ++ cm = _certmonger() ++ update['CA'] = cm.obj_if.find_ca_by_nickname(ca) ++ if profile is not None: ++ update['template-profile'] = profile ++ request.obj_if.modify(update) + + +-def resubmit_request(request_id, profile=None): ++def resubmit_request(request_id, ca=None, profile=None): + request = _get_request({'nickname': request_id}) + if request: +- if profile: +- request.obj_if.modify({'template-profile': profile}) ++ if ca or profile: ++ update = {} ++ if ca is not None: ++ cm = _certmonger() ++ update['CA'] = cm.obj_if.find_ca_by_nickname(ca) ++ if profile is not None: ++ update['template-profile'] = profile ++ request.obj_if.modify(update) + request.obj_if.resubmit() + + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index ff5432d1a7460f1b853c7cf7490b3604c82cd1f7..a4aa4f2069277181501ebd92f3795c452b10acd0 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -279,10 +279,10 @@ class CAInstance(DogtagInstance): + 2 = have signed cert, continue installation + """ + +- tracking_reqs = (('auditSigningCert cert-pki-ca', None), +- ('ocspSigningCert cert-pki-ca', None), +- ('subsystemCert cert-pki-ca', None), +- ('caSigningCert cert-pki-ca', 'ipaCACertRenewal')) ++ tracking_reqs = ('auditSigningCert cert-pki-ca', ++ 'ocspSigningCert cert-pki-ca', ++ 'subsystemCert cert-pki-ca', ++ 'caSigningCert cert-pki-ca') + server_cert_name = 'Server-Cert cert-pki-ca' + + def __init__(self, realm=None, host_name=None): +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index e0515973f7a598b30c6f12675b9ebdbfd0cf3423..3ba13815055612c5fff44831c8f874e6175d94cd 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -287,7 +287,7 @@ class DogtagInstance(service.Service): + """ Configure certmonger to renew system certs """ + pin = self.__get_pin() + +- for nickname, profile in self.tracking_reqs: ++ for nickname in self.tracking_reqs: + try: + certmonger.start_tracking( + certpath=self.nss_db, +@@ -296,7 +296,7 @@ class DogtagInstance(service.Service): + pin=pin, + pre_command='stop_pkicad', + post_command='renew_ca_cert "%s"' % nickname, +- profile=profile) ++ ) + except RuntimeError as e: + self.log.error( + "certmonger failed to start tracking certificate: %s", e) +@@ -331,7 +331,7 @@ class DogtagInstance(service.Service): + services.knownservices.messagebus.start() + cmonger.start() + +- nicknames = [nickname for nickname, _profile in self.tracking_reqs] ++ nicknames = self.tracking_reqs + if self.server_cert_name is not None: + nicknames.append(self.server_cert_name) + +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index 363ba378ab206fae5c220b754f212666f20384af..6d28c62b36b3909c9a3d95a5c6c84d1779fe4c33 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -172,14 +172,14 @@ class CACertManage(admintool.AdminTool): + except errors.NotFound: + raise admintool.ScriptError("CA renewal master not found") + +- self.resubmit_request(ca, 'caCACert') ++ self.resubmit_request() + + print("CA certificate successfully renewed") + + def renew_external_step_1(self, ca): + print("Exporting CA certificate signing request, please wait") + +- self.resubmit_request(ca, 'ipaCSRExport') ++ self.resubmit_request('dogtag-ipa-ca-renew-agent-reuse') + + print(("The next step is to get %s signed by your CA and re-run " + "ipa-cacert-manage as:" % paths.IPA_CA_CSR)) +@@ -282,15 +282,15 @@ class CACertManage(admintool.AdminTool): + except errors.NotFound: + raise admintool.ScriptError("CA renewal master not found") + +- self.resubmit_request(ca, 'ipaRetrieval') ++ self.resubmit_request('dogtag-ipa-ca-renew-agent-reuse') + + print("CA certificate successfully renewed") + +- def resubmit_request(self, ca, profile): ++ def resubmit_request(self, ca='dogtag-ipa-ca-renew-agent'): + timeout = api.env.startup_timeout + 60 + + self.log.debug("resubmitting certmonger request '%s'", self.request_id) +- certmonger.resubmit_request(self.request_id, profile=profile) ++ certmonger.resubmit_request(self.request_id, ca=ca, profile='') + try: + state = certmonger.wait_for_request(self.request_id, timeout) + except RuntimeError: +@@ -304,7 +304,7 @@ class CACertManage(admintool.AdminTool): + "please check the request manually" % self.request_id) + + self.log.debug("modifying certmonger request '%s'", self.request_id) +- certmonger.modify(self.request_id, profile='ipaCACertRenewal') ++ certmonger.modify(self.request_id, ca='dogtag-ipa-ca-renew-agent') + + def install(self): + print("Installing CA certificate, please wait") +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index c39d6874a9d685f91b5d30ea1954320b8ee0c1ed..abb81897a404613e20be10d348096402ef08624b 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -60,9 +60,9 @@ class KRAInstance(DogtagInstance): + be the same for both the CA and KRA. + """ + +- tracking_reqs = (('auditSigningCert cert-pki-kra', None), +- ('transportCert cert-pki-kra', None), +- ('storageCert cert-pki-kra', None)) ++ tracking_reqs = ('auditSigningCert cert-pki-kra', ++ 'transportCert cert-pki-kra', ++ 'storageCert cert-pki-kra') + + def __init__(self, realm): + super(KRAInstance, self).__init__( +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 96fdadf751ef619e198a861d9f62440c98f3abae..5e5c83731d3d3415deb61271baa7865c62f60336 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -937,7 +937,7 @@ def certificate_renewal_update(ca, ds, http): + 'cert-presave-command': template % 'stop_pkicad', + 'cert-postsave-command': + (template % 'renew_ca_cert "caSigningCert cert-pki-ca"'), +- 'template-profile': 'ipaCACertRenewal', ++ 'template-profile': '', + }, + { + 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, +-- +2.9.3 + diff --git a/SOURCES/0133-ipa-cacert-manage-add-external-ca-type.patch b/SOURCES/0133-ipa-cacert-manage-add-external-ca-type.patch new file mode 100644 index 0000000..0e037b2 --- /dev/null +++ b/SOURCES/0133-ipa-cacert-manage-add-external-ca-type.patch @@ -0,0 +1,97 @@ +From 993d41e5105653412cec40b8e2a386da802a62bb Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 24 Apr 2017 07:10:41 +0000 +Subject: [PATCH] ipa-cacert-manage: add --external-ca-type + +Add the `--external-ca-type`, as known from `ipa-server-install` and +`ipa-ca-install`, to `ipa-cacert-manage`. + +This allows creating IPA CA CSRs suitable for use with Microsoft CS using +`ipa-cacert-manage`: + +``` +ipa-cacert-manage renew --external-ca --external-ca-type=ms-cs +``` + +https://pagure.io/freeipa/issue/5799 + +Reviewed-By: David Kupka +Reviewed-By: Stanislav Laznicka +--- + install/tools/man/ipa-cacert-manage.1 | 3 +++ + ipaserver/install/ipa_cacert_manage.py | 21 +++++++++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1 +index 128edd8bd2500a09f406da8dc01a53b269007ab0..e36258d0f96aa1050fe88b05f4fe9a1a8f9a7978 100644 +--- a/install/tools/man/ipa-cacert-manage.1 ++++ b/install/tools/man/ipa-cacert-manage.1 +@@ -78,6 +78,9 @@ Sign the renewed certificate by itself. + \fB\-\-external\-ca\fR + Sign the renewed certificate by external CA. + .TP ++\fB\-\-external\-ca\-type\fR=\fITYPE\fR ++Type of the external CA. Possible values are "generic", "ms-cs". Default value is "generic". Use "ms-cs" to include template name required by Microsoft Certificate Services (MS CS) in the generated CSR. ++.TP + \fB\-\-external\-cert\-file\fR=\fIFILE\fR + File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. + .RE +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index 6d28c62b36b3909c9a3d95a5c6c84d1779fe4c33..3b732e4dcbb5c9b4dfbb9e3608bc7d7afd3e10c2 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -54,6 +54,12 @@ class CACertManage(admintool.AdminTool): + "--self-signed", dest='self_signed', + action='store_true', + help="Sign the renewed certificate by itself") ++ ext_cas = ("generic", "ms-cs") ++ renew_group.add_option( ++ "--external-ca-type", dest="external_ca_type", ++ type="choice", choices=ext_cas, ++ metavar="{{{0}}}".format(",".join(ext_cas)), ++ help="Type of the external CA. Default: generic") + renew_group.add_option( + "--external-ca", dest='self_signed', + action='store_false', +@@ -179,7 +185,12 @@ class CACertManage(admintool.AdminTool): + def renew_external_step_1(self, ca): + print("Exporting CA certificate signing request, please wait") + +- self.resubmit_request('dogtag-ipa-ca-renew-agent-reuse') ++ if self.options.external_ca_type == 'ms-cs': ++ profile = 'SubCA' ++ else: ++ profile = '' ++ ++ self.resubmit_request('dogtag-ipa-ca-renew-agent-reuse', profile) + + print(("The next step is to get %s signed by your CA and re-run " + "ipa-cacert-manage as:" % paths.IPA_CA_CSR)) +@@ -286,11 +297,11 @@ class CACertManage(admintool.AdminTool): + + print("CA certificate successfully renewed") + +- def resubmit_request(self, ca='dogtag-ipa-ca-renew-agent'): ++ def resubmit_request(self, ca='dogtag-ipa-ca-renew-agent', profile=''): + timeout = api.env.startup_timeout + 60 + + self.log.debug("resubmitting certmonger request '%s'", self.request_id) +- certmonger.resubmit_request(self.request_id, ca=ca, profile='') ++ certmonger.resubmit_request(self.request_id, ca=ca, profile=profile) + try: + state = certmonger.wait_for_request(self.request_id, timeout) + except RuntimeError: +@@ -304,7 +315,9 @@ class CACertManage(admintool.AdminTool): + "please check the request manually" % self.request_id) + + self.log.debug("modifying certmonger request '%s'", self.request_id) +- certmonger.modify(self.request_id, ca='dogtag-ipa-ca-renew-agent') ++ certmonger.modify(self.request_id, ++ ca='dogtag-ipa-ca-renew-agent', ++ profile='') + + def install(self): + print("Installing CA certificate, please wait") +-- +2.9.3 + diff --git a/SOURCES/0134-Fixing-adding-authenticator-indicators-to-host.patch b/SOURCES/0134-Fixing-adding-authenticator-indicators-to-host.patch new file mode 100644 index 0000000..e080db4 --- /dev/null +++ b/SOURCES/0134-Fixing-adding-authenticator-indicators-to-host.patch @@ -0,0 +1,70 @@ +From 43871c023ac22a0ae2c4b5fb264b69c6e8029f49 Mon Sep 17 00:00:00 2001 +From: Felipe Volpone +Date: Thu, 11 May 2017 10:26:03 -0300 +Subject: [PATCH] Fixing adding authenticator indicators to host + +The check for krbprincipalaux in the entries is now made +case-insensitively. + +https://pagure.io/freeipa/issue/6911 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Petr Vobornik +--- + ipaserver/plugins/host.py | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py +index dcadd54a10692f64f0464d964f43c7881875d433..1e1f9d82dfdfcf9e7fef65ce729cd8ee7b76e605 100644 +--- a/ipaserver/plugins/host.py ++++ b/ipaserver/plugins/host.py +@@ -884,7 +884,8 @@ class host_mod(LDAPUpdate): + msg = 'Principal name already set, it is unchangeable.' + raise errors.ACIError(info=msg) + obj_classes = entry_attrs_old['objectclass'] +- if 'krbprincipalaux' not in obj_classes: ++ if 'krbprincipalaux' not in (item.lower() for item in ++ obj_classes): + obj_classes.append('krbprincipalaux') + entry_attrs['objectclass'] = obj_classes + +@@ -920,7 +921,7 @@ class host_mod(LDAPUpdate): + else: + _entry_attrs = ldap.get_entry(dn, ['objectclass']) + obj_classes = _entry_attrs['objectclass'] +- if 'ieee802device' not in obj_classes: ++ if 'ieee802device' not in (item.lower() for item in obj_classes): + obj_classes.append('ieee802device') + entry_attrs['objectclass'] = obj_classes + +@@ -940,7 +941,7 @@ class host_mod(LDAPUpdate): + else: + _entry_attrs = ldap.get_entry(dn, ['objectclass']) + obj_classes = entry_attrs['objectclass'] = _entry_attrs['objectclass'] +- if 'ipasshhost' not in obj_classes: ++ if 'ipasshhost' not in (item.lower() for item in obj_classes): + obj_classes.append('ipasshhost') + + update_krbticketflags(ldap, entry_attrs, attrs_list, options, True) +@@ -949,14 +950,16 @@ class host_mod(LDAPUpdate): + if 'objectclass' not in entry_attrs: + entry_attrs_old = ldap.get_entry(dn, ['objectclass']) + entry_attrs['objectclass'] = entry_attrs_old['objectclass'] +- if 'krbticketpolicyaux' not in entry_attrs['objectclass']: ++ if 'krbticketpolicyaux' not in (item.lower() for item in ++ entry_attrs['objectclass']): + entry_attrs['objectclass'].append('krbticketpolicyaux') + + if 'krbprincipalauthind' in entry_attrs: + if 'objectclass' not in entry_attrs: + entry_attrs_old = ldap.get_entry(dn, ['objectclass']) + entry_attrs['objectclass'] = entry_attrs_old['objectclass'] +- if 'krbprincipalaux' not in entry_attrs['objectclass']: ++ if 'krbprincipalaux' not in (item.lower() for item in ++ entry_attrs['objectclass']): + entry_attrs['objectclass'].append('krbprincipalaux') + + add_sshpubkey_to_attrs_pre(self.context, attrs_list) +-- +2.9.3 + diff --git a/SOURCES/0135-Added-plugins-directory-to-ipaclient-subpackages.patch b/SOURCES/0135-Added-plugins-directory-to-ipaclient-subpackages.patch new file mode 100644 index 0000000..eb46399 --- /dev/null +++ b/SOURCES/0135-Added-plugins-directory-to-ipaclient-subpackages.patch @@ -0,0 +1,35 @@ +From b62710ef8fe96ac012d61f6fc76f7d4e69a49f09 Mon Sep 17 00:00:00 2001 +From: Oliver Gutierrez +Date: Fri, 28 Apr 2017 15:21:49 +0100 +Subject: [PATCH] Added plugins directory to ipaclient subpackages + +https://pagure.io/freeipa/issue/6927 + +Reviewed-By: Pavel Vomacka +--- + freeipa.spec.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 1b3ed15036eab6262b144d970cbdfdad31ac13ea..3a5a9b4087d2394d6e8556d62da46a3dc922c913 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1404,6 +1404,7 @@ fi + %doc README.md Contributors.txt + %license COPYING + %dir %{python_sitelib}/ipaclient ++%dir %{python_sitelib}/ipaclient/plugins + %{python_sitelib}/ipaclient/*.py* + %{python_sitelib}/ipaclient/install/*.py* + %{python_sitelib}/ipaclient/plugins/*.py* +@@ -1422,6 +1423,7 @@ fi + %doc README.md Contributors.txt + %license COPYING + %dir %{python3_sitelib}/ipaclient ++%dir %{python3_sitelib}/ipaclient/plugins + %{python3_sitelib}/ipaclient/*.py + %{python3_sitelib}/ipaclient/__pycache__/*.py* + %{python3_sitelib}/ipaclient/install/*.py +-- +2.9.3 + diff --git a/SOURCES/0136-ipaclient-fix-missing-RPM-ownership.patch b/SOURCES/0136-ipaclient-fix-missing-RPM-ownership.patch new file mode 100644 index 0000000..3f1cbff --- /dev/null +++ b/SOURCES/0136-ipaclient-fix-missing-RPM-ownership.patch @@ -0,0 +1,71 @@ +From 6f2708e350a096a8d3ea4feb177370d9cb1afa81 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 10 May 2017 18:39:22 +0200 +Subject: [PATCH] ipaclient: fix missing RPM ownership + +FreeIPA package should own all subdirectories to work properly with +3rd party packages/plugins. + +https://pagure.io/freeipa/issue/6927 + +Reviewed-By: Pavel Vomacka +--- + freeipa.spec.in | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 3a5a9b4087d2394d6e8556d62da46a3dc922c913..0335a9970be82e80e98696f3d7fd4ec64894ef5f 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1404,14 +1404,20 @@ fi + %doc README.md Contributors.txt + %license COPYING + %dir %{python_sitelib}/ipaclient +-%dir %{python_sitelib}/ipaclient/plugins + %{python_sitelib}/ipaclient/*.py* ++%dir %{python_sitelib}/ipaclient/install + %{python_sitelib}/ipaclient/install/*.py* ++%dir %{python_sitelib}/ipaclient/plugins + %{python_sitelib}/ipaclient/plugins/*.py* ++%dir %{python_sitelib}/ipaclient/remote_plugins + %{python_sitelib}/ipaclient/remote_plugins/*.py* + %{python_sitelib}/ipaclient/remote_plugins/2_*/*.py* ++%dir %{python_sitelib}/ipaclient/csrgen ++%dir %{python_sitelib}/ipaclient/csrgen/profiles + %{python_sitelib}/ipaclient/csrgen/profiles/*.json ++%dir %{python_sitelib}/ipaclient/csrgen/rules + %{python_sitelib}/ipaclient/csrgen/rules/*.json ++%dir %{python_sitelib}/ipaclient/csrgen/templates + %{python_sitelib}/ipaclient/csrgen/templates/*.tmpl + %{python_sitelib}/ipaclient-*.egg-info + +@@ -1423,19 +1429,25 @@ fi + %doc README.md Contributors.txt + %license COPYING + %dir %{python3_sitelib}/ipaclient +-%dir %{python3_sitelib}/ipaclient/plugins + %{python3_sitelib}/ipaclient/*.py + %{python3_sitelib}/ipaclient/__pycache__/*.py* ++%dir %{python3_sitelib}/ipaclient/install + %{python3_sitelib}/ipaclient/install/*.py + %{python3_sitelib}/ipaclient/install/__pycache__/*.py* ++%dir %{python3_sitelib}/ipaclient/plugins + %{python3_sitelib}/ipaclient/plugins/*.py + %{python3_sitelib}/ipaclient/plugins/__pycache__/*.py* ++%dir %{python3_sitelib}/ipaclient/remote_plugins + %{python3_sitelib}/ipaclient/remote_plugins/*.py + %{python3_sitelib}/ipaclient/remote_plugins/__pycache__/*.py* + %{python3_sitelib}/ipaclient/remote_plugins/2_*/*.py + %{python3_sitelib}/ipaclient/remote_plugins/2_*/__pycache__/*.py* ++%dir %{python3_sitelib}/ipaclient/csrgen ++%dir %{python3_sitelib}/ipaclient/csrgen/profiles + %{python3_sitelib}/ipaclient/csrgen/profiles/*.json ++%dir %{python3_sitelib}/ipaclient/csrgen/rules + %{python3_sitelib}/ipaclient/csrgen/rules/*.json ++%dir %{python3_sitelib}/ipaclient/csrgen/templates + %{python3_sitelib}/ipaclient/csrgen/templates/*.tmpl + %{python3_sitelib}/ipaclient-*.egg-info + +-- +2.9.3 + diff --git a/SOURCES/0137-otptoken-add-yubikey-When-digits-not-provided-use-de.patch b/SOURCES/0137-otptoken-add-yubikey-When-digits-not-provided-use-de.patch new file mode 100644 index 0000000..62760ea --- /dev/null +++ b/SOURCES/0137-otptoken-add-yubikey-When-digits-not-provided-use-de.patch @@ -0,0 +1,36 @@ +From b16661f4dab141ef692ed6c893c0cb05566b64ec Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Fri, 12 May 2017 17:17:05 +0200 +Subject: [PATCH] otptoken-add-yubikey: When --digits not provided use default + value + +Since Thin client was introduced default values for options are not populated +in client side plugins. When option has default value and is needed in client +plugin it must be handled by explicitly. + +https://pagure.io/freeipa/issue/6900 + +Reviewed-By: Stanislav Laznicka +--- + ipaclient/plugins/otptoken_yubikey.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ipaclient/plugins/otptoken_yubikey.py b/ipaclient/plugins/otptoken_yubikey.py +index 759b7226819f0fa693e475b25262b5d93ac2d39f..9e0e98996ca2d34f34f61c9c07088a3f544ad166 100644 +--- a/ipaclient/plugins/otptoken_yubikey.py ++++ b/ipaclient/plugins/otptoken_yubikey.py +@@ -142,7 +142,10 @@ class otptoken_add_yubikey(Command): + + # Write the config. + cfg = yk.init_config() +- cfg.mode_oath_hotp(key, kwargs['ipatokenotpdigits']) ++ cfg.mode_oath_hotp(key, kwargs.get( ++ 'ipatokenotpdigits', ++ self.get_default_of('ipatokenotpdigits') ++ )) + cfg.extended_flag('SERIAL_API_VISIBLE', True) + yk.write_config(cfg, slot=kwargs['slot']) + +-- +2.9.3 + diff --git a/SOURCES/0138-ipa-server-install-fix-uninstall.patch b/SOURCES/0138-ipa-server-install-fix-uninstall.patch new file mode 100644 index 0000000..badd794 --- /dev/null +++ b/SOURCES/0138-ipa-server-install-fix-uninstall.patch @@ -0,0 +1,34 @@ +From 632a1d97c2110cf8ccb4311fac51b98b03b7e26b Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 15 May 2017 16:36:44 +0200 +Subject: [PATCH] ipa-server-install: fix uninstall + +ipa-server-install --uninstall fails to stop tracking the certificates +because it assigns a tuple to the variable nicknames, then tries to +call nicknames.append(). This is a regression introduced by 21f4cbf8. + +Assignment should be done using nicknames = list(self.tracking_reqs) instead. + +https://pagure.io/freeipa/issue/6950 + +Reviewed-By: Jan Cholasta +--- + ipaserver/install/dogtaginstance.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index 3ba13815055612c5fff44831c8f874e6175d94cd..4c6e1f70672f1553696d53bcd0cf8064c411441d 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -331,7 +331,7 @@ class DogtagInstance(service.Service): + services.knownservices.messagebus.start() + cmonger.start() + +- nicknames = self.tracking_reqs ++ nicknames = list(self.tracking_reqs) + if self.server_cert_name is not None: + nicknames.append(self.server_cert_name) + +-- +2.9.4 + diff --git a/SOURCES/0139-ca-install-merge-duplicated-code-for-DM-password.patch b/SOURCES/0139-ca-install-merge-duplicated-code-for-DM-password.patch new file mode 100644 index 0000000..e0ac02e --- /dev/null +++ b/SOURCES/0139-ca-install-merge-duplicated-code-for-DM-password.patch @@ -0,0 +1,85 @@ +From fd7b5b32f907fff7c454a91838f5483501220971 Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Wed, 3 May 2017 10:05:25 +0200 +Subject: [PATCH] ca install: merge duplicated code for DM password + +Extract copy-pasted code to a single function. + +Related https://pagure.io/freeipa/issue/6892 + +Signed-off-by: Tomas Krizek +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + install/tools/ipa-ca-install | 40 +++++++++++++++++----------------------- + 1 file changed, 17 insertions(+), 23 deletions(-) + +diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install +index c05abb9646884ad5a4411dba98df466c37f09613..4bcb59a29d5a64c118649374104ae8f1cd451ea4 100755 +--- a/install/tools/ipa-ca-install ++++ b/install/tools/ipa-ca-install +@@ -116,9 +116,19 @@ def parse_options(): + return safe_options, options, filename + + +-def get_dirman_password(): +- return installutils.read_password( +- "Directory Manager (existing master)", confirm=False, validate=False) ++def _get_dirman_password(password=None, unattended=False): ++ if not password: ++ if unattended: ++ sys.exit('Directory Manager password required') ++ try: ++ password = installutils.read_password( ++ "Directory Manager (existing master)", confirm=False, ++ validate=False) ++ except KeyboardInterrupt: ++ sys.exit(0) ++ if password is None: ++ sys.exit("Directory Manager password required") ++ return password + + + def install_replica(safe_options, options, filename): +@@ -142,16 +152,8 @@ def install_replica(safe_options, options, filename): + check_creds(options, api.env.realm) + + # get the directory manager password +- dirman_password = options.password +- if not dirman_password: +- if options.unattended: +- sys.exit('Directory Manager password required') +- try: +- dirman_password = get_dirman_password() +- except KeyboardInterrupt: +- sys.exit(0) +- if dirman_password is None: +- sys.exit("Directory Manager password required") ++ dirman_password = _get_dirman_password( ++ options.password, options.unattended) + + if (not options.promote and not options.admin_password and + not options.skip_conncheck and options.unattended): +@@ -199,16 +201,8 @@ def install_replica(safe_options, options, filename): + + + def install_master(safe_options, options): +- dm_password = options.password +- if not dm_password: +- if options.unattended: +- sys.exit('Directory Manager password required') +- try: +- dm_password = get_dirman_password() +- except KeyboardInterrupt: +- sys.exit(0) +- if dm_password is None: +- sys.exit("Directory Manager password required") ++ dm_password = _get_dirman_password( ++ options.password, options.unattended) + + options.realm_name = api.env.realm + options.domain_name = api.env.domain +-- +2.9.4 + diff --git a/SOURCES/0140-installutils-add-DM-password-validator.patch b/SOURCES/0140-installutils-add-DM-password-validator.patch new file mode 100644 index 0000000..91ee892 --- /dev/null +++ b/SOURCES/0140-installutils-add-DM-password-validator.patch @@ -0,0 +1,55 @@ +From fe7778b52ac9bacbedceec641ccb41d5f79f131c Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Wed, 3 May 2017 10:01:09 +0200 +Subject: [PATCH] installutils: add DM password validator + +Add a validator that checks whether provided Directory Manager +is valid by attempting to connect to LDAP. + +Related https://pagure.io/freeipa/issue/6892 + +Signed-off-by: Tomas Krizek +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/installutils.py | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 9230e70056b1a773246a0d95e6ecb943cada953c..b6f01489ccc65dcbc360929e0a7b315b074df8ce 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -50,6 +50,7 @@ import ipaplatform + from ipapython import ipautil, admintool, version + from ipapython.admintool import ScriptError + from ipapython.ipa_log_manager import root_logger ++from ipapython.ipaldap import DIRMAN_DN, LDAPClient + from ipalib.util import validate_hostname + from ipalib import api, errors, x509 + from ipapython.dn import DN +@@ -329,6 +330,21 @@ def _read_password_default_validator(password): + if len(password) < 8: + raise ValueError("Password must be at least 8 characters long") + ++ ++def validate_dm_password_ldap(password): ++ """ ++ Validate DM password by attempting to connect to LDAP. api.env has to ++ contain valid ldap_uri. ++ """ ++ client = LDAPClient(api.env.ldap_uri, cacert=paths.IPA_CA_CRT) ++ try: ++ client.simple_bind(DIRMAN_DN, password) ++ except errors.ACIError: ++ raise ValueError("Invalid Directory Manager password") ++ else: ++ client.unbind() ++ ++ + def read_password(user, confirm=True, validate=True, retry=True, validator=_read_password_default_validator): + correct = False + pwd = None +-- +2.9.4 + diff --git a/SOURCES/0141-ca-kra-install-validate-DM-password.patch b/SOURCES/0141-ca-kra-install-validate-DM-password.patch new file mode 100644 index 0000000..57ae8c1 --- /dev/null +++ b/SOURCES/0141-ca-kra-install-validate-DM-password.patch @@ -0,0 +1,73 @@ +From 391fe8e9d0587ad44a92c320a8d1c9de2c9b980a Mon Sep 17 00:00:00 2001 +From: Tomas Krizek +Date: Wed, 3 May 2017 10:16:13 +0200 +Subject: [PATCH] ca, kra install: validate DM password + +Before proceeding with installation, validate DM password. If the +provided DM password is invalid, abort the installation. + +Fixes https://pagure.io/freeipa/issue/6892 + +Signed-off-by: Tomas Krizek +Reviewed-By: Martin Basti +Reviewed-By: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + install/tools/ipa-ca-install | 18 ++++++++++-------- + ipaserver/install/ipa_kra_install.py | 8 ++++++++ + 2 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install +index 4bcb59a29d5a64c118649374104ae8f1cd451ea4..f84b4749a3e2a80aca002a2aa057b200e6187f18 100755 +--- a/install/tools/ipa-ca-install ++++ b/install/tools/ipa-ca-install +@@ -117,17 +117,19 @@ def parse_options(): + + + def _get_dirman_password(password=None, unattended=False): ++ # sys.exit() is used on purpose, because otherwise user is advised to ++ # uninstall the component, even though it is not needed + if not password: + if unattended: + sys.exit('Directory Manager password required') +- try: +- password = installutils.read_password( +- "Directory Manager (existing master)", confirm=False, +- validate=False) +- except KeyboardInterrupt: +- sys.exit(0) +- if password is None: +- sys.exit("Directory Manager password required") ++ password = installutils.read_password( ++ "Directory Manager (existing master)", confirm=False, ++ validate=False) ++ try: ++ installutils.validate_dm_password_ldap(password) ++ except ValueError: ++ sys.exit("Directory Manager password is invalid") ++ + return password + + +diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py +index b06d49c834d0ffa4f2e35c3241a83e42c4c9c337..8369d2f4082d35b453487ee0f17c9ce050188daf 100644 +--- a/ipaserver/install/ipa_kra_install.py ++++ b/ipaserver/install/ipa_kra_install.py +@@ -137,6 +137,14 @@ class KRAInstaller(KRAInstall): + def run(self): + super(KRAInstaller, self).run() + ++ # Verify DM password. This has to be called after ask_for_options(), ++ # so it can't be placed in validate_options(). ++ try: ++ installutils.validate_dm_password_ldap(self.options.password) ++ except ValueError: ++ raise admintool.ScriptError( ++ "Directory Manager password is invalid") ++ + if not cainstance.is_ca_installed_locally(): + raise RuntimeError("Dogtag CA is not installed. " + "Please install the CA first") +-- +2.9.4 + diff --git a/SOURCES/0142-ipa-kra-install-fix-pkispawn-setting-for-pki_securit.patch b/SOURCES/0142-ipa-kra-install-fix-pkispawn-setting-for-pki_securit.patch new file mode 100644 index 0000000..ff1f2f8 --- /dev/null +++ b/SOURCES/0142-ipa-kra-install-fix-pkispawn-setting-for-pki_securit.patch @@ -0,0 +1,45 @@ +From 58a9bd7ec98de555db23159e614b2021ec91b2e3 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 11 May 2017 14:53:09 +0200 +Subject: [PATCH] ipa-kra-install: fix pkispawn setting for + pki_security_domain_hostname + +During ipa-kra-install, the installer prepares a configuration file +provided to pkispawn. This configuration file defines +pki_security_domain_hostname=(first master) + +but when we are installing a clone, it should be set to the local hostname +instead, see man page pki_default.cfg: + pki_security_domain_hostname, pki_security_domain_https_port + Location of the security domain. Required for KRA, OCSP, TKS, + and TPS subsystems and for CA subsystems joining a security + domain. Defaults to the location of the CA subsystem within the + same instance. + +When pki_security_domain_hostname points to the 1st master, and this first +master is decommissioned, ipa-kra-install fails on new replicas because pkispawn +tries to connect to this (non-existing) host. + +https://pagure.io/freeipa/issue/6895 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/krainstance.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index abb81897a404613e20be10d348096402ef08624b..cdd25b9d05bcb1a30260475cc2341a258a3cf93c 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -252,7 +252,7 @@ class KRAInstance(DogtagInstance): + os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid) + + # Security domain registration +- config.set("KRA", "pki_security_domain_hostname", self.master_host) ++ config.set("KRA", "pki_security_domain_hostname", self.fqdn) + config.set("KRA", "pki_security_domain_https_port", "443") + config.set("KRA", "pki_security_domain_user", self.admin_user) + config.set("KRA", "pki_security_domain_password", +-- +2.9.4 + diff --git a/SOURCES/0143-certdb-add-named-trust-flag-constants.patch b/SOURCES/0143-certdb-add-named-trust-flag-constants.patch new file mode 100644 index 0000000..3c81de1 --- /dev/null +++ b/SOURCES/0143-certdb-add-named-trust-flag-constants.patch @@ -0,0 +1,344 @@ +From b98b21aaa709ccd91369e89a836f64c06c4593e8 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 27 Apr 2017 09:33:25 +0200 +Subject: [PATCH] certdb: add named trust flag constants + +Add named constants for common trust flag combinations. + +Use the named constants instead of trust flags strings in the code. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + install/restart_scripts/restart_httpd | 3 ++- + install/tools/ipa-replica-conncheck | 4 +++- + ipaclient/install/client.py | 9 ++++++--- + ipapython/certdb.py | 9 +++++++-- + ipaserver/install/ca.py | 2 +- + ipaserver/install/certs.py | 5 +++-- + ipaserver/install/dsinstance.py | 5 +++-- + ipaserver/install/httpinstance.py | 5 +++-- + ipaserver/install/ipa_cacert_manage.py | 16 +++++++++++----- + ipaserver/install/plugins/upload_cacrt.py | 2 +- + ipaserver/install/server/replicainstall.py | 3 ++- + ipaserver/install/server/upgrade.py | 4 ++-- + 12 files changed, 44 insertions(+), 23 deletions(-) + +diff --git a/install/restart_scripts/restart_httpd b/install/restart_scripts/restart_httpd +index b661b82b896b109c3859ac82c2d84ab27b839f72..cd7f12024ea3cab16e9c664687cd854e666c9570 100644 +--- a/install/restart_scripts/restart_httpd ++++ b/install/restart_scripts/restart_httpd +@@ -24,6 +24,7 @@ import traceback + from ipalib import api + from ipaplatform import services + from ipaplatform.paths import paths ++from ipapython.certdb import TRUSTED_PEER_TRUST_FLAGS + from ipaserver.install import certs, installutils + + +@@ -36,7 +37,7 @@ def _main(): + nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, "NSSNickname") + + # Add trust flag which set certificate trusted for SSL connections. +- db.trust_root_cert(nickname, "P,,") ++ db.trust_root_cert(nickname, TRUSTED_PEER_TRUST_FLAGS) + + syslog.syslog(syslog.LOG_NOTICE, 'certmonger restarted httpd') + +diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck +index fdbd4f32d9fa4a625cca3614e13e71d00f58e57e..528242268f9992e903781b76a379039d533853c0 100755 +--- a/install/tools/ipa-replica-conncheck ++++ b/install/tools/ipa-replica-conncheck +@@ -549,7 +549,9 @@ def main(): + data = ca_cert.public_bytes( + serialization.Encoding.DER) + nss_db.add_cert( +- data, str(DN(ca_cert.subject)), 'C,,') ++ data, ++ str(DN(ca_cert.subject)), ++ certdb.EXTERNAL_CA_TRUST_FLAGS) + + api.bootstrap(context='client', + confdir=paths.ETC_IPA, +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index abca692fd61be4a9f35a1398fb2af4b1d9e8689b..e78be904dd6bad491d9f3c1bb1e1410bc1779d45 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -2318,8 +2318,9 @@ def update_ipa_nssdb(): + if not os.path.exists(os.path.join(ipa_db.secdir, 'cert8.db')): + create_ipa_nssdb() + +- for nickname, trust_flags in (('IPA CA', 'CT,C,C'), +- ('External CA cert', 'C,,')): ++ for nickname, trust_flags in ( ++ ('IPA CA', certdb.IPA_CA_TRUST_FLAGS), ++ ('External CA cert', certdb.EXTERNAL_CA_TRUST_FLAGS)): + try: + cert = sys_db.get_cert(nickname) + except RuntimeError: +@@ -2680,7 +2681,9 @@ def _install(options): + tmp_db.create_db() + + for i, cert in enumerate(ca_certs): +- tmp_db.add_cert(cert, 'CA certificate %d' % (i + 1), 'C,,') ++ tmp_db.add_cert(cert, ++ 'CA certificate %d' % (i + 1), ++ certdb.EXTERNAL_CA_TRUST_FLAGS) + except CalledProcessError: + raise ScriptError( + "Failed to add CA to temporary NSS database.", +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index ea73ec139df9013b860df447fcffd9038cf7c8f2..44c7bf3197c198295035742e6db48527d76e85a6 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -52,6 +52,11 @@ CA_NICKNAME_FMT = "%s IPA CA" + + NSS_FILES = ("cert8.db", "key3.db", "secmod.db", "pwdfile.txt") + ++EMPTY_TRUST_FLAGS = ',,' ++IPA_CA_TRUST_FLAGS = 'CT,C,C' ++EXTERNAL_CA_TRUST_FLAGS = 'C,,' ++TRUSTED_PEER_TRUST_FLAGS = 'P,,' ++ + + def get_ca_nickname(realm, format=CA_NICKNAME_FMT): + return format % realm +@@ -441,7 +446,7 @@ class NSSDatabase(object): + cert = x509.load_certificate(cert_pem) + nickname = str(DN(cert.subject)) + data = cert.public_bytes(serialization.Encoding.DER) +- self.add_cert(data, nickname, ',,') ++ self.add_cert(data, nickname, EMPTY_TRUST_FLAGS) + + if extracted_key: + in_file = ipautil.write_tmp_file( +@@ -473,7 +478,7 @@ class NSSDatabase(object): + root_nickname) + else: + if trust_flags is None: +- trust_flags = 'C,,' ++ trust_flags = EXTERNAL_CA_TRUST_FLAGS + try: + self.run_certutil(["-M", "-n", root_nickname, + "-t", trust_flags]) +diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py +index 8ee0fda23411563c70b7db5f39f43c2869c108b5..52cb20f1cb3612394544a6a41f10e9e939bc0657 100644 +--- a/ipaserver/install/ca.py ++++ b/ipaserver/install/ca.py +@@ -320,7 +320,7 @@ def install_step_1(standalone, replica_config, options): + realm_name, nssdir=dirname, subject_base=subject_base) + cacert = cadb.get_cert_from_db('caSigningCert cert-pki-ca', pem=False) + nickname = certdb.get_ca_nickname(realm_name) +- trust_flags = 'CT,C,C' ++ trust_flags = certdb.IPA_CA_TRUST_FLAGS + dsdb.add_cert(cacert, nickname, trust_flags) + certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn, + cacert, nickname, trust_flags, +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 89e57134f24c505d669057eefffb7862b3b8179a..f87e00eb5e9c14ed30d39ef9f6e86b6f24bb1c61 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -37,6 +37,7 @@ from ipalib.install import certmonger, sysrestore + from ipapython.ipa_log_manager import root_logger + from ipapython import dogtag + from ipapython import ipautil ++from ipapython.certdb import EMPTY_TRUST_FLAGS, IPA_CA_TRUST_FLAGS + from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase + from ipapython.dn import DN + from ipalib import pkcs10, x509, api +@@ -597,7 +598,7 @@ class CertDB(object): + # a new certificate database. + self.create_passwd_file() + self.create_certdbs() +- self.load_cacert(cacert_fname, 'CT,C,C') ++ self.load_cacert(cacert_fname, IPA_CA_TRUST_FLAGS) + + def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, passwd=None, + ca_file=None, trust_flags=None): +@@ -643,7 +644,7 @@ class CertDB(object): + cert, st = find_cert_from_txt(certs, st) + except RuntimeError: + break +- self.add_cert(cert, 'CA %s' % num, ',,', pem=True) ++ self.add_cert(cert, 'CA %s' % num, EMPTY_TRUST_FLAGS, pem=True) + num += 1 + + # We only handle one server cert +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 403fe8489fdd9e0dbf40dd4df3794b51185d45b9..0db0368fa4b48495718afd779291ce164d1687c8 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -32,6 +32,7 @@ import fnmatch + import ldap + + from ipalib.install import certmonger, certstore ++from ipapython.certdb import IPA_CA_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS + from ipapython.ipa_log_manager import root_logger + from ipapython import ipautil, ipaldap + from ipapython import dogtag +@@ -766,7 +767,7 @@ class DsInstance(service.Service): + ) + if self.pkcs12_info: + if self.ca_is_configured: +- trust_flags = 'CT,C,C' ++ trust_flags = IPA_CA_TRUST_FLAGS + else: + trust_flags = None + dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], +@@ -1065,7 +1066,7 @@ class DsInstance(service.Service): + certdb.cacert_name = cacert_name + status = True + try: +- certdb.load_cacert(cacert_fname, 'C,,') ++ certdb.load_cacert(cacert_fname, EXTERNAL_CA_TRUST_FLAGS) + except ipautil.CalledProcessError as e: + root_logger.critical("Error importing CA cert file named [%s]: %s" % + (cacert_fname, str(e))) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index ab688a85f157b1886842a91bb7d22f9ea99e3615..a6aeb21edc73783ff9a3f9b526409ea525aa66dd 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -32,6 +32,7 @@ import six + from augeas import Augeas + + from ipalib.install import certmonger ++from ipapython.certdb import IPA_CA_TRUST_FLAGS, TRUSTED_PEER_TRUST_FLAGS + from ipaserver.install import service + from ipaserver.install import certs + from ipaserver.install import installutils +@@ -381,7 +382,7 @@ class HTTPInstance(service.Service): + + if self.pkcs12_info: + if self.ca_is_configured: +- trust_flags = 'CT,C,C' ++ trust_flags = IPA_CA_TRUST_FLAGS + else: + trust_flags = None + db.init_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], +@@ -403,7 +404,7 @@ class HTTPInstance(service.Service): + self.__set_mod_nss_nickname(nickname) + self.add_cert_to_service() + +- db.trust_root_cert(nickname, "P,,") ++ db.trust_root_cert(nickname, TRUSTED_PEER_TRUST_FLAGS) + + else: + if not self.promote: +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index 3b732e4dcbb5c9b4dfbb9e3608bc7d7afd3e10c2..88b40d45e10281d272882d21e06f5d53cf5a701d 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -26,6 +26,7 @@ import gssapi + + from ipalib.install import certmonger, certstore + from ipapython import admintool, ipautil ++from ipapython.certdb import EMPTY_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS + from ipapython.dn import DN + from ipaplatform.paths import paths + from ipalib import api, errors, x509 +@@ -242,10 +243,10 @@ class CACertManage(admintool.AdminTool): + + with certs.NSSDatabase() as tmpdb: + tmpdb.create_db() +- tmpdb.add_cert(old_cert_der, 'IPA CA', 'C,,') ++ tmpdb.add_cert(old_cert_der, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS) + + try: +- tmpdb.add_cert(new_cert_der, 'IPA CA', 'C,,') ++ tmpdb.add_cert(new_cert_der, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS) + except ipautil.CalledProcessError as e: + raise admintool.ScriptError( + "Not compatible with the current CA certificate: %s" % e) +@@ -253,7 +254,8 @@ class CACertManage(admintool.AdminTool): + ca_certs = x509.load_certificate_list_from_file(ca_file.name) + for ca_cert in ca_certs: + data = ca_cert.public_bytes(serialization.Encoding.DER) +- tmpdb.add_cert(data, str(DN(ca_cert.subject)), 'C,,') ++ tmpdb.add_cert( ++ data, str(DN(ca_cert.subject)), EXTERNAL_CA_TRUST_FLAGS) + + try: + tmpdb.verify_ca_cert_validity('IPA CA') +@@ -270,7 +272,11 @@ class CACertManage(admintool.AdminTool): + except RuntimeError: + break + certstore.put_ca_cert_nss( +- conn, api.env.basedn, ca_cert, nickname, ',,') ++ conn, ++ api.env.basedn, ++ ca_cert, ++ nickname, ++ EMPTY_TRUST_FLAGS) + + dn = DN(('cn', self.cert_nickname), ('cn', 'ca_renewal'), + ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) +@@ -343,7 +349,7 @@ class CACertManage(admintool.AdminTool): + + with certs.NSSDatabase() as tmpdb: + tmpdb.create_db() +- tmpdb.add_cert(cert, nickname, 'C,,') ++ tmpdb.add_cert(cert, nickname, EXTERNAL_CA_TRUST_FLAGS) + for ca_cert, ca_nickname, ca_trust_flags in ca_certs: + tmpdb.add_cert(ca_cert, ca_nickname, ca_trust_flags) + +diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py +index 425ea63976ec92a6d69492d90a1e970e528c4a26..7d294ff971bd109e5fbb3570bfff0198f24b68d3 100644 +--- a/ipaserver/install/plugins/upload_cacrt.py ++++ b/ipaserver/install/plugins/upload_cacrt.py +@@ -55,7 +55,7 @@ class update_upload_cacrt(Updater): + if 'u' in trust_flags: + continue + if nickname == ca_nickname and ca_enabled: +- trust_flags = 'CT,C,C' ++ trust_flags = certdb.IPA_CA_TRUST_FLAGS + cert = db.get_cert_from_db(nickname, pem=False) + trust, _ca, eku = certstore.trust_flags_to_key_policy(trust_flags) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index aa8e67f60b8abe591d55a907c409b584c74d4541..5e78e6faf51ded2fe7634f230c66aa15ae84bad4 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -23,6 +23,7 @@ import ipaclient.install.ntpconf + from ipalib.install import certstore, sysrestore + from ipalib.install.kinit import kinit_keytab + from ipapython import ipaldap, ipautil ++from ipapython.certdb import IPA_CA_TRUST_FLAGS + from ipapython.dn import DN + from ipapython.ipa_log_manager import root_logger + from ipapython.admintool import ScriptError +@@ -737,7 +738,7 @@ def install_check(installer): + nssdir=tmp_db_dir, + subject_base=config.subject_base) + if ca_enabled: +- trust_flags = 'CT,C,C' ++ trust_flags = IPA_CA_TRUST_FLAGS + else: + trust_flags = None + tmp_db.create_from_pkcs12(pkcs12_info[0], pkcs12_info[1], +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 5e5c83731d3d3415deb61271baa7865c62f60336..73a4f1108a56a766cdbbcb93d7050482a8264a75 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1389,7 +1389,7 @@ def fix_trust_flags(): + nickname = certdb.get_ca_nickname(api.env.realm) + cert = db.get_cert_from_db(nickname) + if cert: +- db.trust_root_cert(nickname, 'CT,C,C') ++ db.trust_root_cert(nickname, certdb.IPA_CA_TRUST_FLAGS) + + sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) + +@@ -1407,7 +1407,7 @@ def fix_server_cert_trust_flags(): + sc_nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, + "NSSNickname") + # Add trust flag which set certificate trusted for SSL connections. +- db.trust_root_cert(sc_nickname, "P,,") ++ db.trust_root_cert(sc_nickname, certdb.TRUSTED_PEER_TRUST_FLAGS) + + sysupgrade.set_upgrade_state('http', 'fix_serv_cert_trust_flags', True) + +-- +2.9.4 + diff --git a/SOURCES/0144-certdb-certs-make-trust-flags-argument-mandatory.patch b/SOURCES/0144-certdb-certs-make-trust-flags-argument-mandatory.patch new file mode 100644 index 0000000..8ed40ad --- /dev/null +++ b/SOURCES/0144-certdb-certs-make-trust-flags-argument-mandatory.patch @@ -0,0 +1,181 @@ +From 997ebc0f56963769bdcbeda60a2dca222c884b1e Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 27 Apr 2017 09:57:45 +0200 +Subject: [PATCH] certdb, certs: make trust flags argument mandatory + +Make the trust flags argument mandatory in all functions in `certdb` and +`certs`. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipapython/certdb.py | 4 +--- + ipaserver/install/certs.py | 11 +++++------ + ipaserver/install/dsinstance.py | 2 +- + ipaserver/install/httpinstance.py | 6 ++++-- + ipaserver/install/installutils.py | 5 +++-- + ipaserver/install/server/replicainstall.py | 4 ++-- + 6 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 44c7bf3197c198295035742e6db48527d76e85a6..88dcae750de5881ae7b4921ca1ae23daa9c5d4b0 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -471,14 +471,12 @@ class NSSDatabase(object): + + self.import_pkcs12(out_file.name, out_password) + +- def trust_root_cert(self, root_nickname, trust_flags=None): ++ def trust_root_cert(self, root_nickname, trust_flags): + if root_nickname[:7] == "Builtin": + root_logger.debug( + "No need to add trust for built-in root CAs, skipping %s" % + root_nickname) + else: +- if trust_flags is None: +- trust_flags = EXTERNAL_CA_TRUST_FLAGS + try: + self.run_certutil(["-M", "-n", root_nickname, + "-t", trust_flags]) +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index f87e00eb5e9c14ed30d39ef9f6e86b6f24bb1c61..17b9ebad4a128e292e453af44ca9d63cfb1e6ea2 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -550,7 +550,7 @@ class CertDB(object): + + return root_nicknames + +- def trust_root_cert(self, root_nickname, trust_flags=None): ++ def trust_root_cert(self, root_nickname, trust_flags): + if root_nickname is None: + root_logger.debug("Unable to identify root certificate to trust. Continuing but things are likely to fail.") + return +@@ -600,14 +600,13 @@ class CertDB(object): + self.create_certdbs() + self.load_cacert(cacert_fname, IPA_CA_TRUST_FLAGS) + +- def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, passwd=None, +- ca_file=None, trust_flags=None): ++ def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, ++ ca_file, trust_flags): + """Create a new NSS database using the certificates in a PKCS#12 file. + + pkcs12_fname: the filename of the PKCS#12 file + pkcs12_pwd_fname: the file containing the pin for the PKCS#12 file + nickname: the nickname/friendly-name of the cert we are loading +- passwd: The password to use for the new NSS database we are creating + + The global CA may be added as well in case it wasn't included in the + PKCS#12 file. Extra certs won't hurt in any case. +@@ -615,7 +614,7 @@ class CertDB(object): + The global CA may be specified in ca_file, as a PEM filename. + """ + self.create_noise_file() +- self.create_passwd_file(passwd) ++ self.create_passwd_file() + self.create_certdbs() + self.init_from_pkcs12( + pkcs12_fname, +@@ -624,7 +623,7 @@ class CertDB(object): + trust_flags=trust_flags) + + def init_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, +- ca_file=None, trust_flags=None): ++ ca_file, trust_flags): + self.import_pkcs12(pkcs12_fname, pkcs12_passwd) + server_certs = self.find_server_certs() + if len(server_certs) == 0: +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 0db0368fa4b48495718afd779291ce164d1687c8..0e4ae4bfe6f1445de167df8fe5328d6a421e416f 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -769,7 +769,7 @@ class DsInstance(service.Service): + if self.ca_is_configured: + trust_flags = IPA_CA_TRUST_FLAGS + else: +- trust_flags = None ++ trust_flags = EXTERNAL_CA_TRUST_FLAGS + dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], + ca_file=self.ca_file, + trust_flags=trust_flags) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index a6aeb21edc73783ff9a3f9b526409ea525aa66dd..c76a1a4e484c5777ced92761916c1c586e8b2d5d 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -32,7 +32,9 @@ import six + from augeas import Augeas + + from ipalib.install import certmonger +-from ipapython.certdb import IPA_CA_TRUST_FLAGS, TRUSTED_PEER_TRUST_FLAGS ++from ipapython.certdb import (IPA_CA_TRUST_FLAGS, ++ EXTERNAL_CA_TRUST_FLAGS, ++ TRUSTED_PEER_TRUST_FLAGS) + from ipaserver.install import service + from ipaserver.install import certs + from ipaserver.install import installutils +@@ -384,7 +386,7 @@ class HTTPInstance(service.Service): + if self.ca_is_configured: + trust_flags = IPA_CA_TRUST_FLAGS + else: +- trust_flags = None ++ trust_flags = EXTERNAL_CA_TRUST_FLAGS + db.init_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], + ca_file=self.ca_file, + trust_flags=trust_flags) +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index b6f01489ccc65dcbc360929e0a7b315b074df8ce..0445a1d3c403fab690e5afb7c8801ed85773b1e0 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -49,6 +49,7 @@ from ipalib.install.kinit import kinit_password + import ipaplatform + from ipapython import ipautil, admintool, version + from ipapython.admintool import ScriptError ++from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS + from ipapython.ipa_log_manager import root_logger + from ipapython.ipaldap import DIRMAN_DN, LDAPClient + from ipalib.util import validate_hostname +@@ -1036,7 +1037,7 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files, + if 'u' in trust_flags: + key_nickname = nickname + continue +- nssdb.trust_root_cert(nickname) ++ nssdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS) + + # Check we have the whole cert chain & the CA is in it + trust_chain = list(reversed(nssdb.get_trust_chain(key_nickname))) +@@ -1176,7 +1177,7 @@ def load_external_cert(files, ca_subject): + cache[nickname] = (cert, subject, issuer) + if subject == ca_subject: + ca_nickname = nickname +- nssdb.trust_root_cert(nickname) ++ nssdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS) + + if ca_nickname is None: + raise ScriptError( +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 5e78e6faf51ded2fe7634f230c66aa15ae84bad4..fb738cb9f590f3f9595de92ef025c6032e9343f8 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -23,7 +23,7 @@ import ipaclient.install.ntpconf + from ipalib.install import certstore, sysrestore + from ipalib.install.kinit import kinit_keytab + from ipapython import ipaldap, ipautil +-from ipapython.certdb import IPA_CA_TRUST_FLAGS ++from ipapython.certdb import IPA_CA_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS + from ipapython.dn import DN + from ipapython.ipa_log_manager import root_logger + from ipapython.admintool import ScriptError +@@ -740,7 +740,7 @@ def install_check(installer): + if ca_enabled: + trust_flags = IPA_CA_TRUST_FLAGS + else: +- trust_flags = None ++ trust_flags = EXTERNAL_CA_TRUST_FLAGS + tmp_db.create_from_pkcs12(pkcs12_info[0], pkcs12_info[1], + ca_file=cafile, + trust_flags=trust_flags) +-- +2.9.4 + diff --git a/SOURCES/0145-certdb-use-custom-object-for-trust-flags.patch b/SOURCES/0145-certdb-use-custom-object-for-trust-flags.patch new file mode 100644 index 0000000..6699565 --- /dev/null +++ b/SOURCES/0145-certdb-use-custom-object-for-trust-flags.patch @@ -0,0 +1,358 @@ +From e8805e118446a1ad542d183e2f6bea0f87651795 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 27 Apr 2017 09:37:38 +0200 +Subject: [PATCH] certdb: use custom object for trust flags + +Replace trust flag strings with `TrustFlags` objects. The `TrustFlags` +class encapsulates `certstore` key policy and has an additional flag +indicating the presence of a private key. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + install/restart_scripts/renew_ca_cert | 2 +- + ipalib/install/certstore.py | 49 +------------ + ipapython/certdb.py | 109 ++++++++++++++++++++++++++-- + ipaserver/install/installutils.py | 2 +- + ipaserver/install/ipa_cacert_manage.py | 6 +- + ipaserver/install/ipa_server_certinstall.py | 4 +- + ipaserver/install/plugins/upload_cacrt.py | 2 +- + ipaserver/install/server/upgrade.py | 2 +- + 8 files changed, 117 insertions(+), 59 deletions(-) + +diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert +index 7a54b4c7e05a35b40b17e46b75ff8d47db1b2d23..bb31defc0e2bdca044e68ae067f42fb3bd41a57f 100644 +--- a/install/restart_scripts/renew_ca_cert ++++ b/install/restart_scripts/renew_ca_cert +@@ -125,7 +125,7 @@ def _main(): + + # Remove old external CA certificates + for ca_nick, ca_flags in db.list_certs(): +- if 'u' in ca_flags: ++ if ca_flags.has_key: + continue + # Delete *all* certificates that use the nickname + while True: +diff --git a/ipalib/install/certstore.py b/ipalib/install/certstore.py +index 310e08ed2273badba6fde4ada0ee52501fddc72c..bc2079fb12873444cbe6796eebfdfcfebd0e284d 100644 +--- a/ipalib/install/certstore.py ++++ b/ipalib/install/certstore.py +@@ -25,7 +25,7 @@ LDAP shared certificate store. + from pyasn1.error import PyAsn1Error + + from ipapython.dn import DN +-from ipapython.certdb import get_ca_nickname ++from ipapython.certdb import get_ca_nickname, TrustFlags + from ipalib import errors, x509 + + def _parse_cert(dercert): +@@ -344,57 +344,14 @@ def trust_flags_to_key_policy(trust_flags): + """ + Convert certutil trust flags to certificate store key policy. + """ +- if 'p' in trust_flags: +- if 'C' in trust_flags or 'P' in trust_flags or 'T' in trust_flags: +- raise ValueError("cannot be both trusted and not trusted") +- return False, None, None +- elif 'C' in trust_flags or 'T' in trust_flags: +- if 'P' in trust_flags: +- raise ValueError("cannot be both CA and not CA") +- ca = True +- elif 'P' in trust_flags: +- ca = False +- else: +- return None, None, set() +- +- trust_flags = trust_flags.split(',') +- ext_key_usage = set() +- for i, kp in enumerate((x509.EKU_SERVER_AUTH, +- x509.EKU_EMAIL_PROTECTION, +- x509.EKU_CODE_SIGNING)): +- if 'C' in trust_flags[i] or 'P' in trust_flags[i]: +- ext_key_usage.add(kp) +- if 'T' in trust_flags[0]: +- ext_key_usage.add(x509.EKU_CLIENT_AUTH) +- +- return True, ca, ext_key_usage ++ return trust_flags[1:] + + + def key_policy_to_trust_flags(trusted, ca, ext_key_usage): + """ + Convert certificate store key policy to certutil trust flags. + """ +- if trusted is False: +- return 'p,p,p' +- elif trusted is None or ca is None: +- return ',,' +- elif ext_key_usage is None: +- if ca: +- return 'CT,C,C' +- else: +- return 'P,P,P' +- +- trust_flags = ['', '', ''] +- for i, kp in enumerate((x509.EKU_SERVER_AUTH, +- x509.EKU_EMAIL_PROTECTION, +- x509.EKU_CODE_SIGNING)): +- if kp in ext_key_usage: +- trust_flags[i] += ('C' if ca else 'P') +- if ca and x509.EKU_CLIENT_AUTH in ext_key_usage: +- trust_flags[0] += 'T' +- +- trust_flags = ','.join(trust_flags) +- return trust_flags ++ return TrustFlags(False, trusted, ca, ext_key_usage) + + + def put_ca_cert_nss(ldap, base_dn, dercert, nickname, trust_flags, +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 88dcae750de5881ae7b4921ca1ae23daa9c5d4b0..af95eba3cbad1c354615457ed0501f97bff0e22d 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -17,6 +17,7 @@ + # along with this program. If not, see . + # + ++import collections + import os + import io + import pwd +@@ -52,10 +53,26 @@ CA_NICKNAME_FMT = "%s IPA CA" + + NSS_FILES = ("cert8.db", "key3.db", "secmod.db", "pwdfile.txt") + +-EMPTY_TRUST_FLAGS = ',,' +-IPA_CA_TRUST_FLAGS = 'CT,C,C' +-EXTERNAL_CA_TRUST_FLAGS = 'C,,' +-TRUSTED_PEER_TRUST_FLAGS = 'P,,' ++TrustFlags = collections.namedtuple('TrustFlags', 'has_key trusted ca usages') ++ ++EMPTY_TRUST_FLAGS = TrustFlags(False, None, None, None) ++ ++IPA_CA_TRUST_FLAGS = TrustFlags( ++ False, True, True, frozenset({ ++ x509.EKU_SERVER_AUTH, ++ x509.EKU_CLIENT_AUTH, ++ x509.EKU_CODE_SIGNING, ++ x509.EKU_EMAIL_PROTECTION, ++ }), ++) ++ ++EXTERNAL_CA_TRUST_FLAGS = TrustFlags( ++ False, True, True, frozenset({x509.EKU_SERVER_AUTH}), ++) ++ ++TRUSTED_PEER_TRUST_FLAGS = TrustFlags( ++ False, True, False, frozenset({x509.EKU_SERVER_AUTH}), ++) + + + def get_ca_nickname(realm, format=CA_NICKNAME_FMT): +@@ -87,6 +104,82 @@ def get_file_cont(slot, token, filename): + return f.read() + + ++def parse_trust_flags(trust_flags): ++ """ ++ Convert certutil trust flags to TrustFlags object. ++ """ ++ has_key = 'u' in trust_flags ++ ++ if 'p' in trust_flags: ++ if 'C' in trust_flags or 'P' in trust_flags or 'T' in trust_flags: ++ raise ValueError("cannot be both trusted and not trusted") ++ return False, None, None ++ elif 'C' in trust_flags or 'T' in trust_flags: ++ if 'P' in trust_flags: ++ raise ValueError("cannot be both CA and not CA") ++ ca = True ++ elif 'P' in trust_flags: ++ ca = False ++ else: ++ return TrustFlags(has_key, None, None, frozenset()) ++ ++ trust_flags = trust_flags.split(',') ++ ext_key_usage = set() ++ for i, kp in enumerate((x509.EKU_SERVER_AUTH, ++ x509.EKU_EMAIL_PROTECTION, ++ x509.EKU_CODE_SIGNING)): ++ if 'C' in trust_flags[i] or 'P' in trust_flags[i]: ++ ext_key_usage.add(kp) ++ if 'T' in trust_flags[0]: ++ ext_key_usage.add(x509.EKU_CLIENT_AUTH) ++ ++ return TrustFlags(has_key, True, ca, frozenset(ext_key_usage)) ++ ++ ++def unparse_trust_flags(trust_flags): ++ """ ++ Convert TrustFlags object to certutil trust flags. ++ """ ++ has_key, trusted, ca, ext_key_usage = trust_flags ++ ++ if trusted is False: ++ if has_key: ++ return 'pu,pu,pu' ++ else: ++ return 'p,p,p' ++ elif trusted is None or ca is None: ++ if has_key: ++ return 'u,u,u' ++ else: ++ return ',,' ++ elif ext_key_usage is None: ++ if ca: ++ if has_key: ++ return 'CTu,Cu,Cu' ++ else: ++ return 'CT,C,C' ++ else: ++ if has_key: ++ return 'Pu,Pu,Pu' ++ else: ++ return 'P,P,P' ++ ++ trust_flags = ['', '', ''] ++ for i, kp in enumerate((x509.EKU_SERVER_AUTH, ++ x509.EKU_EMAIL_PROTECTION, ++ x509.EKU_CODE_SIGNING)): ++ if kp in ext_key_usage: ++ trust_flags[i] += ('C' if ca else 'P') ++ if ca and x509.EKU_CLIENT_AUTH in ext_key_usage: ++ trust_flags[0] += 'T' ++ if has_key: ++ for i in range(3): ++ trust_flags[i] += 'u' ++ ++ trust_flags = ','.join(trust_flags) ++ return trust_flags ++ ++ + class NSSDatabase(object): + """A general-purpose wrapper around a NSS cert database + +@@ -205,7 +298,9 @@ class NSSDatabase(object): + for cert in certs: + match = re.match(r'^(.+?)\s+(\w*,\w*,\w*)\s*$', cert) + if match: +- certlist.append(match.groups()) ++ nickname = match.group(1) ++ trust_flags = parse_trust_flags(match.group(2)) ++ certlist.append((nickname, trust_flags)) + + return tuple(certlist) + +@@ -218,7 +313,7 @@ class NSSDatabase(object): + """ + server_certs = [] + for name, flags in self.list_certs(): +- if 'u' in flags: ++ if flags.has_key: + server_certs.append((name, flags)) + + return server_certs +@@ -477,6 +572,7 @@ class NSSDatabase(object): + "No need to add trust for built-in root CAs, skipping %s" % + root_nickname) + else: ++ trust_flags = unparse_trust_flags(trust_flags) + try: + self.run_certutil(["-M", "-n", root_nickname, + "-t", trust_flags]) +@@ -538,6 +634,7 @@ class NSSDatabase(object): + location) + + def add_cert(self, cert, nick, flags, pem=False): ++ flags = unparse_trust_flags(flags) + args = ["-A", "-n", nick, "-t", flags] + if pem: + args.append("-a") +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 0445a1d3c403fab690e5afb7c8801ed85773b1e0..5bce9894780bd920db11196b925492a7fe8f22d0 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -1034,7 +1034,7 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files, + raise ScriptError(str(e)) + + for nickname, trust_flags in nssdb.list_certs(): +- if 'u' in trust_flags: ++ if trust_flags.has_key: + key_nickname = nickname + continue + nssdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS) +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index 88b40d45e10281d272882d21e06f5d53cf5a701d..d28a5966f054141819463cdb1dfef48ee1e46e92 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -26,7 +26,9 @@ import gssapi + + from ipalib.install import certmonger, certstore + from ipapython import admintool, ipautil +-from ipapython.certdb import EMPTY_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS ++from ipapython.certdb import (EMPTY_TRUST_FLAGS, ++ EXTERNAL_CA_TRUST_FLAGS, ++ parse_trust_flags) + from ipapython.dn import DN + from ipaplatform.paths import paths + from ipalib import api, errors, x509 +@@ -366,6 +368,8 @@ class CACertManage(admintool.AdminTool): + len(trust_flags.split(',')) != 3): + raise admintool.ScriptError("Invalid trust flags") + ++ trust_flags = parse_trust_flags(trust_flags) ++ + try: + certstore.put_ca_cert_nss( + api.Backend.ldap2, api.env.basedn, cert, nickname, trust_flags) +diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py +index ee93535edfd258fe71099881c54c413516b24d17..9f2cd9573a156949ae979e7b69fbd23adaf2feb8 100644 +--- a/ipaserver/install/ipa_server_certinstall.py ++++ b/ipaserver/install/ipa_server_certinstall.py +@@ -170,13 +170,13 @@ class ServerCertInstall(admintool.AdminTool): + # this leaves only the server certs in the temp db + tempnssdb.import_pkcs12(pkcs12_filename, pkcs12_pin) + for nickname, flags in tempnssdb.list_certs(): +- if 'u' not in flags: ++ if not flags.has_key: + while tempnssdb.has_nickname(nickname): + tempnssdb.delete_cert(nickname) + + # import all the CA certs from nssdb into the temp db + for nickname, flags in nssdb.list_certs(): +- if 'u' not in flags: ++ if not flags.has_key: + cert = nssdb.get_cert_from_db(nickname) + tempnssdb.add_cert(cert, nickname, flags) + +diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py +index 7d294ff971bd109e5fbb3570bfff0198f24b68d3..73cc91d8f6dd5811ec74efecd6c885cd8937a0f2 100644 +--- a/ipaserver/install/plugins/upload_cacrt.py ++++ b/ipaserver/install/plugins/upload_cacrt.py +@@ -52,7 +52,7 @@ class update_upload_cacrt(Updater): + ldap = self.api.Backend.ldap2 + + for nickname, trust_flags in db.list_certs(): +- if 'u' in trust_flags: ++ if trust_flags.has_key: + continue + if nickname == ca_nickname and ca_enabled: + trust_flags = certdb.IPA_CA_TRUST_FLAGS +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 73a4f1108a56a766cdbbcb93d7050482a8264a75..c244958f4cddba0d1edded5165a295b1e1ee2b8a 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1547,7 +1547,7 @@ def disable_httpd_system_trust(http): + + db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) + for nickname, trust_flags in db.list_certs(): +- if 'u' not in trust_flags: ++ if not trust_flags.has_key: + cert = db.get_cert_from_db(nickname, pem=False) + if cert: + ca_certs.append((cert, nickname, trust_flags)) +-- +2.9.4 + diff --git a/SOURCES/0146-install-trust-IPA-CA-for-PKINIT.patch b/SOURCES/0146-install-trust-IPA-CA-for-PKINIT.patch new file mode 100644 index 0000000..c69b8f0 --- /dev/null +++ b/SOURCES/0146-install-trust-IPA-CA-for-PKINIT.patch @@ -0,0 +1,202 @@ +From e45762bf5b94c064668752160271a00af854b6cf Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:38:20 +0000 +Subject: [PATCH] install: trust IPA CA for PKINIT + +Trust IPA CA to issue PKINIT KDC and client authentication certificates in +the IPA certificate store. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipalib/x509.py | 2 ++ + ipapython/certdb.py | 2 ++ + ipaserver/install/dsinstance.py | 31 +++++++++++++++++++++++------- + ipaserver/install/plugins/upload_cacrt.py | 6 +++++- + ipaserver/install/server/install.py | 9 ++++++--- + ipaserver/install/server/replicainstall.py | 1 + + 6 files changed, 40 insertions(+), 11 deletions(-) + +diff --git a/ipalib/x509.py b/ipalib/x509.py +index f65cf816c9ead50a43e08a3b982f428112e7c1b3..5d1a7b8f4b99e057d4732d388efb0f27def07085 100644 +--- a/ipalib/x509.py ++++ b/ipalib/x509.py +@@ -64,6 +64,8 @@ EKU_SERVER_AUTH = '1.3.6.1.5.5.7.3.1' + EKU_CLIENT_AUTH = '1.3.6.1.5.5.7.3.2' + EKU_CODE_SIGNING = '1.3.6.1.5.5.7.3.3' + EKU_EMAIL_PROTECTION = '1.3.6.1.5.5.7.3.4' ++EKU_PKINIT_CLIENT_AUTH = '1.3.6.1.5.2.3.4' ++EKU_PKINIT_KDC = '1.3.6.1.5.2.3.5' + EKU_ANY = '2.5.29.37.0' + EKU_PLACEHOLDER = '1.3.6.1.4.1.3319.6.10.16' + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index af95eba3cbad1c354615457ed0501f97bff0e22d..1ee2603653452577476cf413e6af951cd29c273e 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -63,6 +63,8 @@ IPA_CA_TRUST_FLAGS = TrustFlags( + x509.EKU_CLIENT_AUTH, + x509.EKU_CODE_SIGNING, + x509.EKU_EMAIL_PROTECTION, ++ x509.EKU_PKINIT_CLIENT_AUTH, ++ x509.EKU_PKINIT_KDC, + }), + ) + +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 0e4ae4bfe6f1445de167df8fe5328d6a421e416f..39248edb285ee4d792b4500d83d88b24f5732d10 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -31,8 +31,11 @@ import fnmatch + + import ldap + ++from ipalib import x509 + from ipalib.install import certmonger, certstore +-from ipapython.certdb import IPA_CA_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS ++from ipapython.certdb import (IPA_CA_TRUST_FLAGS, ++ EXTERNAL_CA_TRUST_FLAGS, ++ TrustFlags) + from ipapython.ipa_log_manager import root_logger + from ipapython import ipautil, ipaldap + from ipapython import dogtag +@@ -289,7 +292,8 @@ class DsInstance(service.Service): + + def init_info(self, realm_name, fqdn, domain_name, dm_password, + subject_base, ca_subject, +- idstart, idmax, pkcs12_info, ca_file=None): ++ idstart, idmax, pkcs12_info, ca_file=None, ++ setup_pkinit=False): + self.realm = realm_name.upper() + self.serverid = installutils.realm_to_serverid(self.realm) + self.suffix = ipautil.realm_to_suffix(self.realm) +@@ -303,6 +307,7 @@ class DsInstance(service.Service): + self.pkcs12_info = pkcs12_info + if pkcs12_info: + self.ca_is_configured = False ++ self.setup_pkinit = setup_pkinit + self.ca_file = ca_file + + self.__setup_sub_dict() +@@ -311,11 +316,12 @@ class DsInstance(service.Service): + dm_password, pkcs12_info=None, + idstart=1100, idmax=999999, + subject_base=None, ca_subject=None, +- hbac_allow=True, ca_file=None): ++ hbac_allow=True, ca_file=None, setup_pkinit=False): + self.init_info( + realm_name, fqdn, domain_name, dm_password, + subject_base, ca_subject, +- idstart, idmax, pkcs12_info, ca_file=ca_file) ++ idstart, idmax, pkcs12_info, ca_file=ca_file, ++ setup_pkinit=setup_pkinit) + + self.__common_setup() + self.step("restarting directory server", self.__restart_instance) +@@ -354,7 +360,8 @@ class DsInstance(service.Service): + domain_name, dm_password, + subject_base, ca_subject, + api, pkcs12_info=None, ca_file=None, +- ca_is_configured=None, promote=False): ++ ca_is_configured=None, promote=False, ++ setup_pkinit=False): + # idstart and idmax are configured so that the range is seen as + # depleted by the DNA plugin and the replica will go and get a + # new range from the master. +@@ -372,7 +379,8 @@ class DsInstance(service.Service): + idstart=idstart, + idmax=idmax, + pkcs12_info=pkcs12_info, +- ca_file=ca_file ++ ca_file=ca_file, ++ setup_pkinit=setup_pkinit, + ) + self.master_fqdn = master_fqdn + if ca_is_configured is not None: +@@ -882,8 +890,17 @@ class DsInstance(service.Service): + + nickname = self.cacert_name + cert = dsdb.get_cert_from_db(nickname, pem=False) ++ cacert_flags = trust_flags[nickname] ++ if self.setup_pkinit: ++ cacert_flags = TrustFlags( ++ cacert_flags.has_key, ++ cacert_flags.trusted, ++ cacert_flags.ca, ++ (cacert_flags.usages | ++ {x509.EKU_PKINIT_CLIENT_AUTH, x509.EKU_PKINIT_KDC}), ++ ) + certstore.put_ca_cert_nss(conn, self.suffix, cert, nickname, +- trust_flags[nickname], ++ cacert_flags, + config_ipa=self.ca_is_configured, + config_compat=self.master_fqdn is None) + +diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py +index 73cc91d8f6dd5811ec74efecd6c885cd8937a0f2..a1957ca5b675b86f0df36dc820ee31305f54f863 100644 +--- a/ipaserver/install/plugins/upload_cacrt.py ++++ b/ipaserver/install/plugins/upload_cacrt.py +@@ -79,7 +79,11 @@ class update_upload_cacrt(Updater): + try: + ldap.add_entry(entry) + except errors.DuplicateEntry: +- pass ++ if nickname == ca_nickname and ca_enabled: ++ try: ++ ldap.update_entry(entry) ++ except errors.EmptyModlist: ++ pass + + if ca_cert: + dn = DN(('cn', 'CACert'), ('cn', 'ipa'), ('cn','etc'), +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 0ce60e964cb210708e56fb43a5b70f8e3405caf2..25c21db721c58388ae8fd6ab1fbc443d513a4324 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -737,7 +737,8 @@ def install(installer): + idstart=options.idstart, idmax=options.idmax, + subject_base=options.subject_base, + ca_subject=options.ca_subject, +- hbac_allow=not options.no_hbac_allow) ++ hbac_allow=not options.no_hbac_allow, ++ setup_pkinit=not options.no_pkinit) + else: + ds = dsinstance.DsInstance(fstore=fstore, + domainlevel=options.domainlevel, +@@ -748,7 +749,8 @@ def install(installer): + idstart=options.idstart, idmax=options.idmax, + subject_base=options.subject_base, + ca_subject=options.ca_subject, +- hbac_allow=not options.no_hbac_allow) ++ hbac_allow=not options.no_hbac_allow, ++ setup_pkinit=not options.no_pkinit) + + ntpinstance.ntp_ldap_enable(host_name, ds.suffix, realm_name) + +@@ -759,7 +761,8 @@ def install(installer): + installer._ds = ds + ds.init_info( + realm_name, host_name, domain_name, dm_password, +- options.subject_base, options.ca_subject, 1101, 1100, None) ++ options.subject_base, options.ca_subject, 1101, 1100, None, ++ setup_pkinit=not options.no_pkinit) + + krb = krbinstance.KrbInstance(fstore) + if not options.external_cert_files: +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index fb738cb9f590f3f9595de92ef025c6032e9343f8..c19edceec42845f3169adc923762f700739232f2 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -107,6 +107,7 @@ def install_replica_ds(config, options, ca_is_configured, remote_api, + ca_file=ca_file, + promote=promote, # we need promote because of replication setup + api=remote_api, ++ setup_pkinit=not options.no_pkinit, + ) + + return ds +-- +2.9.4 + diff --git a/SOURCES/0147-client-install-fix-client-PKINIT-configuration.patch b/SOURCES/0147-client-install-fix-client-PKINIT-configuration.patch new file mode 100644 index 0000000..2f7ca49 --- /dev/null +++ b/SOURCES/0147-client-install-fix-client-PKINIT-configuration.patch @@ -0,0 +1,254 @@ +From e5491b62d3ec21feb7809f7f65797151d256c580 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:48:57 +0000 +Subject: [PATCH] client install: fix client PKINIT configuration + +Set `pkinit_anchors` in `krb5.conf` to a CA certificate bundle of CAs +trusted to issue KDC certificates rather than `/etc/ipa/ca.crt`. + +Set `pkinit_pool` in `krb5.conf` to a CA certificate bundle of all CAs +known to IPA. + +Make sure both bundles are exported in all installation code paths. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + client/Makefile.am | 1 + + freeipa.spec.in | 10 ++++++++++ + install/share/krb5.conf.template | 3 ++- + ipaclient/install/client.py | 15 ++++++++++++++- + ipaclient/install/ipa_certupdate.py | 2 ++ + ipaplatform/base/paths.py | 2 ++ + ipaserver/install/cainstance.py | 11 +++++++---- + ipaserver/install/ipa_backup.py | 2 ++ + ipaserver/install/krbinstance.py | 4 +++- + ipaserver/install/server/install.py | 10 ++++++++++ + ipaserver/install/server/replicainstall.py | 4 ++++ + ipaserver/install/server/upgrade.py | 4 +++- + 12 files changed, 60 insertions(+), 8 deletions(-) + +diff --git a/client/Makefile.am b/client/Makefile.am +index b6c9dea437460b0f912854a6a2fb9d1f30f3b1e7..e354cb41a4ee0d7da04197abe0e750c5d727bb4d 100644 +--- a/client/Makefile.am ++++ b/client/Makefile.am +@@ -101,4 +101,5 @@ EXTRA_DIST = \ + + install-data-hook: + $(INSTALL) -d -m 755 $(DESTDIR)$(IPA_SYSCONF_DIR)/nssdb ++ $(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa-client/pki + $(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa-client/sysrestore +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 0335a9970be82e80e98696f3d7fd4ec64894ef5f..6cb37ae53b039aa1d0e0509f62a3237504be6555 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1097,6 +1097,15 @@ if [ $1 -gt 1 ] ; then + fi + fi + ++ if [ $restore -ge 2 ]; then ++ if grep -E -q '\s*pkinit_anchors = FILE:/etc/ipa/ca.crt$' /etc/krb5.conf 2>/dev/null; then ++ sed -E 's|(\s*)pkinit_anchors = FILE:/etc/ipa/ca.crt$|\1pkinit_anchors = FILE:/var/lib/ipa-client/pki/kdc-ca-bundle.pem\n\1pkinit_pool = FILE:/var/lib/ipa-client/pki/ca-bundle.pem|' /etc/krb5.conf >/etc/krb5.conf.ipanew ++ mv -Z /etc/krb5.conf.ipanew /etc/krb5.conf ++ cp /etc/ipa/ca.crt /var/lib/ipa-client/pki/kdc-ca-bundle.pem ++ cp /etc/ipa/ca.crt /var/lib/ipa-client/pki/ca-bundle.pem ++ fi ++ fi ++ + if [ -f '/etc/sysconfig/ntpd' -a $restore -ge 2 ]; then + if grep -E -q 'OPTIONS=.*-u ntp:ntp' /etc/sysconfig/ntpd 2>/dev/null; then + sed -r '/OPTIONS=/ { s/\s+-u ntp:ntp\s+/ /; s/\s*-u ntp:ntp\s*// }' /etc/sysconfig/ntpd >/etc/sysconfig/ntpd.ipanew +@@ -1468,6 +1477,7 @@ fi + %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt + %ghost %config(noreplace) %{_sysconfdir}/pki/ca-trust/source/ipa.p11-kit + %dir %{_localstatedir}/lib/ipa-client ++%dir %{_localstatedir}/lib/ipa-client/pki + %dir %{_localstatedir}/lib/ipa-client/sysrestore + %{_mandir}/man5/default.conf.5* + +diff --git a/install/share/krb5.conf.template b/install/share/krb5.conf.template +index e8b2ad8dace8264cd9345285f55c42422bf81ca3..1f18ff90d34ccccb42c4b64d188e7d70e9892b71 100644 +--- a/install/share/krb5.conf.template ++++ b/install/share/krb5.conf.template +@@ -21,7 +21,8 @@ $OTHER_LIBDEFAULTS + master_kdc = $FQDN:88 + admin_server = $FQDN:749 + default_domain = $DOMAIN +- pkinit_anchors = FILE:/etc/ipa/ca.crt ++ pkinit_anchors = FILE:$KDC_CA_BUNDLE_PEM ++ pkinit_pool = FILE:$CA_BUNDLE_PEM + } + + [domain_realm] +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index e78be904dd6bad491d9f3c1bb1e1410bc1779d45..6f10f5258747881b9af8c6b70b499f9ff7d577ff 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -710,7 +710,11 @@ def configure_krb5_conf( + kropts.append(krbconf.setOption('default_domain', cli_domain)) + + kropts.append( +- krbconf.setOption('pkinit_anchors', 'FILE:%s' % paths.IPA_CA_CRT)) ++ krbconf.setOption('pkinit_anchors', ++ 'FILE:%s' % paths.KDC_CA_BUNDLE_PEM)) ++ kropts.append( ++ krbconf.setOption('pkinit_pool', ++ 'FILE:%s' % paths.CA_BUNDLE_PEM)) + ropts = [{ + 'name': cli_realm, + 'type': 'subsection', +@@ -2770,6 +2774,13 @@ def _install(options): + ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u)) + for (c, n, t, u) in ca_certs] + ++ x509.write_certificate_list( ++ [c for c, n, t, u in ca_certs if t is not False], ++ paths.KDC_CA_BUNDLE_PEM) ++ x509.write_certificate_list( ++ [c for c, n, t, u in ca_certs if t is not False], ++ paths.CA_BUNDLE_PEM) ++ + # Add the CA certificates to the IPA NSS database + root_logger.debug("Adding CA certificates to the IPA NSS database.") + ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR) +@@ -3317,6 +3328,8 @@ def uninstall(options): + + # Remove the CA cert + remove_file(paths.IPA_CA_CRT) ++ remove_file(paths.KDC_CA_BUNDLE_PEM) ++ remove_file(paths.CA_BUNDLE_PEM) + + root_logger.info("Client uninstall complete.") + +diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py +index 7dc88f07ae14e5416f6fe3dc8400b7d4bcabef72..7e8527e1fcb575844e8f4c90016435124b70e381 100644 +--- a/ipaclient/install/ipa_certupdate.py ++++ b/ipaclient/install/ipa_certupdate.py +@@ -113,6 +113,8 @@ class CertUpdate(admintool.AdminTool): + + def update_client(self, certs): + self.update_file(paths.IPA_CA_CRT, certs) ++ self.update_file(paths.KDC_CA_BUNDLE_PEM, certs) ++ self.update_file(paths.CA_BUNDLE_PEM, certs) + + ipa_db = certdb.NSSDatabase(api.env.nss_dir) + +diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py +index f80c9e95ab875222887e3692ab80151f84345469..804fddee60f787e161947bbe4b1914995257ceb4 100644 +--- a/ipaplatform/base/paths.py ++++ b/ipaplatform/base/paths.py +@@ -331,6 +331,8 @@ class BasePathNamespace(object): + VAR_RUN_DIRSRV_DIR = "/var/run/dirsrv" + IPA_CCACHES = "/var/run/ipa/ccaches" + HTTP_CCACHE = "/var/lib/ipa/gssproxy/http.ccache" ++ CA_BUNDLE_PEM = "/var/lib/ipa-client/pki/ca-bundle.pem" ++ KDC_CA_BUNDLE_PEM = "/var/lib/ipa-client/pki/kdc-ca-bundle.pem" + IPA_RENEWAL_LOCK = "/var/run/ipa/renewal.lock" + SVC_LIST_FILE = "/var/run/ipa/services.list" + KRB5CC_SAMBA = "/var/run/samba/krb5cc_samba" +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index a4aa4f2069277181501ebd92f3795c452b10acd0..b8c8cc4fc4532fc2c911ec174d363f8280ce863b 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -794,10 +794,13 @@ class CAInstance(DogtagInstance): + certlist = x509.pkcs7_to_pems(data, x509.DER) + + # We have all the certificates in certlist, write them to a PEM file +- with open(paths.IPA_CA_CRT, 'w') as ipaca_pem: +- for cert in certlist: +- ipaca_pem.write(cert) +- ipaca_pem.write('\n') ++ for path in [paths.IPA_CA_CRT, ++ paths.KDC_CA_BUNDLE_PEM, ++ paths.CA_BUNDLE_PEM]: ++ with open(path, 'w') as ipaca_pem: ++ for cert in certlist: ++ ipaca_pem.write(cert) ++ ipaca_pem.write('\n') + + def __request_ra_certificate(self): + # create a temp file storing the pwd +diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py +index 40f08d7d727a8b97b5996f15d27c1e20788e1473..f8cdd56d26636678279ba5afb423c5eef10c33d0 100644 +--- a/ipaserver/install/ipa_backup.py ++++ b/ipaserver/install/ipa_backup.py +@@ -150,6 +150,8 @@ class Backup(admintool.AdminTool): + paths.SSHD_CONFIG, + paths.SSH_CONFIG, + paths.KRB5_CONF, ++ paths.KDC_CA_BUNDLE_PEM, ++ paths.CA_BUNDLE_PEM, + paths.IPA_CA_CRT, + paths.IPA_DEFAULT_CONF, + paths.DS_KEYTAB, +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 2f14ff592064d3446f73b31e615b2de88d6d786c..e52577bbaa15064946f9a3c9720aa40ffc3251aa 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -261,7 +261,9 @@ class KrbInstance(service.Service): + KRB5KDC_KADM5_KEYTAB=paths.KRB5KDC_KADM5_KEYTAB, + KDC_CERT=paths.KDC_CERT, + KDC_KEY=paths.KDC_KEY, +- CACERT_PEM=paths.CACERT_PEM) ++ CACERT_PEM=paths.CACERT_PEM, ++ KDC_CA_BUNDLE_PEM=paths.KDC_CA_BUNDLE_PEM, ++ CA_BUNDLE_PEM=paths.CA_BUNDLE_PEM) + + # IPA server/KDC is not a subdomain of default domain + # Proper domain-realm mapping needs to be specified +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 25c21db721c58388ae8fd6ab1fbc443d513a4324..c1bdce6c8459dfeabd0096d105e535ec4ee56a2a 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -796,6 +796,16 @@ def install(installer): + x509.write_certificate(http_ca_cert, paths.IPA_CA_CRT) + os.chmod(paths.IPA_CA_CRT, 0o444) + ++ if not options.no_pkinit: ++ x509.write_certificate(http_ca_cert, paths.KDC_CA_BUNDLE_PEM) ++ else: ++ with open(paths.KDC_CA_BUNDLE_PEM, 'w'): ++ pass ++ os.chmod(paths.KDC_CA_BUNDLE_PEM, 0o444) ++ ++ x509.write_certificate(http_ca_cert, paths.CA_BUNDLE_PEM) ++ os.chmod(paths.CA_BUNDLE_PEM, 0o444) ++ + # we now need to enable ssl on the ds + ds.enable_ssl() + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index c19edceec42845f3169adc923762f700739232f2..66d7ba44645aed69b12f0e5ea14f5080492fe5ef 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1390,6 +1390,10 @@ def install(installer): + + # Update and istall updated CA file + cafile = install_ca_cert(conn, api.env.basedn, api.env.realm, cafile) ++ install_ca_cert(conn, api.env.basedn, api.env.realm, cafile, ++ destfile=paths.KDC_CA_BUNDLE_PEM) ++ install_ca_cert(conn, api.env.basedn, api.env.realm, cafile, ++ destfile=paths.CA_BUNDLE_PEM) + + # Configure dirsrv + ds = install_replica_ds(config, options, ca_enabled, +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index c244958f4cddba0d1edded5165a295b1e1ee2b8a..648dc1f29c44f89d9fbceb7b50373d93c88b5c1a 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1831,7 +1831,9 @@ def upgrade_configuration(): + KRB5KDC_KADM5_KEYTAB=paths.KRB5KDC_KADM5_KEYTAB, + KDC_CERT=paths.KDC_CERT, + KDC_KEY=paths.KDC_KEY, +- CACERT_PEM=paths.CACERT_PEM) ++ CACERT_PEM=paths.CACERT_PEM, ++ KDC_CA_BUNDLE_PEM=paths.KDC_CA_BUNDLE_PEM, ++ CA_BUNDLE_PEM=paths.CA_BUNDLE_PEM) + krb.add_anonymous_principal() + setup_pkinit(krb) + +-- +2.9.4 + diff --git a/SOURCES/0148-install-introduce-generic-Kerberos-Augeas-lens.patch b/SOURCES/0148-install-introduce-generic-Kerberos-Augeas-lens.patch new file mode 100644 index 0000000..eed37f5 --- /dev/null +++ b/SOURCES/0148-install-introduce-generic-Kerberos-Augeas-lens.patch @@ -0,0 +1,98 @@ +From 01440531b0805d647b0a0a37e2c3ea9489d19a35 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 18 May 2017 07:57:40 +0000 +Subject: [PATCH] install: introduce generic Kerberos Augeas lens + +Introduce new IPAKrb5 lens to handle krb5.conf and kdc.conf changes using +Augeas. The stock Krb5 lens does not work on our krb5.conf and kdc.conf. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + freeipa.spec.in | 1 + + install/share/Makefile.am | 1 + + install/share/ipakrb5.aug | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 48 insertions(+) + create mode 100644 install/share/ipakrb5.aug + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 6cb37ae53b039aa1d0e0509f62a3237504be6555..790e5838e0ba45ea9bbfe3bc3a1bd40c0bd3ac1a 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1362,6 +1362,7 @@ fi + %dir %{_usr}/share/ipa/schema.d + %attr(0644,root,root) %{_usr}/share/ipa/schema.d/README + %attr(0644,root,root) %{_usr}/share/ipa/gssapi.login ++%{_usr}/share/ipa/ipakrb5.aug + + %files server-dns + %defattr(-,root,root,-) +diff --git a/install/share/Makefile.am b/install/share/Makefile.am +index b27861da37153d77d693ce6e46340525bbd50173..85a061c6976dcc55b0ba2250423a344e14f2ce97 100644 +--- a/install/share/Makefile.am ++++ b/install/share/Makefile.am +@@ -89,6 +89,7 @@ dist_app_DATA = \ + gssapi.login \ + ipa.conf.tmpfiles \ + gssproxy.conf.template \ ++ ipakrb5.aug \ + $(NULL) + + kdcproxyconfdir = $(IPA_SYSCONF_DIR)/kdcproxy +diff --git a/install/share/ipakrb5.aug b/install/share/ipakrb5.aug +new file mode 100644 +index 0000000000000000000000000000000000000000..4a31a84e147a680067acddac683c672ccb6f9c31 +--- /dev/null ++++ b/install/share/ipakrb5.aug +@@ -0,0 +1,46 @@ ++module IPAKrb5 = ++ autoload xfm ++ ++ let dels (s:string) = Util.del_str s ++ ++ let indent = Util.indent ++ let space = Sep.space ++ let opt_space = Sep.opt_space ++ let sep = Sep.space_equal ++ let eol = IniFile.eol ++ ++ let kw = Rx.word ++ let val = Rx.space_in ++ ++ let comment = IniFile.comment IniFile.comment_re "# " ++ let empty = IniFile.empty ++ ++ let entry_generic (v:lens) = [ indent . key kw . sep . v . eol ] ++ ++ (* ++ FIXME: combine entry and subrecord into a single recursive lens ++ ++ This does not work for some reason: ++ let rec entry = entry_generic ( store ( val - "{" ) ) ++ | entry_generic ( dels "{" . eol ++ . ( entry | comment | empty )* ++ . indent . dels "}" ) ++ *) ++ let entry = entry_generic ( store ( val - "{" ) ) ++ let subrecord = entry_generic ( dels "{" . eol ++ . ( entry | comment | empty )* ++ . indent . dels "}" ) ++ ++ let title = IniFile.indented_title kw ++ let record = IniFile.record title ( entry | subrecord | comment ) ++ ++ let directive = Build.key_value_line kw space ( store val ) ++ ++ let lns = IniFile.lns record ( directive | comment ) ++ ++ let filter = incl "/etc/krb5.conf" ++ . incl "/etc/krb5.conf.d/*" ++ . incl "/var/kerberos/krb5kdc/kdc.conf" ++ . Util.stdexcl ++ ++ let xfm = transform lns filter +-- +2.9.4 + diff --git a/SOURCES/0149-server-install-fix-KDC-PKINIT-configuration.patch b/SOURCES/0149-server-install-fix-KDC-PKINIT-configuration.patch new file mode 100644 index 0000000..5e9747e --- /dev/null +++ b/SOURCES/0149-server-install-fix-KDC-PKINIT-configuration.patch @@ -0,0 +1,292 @@ +From 2558a0336e9d61b2b7e321b7dfa32426151b4bbb Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:09:03 +0000 +Subject: [PATCH] server install: fix KDC PKINIT configuration + +Set `pkinit_pool` in `kdc.conf` to a CA certificate bundle of all CAs known +to IPA. + +Make sure `cacert.pem` is exported in all installation code paths. + +Use the KDC certificate itself as a PKINIT anchor in `login_password`. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + install/restart_scripts/Makefile.am | 1 + + install/restart_scripts/renew_kdc_cert | 31 ++++++++++++++++++ + install/share/kdc.conf.template | 2 ++ + ipaclient/install/ipa_certupdate.py | 1 + + ipalib/install/kinit.py | 7 +++-- + ipaserver/install/krbinstance.py | 27 +++++++++------- + ipaserver/install/server/upgrade.py | 57 ++++++++++++++++++++++++++-------- + ipaserver/rpcserver.py | 5 ++- + 8 files changed, 103 insertions(+), 28 deletions(-) + create mode 100755 install/restart_scripts/renew_kdc_cert + +diff --git a/install/restart_scripts/Makefile.am b/install/restart_scripts/Makefile.am +index 04881b406b6be92b46e630f30d724918506e2aa8..240cebdee8cae7a0c7bdf88f5300583b4232fc94 100644 +--- a/install/restart_scripts/Makefile.am ++++ b/install/restart_scripts/Makefile.am +@@ -5,6 +5,7 @@ app_DATA = \ + restart_dirsrv \ + restart_httpd \ + renew_ca_cert \ ++ renew_kdc_cert \ + renew_ra_cert \ + stop_pkicad \ + renew_ra_cert_pre \ +diff --git a/install/restart_scripts/renew_kdc_cert b/install/restart_scripts/renew_kdc_cert +new file mode 100755 +index 0000000000000000000000000000000000000000..9247920874fc9540ac3421dd59fd902cc195243f +--- /dev/null ++++ b/install/restart_scripts/renew_kdc_cert +@@ -0,0 +1,31 @@ ++#!/usr/bin/python2 -E ++# ++# Copyright (C) 2017 FreeIPA Contributors see COPYING for license ++# ++ ++import os ++import syslog ++import traceback ++ ++from ipaplatform import services ++from ipaplatform.paths import paths ++from ipaserver.install import certs ++ ++ ++def main(): ++ with certs.renewal_lock: ++ os.chmod(paths.KDC_CERT, 0o644) ++ ++ try: ++ if services.knownservices.krb5kdc.is_running(): ++ syslog.syslog(syslog.LOG_NOTICE, 'restarting krb5kdc') ++ services.knownservices.krb5kdc.restart() ++ except Exception as e: ++ syslog.syslog( ++ syslog.LOG_ERR, "cannot restart krb5kdc: {}".format(e)) ++ ++ ++try: ++ main() ++except Exception: ++ syslog.syslog(syslog.LOG_ERR, traceback.format_exc()) +diff --git a/install/share/kdc.conf.template b/install/share/kdc.conf.template +index ec53a1ff5f7110704143074bc7a5d1dfdc705344..306351b86111eb0e883b2398678f50b821e0ad7f 100644 +--- a/install/share/kdc.conf.template ++++ b/install/share/kdc.conf.template +@@ -13,5 +13,7 @@ + default_principal_flags = +preauth + ; admin_keytab = $KRB5KDC_KADM5_KEYTAB + pkinit_identity = FILE:$KDC_CERT,$KDC_KEY ++ pkinit_anchors = FILE:$KDC_CERT + pkinit_anchors = FILE:$CACERT_PEM ++ pkinit_pool = FILE:$CA_BUNDLE_PEM + } +diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py +index 7e8527e1fcb575844e8f4c90016435124b70e381..93da8422b6f503b8c44db678736d7f71f7d7567e 100644 +--- a/ipaclient/install/ipa_certupdate.py ++++ b/ipaclient/install/ipa_certupdate.py +@@ -172,6 +172,7 @@ class CertUpdate(admintool.AdminTool): + certmonger.modify(request_id, ca='dogtag-ipa-ca-renew-agent') + + self.update_file(paths.CA_CRT, certs) ++ self.update_file(paths.CACERT_PEM, certs) + + def update_file(self, filename, certs, mode=0o444): + certs = (c[0] for c in certs if c[2] is not False) +diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py +index fb6caee4d6b5fef27b53753b21ad83572da31ac4..73471f103eabfe39580c8fbd0665157f635fa5c5 100644 +--- a/ipalib/install/kinit.py ++++ b/ipalib/install/kinit.py +@@ -96,7 +96,7 @@ def kinit_password(principal, password, ccache_name, config=None, + raise RuntimeError(result.error_output) + + +-def kinit_armor(ccache_name, pkinit_anchor=None): ++def kinit_armor(ccache_name, pkinit_anchors=None): + """ + perform anonymous pkinit to obtain anonymous ticket to be used as armor + for FAST. +@@ -113,8 +113,9 @@ def kinit_armor(ccache_name, pkinit_anchor=None): + env = {'LC_ALL': 'C'} + args = [paths.KINIT, '-n', '-c', ccache_name] + +- if pkinit_anchor is not None: +- args.extend(['-X', 'X509_anchors=FILE:{}'.format(pkinit_anchor)]) ++ if pkinit_anchors is not None: ++ for pkinit_anchor in pkinit_anchors: ++ 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 +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index e52577bbaa15064946f9a3c9720aa40ffc3251aa..1692e0b2badb23c18386346a552c83881018cf60 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -20,7 +20,6 @@ + from __future__ import absolute_import + from __future__ import print_function + +-import shutil + import os + import pwd + import socket +@@ -28,6 +27,8 @@ import dbus + + import dns.name + ++from ipalib import x509 ++from ipalib.install import certstore + from ipaserver.install import service + from ipaserver.install import installutils + from ipapython import ipaldap +@@ -430,7 +431,8 @@ class KrbInstance(service.Service): + ca=certmonger_ca, + dns=self.fqdn, + storage='FILE', +- profile=KDC_PROFILE) ++ profile=KDC_PROFILE, ++ post_command='renew_kdc_cert') + except dbus.DBusException as e: + # if the certificate is already tracked, ignore the error + name = e.get_dbus_name() +@@ -448,17 +450,23 @@ class KrbInstance(service.Service): + service.set_service_entry_config( + 'KDC', self.fqdn, [PKINIT_ENABLED], self.suffix) + ++ def _install_pkinit_ca_bundle(self): ++ ca_certs = certstore.get_ca_certs(self.api.Backend.ldap2, ++ self.api.env.basedn, ++ self.api.env.realm, ++ False) ++ ca_certs = [c for c, _n, t, _u in ca_certs if t is not False] ++ x509.write_certificate_list(ca_certs, paths.CACERT_PEM) ++ + def issue_selfsigned_pkinit_certs(self): + self._call_certmonger(certmonger_ca="SelfSign") +- # for self-signed certificate, the certificate is its own CA, copy it +- # as CA cert +- shutil.copyfile(paths.KDC_CERT, paths.CACERT_PEM) ++ with open(paths.CACERT_PEM, 'w'): ++ pass + + def issue_ipa_ca_signed_pkinit_certs(self): + try: + self._call_certmonger() +- # copy IPA CA bundle to the KDC's CA cert bundle +- shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) ++ self._install_pkinit_ca_bundle() + self.pkinit_enable() + except RuntimeError as e: + root_logger.error("PKINIT certificate request failed: %s", e) +@@ -473,10 +481,7 @@ class KrbInstance(service.Service): + certs.install_key_from_p12(self.pkcs12_info[0], + self.pkcs12_info[1], + paths.KDC_KEY) +- # copy IPA CA bundle to the KDC's CA cert bundle +- # NOTE: this may not be the same set of CA certificates trusted by +- # externally provided PKINIT cert. +- shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) ++ self._install_pkinit_ca_bundle() + self.pkinit_enable() + + def setup_pkinit(self): +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 648dc1f29c44f89d9fbceb7b50373d93c88b5c1a..db86353165809c57d1ac27bf762393721231fefd 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -11,6 +11,7 @@ import pwd + import fileinput + import sys + ++from augeas import Augeas + import dns.exception + + import six +@@ -1527,19 +1528,49 @@ def setup_pkinit(krb): + else: + krb.issue_selfsigned_pkinit_certs() + +- # reconfigure KDC just in case in order to handle potentially broken +- # 4.5.0 -> 4.5.1 upgrade path +- replacevars = dict() +- replacevars['pkinit_identity'] = 'FILE:{},{}'.format( +- paths.KDC_CERT,paths.KDC_KEY) +- appendvars = {} +- ipautil.backup_config_and_replace_variables( +- krb.fstore, paths.KRB5KDC_KDC_CONF, replacevars=replacevars, +- appendvars=appendvars) +- tasks.restore_context(paths.KRB5KDC_KDC_CONF) +- if krb.is_running(): +- krb.stop() +- krb.start() ++ aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD, ++ loadpath=paths.USR_SHARE_IPA_DIR) ++ try: ++ aug.transform('IPAKrb5', paths.KRB5KDC_KDC_CONF) ++ aug.load() ++ ++ path = '/files{}/realms/{}'.format(paths.KRB5KDC_KDC_CONF, krb.realm) ++ modified = False ++ ++ value = 'FILE:{},{}'.format(paths.KDC_CERT, paths.KDC_KEY) ++ expr = '{}[count(pkinit_identity)=1][pkinit_identity="{}"]'.format( ++ path, value) ++ if not aug.match(expr): ++ aug.remove('{}/pkinit_identity'.format(path)) ++ aug.set('{}/pkinit_identity'.format(path), value) ++ modified = True ++ ++ for value in ['FILE:{}'.format(paths.KDC_CERT), ++ 'FILE:{}'.format(paths.CACERT_PEM)]: ++ expr = '{}/pkinit_anchors[.="{}"]'.format(path, value) ++ if not aug.match(expr): ++ aug.set('{}/pkinit_anchors[last()+1]'.format(path), value) ++ modified = True ++ ++ value = 'FILE:{}'.format(paths.CA_BUNDLE_PEM) ++ expr = '{}/pkinit_pool[.="{}"]'.format(path, value) ++ if not aug.match(expr): ++ aug.set('{}/pkinit_pool[last()+1]'.format(path), value) ++ modified = True ++ ++ if modified: ++ try: ++ aug.save() ++ except IOError: ++ for error_path in aug.match('/augeas//error'): ++ root_logger.error('augeas: %s', aug.get(error_path)) ++ raise ++ ++ if krb.is_running(): ++ krb.stop() ++ krb.start() ++ finally: ++ aug.close() + + + def disable_httpd_system_trust(http): +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 996a3d29884ca0180c39841f6986abf9b23ff13a..4cde2815a0fe9332d67c84b531f573ff88b1a302 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -945,7 +945,10 @@ class login_password(Backend, KerberosSession): + self.debug('Obtaining armor in ccache %s', armor_path) + + try: +- kinit_armor(armor_path, pkinit_anchor=paths.CACERT_PEM) ++ kinit_armor( ++ armor_path, ++ pkinit_anchors=[paths.KDC_CERT, paths.KDC_CA_BUNDLE_PEM], ++ ) + except RuntimeError as e: + self.error("Failed to obtain armor cache") + # We try to continue w/o armor, 2FA will be impacted +-- +2.9.4 + diff --git a/SOURCES/0150-ipapython.ipautil.run-Add-option-to-set-umask-before.patch b/SOURCES/0150-ipapython.ipautil.run-Add-option-to-set-umask-before.patch new file mode 100644 index 0000000..bc31a32 --- /dev/null +++ b/SOURCES/0150-ipapython.ipautil.run-Add-option-to-set-umask-before.patch @@ -0,0 +1,85 @@ +From 68d97e2beca1ee3b398fc5f0d3ed70aa8b69e732 Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Tue, 11 Apr 2017 17:35:30 +0200 +Subject: [PATCH] ipapython.ipautil.run: Add option to set umask before + executing command + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipapython/ipautil.py | 43 +++++++++++++++++++++++-------------------- + 1 file changed, 23 insertions(+), 20 deletions(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index cd66328e6c9a0f69e6f83582a9d288ac239c5be3..317fc225b722ad3ce2f4b9d92822b4f19d49adb9 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -309,7 +309,7 @@ class _RunResult(collections.namedtuple('_RunResult', + def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, + capture_output=False, skip_output=False, cwd=None, + runas=None, suplementary_groups=[], +- capture_error=False, encoding=None, redirect_output=False): ++ capture_error=False, encoding=None, redirect_output=False, umask=None): + """ + Execute an external command. + +@@ -345,6 +345,7 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, + error_output, and (if it's not bytes) stdin. + If None, the current encoding according to locale is used. + :param redirect_output: Redirect (error) output to standard (error) output. ++ :param umask: Set file-creation mask before running the command. + + :return: An object with these attributes: + +@@ -416,25 +417,27 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, + root_logger.debug('Starting external process') + root_logger.debug('args=%s' % arg_string) + +- preexec_fn = None +- if runas is not None: +- pent = pwd.getpwnam(runas) +- +- suplementary_gids = [ +- grp.getgrnam(group).gr_gid for group in suplementary_groups +- ] +- +- root_logger.debug('runas=%s (UID %d, GID %s)', runas, +- pent.pw_uid, pent.pw_gid) +- if suplementary_groups: +- for group, gid in zip(suplementary_groups, suplementary_gids): +- root_logger.debug('suplementary_group=%s (GID %d)', group, gid) +- +- preexec_fn = lambda: ( +- os.setgroups(suplementary_gids), +- os.setregid(pent.pw_gid, pent.pw_gid), +- os.setreuid(pent.pw_uid, pent.pw_uid), +- ) ++ def preexec_fn(): ++ if runas is not None: ++ pent = pwd.getpwnam(runas) ++ ++ suplementary_gids = [ ++ grp.getgrnam(group).gr_gid for group in suplementary_groups ++ ] ++ ++ root_logger.debug('runas=%s (UID %d, GID %s)', runas, ++ pent.pw_uid, pent.pw_gid) ++ if suplementary_groups: ++ for group, gid in zip(suplementary_groups, suplementary_gids): ++ root_logger.debug('suplementary_group=%s (GID %d)', ++ group, gid) ++ ++ os.setgroups(suplementary_gids) ++ os.setregid(pent.pw_gid, pent.pw_gid) ++ os.setreuid(pent.pw_uid, pent.pw_uid) ++ ++ if umask: ++ os.umask(umask) + + try: + p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err, +-- +2.9.4 + diff --git a/SOURCES/0151-certs-do-not-export-keys-world-readable-in-install_k.patch b/SOURCES/0151-certs-do-not-export-keys-world-readable-in-install_k.patch new file mode 100644 index 0000000..b12e21d --- /dev/null +++ b/SOURCES/0151-certs-do-not-export-keys-world-readable-in-install_k.patch @@ -0,0 +1,33 @@ +From 9e724963967a79fd171e79d2353ec7b655f13c47 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 11 May 2017 07:00:42 +0000 +Subject: [PATCH] certs: do not export keys world-readable in + install_key_from_p12 + +Make sure the exported private key files are readable only by the owner. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipaserver/install/certs.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 17b9ebad4a128e292e453af44ca9d63cfb1e6ea2..06a7e2143964484fa45106ca381043eb440dc5b1 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -73,7 +73,8 @@ def install_key_from_p12(p12_fname, p12_passwd, pem_fname): + pwd = ipautil.write_tmp_file(p12_passwd) + ipautil.run([paths.OPENSSL, "pkcs12", "-nodes", "-nocerts", + "-in", p12_fname, "-out", pem_fname, +- "-passin", "file:" + pwd.name]) ++ "-passin", "file:" + pwd.name], ++ umask=0o077) + + + def export_pem_p12(pkcs12_fname, pkcs12_pwd_fname, nickname, pem_fname): +-- +2.9.4 + diff --git a/SOURCES/0152-certs-do-not-export-CA-certs-in-install_pem_from_p12.patch b/SOURCES/0152-certs-do-not-export-CA-certs-in-install_pem_from_p12.patch new file mode 100644 index 0000000..90d2d3e --- /dev/null +++ b/SOURCES/0152-certs-do-not-export-CA-certs-in-install_pem_from_p12.patch @@ -0,0 +1,33 @@ +From b66796bcd888e0204955913e642d8e45937843dd Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:12:36 +0000 +Subject: [PATCH] certs: do not export CA certs in install_pem_from_p12 + +This fixes `kdc.crt` containing the full chain rather than just the KDC +certificate in CA-less server install. + +https://pagure.io/freeipa/issue/6831 +https://pagure.io/freeipa/issue/6869 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipaserver/install/certs.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 06a7e2143964484fa45106ca381043eb440dc5b1..02c479d92511fcf4043e7d6798c85cf8256c3299 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -64,7 +64,7 @@ def get_cert_nickname(cert): + + def install_pem_from_p12(p12_fname, p12_passwd, pem_fname): + pwd = ipautil.write_tmp_file(p12_passwd) +- ipautil.run([paths.OPENSSL, "pkcs12", "-nokeys", ++ ipautil.run([paths.OPENSSL, "pkcs12", "-nokeys", "-clcerts", + "-in", p12_fname, "-out", pem_fname, + "-passin", "file:" + pwd.name]) + +-- +2.9.4 + diff --git a/SOURCES/0153-server-install-fix-KDC-certificate-validation-in-CA-.patch b/SOURCES/0153-server-install-fix-KDC-certificate-validation-in-CA-.patch new file mode 100644 index 0000000..039d3c5 --- /dev/null +++ b/SOURCES/0153-server-install-fix-KDC-certificate-validation-in-CA-.patch @@ -0,0 +1,203 @@ +From 5301e86fccfde6ab444a2c600a412487318fbd13 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:14:27 +0000 +Subject: [PATCH] server install: fix KDC certificate validation in CA-less + +Verify that the provided certificate has the extended key usage and subject +alternative name required for KDC. + +https://pagure.io/freeipa/issue/6831 +https://pagure.io/freeipa/issue/6869 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipapython/certdb.py | 42 ++++++++++++++++++++++++++++++ + ipaserver/install/installutils.py | 24 +++++++++++------ + ipaserver/install/server/install.py | 11 ++++++-- + ipaserver/install/server/replicainstall.py | 11 ++++++-- + 4 files changed, 76 insertions(+), 12 deletions(-) + +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 1ee2603653452577476cf413e6af951cd29c273e..114c58340253141706afa461ecaf87797562ca1d 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -24,14 +24,17 @@ import pwd + import grp + import re + import tempfile ++from tempfile import NamedTemporaryFile + import shutil + import base64 + from cryptography.hazmat.primitives import serialization ++import cryptography.x509 + from nss import nss + from nss.error import NSPRError + + from ipapython.dn import DN + from ipapython.ipa_log_manager import root_logger ++from ipapython.kerberos import Principal + from ipapython import ipautil + from ipalib import x509 # pylint: disable=ipa-forbidden-import + +@@ -182,6 +185,38 @@ def unparse_trust_flags(trust_flags): + return trust_flags + + ++def verify_kdc_cert_validity(kdc_cert, ca_certs, realm): ++ pem_kdc_cert = kdc_cert.public_bytes(serialization.Encoding.PEM) ++ pem_ca_certs = '\n'.join( ++ cert.public_bytes(serialization.Encoding.PEM) for cert in ca_certs) ++ ++ with NamedTemporaryFile() as kdc_file, NamedTemporaryFile() as ca_file: ++ kdc_file.write(pem_kdc_cert) ++ kdc_file.flush() ++ ca_file.write(pem_ca_certs) ++ ca_file.flush() ++ ++ try: ++ ipautil.run( ++ [OPENSSL, 'verify', '-CAfile', ca_file.name, kdc_file.name]) ++ eku = kdc_cert.extensions.get_extension_for_class( ++ cryptography.x509.ExtendedKeyUsage) ++ list(eku.value).index( ++ cryptography.x509.ObjectIdentifier(x509.EKU_PKINIT_KDC)) ++ except (ipautil.CalledProcessError, ++ cryptography.x509.ExtensionNotFound, ++ ValueError): ++ raise ValueError("invalid for a KDC") ++ ++ principal = str(Principal(['krbtgt', realm], realm)) ++ gns = x509.process_othernames(x509.get_san_general_names(kdc_cert)) ++ for gn in gns: ++ if isinstance(gn, x509.KRB5PrincipalName) and gn.name == principal: ++ break ++ else: ++ raise ValueError("invalid for realm %s" % realm) ++ ++ + class NSSDatabase(object): + """A general-purpose wrapper around a NSS cert database + +@@ -707,3 +742,10 @@ class NSSDatabase(object): + finally: + del certdb, cert + nss.nss_shutdown() ++ ++ def verify_kdc_cert_validity(self, nickname, realm): ++ nicknames = self.get_trust_chain(nickname) ++ certs = [self.get_cert(nickname) for nickname in nicknames] ++ certs = [x509.load_certificate(cert, x509.DER) for cert in certs] ++ ++ verify_kdc_cert_validity(certs[-1], certs[:-1], realm) +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 5bce9894780bd920db11196b925492a7fe8f22d0..d2283af20485fd5d66bfd3cc49059d08d1802575 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -1001,7 +1001,7 @@ def handle_error(error, log_file_name=None): + + + def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files, +- host_name): ++ host_name=None, realm_name=None): + """ + Load and verify server certificate and private key from multiple files + +@@ -1066,13 +1066,21 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files, + "CA certificate %s in %s is not valid: %s" % + (subject, ", ".join(cert_files), e)) + +- # Check server validity +- try: +- nssdb.verify_server_cert_validity(key_nickname, host_name) +- except ValueError as e: +- raise ScriptError( +- "The server certificate in %s is not valid: %s" % +- (", ".join(cert_files), e)) ++ if host_name is not None: ++ try: ++ nssdb.verify_server_cert_validity(key_nickname, host_name) ++ except ValueError as e: ++ raise ScriptError( ++ "The server certificate in %s is not valid: %s" % ++ (", ".join(cert_files), e)) ++ ++ if realm_name is not None: ++ try: ++ nssdb.verify_kdc_cert_validity(key_nickname, realm_name) ++ except ValueError as e: ++ raise ScriptError( ++ "The KDC certificate in %s is not valid: %s" % ++ (", ".join(cert_files), e)) + + out_file = tempfile.NamedTemporaryFile() + out_password = ipautil.ipa_generate_password() +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index c1bdce6c8459dfeabd0096d105e535ec4ee56a2a..03380b8d0e9150224b014a1a174d7ea81ccdcf00 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -520,12 +520,12 @@ def install_check(installer): + if options.pkinit_pin is None: + raise ScriptError( + "Kerberos KDC private key unlock password required") +- pkinit_pkcs12_file, pkinit_pin, _pkinit_ca_cert = load_pkcs12( ++ pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12( + cert_files=options.pkinit_cert_files, + key_password=options.pkinit_pin, + key_nickname=options.pkinit_cert_name, + ca_cert_files=options.ca_cert_files, +- host_name=host_name) ++ realm_name=realm_name) + pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin) + + if (options.http_cert_files and options.dirsrv_cert_files and +@@ -534,6 +534,13 @@ def install_check(installer): + "Apache Server SSL certificate and Directory Server SSL " + "certificate are not signed by the same CA certificate") + ++ if (options.http_cert_files and ++ options.pkinit_cert_files and ++ http_ca_cert != pkinit_ca_cert): ++ raise ScriptError( ++ "Apache Server SSL certificate and PKINIT KDC " ++ "certificate are not signed by the same CA certificate") ++ + if not options.dm_password: + dm_password = read_dm_password() + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 66d7ba44645aed69b12f0e5ea14f5080492fe5ef..6f71f0b51812943fea3fb1c576a0174c739a070b 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1069,12 +1069,12 @@ def promote_check(installer): + if options.pkinit_pin is None: + raise ScriptError( + "Kerberos KDC private key unlock password required") +- pkinit_pkcs12_file, pkinit_pin, _pkinit_ca_cert = load_pkcs12( ++ pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12( + cert_files=options.pkinit_cert_files, + key_password=options.pkinit_pin, + key_nickname=options.pkinit_cert_name, + ca_cert_files=options.ca_cert_files, +- host_name=config.host_name) ++ realm_name=config.realm_name) + pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin) + + if (options.http_cert_files and options.dirsrv_cert_files and +@@ -1083,6 +1083,13 @@ def promote_check(installer): + "Server SSL certificate are not signed by the same" + " CA certificate") + ++ if (options.http_cert_files and ++ options.pkinit_cert_files and ++ http_ca_cert != pkinit_ca_cert): ++ raise RuntimeError("Apache Server SSL certificate and PKINIT KDC " ++ "certificate are not signed by the same CA " ++ "certificate") ++ + installutils.verify_fqdn(config.host_name, options.no_host_dns) + installutils.verify_fqdn(config.master_host_name, options.no_host_dns) + +-- +2.9.4 + diff --git a/SOURCES/0154-replica-install-respect-pkinit-cert-file.patch b/SOURCES/0154-replica-install-respect-pkinit-cert-file.patch new file mode 100644 index 0000000..c0673f4 --- /dev/null +++ b/SOURCES/0154-replica-install-respect-pkinit-cert-file.patch @@ -0,0 +1,56 @@ +From c1b49645c22b91aff51a29e715e29c5df7a0892a Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Thu, 11 May 2017 07:40:40 +0000 +Subject: [PATCH] replica install: respect --pkinit-cert-file + +When --pkinit-cert-file is used, make sure the certificate and key is +actually passed to `KrbInstance`. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + ipaserver/install/server/replicainstall.py | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 6f71f0b51812943fea3fb1c576a0174c739a070b..b30133ffa22d410452ae04624d49db209175bed9 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -113,12 +113,13 @@ def install_replica_ds(config, options, ca_is_configured, remote_api, + return ds + + +-def install_krb(config, setup_pkinit=False, promote=False): ++def install_krb(config, setup_pkinit=False, pkcs12_info=None, promote=False): + krb = krbinstance.KrbInstance() + + # pkinit files +- pkcs12_info = make_pkcs12_info(config.dir, "pkinitcert.p12", +- "pkinit_pin.txt") ++ if pkcs12_info is None: ++ pkcs12_info = make_pkcs12_info(config.dir, "pkinitcert.p12", ++ "pkinit_pin.txt") + + krb.create_replica(config.realm_name, + config.master_host_name, config.host_name, +@@ -1350,6 +1351,7 @@ def install(installer): + cafile = installer._ca_file + dirsrv_pkcs12_info = installer._dirsrv_pkcs12_info + http_pkcs12_info = installer._http_pkcs12_info ++ pkinit_pkcs12_info = installer._pkinit_pkcs12_info + + remote_api = installer._remote_api + conn = remote_api.Backend.ldap2 +@@ -1430,6 +1432,7 @@ def install(installer): + krb = install_krb( + config, + setup_pkinit=not options.no_pkinit, ++ pkcs12_info=pkinit_pkcs12_info, + promote=promote) + + # we now need to enable ssl on the ds +-- +2.9.4 + diff --git a/SOURCES/0155-cacert-manage-support-PKINIT.patch b/SOURCES/0155-cacert-manage-support-PKINIT.patch new file mode 100644 index 0000000..5c59888 --- /dev/null +++ b/SOURCES/0155-cacert-manage-support-PKINIT.patch @@ -0,0 +1,74 @@ +From 7aca75a7142eba58d9cb3ab5d40f3224e53e2243 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:17:32 +0000 +Subject: [PATCH] cacert manage: support PKINIT + +Allow installing 3rd party CA certificates trusted to issue PKINIT KDC +and/or client certificates. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + install/tools/man/ipa-cacert-manage.1 | 2 +- + ipaserver/install/ipa_cacert_manage.py | 21 +++++++++++++++++---- + 2 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1 +index e36258d0f96aa1050fe88b05f4fe9a1a8f9a7978..03172814ffb603b656952ce5e9ad6af9c8238ab3 100644 +--- a/install/tools/man/ipa-cacert-manage.1 ++++ b/install/tools/man/ipa-cacert-manage.1 +@@ -90,7 +90,7 @@ File containing the IPA CA certificate and the external CA certificate chain. Th + Nickname for the certificate. + .TP + \fB\-t\fR \fITRUST_FLAGS\fR, \fB\-\-trust\-flags\fR=\fITRUST_FLAGS\fR +-Trust flags for the certificate in certutil format. Trust flags are of the form "X,Y,Z" where X is for SSL, Y is for S/MIME, and Z is for code signing. Use ",," for no explicit trust. ++Trust flags for the certificate in certutil format. Trust flags are of the form "A,B,C" or "A,B,C,D" where A is for SSL, B is for S/MIME, C is for code signing, and D is for PKINIT. Use ",," for no explicit trust. + .sp + The supported trust flags are: + .RS +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index d28a5966f054141819463cdb1dfef48ee1e46e92..e88e8b63ae94759ac835f3b3b31b0735d68a67b0 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -28,6 +28,7 @@ from ipalib.install import certmonger, certstore + from ipapython import admintool, ipautil + from ipapython.certdb import (EMPTY_TRUST_FLAGS, + EXTERNAL_CA_TRUST_FLAGS, ++ TrustFlags, + parse_trust_flags) + from ipapython.dn import DN + from ipaplatform.paths import paths +@@ -363,12 +364,24 @@ class CACertManage(admintool.AdminTool): + "http://www.freeipa.org/page/Troubleshooting for " + "troubleshooting guide)" % e) + +- trust_flags = options.trust_flags +- if ((set(trust_flags) - set(',CPTcgpuw')) or +- len(trust_flags.split(',')) != 3): ++ trust_flags = options.trust_flags.split(',') ++ if (set(options.trust_flags) - set(',CPTcgpuw') or ++ len(trust_flags) not in [3, 4]): + raise admintool.ScriptError("Invalid trust flags") + +- trust_flags = parse_trust_flags(trust_flags) ++ extra_flags = trust_flags[3:] ++ extra_usages = set() ++ if extra_flags: ++ if 'C' in extra_flags[0]: ++ extra_usages.add(x509.EKU_PKINIT_KDC) ++ if 'T' in extra_flags[0]: ++ extra_usages.add(x509.EKU_PKINIT_CLIENT_AUTH) ++ ++ trust_flags = parse_trust_flags(','.join(trust_flags[:3])) ++ trust_flags = TrustFlags(trust_flags.has_key, ++ trust_flags.trusted, ++ trust_flags.ca, ++ trust_flags.usages | extra_usages) + + try: + certstore.put_ca_cert_nss( +-- +2.9.4 + diff --git a/SOURCES/0156-server-certinstall-support-PKINIT.patch b/SOURCES/0156-server-certinstall-support-PKINIT.patch new file mode 100644 index 0000000..82dcce4 --- /dev/null +++ b/SOURCES/0156-server-certinstall-support-PKINIT.patch @@ -0,0 +1,163 @@ +From 96afd05dda2ce502994b6c9ceae819d79d96a666 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Wed, 3 May 2017 06:18:05 +0000 +Subject: [PATCH] server certinstall: support PKINIT + +Allow replacing the KDC certificate. + +https://pagure.io/freeipa/issue/6831 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Martin Babinsky +--- + install/tools/man/ipa-server-certinstall.1 | 5 ++- + ipaserver/install/ipa_server_certinstall.py | 70 +++++++++++++++++++++++++++-- + 2 files changed, 70 insertions(+), 5 deletions(-) + +diff --git a/install/tools/man/ipa-server-certinstall.1 b/install/tools/man/ipa-server-certinstall.1 +index d23bbd490e2b0454b8fb908e22f33c7a611c8874..35cd8c6c711119d7c782c6a89ac78b4894cec073 100644 +--- a/install/tools/man/ipa-server-certinstall.1 ++++ b/install/tools/man/ipa-server-certinstall.1 +@@ -22,7 +22,7 @@ ipa\-server\-certinstall \- Install new SSL server certificates + .SH "SYNOPSIS" + ipa\-server\-certinstall [\fIOPTION\fR]... FILE... + .SH "DESCRIPTION" +-Replace the current SSL Directory and/or Apache server certificate(s) with the certificate in the specified files. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. ++Replace the current Directory server SSL certificate, Apache server SSL certificate and/or Kerberos KDC certificate with the certificate in the specified files. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. + + PKCS#12 is a file format used to safely transport SSL certificates and public/private keypairs. + +@@ -37,6 +37,9 @@ Install the certificate on the Directory Server + \fB\-w\fR, \fB\-\-http\fR + Install the certificate in the Apache Web Server + .TP ++\fB\-k\fR, \fB\-\-kdc\fR ++Install the certificate in the Kerberos KDC ++.TP + \fB\-\-pin\fR=\fIPIN\fR + The password to unlock the private key + .TP +diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py +index 9f2cd9573a156949ae979e7b69fbd23adaf2feb8..a14a84f188c62170c8ac11f823ebba60609e4cc7 100644 +--- a/ipaserver/install/ipa_server_certinstall.py ++++ b/ipaserver/install/ipa_server_certinstall.py +@@ -21,12 +21,17 @@ + import os + import os.path + import pwd ++import tempfile + import optparse # pylint: disable=deprecated-module + ++from ipalib import x509 ++from ipalib.install import certmonger + from ipaplatform.constants import constants + from ipaplatform.paths import paths + from ipapython import admintool +-from ipapython.certdb import get_ca_nickname, NSSDatabase ++from ipapython.certdb import (get_ca_nickname, ++ NSSDatabase, ++ verify_kdc_cert_validity) + from ipapython.dn import DN + from ipalib import api, errors + from ipaserver.install import certs, dsinstance, installutils +@@ -35,7 +40,7 @@ from ipaserver.install import certs, dsinstance, installutils + class ServerCertInstall(admintool.AdminTool): + command_name = 'ipa-server-certinstall' + +- usage = "%prog <-d|-w> [options] ..." ++ usage = "%prog <-d|-w|-k> [options] ..." + + description = "Install new SSL server certificates." + +@@ -52,6 +57,10 @@ class ServerCertInstall(admintool.AdminTool): + dest="http", action="store_true", default=False, + help="install certificate for the http server") + parser.add_option( ++ "-k", "--kdc", ++ dest="kdc", action="store_true", default=False, ++ help="install PKINIT certificate for the KDC") ++ parser.add_option( + "--pin", + dest="pin", metavar="PIN", sensitive=True, + help="The password of the PKCS#12 file") +@@ -73,8 +82,9 @@ class ServerCertInstall(admintool.AdminTool): + + installutils.check_server_configuration() + +- if not self.options.dirsrv and not self.options.http: +- self.option_parser.error("you must specify dirsrv and/or http") ++ if not any((self.options.dirsrv, self.options.http, self.options.kdc)): ++ self.option_parser.error( ++ "you must specify dirsrv, http and/or kdc") + + if not self.args: + self.option_parser.error("you must provide certificate filename") +@@ -108,6 +118,9 @@ class ServerCertInstall(admintool.AdminTool): + if self.options.http: + self.install_http_cert() + ++ if self.options.kdc: ++ self.install_kdc_cert() ++ + api.Backend.ldap2.disconnect() + + def install_dirsrv_cert(self): +@@ -161,6 +174,55 @@ class ServerCertInstall(admintool.AdminTool): + os.chown(os.path.join(dirname, 'key3.db'), 0, pent.pw_gid) + os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid) + ++ def install_kdc_cert(self): ++ ca_cert_file = paths.CA_BUNDLE_PEM ++ pkcs12_file, pin, ca_cert = installutils.load_pkcs12( ++ cert_files=self.args, ++ key_password=self.options.pin, ++ key_nickname=self.options.cert_name, ++ ca_cert_files=[ca_cert_file], ++ realm_name=api.env.realm) ++ ++ cdb = certs.CertDB(api.env.realm, nssdir=paths.IPA_NSSDB_DIR) ++ ++ # Check that the ca_cert is known and trusted ++ with tempfile.NamedTemporaryFile() as temp: ++ certs.install_pem_from_p12(pkcs12_file.name, pin, temp.name) ++ ++ kdc_cert = x509.load_certificate_from_file(temp.name) ++ ca_certs = x509.load_certificate_list_from_file(ca_cert_file) ++ ++ try: ++ verify_kdc_cert_validity(kdc_cert, ca_certs, api.env.realm) ++ except ValueError as e: ++ raise admintool.ScriptError( ++ "Peer's certificate issuer is not trusted (%s). " ++ "Please run ipa-cacert-manage install and ipa-certupdate " ++ "to install the CA certificate." % str(e)) ++ ++ try: ++ ca_enabled = api.Command.ca_is_enabled()['result'] ++ if ca_enabled: ++ certmonger.stop_tracking(certfile=paths.KDC_CERT) ++ ++ certs.install_pem_from_p12(pkcs12_file.name, pin, paths.KDC_CERT) ++ certs.install_key_from_p12(pkcs12_file.name, pin, paths.KDC_KEY) ++ ++ if ca_enabled: ++ # Start tracking only if the cert was issued by IPA CA ++ # Retrieve IPA CA ++ ipa_ca_cert = cdb.get_cert_from_db( ++ get_ca_nickname(api.env.realm), ++ pem=False) ++ # And compare with the CA which signed this certificate ++ if ca_cert == ipa_ca_cert: ++ certmonger.start_tracking( ++ (paths.KDC_CERT, paths.KDC_KEY), ++ storage='FILE', ++ profile='KDCs_PKINIT_Certs') ++ except RuntimeError as e: ++ raise admintool.ScriptError(str(e)) ++ + def check_chain(self, pkcs12_filename, pkcs12_pin, nssdb): + # create a temp nssdb + with NSSDatabase() as tempnssdb: +-- +2.9.4 + diff --git a/SOURCES/0157-ipa-ca-install-append-CA-cert-chain-into-etc-ipa-ca..patch b/SOURCES/0157-ipa-ca-install-append-CA-cert-chain-into-etc-ipa-ca..patch new file mode 100644 index 0000000..d2ba8e1 --- /dev/null +++ b/SOURCES/0157-ipa-ca-install-append-CA-cert-chain-into-etc-ipa-ca..patch @@ -0,0 +1,41 @@ +From ceb0d5c2a4a8e8fae271e5a37ee32f58a2d36273 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 16 May 2017 17:24:09 +0200 +Subject: [PATCH] ipa-ca-install: append CA cert chain into /etc/ipa/ca.crt + +ipa-ca-install currently overwrites /etc/ipa/ca.crt with the CA chain +retrieved from Dogtag. It should instead append the new certs, otherwise +the CA that signed dirsrv and httpd certificates is removed and ipa tools +fail. +A consequence is that ipa-kra-install fails. +This is a regression introduced by 5ab85b36. + +https://pagure.io/freeipa/issue/6925 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/cainstance.py | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index b8c8cc4fc4532fc2c911ec174d363f8280ce863b..b0e9e8757ec3e3c0d03ed930743ef5a1253b864a 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -793,6 +793,14 @@ class CAInstance(DogtagInstance): + # Get list of PEM certificates + certlist = x509.pkcs7_to_pems(data, x509.DER) + ++ # We need to append the certs to the existing file, so start by ++ # reading the file ++ if ipautil.file_exists(paths.IPA_CA_CRT): ++ ca_certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT) ++ ca_certs = [cert.public_bytes(serialization.Encoding.PEM) ++ for cert in ca_certs] ++ certlist.extend(ca_certs) ++ + # We have all the certificates in certlist, write them to a PEM file + for path in [paths.IPA_CA_CRT, + paths.KDC_CA_BUNDLE_PEM, +-- +2.9.4 + diff --git a/SOURCES/0158-ca-cert-show-check-certificate_out-in-options.patch b/SOURCES/0158-ca-cert-show-check-certificate_out-in-options.patch new file mode 100644 index 0000000..e02a510 --- /dev/null +++ b/SOURCES/0158-ca-cert-show-check-certificate_out-in-options.patch @@ -0,0 +1,73 @@ +From c1258d68268bc93536ba66921d65a2550bdf475e Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Tue, 9 May 2017 17:45:20 +0200 +Subject: [PATCH] ca/cert-show: check certificate_out in options + +If --certificate-out was specified on the command line, it will appear +among the options. If it was empty, it will be None. + +This check was done properly in the ca plugin. Lets' just unify how this +is handled and improve user experience by announcing which option causes +the failure. + +https://pagure.io/freeipa/issue/6885 + +Reviewed-By: Fraser Tweedale +Reviewed-By: Jan Cholasta +--- + ipaclient/plugins/ca.py | 8 ++++++-- + ipaclient/plugins/cert.py | 12 +++++++++--- + 2 files changed, 15 insertions(+), 5 deletions(-) + +diff --git a/ipaclient/plugins/ca.py b/ipaclient/plugins/ca.py +index fcdf484635c7611d905f28629a380a0152c7bde1..fe9c55f4c07b4682de1ad882b6c5651dafece716 100644 +--- a/ipaclient/plugins/ca.py ++++ b/ipaclient/plugins/ca.py +@@ -4,7 +4,7 @@ + + import base64 + from ipaclient.frontend import MethodOverride +-from ipalib import util, x509, Str ++from ipalib import errors, util, x509, Str + from ipalib.plugable import Registry + from ipalib.text import _ + +@@ -26,7 +26,11 @@ class WithCertOutArgs(MethodOverride): + filename = None + if 'certificate_out' in options: + filename = options.pop('certificate_out') +- util.check_writable_file(filename) ++ try: ++ util.check_writable_file(filename) ++ except errors.FileError as e: ++ raise errors.ValidationError(name='certificate-out', ++ error=str(e)) + + result = super(WithCertOutArgs, self).forward(*keys, **options) + if filename: +diff --git a/ipaclient/plugins/cert.py b/ipaclient/plugins/cert.py +index 9ec6970b18d0cdc3863259faee3a697f63799c3f..93cd3cef1a14925bc0795b32e97e44d69897be5c 100644 +--- a/ipaclient/plugins/cert.py ++++ b/ipaclient/plugins/cert.py +@@ -50,9 +50,15 @@ class CertRetrieveOverride(MethodOverride): + ) + + def forward(self, *args, **options): +- certificate_out = options.pop('certificate_out', None) +- if certificate_out is not None: +- util.check_writable_file(certificate_out) ++ if 'certificate_out' in options: ++ certificate_out = options.pop('certificate_out') ++ try: ++ util.check_writable_file(certificate_out) ++ except errors.FileError as e: ++ raise errors.ValidationError(name='certificate-out', ++ error=str(e)) ++ else: ++ certificate_out = None + + result = super(CertRetrieveOverride, self).forward(*args, **options) + +-- +2.9.4 + diff --git a/SOURCES/0159-Fix-rare-race-condition-with-missing-ccache-file.patch b/SOURCES/0159-Fix-rare-race-condition-with-missing-ccache-file.patch new file mode 100644 index 0000000..b30af31 --- /dev/null +++ b/SOURCES/0159-Fix-rare-race-condition-with-missing-ccache-file.patch @@ -0,0 +1,47 @@ +From 341d5790afb01e9d99c73ba336103e38e2b30091 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Mon, 22 May 2017 10:56:41 -0400 +Subject: [PATCH] Fix rare race condition with missing ccache file + +In some circumstances the ccache file may disappear while +mod_auth_gssapi still has a valid cookie and the client is performing a +json server call. + +This may lead to credentials getting sourced from the keytab. +Make sure we enforce what GSS NAME we want to resolve so HTTP creds are +never mistakenly sourced. + +Ticket: #6972 + +Signed-off-by: Simo Sorce +Reviewed-By: Alexander Bokovoy +--- + ipaserver/rpcserver.py | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 4cde2815a0fe9332d67c84b531f573ff88b1a302..32f286148bbdf294f941116b4bdca85714a52837 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -777,8 +777,17 @@ class jsonserver_session(jsonserver, KerberosSession): + self.debug('no ccache, need login') + return self.need_login(start_response) + ++ # If we have a ccache, make sure we have a GSS_NAME and use ++ # it to resolve the ccache name (Issue: 6972 ) ++ principal = environ.get('GSS_NAME') ++ if principal is None: ++ self.debug('no GSS Name, need login') ++ return self.need_login(start_response) ++ gss_name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) ++ + # Redirect to login if Kerberos credentials are expired +- creds = get_credentials_if_valid(ccache_name=ccache_name) ++ creds = get_credentials_if_valid(name=gss_name, ++ ccache_name=ccache_name) + if not creds: + self.debug('ccache expired, deleting session, need login') + # The request is finished with the ccache, destroy it. +-- +2.9.4 + diff --git a/SOURCES/0160-Remove-pkinit-anonymous-command.patch b/SOURCES/0160-Remove-pkinit-anonymous-command.patch new file mode 100644 index 0000000..9bd28fe --- /dev/null +++ b/SOURCES/0160-Remove-pkinit-anonymous-command.patch @@ -0,0 +1,176 @@ +From 2eb94f86872ee7ea191dc3e44fcb3d5a4683ae67 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 10 May 2017 15:54:21 +0200 +Subject: [PATCH] Remove pkinit-anonymous command + +Ever since from v4.5, FreeIPA expects at least some kind of +anonymous PKINIT to work. The pkinit-anonymous command was supposed +to enable/disable anonymous pkinit by locking/unlocking the +anonymous principal. We can't allow this for FreeIPA to work +so we are removing the command as it was never supported anyway. + +https://pagure.io/freeipa/issue/6936 + +Reviewed-By: Martin Babinsky +--- + API.txt | 6 --- + VERSION.m4 | 4 +- + ipaserver/plugins/pkinit.py | 94 ++------------------------------------------- + 3 files changed, 6 insertions(+), 98 deletions(-) + +diff --git a/API.txt b/API.txt +index 7594157384511c1317738dafb41361676a2a0fd7..4e6754afe2deab5c963577f1e1363f1123a31a86 100644 +--- a/API.txt ++++ b/API.txt +@@ -3736,11 +3736,6 @@ command: ping/1 + args: 0,1,1 + option: Str('version?') + output: Output('summary', type=[, ]) +-command: pkinit_anonymous/1 +-args: 1,1,1 +-arg: Str('action') +-option: Str('version?') +-output: Output('result') + command: plugins/1 + args: 0,3,3 + option: Flag('all', autofill=True, cli_name='all', default=True) +@@ -6803,7 +6798,6 @@ default: permission_remove_member/1 + default: permission_show/1 + default: ping/1 + default: pkinit/1 +-default: pkinit_anonymous/1 + default: plugins/1 + default: privilege/1 + default: privilege_add/1 +diff --git a/VERSION.m4 b/VERSION.m4 +index 31e7c1d6e7054d3b4ef1d9dfaf349d2959f8330a..e10ee3cad6f5a6e023ea3cb9ec20591b7caae0bd 100644 +--- a/VERSION.m4 ++++ b/VERSION.m4 +@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000) + # # + ######################################################## + define(IPA_API_VERSION_MAJOR, 2) +-define(IPA_API_VERSION_MINOR, 224) +-# Last change: Add rename option to sudorule objects ++define(IPA_API_VERSION_MINOR, 226) ++# Last change: Remove the pkinit-anonymous command + + + ######################################################## +diff --git a/ipaserver/plugins/pkinit.py b/ipaserver/plugins/pkinit.py +index b6b3f38828e86e6e677fadc9c4a638b2eee5171f..e49b31091d676865fa7f023be8edc3cdef9d6d2c 100644 +--- a/ipaserver/plugins/pkinit.py ++++ b/ipaserver/plugins/pkinit.py +@@ -1,52 +1,14 @@ +-# Authors: +-# Simo Sorce + # +-# Copyright (C) 2010 Red Hat +-# see file 'COPYING' for use and warranty information ++# Copyright (C) 2017 FreeIPA Contributors see COPYING for license + # +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation, either version 3 of the License, or +-# (at your option) any later version. +-# +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program. If not, see . + +-from ipalib import api, errors +-from ipalib import Str +-from ipalib import Object, Command ++from ipalib import Object + from ipalib import _ + from ipalib.plugable import Registry +-from ipalib.constants import ANON_USER +-from ipapython.dn import DN +- +-__doc__ = _(""" +-Kerberos pkinit options +- +-Enable or disable anonymous pkinit using the principal +-WELLKNOWN/ANONYMOUS@REALM. The server must have been installed with +-pkinit support. +- +-EXAMPLES: +- +- Enable anonymous pkinit: +- ipa pkinit-anonymous enable +- +- Disable anonymous pkinit: +- ipa pkinit-anonymous disable +- +-For more information on anonymous pkinit see: +- +-http://k5wiki.kerberos.org/wiki/Projects/Anonymous_pkinit +-""") + + register = Registry() + ++ + @register() + class pkinit(Object): + """ +@@ -54,52 +16,4 @@ class pkinit(Object): + """ + object_name = _('pkinit') + +- label=_('PKINIT') +- +- +-def valid_arg(ugettext, action): +- """ +- Accepts only Enable/Disable. +- """ +- a = action.lower() +- if a != 'enable' and a != 'disable': +- raise errors.ValidationError( +- name='action', +- error=_('Unknown command %s') % action +- ) +- +-@register() +-class pkinit_anonymous(Command): +- __doc__ = _('Enable or Disable Anonymous PKINIT.') +- +- princ_name = '%s@%s' % (ANON_USER, api.env.realm) +- default_dn = DN(('krbprincipalname', princ_name), ('cn', api.env.realm), ('cn', 'kerberos'), api.env.basedn) +- +- takes_args = ( +- Str('action', valid_arg), +- ) +- +- def execute(self, action, **options): +- ldap = self.api.Backend.ldap2 +- set_lock = False +- lock = None +- +- entry_attrs = ldap.get_entry(self.default_dn, ['nsaccountlock']) +- +- if 'nsaccountlock' in entry_attrs: +- lock = entry_attrs['nsaccountlock'][0].lower() +- +- if action.lower() == 'enable': +- if lock == 'true': +- set_lock = True +- lock = None +- elif action.lower() == 'disable': +- if lock != 'true': +- set_lock = True +- lock = 'TRUE' +- +- if set_lock: +- entry_attrs['nsaccountlock'] = lock +- ldap.update_entry(entry_attrs) +- +- return dict(result=True) ++ label = _('PKINIT') +-- +2.9.4 + diff --git a/SOURCES/0161-krb5-make-sure-KDC-certificate-is-readable.patch b/SOURCES/0161-krb5-make-sure-KDC-certificate-is-readable.patch new file mode 100644 index 0000000..20e9312 --- /dev/null +++ b/SOURCES/0161-krb5-make-sure-KDC-certificate-is-readable.patch @@ -0,0 +1,108 @@ +From 131fbeff0397aa4e98bab8a22f0a1d366f223f05 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Mon, 22 May 2017 22:36:18 +0300 +Subject: [PATCH] krb5: make sure KDC certificate is readable + +When requesting certificate for KDC profile, make sure its public part +is actually readable to others. + +Fixes https://pagure.io/freeipa/issue/6973 + +Reviewed-By: Simo Sorce +Reviewed-By: Jan Cholasta +--- + install/restart_scripts/renew_kdc_cert | 4 ---- + ipalib/install/certmonger.py | 12 +++++++++--- + ipaserver/install/krbinstance.py | 3 ++- + 3 files changed, 11 insertions(+), 8 deletions(-) + +diff --git a/install/restart_scripts/renew_kdc_cert b/install/restart_scripts/renew_kdc_cert +index 9247920874fc9540ac3421dd59fd902cc195243f..14902893f0e61e31f798fa39737a6ed9d31de111 100755 +--- a/install/restart_scripts/renew_kdc_cert ++++ b/install/restart_scripts/renew_kdc_cert +@@ -3,19 +3,15 @@ + # Copyright (C) 2017 FreeIPA Contributors see COPYING for license + # + +-import os + import syslog + import traceback + + from ipaplatform import services +-from ipaplatform.paths import paths + from ipaserver.install import certs + + + def main(): + with certs.renewal_lock: +- os.chmod(paths.KDC_CERT, 0o644) +- + try: + if services.knownservices.krb5kdc.is_running(): + syslog.syslog(syslog.LOG_NOTICE, 'restarting krb5kdc') +diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py +index 5709853ffebdbf58929b9a935e906ae67341bea8..ad031a738f4397d230ed131bde6ac7ddb7ef6fdb 100644 +--- a/ipalib/install/certmonger.py ++++ b/ipalib/install/certmonger.py +@@ -302,7 +302,7 @@ def add_subject(request_id, subject): + def request_and_wait_for_cert( + certpath, subject, principal, nickname=None, passwd_fname=None, + dns=None, ca='IPA', profile=None, +- pre_command=None, post_command=None, storage='NSSDB'): ++ pre_command=None, post_command=None, storage='NSSDB', perms=None): + """ + Execute certmonger to request a server certificate. + +@@ -310,7 +310,7 @@ def request_and_wait_for_cert( + """ + reqId = request_cert(certpath, subject, principal, nickname, + passwd_fname, dns, ca, profile, +- pre_command, post_command, storage) ++ pre_command, post_command, storage, perms) + state = wait_for_request(reqId, api.env.startup_timeout) + ca_error = get_request_value(reqId, 'ca-error') + if state != 'MONITORING' or ca_error: +@@ -321,12 +321,14 @@ def request_and_wait_for_cert( + def request_cert( + certpath, subject, principal, nickname=None, passwd_fname=None, + dns=None, ca='IPA', profile=None, +- pre_command=None, post_command=None, storage='NSSDB'): ++ pre_command=None, post_command=None, storage='NSSDB', perms=None): + """ + Execute certmonger to request a server certificate. + + ``dns`` + A sequence of DNS names to appear in SAN request extension. ++ ``perms`` ++ A tuple of (cert, key) permissions in e.g., (0644,0660) + """ + if storage == 'FILE': + certfile, keyfile = certpath +@@ -367,6 +369,10 @@ def request_cert( + post_command = certmonger_cmd_template % (post_command) + request_parameters['cert-postsave-command'] = post_command + ++ if perms: ++ request_parameters['key-perms'] = perms[0] ++ request_parameters['cert-perms'] = perms[1] ++ + result = cm.obj_if.add_request(request_parameters) + try: + if result[0]: +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 1692e0b2badb23c18386346a552c83881018cf60..a1053d55ccaae17bef93547c036fb9d08d296f0b 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -432,7 +432,8 @@ class KrbInstance(service.Service): + dns=self.fqdn, + storage='FILE', + profile=KDC_PROFILE, +- post_command='renew_kdc_cert') ++ post_command='renew_kdc_cert', ++ perms=(0o644, 0o600)) + except dbus.DBusException as e: + # if the certificate is already tracked, ignore the error + name = e.get_dbus_name() +-- +2.9.4 + diff --git a/SOURCES/0162-Change-python-cryptography-to-python2-cryptography.patch b/SOURCES/0162-Change-python-cryptography-to-python2-cryptography.patch new file mode 100644 index 0000000..ac26a93 --- /dev/null +++ b/SOURCES/0162-Change-python-cryptography-to-python2-cryptography.patch @@ -0,0 +1,41 @@ +From a2747fd0b73818babe82a81c07a098124830b85d Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Fri, 19 May 2017 16:32:28 +0200 +Subject: [PATCH] Change python-cryptography to python2-cryptography + +Package name is python2-cryptography and even that it Provides +python-cryptography package, it causes problems during update of IPA +on RHEL - python2-cryptography is not updated. After changing required package +name to python2-cryptography upgrade on RHEL works well. + +Fixes: https://pagure.io/freeipa/issue/6749 +Reviewed-By: Martin Babinsky +--- + freeipa.spec.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 790e5838e0ba45ea9bbfe3bc3a1bd40c0bd3ac1a..2cbaa60df0db021a4a1ce10af383cd6a15e1e57c 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -164,7 +164,7 @@ BuildRequires: python3-wheel + %if 0%{?with_lint} + BuildRequires: samba-python + # 1.4: the version where Certificate.serial changed to .serial_number +-BuildRequires: python-cryptography >= 1.4 ++BuildRequires: python2-cryptography >= 1.4 + BuildRequires: python-gssapi >= 1.2.0 + BuildRequires: pylint >= 1.6 + # workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1096506 +@@ -645,7 +645,7 @@ Requires: keyutils + Requires: pyOpenSSL + Requires: python >= 2.7.9 + Requires: python-nss >= 0.16 +-Requires: python-cryptography >= 1.4 ++Requires: python2-cryptography >= 1.4 + Requires: python-netaddr + Requires: python-libipa_hbac + Requires: python-qrcode-core >= 5.0.0 +-- +2.9.4 + diff --git a/SOURCES/0163-Allow-for-multivalued-server-attributes.patch b/SOURCES/0163-Allow-for-multivalued-server-attributes.patch new file mode 100644 index 0000000..f6fc013 --- /dev/null +++ b/SOURCES/0163-Allow-for-multivalued-server-attributes.patch @@ -0,0 +1,270 @@ +From a3801d66f5cf3f08062c3aa67a7b33d13fae56b7 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 11 May 2017 15:55:53 +0200 +Subject: [PATCH] Allow for multivalued server attributes + +In order to achieve the task, the following changes were required: + +* vectorize the base class for server attributes +* add a child class that enforces single-value attributes. It still + accepts/returns single-value lists in order to not break Liskov + substitution principle +* Existing attributes inherit from the child class + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/serverroles.py | 4 +- + ipaserver/servroles.py | 109 +++++++++++++++++++--------- + ipatests/test_ipaserver/test_serverroles.py | 10 +-- + 3 files changed, 79 insertions(+), 44 deletions(-) + +diff --git a/ipaserver/plugins/serverroles.py b/ipaserver/plugins/serverroles.py +index e22eadd7b163469cc9fc4472640aa64d21c9d38f..e81635c3315cc3fca84450f43fb7df883aae57d9 100644 +--- a/ipaserver/plugins/serverroles.py ++++ b/ipaserver/plugins/serverroles.py +@@ -136,9 +136,7 @@ class serverroles(Backend): + + for name, attr in assoc_attributes.items(): + attr_value = attr.get(self.api) +- +- if attr_value is not None: +- result.update({name: attr_value}) ++ result.update({name: attr_value}) + + return result + +diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py +index cf4599995118c589a5b51236c68e13f14ac1257b..84fed1046b3b46dc9f5f8bbe6e03354725f1136c 100644 +--- a/ipaserver/servroles.py ++++ b/ipaserver/servroles.py +@@ -277,29 +277,33 @@ class ServerAttribute(LDAPBasedProperty): + try: + entries = ldap2.get_entries(search_base, filter=search_filter) + except errors.EmptyResult: +- return ++ return [] + +- master_cn = entries[0].dn[1]['cn'] ++ master_cns = {e.dn[1]['cn'] for e in entries} + + associated_role_providers = set( + self._get_assoc_role_providers(api_instance)) + +- if master_cn not in associated_role_providers: ++ if not master_cns.issubset(associated_role_providers): + raise errors.ValidationError( + name=self.name, + error=_("all masters must have %(role)s role enabled" % + {'role': self.associated_role.name}) + ) + +- return master_cn ++ return sorted(master_cns) + +- def _get_master_dn(self, api_instance, server): +- return DN(('cn', server), api_instance.env.container_masters, +- api_instance.env.basedn) ++ def _get_master_dns(self, api_instance, servers): ++ return [ ++ DN(('cn', server), api_instance.env.container_masters, ++ api_instance.env.basedn) for server in servers] ++ ++ def _get_masters_service_entries(self, ldap, master_dns): ++ service_dns = [ ++ DN(('cn', self.associated_service_name), master_dn) for master_dn ++ in master_dns] + +- def _get_masters_service_entry(self, ldap, master_dn): +- service_dn = DN(('cn', self.associated_service_name), master_dn) +- return ldap.get_entry(service_dn) ++ return [ldap.get_entry(service_dn) for service_dn in service_dns] + + def _add_attribute_to_svc_entry(self, ldap, service_entry): + """ +@@ -341,65 +345,98 @@ class ServerAttribute(LDAPBasedProperty): + r[u'server_server'] for r in self.associated_role.status( + api_instance) if r[u'status'] == ENABLED] + +- def _remove(self, api_instance, master): ++ def _remove(self, api_instance, masters): + """ +- remove attribute from the master ++ remove attribute from one or more masters + + :param api_instance: API instance +- :param master: master FQDN ++ :param master: list or iterable containing master FQDNs + """ + + ldap = api_instance.Backend.ldap2 + +- master_dn = self._get_master_dn(api_instance, master) +- service_entry = self._get_masters_service_entry(ldap, master_dn) +- self._remove_attribute_from_svc_entry(ldap, service_entry) ++ master_dns = self._get_master_dns(api_instance, masters) ++ service_entries = self._get_masters_service_entries(ldap, master_dns) ++ ++ for service_entry in service_entries: ++ self._remove_attribute_from_svc_entry(ldap, service_entry) + +- def _add(self, api_instance, master): ++ def _add(self, api_instance, masters): + """ + add attribute to the master + :param api_instance: API instance +- :param master: master FQDN ++ :param master: iterable containing master FQDNs + + :raises: * errors.ValidationError if the associated role is not enabled + on the master + """ + +- assoc_role_providers = self._get_assoc_role_providers(api_instance) ++ assoc_role_providers = set( ++ self._get_assoc_role_providers(api_instance)) ++ masters_set = set(masters) + ldap = api_instance.Backend.ldap2 + +- if master not in assoc_role_providers: ++ masters_without_role = masters_set - assoc_role_providers ++ ++ if masters_without_role: + raise errors.ValidationError( +- name=master, ++ name=', '.join(sorted(masters_without_role)), + error=_("must have %(role)s role enabled" % + {'role': self.associated_role.name}) + ) + +- master_dn = self._get_master_dn(api_instance, master) +- service_entry = self._get_masters_service_entry(ldap, master_dn) +- self._add_attribute_to_svc_entry(ldap, service_entry) ++ master_dns = self._get_master_dns(api_instance, masters) ++ service_entries = self._get_masters_service_entries(ldap, master_dns) ++ for service_entry in service_entries: ++ self._add_attribute_to_svc_entry(ldap, service_entry) + +- def set(self, api_instance, master): ++ def set(self, api_instance, masters): + """ +- set the attribute on master ++ set the attribute on masters + + :param api_instance: API instance +- :param master: FQDN of the new master ++ :param masters: an interable with FQDNs of the new masters + +- the attribute is automatically unset from previous master if present ++ the attribute is automatically unset from previous masters if present + + :raises: errors.EmptyModlist if the new masters is the same as +- the original on ++ the original ones + """ +- old_master = self.get(api_instance) ++ old_masters = self.get(api_instance) + +- if old_master == master: ++ if sorted(old_masters) == sorted(masters): + raise errors.EmptyModlist + +- self._add(api_instance, master) ++ if old_masters: ++ self._remove(api_instance, old_masters) ++ ++ self._add(api_instance, masters) ++ ++ ++class SingleValuedServerAttribute(ServerAttribute): ++ """ ++ Base class for server attributes that are forced to be single valued ++ ++ this means that `get` method will return a one-element list, and `set` ++ method will accept only one-element list ++ """ ++ ++ def set(self, api_instance, masters): ++ if len(masters) > 1: ++ raise errors.ValidationError( ++ name=self.attr_name, ++ error=_("must be enabled only on a single master")) ++ ++ super(SingleValuedServerAttribute, self).set(api_instance, masters) ++ ++ def get(self, api_instance): ++ masters = super(SingleValuedServerAttribute, self).get(api_instance) ++ num_masters = len(masters) ++ ++ if num_masters > 1: ++ raise errors.SingleMatchExpected(found=num_masters) + +- if old_master is not None: +- self._remove(api_instance, old_master) ++ return masters + + + _Service = namedtuple('Service', ['name', 'enabled']) +@@ -574,14 +611,14 @@ role_instances = ( + ) + + attribute_instances = ( +- ServerAttribute( ++ SingleValuedServerAttribute( + u"ca_renewal_master_server", + u"CA renewal master", + u"ca_server_server", + u"CA", + u"caRenewalMaster", + ), +- ServerAttribute( ++ SingleValuedServerAttribute( + u"dnssec_key_master_server", + u"DNSSec key master", + u"dns_server_server", +diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py +index d8844df3007f076532a34d625379ab552ef45363..e671272783d8d71c2ee56074459433b98b79dd0a 100644 +--- a/ipatests/test_ipaserver/test_serverroles.py ++++ b/ipatests/test_ipaserver/test_serverroles.py +@@ -706,7 +706,7 @@ class TestServerAttributes(object): + actual_attr_masters = self.config_retrieve( + assoc_role, mock_api)[attr_name] + +- assert actual_attr_masters == fqdn ++ assert actual_attr_masters == [fqdn] + + def test_set_attribute_on_the_same_provider_raises_emptymodlist( + self, mock_api, mock_masters): +@@ -727,7 +727,7 @@ class TestServerAttributes(object): + non_ca_fqdn = mock_masters.get_fqdn('trust-controller-dns') + + with pytest.raises(errors.ValidationError): +- self.config_update(mock_api, **{attr_name: non_ca_fqdn}) ++ self.config_update(mock_api, **{attr_name: [non_ca_fqdn]}) + + def test_set_unknown_attribute_on_master_raises_notfound( + self, mock_api, mock_masters): +@@ -735,7 +735,7 @@ class TestServerAttributes(object): + fqdn = mock_masters.get_fqdn('trust-controller-ca') + + with pytest.raises(errors.NotFound): +- self.config_update(mock_api, **{attr_name: fqdn}) ++ self.config_update(mock_api, **{attr_name: [fqdn]}) + + def test_set_ca_renewal_master_on_other_ca_and_back(self, mock_api, + mock_masters): +@@ -747,7 +747,7 @@ class TestServerAttributes(object): + other_ca_server = mock_masters.get_fqdn('trust-controller-ca') + + for host in (other_ca_server, original_renewal_master): +- self.config_update(mock_api, **{attr_name: host}) ++ self.config_update(mock_api, **{attr_name: [host]}) + + assert ( +- self.config_retrieve(role_name, mock_api)[attr_name] == host) ++ self.config_retrieve(role_name, mock_api)[attr_name] == [host]) +-- +2.9.4 + diff --git a/SOURCES/0164-Refactor-the-role-attribute-member-reporting-code.patch b/SOURCES/0164-Refactor-the-role-attribute-member-reporting-code.patch new file mode 100644 index 0000000..c234e58 --- /dev/null +++ b/SOURCES/0164-Refactor-the-role-attribute-member-reporting-code.patch @@ -0,0 +1,179 @@ +From 352b1bc2735e8571bd4bf3a46f599834c6b0aefa Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 16 May 2017 17:29:39 +0200 +Subject: [PATCH] Refactor the role/attribute member reporting code + +The `config` object now hosts a generic method for updating the config +entry for desired server role configuration (if not empty). The +duplicated code in dns/trust/vaultconfig commands was replaced by a call +to a common method. + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/config.py | 24 ++++++++++++++++-------- + ipaserver/plugins/dns.py | 16 ++++------------ + ipaserver/plugins/trust.py | 22 ++++------------------ + ipaserver/plugins/vault.py | 6 +++--- + 4 files changed, 27 insertions(+), 41 deletions(-) + +diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py +index b50e7a4691bd76bfaf7c332cd89b0f1bf55bac46..c88cb99b47ac746f8e18cf189708d457b535416a 100644 +--- a/ipaserver/plugins/config.py ++++ b/ipaserver/plugins/config.py +@@ -267,15 +267,21 @@ class config(LDAPObject): + def get_dn(self, *keys, **kwargs): + return DN(('cn', 'ipaconfig'), ('cn', 'etc'), api.env.basedn) + +- def show_servroles_attributes(self, entry_attrs, **options): ++ def update_entry_with_role_config(self, role_name, entry_attrs): ++ backend = self.api.Backend.serverroles ++ ++ role_config = backend.config_retrieve(role_name) ++ for key, value in role_config.items(): ++ if value: ++ entry_attrs.update({key: value}) ++ ++ ++ def show_servroles_attributes(self, entry_attrs, *roles, **options): + if options.get('raw', False): + return + +- backend = self.api.Backend.serverroles +- +- for role in ("CA server", "IPA master", "NTP server"): +- config = backend.config_retrieve(role) +- entry_attrs.update(config) ++ for role in roles: ++ self.update_entry_with_role_config(role, entry_attrs) + + def gather_trusted_domains(self): + """ +@@ -525,7 +531,8 @@ class config_mod(LDAPUpdate): + keys, options, exc, call_func, *call_args, **call_kwargs) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): +- self.obj.show_servroles_attributes(entry_attrs, **options) ++ self.obj.show_servroles_attributes( ++ entry_attrs, "CA server", "IPA master", "NTP server", **options) + return dn + + +@@ -534,5 +541,6 @@ class config_show(LDAPRetrieve): + __doc__ = _('Show the current configuration.') + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): +- self.obj.show_servroles_attributes(entry_attrs, **options) ++ self.obj.show_servroles_attributes( ++ entry_attrs, "CA server", "IPA master", "NTP server", **options) + return dn +diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py +index 47ac963a0ae26fcaa81e70a8143bd7d0c172d20e..f0e6c48f06313def57cdd6a4c7114357c9d8de8a 100644 +--- a/ipaserver/plugins/dns.py ++++ b/ipaserver/plugins/dns.py +@@ -4184,16 +4184,6 @@ class dnsconfig(LDAPObject): + if is_config_empty: + result['summary'] = unicode(_('Global DNS configuration is empty')) + +- def show_servroles_attributes(self, entry_attrs, **options): +- if options.get('raw', False): +- return +- +- backend = self.api.Backend.serverroles +- entry_attrs.update( +- backend.config_retrieve("DNS server") +- ) +- +- + @register() + class dnsconfig_mod(LDAPUpdate): + __doc__ = _('Modify global DNS configuration.') +@@ -4247,7 +4237,8 @@ class dnsconfig_mod(LDAPUpdate): + return result + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): +- self.obj.show_servroles_attributes(entry_attrs, **options) ++ self.api.Object.config.show_servroles_attributes( ++ entry_attrs, "DNS server", **options) + return dn + + +@@ -4261,7 +4252,8 @@ class dnsconfig_show(LDAPRetrieve): + return result + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): +- self.obj.show_servroles_attributes(entry_attrs, **options) ++ self.api.Object.config.show_servroles_attributes( ++ entry_attrs, "DNS server", **options) + return dn + + +diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py +index 0829f8c714f15c4384a89e18ba29e417405c249c..075b39dcc33a79f3e73e8e1e9e31ebbef17618fe 100644 +--- a/ipaserver/plugins/trust.py ++++ b/ipaserver/plugins/trust.py +@@ -1278,22 +1278,6 @@ class trustconfig(LDAPObject): + + entry_attrs['ipantfallbackprimarygroup'] = [groupdn[0][0].value] + +- def show_servroles(self, entry_attrs, **options): +- if options.get('raw', False): +- return +- +- backend = self.api.Backend.serverroles +- +- adtrust_agents = backend.config_retrieve( +- "AD trust agent" +- ) +- adtrust_controllers = backend.config_retrieve( +- "AD trust controller" +- ) +- +- entry_attrs.update(adtrust_agents) +- entry_attrs.update(adtrust_controllers) +- + + @register() + class trustconfig_mod(LDAPUpdate): +@@ -1314,7 +1298,8 @@ class trustconfig_mod(LDAPUpdate): + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + self.obj._convert_groupdn(entry_attrs, options) +- self.obj.show_servroles(entry_attrs, **options) ++ self.api.Object.config.show_servroles_attributes( ++ entry_attrs, "AD trust agent", "AD trust controller", **options) + return dn + + +@@ -1333,7 +1318,8 @@ class trustconfig_show(LDAPRetrieve): + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + self.obj._convert_groupdn(entry_attrs, options) +- self.obj.show_servroles(entry_attrs, **options) ++ self.api.Object.config.show_servroles_attributes( ++ entry_attrs, "AD trust agent", "AD trust controller", **options) + + return dn + +diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py +index d46aca821d2ec94a38dd7cc930f26038d5d80a90..d05a240c39bc1b47f1eba19cb893ab7408b35fa8 100644 +--- a/ipaserver/plugins/vault.py ++++ b/ipaserver/plugins/vault.py +@@ -997,9 +997,9 @@ class vaultconfig_show(Retrieve): + with self.api.Backend.kra.get_client() as kra_client: + transport_cert = kra_client.system_certs.get_transport_cert() + config = {'transport_cert': transport_cert.binary} +- config.update( +- self.api.Backend.serverroles.config_retrieve("KRA server") +- ) ++ ++ self.api.Object.config.show_servroles_attributes( ++ config, "KRA server", **options) + + return { + 'result': config, +-- +2.9.4 + diff --git a/SOURCES/0165-Add-an-attribute-reporting-client-PKINIT-capable-ser.patch b/SOURCES/0165-Add-an-attribute-reporting-client-PKINIT-capable-ser.patch new file mode 100644 index 0000000..4c9a12e --- /dev/null +++ b/SOURCES/0165-Add-an-attribute-reporting-client-PKINIT-capable-ser.patch @@ -0,0 +1,275 @@ +From 48f20550f175503cb226bac47c570a2ff3e79be1 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 12 May 2017 15:15:37 +0200 +Subject: [PATCH] Add an attribute reporting client PKINIT-capable servers + +A new multi-valued server attribute `pkinit_server` was added which +reports IPA masters that have PKINIT configuration usable by clients. + +The existing tests were modified to allow for testing the new attribute. + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + ipaserver/servroles.py | 7 ++ + ipatests/test_ipaserver/test_serverroles.py | 109 +++++++++++++--------------- + 2 files changed, 59 insertions(+), 57 deletions(-) + +diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py +index 84fed1046b3b46dc9f5f8bbe6e03354725f1136c..f6e79338b9187aa741fe45b9fae42476cc65f724 100644 +--- a/ipaserver/servroles.py ++++ b/ipaserver/servroles.py +@@ -625,4 +625,11 @@ attribute_instances = ( + u"DNSSEC", + u"dnssecKeyMaster", + ), ++ ServerAttribute( ++ u"pkinit_server_server", ++ u"PKINIT enabled server", ++ u"ipa_master_server", ++ u"KDC", ++ u"pkinitEnabled" ++ ) + ) +diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py +index e671272783d8d71c2ee56074459433b98b79dd0a..b373a4d32f60e5ef48bcf07ac29162516113e8a8 100644 +--- a/ipatests/test_ipaserver/test_serverroles.py ++++ b/ipatests/test_ipaserver/test_serverroles.py +@@ -58,7 +58,7 @@ _adtrust_agents = DN( + + + master_data = { +- 'ca-dns-dnssec-keymaster': { ++ 'ca-dns-dnssec-keymaster-pkinit-server': { + 'services': { + 'CA': { + 'enabled': True, +@@ -72,14 +72,19 @@ master_data = { + 'DNSSEC': { + 'enabled': True, + 'config': ['DNSSecKeyMaster'] ++ }, ++ 'KDC': { ++ 'enabled': True, ++ 'config': ['pkinitEnabled'] + } + }, + 'expected_roles': { + 'enabled': ['IPA master', 'CA server', 'DNS server'] + }, +- 'expected_attributes': {'DNS server': 'dnssec_key_master_server'} ++ 'expected_attributes': {'DNS server': 'dnssec_key_master_server', ++ 'IPA master': 'pkinit_server_server'} + }, +- 'ca-kra-renewal-master': { ++ 'ca-kra-renewal-master-pkinit-server': { + 'services': { + 'CA': { + 'enabled': True, +@@ -88,11 +93,16 @@ master_data = { + 'KRA': { + 'enabled': True, + }, ++ 'KDC': { ++ 'enabled': True, ++ 'config': ['pkinitEnabled'] ++ }, + }, + 'expected_roles': { + 'enabled': ['IPA master', 'CA server', 'KRA server'] + }, +- 'expected_attributes': {'CA server': 'ca_renewal_master_server'} ++ 'expected_attributes': {'CA server': 'ca_renewal_master_server', ++ 'IPA master': 'pkinit_server_server'} + }, + 'dns-trust-agent': { + 'services': { +@@ -234,7 +244,7 @@ class MockMasterTopology(object): + no_members=True, + raw=True)['result']} + +- self.existing_attributes = self._check_test_host_attributes() ++ self.original_dns_configs = self._remove_test_host_attrs() + + def iter_domain_data(self): + MasterData = namedtuple('MasterData', +@@ -287,7 +297,6 @@ class MockMasterTopology(object): + pass + + def _add_svc_entries(self, master_dn, svc_desc): +- self._add_ipamaster_services(master_dn) + for name in svc_desc: + svc_dn = self.get_service_dn(name, master_dn) + svc_mods = svc_desc[name] +@@ -298,6 +307,8 @@ class MockMasterTopology(object): + enabled=svc_mods['enabled'], + other_config=svc_mods.get('config', None))) + ++ self._add_ipamaster_services(master_dn) ++ + def _remove_svc_master_entries(self, master_dn): + try: + entries = self.ldap.connection.search_s( +@@ -317,7 +328,11 @@ class MockMasterTopology(object): + """ + for svc_name in self.ipamaster_services: + svc_dn = self.get_service_dn(svc_name, master_dn) +- self.ldap.add_entry(str(svc_dn), _make_service_entry_mods()) ++ try: ++ self.api.Backend.ldap2.get_entry(svc_dn) ++ except errors.NotFound: ++ self.ldap.add_entry( ++ str(svc_dn), _make_service_entry_mods()) + + def _add_members(self, dn, fqdn, member_attrs): + _entry, attrs = self.ldap.connection.search_s( +@@ -376,57 +391,36 @@ class MockMasterTopology(object): + except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE): + pass + +- def _check_test_host_attributes(self): +- existing_attributes = set() +- +- for service, value, attr_name in ( +- ('CA', 'caRenewalMaster', 'ca renewal master'), +- ('DNSSEC', 'DNSSecKeyMaster', 'dnssec key master')): ++ def _remove_test_host_attrs(self): ++ original_dns_configs = [] + +- svc_dn = DN(('cn', service), self.test_master_dn) ++ for attr_name in ( ++ 'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'): + try: +- svc_entry = self.api.Backend.ldap2.get_entry(svc_dn) ++ svc_entry = self.api.Backend.ldap2.find_entry_by_attr( ++ 'ipaConfigString', attr_name, 'ipaConfigObject', ++ base_dn=self.test_master_dn) + except errors.NotFound: + continue + else: +- config_string_val = svc_entry.get('ipaConfigString', []) ++ original_dns_configs.append( ++ (svc_entry.dn, list(svc_entry.get('ipaConfigString', []))) ++ ) ++ svc_entry[u'ipaConfigString'].remove(attr_name) ++ self.api.Backend.ldap2.update_entry(svc_entry) + +- if value in config_string_val: +- existing_attributes.add(attr_name) +- +- return existing_attributes +- +- def _remove_ca_renewal_master(self): +- if 'ca renewal master' not in self.existing_attributes: +- return ++ return original_dns_configs + +- ca_dn = DN(('cn', 'CA'), self.test_master_dn) +- ca_entry = self.api.Backend.ldap2.get_entry(ca_dn) +- +- config_string_val = ca_entry.get('ipaConfigString', []) +- try: +- config_string_val.remove('caRenewalMaster') +- except KeyError: +- return +- +- ca_entry.update({'ipaConfigString': config_string_val}) +- self.api.Backend.ldap2.update_entry(ca_entry) +- +- def _restore_ca_renewal_master(self): +- if 'ca renewal master' not in self.existing_attributes: +- return +- +- ca_dn = DN(('cn', 'CA'), self.test_master_dn) +- ca_entry = self.api.Backend.ldap2.get_entry(ca_dn) +- +- config_string_val = ca_entry.get('ipaConfigString', []) +- config_string_val.append('caRenewalMaster') +- +- ca_entry.update({'ipaConfigString': config_string_val}) +- self.api.Backend.ldap2.update_entry(ca_entry) ++ def _restore_test_host_attrs(self): ++ for dn, config in self.original_dns_configs: ++ try: ++ svc_entry = self.api.Backend.ldap2.get_entry(dn) ++ svc_entry['ipaConfigString'] = config ++ self.api.Backend.ldap2.update_entry(svc_entry) ++ except (errors.NotFound, errors.EmptyModlist): ++ continue + + def setup_data(self): +- self._remove_ca_renewal_master() + for master_data in self.iter_domain_data(): + # create host + self._add_host_entry(master_data.fqdn) +@@ -449,7 +443,6 @@ class MockMasterTopology(object): + ) + + def teardown_data(self): +- self._restore_ca_renewal_master() + for master_data in self.iter_domain_data(): + # first remove the master entries and service containers + self._remove_svc_master_entries(master_data.dn) +@@ -466,6 +459,8 @@ class MockMasterTopology(object): + # finally remove host entry + self._del_host_entry(master_data.fqdn) + ++ self._restore_test_host_attrs() ++ + + @pytest.fixture(scope='module') + def mock_api(request): +@@ -665,14 +660,14 @@ class TestServerRoleStatusRetrieval(object): + + def test_unknown_role_status_raises_notfound(self, mock_api, mock_masters): + unknown_role = 'IAP maestr' +- fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster') ++ fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster-pkinit-server') + with pytest.raises(errors.NotFound): + mock_api.Backend.serverroles.server_role_retrieve( + fqdn, unknown_role) + + def test_no_servrole_queries_all_roles_on_server(self, mock_api, + mock_masters): +- master_name = 'ca-dns-dnssec-keymaster' ++ master_name = 'ca-dns-dnssec-keymaster-pkinit-server' + enabled_roles = master_data[master_name]['expected_roles']['enabled'] + result = self.find_role(None, mock_api, mock_masters, + master=master_name) +@@ -688,7 +683,7 @@ class TestServerRoleStatusRetrieval(object): + invalid_substr = 'fwfgbb' + + assert (not self.find_role(invalid_substr, mock_api, mock_masters, +- 'ca-dns-dnssec-keymaster')) ++ 'ca-dns-dnssec-keymaster-pkinit-server')) + + + class TestServerAttributes(object): +@@ -706,7 +701,7 @@ class TestServerAttributes(object): + actual_attr_masters = self.config_retrieve( + assoc_role, mock_api)[attr_name] + +- assert actual_attr_masters == [fqdn] ++ assert fqdn in actual_attr_masters + + def test_set_attribute_on_the_same_provider_raises_emptymodlist( + self, mock_api, mock_masters): +@@ -744,10 +739,10 @@ class TestServerAttributes(object): + original_renewal_master = self.config_retrieve( + role_name, mock_api)[attr_name] + +- other_ca_server = mock_masters.get_fqdn('trust-controller-ca') ++ other_ca_server = [mock_masters.get_fqdn('trust-controller-ca')] + + for host in (other_ca_server, original_renewal_master): +- self.config_update(mock_api, **{attr_name: [host]}) ++ self.config_update(mock_api, **{attr_name: host}) + + assert ( +- self.config_retrieve(role_name, mock_api)[attr_name] == [host]) ++ self.config_retrieve(role_name, mock_api)[attr_name] == host) +-- +2.9.4 + diff --git a/SOURCES/0166-Add-the-list-of-PKINIT-servers-as-a-virtual-attribut.patch b/SOURCES/0166-Add-the-list-of-PKINIT-servers-as-a-virtual-attribut.patch new file mode 100644 index 0000000..486d553 --- /dev/null +++ b/SOURCES/0166-Add-the-list-of-PKINIT-servers-as-a-virtual-attribut.patch @@ -0,0 +1,34 @@ +From 095e8387572432749cf4231f7af67b9d5b597440 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 12 May 2017 15:27:36 +0200 +Subject: [PATCH] Add the list of PKINIT servers as a virtual attribute to + global config + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + ipaserver/plugins/config.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py +index c88cb99b47ac746f8e18cf189708d457b535416a..df6bd466afa937be96722a570385fb5b3419830d 100644 +--- a/ipaserver/plugins/config.py ++++ b/ipaserver/plugins/config.py +@@ -256,6 +256,12 @@ class config(LDAPObject): + flags={'virtual_attribute', 'no_create'} + ), + Str( ++ 'pkinit_server_server*', ++ label=_('IPA master capable of PKINIT'), ++ doc=_('IPA master which can process PKINIT requests'), ++ flags={'virtual_attribute', 'no_create', 'no_update'} ++ ), ++ Str( + 'ipadomainresolutionorder?', + cli_name='domain_resolution_order', + label=_('Domain resolution order'), +-- +2.9.4 + diff --git a/SOURCES/0167-Add-pkinit-status-command.patch b/SOURCES/0167-Add-pkinit-status-command.patch new file mode 100644 index 0000000..c40c1ef --- /dev/null +++ b/SOURCES/0167-Add-pkinit-status-command.patch @@ -0,0 +1,201 @@ +From 5012843d350b7a39b78e4eb7cab6cff98cae59d5 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 12 May 2017 17:25:30 +0200 +Subject: [PATCH] Add `pkinit-status` command + +This command is a more streamlined reporting tool for PKINIT feature +status in the FreeIPA topology. It prints out whether PKINIT is enabled +or disabled on individual masters in a topology. If a`--server` is +specified, it reports status for an individual server. If `--status` is +specified, it searches for all servers that have PKINIT enabled or +disabled. + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + API.txt | 15 ++++++ + VERSION.m4 | 4 +- + ipaserver/plugins/pkinit.py | 109 +++++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 125 insertions(+), 3 deletions(-) + +diff --git a/API.txt b/API.txt +index 4e6754afe2deab5c963577f1e1363f1123a31a86..6511ad8d1cb4dc9079628fc058312f31aaec624d 100644 +--- a/API.txt ++++ b/API.txt +@@ -3736,6 +3736,20 @@ command: ping/1 + args: 0,1,1 + option: Str('version?') + output: Output('summary', type=[, ]) ++command: pkinit_status/1 ++args: 1,7,4 ++arg: Str('criteria?') ++option: Flag('all', autofill=True, cli_name='all', default=False) ++option: Flag('raw', autofill=True, cli_name='raw', default=False) ++option: Str('server_server?', autofill=False, cli_name='server') ++option: Int('sizelimit?', autofill=False) ++option: StrEnum('status?', autofill=False, cli_name='status', values=[u'enabled', u'disabled']) ++option: Int('timelimit?', autofill=False) ++option: Str('version?') ++output: Output('count', type=[]) ++output: ListOfEntries('result') ++output: Output('summary', type=[, ]) ++output: Output('truncated', type=[]) + command: plugins/1 + args: 0,3,3 + option: Flag('all', autofill=True, cli_name='all', default=True) +@@ -6798,6 +6812,7 @@ default: permission_remove_member/1 + default: permission_show/1 + default: ping/1 + default: pkinit/1 ++default: pkinit_status/1 + default: plugins/1 + default: privilege/1 + default: privilege_add/1 +diff --git a/VERSION.m4 b/VERSION.m4 +index e10ee3cad6f5a6e023ea3cb9ec20591b7caae0bd..8aa3ef03f352cd176579c5d5848ed9550f22105d 100644 +--- a/VERSION.m4 ++++ b/VERSION.m4 +@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000) + # # + ######################################################## + define(IPA_API_VERSION_MAJOR, 2) +-define(IPA_API_VERSION_MINOR, 226) +-# Last change: Remove the pkinit-anonymous command ++define(IPA_API_VERSION_MINOR, 227) ++# Last change: Add `pkinit-status` command + + + ######################################################## +diff --git a/ipaserver/plugins/pkinit.py b/ipaserver/plugins/pkinit.py +index e49b31091d676865fa7f023be8edc3cdef9d6d2c..970f955c54bc489765d2565255e8805138a35307 100644 +--- a/ipaserver/plugins/pkinit.py ++++ b/ipaserver/plugins/pkinit.py +@@ -3,11 +3,33 @@ + # + + from ipalib import Object +-from ipalib import _ ++from ipalib import _, ngettext ++from ipalib.crud import Search ++from ipalib.parameters import Int, Str, StrEnum + from ipalib.plugable import Registry + + register = Registry() + ++__doc__ = _(""" ++Kerberos PKINIT feature status reporting tools. ++ ++Report IPA masters on which Kerberos PKINIT is enabled or disabled ++ ++EXAMPLES: ++ List PKINIT status on all masters: ++ ipa pkinit-status ++ ++ Check PKINIT status on `ipa.example.com`: ++ ipa pkinit-status --server ipa.example.com ++ ++ List all IPA masters with disabled PKINIT: ++ ipa pkinit-status --status='disabled' ++ ++For more info about PKINIT support see: ++ ++https://www.freeipa.org/page/V4/Kerberos_PKINIT ++""") ++ + + @register() + class pkinit(Object): +@@ -17,3 +39,88 @@ class pkinit(Object): + object_name = _('pkinit') + + label = _('PKINIT') ++ ++ takes_params = ( ++ Str( ++ 'server_server?', ++ cli_name='server', ++ label=_('Server name'), ++ doc=_('IPA server hostname'), ++ ), ++ StrEnum( ++ 'status?', ++ cli_name='status', ++ label=_('PKINIT status'), ++ doc=_('Whether PKINIT is enabled or disabled'), ++ values=(u'enabled', u'disabled'), ++ flags={'virtual_attribute', 'no_create', 'no_update'} ++ ) ++ ) ++ ++ ++@register() ++class pkinit_status(Search): ++ __doc__ = _('Report PKINIT status on the IPA masters') ++ ++ msg_summary = ngettext('%(count)s server matched', ++ '%(count)s servers matched', 0) ++ ++ takes_options = Search.takes_options + ( ++ Int( ++ 'timelimit?', ++ label=_('Time Limit'), ++ doc=_('Time limit of search in seconds (0 is unlimited)'), ++ flags=['no_display'], ++ minvalue=0, ++ autofill=False, ++ ), ++ Int( ++ 'sizelimit?', ++ label=_('Size Limit'), ++ doc=_('Maximum number of entries returned (0 is unlimited)'), ++ flags=['no_display'], ++ minvalue=0, ++ autofill=False, ++ ), ++ ) ++ ++ def get_pkinit_status(self, server, status): ++ backend = self.api.Backend.serverroles ++ ipa_master_config = backend.config_retrieve("IPA master") ++ ++ if server is not None: ++ servers = [server] ++ else: ++ servers = ipa_master_config['ipa_master_server'] ++ ++ pkinit_servers = ipa_master_config['pkinit_server_server'] ++ ++ for s in servers: ++ pkinit_status = { ++ u'server_server': s, ++ u'status': ( ++ u'enabled' if s in pkinit_servers else u'disabled' ++ ) ++ } ++ if status is not None and pkinit_status[u'status'] != status: ++ continue ++ ++ yield pkinit_status ++ ++ def execute(self, *keys, **options): ++ if keys: ++ return dict( ++ result=[], ++ count=0, ++ truncated=False ++ ) ++ ++ server = options.get('server_server', None) ++ status = options.get('status', None) ++ ++ if server is not None: ++ self.api.Object.server_role.ensure_master_exists(server) ++ ++ result = sorted(self.get_pkinit_status(server, status)) ++ ++ return dict(result=result, count=len(result), truncated=False) +-- +2.9.4 + diff --git a/SOURCES/0168-test_serverroles-Get-rid-of-MockLDAP-and-use-ldap2-i.patch b/SOURCES/0168-test_serverroles-Get-rid-of-MockLDAP-and-use-ldap2-i.patch new file mode 100644 index 0000000..5347e9a --- /dev/null +++ b/SOURCES/0168-test_serverroles-Get-rid-of-MockLDAP-and-use-ldap2-i.patch @@ -0,0 +1,238 @@ +From e0f1082c7664235a298bbb1d574549917a00e8a0 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 18 May 2017 16:20:13 +0200 +Subject: [PATCH] test_serverroles: Get rid of MockLDAP and use ldap2 instead + +The test fixture haphazardly intermixed MockLDAP and ldap2 calls in +setup and teardown code, greatly hampering extension of the code and +also porting efforts to Python 3. Get rid of MockLDAP and use ldap2 for +all LDAP operations. + +https://pagure.io/freeipa/issue/6937 + +Reviewed-By: Jan Cholasta +Reviewed-By: Stanislav Laznicka +--- + ipatests/test_ipaserver/test_serverroles.py | 109 +++++++++++++--------------- + 1 file changed, 51 insertions(+), 58 deletions(-) + +diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py +index b373a4d32f60e5ef48bcf07ac29162516113e8a8..985c750b64f109e0a83686f31ddb3b8d4171072d 100644 +--- a/ipatests/test_ipaserver/test_serverroles.py ++++ b/ipatests/test_ipaserver/test_serverroles.py +@@ -14,40 +14,39 @@ import pytest + from ipaplatform.paths import paths + from ipalib import api, create_api, errors + from ipapython.dn import DN +-from ipatests.util import MockLDAP + + +-def _make_service_entry_mods(enabled=True, other_config=None): ++def _make_service_entry(ldap_backend, dn, enabled=True, other_config=None): + mods = { +- b'objectClass': [b'top', b'nsContainer', b'ipaConfigObject'], ++ 'objectClass': ['top', 'nsContainer', 'ipaConfigObject'], + } + if enabled: +- mods.update({b'ipaConfigString': [b'enabledService']}) ++ mods.update({'ipaConfigString': ['enabledService']}) + + if other_config is not None: +- mods.setdefault(b'ipaConfigString', []) +- mods[b'ipaConfigString'].extend(other_config) ++ mods.setdefault('ipaConfigString', []) ++ mods['ipaConfigString'].extend(other_config) + +- return mods ++ return ldap_backend.make_entry(dn, **mods) + + +-def _make_master_entry_mods(ca=False): ++def _make_master_entry(ldap_backend, dn, ca=False): + mods = { +- b'objectClass': [ +- b'top', +- b'nsContainer', +- b'ipaReplTopoManagedServer', +- b'ipaSupportedDomainLevelConfig', +- b'ipaConfigObject', ++ 'objectClass': [ ++ 'top', ++ 'nsContainer', ++ 'ipaReplTopoManagedServer', ++ 'ipaSupportedDomainLevelConfig', ++ 'ipaConfigObject', + ], +- b'ipaMaxDomainLevel': [b'1'], +- b'ipaMinDomainLevel': [b'0'], +- b'ipaReplTopoManagedsuffix': [str(api.env.basedn)] ++ 'ipaMaxDomainLevel': ['1'], ++ 'ipaMinDomainLevel': ['0'], ++ 'ipaReplTopoManagedsuffix': [str(api.env.basedn)] + } + if ca: +- mods[b'ipaReplTopoManagedsuffix'].append(b'o=ipaca') ++ mods['ipaReplTopoManagedsuffix'].append('o=ipaca') + +- return mods ++ return ldap_backend.make_entry(dn, **mods) + + _adtrust_agents = DN( + ('cn', 'adtrust agents'), +@@ -235,7 +234,7 @@ class MockMasterTopology(object): + ('cn', self.api.env.host), self.api.env.container_masters, + self.api.env.basedn) + +- self.ldap = MockLDAP() ++ self.ldap = self.api.Backend.ldap2 + + self.existing_masters = { + m['cn'][0] for m in self.api.Command.server_find( +@@ -302,8 +301,9 @@ class MockMasterTopology(object): + svc_mods = svc_desc[name] + + self.ldap.add_entry( +- str(svc_dn), +- _make_service_entry_mods( ++ _make_service_entry( ++ self.ldap, ++ svc_dn, + enabled=svc_mods['enabled'], + other_config=svc_mods.get('config', None))) + +@@ -311,16 +311,16 @@ class MockMasterTopology(object): + + def _remove_svc_master_entries(self, master_dn): + try: +- entries = self.ldap.connection.search_s( +- str(master_dn), ldap.SCOPE_SUBTREE ++ entries = self.ldap.get_entries( ++ master_dn, ldap.SCOPE_SUBTREE + ) +- except ldap.NO_SUCH_OBJECT: ++ except errors.NotFound: + return + + if entries: +- entries.sort(key=lambda x: len(x[0]), reverse=True) +- for entry_dn, _attrs in entries: +- self.ldap.del_entry(str(entry_dn)) ++ entries.sort(key=lambda x: len(x.dn), reverse=True) ++ for entry in entries: ++ self.ldap.delete_entry(entry) + + def _add_ipamaster_services(self, master_dn): + """ +@@ -329,19 +329,14 @@ class MockMasterTopology(object): + for svc_name in self.ipamaster_services: + svc_dn = self.get_service_dn(svc_name, master_dn) + try: +- self.api.Backend.ldap2.get_entry(svc_dn) ++ self.ldap.get_entry(svc_dn) + except errors.NotFound: +- self.ldap.add_entry( +- str(svc_dn), _make_service_entry_mods()) ++ self.ldap.add_entry(_make_service_entry(self.ldap, svc_dn)) + + def _add_members(self, dn, fqdn, member_attrs): +- _entry, attrs = self.ldap.connection.search_s( +- str(dn), ldap.SCOPE_SUBTREE)[0] +- mods = [] +- value = attrs.get('member', []) +- mod_op = ldap.MOD_REPLACE +- if not value: +- mod_op = ldap.MOD_ADD ++ entry_attrs = self.ldap.get_entry(dn) ++ ++ value = entry_attrs.get('member', []) + + for a in member_attrs: + +@@ -352,20 +347,18 @@ class MockMasterTopology(object): + result = self._add_service_entry(a, fqdn)['result'] + value.append(str(result['dn'])) + +- mods.append( +- (mod_op, 'member', value) +- ) +- +- self.ldap.connection.modify_s(str(dn), mods) ++ entry_attrs['member'] = value ++ self.ldap.update_entry(entry_attrs) + + def _remove_members(self, dn, fqdn, member_attrs): +- _entry, attrs = self.ldap.connection.search_s( +- str(dn), ldap.SCOPE_SUBTREE)[0] +- mods = [] ++ entry_attrs = self.ldap.get_entry(dn) ++ ++ value = set(entry_attrs.get('member', [])) ++ ++ if not value: ++ return ++ + for a in member_attrs: +- value = set(attrs.get('member', [])) +- if not value: +- continue + + if a == 'host': + try: +@@ -382,13 +375,11 @@ class MockMasterTopology(object): + pass + self._del_service_entry(a, fqdn) + +- mods.append( +- (ldap.MOD_REPLACE, 'member', list(value)) +- ) ++ entry_attrs['member'] = list(value) + + try: +- self.ldap.connection.modify_s(str(dn), mods) +- except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE): ++ self.ldap.update_entry(entry_attrs) ++ except (errors.NotFound, errors.EmptyModlist): + pass + + def _remove_test_host_attrs(self): +@@ -397,7 +388,7 @@ class MockMasterTopology(object): + for attr_name in ( + 'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'): + try: +- svc_entry = self.api.Backend.ldap2.find_entry_by_attr( ++ svc_entry = self.ldap.find_entry_by_attr( + 'ipaConfigString', attr_name, 'ipaConfigObject', + base_dn=self.test_master_dn) + except errors.NotFound: +@@ -407,7 +398,7 @@ class MockMasterTopology(object): + (svc_entry.dn, list(svc_entry.get('ipaConfigString', []))) + ) + svc_entry[u'ipaConfigString'].remove(attr_name) +- self.api.Backend.ldap2.update_entry(svc_entry) ++ self.ldap.update_entry(svc_entry) + + return original_dns_configs + +@@ -416,7 +407,7 @@ class MockMasterTopology(object): + try: + svc_entry = self.api.Backend.ldap2.get_entry(dn) + svc_entry['ipaConfigString'] = config +- self.api.Backend.ldap2.update_entry(svc_entry) ++ self.ldap.update_entry(svc_entry) + except (errors.NotFound, errors.EmptyModlist): + continue + +@@ -427,7 +418,9 @@ class MockMasterTopology(object): + + # create master + self.ldap.add_entry( +- str(master_data.dn), _make_master_entry_mods( ++ _make_master_entry( ++ self.ldap, ++ master_data.dn, + ca='CA' in master_data.services)) + + # now add service entries +-- +2.9.4 + diff --git a/SOURCES/0169-only-stop-disable-simple-service-if-it-is-installed.patch b/SOURCES/0169-only-stop-disable-simple-service-if-it-is-installed.patch new file mode 100644 index 0000000..46467f9 --- /dev/null +++ b/SOURCES/0169-only-stop-disable-simple-service-if-it-is-installed.patch @@ -0,0 +1,54 @@ +From 2a20fbe381dd8a740e05833dd8d2b5440ce84812 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 23 May 2017 16:35:01 +0200 +Subject: [PATCH] only stop/disable simple service if it is installed + +The SimpleServiceInstance uninstaller assument that the service to +uninstall was always present on the system. This may not be valid in +some cases (e.g. containerized deployments) and thus we need to change +the service state only when we know that the unit file exists. + +https://pagure.io/freeipa/issue/6977 + +Reviewed-By: Martin Basti +--- + ipaserver/install/service.py | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 1aa49ed25b25366afd2bb17073b4b214c231d54b..0523e914aa7debf6aaa82ddcce9b7b26c1833cd3 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -674,18 +674,21 @@ class SimpleServiceInstance(Service): + else: + self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix) + ++ def is_installed(self): ++ return self.service.is_installed() ++ + def uninstall(self): + if self.is_configured(): + self.print_msg("Unconfiguring %s" % self.service_name) + +- self.stop() +- self.disable() +- + running = self.restore_state("running") + enabled = self.restore_state("enabled") + +- # restore the original state of service +- if running: +- self.start() +- if enabled: +- self.enable() ++ if self.is_installed(): ++ self.stop() ++ self.disable() ++ ++ if running: ++ self.start() ++ if enabled: ++ self.enable() +-- +2.9.4 + diff --git a/SOURCES/0170-Fix-index-definition-for-ipaAnchorUUID.patch b/SOURCES/0170-Fix-index-definition-for-ipaAnchorUUID.patch new file mode 100644 index 0000000..33d73e3 --- /dev/null +++ b/SOURCES/0170-Fix-index-definition-for-ipaAnchorUUID.patch @@ -0,0 +1,33 @@ +From de0ed1b49c84edebb65f31ff18e0b4bb4e0d794c Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 24 May 2017 18:00:14 +0300 +Subject: [PATCH] Fix index definition for ipaAnchorUUID + +Fixes https://pagure.io/freeipa/issue/6975 + +Reviewed-By: Martin Basti +--- + install/updates/20-idoverride_index.update | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/install/updates/20-idoverride_index.update b/install/updates/20-idoverride_index.update +index bfc9c6e235fb7a4300fd755e53fc2154e01573a2..63d622f1f0fb7d6e49df7fde65a33bc21ab0c4a0 100644 +--- a/install/updates/20-idoverride_index.update ++++ b/install/updates/20-idoverride_index.update +@@ -11,9 +11,12 @@ only: nsIndexType: eq + only: nsIndexType: pres + + dn: cn=ipaAnchorUUID,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config +-default:cn: ipaOriginalUid ++default:cn: ipaAnchorUUID + default:ObjectClass: top + default:ObjectClass: nsIndex + default:nsSystemIndex: false + only: nsIndexType: eq + only: nsIndexType: pres ++ ++dn: cn=ipaAnchorUUID,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config ++remove:cn: ipaOriginalUid +-- +2.9.4 + diff --git a/SOURCES/0171-httpinstance-wait-until-the-service-entry-is-replica.patch b/SOURCES/0171-httpinstance-wait-until-the-service-entry-is-replica.patch new file mode 100644 index 0000000..068834e --- /dev/null +++ b/SOURCES/0171-httpinstance-wait-until-the-service-entry-is-replica.patch @@ -0,0 +1,126 @@ +From 07469b2cc7bd1478836a1c755b301dbf9234d61a Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 22 May 2017 08:15:14 +0000 +Subject: [PATCH] httpinstance: wait until the service entry is replicated + +Wait until the local HTTP service entry is replicated to the remote master +before requesting the server certificate. + +This prevents a replication conflict between the service entry added +locally and service entry added remotely when requesting the certificate. + +https://pagure.io/freeipa/issue/6867 + +Reviewed-By: Martin Babinsky +Reviewed-By: Martin Basti +--- + ipaserver/install/httpinstance.py | 29 +++++++++++++++++++++++++++-- + ipaserver/install/server/install.py | 4 ++-- + ipaserver/install/server/replicainstall.py | 5 +++-- + 3 files changed, 32 insertions(+), 6 deletions(-) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index c76a1a4e484c5777ced92761916c1c586e8b2d5d..12fdddccc26b0c1132bcdca7fe2249a85997892e 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -32,9 +32,11 @@ import six + from augeas import Augeas + + from ipalib.install import certmonger ++from ipapython import ipaldap + from ipapython.certdb import (IPA_CA_TRUST_FLAGS, + EXTERNAL_CA_TRUST_FLAGS, + TRUSTED_PEER_TRUST_FLAGS) ++from ipaserver.install import replication + from ipaserver.install import service + from ipaserver.install import certs + from ipaserver.install import installutils +@@ -127,12 +129,15 @@ class HTTPInstance(service.Service): + + subject_base = ipautil.dn_attribute_property('_subject_base') + +- def create_instance(self, realm, fqdn, domain_name, pkcs12_info=None, ++ def create_instance(self, realm, fqdn, domain_name, dm_password=None, ++ pkcs12_info=None, + subject_base=None, auto_redirect=True, ca_file=None, +- ca_is_configured=None, promote=False): ++ ca_is_configured=None, promote=False, ++ master_fqdn=None): + self.fqdn = fqdn + self.realm = realm + self.domain = domain_name ++ self.dm_password = dm_password + self.suffix = ipautil.realm_to_suffix(self.realm) + self.pkcs12_info = pkcs12_info + self.dercert = None +@@ -148,6 +153,7 @@ class HTTPInstance(service.Service): + if ca_is_configured is not None: + self.ca_is_configured = ca_is_configured + self.promote = promote ++ self.master_fqdn = master_fqdn + + self.step("stopping httpd", self.__stop) + self.step("setting mod_nss port to 443", self.__set_mod_nss_port) +@@ -577,3 +583,22 @@ class HTTPInstance(service.Service): + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR) + db.track_server_cert(self.cert_nickname, self.principal, + db.passwd_fname, 'restart_httpd') ++ ++ def request_service_keytab(self): ++ super(HTTPInstance, self).request_service_keytab() ++ ++ if self.master_fqdn is not None: ++ service_dn = DN(('krbprincipalname', self.principal), ++ api.env.container_service, ++ self.suffix) ++ ++ ldap_uri = ipaldap.get_ldap_uri(self.master_fqdn) ++ with ipaldap.LDAPClient(ldap_uri, ++ start_tls=not self.promote, ++ cacert=paths.IPA_CA_CRT) as remote_ldap: ++ if self.promote: ++ remote_ldap.gssapi_bind() ++ else: ++ remote_ldap.simple_bind(ipaldap.DIRMAN_DN, ++ self.dm_password) ++ replication.wait_for_entry(remote_ldap, service_dn, timeout=60) +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 03380b8d0e9150224b014a1a174d7ea81ccdcf00..9dcf903f4582740f007c049fae3ec247ddf52aef 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -830,13 +830,13 @@ def install(installer): + http = httpinstance.HTTPInstance(fstore) + if options.http_cert_files: + http.create_instance( +- realm_name, host_name, domain_name, ++ realm_name, host_name, domain_name, dm_password, + pkcs12_info=http_pkcs12_info, subject_base=options.subject_base, + auto_redirect=not options.no_ui_redirect, + ca_is_configured=setup_ca) + else: + http.create_instance( +- realm_name, host_name, domain_name, ++ realm_name, host_name, domain_name, dm_password, + subject_base=options.subject_base, + auto_redirect=not options.no_ui_redirect, + ca_is_configured=setup_ca) +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index b30133ffa22d410452ae04624d49db209175bed9..20eaf98397101b49c751c325afc0591e0babcc18 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -163,9 +163,10 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file, + http = httpinstance.HTTPInstance() + http.create_instance( + config.realm_name, config.host_name, config.domain_name, +- pkcs12_info, auto_redirect=auto_redirect, ca_file=ca_file, ++ config.dirman_password, pkcs12_info, ++ auto_redirect=auto_redirect, ca_file=ca_file, + ca_is_configured=ca_is_configured, promote=promote, +- subject_base=config.subject_base) ++ subject_base=config.subject_base, master_fqdn=config.master_host_name) + + return http + +-- +2.9.4 + diff --git a/SOURCES/0172-kdc.key-should-not-be-visible-to-all.patch b/SOURCES/0172-kdc.key-should-not-be-visible-to-all.patch new file mode 100644 index 0000000..abb5411 --- /dev/null +++ b/SOURCES/0172-kdc.key-should-not-be-visible-to-all.patch @@ -0,0 +1,34 @@ +From f6cac267e99c6f47ca6b78568182a82d48a6bb4c Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 31 May 2017 14:14:34 +0200 +Subject: [PATCH] kdc.key should not be visible to all + +While the world certainly is interested in our privates, we +should not just go ahead and show it to them. + +https://pagure.io/freeipa/issue/6973 + +Reviewed-By: Martin Babinsky +Reviewed-By: Alexander Bokovoy +--- + ipalib/install/certmonger.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py +index ad031a738f4397d230ed131bde6ac7ddb7ef6fdb..c286996ee2318e241b4af190d1a01f42e28aa9f3 100644 +--- a/ipalib/install/certmonger.py ++++ b/ipalib/install/certmonger.py +@@ -370,8 +370,8 @@ def request_cert( + request_parameters['cert-postsave-command'] = post_command + + if perms: +- request_parameters['key-perms'] = perms[0] +- request_parameters['cert-perms'] = perms[1] ++ request_parameters['cert-perms'] = perms[0] ++ request_parameters['key-perms'] = perms[1] + + result = cm.obj_if.add_request(request_parameters) + try: +-- +2.9.4 + diff --git a/SOURCES/0173-ipa-kdb-reload-certificate-mapping-rules-periodicall.patch b/SOURCES/0173-ipa-kdb-reload-certificate-mapping-rules-periodicall.patch new file mode 100644 index 0000000..659dcea --- /dev/null +++ b/SOURCES/0173-ipa-kdb-reload-certificate-mapping-rules-periodicall.patch @@ -0,0 +1,221 @@ +From 3a946e38a911fdfb1575135c41128f41ab44324c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 26 May 2017 18:19:48 +0200 +Subject: [PATCH] ipa-kdb: reload certificate mapping rules periodically + +With this patch the certificate mapping rules are reloaded every 5 +minutes. + +Resolves https://pagure.io/freeipa/issue/6963 + +Reviewed-By: David Kupka +--- + daemons/ipa-kdb/ipa_kdb_certauth.c | 153 ++++++++++++++++++++----------------- + 1 file changed, 81 insertions(+), 72 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_certauth.c b/daemons/ipa-kdb/ipa_kdb_certauth.c +index a53a2ce4e7ceb06ec8de117cdbca2666fdb5a97a..dbe7a0443700784d2b8dbb1fb9196d6249e5522a 100644 +--- a/daemons/ipa-kdb/ipa_kdb_certauth.c ++++ b/daemons/ipa-kdb/ipa_kdb_certauth.c +@@ -58,6 +58,8 @@ + #define CERTMAP_FILTER "(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \ + "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" + ++#define DEFAULT_CERTMAP_LIFETIME 300 ++ + #ifndef discard_const + #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) + #endif +@@ -67,6 +69,7 @@ struct krb5_certauth_moddata_st { + char *local_domain; + struct sss_certmap_ctx *sss_certmap_ctx; + struct ipadb_context *ipactx; ++ time_t valid_until; + }; + + void ipa_certmap_debug(void *private, +@@ -133,95 +136,101 @@ static krb5_error_code ipa_get_init_data(krb5_context kcontext, + } + + if (ipactx->certauth_moddata == NULL) { +- ret = asprintf(&basedn, "cn=certmap,%s", ipactx->base); +- if (ret == -1) { +- return ENOMEM; +- } ++ ipactx->certauth_moddata = moddata_out; + +- kerr = ipadb_simple_search(ipactx,basedn, LDAP_SCOPE_SUBTREE, +- CERTMAP_FILTER, discard_const(certmap_attrs), +- &result); +- if (kerr != 0 && kerr != KRB5_KDB_NOENTRY) { +- goto done; ++ if (ipactx->realm != NULL) { ++ ipactx->certauth_moddata->local_domain = strdup(ipactx->realm); ++ if (ipactx->certauth_moddata->local_domain == NULL) { ++ free(ipactx->certauth_moddata); ++ ipactx->certauth_moddata = NULL; ++ ret = ENOMEM; ++ goto done; ++ } + } + +- ret = sss_certmap_init(NULL, ipa_certmap_debug, NULL, &ctx); ++ ipactx->certauth_moddata->ipactx = ipactx; ++ ++ } ++ ++ ret = asprintf(&basedn, "cn=certmap,%s", ipactx->base); ++ if (ret == -1) { ++ return ENOMEM; ++ } ++ ++ kerr = ipadb_simple_search(ipactx,basedn, LDAP_SCOPE_SUBTREE, ++ CERTMAP_FILTER, discard_const(certmap_attrs), ++ &result); ++ if (kerr != 0 && kerr != KRB5_KDB_NOENTRY) { ++ goto done; ++ } ++ ++ ret = sss_certmap_init(NULL, ipa_certmap_debug, NULL, &ctx); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ if (kerr == KRB5_KDB_NOENTRY) { ++ ret = sss_certmap_add_rule(ctx, SSS_CERTMAP_MIN_PRIO, ++ NULL, NULL, NULL); + if (ret != 0) { +- return ret; ++ goto done; + } +- +- if (kerr == KRB5_KDB_NOENTRY) { +- ret = sss_certmap_add_rule(ctx, SSS_CERTMAP_MIN_PRIO, +- NULL, NULL, NULL); +- if (ret != 0) { ++ } else { ++ lc = ipactx->lcontext; ++ ++ for (le = ldap_first_entry(lc, result); le; ++ le = ldap_next_entry(lc, le)) { ++ prio = SSS_CERTMAP_MIN_PRIO; ++ ret = ipadb_ldap_attr_to_uint32(lc, le, IPA_CERTMAP_PRIORITY, ++ &prio); ++ if (ret != 0 && ret != ENOENT) { + goto done; + } +- } else { +- lc = ipactx->lcontext; +- +- for (le = ldap_first_entry(lc, result); le; +- le = ldap_next_entry(lc, le)) { +- prio = SSS_CERTMAP_MIN_PRIO; +- ret = ipadb_ldap_attr_to_uint32(lc, le, IPA_CERTMAP_PRIORITY, +- &prio); +- if (ret != 0 && ret != ENOENT) { +- goto done; +- } +- +- free(map_rule); +- map_rule = NULL; +- ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MAPRULE, +- &map_rule); +- if (ret != 0 && ret != ENOENT) { +- goto done; +- } + +- free(match_rule); +- match_rule = NULL; +- ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MATCHRULE, +- &match_rule); +- if (ret != 0 && ret != ENOENT) { +- goto done; +- } ++ free(map_rule); ++ map_rule = NULL; ++ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MAPRULE, ++ &map_rule); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } + +- if (domains != NULL) { +- for (c = 0; domains[c] != NULL; c++) { +- free(domains[c]); +- } +- free(domains); +- domains = NULL; +- } +- ret = ipadb_ldap_attr_to_strlist(lc, le, IPA_ASSOCIATED_DOMAIN, +- &domains); +- if (ret != 0 && ret != ENOENT) { +- goto done; +- } ++ free(match_rule); ++ match_rule = NULL; ++ ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MATCHRULE, ++ &match_rule); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; ++ } + +- ret = sss_certmap_add_rule(ctx, prio, match_rule, map_rule, +- (const char **) domains); +- if (ret != 0) { +- goto done; ++ if (domains != NULL) { ++ for (c = 0; domains[c] != NULL; c++) { ++ free(domains[c]); + } ++ free(domains); ++ domains = NULL; ++ } ++ ret = ipadb_ldap_attr_to_strlist(lc, le, IPA_ASSOCIATED_DOMAIN, ++ &domains); ++ if (ret != 0 && ret != ENOENT) { ++ goto done; + } +- } +- +- ipactx->certauth_moddata = moddata_out; + +- if (ipactx->realm != NULL) { +- ipactx->certauth_moddata->local_domain = strdup(ipactx->realm); +- if (ipactx->certauth_moddata->local_domain == NULL) { +- free(ipactx->certauth_moddata); +- ipactx->certauth_moddata = NULL; +- ret = ENOMEM; ++ ret = sss_certmap_add_rule(ctx, prio, match_rule, map_rule, ++ (const char **) domains); ++ if (ret != 0) { + goto done; + } + } +- +- ipactx->certauth_moddata->sss_certmap_ctx = ctx; +- ipactx->certauth_moddata->ipactx = ipactx; +- + } + ++ sss_certmap_free_ctx(ipactx->certauth_moddata->sss_certmap_ctx); ++ ipactx->certauth_moddata->sss_certmap_ctx = ctx; ++ ipactx->certauth_moddata->valid_until = time(NULL) ++ + DEFAULT_CERTMAP_LIFETIME; ++ krb5_klog_syslog(LOG_DEBUG, ++ "Successfully updates certificate mapping rules."); ++ + ret = 0; + + done: +@@ -266,7 +275,7 @@ static krb5_error_code ipa_certauth_authorize(krb5_context context, + return KRB5_PLUGIN_NO_HANDLE; + } + +- if (moddata->sss_certmap_ctx == NULL) { ++ if (moddata->sss_certmap_ctx == NULL || time(NULL) > moddata->valid_until) { + kerr = ipa_get_init_data(context, moddata); + if (kerr != 0) { + krb5_klog_syslog(LOG_ERR, "Failed to init certmapping data"); +-- +2.9.4 + diff --git a/SOURCES/0174-Avoid-possible-endless-recursion-in-RPC-call.patch b/SOURCES/0174-Avoid-possible-endless-recursion-in-RPC-call.patch new file mode 100644 index 0000000..887f87a --- /dev/null +++ b/SOURCES/0174-Avoid-possible-endless-recursion-in-RPC-call.patch @@ -0,0 +1,127 @@ +From 0367e6dd66d47a78484e12e76119d9662356bf48 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Fri, 26 May 2017 08:37:36 +0200 +Subject: [PATCH] Avoid possible endless recursion in RPC call + +This commit removes recursion in RPCClient.forward() which may lack +end condition. + +https://pagure.io/freeipa/issue/6796 + +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/rpc.py | 95 +++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 54 insertions(+), 41 deletions(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index e23ca3d061645b2695a9e0deaa0b7d666f986e0e..297ed80414fae3d8b27558567425fec704f3e862 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -1088,50 +1088,63 @@ class RPCClient(Connectible): + :param kw: Keyword arguments to pass to remote command. + """ + server = getattr(context, 'request_url', None) +- self.log.info("Forwarding '%s' to %s server '%s'", +- name, self.protocol, server) + command = getattr(self.conn, name) + params = [args, kw] +- try: +- return self._call_command(command, params) +- except Fault as e: +- e = decode_fault(e) +- self.debug('Caught fault %d from server %s: %s', e.faultCode, +- server, e.faultString) +- if e.faultCode in errors_by_code: +- error = errors_by_code[e.faultCode] +- raise error(message=e.faultString) +- raise UnknownError( +- code=e.faultCode, +- error=e.faultString, +- server=server, +- ) +- except SSLError as e: +- raise NetworkError(uri=server, error=str(e)) +- except ProtocolError as e: +- # By catching a 401 here we can detect the case where we have +- # a single IPA server and the session is invalid. Otherwise +- # we always have to do a ping(). +- session_cookie = getattr(context, 'session_cookie', None) +- if session_cookie and e.errcode == 401: +- # Unauthorized. Remove the session and try again. +- delattr(context, 'session_cookie') +- try: +- principal = getattr(context, 'principal', None) +- delete_persistent_client_session_data(principal) +- except Exception as e: +- # This shouldn't happen if we have a session but it isn't fatal. +- pass + +- # Create a new serverproxy with the non-session URI +- serverproxy = self.create_connection(os.environ.get('KRB5CCNAME'), self.env.verbose, self.env.fallback, self.env.delegate) +- setattr(context, self.id, Connection(serverproxy, self.disconnect)) +- return self.forward(name, *args, **kw) +- raise NetworkError(uri=server, error=e.errmsg) +- except socket.error as e: +- raise NetworkError(uri=server, error=str(e)) +- except (OverflowError, TypeError) as e: +- raise XMLRPCMarshallError(error=str(e)) ++ # we'll be trying to connect multiple times with a new session cookie ++ # each time should we be getting UNAUTHORIZED error from the server ++ max_tries = 5 ++ for try_num in range(0, max_tries): ++ self.log.info("[try %d]: Forwarding '%s' to %s server '%s'", ++ try_num+1, name, self.protocol, server) ++ try: ++ return self._call_command(command, params) ++ except Fault as e: ++ e = decode_fault(e) ++ self.debug('Caught fault %d from server %s: %s', e.faultCode, ++ server, e.faultString) ++ if e.faultCode in errors_by_code: ++ error = errors_by_code[e.faultCode] ++ raise error(message=e.faultString) ++ raise UnknownError( ++ code=e.faultCode, ++ error=e.faultString, ++ server=server, ++ ) ++ except ProtocolError as e: ++ # By catching a 401 here we can detect the case where we have ++ # a single IPA server and the session is invalid. Otherwise ++ # we always have to do a ping(). ++ session_cookie = getattr(context, 'session_cookie', None) ++ if session_cookie and e.errcode == 401: ++ # Unauthorized. Remove the session and try again. ++ delattr(context, 'session_cookie') ++ try: ++ principal = getattr(context, 'principal', None) ++ delete_persistent_client_session_data(principal) ++ except Exception as e: ++ # This shouldn't happen if we have a session ++ # but it isn't fatal. ++ self.debug("Error trying to remove persisent session " ++ "data: {err}".format(err=e)) ++ ++ # Create a new serverproxy with the non-session URI ++ serverproxy = self.create_connection( ++ os.environ.get('KRB5CCNAME'), self.env.verbose, ++ self.env.fallback, self.env.delegate) ++ ++ setattr(context, self.id, ++ Connection(serverproxy, self.disconnect)) ++ # try to connect again with the new session cookie ++ continue ++ raise NetworkError(uri=server, error=e.errmsg) ++ except (SSLError, socket.error) as e: ++ raise NetworkError(uri=server, error=str(e)) ++ except (OverflowError, TypeError) as e: ++ raise XMLRPCMarshallError(error=str(e)) ++ raise NetworkError( ++ uri=server, ++ error=_("Exceeded number of tries to forward a request.")) + + + class xmlclient(RPCClient): +-- +2.9.4 + diff --git a/SOURCES/0175-rpc-preparations-for-recursion-fix.patch b/SOURCES/0175-rpc-preparations-for-recursion-fix.patch new file mode 100644 index 0000000..156557c --- /dev/null +++ b/SOURCES/0175-rpc-preparations-for-recursion-fix.patch @@ -0,0 +1,103 @@ +From ae8d12b2f764fa49bebf263ec646709900d90a6b Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Wed, 31 May 2017 15:45:19 +0200 +Subject: [PATCH] rpc: preparations for recursion fix + +Made several improvements to coding style: + - same use of KerberosError throughout the module + - removed some unused variables + - moved code from try-except blocks if it didn't have to be there + - preparations for putting most of RPCClient.create_connection() + to loop + +https://pagure.io/freeipa/issue/6796 + +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/rpc.py | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index 297ed80414fae3d8b27558567425fec704f3e862..b12ce4c5365299332587ad0d2990ca30070217bf 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -52,7 +52,7 @@ from six.moves import urllib + from ipalib.backend import Connectible + from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT + from ipalib.errors import (public_errors, UnknownError, NetworkError, +- KerberosError, XMLRPCMarshallError, JSONError) ++ XMLRPCMarshallError, JSONError) + from ipalib import errors, capabilities + from ipalib.request import context, Connection + from ipapython.ipa_log_manager import root_logger +@@ -653,7 +653,7 @@ class KerbTransport(SSLTransport): + except (TypeError, UnicodeError): + pass + if not token: +- raise KerberosError( ++ raise errors.KerberosError( + message=u"No valid Negotiate header in server response") + token = self._sec_context.step(token=token) + if self._sec_context.complete: +@@ -979,8 +979,10 @@ class RPCClient(Connectible): + delegate = self.api.env.delegate + if ca_certfile is None: + ca_certfile = self.api.env.tls_ca_cert ++ context.ca_certfile = ca_certfile ++ ++ rpc_uri = self.env[self.env_rpc_uri_key] + try: +- rpc_uri = self.env[self.env_rpc_uri_key] + principal = get_principal(ccache_name=ccache) + stored_principal = getattr(context, 'principal', None) + if principal != stored_principal: +@@ -996,12 +998,14 @@ class RPCClient(Connectible): + except (errors.CCacheError, ValueError): + # No session key, do full Kerberos auth + pass +- context.ca_certfile = ca_certfile + urls = self.get_url_list(rpc_uri) + serverproxy = None + for url in urls: +- kw = dict(allow_none=True, encoding='UTF-8') +- kw['verbose'] = verbose ++ kw = { ++ 'allow_none': True, ++ 'encoding': 'UTF-8', ++ 'verbose': verbose ++ } + if url.startswith('https://'): + if delegate: + transport_class = DelegatedKerbTransport +@@ -1036,21 +1040,24 @@ class RPCClient(Connectible): + ) + # We don't care about the response, just that we got one + break +- except KerberosError as krberr: ++ except errors.KerberosError: + # kerberos error on one server is likely on all +- raise errors.KerberosError(message=unicode(krberr)) ++ raise + except ProtocolError as e: + if hasattr(context, 'session_cookie') and e.errcode == 401: + # Unauthorized. Remove the session and try again. + delattr(context, 'session_cookie') + try: + delete_persistent_client_session_data(principal) +- except Exception as e: ++ except Exception: + # This shouldn't happen if we have a session but it isn't fatal. + pass +- return self.create_connection(ccache, verbose, fallback, delegate) ++ return self.create_connection( ++ ccache, verbose, fallback, delegate) + if not fallback: + raise ++ else: ++ self.log.info('Connection to %s failed with %s', url, e) + serverproxy = None + except Exception as e: + if not fallback: +-- +2.9.4 + diff --git a/SOURCES/0176-rpc-avoid-possible-recursion-in-create_connection.patch b/SOURCES/0176-rpc-avoid-possible-recursion-in-create_connection.patch new file mode 100644 index 0000000..885522f --- /dev/null +++ b/SOURCES/0176-rpc-avoid-possible-recursion-in-create_connection.patch @@ -0,0 +1,175 @@ +From f5f7cbc63f9235ce040687cdf102aeaef69ab422 Mon Sep 17 00:00:00 2001 +From: Stanislav Laznicka +Date: Thu, 1 Jun 2017 09:00:15 +0200 +Subject: [PATCH] rpc: avoid possible recursion in create_connection + +There was a recursion in RPCClient.create_connection() which under rare +circumstances would not have an ending condition. This commit removes +it and cleans up the code a bit as well. + +https://pagure.io/freeipa/issue/6796 + +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/rpc.py | 140 +++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 74 insertions(+), 66 deletions(-) + +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index b12ce4c5365299332587ad0d2990ca30070217bf..e3b8d67d69c084ad1a43390b5f93061826a27e1d 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -999,77 +999,85 @@ class RPCClient(Connectible): + # No session key, do full Kerberos auth + pass + urls = self.get_url_list(rpc_uri) +- serverproxy = None ++ ++ proxy_kw = { ++ 'allow_none': True, ++ 'encoding': 'UTF-8', ++ 'verbose': verbose ++ } ++ + for url in urls: +- kw = { +- 'allow_none': True, +- 'encoding': 'UTF-8', +- 'verbose': verbose +- } +- if url.startswith('https://'): +- if delegate: +- transport_class = DelegatedKerbTransport ++ # should we get ProtocolError (=> error in HTTP response) and ++ # 401 (=> Unauthorized), we'll be re-trying with new session ++ # cookies several times ++ for _try_num in range(0, 5): ++ if url.startswith('https://'): ++ if delegate: ++ transport_class = DelegatedKerbTransport ++ else: ++ transport_class = KerbTransport + else: +- transport_class = KerbTransport +- else: +- transport_class = LanguageAwareTransport +- kw['transport'] = transport_class(protocol=self.protocol, +- service='HTTP', ccache=ccache) +- self.log.info('trying %s' % url) +- setattr(context, 'request_url', url) +- serverproxy = self.server_proxy_class(url, **kw) +- if len(urls) == 1: +- # if we have only 1 server and then let the +- # main requester handle any errors. This also means it +- # must handle a 401 but we save a ping. +- return serverproxy +- try: +- command = getattr(serverproxy, 'ping') ++ transport_class = LanguageAwareTransport ++ proxy_kw['transport'] = transport_class( ++ protocol=self.protocol, service='HTTP', ccache=ccache) ++ self.log.info('trying %s' % url) ++ setattr(context, 'request_url', url) ++ serverproxy = self.server_proxy_class(url, **proxy_kw) ++ if len(urls) == 1: ++ # if we have only 1 server and then let the ++ # main requester handle any errors. This also means it ++ # must handle a 401 but we save a ping. ++ return serverproxy + try: +- command([], {}) +- except Fault as e: +- e = decode_fault(e) +- if e.faultCode in errors_by_code: +- error = errors_by_code[e.faultCode] +- raise error(message=e.faultString) +- else: +- raise UnknownError( +- code=e.faultCode, +- error=e.faultString, +- server=url, +- ) +- # We don't care about the response, just that we got one +- break +- except errors.KerberosError: +- # kerberos error on one server is likely on all +- raise +- except ProtocolError as e: +- if hasattr(context, 'session_cookie') and e.errcode == 401: +- # Unauthorized. Remove the session and try again. +- delattr(context, 'session_cookie') ++ command = getattr(serverproxy, 'ping') + try: +- delete_persistent_client_session_data(principal) +- except Exception: +- # This shouldn't happen if we have a session but it isn't fatal. +- pass +- return self.create_connection( +- ccache, verbose, fallback, delegate) +- if not fallback: ++ command([], {}) ++ except Fault as e: ++ e = decode_fault(e) ++ if e.faultCode in errors_by_code: ++ error = errors_by_code[e.faultCode] ++ raise error(message=e.faultString) ++ else: ++ raise UnknownError( ++ code=e.faultCode, ++ error=e.faultString, ++ server=url, ++ ) ++ # We don't care about the response, just that we got one ++ return serverproxy ++ except errors.KerberosError: ++ # kerberos error on one server is likely on all + raise +- else: +- self.log.info('Connection to %s failed with %s', url, e) +- serverproxy = None +- except Exception as e: +- if not fallback: +- raise +- else: +- self.log.info('Connection to %s failed with %s', url, e) +- serverproxy = None +- +- if serverproxy is None: +- raise NetworkError(uri=_('any of the configured servers'), +- error=', '.join(urls)) +- return serverproxy ++ except ProtocolError as e: ++ if hasattr(context, 'session_cookie') and e.errcode == 401: ++ # Unauthorized. Remove the session and try again. ++ delattr(context, 'session_cookie') ++ try: ++ delete_persistent_client_session_data(principal) ++ except Exception: ++ # This shouldn't happen if we have a session but ++ # it isn't fatal. ++ pass ++ # try the same url once more with a new session cookie ++ continue ++ if not fallback: ++ raise ++ else: ++ self.log.info( ++ 'Connection to %s failed with %s', url, e) ++ # try the next url ++ break ++ except Exception as e: ++ if not fallback: ++ raise ++ else: ++ self.log.info( ++ 'Connection to %s failed with %s', url, e) ++ # try the next url ++ break ++ # finished all tries but no serverproxy was found ++ raise NetworkError(uri=_('any of the configured servers'), ++ error=', '.join(urls)) + + def destroy_connection(self): + conn = getattr(context, self.id, None) +-- +2.9.4 + diff --git a/SOURCES/0177-Changing-cert-find-to-do-not-use-only-primary-key-to.patch b/SOURCES/0177-Changing-cert-find-to-do-not-use-only-primary-key-to.patch new file mode 100644 index 0000000..ee9e062 --- /dev/null +++ b/SOURCES/0177-Changing-cert-find-to-do-not-use-only-primary-key-to.patch @@ -0,0 +1,102 @@ +From d5af6b5e3ee50f97db730a4097c46baf07e09002 Mon Sep 17 00:00:00 2001 +From: Felipe Volpone +Date: Thu, 1 Jun 2017 16:53:11 -0300 +Subject: [PATCH] Changing cert-find to do not use only primary key to search + in LDAP. + +In service.py the primary key is krbCanonicalName, which we +don't want to use to do searchs. Now, cert-find uses primary +key or a specified attribute to do searches in LDAP, instead +of using only a primary key. + +https://pagure.io/freeipa/issue/6948 + +Reviewed-By: Martin Babinsky +Reviewed-By: Jan Cholasta +Reviewed-By: Fraser Tweedale +--- + ipaserver/plugins/cert.py | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 68402679cf0320e9c664ea89276f6c4332730a15..bb11713317abad55577b1c280253ab5d6d68c508 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -981,8 +981,8 @@ class cert(BaseCertObject): + param = param.clone(flags=param.flags - {'no_search'}) + yield param + +- for owner in self._owners(): +- yield owner.primary_key.clone_rename( ++ for owner, search_key in self._owners(): ++ yield search_key.clone_rename( + 'owner_{0}'.format(owner.name), + required=False, + multivalue=True, +@@ -992,15 +992,22 @@ class cert(BaseCertObject): + ) + + def _owners(self): +- for name in ('user', 'host', 'service'): +- yield self.api.Object[name] ++ for obj_name, search_key in [('user', None), ++ ('host', None), ++ ('service', 'krbprincipalname')]: ++ obj = self.api.Object[obj_name] ++ if search_key is None: ++ pkey = obj.primary_key ++ else: ++ pkey = obj.params[search_key] ++ yield obj, pkey + + def _fill_owners(self, obj): + dns = obj.pop('owner', None) + if dns is None: + return + +- for owner in self._owners(): ++ for owner, _search_key in self._owners(): + container_dn = DN(owner.container_dn, self.api.env.basedn) + name = 'owner_' + owner.name + for dn in dns: +@@ -1264,8 +1271,8 @@ class cert_find(Search, CertMethod): + option = option.clone(default=None, autofill=None) + yield option + +- for owner in self.obj._owners(): +- yield owner.primary_key.clone_rename( ++ for owner, search_key in self.obj._owners(): ++ yield search_key.clone_rename( + '{0}'.format(owner.name), + required=False, + multivalue=True, +@@ -1276,7 +1283,7 @@ class cert_find(Search, CertMethod): + owner.object_name_plural), + label=owner.object_name, + ) +- yield owner.primary_key.clone_rename( ++ yield search_key.clone_rename( + 'no_{0}'.format(owner.name), + required=False, + multivalue=True, +@@ -1395,7 +1402,7 @@ class cert_find(Search, CertMethod): + ldap = self.api.Backend.ldap2 + + filters = [] +- for owner in self.obj._owners(): ++ for owner, search_key in self.obj._owners(): + for prefix, rule in (('', ldap.MATCH_ALL), + ('no_', ldap.MATCH_NONE)): + try: +@@ -1411,7 +1418,7 @@ class cert_find(Search, CertMethod): + filters.append(filter) + + filter = ldap.make_filter_from_attr( +- owner.primary_key.name, ++ search_key.name, + value, + rule) + filters.append(filter) +-- +2.9.4 + diff --git a/SOURCES/0178-ipa-kdb-add-pkinit-authentication-indicator-in-case-.patch b/SOURCES/0178-ipa-kdb-add-pkinit-authentication-indicator-in-case-.patch new file mode 100644 index 0000000..3a78753 --- /dev/null +++ b/SOURCES/0178-ipa-kdb-add-pkinit-authentication-indicator-in-case-.patch @@ -0,0 +1,101 @@ +From 5e052107dcb70630c1cccee191ae5317a43ec2cf Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Sun, 4 Jun 2017 22:49:13 +0300 +Subject: [PATCH] ipa-kdb: add pkinit authentication indicator in case of a + successful certauth + +We automatically add 'otp' and 'radius' authentication indicators when +pre-authentication with OTP or RADIUS did succeed. Do the same for +certauth-based pre-authentication (PKINIT). + +A default PKINIT configuration does not add any authentication +indicators unless 'pkinit_indicator = pkinit' is set in kdc.conf. +Unfortunately, modifying kdc.conf automatically is a bit more +complicated than modifying krb5.conf. Given that we have 'otp' and +'radius' authentication indicators also defined in the code not in the +kdc.conf, this change is following an established trend. + +SSSD certauth interface does not provide additional information about +which rule(s) succeeded in matching the incoming certificate. Thus, +there is not much information we can automatically provide in the +indicator. It would be good to generate indicators that include some +information from the certmapping rules in future but for now a single +'pkinit' indicator is enough. + +Fixes https://pagure.io/freeipa/issue/6736 + +Reviewed-By: Simo Sorce +--- + daemons/ipa-kdb/ipa_kdb_certauth.c | 36 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_certauth.c b/daemons/ipa-kdb/ipa_kdb_certauth.c +index dbe7a0443700784d2b8dbb1fb9196d6249e5522a..da9a9cb87feca68ee591da70a3239dc86749bae5 100644 +--- a/daemons/ipa-kdb/ipa_kdb_certauth.c ++++ b/daemons/ipa-kdb/ipa_kdb_certauth.c +@@ -267,6 +267,7 @@ static krb5_error_code ipa_certauth_authorize(krb5_context context, + int ret; + size_t c; + char *principal = NULL; ++ char **auth_inds = NULL; + LDAPMessage *res = NULL; + krb5_error_code kerr; + LDAPMessage *lentry; +@@ -350,6 +351,20 @@ static krb5_error_code ipa_certauth_authorize(krb5_context context, + goto done; + } + ++ /* Associate authentication indicator "pkinit" with the successful match. ++ * SSSD interface doesn't give us a clue which rule did match ++ * so there is nothing more to add here. */ ++ auth_inds = calloc(2, sizeof(char *)); ++ if (auth_inds != NULL) { ++ ret = asprintf(&auth_inds[0], "pkinit"); ++ if (ret != -1) { ++ auth_inds[1] = NULL; ++ *authinds_out = auth_inds; ++ } else { ++ free(auth_inds); ++ } ++ } ++ + /* TODO: add more tests ? */ + + ret = 0; +@@ -384,6 +399,24 @@ static void ipa_certauth_fini(krb5_context context, + return; + } + ++static void ipa_certauth_free_indicator(krb5_context context, ++ krb5_certauth_moddata moddata, ++ char **authinds) ++{ ++ size_t i = 0; ++ ++ if ((authinds == NULL) || (moddata == NULL)) { ++ return; ++ } ++ ++ for(i=0; authinds[i]; i++) { ++ free(authinds[i]); ++ authinds[i] = NULL; ++ } ++ ++ free(authinds); ++} ++ + + krb5_error_code certauth_ipakdb_initvt(krb5_context context, + int maj_ver, int min_ver, +@@ -401,7 +434,6 @@ krb5_error_code certauth_ipakdb_initvt(krb5_context context, + vt->authorize = ipa_certauth_authorize; + vt->init = ipa_certauth_init; + vt->fini = ipa_certauth_fini; +- /* currently we do not return authentication indicators */ +- vt->free_ind = NULL; ++ vt->free_ind = ipa_certauth_free_indicator; + return 0; + } +-- +2.9.4 + diff --git a/SOURCES/0179-fix-incorrect-suffix-handling-in-topology-checks.patch b/SOURCES/0179-fix-incorrect-suffix-handling-in-topology-checks.patch new file mode 100644 index 0000000..e42bb98 --- /dev/null +++ b/SOURCES/0179-fix-incorrect-suffix-handling-in-topology-checks.patch @@ -0,0 +1,49 @@ +From 6d44c0c1455442ffd61ad532635c109b92ca96d1 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 26 May 2017 12:23:51 +0200 +Subject: [PATCH] fix incorrect suffix handling in topology checks + +When trying to delete a partially removed master entry lacking +'iparepltopomanagedsuffix' attribute, the code that tries to retrieve +tha value for further computations passes None and causes unhandled +internal errors. + +If the attribute is empty or not present, we should return empty list +instead as to not break calling cod attribute, the code that tries to +retrieve tha value for further computations passes None and causes +unhandled internal errors. We should return empty list instead. + +https://pagure.io/freeipa/issue/6965 + +Reviewed-By: Felipe Volpone +--- + ipaserver/topology.py | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/topology.py b/ipaserver/topology.py +index 385da29a66fb7276c55e9aac5c8c266b897721a7..2b6b0835473097eeec673230fab338bef41b8c49 100644 +--- a/ipaserver/topology.py ++++ b/ipaserver/topology.py +@@ -72,12 +72,15 @@ def get_topology_connection_errors(graph): + + def map_masters_to_suffixes(masters): + masters_to_suffix = {} ++ managed_suffix_attr = 'iparepltopomanagedsuffix_topologysuffix' + + for master in masters: +- try: +- managed_suffixes = master.get( +- 'iparepltopomanagedsuffix_topologysuffix') +- except KeyError: ++ if managed_suffix_attr not in master: ++ continue ++ ++ managed_suffixes = master[managed_suffix_attr] ++ ++ if managed_suffixes is None: + continue + + for suffix_name in managed_suffixes: +-- +2.9.4 + diff --git a/SOURCES/0180-server-certinstall-update-KDC-master-entry.patch b/SOURCES/0180-server-certinstall-update-KDC-master-entry.patch new file mode 100644 index 0000000..5fa9aca --- /dev/null +++ b/SOURCES/0180-server-certinstall-update-KDC-master-entry.patch @@ -0,0 +1,45 @@ +From 82af886e17905b8bdaadf8fc2b8214ad85a94470 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 5 Jun 2017 12:35:52 +0000 +Subject: [PATCH] server certinstall: update KDC master entry + +After the KDC certificate is installed, add the PKINIT enabled flag to the +KDC master entry. + +https://pagure.io/freeipa/issue/7000 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/ipa_server_certinstall.py | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py +index a14a84f188c62170c8ac11f823ebba60609e4cc7..9c8f6e81a802e1a87bab1fd15f729e10676fe3a3 100644 +--- a/ipaserver/install/ipa_server_certinstall.py ++++ b/ipaserver/install/ipa_server_certinstall.py +@@ -34,7 +34,7 @@ from ipapython.certdb import (get_ca_nickname, + verify_kdc_cert_validity) + from ipapython.dn import DN + from ipalib import api, errors +-from ipaserver.install import certs, dsinstance, installutils ++from ipaserver.install import certs, dsinstance, installutils, krbinstance + + + class ServerCertInstall(admintool.AdminTool): +@@ -223,6 +223,13 @@ class ServerCertInstall(admintool.AdminTool): + except RuntimeError as e: + raise admintool.ScriptError(str(e)) + ++ krb = krbinstance.KrbInstance() ++ krb.init_info( ++ realm_name=api.env.realm, ++ host_name=api.env.host, ++ ) ++ krb.pkinit_enable() ++ + def check_chain(self, pkcs12_filename, pkcs12_pin, nssdb): + # create a temp nssdb + with NSSDatabase() as tempnssdb: +-- +2.9.4 + diff --git a/SOURCES/0181-pkinit-manage-introduce-ipa-pkinit-manage.patch b/SOURCES/0181-pkinit-manage-introduce-ipa-pkinit-manage.patch new file mode 100644 index 0000000..9662a62 --- /dev/null +++ b/SOURCES/0181-pkinit-manage-introduce-ipa-pkinit-manage.patch @@ -0,0 +1,259 @@ +From d224655e4b1e218bac19dff5a10bf3e0d83edcb0 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 5 Jun 2017 12:41:02 +0000 +Subject: [PATCH] pkinit manage: introduce ipa-pkinit-manage + +Add the ipa-pkinit-manage tool to allow enabling / disabling PKINIT after +the initial server install. + +https://pagure.io/freeipa/issue/7000 + +Reviewed-By: Martin Babinsky +--- + freeipa.spec.in | 2 + + install/tools/Makefile.am | 1 + + install/tools/ipa-pkinit-manage | 8 +++ + install/tools/man/Makefile.am | 1 + + install/tools/man/ipa-pkinit-manage.1 | 34 +++++++++++++ + ipaserver/install/ipa_pkinit_manage.py | 93 ++++++++++++++++++++++++++++++++++ + ipaserver/install/krbinstance.py | 24 +++++++++ + 7 files changed, 163 insertions(+) + create mode 100755 install/tools/ipa-pkinit-manage + create mode 100644 install/tools/man/ipa-pkinit-manage.1 + create mode 100644 ipaserver/install/ipa_pkinit_manage.py + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 2cbaa60df0db021a4a1ce10af383cd6a15e1e57c..ae77a9a23645c1490c32195203e2c4f665783a80 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1184,6 +1184,7 @@ fi + %{_sbindir}/ipa-advise + %{_sbindir}/ipa-cacert-manage + %{_sbindir}/ipa-winsync-migrate ++%{_sbindir}/ipa-pkinit-manage + %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit + %{_libexecdir}/certmonger/ipa-server-guard + %dir %{_libexecdir}/ipa +@@ -1247,6 +1248,7 @@ fi + %{_mandir}/man1/ipa-otptoken-import.1* + %{_mandir}/man1/ipa-cacert-manage.1* + %{_mandir}/man1/ipa-winsync-migrate.1* ++%{_mandir}/man1/ipa-pkinit-manage.1* + + + %files -n python2-ipaserver +diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am +index 493e5ff4a8290be8ef076135104a85f8315b7842..47ecc14d7320336315c16587956c4965387853d9 100644 +--- a/install/tools/Makefile.am ++++ b/install/tools/Makefile.am +@@ -28,6 +28,7 @@ dist_sbin_SCRIPTS = \ + ipa-advise \ + ipa-cacert-manage \ + ipa-winsync-migrate \ ++ ipa-pkinit-manage \ + $(NULL) + + appdir = $(libexecdir)/ipa/ +diff --git a/install/tools/ipa-pkinit-manage b/install/tools/ipa-pkinit-manage +new file mode 100755 +index 0000000000000000000000000000000000000000..5b2413bd7cdc97632f82a77e18f3424a2ff63309 +--- /dev/null ++++ b/install/tools/ipa-pkinit-manage +@@ -0,0 +1,8 @@ ++#! /usr/bin/python2 -E ++# ++# Copyright (C) 2017 FreeIPA Contributors see COPYING for license ++# ++ ++from ipaserver.install.ipa_pkinit_manage import PKINITManage ++ ++PKINITManage.run_cli() +diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am +index 0d06ec7306b0cc1e656dac244bcb2c480b0ae61e..2dac9ac716352847aeb0d1fd3c6375ede956c751 100644 +--- a/install/tools/man/Makefile.am ++++ b/install/tools/man/Makefile.am +@@ -27,6 +27,7 @@ dist_man1_MANS = \ + ipa-otptoken-import.1 \ + ipa-cacert-manage.1 \ + ipa-winsync-migrate.1 \ ++ ipa-pkinit-manage.1 \ + $(NULL) + + dist_man8_MANS = \ +diff --git a/install/tools/man/ipa-pkinit-manage.1 b/install/tools/man/ipa-pkinit-manage.1 +new file mode 100644 +index 0000000000000000000000000000000000000000..5018ce8aa3f89470453d9cfc590a0c5f44f78f3c +--- /dev/null ++++ b/install/tools/man/ipa-pkinit-manage.1 +@@ -0,0 +1,34 @@ ++.\" ++.\" Copyright (C) 2017 FreeIPA Contributors see COPYING for license ++.\" ++.TH "ipa-pkinit-manage" "1" "Jun 05 2017" "FreeIPA" "FreeIPA Manual Pages" ++.SH "NAME" ++ipa\-pkinit\-manage \- Enables or disables PKINIT ++.SH "SYNOPSIS" ++ipa\-pkinit\-manage [options] ++.SH "DESCRIPTION" ++Run the command with the \fBenable\fR option to enable PKINIT. ++ ++Run the command with the \fBdisable\fR option to disable PKINIT. ++ ++Run the command with the \fBstatus\fR to determine the current status of PKINIT. ++.SH "OPTIONS" ++.TP ++\fB\-\-version\fR ++Show the program's version and exit. ++.TP ++\fB\-h\fR, \fB\-\-help\fR ++Show the help for this program. ++.TP ++\fB\-v\fR, \fB\-\-verbose\fR ++Print debugging information. ++.TP ++\fB\-q\fR, \fB\-\-quiet\fR ++Output only errors. ++.TP ++\fB\-\-log\-file\fR=\fIFILE\fR ++Log to the given file. ++.SH "EXIT STATUS" ++0 if the command was successful ++ ++1 if an error occurred +diff --git a/ipaserver/install/ipa_pkinit_manage.py b/ipaserver/install/ipa_pkinit_manage.py +new file mode 100644 +index 0000000000000000000000000000000000000000..428c0e3476b4dbd13a9ee5a40a42447f9fa95f2d +--- /dev/null ++++ b/ipaserver/install/ipa_pkinit_manage.py +@@ -0,0 +1,93 @@ ++# ++# Copyright (C) 2017 FreeIPA Contributors see COPYING for license ++# ++ ++from __future__ import print_function ++ ++from ipalib import api ++from ipaplatform.paths import paths ++from ipapython.admintool import AdminTool ++from ipaserver.install.krbinstance import KrbInstance, is_pkinit_enabled ++ ++ ++class PKINITManage(AdminTool): ++ command_name = "ipa-pkinit-manage" ++ usage = "%prog " ++ description = "Manage PKINIT." ++ ++ def validate_options(self): ++ super(PKINITManage, self).validate_options(needs_root=True) ++ ++ option_parser = self.option_parser ++ ++ if not self.args: ++ option_parser.error("action not specified") ++ elif len(self.args) > 1: ++ option_parser.error("too many arguments") ++ ++ action = self.args[0] ++ if action not in {'enable', 'disable', 'status'}: ++ option_parser.error("unrecognized action '{}'".format(action)) ++ ++ def run(self): ++ api.bootstrap(in_server=True, confdir=paths.ETC_IPA) ++ api.finalize() ++ ++ api.Backend.ldap2.connect() ++ try: ++ action = self.args[0] ++ if action == 'enable': ++ self.enable() ++ elif action == 'disable': ++ self.disable() ++ elif action == 'status': ++ self.status() ++ finally: ++ api.Backend.ldap2.disconnect() ++ ++ return 0 ++ ++ def _setup(self, setup_pkinit): ++ config = api.Command.config_show()['result'] ++ ca_enabled = api.Command.ca_is_enabled()['result'] ++ ++ krb = KrbInstance() ++ krb.init_info( ++ realm_name=api.env.realm, ++ host_name=api.env.host, ++ setup_pkinit=setup_pkinit, ++ subject_base=config['ipacertificatesubjectbase'][0], ++ ) ++ ++ if bool(is_pkinit_enabled()) is not bool(setup_pkinit): ++ try: ++ krb.stop_tracking_certs() ++ except RuntimeError as e: ++ if ca_enabled: ++ self.log.warning( ++ "Failed to stop tracking certificates: %s", e) ++ ++ krb.enable_ssl() ++ ++ if setup_pkinit: ++ krb.pkinit_enable() ++ else: ++ krb.pkinit_disable() ++ ++ def enable(self): ++ if not api.Command.ca_is_enabled()['result']: ++ self.log.error("Cannot enable PKINIT in CA-less deployment") ++ self.log.error("Use ipa-server-certinstall to install KDC " ++ "certificate manually") ++ raise RuntimeError("Cannot enable PKINIT in CA-less deployment") ++ ++ self._setup(True) ++ ++ def disable(self): ++ self._setup(False) ++ ++ def status(self): ++ if is_pkinit_enabled(): ++ print("PKINIT is enabled") ++ else: ++ print("PKINIT is disabled") +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index a1053d55ccaae17bef93547c036fb9d08d296f0b..6b51e65d1ec985bfc01f167aea3fe3ca11c7ec29 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -451,6 +451,30 @@ class KrbInstance(service.Service): + service.set_service_entry_config( + 'KDC', self.fqdn, [PKINIT_ENABLED], self.suffix) + ++ def pkinit_disable(self): ++ """ ++ unadvertise enabled PKINIT feature in master's KDC entry in LDAP ++ """ ++ ldap = api.Backend.ldap2 ++ dn = DN(('cn', 'KDC'), ++ ('cn', self.fqdn), ++ ('cn', 'masters'), ++ ('cn', 'ipa'), ++ ('cn', 'etc'), ++ self.suffix) ++ ++ entry = ldap.get_entry(dn, ['ipaConfigString']) ++ ++ config = entry.setdefault('ipaConfigString', []) ++ config = [value for value in config ++ if value.lower() != PKINIT_ENABLED.lower()] ++ entry['ipaConfigString'][:] = config ++ ++ try: ++ ldap.update_entry(entry) ++ except errors.EmptyModlist: ++ pass ++ + def _install_pkinit_ca_bundle(self): + ca_certs = certstore.get_ca_certs(self.api.Backend.ldap2, + self.api.env.basedn, +-- +2.9.4 + diff --git a/SOURCES/0182-server-upgrade-do-not-enable-PKINIT-by-default.patch b/SOURCES/0182-server-upgrade-do-not-enable-PKINIT-by-default.patch new file mode 100644 index 0000000..5d539d4 --- /dev/null +++ b/SOURCES/0182-server-upgrade-do-not-enable-PKINIT-by-default.patch @@ -0,0 +1,42 @@ +From 1ab5b1a4cdcab8b913f42488ae642a9f0ef77d92 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Mon, 5 Jun 2017 12:42:52 +0000 +Subject: [PATCH] server upgrade: do not enable PKINIT by default + +Enabling PKINIT often fails during server upgrade when requesting the KDC +certificate. + +Now that PKINIT can be enabled post-install using ipa-pkinit-manage, avoid +the upgrade failure by not enabling PKINIT by default. + +https://pagure.io/freeipa/issue/7000 + +Reviewed-By: Martin Babinsky +--- + ipaserver/install/server/upgrade.py | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index db86353165809c57d1ac27bf762393721231fefd..b1f59d3e29d69bffc11935ec22d4b5f510293355 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1519,14 +1519,8 @@ def add_default_caacl(ca): + def setup_pkinit(krb): + root_logger.info("[Setup PKINIT]") + +- pkinit_is_enabled = krbinstance.is_pkinit_enabled() +- ca_is_enabled = api.Command.ca_is_enabled()['result'] +- +- if not pkinit_is_enabled: +- if ca_is_enabled: +- krb.issue_ipa_ca_signed_pkinit_certs() +- else: +- krb.issue_selfsigned_pkinit_certs() ++ if not krbinstance.is_pkinit_enabled(): ++ krb.issue_selfsigned_pkinit_certs() + + aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD, + loadpath=paths.USR_SHARE_IPA_DIR) +-- +2.9.4 + diff --git a/SOURCES/0183-Turn-off-OCSP-check.patch b/SOURCES/0183-Turn-off-OCSP-check.patch new file mode 100644 index 0000000..6556af5 --- /dev/null +++ b/SOURCES/0183-Turn-off-OCSP-check.patch @@ -0,0 +1,196 @@ +From ea2fc433d3f72364340919345805c667ce0d7524 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Thu, 1 Jun 2017 09:56:16 +0200 +Subject: [PATCH] Turn off OCSP check + +The OCSP check was previously turned on but it introduced several +issues. Therefore the check will be turned off by default. + +For turning on should be used ipa advise command with correct recipe. +The solution is tracked here: https://pagure.io/freeipa/issue/6982 + +Fixes: https://pagure.io/freeipa/issue/6981 +Reviewed-By: Martin Babinsky +--- + install/restart_scripts/restart_httpd | 15 +----------- + ipaserver/install/httpinstance.py | 43 +++++++++++++++++++---------------- + ipaserver/install/server/upgrade.py | 25 +++----------------- + 3 files changed, 28 insertions(+), 55 deletions(-) + +diff --git a/install/restart_scripts/restart_httpd b/install/restart_scripts/restart_httpd +index cd7f12024ea3cab16e9c664687cd854e666c9570..d1684812904a9d32842a0ca548ec6b9df5a5a0b7 100644 +--- a/install/restart_scripts/restart_httpd ++++ b/install/restart_scripts/restart_httpd +@@ -21,24 +21,11 @@ + + import syslog + import traceback +-from ipalib import api + from ipaplatform import services +-from ipaplatform.paths import paths +-from ipapython.certdb import TRUSTED_PEER_TRUST_FLAGS +-from ipaserver.install import certs, installutils ++from ipaserver.install import certs + + + def _main(): +- +- api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) +- api.finalize() +- +- db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) +- nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, "NSSNickname") +- +- # Add trust flag which set certificate trusted for SSL connections. +- db.trust_root_cert(nickname, TRUSTED_PEER_TRUST_FLAGS) +- + syslog.syslog(syslog.LOG_NOTICE, 'certmonger restarted httpd') + + try: +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 12fdddccc26b0c1132bcdca7fe2249a85997892e..f637b97db8f21ddbc00c4f70e18e836d300b2f33 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -34,8 +34,7 @@ from augeas import Augeas + from ipalib.install import certmonger + from ipapython import ipaldap + from ipapython.certdb import (IPA_CA_TRUST_FLAGS, +- EXTERNAL_CA_TRUST_FLAGS, +- TRUSTED_PEER_TRUST_FLAGS) ++ EXTERNAL_CA_TRUST_FLAGS) + from ipaserver.install import replication + from ipaserver.install import service + from ipaserver.install import certs +@@ -74,6 +73,10 @@ NSS_CIPHER_SUITE = [ + ] + NSS_CIPHER_REVISION = '20160129' + ++OCSP_DIRECTIVE = 'NSSOCSP' ++ ++NSS_OCSP_ENABLED = 'nss_ocsp_enabled' ++ + + def httpd_443_configured(): + """ +@@ -163,7 +166,7 @@ class HTTPInstance(service.Service): + self.set_mod_nss_protocol) + self.step("setting mod_nss password file", self.__set_mod_nss_passwordfile) + self.step("enabling mod_nss renegotiate", self.enable_mod_nss_renegotiate) +- self.step("enabling mod_nss OCSP", self.enable_mod_nss_ocsp) ++ self.step("disabling mod_nss OCSP", self.disable_mod_nss_ocsp) + self.step("adding URL rewriting rules", self.__add_include) + self.step("configuring httpd", self.__configure_http) + self.step("setting up httpd keytab", self.request_service_keytab) +@@ -270,7 +273,12 @@ class HTTPInstance(service.Service): + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRenegotiation', 'on', False) + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRequireSafeNegotiation', 'on', False) + +- def enable_mod_nss_ocsp(self): ++ def disable_mod_nss_ocsp(self): ++ if sysupgrade.get_upgrade_state('http', NSS_OCSP_ENABLED) is None: ++ self.__disable_mod_nss_ocsp() ++ sysupgrade.set_upgrade_state('http', NSS_OCSP_ENABLED, False) ++ ++ def __disable_mod_nss_ocsp(self): + aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD) + + aug.set('/augeas/load/Httpd/lens', 'Httpd.lns') +@@ -278,22 +286,21 @@ class HTTPInstance(service.Service): + aug.load() + + path = '/files{}/VirtualHost'.format(paths.HTTPD_NSS_CONF) ++ ocsp_path = '{}/directive[.="{}"]'.format(path, OCSP_DIRECTIVE) ++ ocsp_arg = '{}/arg'.format(ocsp_path) ++ ocsp_comment = '{}/#comment[.="{}"]'.format(path, OCSP_DIRECTIVE) + +- ocsp_comment = aug.get( +- '{}/#comment[.=~regexp("NSSOCSP .*")]'.format(path)) +- ocsp_dir = aug.get('{}/directive[.="NSSOCSP"]'.format(path)) ++ ocsp_dir = aug.get(ocsp_path) + +- if ocsp_dir is None and ocsp_comment is not None: +- # Directive is missing, comment is present +- aug.set('{}/#comment[.=~regexp("NSSOCSP .*")]'.format(path), +- 'NSSOCSP') +- aug.rename('{}/#comment[.="NSSOCSP"]'.format(path), 'directive') +- elif ocsp_dir is None: +- # Directive is missing and comment is missing +- aug.set('{}/directive[last()+1]'.format(path), "NSSOCSP") ++ # there is NSSOCSP directive in nss.conf file, comment it ++ # otherwise just do nothing ++ if ocsp_dir is not None: ++ ocsp_state = aug.get(ocsp_arg) ++ aug.remove(ocsp_arg) ++ aug.rename(ocsp_path, '#comment') ++ aug.set(ocsp_comment, '{} {}'.format(OCSP_DIRECTIVE, ocsp_state)) ++ aug.save() + +- aug.set('{}/directive[. = "NSSOCSP"]/arg'.format(path), 'on') +- aug.save() + + def set_mod_nss_cipher_suite(self): + ciphers = ','.join(NSS_CIPHER_SUITE) +@@ -412,8 +419,6 @@ class HTTPInstance(service.Service): + self.__set_mod_nss_nickname(nickname) + self.add_cert_to_service() + +- db.trust_root_cert(nickname, TRUSTED_PEER_TRUST_FLAGS) +- + else: + if not self.promote: + ca_args = [ +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index b1f59d3e29d69bffc11935ec22d4b5f510293355..732776f2cf513a4bb11d8f3f0dfaac78217e460f 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1395,24 +1395,6 @@ def fix_trust_flags(): + sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) + + +-def fix_server_cert_trust_flags(): +- root_logger.info( +- '[Fixing server certificate trust flags in %s]' % +- paths.HTTPD_ALIAS_DIR) +- +- if sysupgrade.get_upgrade_state('http', 'fix_serv_cert_trust_flags'): +- root_logger.info("Trust flags already processed") +- return +- +- db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) +- sc_nickname = installutils.get_directive(paths.HTTPD_NSS_CONF, +- "NSSNickname") +- # Add trust flag which set certificate trusted for SSL connections. +- db.trust_root_cert(sc_nickname, certdb.TRUSTED_PEER_TRUST_FLAGS) +- +- sysupgrade.set_upgrade_state('http', 'fix_serv_cert_trust_flags', True) +- +- + def update_mod_nss_protocol(http): + root_logger.info('[Updating mod_nss protocol versions]') + +@@ -1425,9 +1407,9 @@ def update_mod_nss_protocol(http): + sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True) + + +-def enable_mod_nss_ocsp(http): ++def disable_mod_nss_ocsp(http): + root_logger.info('[Updating mod_nss enabling OCSP]') +- http.enable_mod_nss_ocsp() ++ http.disable_mod_nss_ocsp() + + + def update_mod_nss_cipher_suite(http): +@@ -1721,9 +1703,8 @@ def upgrade_configuration(): + update_ipa_httpd_service_conf(http) + update_mod_nss_protocol(http) + update_mod_nss_cipher_suite(http) +- enable_mod_nss_ocsp(http) ++ disable_mod_nss_ocsp(http) + fix_trust_flags() +- fix_server_cert_trust_flags() + update_http_keytab(http) + http.configure_gssproxy() + http.start() +-- +2.9.4 + diff --git a/SOURCES/0184-Only-warn-when-specified-server-IP-addresses-don-t-m.patch b/SOURCES/0184-Only-warn-when-specified-server-IP-addresses-don-t-m.patch new file mode 100644 index 0000000..ae6ebbf --- /dev/null +++ b/SOURCES/0184-Only-warn-when-specified-server-IP-addresses-don-t-m.patch @@ -0,0 +1,244 @@ +From a04defc43419906675107e483f0f2f3153685c8d Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 31 May 2017 15:50:05 +0200 +Subject: [PATCH] Only warn when specified server IP addresses don't match intf + +In containers local addresses differ from public addresses and we need +a way to provide only public address to installers. + +https://pagure.io/freeipa/issue/2715 +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: Tomas Krizek +--- + ipaclient/install/client.py | 4 +- + ipalib/install/hostname.py | 2 +- + ipalib/util.py | 14 +++++++ + ipapython/ipautil.py | 62 ++++++++++++++++-------------- + ipaserver/install/dns.py | 1 + + ipaserver/install/installutils.py | 4 +- + ipaserver/install/server/install.py | 2 + + ipaserver/install/server/replicainstall.py | 2 + + 8 files changed, 59 insertions(+), 32 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 6f10f5258747881b9af8c6b70b499f9ff7d577ff..41dae3004d1f4836e79c2048ae0a12f722595ca0 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -41,6 +41,7 @@ from ipalib.util import ( + broadcast_ip_address_warning, + network_ip_address_warning, + normalize_hostname, ++ no_matching_interface_for_ip_address_warning, + verify_host_resolvable, + ) + from ipaplatform import services +@@ -1300,6 +1301,7 @@ def update_dns(server, hostname, options): + + network_ip_address_warning(update_ips) + broadcast_ip_address_warning(update_ips) ++ no_matching_interface_for_ip_address_warning(update_ips) + + update_txt = "debug\n" + update_txt += ipautil.template_str(DELETE_TEMPLATE_A, +@@ -1445,7 +1447,7 @@ def check_ip_addresses(options): + if options.ip_addresses: + for ip in options.ip_addresses: + try: +- ipautil.CheckedIPAddress(ip, match_local=True) ++ ipautil.CheckedIPAddress(ip) + except ValueError as e: + root_logger.error(e) + return False +diff --git a/ipalib/install/hostname.py b/ipalib/install/hostname.py +index 74c569d972df9975d677762b5769b2bf84dfddf0..5422ba6390ce13aa40f34938ed777d8821e8231b 100644 +--- a/ipalib/install/hostname.py ++++ b/ipalib/install/hostname.py +@@ -34,7 +34,7 @@ class HostNameInstallInterface(service.ServiceInstallInterface): + def ip_addresses(self, values): + for value in values: + try: +- CheckedIPAddress(value, match_local=True) ++ CheckedIPAddress(value) + except Exception as e: + raise ValueError("invalid IP address {0}: {1}".format( + value, e)) +diff --git a/ipalib/util.py b/ipalib/util.py +index 713fc107e9374eefe7805bc4e1abc40b6d150c32..1bd8495a49b010e7a3ac926dad516ab5f8219b39 100644 +--- a/ipalib/util.py ++++ b/ipalib/util.py +@@ -1128,3 +1128,17 @@ def broadcast_ip_address_warning(addr_list): + # print + print("WARNING: IP address {} might be broadcast address".format( + ip), file=sys.stderr) ++ ++ ++def no_matching_interface_for_ip_address_warning(addr_list): ++ for ip in addr_list: ++ if not ip.get_matching_interface(): ++ root_logger.warning( ++ "No network interface matches the IP address %s", ip) ++ # fixme: once when loggers will be fixed, we can remove this ++ # print ++ print( ++ "WARNING: No network interface matches the IP address " ++ "{}".format(ip), ++ file=sys.stderr ++ ) +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 317fc225b722ad3ce2f4b9d92822b4f19d49adb9..a277ed87473f3c591f34fcc00e1159f3bbfe3e9b 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -161,34 +161,7 @@ class CheckedIPAddress(UnsafeIPAddress): + raise ValueError("cannot use multicast IP address {}".format(addr)) + + if match_local: +- if self.version == 4: +- family = netifaces.AF_INET +- elif self.version == 6: +- family = netifaces.AF_INET6 +- else: +- raise ValueError( +- "Unsupported address family ({})".format(self.version) +- ) +- +- iface = None +- for interface in netifaces.interfaces(): +- for ifdata in netifaces.ifaddresses(interface).get(family, []): +- +- # link-local addresses contain '%suffix' that causes parse +- # errors in IPNetwork +- ifaddr = ifdata['addr'].split(u'%', 1)[0] +- +- ifnet = netaddr.IPNetwork('{addr}/{netmask}'.format( +- addr=ifaddr, +- netmask=ifdata['netmask'] +- )) +- if ifnet == self._net or ( +- self._net is None and ifnet.ip == self): +- self._net = ifnet +- iface = interface +- break +- +- if iface is None: ++ if not self.get_matching_interface(): + raise ValueError('no network interface matches the IP address ' + 'and netmask {}'.format(addr)) + +@@ -218,6 +191,39 @@ class CheckedIPAddress(UnsafeIPAddress): + def is_broadcast_addr(self): + return self.version == 4 and self == self._net.broadcast + ++ def get_matching_interface(self): ++ """Find matching local interface for address ++ :return: Interface name or None if no interface has this address ++ """ ++ if self.version == 4: ++ family = netifaces.AF_INET ++ elif self.version == 6: ++ family = netifaces.AF_INET6 ++ else: ++ raise ValueError( ++ "Unsupported address family ({})".format(self.version) ++ ) ++ ++ iface = None ++ for interface in netifaces.interfaces(): ++ for ifdata in netifaces.ifaddresses(interface).get(family, []): ++ ++ # link-local addresses contain '%suffix' that causes parse ++ # errors in IPNetwork ++ ifaddr = ifdata['addr'].split(u'%', 1)[0] ++ ++ ifnet = netaddr.IPNetwork('{addr}/{netmask}'.format( ++ addr=ifaddr, ++ netmask=ifdata['netmask'] ++ )) ++ if ifnet == self._net or ( ++ self._net is None and ifnet.ip == self): ++ self._net = ifnet ++ iface = interface ++ break ++ ++ return iface ++ + + def valid_ip(addr): + return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr) +diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py +index 0dddf2a6427f3f939171d755bfe2b1f05cfafa67..090b79493652566a433da248fa7fd9e33dd2cb72 100644 +--- a/ipaserver/install/dns.py ++++ b/ipaserver/install/dns.py +@@ -266,6 +266,7 @@ def install_check(standalone, api, replica, options, hostname): + + util.network_ip_address_warning(ip_addresses) + util.broadcast_ip_address_warning(ip_addresses) ++ util.no_matching_interface_for_ip_address_warning(ip_addresses) + + if not options.forward_policy: + # user did not specify policy, derive it: default is 'first' but +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index d2283af20485fd5d66bfd3cc49059d08d1802575..3521d555914714351160213df60ed9167ac6e370 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -276,7 +276,7 @@ def read_ip_addresses(): + if not ip: + break + try: +- ip_parsed = ipautil.CheckedIPAddress(ip, match_local=True) ++ ip_parsed = ipautil.CheckedIPAddress(ip) + except Exception as e: + print("Error: Invalid IP Address %s: %s" % (ip, e)) + continue +@@ -585,7 +585,7 @@ def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses): + if len(hostaddr): + for ha in hostaddr: + try: +- ips.append(ipautil.CheckedIPAddress(ha, match_local=True)) ++ ips.append(ipautil.CheckedIPAddress(ha, match_local=False)) + except ValueError as e: + root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e)) + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 9dcf903f4582740f007c049fae3ec247ddf52aef..7eb291e07c00e0407ce534c3d4088e6f6378260f 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -29,6 +29,7 @@ from ipalib.util import ( + validate_domain_name, + network_ip_address_warning, + broadcast_ip_address_warning, ++ no_matching_interface_for_ip_address_warning, + ) + import ipaclient.install.ntpconf + from ipaserver.install import ( +@@ -617,6 +618,7 @@ def install_check(installer): + # check addresses here, dns module is doing own check + network_ip_address_warning(ip_addresses) + broadcast_ip_address_warning(ip_addresses) ++ no_matching_interface_for_ip_address_warning(ip_addresses) + + if options.setup_adtrust: + adtrust.install_check(False, options, api) +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 20eaf98397101b49c751c325afc0591e0babcc18..6620f0222f9d38112ce0d0fd72381e5673921cba 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -35,6 +35,7 @@ from ipalib.config import Env + from ipalib.util import ( + network_ip_address_warning, + broadcast_ip_address_warning, ++ no_matching_interface_for_ip_address_warning, + ) + from ipaclient.install.client import configure_krb5_conf, purge_host_keytab + from ipaserver.install import ( +@@ -1285,6 +1286,7 @@ def promote_check(installer): + # check addresses here, dns module is doing own check + network_ip_address_warning(config.ips) + broadcast_ip_address_warning(config.ips) ++ no_matching_interface_for_ip_address_warning(config.ips) + + if options.setup_adtrust: + adtrust.install_check(False, options, remote_api) +-- +2.9.4 + diff --git a/SOURCES/0185-ipa-kdb-use-canonical-principal-in-certauth-plugin.patch b/SOURCES/0185-ipa-kdb-use-canonical-principal-in-certauth-plugin.patch new file mode 100644 index 0000000..4fa3e14 --- /dev/null +++ b/SOURCES/0185-ipa-kdb-use-canonical-principal-in-certauth-plugin.patch @@ -0,0 +1,33 @@ +From 25033eb499af95f458bd975eddd954c4b6a086ff Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 1 Jun 2017 18:17:53 +0200 +Subject: [PATCH] ipa-kdb: use canonical principal in certauth plugin + +Currently the certauth plugin use the unmodified principal from the +request to lookup the user. This might fail if e.g. enterprise +principals are use. With this patch the canonical principal form the kdc +entry is used. + +Resolves https://pagure.io/freeipa/issue/6993 + +Reviewed-By: David Kupka +--- + daemons/ipa-kdb/ipa_kdb_certauth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_certauth.c b/daemons/ipa-kdb/ipa_kdb_certauth.c +index da9a9cb87feca68ee591da70a3239dc86749bae5..66c2d08cbb9d23a8891b9cb6ca238925530eb40c 100644 +--- a/daemons/ipa-kdb/ipa_kdb_certauth.c ++++ b/daemons/ipa-kdb/ipa_kdb_certauth.c +@@ -284,7 +284,7 @@ static krb5_error_code ipa_certauth_authorize(krb5_context context, + } + } + +- ret = krb5_unparse_name(context, princ, &principal); ++ ret = krb5_unparse_name(context, db_entry->princ, &principal); + if (ret != 0) { + ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; + goto done; +-- +2.9.4 + diff --git a/SOURCES/0186-Bump-version-of-python-gssapi.patch b/SOURCES/0186-Bump-version-of-python-gssapi.patch new file mode 100644 index 0000000..546067e --- /dev/null +++ b/SOURCES/0186-Bump-version-of-python-gssapi.patch @@ -0,0 +1,66 @@ +From b117507de5cc68282b156a8e4751ef2cb5db74a9 Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Wed, 7 Jun 2017 10:11:39 +0200 +Subject: [PATCH] Bump version of python-gssapi + +Complete fixing of the bug requires fix on python-gssapi side. +That fix is included in version 1.2.0-5. + +Fixes: https://pagure.io/freeipa/issue/6796 +Reviewed-By: Martin Basti +--- + freeipa.spec.in | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index ae77a9a23645c1490c32195203e2c4f665783a80..d7f8d11ec553cfe299937e1e5f8cc27caed32b08 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -165,7 +165,7 @@ BuildRequires: python3-wheel + BuildRequires: samba-python + # 1.4: the version where Certificate.serial changed to .serial_number + BuildRequires: python2-cryptography >= 1.4 +-BuildRequires: python-gssapi >= 1.2.0 ++BuildRequires: python-gssapi >= 1.2.0-5 + BuildRequires: pylint >= 1.6 + # workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1096506 + BuildRequires: python2-polib +@@ -282,7 +282,7 @@ Requires: mod_session + # 0.9.9: https://github.com/adelton/mod_lookup_identity/pull/3 + Requires: mod_lookup_identity >= 0.9.9 + Requires: python-ldap >= 2.4.15 +-Requires: python-gssapi >= 1.2.0 ++Requires: python-gssapi >= 1.2.0-5 + Requires: acl + Requires: systemd-units >= 38 + Requires(pre): shadow-utils +@@ -349,7 +349,7 @@ Requires: python2-ipaclient = %{version}-%{release} + Requires: python-custodia >= 0.3.1 + Requires: python-ldap >= 2.4.15 + Requires: python-lxml +-Requires: python-gssapi >= 1.2.0 ++Requires: python-gssapi >= 1.2.0-5 + Requires: python-sssdconfig + Requires: python-pyasn1 + Requires: dbus-python +@@ -502,7 +502,7 @@ Requires: certmonger >= 0.78 + Requires: nss-tools + Requires: bind-utils + Requires: oddjob-mkhomedir +-Requires: python-gssapi >= 1.2.0 ++Requires: python-gssapi >= 1.2.0-5 + Requires: libsss_autofs + Requires: autofs + Requires: libnfsidmap +@@ -639,7 +639,7 @@ Provides: python2-ipaplatform = %{version}-%{release} + %{?python_provide:%python_provide python2-ipaplatform} + %{!?python_provide:Provides: python-ipaplatform = %{version}-%{release}} + Requires: %{name}-common = %{version}-%{release} +-Requires: python-gssapi >= 1.2.0 ++Requires: python-gssapi >= 1.2.0-5 + Requires: gnupg + Requires: keyutils + Requires: pyOpenSSL +-- +2.9.4 + diff --git a/SOURCES/0187-Add-code-to-be-able-to-set-default-kinit-lifetime.patch b/SOURCES/0187-Add-code-to-be-able-to-set-default-kinit-lifetime.patch new file mode 100644 index 0000000..438cf23 --- /dev/null +++ b/SOURCES/0187-Add-code-to-be-able-to-set-default-kinit-lifetime.patch @@ -0,0 +1,84 @@ +From 1e37dbe2c41ff0339873cd2347cb90c39a59d8ed Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Mon, 5 Jun 2017 09:50:22 -0400 +Subject: [PATCH] Add code to be able to set default kinit lifetime + +This is done by setting the kinit_lifetime option in default.conf +to a value that can be passed in with the -l option syntax of kinit. + +https://pagure.io/freeipa/issue/7001 + +Signed-off-by: Simo Sorce +Reviewed-By: Pavel Vomacka +Reviewed-By: Alexander Bokovoy +--- + ipalib/constants.py | 1 + + ipalib/install/kinit.py | 5 ++++- + ipaserver/rpcserver.py | 3 ++- + pylint_plugins.py | 1 + + 4 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/ipalib/constants.py b/ipalib/constants.py +index f8a194c1f559db9aeffef058578d700cde41fd0b..5adff97fbd6ad8ab4cfa5322481be2d9056f925a 100644 +--- a/ipalib/constants.py ++++ b/ipalib/constants.py +@@ -153,6 +153,7 @@ DEFAULT_CONFIG = ( + ('session_auth_duration', '20 minutes'), + # How a session expiration is computed, see SessionManager.set_session_expiration_time() + ('session_duration_type', 'inactivity_timeout'), ++ ('kinit_lifetime', None), + + # Debugging: + ('verbose', 0), +diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py +index 73471f103eabfe39580c8fbd0665157f635fa5c5..91ea5132aa1cb1e192af46b4896d55670e375f7a 100644 +--- a/ipalib/install/kinit.py ++++ b/ipalib/install/kinit.py +@@ -63,7 +63,7 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1): + + def kinit_password(principal, password, ccache_name, config=None, + armor_ccache_name=None, canonicalize=False, +- enterprise=False): ++ enterprise=False, lifetime=None): + """ + perform interactive kinit as principal using password. If using FAST for + web-based authentication, use armor_ccache_path to specify http service +@@ -76,6 +76,9 @@ def kinit_password(principal, password, ccache_name, config=None, + % armor_ccache_name) + args.extend(['-T', armor_ccache_name]) + ++ if lifetime: ++ args.extend(['-l', lifetime]) ++ + if canonicalize: + root_logger.debug("Requesting principal canonicalization") + args.append('-C') +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 32f286148bbdf294f941116b4bdca85714a52837..2990df25985eab63d4bcfc8edf7f2b12da3e9832 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -969,7 +969,8 @@ class login_password(Backend, KerberosSession): + password, + ccache_name, + armor_ccache_name=armor_path, +- enterprise=True) ++ enterprise=True, ++ lifetime=self.api.env.kinit_lifetime) + + if armor_path: + self.debug('Cleanup the armor ccache') +diff --git a/pylint_plugins.py b/pylint_plugins.py +index db80efeba8824eb221d988bb494400da173675a9..550f269b308b6c5b21cb13404040aa0934381f0e 100644 +--- a/pylint_plugins.py ++++ b/pylint_plugins.py +@@ -67,6 +67,7 @@ fake_api_env = {'env': [ + 'realm', + 'session_auth_duration', + 'session_duration_type', ++ 'kinit_lifetime', + ]} + + # this is due ipaserver.rpcserver.KerberosSession where api is undefined +-- +2.9.4 + diff --git a/SOURCES/0188-Revert-setting-sessionMaxAge-for-old-clients.patch b/SOURCES/0188-Revert-setting-sessionMaxAge-for-old-clients.patch new file mode 100644 index 0000000..4f69baa --- /dev/null +++ b/SOURCES/0188-Revert-setting-sessionMaxAge-for-old-clients.patch @@ -0,0 +1,44 @@ +From f231d5ceb283723c42f6c15210c76f28324c2e15 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Tue, 6 Jun 2017 09:04:58 -0400 +Subject: [PATCH] Revert setting sessionMaxAge for old clients + +Older clients have issues properly parsing cookies and the sessionMaxAge +setting is one of those that breaks them. +Comment out the setting and add a comment that explains why it is not +set by default. + +https://pagure.io/freeipa/issue/7001 + +Signed-off-by: Simo Sorce +Reviewed-By: Pavel Vomacka +Reviewed-By: Alexander Bokovoy +--- + install/conf/ipa.conf | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf +index a7ca5ce715e55960b8edd307cdbe41dcbd6b29ca..01bf9a4f97fc0cf197c0ad12743affa597b54911 100644 +--- a/install/conf/ipa.conf ++++ b/install/conf/ipa.conf +@@ -1,5 +1,5 @@ + # +-# VERSION 26 - DO NOT REMOVE THIS LINE ++# VERSION 27 - DO NOT REMOVE THIS LINE + # + # This file may be overwritten on upgrades. + # +@@ -77,7 +77,9 @@ WSGIScriptReloading Off + Session On + SessionCookieName ipa_session path=/ipa;httponly;secure; + SessionHeader IPASESSION +- SessionMaxAge 1800 ++ # Uncomment the following to have shorter sessions, but beware this may break ++ # old IPA client tols that incorrectly parse cookies. ++ # SessionMaxAge 1800 + GssapiSessionKey file:/etc/httpd/alias/ipasession.key + + GssapiImpersonate On +-- +2.9.4 + diff --git a/SOURCES/0189-Extend-the-advice-printing-code-by-some-useful-abstr.patch b/SOURCES/0189-Extend-the-advice-printing-code-by-some-useful-abstr.patch new file mode 100644 index 0000000..71fe9f8 --- /dev/null +++ b/SOURCES/0189-Extend-the-advice-printing-code-by-some-useful-abstr.patch @@ -0,0 +1,93 @@ +From 17f988f0524ff682a95fe6a4be55b49ea7a0a419 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Mon, 5 Jun 2017 16:59:25 +0200 +Subject: [PATCH] Extend the advice printing code by some useful abstractions + +The advise printing code was augmented by methods that simplify +generating bash snippets that report errors or failed commands. + +https://pagure.io/freeipa/issue/6982 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/advise/base.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 61 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index 40dabd0426719c5a791fee5be81a998e1c45854b..ba412b872472580cd32baf2a326a14edb951cab1 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -94,8 +94,67 @@ class _AdviceOutput(object): + if self.options.verbose: + self.comment('DEBUG: ' + line) + +- def command(self, line): +- self.content.append(line) ++ def command(self, line, indent_spaces=0): ++ self.content.append( ++ '{}{}'.format(self._format_indent(indent_spaces), line)) ++ ++ def _format_indent(self, num_spaces): ++ return ' ' * num_spaces ++ ++ def echo_error(self, error_message, indent_spaces=0): ++ self.command( ++ self._format_error(error_message), indent_spaces=indent_spaces) ++ ++ def _format_error(self, error_message): ++ return 'echo "{}" >&2'.format(error_message) ++ ++ def exit_on_failed_command(self, command_to_run, ++ error_message_lines, indent_spaces=0): ++ self.command(command_to_run, indent_spaces=indent_spaces) ++ self.exit_on_predicate( ++ '[ "$?" -ne "0" ]', ++ error_message_lines, ++ indent_spaces=indent_spaces) ++ ++ def exit_on_nonroot_euid(self): ++ self.exit_on_predicate( ++ '[ "$(id -u)" -ne "0" ]', ++ ["This script has to be run as root user"] ++ ) ++ ++ def exit_on_predicate(self, predicate, error_message_lines, ++ indent_spaces=0): ++ commands_to_run = [ ++ self._format_error(error_message_line) ++ for error_message_line in error_message_lines] ++ ++ commands_to_run.append('exit 1') ++ self.commands_on_predicate( ++ predicate, ++ commands_to_run, ++ indent_spaces=indent_spaces) ++ ++ def commands_on_predicate(self, predicate, commands_to_run_when_true, ++ commands_to_run_when_false=None, ++ indent_spaces=0): ++ if_command = 'if {}'.format(predicate) ++ self.command(if_command, indent_spaces=indent_spaces) ++ self.command('then', indent_spaces=indent_spaces) ++ ++ indented_block_spaces = indent_spaces + 2 ++ ++ for command_to_run_when_true in commands_to_run_when_true: ++ self.command( ++ command_to_run_when_true, indent_spaces=indented_block_spaces) ++ ++ if commands_to_run_when_false is not None: ++ self.command("else", indent_spaces=indent_spaces) ++ for command_to_run_when_false in commands_to_run_when_false: ++ self.command( ++ command_to_run_when_false, ++ indent_spaces=indented_block_spaces) ++ ++ self.command('fi', indent_spaces=indent_spaces) + + + class Advice(Plugin): +-- +2.9.4 + diff --git a/SOURCES/0190-Prepare-advise-plugin-for-smart-card-auth-configurat.patch b/SOURCES/0190-Prepare-advise-plugin-for-smart-card-auth-configurat.patch new file mode 100644 index 0000000..b17a1e6 --- /dev/null +++ b/SOURCES/0190-Prepare-advise-plugin-for-smart-card-auth-configurat.patch @@ -0,0 +1,293 @@ +From f16d9533c7917a8a57a9148dee61df3b12a5e767 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 2 Jun 2017 18:36:29 +0200 +Subject: [PATCH] Prepare advise plugin for smart card auth configuration + +The plugin contains recipes for configuring Smart Card authentication +on FreeIPA server and enrolled client. + +https://www.freeipa.org/page/V4/Smartcard_authentication_ipa-advise_recipes +https://pagure.io/freeipa/issue/6982 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/advise/plugins/smart_card_auth.py | 266 ++++++++++++++++++++++++++++ + 1 file changed, 266 insertions(+) + create mode 100644 ipaserver/advise/plugins/smart_card_auth.py + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +new file mode 100644 +index 0000000000000000000000000000000000000000..5859e350939fdba0a8b258de5285dd10c7b3bc23 +--- /dev/null ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -0,0 +1,266 @@ ++# ++# Copyright (C) 2017 FreeIPA Contributors see COPYING for license ++# ++ ++from ipalib.plugable import Registry ++from ipaplatform.paths import paths ++from ipaserver.advise.base import Advice ++from ipaserver.install.httpinstance import NSS_OCSP_ENABLED ++ ++register = Registry() ++ ++ ++@register() ++class config_server_for_smart_card_auth(Advice): ++ """ ++ Configures smart card authentication via Kerberos (PKINIT) and for WebUI ++ """ ++ ++ description = ("Instructions for enabling Smart Card authentication on " ++ " a single FreeIPA server. Includes Apache configuration, " ++ "enabling PKINIT on KDC and configuring WebUI to accept " ++ "Smart Card auth requests. To enable the feature in the " ++ "whole topology you have to run the script on each master") ++ ++ nss_conf = paths.HTTPD_NSS_CONF ++ nss_ocsp_directive = 'NSSOCSP' ++ nss_nickname_directive = 'NSSNickname' ++ ++ def get_info(self): ++ self.log.exit_on_nonroot_euid() ++ self.check_ccache_not_empty() ++ self.check_hostname_is_in_masters() ++ self.resolve_ipaca_records() ++ self.enable_nss_ocsp() ++ self.mark_httpd_cert_as_trusted() ++ self.restart_httpd() ++ self.record_httpd_ocsp_status() ++ self.check_and_enable_pkinit() ++ self.enable_ok_to_auth_as_delegate_on_http_principal() ++ ++ def check_ccache_not_empty(self): ++ self.log.comment('Check whether the credential cache is not empty') ++ self.log.exit_on_failed_command( ++ 'klist', ++ [ ++ "Credential cache is empty", ++ 'Use kinit as privileged user to obtain Kerberos credentials' ++ ]) ++ ++ def check_hostname_is_in_masters(self): ++ self.log.comment('Check whether the host is IPA master') ++ self.log.exit_on_failed_command( ++ 'ipa server-find $(hostname -f)', ++ ["This script can be run on IPA master only"]) ++ ++ def resolve_ipaca_records(self): ++ ipa_domain_name = self.api.env.domain ++ ++ self.log.comment('make sure bind-utils are installed so that we can ' ++ 'dig for ipa-ca records') ++ self.log.exit_on_failed_command( ++ 'yum install -y bind-utils', ++ ['Failed to install bind-utils']) ++ ++ self.log.comment('make sure ipa-ca records are resolvable, ' ++ 'otherwise error out and instruct') ++ self.log.comment('the user to update the DNS infrastructure') ++ self.log.command('ipaca_records=$(dig +short ' ++ 'ipa-ca.{})'.format(ipa_domain_name)) ++ ++ self.log.exit_on_predicate( ++ '[ -z "$ipaca_records" ]', ++ [ ++ 'Can not resolve ipa-ca records for ${domain_name}', ++ 'Please make sure to update your DNS infrastructure with ', ++ 'ipa-ca record pointing to IP addresses of IPA CA masters' ++ ]) ++ ++ def enable_nss_ocsp(self): ++ self.log.comment('look for the OCSP directive in nss.conf') ++ self.log.comment(' if it is present, switch it on') ++ self.log.comment( ++ 'if it is absent, append it to the end of VirtualHost section') ++ predicate = self._interpolate_ocsp_directive_file_into_command( ++ "grep -q '{directive} ' {filename}") ++ ++ self.log.commands_on_predicate( ++ predicate, ++ [ ++ self._interpolate_ocsp_directive_file_into_command( ++ " sed -i.ipabkp -r " ++ "'s/^#*[[:space:]]*{directive}[[:space:]]+(on|off)$" ++ "/{directive} on/' {filename}") ++ ], ++ commands_to_run_when_false=[ ++ self._interpolate_ocsp_directive_file_into_command( ++ " sed -i.ipabkp '/<\/VirtualHost>/i {directive} on' " ++ "{filename}") ++ ] ++ ) ++ ++ def _interpolate_ocsp_directive_file_into_command(self, fmt_line): ++ return self._format_command( ++ fmt_line, self.nss_ocsp_directive, self.nss_conf) ++ ++ def _format_command(self, fmt_line, directive, filename): ++ return fmt_line.format(directive=directive, filename=filename) ++ ++ def mark_httpd_cert_as_trusted(self): ++ self.log.comment( ++ 'mark the HTTP certificate as trusted peer to avoid ' ++ 'chicken-egg startup issue') ++ self.log.command( ++ self._interpolate_nssnickname_directive_file_into_command( ++ "http_cert_nick=$(grep '{directive}' {filename} |" ++ " cut -f 2 -d ' ')")) ++ ++ self.log.exit_on_failed_command( ++ 'certutil -M -n $http_cert_nick -d "{}" -t "Pu,u,u"'.format( ++ paths.HTTPD_ALIAS_DIR), ++ ['Can not set trust flags on HTTP certificate']) ++ ++ def _interpolate_nssnickname_directive_file_into_command(self, fmt_line): ++ return self._format_command( ++ fmt_line, self.nss_nickname_directive, self.nss_conf) ++ ++ def restart_httpd(self): ++ self.log.comment('finally restart apache') ++ self.log.command('systemctl restart httpd') ++ ++ def record_httpd_ocsp_status(self): ++ self.log.comment('store the OCSP upgrade state') ++ self.log.command( ++ "python -c 'from ipaserver.install import sysupgrade; " ++ "sysupgrade.set_upgrade_state(\"httpd\", " ++ "\"{}\", True)'".format(NSS_OCSP_ENABLED)) ++ ++ def check_and_enable_pkinit(self): ++ self.log.comment('check whether PKINIT is configured on the master') ++ self.log.command( ++ "if ipa-pkinit-manage status | grep -q 'enabled'") ++ self.log.command('then') ++ self.log.command(' echo "PKINIT already enabled"') ++ self.log.command('else') ++ self.log.exit_on_failed_command( ++ 'ipa-pkinit-manage enable', ++ ['Failed to issue PKINIT certificates to local KDC'], ++ indent_spaces=2) ++ self.log.command('fi') ++ ++ def enable_ok_to_auth_as_delegate_on_http_principal(self): ++ self.log.comment('Enable OK-AS-DELEGATE flag on the HTTP principal') ++ self.log.comment('This enables smart card login to WebUI') ++ self.log.command( ++ 'output=$(ipa service-mod HTTP/$(hostname -f) ' ++ '--ok-to-auth-as-delegate=True 2>&1)') ++ self.log.exit_on_predicate( ++ '[ "$?" -ne "0" -a ' ++ '-z "$(echo $output | grep \'no modifications\')" ]', ++ ["Failed to set OK_AS_AUTH_AS_DELEGATE flag on HTTP principal"] ++ ) ++ ++ ++@register() ++class config_client_for_smart_card_auth(Advice): ++ """ ++ Configures smart card authentication on FreeIPA client ++ """ ++ smart_card_ca_cert_variable_name = "SC_CA_CERT" ++ ++ description = ("Instructions for enabling Smart Card authentication on " ++ " a single FreeIPA client. Configures Smart Card daemon, " ++ "set the system-wide trust store and configures SSSD to " ++ "allow smart card logins to desktop") ++ ++ opensc_module_name = "OpenSC" ++ pkcs11_shared_lib = '/usr/lib64/opensc-pkcs11.so' ++ smart_card_service_file = 'pcscd.service' ++ smart_card_socket = 'pcscd.socket' ++ systemwide_nssdb = paths.NSS_DB_DIR ++ ++ def get_info(self): ++ self.log.exit_on_nonroot_euid() ++ self.check_and_set_ca_cert_path() ++ self.check_and_remove_pam_pkcs11() ++ self.install_opensc_and_dconf_packages() ++ self.start_enable_smartcard_daemon() ++ self.add_pkcs11_module_to_systemwide_db() ++ self.upload_smartcard_ca_certificate_to_systemwide_db() ++ self.run_authconfig_to_configure_smart_card_auth() ++ self.restart_sssd() ++ ++ def check_and_set_ca_cert_path(self): ++ ca_path_variable = self.smart_card_ca_cert_variable_name ++ self.log.command("{}=$1".format(ca_path_variable)) ++ self.log.exit_on_predicate( ++ '[ -z "${}" ]'.format(ca_path_variable), ++ ['You need to provide the path to the PEM file containing CA ' ++ 'signing the Smart Cards'] ++ ) ++ self.log.exit_on_predicate( ++ '[ ! -f "${}" ]'.format(ca_path_variable), ++ ['Invalid CA certificate filename: ${}'.format(ca_path_variable), ++ 'Please check that the path exists and is a valid file'] ++ ) ++ ++ def check_and_remove_pam_pkcs11(self): ++ self.log.command('rpm -qi pam_pkcs11 > /dev/null') ++ self.log.commands_on_predicate( ++ '[ "$?" -eq "0" ]', ++ [ ++ 'yum remove -y pam_pkcs11' ++ ] ++ ) ++ ++ def install_opensc_and_dconf_packages(self): ++ self.log.comment( ++ 'authconfig often complains about missing dconf, ' ++ 'install it explicitly') ++ self.log.exit_on_failed_command( ++ 'yum install -y {} dconf'.format(self.opensc_module_name.lower()), ++ ['Could not install OpenSC package'] ++ ) ++ ++ def start_enable_smartcard_daemon(self): ++ self.log.command( ++ 'systemctl start {service} {socket} ' ++ '&& systemctl enable {service} {socket}'.format( ++ service=self.smart_card_service_file, ++ socket=self.smart_card_socket)) ++ ++ def add_pkcs11_module_to_systemwide_db(self): ++ module_name = self.opensc_module_name ++ nssdb = self.systemwide_nssdb ++ shared_lib = self.pkcs11_shared_lib ++ ++ self.log.commands_on_predicate( ++ 'modutil -dbdir {} -list | grep -q {}'.format( ++ nssdb, module_name), ++ [ ++ 'echo "{} PKCS#11 module already configured"'.format( ++ module_name) ++ ], ++ commands_to_run_when_false=[ ++ 'echo "" | modutil -dbdir {} -add "{}" -libfile {}'.format( ++ nssdb, module_name, shared_lib), ++ ] ++ ) ++ ++ def upload_smartcard_ca_certificate_to_systemwide_db(self): ++ self.log.command( ++ 'certutil -d {} -A -i ${} -n "Smart Card CA" -t CT,C,C'.format( ++ self.systemwide_nssdb, self.smart_card_ca_cert_variable_name ++ ) ++ ) ++ ++ def run_authconfig_to_configure_smart_card_auth(self): ++ self.log.exit_on_failed_command( ++ 'authconfig --enablesmartcard --smartcardmodule=sssd --updateall', ++ [ ++ 'Failed to configure Smart Card authentication in SSSD' ++ ] ++ ) ++ ++ def restart_sssd(self): ++ self.log.command('systemctl restart sssd.service') +-- +2.9.4 + diff --git a/SOURCES/0191-trust-mod-allow-modifying-list-of-UPNs-of-a-trusted-.patch b/SOURCES/0191-trust-mod-allow-modifying-list-of-UPNs-of-a-trusted-.patch new file mode 100644 index 0000000..80a1388 --- /dev/null +++ b/SOURCES/0191-trust-mod-allow-modifying-list-of-UPNs-of-a-trusted-.patch @@ -0,0 +1,93 @@ +From 4df20255c1738526696ea72af6fc70e9c6aa6694 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Mon, 12 Jun 2017 11:05:06 +0300 +Subject: [PATCH] trust-mod: allow modifying list of UPNs of a trusted forest + +There are two ways for maintaining user principal names (UPNs) in Active +Directory: + - associate UPN suffixes with the forest root and then allow for each + user account to choose UPN suffix for logon + - directly modify userPrincipalName attribute in LDAP + +Both approaches lead to the same result: AD DC accepts user@UPN-Suffix +as a proper principal in AS-REQ and TGS-REQ. + +The latter (directly modify userPrincipalName) case has a consequence +that this UPN suffix is not visible via netr_DsRGetForestTrustInformation +DCE RPC call. As result, FreeIPA KDC will not know that a particular UPN +suffix does belong to a trusted Active Directory forest. As result, SSSD +will not be able to authenticate and validate this user from a trusted +Active Directory forest. + +This is especially true for one-word UPNs which otherwise wouldn't work +properly on Kerberos level for both FreeIPA and Active Directory. + +Administrators are responsible for amending the list of UPNs associated +with the forest in this case. With this commit, an option is added to +'ipa trust-mod' that allows specifying arbitrary UPN suffixes to a +trusted forest root. + +As with all '-mod' commands, the change replaces existing UPNs when +applied, so administrators are responsible to specify all of them: + + ipa trust-mod ad.test --upn-suffixes={existing.upn,another_upn,new} + +Fixes: https://pagure.io/freeipa/issue/7015 +Reviewed-By: Martin Babinsky +--- + API.txt | 3 ++- + VERSION.m4 | 4 ++-- + ipaserver/plugins/trust.py | 3 ++- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/API.txt b/API.txt +index 6511ad8d1cb4dc9079628fc058312f31aaec624d..86229156adfba829a46b6a831ad6843cc1a17d6a 100644 +--- a/API.txt ++++ b/API.txt +@@ -5769,11 +5769,12 @@ output: ListOfEntries('result') + output: Output('summary', type=[, ]) + output: Output('truncated', type=[]) + command: trust_mod/1 +-args: 1,9,3 ++args: 1,10,3 + arg: Str('cn', cli_name='realm') + option: Str('addattr*', cli_name='addattr') + option: Flag('all', autofill=True, cli_name='all', default=False) + option: Str('delattr*', cli_name='delattr') ++option: Str('ipantadditionalsuffixes*', autofill=False, cli_name='upn_suffixes') + option: Str('ipantsidblacklistincoming*', autofill=False, cli_name='sid_blacklist_incoming') + option: Str('ipantsidblacklistoutgoing*', autofill=False, cli_name='sid_blacklist_outgoing') + option: Flag('raw', autofill=True, cli_name='raw', default=False) +diff --git a/VERSION.m4 b/VERSION.m4 +index 8aa3ef03f352cd176579c5d5848ed9550f22105d..25aaa1dd0e3c2868e63300dec7fe9228f1ebcb43 100644 +--- a/VERSION.m4 ++++ b/VERSION.m4 +@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000) + # # + ######################################################## + define(IPA_API_VERSION_MAJOR, 2) +-define(IPA_API_VERSION_MINOR, 227) +-# Last change: Add `pkinit-status` command ++define(IPA_API_VERSION_MINOR, 228) ++# Last change: Expose ipaNTAdditionalSuffixes in trust-mod + + + ######################################################## +diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py +index 075b39dcc33a79f3e73e8e1e9e31ebbef17618fe..d0bbfbc47ca65c9c5229685fc9d202c293fe41cd 100644 +--- a/ipaserver/plugins/trust.py ++++ b/ipaserver/plugins/trust.py +@@ -553,8 +553,9 @@ class trust(LDAPObject): + flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'}, + ), + Str('ipantadditionalsuffixes*', ++ cli_name='upn_suffixes', + label=_('UPN suffixes'), +- flags={'no_create', 'no_update', 'no_search'}, ++ flags={'no_create', 'no_search'}, + ), + ) + +-- +2.9.4 + diff --git a/SOURCES/0192-WebUI-add-support-for-changing-trust-UPN-suffixes.patch b/SOURCES/0192-WebUI-add-support-for-changing-trust-UPN-suffixes.patch new file mode 100644 index 0000000..2e188cd --- /dev/null +++ b/SOURCES/0192-WebUI-add-support-for-changing-trust-UPN-suffixes.patch @@ -0,0 +1,32 @@ +From 4ec1a4fd564bff73c37810bd5326c9382e3a26bf Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Mon, 12 Jun 2017 13:18:44 +0200 +Subject: [PATCH] WebUI: add support for changing trust UPN suffixes + +It is now possible to change UPN suffixes in WebUI. This change +allows another way to changing UPN suffixes for AD users. + +https://pagure.io/freeipa/issue/7015 + +Reviewed-By: Alexander Bokovoy +--- + install/ui/src/freeipa/trust.js | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/install/ui/src/freeipa/trust.js b/install/ui/src/freeipa/trust.js +index 39e79ebef1c1fb3cb730d9304c46862ec0d0e651..ff019cab3070312b90e14e08a9070c353b1e7f71 100644 +--- a/install/ui/src/freeipa/trust.js ++++ b/install/ui/src/freeipa/trust.js +@@ -147,8 +147,7 @@ return { + fields: [ + { + $type: 'multivalued', +- name: 'ipantadditionalsuffixes', +- read_only: true ++ name: 'ipantadditionalsuffixes' + } + ] + }, +-- +2.9.4 + diff --git a/SOURCES/0193-kra-promote-Get-ticket-before-calling-custodia.patch b/SOURCES/0193-kra-promote-Get-ticket-before-calling-custodia.patch new file mode 100644 index 0000000..9e09b32 --- /dev/null +++ b/SOURCES/0193-kra-promote-Get-ticket-before-calling-custodia.patch @@ -0,0 +1,59 @@ +From efd08380bbdda59a63afd584bc4c0ef3426b14ce Mon Sep 17 00:00:00 2001 +From: David Kupka +Date: Wed, 14 Jun 2017 15:39:58 +0200 +Subject: [PATCH] kra: promote: Get ticket before calling custodia + +When installing second (or consequent) KRA instance keys are retrieved +using custodia. Custodia checks that the keys are synchronized in +master's directory server and the check uses GSSAPI and therefore fails +if there's no ticket in ccache. + +https://pagure.io/freeipa/issue/7020 + +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/kra.py | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py +index f3454061280661d7b0fc2899142da9dc8783841a..3545b301a977f4b7e7801ca1ef87d594bb3ba54f 100644 +--- a/ipaserver/install/kra.py ++++ b/ipaserver/install/kra.py +@@ -10,6 +10,7 @@ import os + import shutil + + from ipalib import api ++from ipalib.install.kinit import kinit_keytab + from ipaplatform import services + from ipaplatform.paths import paths + from ipapython import certdb +@@ -84,13 +85,19 @@ def install(api, replica_config, options): + return + krafile = os.path.join(replica_config.dir, 'kracert.p12') + if options.promote: +- custodia = custodiainstance.CustodiaInstance( +- replica_config.host_name, +- replica_config.realm_name) +- custodia.get_kra_keys( +- replica_config.kra_host_name, +- krafile, +- replica_config.dirman_password) ++ with ipautil.private_ccache(): ++ ccache = os.environ['KRB5CCNAME'] ++ kinit_keytab( ++ 'host/{env.host}@{env.realm}'.format(env=api.env), ++ paths.KRB5_KEYTAB, ++ ccache) ++ custodia = custodiainstance.CustodiaInstance( ++ replica_config.host_name, ++ replica_config.realm_name) ++ custodia.get_kra_keys( ++ replica_config.kra_host_name, ++ krafile, ++ replica_config.dirman_password) + else: + cafile = os.path.join(replica_config.dir, 'cacert.p12') + if not ipautil.file_exists(cafile): +-- +2.9.4 + diff --git a/SOURCES/0194-Fix-local-IP-address-validation.patch b/SOURCES/0194-Fix-local-IP-address-validation.patch new file mode 100644 index 0000000..154d20c --- /dev/null +++ b/SOURCES/0194-Fix-local-IP-address-validation.patch @@ -0,0 +1,45 @@ +From 935029c3192221c480c88b870a507cfac4c4b954 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Tue, 13 Jun 2017 17:03:30 +0200 +Subject: [PATCH] Fix local IP address validation + +Previously bf9886a84393d1d1546db7e49b102e08a16a83e7 match_local has +undesirable side effect that CheckedIPAddress object has set self._net +from local interface. + +However with the recent changes, match_local is usually set to False, +thus this side effect stops happening and default mask per address class +is used. This causes validation error because mask on interface and mask +used for provided IP addresses differ (reporducible only with classless +masks). + +FreeIPA should compare only IP addresses with local addresses without masks + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipapython/ipautil.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index a277ed87473f3c591f34fcc00e1159f3bbfe3e9b..647ee833ae33f246de6d6b13703fac6e20eef7bc 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -216,10 +216,10 @@ class CheckedIPAddress(UnsafeIPAddress): + addr=ifaddr, + netmask=ifdata['netmask'] + )) +- if ifnet == self._net or ( +- self._net is None and ifnet.ip == self): +- self._net = ifnet ++ ++ if ifnet.ip == self: + iface = interface ++ self._net = ifnet + break + + return iface +-- +2.9.4 + diff --git a/SOURCES/0195-ipa-dns-install-remove-check-for-local-ip-address.patch b/SOURCES/0195-ipa-dns-install-remove-check-for-local-ip-address.patch new file mode 100644 index 0000000..adf7bf9 --- /dev/null +++ b/SOURCES/0195-ipa-dns-install-remove-check-for-local-ip-address.patch @@ -0,0 +1,32 @@ +From 09fad2745b3f49192d6d43550ccb82145f223be0 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 14 Jun 2017 14:45:03 +0200 +Subject: [PATCH] ipa-dns-install: remove check for local ip address + +This check was forgotten and will be removed now. + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + install/tools/ipa-dns-install | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install +index 5bd0ba6d77335d9fa32ea4269422cb3dd1cfca4a..cb6c5d887f101135ca593ea6d4ed0caf51478a4c 100755 +--- a/install/tools/ipa-dns-install ++++ b/install/tools/ipa-dns-install +@@ -47,7 +47,9 @@ def parse_options(): + default=False, help="print debugging information") + parser.add_option("--ip-address", dest="ip_addresses", metavar="IP_ADDRESS", + default=[], action="append", +- type="ip", ip_local=True, help="Master Server IP Address. This option can be used multiple times") ++ type="ip", ++ help="Master Server IP Address. This option can be used " ++ "multiple times") + parser.add_option("--forwarder", dest="forwarders", action="append", + type="ip", help="Add a DNS forwarder. This option can be used multiple times") + parser.add_option("--no-forwarders", dest="no_forwarders", action="store_true", +-- +2.9.4 + diff --git a/SOURCES/0196-refactor-CheckedIPAddress-class.patch b/SOURCES/0196-refactor-CheckedIPAddress-class.patch new file mode 100644 index 0000000..7c1d745 --- /dev/null +++ b/SOURCES/0196-refactor-CheckedIPAddress-class.patch @@ -0,0 +1,88 @@ +From 8d88b50c3f79e054a039d123fdaf6aa3a5339135 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 14 Jun 2017 14:47:23 +0200 +Subject: [PATCH] refactor CheckedIPAddress class + +Make methods without side effects (setting mask) + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipapython/ipautil.py | 29 ++++++++++++++++++++++------- + 1 file changed, 22 insertions(+), 7 deletions(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 647ee833ae33f246de6d6b13703fac6e20eef7bc..2c020e3ecbf4d8b969511a6dd9b36ee955ba1f15 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -62,6 +62,12 @@ PROTOCOL_NAMES = { + socket.SOCK_DGRAM: 'udp' + } + ++InterfaceDetails = collections.namedtuple( ++ 'InterfaceDetails', [ ++ 'name', # interface name ++ 'ifnet' # network details of interface ++ ]) ++ + + class UnsafeIPAddress(netaddr.IPAddress): + """Any valid IP address with or without netmask.""" +@@ -161,9 +167,12 @@ class CheckedIPAddress(UnsafeIPAddress): + raise ValueError("cannot use multicast IP address {}".format(addr)) + + if match_local: +- if not self.get_matching_interface(): ++ intf_details = self.get_matching_interface() ++ if not intf_details: + raise ValueError('no network interface matches the IP address ' + 'and netmask {}'.format(addr)) ++ else: ++ self.set_ip_net(intf_details.ifnet) + + if self._net is None: + if self.version == 4: +@@ -193,7 +202,8 @@ class CheckedIPAddress(UnsafeIPAddress): + + def get_matching_interface(self): + """Find matching local interface for address +- :return: Interface name or None if no interface has this address ++ :return: InterfaceDetails named tuple or None if no interface has ++ this address + """ + if self.version == 4: + family = netifaces.AF_INET +@@ -204,7 +214,6 @@ class CheckedIPAddress(UnsafeIPAddress): + "Unsupported address family ({})".format(self.version) + ) + +- iface = None + for interface in netifaces.interfaces(): + for ifdata in netifaces.ifaddresses(interface).get(family, []): + +@@ -218,11 +227,17 @@ class CheckedIPAddress(UnsafeIPAddress): + )) + + if ifnet.ip == self: +- iface = interface +- self._net = ifnet +- break ++ return InterfaceDetails(interface, ifnet) + +- return iface ++ def set_ip_net(self, ifnet): ++ """Set IP Network details for this address. IPNetwork is valid only ++ locally, so this should be set only for local IP addresses ++ ++ :param ifnet: netaddr.IPNetwork object with information about IP ++ network where particula address belongs locally ++ """ ++ assert isinstance(ifnet, netaddr.IPNetwork) ++ self._net = ifnet + + + def valid_ip(addr): +-- +2.9.4 + diff --git a/SOURCES/0197-CheckedIPAddress-remove-match_local-param.patch b/SOURCES/0197-CheckedIPAddress-remove-match_local-param.patch new file mode 100644 index 0000000..6ed40b5 --- /dev/null +++ b/SOURCES/0197-CheckedIPAddress-remove-match_local-param.patch @@ -0,0 +1,141 @@ +From c4482f0441e610c077d550c10b9525f97ea2f984 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 14 Jun 2017 14:54:43 +0200 +Subject: [PATCH] CheckedIPAddress: remove match_local param + +This parameter is unused in code. We are no longer testing if IP address +matches an interface in constructor. + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipapython/config.py | 5 ++--- + ipapython/ipautil.py | 10 +--------- + ipaserver/install/installutils.py | 2 +- + ipaserver/plugins/dns.py | 4 ++-- + ipaserver/plugins/host.py | 2 +- + ipatests/test_ipapython/test_ipautil.py | 3 +-- + 6 files changed, 8 insertions(+), 18 deletions(-) + +diff --git a/ipapython/config.py b/ipapython/config.py +index 9db2dcd4dbf3ed0fbe3e3166c3f0c5bae0f1716b..6349892fe88757629129f464401efce64e30f058 100644 +--- a/ipapython/config.py ++++ b/ipapython/config.py +@@ -68,10 +68,9 @@ class IPAFormatter(IndentedHelpFormatter): + def check_ip_option(option, opt, value): + from ipapython.ipautil import CheckedIPAddress + +- ip_local = option.ip_local is True + ip_netmask = option.ip_netmask is True + try: +- return CheckedIPAddress(value, parse_netmask=ip_netmask, match_local=ip_local) ++ return CheckedIPAddress(value, parse_netmask=ip_netmask) + except Exception as e: + raise OptionValueError("option %s: invalid IP address %s: %s" % (opt, value, e)) + +@@ -86,7 +85,7 @@ class IPAOption(Option): + optparse.Option subclass with support of options labeled as + security-sensitive such as passwords. + """ +- ATTRS = Option.ATTRS + ["sensitive", "ip_local", "ip_netmask"] ++ ATTRS = Option.ATTRS + ["sensitive", "ip_netmask"] + TYPES = Option.TYPES + ("ip", "dn") + TYPE_CHECKER = copy(Option.TYPE_CHECKER) + TYPE_CHECKER["ip"] = check_ip_option +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 2c020e3ecbf4d8b969511a6dd9b36ee955ba1f15..5a6bf5a27d5a6e25c51fbaa6e2b1167652e2735d 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -135,7 +135,7 @@ class CheckedIPAddress(UnsafeIPAddress): + + Reserved or link-local addresses are never accepted. + """ +- def __init__(self, addr, match_local=False, parse_netmask=True, ++ def __init__(self, addr, parse_netmask=True, + allow_loopback=False, allow_multicast=False): + try: + super(CheckedIPAddress, self).__init__(addr) +@@ -166,14 +166,6 @@ class CheckedIPAddress(UnsafeIPAddress): + if not allow_multicast and self.is_multicast(): + raise ValueError("cannot use multicast IP address {}".format(addr)) + +- if match_local: +- intf_details = self.get_matching_interface() +- if not intf_details: +- raise ValueError('no network interface matches the IP address ' +- 'and netmask {}'.format(addr)) +- else: +- self.set_ip_net(intf_details.ifnet) +- + if self._net is None: + if self.version == 4: + self._net = netaddr.IPNetwork( +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 3521d555914714351160213df60ed9167ac6e370..01930c4de6f0edd16b31aeba1c926fe581e9635b 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -585,7 +585,7 @@ def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses): + if len(hostaddr): + for ha in hostaddr: + try: +- ips.append(ipautil.CheckedIPAddress(ha, match_local=False)) ++ ips.append(ipautil.CheckedIPAddress(ha)) + except ValueError as e: + root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e)) + +diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py +index f0e6c48f06313def57cdd6a4c7114357c9d8de8a..f01baf515525c43824eddf06abc7af9fef228efe 100644 +--- a/ipaserver/plugins/dns.py ++++ b/ipaserver/plugins/dns.py +@@ -567,7 +567,7 @@ def add_records_for_host_validation(option_name, host, domain, ip_addresses, che + for ip_address in ip_addresses: + try: + ip = CheckedIPAddress( +- ip_address, match_local=False, allow_multicast=True) ++ ip_address, allow_multicast=True) + except Exception as e: + raise errors.ValidationError(name=option_name, error=unicode(e)) + +@@ -599,7 +599,7 @@ def add_records_for_host(host, domain, ip_addresses, add_forward=True, add_rever + + for ip_address in ip_addresses: + ip = CheckedIPAddress( +- ip_address, match_local=False, allow_multicast=True) ++ ip_address, allow_multicast=True) + + if add_forward: + add_forward_record(domain, host, unicode(ip)) +diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py +index 1e1f9d82dfdfcf9e7fef65ce729cd8ee7b76e605..364e5be6002eeb9c5e6b4c594b71f38169598227 100644 +--- a/ipaserver/plugins/host.py ++++ b/ipaserver/plugins/host.py +@@ -245,7 +245,7 @@ def validate_ipaddr(ugettext, ipaddr): + Verify that we have either an IPv4 or IPv6 address. + """ + try: +- CheckedIPAddress(ipaddr, match_local=False) ++ CheckedIPAddress(ipaddr) + except Exception as e: + return unicode(e) + return None +diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py +index 6427935b162b087c55e069cb2a576a7379cbe7a7..9c351bd0ed9cd96488ac74deadf97996668a75d2 100644 +--- a/ipatests/test_ipapython/test_ipautil.py ++++ b/ipatests/test_ipapython/test_ipautil.py +@@ -30,11 +30,10 @@ from ipapython import ipautil + + pytestmark = pytest.mark.tier0 + +- + def make_ipaddress_checker(addr, words=None, prefixlen=None): + def check_ipaddress(): + try: +- ip = ipautil.CheckedIPAddress(addr, match_local=False) ++ ip = ipautil.CheckedIPAddress(addr) + assert ip.words == words and ip.prefixlen == prefixlen + except Exception: + assert words is None and prefixlen is None +-- +2.9.4 + diff --git a/SOURCES/0198-Remove-ip_netmask-from-option-parser.patch b/SOURCES/0198-Remove-ip_netmask-from-option-parser.patch new file mode 100644 index 0000000..38924ef --- /dev/null +++ b/SOURCES/0198-Remove-ip_netmask-from-option-parser.patch @@ -0,0 +1,44 @@ +From dd004f9393f0543c556d417d27b9f652b6fe4c99 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Wed, 14 Jun 2017 15:02:21 +0200 +Subject: [PATCH] Remove ip_netmask from option parser + +ipa-dns-install uses ip_netmask=False --> parse_netmask=False, other installers uses default (parse_netmask=True). +Use this consistent accross all installers. + +Also this option is unused (and shouldn't be used). + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipapython/config.py | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/ipapython/config.py b/ipapython/config.py +index 6349892fe88757629129f464401efce64e30f058..19abfc51ee354d2971be836fa6bad70eea3a6720 100644 +--- a/ipapython/config.py ++++ b/ipapython/config.py +@@ -68,9 +68,8 @@ class IPAFormatter(IndentedHelpFormatter): + def check_ip_option(option, opt, value): + from ipapython.ipautil import CheckedIPAddress + +- ip_netmask = option.ip_netmask is True + try: +- return CheckedIPAddress(value, parse_netmask=ip_netmask) ++ return CheckedIPAddress(value) + except Exception as e: + raise OptionValueError("option %s: invalid IP address %s: %s" % (opt, value, e)) + +@@ -85,7 +84,7 @@ class IPAOption(Option): + optparse.Option subclass with support of options labeled as + security-sensitive such as passwords. + """ +- ATTRS = Option.ATTRS + ["sensitive", "ip_netmask"] ++ ATTRS = Option.ATTRS + ["sensitive"] + TYPES = Option.TYPES + ("ip", "dn") + TYPE_CHECKER = copy(Option.TYPE_CHECKER) + TYPE_CHECKER["ip"] = check_ip_option +-- +2.9.4 + diff --git a/SOURCES/0199-replica-install-add-missing-check-for-non-local-IP-a.patch b/SOURCES/0199-replica-install-add-missing-check-for-non-local-IP-a.patch new file mode 100644 index 0000000..a47f9c4 --- /dev/null +++ b/SOURCES/0199-replica-install-add-missing-check-for-non-local-IP-a.patch @@ -0,0 +1,29 @@ +From 56c6b8432a50c7b857303ffc4345c75171e0ac92 Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Thu, 15 Jun 2017 10:26:03 +0200 +Subject: [PATCH] replica install: add missing check for non-local IP address + +Add missing warning for used non-local IP address. + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipaserver/install/server/replicainstall.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 6620f0222f9d38112ce0d0fd72381e5673921cba..9e328bf83bbdb2883ba823cb098b70eeaa078403 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -854,6 +854,7 @@ def install_check(installer): + # check addresses here, dns module is doing own check + network_ip_address_warning(config.ips) + broadcast_ip_address_warning(config.ips) ++ no_matching_interface_for_ip_address_warning(config.ips) + + if options.setup_adtrust: + adtrust.install_check(False, options, remote_api) +-- +2.9.4 + diff --git a/SOURCES/0200-Remove-network-and-broadcast-address-warnings.patch b/SOURCES/0200-Remove-network-and-broadcast-address-warnings.patch new file mode 100644 index 0000000..c5d440d --- /dev/null +++ b/SOURCES/0200-Remove-network-and-broadcast-address-warnings.patch @@ -0,0 +1,146 @@ +From 52be5b4d693febdc1fa1fe9d54b1d052a09c347f Mon Sep 17 00:00:00 2001 +From: Martin Basti +Date: Thu, 15 Jun 2017 10:27:55 +0200 +Subject: [PATCH] Remove network and broadcast address warnings + +We cannot reliably determine when an IP Address is network or broadcast. +We allowed to use non-local IP addresses due container use cases, we +don't know subnets of used IP addresses. + +https://pagure.io/freeipa/issue/4317 + +Reviewed-By: David Kupka +--- + ipaclient/install/client.py | 4 ---- + ipalib/util.py | 20 -------------------- + ipaserver/install/dns.py | 2 -- + ipaserver/install/server/install.py | 4 ---- + ipaserver/install/server/replicainstall.py | 10 +--------- + 5 files changed, 1 insertion(+), 39 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 41dae3004d1f4836e79c2048ae0a12f722595ca0..6242c19636168a5b2922f6f6f0e8bc8aa9b4bc80 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -38,8 +38,6 @@ from ipalib.install.kinit import kinit_keytab, kinit_password + from ipalib.install.service import enroll_only, prepare_only + from ipalib.rpc import delete_persistent_client_session_data + from ipalib.util import ( +- broadcast_ip_address_warning, +- network_ip_address_warning, + normalize_hostname, + no_matching_interface_for_ip_address_warning, + verify_host_resolvable, +@@ -1299,8 +1297,6 @@ def update_dns(server, hostname, options): + root_logger.info("Failed to determine this machine's ip address(es).") + return + +- network_ip_address_warning(update_ips) +- broadcast_ip_address_warning(update_ips) + no_matching_interface_for_ip_address_warning(update_ips) + + update_txt = "debug\n" +diff --git a/ipalib/util.py b/ipalib/util.py +index 1bd8495a49b010e7a3ac926dad516ab5f8219b39..31e73230da49a47e8e0fbcba9934f13cef16460e 100644 +--- a/ipalib/util.py ++++ b/ipalib/util.py +@@ -1110,26 +1110,6 @@ def check_principal_realm_in_trust_namespace(api_instance, *keys): + 'namespace')) + + +-def network_ip_address_warning(addr_list): +- for ip in addr_list: +- if ip.is_network_addr(): +- root_logger.warning("IP address %s might be network address", ip) +- # fixme: once when loggers will be fixed, we can remove this +- # print +- print("WARNING: IP address {} might be network address".format(ip), +- file=sys.stderr) +- +- +-def broadcast_ip_address_warning(addr_list): +- for ip in addr_list: +- if ip.is_broadcast_addr(): +- root_logger.warning("IP address %s might be broadcast address", ip) +- # fixme: once when loggers will be fixed, we can remove this +- # print +- print("WARNING: IP address {} might be broadcast address".format( +- ip), file=sys.stderr) +- +- + def no_matching_interface_for_ip_address_warning(addr_list): + for ip in addr_list: + if not ip.get_matching_interface(): +diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py +index 090b79493652566a433da248fa7fd9e33dd2cb72..1c1aac06a18fe3c1f63b5881c7887f6a4cfc9ac2 100644 +--- a/ipaserver/install/dns.py ++++ b/ipaserver/install/dns.py +@@ -264,8 +264,6 @@ def install_check(standalone, api, replica, options, hostname): + ip_addresses = get_server_ip_address(hostname, options.unattended, + True, options.ip_addresses) + +- util.network_ip_address_warning(ip_addresses) +- util.broadcast_ip_address_warning(ip_addresses) + util.no_matching_interface_for_ip_address_warning(ip_addresses) + + if not options.forward_policy: +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 7eb291e07c00e0407ce534c3d4088e6f6378260f..dced253e7f039dc9d66466bf8bcd777e53919f54 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -27,8 +27,6 @@ from ipalib import api, errors, x509 + from ipalib.constants import DOMAIN_LEVEL_0 + from ipalib.util import ( + validate_domain_name, +- network_ip_address_warning, +- broadcast_ip_address_warning, + no_matching_interface_for_ip_address_warning, + ) + import ipaclient.install.ntpconf +@@ -616,8 +614,6 @@ def install_check(installer): + options.ip_addresses) + + # check addresses here, dns module is doing own check +- network_ip_address_warning(ip_addresses) +- broadcast_ip_address_warning(ip_addresses) + no_matching_interface_for_ip_address_warning(ip_addresses) + + if options.setup_adtrust: +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 9e328bf83bbdb2883ba823cb098b70eeaa078403..4f28de25bd0adf958187c19edf90de4ba57dd98e 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -32,11 +32,7 @@ from ipaplatform.tasks import tasks + from ipaplatform.paths import paths + from ipalib import api, constants, create_api, errors, rpc, x509 + from ipalib.config import Env +-from ipalib.util import ( +- network_ip_address_warning, +- broadcast_ip_address_warning, +- no_matching_interface_for_ip_address_warning, +-) ++from ipalib.util import no_matching_interface_for_ip_address_warning + from ipaclient.install.client import configure_krb5_conf, purge_host_keytab + from ipaserver.install import ( + adtrust, bindinstance, ca, certs, dns, dsinstance, httpinstance, +@@ -852,8 +848,6 @@ def install_check(installer): + options.ip_addresses) + + # check addresses here, dns module is doing own check +- network_ip_address_warning(config.ips) +- broadcast_ip_address_warning(config.ips) + no_matching_interface_for_ip_address_warning(config.ips) + + if options.setup_adtrust: +@@ -1285,8 +1279,6 @@ def promote_check(installer): + False, options.ip_addresses) + + # check addresses here, dns module is doing own check +- network_ip_address_warning(config.ips) +- broadcast_ip_address_warning(config.ips) + no_matching_interface_for_ip_address_warning(config.ips) + + if options.setup_adtrust: +-- +2.9.4 + diff --git a/SOURCES/0201-ipa-sam-replace-encode_nt_key-with-E_md4hash.patch b/SOURCES/0201-ipa-sam-replace-encode_nt_key-with-E_md4hash.patch new file mode 100644 index 0000000..06623f1 --- /dev/null +++ b/SOURCES/0201-ipa-sam-replace-encode_nt_key-with-E_md4hash.patch @@ -0,0 +1,80 @@ +From 4504cb10cb7bf489be5ce221358b237afc1e52ca Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 16 Jun 2017 16:26:41 +0200 +Subject: [PATCH] ipa-sam: replace encode_nt_key() with E_md4hash() + +Since ipa-sam is running as part of smbd is it safe to use the +E_md4hash() from Samba. This way ipa-sam does not depend on other crypto +libraries which might depend on other rules like e.g. FIPS mode. + +Resolves https://pagure.io/freeipa/issue/7026 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Stanislav Laznicka +--- + daemons/ipa-sam/ipa_sam.c | 27 ++------------------------- + 1 file changed, 2 insertions(+), 25 deletions(-) + +diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c +index 6a29e8e10b4299356b9ead76276eecc8083791a3..59d92f37c9b7104c2fba5bd530b4dbff3ca675db 100644 +--- a/daemons/ipa-sam/ipa_sam.c ++++ b/daemons/ipa-sam/ipa_sam.c +@@ -110,6 +110,7 @@ char *sid_string_dbg(const struct dom_sid *sid); /* available in libsmbconf.so * + char *escape_ldap_string(TALLOC_CTX *mem_ctx, const char *s); /* available in libsmbconf.so */ + bool secrets_store(const char *key, const void *data, size_t size); /* available in libpdb.so */ + void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id); /* available in libsmbconf.so */ ++bool E_md4hash(const char *passwd, uint8_t p16[16]); /* available in libcliauth-samba4.so */ + + #define LDAP_OBJ_SAMBASAMACCOUNT "ipaNTUserAttrs" + #define LDAP_OBJ_TRUSTED_DOMAIN "ipaNTTrustedDomain" +@@ -2836,11 +2837,7 @@ static bool init_sam_from_td(struct samu *user, struct pdb_trusted_domain *td, + struct dom_sid *g_sid; + char *name; + char *trustpw = NULL; +- char *trustpw_utf8 = NULL; +- char *tmp_str = NULL; +- int ret; + uint8_t nt_key[16]; +- size_t converted_size; + bool res; + char *sid_str; + enum idmap_error_code err; +@@ -2899,19 +2896,7 @@ static bool init_sam_from_td(struct samu *user, struct pdb_trusted_domain *td, + return false; + } + +- if (!push_utf8_talloc(user, &trustpw_utf8, trustpw, &converted_size)) { +- res = false; +- goto done; +- } +- +- tmp_str = talloc_strdup_upper(user, trustpw); +- if (tmp_str == NULL) { +- res = false; +- goto done; +- } +- +- ret = encode_nt_key(trustpw_utf8, nt_key); +- if (ret != 0) { ++ if (!E_md4hash(trustpw, nt_key)) { + res = false; + goto done; + } +@@ -2927,14 +2912,6 @@ done: + memset(trustpw, 0, strlen(trustpw)); + talloc_free(trustpw); + } +- if (trustpw_utf8 != NULL) { +- memset(trustpw_utf8, 0, strlen(trustpw_utf8)); +- talloc_free(trustpw_utf8); +- } +- if (tmp_str != NULL) { +- memset(tmp_str, 0, strlen(tmp_str)); +- talloc_free(tmp_str); +- } + + return res; + } +-- +2.9.4 + diff --git a/SOURCES/0202-ipa_pwd_extop-do-not-generate-NT-hashes-in-FIPS-mode.patch b/SOURCES/0202-ipa_pwd_extop-do-not-generate-NT-hashes-in-FIPS-mode.patch new file mode 100644 index 0000000..32a5ffe --- /dev/null +++ b/SOURCES/0202-ipa_pwd_extop-do-not-generate-NT-hashes-in-FIPS-mode.patch @@ -0,0 +1,102 @@ +From 84be5dc9e72fbf4c85b6f061da94a4316c90d65e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 16 Jun 2017 17:49:44 +0200 +Subject: [PATCH] ipa_pwd_extop: do not generate NT hashes in FIPS mode + +In FIPS mode NT hashes (aka md4) are not allowed. If FIPS more is +detected we disable NT hashes even is the are allowed by IPA +configuration. + +Resolves https://pagure.io/freeipa/issue/7026 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Stanislav Laznicka +--- + daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c | 53 ++++++++++++++++++------ + 1 file changed, 40 insertions(+), 13 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +index 761f7a8e3e9ee539f97797c98b8719ad752bdcf1..5efadac5b1fd57e5f91a886224fa2f1ab88305ac 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +@@ -46,6 +46,8 @@ + /* Type of connection for this operation;*/ + #define LDAP_EXTOP_PASSMOD_CONN_SECURE + ++#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled" ++ + /* Uncomment the following #undef FOR TESTING: + * allows non-SSL connections to use the password change extended op */ + /* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */ +@@ -62,6 +64,27 @@ static const char *ipapwd_def_encsalts[] = { + NULL + }; + ++static bool fips_enabled(void) ++{ ++ int fd; ++ ssize_t len; ++ char buf[8]; ++ ++ fd = open(PROC_SYS_FIPS, O_RDONLY); ++ if (fd != -1) { ++ len = read(fd, buf, sizeof(buf)); ++ close(fd); ++ /* Assume FIPS in enabled if PROC_SYS_FIPS contains a non-0 value ++ * similar to the is_fips_enabled() check in ++ * ipaplatform/redhat/tasks.py */ ++ if (!(len == 2 && buf[0] == '0' && buf[1] == '\n')) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ + static struct ipapwd_krbcfg *ipapwd_getConfig(void) + { + krb5_error_code krberr; +@@ -232,23 +255,27 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void) + + /* get the ipa etc/ipaConfig entry */ + config->allow_nt_hash = false; +- ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL); +- if (ret != LDAP_SUCCESS) { +- LOG_FATAL("No config Entry?\n"); +- goto free_and_error; ++ if (fips_enabled()) { ++ LOG("FIPS mode is enabled, NT hashes are not allowed.\n"); + } else { +- tmparray = slapi_entry_attr_get_charray(config_entry, +- "ipaConfigString"); +- for (i = 0; tmparray && tmparray[i]; i++) { +- if (strcasecmp(tmparray[i], "AllowNThash") == 0) { +- config->allow_nt_hash = true; +- continue; ++ ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL); ++ if (ret != LDAP_SUCCESS) { ++ LOG_FATAL("No config Entry?\n"); ++ goto free_and_error; ++ } else { ++ tmparray = slapi_entry_attr_get_charray(config_entry, ++ "ipaConfigString"); ++ for (i = 0; tmparray && tmparray[i]; i++) { ++ if (strcasecmp(tmparray[i], "AllowNThash") == 0) { ++ config->allow_nt_hash = true; ++ continue; ++ } + } ++ if (tmparray) slapi_ch_array_free(tmparray); + } +- if (tmparray) slapi_ch_array_free(tmparray); +- } + +- slapi_entry_free(config_entry); ++ slapi_entry_free(config_entry); ++ } + + return config; + +-- +2.9.4 + diff --git a/SOURCES/0203-Make-sure-we-check-ccaches-in-all-rpcserver-paths.patch b/SOURCES/0203-Make-sure-we-check-ccaches-in-all-rpcserver-paths.patch new file mode 100644 index 0000000..c246344 --- /dev/null +++ b/SOURCES/0203-Make-sure-we-check-ccaches-in-all-rpcserver-paths.patch @@ -0,0 +1,126 @@ +From 8c6cafb9d331d15cb224820c3bc254c84b49a0c7 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Thu, 22 Jun 2017 10:57:25 -0400 +Subject: [PATCH] Make sure we check ccaches in all rpcserver paths + +We need to verify the ccache is avcailable in all cases or finalize +will cause us to acquire creds with the keytab which is not what we +want. + +Ticket #7037 + +Signed-off-by: Simo Sorce +Reviewed-By: Stanislav Laznicka +--- + ipaserver/rpcserver.py | 72 +++++++++++++++++++++++++++----------------------- + 1 file changed, 39 insertions(+), 33 deletions(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 2990df25985eab63d4bcfc8edf7f2b12da3e9832..9efe3c1f4b9e0114a02e8e04aafc76c3bc04c6f1 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -592,6 +592,41 @@ class KerberosSession(HTTP_Status): + needing this do not share a common base class. + ''' + ++ def need_login(self, start_response): ++ status = '401 Unauthorized' ++ headers = [] ++ response = b'' ++ ++ self.debug('%s need login', status) ++ ++ start_response(status, headers) ++ return [response] ++ ++ def get_environ_creds(self, environ): ++ # If we have a ccache ... ++ ccache_name = environ.get('KRB5CCNAME') ++ if ccache_name is None: ++ self.debug('no ccache, need login') ++ return ++ ++ # ... make sure we have a name ... ++ principal = environ.get('GSS_NAME') ++ if principal is None: ++ self.debug('no Principal Name, need login') ++ return ++ ++ # ... and use it to resolve the ccache name (Issue: 6972 ) ++ gss_name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) ++ ++ # Fail if Kerberos credentials are expired or missing ++ creds = get_credentials_if_valid(name=gss_name, ++ ccache_name=ccache_name) ++ if not creds: ++ self.debug('ccache expired or invalid, deleting session, need login') ++ return ++ ++ return ccache_name ++ + + def finalize_kerberos_acquisition(self, who, ccache_name, environ, start_response, headers=None): + if headers is None: +@@ -754,43 +789,15 @@ class jsonserver_session(jsonserver, KerberosSession): + def _on_finalize(self): + super(jsonserver_session, self)._on_finalize() + +- def need_login(self, start_response): +- status = '401 Unauthorized' +- headers = [] +- response = b'' +- +- self.debug('jsonserver_session: %s need login', status) +- +- start_response(status, headers) +- return [response] +- + def __call__(self, environ, start_response): + ''' + ''' + + self.debug('WSGI jsonserver_session.__call__:') + +- ccache_name = environ.get('KRB5CCNAME') +- + # Redirect to login if no Kerberos credentials ++ ccache_name = self.get_environ_creds(environ) + if ccache_name is None: +- self.debug('no ccache, need login') +- return self.need_login(start_response) +- +- # If we have a ccache, make sure we have a GSS_NAME and use +- # it to resolve the ccache name (Issue: 6972 ) +- principal = environ.get('GSS_NAME') +- if principal is None: +- self.debug('no GSS Name, need login') +- return self.need_login(start_response) +- gss_name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) +- +- # Redirect to login if Kerberos credentials are expired +- creds = get_credentials_if_valid(name=gss_name, +- ccache_name=ccache_name) +- if not creds: +- self.debug('ccache expired, deleting session, need login') +- # The request is finished with the ccache, destroy it. + return self.need_login(start_response) + + # Store the ccache name in the per-thread context +@@ -828,11 +835,10 @@ class KerberosLogin(Backend, KerberosSession): + def __call__(self, environ, start_response): + self.debug('WSGI KerberosLogin.__call__:') + +- # Get the ccache created by mod_auth_gssapi +- user_ccache_name=environ.get('KRB5CCNAME') ++ # Redirect to login if no Kerberos credentials ++ user_ccache_name = self.get_environ_creds(environ) + if user_ccache_name is None: +- return self.internal_error(environ, start_response, +- 'login_kerberos: KRB5CCNAME not defined in HTTP request environment') ++ return self.need_login(start_response) + + return self.finalize_kerberos_acquisition('login_kerberos', user_ccache_name, environ, start_response) + +-- +2.9.4 + diff --git a/SOURCES/0204-replica-install-drop-in-IPA-specific-config-to-tmpfi.patch b/SOURCES/0204-replica-install-drop-in-IPA-specific-config-to-tmpfi.patch new file mode 100644 index 0000000..4e27e7f --- /dev/null +++ b/SOURCES/0204-replica-install-drop-in-IPA-specific-config-to-tmpfi.patch @@ -0,0 +1,34 @@ +From 9c70e00901ed1453767d085ea4c5496b2341c212 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Tue, 11 Jul 2017 12:41:38 +0200 +Subject: [PATCH] replica install: drop-in IPA specific config to tmpfiles.d + +While server installation and upgrade code configures the IPA specific +tmpfiles location and creates relevant directories, the replica +installer code path is covered incompletely and one step is missing. + +https://pagure.io/freeipa/issue/7053 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/server/replicainstall.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 4f28de25bd0adf958187c19edf90de4ba57dd98e..814925de152809808f726c60ae7f35a24bc32a4a 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1515,6 +1515,9 @@ def install(installer): + # remove the extracted replica file + remove_replica_info_dir(installer) + ++ # Make sure the files we crated in /var/run are recreated at startup ++ tasks.configure_tmpfiles() ++ + # Everything installed properly, activate ipa service. + services.knownservices.ipa.enable() + +-- +2.9.4 + diff --git a/SOURCES/0205-Add-CommonNameToSANDefault-to-default-cert-profile.patch b/SOURCES/0205-Add-CommonNameToSANDefault-to-default-cert-profile.patch new file mode 100644 index 0000000..73b0c6d --- /dev/null +++ b/SOURCES/0205-Add-CommonNameToSANDefault-to-default-cert-profile.patch @@ -0,0 +1,62 @@ +From bf0a34b06e4a44b71b5a9b5f7b7537d3d99e0441 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 7 Jun 2017 19:41:26 +1000 +Subject: [PATCH] Add CommonNameToSANDefault to default cert profile + +The CommonNameToSANDefault component was added to Dogtag 10.4. When +a profile is configured to use it, this profile copies the CN in the +certificate to the Subject Alternative Name extension as a dNSName +(if and only if it does look like a DNS name). + +It is desirable that the default service profile use this component. +Add it to the default profile, for new installations only. For +existing installations, until a proper profile update mechanism is +implemented, administrators who wish to use it must configure it via +the 'certprofile-mod' command. + +Fixes: https://pagure.io/freeipa/issue/7007 +Reviewed-By: Jan Cholasta +--- + freeipa.spec.in | 4 ++-- + install/share/profiles/caIPAserviceCert.cfg | 6 +++++- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index d7f8d11ec553cfe299937e1e5f8cc27caed32b08..721e512039a4d7f9d2ed94d7620b083732c56304 100644 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -291,8 +291,8 @@ Requires(post): systemd-units + Requires: selinux-policy >= %{selinux_policy_version} + Requires(post): selinux-policy-base >= %{selinux_policy_version} + Requires: slapi-nis >= %{slapi_nis_version} +-Requires: pki-ca >= 10.3.5-11 +-Requires: pki-kra >= 10.3.5-11 ++Requires: pki-ca >= 10.4.0-1 ++Requires: pki-kra >= 10.4.0-1 + Requires(preun): python systemd-units + Requires(postun): python systemd-units + Requires: policycoreutils >= 2.1.12-5 +diff --git a/install/share/profiles/caIPAserviceCert.cfg b/install/share/profiles/caIPAserviceCert.cfg +index 6c5102f0dbd6bd6c6eaf2fa22e87ed4a5f34553c..3bec9ed10c7c053a67271de52dd95e71fe1fb6b8 100644 +--- a/install/share/profiles/caIPAserviceCert.cfg ++++ b/install/share/profiles/caIPAserviceCert.cfg +@@ -12,7 +12,7 @@ input.i2.class_id=submitterInfoInputImpl + output.list=o1 + output.o1.class_id=certOutputImpl + policyset.list=serverCertSet +-policyset.serverCertSet.list=1,2,3,4,5,6,7,8,9,10,11 ++policyset.serverCertSet.list=1,2,3,4,5,6,7,8,9,10,11,12 + policyset.serverCertSet.1.constraint.class_id=subjectNameConstraintImpl + policyset.serverCertSet.1.constraint.name=Subject Name Constraint + policyset.serverCertSet.1.constraint.params.pattern=CN=[^,]+,.+ +@@ -107,3 +107,7 @@ policyset.serverCertSet.11.constraint.name=No Constraint + policyset.serverCertSet.11.default.class_id=userExtensionDefaultImpl + policyset.serverCertSet.11.default.name=User Supplied Extension Default + policyset.serverCertSet.11.default.params.userExtOID=2.5.29.17 ++policyset.serverCertSet.12.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.12.constraint.name=No Constraint ++policyset.serverCertSet.12.default.class_id=commonNameToSANDefaultImpl ++policyset.serverCertSet.12.default.name=Copy Common Name to Subject Alternative Name +-- +2.9.4 + diff --git a/SOURCES/0206-smart-card-advises-configure-systemwide-NSS-DB-also-.patch b/SOURCES/0206-smart-card-advises-configure-systemwide-NSS-DB-also-.patch new file mode 100644 index 0000000..f83b0f2 --- /dev/null +++ b/SOURCES/0206-smart-card-advises-configure-systemwide-NSS-DB-also-.patch @@ -0,0 +1,131 @@ +From 57c93cb21d542e1d0eab52baa01ac60f30459dc7 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 21 Jun 2017 18:28:50 +0200 +Subject: [PATCH] smart-card advises: configure systemwide NSS DB also on + master + +Previously the Smart card signing CA cert was uploaded to systemwide NSS +DB only on the client, but it need to be added also to the server. +Modify the advise plugins to allow for common configuration steps to +occur in both cases. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 59 +++++++++++++++++------------ + 1 file changed, 35 insertions(+), 24 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 5859e350939fdba0a8b258de5285dd10c7b3bc23..0ee4808d47aa87a4b1b838d427e9958d98075a4a 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -10,8 +10,39 @@ from ipaserver.install.httpinstance import NSS_OCSP_ENABLED + register = Registry() + + ++class common_smart_card_auth_config(Advice): ++ """ ++ Common steps required to properly configure both server and client for ++ smart card auth ++ """ ++ ++ systemwide_nssdb = paths.NSS_DB_DIR ++ smart_card_ca_cert_variable_name = "SC_CA_CERT" ++ ++ def check_and_set_ca_cert_path(self): ++ ca_path_variable = self.smart_card_ca_cert_variable_name ++ self.log.command("{}=$1".format(ca_path_variable)) ++ self.log.exit_on_predicate( ++ '[ -z "${}" ]'.format(ca_path_variable), ++ ['You need to provide the path to the PEM file containing CA ' ++ 'signing the Smart Cards'] ++ ) ++ self.log.exit_on_predicate( ++ '[ ! -f "${}" ]'.format(ca_path_variable), ++ ['Invalid CA certificate filename: ${}'.format(ca_path_variable), ++ 'Please check that the path exists and is a valid file'] ++ ) ++ ++ def upload_smartcard_ca_certificate_to_systemwide_db(self): ++ self.log.command( ++ 'certutil -d {} -A -i ${} -n "Smart Card CA" -t CT,C,C'.format( ++ self.systemwide_nssdb, self.smart_card_ca_cert_variable_name ++ ) ++ ) ++ ++ + @register() +-class config_server_for_smart_card_auth(Advice): ++class config_server_for_smart_card_auth(common_smart_card_auth_config): + """ + Configures smart card authentication via Kerberos (PKINIT) and for WebUI + """ +@@ -28,6 +59,7 @@ class config_server_for_smart_card_auth(Advice): + + def get_info(self): + self.log.exit_on_nonroot_euid() ++ self.check_and_set_ca_cert_path() + self.check_ccache_not_empty() + self.check_hostname_is_in_masters() + self.resolve_ipaca_records() +@@ -37,6 +69,7 @@ class config_server_for_smart_card_auth(Advice): + self.record_httpd_ocsp_status() + self.check_and_enable_pkinit() + self.enable_ok_to_auth_as_delegate_on_http_principal() ++ self.upload_smartcard_ca_certificate_to_systemwide_db() + + def check_ccache_not_empty(self): + self.log.comment('Check whether the credential cache is not empty') +@@ -162,11 +195,10 @@ class config_server_for_smart_card_auth(Advice): + + + @register() +-class config_client_for_smart_card_auth(Advice): ++class config_client_for_smart_card_auth(common_smart_card_auth_config): + """ + Configures smart card authentication on FreeIPA client + """ +- smart_card_ca_cert_variable_name = "SC_CA_CERT" + + description = ("Instructions for enabling Smart Card authentication on " + " a single FreeIPA client. Configures Smart Card daemon, " +@@ -190,20 +222,6 @@ class config_client_for_smart_card_auth(Advice): + self.run_authconfig_to_configure_smart_card_auth() + self.restart_sssd() + +- def check_and_set_ca_cert_path(self): +- ca_path_variable = self.smart_card_ca_cert_variable_name +- self.log.command("{}=$1".format(ca_path_variable)) +- self.log.exit_on_predicate( +- '[ -z "${}" ]'.format(ca_path_variable), +- ['You need to provide the path to the PEM file containing CA ' +- 'signing the Smart Cards'] +- ) +- self.log.exit_on_predicate( +- '[ ! -f "${}" ]'.format(ca_path_variable), +- ['Invalid CA certificate filename: ${}'.format(ca_path_variable), +- 'Please check that the path exists and is a valid file'] +- ) +- + def check_and_remove_pam_pkcs11(self): + self.log.command('rpm -qi pam_pkcs11 > /dev/null') + self.log.commands_on_predicate( +@@ -247,13 +265,6 @@ class config_client_for_smart_card_auth(Advice): + ] + ) + +- def upload_smartcard_ca_certificate_to_systemwide_db(self): +- self.log.command( +- 'certutil -d {} -A -i ${} -n "Smart Card CA" -t CT,C,C'.format( +- self.systemwide_nssdb, self.smart_card_ca_cert_variable_name +- ) +- ) +- + def run_authconfig_to_configure_smart_card_auth(self): + self.log.exit_on_failed_command( + 'authconfig --enablesmartcard --smartcardmodule=sssd --updateall', +-- +2.9.4 + diff --git a/SOURCES/0207-smart-card-advises-add-steps-to-store-smart-card-sig.patch b/SOURCES/0207-smart-card-advises-add-steps-to-store-smart-card-sig.patch new file mode 100644 index 0000000..344a96c --- /dev/null +++ b/SOURCES/0207-smart-card-advises-add-steps-to-store-smart-card-sig.patch @@ -0,0 +1,125 @@ +From 7bbf7dbc27d1bcde8bf3e4d0bb8fec65de2660c8 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 21 Jun 2017 18:52:57 +0200 +Subject: [PATCH] smart-card advises: add steps to store smart card signing CA + cert + +On master, upload the CA certificate to IPA LDAP and NSS databases. On +both master and client run ipa-certupdate to update client-side CA +certificate bundles used as PKINIT anchors. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 46 +++++++++++++++++++++++------ + 1 file changed, 37 insertions(+), 9 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 0ee4808d47aa87a4b1b838d427e9958d98075a4a..0217bd190778f1235981a49e7b0764b8b9cdf582 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -3,6 +3,7 @@ + # + + from ipalib.plugable import Registry ++from ipaplatform import services + from ipaplatform.paths import paths + from ipaserver.advise.base import Advice + from ipaserver.install.httpinstance import NSS_OCSP_ENABLED +@@ -19,6 +20,16 @@ class common_smart_card_auth_config(Advice): + systemwide_nssdb = paths.NSS_DB_DIR + smart_card_ca_cert_variable_name = "SC_CA_CERT" + ++ def check_ccache_not_empty(self): ++ self.log.comment('Check whether the credential cache is not empty') ++ self.log.exit_on_failed_command( ++ 'klist', ++ [ ++ "Credential cache is empty", ++ 'Use kinit as privileged user to obtain Kerberos credentials' ++ ]) ++ ++ + def check_and_set_ca_cert_path(self): + ca_path_variable = self.smart_card_ca_cert_variable_name + self.log.command("{}=$1".format(ca_path_variable)) +@@ -40,6 +51,20 @@ class common_smart_card_auth_config(Advice): + ) + ) + ++ def install_smart_card_signing_ca_cert(self): ++ self.log.exit_on_failed_command( ++ 'ipa-cacert-manage install ${} -t CT,C,C'.format( ++ self.smart_card_ca_cert_variable_name ++ ), ++ ['Failed to install external CA certificate to IPA'] ++ ) ++ ++ def update_ipa_ca_certificate_store(self): ++ self.log.exit_on_failed_command( ++ 'ipa-certupdate', ++ ['Failed to update IPA CA certificate database'] ++ ) ++ + + @register() + class config_server_for_smart_card_auth(common_smart_card_auth_config): +@@ -56,6 +81,7 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + nss_conf = paths.HTTPD_NSS_CONF + nss_ocsp_directive = 'NSSOCSP' + nss_nickname_directive = 'NSSNickname' ++ kdc_service_name = services.knownservices.krb5kdc.systemd_name + + def get_info(self): + self.log.exit_on_nonroot_euid() +@@ -70,15 +96,8 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + self.check_and_enable_pkinit() + self.enable_ok_to_auth_as_delegate_on_http_principal() + self.upload_smartcard_ca_certificate_to_systemwide_db() +- +- def check_ccache_not_empty(self): +- self.log.comment('Check whether the credential cache is not empty') +- self.log.exit_on_failed_command( +- 'klist', +- [ +- "Credential cache is empty", +- 'Use kinit as privileged user to obtain Kerberos credentials' +- ]) ++ self.update_ipa_ca_certificate_store() ++ self.restart_kdc() + + def check_hostname_is_in_masters(self): + self.log.comment('Check whether the host is IPA master') +@@ -193,6 +212,12 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + ["Failed to set OK_AS_AUTH_AS_DELEGATE flag on HTTP principal"] + ) + ++ def restart_kdc(self): ++ self.log.exit_on_failed_command( ++ 'systemctl restart {}'.format(self.kdc_service_name), ++ ['Failed to restart KDC. Please restart the service manually.'] ++ ) ++ + + @register() + class config_client_for_smart_card_auth(common_smart_card_auth_config): +@@ -214,11 +239,14 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config): + def get_info(self): + self.log.exit_on_nonroot_euid() + self.check_and_set_ca_cert_path() ++ self.check_ccache_not_empty() + self.check_and_remove_pam_pkcs11() + self.install_opensc_and_dconf_packages() + self.start_enable_smartcard_daemon() + self.add_pkcs11_module_to_systemwide_db() + self.upload_smartcard_ca_certificate_to_systemwide_db() ++ self.install_smart_card_signing_ca_cert() ++ self.update_ipa_ca_certificate_store() + self.run_authconfig_to_configure_smart_card_auth() + self.restart_sssd() + +-- +2.9.4 + diff --git a/SOURCES/0208-Allow-to-pass-in-multiple-CA-cert-paths-to-the-smart.patch b/SOURCES/0208-Allow-to-pass-in-multiple-CA-cert-paths-to-the-smart.patch new file mode 100644 index 0000000..612e334 --- /dev/null +++ b/SOURCES/0208-Allow-to-pass-in-multiple-CA-cert-paths-to-the-smart.patch @@ -0,0 +1,150 @@ +From 6bfb11f2110f5fbaecc2d6a27d89289c58edc171 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 10:06:21 +0200 +Subject: [PATCH] Allow to pass in multiple CA cert paths to the smart card + advises + +If the user has a series of CA certificates required to verify smart +card certs (e.g. intermediary CAs and root CA) it is convenient to allow +for passing them to the advise scripts as a series of PEM files. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 69 +++++++++++++++++++---------- + 1 file changed, 46 insertions(+), 23 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 0217bd190778f1235981a49e7b0764b8b9cdf582..16c01204444883ed949db73b2314ba5c404124df 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -18,7 +18,8 @@ class common_smart_card_auth_config(Advice): + """ + + systemwide_nssdb = paths.NSS_DB_DIR +- smart_card_ca_cert_variable_name = "SC_CA_CERT" ++ smart_card_ca_certs_variable_name = "SC_CA_CERTS" ++ single_ca_cert_variable_name = 'ca_cert' + + def check_ccache_not_empty(self): + self.log.comment('Check whether the credential cache is not empty') +@@ -29,35 +30,58 @@ class common_smart_card_auth_config(Advice): + 'Use kinit as privileged user to obtain Kerberos credentials' + ]) + ++ def check_and_set_ca_cert_paths(self): ++ ca_paths_variable = self.smart_card_ca_certs_variable_name ++ single_ca_path_variable = self.single_ca_cert_variable_name + +- def check_and_set_ca_cert_path(self): +- ca_path_variable = self.smart_card_ca_cert_variable_name +- self.log.command("{}=$1".format(ca_path_variable)) ++ self.log.command("{}=$@".format(ca_paths_variable)) + self.log.exit_on_predicate( +- '[ -z "${}" ]'.format(ca_path_variable), +- ['You need to provide the path to the PEM file containing CA ' +- 'signing the Smart Cards'] ++ '[ -z "${}" ]'.format(ca_paths_variable), ++ ['You need to provide one or more paths to the PEM files ' ++ 'containing CAs signing the Smart Cards'] + ) ++ self.log.command( ++ "for {} in ${}".format( ++ single_ca_path_variable, ca_paths_variable)) ++ self.log.command("do") + self.log.exit_on_predicate( +- '[ ! -f "${}" ]'.format(ca_path_variable), +- ['Invalid CA certificate filename: ${}'.format(ca_path_variable), +- 'Please check that the path exists and is a valid file'] ++ '[ ! -f "${}" ]'.format(single_ca_path_variable), ++ ['Invalid CA certificate filename: ${}'.format( ++ single_ca_path_variable), ++ 'Please check that the path exists and is a valid file'], ++ indent_spaces=2 + ) ++ self.log.command("done") + +- def upload_smartcard_ca_certificate_to_systemwide_db(self): ++ def upload_smartcard_ca_certificates_to_systemwide_db(self): ++ self.log.command( ++ "for {} in ${}".format( ++ self.single_ca_cert_variable_name, ++ self.smart_card_ca_certs_variable_name)) ++ self.log.command("do") + self.log.command( +- 'certutil -d {} -A -i ${} -n "Smart Card CA" -t CT,C,C'.format( +- self.systemwide_nssdb, self.smart_card_ca_cert_variable_name +- ) ++ 'certutil -d {} -A -i ${} -n "Smart Card CA $(uuidgen)" ' ++ '-t CT,C,C'.format( ++ self.systemwide_nssdb, self.single_ca_cert_variable_name ++ ), ++ indent_spaces=2 + ) ++ self.log.command("done") + +- def install_smart_card_signing_ca_cert(self): ++ def install_smart_card_signing_ca_certs(self): ++ self.log.command( ++ "for {} in ${}".format( ++ self.single_ca_cert_variable_name, ++ self.smart_card_ca_certs_variable_name)) ++ self.log.command("do") + self.log.exit_on_failed_command( + 'ipa-cacert-manage install ${} -t CT,C,C'.format( +- self.smart_card_ca_cert_variable_name ++ self.single_ca_cert_variable_name + ), +- ['Failed to install external CA certificate to IPA'] ++ ['Failed to install external CA certificate to IPA'], ++ indent_spaces=2 + ) ++ self.log.command("done") + + def update_ipa_ca_certificate_store(self): + self.log.exit_on_failed_command( +@@ -85,7 +109,7 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + + def get_info(self): + self.log.exit_on_nonroot_euid() +- self.check_and_set_ca_cert_path() ++ self.check_and_set_ca_cert_paths() + self.check_ccache_not_empty() + self.check_hostname_is_in_masters() + self.resolve_ipaca_records() +@@ -95,7 +119,8 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + self.record_httpd_ocsp_status() + self.check_and_enable_pkinit() + self.enable_ok_to_auth_as_delegate_on_http_principal() +- self.upload_smartcard_ca_certificate_to_systemwide_db() ++ self.upload_smartcard_ca_certificates_to_systemwide_db() ++ self.install_smart_card_signing_ca_certs() + self.update_ipa_ca_certificate_store() + self.restart_kdc() + +@@ -234,18 +259,16 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config): + pkcs11_shared_lib = '/usr/lib64/opensc-pkcs11.so' + smart_card_service_file = 'pcscd.service' + smart_card_socket = 'pcscd.socket' +- systemwide_nssdb = paths.NSS_DB_DIR + + def get_info(self): + self.log.exit_on_nonroot_euid() +- self.check_and_set_ca_cert_path() ++ self.check_and_set_ca_cert_paths() + self.check_ccache_not_empty() + self.check_and_remove_pam_pkcs11() + self.install_opensc_and_dconf_packages() + self.start_enable_smartcard_daemon() + self.add_pkcs11_module_to_systemwide_db() +- self.upload_smartcard_ca_certificate_to_systemwide_db() +- self.install_smart_card_signing_ca_cert() ++ self.upload_smartcard_ca_certificates_to_systemwide_db() + self.update_ipa_ca_certificate_store() + self.run_authconfig_to_configure_smart_card_auth() + self.restart_sssd() +-- +2.9.4 + diff --git a/SOURCES/0209-add-a-class-that-tracks-the-indentation-in-the-gener.patch b/SOURCES/0209-add-a-class-that-tracks-the-indentation-in-the-gener.patch new file mode 100644 index 0000000..e6ac4ed --- /dev/null +++ b/SOURCES/0209-add-a-class-that-tracks-the-indentation-in-the-gener.patch @@ -0,0 +1,77 @@ +From bdf024c20213110306b2fcf3651f274c229aae29 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 13:18:54 +0200 +Subject: [PATCH] add a class that tracks the indentation in the generated + advises + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/base.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 49 insertions(+) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index ba412b872472580cd32baf2a326a14edb951cab1..639fd1807f72f11f46136999c4ce4c6eec6b3698 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -76,6 +76,55 @@ As a result, you can redirect the advice's output directly to a script file. + """ + + ++class _IndentationTracker(object): ++ """ ++ A simple wrapper that tracks the indentation level of the generated bash ++ commands ++ """ ++ def __init__(self, spaces_per_indent=0): ++ if spaces_per_indent <= 0: ++ raise ValueError( ++ "Indentation increments cannot be zero or negative") ++ self.spaces_per_indent = spaces_per_indent ++ self._indentation_stack = [] ++ self._total_indentation_level = 0 ++ ++ @property ++ def indentation_string(self): ++ """ ++ return a string containing number of spaces corresponding to ++ indentation level ++ """ ++ return " " * self._total_indentation_level ++ ++ def indent(self): ++ """ ++ track a single indentation of the generated code ++ """ ++ self._indentation_stack.append(self.spaces_per_indent) ++ self._recompute_indentation_level() ++ ++ def _recompute_indentation_level(self): ++ """ ++ Track total indentation level of the generated code ++ """ ++ self._total_indentation_level = sum(self._indentation_stack) ++ ++ def dedent(self): ++ """ ++ track a single dedentation of the generated code ++ dedents that would result in zero or negative indentation level will be ++ ignored ++ """ ++ try: ++ self._indentation_stack.pop() ++ except IndexError: ++ # can not dedent any further ++ pass ++ ++ self._recompute_indentation_level() ++ ++ + class _AdviceOutput(object): + + def __init__(self): +-- +2.9.4 + diff --git a/SOURCES/0210-delegate-the-indentation-handling-in-advises-to-dedi.patch b/SOURCES/0210-delegate-the-indentation-handling-in-advises-to-dedi.patch new file mode 100644 index 0000000..901dd54 --- /dev/null +++ b/SOURCES/0210-delegate-the-indentation-handling-in-advises-to-dedi.patch @@ -0,0 +1,267 @@ +From 243f7ebf6a07fa54cbd5db6bf7f67fee04e4f14c Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 13:20:05 +0200 +Subject: [PATCH] delegate the indentation handling in advises to dedicated + class + +Indentation levels are now handled transparently by a dedicated class +and should not pollute the statement printing logic. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/base.py | 106 +++++++++++++++++++--------- + ipaserver/advise/plugins/smart_card_auth.py | 45 ++++++------ + 2 files changed, 93 insertions(+), 58 deletions(-) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index 639fd1807f72f11f46136999c4ce4c6eec6b3698..c320b002c83198cbb0fd73a5c158df07dd309242 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -19,6 +19,7 @@ + + from __future__ import print_function + ++from contextlib import contextmanager + import os + from textwrap import wrap + +@@ -75,6 +76,8 @@ As a result, you can redirect the advice's output directly to a script file. + # ./script.sh + """ + ++DEFAULT_INDENTATION_INCREMENT = 2 ++ + + class _IndentationTracker(object): + """ +@@ -131,39 +134,77 @@ class _AdviceOutput(object): + self.content = [] + self.prefix = '# ' + self.options = None ++ self._indentation_tracker = _IndentationTracker( ++ spaces_per_indent=DEFAULT_INDENTATION_INCREMENT) ++ ++ def indent(self): ++ """ ++ Indent the statements by one level ++ """ ++ self._indentation_tracker.indent() ++ ++ def dedent(self): ++ """ ++ Dedent the statements by one level ++ """ ++ self._indentation_tracker.dedent() ++ ++ @contextmanager ++ def indented_block(self): ++ self.indent() ++ try: ++ yield ++ finally: ++ self.dedent() + + def comment(self, line, wrapped=True): + if wrapped: +- for wrapped_line in wrap(line, 70): +- self.content.append(self.prefix + wrapped_line) ++ self.append_wrapped_and_indented_comment(line) + else: +- self.content.append(self.prefix + line) ++ self.append_comment(line) ++ ++ def append_wrapped_and_indented_comment(self, line, character_limit=70): ++ """ ++ append wrapped and indented comment to the output ++ """ ++ for wrapped_indented_line in wrap( ++ self.indent_statement(line), character_limit): ++ self.append_comment(wrapped_indented_line) ++ ++ def append_comment(self, line): ++ self.append_statement(self.prefix + line) ++ ++ def append_statement(self, statement): ++ """ ++ Append a line to the generated content indenting it by tracked number ++ of spaces ++ """ ++ self.content.append(self.indent_statement(statement)) ++ ++ def indent_statement(self, statement): ++ return '{indent}{statement}'.format( ++ indent=self._indentation_tracker.indentation_string, ++ statement=statement) + + def debug(self, line): + if self.options.verbose: + self.comment('DEBUG: ' + line) + +- def command(self, line, indent_spaces=0): +- self.content.append( +- '{}{}'.format(self._format_indent(indent_spaces), line)) +- +- def _format_indent(self, num_spaces): +- return ' ' * num_spaces ++ def command(self, line): ++ self.append_statement(line) + +- def echo_error(self, error_message, indent_spaces=0): +- self.command( +- self._format_error(error_message), indent_spaces=indent_spaces) ++ def echo_error(self, error_message): ++ self.command(self._format_error(error_message)) + + def _format_error(self, error_message): + return 'echo "{}" >&2'.format(error_message) + + def exit_on_failed_command(self, command_to_run, +- error_message_lines, indent_spaces=0): +- self.command(command_to_run, indent_spaces=indent_spaces) ++ error_message_lines): ++ self.command(command_to_run) + self.exit_on_predicate( + '[ "$?" -ne "0" ]', +- error_message_lines, +- indent_spaces=indent_spaces) ++ error_message_lines) + + def exit_on_nonroot_euid(self): + self.exit_on_predicate( +@@ -171,8 +212,7 @@ class _AdviceOutput(object): + ["This script has to be run as root user"] + ) + +- def exit_on_predicate(self, predicate, error_message_lines, +- indent_spaces=0): ++ def exit_on_predicate(self, predicate, error_message_lines): + commands_to_run = [ + self._format_error(error_message_line) + for error_message_line in error_message_lines] +@@ -180,30 +220,26 @@ class _AdviceOutput(object): + commands_to_run.append('exit 1') + self.commands_on_predicate( + predicate, +- commands_to_run, +- indent_spaces=indent_spaces) ++ commands_to_run) + + def commands_on_predicate(self, predicate, commands_to_run_when_true, +- commands_to_run_when_false=None, +- indent_spaces=0): ++ commands_to_run_when_false=None): + if_command = 'if {}'.format(predicate) +- self.command(if_command, indent_spaces=indent_spaces) +- self.command('then', indent_spaces=indent_spaces) +- +- indented_block_spaces = indent_spaces + 2 ++ self.command(if_command) ++ self.command('then') + +- for command_to_run_when_true in commands_to_run_when_true: +- self.command( +- command_to_run_when_true, indent_spaces=indented_block_spaces) ++ with self.indented_block(): ++ for command_to_run_when_true in commands_to_run_when_true: ++ self.command( ++ command_to_run_when_true) + + if commands_to_run_when_false is not None: +- self.command("else", indent_spaces=indent_spaces) +- for command_to_run_when_false in commands_to_run_when_false: +- self.command( +- command_to_run_when_false, +- indent_spaces=indented_block_spaces) ++ self.command("else") ++ with self.indented_block(): ++ for command_to_run_when_false in commands_to_run_when_false: ++ self.command(command_to_run_when_false) + +- self.command('fi', indent_spaces=indent_spaces) ++ self.command('fi') + + + class Advice(Plugin): +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 16c01204444883ed949db73b2314ba5c404124df..75efa6f854acd5f746111ea44957a538117381ae 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -44,13 +44,13 @@ class common_smart_card_auth_config(Advice): + "for {} in ${}".format( + single_ca_path_variable, ca_paths_variable)) + self.log.command("do") +- self.log.exit_on_predicate( +- '[ ! -f "${}" ]'.format(single_ca_path_variable), +- ['Invalid CA certificate filename: ${}'.format( +- single_ca_path_variable), +- 'Please check that the path exists and is a valid file'], +- indent_spaces=2 +- ) ++ with self.log.indented_block(): ++ self.log.exit_on_predicate( ++ '[ ! -f "${}" ]'.format(single_ca_path_variable), ++ ['Invalid CA certificate filename: ${}'.format( ++ single_ca_path_variable), ++ 'Please check that the path exists and is a valid file'] ++ ) + self.log.command("done") + + def upload_smartcard_ca_certificates_to_systemwide_db(self): +@@ -59,13 +59,13 @@ class common_smart_card_auth_config(Advice): + self.single_ca_cert_variable_name, + self.smart_card_ca_certs_variable_name)) + self.log.command("do") +- self.log.command( +- 'certutil -d {} -A -i ${} -n "Smart Card CA $(uuidgen)" ' +- '-t CT,C,C'.format( +- self.systemwide_nssdb, self.single_ca_cert_variable_name +- ), +- indent_spaces=2 +- ) ++ with self.log.indented_block(): ++ self.log.command( ++ 'certutil -d {} -A -i ${} -n "Smart Card CA $(uuidgen)" ' ++ '-t CT,C,C'.format( ++ self.systemwide_nssdb, self.single_ca_cert_variable_name ++ ), ++ ) + self.log.command("done") + + def install_smart_card_signing_ca_certs(self): +@@ -74,13 +74,13 @@ class common_smart_card_auth_config(Advice): + self.single_ca_cert_variable_name, + self.smart_card_ca_certs_variable_name)) + self.log.command("do") +- self.log.exit_on_failed_command( +- 'ipa-cacert-manage install ${} -t CT,C,C'.format( +- self.single_ca_cert_variable_name +- ), +- ['Failed to install external CA certificate to IPA'], +- indent_spaces=2 +- ) ++ with self.log.indented_block(): ++ self.log.exit_on_failed_command( ++ 'ipa-cacert-manage install ${} -t CT,C,C'.format( ++ self.single_ca_cert_variable_name ++ ), ++ ['Failed to install external CA certificate to IPA'] ++ ) + self.log.command("done") + + def update_ipa_ca_certificate_store(self): +@@ -221,8 +221,7 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + self.log.command('else') + self.log.exit_on_failed_command( + 'ipa-pkinit-manage enable', +- ['Failed to issue PKINIT certificates to local KDC'], +- indent_spaces=2) ++ ['Failed to issue PKINIT certificates to local KDC']) + self.log.command('fi') + + def enable_ok_to_auth_as_delegate_on_http_principal(self): +-- +2.9.4 + diff --git a/SOURCES/0211-advise-add-an-infrastructure-for-formatting-Bash-com.patch b/SOURCES/0211-advise-add-an-infrastructure-for-formatting-Bash-com.patch new file mode 100644 index 0000000..297b114 --- /dev/null +++ b/SOURCES/0211-advise-add-an-infrastructure-for-formatting-Bash-com.patch @@ -0,0 +1,104 @@ +From 7e2702164e28576dfa64c0c9bbc83dc7dcb30ba7 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 15:00:00 +0200 +Subject: [PATCH] advise: add an infrastructure for formatting Bash compound + statements + +A series of context managers simplify formatting of common compound +statements such as `if`, `else if`, `else` blocks. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/base.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index c320b002c83198cbb0fd73a5c158df07dd309242..940d87ed4c1804326a46e2866381364e6f4f3f3e 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -128,6 +128,79 @@ class _IndentationTracker(object): + self._recompute_indentation_level() + + ++class CompoundStatement(object): ++ """ ++ Wrapper around indented blocks of Bash statements. ++ ++ Override `begin_statement` and `end_statement` methods to issue ++ opening/closing commands using the passed in _AdviceOutput instance ++ """ ++ ++ def __init__(self, advice_output): ++ self.advice_output = advice_output ++ ++ def __enter__(self): ++ self.begin_statement() ++ self.advice_output.indent() ++ ++ def begin_statement(self): ++ pass ++ ++ def __exit__(self, exc_type, exc_value, traceback): ++ self.advice_output.dedent() ++ self.end_statement() ++ ++ def end_statement(self): ++ pass ++ ++ ++class IfBranch(CompoundStatement): ++ """ ++ Base wrapper around `if` branch. The closing statement is empty so it ++ leaves trailing block that can be closed off or continued by else branches ++ """ ++ def __init__(self, advice_output, conditional): ++ super(IfBranch, self).__init__(advice_output) ++ self.conditional = conditional ++ ++ def begin_statement(self): ++ self.advice_output.command('if {}'.format(self.conditional)) ++ self.advice_output.command('then') ++ ++ ++class ElseIfBranch(CompoundStatement): ++ """ ++ Wrapper for `else if ` ++ """ ++ def __init__(self, advice_output, alternative_conditional): ++ super(ElseIfBranch, self).__init__(advice_output) ++ self.alternative_conditional = alternative_conditional ++ ++ def begin_statement(self): ++ command = 'else if {}'.format(self.alternative_conditional) ++ ++ self.advice_output.command(command) ++ ++ ++class ElseBranch(CompoundStatement): ++ """ ++ Wrapper for final `else` block ++ """ ++ def begin_statement(self): ++ self.advice_output.command('else') ++ ++ def end_statement(self): ++ self.advice_output.command('fi') ++ ++ ++class UnbranchedIfStatement(IfBranch): ++ """ ++ Plain `if` without branches ++ """ ++ def end_statement(self): ++ self.advice_output.command('fi') ++ ++ + class _AdviceOutput(object): + + def __init__(self): +-- +2.9.4 + diff --git a/SOURCES/0212-delegate-formatting-of-compound-Bash-statements-to-d.patch b/SOURCES/0212-delegate-formatting-of-compound-Bash-statements-to-d.patch new file mode 100644 index 0000000..59b6b5b --- /dev/null +++ b/SOURCES/0212-delegate-formatting-of-compound-Bash-statements-to-d.patch @@ -0,0 +1,93 @@ +From d22e7953295f878950ca5be976d89bf9af8d36b1 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 15:02:25 +0200 +Subject: [PATCH] delegate formatting of compound Bash statements to dedicated + classes + +this simplifies handling compound statements using _AdviceOutput class. +The necessary statements are exposed as context managers and API for +most common constructs is provided. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/base.py | 48 ++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 34 insertions(+), 14 deletions(-) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index 940d87ed4c1804326a46e2866381364e6f4f3f3e..581478fb75bc4f50b6bffe2e4cf9b51de46fa095 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -286,33 +286,53 @@ class _AdviceOutput(object): + ) + + def exit_on_predicate(self, predicate, error_message_lines): +- commands_to_run = [ +- self._format_error(error_message_line) +- for error_message_line in error_message_lines] ++ with self.unbranched_if(predicate): ++ for error_message_line in error_message_lines: ++ self.command(self._format_error(error_message_line)) + +- commands_to_run.append('exit 1') +- self.commands_on_predicate( +- predicate, +- commands_to_run) ++ self.command('exit 1') ++ ++ @contextmanager ++ def unbranched_if(self, predicate): ++ with self._compound_statement(UnbranchedIfStatement, predicate): ++ yield ++ ++ @contextmanager ++ def _compound_statement(self, statement_cls, *args): ++ with statement_cls(self, *args): ++ yield + + def commands_on_predicate(self, predicate, commands_to_run_when_true, + commands_to_run_when_false=None): +- if_command = 'if {}'.format(predicate) +- self.command(if_command) +- self.command('then') ++ if commands_to_run_when_false is not None: ++ if_statement = self.if_branch ++ else: ++ if_statement = self.unbranched_if + +- with self.indented_block(): ++ with if_statement(predicate): + for command_to_run_when_true in commands_to_run_when_true: + self.command( + command_to_run_when_true) + + if commands_to_run_when_false is not None: +- self.command("else") +- with self.indented_block(): ++ with self.else_branch(): + for command_to_run_when_false in commands_to_run_when_false: + self.command(command_to_run_when_false) + +- self.command('fi') ++ @contextmanager ++ def if_branch(self, predicate): ++ with self._compound_statement(IfBranch, predicate): ++ yield ++ ++ @contextmanager ++ def else_branch(self): ++ with self._compound_statement(ElseBranch): ++ yield ++ ++ @contextmanager ++ def else_if_branch(self, predicate): ++ with self._compound_statement(ElseIfBranch, predicate): ++ yield + + + class Advice(Plugin): +-- +2.9.4 + diff --git a/SOURCES/0213-Fix-indentation-of-statements-in-Smart-card-advises.patch b/SOURCES/0213-Fix-indentation-of-statements-in-Smart-card-advises.patch new file mode 100644 index 0000000..452b7df --- /dev/null +++ b/SOURCES/0213-Fix-indentation-of-statements-in-Smart-card-advises.patch @@ -0,0 +1,36 @@ +From 5bf0faed2a4692d5ce2747c5036d3fca8b0f7b04 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 15:03:45 +0200 +Subject: [PATCH] Fix indentation of statements in Smart card advises + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 75efa6f854acd5f746111ea44957a538117381ae..138a44316473f6b504a44a1b68d01fa4d5a58308 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -165,13 +165,13 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + predicate, + [ + self._interpolate_ocsp_directive_file_into_command( +- " sed -i.ipabkp -r " ++ "sed -i.ipabkp -r " + "'s/^#*[[:space:]]*{directive}[[:space:]]+(on|off)$" + "/{directive} on/' {filename}") + ], + commands_to_run_when_false=[ + self._interpolate_ocsp_directive_file_into_command( +- " sed -i.ipabkp '/<\/VirtualHost>/i {directive} on' " ++ "sed -i.ipabkp '/<\/VirtualHost>/i {directive} on' " + "{filename}") + ] + ) +-- +2.9.4 + diff --git a/SOURCES/0214-Use-the-compound-statement-formatting-API-for-config.patch b/SOURCES/0214-Use-the-compound-statement-formatting-API-for-config.patch new file mode 100644 index 0000000..6c24663 --- /dev/null +++ b/SOURCES/0214-Use-the-compound-statement-formatting-API-for-config.patch @@ -0,0 +1,48 @@ +From c702bb6ca3742cf7ea156e062840623f95a001b7 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 15:08:08 +0200 +Subject: [PATCH] Use the compound statement formatting API for configuring + PKINIT + +Use `if_branch` and `else_branch` context managers instead of raw +`command` calls in the method that generates Bash snippet that +configures PKINIT on the master. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 138a44316473f6b504a44a1b68d01fa4d5a58308..2dc9ddb25ce41a8c85aab827a92a1143784d9457 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -214,15 +214,13 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + + def check_and_enable_pkinit(self): + self.log.comment('check whether PKINIT is configured on the master') +- self.log.command( +- "if ipa-pkinit-manage status | grep -q 'enabled'") +- self.log.command('then') +- self.log.command(' echo "PKINIT already enabled"') +- self.log.command('else') +- self.log.exit_on_failed_command( +- 'ipa-pkinit-manage enable', +- ['Failed to issue PKINIT certificates to local KDC']) +- self.log.command('fi') ++ with self.log.if_branch( ++ "ipa-pkinit-manage status | grep -q 'enabled'"): ++ self.log.command('echo "PKINIT already enabled"') ++ with self.log.else_branch(): ++ self.log.exit_on_failed_command( ++ 'ipa-pkinit-manage enable', ++ ['Failed to issue PKINIT certificates to local KDC']) + + def enable_ok_to_auth_as_delegate_on_http_principal(self): + self.log.comment('Enable OK-AS-DELEGATE flag on the HTTP principal') +-- +2.9.4 + diff --git a/SOURCES/0215-smart-card-advises-use-a-wrapper-around-Bash-for-loo.patch b/SOURCES/0215-smart-card-advises-use-a-wrapper-around-Bash-for-loo.patch new file mode 100644 index 0000000..7102db1 --- /dev/null +++ b/SOURCES/0215-smart-card-advises-use-a-wrapper-around-Bash-for-loo.patch @@ -0,0 +1,121 @@ +From 4e6992f985ebfb6e6c3fb4a6fa7a2959d84ca243 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Thu, 22 Jun 2017 15:30:41 +0200 +Subject: [PATCH] smart card advises: use a wrapper around Bash `for` loops + +Replace the raw `command` calls constructing the for loops in some +methods by a wrapper hiding this detail. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/base.py | 23 +++++++++++++++++++++++ + ipaserver/advise/plugins/smart_card_auth.py | 26 +++++++------------------- + 2 files changed, 30 insertions(+), 19 deletions(-) + +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index 581478fb75bc4f50b6bffe2e4cf9b51de46fa095..be7274417042fca521039b56af60831563f6952b 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -201,6 +201,24 @@ class UnbranchedIfStatement(IfBranch): + self.advice_output.command('fi') + + ++class ForLoop(CompoundStatement): ++ """ ++ Wrapper around the for loop ++ """ ++ def __init__(self, advice_output, loop_variable, iterable): ++ super(ForLoop, self).__init__(advice_output) ++ self.loop_variable = loop_variable ++ self.iterable = iterable ++ ++ def begin_statement(self): ++ self.advice_output.command( ++ 'for {} in {}'.format(self.loop_variable, self.iterable)) ++ self.advice_output.command('do') ++ ++ def end_statement(self): ++ self.advice_output.command('done') ++ ++ + class _AdviceOutput(object): + + def __init__(self): +@@ -334,6 +352,11 @@ class _AdviceOutput(object): + with self._compound_statement(ElseIfBranch, predicate): + yield + ++ @contextmanager ++ def for_loop(self, loop_variable, iterable): ++ with self._compound_statement(ForLoop, loop_variable, iterable): ++ yield ++ + + class Advice(Plugin): + """ +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 2dc9ddb25ce41a8c85aab827a92a1143784d9457..3ff94be1e8b108668989602b1b406a39d23ff501 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -40,48 +40,36 @@ class common_smart_card_auth_config(Advice): + ['You need to provide one or more paths to the PEM files ' + 'containing CAs signing the Smart Cards'] + ) +- self.log.command( +- "for {} in ${}".format( +- single_ca_path_variable, ca_paths_variable)) +- self.log.command("do") +- with self.log.indented_block(): ++ with self.log.for_loop(single_ca_path_variable, ++ '${}'.format(ca_paths_variable)): + self.log.exit_on_predicate( + '[ ! -f "${}" ]'.format(single_ca_path_variable), + ['Invalid CA certificate filename: ${}'.format( + single_ca_path_variable), + 'Please check that the path exists and is a valid file'] + ) +- self.log.command("done") + + def upload_smartcard_ca_certificates_to_systemwide_db(self): +- self.log.command( +- "for {} in ${}".format( ++ with self.log.for_loop( + self.single_ca_cert_variable_name, +- self.smart_card_ca_certs_variable_name)) +- self.log.command("do") +- with self.log.indented_block(): ++ '${}'.format(self.smart_card_ca_certs_variable_name)): + self.log.command( + 'certutil -d {} -A -i ${} -n "Smart Card CA $(uuidgen)" ' + '-t CT,C,C'.format( + self.systemwide_nssdb, self.single_ca_cert_variable_name +- ), ++ ) + ) +- self.log.command("done") + + def install_smart_card_signing_ca_certs(self): +- self.log.command( +- "for {} in ${}".format( ++ with self.log.for_loop( + self.single_ca_cert_variable_name, +- self.smart_card_ca_certs_variable_name)) +- self.log.command("do") +- with self.log.indented_block(): ++ '${}'.format(self.smart_card_ca_certs_variable_name)): + self.log.exit_on_failed_command( + 'ipa-cacert-manage install ${} -t CT,C,C'.format( + self.single_ca_cert_variable_name + ), + ['Failed to install external CA certificate to IPA'] + ) +- self.log.command("done") + + def update_ipa_ca_certificate_store(self): + self.log.exit_on_failed_command( +-- +2.9.4 + diff --git a/SOURCES/0216-smart-card-advise-use-password-when-changing-trust-f.patch b/SOURCES/0216-smart-card-advise-use-password-when-changing-trust-f.patch new file mode 100644 index 0000000..c49524c --- /dev/null +++ b/SOURCES/0216-smart-card-advise-use-password-when-changing-trust-f.patch @@ -0,0 +1,54 @@ +From 4a9ff573f1c9c91e1e2e1e2d7de70951b7333fb4 Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Fri, 23 Jun 2017 15:47:48 +0200 +Subject: [PATCH] smart card advise: use password when changing trust flags on + HTTP cert + +This is to prevent NSS asking for database password when operating in +FIPS 140 mode. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 3ff94be1e8b108668989602b1b406a39d23ff501..5134db535e8f10e8cf850dbf0696b679aacec4f5 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -2,6 +2,8 @@ + # Copyright (C) 2017 FreeIPA Contributors see COPYING for license + # + ++import os ++ + from ipalib.plugable import Registry + from ipaplatform import services + from ipaplatform.paths import paths +@@ -172,6 +174,8 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + return fmt_line.format(directive=directive, filename=filename) + + def mark_httpd_cert_as_trusted(self): ++ httpd_nss_database_pwd_file = os.path.join( ++ paths.HTTPD_ALIAS_DIR, 'pwdfile.txt') + self.log.comment( + 'mark the HTTP certificate as trusted peer to avoid ' + 'chicken-egg startup issue') +@@ -181,8 +185,9 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config): + " cut -f 2 -d ' ')")) + + self.log.exit_on_failed_command( +- 'certutil -M -n $http_cert_nick -d "{}" -t "Pu,u,u"'.format( +- paths.HTTPD_ALIAS_DIR), ++ 'certutil -M -n $http_cert_nick -d "{}" -f {} -t "Pu,u,u"'.format( ++ paths.HTTPD_ALIAS_DIR, ++ httpd_nss_database_pwd_file), + ['Can not set trust flags on HTTP certificate']) + + def _interpolate_nssnickname_directive_file_into_command(self, fmt_line): +-- +2.9.4 + diff --git a/SOURCES/0217-smart-card-advises-ensure-that-krb5-pkinit-is-instal.patch b/SOURCES/0217-smart-card-advises-ensure-that-krb5-pkinit-is-instal.patch new file mode 100644 index 0000000..66e090e --- /dev/null +++ b/SOURCES/0217-smart-card-advises-ensure-that-krb5-pkinit-is-instal.patch @@ -0,0 +1,46 @@ +From aa123edfdab1836c0915bb75f3bf82e46083b17f Mon Sep 17 00:00:00 2001 +From: Martin Babinsky +Date: Wed, 28 Jun 2017 09:49:18 +0200 +Subject: [PATCH] smart-card-advises: ensure that krb5-pkinit is installed on + client + +This library is a prerequisite for successful Smart Card authentication +on the client. The client-side advise should make sure this dependency +is present. + +https://pagure.io/freeipa/issue/7036 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/advise/plugins/smart_card_auth.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py +index 5134db535e8f10e8cf850dbf0696b679aacec4f5..fb328f29ca5051ad52c9c5e0000021ad5e8b94e8 100644 +--- a/ipaserver/advise/plugins/smart_card_auth.py ++++ b/ipaserver/advise/plugins/smart_card_auth.py +@@ -256,6 +256,7 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config): + self.check_ccache_not_empty() + self.check_and_remove_pam_pkcs11() + self.install_opensc_and_dconf_packages() ++ self.install_krb5_client_dependencies() + self.start_enable_smartcard_daemon() + self.add_pkcs11_module_to_systemwide_db() + self.upload_smartcard_ca_certificates_to_systemwide_db() +@@ -281,6 +282,12 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config): + ['Could not install OpenSC package'] + ) + ++ def install_krb5_client_dependencies(self): ++ self.log.exit_on_failed_command( ++ 'yum install -y krb5-pkinit-openssl', ++ ['Failed to install Kerberos client PKINIT extensions.'] ++ ) ++ + def start_enable_smartcard_daemon(self): + self.log.command( + 'systemctl start {service} {socket} ' +-- +2.9.4 + diff --git a/SOURCES/0218-NULL-LDAP-context-in-call-to-ldap_search_ext_s-durin.patch b/SOURCES/0218-NULL-LDAP-context-in-call-to-ldap_search_ext_s-durin.patch new file mode 100644 index 0000000..d4ac0c7 --- /dev/null +++ b/SOURCES/0218-NULL-LDAP-context-in-call-to-ldap_search_ext_s-durin.patch @@ -0,0 +1,33 @@ +From 0703a575b4e337e7ce41860956bd339c12cd44ea Mon Sep 17 00:00:00 2001 +From: Thierry Bordaz +Date: Tue, 20 Jun 2017 18:22:33 +0200 +Subject: [PATCH] NULL LDAP context in call to ldap_search_ext_s during search + + KDC crashes on quite random interval while trying to reach LDAP + https://pagure.io/freeipa/issue/7017 + +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-kdb/ipa_kdb.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c +index 050bfc90cef1bce4c932f54bb6050438c60ca79f..c0f1e276ca32ecb318add3a0d36f57acc3d17d51 100644 +--- a/daemons/ipa-kdb/ipa_kdb.c ++++ b/daemons/ipa-kdb/ipa_kdb.c +@@ -465,6 +465,12 @@ int ipadb_get_connection(struct ipadb_context *ipactx) + ret = ipadb_reinit_mspac(ipactx, false); + if (ret && ret != ENOENT) { + /* TODO: log that there is an issue with adtrust settings */ ++ if (ipactx->lcontext == NULL) { ++ /* for some reason ldap connection was reset in ipadb_reinit_mspac ++ * and is no longer established => failure of ipadb_get_connection ++ */ ++ goto done; ++ } + } + + ret = 0; +-- +2.9.4 + diff --git a/SOURCES/0219-Restore-old-version-of-caIPAserviceCert-for-upgrade-.patch b/SOURCES/0219-Restore-old-version-of-caIPAserviceCert-for-upgrade-.patch new file mode 100644 index 0000000..0c35b69 --- /dev/null +++ b/SOURCES/0219-Restore-old-version-of-caIPAserviceCert-for-upgrade-.patch @@ -0,0 +1,187 @@ +From 533f2539cbc8fe5b4bb748982a6cfee7d73416e6 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 9 Aug 2017 12:55:57 +1000 +Subject: [PATCH] Restore old version of caIPAserviceCert for upgrade only + +The latest version of caIPAserviceCert profile includes a feature +that is not available before Dogtag 10.4, and this version of the +profile is intended for new installs only (otherwise, problems will +arise in topologies containing CA replicas at an earlier version). +But IPA versions before v4.2 did not use LDAP-based profiles, so the +new version of the profile gets imported when upgrading from +pre-v4.2 to v4.5 or later. + +We do not yet have a proper version- and topology-aware profile +update mechanism, so to resolve this issue, ship the older version +of the profile alongside the newer version, and make sure we use the +older version when importing the profile in an upgrade context. + +https://pagure.io/freeipa/issue/7097 + +Reviewed-By: Florence Blanc-Renaud +--- + install/share/profiles/Makefile.am | 1 + + .../share/profiles/caIPAserviceCert.UPGRADE.cfg | 109 +++++++++++++++++++++ + ipaserver/install/cainstance.py | 18 +++- + 3 files changed, 126 insertions(+), 2 deletions(-) + create mode 100644 install/share/profiles/caIPAserviceCert.UPGRADE.cfg + +diff --git a/install/share/profiles/Makefile.am b/install/share/profiles/Makefile.am +index 640ca0a4a54c574da57b62b2b3c23f6db78df2fb..7f188e3fcac2ad80558399015d49216caa32c14b 100644 +--- a/install/share/profiles/Makefile.am ++++ b/install/share/profiles/Makefile.am +@@ -3,6 +3,7 @@ NULL = + appdir = $(IPA_DATA_DIR)/profiles + app_DATA = \ + caIPAserviceCert.cfg \ ++ caIPAserviceCert.UPGRADE.cfg \ + IECUserRoles.cfg \ + KDCs_PKINIT_Certs.cfg \ + $(NULL) +diff --git a/install/share/profiles/caIPAserviceCert.UPGRADE.cfg b/install/share/profiles/caIPAserviceCert.UPGRADE.cfg +new file mode 100644 +index 0000000000000000000000000000000000000000..1efd2066b9f75b4e26c390932353f20141d800b9 +--- /dev/null ++++ b/install/share/profiles/caIPAserviceCert.UPGRADE.cfg +@@ -0,0 +1,109 @@ ++profileId=caIPAserviceCert ++classId=caEnrollImpl ++desc=This certificate profile is for enrolling server certificates with IPA-RA agent authentication. ++visible=false ++enable=true ++enableBy=admin ++auth.instance_id=raCertAuth ++name=IPA-RA Agent-Authenticated Server Certificate Enrollment ++input.list=i1,i2 ++input.i1.class_id=certReqInputImpl ++input.i2.class_id=submitterInfoInputImpl ++output.list=o1 ++output.o1.class_id=certOutputImpl ++policyset.list=serverCertSet ++policyset.serverCertSet.list=1,2,3,4,5,6,7,8,9,10,11 ++policyset.serverCertSet.1.constraint.class_id=subjectNameConstraintImpl ++policyset.serverCertSet.1.constraint.name=Subject Name Constraint ++policyset.serverCertSet.1.constraint.params.pattern=CN=[^,]+,.+ ++policyset.serverCertSet.1.constraint.params.accept=true ++policyset.serverCertSet.1.default.class_id=subjectNameDefaultImpl ++policyset.serverCertSet.1.default.name=Subject Name Default ++policyset.serverCertSet.1.default.params.name=CN=$$request.req_subject_name.cn$$, $SUBJECT_DN_O ++policyset.serverCertSet.2.constraint.class_id=validityConstraintImpl ++policyset.serverCertSet.2.constraint.name=Validity Constraint ++policyset.serverCertSet.2.constraint.params.range=740 ++policyset.serverCertSet.2.constraint.params.notBeforeCheck=false ++policyset.serverCertSet.2.constraint.params.notAfterCheck=false ++policyset.serverCertSet.2.default.class_id=validityDefaultImpl ++policyset.serverCertSet.2.default.name=Validity Default ++policyset.serverCertSet.2.default.params.range=731 ++policyset.serverCertSet.2.default.params.startTime=0 ++policyset.serverCertSet.3.constraint.class_id=keyConstraintImpl ++policyset.serverCertSet.3.constraint.name=Key Constraint ++policyset.serverCertSet.3.constraint.params.keyType=RSA ++policyset.serverCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,8192 ++policyset.serverCertSet.3.default.class_id=userKeyDefaultImpl ++policyset.serverCertSet.3.default.name=Key Default ++policyset.serverCertSet.4.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.4.constraint.name=No Constraint ++policyset.serverCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl ++policyset.serverCertSet.4.default.name=Authority Key Identifier Default ++policyset.serverCertSet.5.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.5.constraint.name=No Constraint ++policyset.serverCertSet.5.default.class_id=authInfoAccessExtDefaultImpl ++policyset.serverCertSet.5.default.name=AIA Extension Default ++policyset.serverCertSet.5.default.params.authInfoAccessADEnable_0=true ++policyset.serverCertSet.5.default.params.authInfoAccessADLocationType_0=URIName ++policyset.serverCertSet.5.default.params.authInfoAccessADLocation_0=http://$IPA_CA_RECORD.$DOMAIN/ca/ocsp ++policyset.serverCertSet.5.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1 ++policyset.serverCertSet.5.default.params.authInfoAccessCritical=false ++policyset.serverCertSet.5.default.params.authInfoAccessNumADs=1 ++policyset.serverCertSet.6.constraint.class_id=keyUsageExtConstraintImpl ++policyset.serverCertSet.6.constraint.name=Key Usage Extension Constraint ++policyset.serverCertSet.6.constraint.params.keyUsageCritical=true ++policyset.serverCertSet.6.constraint.params.keyUsageDigitalSignature=true ++policyset.serverCertSet.6.constraint.params.keyUsageNonRepudiation=true ++policyset.serverCertSet.6.constraint.params.keyUsageDataEncipherment=true ++policyset.serverCertSet.6.constraint.params.keyUsageKeyEncipherment=true ++policyset.serverCertSet.6.constraint.params.keyUsageKeyAgreement=false ++policyset.serverCertSet.6.constraint.params.keyUsageKeyCertSign=false ++policyset.serverCertSet.6.constraint.params.keyUsageCrlSign=false ++policyset.serverCertSet.6.constraint.params.keyUsageEncipherOnly=false ++policyset.serverCertSet.6.constraint.params.keyUsageDecipherOnly=false ++policyset.serverCertSet.6.default.class_id=keyUsageExtDefaultImpl ++policyset.serverCertSet.6.default.name=Key Usage Default ++policyset.serverCertSet.6.default.params.keyUsageCritical=true ++policyset.serverCertSet.6.default.params.keyUsageDigitalSignature=true ++policyset.serverCertSet.6.default.params.keyUsageNonRepudiation=true ++policyset.serverCertSet.6.default.params.keyUsageDataEncipherment=true ++policyset.serverCertSet.6.default.params.keyUsageKeyEncipherment=true ++policyset.serverCertSet.6.default.params.keyUsageKeyAgreement=false ++policyset.serverCertSet.6.default.params.keyUsageKeyCertSign=false ++policyset.serverCertSet.6.default.params.keyUsageCrlSign=false ++policyset.serverCertSet.6.default.params.keyUsageEncipherOnly=false ++policyset.serverCertSet.6.default.params.keyUsageDecipherOnly=false ++policyset.serverCertSet.7.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.7.constraint.name=No Constraint ++policyset.serverCertSet.7.default.class_id=extendedKeyUsageExtDefaultImpl ++policyset.serverCertSet.7.default.name=Extended Key Usage Extension Default ++policyset.serverCertSet.7.default.params.exKeyUsageCritical=false ++policyset.serverCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2 ++policyset.serverCertSet.8.constraint.class_id=signingAlgConstraintImpl ++policyset.serverCertSet.8.constraint.name=No Constraint ++policyset.serverCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withEC,SHA512withEC ++policyset.serverCertSet.8.default.class_id=signingAlgDefaultImpl ++policyset.serverCertSet.8.default.name=Signing Alg ++policyset.serverCertSet.8.default.params.signingAlg=- ++policyset.serverCertSet.9.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.9.constraint.name=No Constraint ++policyset.serverCertSet.9.default.class_id=crlDistributionPointsExtDefaultImpl ++policyset.serverCertSet.9.default.name=CRL Distribution Points Extension Default ++policyset.serverCertSet.9.default.params.crlDistPointsCritical=false ++policyset.serverCertSet.9.default.params.crlDistPointsNum=1 ++policyset.serverCertSet.9.default.params.crlDistPointsEnable_0=true ++policyset.serverCertSet.9.default.params.crlDistPointsIssuerName_0=$CRL_ISSUER ++policyset.serverCertSet.9.default.params.crlDistPointsIssuerType_0=DirectoryName ++policyset.serverCertSet.9.default.params.crlDistPointsPointName_0=http://$IPA_CA_RECORD.$DOMAIN/ipa/crl/MasterCRL.bin ++policyset.serverCertSet.9.default.params.crlDistPointsPointType_0=URIName ++policyset.serverCertSet.9.default.params.crlDistPointsReasons_0= ++policyset.serverCertSet.10.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.10.constraint.name=No Constraint ++policyset.serverCertSet.10.default.class_id=subjectKeyIdentifierExtDefaultImpl ++policyset.serverCertSet.10.default.name=Subject Key Identifier Extension Default ++policyset.serverCertSet.10.default.params.critical=false ++policyset.serverCertSet.11.constraint.class_id=noConstraintImpl ++policyset.serverCertSet.11.constraint.name=No Constraint ++policyset.serverCertSet.11.default.class_id=userExtensionDefaultImpl ++policyset.serverCertSet.11.default.name=User Supplied Extension Default ++policyset.serverCertSet.11.default.params.userExtOID=2.5.29.17 +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index b0e9e8757ec3e3c0d03ed930743ef5a1253b864a..62f79b28000b015edb66f4c39a270097ab3ed666 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -1568,8 +1568,22 @@ def __get_profile_config(profile_id): + CRL_ISSUER='CN=Certificate Authority,o=ipaca', + SUBJECT_DN_O=dsinstance.DsInstance().find_subject_base(), + ) +- return ipautil.template_file( +- '/usr/share/ipa/profiles/{}.cfg'.format(profile_id), sub_dict) ++ ++ # To work around lack of proper profile upgrade system, we ship ++ # two versions of some profiles - one for new installs only, and ++ # the other for upgrading to LDAP-based profiles in an existing ++ # deployment. ++ # ++ # Select UPGRADE version if we are in the 'updates' API context ++ # and an upgrade-specific version of the profile exists. ++ # ++ profile_filename = '/usr/share/ipa/profiles/{}.cfg'.format(profile_id) ++ profile_upg_filename = \ ++ '/usr/share/ipa/profiles/{}.UPGRADE.cfg'.format(profile_id) ++ if api.env.context == 'updates' and os.path.isfile(profile_upg_filename): ++ profile_filename = profile_upg_filename ++ ++ return ipautil.template_file(profile_filename, sub_dict) + + def import_included_profiles(): + server_id = installutils.realm_to_serverid(api.env.realm) +-- +2.9.4 + diff --git a/SOURCES/0220-ipa-otptoken-import-Make-PBKDF2-refer-to-the-pkcs5-n.patch b/SOURCES/0220-ipa-otptoken-import-Make-PBKDF2-refer-to-the-pkcs5-n.patch new file mode 100644 index 0000000..b56a901 --- /dev/null +++ b/SOURCES/0220-ipa-otptoken-import-Make-PBKDF2-refer-to-the-pkcs5-n.patch @@ -0,0 +1,84 @@ +From 92f450a4b6eacb7950e5414d40d9949076cb096e Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Tue, 20 Jun 2017 10:31:15 -0400 +Subject: [PATCH] ipa-otptoken-import: Make PBKDF2 refer to the pkcs5 namespace + +For some unknown reason, when I wrote the ipa-otptoken-import script +I used bad input data which had the PBKDF2 parameters in the wrong +XML namespace. I have corrected this input data to match RFC 6030. + +https://pagure.io/freeipa/issue/7035 + +Signed-off-by: Nathaniel McCallum +Reviewed-By: Martin Basti +Reviewed-By: Stanislav Laznicka +--- + ipaserver/install/ipa_otptoken_import.py | 15 ++++++--------- + ipatests/test_ipaserver/data/pskc-figure7.xml | 16 ++++++++-------- + 2 files changed, 14 insertions(+), 17 deletions(-) + +diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py +index 2580e2cfc97f4960af68a5eae407a7ebe3c7a257..31225e96b55c20bd78e9a8650848a28cf9feef63 100644 +--- a/ipaserver/install/ipa_otptoken_import.py ++++ b/ipaserver/install/ipa_otptoken_import.py +@@ -52,6 +52,7 @@ class ValidationError(Exception): + + def fetchAll(element, xpath, conv=lambda x: x): + return [conv(e) for e in element.xpath(xpath, namespaces={ ++ "pkcs5": "http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#", + "pskc": "urn:ietf:params:xml:ns:keyprov:pskc", + "xenc11": "http://www.w3.org/2009/xmlenc11#", + "xenc": "http://www.w3.org/2001/04/xmlenc#", +@@ -175,18 +176,14 @@ class XMLKeyDerivation(six.with_metaclass(abc.ABCMeta, object)): + + class PBKDF2KeyDerivation(XMLKeyDerivation): + def __init__(self, enckey): +- params = fetch(enckey, "./xenc11:DerivedKey/xenc11:KeyDerivationMethod/xenc11:PBKDF2-params") ++ params = fetch(enckey, "./xenc11:DerivedKey/xenc11:KeyDerivationMethod/pkcs5:PBKDF2-params") + if params is None: + raise ValueError("XML file is missing PBKDF2 parameters!") + +- salt = fetch( +- params, "./xenc11:Salt/xenc11:Specified/text()", base64.b64decode) +- itrs = fetch( +- params, "./xenc11:IterationCount/text()", int) +- klen = fetch( +- params, "./xenc11:KeyLength/text()", int) +- hmod = fetch( +- params, "./xenc11:PRF/@Algorithm", convertHMACType, hashes.SHA1) ++ salt = fetch(params, "./Salt/Specified/text()", base64.b64decode) ++ itrs = fetch(params, "./IterationCount/text()", int) ++ klen = fetch(params, "./KeyLength/text()", int) ++ hmod = fetch(params, "./PRF/@Algorithm", convertHMACType, hashes.SHA1) + + if salt is None: + raise ValueError("XML file is missing PBKDF2 salt!") +diff --git a/ipatests/test_ipaserver/data/pskc-figure7.xml b/ipatests/test_ipaserver/data/pskc-figure7.xml +index 1fb04fc319d7572d9d25ff34a0ce3378a939dfc6..808e272a5469a1c9eb4087ed53e0907bb80b39ad 100644 +--- a/ipatests/test_ipaserver/data/pskc-figure7.xml ++++ b/ipatests/test_ipaserver/data/pskc-figure7.xml +@@ -8,14 +8,14 @@ + + +- +- +- Ej7/PEpyEpw= +- +- 1000 +- 16 +- +- ++ ++ ++ Ej7/PEpyEpw= ++ ++ 1000 ++ 16 ++ ++ + + + +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0221-Adds-whoami-DS-plugin-in-case-that-plugin-is-missing.patch b/SOURCES/0221-Adds-whoami-DS-plugin-in-case-that-plugin-is-missing.patch new file mode 100644 index 0000000..e247dd1 --- /dev/null +++ b/SOURCES/0221-Adds-whoami-DS-plugin-in-case-that-plugin-is-missing.patch @@ -0,0 +1,59 @@ +From d06b29772609d14dccfe0d556fdb83140fcb2b3f Mon Sep 17 00:00:00 2001 +From: Pavel Vomacka +Date: Mon, 28 Aug 2017 10:51:53 +0200 +Subject: [PATCH] Adds whoami DS plugin in case that plugin is missing + +When first installation of IPA has been done when whoami +plugin was not enabled in DS by default and then IPA was +upgraded to newer versions, then after upgrade to IPA 4.5 +WebUI stops working. This is caused by new requirement on +whoami DS plugin which is used to obtain information about +logged in entity. + +This fix adds the whoami plugin during update in case that the plugin +is not enabled. + +https://pagure.io/freeipa/issue/7126 + +Reviewed-By: Tibor Dudlak +Reviewed-By: Rob Crittenden +--- + install/updates/20-whoami.update | 14 ++++++++++++++ + install/updates/Makefile.am | 1 + + 2 files changed, 15 insertions(+) + create mode 100644 install/updates/20-whoami.update + +diff --git a/install/updates/20-whoami.update b/install/updates/20-whoami.update +new file mode 100644 +index 0000000000000000000000000000000000000000..ed2c6cbd772ec1c2b664e450463bb64d61b1ceab +--- /dev/null ++++ b/install/updates/20-whoami.update +@@ -0,0 +1,14 @@ ++dn: cn=whoami,cn=plugins,cn=config ++default:objectClass: top ++default:objectClass: nsSlapdPlugin ++default:objectClass: extensibleObject ++default:cn: whoami ++default:nsslapd-plugin-depends-on-type: database ++default:nsslapd-pluginDescription: whoami extended operation plugin ++default:nsslapd-pluginEnabled: on ++default:nsslapd-pluginId: whoami-plugin ++default:nsslapd-pluginInitfunc: whoami_init ++default:nsslapd-pluginPath: libwhoami-plugin ++default:nsslapd-pluginType: extendedop ++default:nsslapd-pluginVendor: 389 Project ++default:nsslapd-pluginVersion: 1.0 +diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am +index e18d01127b592a6c7941729d6160d10fb2d3e76c..ae3d3e0528929a30922f0395d7092654dd753a64 100644 +--- a/install/updates/Makefile.am ++++ b/install/updates/Makefile.am +@@ -24,6 +24,7 @@ app_DATA = \ + 20-idoverride_index.update \ + 20-uuid.update \ + 20-default_password_policy.update \ ++ 20-whoami.update \ + 21-replicas_container.update \ + 21-ca_renewal_container.update \ + 21-certstore_container.update \ +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0222-Fix-ipa-config-mod-ca-renewal-master.patch b/SOURCES/0222-Fix-ipa-config-mod-ca-renewal-master.patch new file mode 100644 index 0000000..b4778ba --- /dev/null +++ b/SOURCES/0222-Fix-ipa-config-mod-ca-renewal-master.patch @@ -0,0 +1,88 @@ +From 9a8352637aeb32ddffd83f4477695ec290da8429 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 23 Aug 2017 16:31:18 +0200 +Subject: [PATCH] Fix ipa config-mod --ca-renewal-master + +commit bddb90f38a3505a2768862d2f814c5e749a7dcde added the support for +multivalued server attributes (for pkinit_server_server), but this +introduced an API change where the setter and getter of ServerAttribute +are expecting list of values. + +When a SingleValuedServerAttribute is used, we need to convert one elem +into a list containing this elem and vice-versa, so that the ipa config-mod +and ipa config_show APIs are not modified. + +https://pagure.io/freeipa/issue/7120 + +Reviewed-By: Alexander Bokovoy +Reviewed-By: Fraser Tweedale +--- + ipaserver/plugins/serverroles.py | 16 +++++++++++++++- + ipatests/test_ipaserver/test_serverroles.py | 4 ++-- + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/plugins/serverroles.py b/ipaserver/plugins/serverroles.py +index e81635c3315cc3fca84450f43fb7df883aae57d9..04e21090657197b9267f2ffc05048399a7ce3d38 100644 +--- a/ipaserver/plugins/serverroles.py ++++ b/ipaserver/plugins/serverroles.py +@@ -46,6 +46,7 @@ from ipalib import errors, _ + from ipalib.backend import Backend + from ipalib.plugable import Registry + from ipaserver.servroles import (attribute_instances, ENABLED, role_instances) ++from ipaserver.servroles import SingleValuedServerAttribute + + + if six.PY3: +@@ -136,13 +137,26 @@ class serverroles(Backend): + + for name, attr in assoc_attributes.items(): + attr_value = attr.get(self.api) +- result.update({name: attr_value}) ++ ++ if attr_value: ++ # attr can be a SingleValuedServerAttribute ++ # in this case, the API expects a value, not a list of values ++ if isinstance(attr, SingleValuedServerAttribute): ++ attr_value = attr_value[0] ++ result.update({name: attr_value}) + + return result + + def config_update(self, **attrs_values): + for attr, value in attrs_values.items(): + try: ++ # when the attribute is single valued, it will be stored ++ # in a SingleValuedServerAttribute. The set method expects ++ # a list containing a single value. ++ # We need to convert value to a list containing value ++ if isinstance(self.attributes[attr], ++ SingleValuedServerAttribute): ++ value = [value] + self.attributes[attr].set(self.api, value) + except KeyError: + raise errors.NotFound( +diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py +index 985c750b64f109e0a83686f31ddb3b8d4171072d..e8967517d0c65fb6e3daebf220cae7df38bfe044 100644 +--- a/ipatests/test_ipaserver/test_serverroles.py ++++ b/ipatests/test_ipaserver/test_serverroles.py +@@ -715,7 +715,7 @@ class TestServerAttributes(object): + non_ca_fqdn = mock_masters.get_fqdn('trust-controller-dns') + + with pytest.raises(errors.ValidationError): +- self.config_update(mock_api, **{attr_name: [non_ca_fqdn]}) ++ self.config_update(mock_api, **{attr_name: non_ca_fqdn}) + + def test_set_unknown_attribute_on_master_raises_notfound( + self, mock_api, mock_masters): +@@ -732,7 +732,7 @@ class TestServerAttributes(object): + original_renewal_master = self.config_retrieve( + role_name, mock_api)[attr_name] + +- other_ca_server = [mock_masters.get_fqdn('trust-controller-ca')] ++ other_ca_server = mock_masters.get_fqdn('trust-controller-ca') + + for host in (other_ca_server, original_renewal_master): + self.config_update(mock_api, **{attr_name: host}) +-- +2.13.5 + diff --git a/SOURCES/0223-Backport-PR-988-to-ipa-4-5-Fix-Certificate-renewal-w.patch b/SOURCES/0223-Backport-PR-988-to-ipa-4-5-Fix-Certificate-renewal-w.patch new file mode 100644 index 0000000..db614e2 --- /dev/null +++ b/SOURCES/0223-Backport-PR-988-to-ipa-4-5-Fix-Certificate-renewal-w.patch @@ -0,0 +1,60 @@ +From 21b0fdb48179e6060eff0ecb11ce6522983ccc00 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 18 Aug 2017 18:02:57 +0200 +Subject: [PATCH] Backport PR 988 to ipa-4-5 Fix Certificate renewal (with ext + ca) + +Fix certificate renewal scripts that use IPACertificate object: +- renew_ca_cert adds the C flag to the trust flags and needs to +be adapted to IPACertificate object +- ipa-cacert-manage: fix python3 encoding issue + +https://pagure.io/freeipa/issue/7106 + +Reviewed-By: Fraser Tweedale +Reviewed-By: Stanislav Laznicka +--- + install/restart_scripts/renew_ca_cert | 7 ++++++- + ipaserver/install/ipa_cacert_manage.py | 2 +- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert +index bb31defc0e2bdca044e68ae067f42fb3bd41a57f..3bbf003bad47a189fd26df19e6ab137fcbb67ed0 100644 +--- a/install/restart_scripts/renew_ca_cert ++++ b/install/restart_scripts/renew_ca_cert +@@ -35,6 +35,7 @@ from ipaserver.install import certs, cainstance, installutils + from ipaserver.plugins.ldap2 import ldap2 + from ipaplatform import services + from ipaplatform.paths import paths ++from ipapython.certdb import TrustFlags + + + def _main(): +@@ -180,7 +181,11 @@ def _main(): + # Pass Dogtag's self-tests + for ca_nick in db.find_root_cert(nickname)[-2:-1]: + ca_flags = dict(cc[1:] for cc in ca_certs)[ca_nick] +- db.trust_root_cert(ca_nick, 'C' + ca_flags) ++ usages = ca_flags.usages or set() ++ ca_flags_modified = TrustFlags(ca_flags.has_key, ++ True, True, ++ usages | {x509.EKU_SERVER_AUTH}) ++ db.trust_root_cert(ca_nick, ca_flags_modified) + finally: + if conn is not None and conn.isconnected(): + conn.disconnect() +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index e88e8b63ae94759ac835f3b3b31b0735d68a67b0..fcbf09155a3abc9ce9481aa2519ed39aaa6aa9bb 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -218,7 +218,7 @@ class CACertManage(admintool.AdminTool): + cert_file, ca_file = installutils.load_external_cert( + options.external_cert_files, DN(old_cert_obj.subject)) + +- with open(cert_file.name) as f: ++ with open(cert_file.name, 'rb') as f: + new_cert_data = f.read() + new_cert_der = x509.normalize_certificate(new_cert_data) + new_cert_obj = x509.load_certificate(new_cert_der, x509.DER) +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0224-Backport-PR-1008-to-ipa-4-5-Fix-ipa-server-upgrade-T.patch b/SOURCES/0224-Backport-PR-1008-to-ipa-4-5-Fix-ipa-server-upgrade-T.patch new file mode 100644 index 0000000..7de886b --- /dev/null +++ b/SOURCES/0224-Backport-PR-1008-to-ipa-4-5-Fix-ipa-server-upgrade-T.patch @@ -0,0 +1,213 @@ +From c9fb09190ac243bcf45622693944d7e6785141b4 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 28 Aug 2017 10:50:58 +0200 +Subject: [PATCH] Backport PR 1008 to ipa-4-5 Fix ipa-server-upgrade: This + entry already exists + +ipa-server-upgrade fails when running the ipaload_cacrt plugin. The plugin +finds all CA certificates in /etc/httpd/alias and uploads them in LDAP +below cn=certificates,cn=ipa,cn=etc,$BASEDN. +The issue happens because there is already an entry in LDAP for IPA CA, but +with a different DN. The nickname in /etc/httpd/alias can differ from +$DOMAIN IPA CA. + +To avoid the issue: +1/ during upgrade, run a new plugin that removes duplicates and restarts ldap +(to make sure that uniqueness attr plugin is working after the new plugin) +2/ modify upload_cacert plugin so that it is using $DOMAIN IPA CA instead of +cn=$nickname,cn=ipa,cn=etc,$BASEDN when uploading IPA CA. + +https://pagure.io/freeipa/issue/7125 + +Reviewed-By: Fraser Tweedale +--- + install/updates/90-post_upgrade_plugins.update | 1 + + ipalib/install/certstore.py | 19 +++++ + .../plugins/update_fix_duplicate_cacrt_in_ldap.py | 84 ++++++++++++++++++++++ + ipaserver/install/plugins/upload_cacrt.py | 19 ++++- + 4 files changed, 120 insertions(+), 3 deletions(-) + create mode 100644 ipaserver/install/plugins/update_fix_duplicate_cacrt_in_ldap.py + +diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update +index 8477199e07d6729d5847e58bfa67d061bd1410c2..bbc3e29422fc0f139c2ca68a7033863e4c25f8cf 100644 +--- a/install/updates/90-post_upgrade_plugins.update ++++ b/install/updates/90-post_upgrade_plugins.update +@@ -15,6 +15,7 @@ plugin: update_ca_renewal_master + plugin: update_idrange_type + plugin: update_pacs + plugin: update_service_principalalias ++plugin: update_fix_duplicate_cacrt_in_ldap + plugin: update_upload_cacrt + # update_ra_cert_store has to be executed after update_ca_renewal_master + plugin: update_ra_cert_store +diff --git a/ipalib/install/certstore.py b/ipalib/install/certstore.py +index bc2079fb12873444cbe6796eebfdfcfebd0e284d..76181fe47de585974f3fb33ec586f5c576adebb5 100644 +--- a/ipalib/install/certstore.py ++++ b/ipalib/install/certstore.py +@@ -27,6 +27,7 @@ from pyasn1.error import PyAsn1Error + from ipapython.dn import DN + from ipapython.certdb import get_ca_nickname, TrustFlags + from ipalib import errors, x509 ++from ipalib.constants import IPA_CA_CN + + def _parse_cert(dercert): + try: +@@ -381,3 +382,21 @@ def get_ca_certs_nss(ldap, base_dn, compat_realm, compat_ipa_ca, + nss_certs.append((cert, nickname, trust_flags)) + + return nss_certs ++ ++ ++def get_ca_subject(ldap, container_ca, base_dn): ++ """ ++ Look for the IPA CA certificate subject. ++ """ ++ dn = DN(('cn', IPA_CA_CN), container_ca, base_dn) ++ try: ++ cacert_subject = ldap.get_entry(dn)['ipacasubjectdn'][0] ++ except errors.NotFound: ++ # if the entry doesn't exist, we are dealing with a pre-v4.4 ++ # installation, where the default CA subject was always based ++ # on the subject_base. ++ attrs = ldap.get_ipa_config() ++ subject_base = attrs.get('ipacertificatesubjectbase')[0] ++ cacert_subject = DN(('CN', 'Certificate Authority'), subject_base) ++ ++ return cacert_subject +diff --git a/ipaserver/install/plugins/update_fix_duplicate_cacrt_in_ldap.py b/ipaserver/install/plugins/update_fix_duplicate_cacrt_in_ldap.py +new file mode 100644 +index 0000000000000000000000000000000000000000..cd4f13a8eb6b5bc9e04fcdd407907497528f8be1 +--- /dev/null ++++ b/ipaserver/install/plugins/update_fix_duplicate_cacrt_in_ldap.py +@@ -0,0 +1,84 @@ ++# Authors: ++# Florence Blanc-Renaud ++# ++# Copyright (C) 2017 Red Hat ++# see file 'COPYING' for use and warranty information ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++import logging ++ ++from ipalib import Registry, errors ++from ipalib import Updater ++from ipalib.install import certstore ++from ipapython.dn import DN ++from ipapython.certdb import get_ca_nickname ++ ++logger = logging.getLogger(__name__) ++ ++register = Registry() ++ ++ ++@register() ++class update_fix_duplicate_cacrt_in_ldap(Updater): ++ """ ++ When multiple entries exist for IPA CA cert in ldap, remove the duplicate ++ ++ After this plugin, ds needs to be restarted. This ensures that ++ the attribute uniqueness plugin is working and prevents ++ other plugins from adding duplicates. ++ """ ++ ++ def execute(self, **options): ++ # If CA is disabled, no need to check for duplicates of IPA CA ++ ca_enabled = self.api.Command.ca_is_enabled()['result'] ++ if not ca_enabled: ++ return True, [] ++ ++ # Look for the IPA CA cert subject ++ ldap = self.api.Backend.ldap2 ++ cacert_subject = certstore.get_ca_subject( ++ ldap, ++ self.api.env.container_ca, ++ self.api.env.basedn) ++ ++ # Find if there are other certificates with the same subject ++ # They are duplicates resulting of BZ 1480102 ++ base_dn = DN(('cn', 'certificates'), ('cn', 'ipa'), ('cn', 'etc'), ++ self.api.env.basedn) ++ try: ++ filter = ldap.make_filter({'ipaCertSubject': cacert_subject}) ++ result, _truncated = ldap.find_entries( ++ base_dn=base_dn, ++ filter=filter, ++ attrs_list=[]) ++ except errors.NotFound: ++ # No duplicate, we're good ++ logger.debug("No duplicates for IPA CA in LDAP") ++ return True, [] ++ ++ logger.debug("Found %d entrie(s) for IPA CA in LDAP", len(result)) ++ cacert_dn = DN(('cn', get_ca_nickname(self.api.env.realm)), base_dn) ++ for entry in result: ++ if entry.dn == cacert_dn: ++ continue ++ # Remove the duplicate ++ try: ++ ldap.delete_entry(entry) ++ logger.debug("Removed the duplicate %s", entry.dn) ++ except Exception as e: ++ logger.warning("Failed to remove the duplicate %s: %s", ++ entry.dn, e) ++ ++ return True, [] +diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py +index a1957ca5b675b86f0df36dc820ee31305f54f863..985b74c06e80a3620eb6454c0bd9c7590b04184d 100644 +--- a/ipaserver/install/plugins/upload_cacrt.py ++++ b/ipaserver/install/plugins/upload_cacrt.py +@@ -20,7 +20,7 @@ + from ipalib.install import certstore + from ipaplatform.paths import paths + from ipaserver.install import certs +-from ipalib import Registry, errors ++from ipalib import Registry, errors, x509 + from ipalib import Updater + from ipapython import certdb + from ipapython.dn import DN +@@ -41,6 +41,10 @@ class update_upload_cacrt(Updater): + ca_enabled = self.api.Command.ca_is_enabled()['result'] + if ca_enabled: + ca_nickname = certdb.get_ca_nickname(self.api.env.realm) ++ ca_subject = certstore.get_ca_subject( ++ self.api.Backend.ldap2, ++ self.api.env.container_ca, ++ self.api.env.basedn) + else: + ca_nickname = None + server_certs = db.find_server_certs() +@@ -54,9 +58,18 @@ class update_upload_cacrt(Updater): + for nickname, trust_flags in db.list_certs(): + if trust_flags.has_key: + continue +- if nickname == ca_nickname and ca_enabled: +- trust_flags = certdb.IPA_CA_TRUST_FLAGS + cert = db.get_cert_from_db(nickname, pem=False) ++ subject = DN( ++ x509.load_certificate(cert, datatype=x509.DER).subject) ++ if ca_enabled and subject == ca_subject: ++ # When ca is enabled, we can have the IPA CA cert stored ++ # in the nss db with a different nickname (for instance ++ # when the server was installed with --subject to ++ # customize the CA cert subject), but it must always be ++ # stored in LDAP with the DN cn=$DOMAIN IPA CA ++ # This is why we check the subject instead of the nickname here ++ nickname = ca_nickname ++ trust_flags = certdb.IPA_CA_TRUST_FLAGS + trust, _ca, eku = certstore.trust_flags_to_key_policy(trust_flags) + + dn = DN(('cn', nickname), ('cn', 'certificates'), ('cn', 'ipa'), +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0225-Fixing-how-sssd.conf-is-updated-when-promoting-a-cli.patch b/SOURCES/0225-Fixing-how-sssd.conf-is-updated-when-promoting-a-cli.patch new file mode 100644 index 0000000..28455ee --- /dev/null +++ b/SOURCES/0225-Fixing-how-sssd.conf-is-updated-when-promoting-a-cli.patch @@ -0,0 +1,92 @@ +From c8fcaa5dc792e7b87c8f21c7c322ddfabe219980 Mon Sep 17 00:00:00 2001 +From: Felipe Volpone +Date: Wed, 13 Sep 2017 09:26:41 -0300 +Subject: [PATCH] Fixing how sssd.conf is updated when promoting a client to + replica + +When promoting a client to a replica we have to change sssd.conf, +deleting _srv_ part from 'ipa_server' property and setting +'ipa_server_mode' to true. + +Previously, the wrong domain could be updated since the ipa_domain +variable was not being used properly. + +https://pagure.io/freeipa/issue/7127 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Alexander Bokovoy +Reviewed-By: Rob Crittenden +--- + ipaserver/install/server/replicainstall.py | 27 ++++++++++++--------------- + ipaserver/install/server/upgrade.py | 4 ++++ + 2 files changed, 16 insertions(+), 15 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 814925de152809808f726c60ae7f35a24bc32a4a..326daf708f091d9d2c56ad399e46aef659dbba2e 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -432,30 +432,27 @@ def promote_sssd(host_name): + sssdconfig.import_config() + domains = sssdconfig.list_active_domains() + +- ipa_domain = None +- + for name in domains: + domain = sssdconfig.get_domain(name) + try: + hostname = domain.get_option('ipa_hostname') + if hostname == host_name: +- ipa_domain = domain ++ break + except SSSDConfig.NoOptionError: + continue +- +- if ipa_domain is None: +- raise RuntimeError("Couldn't find IPA domain in sssd.conf") + else: +- domain.set_option('ipa_server', host_name) +- domain.set_option('ipa_server_mode', True) +- sssdconfig.save_domain(domain) +- sssdconfig.write() ++ raise RuntimeError("Couldn't find IPA domain in sssd.conf") + +- sssd = services.service('sssd', api) +- try: +- sssd.restart() +- except CalledProcessError: +- root_logger.warning("SSSD service restart was unsuccessful.") ++ domain.set_option('ipa_server', host_name) ++ domain.set_option('ipa_server_mode', True) ++ sssdconfig.save_domain(domain) ++ sssdconfig.write() ++ ++ sssd = services.service('sssd', api) ++ try: ++ sssd.restart() ++ except CalledProcessError: ++ root_logger.warning("SSSD service restart was unsuccessful.") + + + def promote_openldap_conf(hostname, master): +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 732776f2cf513a4bb11d8f3f0dfaac78217e460f..109e922e3a3ea25f882fdd81765788a3881e87bd 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1816,11 +1816,15 @@ def upgrade_configuration(): + cainstance.ensure_ipa_authority_entry() + + set_sssd_domain_option('ipa_server_mode', 'True') ++ set_sssd_domain_option('ipa_server', api.env.host) + + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.import_config() + sssd_enable_service(sssdconfig, 'ifp') + ++ sssd = services.service('sssd', api) ++ sssd.restart() ++ + krb = krbinstance.KrbInstance(fstore) + krb.fqdn = fqdn + krb.realm = api.env.realm +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0226-Backport-4-5-Fix-ipa-server-upgrade-with-server-cert.patch b/SOURCES/0226-Backport-4-5-Fix-ipa-server-upgrade-with-server-cert.patch new file mode 100644 index 0000000..27b8c70 --- /dev/null +++ b/SOURCES/0226-Backport-4-5-Fix-ipa-server-upgrade-with-server-cert.patch @@ -0,0 +1,199 @@ +From 8c576e8c3640b84869abacc43a74aa250df5a8e9 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 5 Sep 2017 16:17:31 +0200 +Subject: [PATCH] Backport 4-5: Fix ipa-server-upgrade with server cert + tracking + +ipa-server-upgrade fails with Server-Cert not found, when trying to +track httpd/ldap server certificates. There are 2 issues in the upgrade: +- the certificates should be tracked only if they were issued by IPA CA +(it is possible to have CA configured but 3rd part certs) +- the certificate nickname can be different from Server-Cert + +The fix provides methods to find the server crt nickname for http and ldap, +and a method to check if the server certs are issued by IPA and need to be +tracked by certmonger. + +https://pagure.io/freeipa/issue/7141 + +Reviewed-By: Stanislav Laznicka +Reviewed-By: Fraser Tweedale +--- + ipaserver/install/certs.py | 27 ++++++++++++++++++++++ + ipaserver/install/dsinstance.py | 45 +++++++++++++++++++++++++++++++++---- + ipaserver/install/httpinstance.py | 16 ++++++++++--- + ipaserver/install/server/upgrade.py | 4 ++-- + 4 files changed, 83 insertions(+), 9 deletions(-) + +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 02c479d92511fcf4043e7d6798c85cf8256c3299..de96318db51b03f2515814d574cfebf1b242b6a6 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -42,6 +42,7 @@ from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase + from ipapython.dn import DN + from ipalib import pkcs10, x509, api + from ipalib.errors import CertificateOperationError ++from ipalib.install import certstore + from ipalib.text import _ + from ipaplatform.paths import paths + +@@ -669,6 +670,32 @@ class CertDB(object): + subject=host, + passwd_fname=self.passwd_fname) + ++ def is_ipa_issued_cert(self, api, nickname): ++ """ ++ Return True if the certificate contained in the CertDB with the ++ provided nickname has been issued by IPA. ++ ++ Note that this method can only be executed if api has been initialized ++ """ ++ # This method needs to compare the cert issuer (from the NSS DB ++ # and the subject from the CA (from LDAP), because nicknames are not ++ # always aligned. ++ ++ cacert_subject = certstore.get_ca_subject( ++ api.Backend.ldap2, ++ api.env.container_ca, ++ api.env.basedn) ++ ++ # The cert can be issued directly by IPA. In this case, the cert ++ # issuer is IPA CA subject. ++ cert = self.get_cert_from_db(nickname) ++ if cert is None: ++ raise RuntimeError("Could not find the cert %s in %s" ++ % (nickname, self.secdir)) ++ issuer = DN(x509.load_certificate(cert).issuer) ++ ++ return issuer == cacert_subject ++ + + class _CrossProcessLock(object): + _DATETIME_FORMAT = '%Y%m%d%H%M%S%f' +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 39248edb285ee4d792b4500d83d88b24f5732d10..c9db8ac28c3ca10539b745ca09f4d8aaece02e0c 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -1028,22 +1028,59 @@ class DsInstance(service.Service): + root_logger.error( + 'Unable to restart DS instance %s: %s', ds_instance, e) + ++ def get_server_cert_nickname(self, serverid=None): ++ """ ++ Retrieve the nickname of the server cert used by dirsrv. ++ ++ The method directly reads the dse.ldif to find the attribute ++ nsSSLPersonalitySSL of cn=RSA,cn=encryption,cn=config because ++ LDAP is not always accessible when we need to get the nickname ++ (for instance during uninstall). ++ """ ++ if serverid is None: ++ serverid = self.get_state("serverid") ++ if serverid is not None: ++ dirname = config_dirname(serverid) ++ config_file = os.path.join(dirname, "dse.ldif") ++ rsa_dn = "cn=RSA,cn=encryption,cn=config" ++ with open(config_file, "r") as in_file: ++ parser = upgradeinstance.GetEntryFromLDIF( ++ in_file, ++ entries_dn=[rsa_dn]) ++ parser.parse() ++ try: ++ config_entry = parser.get_results()[rsa_dn] ++ nickname = config_entry["nsSSLPersonalitySSL"][0] ++ return nickname.decode('utf-8') ++ except (KeyError, IndexError): ++ root_logger.error("Unable to find server cert nickname in " ++ "%s", config_file) ++ ++ root_logger.debug("Falling back to nickname Server-Cert") ++ return 'Server-Cert' ++ + def stop_tracking_certificates(self, serverid=None): + if serverid is None: + serverid = self.get_state("serverid") + if not serverid is None: ++ nickname = self.get_server_cert_nickname(serverid) + # drop the trailing / off the config_dirname so the directory + # will match what is in certmonger + dirname = config_dirname(serverid)[:-1] + dsdb = certs.CertDB(self.realm, nssdir=dirname) +- dsdb.untrack_server_cert(self.nickname) ++ dsdb.untrack_server_cert(nickname) + + def start_tracking_certificates(self, serverid): ++ nickname = self.get_server_cert_nickname(serverid) + dirname = config_dirname(serverid)[:-1] + dsdb = certs.CertDB(self.realm, nssdir=dirname) +- dsdb.track_server_cert(self.nickname, self.principal, +- dsdb.passwd_fname, +- 'restart_dirsrv %s' % serverid) ++ if dsdb.is_ipa_issued_cert(api, nickname): ++ dsdb.track_server_cert(nickname, self.principal, ++ dsdb.passwd_fname, ++ 'restart_dirsrv %s' % serverid) ++ else: ++ root_logger.debug("Will not track DS server certificate %s as it " ++ "is not issued by IPA", nickname) + + # we could probably move this function into the service.Service + # class - it's very generic - all we need is a way to get an +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index f637b97db8f21ddbc00c4f70e18e836d300b2f33..e55edebc5d4e45d7cb4cb66d28a270e6d6a56e33 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -266,6 +266,11 @@ class HTTPInstance(service.Service): + installutils.set_directive( + paths.HTTPD_NSS_CONF, 'NSSNickname', quoted_nickname, quotes=False) + ++ def get_mod_nss_nickname(self): ++ cert = installutils.get_directive(paths.HTTPD_NSS_CONF, 'NSSNickname') ++ nickname = installutils.unquote_directive_value(cert, quote_char="'") ++ return nickname ++ + def set_mod_nss_protocol(self): + installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSProtocol', 'TLSv1.0,TLSv1.1,TLSv1.2', False) + +@@ -582,12 +587,17 @@ class HTTPInstance(service.Service): + + def stop_tracking_certificates(self): + db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) +- db.untrack_server_cert(self.cert_nickname) ++ db.untrack_server_cert(self.get_mod_nss_nickname()) + + def start_tracking_certificates(self): + db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR) +- db.track_server_cert(self.cert_nickname, self.principal, +- db.passwd_fname, 'restart_httpd') ++ nickname = self.get_mod_nss_nickname() ++ if db.is_ipa_issued_cert(api, nickname): ++ db.track_server_cert(nickname, self.principal, ++ db.passwd_fname, 'restart_httpd') ++ else: ++ root_logger.debug("Will not track HTTP server cert %s as it is " ++ "not issued by IPA", nickname) + + def request_service_keytab(self): + super(HTTPInstance, self).request_service_keytab() +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 109e922e3a3ea25f882fdd81765788a3881e87bd..0947766c076251e7608241803d3a1eabee65ae11 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -957,13 +957,13 @@ def certificate_renewal_update(ca, ds, http): + }, + { + 'cert-database': paths.HTTPD_ALIAS_DIR, +- 'cert-nickname': 'Server-Cert', ++ 'cert-nickname': http.get_mod_nss_nickname(), + 'ca': 'IPA', + 'cert-postsave-command': template % 'restart_httpd', + }, + { + 'cert-database': dsinstance.config_dirname(serverid), +- 'cert-nickname': 'Server-Cert', ++ 'cert-nickname': ds.get_server_cert_nickname(serverid), + 'ca': 'IPA', + 'cert-postsave-command': + '%s %s' % (template % 'restart_dirsrv', serverid), +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0227-Always-check-peer-has-keys-before-connecting.patch b/SOURCES/0227-Always-check-peer-has-keys-before-connecting.patch new file mode 100644 index 0000000..684f4a0 --- /dev/null +++ b/SOURCES/0227-Always-check-peer-has-keys-before-connecting.patch @@ -0,0 +1,73 @@ +From 82e860ae81b9e34fc6a326be4183f37a21ac1564 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 23 Jun 2017 04:48:41 -0400 +Subject: [PATCH] Always check peer has keys before connecting + +When pulling the DM password we may have the same issues reported in +ticket #6838 for CA keys. +This commit makes sure we always check the peer has keys before any +client operation. + +Ticket #6838 + +Signed-off-by: Simo Sorce +Reviewed-By: Stanislav Laznicka +Reviewed-By: Michal Reznik +--- + ipaserver/install/custodiainstance.py | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py +index 390576bc0c0edfb7d8f8895eca9df30079526aa8..bc3cea7063dff183c85b4f6e8ced7567f691001d 100644 +--- a/ipaserver/install/custodiainstance.py ++++ b/ipaserver/install/custodiainstance.py +@@ -13,7 +13,6 @@ from ipaserver.install import ldapupdate + from ipaserver.install import sysupgrade + from base64 import b64decode + from jwcrypto.common import json_decode +-import functools + import shutil + import os + import stat +@@ -31,13 +30,6 @@ class CustodiaInstance(SimpleServiceInstance): + self.ldap_uri = None + self.fqdn = host_name + self.realm = realm +- self.__CustodiaClient = functools.partial( +- CustodiaClient, +- client_service='host@%s' % self.fqdn, +- keyfile=self.server_keys, +- keytab=paths.KRB5_KEYTAB, +- realm=realm, +- ) + + def __config_file(self): + template_file = os.path.basename(self.config_file) + '.template' +@@ -144,6 +136,14 @@ class CustodiaInstance(SimpleServiceInstance): + raise RuntimeError("Timed out trying to obtain keys.") + time.sleep(1) + ++ def __CustodiaClient(self, server): ++ # Before we attempt to fetch keys from this host, make sure our public ++ # keys have been replicated there. ++ self.__wait_keys(server) ++ ++ return CustodiaClient('host@%s' % self.fqdn, self.server_keys, ++ paths.KRB5_KEYTAB, server, realm=self.realm) ++ + def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): + # Fecth all needed certs one by one, then combine them in a single + # p12 file +@@ -151,10 +151,6 @@ class CustodiaInstance(SimpleServiceInstance): + prefix = data['prefix'] + certlist = data['list'] + +- # Before we attempt to fetch keys from this host, make sure our public +- # keys have been replicated there. +- self.__wait_keys(ca_host) +- + cli = self.__CustodiaClient(server=ca_host) + + # Temporary nssdb +-- +2.13.5 \ No newline at end of file diff --git a/SOURCES/0228-Make-sure-upgrade-also-checks-for-IPv6-stack.patch b/SOURCES/0228-Make-sure-upgrade-also-checks-for-IPv6-stack.patch new file mode 100644 index 0000000..600401d --- /dev/null +++ b/SOURCES/0228-Make-sure-upgrade-also-checks-for-IPv6-stack.patch @@ -0,0 +1,61 @@ +From 010f6405288b1ca519d684d85ca25ce86de60b66 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Tue, 19 Sep 2017 12:06:39 +0300 +Subject: [PATCH] Make sure upgrade also checks for IPv6 stack + + - Add check for IPv6 stack to upgrade process + - Change IPv6 checker to also check that localhost resolves to ::1 + +Part of fixes https://pagure.io/freeipa/issue/7083 + +Reviewed-By: Tomas Krizek +--- + ipaplatform/redhat/tasks.py | 19 ++++++++++++++++--- + ipaserver/install/server/upgrade.py | 1 + + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py +index 07efebab97eabcf2dc39bd345920a1c7be56e9f5..94d1863c5cc20ec6c2399f339ce19498976553bc 100644 +--- a/ipaplatform/redhat/tasks.py ++++ b/ipaplatform/redhat/tasks.py +@@ -153,9 +153,22 @@ class RedHatTaskNamespace(BaseTaskNamespace): + """ + if not os.path.exists(paths.IF_INET6): + raise RuntimeError( +- "IPv6 kernel module has to be enabled. If you do not wish to " +- "use IPv6, please disable it on the interfaces in " +- "sysctl.conf and enable the IPv6 kernel module.") ++ "IPv6 stack has to be enabled in the kernel and some " ++ "interface has to have ::1 address assigned. Typically " ++ "this is 'lo' interface. If you do not wish to use IPv6 " ++ "globally, disable it on the specific interfaces in " ++ "sysctl.conf except 'lo' interface.") ++ ++ try: ++ localhost6 = ipautil.CheckedIPAddress('::1', allow_loopback=True) ++ if localhost6.get_matching_interface() is None: ++ raise ValueError("no interface for ::1 address found") ++ except ValueError: ++ raise RuntimeError( ++ "IPv6 stack is enabled in the kernel but there is no " ++ "interface that has ::1 address assigned. Add ::1 address " ++ "resolution to 'lo' interface. You might need to enable IPv6 " ++ "on the interface 'lo' in sysctl.conf.") + + def restore_pre_ipa_client_configuration(self, fstore, statestore, + was_sssd_installed, +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 0947766c076251e7608241803d3a1eabee65ae11..1c4b2357d5d016b8a7501f46380d5e0a61dc21a0 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1860,6 +1860,7 @@ def upgrade_configuration(): + def upgrade_check(options): + try: + installutils.check_server_configuration() ++ tasks.check_ipv6_stack_enabled() + except RuntimeError as e: + root_logger.error(e) + sys.exit(1) +-- +2.13.5 + diff --git a/SOURCES/0229-control-logging-of-host_port_open-from-caller.patch b/SOURCES/0229-control-logging-of-host_port_open-from-caller.patch new file mode 100644 index 0000000..951e7c2 --- /dev/null +++ b/SOURCES/0229-control-logging-of-host_port_open-from-caller.patch @@ -0,0 +1,103 @@ +From fbaa55fbe8447745a20c99a68d62790f5dd5a0f7 Mon Sep 17 00:00:00 2001 +From: Petr Vobornik +Date: Thu, 3 Aug 2017 15:48:33 +0200 +Subject: [PATCH] control logging of host_port_open from caller + +host_port_open copied logging behavior of ipa-replica-conncheck utility +which doesn't make it much reusable. + +Now log level can be controlled from caller so other callers might use +other logging level without host_port_open guessing what was the +intention. + +https://pagure.io/freeipa/issue/7083 + +Reviewed-By: Tomas Krizek +--- + install/tools/ipa-replica-conncheck | 9 ++++++++- + ipapython/ipautil.py | 17 ++++++----------- + 2 files changed, 14 insertions(+), 12 deletions(-) + +diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck +index 528242268f9992e903781b76a379039d533853c0..f10b7e3d2f94540dba3956bf460c4b9f38da90da 100755 +--- a/install/tools/ipa-replica-conncheck ++++ b/install/tools/ipa-replica-conncheck +@@ -46,6 +46,8 @@ import distutils.spawn + from ipaplatform.paths import paths + import gssapi + from cryptography.hazmat.primitives import serialization ++import logging ++ + + CONNECT_TIMEOUT = 5 + RESPONDER = None +@@ -379,11 +381,16 @@ class PortResponder(threading.Thread): + def port_check(host, port_list): + ports_failed = [] + ports_udp_warning = [] # conncheck could not verify that port is open ++ log_level = { ++ SOCK_DGRAM: logging.WARNING, ++ SOCK_STREAM: logging.ERROR ++ } + for port in port_list: + try: + port_open = ipautil.host_port_open( + host, port.port, port.port_type, +- socket_timeout=CONNECT_TIMEOUT, log_errors=True) ++ socket_timeout=CONNECT_TIMEOUT, log_errors=True, ++ log_level=log_level[port.port_type]) + except socket.gaierror: + raise RuntimeError("Port check failed! Unable to resolve host name '%s'" % host) + if port_open: +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 5a6bf5a27d5a6e25c51fbaa6e2b1167652e2735d..e1e6e32b15559486caecb070627db82e14a57bdf 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -42,6 +42,7 @@ from contextlib import contextmanager + import locale + import collections + from subprocess import CalledProcessError ++import logging + + from dns import resolver, reversename + from dns.exception import DNSException +@@ -948,7 +949,8 @@ def user_input(prompt, default = None, allow_empty = True): + + + def host_port_open(host, port, socket_type=socket.SOCK_STREAM, +- socket_timeout=None, log_errors=False): ++ socket_timeout=None, log_errors=False, ++ log_level=logging.DEBUG): + """ + host: either hostname or IP address; + if hostname is provided, port MUST be open on ALL resolved IPs +@@ -970,23 +972,16 @@ def host_port_open(host, port, socket_type=socket.SOCK_STREAM, + s.connect(sa) + + if socket_type == socket.SOCK_DGRAM: +- s.send('') ++ s.send(b'') + s.recv(512) + except socket.error: + port_open = False +- + if log_errors: +- msg = ('Failed to connect to port %(port)d %(proto)s on ' ++ msg = ('Failed to connect to port %(port)s %(proto)s on ' + '%(addr)s' % dict(port=port, + proto=PROTOCOL_NAMES[socket_type], + addr=sa[0])) +- +- # Do not log udp failures as errors (to be consistent with +- # the rest of the code that checks for open ports) +- if socket_type == socket.SOCK_DGRAM: +- root_logger.warning(msg) +- else: +- root_logger.error(msg) ++ root_logger.log(log_level, msg) + finally: + if s is not None: + s.close() +-- +2.13.5 + diff --git a/SOURCES/0230-log-progress-of-wait_for_open_ports.patch b/SOURCES/0230-log-progress-of-wait_for_open_ports.patch new file mode 100644 index 0000000..480be17 --- /dev/null +++ b/SOURCES/0230-log-progress-of-wait_for_open_ports.patch @@ -0,0 +1,44 @@ +From 647e23eb307e09597f355fb10abfd4c74a4b6f84 Mon Sep 17 00:00:00 2001 +From: Petr Vobornik +Date: Thu, 3 Aug 2017 16:03:29 +0200 +Subject: [PATCH] log progress of wait_for_open_ports + +To know what to focus on when some check fail. E.g. to detect that +IPv6 address or its resolution for localhost is misconfigured. + +https://pagure.io/freeipa/issue/7083 + +Reviewed-By: Tomas Krizek +--- + ipapython/ipautil.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index e1e6e32b15559486caecb070627db82e14a57bdf..59ea84da4ac667a39bdaa9a6fd7d87ab1b6e658d 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -1213,15 +1213,20 @@ def wait_for_open_ports(host, ports, timeout=0): + op_timeout = time.time() + timeout + + for port in ports: ++ root_logger.debug('waiting for port: %s', port) ++ log_error = True + while True: +- port_open = host_port_open(host, port) ++ port_open = host_port_open(host, port, log_errors=log_error) ++ log_error = False # Log only first err so that the log is readable + + if port_open: ++ root_logger.debug('SUCCESS: port: %s', port) + break + if timeout and time.time() > op_timeout: # timeout exceeded + raise socket.timeout("Timeout exceeded") + time.sleep(1) + ++ + def wait_for_open_socket(socket_name, timeout=0): + """ + Wait until the specified socket on the local host is open. Timeout +-- +2.13.5 + diff --git a/SOURCES/0231-Store-help-in-Schema-before-writing-to-disk.patch b/SOURCES/0231-Store-help-in-Schema-before-writing-to-disk.patch new file mode 100644 index 0000000..b336e43 --- /dev/null +++ b/SOURCES/0231-Store-help-in-Schema-before-writing-to-disk.patch @@ -0,0 +1,39 @@ +From 5693c0fe6dfe998fa5ea3f86f477dc9dcbfab881 Mon Sep 17 00:00:00 2001 +From: David Kreitschmann +Date: Fri, 7 Apr 2017 18:22:25 +0200 +Subject: [PATCH] Store help in Schema before writing to disk + +Signed-off-by: David Kreitschmann +Reviewed-By: David Kupka +Reviewed-By: Martin Babinsky +Reviewed-By: David Kupka +Reviewed-By: Martin Babinsky +Reviewed-By: Rob Crittenden +--- + ipaclient/remote_plugins/schema.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py +index 3ecd608f96e336df57739db380a00c2d95b2ece5..9b6668d26352a24d7249bac5e304277563ee450f 100644 +--- a/ipaclient/remote_plugins/schema.py ++++ b/ipaclient/remote_plugins/schema.py +@@ -383,6 +383,7 @@ class Schema(object): + + if fingerprint is None: + fingerprint, ttl = self._fetch(client, ignore_cache=read_failed) ++ self._help = self._generate_help(self._dict) + try: + self._write_schema(fingerprint) + except Exception as e: +@@ -498,7 +499,7 @@ class Schema(object): + + schema.writestr( + '_help', +- json.dumps(self._generate_help(self._dict)).encode('utf-8') ++ json.dumps(self._help).encode('utf-8') + ) + + def read_namespace_member(self, namespace, member): +-- +2.13.5 + diff --git a/SOURCES/0232-Disable-pylint-in-get_help-function-because-of-type-.patch b/SOURCES/0232-Disable-pylint-in-get_help-function-because-of-type-.patch new file mode 100644 index 0000000..6d97395 --- /dev/null +++ b/SOURCES/0232-Disable-pylint-in-get_help-function-because-of-type-.patch @@ -0,0 +1,33 @@ +From df9933a8cbc5c6cf4041709ed61c589adaae7a08 Mon Sep 17 00:00:00 2001 +From: David Kreitschmann +Date: Fri, 9 Jun 2017 17:59:35 +0200 +Subject: [PATCH] Disable pylint in get_help function because of type + confusion. + +Reviewed-By: David Kupka +Reviewed-By: Martin Babinsky +Reviewed-By: David Kupka +Reviewed-By: Martin Babinsky +Reviewed-By: Rob Crittenden +--- + ipaclient/remote_plugins/schema.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py +index 9b6668d26352a24d7249bac5e304277563ee450f..2892ab9ec7c6fb092ef470b7e5bd2b9c0760c2af 100644 +--- a/ipaclient/remote_plugins/schema.py ++++ b/ipaclient/remote_plugins/schema.py +@@ -516,7 +516,9 @@ class Schema(object): + + def get_help(self, namespace, member): + if isinstance(self._help, bytes): +- self._help = json.loads(self._help.decode('utf-8')) ++ self._help = json.loads( ++ self._help.decode('utf-8') # pylint: disable=no-member ++ ) + + return self._help[namespace][member] + +-- +2.13.5 + diff --git a/SOURCES/0233-Less-confusing-message-for-PKINIT-configuration-duri.patch b/SOURCES/0233-Less-confusing-message-for-PKINIT-configuration-duri.patch new file mode 100644 index 0000000..6cbca7b --- /dev/null +++ b/SOURCES/0233-Less-confusing-message-for-PKINIT-configuration-duri.patch @@ -0,0 +1,50 @@ +From ad0f85945daa0b0bfbddbcde992c5388c170518f Mon Sep 17 00:00:00 2001 +From: Aleksei Slaikovskii +Date: Wed, 18 Oct 2017 09:52:08 +0200 +Subject: [PATCH] Less confusing message for PKINIT configuration during + install + +The message about an error during replica setup was causing the +users to think the installation gone wrong even though this was +an expected behavior when ipa-replica-install was ran without +--no-pkinit flag and CA somehow is not reachable which defines +that there is something wrong in a topology but does not lead +to failure of the replica's installation. So now installation +will not print error messages to stdout but rather will give a +recomendation to user and write the old error message to log +as a warning so it still will be easy to find if needed. + +https://pagure.io/freeipa/issue/7179 + +Reviewed-By: Tomas Krizek +--- + ipaserver/install/krbinstance.py | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 6b51e65d1ec985bfc01f167aea3fe3ca11c7ec29..34fe46aa8ef297bf69eb74953c956ad9c3d30def 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -494,8 +494,17 @@ class KrbInstance(service.Service): + self._install_pkinit_ca_bundle() + self.pkinit_enable() + except RuntimeError as e: +- root_logger.error("PKINIT certificate request failed: %s", e) +- root_logger.error("Failed to configure PKINIT") ++ root_logger.warning("PKINIT certificate request failed: %s", e) ++ root_logger.warning("Failed to configure PKINIT") ++ ++ self.print_msg("Full PKINIT configuration did not succeed") ++ self.print_msg( ++ "The setup will only install bits " ++ "essential to the server functionality") ++ self.print_msg( ++ "You can enable PKINIT after the " ++ "setup completed using 'ipa-pkinit-manage'") ++ + self.stop_tracking_certs() + self.issue_selfsigned_pkinit_certs() + +-- +2.13.5 + diff --git a/SOURCES/0234-server.py-Removes-dns-server-configuration-from-ldap.patch b/SOURCES/0234-server.py-Removes-dns-server-configuration-from-ldap.patch new file mode 100644 index 0000000..fe12f23 --- /dev/null +++ b/SOURCES/0234-server.py-Removes-dns-server-configuration-from-ldap.patch @@ -0,0 +1,45 @@ +From d71488fd450615ade6c10978af38d0dda27ec859 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= +Date: Tue, 6 Jun 2017 15:13:26 +0200 +Subject: [PATCH] server.py: Removes dns-server configuration from ldap + +After invocation of the ipa server-del +command there was still record in ldap if DNS +was installed on the server. + +Fixes: https://pagure.io/freeipa/issue/6572 +Reviewed-By: Martin Basti +--- + ipaserver/plugins/server.py | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py +index b1ee4722841509f4614c688ac39095c723aff167..e0dc953a1ef870c95fdcdb629fb6ab3103e8f999 100644 +--- a/ipaserver/plugins/server.py ++++ b/ipaserver/plugins/server.py +@@ -692,6 +692,12 @@ class server_del(LDAPDelete): + message=_("You may need to manually remove them from the " + "tree"))) + ++ def _cleanup_server_dns_config(self, hostname): ++ try: ++ self.api.Command.dnsserver_del(hostname) ++ except errors.NotFound: ++ pass ++ + def pre_callback(self, ldap, dn, *keys, **options): + pkey = self.obj.get_primary_key_from_dn(dn) + +@@ -731,6 +737,9 @@ class server_del(LDAPDelete): + # try to clean up the leftover DNS entries + self._cleanup_server_dns_records(pkey) + ++ # try to clean up the DNS config from ldap ++ self._cleanup_server_dns_config(pkey) ++ + return dn + + def exc_callback(self, keys, options, exc, call_func, *call_args, +-- +2.13.5 + diff --git a/SOURCES/0235-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch b/SOURCES/0235-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch new file mode 100644 index 0000000..2ff94ff --- /dev/null +++ b/SOURCES/0235-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch @@ -0,0 +1,75 @@ +From a6b7f433c1c8c30e455f345fcd97e7428ae63322 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Wed, 9 Aug 2017 17:28:35 -0400 +Subject: [PATCH] Include the CA basic constraint in CSRs when renewing a CA + +The CSR generated by `ipa-cacert-manage renew --external-ca` did +not include the CA basic constraint: + + X509v3 Basic Constraints: critical + CA:TRUE + +Add a flag to certmonger::resubmit_request to specify that a +CA is being requested. + +Note that this also sets pathlen to -1 which means an unlimited +pathlen. Leave it up to the issuing CA to set this. + +https://pagure.io/freeipa/issue/7088 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/install/certmonger.py | 13 +++++++++++-- + ipaserver/install/ipa_cacert_manage.py | 3 ++- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py +index c286996ee2318e241b4af190d1a01f42e28aa9f3..d2b782ddb0c746a3dfd96d0222bb31c6a960fdff 100644 +--- a/ipalib/install/certmonger.py ++++ b/ipalib/install/certmonger.py +@@ -519,16 +519,25 @@ def modify(request_id, ca=None, profile=None): + request.obj_if.modify(update) + + +-def resubmit_request(request_id, ca=None, profile=None): ++def resubmit_request(request_id, ca=None, profile=None, is_ca=False): ++ """ ++ :param request_id: the certmonger numeric request ID ++ :param ca: the nickname for the certmonger CA, e.g. IPA or SelfSign ++ :param profile: the dogtag template profile to use, e.g. SubCA ++ :param is_ca: boolean that if True adds the CA basic constraint ++ """ + request = _get_request({'nickname': request_id}) + if request: +- if ca or profile: ++ if ca or profile or is_ca: + update = {} + if ca is not None: + cm = _certmonger() + update['CA'] = cm.obj_if.find_ca_by_nickname(ca) + if profile is not None: + update['template-profile'] = profile ++ if is_ca: ++ update['template-is-ca'] = True ++ update['template-ca-path-length'] = -1 # no path length + request.obj_if.modify(update) + request.obj_if.resubmit() + +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index fcbf09155a3abc9ce9481aa2519ed39aaa6aa9bb..9607620d6c3e63b70b9e586f94282bf478c8c53e 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -310,7 +310,8 @@ class CACertManage(admintool.AdminTool): + timeout = api.env.startup_timeout + 60 + + self.log.debug("resubmitting certmonger request '%s'", self.request_id) +- certmonger.resubmit_request(self.request_id, ca=ca, profile=profile) ++ certmonger.resubmit_request(self.request_id, ca=ca, profile=profile, ++ is_ca=True) + try: + state = certmonger.wait_for_request(self.request_id, timeout) + except RuntimeError: +-- +2.13.5 + diff --git a/SOURCES/0236-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch b/SOURCES/0236-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch new file mode 100644 index 0000000..54a13e6 --- /dev/null +++ b/SOURCES/0236-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch @@ -0,0 +1,51 @@ +From f6ce0099adc7c8508b3bf2f82102c1dd70fa08dc Mon Sep 17 00:00:00 2001 +From: Felipe Barreto +Date: Fri, 13 Oct 2017 09:19:43 +0200 +Subject: [PATCH] Checks if replica-s4u2proxy.ldif should be applied + +Before applying replica-s3u2proxy.ldif, we check +if the values are already there. The values can be +there if a replica installation was done in the past +and some info was left behind. Also, the code checks +the values independently. + +https://pagure.io/freeipa/issue/7174 + +Reviewed-By: Rob Crittenden +--- + ipaserver/install/dsinstance.py | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index c9db8ac28c3ca10539b745ca09f4d8aaece02e0c..f7edcffc5904d8c9ce46f5862d496a4df3ad8d75 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -930,7 +930,24 @@ class DsInstance(service.Service): + self._ldap_mod("replica-acis.ldif", self.sub_dict) + + def __setup_s4u2proxy(self): +- self._ldap_mod("replica-s4u2proxy.ldif", self.sub_dict) ++ ++ def __add_principal(last_cn, principal, self): ++ dn = DN(('cn', last_cn), ('cn', 's4u2proxy'), ++ ('cn', 'etc'), self.suffix) ++ ++ value = '{principal}/{fqdn}@{realm}'.format(fqdn=self.fqdn, ++ realm=self.realm, ++ principal=principal) ++ ++ entry = api.Backend.ldap2.get_entry(dn, ['memberPrincipal']) ++ try: ++ entry['memberPrincipal'].append(value) ++ api.Backend.ldap2.update_entry(entry) ++ except errors.EmptyModlist: ++ pass ++ ++ __add_principal('ipa-http-delegation', 'HTTP', self) ++ __add_principal('ipa-ldap-delegation-targets', 'ldap', self) + + def __create_indices(self): + self._ldap_mod("indices.ldif") +-- +2.13.5 + diff --git a/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch new file mode 100644 index 0000000..24c8cb4 --- /dev/null +++ b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch @@ -0,0 +1,999 @@ +From 7a5799402ddfbe2704afa4449bb597f2feeea6c2 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Tue, 14 Mar 2017 15:48:07 +0000 +Subject: [PATCH] Change branding to IPA and Identity Management + +--- + client/man/default.conf.5 | 2 +- + client/man/ipa-certupdate.1 | 2 +- + client/man/ipa-client-automount.1 | 2 +- + client/man/ipa-client-install.1 | 2 +- + client/man/ipa-getkeytab.1 | 4 +- + client/man/ipa-join.1 | 2 +- + client/man/ipa-rmkeytab.1 | 2 +- + client/man/ipa.1 | 2 +- + install/html/browserconfig.html | 4 +- + install/html/ssbrowser.html | 4 +- + install/html/unauthorized.html | 4 +- + install/migration/error.html | 4 +- + install/migration/index.html | 2 +- + install/migration/invalid.html | 2 +- + install/share/schema.d/README | 4 +- + install/tools/ipa-adtrust-install | 4 +- + install/tools/ipa-replica-conncheck | 2 +- + install/tools/man/ipa-adtrust-install.1 | 2 +- + install/tools/man/ipa-advise.1 | 4 +- + install/tools/man/ipa-backup.1 | 2 +- + install/tools/man/ipa-ca-install.1 | 2 +- + install/tools/man/ipa-cacert-manage.1 | 2 +- + install/tools/man/ipa-compat-manage.1 | 2 +- + install/tools/man/ipa-csreplica-manage.1 | 2 +- + install/tools/man/ipa-dns-install.1 | 4 +- + install/tools/man/ipa-kra-install.1 | 2 +- + install/tools/man/ipa-ldap-updater.1 | 2 +- + install/tools/man/ipa-managed-entries.1 | 2 +- + install/tools/man/ipa-nis-manage.1 | 2 +- + install/tools/man/ipa-otptoken-import.1 | 2 +- + install/tools/man/ipa-replica-conncheck.1 | 2 +- + install/tools/man/ipa-replica-install.1 | 6 +- + install/tools/man/ipa-replica-manage.1 | 2 +- + install/tools/man/ipa-replica-prepare.1 | 2 +- + install/tools/man/ipa-restore.1 | 2 +- + install/tools/man/ipa-server-certinstall.1 | 2 +- + install/tools/man/ipa-server-install.1 | 4 +- + install/tools/man/ipa-server-upgrade.1 | 2 +- + install/tools/man/ipactl.8 | 2 +- + install/ui/css/patternfly.css | 2 +- + install/ui/index.html | 2 +- + install/ui/less/brand.less | 103 ++++++++++++++--------------- + install/ui/less/patternfly.less | 48 ++++++++++++++ + install/ui/reset_password.html | 2 +- + install/ui/src/freeipa/widgets/App.js | 2 +- + install/ui/sync_otp.html | 2 +- + ipaserver/advise/plugins/legacy_clients.py | 8 +-- + ipaserver/install/dns.py | 2 +- + ipaserver/install/ipa_kra_install.py | 4 +- + ipaserver/install/server/install.py | 2 +- + ipaserver/install/server/replicainstall.py | 2 +- + 51 files changed, 163 insertions(+), 118 deletions(-) + +diff --git a/client/man/default.conf.5 b/client/man/default.conf.5 +index 35ce6bb9f871365ffbc74b66be46d49fdcb3f7ad..b519d15bca9b7ddf8d22a776fa4f4a8c7fac0ca8 100644 +--- a/client/man/default.conf.5 ++++ b/client/man/default.conf.5 +@@ -16,7 +16,7 @@ + .\" + .\" Author: Rob Crittenden + .\" +-.TH "default.conf" "5" "Feb 21 2011" "FreeIPA" "FreeIPA Manual Pages" ++.TH "default.conf" "5" "Feb 21 2011" "IPA" "IPA Manual Pages" + .SH "NAME" + default.conf \- IPA configuration file + .SH "SYNOPSIS" +diff --git a/client/man/ipa-certupdate.1 b/client/man/ipa-certupdate.1 +index d95790a366aac4635e32bbc6bc81773bb9a52e68..431b395a907b6978d9e0dba9870ed0bd0c54f07b 100644 +--- a/client/man/ipa-certupdate.1 ++++ b/client/man/ipa-certupdate.1 +@@ -16,7 +16,7 @@ + .\" + .\" Author: Jan Cholasta + .\" +-.TH "ipa-certupdate" "1" "Jul 2 2014" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-certupdate" "1" "Jul 2 2014" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-certupdate \- Update local IPA certificate databases with certificates from the server + .SH "SYNOPSIS" +diff --git a/client/man/ipa-client-automount.1 b/client/man/ipa-client-automount.1 +index 8b9989dec7a2e31f9ccbc0b79d336449bea9e70d..72562ab9582e987cde387583ba033464526899e2 100644 +--- a/client/man/ipa-client-automount.1 ++++ b/client/man/ipa-client-automount.1 +@@ -16,7 +16,7 @@ + .\" + .\" Author: Rob Crittenden + .\" +-.TH "ipa-client-automount" "1" "May 25 2012" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-client-automount" "1" "May 25 2012" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-client\-automount \- Configure automount and NFS for IPA + .SH "SYNOPSIS" +diff --git a/client/man/ipa-client-install.1 b/client/man/ipa-client-install.1 +index 319952cb6ffe82339b578e8d7fe3eb7a83d53169..e631b89c6774b8ea43f5156293fee137c79dbb98 100644 +--- a/client/man/ipa-client-install.1 ++++ b/client/man/ipa-client-install.1 +@@ -1,7 +1,7 @@ + .\" A man page for ipa-client-install + .\" Copyright (C) 2008-2016 FreeIPA Contributors see COPYING for license + .\" +-.TH "ipa-client-install" "1" "Dec 19 2016" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-client-install" "1" "Dec 19 2016" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-client\-install \- Configure an IPA client + .SH "SYNOPSIS" +diff --git a/client/man/ipa-getkeytab.1 b/client/man/ipa-getkeytab.1 +index 08f6ec40d362b88a974e6ec735ed37c271e01882..3db48cc9204908dc63fdee6b3917331da43cd424 100644 +--- a/client/man/ipa-getkeytab.1 ++++ b/client/man/ipa-getkeytab.1 +@@ -17,7 +17,7 @@ + .\" Author: Karl MacMillan + .\" Author: Simo Sorce + .\" +-.TH "ipa-getkeytab" "1" "Oct 10 2007" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-getkeytab" "1" "Oct 10 2007" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-getkeytab \- Get a keytab for a Kerberos principal + .SH "SYNOPSIS" +@@ -112,7 +112,7 @@ GSSAPI or EXTERNAL. + \fB\-r\fR + Retrieve mode. Retrieve an existing key from the server instead of generating a + new one. This is incompatibile with the \-\-password option, and will work only +-against a FreeIPA server more recent than version 3.3. The user requesting the ++against a IPA server more recent than version 3.3. The user requesting the + keytab must have access to the keys for this operation to succeed. + .SH "EXAMPLES" + Add and retrieve a keytab for the NFS service principal on +diff --git a/client/man/ipa-join.1 b/client/man/ipa-join.1 +index d881607842bb0227c2da863bd1674db01530e910..30b667558ba3105cf320896ef40b0661a18066f5 100644 +--- a/client/man/ipa-join.1 ++++ b/client/man/ipa-join.1 +@@ -16,7 +16,7 @@ + .\" + .\" Author: Rob Crittenden + .\" +-.TH "ipa-join" "1" "Oct 8 2009" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-join" "1" "Oct 8 2009" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-join \- Join a machine to an IPA realm and get a keytab for the host service principal + .SH "SYNOPSIS" +diff --git a/client/man/ipa-rmkeytab.1 b/client/man/ipa-rmkeytab.1 +index 53f775439dbdb5a4b9dfee7fe6c7277fce10893c..2c8218c94996b1411637a7c53f2f5bc5612f3ea3 100644 +--- a/client/man/ipa-rmkeytab.1 ++++ b/client/man/ipa-rmkeytab.1 +@@ -17,7 +17,7 @@ + .\" Author: Rob Crittenden + .\" + .\" +-.TH "ipa-rmkeytab" "1" "Oct 30 2009" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa-rmkeytab" "1" "Oct 30 2009" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa\-rmkeytab \- Remove a kerberos principal from a keytab + .SH "SYNOPSIS" +diff --git a/client/man/ipa.1 b/client/man/ipa.1 +index b843e7ba71ff400fa0712ba491b85e8b34f8a09f..0bd10a7edf8849dc6a77f364d662ee5841054d41 100644 +--- a/client/man/ipa.1 ++++ b/client/man/ipa.1 +@@ -16,7 +16,7 @@ + .\" + .\" Author: Pavel Zuna + .\" +-.TH "ipa" "1" "Apr 29 2016" "FreeIPA" "FreeIPA Manual Pages" ++.TH "ipa" "1" "Apr 29 2016" "IPA" "IPA Manual Pages" + .SH "NAME" + ipa \- IPA command\-line interface + .SH "SYNOPSIS" +diff --git a/install/html/browserconfig.html b/install/html/browserconfig.html +index 9c5cf68211281723e12b518f346aac43c1541cdc..14c4ca1f98a60cd8dfe486f8b942fcf9ae9de4c0 100644 +--- a/install/html/browserconfig.html ++++ b/install/html/browserconfig.html +@@ -2,7 +2,7 @@ + + + +- IPA: Identity Policy Audit ++ Identity Management + + + +