From 6b005a5bacf83e434bf7b62e7440bb87e4a1aed1 Mon Sep 17 00:00:00 2001 From: Michael Howe Date: Mon, 31 May 2021 20:12:24 +0100 Subject: [PATCH] Fudge things by including Password::My::KerberosAuth too This does password changing Should probably name the package better, but that can go on the todo list... --- debian/changelog | 3 +- .../NG/Portal/Password/My/KerberosAuth.pm | 158 ++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 lib/Lemonldap/NG/Portal/Password/My/KerberosAuth.pm diff --git a/debian/changelog b/debian/changelog index 4ac4756..f5d9312 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,8 @@ -liblemonldap-ng-portal-auth-my-kerberosauth-perl (1.0~test.1) unstable; urgency=medium +liblemonldap-ng-portal-auth-my-kerberosauth-perl (1.0~test.2) unstable; urgency=medium * Initial Release. * Rename because there are collisions with the Kerberos name, so use KerberosAuth instead + * Include Password::My::KerberosAuth too, for now -- Michael Howe Tue, 19 May 2020 16:13:11 +0100 diff --git a/lib/Lemonldap/NG/Portal/Password/My/KerberosAuth.pm b/lib/Lemonldap/NG/Portal/Password/My/KerberosAuth.pm new file mode 100644 index 0000000..9094d7b --- /dev/null +++ b/lib/Lemonldap/NG/Portal/Password/My/KerberosAuth.pm @@ -0,0 +1,158 @@ +package Lemonldap::NG::Portal::Password::My::KerberosAuth; + +use strict; +use Mouse; +use Expect; +use Lemonldap::NG::Portal::Main::Constants qw( + PE_OK + PE_ERROR + PE_BADOLDPASSWORD + PE_PASSWORD_OK + PE_PASSWORD_MISMATCH + PE_PP_PASSWORD_TOO_SHORT + PE_PP_NOT_ALLOWED_CHARACTER + PE_PP_NOT_ALLOWED_CHARACTERS + PE_PP_MUST_SUPPLY_OLD_PASSWORD + PE_PP_INSUFFICIENT_PASSWORD_QUALITY + PE_PP_PASSWORD_IN_HISTORY +); + +extends 'Lemonldap::NG::Portal::Password::Base'; + +our $VERSION = '0.1.0'; + +sub init { + my ($self) = @_; + return $self->SUPER::init; +} + +sub confirm { + return 1; +} + +sub modifyPassword { + my ( $self, $req, $pwd ) = @_; + my $kpasswd = "/usr/bin/kpasswd"; + + my $exp = new Expect; + $exp->raw_pty(1); + + $exp->spawn($kpasswd, $req->{user}); + my $r1 = $exp->expect(5, "Password for " . $req->{user}); + unless( $r1 == 1 ){ + $exp->hard_close(); + $self->logger->error("kpasswd did not prompt for credentials for " . $req->{user}); + return PE_ERROR; + } + + $exp->send($req->data->{oldpassword} . "\n"); + my @r2 = $exp->expect(5, "Enter new password: ", "Preauthentication failed getting initial ticket"); + if( $r2[0] != 1 ){ + $exp->hard_close(); + if( $r2[0] == 2 ){ + $self->logger->error("kpasswd failed for " . $req->{user} . ": incorrect old password" ); + return PE_BADOLDPASSWORD; + } else { + $self->logger->error("kpasswd failed after prompt for old password for " . $req->{user} . ": " . join(' | ', @r2[0,2-4]) ); + return PE_ERROR; + } + } + $exp->send($pwd . "\n"); + my @r3 = $exp->expect(5, "Enter it again: "); + unless ( @r3[0] == 1 ){ + $exp->hard_close(); + $self->logger->error("kpasswd failed for " . $req->{user} . ": " . join(' | ', @r3[0,2-4]) ); + # TODO: most appropriate error? + return PE_ERROR; + } + $exp->send($pwd . "\n"); + my @r4 = $exp->expect(5, "Password changed", "Password change rejected: "); + unless( $r4[0] == 1 ){ + $exp->hard_close(); + $self->logger->error("kpasswd password change failed for " . $req->{user} . ": " . join(' | ', @r4[0,2-4]) ); + if( $r4[4] =~ m{New password was used previously. Please choose a different password} ){ + return PE_PP_PASSWORD_IN_HISTORY; + } else { + return PE_ERROR; + } + } + + $exp->soft_close(); + $self->logger->info("kpassword successful password change for " . $req->{user}); + + # if we get this far we've changed the password successfully + return PE_PASSWORD_OK; +} + +# Override the checkPasswordQuality function from Password::Base because it has +# a brain-dead 'special characters' policy. +sub checkPasswordQuality { + my ( $self, $password ) = @_; + + # Min size + if ( $self->conf->{passwordPolicyMinSize} + and length($password) < $self->conf->{passwordPolicyMinSize} ) + { + $self->logger->error("Password too short"); + return PE_PP_PASSWORD_TOO_SHORT; + } + + # Min lower + if ( $self->conf->{passwordPolicyMinLower} ) { + my $lower = 0; + $lower++ while ( $password =~ m/\p{lowercase}/g ); + if ( $lower < $self->conf->{passwordPolicyMinLower} ) { + $self->logger->error("Password has not enough lower characters"); + return PE_PP_INSUFFICIENT_PASSWORD_QUALITY; + } + } + + # Min upper + if ( $self->conf->{passwordPolicyMinUpper} ) { + my $upper = 0; + $upper++ while ( $password =~ m/\p{uppercase}/g ); + if ( $upper < $self->conf->{passwordPolicyMinUpper} ) { + $self->logger->error("Password has not enough upper characters"); + return PE_PP_INSUFFICIENT_PASSWORD_QUALITY; + } + } + + # Min digit + if ( $self->conf->{passwordPolicyMinDigit} ) { + my $digit = 0; + $digit++ while ( $password =~ m/\d/g ); + if ( $digit < $self->conf->{passwordPolicyMinDigit} ) { + $self->logger->error("Password has not enough digit characters"); + return PE_PP_INSUFFICIENT_PASSWORD_QUALITY; + } + } + + ## Special characters policy + my $speChars = $self->conf->{passwordPolicySpecialChar}; + $speChars =~ s/\s+//g; + + # Min special characters + if ( $self->conf->{passwordPolicyMinSpeChar} && $speChars ) { + my $spe = 0; + my $test = $password; + $spe = $test =~ s/[\Q$speChars\E]//g; + if ( $spe < $self->conf->{passwordPolicyMinSpeChar} ) { + $self->logger->error("Password has not enough special characters"); + return PE_PP_INSUFFICIENT_PASSWORD_QUALITY; + } + } + +# # Fobidden special characters +# $password =~ s/[\Q$speChars\E\w]//g; +# if ($password) { +# $self->logger->error( 'Password contains ' +# . length($password) +# . " forbidden character(s): $password" ); +# return +# length($password) > 1 +# ? PE_PP_NOT_ALLOWED_CHARACTERS +# : PE_PP_NOT_ALLOWED_CHARACTER; +# } + + return PE_OK; +} -- 2.39.5