# Interface zu Apache MD5 Password use strict; use warnings; use Digest::MD5; use Carp; my $Magic = q/$1$/; # Magic string my $itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; ##################### Crypt Methods ####################################### sub to64 { my ($v, $n) = @_; my $ret = ''; while (--$n >= 0) { $ret .= substr($itoa64, $v & 0x3f, 1); $v >>= 6; } $ret; } sub apache_md5_crypt { # change the Magic string to match the one used by Apache $Magic = q/$apr1$/; unix_md5_crypt(@_); } sub unix_md5_crypt { my($pw, $salt) = @_; croak "No Password given" if not defined $pw; my $passwd = ''; if ( defined $salt ) { $salt =~ s/^\Q$Magic//; # Take care of the magic string if # if present. $salt =~ s/^(.*)\$.*$/$1/; # Salt can have up to 8 chars... $salt = substr($salt, 0, 8); } else { $salt = ''; # in case no salt was proffered $salt .= substr($itoa64,int(rand(64)+1),1) while length($salt) < 8; } my $ctx = new Digest::MD5; # Here we start the calculation $ctx->add($pw); # Original password... $ctx->add($Magic); # ...our magic string... $ctx->add($salt); # ...the salt... my ($final) = new Digest::MD5; $final->add($pw); $final->add($salt); $final->add($pw); $final = $final->digest; for (my $pl = length($pw); $pl > 0; $pl -= 16) { $ctx->add(substr($final, 0, $pl > 16 ? 16 : $pl)); } # Now the 'weird' xform for (my $i = length($pw); $i; $i >>= 1) { if ($i & 1) { $ctx->add(pack("C", 0)); } # This comes from the original version, # where a memset() is done to $final # before this loop. else { $ctx->add(substr($pw, 0, 1)); } } $final = $ctx->digest; # The following is supposed to make # things run slower. In perl, perhaps # it'll be *really* slow! for (my $i = 0; $i < 1000; $i++) { my $ctx1 = new Digest::MD5; if ($i & 1) { $ctx1->add($pw); } else { $ctx1->add(substr($final, 0, 16)); } if ($i % 3) { $ctx1->add($salt); } if ($i % 7) { $ctx1->add($pw); } if ($i & 1) { $ctx1->add(substr($final, 0, 16)); } else { $ctx1->add($pw); } $final = $ctx1->digest; } # Final xform $passwd = ''; $passwd .= to64(int(unpack("C", (substr($final, 0, 1))) << 16) | int(unpack("C", (substr($final, 6, 1))) << 8) | int(unpack("C", (substr($final, 12, 1)))), 4); $passwd .= to64(int(unpack("C", (substr($final, 1, 1))) << 16) | int(unpack("C", (substr($final, 7, 1))) << 8) | int(unpack("C", (substr($final, 13, 1)))), 4); $passwd .= to64(int(unpack("C", (substr($final, 2, 1))) << 16) | int(unpack("C", (substr($final, 8, 1))) << 8) | int(unpack("C", (substr($final, 14, 1)))), 4); $passwd .= to64(int(unpack("C", (substr($final, 3, 1))) << 16) | int(unpack("C", (substr($final, 9, 1))) << 8) | int(unpack("C", (substr($final, 15, 1)))), 4); $passwd .= to64(int(unpack("C", (substr($final, 4, 1))) << 16) | int(unpack("C", (substr($final, 10, 1))) << 8) | int(unpack("C", (substr($final, 5, 1)))), 4); $passwd .= to64(int(unpack("C", substr($final, 11, 1))), 2); $final = ''; return $Magic.$salt.q/$/.$passwd; } # get SALT from crypted password sub getsalt{ my $pass = shift; $pass =~ /^\$.*\$(.*)\$/; return $1; } ########################################################################### ########################################################################### # Return Value: # 1 wenn plain mit hashed übereinstimmt, hierzu wird beides übergeben # 0 wenn das nicht übereinstimmt # wenn nur plain übergeben wurde: RV ist das hashed Password sub htpasswd{ my $self = shift; my %in =( plain => '', hashed => '', @_); if($in{plain} && $in{hashed}){ my $salt = getsalt($in{hashed}); my $cmppass = apache_md5_crypt($in{plain}, $salt); # von Eingabe return $cmppass eq $in{hashed} ? 1 : 0; } elsif($in{plain}){ return apache_md5_crypt($in{plain}); } else{ croak "Unwise arguments"; } } sub _realmfile{ my $file = shift; local @ARGV = $file; my %passtab = (); while(my $zeile = <> ){ chomp $zeile; my($user,$hash) = split ":", $zeile; $passtab{$user} = $hash; } return \%passtab; } # find user in file sub checkpass{ my $self = shift; my %chk = ( user => '', pass => '', file => '', @_); my $passtab = _realmfile($chk{file}); foreach my $user(keys %$passtab){ if($user eq $chk{user}){ if( $self->htpasswd(plain => $chk{pass}, hashed => $passtab->{$user}) ){ return $user } else{return ''} } } return undef; } 1;######################################################################### #print main->checkpass(pass => 'pass', user => 'user', file => '/home/netsh100633/files/demo.realm'); #print main->htpasswd( plain => 'asdf')."\n"; # $apr1$cNTFE9qh$ifDhd5VfvJh1/0fLsaTPH0 #print main->htpasswd( plain => 'boo', hashed => '$apr1$Q0IgAQ2p$H4X10USnEt1URTH3Wk4fn.');