#!/usr/bin/perl -w

=head1 NAME

auth_cvm_unix_local - SMTP AUTH LOGIN module using 
Bruce Guenther's Credential Validation Module (CVM) 
    http://untroubled.org/cvm/

=head1 SYNOPSIS

In config/plugins:

  auth/auth_cvm_unix_local \
	cvm_socket /var/lib/cvm/cvm-unix-local.socket	\
	enable_smtp no \
	enable_ssmtp yes

=head1 BUGS

- Should probably handle auth-cram-md5 as well. However, this requires
access to the plain text password. We could store a separate database
of passwords purely for SMTP AUTH, for example as an optional 
SMTPAuthPassword property of an account in the esmith::AccountsDB;

=head1 DESCRIPTION

This plugin implements an authentication plugin using Bruce Guenther's
Credential Validation Module (http://untroubled.org/cvm).

=head1 AUTHOR

Copyright 2005 Gordon Rowell <gordonr@gormand.com.au>

This software is free software and may be distributed under the same
terms as qpsmtpd itself.

=head1 VERSION

Version $Id: auth_cvm_unix_local,v 1.1 2005/06/09 22:50:06 gordonr Exp gordonr $

=cut

use Socket;
use constant SMTP_PORT => getservbyname("smtp", "tcp") || 25;
use constant SSMTP_PORT => getservbyname("ssmtp", "tcp") || 465;

sub register 
{
    my ( $self, $qp, %arg ) = @_;

    unless ($arg{cvm_socket})
    {
        $self->log(LOGERROR, "authcvm - requires cvm_socket argument");
        return 0;
    }

    $self->{_enable_smtp} = $arg{enable_smtp} || 'no';
    $self->{_enable_ssmtp} = $arg{enable_ssmtp} || 'yes';

    my $port = $ENV{PORT} || SMTP_PORT;

    return 0 if ($port == SMTP_PORT and $self->{_enable_smtp} ne 'yes');
    return 0 if ($port == SSMTP_PORT and $self->{_enable_ssmtp} ne 'yes');

    if ($arg{cvm_socket} =~ /^([\w\/.-]+)$/)
    {
        $self->{_cvm_socket} = $1;
    }

    unless (-S $self->{_cvm_socket})
    {
	$self->log(LOGERROR, "authcvm - cvm_socket missing or not usable");
	return 0;
    }

    $self->register_hook("auth-plain", "authcvm_plain");
    $self->register_hook("auth-login", "authcvm_plain");
#    $self->register_hook("auth-cram-md5", "authcvm_hash");
}

sub authcvm_plain
{
    my ( $self, $transaction, $method, $user, $passClear, $passHash, $ticket ) =
      @_;

    $self->log(LOGINFO, "authcvm/$method authentication attempt for: $user");

    socket(SOCK, PF_UNIX, SOCK_STREAM, 0) 
        or return (DENY, "authcvm/$method");

    connect(SOCK, sockaddr_un($self->{_cvm_socket}))
        or return (DENY, "authcvm/$method");

    my $o = select(SOCK); $| = 1; select($o);

    my ($u, $host) = split(/\@/, $user);
    $host ||= "localhost";

    print SOCK "\001$u\000$host\000$passClear\000\000";

    shutdown SOCK, 1;

    my $ret = <SOCK>;
    my ($s) = unpack ("C", $ret);
    return (
	($s ? $s == 100 ? DENY : DECLINED
	    : OK), 
	"authcvm/$method");
}