Configuring SASL Auth in Postfix
A) SASL Configuration
First ensure that Cyrus-SASL and salsauthd are installed.
Then configure /etc/sysconfig/saslauthd so that SASL uses IMAP authentication =>
MECH=rimap
FLAGS="-O localhost"
(Assuming that localhost is the IMAP Server)
restart saslauthd service
test sasl by using the command - testsaslauthd.
The testsaslauthd command for a user should return success. If it does, SASL configuration is fine. Now to configure Postfix.
B) Configure Postfix to use this underlying SASL =>
1. Edit main.cf =>
# vim /etc/postfix/main.cf
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes smtpd_sasl_security_options = noanonymous
2. Edit /usr/lib/sasl2/smtpd.conf =>
pwcheck_method: saslauthd mech_list: PLAIN LOGIN
To ensure that username and the "from" address in an email client match =>
Create the File
vim /etc/postfix/controlled_ envelope_senders
Restart Postfix. Now if you do a telnet on Port 25, you should receive the extra lines for auth =>
Generate the encoded password to be used while testing
download the attached file - genauth.pl
make sure you have installed - MIME::BASE64 package from CPAN
execute it =>
# envelope sender owners (SASL login names) john@example.com john@example.com abc@example.com abc@example.comNow ensure that relaying is done thru SASL auth only, but for mynetworks =>
smtpd_sender_login_maps = hash:/etc/postfix/controlled_envelope_senders
smtpd_recipient_restrictions = reject_sender_login_mismatch permit_sasl_authenticated permit_mynetworks reject_unauth_destination
restart the Service
# /etc/init.d/postfix reload
# /etc/init.d/saslauthd restart
Restart Postfix. Now if you do a telnet on Port 25, you should receive the extra lines for auth =>
% telnet server.example.com 25 ... 220 server.example.com ESMTP Postfix EHLO client.example.com 250-server.example.com 250-PIPELINING 250-SIZE 10240000 250-AUTH PLAIN LOGIN 250-AUTH=PLAIN LOGIN
Generate the encoded password to be used while testing
download the attached file - genauth.pl
make sure you have installed - MIME::BASE64 package from CPAN
execute it =>
Create file
# vim genauth.pl
#####################################################################
#!/usr/bin/perl
use strict;
use MIME::Base64;
use Getopt::Std;
my($p_name) = $0 =~ m|/?([^/]+)$|;
my $p_version = "20060620.0";
my $p_usage = "Usage: $p_name [--help|--version] | <type> ...";
my $p_cp = <<EOM;
Copyright (c) 2002-2006 John Jetmore <jj33\@pobox.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
EOM
ext_usage();
my %O = ();
getopts('s', \%O);
my $type = get_input(\@ARGV, "encryption type: ");
if ($type =~ /^plain$/i) {
my $user = get_input(\@ARGV, "username: ", $O{s}||0);
my $pass = get_input(\@ARGV, "password: ", $O{s}||1);
print "Auth String: ", encode_base64("\0$user\0$pass", ''), "\n";
} elsif ($type =~ /^decode$/i) {
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
print decode_base64($user), "\n";
} elsif ($type =~ /^encode$/i) {
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
print encode_base64($user, ""), "\n";
} elsif ($type =~ /^rot13$/i) {
my $str = get_input(\@ARGV, "string: ", $O{s}||0);
my @c = unpack("c*", $str);
foreach my $c (@c) {
if ($c <= 123 && $c >= 97) { $c = ((($c - 97 + 13) % 26) + 97); }
elsif ($c <= 90 && $c >= 65) { $c = ((($c - 65 + 13) % 26) + 65); }
}
print pack("c*", @c), "\n";
} elsif ($type =~ /^atbash$/i) {
my $str = get_input(\@ARGV, "string: ", $O{s}||0);
my @c = unpack("c*", $str);
foreach my $c (@c) {
if ($c <= 123 && $c >= 97) { $c = (25 - ($c - 97)) + 97; }
elsif ($c <= 90 && $c >= 65) { $c = (25 - ($c - 65)) + 65; }
}
print pack("c*", @c), "\n";
} elsif ($type =~ /^http(-basic)?$/i) {
my $user = get_input(\@ARGV, "username: ", $O{s}||0);
my $pass = get_input(\@ARGV, "password: ", $O{s}||1);
print "Auth String: ", encode_base64("${user}:$pass", ''), "\n";
} elsif ($type =~ /^wcsencode$/i) {
try_load("WCS::Encode") || die "WCS::Encode required for rce\n";
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
chomp($user = WCS::Encode::encode($user));
print $user, "\n";
} elsif ($type =~ /^wcsdecode$/i) {
try_load("WCS::Encode") || die "WCS::Encode required for rce\n";
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
print WCS::Encode::decode($user), "\n";
} elsif ($type =~ /^rce$/i) {
try_load("WCS::Passwd") || die "WCS::Passwd required for rce\n";
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
print WCS::Passwd::rce($user), "\n";
} elsif ($type =~ /^rcd$/i) {
try_load("WCS::Passwd") || die "WCS::Passwd required for rce\n";
my $user = get_input(\@ARGV, "string: ", $O{s}||0);
print WCS::Passwd::rcd($user), "\n";
} elsif ($type =~ /^(salt)?encrypt$/i) {
my $user = get_input(\@ARGV, "string: ", $O{s}||1);
my $salt = join('', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]);
$salt = get_input(\@ARGV, "salt: ", $O{s}||0) if ($type =~ /^saltencrypt/i);
print crypt($user, $salt), "\n";
} elsif ($type =~ /^login$/i) {
my $user = get_input(\@ARGV, "username: ", $O{s}||0);
my $pass = get_input(\@ARGV, "password: ", $O{s}||1);
print "Username: ", encode_base64($user, ""), "\n",
"Password: ", encode_base64($pass, ""), "\n";
} elsif ($type =~ /^md5-(base)?64$/i) {
try_load("Digest::MD5") || die "Digest::MD5 required for md5\n";
my $string = get_input(\@ARGV, "string: ", $O{s}||0);
print Digest::MD5::md5_base64($string), "\n";
} elsif ($type =~ /^md5(-hex)?$/i) {
try_load("Digest::MD5") || die "Digest::MD5 required for md5\n";
my $string = get_input(\@ARGV, "string: ", $O{s}||0);
print Digest::MD5::md5_hex($string), "\n";
} elsif ($type =~ /^cram(-(md5|sha1))?$/i) {
my $digest_type = lc($2) || 'md5';
if ($digest_type eq 'md5') {
try_load("Digest::MD5") || die "Digest::MD5 required for CRAM-MD5\n";
} elsif ($digest_type eq 'sha1') {
try_load("Digest::SHA1") || die "Digest::SHA1 required for CRAM-SHA1\n";
}
my $user = get_input(\@ARGV, "username: ", $O{s}||0);
my $pass = get_input(\@ARGV, "password: ", $O{s}||1);
my $chal = get_input(\@ARGV, "challenge: ", $O{s}||0);
if ($chal !~ /^</) {
chomp($chal = decode_base64($chal));
}
my $digest = get_digest($pass, $chal, $digest_type);
print encode_base64("$user $digest", ""), "\n";
} elsif ($type =~ /^(ntlm|spa|msn)$/i) {
try_load("Authen::NTLM") || die "Authen::NTLM required for $type\n";
my $user = get_input(\@ARGV, "username: ", $O{s}||0);
my $pass = get_input(\@ARGV, "password: ", $O{s}||1);
my $domn = get_input(\@ARGV, "domain: ", $O{s}||0);
print "Auth Request: ", Authen::NTLM::ntlm(), "\n";
Authen::NTLM::ntlm_user($user);
Authen::NTLM::ntlm_password($pass);
Authen::NTLM::ntlm_domain($domn);
my $chal = get_input(\@ARGV, "challenge: ", $O{s}||0);
print "Auth Response: ", Authen::NTLM::ntlm($chal), "\n";
} elsif ($type =~ /^apop$/i) {
try_load("Digest::MD5") || die "Digest::MD5 required for APOP\n";
my $chal = get_input(\@ARGV, "challenge: ");
my $pass = get_input(\@ARGV, "password: ", 1);
my $ctx = Digest::MD5->new;
$ctx->add($chal . $pass);
print $ctx->hexdigest, "\n";
} else {
print STDERR "I don't speak $type\n";
exit 1;
}
exit 0;
sub get_input {
my $a = shift; # command line array
my $s = shift; # prompt string
my $q = shift; # quiet
my $r; # response
if (scalar(@$a) > 0) {
$r = shift(@$a);
} else {
print $s;
system('stty', '-echo') if ($q);
$r = <>;
system('stty', 'echo') if ($q);
print "\n" if ($q);
chomp($r);
}
$r = '' if ($r eq '<>');
return($r);
}
sub get_digest {
my $secr = shift;
my $chal = shift;
my $type = shift;
my $ipad = chr(0x36) x 64;
my $opad = chr(0x5c) x 64;
if (length($secr) > 64) {
if ($type eq 'md5') {
$secr = Digest::MD5::md5($secr);
} elsif ($type eq 'sha1') {
$secr = Digest::SHA1::sha1($secr);
} else {
# unknown digest type
return;
}
} else {
$secr .= chr(0) x (64 - length($secr));
}
my $digest = $type eq 'md5'
? Digest::MD5::md5_hex(($secr ^ $opad),
Digest::MD5::md5(($secr ^ $ipad), $chal))
: Digest::SHA1::sha1_hex(($secr ^ $opad),
Digest::SHA1::sha1(($secr ^ $ipad), $chal));
return($digest);
}
sub try_load {
my $mod = shift;
eval("use $mod");
return $@ ? 0 : 1;
}
sub ext_usage {
if ($ARGV[0] =~ /^--help$/i) {
require Config;
$ENV{PATH} .= ":" unless $ENV{PATH} eq "";
$ENV{PATH} = "$ENV{PATH}$Config::Config{'installscript'}";
exec("perldoc", "-F", "-U", $0) || exit 1;
# make parser happy
%Config::Config = ();
} elsif ($ARGV[0] =~ /^--version$/i) {
print "$p_name version $p_version\n\n$p_cp\n";
} else {
return;
}
exit(0);
}
__END__
file Save & Quit
####################################################################
[root@localhos~]# chmod 555 genauth.pl
[root@localhos~]# ./genauth.pl
encryption type: plain
username: abc@example.com
password:
Auth String: AHZiZ0B2Ymcua25hZmwub3JnAHZiZw ==
[root@localhos~]# ./genauth.pl
encryption type: plain
username: abc@example.com
password:
Auth String: AHZiZ0B2Ymcua25hZmwub3JnAHZiZw
Use "mail from" and "rcpt to" to send a test mail. It should ask for a password,
use tha ebove string when you need to authorise
If all goes well, test with mail client
If you face an error, test the permissions for /var/run/saslauthd. Postfix should have read/write permissions on that.
use tha ebove string when you need to authorise
If all goes well, test with mail client
If you face an error, test the permissions for /var/run/saslauthd. Postfix should have read/write permissions on that.
Comments
Post a Comment