mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-30 21:55:20 +00:00
snapshot-20000418
This commit is contained in:
@@ -3819,4 +3819,11 @@ Apologies for any names omitted.
|
||||
20000417
|
||||
|
||||
The SASL authentication in the SMTP server and client works,
|
||||
but only on Linux and Solaris.
|
||||
but only on Linux and Solaris, neither of which I wish to
|
||||
run on my laptop.
|
||||
|
||||
20000418
|
||||
|
||||
Added LMTP support to the smtp-source and smtp-sink utilities
|
||||
so that I don't have to install Cyrus IMAP just to test
|
||||
LMTP.
|
||||
|
@@ -2,14 +2,16 @@ WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
|
||||
===============================================================
|
||||
|
||||
Do not use this code. The Postfix SASL support is based on the
|
||||
Cyrus SASL library, which is light years away from production
|
||||
quality. There is not enough documentation to figure out how the
|
||||
software is supposed to work.
|
||||
Cyrus SASL library, which has not enough documentation about how
|
||||
the software is supposed to work.
|
||||
|
||||
The SASL library code works only on LINUX and Solaris. If you
|
||||
build Postfix+SASL on other systems, the software builds without
|
||||
trouble but fails at runtime due to no available authentication
|
||||
mechanisms. It can be made to work with considerable tweaking.
|
||||
Postfix+SASL 1.5.5 appears to work on RedHat 6.1 (pwcheck_method
|
||||
of shadow or sasldb), Solaris 2.7 (pwcheck_method of shadow or
|
||||
sasldb), and FreeBSD 3.4 (pwcheck_method of sasldb). On RedHat
|
||||
6.1, SASL 1.5.5 needed write access to the sasldb file.
|
||||
|
||||
SASL is a lot of complex code. In a future version the Postfix SASL
|
||||
code is likely to be put outside the SMTP server.
|
||||
|
||||
Introduction
|
||||
============
|
||||
@@ -18,16 +20,17 @@ The Postfix SASL support (RFC 2554) was originally implemented by
|
||||
Till Franke of SuSE Rhein/Main AG. The present code is a trimmed-down
|
||||
version with only the bare necessities.
|
||||
|
||||
When receiving mail, Postfix logs the client-provided username and
|
||||
sender address to the maillog file, and optionally grants mail
|
||||
relay access to authenticated clients. SASL authentication information
|
||||
is not passed on via message headers or via SMTP. It is no-one's
|
||||
business what username and authentication method the poster was
|
||||
using in order to access the mail server.
|
||||
When receiving mail, Postfix logs the client-provided username,
|
||||
authentication method, and sender address to the maillog file, and
|
||||
optionally grants mail access via the permit_sasl_authenticated
|
||||
UCE restriction. SASL authentication information is not passed on
|
||||
via message headers or via SMTP. It is no-one's business what
|
||||
username and authentication method the poster was using in order
|
||||
to access the mail server.
|
||||
|
||||
When sending mail, Postfix looks up the server hostname and if
|
||||
a username/password is known, it will use that to authenticate
|
||||
to the server.
|
||||
When sending mail, Postfix looks up the server hostname in a table,
|
||||
and if a username/password is found, it will use that username and
|
||||
password to authenticate to the server.
|
||||
|
||||
Building the SASL library
|
||||
=========================
|
||||
@@ -80,15 +83,13 @@ In order to allow mail relaying by authenticated clients:
|
||||
In /usr/local/lib/sasl/smtpd.conf you need to specify what authentication
|
||||
mechanism the server will support, for example:
|
||||
|
||||
pwcheck_method: shadow
|
||||
pwcheck_method: sasldb
|
||||
|
||||
This will use the Linux or Solaris shadow passwd file, which is
|
||||
the only way that I was able to test, but which is undesirable
|
||||
because it uses plaintext passwords.
|
||||
|
||||
If you wish to use the system shadow password file, the Postfix
|
||||
SMTP server can't run chrooted (see master.cf), and the postfix
|
||||
user or group needs read access to the shadow passwd file.
|
||||
This will use the SASL password file (default: /etc/sasldb), which
|
||||
is maintained with the saslpasswd command. On some systems the
|
||||
saslpasswd command needs to be run multiple times before it stops
|
||||
complaining. The Postfix SMTP server needs read access to the
|
||||
sasldb file - you have to play games with group access permissions.
|
||||
|
||||
To run chrooted with SASL support is an interesting exercise.
|
||||
|
||||
@@ -121,8 +122,8 @@ per-host username and password information.
|
||||
smtp_sasl_passwd_maps = hash:/etc/postfix/sasl_passwd
|
||||
|
||||
/etc/postfix/sasl_passwd:
|
||||
host.domain username:password
|
||||
host.domain username
|
||||
foo.com username:password
|
||||
bar.com username
|
||||
|
||||
The SASL password file is opened before the SMTP server enters the
|
||||
optional chroot jail, so there is no need to copy the sasl_passwd
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* Version of this program.
|
||||
*/
|
||||
#define VAR_MAIL_VERSION "mail_version"
|
||||
#define DEF_MAIL_VERSION "Snapshot-20000417"
|
||||
#define DEF_MAIL_VERSION "Snapshot-20000418"
|
||||
extern char *var_mail_version;
|
||||
|
||||
/* LICENSE
|
||||
|
@@ -14,7 +14,7 @@
|
||||
/* SMTP_STATE *state;
|
||||
/* DESCRIPTION
|
||||
/* This module contains random chunks of code that implement
|
||||
/* the SMTP protocol interface for SASL negotiation. The goal
|
||||
/* the SMTP protocol interface for SASL negotiation. The goal
|
||||
/* is to reduce clutter in the main SMTP client source code.
|
||||
/*
|
||||
/* smtp_sasl_helo_auth() processes the AUTH option in the
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
/* smtp_sasl_helo_auth - handle AUTH option in EHLO reply */
|
||||
|
||||
void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words)
|
||||
void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words)
|
||||
{
|
||||
|
||||
/*
|
||||
@@ -88,20 +88,24 @@ void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words)
|
||||
if (strlen(words) > 0) {
|
||||
state->sasl_mechanism_list = mystrdup(words);
|
||||
state->features |= SMTP_FEATURE_AUTH;
|
||||
} else {
|
||||
msg_warn("%s offered null AUTH mechanism list",
|
||||
state->session->namaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* smtp_sasl_helo_login - perform SASL login */
|
||||
|
||||
int smtp_sasl_helo_login(SMTP_STATE *state)
|
||||
int smtp_sasl_helo_login(SMTP_STATE *state)
|
||||
{
|
||||
VSTRING *why = vstring_alloc(10);
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Skip authentication when we have no authentication info for this
|
||||
* server. In that case it should simply treat us like any stranger.
|
||||
* Otherwise, if authentication fails assume the error is recoverable.
|
||||
* Skip authentication when no authentication info exists for this
|
||||
* server, so that we talk to each other like strangers. Otherwise, if
|
||||
* authentication information exists, assume that authentication is
|
||||
* required, and assume that an authentication error is recoverable.
|
||||
*/
|
||||
if (smtp_sasl_passwd_lookup(state) != 0) {
|
||||
smtp_sasl_start(state);
|
||||
|
@@ -47,7 +47,7 @@
|
||||
/* This member is a null pointer in the absence of successful
|
||||
/* authentication.
|
||||
/* .PP
|
||||
/* smtpd_sasl_logout() cleant up after smtpd_sasl_authenticate().
|
||||
/* smtpd_sasl_logout() cleans up after smtpd_sasl_authenticate().
|
||||
/* This routine exists for the sake of symmetry.
|
||||
/*
|
||||
/* smtpd_sasl_disconnect() performs per-connection cleanup.
|
||||
@@ -186,12 +186,10 @@ void smtpd_sasl_connect(SMTPD_STATE *state)
|
||||
msg_fatal("SASL per-connection server initialization");
|
||||
|
||||
/*
|
||||
* Security options. XXX What exactly is this supposed to be doing? The
|
||||
* cyrus-sasl-1.5.15 source code has no documentation at all about this
|
||||
* routine.
|
||||
*
|
||||
* Disallow anonymous authentication. The permit_sasl_authenticated feature
|
||||
* is restricted to authenticated clients only.
|
||||
* Security options. Some information can be found in the sasl.h include
|
||||
* file. Disallow anonymous authentication; this is because the
|
||||
* permit_sasl_authenticated feature is restricted to authenticated
|
||||
* clients only.
|
||||
*/
|
||||
memset(&sec_props, 0, sizeof(sec_props));
|
||||
sec_props.min_ssf = 0;
|
||||
@@ -277,7 +275,7 @@ char *smtpd_sasl_authenticate(SMTPD_STATE *state,
|
||||
dec_buffer = STR(state->sasl_decoded);
|
||||
if (sasl_decode64(init_response, reply_len,
|
||||
dec_buffer, &dec_length) != SASL_OK)
|
||||
return ("501 AUTH failed: malformed initial response");
|
||||
return ("501 Authentication failed: malformed initial response");
|
||||
if (msg_verbose)
|
||||
msg_info("%s: decoded initial response %s", myname, dec_buffer);
|
||||
} else {
|
||||
@@ -302,6 +300,9 @@ char *smtpd_sasl_authenticate(SMTPD_STATE *state,
|
||||
* comes in multiples of four bytes for each triple of input bytes,
|
||||
* plus four bytes for any incomplete last triple, plus one byte for
|
||||
* the null terminator.
|
||||
*
|
||||
* XXX Replace the klunky sasl_encode64() interface by something that
|
||||
* uses VSTRING buffers.
|
||||
*/
|
||||
if (msg_verbose)
|
||||
msg_info("%s: uncoded challenge: %.*s",
|
||||
@@ -337,7 +338,7 @@ char *smtpd_sasl_authenticate(SMTPD_STATE *state,
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup. What a horrible interface.
|
||||
* Cleanup. What an awful interface.
|
||||
*/
|
||||
if (serverout)
|
||||
free(serverout);
|
||||
|
@@ -2,9 +2,9 @@
|
||||
/* NAME
|
||||
/* smtp-sink 8
|
||||
/* SUMMARY
|
||||
/* multi-threaded smtp test server
|
||||
/* multi-threaded SMTP/LMTP test server
|
||||
/* SYNOPSIS
|
||||
/* smtp-sink [-c] [-p] [-v] [-w delay] [host]:port backlog
|
||||
/* smtp-sink [-cLpv] [-w delay] [host]:port backlog
|
||||
/* DESCRIPTION
|
||||
/* \fIsmtp-sink\fR listens on the named host (or address) and port.
|
||||
/* It takes SMTP messages from the network and throws them away.
|
||||
@@ -14,6 +14,8 @@
|
||||
/* .IP -c
|
||||
/* Display a running counter that is updated whenever an SMTP
|
||||
/* QUIT command is executed.
|
||||
/* .IP -L
|
||||
/* Speak LMTP rather than SMTP.
|
||||
/* .IP -p
|
||||
/* Disable ESMTP command pipelining.
|
||||
/* .IP -v
|
||||
@@ -21,7 +23,7 @@
|
||||
/* .IP "-w delay"
|
||||
/* Wait \fIdelay\fR seconds before responding to a DATA command.
|
||||
/* SEE ALSO
|
||||
/* smtp-source, SMTP test message generator
|
||||
/* smtp-source, SMTP/LMTP test message generator
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@@ -71,6 +73,7 @@ typedef struct SINK_STATE {
|
||||
VSTREAM *stream;
|
||||
int data_state;
|
||||
int (*read) (struct SINK_STATE *);
|
||||
int rcpts;
|
||||
} SINK_STATE;
|
||||
|
||||
#define ST_ANY 0
|
||||
@@ -91,6 +94,7 @@ static int count;
|
||||
static int counter;
|
||||
static int disable_pipelining;
|
||||
static int fixed_delay;
|
||||
static int enable_lmtp;
|
||||
|
||||
/* ehlo_response - respond to EHLO command */
|
||||
|
||||
@@ -109,6 +113,22 @@ static void ok_response(SINK_STATE *state)
|
||||
smtp_printf(state->stream, "250 Ok");
|
||||
}
|
||||
|
||||
/* mail_response - reset recipient count, send 250 OK */
|
||||
|
||||
static void mail_response(SINK_STATE *state)
|
||||
{
|
||||
state->rcpts = 0;
|
||||
ok_response(state);
|
||||
}
|
||||
|
||||
/* rcpt_response - bump recipient count, send 250 OK */
|
||||
|
||||
static void rcpt_response(SINK_STATE *state)
|
||||
{
|
||||
state->rcpts++;
|
||||
ok_response(state);
|
||||
}
|
||||
|
||||
/* data_response - respond to DATA command */
|
||||
|
||||
static void data_response(SINK_STATE *state)
|
||||
@@ -127,6 +147,18 @@ static void data_event(int unused_event, char *context)
|
||||
data_response(state);
|
||||
}
|
||||
|
||||
/* dot_response - response to . command */
|
||||
|
||||
static void dot_response(SINK_STATE *state)
|
||||
{
|
||||
if (enable_lmtp) {
|
||||
while (state->rcpts-- > 0) /* XXX this could block */
|
||||
ok_response(state);
|
||||
} else {
|
||||
ok_response(state);
|
||||
}
|
||||
}
|
||||
|
||||
/* quit_response - respond to QUIT command */
|
||||
|
||||
static void quit_response(SINK_STATE *state)
|
||||
@@ -184,7 +216,7 @@ static int data_read(SINK_STATE *state)
|
||||
if (state->data_state == ST_CR_LF_DOT_CR_LF) {
|
||||
if (msg_verbose)
|
||||
msg_info(".");
|
||||
ok_response(state);
|
||||
dot_response(state);
|
||||
state->read = command_read;
|
||||
break;
|
||||
}
|
||||
@@ -203,8 +235,9 @@ typedef struct SINK_COMMAND {
|
||||
static SINK_COMMAND command_table[] = {
|
||||
"helo", ok_response,
|
||||
"ehlo", ehlo_response,
|
||||
"mail", ok_response,
|
||||
"rcpt", ok_response,
|
||||
"lhlo", ehlo_response,
|
||||
"mail", mail_response,
|
||||
"rcpt", rcpt_response,
|
||||
"data", data_response,
|
||||
"rset", ok_response,
|
||||
"noop", ok_response,
|
||||
@@ -311,7 +344,7 @@ static void connect_event(int unused_event, char *context)
|
||||
|
||||
static void usage(char *myname)
|
||||
{
|
||||
msg_fatal("usage: %s [-c] [-p] [-v] [host]:port backlog", myname);
|
||||
msg_fatal("usage: %s [-cLpv] [host]:port backlog", myname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -328,11 +361,14 @@ int main(int argc, char **argv)
|
||||
/*
|
||||
* Parse JCL.
|
||||
*/
|
||||
while ((ch = GETOPT(argc, argv, "cpvw:")) > 0) {
|
||||
while ((ch = GETOPT(argc, argv, "cLpvw:")) > 0) {
|
||||
switch (ch) {
|
||||
case 'c':
|
||||
count++;
|
||||
break;
|
||||
case 'L':
|
||||
enable_lmtp = 1;
|
||||
break;
|
||||
case 'p':
|
||||
disable_pipelining = 1;
|
||||
break;
|
||||
|
@@ -28,6 +28,8 @@
|
||||
/* Old mode: don't send HELO, and don't send message headers.
|
||||
/* .IP "-l length"
|
||||
/* Send \fIlength\fR bytes as message payload.
|
||||
/* .IP -L
|
||||
/* Speak LMTP rather than SMTP.
|
||||
/* .IP "-m message_count"
|
||||
/* Send the specified number of messages (default: 1).
|
||||
/* .IP "-r recipient_count"
|
||||
@@ -104,6 +106,7 @@
|
||||
*/
|
||||
typedef struct SESSION {
|
||||
int xfer_count; /* # of xfers in session */
|
||||
int rcpt_done; /* # of recipients done */
|
||||
int rcpt_count; /* # of recipients to go */
|
||||
VSTREAM *stream; /* open connection */
|
||||
int connect_count; /* # of connect()s to retry */
|
||||
@@ -142,6 +145,7 @@ static int send_headers = 1;
|
||||
static int connect_count = 1;
|
||||
static int random_delay = 0;
|
||||
static int fixed_delay = 0;
|
||||
static int talk_lmtp = 0;
|
||||
|
||||
static void enqueue_connect(SESSION *);
|
||||
static void start_connect(SESSION *);
|
||||
@@ -445,6 +449,7 @@ static void read_banner(int unused_event, char *context)
|
||||
static void send_helo(SESSION *session)
|
||||
{
|
||||
int except;
|
||||
char *protocol = (talk_lmtp ? "LHLO" : "EHLO");
|
||||
|
||||
/*
|
||||
* Send the standard greeting with our hostname
|
||||
@@ -452,7 +457,7 @@ static void send_helo(SESSION *session)
|
||||
if ((except = setjmp(smtp_timeout_buf)) != 0)
|
||||
msg_fatal("%s while sending HELO", exception_text(except));
|
||||
|
||||
command(session->stream, "HELO %s", var_myhostname);
|
||||
command(session->stream, "%s %s", protocol, var_myhostname);
|
||||
|
||||
/*
|
||||
* Prepare for the next event.
|
||||
@@ -520,6 +525,7 @@ static void mail_done(int unused, char *context)
|
||||
msg_fatal("sender rejected: %d %s", resp->code, resp->str);
|
||||
|
||||
session->rcpt_count = recipients;
|
||||
session->rcpt_done = 0;
|
||||
send_rcpt(unused, context);
|
||||
}
|
||||
|
||||
@@ -542,6 +548,7 @@ static void send_rcpt(int unused_event, char *context)
|
||||
else
|
||||
command(session->stream, "RCPT TO:<%s>", recipient);
|
||||
session->rcpt_count--;
|
||||
session->rcpt_done++;
|
||||
|
||||
/*
|
||||
* Prepare for the next event.
|
||||
@@ -679,8 +686,10 @@ static void dot_done(int unused_event, char *context)
|
||||
*/
|
||||
if ((except = setjmp(smtp_timeout_buf)) != 0)
|
||||
msg_fatal("%s while sending message", exception_text(except));
|
||||
if ((resp = response(session->stream, buffer))->code / 100 != 2)
|
||||
msg_fatal("data %d %s", resp->code, resp->str);
|
||||
do { /* XXX this could block */
|
||||
if ((resp = response(session->stream, buffer))->code / 100 != 2)
|
||||
msg_fatal("data %d %s", resp->code, resp->str);
|
||||
} while (talk_lmtp && --session->rcpt_done > 0);
|
||||
session->xfer_count++;
|
||||
|
||||
/*
|
||||
@@ -740,7 +749,7 @@ int main(int argc, char **argv)
|
||||
/*
|
||||
* Parse JCL.
|
||||
*/
|
||||
while ((ch = GETOPT(argc, argv, "cC:df:l:m:or:R:s:t:vw:")) > 0) {
|
||||
while ((ch = GETOPT(argc, argv, "cC:df:l:Lm:or:R:s:t:vw:")) > 0) {
|
||||
switch (ch) {
|
||||
case 'c':
|
||||
count++;
|
||||
@@ -765,6 +774,9 @@ int main(int argc, char **argv)
|
||||
message_data[i - 1] = '\n';
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
talk_lmtp = 1;
|
||||
break;
|
||||
case 'm':
|
||||
if ((message_count = atoi(optarg)) <= 0)
|
||||
usage(argv[0]);
|
||||
|
Reference in New Issue
Block a user