package Lemonldap::NG::Portal::2F::Ext2F; use strict; use Mouse; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK PE_ERROR PE_BADOTP PE_FORMEMPTY PE_SENDRESPONSE ); our $VERSION = '2.0.10'; extends 'Lemonldap::NG::Portal::Main::SecondFactor'; # INITIALIZATION has prefix => ( is => 'rw', default => 'ext' ); has random => ( is => 'rw' ); sub init { my ($self) = @_; unless ( $self->conf->{ext2fCodeActivation} ) { foreach (qw(ext2FSendCommand ext2FValidateCommand)) { unless ( $self->conf->{$_} ) { $self->error("Missing $_ parameter, aborting"); return 0; } } $self->prefix( $self->conf->{sfPrefix} ) if ( $self->conf->{sfPrefix} ); return $self->SUPER::init(); } if ( $self->conf->{ext2fCodeActivation} ) { unless ( $self->conf->{ext2FSendCommand} ) { $self->error("Missing 'ext2FSendCommand' parameter, aborting"); return 0; } $self->random( Lemonldap::NG::Common::Crypto::srandom() ); $self->prefix( $self->conf->{sfPrefix} ) if ( $self->conf->{sfPrefix} ); return $self->SUPER::init(); } return 0; } # RUNNING METHODS sub run { my ( $self, $req, $token ) = @_; my $checkLogins = $req->param('checkLogins'); $self->logger->debug("Ext2F: checkLogins set") if $checkLogins; my $stayconnected = $req->param('stayconnected'); $self->logger->debug("Ext2F: stayconnected set") if $stayconnected; # Generate Code to send my $code; if ( $self->conf->{ext2fCodeActivation} ) { $code = $self->random->randregex( $self->conf->{ext2fCodeActivation} ); $self->logger->debug("Generated ext2f code : $code"); $self->ott->updateToken( $token, __ext2fcode => $code ); } # Prepare command and launch it $self->logger->debug( 'Launching "Send" external 2F command -> ' . $self->conf->{ext2FSendCommand} ); if ( my $c = $self->launch( $req->sessionInfo, $self->conf->{ext2FSendCommand}, $code ) ) { $self->logger->error("External send command failed (code $c)"); return PE_ERROR; } # Prepare form my $tmp = $self->p->sendHtml( $req, 'ext2fcheck', params => { MAIN_LOGO => $self->conf->{portalMainLogo}, SKIN => $self->p->getSkin($req), TOKEN => $token, PREFIX => $self->prefix, TARGET => '/' . $self->prefix . '2fcheck?skin=' . $self->p->getSkin($req), CHECKLOGINS => $checkLogins, STAYCONNECTED => $stayconnected } ); $self->logger->debug("Prepare external 2F verification"); $req->response($tmp); return PE_SENDRESPONSE; } sub verify { my ( $self, $req, $session ) = @_; my $usercode; unless ( $usercode = $req->param('code') ) { $self->userLogger->error('External 2F: no code found'); return PE_FORMEMPTY; } unless ( $self->conf->{ext2fCodeActivation} ) { # Prepare command and launch it $self->logger->debug( 'Launching "Validate" external 2F command -> ' . $self->conf->{ext2FValidateCommand} ); $self->logger->debug(" code -> $usercode"); if ( my $c = $self->launch( $session, $self->conf->{ext2FValidateCommand}, $usercode ) ) { $self->userLogger->warn( 'Second factor failed for ' . $session->{ $self->conf->{whatToTrace} } ); $self->logger->error("External verify command failed (code $c)"); return PE_BADOTP; } return PE_OK; } my $savedcode = $session->{__ext2fcode}; unless ($savedcode) { $self->logger->error( 'Unable to find generated 2F code in token session'); return PE_ERROR; } $self->logger->debug("Verifying Ext 2F code: $usercode VS $savedcode"); return PE_OK if ( $usercode eq $savedcode ); $self->userLogger->warn( 'Second factor failed for ' . $session->{ $self->conf->{whatToTrace} } ); return PE_BADOTP; } # system() is used with an array to avoid shell injection sub launch { my ( $self, $session, $command, $code ) = @_; my @args; foreach ( split( /\s+/, $command ) ) { if ( defined $code ) { s#\$code\b#$code#g; } s#\$(\w+)#$session->{$1} // ''#ge; push @args, $_; } $self->logger->debug( "Executing command: " . join( " ", @args ) ); return system @args; } 1;