Version in base suite: 2.16.1+ds-deb12u6 Base version: lemonldap-ng_2.16.1+ds-deb12u6 Target version: lemonldap-ng_2.16.1+ds-deb12u7 Base file: /srv/ftp-master.debian.org/ftp/pool/main/l/lemonldap-ng/lemonldap-ng_2.16.1+ds-deb12u6.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/l/lemonldap-ng/lemonldap-ng_2.16.1+ds-deb12u7.dsc changelog | 12 + patches/CVE-2025-59518.patch | 43 +++++++ patches/dont-expose-session-id-in-ajax-responses.patch | 49 ++++++++ patches/fix-bad-table-name.patch | 20 +++ patches/fix-kerberos-js.patch | 103 +++++++++++++++++ patches/fix-oidc-fixed-server-in-case-of-error.patch | 97 ++++++++++++++++ patches/fix-path-info.patch | 49 ++++++++ patches/improve-cors.patch | 86 ++++++++++++++ patches/series | 7 + 9 files changed, 466 insertions(+) diff -Nru lemonldap-ng-2.16.1+ds/debian/changelog lemonldap-ng-2.16.1+ds/debian/changelog --- lemonldap-ng-2.16.1+ds/debian/changelog 2025-04-01 11:39:12.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/changelog 2025-10-18 09:52:30.000000000 +0000 @@ -1,3 +1,15 @@ +lemonldap-ng (2.16.1+ds-deb12u7) bookworm; urgency=medium + + * Fix sessions tablename when not default + * Fix OpenID-Connect flow when user encountered an error on server side + * Fix Kerberos JavaScript when used with "Choice" + * Improve CORS check + * Fix path_info + * Fix shell injection issue CVE-2025-59518 + * Hide is from Ajax responses + + -- Yadd Sat, 18 Oct 2025 11:52:30 +0200 + lemonldap-ng (2.16.1+ds-deb12u6) bookworm-security; urgency=high * Fix XSS vulnerability in Choice module (Closes: CVE-2025-31510) diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/CVE-2025-59518.patch lemonldap-ng-2.16.1+ds/debian/patches/CVE-2025-59518.patch --- lemonldap-ng-2.16.1+ds/debian/patches/CVE-2025-59518.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/CVE-2025-59518.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,43 @@ +Description: fix admin shell injection +Author: Maxime Bessons +Origin: upstream, commit:37116d09f +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3462 +Forwarded: not-needed +Applied-Upstream: 2.16.8, commit:cc2490e, commit:ac1980f +Reviewed-By: Xavier Guimard +Last-Update: 2025-10-17 + +--- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Combination/Parser.pm ++++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Combination/Parser.pm +@@ -204,7 +204,7 @@ + sub buildSub { + my ( $self, $cond ) = @_; + my $safe = Safe->new; +- my $res = $safe->reval("sub{my(\$env)=\@_;return ($cond)}"); ++ my $res = $safe->reval("sub{my(\$env)=\@_;local *_;return ($cond)}"); + die "Bad condition $cond: $@" if ($@); + return $res; + } +--- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm ++++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm +@@ -23,7 +23,7 @@ + ); + $cpt->share_from( 'Lemonldap::NG::Common::Safelib', + $Lemonldap::NG::Common::Safelib::functions ); +- $cpt->reval("BEGIN { 'warnings'->unimport; } $val"); ++ $cpt->reval("BEGIN { 'warnings'->unimport; } local *_;$val"); + my $err = join( '', + grep { $_ =~ /(?:Undefined subroutine|Devel::StackTrace)/ ? () : $_ } + split( /\n/, $@ ) ); +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/AutoSignin.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/AutoSignin.pm +@@ -29,7 +29,8 @@ + my $safe = Safe->new; + foreach my $id ( sort keys %$rules ) { + my $sub = +- $safe->reval( 'sub{my($env)=@_;return (' . $rules->{$id} . ')}' ); ++ $safe->reval( ++ 'sub{my($env)=@_;local *_;return (' . $rules->{$id} . ')}' ); + if ($@) { + $self->logger->error( + 'Bad Autologin rule "' . $rules->{$id} . ": $@" ); diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/dont-expose-session-id-in-ajax-responses.patch lemonldap-ng-2.16.1+ds/debian/patches/dont-expose-session-id-in-ajax-responses.patch --- lemonldap-ng-2.16.1+ds/debian/patches/dont-expose-session-id-in-ajax-responses.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/dont-expose-session-id-in-ajax-responses.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,49 @@ +CVE-2025-59518.patch +Description: don't expose session id into Ajax responses +Author: Yadd +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/778 +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3446 +Forwarded: not-needed +Applied-Upstream: 2.16.8, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3446 +Reviewed-By: Xavier Guimatd +Last-Update: 2025-10-18 + +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenIDConnect.pm +@@ -1067,6 +1067,7 @@ + 'deleteSession' + ] + ); ++ $req->data->{nofail} = 1; + $err = $req->error( $self->p->process($req) ); + if ( $err and $err != PE_LOGOUT_OK ) { + if ( $err > 0 ) { +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm +@@ -624,6 +624,7 @@ + ? $req->{sessionInfo} + : $req->{userData} + ); ++ $req->data->{newAuth} = 1; + $self->userLogger->notice( 'User ' + . $ref->{ $self->conf->{whatToTrace} } + . " successfully authenticated at level $ref->{authenticationLevel}" +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +@@ -172,7 +172,6 @@ + + sub authenticatedRequest { + my ( $self, $req ) = @_; +- $req->data->{alreadyAuthenticated} = 1; + return $self->do( + $req, + [ +@@ -349,7 +348,7 @@ + } + else { + my $res = { result => 1, error => $err }; +- unless ( $req->data->{alreadyAuthenticated} ) { ++ if ( $req->data->{newAuth} ) { + $res->{id} = $req->id; + $res->{id_http} = $req->sessionInfo->{_httpSession} + if $req->sessionInfo->{_httpSession}; diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/fix-bad-table-name.patch lemonldap-ng-2.16.1+ds/debian/patches/fix-bad-table-name.patch --- lemonldap-ng-2.16.1+ds/debian/patches/fix-bad-table-name.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/fix-bad-table-name.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,20 @@ +Description: fix fixed tablename +Author: Yadd +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/commit/d9db2a6b +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3405 +Forwarded: not-needed +Applied-Upstream: 2.16.6, commit:25663e12 +Last-Update: 2025-07-12 + +--- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session.pm ++++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session.pm +@@ -212,7 +212,8 @@ + my $dbh = + DBI->connect( $args->{DataSource}, $args->{UserName}, $args->{Password} ) + or die("$!$@"); +- my $sth = $dbh->prepare('SELECT id,a_session from sessions'); ++ my $sth = $dbh->prepare( ++ 'SELECT id,a_session from ' . ( $args->{TableName} || 'sessions' ) ); + $sth->execute; + my %res; + while ( my @row = $sth->fetchrow_array ) { diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/fix-kerberos-js.patch lemonldap-ng-2.16.1+ds/debian/patches/fix-kerberos-js.patch --- lemonldap-ng-2.16.1+ds/debian/patches/fix-kerberos-js.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/fix-kerberos-js.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,103 @@ +Description: make Kerberos module not submit form in case of error in choice menu +Author: Yadd +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/752 +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3406 +Forwarded: not-needed +Applied-Upstream: 2.16.6, commit:bc193ef9 +Last-Update: 2025-07-12 + +--- a/lemonldap-ng-portal/site/coffee/kerberosChoice.coffee ++++ b/lemonldap-ng-portal/site/coffee/kerberosChoice.coffee +@@ -16,5 +16,30 @@ + $('#lformKerberos').submit() + # Case else, will display PE_BADCREDENTIALS or fallback to next auth + # backend +- error: () -> +- $('#lformKerberos').submit() ++ error: (xhr, status, _error) -> ++ e = jQuery.Event "kerberosFailure" ++ $(document).trigger e, [xhr, status, _error] ++ ++ # Check if we are in a choice menu ++ authMenu = $('#authMenu') ++ ++ # If this is a choice menu, don't submit form ++ if authMenu.length ++ msgBox = $('#errormsg') ++ msgBoxContent = '' ++ msgBox.html msgBoxContent ++ # If this is NOT a choice menu, submit form ++ else ++ if !e.isDefaultPrevented() ++ $('#lformKerberos').submit() +--- a/lemonldap-ng-portal/site/htdocs/static/common/js/kerberosChoice.js ++++ b/lemonldap-ng-portal/site/htdocs/static/common/js/kerberosChoice.js +@@ -1,21 +1,54 @@ +-// Generated by CoffeeScript 1.12.8 ++// Generated by CoffeeScript 2.7.0 + (function() { ++ // Launch Kerberos request + $(document).ready(function() { + return $.ajax(portal + '/authkrb', { + dataType: 'json', ++ // Called if browser can't find Kerberos ticket, will display ++ // PE_BADCREDENTIALS + statusCode: { + 401: function() { + return $('#lformKerberos').submit(); + } + }, ++ // If request succeed cookie is set, posting form to get redirection ++ // or menu + success: function(data) { + if (data.ajax_auth_token) { + $('#lformKerberos').find('input[name="ajax_auth_token"]').attr("value", data.ajax_auth_token); + } + return $('#lformKerberos').submit(); + }, +- error: function() { +- return $('#lformKerberos').submit(); ++ // Case else, will display PE_BADCREDENTIALS or fallback to next auth ++ // backend ++ error: function(xhr, status, _error) { ++ var authMenu, e, msgBox, msgBoxContent; ++ e = jQuery.Event("kerberosFailure"); ++ $(document).trigger(e, [xhr, status, _error]); ++ // Check if we are in a choice menu ++ authMenu = $('#authMenu'); ++ // If this is a choice menu, don't submit form ++ if (authMenu.length) { ++ msgBox = $('#errormsg'); ++ msgBoxContent = ''; ++ return msgBox.html(msgBoxContent); ++ } else { ++ if (!e.isDefaultPrevented()) { ++ return $('#lformKerberos').submit(); ++ } ++ } + } + }); + }); diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/fix-oidc-fixed-server-in-case-of-error.patch lemonldap-ng-2.16.1+ds/debian/patches/fix-oidc-fixed-server-in-case-of-error.patch --- lemonldap-ng-2.16.1+ds/debian/patches/fix-oidc-fixed-server-in-case-of-error.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/fix-oidc-fixed-server-in-case-of-error.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,97 @@ +Description: fix "when Auth::OpenIDConnect returns an error, the user cannot try again" +Author: Maxime Besson +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/762 +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3427 +Forwarded: not-needed +Applied-Upstream: v2.16.6, commit:17cdbbed +Reviewed-By: Yadd +Last-Update: 2025-07-12 + +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm +@@ -102,7 +102,13 @@ + my ( $self, $req ) = @_; + + # Check callback +- if ( $req->param( $self->conf->{oidcRPCallbackGetParam} ) ) { ++ if ( $req->param( $self->conf->{oidcRPCallbackGetParam} ) ++ and not $req->param('oidc_callback_processed') ) ++ { ++ # This makes sure we don't go through the callback code when re-posting ++ # a login form ++ $self->p->setHiddenFormValue( $req, "oidc_callback_processed", "1", "", ++ 0 ); + + $self->logger->debug( + 'OpenIDConnect callback URI detected: ' . $req->uri ); +--- 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 +@@ -409,6 +409,68 @@ + + #print STDERR Dumper($res); + ++# Test OIDC auth retry (#3427) ++ok( ++ $res = $rp->_get( ++ "/", accept => 'text/html', ++ ), ++ "Initiate login" ++); ++count(1); ++ ++( $url, $query ) = ++ expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# ); ++ ++my $state = do { ++ my $u = URI->new; ++ $u->query($query); ++ $u->query_param('state'); ++}; ++ ++ok( ++ $res = $rp->_get( ++ "/", ++ query => { ++ openidconnectcallback => 1, ++ error => "canceled", ++ state => $state, ++ }, ++ accept => 'text/html', ++ ), ++ "Return with error" ++); ++count(1); ++expectPortalError( $res, 106 ); ++ ++( $host, $url, $query ) = ++ expectForm( $res, '#', undef, 'oidc_callback_processed' ); ++ ++ok( ++ $res = $rp->_post( ++ "/", $query, ++ query => { ++ openidconnectcallback => 1, ++ error => "canceled", ++ state => $state, ++ }, ++ accept => 'text/html', ++ ), ++ "Submit form again" ++); ++count(1); ++ ++( $url, $query ) = ++ expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# ); ++ ++my $new_state = do { ++ my $u = URI->new; ++ $u->query($query); ++ $u->query_param('state'); ++}; ++ok( $new_state, "New state was generated" ); ++isnt( $new_state, $state, "New state is different than previous" ); ++count(2); ++ + clean_sessions(); + done_testing( count() ); + diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/fix-path-info.patch lemonldap-ng-2.16.1+ds/debian/patches/fix-path-info.patch --- lemonldap-ng-2.16.1+ds/debian/patches/fix-path-info.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/fix-path-info.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,49 @@ +Description: fix path_info +Author: Maxime Besson +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/763 +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3338 +Forwarded: not-needed +Applied-Upstream: 2.16.6, commit:8dfc8be2 +Reviewed-By: Xavier Guimard +Last-Update: 2025-07-12 + +--- a/e2e-tests/llng-server.psgi ++++ b/e2e-tests/llng-server.psgi +@@ -51,10 +51,13 @@ + psgi => sub { + return sub { + +- # Fix PATH_INFO when using Nginx with default uwsgi_params +- # See #2031 +- ( $_[0]->{PATH_INFO} ) = +- $_[0]->{REQUEST_URI} =~ /^(?:\Q$_[0]->{SCRIPT_NAME}\E)?([^?]*)/; ++ # Reimplement split_pathinfo ++ if ( $_[0]->{SCRIPT_NAME} ++ and rindex( $_[0]->{PATH_INFO}, $_[0]->{SCRIPT_NAME}, 0 ) == 0 ) ++ { ++ $_[0]->{PATH_INFO} = ++ substr( $_[0]->{PATH_INFO}, length( $_[0]->{SCRIPT_NAME} ) ); ++ } + + my $script = $_[0]->{SCRIPT_FILENAME}; + return $_apps{$script}->(@_) if ( $_apps{$script} ); +--- a/lemonldap-ng-handler/eg/llng-server.psgi ++++ b/lemonldap-ng-handler/eg/llng-server.psgi +@@ -53,10 +53,13 @@ + psgi => sub { + return sub { + +- # Fix PATH_INFO when using Nginx with default uwsgi_params +- # See #2031 +- ( $_[0]->{PATH_INFO} ) = +- $_[0]->{REQUEST_URI} =~ /^(?:\Q$_[0]->{SCRIPT_NAME}\E)?([^?]*)/; ++ # Reimplement split_pathinfo ++ if ( $_[0]->{SCRIPT_NAME} ++ and rindex( $_[0]->{PATH_INFO}, $_[0]->{SCRIPT_NAME}, 0 ) == 0 ) ++ { ++ $_[0]->{PATH_INFO} = ++ substr( $_[0]->{PATH_INFO}, length( $_[0]->{SCRIPT_NAME} ) ); ++ } + + my $script = $_[0]->{SCRIPT_FILENAME}; + return $_apps{$script}->(@_) if ( $_apps{$script} ); diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/improve-cors.patch lemonldap-ng-2.16.1+ds/debian/patches/improve-cors.patch --- lemonldap-ng-2.16.1+ds/debian/patches/improve-cors.patch 1970-01-01 00:00:00.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/improve-cors.patch 2025-10-18 09:52:30.000000000 +0000 @@ -0,0 +1,86 @@ +Description: improve CORS checks +Author: Maxime Besson +Origin: upstream, https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/merge_requests/767 +Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/3430 +Forwarded: not-needed +Applied-Upstream: 2.16.6, commit:a34389a6 +Reviewed-By: Xavier Guimard +Last-Update: 2025-07-12 + +--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm ++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +@@ -1249,9 +1249,7 @@ + # If this is a cross-domain request from the portal itself + # (Ajax SSL to a different VHost) + # we allow CORS +- if ( $req->origin +- and index( $self->conf->{portal}, $req->origin ) == 0 ) +- { ++ if ( $self->_checkSelfCors($req) ) { + $self->logger->debug('AJAX request from portal, allowing CORS'); + push @{ $res->[1] }, + "Access-Control-Allow-Origin" => $req->origin, +@@ -1265,6 +1263,21 @@ + return $res; + } + ++sub _checkSelfCors { ++ my ( $self, $req ) = @_; ++ ++ if ( $req->origin ) { ++ my $origin = URI->new( $req->origin ); ++ my $portal = URI->new( $self->conf->{portal} ); ++ ++ return ( $origin->scheme ++ and $portal->scheme eq $origin->scheme ++ and $origin->host_port ++ and $origin->host_port eq $portal->host_port ); ++ } ++ return; ++} ++ + sub sendRawHtml { + my ($self) = $_[0]; + my $res = Lemonldap::NG::Common::PSGI::sendRawHtml(@_); +--- a/lemonldap-ng-portal/t/01-CSP-and-CORS-headers.t ++++ b/lemonldap-ng-portal/t/01-CSP-and-CORS-headers.t +@@ -23,6 +23,14 @@ + } + ); + ++# Test request from alternate vhost to portal ++checkCorsAllowed( $client, "http://auth.example.com", 1 ); ++checkCorsAllowed( $client, "http://auth.example.com:80", 1 ); ++checkCorsAllowed( $client, "http://auth.example.comm", 0 ); ++checkCorsAllowed( $client, "http://auth.example.co", 0 ); ++checkCorsAllowed( $client, "http://example.com", 0 ); ++checkCorsAllowed( $client, "https://auth.example.com", 0 ); ++ + # Test normal first access + # ------------------------ + ok( $res = $client->_get('/'), 'Unauth JSON request' ); +@@ -157,6 +165,24 @@ + + done_testing( count() ); + ++sub checkCorsAllowed { ++ my ( $client, $origin, $result ) = @_; ++ ok( ++ my $res = $client->_get( '/', custom => { HTTP_ORIGIN => "$origin" } ), ++ "Unauth JSON request from $origin" ++ ); ++ my %headers = @{ $res->[1] }; ++ if ($result) { ++ is( $headers{'Access-Control-Allow-Origin'}, ++ $origin, "$origin is allowed" ); ++ } ++ else { ++ ok( !$headers{'Access-Control-Allow-Origin'}, ++ "$origin is not allowed" ); ++ } ++ count(2); ++} ++ + sub checkCorsPolicy { + my ($res) = @_; + my %headers = @{ $res->[1] }; diff -Nru lemonldap-ng-2.16.1+ds/debian/patches/series lemonldap-ng-2.16.1+ds/debian/patches/series --- lemonldap-ng-2.16.1+ds/debian/patches/series 2025-04-01 11:39:12.000000000 +0000 +++ lemonldap-ng-2.16.1+ds/debian/patches/series 2025-10-18 09:52:30.000000000 +0000 @@ -16,3 +16,10 @@ CVE-2024-52948.patch fix-test-when-ldap-server-exists.patch CVE-2025-31510.patch +fix-bad-table-name.patch +fix-oidc-fixed-server-in-case-of-error.patch +fix-kerberos-js.patch +improve-cors.patch +fix-path-info.patch +CVE-2025-59518.patch +dont-expose-session-id-in-ajax-responses.patch