Jez Hancock wrote:
> Hi all,
>
> This is a summary of one solution to the subject problem which was put
> forward by Kirill Miazine and hacked at to do MD5 authentication
> against the system password file /etc/master.passwd on FreeBSD by.
>
> The following perl code uses the Net::Server module to fork a
> specified number of server processes (similar in manner to the
> mechanism employed by apache webserver), each process listening for
> incoming authentication traffic on a unix domain socket.
>
> Upon receiving an authentication request on a listening socket, the
> server does some minimal data validation checking on the submitted
> user/password pair.
>
> The server then retrieves the pwd hash from /etc/master.passwd based
> on the 'username' key submitted and then hashes the submitted
> 'password' string using the salt obtained from the pwd hash. If the
> resulting hash matches the pwd hash, the user is authenticated.
>
> The script has to be suid root with suidperl unfortunately since it
> has to read /etc/master.passwd. However this entails that the domain
This is not true. You just start that script as root and it will have
the neccessary permissions.
> sockets created by the server are not readable by the exim user
> (unless you like running exim as root:) It's conceivable to have the
This is not true either ;). Just say umask(0000) before you say
__PACKAGE__->run() the socket file is created and the socket will be
created in such way that everybody will be able to speak to it.
> server not suid and instead have it call a suid root helper that makes
> the lookup on the db, but to be honest I'm happy now I've got pwcheck
Huh?
> back and won't go to the effort.
>
> The code is here anyway in case anyone in future is interested:
I've some comments, please read on.
> -----------------------------------------------------------------------------
> #!/usr/bin/perl
>
> package EximAuth;
>
> use strict;
> use vars qw(@ISA);
> use Crypt::PasswdMD5;
> use Net::Server::PreFork;
> @ISA = qw(Net::Server::PreFork);
>
> sub process_request {
> my $self = shift;
> my $sock = $self->{'server'}->{'client'};
> chomp(my $username = $sock->getline());
> chomp(my $password = $sock->getline());
>
> # some minimal data validation:
> !$username ||
> !$password ||
> length($username) >16 ||
> length($password) >255 ||
> $username=~/[^0-9A-Za-z]/ ||
> $password=~/[^0-9A-Za-z]/ &&
Weak passwords, weak passwords... I like to use some funny characters in
passwords myself...
> $sock->print("no");
^^^^^^^^^^^^^^^^^^
The above line really should be
return $sock->print("no");
To be honest, your data validation routine sucks a lot. Sorry, but it
does that. It does not do what you think it does. "no" will be output if
and only if $password =~ /[^0-9A-Za-z]/. Please read "man perop" - that
will help. Hint: you'll see how much better your "validator" works if
you replace "&&" with "and".
> # get the line from the pwd db based on $username:
> my (undef,$sys_password) = getpwnam($username);
>
> # get the salt:
> my ($salt) = $sys_password =~ /\$.*\$(.*)\$/;
>
> # get our hash:
> my $hash=unix_md5_crypt($password, $salt);
>
> $sock->print($hash eq $sys_password ? 'yes' : 'no');
> # for debug output:
> # $sock->print("username: $username submitted password: "
> # ."$password salt: $salt myhash: $hash syshash: $sys_password\n");
> }
>
> __PACKAGE__->run(
> proto => 'unix',
> port => '/var/spool/exim/auth/auth.sock',
> min_servers => 5,
> min_spare_servers => 5,
> max_spare_servers => 10,
> max_servers => 15,
> max_requests => 1000,
> log_level => 4,
You don't need such a high log_level. Set it to 0.
> );
--
Kirill Miazine, Stud. Jur.
Faculty of Law, University of Oslo