Now it does work. However, the SMTP server wont send its queue/OK message until the hashcash generation is done, which means that client will timeout if hashcash generation takes too long. Any way to solve that? I wish hashcash generation start just when the mail is ready for dispatch, when the mail is queued, after client has got its "OK" reply. Then it wont matter if hashcash generation takes a hour, it will still not timeout any clients.

Should however try with hashcash generation after each RCPT to, and then pushing the headers when eom. Then it wont timeout since each RCPT TO will only incur one single hashcash generation.
Maybe that solves the timeout issue.

Here is the full script (runs as root as a init.d daemon):
(And no, relying on the domain isnt unsafe here, since I have rules that prevent usage of the "sebbe.eu" domain as "MAIL FROM" if you isnt inside permit_mynetworks, regardless of relaying or sending to a local mailbox)

----- BEGIN SCRIPT -----
#!/usr/local/bin/perl

use Sendmail::PMilter ':all';
use Proc::Daemon;
Proc::Daemon::Init;

$milter = Sendmail::PMilter->new();
$milter->setconn('inet:8991');
$milter->register('hcmilter', { eom => \&eom_callback, envrcpt => \&mto_callback}, SMFIF_ADDHDRS);
$milter->set_dispatcher( Sendmail::PMilter::postfork_dispatcher() );
$milter->main();

sub eom_callback {
$ctx = shift;
$senderunsafe = $ctx->getsymval('{mail_addr}');
($suser, $sdomain) = split("\@", $senderunsafe);
$suser =~ s/[^A-Z0-9._%+-]*//sgi;
$sdomain =~ s/[^A-Z0-9.-]*//sgi;
if ((length($suser) > 0)&&($sdomain eq "sebbe.eu")) {
$recipientlist = $ctx->getpriv;
@allrecipients = split("=", $recipientlist);
$cmdprepare = "/usr/bin/hashcash -mqb 26";
foreach $rsafe (@allrecipients) {
$cmdprepare = $cmdprepare . " " . $rsafe;
}
$output = `$cmdprepare`;
@hashcashs = split(/\n/s,$output);
foreach $hc (@hashcashs) {
if (length($hc) > 0) {
$ctx->addheader("X-Hashcash",$hc);
}
}
}
return(SMFIS_CONTINUE);
}
sub mto_callback {
$ctx = shift;
if ($rdata = $ctx->getpriv) {
@recipients = split("=", $rdata);
$recipientunsafe = $ctx->getsymval('{rcpt_addr}');
#Recipientunsafe contains POTENTIALLY UNSAFE EMAIL.
#Dont feed this into a system command!
#Recipientsafe does contain a completely safe, filtered email.
#Just feed it into system() if u want. Or a raw root
#shell.
($ruser, $rdomain) = split("\@", $recipientunsafe);
$ruser =~ s/[^A-Z0-9._%+-]*//sgi;
$rdomain =~ s/[^A-Z0-9.-]*//sgi;
if ((length($ruser) > 0)&&(length($rdomain) > 0)) {
$recipientsafe = $ruser . "\@". $rdomain;
push(@recipients, $recipientsafe);
}
$storedata = join("=", @recipients);
$ctx->setpriv($storedata);
}
else
{
$recipientunsafe = $ctx->getsymval('{rcpt_addr}');
($ruser, $rdomain) = split("\@", $recipientunsafe);
$ruser =~ s/[^A-Z0-9._%+-]*//sgi;
$rdomain =~ s/[^A-Z0-9.-]*//sgi;
if ((length($ruser) > 0)&&(length($rdomain) > 0)) {
$recipientsafe = $ruser . "\@". $rdomain;
$ctx->setpriv($recipientsafe);
}
}
return(SMFIS_CONTINUE);
}
---- END SCRIPT -----

-----Ursprungligt meddelande----- From: Wietse Venema
Sent: Wednesday, February 18, 2015 1:29 AM
To: Postfix users
Subject: Re: How I do to add headers by command?

Sebastian Nielsen:
On the server, on mail put in outgoing queue (to be relayed), I
want to run the following command: /usr/bin/hashcash -mXb 26
[recipient01] [recipient02] .. [recipientNN]

Wietse:
Use a Milter, written in Python or Perl. It needs to receive the
"end of message" event and from there it needs to invoke the "insert
header" action. For example, pymilter or pmilter.

Sebastian Nielsen:
My next problem is that " $recipient = $ctx->getsymval('{rcpt_addr}'); "
only return the latest recipient.

You need to register a handler for the RCPT command (in addition to
the end-of-message handler).

Oh, and DO NOT USE A SHELL when invoking the command, otherwise you
are re-implementing the PHF security hole (enter those three words
into a web search engine to educate yourself).

Untested code follows:

# Initialization in CONNECT event handler.
@command = ("|- ", "/usr/bin/hashcash", "-mXb", "26");

# Update the command with each RCPT event.
push(@command, $recipient);

# Finally, run hashcash.
open(HANDLE, @command) || die "cannot run /usr/bin/hashcash: $!\n";
while (<HANDLE>) { ... }

Wietse

Reply via email to