1178825Sdfr#!/usr/pkg/bin/perl
2178825Sdfr#
3178825Sdfr# Sample password verifier for Heimdals external password
4178825Sdfr# verifier, see the chapter "Password changing" in the the info
5178825Sdfr# documentation for more information about the protocol used.
6178825Sdfr#
7178825Sdfr# Three checks
8178825Sdfr#  1. Check that password is not the principal name
9178825Sdfr#  2. Check that the password passes cracklib
10178825Sdfr#  3. Check that password isn't repeated for this principal
11178825Sdfr#
12178825Sdfr# The repeat check must be last because some clients ask
13178825Sdfr# twice when getting "no" back and thus the error message
14178825Sdfr# would be wrong.
15178825Sdfr#
16178825Sdfr# Prereqs (example versions):
17178825Sdfr#
18178825Sdfr# * perl (5.8.5) http://www.perl.org/
19178825Sdfr# * cracklib (2.8.5) http://sourceforge.net/projects/cracklib
20178825Sdfr# * Crypt-Cracklib perlmodule (0.01) http://search.cpan.org/~daniel/
21178825Sdfr#
22178825Sdfr# Sample dictionaries:
23178825Sdfr#     cracklib-words (1.1) http://sourceforge.net/projects/cracklib
24178825Sdfr#     miscfiles (1.4.2) http://directory.fsf.org/miscfiles.html
25178825Sdfr#
26178825Sdfr# Configuration for krb5.conf or kdc.conf
27178825Sdfr#
28178825Sdfr#   [password_quality]
29178825Sdfr#     	policies = builtin:external-check
30178825Sdfr#     	external_program = <your-path>/check-cracklib.pl
31178825Sdfr#
32233294Sstas# $Id$
33178825Sdfr
34178825Sdfruse strict;
35178825Sdfruse Crypt::Cracklib;
36178825Sdfruse Digest::MD5;
37178825Sdfr
38178825Sdfr# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
39178825Sdfrmy $database = '/usr/lib/cracklib_dict';
40178825Sdfrmy $historydb = '/var/heimdal/historydb';
41178825Sdfr# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
42178825Sdfr
43233294Sstas# seconds password reuse allowed (to catch retries from clients)
44233294Sstasmy $reusetime = 60;
45233294Sstas
46178825Sdfrmy %params;
47178825Sdfr
48178825Sdfrsub check_basic
49178825Sdfr{
50178825Sdfr    my $principal = shift;
51178825Sdfr    my $passwd = shift;
52178825Sdfr
53178825Sdfr    if ($principal eq $passwd) {
54178825Sdfr	return "Principal name as password is not allowed";
55178825Sdfr    }
56178825Sdfr    return "ok";
57178825Sdfr}
58178825Sdfr
59178825Sdfrsub check_repeat
60178825Sdfr{
61178825Sdfr    my $principal = shift;
62178825Sdfr    my $passwd = shift;
63178825Sdfr    my $result  = 'Do not reuse passwords';
64178825Sdfr    my %DB;
65178825Sdfr    my $md5context = new Digest::MD5;
66233294Sstas    my $timenow = scalar(time());
67178825Sdfr
68178825Sdfr    $md5context->reset();
69178825Sdfr    $md5context->add($principal, ":", $passwd);
70178825Sdfr
71178825Sdfr    my $key=$md5context->hexdigest();
72178825Sdfr
73178825Sdfr    dbmopen(%DB,$historydb,0600) or die "Internal: Could not open $historydb";
74233294Sstas    if (!$DB{$key} || ($timenow - $DB{$key} < $reusetime)) {
75233294Sstas	$result = "ok";
76233294Sstas	$DB{$key}=$timenow;
77233294Sstas    }
78178825Sdfr    dbmclose(%DB) or die "Internal: Could not close $historydb";
79178825Sdfr    return $result;
80178825Sdfr}
81178825Sdfr
82178825Sdfrsub badpassword
83178825Sdfr{
84178825Sdfr    my $reason = shift;
85178825Sdfr    print "$reason\n";
86178825Sdfr    exit 0
87178825Sdfr}
88178825Sdfr
89233294Sstaswhile (<STDIN>) {
90178825Sdfr    last if /^end$/;
91178825Sdfr    if (!/^([^:]+): (.+)$/) {
92178825Sdfr	die "key value pair not correct: $_";
93178825Sdfr    }
94178825Sdfr    $params{$1} = $2;
95178825Sdfr}
96178825Sdfr
97178825Sdfrdie "missing principal" if (!defined $params{'principal'});
98178825Sdfrdie "missing password" if (!defined $params{'new-password'});
99178825Sdfr
100178825Sdfrmy $reason;
101178825Sdfr
102178825Sdfr$reason = check_basic($params{'principal'}, $params{'new-password'});
103178825Sdfrbadpassword($reason) if ($reason ne "ok");
104178825Sdfr
105178825Sdfr$reason = fascist_check($params{'new-password'}, $database);
106178825Sdfrbadpassword($reason) if ($reason ne "ok");
107178825Sdfr
108178825Sdfr$reason = check_repeat($params{'principal'}, $params{'new-password'});
109178825Sdfrbadpassword($reason) if ($reason ne "ok");
110178825Sdfr
111178825Sdfrprint "APPROVED\n";
112178825Sdfrexit 0
113