| 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 | 
 | 
|---|
| 34 | use strict;
 | 
|---|
| 35 | use Crypt::Cracklib;
 | 
|---|
| 36 | use Digest::MD5;
 | 
|---|
| 37 | 
 | 
|---|
| 38 | # NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
 | 
|---|
| 39 | my $database = '/usr/lib/cracklib_dict';
 | 
|---|
| 40 | my $historydb = '/var/heimdal/historydb';
 | 
|---|
| 41 | # NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
 | 
|---|
| 42 | 
 | 
|---|
| 43 | # seconds password reuse allowed (to catch retries from clients)
 | 
|---|
| 44 | my $reusetime = 60; 
 | 
|---|
| 45 | 
 | 
|---|
| 46 | my %params;
 | 
|---|
| 47 | 
 | 
|---|
| 48 | sub 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 | 
 | 
|---|
| 59 | sub 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 | 
 | 
|---|
| 82 | sub badpassword
 | 
|---|
| 83 | {
 | 
|---|
| 84 |     my $reason = shift;
 | 
|---|
| 85 |     print "$reason\n";
 | 
|---|
| 86 |     exit 0
 | 
|---|
| 87 | }
 | 
|---|
| 88 | 
 | 
|---|
| 89 | while (<STDIN>) {
 | 
|---|
| 90 |     last if /^end$/;
 | 
|---|
| 91 |     if (!/^([^:]+): (.+)$/) {
 | 
|---|
| 92 |         die "key value pair not correct: $_";
 | 
|---|
| 93 |     }
 | 
|---|
| 94 |     $params{$1} = $2;
 | 
|---|
| 95 | }
 | 
|---|
| 96 | 
 | 
|---|
| 97 | die "missing principal" if (!defined $params{'principal'});
 | 
|---|
| 98 | die "missing password" if (!defined $params{'new-password'});
 | 
|---|
| 99 | 
 | 
|---|
| 100 | my $reason;
 | 
|---|
| 101 | 
 | 
|---|
| 102 | $reason = check_basic($params{'principal'}, $params{'new-password'});
 | 
|---|
| 103 | badpassword($reason) if ($reason ne "ok");
 | 
|---|
| 104 | 
 | 
|---|
| 105 | $reason = fascist_check($params{'new-password'}, $database);
 | 
|---|
| 106 | badpassword($reason) if ($reason ne "ok");
 | 
|---|
| 107 | 
 | 
|---|
| 108 | $reason = check_repeat($params{'principal'}, $params{'new-password'});
 | 
|---|
| 109 | badpassword($reason) if ($reason ne "ok");
 | 
|---|
| 110 | 
 | 
|---|
| 111 | print "APPROVED\n";
 | 
|---|
| 112 | exit 0
 | 
|---|