File Coverage

blib/lib/Data/Password.pm
Criterion Covered Total %
statement 75 80 93.7
branch 36 50 72.0
condition 5 9 55.5
subroutine 10 10 100.0
pod 0 8 0.0
total 126 157 80.2


line stmt bran cond sub pod time code
1             package Data::Password;
2              
3             # Ariel Brosh (RIP), January 2002, for Raz Information Systems
4             # Oded S. Resnik, 3 April 2004, for Raz Information Systems
5              
6              
7              
8 3     3   80569 use strict;
  3         8  
  3         194  
9             require Exporter;
10 3         4673 use vars qw($DICTIONARY $FOLLOWING $GROUPS $MINLEN $MAXLEN $SKIPCHAR
11             $FOLLOWING_KEYBOARD @DICTIONARIES $BADCHARS
12 3     3   19 $VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
  3         7  
13              
14             @EXPORT_OK = qw($DICTIONARY $FOLLOWING $GROUPS $FOLLOWING_KEYBOARD $SKIPCHAR $BADCHARS
15             @DICTIONARIES $MINLEN $MAXLEN IsBadPassword IsBadPasswordForUNIX);
16             %EXPORT_TAGS = ('all' => [@EXPORT_OK]);
17             @ISA = qw(Exporter);
18              
19             $VERSION = '1.12';
20              
21             # Settings
22             $DICTIONARY = 5;
23             $FOLLOWING = 3;
24             $FOLLOWING_KEYBOARD = 1;
25             $GROUPS = 2;
26              
27             $MINLEN = 6;
28             $MAXLEN = 8;
29             $SKIPCHAR = 0;
30             $BADCHARS = '\0-\x1F\x7F';
31              
32             @DICTIONARIES = qw(/usr/dict/web2 /usr/dict/words /usr/share/dict/words /usr/share/dict/linux.words);
33              
34             sub OpenDictionary {
35 14     14 0 30 foreach my $sym (@DICTIONARIES) {
36 23 100       291 return $sym if -r $sym;
37             }
38 3         5 return;
39             }
40              
41             sub CheckDict {
42 14 50   14 0 28 return unless $DICTIONARY;
43 14         19 my $pass = shift;
44 14         22 my $dict = OpenDictionary();
45 14 100       34 return unless $dict;
46 11 50       260 open (DICT,"$dict") || return;
47 11         417 $pass = lc($pass);
48              
49 11         125 while (my $dict_line = ) {
50 20         27 chomp ($dict_line);
51 20 50       61 next if length($dict_line) < $DICTIONARY;
52 20         23 $dict_line = lc($dict_line);
53 20 100       101 if (index($pass,$dict_line)>-1) {
54 2         13 close(DICT);
55 2         8 return $dict_line;
56             }
57             }
58 9         58 close(DICT);
59 9         17 return;
60             }
61              
62             sub CheckSort {
63 20 50   20 0 40 return unless $FOLLOWING;
64 20         24 my $pass = shift;
65 20         33 foreach (1 .. 2) {
66 36         124 my @letters = split(//, $pass);
67 36         53 my $diffs;
68 36         43 my $last = shift @letters;
69 36         50 foreach (@letters) {
70 224         293 $diffs .= chr((ord($_) - ord($last) + 256 + 65) % 256);
71 224         248 $last = $_;
72             }
73 36         46 my $len = $FOLLOWING - 1;
74 36 100       277 return 1 if $diffs =~ /([\@AB])\1{$len}/;
75 30 50       54 return unless $FOLLOWING_KEYBOARD;
76              
77 30         45 my $mask = $pass;
78 30         45 $pass =~ tr/A-Z/a-z/;
79 30         41 $mask ^= $pass;
80 30         32 $pass =~ tr/qwertyuiopasdfghjklzxcvbnm/abcdefghijKLMNOPQRStuvwxyz/;
81 30         71 $pass ^= $mask;
82             }
83 14         51 return;
84             }
85              
86             sub CheckTypes {
87 24 50   24 0 63 return undef unless $GROUPS;
88 24         28 my $pass = shift;
89 24         48 my @groups = qw(a-z A-Z 0-9 ^A-Za-z0-9);
90 24         21 my $count;
91 24         40 foreach (@groups) {
92 96 100       975 $count++ if $pass =~ /[$_]/;
93             }
94 24         85 $count < $GROUPS;
95             }
96              
97             sub CheckCharset {
98 27     27 0 26 my $pass = shift;
99 27 100       51 return 0 if $SKIPCHAR;
100 26         214 $pass =~ /[$BADCHARS]/;
101             }
102              
103             sub CheckLength {
104 29     29 0 33 my $pass = shift;
105 29         36 my $len = length($pass);
106 29 50 33     148 return 1 if ($MINLEN && $len < $MINLEN);
107 29 100 100     113 return 1 if ($MAXLEN && $len > $MAXLEN);
108 27         70 return;
109             }
110              
111             sub IsBadPassword {
112 29     29 0 9420 my $pass = shift;
113 29 100       58 if (CheckLength($pass)) {
114 2 50 33     16 if ($MAXLEN && $MINLEN) {
    0          
115 2         10 return "Not between $MINLEN and $MAXLEN characters";
116             }
117 0         0 elsif (!$MAXLEN) { return "Not $MINLEN characters or greater"; }
118 0         0 else { return "Not less than or equal to $MAXLEN characters"; }
119             }
120 27 100       49 return "contains bad characters" if CheckCharset($pass);
121 24 100       50 return "contains less than $GROUPS character groups"
122             if CheckTypes($pass);
123 20 100       43 return "contains over $FOLLOWING leading characters in sequence"
124             if CheckSort($pass);
125 14         28 my $dict = CheckDict($pass);
126 14 100       38 return "contains the dictionary word '$dict'" if $dict;
127 12         21 return;
128             }
129              
130             sub IsBadPasswordForUNIX {
131 11     11 0 5028 my ($user, $pass) = @_;
132 11         30 my $reason = IsBadPassword($pass);
133 11 100       24 return $reason if $reason;
134 4         4 my $tuser = $user;
135 4         7 $tuser =~ s/[^a-zA-Z]//g;
136 4 50       17 return "is based on the username" if ($pass =~ /$tuser/i);
137              
138 4         168 my ($name,$passwd,$uid,$gid,
139             $quota,$comment,$gcos,$dir,$shell,$expire) = getpwnam($user);
140 4 50       20 return unless $comment;
141 0           foreach ($comment =~ /([A-Z]+)/ig) {
142 0 0         return "is based on the finger information" if ($pass =~ /$_/i);
143             }
144 0           return;
145             }
146              
147             1;
148             __END__