1#!/usr/pkg/bin/perl
2#
3# Sample password verifier for Heimdals external password
4# verifier, see the chapter "Password changing" in the the info
5# documentation for more information about the protocol used.
6#
7# Three checks
8#  1. Check that password is not the principal name
9#  2. Check that the password passes cracklib
10#  3. Check that password isn't repeated for this principal
11#
12# The repeat check must be last because some clients ask
13# twice when getting "no" back and thus the error message
14# would be wrong.
15#
16# Prereqs (example versions):
17#
18# * perl (5.8.5) http://www.perl.org/
19# * cracklib (2.8.5) http://sourceforge.net/projects/cracklib
20# * Crypt-Cracklib perlmodule (0.01) http://search.cpan.org/~daniel/
21#
22# Sample dictionaries:
23#     cracklib-words (1.1) http://sourceforge.net/projects/cracklib
24#     miscfiles (1.4.2) http://directory.fsf.org/miscfiles.html
25#
26# Configuration for krb5.conf or kdc.conf
27#
28#   [password_quality]
29#     	policies = builtin:external-check
30#     	external_program = <your-path>/check-cracklib.pl
31#
32# $Id$
33
34use strict;
35use Crypt::Cracklib;
36use Digest::MD5;
37
38# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
39my $database = '/usr/lib/cracklib_dict';
40my $historydb = '/var/heimdal/historydb';
41# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
42
43# seconds password reuse allowed (to catch retries from clients)
44my $reusetime = 60;
45
46my %params;
47
48sub check_basic
49{
50    my $principal = shift;
51    my $passwd = shift;
52
53    if ($principal eq $passwd) {
54	return "Principal name as password is not allowed";
55    }
56    return "ok";
57}
58
59sub check_repeat
60{
61    my $principal = shift;
62    my $passwd = shift;
63    my $result  = 'Do not reuse passwords';
64    my %DB;
65    my $md5context = new Digest::MD5;
66    my $timenow = scalar(time());
67
68    $md5context->reset();
69    $md5context->add($principal, ":", $passwd);
70
71    my $key=$md5context->hexdigest();
72
73    dbmopen(%DB,$historydb,0600) or die "Internal: Could not open $historydb";
74    if (!$DB{$key} || ($timenow - $DB{$key} < $reusetime)) {
75	$result = "ok";
76	$DB{$key}=$timenow;
77    }
78    dbmclose(%DB) or die "Internal: Could not close $historydb";
79    return $result;
80}
81
82sub badpassword
83{
84    my $reason = shift;
85    print "$reason\n";
86    exit 0
87}
88
89while (<STDIN>) {
90    last if /^end$/;
91    if (!/^([^:]+): (.+)$/) {
92	die "key value pair not correct: $_";
93    }
94    $params{$1} = $2;
95}
96
97die "missing principal" if (!defined $params{'principal'});
98die "missing password" if (!defined $params{'new-password'});
99
100my $reason;
101
102$reason = check_basic($params{'principal'}, $params{'new-password'});
103badpassword($reason) if ($reason ne "ok");
104
105$reason = fascist_check($params{'new-password'}, $database);
106badpassword($reason) if ($reason ne "ok");
107
108$reason = check_repeat($params{'principal'}, $params{'new-password'});
109badpassword($reason) if ($reason ne "ok");
110
111print "APPROVED\n";
112exit 0
113