2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-09-01 06:35:27 +00:00

snapshot-20000418

This commit is contained in:
Wietse Venema
2000-04-18 00:00:00 +00:00
parent 1656957b3c
commit 17d46ec1c0
7 changed files with 116 additions and 55 deletions

View File

@@ -3819,4 +3819,11 @@ Apologies for any names omitted.
20000417 20000417
The SASL authentication in the SMTP server and client works, 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.

View File

@@ -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 Do not use this code. The Postfix SASL support is based on the
Cyrus SASL library, which is light years away from production Cyrus SASL library, which has not enough documentation about how
quality. There is not enough documentation to figure out how the the software is supposed to work.
software is supposed to work.
The SASL library code works only on LINUX and Solaris. If you Postfix+SASL 1.5.5 appears to work on RedHat 6.1 (pwcheck_method
build Postfix+SASL on other systems, the software builds without of shadow or sasldb), Solaris 2.7 (pwcheck_method of shadow or
trouble but fails at runtime due to no available authentication sasldb), and FreeBSD 3.4 (pwcheck_method of sasldb). On RedHat
mechanisms. It can be made to work with considerable tweaking. 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 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 Till Franke of SuSE Rhein/Main AG. The present code is a trimmed-down
version with only the bare necessities. version with only the bare necessities.
When receiving mail, Postfix logs the client-provided username and When receiving mail, Postfix logs the client-provided username,
sender address to the maillog file, and optionally grants mail authentication method, and sender address to the maillog file, and
relay access to authenticated clients. SASL authentication information optionally grants mail access via the permit_sasl_authenticated
is not passed on via message headers or via SMTP. It is no-one's UCE restriction. SASL authentication information is not passed on
business what username and authentication method the poster was via message headers or via SMTP. It is no-one's business what
using in order to access the mail server. 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 When sending mail, Postfix looks up the server hostname in a table,
a username/password is known, it will use that to authenticate and if a username/password is found, it will use that username and
to the server. password to authenticate to the server.
Building the SASL library 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 In /usr/local/lib/sasl/smtpd.conf you need to specify what authentication
mechanism the server will support, for example: 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 This will use the SASL password file (default: /etc/sasldb), which
the only way that I was able to test, but which is undesirable is maintained with the saslpasswd command. On some systems the
because it uses plaintext passwords. saslpasswd command needs to be run multiple times before it stops
complaining. The Postfix SMTP server needs read access to the
If you wish to use the system shadow password file, the Postfix sasldb file - you have to play games with group access permissions.
SMTP server can't run chrooted (see master.cf), and the postfix
user or group needs read access to the shadow passwd file.
To run chrooted with SASL support is an interesting exercise. 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 smtp_sasl_passwd_maps = hash:/etc/postfix/sasl_passwd
/etc/postfix/sasl_passwd: /etc/postfix/sasl_passwd:
host.domain username:password foo.com username:password
host.domain username bar.com username
The SASL password file is opened before the SMTP server enters the 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 optional chroot jail, so there is no need to copy the sasl_passwd

View File

@@ -15,7 +15,7 @@
* Version of this program. * Version of this program.
*/ */
#define VAR_MAIL_VERSION "mail_version" #define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-20000417" #define DEF_MAIL_VERSION "Snapshot-20000418"
extern char *var_mail_version; extern char *var_mail_version;
/* LICENSE /* LICENSE

View File

@@ -14,7 +14,7 @@
/* SMTP_STATE *state; /* SMTP_STATE *state;
/* DESCRIPTION /* DESCRIPTION
/* This module contains random chunks of code that implement /* 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. /* is to reduce clutter in the main SMTP client source code.
/* /*
/* smtp_sasl_helo_auth() processes the AUTH option in the /* smtp_sasl_helo_auth() processes the AUTH option in the
@@ -71,7 +71,7 @@
/* smtp_sasl_helo_auth - handle AUTH option in EHLO reply */ /* 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) { if (strlen(words) > 0) {
state->sasl_mechanism_list = mystrdup(words); state->sasl_mechanism_list = mystrdup(words);
state->features |= SMTP_FEATURE_AUTH; state->features |= SMTP_FEATURE_AUTH;
} else {
msg_warn("%s offered null AUTH mechanism list",
state->session->namaddr);
} }
} }
/* smtp_sasl_helo_login - perform SASL login */ /* 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); VSTRING *why = vstring_alloc(10);
int ret = 0; int ret = 0;
/* /*
* Skip authentication when we have no authentication info for this * Skip authentication when no authentication info exists for this
* server. In that case it should simply treat us like any stranger. * server, so that we talk to each other like strangers. Otherwise, if
* Otherwise, if authentication fails assume the error is recoverable. * authentication information exists, assume that authentication is
* required, and assume that an authentication error is recoverable.
*/ */
if (smtp_sasl_passwd_lookup(state) != 0) { if (smtp_sasl_passwd_lookup(state) != 0) {
smtp_sasl_start(state); smtp_sasl_start(state);

View File

@@ -47,7 +47,7 @@
/* This member is a null pointer in the absence of successful /* This member is a null pointer in the absence of successful
/* authentication. /* authentication.
/* .PP /* .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. /* This routine exists for the sake of symmetry.
/* /*
/* smtpd_sasl_disconnect() performs per-connection cleanup. /* 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"); msg_fatal("SASL per-connection server initialization");
/* /*
* Security options. XXX What exactly is this supposed to be doing? The * Security options. Some information can be found in the sasl.h include
* cyrus-sasl-1.5.15 source code has no documentation at all about this * file. Disallow anonymous authentication; this is because the
* routine. * permit_sasl_authenticated feature is restricted to authenticated
* * clients only.
* Disallow anonymous authentication. The permit_sasl_authenticated feature
* is restricted to authenticated clients only.
*/ */
memset(&sec_props, 0, sizeof(sec_props)); memset(&sec_props, 0, sizeof(sec_props));
sec_props.min_ssf = 0; sec_props.min_ssf = 0;
@@ -277,7 +275,7 @@ char *smtpd_sasl_authenticate(SMTPD_STATE *state,
dec_buffer = STR(state->sasl_decoded); dec_buffer = STR(state->sasl_decoded);
if (sasl_decode64(init_response, reply_len, if (sasl_decode64(init_response, reply_len,
dec_buffer, &dec_length) != SASL_OK) dec_buffer, &dec_length) != SASL_OK)
return ("501 AUTH failed: malformed initial response"); return ("501 Authentication failed: malformed initial response");
if (msg_verbose) if (msg_verbose)
msg_info("%s: decoded initial response %s", myname, dec_buffer); msg_info("%s: decoded initial response %s", myname, dec_buffer);
} else { } else {
@@ -302,6 +300,9 @@ char *smtpd_sasl_authenticate(SMTPD_STATE *state,
* comes in multiples of four bytes for each triple of input bytes, * comes in multiples of four bytes for each triple of input bytes,
* plus four bytes for any incomplete last triple, plus one byte for * plus four bytes for any incomplete last triple, plus one byte for
* the null terminator. * the null terminator.
*
* XXX Replace the klunky sasl_encode64() interface by something that
* uses VSTRING buffers.
*/ */
if (msg_verbose) if (msg_verbose)
msg_info("%s: uncoded challenge: %.*s", 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) if (serverout)
free(serverout); free(serverout);

View File

@@ -2,9 +2,9 @@
/* NAME /* NAME
/* smtp-sink 8 /* smtp-sink 8
/* SUMMARY /* SUMMARY
/* multi-threaded smtp test server /* multi-threaded SMTP/LMTP test server
/* SYNOPSIS /* SYNOPSIS
/* smtp-sink [-c] [-p] [-v] [-w delay] [host]:port backlog /* smtp-sink [-cLpv] [-w delay] [host]:port backlog
/* DESCRIPTION /* DESCRIPTION
/* \fIsmtp-sink\fR listens on the named host (or address) and port. /* \fIsmtp-sink\fR listens on the named host (or address) and port.
/* It takes SMTP messages from the network and throws them away. /* It takes SMTP messages from the network and throws them away.
@@ -14,6 +14,8 @@
/* .IP -c /* .IP -c
/* Display a running counter that is updated whenever an SMTP /* Display a running counter that is updated whenever an SMTP
/* QUIT command is executed. /* QUIT command is executed.
/* .IP -L
/* Speak LMTP rather than SMTP.
/* .IP -p /* .IP -p
/* Disable ESMTP command pipelining. /* Disable ESMTP command pipelining.
/* .IP -v /* .IP -v
@@ -21,7 +23,7 @@
/* .IP "-w delay" /* .IP "-w delay"
/* Wait \fIdelay\fR seconds before responding to a DATA command. /* Wait \fIdelay\fR seconds before responding to a DATA command.
/* SEE ALSO /* SEE ALSO
/* smtp-source, SMTP test message generator /* smtp-source, SMTP/LMTP test message generator
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@@ -71,6 +73,7 @@ typedef struct SINK_STATE {
VSTREAM *stream; VSTREAM *stream;
int data_state; int data_state;
int (*read) (struct SINK_STATE *); int (*read) (struct SINK_STATE *);
int rcpts;
} SINK_STATE; } SINK_STATE;
#define ST_ANY 0 #define ST_ANY 0
@@ -91,6 +94,7 @@ static int count;
static int counter; static int counter;
static int disable_pipelining; static int disable_pipelining;
static int fixed_delay; static int fixed_delay;
static int enable_lmtp;
/* ehlo_response - respond to EHLO command */ /* ehlo_response - respond to EHLO command */
@@ -109,6 +113,22 @@ static void ok_response(SINK_STATE *state)
smtp_printf(state->stream, "250 Ok"); 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 */ /* data_response - respond to DATA command */
static void data_response(SINK_STATE *state) static void data_response(SINK_STATE *state)
@@ -127,6 +147,18 @@ static void data_event(int unused_event, char *context)
data_response(state); 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 */ /* quit_response - respond to QUIT command */
static void quit_response(SINK_STATE *state) 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 (state->data_state == ST_CR_LF_DOT_CR_LF) {
if (msg_verbose) if (msg_verbose)
msg_info("."); msg_info(".");
ok_response(state); dot_response(state);
state->read = command_read; state->read = command_read;
break; break;
} }
@@ -203,8 +235,9 @@ typedef struct SINK_COMMAND {
static SINK_COMMAND command_table[] = { static SINK_COMMAND command_table[] = {
"helo", ok_response, "helo", ok_response,
"ehlo", ehlo_response, "ehlo", ehlo_response,
"mail", ok_response, "lhlo", ehlo_response,
"rcpt", ok_response, "mail", mail_response,
"rcpt", rcpt_response,
"data", data_response, "data", data_response,
"rset", ok_response, "rset", ok_response,
"noop", ok_response, "noop", ok_response,
@@ -311,7 +344,7 @@ static void connect_event(int unused_event, char *context)
static void usage(char *myname) 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) int main(int argc, char **argv)
@@ -328,11 +361,14 @@ int main(int argc, char **argv)
/* /*
* Parse JCL. * Parse JCL.
*/ */
while ((ch = GETOPT(argc, argv, "cpvw:")) > 0) { while ((ch = GETOPT(argc, argv, "cLpvw:")) > 0) {
switch (ch) { switch (ch) {
case 'c': case 'c':
count++; count++;
break; break;
case 'L':
enable_lmtp = 1;
break;
case 'p': case 'p':
disable_pipelining = 1; disable_pipelining = 1;
break; break;

View File

@@ -28,6 +28,8 @@
/* Old mode: don't send HELO, and don't send message headers. /* Old mode: don't send HELO, and don't send message headers.
/* .IP "-l length" /* .IP "-l length"
/* Send \fIlength\fR bytes as message payload. /* Send \fIlength\fR bytes as message payload.
/* .IP -L
/* Speak LMTP rather than SMTP.
/* .IP "-m message_count" /* .IP "-m message_count"
/* Send the specified number of messages (default: 1). /* Send the specified number of messages (default: 1).
/* .IP "-r recipient_count" /* .IP "-r recipient_count"
@@ -104,6 +106,7 @@
*/ */
typedef struct SESSION { typedef struct SESSION {
int xfer_count; /* # of xfers in session */ int xfer_count; /* # of xfers in session */
int rcpt_done; /* # of recipients done */
int rcpt_count; /* # of recipients to go */ int rcpt_count; /* # of recipients to go */
VSTREAM *stream; /* open connection */ VSTREAM *stream; /* open connection */
int connect_count; /* # of connect()s to retry */ int connect_count; /* # of connect()s to retry */
@@ -142,6 +145,7 @@ static int send_headers = 1;
static int connect_count = 1; static int connect_count = 1;
static int random_delay = 0; static int random_delay = 0;
static int fixed_delay = 0; static int fixed_delay = 0;
static int talk_lmtp = 0;
static void enqueue_connect(SESSION *); static void enqueue_connect(SESSION *);
static void start_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) static void send_helo(SESSION *session)
{ {
int except; int except;
char *protocol = (talk_lmtp ? "LHLO" : "EHLO");
/* /*
* Send the standard greeting with our hostname * Send the standard greeting with our hostname
@@ -452,7 +457,7 @@ static void send_helo(SESSION *session)
if ((except = setjmp(smtp_timeout_buf)) != 0) if ((except = setjmp(smtp_timeout_buf)) != 0)
msg_fatal("%s while sending HELO", exception_text(except)); 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. * 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); msg_fatal("sender rejected: %d %s", resp->code, resp->str);
session->rcpt_count = recipients; session->rcpt_count = recipients;
session->rcpt_done = 0;
send_rcpt(unused, context); send_rcpt(unused, context);
} }
@@ -542,6 +548,7 @@ static void send_rcpt(int unused_event, char *context)
else else
command(session->stream, "RCPT TO:<%s>", recipient); command(session->stream, "RCPT TO:<%s>", recipient);
session->rcpt_count--; session->rcpt_count--;
session->rcpt_done++;
/* /*
* Prepare for the next event. * 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) if ((except = setjmp(smtp_timeout_buf)) != 0)
msg_fatal("%s while sending message", exception_text(except)); msg_fatal("%s while sending message", exception_text(except));
if ((resp = response(session->stream, buffer))->code / 100 != 2) do { /* XXX this could block */
msg_fatal("data %d %s", resp->code, resp->str); 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++; session->xfer_count++;
/* /*
@@ -740,7 +749,7 @@ int main(int argc, char **argv)
/* /*
* Parse JCL. * 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) { switch (ch) {
case 'c': case 'c':
count++; count++;
@@ -765,6 +774,9 @@ int main(int argc, char **argv)
message_data[i - 1] = '\n'; message_data[i - 1] = '\n';
} }
break; break;
case 'L':
talk_lmtp = 1;
break;
case 'm': case 'm':
if ((message_count = atoi(optarg)) <= 0) if ((message_count = atoi(optarg)) <= 0)
usage(argv[0]); usage(argv[0]);