#!/usr/bin/perl

use strict;
use Authen::PAM;

&main;

sub prompt
{
	my $name = shift;
	print "$name: ";
	my $rv = <STDIN>;
	chomp($rv);
	return $rv;
}

sub main
{
	$| = 1;

	my $what = prompt "what to do (check or change)";

	&check_password_prompted  if ( $what eq "check" );
	&change_password_prompted if ( $what eq "change" );
}

######################

sub change_password_prompted
{
	my $user = prompt "user";
	my $pass = prompt "pass";

	my $rv = change_password($user, $pass);
	print "success\n";
}

sub change_password
{
	my $user = shift;
	my $pass = shift;

	my $error_msg;
	my $text_info;

	my $conv_func = sub {
		my @res;
		while ( @_ ) {
			my $msg_type = shift;
			my $msg = shift;

			my $ans;
			$ans = $user if ( $msg_type == PAM_PROMPT_ECHO_ON  );
			$ans = $pass if ( $msg_type == PAM_PROMPT_ECHO_OFF );
			$error_msg .= "$msg\n" if ( $msg_type == PAM_ERROR_MSG );
			$text_info .= "$msg\n" if ( $msg_type == PAM_TEXT_INFO );

			push @res, 0;
			push @res, $ans;
		}
		push @res, PAM_SUCCESS;
		return @res;
	};

	my $pamh = new Authen::PAM("passwd", $user, $conv_func);
	$pamh->pam_chauthtok == PAM_SUCCESS
		or die "failed to change auth ($error_msg) ($text_info)";
	undef $pamh;

	warn "error_msg = $error_msg\n" if ( $error_msg ne "" );
	warn "text_info = $text_info\n" if ( $error_msg ne "" );
}

######################

sub check_password_prompted
{
	my $user    = prompt "user";
	my $pass    = prompt "pass";
	my $service = prompt "service";

	my $auth = check_password($user, $pass, $service);

	print $auth ? "authentication okay\n" : "authentication bad\n";
}

sub check_password
{
	my $user = shift;
	my $pass = shift;
	my $service_name = shift;

	my $conv_func = sub {
		my @res;
		while ( @_ ) {
			my $msg_type = shift;
			my $msg = shift;

			my $ans;
			$ans = $user if ( $msg_type == PAM_PROMPT_ECHO_ON  );
			$ans = $pass if ( $msg_type == PAM_PROMPT_ECHO_OFF );

			push @res, 0;
			push @res, $ans;
		}
		push @res, PAM_SUCCESS;
		return @res;
	};

	my $pamh = Authen::PAM->new($service_name, $user, $conv_func);

	$pamh->pam_authenticate(PAM_DISALLOW_NULL_AUTHTOK) == PAM_SUCCESS
		or return 0;

	$pamh->pam_acct_mgmt(PAM_DISALLOW_NULL_AUTHTOK) == PAM_SUCCESS
		or return 0;

	return 1;
}


