--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian....@packages.debian.org
Usertags: pu
[ Reason ]
lemonldap-ng is vulnerable to password bypass (impact critical) in a very
unlikely setup (probability very low). CVE-2021-40874
[ Impact ]
In such configuration, a remote lemonldap-ng system that queries the
main lemonldap-ng system using internal lemonldap-ng protocol instead of
SAML/OpenID-Connect, accepts user with _wrong password; if and only if_
main lemonldap-ng system is configured to use both Kerberos and LDAP
authentication.
[ Tests ]
Tests passed and upstream patch adds a new test
[ Risks ]
Low risk, test coverage proves that package isn't broken with such
change (trivial for a lemonldap-ng dev ;-))
[ 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 ]
Instead of setting login/password into result variables ($req->user),
RESTServer stores them in form and launch the whole authentication
process ($self->p->authProcess) instead of selected steps.
Same change is applied to CheckState plugin (no major risk here, this
plugin is reserved to LLNG administrators).
Cheers,
Yadd
diff --git a/debian/changelog b/debian/changelog
index a56d54279..f6be653a8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+lemonldap-ng (2.0.11+ds-4+deb11u1) bullseye; urgency=medium
+
+ * Fix auth process in password-testing plugins (Closes: CVE-2021-20874)
+
+ -- Yadd <y...@debian.org> Thu, 24 Feb 2022 15:16:09 +0100
+
lemonldap-ng (2.0.11+ds-4) unstable; urgency=high
* Import security fixes from 2.0.12
diff --git a/debian/patches/CVE-2021-40874.patch
b/debian/patches/CVE-2021-40874.patch
new file mode 100644
index 000000000..a333d3260
--- /dev/null
+++ b/debian/patches/CVE-2021-40874.patch
@@ -0,0 +1,238 @@
+Description: Fix auth process in password-testing plugins (#2611)
+Author: Maxime Besson <maxime.bes...@worteks.com>
+Origin: upstream,
https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/commit/66946e8
+Bug: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2612
+Forwarded: not-needed
+Reviewed-By: Yadd <y...@debian.org>
+Last-Update: 2022-01-14
+
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/_WebForm.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/_WebForm.pm
+@@ -73,7 +73,10 @@
+ my $res = PE_OK;
+
+ # 1. No user defined at all -> first access
+- unless ( $defUser and $req->method =~ /^POST$/i ) {
++ # _pwdCheck is a workaround to make CheckUser work while using a GET
++ unless ( $defUser
++ and ( uc( $req->method ) eq "POST" or $req->data->{_pwdCheck} ) )
++ {
+ $res = PE_FIRSTACCESS;
+ }
+
+@@ -170,6 +173,7 @@
+
+ sub setSecurity {
+ my ( $self, $req ) = @_;
++ return if $req->data->{skipToken};
+
+ # If captcha is enable, prepare it
+ if ( $self->captcha ) {
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CheckState.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CheckState.pm
+@@ -41,16 +41,22 @@
+
+ if ( my $user = $req->param('user') and my $pwd = $req->param('password')
)
+ {
+- $req->user($user);
+- $req->data->{password} = $pwd;
++ $req->parameters->{user} = ($user);
++ $req->parameters->{password} = $pwd;
++ $req->data->{skipToken} = 1;
++
++ # This makes Auth::Choice use authChoiceAuthBasic if defined
++ $req->data->{_pwdCheck} = 1;
+
+ # Not launched methods:
+- # - "extractFormInfo" due to "token"
+ # - "buildCookie" useless here
+ $req->steps( [
+- 'getUser', 'authenticate',
+- @{ $self->p->betweenAuthAndData }, $self->p->sessionData,
+- @{ $self->p->afterData }, 'storeHistory',
++ @{ $self->p->beforeAuth },
++ $self->p->authProcess,
++ @{ $self->p->betweenAuthAndData },
++ $self->p->sessionData,
++ @{ $self->p->afterData },
++ 'storeHistory',
+ @{ $self->p->endAuth }
+ ]
+ );
+--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm
++++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm
+@@ -681,11 +681,13 @@
+ 400 );
+ }
+
+- $req->user($user);
+- $req->data->{password} = $password;
++ $req->parameters->{user} = $user;
++ $req->parameters->{password} = $password;
++ $req->data->{_pwdCheck} = 1;
++ $req->data->{skipToken} = 1;
+
+ if ( $self->p->_userDB ) {
+- $req->steps( [ 'getUser', 'authenticate' ] );
++ $req->steps( [ $self->p->authProcess ] );
+ my $result = $self->p->process($req);
+ if ( $result == PE_PASSWORD_OK or $result == PE_OK ) {
+ return $self->p->sendJSONresponse( $req,
+--- /dev/null
++++ b/lemonldap-ng-portal/t/35-REST-auth-password-server.t
+@@ -0,0 +1,126 @@
++use Test::More;
++use strict;
++use IO::String;
++use MIME::Base64;
++use JSON;
++
++require 't/test-lib.pm';
++
++my $res;
++
++my $client = LLNG::Manager::Test->new( {
++ ini => {
++ logLevel => 'error',
++ useSafeJail => 1,
++ requireToken => 1,
++ restAuthServer => 1,
++ restPasswordServer => 1,
++ authentication => 'Combination',
++ userDB => 'Same',
++
++ combination => '[K,Dm] or [Dm]',
++ combModules => {
++ K => {
++ for => 1,
++ type => 'Kerberos',
++ },
++ Dm => {
++ for => 0,
++ type => 'Demo',
++ },
++ },
++ krbKeytab => '/etc/keytab',
++ krbByJs => 1,
++ }
++ }
++);
++
++# Test pwdConfirm endpoint
++
++my $res = expectJSON(
++ postJSON(
++ $client,
++ "/proxy/pwdConfirm",
++ {
++ user => "dwho",
++ password => "dwho",
++ }
++ )
++);
++
++is( $res->{result}, 1, "Correct password is accepted" );
++count(1);
++
++my $res = expectJSON(
++ postJSON(
++ $client,
++ "/proxy/pwdConfirm",
++ {
++ user => "waldo",
++ password => "dwho",
++ }
++ )
++);
++
++is( $res->{result}, 0, "Incorrect user is rejected" );
++count(1);
++
++my $res = expectJSON(
++ postJSON(
++ $client,
++ "/proxy/pwdConfirm",
++ {
++ user => "dwho",
++ password => "wrongpass",
++ }
++ )
++);
++
++is( $res->{result}, 0, "Incorrect password is rejected" );
++count(1);
++
++# Test getUser endpoint
++# Existing user
++my $res = expectJSON(
++ postJSON(
++ $client,
++ "/proxy/getUser",
++ {
++ user => "dwho",
++ }
++ )
++);
++is( $res->{result}, 1, "Correct result" );
++is( $res->{info}->{cn}, "Doctor Who", "Correct attributes" );
++is( $res->{info}->{_whatToTrace}, "dwho", "Correct macro" );
++count(3);
++
++# Missing user
++my $res = expectJSON(
++ postJSON(
++ $client,
++ "/proxy/getUser",
++ {
++ user => "notfound",
++ }
++ )
++);
++is( $res->{result}, 0, "Correct result" );
++is( $res->{info}, undef, "No attributes" );
++count(2);
++
++clean_sessions();
++
++done_testing( count() );
++
++sub postJSON {
++ my ( $portal, $url, $payload ) = @_;
++ my $string_payload = to_json($payload);
++ return $portal->_post(
++ $url,
++ IO::String->new($string_payload),
++ accept => 'application/json',
++ type => 'application/json',
++ length => length($string_payload)
++ );
++}
+--- a/lemonldap-ng-portal/t/65-CheckState.t
++++ b/lemonldap-ng-portal/t/65-CheckState.t
+@@ -8,10 +8,25 @@
+ my $client = LLNG::Manager::Test->new( {
+ ini => {
+ logLevel => 'error',
+- authentication => 'Demo',
++ requireToken => 1,
+ checkStateSecret => 'x',
+ checkState => 1,
++ authentication => 'Combination',
+ userDB => 'Same',
++
++ combination => '[K,Dm] or [Dm]',
++ combModules => {
++ K => {
++ for => 1,
++ type => 'Kerberos',
++ },
++ Dm => {
++ for => 0,
++ type => 'Demo',
++ },
++ },
++ krbKeytab => '/etc/keytab',
++ krbByJs => 1,
+ }
+ }
+ );
diff --git a/debian/patches/series b/debian/patches/series
index a1245fc76..644277be7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -8,3 +8,4 @@ fix-trusted-domain-wildcard.patch
fix-trusted-domain-regex.patch
fix-xss-on-register-form.patch
dont-display-totp-secret.patch
+CVE-2021-40874.patch
--- End Message ---