#!/usr/bin/perl
#
# AuthDriver.pm:
# External authenticator for use with tpop3d's auth-other.
#
# Copyright (c) 2001 Chris Lightfoot. All rights reserved.
#
# $Id: AuthDriver.pm,v 1.7 2002/02/24 18:07:52 chris Exp $
#
# POD begins
=head1 NAME
TPOP3D::AuthDriver - library to interface to tpop3d's auth-other and auth-perl
=head1 SYNOPSIS
package FredAuthDriver;
use TPOP3D::AuthDriver;
@ISA = (TPOP3D::AuthDriver);
sub start($) {
# startup code
}
sub finish($) {
# shutdown code
}
sub pass($$) {
my ($self, $req) = @_;
if ($req->{user} eq "fred" and $req->{pass} eq "secret") {
# Success.
return { 'result' => 'YES',
'logmsg' => 'authenticated Fred',
'uid' => 'fred', # Fred's UID
'gid' => 'mail',
'mboxtype' => 'bsd',
'mailbox' => '/var/spool/mail/fred' };
} else {
# Failure.
return { 'result' => 'NO',
'logmsg' => 'authentication failed' };
}
}
1;
package main;
$auth = new FredAuthDriver;
# function for auth-perl compatibility
sub passauth ($) {
return $auth->pass($_[0]);
}
# run in auth-other mode
if (defined($ENV{TPOP3D_CONTEXT})
and $ENV{TPOP3D_CONTEXT eq 'auth_other') {
$auth->run();
exit 0;
}
1;
=head1 DESCRIPTION
The TPOP3D::AuthDriver object presents a generic interface to authentication
for tpop3d. Objects derived from it can implement specific authentication
strategies to customise the server. This is designed mainly for use with
virtual-domains configurations.
=head1 PUBLIC INTERFACE
=over 4
=cut
package TPOP3D::AuthDriver;
$VERSION = '0.2';
=item new
I<Class method.>
Creates a new authentication driver object.
=cut
sub new ($) {
my ($proto) = @_;
my $class = ref($proto) || $proto;
my $self = { foad => 0 };
bless($self, $class);
return $self;
}
=item run
I<Instance method.>
Start processing requests from tpop3d. This will install a handler for
SIGTERM; on receiving this signal, the method will return.
=cut
sub run ($) {
my $self = shift;
my ($buffer);
local %SIG;
$SIG{TERM} = sub { $self->{foad} = 1; };
$self->start();
$buffer = '';
do {
my $readfds = '';
vec($readfds, fileno(STDIN), 1) = 1;
if (select($readfds, undef, undef, 0.1) == 1) {
my $i = sysread(STDIN, $buffer, 4096, length($buffer));
if ($i <= 0) {
$self->{foad} = 1;
}
# Now see whether the first part of the buffer has the right
# structure:
if ($buffer =~ /^((?:(?:[^\0]+\0){2})+)\0/) {
my $packet = $1;
$buffer = substr($buffer, length($packet) + 1);
last unless ($self->process_packet($packet));
}
}
} while (!$self->{foad});
$self->finish();
}
# process_packet:
# Process a formatted packet of data, and dispatch it to the appropriate
# handler.
sub process_packet ($$) {
my ($self, $packet) = @_;
my %hash = split("\0", $packet);
$res = $self->apop(\%hash) if ($hash{method} eq 'APOP');
$res = $self->pass(\%hash) if ($hash{method} eq 'PASS');
$res = $self->onlogin(\%hash) if ($hash{method} eq 'ONLOGIN');
if (!defined($res)) {
# OK, we didn't handle it; perhaps we are chained to another handler?
$res = $self->{next}->process_request(\%hash) if (defined($self->{next}));
}
return $self->send_packet($res);
}
# send_packet:
# Return the results to the server, appropriately formatted.
sub send_packet ($$) {
my ($self, $res) = @_;
my $packet = join("\0", map { "$_\0$res->{$_}"; } keys %{$res}) . "\0\0";
return syswrite(STDOUT, "$packet", length($packet)) == length($packet);
}
=item start
I<Instance method.>
This method is called by run() before it starts processing requests. You
should override this to perform any initialisation steps your authentication
driver needs.
=cut
sub start ($) {
my $self = shift;
# do nothing
}
=item finish
I<Instance method.>
This method is called by run() when it ceases to process requests after
receipt of SIGTERM. You should override this to perform any shutdown steps
your authentication driver needs.
=cut
sub finish ($) {
my $self = shift;
# do nothing
}
=item apop REQUEST
I<Instance method.>
This method is called by run() when a request for APOP authentication is
received. REQUEST is a reference to a hash of the parameters supplied by the
server, including
timestamp
server's RFC1939 timestamp
user
client's supplied username
local_part
domain
local-part and domain derived from client's username;
may not be present
digest
client's supplied digest, in hex
clienthost
IP number of the client host
It should return a reference to a hash containing the following keys:
result
if authentication was successful, `YES'; otherwise `NO'
uid
username/uid with which to access mailspool
gid
groupname/gid with which to access mailspool
domain
(optional) domain in which the user has been authenticated
mailbox
(optional) location of mailbox
mboxtype
(optional) name of mailbox driver
logmsg
(optional) message to log
Note that if you do not supply a value for C<mailspool>, then the mailspool
name will be determined from the `mailbox:' and `auth-other-mailbox:'
tpop3d configuration directives; if you supply a value for C<mailbox>, but
not for C<mboxtype>, then the `default' mailbox type will be used; but this is
dependent on your installed version of tpop3d, and should not be relied upon.
You should override this to perform APOP authentication, if you want to use
it.
=cut
sub apop ($$) {
my ($self, $req) = @_;
return { 'result' => 'NO' }; # default
}
=item apopauth DIGEST, TIMESTAMP, PASSWORD
I<Class method.>
Returns true if the given plaintext PASSWORD and TIMESTAMP correspond to the
given DIGEST. This is a utility method designed to make it easier to write
APOP authenticators.
=cut
sub apopauth ($$$) {
my ($digest, $timestamp, $password) = @_;
if (lc($digest) eq lc(Digest::MD5::md5_hex($timestamp . $password))) {
return 1;
} else {
return 0;
}
}
=item pass REQUEST
I<Instance method.>
This method is called by run() when a request for USER/PASS authentication is
received. REQUEST is a reference to a hash of the parameters supplied by the
server, including
user
client's supplied username
local_part
domain
local-part and domain derived from client's username;
may not be present
pass
client's supplied password
clienthost
IP number of the client host
It should return a reference to a hash, as described for the C<apop> method
above. You should override this to perform USER/PASS authentication, if you
want to use it.
=cut
sub pass ($$) {
my ($self, $req) = @_;
return { 'result' => 'NO' }; # default
}
=item onlogin REQUEST
I<Instance method.>
This method is called by run() when a packet describing a successful login is
received. REQUEST is a reference to a hash of the parameters supplied by the
server, including
local_part
client's supplied username or local-part
domain
domain in which client is authenticated
clienthost
IP number of the client host
It should return either an empty hash reference, or one containing only the
logmsg member.
=cut
sub onlogin ($$) {
my ($self, $req) = @_;
return { };
}
1;
__END__
=back
=head1 COPYING
Copyright (c) 2001-2 Chris Lightfoot, <chris@ex-parrot.com>
F<http://www.ex-parrot.com/~chris/tpop3d/>
This program is free software; you can redistribute and/or modify it under the
same terms as Perl itself.
=head1 BUGS
None yet; please send me information when you find them.
=head1 VERSION
$Id: AuthDriver.pm,v 1.7 2002/02/24 18:07:52 chris Exp $
=head1 SEE ALSO
L<tpop3d(8)>, L<tpop3d.conf(5)>, F<RFC1939>, F<http://www.ex-parrot.com/~chris/tpop3d/>
syntax highlighted by Code2HTML, v. 0.9.1