Your message dated Sat, 07 Oct 2023 12:41:28 +0100
with message-id 
<84bb5ff8312f749ebe536897993782bf35aa1977.ca...@adam-barratt.org.uk>
and subject line Closing opu requests for updates included in 11.8
has caused the Debian Bug report #1053220,
regarding bullseye-pu: package lemonldap-ng/2.0.11+ds-4+deb11u5
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
1053220: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1053220
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian....@packages.debian.org
Usertags: pu
X-Debbugs-Cc: lemonldap...@packages.debian.org, y...@debian.org
Control: affects -1 + src:lemonldap-ng

[ Reason ]
Two new vulnerabilities have been dicovered and fixed in lemonldap-ng:
 - an open redirection due to incorrect escape handling
 - an open redirection only when configuration is edited by hand and
   doesn't follow OIDC specifications
 - a server-side-request-forgery (CVE-2023-44469) in OIDC protocol:
   A little-know feature of OIDC allows the OpenID Provider to fetch the
   Authorization request parameters itself by indicating a request_uri
   parameter. This feature is now restricted to a white list using this
   patch

[ Impact ]
Two low and one medium security issue.

[ Tests ]
Patches includes test updates

[ Risks ]
Outside of test changes, patches are not so big and the test coverage
provided by upstream is good, so risk is moderate.

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
- open redirection patch: use `URI->new($url)->as_string` in each
  redirections
- OIDC open redirection patch: just rejects requests with `redirect_uri` if
  relying party configuration has no declared redirect URIs.
- SSRF patch:
  * add new configuration parameter to list authorized "request_uris"
  * change the algorithm that manage request_uri parameter

Cheers,
Yadd
diff --git a/debian/NEWS b/debian/NEWS
index c4d7ee951..ba4a14a12 100644
--- a/debian/NEWS
+++ b/debian/NEWS
@@ -1,3 +1,13 @@
+lemonldap-ng (2.0.11+ds-4+deb11u5) bullseye; urgency=medium
+
+  A little-know feature of OIDC allows the OpenID Provider to fetch the
+  Authorization request parameters itself by indicating a request_uri
+  parameter.
+  By default, this feature is now restricted to a white list. See
+  Relying-Party security option to fill this field.
+
+ -- Yadd <y...@debian.org>  Fri, 29 Sep 2023 17:38:51 +0400
+
 lemonldap-ng (2.0.11+ds-4+deb11u4) bullseye; urgency=medium
 
   AuthBasic now enforces 2FA activation (CVE-2023-28862):
diff --git a/debian/changelog b/debian/changelog
index 5d2c62ac0..35d5599a4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+lemonldap-ng (2.0.11+ds-4+deb11u5) bullseye; urgency=medium
+
+  * Fix open redirection when OIDC RP has no redirect uris
+  * Fix open redirection due to incorrect escape handling
+  * Fix Server-Side-Request-Forgery issue in OIDC (CVE-2023-44469)
+
+ -- Yadd <y...@debian.org>  Fri, 29 Sep 2023 16:35:14 +0400
+
 lemonldap-ng (2.0.11+ds-4+deb11u4) bullseye; urgency=medium
 
   * Fix 2FA issue when using AuthBasic handler (CVE-2023-28862)
@@ -19,7 +27,7 @@ lemonldap-ng (2.0.11+ds-4+deb11u2) bullseye; urgency=medium
 
 lemonldap-ng (2.0.11+ds-4+deb11u1) bullseye; urgency=medium
 
-  * Fix auth process in password-testing plugins (Closes: CVE-2021-20874)
+  * Fix auth process in password-testing plugins (Closes: #1005302, 
CVE-2021-40874)
 
  -- Yadd <y...@debian.org>  Thu, 24 Feb 2022 15:16:09 +0100
 
diff --git a/debian/clean b/debian/clean
index 73f167814..cdb4a5ae4 100644
--- a/debian/clean
+++ b/debian/clean
@@ -1,3 +1,4 @@
+doc/pages/documentation/current/.buildinfo
 lemonldap-ng-manager/site/htdocs/static/js/conftree.js
 lemonldap-ng-manager/site/htdocs/static/struct.json
 lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Attributes.pm
diff --git a/debian/patches/SSRF-issue.patch b/debian/patches/SSRF-issue.patch
new file mode 100644
index 000000000..dce756430
--- /dev/null
+++ b/debian/patches/SSRF-issue.patch
@@ -0,0 +1,627 @@
+Description: fix SSRF vulnerability
+ Issue described here: 
https://security.lauritz-holtmann.de/post/sso-security-ssrf/
+Author: Maxime Besson <maxime.bes...@worteks.com>
+Origin: upstream, 
https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/383/diffs
+Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2998
+Forwarded: not-needed
+Applied-Upstream: 2.17.1, 
https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/383/diffs
+Reviewed-By: Yadd <y...@debian.org>
+Last-Update: 2023-09-23
+
+--- a/doc/sources/admin/idpopenidconnect.rst
++++ b/doc/sources/admin/idpopenidconnect.rst
+@@ -278,6 +278,11 @@
+       the Session Browser.
+    - **Allow OAuth2.0 Password Grant** (since version ``2.0.8``): Allow the 
use of the :ref:`Resource Owner Password Credentials Grant 
<resource-owner-password-grant>` by this client. This feature only works if you 
have configured a form-based authentication module.
+    - **Allow OAuth2.0 Client Credentials Grant** (since version ``2.0.11``): 
Allow the use of the :ref:`Resource Owner Password Credentials Grant 
<client-credentials-grant>` by this client.
++   - **Allowed URLs for fetching Request Object**: (since version ``2.17.1``):
++     which URLs may be called by the portal to fetch the request object (see
++     `request_uri
++     
<https://openid.net/specs/openid-connect-core-1_0.html#RequestUriParameter>`__
++     in OIDC specifications). These URLs may use wildcards 
(``https://app.example.com/*``).
+    - **Authentication Level**: required authentication level to access this 
application
+    - **Access Rule**: lets you specify a :doc:`Perl rule<rules_examples>` to 
restrict access to this client
+ 
+--- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm
++++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm
+@@ -4202,6 +4202,7 @@
+         oidcRPMetaDataOptionsAuthorizationCodeExpiration => { type => 'int' },
+         oidcRPMetaDataOptionsOfflineSessionExpiration    => { type => 'int' },
+         oidcRPMetaDataOptionsRedirectUris                => { type => 'text', 
},
++        oidcRPMetaDataOptionsRequestUris                 => { type => 'text', 
},
+         oidcRPMetaDataOptionsExtraClaims                 => {
+             type    => 'keyTextContainer',
+             keyTest => qr/^[\x21\x23-\x5B\x5D-\x7E]+$/,
+--- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/CTrees.pm
++++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/CTrees.pm
+@@ -225,6 +225,7 @@
+                             'oidcRPMetaDataOptionsAllowOffline',
+                             'oidcRPMetaDataOptionsAllowPasswordGrant',
+                             
'oidcRPMetaDataOptionsAllowClientCredentialsGrant',
++                            'oidcRPMetaDataOptionsRequestUris',
+                             'oidcRPMetaDataOptionsAuthnLevel',
+                             'oidcRPMetaDataOptionsRule',
+                         ]
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/ar.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/ar.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"يو آر إل",
+ "oidcOPMetaDataOptionsProtocol":"بروتوكول",
+ "oidcRPMetaDataOptionsPublic":"Public client",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"مستوى إثبات الهوية",
+ "oidcRPMetaDataOptionsRule":"قاعدة الدخول",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
+ "samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
+ "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/de.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/de.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocol",
+ "oidcRPMetaDataOptionsPublic":"Public client",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Authentication level",
+ "oidcRPMetaDataOptionsRule":"Access rule",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"RelayState session timeout",
+ "samlUseQueryStringSpecific":"Use specific query_string method",
+ "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/en.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/en.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocol",
+ "oidcRPMetaDataOptionsPublic":"Public client",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Authentication level",
+ "oidcRPMetaDataOptionsRule":"Access rule",
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/es.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/es.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocolo",
+ "oidcRPMetaDataOptionsPublic":"Cliente público",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Se requiere PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Authentication level",
+ "oidcRPMetaDataOptionsRule":"Regla de acceso",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"RelayState session timeout",
+ "samlUseQueryStringSpecific":"Use specific query_string method",
+ "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/fr.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/fr.json
+@@ -627,6 +627,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocole",
+ "oidcRPMetaDataOptionsPublic":"Client public",
++"oidcRPMetaDataOptionsRequestUris":"URLs autorisées pour récupérer les 
paramètres de la requête",
+ "oidcRPMetaDataOptionsRequirePKCE":"PKCE requis",
+ "oidcRPMetaDataOptionsAuthnLevel":"Niveau d'authentification",
+ "oidcRPMetaDataOptionsRule":"Règle d'accès",
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/it.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/it.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocollo",
+ "oidcRPMetaDataOptionsPublic":"Cliente pubblico",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Richiedi PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Livello di autenticazione",
+ "oidcRPMetaDataOptionsRule":"Regola di accesso",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"Timeout di sessione di RelayState",
+ "samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
+ "samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/pl.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/pl.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protokół",
+ "oidcRPMetaDataOptionsPublic":"Klient publiczny",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Wymagaj PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Poziom uwierzytelnienia",
+ "oidcRPMetaDataOptionsRule":"Reguła dostępu",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"Limit czasu sesji RelayState",
+ "samlUseQueryStringSpecific":"Użyj określonej metody query_string",
+ "samlOverrideIDPEntityID":"Zastąp identyfikator jednostki podczas działania 
jako IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/tr.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/tr.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protokol",
+ "oidcRPMetaDataOptionsPublic":"Açık istemci",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"PKCE gerektir",
+ "oidcRPMetaDataOptionsAuthnLevel":"Doğrulama seviyesi",
+ "oidcRPMetaDataOptionsRule":"Erişim kuralı",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"RelayState oturum zaman aşımı",
+ "samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
+ "samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/vi.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/vi.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Giao thức",
+ "oidcRPMetaDataOptionsPublic":"Public client",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"Mức xác thực",
+ "oidcRPMetaDataOptionsRule":"Quy tắc truy cập",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
+ "samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
+ "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/zh.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/zh.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"Protocol",
+ "oidcRPMetaDataOptionsPublic":"Public client",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"认证级别",
+ "oidcRPMetaDataOptionsRule":"Access rule",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"RelayState session timeout",
+ "samlUseQueryStringSpecific":"Use specific query_string method",
+ "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-manager/site/htdocs/static/languages/zh_TW.json
++++ b/lemonldap-ng-manager/site/htdocs/static/languages/zh_TW.json
+@@ -626,6 +626,7 @@
+ "oidcRPMetaDataOptionsLogoutUrl":"URL",
+ "oidcOPMetaDataOptionsProtocol":"協定",
+ "oidcRPMetaDataOptionsPublic":"公開客戶端",
++"oidcRPMetaDataOptionsRequestUris":"Allowed URLs for fetching Request Object",
+ "oidcRPMetaDataOptionsRequirePKCE":"需要 PKCE",
+ "oidcRPMetaDataOptionsAuthnLevel":"驗證等級",
+ "oidcRPMetaDataOptionsRule":"存取規則",
+@@ -1194,4 +1195,4 @@
+ "samlRelayStateTimeout":"RelayState 工作階段逾時",
+ "samlUseQueryStringSpecific":"使用特定的 query_string 方法",
+ "samlOverrideIDPEntityID":"充當 IDP 覆寫實體 ID"
+-}
+\ No newline at end of file
++}
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm
+@@ -195,32 +195,97 @@
+             $self->logger->debug(
+                 "OIDC $flow flow requested (response type: $response_type)");
+ 
+-            # Extract request_uri/request parameter
+-            if ( $oidc_request->{'request_uri'} ) {
+-                my $request =
+-                  $self->getRequestJWT( $oidc_request->{'request_uri'} );
++            # Client ID must be provided and cannot come from
++            # request or request_uri
++            unless ( $oidc_request->{'client_id'} ) {
++                $self->logger->error("Client ID is required");
++                return PE_ERROR;
++            }
++
++            # Check client_id
++            my $client_id = $oidc_request->{'client_id'};
++            $self->logger->debug("Request from client id $client_id");
++
++            # Verify that client_id is registered in configuration
++            my $rp = $self->getRP($client_id);
++
++            unless ($rp) {
++                $self->logger->error(
++                        "No registered Relying Party found with"
++                      . " client_id $client_id" );
++                return PE_UNAUTHORIZEDPARTNER;
++            }
++            else {
++                $self->logger->debug("Client id $client_id matches RP $rp");
++            }
+ 
+-                if ($request) {
+-                    $oidc_request->{'request'} = $request;
++            # Scope must be provided and cannot come from request or 
request_uri
++            unless ( $oidc_request->{'scope'} ) {
++                $self->logger->error("Scope is required");
++                return PE_ERROR;
++            }
++
++            # Extract request_uri/request parameter
++            if ( my $request_uri = $oidc_request->{'request_uri'} ) {
++                if (
++                    $self->isUriAllowedForRP(
++                        $request_uri,                       $rp,
++                        "oidcRPMetaDataOptionsRequestUris", 1
++                    )
++                  )
++                {
++                    my $request = $self->getRequestJWT($request_uri);
++                    if ($request) {
++                        $oidc_request->{'request'} = $request;
++                    }
++                    else {
++                        $self->logger->error(
++                            "Error with Request URI resolution");
++                        return PE_ERROR;
++                    }
+                 }
+                 else {
+-                    $self->logger->error("Error with Request URI resolution");
++                    $self->logger->error(
++                        "Request URI $request_uri is not allowed for $rp");
+                     return PE_ERROR;
+                 }
+             }
+ 
+             if ( $oidc_request->{'request'} ) {
+-                my $request =
+-                  $self->getJWTJSONData( $oidc_request->{'request'} );
++                if (
++                    $self->verifyJWTSignature(
++                        $oidc_request->{'request'},
++                        undef, $rp
++                    )
++                  )
++                {
++                    $self->logger->debug("JWT signature request verified");
++                    my $request = getJWTPayload( $oidc_request->{'request'} );
+ 
+-                # Override OIDC parameters by request content
+-                foreach ( keys %$request ) {
+-                    $self->logger->debug(
+-"Override $_ OIDC param by value present in request parameter"
+-                    );
+-                    $oidc_request->{$_} = $request->{$_};
+-                    $self->p->setHiddenFormValue( $req, $_, $request->{$_}, 
'',
+-                        0 );
++                    # Override OIDC parameters by request content
++                    foreach ( keys %$request ) {
++                        $self->logger->debug( "Override $_ OIDC param"
++                              . " by value present in request parameter" );
++
++                        if ( $_ eq "client_id" or $_ eq "response_type" ) {
++                            if ( $oidc_request->{$_} ne $request->{$_} ) {
++                                $self->logger->error( "$_ from request JWT ("
++                                      . $oidc_request->{$_}
++                                      . ") does not match $_ from request URI 
("
++                                      . $request->{$_}
++                                      . ")" );
++                                return PE_ERROR;
++                            }
++                        }
++                        $oidc_request->{$_} = $request->{$_};
++                        $self->p->setHiddenFormValue( $req, $_, 
$request->{$_},
++                            '', 0 );
++                    }
++                }
++                else {
++                    $self->logger->error(
++                        "JWT signature request can not be verified");
++                    return PE_ERROR;
+                 }
+             }
+ 
+@@ -229,37 +294,12 @@
+                 $self->logger->error("Redirect URI is required");
+                 return PE_ERROR;
+             }
+-            unless ( $oidc_request->{'scope'} ) {
+-                $self->logger->error("Scope is required");
+-                return PE_ERROR;
+-            }
+-            unless ( $oidc_request->{'client_id'} ) {
+-                $self->logger->error("Client ID is required");
+-                return PE_ERROR;
+-            }
+             if ( $flow eq "implicit" and not defined $oidc_request->{'nonce'} 
)
+             {
+                 $self->logger->error("Nonce is required for implicit flow");
+                 return PE_ERROR;
+             }
+ 
+-            # Check client_id
+-            my $client_id = $oidc_request->{'client_id'};
+-            $self->logger->debug("Request from client id $client_id");
+-
+-            # Verify that client_id is registered in configuration
+-            my $rp = $self->getRP($client_id);
+-
+-            unless ($rp) {
+-                $self->logger->error(
+-"No registered Relying Party found with client_id $client_id"
+-                );
+-                return PE_UNAUTHORIZEDPARTNER;
+-            }
+-            else {
+-                $self->logger->debug("Client id $client_id matches RP $rp");
+-            }
+-
+             # Check if this RP is authorized
+             if ( my $rule = $self->spRules->{$rp} ) {
+                 unless ( $rule->( $req, $req->sessionInfo ) ) {
+@@ -276,24 +316,14 @@
+ 
+             # Check redirect_uri
+             my $redirect_uri  = $oidc_request->{'redirect_uri'};
+-            my $redirect_uris = $self->conf->{oidcRPMetaDataOptions}->{$rp}
+-              ->{oidcRPMetaDataOptionsRedirectUris};
+-
+-            if ($redirect_uris) {
+-                my $redirect_uri_allowed = 0;
+-                foreach ( split( /\s+/, $redirect_uris ) ) {
+-                    $redirect_uri_allowed = 1 if $redirect_uri eq $_;
+-                }
+-                unless ($redirect_uri_allowed) {
+-                    $self->userLogger->error(
+-                        "Redirect URI $redirect_uri not allowed");
+-                    return PE_BADURL;
+-                }
+-            }
+-            elsif ($redirect_uri) {
+-                $self->logger->error(
+-"RP $rp has no RedirectUris, unable to handle accept 
redirect_uri=$redirect_uri"
+-                );
++            if (
++                !$self->isUriAllowedForRP(
++                    $redirect_uri, $rp, 'oidcRPMetaDataOptionsRedirectUris'
++                )
++              )
++            {
++                $self->userLogger->error(
++                    "Redirect URI $redirect_uri not allowed");
+                 return 37; # PE_BADURL value
+             }
+ 
+@@ -397,24 +427,6 @@
+                 return PE_OK;
+             }
+ 
+-            # Check Request JWT signature
+-            if ( $oidc_request->{'request'} ) {
+-                unless (
+-                    $self->verifyJWTSignature(
+-                        $oidc_request->{'request'},
+-                        undef, $rp
+-                    )
+-                  )
+-                {
+-                    $self->logger->error(
+-                        "JWT signature request can not be verified");
+-                    return PE_ERROR;
+-                }
+-                else {
+-                    $self->logger->debug("JWT signature request verified");
+-                }
+-            }
+-
+             # Check id_token_hint
+             my $id_token_hint = $oidc_request->{'id_token_hint'};
+             if ($id_token_hint) {
+@@ -957,27 +969,13 @@
+ 
+                 if ($post_logout_redirect_uri) {
+ 
+-                    # Check redirect URI is allowed
+-                    my $redirect_uri_allowed = 0;
+-                    foreach ( keys %{ $self->conf->{oidcRPMetaDataOptions} } 
) {
+-                        my $logout_rp = $_;
+-                        if ( my $redirect_uris =
+-                            $self->conf->{oidcRPMetaDataOptions}->{$logout_rp}
+-                            ->{oidcRPMetaDataOptionsPostLogoutRedirectUris} )
+-                        {
+-
+-                            foreach ( split( /\s+/, $redirect_uris ) ) {
+-                                if ( $post_logout_redirect_uri eq $_ ) {
+-                                    $self->logger->debug(
+-"$post_logout_redirect_uri is an allowed logout redirect URI for RP 
$logout_rp"
+-                                    );
+-                                    $redirect_uri_allowed = 1;
+-                                }
+-                            }
+-                        }
+-                    }
+-
+-                    unless ($redirect_uri_allowed) {
++                    unless (
++                        $self->findRPFromUri(
++                            $post_logout_redirect_uri,
++                            'oidcRPMetaDataOptionsPostLogoutRedirectUris'
++                        )
++                      )
++                    {
+                         $self->logger->error(
+                             "$post_logout_redirect_uri is not allowed");
+                         return PE_BADURL;
+@@ -1009,6 +1007,43 @@
+     return PE_ERROR;
+ }
+ 
++sub findRPFromUri {
++    my ( $self, $uri, $option ) = @_;
++
++    my $found_rp;
++    foreach my $rp ( keys %{ $self->conf->{oidcRPMetaDataOptions} } ) {
++        $found_rp = $rp if $self->isUriAllowedForRP( $uri, $rp, $option );
++    }
++    return $found_rp;
++}
++
++sub isUriAllowedForRP {
++    my ( $self, $uri, $rp, $option, $wildcard_allowed ) = @_;
++    my $allowed_uris = $self->conf->{oidcRPMetaDataOptions}->{$rp}->{$option} 
// "";
++
++    my $is_uri_allowed;
++    if ($wildcard_allowed) {
++        $is_uri_allowed =
++          grep { _wildcard_match( $_, $uri ) } split( /\s+/, $allowed_uris );
++    }
++    else {
++        $is_uri_allowed = grep { $_ eq $uri } split( /\s+/, $allowed_uris );
++    }
++    return $is_uri_allowed;
++}
++
++sub _wildcard_match {
++    my ( $config_url, $candidate ) = @_;
++
++    # Quote everything
++    my $config_re = $config_url =~ s/(.)/\Q$1/gr;
++
++    # Replace \* by .*
++    $config_re =~ s/\\\*/.*/g;
++
++    return ( $candidate =~ qr/^$config_re$/ ? 1 : 0 );
++}
++
+ # Handle token endpoint
+ sub token {
+     my ( $self, $req ) = @_;
+@@ -1917,6 +1952,7 @@
+     my $userinfo_signed_response_alg =
+       $client_metadata->{userinfo_signed_response_alg};
+     my $redirect_uris = $client_metadata->{redirect_uris};
++    my $request_uris  = $client_metadata->{request_uris};
+ 
+     # Register RP in global configuration
+     my $conf = $self->confAcc->getConf( { raw => 1, noCache => 1 } );
+@@ -1938,6 +1974,9 @@
+       = $id_token_signed_response_alg;
+     $conf->{oidcRPMetaDataOptions}->{$rp}->{oidcRPMetaDataOptionsRedirectUris}
+       = join( ' ', @$redirect_uris );
++    $conf->{oidcRPMetaDataOptions}->{$rp}->{oidcRPMetaDataOptionsRequestUris} 
=
++      join( ' ', @$request_uris )
++      if $request_uris and @$request_uris;
+     $conf->{oidcRPMetaDataOptions}->{$rp}
+       ->{oidcRPMetaDataOptionsUserInfoSignAlg} = $userinfo_signed_response_alg
+       if defined $userinfo_signed_response_alg;
+@@ -1975,6 +2014,8 @@
+         $registration_response->{'id_token_signed_response_alg'} =
+           $id_token_signed_response_alg;
+         $registration_response->{'redirect_uris'} = $redirect_uris;
++        $registration_response->{'request_uris'}  = $request_uris
++          if $request_uris and @$request_uris;
+         $registration_response->{'userinfo_signed_response_alg'} =
+           $userinfo_signed_response_alg
+           if defined $userinfo_signed_response_alg;
+@@ -2001,25 +2042,13 @@
+ 
+     if ($post_logout_redirect_uri) {
+ 
+-        # Check redirect URI is allowed
+-        my $redirect_uri_allowed = 0;
+-        foreach ( keys %{ $self->conf->{oidcRPMetaDataOptions} } ) {
+-            my $logout_rp = $_;
+-            my $redirect_uris =
+-              $self->conf->{oidcRPMetaDataOptions}->{$logout_rp}
+-              ->{oidcRPMetaDataOptionsPostLogoutRedirectUris};
+-
+-            foreach ( split( /\s+/, $redirect_uris ) ) {
+-                if ( $post_logout_redirect_uri eq $_ ) {
+-                    $self->logger->debug(
+-"$post_logout_redirect_uri is an allowed logout redirect URI for RP 
$logout_rp"
+-                    );
+-                    $redirect_uri_allowed = 1;
+-                }
+-            }
+-        }
+-
+-        unless ($redirect_uri_allowed) {
++        unless (
++            $self->findRPFromUri(
++                $post_logout_redirect_uri,
++                'oidcRPMetaDataOptionsPostLogoutRedirectUris'
++            )
++          )
++        {
+             $self->logger->error("$post_logout_redirect_uri is not allowed");
+             return $self->p->login($req);
+         }
+@@ -2202,7 +2231,7 @@
+             claims_supported                 => [qw/sub iss auth_time acr/],
+             request_parameter_supported      => JSON::true,
+             request_uri_parameter_supported  => JSON::true,
+-            require_request_uri_registration => JSON::false,
++            require_request_uri_registration => JSON::true,
+ 
+             # Algorithms
+             id_token_signing_alg_values_supported =>
+@@ -2254,19 +2283,7 @@
+         }
+     }
+ 
+-    # Extract request_uri/request parameter
+-    my $request = $req->param('request');
+-    if ( $req->param('request_uri') ) {
+-        $request = $self->getRequestJWT( $req->param('request_uri') );
+-    }
+-
+-    if ($request) {
+-        my $request_data = $self->getJWTJSONData($request);
+-        foreach ( keys %$request_data ) {
+-            $req->env->{ "llng_oidc_" . $_ } = $request_data->{$_};
+-        }
+-    }
+-
++    my $rp;
+     if ( $req->param('client_id') ) {
+         my $rp = $self->getRP( $req->param('client_id') );
+         $req->env->{"llng_oidc_rp"} = $rp if $rp;
+@@ -2278,6 +2295,27 @@
+           if $targetAuthnLevel;
+     }
+ 
++    # Extract request_uri/request parameter
++    my $request = $req->param('request');
++    if ( my $request_uri = $req->param('request_uri') ) {
++        if (
++            $rp
++            and $self->isUriAllowedForRP(
++                $request_uri, $rp, 'oidcRPMetaDataOptionsRequestUris', 1
++            )
++          )
++        {
++            $request = $self->getRequestJWT($request_uri);
++        }
++    }
++
++    if ($request) {
++        my $request_data = getJWTPayload($request);
++        foreach ( keys %$request_data ) {
++            $req->env->{ "llng_oidc_" . $_ } = $request_data->{$_};
++        }
++    }
++
+     return PE_OK;
+ }
+ 
diff --git 
a/debian/patches/fix-open-redirection-without-OIDC-redirect-uris.patch 
b/debian/patches/fix-open-redirection-without-OIDC-redirect-uris.patch
new file mode 100644
index 000000000..d65366fd1
--- /dev/null
+++ b/debian/patches/fix-open-redirection-without-OIDC-redirect-uris.patch
@@ -0,0 +1,365 @@
+Description: Fix open redirection when OIDC RP has no 
oidcRPMetaDataOptionsRedirectUris
+ This issue concerns only people that modify config by hand. The manager
+ refuses already a relying party without redirect URIs.
+Author: Yadd <y...@debian.org>
+Origin: upstream, commit:c1de35ad
+Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3003
+Forwarded: not-needed
+Applied-Upstream: v2.17.1, commit:c1de35ad
+Reviewed-By: <name and email of someone who approved/reviewed the patch>
+Last-Update: 2023-09-20
+
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm
+@@ -290,6 +290,12 @@
+                     return PE_BADURL;
+                 }
+             }
++            elsif ($redirect_uri) {
++                $self->logger->error(
++"RP $rp has no RedirectUris, unable to handle accept 
redirect_uri=$redirect_uri"
++                );
++                return 37; # PE_BADURL value
++            }
+ 
+             # Check if flow is allowed
+             if ( $flow eq "authorizationcode"
+--- 
a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-OP-logout.t
++++ 
b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-OP-logout.t
+@@ -228,6 +228,8 @@
+                           'http://auth.rp.com/oidc/logout',
+                         oidcRPMetaDataOptionsLogoutType            => 'front',
+                         oidcRPMetaDataOptionsLogoutSessionRequired => 0,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- 
a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-public_client.t
++++ 
b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-public_client.t
+@@ -338,7 +338,9 @@
+                         oidcRPMetaDataOptionsUserIDAttr            => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- 
a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-authchoice.t
++++ 
b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-authchoice.t
+@@ -292,7 +292,9 @@
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- 
a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-info.t
++++ 
b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-info.t
+@@ -342,7 +342,9 @@
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- 
a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-none-alg.t
++++ 
b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code-with-none-alg.t
+@@ -334,7 +334,9 @@
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code.t
++++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-authorization_code.t
+@@ -337,6 +337,8 @@
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+                           "http://auth.rp.com/?logout=1";
+                     }
+--- a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-hybrid.t
++++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-hybrid.t
+@@ -255,7 +255,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 1,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-implicit-no-token.t
++++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-implicit-no-token.t
+@@ -237,7 +237,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-implicit.t
++++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-implicit.t
+@@ -253,7 +253,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F-UpgradeOnly.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F-UpgradeOnly.t
+@@ -362,7 +362,9 @@
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsAuthnLevel            => 5,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F.t
+@@ -362,7 +362,9 @@
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.rp.com/?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Hooks.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Hooks.t
+@@ -57,6 +57,7 @@
+                     oidcRPMetaDataOptionsUserIDAttr            => "",
+                     oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                     oidcRPMetaDataOptionsBypassConsent         => 1,
++                    oidcRPMetaDataOptionsRedirectUris => 'http://rp2.com/',
+                 },
+                 oauth => {
+                     oidcRPMetaDataOptionsDisplayName  => "oauth",
+--- a/lemonldap-ng-portal/t/32-OIDC-Macro.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Macro.t
+@@ -136,6 +136,7 @@
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => 
"custom_sub",
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris => 'http://rp.com/',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Offline-Session.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Offline-Session.t
+@@ -60,7 +60,7 @@
+                     oidcRPMetaDataOptionsIDTokenForceClaims    => 1,
+                     oidcRPMetaDataOptionsAdditionalAudiences =>
+                       "http://my.extra.audience/test urn:extra2",
+-
++                      oidcRPMetaDataOptionsRedirectUris => 'http://test/',
+                 }
+             },
+             oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Refresh-Token.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Refresh-Token.t
+@@ -56,6 +56,7 @@
+                     oidcRPMetaDataOptionsIDTokenForceClaims    => 1,
+                     oidcRPMetaDataOptionsAdditionalAudiences =>
+                       "http://my.extra.audience/test urn:extra2",
++                    oidcRPMetaDataOptionsRedirectUris => 'http://test/',
+                 }
+             },
+             oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/32-OIDC-Token-Introspection.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Token-Introspection.t
+@@ -57,6 +57,7 @@
+                     oidcRPMetaDataOptionsUserIDAttr            => "",
+                     oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                     oidcRPMetaDataOptionsBypassConsent         => 1,
++                    oidcRPMetaDataOptionsRedirectUris => 'http://rp2.com/',
+                 },
+                 oauth => {
+                     oidcRPMetaDataOptionsDisplayName  => "oauth",
+--- a/lemonldap-ng-portal/t/32-OIDC-Token-Security.t
++++ b/lemonldap-ng-portal/t/32-OIDC-Token-Security.t
+@@ -57,6 +57,7 @@
+                     oidcRPMetaDataOptionsUserIDAttr            => "",
+                     oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                     oidcRPMetaDataOptionsBypassConsent         => 1,
++                    oidcRPMetaDataOptionsRedirectUris => 'http://rp.com/',
+                 },
+                 rp2 => {
+                     oidcRPMetaDataOptionsDisplayName           => "RP2",
+@@ -67,7 +68,8 @@
+                     oidcRPMetaDataOptionsUserIDAttr            => "",
+                     oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                     oidcRPMetaDataOptionsBypassConsent         => 1,
+-                    oidcRPMetaDataOptionsRule => '$uid eq "dwho"',
++                    oidcRPMetaDataOptionsRule         => '$uid eq "dwho"',
++                    oidcRPMetaDataOptionsRedirectUris => 'http://rp2.com/',
+                 }
+             },
+             oidcOPMetaDataOptions           => {},
+@@ -104,7 +106,7 @@
+ 
+ # Try to get code for RP1 with invalide scope name
+ $query =
+-"response_type=code&scope=openid%20profile%20email%22&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp2.com%2F";
++"response_type=code&scope=openid%20profile%20email%22&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp.com%2F";
+ ok(
+     $res = $op->_get(
+         "/oauth2/authorize",
+@@ -119,7 +121,7 @@
+ #
+ # Get code for RP1
+ $query =
+-"response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp2.com%2F";
++"response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp.com%2F";
+ ok(
+     $res = $op->_get(
+         "/oauth2/authorize",
+@@ -131,7 +133,7 @@
+ );
+ count(1);
+ 
+-my ($code) = expectRedirection( $res, qr#http://rp2\.com/.*code=([^\&]*)# );
++my ($code) = expectRedirection( $res, qr#http://rp\.com/.*code=([^\&]*)# );
+ 
+ # Play code on RP2
+ $query =
+--- a/lemonldap-ng-portal/t/37-Issuer-Timeout.t
++++ b/lemonldap-ng-portal/t/37-Issuer-Timeout.t
+@@ -182,7 +182,9 @@
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsBypassConsent         => 1,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp.com/?logout=1";
++                          "http://auth.rp.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://rp.example.com/',
+                     },
+                     rp2 => {
+                         oidcRPMetaDataOptionsDisplayName       => "RP",
+@@ -195,7 +197,9 @@
+                         oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
+                         oidcRPMetaDataOptionsBypassConsent         => 1,
+                         oidcRPMetaDataOptionsPostLogoutRedirectUris =>
+-                          "http://auth.rp2.com/?logout=1";
++                          "http://auth.rp2.com/?logout=1";,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://rp2.example.com/',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-Logout-from-OIDC-RP-to-SAML-SP.t
++++ b/lemonldap-ng-portal/t/37-Logout-from-OIDC-RP-to-SAML-SP.t
+@@ -356,7 +356,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-GET-with-WAYF.t
++++ b/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-GET-with-WAYF.t
+@@ -377,7 +377,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-GET.t
++++ b/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-GET.t
+@@ -357,7 +357,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-POST.t
++++ b/lemonldap-ng-portal/t/37-OIDC-RP-to-SAML-IdP-POST.t
+@@ -359,7 +359,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.rp.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-SAML-SP-GET-to-OIDC-OP.t
++++ b/lemonldap-ng-portal/t/37-SAML-SP-GET-to-OIDC-OP.t
+@@ -295,7 +295,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris          =>
++                          'http://auth.proxy.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
+--- a/lemonldap-ng-portal/t/37-SAML-SP-POST-to-OIDC-OP.t
++++ b/lemonldap-ng-portal/t/37-SAML-SP-POST-to-OIDC-OP.t
+@@ -294,7 +294,9 @@
+                         oidcRPMetaDataOptionsBypassConsent     => 0,
+                         oidcRPMetaDataOptionsClientSecret      => "rpsecret",
+                         oidcRPMetaDataOptionsUserIDAttr        => "",
+-                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600
++                        oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
++                        oidcRPMetaDataOptionsRedirectUris =>
++                          'http://auth.proxy.com?openidconnectcallback=1',
+                     }
+                 },
+                 oidcOPMetaDataOptions           => {},
diff --git a/debian/patches/fix-open-redirection.patch 
b/debian/patches/fix-open-redirection.patch
new file mode 100644
index 000000000..db9db737a
--- /dev/null
+++ b/debian/patches/fix-open-redirection.patch
@@ -0,0 +1,262 @@
+Description: fix open redirection
+Author: Yadd <y...@debian.org>
+ Maxime Besson <maxime.bes...@worteks.com>
+Origin: upstream, 
https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/342/diffs
+Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2931
+Forwarded: not-needed
+Applied-Upstream: 2.17.0, 
https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/342
+Last-Update: 2023-09-20
+
+--- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm
++++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm
+@@ -16,6 +16,7 @@
+ use APR::Table;
+ use Apache2::Const -compile =>
+   qw(FORBIDDEN HTTP_UNAUTHORIZED REDIRECT OK DECLINED DONE SERVER_ERROR 
AUTH_REQUIRED HTTP_SERVICE_UNAVAILABLE);
++use URI;
+ use base 'Lemonldap::NG::Handler::Main';
+ 
+ use constant FORBIDDEN         => Apache2::Const::FORBIDDEN;
+@@ -166,7 +167,7 @@
+         $f->r->status( $class->REDIRECT );
+         $f->r->status_line("303 See Other");
+         $f->r->headers_out->unset('Location');
+-        $f->r->err_headers_out->set( 'Location' => $url );
++        $f->r->err_headers_out->set( 'Location' => URI->new($url)->as_string 
);
+         $f->ctx(1);
+     }
+     while ( $f->read( my $buffer, 1024 ) ) {
+--- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm
++++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm
+@@ -9,6 +9,7 @@
+ 
+ #use AutoLoader 'AUTOLOAD';
+ use MIME::Base64;
++use URI;
+ use URI::Escape;
+ use Lemonldap::NG::Common::Session;
+ 
+@@ -697,7 +698,7 @@
+     ) ? '' : ":$portString";
+     my $url = "http" . ( $_https ? "s" : "" ) . "://$realvhost$portString$s";
+     $class->logger->debug("Build URL $url");
+-    return $url;
++    return URI->new($url)->as_string;
+ }
+ 
+ ## @rmethod protected int isUnprotected()
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm
+@@ -9,6 +9,7 @@
+ use Mouse;
+ use MIME::Base64;
+ use Lemonldap::NG::Common::FormEncode;
++use URI;
+ 
+ our $VERSION = '2.0.6';
+ 
+@@ -163,7 +164,10 @@
+         );
+ 
+         # Redirect
+-        return [ 302, [ Location => $urldc, $req->spliceHdrs ], [] ];
++        return [
++            302, [ Location => URI->new($urldc)->as_string, $req->spliceHdrs 
],
++            []
++        ];
+ 
+     }
+ 
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm
+@@ -13,6 +13,7 @@
+   PE_BADURL
+   PE_SENDRESPONSE
+ );
++use URI;
+ 
+ our $VERSION = '2.0.9';
+ 
+@@ -84,7 +85,8 @@
+     if ( $gateway and $gateway eq "true" ) {
+         $self->logger->debug(
+             "Gateway mode requested, redirect without authentication");
+-        $req->response( [ 302, [ Location => $service ], [] ] );
++        $req->response(
++            [ 302, [ Location => URI->new($service)->as_string ], [] ] );
+         for my $s ( $self->ipath, $self->ipath . 'Path' ) {
+             $self->logger->debug("Removing $s from pdata")
+               if delete $req->pdata->{$s};
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm
+@@ -16,6 +16,7 @@
+ use Lemonldap::NG::Common::UserAgent;
+ use MIME::Base64 qw/encode_base64 decode_base64/;
+ use Mouse;
++use URI;
+ 
+ use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_REDIRECT);
+ 
+@@ -1684,7 +1685,7 @@
+         $response_url .= build_urlencoded( state => $state );
+     }
+ 
+-    return $response_url;
++    return URI->new($response_url)->as_string;
+ }
+ 
+ # Create session_state parameter
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm
+@@ -2493,7 +2493,7 @@
+ 
+         # Redirect user to response URL
+         my $slo_url = $logout->msg_url;
+-        return [ 302, [ Location => $slo_url ], [] ];
++        return [ 302, [ Location => URI->new($slo_url)->as_string ], [] ];
+     }
+ 
+     # HTTP-POST
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm
+@@ -132,6 +132,7 @@
+                 $req->{urldc} =~ s/[\r\n]//sg;
+             }
+         }
++        $req->{urldc} = URI->new( $req->{urldc} )->as_string;
+ 
+         # For logout request, test if Referer comes from an authorized site
+         my $tmp = (
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm
+@@ -402,7 +402,13 @@
+                 $self->logger->info("Force cleaning pdata");
+                 delete $req->{pdata}->{_url};
+             }
+-            return [ 302, [ Location => $req->{urldc}, $req->spliceHdrs ], [] 
];
++            return [
++                302,
++                [
++                    Location => URI->new( $req->{urldc} )->as_string,
++                ],
++                []
++            ];
+         }
+     }
+     my ( $tpl, $prms ) = $self->display($req);
+--- a/lemonldap-ng-portal/t/03-XSS-protection.t
++++ b/lemonldap-ng-portal/t/03-XSS-protection.t
+@@ -19,21 +19,25 @@
+     '' => 0, 'Empty',
+ 
+     # 2 http://test1.example.com/
+-    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==' => 1, 'Protected virtual host',
++    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==' => 'http://test1.example.com/',
++    'Protected virtual host',
+ 
+     # 3 http://test1.example.com
+-    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t' => 1, 'Missing / in URL',
++    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t' => 'http://test1.example.com',
++    'Missing / in URL',
+ 
+     # 4 http://test1.example.com:8000/test
+-    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAvdGVzdA==' => 1,
++    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAvdGVzdA==' =>
++      'http://test1.example.com:8000/test',
+     'Non default port',
+ 
+     # 5 http://test1.example.com:8000/
+-    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAv' => 1,
++    'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAv' =>
++      'http://test1.example.com:8000/',
+     'Non default port with missing /',
+ 
+     # 6 http://t.example2.com/test
+-    'aHR0cDovL3QuZXhhbXBsZTIuY29tL3Rlc3Q=' => 1,
++    'aHR0cDovL3QuZXhhbXBsZTIuY29tL3Rlc3Q=' => 'http://t.example2.com/test',
+     'Undeclared virtual host in trusted domain',
+ 
+     # 7 http://testexample2.com/
+@@ -47,7 +51,7 @@
+       . ' "example3.com" is trusted, but domain "*.example3.com" not)',
+ 
+     # 9 http://example3.com/
+-    'aHR0cDovL2V4YW1wbGUzLmNvbS8K' => 1,
++    'aHR0cDovL2V4YW1wbGUzLmNvbS8K' => 'http://example3.com/',
+     'Undeclared virtual host with trusted domain name',
+ 
+     # 10 http://t.example.com/test
+@@ -85,6 +89,21 @@
+     'aHR0cHM6Ly90ZXN0MS5leGFtcGxlLmNvbTp0ZXN0QGhhY2tlci5jb20=' => 0,
+     'userinfo trick',
+ 
++    # 22 url=https://hacker.com\@@test1.example.com/
++    'aHR0cHM6Ly9oYWNrZXIuY29tXEBAdGVzdDEuZXhhbXBsZS5jb20v' =>
++      'https://hacker.com%5C@@test1.example.com/',
++    'Good reencoding (2931)',
++
++    # 23 url=https://hacker.com:\@@test1.example.com/
++    'aHR0cHM6Ly9oYWNrZXIuY29tOlxAQHRlc3QxLmV4YW1wbGUuY29tLw==' =>
++      'https://hacker.com:%5C@@test1.example.com/',
++    'Good reencoding (2931)',
++
++    # 24 url='https://hacker.com\anyth...@test1.example.com/'
++    'aHR0cHM6Ly9oYWNrZXIuY29tXGFueXRoaW5nQHRlc3QxLmV4YW1wbGUuY29tLw==' =>
++      'https://hacker.com%5canyth...@test1.example.com/',
++    'Good reencoding (2931)',
++
+     # LOGOUT TESTS
+     'LOGOUT',
+ 
+@@ -95,7 +114,7 @@
+ 
+     # 19 url=http://www.toto.com/, good referer
+     'aHR0cDovL3d3dy50b3RvLmNvbS8=',
+-    'http://test1.example.com/' => 1,
++    'http://test1.example.com/' => 'http://www.toto.com/',
+     'Logout required by good site',
+ 
+     # 20 url=http://www?<script>, good referer
+@@ -130,10 +149,13 @@
+         ),
+         $detail
+     );
+-    ok( ( $res->[0] == ( $redir ? 302 : 200 ) ),
+-        ( $redir ? 'Get redirection' : 'Redirection dropped' ) )
+-      or explain( $res->[0], ( $redir ? 302 : 200 ) );
+-    count(2);
++    if ($redir) {
++        expectRedirection( $res, $redir );
++    }
++    else {
++        expectOK($res);
++    }
++    count(1);
+ }
+ 
+ while ( defined( my $url = shift(@tests) ) ) {
+@@ -151,9 +173,12 @@
+         ),
+         $detail
+     );
+-    ok( ( $res->[0] == ( $redir ? 302 : 200 ) ),
+-        ( $redir ? 'Get redirection' : 'Redirection dropped' ) )
+-      or explain( $res->[0], ( $redir ? 302 : 200 ) );
++    if ($redir) {
++        expectRedirection( $res, $redir );
++    }
++    else {
++        expectOK($res);
++    }
+     ok(
+         $res = $client->_post(
+             '/',
+@@ -164,7 +189,7 @@
+     );
+     expectOK($res);
+     $id = expectCookie($res);
+-    count(3);
++    count(2);
+ }
+ 
+ clean_sessions();
diff --git a/debian/patches/series b/debian/patches/series
index 8cd6b510b..a7803dae5 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -12,3 +12,6 @@ CVE-2021-40874.patch
 CVE-2022-37186.patch
 fix-url-validation-bypass.patch
 CVE-2023-28862.patch
+fix-open-redirection-without-OIDC-redirect-uris.patch
+fix-open-redirection.patch
+SSRF-issue.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 11.8

Hi,

The updates referred to by each of these requests were included in
today's 11.8 bullseye point release.

Regards,

Adam

--- End Message ---

Reply via email to