From 999060590fc6bfe6d87ffe9e6c54f34222996103 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Wed, 8 Feb 2023 10:16:00 +0100 Subject: [PATCH 1/6] Add a function for 2fDevice log formatting (#2858) --- lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm index 569bea4a88..56560684c2 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm @@ -7,7 +7,7 @@ use MIME::Base64 qw(encode_base64); our $VERSION = '2.0.14'; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(getSameSite getPSessionID genId2F); +our @EXPORT_OK = qw(getSameSite getPSessionID genId2F display2F); sub getPSessionID { my ($uid) = @_; @@ -20,6 +20,11 @@ sub genId2F { "" ); } +sub display2F { + my ($device) = @_; + return sprintf( "[%s]%s", $device->{type}, $device->{epoch} ); +} + sub getSameSite { my ($conf) = @_; -- GitLab From 62a7d0a5280644857fa7b7cdd3f37d3d5f826fed Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Mon, 30 Jan 2023 15:49:19 +0100 Subject: [PATCH 2/6] Add logs when deleting 2FA from API (#2858) --- .../lib/Lemonldap/NG/Manager/Api/2F.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm index 918339607d..04d5ca0687 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm @@ -10,7 +10,7 @@ use Mouse; use JSON; use Lemonldap::NG::Common::Session; -use Lemonldap::NG::Common::Util qw/genId2F/; +use Lemonldap::NG::Common::Util qw/genId2F display2F/; sub getSecondFactors { my ( $self, $req ) = @_; @@ -230,7 +230,7 @@ sub _delete2FFromSessions { push @keep, $element; } else { - $removed->{ genId2F($element) } = "removed"; + $removed->{ genId2F($element) } = $element; } } if ( ( $total - scalar @keep ) > 0 ) { @@ -291,6 +291,13 @@ sub _delete2F { # merge results $removed = { %$removed, %{ $res->{removed} } }; $count = scalar( keys %$removed ); + if ($count) { + my $list_log = join( + ",", map { display2F( $removed->{$_} ) } + keys %$removed + ); + $self->userLogger->notice("[API] 2FA deletion for $uid: $list_log"); + } return { res => 'ok', -- GitLab From c78f88632661f87ca9ee40120d4094d54959bdc3 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Mon, 30 Jan 2023 16:09:12 +0100 Subject: [PATCH 3/6] Log 2FA deletions in manager (#2858) --- .../lib/Lemonldap/NG/Common/Session/REST.pm | 16 ++++++++++++---- lemonldap-ng-manager/t/60-2ndfa.t | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm index 0d610cae47..5ae1cd7f10 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm @@ -3,6 +3,7 @@ package Lemonldap::NG::Common::Session::REST; use strict; use Mouse; use Lemonldap::NG::Common::Conf::Constants; +use Lemonldap::NG::Common::Util qw/display2F/; use JSON qw(from_json to_json); our $VERSION = '2.0.15'; @@ -46,7 +47,7 @@ sub hAttr { sub delSession { my ( $self, $req ) = @_; my $type = $req->params('sessionType'); - my $mod = $self->getMod($req) + my $mod = $self->getMod($req) or return $self->sendError( $req, undef, 400 ); my $id = $req->params('sessionId') or return $self->sendError( $req, 'sessionId is missing', 400 ); @@ -181,9 +182,16 @@ sub delete2F { $self->logger->debug( "Searching for 2F device to delete -> $type / $epoch ..."); if ( defined $element->{type} && defined $element->{epoch} ) { - push @keep, $element - unless ( ( $element->{type} eq $type ) - and ( $element->{epoch} eq $epoch ) ); + if ( ( $element->{type} eq $type ) + and ( $element->{epoch} eq $epoch ) ) + { + my $uid = $session->data->{_session_uid}; + $self->userLogger->notice( + "2FA deletion for $uid: " . display2F($element) ); + } + else { + push @keep, $element; + } } else { $self->logger->error("Corrupted _2fDevice"); diff --git a/lemonldap-ng-manager/t/60-2ndfa.t b/lemonldap-ng-manager/t/60-2ndfa.t index 0ecd12ad87..e63b5840e2 100644 --- a/lemonldap-ng-manager/t/60-2ndfa.t +++ b/lemonldap-ng-manager/t/60-2ndfa.t @@ -32,6 +32,7 @@ sub newSession { uid => $uid, _utime => time, _session_kind => $kind, + _session_uid => $uid, _2fDevices => to_json($sfaDevices), } ); -- GitLab From e0c146b8dd38496d4f6b8afa942472713d54fe20 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Wed, 8 Feb 2023 10:58:59 +0100 Subject: [PATCH 4/6] Log add/deletion of 2FA in portal (#2858) --- .../Lemonldap/NG/Portal/2F/Register/Generic.pm | 4 ---- .../Lemonldap/NG/Portal/2F/Register/Password.pm | 2 -- .../lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm | 4 ---- .../lib/Lemonldap/NG/Portal/2F/Register/U2F.pm | 5 ----- .../Lemonldap/NG/Portal/2F/Register/WebAuthn.pm | 2 -- .../Lemonldap/NG/Portal/2F/Register/Yubikey.pm | 5 ----- .../lib/Lemonldap/NG/Portal/Lib/2fDevices.pm | 15 ++++++++++----- 7 files changed, 10 insertions(+), 27 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm index 224b369caa..c5e899a3b8 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm @@ -131,8 +131,6 @@ sub run { ) ) { - $self->userLogger->notice( $self->prefix - . "2f: registration of $genericname succeeds for $user" ); return [ 200, [ @@ -160,8 +158,6 @@ sub run { if ( $self->del2fDevice( $req, $req->userData, $self->prefix, $epoch ) ) { - $self->userLogger->notice( - $self->prefix . "2f: device deleted for $user" ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm index 02b3173200..068a652ec2 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm @@ -110,8 +110,6 @@ sub run { ) ) { - $self->userLogger->notice( $self->prefix - . "2f: registration of password succeeds for $user" ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm index b593162768..e365359827 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm @@ -119,8 +119,6 @@ sub run { ) ) { - $self->userLogger->notice( $self->prefix - . "2f: registration of $TOTPName succeeds for $user" ); return [ 200, [ @@ -192,8 +190,6 @@ sub run { or return $self->p->sendError( $req, $self->prefix . '2f: "epoch" parameter is missing', 400 ); if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) { - $self->userLogger->notice( - $self->prefix . "2f: device deleted for $user" ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm index 15cf90ee71..3d361992b3 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm @@ -92,9 +92,6 @@ sub run { ) ) { - $self->userLogger->notice( $self->prefix - . "2f: registration of device $keyName succeeds for $user" - ); return [ 200, [ @@ -210,8 +207,6 @@ sub run { or return $self->p->sendError( $req, $self->prefix . '2f: "epoch" parameter is missing', 400 ); if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) { - $self->userLogger->notice( - $self->prefix . "2f: device deleted for $user" ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm index 135fcd1c3e..6f4b7d9014 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm @@ -192,8 +192,6 @@ sub _registration { ) ) { - $self->userLogger->notice( - $self->prefix . "2f: registration of $keyName succeeds for $user" ); return $self->p->sendJSONresponse( $req, { result => 1 } ); } else { diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm index 749aa36e1b..1d037f71a9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm @@ -71,9 +71,6 @@ sub run { ) ) { - $self->userLogger->notice( $self->prefix - . "2f: registration of device $UBKName succeeds for $user" - ); return $self->p->sendHtml( $req, 'error', params => { @@ -110,8 +107,6 @@ sub run { or return $self->p->sendError( $req, $self->prefix . '2f: "epoch" parameter is missing', 400 ); if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) { - $self->userLogger->notice( - $self->prefix . "2f: device deleted for $user" ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm index b51219c989..214dc6ef01 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm @@ -22,6 +22,7 @@ of this module use strict; use Mouse::Role; +use Lemonldap::NG::Common::Util qw/display2F/; use JSON; requires qw(p conf logger); @@ -103,9 +104,10 @@ sub add2fDevice { my $_2fDevices = $self->get2fDevices( $req, $info ); push @{$_2fDevices}, $device; - $self->logger->debug( - "Append 2F device: { type => $device->{type}, name => $device->{name} }" - ); + + my $uid = $info->{ $self->conf->{whatToTrace} }; + $self->userLogger->notice( + "User " . $uid . " registered 2F device: " . display2F($device) ); $self->p->updatePersistentSession( $req, { _2fDevices => to_json($_2fDevices) } ); return 1; @@ -156,8 +158,11 @@ sub del2fDevices { @updated_2fDevices; if ( @updated_2fDevices < $size_before ) { $need_update = 1; - $self->logger->debug( - "Deleted 2F device: { type => $type, epoch => $epoch }"); + my $uid = $info->{ $self->conf->{whatToTrace} }; + $self->userLogger->notice( "User " + . $uid + . " deleted 2F device: " + . display2F($device_spec) ); } } -- GitLab From 5dd7b6557d4510fb63324a35c992e5a722de0129 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Wed, 8 Feb 2023 12:04:57 +0100 Subject: [PATCH 5/6] Log 2FA use (#2858) --- .../lib/Lemonldap/NG/Portal/2F/Password.pm | 10 +-- .../Lemonldap/NG/Portal/2F/Register/U2F.pm | 2 +- .../lib/Lemonldap/NG/Portal/2F/TOTP.pm | 18 +++-- .../lib/Lemonldap/NG/Portal/2F/U2F.pm | 16 +++-- .../lib/Lemonldap/NG/Portal/2F/Yubikey.pm | 67 +++++++++++++------ .../lib/Lemonldap/NG/Portal/Lib/Code2F.pm | 19 ++++-- .../lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm | 8 ++- .../Lemonldap/NG/Portal/Main/SecondFactor.pm | 2 +- 8 files changed, 98 insertions(+), 44 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm index f6e7c32d61..b0f01c0c2c 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm @@ -8,6 +8,7 @@ use strict; use Mouse; use JSON qw(from_json to_json); use Lemonldap::NG::Common::Crypto; +use Lemonldap::NG::Common::Util qw/display2F/; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK PE_ERROR @@ -82,6 +83,7 @@ sub run { sub verify { my ( $self, $req, $session ) = @_; my ( $password, $password_device, $secret ); + my $uid = $session->{ $self->conf->{whatToTrace} }; $self->logger->debug( $self->prefix . '2f: verification' ); @@ -103,13 +105,13 @@ sub verify { } if ( $password eq $self->crypto->decrypt($secret) ) { - $self->userLogger->info( $self->prefix . '2f: correct' ); + + $self->userLogger->info( "User $uid authenticated with 2F device: " + . display2F($password_device) ); return PE_OK; } else { - $self->userLogger->notice( $self->prefix - . '2f: invalid for ' - . $session->{ $self->conf->{whatToTrace} } ); + $self->userLogger->notice( $self->prefix . '2f: invalid for ' . $uid ); return PE_BADCREDENTIALS; } } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm index 3d361992b3..1e5edc440e 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm @@ -39,7 +39,7 @@ sub run { # Read existing 2F device(s) my @alldevices = $self->find2fDevicesByType( $req, $req->userData ); - my $challenge = $self->crypter->registrationChallenge; + my $challenge = $self->crypter->registrationChallenge; $self->logger->debug( $self->prefix . "2f: registration challenge ($challenge)" ); return [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm index 3acca045cf..c936b71116 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm @@ -14,6 +14,7 @@ use Lemonldap::NG::Portal::Main::Constants qw( PE_FORMEMPTY PE_SENDRESPONSE ); +use Lemonldap::NG::Common::Util qw/display2F/; our $VERSION = '2.0.16'; @@ -65,6 +66,7 @@ sub run { sub verify { my ( $self, $req, $session ) = @_; my ( $code, $secret, @totp2f ); + my $uid = $session->{ $self->conf->{whatToTrace} }; $self->logger->debug( $self->prefix . '2f: verification' ); unless ( $code = $req->param('code') ) { @@ -73,7 +75,13 @@ sub verify { } @totp2f = $self->find2fDevicesByType( $req, $session, $self->type ); - $secret = $_->{_secret} foreach @totp2f; + my $_2fDevice; + foreach (@totp2f) { + if ( $_->{_secret} ) { + $secret = $_->{_secret}; + $_2fDevice = $_; + } + } unless ($secret) { $self->logger->debug( $self->prefix . '2f: no secret found' ); return PE_BADOTP; @@ -88,13 +96,13 @@ sub verify { return PE_ERROR if $r == -1; if ($r) { - $self->userLogger->info( $self->prefix . '2f: succeed' ); + $self->userLogger->info( "User $uid authenticated with 2F device: " + . display2F($_2fDevice) ); return PE_OK; } else { - $self->userLogger->notice( $self->prefix - . '2f: invalid attempt for ' - . $session->{ $self->conf->{whatToTrace} } ); + $self->userLogger->notice( + $self->prefix . '2f: invalid attempt for ' . $uid ); return PE_BADOTP; } } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm index b64fabe3a8..ee788d3fc3 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm @@ -8,6 +8,7 @@ use strict; use Mouse; use JSON qw(from_json to_json); use MIME::Base64 qw(decode_base64url); +use Lemonldap::NG::Common::Util qw/display2F/; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK PE_ERROR @@ -106,6 +107,7 @@ sub run { sub verify { my ( $self, $req, $session ) = @_; my $crypter; + my $uid = $session->{ $self->conf->{whatToTrace} }; # Check U2F signature if ( my $resp = $req->param('signature') @@ -151,13 +153,17 @@ sub verify { return $self->fail($req); } if ( $crypter->authenticationVerify($resp) ) { - $self->userLogger->info( $self->prefix . '2f: signature verified' ); + + # Lookup 2f device from authenticator's keyHandle + my $device = $req->data->{u2fkhtodev}->{ $data->{keyHandle} }; + $self->userLogger->info( "User $uid authenticated with 2F device: " + . display2F($device) ); return PE_OK; } else { $self->userLogger->notice( $self->prefix . '2f: unvalid signature for ' - . $session->{ $self->conf->{whatToTrace} } . ' (' + . $uid . ' (' . Crypt::U2F::Server::u2fclib_getError() . ')' ); $req->error(PE_U2FFAILED); @@ -165,9 +171,8 @@ sub verify { } } else { - $self->userLogger->notice( $self->prefix - . '2f: no valid response for user ' - . $session->{ $self->conf->{whatToTrace} } ); + $self->userLogger->notice( + $self->prefix . '2f: no valid response for user ' . $uid ); $req->authResult(PE_U2FFAILED); return $self->fail($req); } @@ -202,6 +207,7 @@ sub loadUser { foreach (@u2fs) { $kh = $_->{_keyHandle}; + $req->data->{u2fkhtodev}->{$kh} = $_; $uk = decode_base64url( $_->{_userKey} ); $self->logger->debug( $self->prefix . "2f: append crypter with kh = $kh" ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm index c4b85e1c68..56b487bb0b 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm @@ -7,6 +7,7 @@ package Lemonldap::NG::Portal::2F::Yubikey; use strict; use Mouse; use JSON qw(from_json to_json); +use Lemonldap::NG::Common::Util qw/display2F/; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK PE_ERROR @@ -89,44 +90,63 @@ sub init { return $self->SUPER::init(); } -sub _findYubikey { - my ( $self, $req, $session ) = @_; +sub _findYubikeyForCode { + my ( $self, $req, $session, $code ) = @_; my ( $yubikey, @ubk2f ); + my $id_from_code = substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ); + # First, lookup from session attribute if ( $self->conf->{yubikey2fFromSessionAttribute} ) { my $attr = $self->conf->{yubikey2fFromSessionAttribute}; $yubikey = $session->{$attr}; + if ($yubikey) { + if ( $yubikey eq $id_from_code ) { + return { _yubikey => $yubikey, }; + } + else { + return; + } + } } # If we didn't find a key, lookup psession if ( !$yubikey and $session->{_2fDevices} ) { @ubk2f = $self->find2fDevicesByType( $req, $session, $self->type ); - if ( my $code = $req->param('code') ) { - $yubikey = $_->{_yubikey} foreach grep { - $_->{_yubikey} eq - substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ) - } @ubk2f; - } - else { - $yubikey = $_->{_yubikey} foreach @ubk2f; - } + my @results = grep { $_->{_yubikey} eq $id_from_code } @ubk2f; + return $results[0]; } + return; +} + +sub _hasYubikey { + my ( $self, $req, $session ) = @_; + my ( $yubikey, @ubk2f ); - return $yubikey || ''; + # Does the user have a session attribute + if ( $self->conf->{yubikey2fFromSessionAttribute} ) { + my $attr = $self->conf->{yubikey2fFromSessionAttribute}; + $yubikey = $session->{$attr}; + } + + # If we didn't find a value, lookup registered devices + if ( !$yubikey and $session->{_2fDevices} ) { + @ubk2f = $self->find2fDevicesByType( $req, $session, $self->type ); + $yubikey = $_->{_yubikey} foreach @ubk2f; + } + + return ( $yubikey ? 1 : 0 ); } sub run { my ( $self, $req, $token ) = @_; - my $yubikey = $self->_findYubikey( $req, $req->sessionInfo ); - unless ($yubikey) { + unless ( $self->_hasYubikey( $req, $req->sessionInfo ) ) { $self->userLogger->warn( $self->prefix . '2f: user ' . $req->{sessionInfo}->{ $self->conf->{whatToTrace} } . ' has no device registered' ); return PE_BADOTP; } - $self->logger->debug( $self->prefix . "2f: found $yubikey" ); # Prepare form my ( $checkLogins, $stayConnected ) = $self->getFormParams($req); @@ -157,12 +177,8 @@ sub verify { } # Verify OTP - my $yubikey = $self->_findYubikey( $req, $session ); - if ( - index( $yubikey, - substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ) ) == -1 - ) - { + my $yubikey = $self->_findYubikeyForCode( $req, $session, $code ); + unless ($yubikey) { $self->userLogger->warn( $self->prefix . '2f: device not registered' ); return PE_BADOTP; } @@ -173,6 +189,15 @@ sub verify { $self->userLogger->warn( $self->prefix . '2f: verification failed' ); return PE_BADOTP; } + my $uid = $session->{ $self->conf->{whatToTrace} }; + if ( $yubikey->{epoch} ) { + $self->userLogger->info( + "User $uid authenticated with 2F device: " . display2F($yubikey) ); + } + else { + $self->userLogger->info("User $uid authenticated with Yubikey"); + } + return PE_OK; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm index b06837dda4..3a1050c258 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm @@ -10,6 +10,7 @@ package Lemonldap::NG::Portal::Lib::Code2F; use strict; use Mouse; +use Lemonldap::NG::Common::Util qw/display2F/; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK @@ -131,9 +132,9 @@ sub _resend { { # Resend code and update last retry - unless ($self->sendCode( $req, $session, $code )){ + unless ( $self->sendCode( $req, $session, $code ) ) { return $self->p->do( $req, [ sub { PE_ERROR } ] ); - }; + } $self->ott->updateToken( $token, __lastRetry => time ); } else { @@ -151,10 +152,18 @@ sub verify { return PE_FORMEMPTY; } - $self->populateDestAttribute( $req, $req->sessionInfo ); + $self->populateDestAttribute( $req, $session ); - return $self->verify_supplied_code( $req, $session, $usercode ); + my $res = $self->verify_supplied_code( $req, $session, $usercode ); + # If we authenticated the user using a 2F Device, log it + if ( $res == PE_OK && $req->data->{__2fDevice_registrable} ) { + my $uid = $session->{ $self->conf->{whatToTrace} }; + my $device = $req->data->{__2fDevice_registrable}; + $self->userLogger->info( + "User $uid authenticated with 2F device: " . display2F($device) ); + } + return $res; } sub verify_supplied_code { @@ -236,7 +245,9 @@ sub populateDestAttribute { my @registered_devices = $self->find2fDevicesByType( $req, $sessionInfo, $self->prefix ); if (@registered_devices) { + $sessionInfo->{destination} = $registered_devices[0]->{_generic}; + $req->data->{__2fDevice_registrable} = $registered_devices[0]; } } } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm index 90e98a8dc4..63db100020 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm @@ -8,6 +8,7 @@ use Digest::SHA qw(sha256); use URI; use Carp; with 'Lemonldap::NG::Portal::Lib::2fDevices'; +use Lemonldap::NG::Common::Util qw/display2F/; our $VERSION = '2.0.16'; @@ -186,9 +187,10 @@ sub validateAssertion { if ( $validation_result->{success} == 1 ) { my $new_signature_count = $validation_result->{signature_count}; - $self->userLogger->info( - "Successfully verified signature with count " - . "$new_signature_count for $user" ); + + $self->userLogger->info( "User $user authenticated with 2F device: " + . display2F($matching_credential) + . " (signature count: $new_signature_count) " ); # Update storedSignCount to be the value of authData.signCount $self->update2fDevice( $req, $data, $self->type, diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm index 30fe3ec3e4..d19ba6ded1 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm @@ -141,7 +141,7 @@ sub _verify { # Else restore session $req->mustRedirect(1); $self->userLogger->notice( $self->prefix - . '2f verification for ' + . '2f verification succeeded for ' . $req->sessionInfo->{ $self->conf->{whatToTrace} } ); if ( my $l = $self->authnLevel ) { -- GitLab From 68a73e9de3663b0cd4cf4373784ead0eb47ca68a Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Wed, 8 Feb 2023 15:54:58 +0100 Subject: [PATCH 6/6] Update manager 2FA display to show [Type]epoch format (#2858) --- lemonldap-ng-manager/site/coffee/2ndfa.coffee | 4 ++-- lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js | 4 ++-- lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js | 2 +- lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js.map | 2 +- lemonldap-ng-manager/site/htdocs/static/languages/ar.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/en.json | 1 + lemonldap-ng-manager/site/htdocs/static/languages/es.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/fr.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/he.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/it.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/pl.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/tr.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/vi.json | 3 ++- lemonldap-ng-manager/site/htdocs/static/languages/zh.json | 3 ++- .../site/htdocs/static/languages/zh_TW.json | 3 ++- lemonldap-ng-manager/site/templates/2ndfa.tpl | 6 +++--- 16 files changed, 30 insertions(+), 19 deletions(-) diff --git a/lemonldap-ng-manager/site/coffee/2ndfa.coffee b/lemonldap-ng-manager/site/coffee/2ndfa.coffee index 9e2198d3dc..9d8e29d7ae 100644 --- a/lemonldap-ng-manager/site/coffee/2ndfa.coffee +++ b/lemonldap-ng-manager/site/coffee/2ndfa.coffee @@ -203,7 +203,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location', array = JSON.parse(session[attr]) if array.length > 0 subres.push - title: "type" + title: "2fid" value: "name" epoch: "date" for sfDevice in array @@ -215,7 +215,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location', if key == 'epoch' epoch = value subres.push - title: title + title: '[' + title + ']' + epoch value: name epoch: epoch sfrow: true diff --git a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js index b023164be3..741ca0afa3 100644 --- a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js +++ b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js @@ -207,7 +207,7 @@ array = JSON.parse(session[attr]); if (array.length > 0) { subres.push({ - title: "type", + title: "2fid", value: "name", epoch: "date" }); @@ -226,7 +226,7 @@ } } subres.push({ - title: title, + title: '[' + title + ']' + epoch, value: name, epoch: epoch, sfrow: true diff --git a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js index 857e6fcca2..d031ff9ab6 100644 --- a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js +++ b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js @@ -1 +1 @@ -!function(){var a={_whatToTrace:[function(e,t){return"groupBy=substr("+e+",1)"},function(e,t){return e+"="+t+"*"}]},d={_whatToTrace:function(e,t,n,r){return console.log("overSchema => level",n,"over",r),1===n&&t.length>r?e+"="+t+"*&groupBy=substr("+e+","+(n+r+1)+")":null}},_={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},i={home:[]};angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(v,t,e,n,p){var f,r,h;return v.links=links,v.menulinks=menulinks,v.staticPrefix=staticPrefix,v.scriptname=scriptname,v.formPrefix=formPrefix,v.availableLanguages=availableLanguages,v.waiting=!0,v.showM=!1,v.showT=!0,v.data=[],v.currentScope=null,v.currentSession=null,v.menu=i,v.searchString="",v.sfatypes={},v.translateP=t.translateP,v.translate=t.translate,v.translateTitle=function(e){return t.translateField(e,"title")},h="persistent",v.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(v.currentNode,v),v[e.action]();break;case"string":v[e.action]();break;default:console.log(typeof e.action)}return v.showM=!1},v.search2FA=function(e){return e&&(v.searchString=""),v.currentSession=null,v.data=[],v.updateTree2("",v.data,0,0)},v.delete2FA=function(e,t){for(var n=document.querySelectorAll(".data-"+t),r=0,a=n.length;r level",n,"over",r),1===n&&t.length>r?e+"="+t+"*&groupBy=substr("+e+","+(n+r+1)+")":null}},_={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},i={home:[]};angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(v,t,e,n,p){var f,r,h;return v.links=links,v.menulinks=menulinks,v.staticPrefix=staticPrefix,v.scriptname=scriptname,v.formPrefix=formPrefix,v.availableLanguages=availableLanguages,v.waiting=!0,v.showM=!1,v.showT=!0,v.data=[],v.currentScope=null,v.currentSession=null,v.menu=i,v.searchString="",v.sfatypes={},v.translateP=t.translateP,v.translate=t.translate,v.translateTitle=function(e){return t.translateField(e,"title")},h="persistent",v.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(v.currentNode,v),v[e.action]();break;case"string":v[e.action]();break;default:console.log(typeof e.action)}return v.showM=!1},v.search2FA=function(e){return e&&(v.searchString=""),v.currentSession=null,v.data=[],v.updateTree2("",v.data,0,0)},v.delete2FA=function(e,t){for(var n=document.querySelectorAll(".data-"+t),r=0,a=n.length;r {{translate(node.title)}} {{node.title}} - {{translate(node.value)}} - {{node.value}} - {{translate(node.epoch)}} + {{translate(node.value)}} + {{node.value}} + {{translate(node.epoch)}} {{localeDate(node.epoch)}} -- GitLab