- CVE-2023-5455
- ipa-kdb: Detect and block Bronze-Bit attacks
This commit is contained in:
		
							parent
							
								
									697efbdf54
								
							
						
					
					
						commit
						e82ab6efc0
					
				
							
								
								
									
										118
									
								
								SOURCES/0015-CVE-2023-5455.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								SOURCES/0015-CVE-2023-5455.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | From 13778d88ca2ac73b729821bdea844172a18c0cb9 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Rob Crittenden <rcritten@redhat.com> | ||||||
|  | Date: Fri, 6 Oct 2023 20:16:29 +0000 | ||||||
|  | Subject: [PATCH] Check the HTTP Referer header on all requests | ||||||
|  | 
 | ||||||
|  | The referer was only checked in WSGIExecutioner classes: | ||||||
|  | 
 | ||||||
|  |  - jsonserver | ||||||
|  |  - KerberosWSGIExecutioner | ||||||
|  |  - xmlserver | ||||||
|  |  - jsonserver_kerb | ||||||
|  | 
 | ||||||
|  | This left /i18n_messages, /session/login_kerberos, | ||||||
|  | /session/login_x509, /session/login_password, | ||||||
|  | /session/change_password and /session/sync_token unprotected | ||||||
|  | against CSRF attacks. | ||||||
|  | 
 | ||||||
|  | CVE-2023-5455 | ||||||
|  | 
 | ||||||
|  | Signed-off-by: Rob Crittenden <rcritten@redhat.com> | ||||||
|  | ---
 | ||||||
|  |  ipaserver/rpcserver.py | 34 +++++++++++++++++++++++++++++++--- | ||||||
|  |  1 file changed, 31 insertions(+), 3 deletions(-) | ||||||
|  | 
 | ||||||
|  | diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
 | ||||||
|  | index b7116469d73..198fc9e7dba 100644
 | ||||||
|  | --- a/ipaserver/rpcserver.py
 | ||||||
|  | +++ b/ipaserver/rpcserver.py
 | ||||||
|  | @@ -156,6 +156,19 @@ _success_template = """<html>
 | ||||||
|  |  </html>""" | ||||||
|  |   | ||||||
|  |  class HTTP_Status(plugable.Plugin): | ||||||
|  | +    def check_referer(self, environ):
 | ||||||
|  | +        if "HTTP_REFERER" not in environ:
 | ||||||
|  | +            logger.error("Rejecting request with missing Referer")
 | ||||||
|  | +            return False
 | ||||||
|  | +        if (not environ["HTTP_REFERER"].startswith(
 | ||||||
|  | +                "https://%s/ipa" % self.api.env.host)
 | ||||||
|  | +                and not self.env.in_tree):
 | ||||||
|  | +            logger.error("Rejecting request with bad Referer %s",
 | ||||||
|  | +                         environ["HTTP_REFERER"])
 | ||||||
|  | +            return False
 | ||||||
|  | +        logger.debug("Valid Referer %s", environ["HTTP_REFERER"])
 | ||||||
|  | +        return True
 | ||||||
|  | +
 | ||||||
|  |      def not_found(self, environ, start_response, url, message): | ||||||
|  |          """ | ||||||
|  |          Return a 404 Not Found error. | ||||||
|  | @@ -331,9 +344,6 @@ class wsgi_dispatch(Executioner, HTTP_Status):
 | ||||||
|  |          self.__apps[key] = app | ||||||
|  |   | ||||||
|  |   | ||||||
|  | -
 | ||||||
|  | -
 | ||||||
|  | -
 | ||||||
|  |  class WSGIExecutioner(Executioner): | ||||||
|  |      """ | ||||||
|  |      Base class for execution backends with a WSGI application interface. | ||||||
|  | @@ -897,6 +907,9 @@ class jsonserver_session(jsonserver, KerberosSession):
 | ||||||
|  |   | ||||||
|  |          logger.debug('WSGI jsonserver_session.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          # Redirect to login if no Kerberos credentials | ||||||
|  |          ccache_name = self.get_environ_creds(environ) | ||||||
|  |          if ccache_name is None: | ||||||
|  | @@ -949,6 +962,9 @@ class KerberosLogin(Backend, KerberosSession):
 | ||||||
|  |      def __call__(self, environ, start_response): | ||||||
|  |          logger.debug('WSGI KerberosLogin.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          # Redirect to login if no Kerberos credentials | ||||||
|  |          user_ccache_name = self.get_environ_creds(environ) | ||||||
|  |          if user_ccache_name is None: | ||||||
|  | @@ -967,6 +983,9 @@ class login_x509(KerberosLogin):
 | ||||||
|  |      def __call__(self, environ, start_response): | ||||||
|  |          logger.debug('WSGI login_x509.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          if 'KRB5CCNAME' not in environ: | ||||||
|  |              return self.unauthorized( | ||||||
|  |                  environ, start_response, 'KRB5CCNAME not set', | ||||||
|  | @@ -1015,6 +1034,9 @@ class login_password(Backend, KerberosSession):
 | ||||||
|  |   | ||||||
|  |          logger.debug('WSGI login_password.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          # Get the user and password parameters from the request | ||||||
|  |          content_type = environ.get('CONTENT_TYPE', '').lower() | ||||||
|  |          if not content_type.startswith('application/x-www-form-urlencoded'): | ||||||
|  | @@ -1147,6 +1169,9 @@ class change_password(Backend, HTTP_Status):
 | ||||||
|  |      def __call__(self, environ, start_response): | ||||||
|  |          logger.info('WSGI change_password.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          # Get the user and password parameters from the request | ||||||
|  |          content_type = environ.get('CONTENT_TYPE', '').lower() | ||||||
|  |          if not content_type.startswith('application/x-www-form-urlencoded'): | ||||||
|  | @@ -1364,6 +1389,9 @@ class xmlserver_session(xmlserver, KerberosSession):
 | ||||||
|  |   | ||||||
|  |          logger.debug('WSGI xmlserver_session.__call__:') | ||||||
|  |   | ||||||
|  | +        if not self.check_referer(environ):
 | ||||||
|  | +            return self.bad_request(environ, start_response, 'denied')
 | ||||||
|  | +
 | ||||||
|  |          ccache_name = environ.get('KRB5CCNAME') | ||||||
|  |   | ||||||
|  |          # Redirect to /ipa/xml if no Kerberos credentials | ||||||
							
								
								
									
										262
									
								
								SOURCES/0016-ipa-kdb-Detect-and-block-Bronze-Bit-attacks.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								SOURCES/0016-ipa-kdb-Detect-and-block-Bronze-Bit-attacks.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,262 @@ | |||||||
|  | From a847e2483b4c4832ee5129901da169f4eb0d1392 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Julien Rische <jrische@redhat.com> | ||||||
|  | Date: Mon, 9 Oct 2023 15:47:03 +0200 | ||||||
|  | Subject: [PATCH] ipa-kdb: Detect and block Bronze-Bit attacks | ||||||
|  | 
 | ||||||
|  | The C8S/RHEL8 version of FreeIPA is vulnerable to the Bronze-Bit attack | ||||||
|  | because it does not implement PAC ticket signature to protect the | ||||||
|  | "forwardable" flag. However, it does implement the PAC extended KDC | ||||||
|  | signature, which protects against PAC spoofing. | ||||||
|  | 
 | ||||||
|  | Based on information available in the PAC and the | ||||||
|  | "ok-to-auth-as-delegate" attribute in the database. It is possible to | ||||||
|  | detect and reject requests where the "forwardable" flag was flipped by | ||||||
|  | the attacker in the evidence ticket. | ||||||
|  | ---
 | ||||||
|  |  daemons/ipa-kdb/ipa_kdb.h           |  13 +++ | ||||||
|  |  daemons/ipa-kdb/ipa_kdb_kdcpolicy.c |   6 + | ||||||
|  |  daemons/ipa-kdb/ipa_kdb_mspac.c     | 173 ++++++++++++++++++++++++++++ | ||||||
|  |  ipaserver/install/server/install.py |   8 ++ | ||||||
|  |  4 files changed, 200 insertions(+) | ||||||
|  | 
 | ||||||
|  | diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
 | ||||||
|  | index 7aa5be4948e..02b2cb6313e 100644
 | ||||||
|  | --- a/daemons/ipa-kdb/ipa_kdb.h
 | ||||||
|  | +++ b/daemons/ipa-kdb/ipa_kdb.h
 | ||||||
|  | @@ -367,6 +367,19 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
 | ||||||
|  |                                                    const char *test_realm, size_t size, | ||||||
|  |                                                    char **trusted_realm); | ||||||
|  |   | ||||||
|  | +/* Try to detect a Bronze-Bit attack based on the content of the request and
 | ||||||
|  | + * data from the KDB.
 | ||||||
|  | + *
 | ||||||
|  | + *   context     krb5 context
 | ||||||
|  | + *   request     KDB request
 | ||||||
|  | + *   detected    Set to "true" if a bronze bit attack is detected and the
 | ||||||
|  | + *               pointer is not NULL. Remains unset otherwise.
 | ||||||
|  | + *   status      If the call fails and the pointer is not NULL, set it with a
 | ||||||
|  | + *               message describing the cause of the failure. */
 | ||||||
|  | +krb5_error_code
 | ||||||
|  | +ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
 | ||||||
|  | +                                  bool *detected, const char **status);
 | ||||||
|  | +
 | ||||||
|  |  /* DELEGATION CHECKS */ | ||||||
|  |   | ||||||
|  |  krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext, | ||||||
|  | diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
 | ||||||
|  | index f2804c9b23a..1032dff0b5c 100644
 | ||||||
|  | --- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
 | ||||||
|  | +++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
 | ||||||
|  | @@ -185,6 +185,12 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
 | ||||||
|  |                          const char **status, krb5_deltat *lifetime_out, | ||||||
|  |                          krb5_deltat *renew_lifetime_out) | ||||||
|  |  { | ||||||
|  | +    krb5_error_code kerr;
 | ||||||
|  | +
 | ||||||
|  | +    kerr = ipadb_check_for_bronze_bit_attack(context, request, NULL, status);
 | ||||||
|  | +    if (kerr)
 | ||||||
|  | +        return KRB5KDC_ERR_POLICY;
 | ||||||
|  | +
 | ||||||
|  |      *status = NULL; | ||||||
|  |      *lifetime_out = 0; | ||||||
|  |      *renew_lifetime_out = 0; | ||||||
|  | diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
 | ||||||
|  | index 83cb9914d23..b4e22d4316a 100644
 | ||||||
|  | --- a/daemons/ipa-kdb/ipa_kdb_mspac.c
 | ||||||
|  | +++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
 | ||||||
|  | @@ -3298,3 +3298,176 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
 | ||||||
|  |   | ||||||
|  |  	return KRB5_KDB_NOENTRY; | ||||||
|  |  } | ||||||
|  | +
 | ||||||
|  | +krb5_error_code
 | ||||||
|  | +ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
 | ||||||
|  | +                                  bool *detected, const char **status)
 | ||||||
|  | +{
 | ||||||
|  | +    krb5_error_code kerr;
 | ||||||
|  | +    const char *st = NULL;
 | ||||||
|  | +    size_t i, j;
 | ||||||
|  | +    krb5_ticket *evidence_tkt;
 | ||||||
|  | +    krb5_authdata **authdata, **ifrel = NULL;
 | ||||||
|  | +    krb5_pac pac = NULL;
 | ||||||
|  | +    TALLOC_CTX *tmpctx = NULL;
 | ||||||
|  | +    krb5_data fullsign = { 0, 0, NULL }, linfo_blob = { 0, 0, NULL };
 | ||||||
|  | +    DATA_BLOB linfo_data;
 | ||||||
|  | +    struct PAC_LOGON_INFO_CTR linfo;
 | ||||||
|  | +    enum ndr_err_code ndr_err;
 | ||||||
|  | +    struct dom_sid asserted_identity_sid;
 | ||||||
|  | +    bool evtkt_is_s4u2self = false;
 | ||||||
|  | +    krb5_db_entry *proxy_entry = NULL;
 | ||||||
|  | +
 | ||||||
|  | +    /* If no additional ticket, this is not a constrained delegateion request.
 | ||||||
|  | +     * Skip checks. */
 | ||||||
|  | +    if (!(request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT)) {
 | ||||||
|  | +        kerr = 0;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    evidence_tkt = request->second_ticket[0];
 | ||||||
|  | +
 | ||||||
|  | +    /* No need to check the Forwardable flag. If it was not set, this request
 | ||||||
|  | +     * would have failed earlier. */
 | ||||||
|  | +
 | ||||||
|  | +    /* We only support general constrained delegation (not RBCD), which is not
 | ||||||
|  | +     * available for cross-realms. */
 | ||||||
|  | +    if (!krb5_realm_compare(context, evidence_tkt->server, request->server)) {
 | ||||||
|  | +        st = "S4U2PROXY_NOT_SUPPORTED_FOR_CROSS_REALMS";
 | ||||||
|  | +        kerr = ENOTSUP;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    authdata = evidence_tkt->enc_part2->authorization_data;
 | ||||||
|  | +
 | ||||||
|  | +    /* Search for the PAC. */
 | ||||||
|  | +    for (i = 0; authdata != NULL && authdata[i] != NULL; i++) {
 | ||||||
|  | +        if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
 | ||||||
|  | +            continue;
 | ||||||
|  | +
 | ||||||
|  | +        kerr = krb5_decode_authdata_container(context,
 | ||||||
|  | +                                              KRB5_AUTHDATA_IF_RELEVANT,
 | ||||||
|  | +                                              authdata[i], &ifrel);
 | ||||||
|  | +        if (kerr) {
 | ||||||
|  | +            st = "S4U2PROXY_CANNOT_DECODE_EVIDENCE_TKT_AUTHDATA";
 | ||||||
|  | +            goto end;
 | ||||||
|  | +        }
 | ||||||
|  | +
 | ||||||
|  | +        for (j = 0; ifrel[j] != NULL; j++) {
 | ||||||
|  | +            if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
 | ||||||
|  | +                break;
 | ||||||
|  | +        }
 | ||||||
|  | +        if (ifrel[j] != NULL)
 | ||||||
|  | +            break;
 | ||||||
|  | +
 | ||||||
|  | +        krb5_free_authdata(context, ifrel);
 | ||||||
|  | +        ifrel = NULL;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    if (ifrel == NULL) {
 | ||||||
|  | +        st = "S4U2PROXY_EVIDENCE_TKT_WITHOUT_PAC";
 | ||||||
|  | +        kerr = ENOENT;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Parse the PAC. */
 | ||||||
|  | +    kerr = krb5_pac_parse(context, ifrel[j]->contents, ifrel[j]->length, &pac);
 | ||||||
|  | +    if (kerr) {
 | ||||||
|  | +        st = "S4U2PROXY_CANNOT_DECODE_EVICENCE_TKT_PAC";
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Check that the PAC extanded KDC signature is present. If it is, it was
 | ||||||
|  | +     * already tested.
 | ||||||
|  | +     * If absent, the context of the PAC cannot be trusted. */
 | ||||||
|  | +    kerr = krb5_pac_get_buffer(context, pac, KRB5_PAC_FULL_CHECKSUM, &fullsign);
 | ||||||
|  | +    if (kerr) {
 | ||||||
|  | +        st = "S4U2PROXY_MISSING_EXTENDED_KDC_SIGN_IN_EVIDENCE_TKT_PAC";
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Get the PAC Logon Info. */
 | ||||||
|  | +    kerr = krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &linfo_blob);
 | ||||||
|  | +    if (kerr) {
 | ||||||
|  | +        st = "S4U2PROXY_NO_PAC_LOGON_INFO_IN_EVIDENCE_TKT";
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Parse the PAC Logon Info. */
 | ||||||
|  | +    tmpctx = talloc_new(NULL);
 | ||||||
|  | +    if (!tmpctx) {
 | ||||||
|  | +        st = "OUT_OF_MEMORY";
 | ||||||
|  | +        kerr = ENOMEM;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    linfo_data.length = linfo_blob.length;
 | ||||||
|  | +    linfo_data.data = (uint8_t *)linfo_blob.data;
 | ||||||
|  | +    ndr_err = ndr_pull_union_blob(&linfo_data, tmpctx, &linfo,
 | ||||||
|  | +                                  PAC_TYPE_LOGON_INFO,
 | ||||||
|  | +                                  (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
 | ||||||
|  | +    if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 | ||||||
|  | +        st = "S4U2PROXY_CANNOT_PARSE_ENVIDENCE_TKT_PAC_LOGON_INFO";
 | ||||||
|  | +        kerr = EINVAL;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Check that the extra SIDs array is not empty. */
 | ||||||
|  | +    if (linfo.info->info3.sidcount == 0) {
 | ||||||
|  | +        st = "S4U2PROXY_NO_EXTRA_SID";
 | ||||||
|  | +        kerr = ENOENT;
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* Search for the S-1-18-2 domain SID, which indicates the ticket was
 | ||||||
|  | +     * obtained using S4U2Self */
 | ||||||
|  | +    kerr = ipadb_string_to_sid("S-1-18-2", &asserted_identity_sid);
 | ||||||
|  | +    if (kerr) {
 | ||||||
|  | +        st = "S4U2PROXY_CANNOT_CREATE_ASSERTED_IDENTITY_SID";
 | ||||||
|  | +        goto end;
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    for (i = 0; i < linfo.info->info3.sidcount; i++) {
 | ||||||
|  | +        if (dom_sid_check(&asserted_identity_sid,
 | ||||||
|  | +                          linfo.info->info3.sids[0].sid, true)) {
 | ||||||
|  | +            evtkt_is_s4u2self = true;
 | ||||||
|  | +            break;
 | ||||||
|  | +        }
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    /* If the ticket was obtained using S4U2Self, the proxy principal entry must
 | ||||||
|  | +     * have the "ok_to_auth_as_delegate" attribute set to true. */
 | ||||||
|  | +    if (evtkt_is_s4u2self) {
 | ||||||
|  | +        kerr = ipadb_get_principal(context, evidence_tkt->server, 0,
 | ||||||
|  | +                                   &proxy_entry);
 | ||||||
|  | +        if (kerr) {
 | ||||||
|  | +            st = "S4U2PROXY_CANNOT_FIND_PROXY_PRINCIPAL";
 | ||||||
|  | +            goto end;
 | ||||||
|  | +        }
 | ||||||
|  | +
 | ||||||
|  | +        if (!(proxy_entry->attributes & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)) {
 | ||||||
|  | +            /* This evidence ticket cannot be forwardable given the privileges
 | ||||||
|  | +             * of the proxy principal.
 | ||||||
|  | +             * This is a Bronze Bit attack. */
 | ||||||
|  | +            if (detected)
 | ||||||
|  | +                *detected = true;
 | ||||||
|  | +            st = "S4U2PROXY_BRONZE_BIT_ATTACK_DETECTED";
 | ||||||
|  | +            kerr = EBADE;
 | ||||||
|  | +            goto end;
 | ||||||
|  | +        }
 | ||||||
|  | +    }
 | ||||||
|  | +
 | ||||||
|  | +    kerr = 0;
 | ||||||
|  | +
 | ||||||
|  | +end:
 | ||||||
|  | +    if (st && status)
 | ||||||
|  | +        *status = st;
 | ||||||
|  | +
 | ||||||
|  | +    krb5_free_authdata(context, ifrel);
 | ||||||
|  | +    krb5_pac_free(context, pac);
 | ||||||
|  | +    krb5_free_data_contents(context, &linfo_blob);
 | ||||||
|  | +    krb5_free_data_contents(context, &fullsign);
 | ||||||
|  | +    talloc_free(tmpctx);
 | ||||||
|  | +    ipadb_free_principal(context, proxy_entry);
 | ||||||
|  | +    return kerr;
 | ||||||
|  | +}
 | ||||||
|  | diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
 | ||||||
|  | index 4e4076410f1..bfbb83bcbfa 100644
 | ||||||
|  | --- a/ipaserver/install/server/install.py
 | ||||||
|  | +++ b/ipaserver/install/server/install.py
 | ||||||
|  | @@ -978,6 +978,14 @@ def install(installer):
 | ||||||
|  |      # Set the admin user kerberos password | ||||||
|  |      ds.change_admin_password(admin_password) | ||||||
|  |   | ||||||
|  | +    # Force KDC to refresh the cached value of ipaKrbAuthzData by restarting.
 | ||||||
|  | +    # ipaKrbAuthzData has to be set with "MS-PAC" to trigger PAC generation,
 | ||||||
|  | +    # which is required to handle S4U2Proxy with the Bronze-Bit fix.
 | ||||||
|  | +    # Not doing so would cause API malfunction for around a minute, which is
 | ||||||
|  | +    # long enough to cause the hereafter client installation to fail.
 | ||||||
|  | +    service.print_msg("Restarting the KDC")
 | ||||||
|  | +    krb.restart()
 | ||||||
|  | +
 | ||||||
|  |      # Call client install script | ||||||
|  |      service.print_msg("Configuring client side components") | ||||||
|  |      try: | ||||||
							
								
								
									
										356
									
								
								SOURCES/0017-Integration-tests-for-verifying-Referer.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								SOURCES/0017-Integration-tests-for-verifying-Referer.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,356 @@ | |||||||
|  | From 86b073a7f03ba0edf4dd91f85b96c89107e9e673 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Rob Crittenden <rcritten@redhat.com> | ||||||
|  | Date: Thu, 12 Oct 2023 20:34:01 +0000 | ||||||
|  | Subject: [PATCH] Integration tests for verifying Referer header in the UI | ||||||
|  | 
 | ||||||
|  | Validate that the change_password and login_password endpoints | ||||||
|  | verify the HTTP Referer header. There is some overlap in the | ||||||
|  | tests: belt and suspenders. | ||||||
|  | 
 | ||||||
|  | All endpoints except session/login_x509 are covered, sometimes | ||||||
|  | having to rely on expected bad results (see the i18n endpoint). | ||||||
|  | 
 | ||||||
|  | session/login_x509 is not tested yet as it requires significant | ||||||
|  | additional setup in order to associate a user certificate with | ||||||
|  | a user entry, etc. | ||||||
|  | 
 | ||||||
|  | This can be manually verified by modifying /etc/httpd/conf.d/ipa.conf | ||||||
|  | and adding: | ||||||
|  | 
 | ||||||
|  | Satisfy Any | ||||||
|  | Require all granted | ||||||
|  | 
 | ||||||
|  | Then comment out Auth and SSLVerify, etc. and restart httpd. | ||||||
|  | 
 | ||||||
|  | With a valid Referer will fail with a 401 and log that there is no | ||||||
|  | KRB5CCNAME. This comes after the referer check. | ||||||
|  | 
 | ||||||
|  | With an invalid Referer it will fail with a 400 Bad Request as | ||||||
|  | expected. | ||||||
|  | 
 | ||||||
|  | CVE-2023-5455 | ||||||
|  | 
 | ||||||
|  | Signed-off-by: Rob Crittenden <rcritten@redhat.com> | ||||||
|  | ---
 | ||||||
|  |  ipatests/test_ipaserver/httptest.py           |   7 +- | ||||||
|  |  ipatests/test_ipaserver/test_changepw.py      |  12 +- | ||||||
|  |  .../test_ipaserver/test_login_password.py     |  88 ++++++++++++ | ||||||
|  |  ipatests/test_ipaserver/test_referer.py       | 136 ++++++++++++++++++ | ||||||
|  |  ipatests/util.py                              |   4 +- | ||||||
|  |  5 files changed, 242 insertions(+), 5 deletions(-) | ||||||
|  |  create mode 100644 ipatests/test_ipaserver/test_login_password.py | ||||||
|  |  create mode 100644 ipatests/test_ipaserver/test_referer.py | ||||||
|  | 
 | ||||||
|  | diff --git a/ipatests/test_ipaserver/httptest.py b/ipatests/test_ipaserver/httptest.py
 | ||||||
|  | index 6cd034a7196..8924798fc93 100644
 | ||||||
|  | --- a/ipatests/test_ipaserver/httptest.py
 | ||||||
|  | +++ b/ipatests/test_ipaserver/httptest.py
 | ||||||
|  | @@ -36,7 +36,7 @@ class Unauthorized_HTTP_test:
 | ||||||
|  |      content_type = 'application/x-www-form-urlencoded' | ||||||
|  |      accept_language = 'en-us' | ||||||
|  |   | ||||||
|  | -    def send_request(self, method='POST', params=None):
 | ||||||
|  | +    def send_request(self, method='POST', params=None, host=None):
 | ||||||
|  |          """ | ||||||
|  |          Send a request to HTTP server | ||||||
|  |   | ||||||
|  | @@ -45,7 +45,10 @@ class Unauthorized_HTTP_test:
 | ||||||
|  |          if params is not None: | ||||||
|  |              if self.content_type == 'application/x-www-form-urlencoded': | ||||||
|  |                  params = urllib.parse.urlencode(params, True) | ||||||
|  | -        url = 'https://' + self.host + self.app_uri
 | ||||||
|  | +        if host:
 | ||||||
|  | +            url = 'https://' + host + self.app_uri
 | ||||||
|  | +        else:
 | ||||||
|  | +            url = 'https://' + self.host + self.app_uri
 | ||||||
|  |   | ||||||
|  |          headers = {'Content-Type': self.content_type, | ||||||
|  |                     'Accept-Language': self.accept_language, | ||||||
|  | diff --git a/ipatests/test_ipaserver/test_changepw.py b/ipatests/test_ipaserver/test_changepw.py
 | ||||||
|  | index c3a47ab265f..df38ddb3d9e 100644
 | ||||||
|  | --- a/ipatests/test_ipaserver/test_changepw.py
 | ||||||
|  | +++ b/ipatests/test_ipaserver/test_changepw.py
 | ||||||
|  | @@ -53,10 +53,11 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test):
 | ||||||
|  |   | ||||||
|  |          request.addfinalizer(fin) | ||||||
|  |   | ||||||
|  | -    def _changepw(self, user, old_password, new_password):
 | ||||||
|  | +    def _changepw(self, user, old_password, new_password, host=None):
 | ||||||
|  |          return self.send_request(params={'user': str(user), | ||||||
|  |                                    'old_password' : str(old_password), | ||||||
|  |                                    'new_password' : str(new_password)}, | ||||||
|  | +                                 host=host
 | ||||||
|  |                                   ) | ||||||
|  |   | ||||||
|  |      def _checkpw(self, user, password): | ||||||
|  | @@ -89,6 +90,15 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test):
 | ||||||
|  |          # make sure that password is NOT changed | ||||||
|  |          self._checkpw(testuser, old_password) | ||||||
|  |   | ||||||
|  | +    def test_invalid_referer(self):
 | ||||||
|  | +        response = self._changepw(testuser, old_password, new_password,
 | ||||||
|  | +                                  'attacker.test')
 | ||||||
|  | +
 | ||||||
|  | +        assert_equal(response.status, 400)
 | ||||||
|  | +
 | ||||||
|  | +        # make sure that password is NOT changed
 | ||||||
|  | +        self._checkpw(testuser, old_password)
 | ||||||
|  | +
 | ||||||
|  |      def test_pwpolicy_error(self): | ||||||
|  |          response = self._changepw(testuser, old_password, '1') | ||||||
|  |   | ||||||
|  | diff --git a/ipatests/test_ipaserver/test_login_password.py b/ipatests/test_ipaserver/test_login_password.py
 | ||||||
|  | new file mode 100644 | ||||||
|  | index 00000000000..9425cb7977f
 | ||||||
|  | --- /dev/null
 | ||||||
|  | +++ b/ipatests/test_ipaserver/test_login_password.py
 | ||||||
|  | @@ -0,0 +1,88 @@
 | ||||||
|  | +# Copyright (C) 2023  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 <http://www.gnu.org/licenses/>.
 | ||||||
|  | +
 | ||||||
|  | +import os
 | ||||||
|  | +import pytest
 | ||||||
|  | +import uuid
 | ||||||
|  | +
 | ||||||
|  | +from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
 | ||||||
|  | +from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
 | ||||||
|  | +from ipatests.util import assert_equal
 | ||||||
|  | +from ipalib import api, errors
 | ||||||
|  | +from ipapython.ipautil import run
 | ||||||
|  | +
 | ||||||
|  | +testuser = u'tuser'
 | ||||||
|  | +password = u'password'
 | ||||||
|  | +
 | ||||||
|  | +
 | ||||||
|  | +@pytest.mark.tier1
 | ||||||
|  | +class test_login_password(XMLRPC_test, Unauthorized_HTTP_test):
 | ||||||
|  | +    app_uri = '/ipa/session/login_password'
 | ||||||
|  | +
 | ||||||
|  | +    @pytest.fixture(autouse=True)
 | ||||||
|  | +    def login_setup(self, request):
 | ||||||
|  | +        ccache = os.path.join('/tmp', str(uuid.uuid4()))
 | ||||||
|  | +        try:
 | ||||||
|  | +            api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User')
 | ||||||
|  | +            api.Command['passwd'](testuser, password=password)
 | ||||||
|  | +            run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password),
 | ||||||
|  | +                env={"KRB5CCNAME": ccache})
 | ||||||
|  | +        except errors.ExecutionError as e:
 | ||||||
|  | +            pytest.skip(
 | ||||||
|  | +                'Cannot set up test user: %s' % e
 | ||||||
|  | +            )
 | ||||||
|  | +
 | ||||||
|  | +        def fin():
 | ||||||
|  | +            try:
 | ||||||
|  | +                api.Command['user_del']([testuser])
 | ||||||
|  | +            except errors.NotFound:
 | ||||||
|  | +                pass
 | ||||||
|  | +            os.unlink(ccache)
 | ||||||
|  | +
 | ||||||
|  | +        request.addfinalizer(fin)
 | ||||||
|  | +
 | ||||||
|  | +    def _login(self, user, password, host=None):
 | ||||||
|  | +        return self.send_request(params={'user': str(user),
 | ||||||
|  | +                                 'password' : str(password)},
 | ||||||
|  | +                                 host=host)
 | ||||||
|  | +
 | ||||||
|  | +    def test_bad_options(self):
 | ||||||
|  | +        for params in (
 | ||||||
|  | +            None,                             # no params
 | ||||||
|  | +            {"user": "foo"},                  # missing options
 | ||||||
|  | +            {"user": "foo", "password": ""},  # empty option
 | ||||||
|  | +        ):
 | ||||||
|  | +            response = self.send_request(params=params)
 | ||||||
|  | +            assert_equal(response.status, 400)
 | ||||||
|  | +            assert_equal(response.reason, 'Bad Request')
 | ||||||
|  | +
 | ||||||
|  | +    def test_invalid_auth(self):
 | ||||||
|  | +        response = self._login(testuser, 'wrongpassword')
 | ||||||
|  | +
 | ||||||
|  | +        assert_equal(response.status, 401)
 | ||||||
|  | +        assert_equal(response.getheader('X-IPA-Rejection-Reason'),
 | ||||||
|  | +                     'invalid-password')
 | ||||||
|  | +
 | ||||||
|  | +    def test_invalid_referer(self):
 | ||||||
|  | +        response = self._login(testuser, password, 'attacker.test')
 | ||||||
|  | +
 | ||||||
|  | +        assert_equal(response.status, 400)
 | ||||||
|  | +
 | ||||||
|  | +    def test_success(self):
 | ||||||
|  | +        response = self._login(testuser, password)
 | ||||||
|  | +
 | ||||||
|  | +        assert_equal(response.status, 200)
 | ||||||
|  | +        assert response.getheader('X-IPA-Rejection-Reason') is None
 | ||||||
|  | diff --git a/ipatests/test_ipaserver/test_referer.py b/ipatests/test_ipaserver/test_referer.py
 | ||||||
|  | new file mode 100644 | ||||||
|  | index 00000000000..4eade8bbaf3
 | ||||||
|  | --- /dev/null
 | ||||||
|  | +++ b/ipatests/test_ipaserver/test_referer.py
 | ||||||
|  | @@ -0,0 +1,136 @@
 | ||||||
|  | +# Copyright (C) 2023  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 <http://www.gnu.org/licenses/>.
 | ||||||
|  | +
 | ||||||
|  | +import os
 | ||||||
|  | +import pytest
 | ||||||
|  | +import uuid
 | ||||||
|  | +
 | ||||||
|  | +from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
 | ||||||
|  | +from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
 | ||||||
|  | +from ipatests.util import assert_equal
 | ||||||
|  | +from ipalib import api, errors
 | ||||||
|  | +from ipapython.ipautil import run
 | ||||||
|  | +
 | ||||||
|  | +testuser = u'tuser'
 | ||||||
|  | +password = u'password'
 | ||||||
|  | +
 | ||||||
|  | +
 | ||||||
|  | +@pytest.mark.tier1
 | ||||||
|  | +class test_referer(XMLRPC_test, Unauthorized_HTTP_test):
 | ||||||
|  | +
 | ||||||
|  | +    @pytest.fixture(autouse=True)
 | ||||||
|  | +    def login_setup(self, request):
 | ||||||
|  | +        ccache = os.path.join('/tmp', str(uuid.uuid4()))
 | ||||||
|  | +        tokenid = None
 | ||||||
|  | +        try:
 | ||||||
|  | +            api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User')
 | ||||||
|  | +            api.Command['passwd'](testuser, password=password)
 | ||||||
|  | +            run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password),
 | ||||||
|  | +                env={"KRB5CCNAME": ccache})
 | ||||||
|  | +            result = api.Command["otptoken_add"](
 | ||||||
|  | +                type='HOTP', description='testotp',
 | ||||||
|  | +                ipatokenotpalgorithm='sha512', ipatokenowner=testuser,
 | ||||||
|  | +                ipatokenotpdigits='6')
 | ||||||
|  | +            tokenid = result['result']['ipatokenuniqueid'][0]
 | ||||||
|  | +        except errors.ExecutionError as e:
 | ||||||
|  | +            pytest.skip(
 | ||||||
|  | +                'Cannot set up test user: %s' % e
 | ||||||
|  | +            )
 | ||||||
|  | +
 | ||||||
|  | +        def fin():
 | ||||||
|  | +            try:
 | ||||||
|  | +                api.Command['user_del']([testuser])
 | ||||||
|  | +                api.Command['otptoken_del']([tokenid])
 | ||||||
|  | +            except errors.NotFound:
 | ||||||
|  | +                pass
 | ||||||
|  | +            os.unlink(ccache)
 | ||||||
|  | +
 | ||||||
|  | +        request.addfinalizer(fin)
 | ||||||
|  | +
 | ||||||
|  | +    def _request(self, params={}, host=None):
 | ||||||
|  | +        # implicit is that self.app_uri is set to the appropriate value
 | ||||||
|  | +        return self.send_request(params=params, host=host)
 | ||||||
|  | +
 | ||||||
|  | +    def test_login_password_valid(self):
 | ||||||
|  | +        """Valid authentication of a user"""
 | ||||||
|  | +        self.app_uri = "/ipa/session/login_password"
 | ||||||
|  | +        response = self._request(
 | ||||||
|  | +            params={'user': 'tuser', 'password': password})
 | ||||||
|  | +        assert_equal(response.status, 200, self.app_uri)
 | ||||||
|  | +
 | ||||||
|  | +    def test_change_password_valid(self):
 | ||||||
|  | +        """This actually changes the user password"""
 | ||||||
|  | +        self.app_uri = "/ipa/session/change_password"
 | ||||||
|  | +        response = self._request(
 | ||||||
|  | +            params={'user': 'tuser',
 | ||||||
|  | +                    'old_password': password,
 | ||||||
|  | +                    'new_password': 'new_password'}
 | ||||||
|  | +        )
 | ||||||
|  | +        assert_equal(response.status, 200, self.app_uri)
 | ||||||
|  | +
 | ||||||
|  | +    def test_sync_token_valid(self):
 | ||||||
|  | +        """We aren't testing that sync works, just that we can get there"""
 | ||||||
|  | +        self.app_uri = "/ipa/session/sync_token"
 | ||||||
|  | +        response = self._request(
 | ||||||
|  | +            params={'user': 'tuser',
 | ||||||
|  | +                    'first_code': '1234',
 | ||||||
|  | +                    'second_code': '5678',
 | ||||||
|  | +                    'password': 'password'})
 | ||||||
|  | +        assert_equal(response.status, 200, self.app_uri)
 | ||||||
|  | +
 | ||||||
|  | +    def test_i18n_messages_valid(self):
 | ||||||
|  | +        # i18n_messages requires a valid JSON request and we send
 | ||||||
|  | +        # nothing. If we get a 500 error then it got past the
 | ||||||
|  | +        # referer check.
 | ||||||
|  | +        self.app_uri = "/ipa/i18n_messages"
 | ||||||
|  | +        response = self._request()
 | ||||||
|  | +        assert_equal(response.status, 500, self.app_uri)
 | ||||||
|  | +
 | ||||||
|  | +    # /ipa/session/login_x509 is not tested yet as it requires
 | ||||||
|  | +    # significant additional setup.
 | ||||||
|  | +    # This can be manually verified by adding
 | ||||||
|  | +    # Satisfy Any and Require all granted to the configuration
 | ||||||
|  | +    # section and comment out all Auth directives. The request
 | ||||||
|  | +    # will fail and log that there is no KRB5CCNAME which comes
 | ||||||
|  | +    # after the referer check.
 | ||||||
|  | +
 | ||||||
|  | +    def test_endpoints_auth_required(self):
 | ||||||
|  | +        """Test endpoints that require pre-authorization which will
 | ||||||
|  | +           fail before we even get to the Referer check
 | ||||||
|  | +        """
 | ||||||
|  | +        self.endpoints = {
 | ||||||
|  | +            "/ipa/xml",
 | ||||||
|  | +            "/ipa/session/login_kerberos",
 | ||||||
|  | +            "/ipa/session/json",
 | ||||||
|  | +            "/ipa/session/xml"
 | ||||||
|  | +        }
 | ||||||
|  | +        for self.app_uri in self.endpoints:
 | ||||||
|  | +            response = self._request(host="attacker.test")
 | ||||||
|  | +
 | ||||||
|  | +            # referer is checked after auth
 | ||||||
|  | +            assert_equal(response.status, 401, self.app_uri)
 | ||||||
|  | +
 | ||||||
|  | +    def notest_endpoints_invalid(self):
 | ||||||
|  | +        """Pass in a bad Referer, expect a 400 Bad Request"""
 | ||||||
|  | +        self.endpoints = {
 | ||||||
|  | +            "/ipa/session/login_password",
 | ||||||
|  | +            "/ipa/session/change_password",
 | ||||||
|  | +            "/ipa/session/sync_token",
 | ||||||
|  | +        }
 | ||||||
|  | +        for self.app_uri in self.endpoints:
 | ||||||
|  | +            response = self._request(host="attacker.test")
 | ||||||
|  | +
 | ||||||
|  | +            assert_equal(response.status, 400, self.app_uri)
 | ||||||
|  | diff --git a/ipatests/util.py b/ipatests/util.py
 | ||||||
|  | index 929c3e899c3..61af0c40d07 100644
 | ||||||
|  | --- a/ipatests/util.py
 | ||||||
|  | +++ b/ipatests/util.py
 | ||||||
|  | @@ -163,12 +163,12 @@ class ExceptionNotRaised(Exception):
 | ||||||
|  |          return self.msg % self.expected.__name__ | ||||||
|  |   | ||||||
|  |   | ||||||
|  | -def assert_equal(val1, val2):
 | ||||||
|  | +def assert_equal(val1, val2, msg=''):
 | ||||||
|  |      """ | ||||||
|  |      Assert ``val1`` and ``val2`` are the same type and of equal value. | ||||||
|  |      """ | ||||||
|  |      assert type(val1) is type(val2), '%r != %r' % (val1, val2) | ||||||
|  | -    assert val1 == val2, '%r != %r' % (val1, val2)
 | ||||||
|  | +    assert val1 == val2, '%r != %r %r' % (val1, val2, msg)
 | ||||||
|  |   | ||||||
|  |   | ||||||
|  |  def assert_not_equal(val1, val2): | ||||||
| @ -189,7 +189,7 @@ | |||||||
| 
 | 
 | ||||||
| Name:           %{package_name} | Name:           %{package_name} | ||||||
| Version:        %{IPA_VERSION} | Version:        %{IPA_VERSION} | ||||||
| Release:        9%{?rc_version:.%rc_version}%{?dist}.alma.1 | Release:        11%{?rc_version:.%rc_version}%{?dist}.alma.1 | ||||||
| Summary:        The Identity, Policy and Audit system | Summary:        The Identity, Policy and Audit system | ||||||
| 
 | 
 | ||||||
| License:        GPLv3+ | License:        GPLv3+ | ||||||
| @ -226,6 +226,12 @@ Patch0013:      0013-Installer-activate-nss-and-pam-services-in-sssd.conf_rhbz#2 | |||||||
| # Patches were taken from: | # Patches were taken from: | ||||||
| # https://gitlab.com/redhat/centos-stream/rpms/ipa/-/commit/5d0ca0e625aea2553a39ae3e56174285cb123f13 | # https://gitlab.com/redhat/centos-stream/rpms/ipa/-/commit/5d0ca0e625aea2553a39ae3e56174285cb123f13 | ||||||
| Patch0014:      0014-ipa-kdb-Make-AD-SIGNEDPATH-optional-with-krb5-DAL-8.patch | Patch0014:      0014-ipa-kdb-Make-AD-SIGNEDPATH-optional-with-krb5-DAL-8.patch | ||||||
|  | # https://github.com/freeipa/freeipa/commit/13778d88ca2ac73b729821bdea844172a18c0cb9 | ||||||
|  | Patch0015:      0015-CVE-2023-5455.patch | ||||||
|  | # https://github.com/freeipa/freeipa/commit/a847e2483b4c4832ee5129901da169f4eb0d1392 | ||||||
|  | Patch0016:      0016-ipa-kdb-Detect-and-block-Bronze-Bit-attacks.patch | ||||||
|  | # https://github.com/freeipa/freeipa/commit/86b073a7f03ba0edf4dd91f85b96c89107e9e673 | ||||||
|  | Patch0017:      0017-Integration-tests-for-verifying-Referer.patch | ||||||
| 
 | 
 | ||||||
| Patch1001:      1001-Change-branding-to-IPA-and-Identity-Management.patch | Patch1001:      1001-Change-branding-to-IPA-and-Identity-Management.patch | ||||||
| Patch1002:      1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch | Patch1002:      1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch | ||||||
| @ -1741,6 +1747,11 @@ fi | |||||||
| %endif | %endif | ||||||
| 
 | 
 | ||||||
| %changelog | %changelog | ||||||
|  | * Mon Jan 15 2024 Eduard Abdullin <eabdullin@almalinux.org> - 4.9.12-11.alma.1 | ||||||
|  | - CVE-2023-5455 | ||||||
|  | - ipa-kdb: Detect and block Bronze-Bit attacks | ||||||
|  | - Integration tests for verifying Referer header in the UI | ||||||
|  | 
 | ||||||
| * Wed Nov 14 2023 Eduard Abdullin <eabdullin@almalinux.org> - 4.9.12-9.alma.1 | * Wed Nov 14 2023 Eduard Abdullin <eabdullin@almalinux.org> - 4.9.12-9.alma.1 | ||||||
| - ipa-kdb: Make AD-SIGNEDPATH optional with krb5 DAL 8 and older | - ipa-kdb: Make AD-SIGNEDPATH optional with krb5 DAL 8 and older | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user