diff --git a/0003-kdb-memory-leak.patch b/0003-kdb-memory-leak.patch new file mode 100644 index 0000000..bfb4b07 --- /dev/null +++ b/0003-kdb-memory-leak.patch @@ -0,0 +1,91 @@ +From 34b58d8ee93ab385c1f3ba1166377fc1008a9c17 Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Wed, 24 Jan 2024 15:50:17 +0100 +Subject: [PATCH] ipa-kdb: Fix memory leak during PAC verification + +Commit 0022bd70d93708d325855d5271516d6cd894d6e8 introduced a memory leak +during the copy of some PAC buffers, because of an unfreed memory +allocation context. + +Fixes: https://pagure.io/freeipa/issue/9520 + +Signed-off-by: Julien Rische +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-kdb/ipa_kdb_mspac.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c +index 1558e2bea..2866304e1 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -2316,6 +2316,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context, + size_t i; + struct dom_sid *requester_sid = NULL; + struct dom_sid req_sid; ++ TALLOC_CTX *tmpctx = NULL; + + if (signing_krbtgt != NULL && + ipadb_is_cross_realm_krbtgt(signing_krbtgt->princ)) { +@@ -2371,6 +2372,12 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context, + goto done; + } + ++ tmpctx = talloc_new(NULL); ++ if (tmpctx == NULL) { ++ kerr = ENOMEM; ++ goto done; ++ } ++ + for (i = 0; i < num_buffers; i++) { + if (types[i] == KRB5_PAC_SERVER_CHECKSUM || + types[i] == KRB5_PAC_PRIVSVR_CHECKSUM || +@@ -2398,32 +2405,21 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context, + DATA_BLOB pac_attrs_data; + krb5_boolean pac_requested; + +- TALLOC_CTX *tmpctx = talloc_new(NULL); +- if (tmpctx == NULL) { +- kerr = ENOMEM; +- goto done; +- } +- + kerr = ipadb_client_requested_pac(context, old_pac, tmpctx, &pac_requested); +- if (kerr != 0) { +- talloc_free(tmpctx); ++ if (kerr) + goto done; +- } + + kerr = ipadb_get_pac_attrs_blob(tmpctx, &pac_requested, &pac_attrs_data); +- if (kerr) { +- talloc_free(tmpctx); ++ if (kerr) + goto done; +- } ++ + data.magic = KV5M_DATA; + data.data = (char *)pac_attrs_data.data; + data.length = pac_attrs_data.length; + + kerr = krb5_pac_add_buffer(context, new_pac, PAC_TYPE_ATTRIBUTES_INFO, &data); +- if (kerr) { +- talloc_free(tmpctx); ++ if (kerr) + goto done; +- } + + continue; + } +@@ -2470,6 +2466,8 @@ done: + if (kerr != 0 && (new_pac != *pac)) { + krb5_pac_free(context, new_pac); + } ++ if (tmpctx) ++ talloc_free(tmpctx); + krb5_free_data_contents(context, &pac_blob); + free(types); + return kerr; +-- +2.43.0 + diff --git a/0004-ipa-cli-krb5-crash.patch b/0004-ipa-cli-krb5-crash.patch new file mode 100644 index 0000000..9a04289 --- /dev/null +++ b/0004-ipa-cli-krb5-crash.patch @@ -0,0 +1,247 @@ +From 33638de180a8157e369ad6c61f9e3406d9e85404 Mon Sep 17 00:00:00 2001 +From: Stanislav Levin +Date: Tue, 23 Jan 2024 19:12:53 +0300 +Subject: [PATCH 1/3] ipapython: Clean up krb5_error + +`krb5_error` has different definition in MIT krb. +https://web.mit.edu/kerberos/krb5-latest/doc/appdev/refs/types/krb5_error.html + +> Error message structure. +> +> Declaration: +> typedef struct _krb5_error krb5_error + +While `krb5_error_code` +https://web.mit.edu/kerberos/www/krb5-latest/doc/appdev/refs/types/krb5_error_code.html#c.krb5_error_code + +> krb5_error_code +> Used to convey an operation status. +> +> The value 0 indicates success; any other values are com_err codes. Use krb5_get_error_message() to obtain a string describing the error. +> +> Declaration +> typedef krb5_int32 krb5_error_code + +And this is what was actually used. + +To prevent confusion of types `krb5_error` was replaced with +`krb5_error_code`. + +Fixes: https://pagure.io/freeipa/issue/9519 +Signed-off-by: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + ipapython/session_storage.py | 25 ++++++++++++------------- + 1 file changed, 12 insertions(+), 13 deletions(-) + +diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py +index c43ef7d4e..371cf1524 100644 +--- a/ipapython/session_storage.py ++++ b/ipapython/session_storage.py +@@ -111,7 +111,7 @@ class KRB5Error(Exception): + + + def krb5_errcheck(result, func, arguments): +- """Error checker for krb5_error return value""" ++ """Error checker for krb5_error_code return value""" + if result != 0: + raise KRB5Error(result, func.__name__, arguments) + +@@ -119,14 +119,13 @@ def krb5_errcheck(result, func, arguments): + 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), ) +-krb5_init_context.restype = krb5_error ++krb5_init_context.restype = krb5_error_code + krb5_init_context.errcheck = krb5_errcheck + + krb5_free_context = LIBKRB5.krb5_free_context +@@ -143,30 +142,30 @@ krb5_free_data_contents.restype = None + + krb5_cc_default = LIBKRB5.krb5_cc_default + krb5_cc_default.argtypes = (krb5_context, ctypes.POINTER(krb5_ccache), ) +-krb5_cc_default.restype = krb5_error ++krb5_cc_default.restype = krb5_error_code + krb5_cc_default.errcheck = krb5_errcheck + + krb5_cc_close = LIBKRB5.krb5_cc_close + krb5_cc_close.argtypes = (krb5_context, krb5_ccache, ) +-krb5_cc_close.restype = krb5_error ++krb5_cc_close.restype = krb5_error_code + 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.restype = krb5_error ++krb5_parse_name.restype = krb5_error_code + 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.restype = krb5_error ++krb5_cc_set_config.restype = krb5_error_code + krb5_cc_set_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.restype = krb5_error_code + krb5_cc_get_principal.errcheck = krb5_errcheck + + # krb5_build_principal is a variadic function but that can't be expressed +@@ -177,26 +176,26 @@ 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.restype = krb5_error_code + 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.restype = krb5_error_code + 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.restype = krb5_error_code + 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.restype = krb5_error_code + krb5_cc_end_seq_get.errcheck = krb5_errcheck + + krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents +@@ -212,7 +211,7 @@ 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.restype = krb5_error_code + krb5_unparse_name.errcheck = krb5_errcheck + + krb5_free_unparsed_name = LIBKRB5.krb5_free_unparsed_name +-- +2.43.0 + + +From f8a616dc6196324145372713da772fe9b2352e53 Mon Sep 17 00:00:00 2001 +From: Stanislav Levin +Date: Tue, 23 Jan 2024 19:19:43 +0300 +Subject: [PATCH 2/3] ipapython: Correct return type of krb5_free_cred_contents + +According to https://web.mit.edu/kerberos/krb5-latest/doc/appdev/refs/api/krb5_free_cred_contents.html + +> krb5_free_cred_contents - Free the contents of a krb5_creds structure. +> +> void krb5_free_cred_contents(krb5_context context, krb5_creds * val) +> param: +> [in] context - Library context +> +> [in] val - Credential structure to free contents of +> +> This function frees the contents of val , but not the structure itself. + +https://github.com/krb5/krb5/blob/5b00197227231943bd2305328c8260dd0b0dbcf0/src/lib/krb5/krb/kfree.c#L166 + +This leads to undefined behavior and `krb5_free_cred_contents` can +raise KRB5Error (because of garbage data) while actually its foreign +function doesn't. + +Fixes: https://pagure.io/freeipa/issue/9519 +Signed-off-by: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + ipapython/session_storage.py | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py +index 371cf1524..dc36f5493 100644 +--- a/ipapython/session_storage.py ++++ b/ipapython/session_storage.py +@@ -200,8 +200,7 @@ 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_free_cred_contents.restype = None + + krb5_principal_compare = LIBKRB5.krb5_principal_compare + krb5_principal_compare.argtypes = (krb5_context, krb5_principal, +-- +2.43.0 + + +From 59b8a9fb7169561c7ba9168fe84f47ae94e5ce23 Mon Sep 17 00:00:00 2001 +From: Stanislav Levin +Date: Tue, 23 Jan 2024 19:52:34 +0300 +Subject: [PATCH 3/3] ipapython: Propagate KRB5Error exceptions on iterating + ccache + +`ipapython.session_storage.get_data` iterates over +credentials in a credential cache till `krb5_cc_next_cred` returns +an error. This function doesn't expect any error on calling +other kerberos foreign functions during iteration. But that can +actually happen and KRB5Error exceptions stop an iteration while +they should be propagated. + +With this change iteration will exactly stop on `krb5_cc_next_cred` +error as it was supposed to be. + +Fixes: https://pagure.io/freeipa/issue/9519 +Signed-off-by: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + ipapython/session_storage.py | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py +index dc36f5493..e890dc9b1 100644 +--- a/ipapython/session_storage.py ++++ b/ipapython/session_storage.py +@@ -312,8 +312,12 @@ def get_data(princ_name, key): + 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)) ++ try: ++ krb5_cc_next_cred(context, ccache, ctypes.byref(cursor), ++ ctypes.byref(checkcreds)) ++ except KRB5Error: ++ break ++ + if (krb5_principal_compare(context, principal, + checkcreds.client) == 1 and + krb5_principal_compare(context, srv_princ, +@@ -328,8 +332,6 @@ def get_data(princ_name, key): + else: + krb5_free_cred_contents(context, + ctypes.byref(checkcreds)) +- except KRB5Error: +- pass + finally: + krb5_cc_end_seq_get(context, ccache, ctypes.byref(cursor)) + +-- +2.43.0 + diff --git a/0005-pyca-42.0.0-support.patch b/0005-pyca-42.0.0-support.patch new file mode 100644 index 0000000..8318ac4 --- /dev/null +++ b/0005-pyca-42.0.0-support.patch @@ -0,0 +1,424 @@ +From fa46b41af42797dba8dcd04b8cacbc78d602ab80 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Wed, 24 Jan 2024 09:23:22 +0100 +Subject: [PATCH 1/2] Compatibility fix for PyCA cryptography 42.0.0 + +Cryptography 42.0.0 introduced two new abstract properties +`not_valid_before_utc` and `not_valid_after_utc`, which are non-naive UTC +variants of the `not_valid_before` and `not_valid_after` properties. + +The old properties are deprecated. The changeset also modifies code and +tests to use the new `_utc` variants. + +Fixes: https://pagure.io/freeipa/issue/9518 +Signed-off-by: Christian Heimes +Reviewed-By: Florence Blanc-Renaud +--- + ipaclient/install/client.py | 4 ++-- + ipalib/x509.py | 22 +++++++++++++++++++ + ipapython/certdb.py | 15 +++++++------ + ipaserver/install/ipa_cacert_manage.py | 2 +- + ipaserver/install/ipa_cert_fix.py | 12 +++++----- + ipaserver/plugins/cert.py | 4 ++-- + ipaserver/plugins/dogtag.py | 12 +++++----- + ipaserver/plugins/service.py | 5 +++-- + ipatests/test_integration/test_acme.py | 4 ++-- + .../test_integration/test_installation.py | 2 +- + .../test_integration/test_ipa_cert_fix.py | 2 +- + .../test_integration/test_ipahealthcheck.py | 2 +- + ipatests/test_ipalib/test_x509.py | 6 +++++ + 13 files changed, 61 insertions(+), 31 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 976d3821d..5b97a37f2 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -1727,8 +1727,8 @@ def cert_summary(msg, certs, indent=' '): + for cert in certs: + s += '%sSubject: %s\n' % (indent, DN(cert.subject)) + s += '%sIssuer: %s\n' % (indent, DN(cert.issuer)) +- s += '%sValid From: %s\n' % (indent, cert.not_valid_before) +- s += '%sValid Until: %s\n' % (indent, cert.not_valid_after) ++ s += '%sValid From: %s\n' % (indent, cert.not_valid_before_utc) ++ s += '%sValid Until: %s\n' % (indent, cert.not_valid_after_utc) + s += '\n' + s = s[:-1] + +diff --git a/ipalib/x509.py b/ipalib/x509.py +index 769d48007..daeea8195 100644 +--- a/ipalib/x509.py ++++ b/ipalib/x509.py +@@ -272,6 +272,28 @@ class IPACertificate(crypto_x509.Certificate): + def not_valid_after(self): + return self._cert.not_valid_after.replace(tzinfo=datetime.timezone.utc) + ++ if hasattr(crypto_x509.Certificate, "not_valid_before_utc"): ++ # added in python-cryptography 42.0.0 ++ @property ++ def not_valid_before_utc(self): ++ return self._cert.not_valid_before_utc ++ ++ @property ++ def not_valid_after_utc(self): ++ return self._cert.not_valid_after_utc ++ else: ++ @property ++ def not_valid_before_utc(self): ++ return self._cert.not_valid_before.replace( ++ tzinfo=datetime.timezone.utc ++ ) ++ ++ @property ++ def not_valid_after_utc(self): ++ return self._cert.not_valid_after.replace( ++ tzinfo=datetime.timezone.utc ++ ) ++ + @property + def tbs_certificate_bytes(self): + return self._cert.tbs_certificate_bytes +diff --git a/ipapython/certdb.py b/ipapython/certdb.py +index 21af42d23..e3a80bcec 100644 +--- a/ipapython/certdb.py ++++ b/ipapython/certdb.py +@@ -944,19 +944,20 @@ class NSSDatabase: + """Common checks for cert validity + """ + utcnow = datetime.datetime.now(tz=datetime.timezone.utc) +- if cert.not_valid_before > utcnow: ++ if cert.not_valid_before_utc > utcnow: + raise ValueError( +- f"not valid before {cert.not_valid_before} UTC is in the " +- "future." ++ f"not valid before {cert.not_valid_before_utc} UTC is in " ++ "the future." + ) +- if cert.not_valid_after < utcnow: ++ if cert.not_valid_after_utc < utcnow: + raise ValueError( +- f"has expired {cert.not_valid_after} UTC" ++ f"has expired {cert.not_valid_after_utc} UTC" + ) + # make sure the cert does not expire during installation +- if cert.not_valid_after + datetime.timedelta(hours=1) < utcnow: ++ if cert.not_valid_after_utc + datetime.timedelta(hours=1) < utcnow: + raise ValueError( +- f"expires in less than one hour ({cert.not_valid_after} UTC)" ++ f"expires in less than one hour ({cert.not_valid_after_utc} " ++ "UTC)" + ) + + def verify_server_cert_validity(self, nickname, hostname): +diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py +index d371a854b..f6ab736fa 100644 +--- a/ipaserver/install/ipa_cacert_manage.py ++++ b/ipaserver/install/ipa_cacert_manage.py +@@ -558,7 +558,7 @@ class CACertManage(admintool.AdminTool): + + now = datetime.datetime.now(tz=datetime.timezone.utc) + for ca_cert, ca_nickname, _ca_trust_flags in ca_certs: +- if ca_cert.not_valid_after < now: ++ if ca_cert.not_valid_after_utc < now: + expired_certs.append(ca_nickname) + + +diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py +index 834e9557d..8e02d1e75 100644 +--- a/ipaserver/install/ipa_cert_fix.py ++++ b/ipaserver/install/ipa_cert_fix.py +@@ -208,7 +208,7 @@ def expired_dogtag_certs(now): + except RuntimeError: + pass # unfortunately certdb doesn't give us a better exception + else: +- if cert.not_valid_after <= now: ++ if cert.not_valid_after_utc <= now: + certs.append((certid, cert)) + + return certs +@@ -226,12 +226,12 @@ def expired_ipa_certs(now): + + # IPA RA + cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM) +- if cert.not_valid_after <= now: ++ if cert.not_valid_after_utc <= now: + certs.append((IPACertType.IPARA, cert)) + + # Apache HTTPD + cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE) +- if cert.not_valid_after <= now: ++ if cert.not_valid_after_utc <= now: + if not is_ipa_issued_cert(api, cert): + non_renewed.append((IPACertType.HTTPS, cert)) + else: +@@ -244,7 +244,7 @@ def expired_ipa_certs(now): + ds_nickname = ds.get_server_cert_nickname(serverid) + db = NSSDatabase(nssdir=ds_dbdir) + cert = db.get_cert(ds_nickname) +- if cert.not_valid_after <= now: ++ if cert.not_valid_after_utc <= now: + if not is_ipa_issued_cert(api, cert): + non_renewed.append((IPACertType.LDAPS, cert)) + else: +@@ -252,7 +252,7 @@ def expired_ipa_certs(now): + + # KDC + cert = x509.load_certificate_from_file(paths.KDC_CERT) +- if cert.not_valid_after <= now: ++ if cert.not_valid_after_utc <= now: + if not is_ipa_issued_cert(api, cert): + non_renewed.append((IPACertType.HTTPS, cert)) + else: +@@ -286,7 +286,7 @@ def print_cert_info(context, desc, cert): + print("{} {} certificate:".format(context, desc)) + print(" Subject: {}".format(DN(cert.subject))) + print(" Serial: {}".format(cert.serial_number)) +- print(" Expires: {}".format(cert.not_valid_after)) ++ print(" Expires: {}".format(cert.not_valid_after_utc)) + print() + + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 4fb85069a..d52cdc1e2 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -486,9 +486,9 @@ class BaseCertObject(Object): + obj['serial_number'] = str(cert.serial_number) + obj['serial_number_hex'] = '0x%X' % cert.serial_number + obj['valid_not_before'] = x509.format_datetime( +- cert.not_valid_before) ++ cert.not_valid_before_utc) + obj['valid_not_after'] = x509.format_datetime( +- cert.not_valid_after) ++ cert.not_valid_after_utc) + if full: + obj['sha1_fingerprint'] = x509.to_hex_with_colons( + cert.fingerprint(hashes.SHA1())) +diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py +index 7cd51ae58..23667c3dd 100644 +--- a/ipaserver/plugins/dogtag.py ++++ b/ipaserver/plugins/dogtag.py +@@ -1475,14 +1475,14 @@ class ra(rabase.rabase, RestClient): + if issuer_dn: + response_request['issuer'] = issuer_dn + +- not_valid_before = cert.get('NotValidBefore') +- if not_valid_before: ++ not_valid_before_utc = cert.get('NotValidBefore') ++ if not_valid_before_utc: + response_request['valid_not_before'] = ( +- not_valid_before) ++ not_valid_before_utc) + +- not_valid_after = cert.get('NotValidAfter') +- if not_valid_after: +- response_request['valid_not_after'] = (not_valid_after) ++ not_valid_after_utc = cert.get('NotValidAfter') ++ if not_valid_after_utc: ++ response_request['valid_not_after'] = (not_valid_after_utc) + + status = cert.get('Status') + if status: +diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py +index f4b107213..075a1be8a 100644 +--- a/ipaserver/plugins/service.py ++++ b/ipaserver/plugins/service.py +@@ -303,8 +303,9 @@ def set_certificate_attrs(entry_attrs): + entry_attrs['serial_number_hex'] = u'0x%X' % cert.serial_number + entry_attrs['issuer'] = unicode(DN(cert.issuer)) + entry_attrs['valid_not_before'] = x509.format_datetime( +- cert.not_valid_before) +- entry_attrs['valid_not_after'] = x509.format_datetime(cert.not_valid_after) ++ cert.not_valid_before_utc) ++ entry_attrs['valid_not_after'] = x509.format_datetime( ++ cert.not_valid_after_utc) + entry_attrs['sha1_fingerprint'] = x509.to_hex_with_colons( + cert.fingerprint(hashes.SHA1())) + entry_attrs['sha256_fingerprint'] = x509.to_hex_with_colons( +diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py +index ee13eb4a0..8e6243d4c 100644 +--- a/ipatests/test_integration/test_acme.py ++++ b/ipatests/test_integration/test_acme.py +@@ -670,7 +670,7 @@ class TestACMERenew(IntegrationTest): + f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' + ) + cert = x509.load_pem_x509_certificate(data, backend=default_backend()) +- initial_expiry = cert.not_valid_after ++ initial_expiry = cert.not_valid_after_utc + + self.clients[0].run_command(['certbot', 'renew']) + +@@ -678,7 +678,7 @@ class TestACMERenew(IntegrationTest): + f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' + ) + cert = x509.load_pem_x509_certificate(data, backend=default_backend()) +- renewed_expiry = cert.not_valid_after ++ renewed_expiry = cert.not_valid_after_utc + + assert initial_expiry != renewed_expiry + +diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py +index 36142447f..217aae019 100644 +--- a/ipatests/test_integration/test_installation.py ++++ b/ipatests/test_integration/test_installation.py +@@ -1565,7 +1565,7 @@ class TestKRAinstallAfterCertRenew(IntegrationTest): + certs = x509.load_certificate_list(cmd.stdout_text.encode('utf-8')) + + # get expiry date of agent cert +- cert_expiry = certs[0].not_valid_after ++ cert_expiry = certs[0].not_valid_after_utc + + # move date to grace period so that certs get renewed + self.master.run_command(['systemctl', 'stop', 'chronyd']) +diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py +index ec9456e51..219e7d0e1 100644 +--- a/ipatests/test_integration/test_ipa_cert_fix.py ++++ b/ipatests/test_integration/test_ipa_cert_fix.py +@@ -92,7 +92,7 @@ def get_cert_expiry(host, nssdb_path, cert_nick): + ]) + data = host.get_file_contents('/root/cert.pem') + cert = x509.load_pem_x509_certificate(data, backend=default_backend()) +- return cert.not_valid_after ++ return cert.not_valid_after_utc + + + @pytest.fixture +diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py +index 40c848988..28200e096 100644 +--- a/ipatests/test_integration/test_ipahealthcheck.py ++++ b/ipatests/test_integration/test_ipahealthcheck.py +@@ -1595,7 +1595,7 @@ class TestIpaHealthCheck(IntegrationTest): + # Pick a cert to find the upcoming expiration + certfile = self.master.get_file_contents(paths.RA_AGENT_PEM) + cert = x509.load_certificate_list(certfile) +- cert_expiry = cert[0].not_valid_after ++ cert_expiry = cert[0].not_valid_after_utc + + # Stop chronyd so it doesn't freak out with time so off + restart_service(self.master, 'chronyd') +diff --git a/ipatests/test_ipalib/test_x509.py b/ipatests/test_ipalib/test_x509.py +index 74287c84a..8ab2ea8c3 100644 +--- a/ipatests/test_ipalib/test_x509.py ++++ b/ipatests/test_ipalib/test_x509.py +@@ -246,6 +246,8 @@ class test_x509: + assert cert.serial_number == 1093 + assert cert.not_valid_before == not_before + assert cert.not_valid_after == not_after ++ assert cert.not_valid_before_utc == not_before ++ assert cert.not_valid_after_utc == not_after + assert cert.san_general_names == [] + assert cert.san_a_label_dns_names == [] + assert cert.extended_key_usage == {'1.3.6.1.5.5.7.3.1'} +@@ -277,6 +279,8 @@ class test_x509: + # ensure the timezone doesn't mess with not_before and not_after + assert cert.not_valid_before == not_before + assert cert.not_valid_after == not_after ++ assert cert.not_valid_before_utc == not_before ++ assert cert.not_valid_after_utc == not_after + + def test_load_pkcs7_pem(self): + certlist = x509.pkcs7_to_certs(good_pkcs7, datatype=x509.PEM) +@@ -312,6 +316,8 @@ class test_x509: + datetime.timezone.utc) + assert cert.not_valid_before == not_before + assert cert.not_valid_after == not_after ++ assert cert.not_valid_before_utc == not_before ++ assert cert.not_valid_after_utc == not_after + assert cert.san_general_names == [DNSName('ipa.demo1.freeipa.org')] + assert cert.san_a_label_dns_names == ['ipa.demo1.freeipa.org'] + assert cert.extended_key_usage == { +-- +2.43.0 + + +From 18244d7ec1103ec6fba0f94c385e62dba774ed3d Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Thu, 25 Jan 2024 08:56:11 +0100 +Subject: [PATCH 2/2] test_acme: Use ipalib.x509 + +Use IPA's x509 module instead of `cryptography.x509`. This fixes a +regression which was introduced in commit a45a7a20. + +Related: https://pagure.io/freeipa/issue/9518 +Signed-off-by: Christian Heimes +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Mohammad Rizwan Yusuf +--- + ipatests/test_integration/test_acme.py | 9 ++++----- + ipatests/test_integration/test_ipa_cert_fix.py | 5 ++--- + 2 files changed, 6 insertions(+), 8 deletions(-) + +diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py +index 8e6243d4c..4032d266a 100644 +--- a/ipatests/test_integration/test_acme.py ++++ b/ipatests/test_integration/test_acme.py +@@ -4,11 +4,10 @@ + + import time + +-from cryptography.hazmat.backends import default_backend +-from cryptography import x509 + import pytest + + from ipalib.constants import IPA_CA_RECORD ++from ipalib import x509 + from ipatests.test_integration.base import IntegrationTest + from ipatests.pytest_ipa.integration.firewall import Firewall + from ipatests.pytest_ipa.integration import tasks +@@ -278,7 +277,7 @@ class TestACME(CALessBase): + cert_path = \ + f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' + data = self.clients[0].get_file_contents(cert_path) +- cert = x509.load_pem_x509_certificate(data, backend=default_backend()) ++ cert = x509.load_pem_x509_certificate(data) + + # revoke cert via ACME + self.clients[0].run_command( +@@ -669,7 +668,7 @@ class TestACMERenew(IntegrationTest): + data = self.clients[0].get_file_contents( + f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' + ) +- cert = x509.load_pem_x509_certificate(data, backend=default_backend()) ++ cert = x509.load_pem_x509_certificate(data) + initial_expiry = cert.not_valid_after_utc + + self.clients[0].run_command(['certbot', 'renew']) +@@ -677,7 +676,7 @@ class TestACMERenew(IntegrationTest): + data = self.clients[0].get_file_contents( + f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' + ) +- cert = x509.load_pem_x509_certificate(data, backend=default_backend()) ++ cert = x509.load_pem_x509_certificate(data) + renewed_expiry = cert.not_valid_after_utc + + assert initial_expiry != renewed_expiry +diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py +index 219e7d0e1..e6ec30de1 100644 +--- a/ipatests/test_integration/test_ipa_cert_fix.py ++++ b/ipatests/test_integration/test_ipa_cert_fix.py +@@ -5,13 +5,12 @@ + """ + Module provides tests for ipa-cert-fix CLI. + """ +-from cryptography.hazmat.backends import default_backend +-from cryptography import x509 + from datetime import datetime, date + import pytest + import time + + import logging ++from ipalib import x509 + from ipaplatform.paths import paths + from ipapython.ipaldap import realm_to_serverid + from ipatests.pytest_ipa.integration import tasks +@@ -91,7 +90,7 @@ def get_cert_expiry(host, nssdb_path, cert_nick): + '-o', '/root/cert.pem' + ]) + data = host.get_file_contents('/root/cert.pem') +- cert = x509.load_pem_x509_certificate(data, backend=default_backend()) ++ cert = x509.load_pem_x509_certificate(data) + return cert.not_valid_after_utc + + +-- +2.43.0 + diff --git a/0006-ca-affinity-fix.patch b/0006-ca-affinity-fix.patch new file mode 100644 index 0000000..bca4f30 --- /dev/null +++ b/0006-ca-affinity-fix.patch @@ -0,0 +1,69 @@ +From 5dbb3101cee7a96ec8eef40be8e802d456c0d06c Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Mon, 22 Jan 2024 08:36:27 -0500 +Subject: [PATCH] Server affinity: call ca.install() if there is a CA in the + topology + +This should not have been gated on options.setup_ca because we need +the RA agent on all servers if there is a CA in the topology otherwise +the non-CA servers won't be able to communicate with the CA. + +Fixes: https://pagure.io/freeipa/issue/9510 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/ca.py | 7 ++++--- + ipaserver/install/server/replicainstall.py | 7 +++++-- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py +index c93ae1fce..187f8032b 100644 +--- a/ipaserver/install/ca.py ++++ b/ipaserver/install/ca.py +@@ -387,9 +387,10 @@ def install_step_0(standalone, replica_config, options, custodia): + promote = False + else: + cafile = os.path.join(replica_config.dir, 'cacert.p12') +- custodia.get_ca_keys( +- cafile, +- replica_config.dirman_password) ++ if replica_config.setup_ca: ++ custodia.get_ca_keys( ++ cafile, ++ replica_config.dirman_password) + + ca_signing_algorithm = None + ca_type = None +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 191913ddb..b3fd27e6a 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1382,11 +1382,13 @@ def install(installer): + custodia = custodiainstance.get_custodia_instance(config, mode) + custodia.create_instance() + +- if options.setup_ca and ca_enabled: ++ if ca_enabled: + options.realm_name = config.realm_name + options.domain_name = config.domain_name + options.host_name = config.host_name + options.dm_password = config.dirman_password ++ # Always call ca.install() if there is a CA in the topology ++ # to ensure the RA agent is present. + ca.install(False, config, options, custodia=custodia) + + # configure PKINIT now that all required services are in place +@@ -1398,7 +1400,8 @@ def install(installer): + service.print_msg("Finalize replication settings") + ds.finalize_replica_config() + +- if options.setup_kra and kra_enabled: ++ if kra_enabled: ++ # The KRA installer checks for itself the status of setup_kra + kra.install(api, config, options, custodia=custodia) + + service.print_msg("Restarting the KDC") +-- +2.43.0 + diff --git a/freeipa.spec b/freeipa.spec index ab95371..82dd4c6 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -96,9 +96,8 @@ %global alt_name ipa # 0.7.16: https://github.com/drkjam/netaddr/issues/71 %global python_netaddr_version 0.7.16 -# Require 4.7.0 which brings Python 3 bindings -# Require 4.12 which has DsRGetForestTrustInformation access rights fixes -%global samba_version 2:4.12.10 +# Require 4.20.0 for libndr4 +%global samba_version 2:4.20.0 # 38.28 or later includes passkey-related fixes %global selinux_policy_version 38.28-1 @@ -201,7 +200,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 1%{?rc_version:.%rc_version}%{?dist}.2 +Release: 2%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -224,6 +223,10 @@ Patch0001: freeipa-4.11-samba-changes.patch Patch0002: freeipa-4.11-pki-revocation-changes.patch Patch0003: freeipa-4.11-py3.12-timezone-changes.patch Patch0004: freeipa-4.11-pwpolicy-minlength.patch +Patch0005: 0003-kdb-memory-leak.patch +Patch0006: 0004-ipa-cli-krb5-crash.patch +Patch0007: 0005-pyca-42.0.0-support.patch +Patch0008: 0006-ca-affinity-fix.patch # RHEL spec file only: START: Change branding to IPA and Identity Management # Moved branding logos and background to redhat-logos-ipa-80.4: @@ -1744,6 +1747,13 @@ fi %endif %changelog +* Wed Jan 24 2024 Fedora Release Engineering - 4.11.1-2 +- Rebuild against Samba 4.20rc1 +- Fix memory leak in Kerberos KDC driver +- Fix possible crash in IPA command line tool when accessing Kerberos credentials +- Compatibility fix for Python Cryptography 42.0.0 +- Fix CA affinity when installing replica + * Wed Jan 24 2024 Fedora Release Engineering - 4.11.1-1.2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild