2018-04-12 14:20:28 +02:00
|
|
|
# Yubikey second factor authentication
|
|
|
|
#
|
|
|
|
# This plugin handle authentications to ask Yubikey second factor for users that
|
|
|
|
# have registered their Yubikey
|
2018-03-20 18:19:53 +01:00
|
|
|
package Lemonldap::NG::Portal::2F::Yubikey;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Mouse;
|
2018-04-06 00:10:41 +02:00
|
|
|
use JSON qw(from_json to_json);
|
2018-03-20 18:19:53 +01:00
|
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
2018-04-12 14:20:28 +02:00
|
|
|
PE_ERROR
|
2018-03-20 18:19:53 +01:00
|
|
|
PE_BADCREDENTIALS
|
|
|
|
PE_FORMEMPTY
|
|
|
|
PE_OK
|
|
|
|
PE_SENDRESPONSE
|
|
|
|
);
|
|
|
|
|
|
|
|
our $VERSION = '2.0.0';
|
|
|
|
|
|
|
|
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
|
|
|
|
|
|
|
|
# INITIALIZATION
|
|
|
|
|
|
|
|
has prefix => ( is => 'ro', default => 'yubikey' );
|
|
|
|
|
2018-04-07 13:22:06 +02:00
|
|
|
has logo => ( is => 'rw', default => 'yubikey.png' );
|
2018-03-20 18:19:53 +01:00
|
|
|
|
|
|
|
has yubi => ( is => 'rw' );
|
|
|
|
|
|
|
|
sub init {
|
|
|
|
my ($self) = @_;
|
|
|
|
eval { require Auth::Yubikey_WebClient };
|
|
|
|
if ($@) {
|
|
|
|
$self->logger->error($@);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-09-02 17:31:58 +02:00
|
|
|
|
2018-08-18 23:15:38 +02:00
|
|
|
# If self registration is enabled and "activation" is just set to
|
|
|
|
# "enabled", replace the rule to detect if user has registered its key
|
2018-03-20 18:19:53 +01:00
|
|
|
if ( $self->conf->{yubikey2fSelfRegistration}
|
|
|
|
and $self->conf->{yubikey2fActivation} eq '1' )
|
|
|
|
{
|
2018-04-10 16:15:14 +02:00
|
|
|
$self->conf->{yubikey2fActivation} =
|
2018-08-21 17:37:14 +02:00
|
|
|
'$_2fDevices && $_2fDevices =~ /"type":\s*"UBK"/s';
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
unless ($self->conf->{yubikey2fClientID}
|
|
|
|
and $self->conf->{yubikey2fSecretKey} )
|
|
|
|
{
|
2018-03-26 10:15:37 +02:00
|
|
|
$self->error('Missing mandatory parameters (Client ID and secret key)');
|
2018-03-20 18:19:53 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2018-04-06 00:10:41 +02:00
|
|
|
|
2018-03-20 18:19:53 +01:00
|
|
|
$self->yubi(
|
2019-02-07 09:27:56 +01:00
|
|
|
Auth::Yubikey_WebClient->new( {
|
2018-03-20 18:19:53 +01:00
|
|
|
id => $self->conf->{yubikey2fClientID},
|
|
|
|
api => $self->conf->{yubikey2fSecretKey},
|
|
|
|
nonce => $self->conf->{yubikey2fNonce},
|
|
|
|
url => $self->conf->{yubikey2fUrl}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
);
|
|
|
|
return $self->SUPER::init();
|
|
|
|
}
|
|
|
|
|
|
|
|
sub run {
|
2018-04-12 14:20:28 +02:00
|
|
|
my ( $self, $req, $token, $_2fDevices ) = @_;
|
2018-06-21 21:35:16 +02:00
|
|
|
|
2018-06-12 22:45:55 +02:00
|
|
|
my $checkLogins = $req->param('checkLogins');
|
|
|
|
$self->logger->debug("Yubikey checkLogins set") if ($checkLogins);
|
2018-06-21 21:35:16 +02:00
|
|
|
|
2018-04-12 14:20:28 +02:00
|
|
|
my $yubikey = 0;
|
|
|
|
if ( $req->{sessionInfo}->{_2fDevices} ) {
|
|
|
|
$self->logger->debug("Loading 2F Devices ...");
|
2018-04-06 00:10:41 +02:00
|
|
|
|
2018-04-12 14:20:28 +02:00
|
|
|
# Read existing 2FDevices
|
|
|
|
$_2fDevices = eval {
|
|
|
|
from_json( $req->{sessionInfo}->{_2fDevices},
|
|
|
|
{ allow_nonref => 1 } );
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->logger->error("Bad encoding in _2fDevices: $@");
|
|
|
|
return PE_ERROR;
|
|
|
|
}
|
|
|
|
$self->logger->debug("2F Device(s) found");
|
|
|
|
|
|
|
|
foreach (@$_2fDevices) {
|
|
|
|
$self->logger->debug("Reading Yubikey ...");
|
|
|
|
if ( $_->{type} eq 'UBK' ) {
|
|
|
|
$yubikey = $_->{_yubikey};
|
|
|
|
last;
|
|
|
|
}
|
2018-04-06 00:10:41 +02:00
|
|
|
}
|
|
|
|
}
|
2018-03-20 18:19:53 +01:00
|
|
|
|
2018-04-06 00:10:41 +02:00
|
|
|
unless ($yubikey) {
|
2018-03-20 18:19:53 +01:00
|
|
|
$self->userLogger->warn( 'User '
|
|
|
|
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
|
|
|
. ' has no Yubikey registered' );
|
|
|
|
return PE_BADCREDENTIALS;
|
|
|
|
}
|
2018-04-06 00:10:41 +02:00
|
|
|
$self->logger->debug("Found Yubikey : $yubikey");
|
2018-03-20 18:19:53 +01:00
|
|
|
|
|
|
|
# Prepare form
|
|
|
|
my $tmp = $self->p->sendHtml(
|
|
|
|
$req,
|
|
|
|
'ext2fcheck',
|
|
|
|
params => {
|
2018-10-12 19:41:13 +02:00
|
|
|
MAIN_LOGO => $self->conf->{portalMainLogo},
|
2019-06-28 15:56:57 +02:00
|
|
|
SKIN => $self->p->getSkin($req),
|
2018-06-21 21:35:16 +02:00
|
|
|
TOKEN => $token,
|
|
|
|
TARGET => '/yubikey2fcheck',
|
|
|
|
INPUTLOGO => 'yubikey.png',
|
|
|
|
LEGEND => 'clickOnYubikey',
|
2018-06-12 22:45:55 +02:00
|
|
|
CHECKLOGINS => $checkLogins
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
$self->logger->debug("Display Yubikey form");
|
|
|
|
|
|
|
|
$req->response($tmp);
|
|
|
|
return PE_SENDRESPONSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub verify {
|
|
|
|
my ( $self, $req, $session ) = @_;
|
|
|
|
my $code;
|
|
|
|
unless ( $code = $req->param('code') ) {
|
|
|
|
$self->userLogger->error('Yubikey 2F: no code');
|
|
|
|
return PE_FORMEMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Verify OTP
|
2018-04-06 00:10:41 +02:00
|
|
|
my $yubikey = 0;
|
2018-04-07 13:22:06 +02:00
|
|
|
my $_2fDevices = eval {
|
2018-04-06 00:10:41 +02:00
|
|
|
$self->logger->debug("Looking for 2F Devices ...");
|
2018-04-07 13:22:06 +02:00
|
|
|
from_json( $session->{_2fDevices}, { allow_nonref => 1 } );
|
2018-04-06 00:10:41 +02:00
|
|
|
};
|
|
|
|
|
2018-04-07 13:22:06 +02:00
|
|
|
foreach (@$_2fDevices) {
|
2018-04-06 00:10:41 +02:00
|
|
|
$self->logger->debug("Reading Yubikey ...");
|
|
|
|
if ( $_->{type} eq 'UBK' ) {
|
|
|
|
$yubikey = $_->{_yubikey};
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-20 18:19:53 +01:00
|
|
|
if (
|
2018-04-06 00:10:41 +02:00
|
|
|
index( $yubikey,
|
2018-03-26 10:15:37 +02:00
|
|
|
substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ) ) == -1
|
2018-03-20 18:19:53 +01:00
|
|
|
)
|
|
|
|
{
|
|
|
|
$self->userLogger->warn('Yubikey not registered');
|
2018-11-06 09:15:59 +01:00
|
|
|
return PE_BADCREDENTIALS;
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
if ( $self->yubi->otp($code) ne 'OK' ) {
|
|
|
|
$self->userLogger->warn('Yubikey verification failed');
|
2018-11-06 09:15:59 +01:00
|
|
|
return PE_BADCREDENTIALS;
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
PE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
1
|