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.
+
+20000419
+
+ Bugfix: removed the () from the tokenized representation
+ of RFC 822 comments, so that comments with \( or \) can be
+ unparsed correctly. Problem reported by Bodo Moeller.
# HERE JUST SERVES AS AN EXAMPLE.
#
# This file contains example settings of Postfix configuration
-# parameters that control SMTP authentication
+# parameters that control SASL authentication.
-# As of now, SASL authentication support uses the Cyrus SASL library
-# which is known to work on Linux and Solaris.
+# SMTP SERVER CONTROLS
# The smtpd_sasl_auth_enable parameter controls whether authentication
# is enabled in the Postfix SMTP server.
#
-# If a client is authenticated, then it matches the permit_sasl_authenticated
-# UCE restriction, which can be used to grant mail relay access.
+# If a client is authenticated, then the permit_sasl_authenticated
+# can be used to permit relay access.
#
# In order to enable server-side authentication, build Postfix with
# SASL support, and install a configuration file /usr/lib/sasl/smtpd.conf
# with as contents, for example,
#
-# pwcheck_method: PAM
+# pwcheck_method: sasldb
#
-# or whatever is suitable for your environment. The PAM service name
-# for SASL authentication is "smtp".
+# or whatever method is suitable for your environment: PAM, shadow,
+# whatever. If you use sasldb, you can add users with the "saslpasswd"
+# command that is part of the SASL library. If you use PAM, The PAM
+# service name for SASL authentication is "smtp", and adding users
+# depends entirely on how PAM is set up.
#
-# If you run your SMTP server chrooted, then you need to copy the
-# PAM and SASL support libraries and data files into the chroot jail.
+# If you run your SMTP server chrooted, then you need to copy PAM
+# and/or SASL support libraries and data files into the chroot jail.
#
smtpd_sasl_auth_enable = yes
+# The smtpd_sasl_security_options parameter controls what authentication
+# mechanisms the Postfix SMTP server will offer to the client. The
+# list of available authentication mechanisms is system dependent.
+#
+# Specify zero or more of the following:
+#
+# noplaintext: disallow methods that use plaintext passwords
+# noactive: disallow methods subject to active (non-dictionary) attack
+# nodictionary: disallow methods subject to passive (dictionary) attack
+# noanonymous: disallow methods that allow anonymous authentication
+#
+# By default, the Postfix SMTP server accepts plaintext passwords but
+# not anonymous logins.
+#
+# Horror! It appears that clients try authentication methods in the
+# order as advertised by the server (PLAIN ANONYMOUS CRAM-MD5
+# ...) which means that if you disable plaintext passwords, clients
+# will log in anonymously even when they would be able to use CRAM-MD5.
+# So, if you disable plaintext logins, disable anonymous logins too.
+# Postfix treats anonymous login as no authentication.
+#
+#smtpd_sasl_security_options = noanonymous, noplaintext
+smtpd_sasl_security_options = noanonymous
+
+# SMTP CLIENT CONTROLS
+
# The smtp_sasl_auth_enable parameter controls whether authentication
# is enabled in the Postfix SMTP client.
#
# The smtp_sasl_password_maps parameter specifies the names of lookup
# tables with one username:password entry per remote hostname. If a
-# remote host has no username:password entry, then Postfix will not
-# attempt to authenticate to the host.
+# remote host has no username:password entry, then the Postfix SMTP
+# client will not attempt to authenticate to the remote host.
#
-# The Postifx SMTP client opens the lookup table before going to
+# The Postfix SMTP client opens the lookup table before going to
# chroot jail, so you can keep the password file in /etc/postfix.
#
smtp_auth_passwd_map = hash:/etc/postfix/saslpass
+
+# The smtp_sasl_security_options parameter controls what authentication
+# mechanisms the local Postfix SMTP client is allowed to use. The
+# list of available authentication mechanisms is system dependent.
+#
+# Specify zero or more of the following:
+#
+# noplaintext: disallow methods that use plaintext passwords
+# noactive: disallow methods subject to active (non-dictionary) attack
+# nodictionary: disallow methods subject to passive (dictionary) attack
+# noanonymous: disallow methods that allow anonymous authentication
+#
+# By default, the Postfix SMTP client will not use plaintext passwords.
+#
+#smtpd_sasl_security_options =
+smtpd_sasl_security_options = noplaintext
TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
mail_addr_map mail_date maps mynetworks mypwd namadr_list \
off_cvt quote_822_local rec2stream recdump resolve_clnt \
- resolve_local rewrite_clnt stream2rec string_list tok822_parse \
- local_transport
+ resolve_local rewrite_clnt stream2rec string_list tok822_parse
LIBS = ../lib/libutil.a
LIB_DIR = ../lib
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
+tests: tok822_test
+
+tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
+ ./tok822_parse <tok822_parse.in >tok822_parse.tmp
+ diff tok822_parse.ref tok822_parse.tmp
+ rm -f tok822_parse.tmp
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
mail_queue.o: ../include/dir_forest.h
mail_queue.o: ../include/make_dirs.h
mail_queue.o: ../include/split_at.h
+mail_queue.o: ../include/sane_fsops.h
mail_queue.o: file_id.h
mail_queue.o: mail_params.h
mail_queue.o: mail_queue.h
extern int var_smtpd_junk_cmd_limit;
/*
- * SASL authentication support.
+ * SASL authentication support, server side.
*/
#define VAR_SMTPD_SASL_ENABLE "smtpd_sasl_auth_enable"
#define DEF_SMTPD_SASL_ENABLE 0
extern bool var_smtpd_sasl_enable;
+#define VAR_SMTPD_SASL_OPTS "smtpd_sasl_security_options"
+#define DEF_SMTPD_SASL_OPTS "noanonymous"
+extern char *var_smtpd_sasl_opts;
+
+ /*
+ * SASL authentication support, client side.
+ */
#define VAR_SMTP_SASL_ENABLE "smtp_sasl_auth_enable"
#define DEF_SMTP_SASL_ENABLE 0
extern bool var_smtp_sasl_enable;
-#define VAR_SMTP_SASL_PWD_MAPS "smtp_sasl_password_maps"
-#define DEF_SMTP_SASL_PWD_MAPS ""
-extern char *var_smtp_sasl_pwd_maps;
+#define VAR_SMTP_SASL_PASSWD "smtp_sasl_password_maps"
+#define DEF_SMTP_SASL_PASSWD ""
+extern char *var_smtp_sasl_passwd;
+
+#define VAR_SMTP_SASL_OPTS "smtp_sasl_security_options"
+#define DEF_SMTP_SASL_OPTS "noplaintext, noanonymous"
+extern char *var_smtp_sasl_opts;
#define PERMIT_SASL_AUTH "permit_sasl_authenticated"
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20000418"
+#define DEF_MAIL_VERSION "Snapshot-20000421"
extern char *var_mail_version;
/* LICENSE
#define TOK822_DOMLIT 259 /* stuff between [] not nesting */
#define TOK822_ADDR 260 /* actually a token group */
#define TOK822_STARTGRP 261 /* start of named group */
-#define TOK822_MAXTOK 261
+#define TOK822_COMMTEXT 262 /* comment text */
+#define TOK822_MAXTOK 262
/*
* tok822_node.c
{
TOK822 *tp;
+#define CONTAINER_TOKEN(x) \
+ ((x) == TOK822_ADDR || (x) == TOK822_COMMENT || (x) == TOK822_STARTGRP)
+
tp = (TOK822 *) mymalloc(sizeof(*tp));
tp->type = type;
tp->next = tp->prev = tp->head = tp->tail = tp->owner = 0;
- tp->vstr = (type < TOK822_MINTOK ? 0 :
+ tp->vstr = (type < TOK822_MINTOK || CONTAINER_TOKEN(type) ? 0 :
strval == 0 ? vstring_alloc(10) :
vstring_strcpy(vstring_alloc(strlen(strval) + 1), strval));
return (tp);
case TOK822_ADDR:
tok822_internalize(vp, tp->head, TOK822_STR_NONE);
break;
- case TOK822_ATOM:
case TOK822_COMMENT:
+ VSTRING_ADDCH(vp, '(');
+ tok822_internalize(vp, tp->head, TOK822_STR_NONE);
+ VSTRING_ADDCH(vp, ')');
+ break;
+ case TOK822_ATOM:
+ case TOK822_COMMTEXT:
case TOK822_QSTRING:
vstring_strcat(vp, vstring_str(tp->vstr));
break;
vstring_strcat(vp, vstring_str(tp->vstr));
break;
case TOK822_COMMENT:
- tok822_copy_quoted(vp, vstring_str(tp->vstr), "\\\r\n");
+ VSTRING_ADDCH(vp, '(');
+ tok822_externalize(vp, tp->head, TOK822_STR_NONE);
+ VSTRING_ADDCH(vp, ')');
+ break;
+ case TOK822_COMMTEXT:
+ tok822_copy_quoted(vp, vstring_str(tp->vstr), "()\\\r\n");
break;
case TOK822_QSTRING:
VSTRING_ADDCH(vp, '"');
break;
case TOK822_DOMLIT:
VSTRING_ADDCH(vp, '[');
- tok822_copy_quoted(vp, vstring_str(tp->vstr), "\"\\\r\n");
+ tok822_copy_quoted(vp, vstring_str(tp->vstr), "\\\r\n");
VSTRING_ADDCH(vp, ']');
break;
case TOK822_STARTGRP:
continue;
if (ch == '(') {
tp = tok822_alloc(TOK822_COMMENT, (char *) 0);
- VSTRING_ADDCH(tp->vstr, ch);
str = tok822_comment(tp, str);
} else if (ch == '[') {
tp = tok822_alloc(TOK822_DOMLIT, (char *) 0);
COLLECT(tp, str, ch, !ISSPACE(ch) && !strchr(tok822_opchar, ch));
tok822_quote_atom(tp);
}
- tail = (head == 0 ? head = tp : tok822_append(tail, tp));
+ if (head == 0) {
+ head = tail = tp;
+ while (tail->next)
+ tail = tail->next;
+ } else {
+ tail = tok822_append(tail, tp);
+ }
}
if (tailp)
*tailp = tail;
const char *tok822_comment(TOK822 *tp, const char *str)
{
+ TOK822 *tc = 0;
int ch;
+#define COMMENT_TEXT_TOKEN(t) ((t) && (t)->type == TOK822_COMMTEXT)
+
+#define APPEND_NEW_TOKEN(tp, type, strval) \
+ tok822_sub_append(tp, tok822_alloc(type, strval))
+
while ((ch = *(unsigned char *) str) != 0) {
- VSTRING_ADDCH(tp->vstr, ISSPACE(ch) ? ' ' : ch);
str++;
if (ch == '(') { /* comments can nest! */
- str = tok822_comment(tp, str);
+ if (COMMENT_TEXT_TOKEN(tc))
+ VSTRING_TERMINATE(tc->vstr);
+ tc = APPEND_NEW_TOKEN(tp, TOK822_COMMENT, (char *) 0);
+ str = tok822_comment(tc, str);
} else if (ch == ')') {
break;
- } else if (ch == '\\') {
- vstring_truncate(tp->vstr, VSTRING_LEN(tp->vstr) - 1);
- if ((ch = *(unsigned char *) str) == 0)
- break;
- VSTRING_ADDCH(tp->vstr, ch);
- str++;
+ } else {
+ if (ch == '\\') {
+ if ((ch = *(unsigned char *) str) == 0)
+ break;
+ str++;
+ }
+ if (!COMMENT_TEXT_TOKEN(tc))
+ tc = APPEND_NEW_TOKEN(tp, TOK822_COMMTEXT, (char *) 0);
+ VSTRING_ADDCH(tc->vstr, ch);
}
}
- VSTRING_TERMINATE(tp->vstr);
+ if (COMMENT_TEXT_TOKEN(tc))
+ VSTRING_TERMINATE(tc->vstr);
return (str);
}
#ifdef TEST
+#include <unistd.h>
#include <vstream.h>
#include <vstring_vstream.h>
/* tok822_print - display token */
-static void tok822_print(TOK822 *tp, int indent)
+static void tok822_print(TOK822 *list, int indent)
{
- if (tp->type < TOK822_MINTOK) {
- vstream_printf("%*s %s \"%c\"\n", indent, "", "OP", tp->type);
- } else if (tp->type == TOK822_ADDR) {
- vstream_printf("%*s %s\n", indent, "", "address");
- } else {
- vstream_printf("%*s %s \"%s\"\n", indent, "",
- tp->type == TOK822_COMMENT ? "comment" :
- tp->type == TOK822_ATOM ? "atom" :
- tp->type == TOK822_QSTRING ? "quoted string" :
- tp->type == TOK822_DOMLIT ? "domain literal" :
- tp->type == TOK822_ADDR ? "address" :
- "unknown\n", vstring_str(tp->vstr));
+ TOK822 *tp;
+
+ for (tp = list; tp; tp = tp->next) {
+ if (tp->type < TOK822_MINTOK) {
+ vstream_printf("%*s %s \"%c\"\n", indent, "", "OP", tp->type);
+ } else if (tp->type == TOK822_ADDR) {
+ vstream_printf("%*s %s\n", indent, "", "address");
+ tok822_print(tp->head, indent + 2);
+ } else if (tp->type == TOK822_COMMENT) {
+ vstream_printf("%*s %s\n", indent, "", "comment");
+ tok822_print(tp->head, indent + 2);
+ } else if (tp->type == TOK822_STARTGRP) {
+ vstream_printf("%*s %s\n", indent, "", "group \":\"");
+ } else {
+ vstream_printf("%*s %s \"%s\"\n", indent, "",
+ tp->type == TOK822_COMMTEXT ? "text" :
+ tp->type == TOK822_ATOM ? "atom" :
+ tp->type == TOK822_QSTRING ? "quoted string" :
+ tp->type == TOK822_DOMLIT ? "domain literal" :
+ tp->type == TOK822_ADDR ? "address" :
+ "unknown\n", vstring_str(tp->vstr));
+ }
}
}
{
VSTRING *vp = vstring_alloc(100);
TOK822 *list;
- TOK822 *tp;
- TOK822 *ap;
- int indent = 0;
VSTRING *buf = vstring_alloc(100);
- while (vstring_fgets(buf, VSTREAM_IN)) {
+ while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
+ if (!isatty(vstream_fileno(VSTREAM_IN)))
+ vstream_printf(">>>%s<<<\n\n", vstring_str(buf));
list = tok822_parse(vstring_str(buf));
- for (tp = list; tp; tp = tp->next) {
- tok822_print(tp, indent);
- if (tp->type == TOK822_ADDR) {
- indent += 2;
- for (ap = tp->head; ap; ap = ap->next)
- tok822_print(ap, indent);
- indent -= 2;
- }
- }
+ vstream_printf("Parse tree:\n");
+ tok822_print(list, 0);
vstream_printf("\n");
vstream_printf("Internalized:\n%s\n\n",
--- /dev/null
+wietse@porcupine.org
+"wietse venema"@porcupine.org
+wietse@porcupine.org
+wietse @ porcupine.org
+"wietse venema"@porcupine.org ("wietse ) venema")
+"wietse venema" <wietse@porcupine.org>
+"wietse venema"@porcupine.org ( ("wietse ) venema") )
+"wietse venema"@porcupine.org
+wietse\ venema@porcupine.org
+"wietse venema
+wietse@[stuff
+wietse@["stuff]
+named group: foo@bar, baz@barf;
--- /dev/null
+>>>wietse@porcupine.org<<<
+
+Parse tree:
+ address
+ atom "wietse"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse@porcupine.org
+
+Externalized, no newlines inserted:
+wietse@porcupine.org
+
+Externalized, newlines inserted:
+wietse@porcupine.org
+
+>>>"wietse venema"@porcupine.org<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse venema@porcupine.org
+
+Externalized, no newlines inserted:
+"wietse venema"@porcupine.org
+
+Externalized, newlines inserted:
+"wietse venema"@porcupine.org
+
+>>>wietse@porcupine.org<<<
+
+Parse tree:
+ address
+ atom "wietse"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse@porcupine.org
+
+Externalized, no newlines inserted:
+wietse@porcupine.org
+
+Externalized, newlines inserted:
+wietse@porcupine.org
+
+>>>wietse @ porcupine.org<<<
+
+Parse tree:
+ address
+ atom "wietse"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse@porcupine.org
+
+Externalized, no newlines inserted:
+wietse@porcupine.org
+
+Externalized, newlines inserted:
+wietse@porcupine.org
+
+>>>"wietse venema"@porcupine.org ("wietse ) venema")<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+ OP ","
+ address
+ atom "venema"
+ comment
+ text ""wietse "
+ OP ","
+ address
+ quoted string ")"
+
+Internalized:
+wietse venema@porcupine.org, venema ("wietse ), )
+
+Externalized, no newlines inserted:
+"wietse venema"@porcupine.org, venema ("wietse ), ")"
+
+Externalized, newlines inserted:
+"wietse venema"@porcupine.org,
+venema ("wietse ),
+")"
+
+>>>"wietse venema" <wietse@porcupine.org><<<
+
+Parse tree:
+ quoted string "wietse venema"
+ OP "<"
+ address
+ atom "wietse"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+ OP ">"
+
+Internalized:
+wietse venema <wietse@porcupine.org>
+
+Externalized, no newlines inserted:
+"wietse venema" <wietse@porcupine.org>
+
+Externalized, newlines inserted:
+"wietse venema" <wietse@porcupine.org>
+
+>>>"wietse venema"@porcupine.org ( ("wietse ) venema") )<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+ OP ")"
+ comment
+ text " "
+ comment
+ text ""wietse "
+ text " venema""
+
+Internalized:
+wietse venema@porcupine.org) ( ("wietse ) venema")
+
+Externalized, no newlines inserted:
+"wietse venema"@porcupine.org) ( ("wietse ) venema")
+
+Externalized, newlines inserted:
+"wietse venema"@porcupine.org) ( ("wietse ) venema")
+
+>>>"wietse venema"@porcupine.org<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse venema@porcupine.org
+
+Externalized, no newlines inserted:
+"wietse venema"@porcupine.org
+
+Externalized, newlines inserted:
+"wietse venema"@porcupine.org
+
+>>>wietse\ venema@porcupine.org<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+ OP "@"
+ atom "porcupine"
+ OP "."
+ atom "org"
+
+Internalized:
+wietse venema@porcupine.org
+
+Externalized, no newlines inserted:
+"wietse venema"@porcupine.org
+
+Externalized, newlines inserted:
+"wietse venema"@porcupine.org
+
+>>>"wietse venema<<<
+
+Parse tree:
+ address
+ quoted string "wietse venema"
+
+Internalized:
+wietse venema
+
+Externalized, no newlines inserted:
+"wietse venema"
+
+Externalized, newlines inserted:
+"wietse venema"
+
+>>>wietse@[stuff<<<
+
+Parse tree:
+ address
+ atom "wietse"
+ OP "@"
+ domain literal "stuff"
+
+Internalized:
+wietse@[stuff]
+
+Externalized, no newlines inserted:
+wietse@[stuff]
+
+Externalized, newlines inserted:
+wietse@[stuff]
+
+>>>wietse@["stuff]<<<
+
+Parse tree:
+ address
+ atom "wietse"
+ OP "@"
+ domain literal ""stuff"
+
+Internalized:
+wietse@["stuff]
+
+Externalized, no newlines inserted:
+wietse@["stuff]
+
+Externalized, newlines inserted:
+wietse@["stuff]
+
+>>>named group: foo@bar, baz@barf;<<<
+
+Parse tree:
+ atom "named"
+ atom "group"
+ group ":"
+ address
+ atom "foo"
+ OP "@"
+ atom "bar"
+ OP ","
+ address
+ atom "baz"
+ OP "@"
+ atom "barf"
+ OP ";"
+
+Internalized:
+named group: foo@bar, baz@barf;
+
+Externalized, no newlines inserted:
+named group: foo@bar, baz@barf;
+
+Externalized, newlines inserted:
+named group: foo@bar,
+baz@barf;
+
<li><a href="#relay_restrict">Restricting what users can send mail to off-site destinations</a>
+<li><a href="#backup">Configuring Postfix as backup MX host</a>
+
</ul>
<a name="remote_delivery"><h3>Remote delivery</h3>
<hr>
+<a name="backup"><h3>Configuring Postfix as backup MX host</h3></a>
+
+When you are SECONDARY MX for some other domain this is all you need:
+
+<p>
+
+<pre>
+ DNS:
+ the.backed-up.domain.name IN MX 100 your.machine.name
+
+ /etc/postfix/main.cf:
+ relay_domains = the.backed-up.domain.name
+</pre>
+
+<p>
+
+When you are PRIMARY MX for some other domain you also need:
+
+<p>
+
+<pre>
+ /etc/postfix/main.cf:
+ transport_maps = hash:/etc/postfix/transport
+
+ /etc/postfix/transport:
+ the.backed-up.domain.name smtp:[their.mail.host.name]
+</pre>
+
+<p>
+
+Specify <B>dbm</b> instead of <b>hash</b> if your system uses
+<b>dbm</b> files instead of <b>db</b> files. To find out what map
+types Postfix supports, use the command <b>postconf -m</b>.
+
+<hr>
+
<a name="timeouts"><h3>Mail fails consistently with timeout or lost connection</h3></a>
Every now and then, mail fails with "timed out while sending end
maildir.o: ../include/make_dirs.h
maildir.o: ../include/set_eugid.h
maildir.o: ../include/get_hostname.h
+maildir.o: ../include/sane_fsops.h
maildir.o: ../include/mail_copy.h
maildir.o: ../include/bounce.h
maildir.o: ../include/sent.h
smtp_sasl_glue.o: ../include/mymalloc.h
smtp_sasl_glue.o: ../include/stringops.h
smtp_sasl_glue.o: ../include/split_at.h
+smtp_sasl_glue.o: ../include/name_mask.h
smtp_sasl_glue.o: ../include/mail_params.h
smtp_sasl_glue.o: ../include/string_list.h
smtp_sasl_glue.o: ../include/maps.h
smtp_sasl_proto.o: ../include/sys_defs.h
smtp_sasl_proto.o: ../include/msg.h
smtp_sasl_proto.o: ../include/mymalloc.h
+smtp_sasl_proto.o: ../include/mail_params.h
smtp_sasl_proto.o: smtp.h
smtp_sasl_proto.o: ../include/vstream.h
smtp_sasl_proto.o: ../include/vbuf.h
/* .IP \fBsmtp_sasl_password_maps\fR
/* Lookup tables with per-host \fIname\fR:\fIpassword\fR entries.
/* No entry for a host means no attempt to authenticate.
+/* .IP \fBsmtp_sasl_security_options\fR
+/* Zero or more of the following.
+/* .RS
+/* .IP \fBnoplaintext\fR
+/* Disallow authentication methods that use plaintext passwords.
+/* .IP \fBnoactive\fR
+/* Disallow authentication methods that are vulnerable to non-dictionary
+/* active attacks.
+/* .IP \fBnodictionary\fR
+/* Disallow authentication methods that are vulnerable to passive
+/* dictionary attack.
+/* .IP \fBnoanonymous\fR
+/* Disallow anonymous logins.
+/* .RE
/* .SH "Resource controls"
/* .ad
/* .fi
char *var_bestmx_transp;
char *var_error_rcpt;
int var_smtp_always_ehlo;
-
-#ifdef USE_SASL_AUTH
-
-char *var_smtp_sasl_pwd_maps;
+char *var_smtp_sasl_opts;
+char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
-#endif
-
/*
* Global variables. smtp_errno is set by the address lookup routines and by
* the connection management routines.
VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0,
VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
-#ifdef USE_SASL_AUTH
- VAR_SMTP_SASL_PWD_MAPS, DEF_SMTP_SASL_PWD_MAPS, &var_smtp_sasl_pwd_maps, 0, 0,
-#endif
+ VAR_SMTP_SASL_PASSWD, DEF_SMTP_SASL_PASSWD, &var_smtp_sasl_passwd, 0, 0,
+ VAR_SMTP_SASL_OPTS, DEF_SMTP_SASL_OPTS, &var_smtp_sasl_opts, 0, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err,
VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
-#ifdef USE_SASL_AUTH
VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
-#endif
0,
};
#include <mymalloc.h>
#include <stringops.h>
#include <split_at.h>
+#include <name_mask.h>
/*
* Global library
#ifdef USE_SASL_AUTH
+ /*
+ * Authentication security options.
+ */
+static NAME_MASK smtp_sasl_sec_mask[] = {
+ "noplaintext", SASL_SEC_NOPLAINTEXT,
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
+ 0,
+};
+
+static int smtp_sasl_sec_opts;
+
/*
* Silly little macros.
*/
static int smtp_sasl_get_user(void *context, int unused_id, const char **result,
unsigned *len)
{
+ char *myname = "smtp_sasl_get_user";
SMTP_STATE *state = (SMTP_STATE *) context;
if (msg_verbose)
- msg_info("smtp_sasl_get_user: %s", state->sasl_username);
+ msg_info("%s: %s", myname, state->sasl_username);
+
+ /*
+ * Sanity check.
+ */
+ if (state->sasl_passwd == 0)
+ msg_panic("%s: no username looked up", myname);
*result = state->sasl_username;
if (len)
static int smtp_sasl_get_passwd(sasl_conn_t *conn, void *context,
int id, sasl_secret_t **psecret)
{
+ char *myname = "smtp_sasl_get_passwd";
SMTP_STATE *state = (SMTP_STATE *) context;
int len;
if (msg_verbose)
- msg_info("smtp_sasl_get_passwd: %s", state->sasl_passwd);
+ msg_info("%s: %s", myname, state->sasl_passwd);
/*
* Sanity check.
*/
if (!conn || !psecret || id != SASL_CB_PASS)
return (SASL_BADPARAM);
+ if (state->sasl_passwd == 0)
+ msg_panic("%s: no password looked up", myname);
/*
* Convert the password into a counted string.
msg_info("%s: host `%s' user `%s' pass `%s'",
myname, state->session->host,
state->sasl_username, state->sasl_passwd);
- return(1);
+ return (1);
} else {
if (msg_verbose)
msg_info("%s: host `%s' no auth info found",
myname, state->session->host);
- return(0);
+ return (0);
}
}
*/
if (smtp_sasl_passwd_map)
msg_panic("smtp_sasl_initialize: repeated call");
- if (*var_smtp_sasl_pwd_maps == 0)
- msg_fatal("specify password table via the `%s' configuration parameter",
- VAR_SMTP_SASL_PWD_MAPS);
+ if (*var_smtp_sasl_passwd == 0)
+ msg_fatal("specify a password table via the `%s' configuration parameter",
+ VAR_SMTP_SASL_PASSWD);
/*
* Open the per-host password table and initialize the SASL library. Use
* shared locks for reading, just in case someone updates the table.
*/
smtp_sasl_passwd_map = maps_create("smtp_sasl_passwd",
- var_smtp_sasl_pwd_maps, DICT_FLAG_LOCK);
+ var_smtp_sasl_passwd, DICT_FLAG_LOCK);
if (sasl_client_init(callbacks) != SASL_OK)
msg_fatal("SASL library initialization");
+
+ /*
+ * Configuration parameters.
+ */
+ smtp_sasl_sec_opts = name_mask(smtp_sasl_sec_mask, var_smtp_sasl_opts);
}
/* smtp_sasl_connect - per-session client initialization */
state->sasl_callbacks, NULL_SECFLAGS,
(sasl_conn_t **) &state->sasl_conn) != SASL_OK)
msg_fatal("per-session SASL client initialization");
- smtp_sasl_passwd_lookup(state);
/*
* Per-session security properties. XXX This routine is not sufficiently
sec_props.min_ssf = 0;
sec_props.max_ssf = 1; /* don't allow real SASL
* security layer */
- sec_props.security_flags = 0;
+ sec_props.security_flags = smtp_sasl_sec_opts;
sec_props.maxbufsize = 0;
sec_props.property_names = 0;
sec_props.property_values = 0;
if (result != SASL_OK && result != SASL_CONTINUE) {
vstring_sprintf(why, "cannot SASL authenticate to server %s: %s",
state->session->namaddr,
- sasl_errstring(result, NO_SASL_LANGLIST,
+ sasl_errstring(result, NO_SASL_LANGLIST,
NO_SASL_OUTLANG));
return (-1);
}
* Step through the authentication protocol until the server tells us
* that we are done.
*/
- while ((resp = smtp_chat_resp(state))->code % 100 == 3) {
+ while ((resp = smtp_chat_resp(state))->code / 100 == 3) {
/*
* Process a server challenge.
VSTRING_SPACE(state->sasl_decoded, serverinlen);
if (sasl_decode64(line, serverinlen,
STR(state->sasl_decoded), &enc_length) != SASL_OK) {
- vstring_sprintf(why, "unable to decode SASL challenge from %s",
+ vstring_sprintf(why, "malformed SASL challenge from server %s",
state->session->namaddr);
return (-1);
}
STR(state->sasl_decoded), enc_length,
NO_SASL_INTERACTION, &clientout, &clientoutlen);
if (result != SASL_OK && result != SASL_CONTINUE)
- msg_warn("%s: smtp SASL authentication step failed",
- state->session->namaddr);
+ msg_warn("SASL authentication failed to server %s: %s",
+ state->session->namaddr,
+ sasl_errstring(result, NO_SASL_LANGLIST,
+ NO_SASL_OUTLANG));
/*
* Send a client response.
* We completed the authentication protocol.
*/
if (resp->code / 100 != 2) {
- vstring_sprintf(why, "unable to SASL authenticate with %s",
- state->session->namaddr);
+ vstring_sprintf(why, "SASL authentication failed; server %s said: %s",
+ state->session->namaddr, resp->str);
return (0);
}
return (1);
smtpd_sasl_glue.o: ../include/msg.h
smtpd_sasl_glue.o: ../include/mymalloc.h
smtpd_sasl_glue.o: ../include/namadr_list.h
+smtpd_sasl_glue.o: ../include/name_mask.h
smtpd_sasl_glue.o: ../include/mail_params.h
smtpd_sasl_glue.o: ../include/smtp_stream.h
smtpd_sasl_glue.o: ../include/vstring.h
/* Enable per-session authentication as per RFC 2554 (SASL).
/* This functionality is available only when explicitly selected
/* at program build time and explicitly enabled at runtime.
+/* .IP \fBsmtpd_sasl_security_options\fR
+/* Zero or more of the following.
+/* .RS
+/* .IP \fBnoplaintext\fR
+/* Disallow authentication methods that use plaintext passwords.
+/* .IP \fBnoactive\fR
+/* Disallow authentication methods that are vulnerable to non-dictionary
+/* active attacks.
+/* .IP \fBnodictionary\fR
+/* Disallow authentication methods that are vulnerable to passive
+/* dictionary attack.
+/* .IP \fBnoanonymous\fR
+/* Disallow anonymous logins.
+/* .RE
/* .SH Miscellaneous
/* .ad
/* .fi
bool var_allow_untrust_route;
int var_smtpd_junk_cmd_limit;
bool var_smtpd_sasl_enable;
+char *var_smtpd_sasl_opts;
/*
* Global state, for stand-alone mode queue file cleanup. When this is
VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
+ VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
0,
};
return (SMTPD_CHECK_DUNNO);
}
-#ifdef USE_SASL_AUTH
-
-/* permit_sasl_auth - OK for authenticated connection */
-
-static int permit_sasl_auth(SMTPD_STATE *state)
-{
- if (state->sasl_username)
- return (SMTPD_CHECK_OK);
- return (SMTPD_CHECK_DUNNO);
-}
-
-#endif
-
/* check_relay_domains - OK/FAIL for message relaying */
static int check_relay_domains(SMTPD_STATE *state, char *recipient,
cpp[1], CHECK_RELAY_DOMAINS);
#ifdef USE_SASL_AUTH
} else if (strcasecmp(name, PERMIT_SASL_AUTH) == 0) {
- status = permit_sasl_auth(state);
+ status = permit_sasl_auth(state, SMTPD_CHECK_OK, SMTPD_CHECK_DUNNO);
#endif
} else if (strcasecmp(name, REJECT_UNKNOWN_RCPTDOM) == 0) {
if (state->recipient)
#include <msg.h>
#include <mymalloc.h>
#include <namadr_list.h>
+#include <name_mask.h>
/* Global library. */
{SASL_CB_LIST_END, 0, 0}
};
+static NAME_MASK smtpd_sasl_mask[] = {
+ "noplaintext", SASL_SEC_NOPLAINTEXT,
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
+ 0,
+};
+
+static int smtpd_sasl_opts;
+
+
/* smtpd_sasl_initialize - per-process initialization */
void smtpd_sasl_initialize(void)
*/
if (sasl_server_init(callbacks, "smtpd") != SASL_OK)
msg_fatal("SASL per-process initialization failed");
+
+ /*
+ * Configuration parameters.
+ */
+ smtpd_sasl_opts = name_mask(smtpd_sasl_mask, var_smtpd_sasl_opts);
}
/* smtpd_sasl_connect - per-connection initialization */
sec_props.min_ssf = 0;
sec_props.max_ssf = 1; /* don't allow real SASL
* security layer */
- sec_props.security_flags = SASL_SEC_NOANONYMOUS;
+ sec_props.security_flags = smtpd_sasl_opts;
sec_props.maxbufsize = 0;
sec_props.property_names = 0;
sec_props.property_values = 0;
extern void smtpd_sasl_disconnect(SMTPD_STATE *);
extern char *smtpd_sasl_authenticate(SMTPD_STATE *, const char *, const char *);
extern void smtpd_sasl_logout(SMTPD_STATE *);
+extern int permit_sasl_auth(SMTPD_STATE *, int, int);
/* LICENSE
/* .ad
/*
/* void smtpd_sasl_mail_reset(state)
/* SMTPD_STATE *state;
+/*
+/* static int permit_sasl_auth(state, authenticated, unauthenticated)
+/* SMTPD_STATE *state;
+/* int authenticated;
+/* int unauthenticated;
/* DESCRIPTION
/* This module contains random chunks of code that implement
/* the SMTP protocol interface for SASL negotiation. The goal
/* smtpd_sasl_mail_reset() performs cleanup for the SASL-specific
/* AUTH=sender option to the MAIL FROM command.
/*
+/* permit_sasl_auth() permits access from an authenticated client.
+/* This test fails for clients that use anonymous authentication.
+/*
/* Arguments:
/* .IP state
/* SMTP session context.
/* .IP sender
/* Sender address from the AUTH=sender option in the MAIL FROM
/* command.
+/* .IP authenticated
+/* Result for authenticated client.
+/* .IP unauthenticated
+/* Result for unauthenticated client.
/* DIAGNOSTICS
/* All errors are fatal.
/* LICENSE
}
}
+/* permit_sasl_auth - OK for authenticated connection */
+
+int permit_sasl_auth(SMTPD_STATE *state, int ifyes, int ifnot)
+{
+ if (state->sasl_method && strcasecmp(state->sasl_method, "anonymous"))
+ return (ifyes);
+ return (ifnot);
+}
+
#endif