OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
Re: SMTP-SASL auth failure caching.

From: Keean Schupke (keeanfry-it.com)
Date: Sat Dec 01 2007 - 11:30:00 CST


Hi,

Here is V2 of the patch. There is now a generic caching service
(cache/cache.c) that effectively implements a dictionary service with
"var_cache_name" as the cache backing store. It implements "query",
"update" and "delete", but has no timeout policy, and does not hack
the strings around like verify.c does (so all characters are valid in
the key and the value).

This leaves the client side to deal with any string mangling and
timeout policy, without having to worry about what the backend might
be doing.

The key is now stored as a base64 encoded SHA1 hash, and I have done a
bit of a general tidy up as well.

As usual comments etc appreciated.

Regards,
Keean Schupke, Fry-IT Ltd.

diff -r -U5 -N postfix-2.4.5.orig/Makefile.in postfix-2.4.5/Makefile.in
--- postfix-2.4.5.orig/Makefile.in 2007-11-20 08:24:25.000000000 +0000
+++ postfix-2.4.5/Makefile.in 2007-12-01 10:32:01.000000000 +0000
-5,11 +5,11
        src/postfix src/smtpstone \
        src/sendmail src/error src/pickup src/cleanup src/smtpd src/local \
        src/trivial-rewrite src/qmgr src/oqmgr src/smtp src/bounce \
        src/pipe src/showq src/postalias src/postcat src/postconf src/postdrop \
        src/postkick src/postlock src/postlog src/postmap src/postqueue \
- src/postsuper src/qmqpd src/spawn src/flush src/verify \
+ src/postsuper src/qmqpd src/spawn src/flush src/verify src/cache \
        rmail \
        src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr
 MANDIRS = proto man html

 default: update
diff -r -U5 -N postfix-2.4.5.orig/src/cache/Makefile.in
postfix-2.4.5/src/cache/Makefile.in
--- postfix-2.4.5.orig/src/cache/Makefile.in 1970-01-01
01:00:00.000000000 +0100
+++ postfix-2.4.5/src/cache/Makefile.in 2007-12-01 08:47:23.000000000 +0000
-0,0 +1,58
+SHELL = /bin/sh
+SRCS = cache.c
+OBJS = cache.o
+HDRS =
+TESTSRC =
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG=
+PROG = cache
+INC_DIR = ../../include
+LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $ $(OBJS) $(LIBS) $(SYSLIBS)
+
+$(OBJS): ../../conf/makedefs.out
+
+Makefile: Makefile.in
+ cat ../../conf/makedefs.out $? >$
+
+test: $(TESTPROG)
+
+tests: test
+
+root_tests:
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+ cp $(PROG) ../../libexec
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e
'/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
+ -e 's/o: \.\//o: /' -e p -e '}' ; \
+ done | sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ $(EXPORT) make -f Makefile.in Makefile 1>&2
+
diff -r -U5 -N postfix-2.4.5.orig/src/cache/cache.c
postfix-2.4.5/src/cache/cache.c
--- postfix-2.4.5.orig/src/cache/cache.c 1970-01-01
01:00:00.000000000 +0100
+++ postfix-2.4.5/src/cache/cache.c 2007-12-01 17:05:40.000000000 +0000
-0,0 +1,237
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <dict_ht.h>
+#include <dict.h>
+#include <split_at.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <mail_version.h>
+#include <mail_proto.h>
+#include <cache_client.h>
+
+/* Server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+ /*
+ * Tunable parameters.
+ */
+char *var_cache_name;
+
+ /*
+ * State.
+ */
+static DICT *cache_map;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(x) vstring_str(x)
+#define STREQ(x,y) (strcmp(x,y) == 0)
+
+/* cache_update_service */
+
+static void cache_update_service(VSTREAM *client_stream)
+{
+ VSTRING *key = vstring_alloc(10);
+ VSTRING *value = vstring_alloc(10);
+
+ if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_STR, MAIL_ATTR_VALUE, value,
+ ATTR_TYPE_END) == 2) {
+ dict_put(cache_map, STR(key),STR(value));
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_OK,
+ ATTR_TYPE_END);
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ }
+
+ vstring_free(key);
+ vstring_free(value);
+}
+
+/* cache_query_service */
+
+static void cache_query_service(VSTREAM *client_stream)
+{
+ VSTRING *key = vstring_alloc(10);
+ const char *raw_data;
+
+ if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_END) == 1) {
+ if ((raw_data = dict_get(cache_map, STR(key)))) {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_OK,
+ ATTR_TYPE_STR, MAIL_ATTR_VALUE, raw_data,
+ ATTR_TYPE_END);
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_FAIL,
+ ATTR_TYPE_STR, MAIL_ATTR_VALUE, "",
+ ATTR_TYPE_END);
+ }
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ }
+
+ vstring_free(key);
+}
+
+/* cache delete service */
+
+static void cache_delete_service(VSTREAM *client_stream)
+{
+ VSTRING *key = vstring_alloc(10);
+
+ if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_END) == 1) {
+ if (dict_del(cache_map, STR(key))) {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_FAIL,
+ ATTR_TYPE_END);
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_OK,
+ ATTR_TYPE_END);
+ }
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ }
+
+ vstring_free(key);
+}
+
+/* cache_service */
+
+static void cache_service(VSTREAM *client_stream, char *unused_service,
+ char **argv)
+{
+ VSTRING *request = vstring_alloc(10);
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * This routine runs whenever a client connects to the socket dedicated
+ * to the cache service. All connection-management stuff
+ * is handled by the common code in multi_server.c.
+ */
+ if (attr_scan(client_stream,
+ ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, request,
+ ATTR_TYPE_END) == 1) {
+ if (STREQ(STR(request), CACHE_REQ_UPDATE)) {
+ cache_update_service(client_stream);
+ } else if (STREQ(STR(request), CACHE_REQ_QUERY)) {
+ cache_query_service(client_stream);
+ } else if (STREQ(STR(request), CACHE_REQ_DELETE)) {
+ cache_delete_service(client_stream);
+ } else {
+ msg_warn("unrecognized request: \"%s\", ignored", STR(request));
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, CACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ }
+ }
+ vstream_fflush(client_stream);
+ vstring_free(request);
+}
+
+/* post_jail_init - post-jail initialization */
+
+static void post_jail_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * If the database is in volatile memory only, prevent automatic process
+ * suicide after a limited number of client requests or after a limited
+ * amount of idle time.
+ */
+ if (*var_cache_name == 0) {
+ var_use_limit = 0;
+ var_idle_limit = 0;
+ }
+}
+
+/* pre_jail_init - pre-jail initialization */
+
+static void pre_jail_init(char *unused_name, char **unused_argv)
+{
+ mode_t saved_mask;
+
+ /*
+ * Keep state in persistent (external) or volatile (internal) map.
+ */
+#define CACHE_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
+
+ if (*var_cache_name) {
+ saved_mask = umask(022);
+ cache_map = dict_open(var_cache_name,
+ O_CREAT | O_RDWR,
+ CACHE_DICT_OPEN_FLAGS);
+ (void) umask(saved_mask);
+ } else {
+ cache_map = dict_ht_open("verify", htable_create(0), myfree);
+ }
+
+ /*
+ * Never, ever, get killed by a master signal, as that would corrupt the
+ * database when we're in the middle of an update.
+ */
+ setsid();
+}
+
+MAIL_VERSION_STAMP_DECLARE;
+
+/* main - pass control to the multi-threaded skeleton */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_STR_TABLE str_table[] = {
+ VAR_CACHE_NAME, DEF_CACHE_NAME, &var_cache_name, 0, 0,
+ 0,
+ };
+
+ /*
+ * Fingerprint executables and core dumps.
+ */
+ MAIL_VERSION_STAMP_ALLOCATE;
+
+ multi_server_main(argc, argv, cache_service,
+ MAIL_SERVER_STR_TABLE, str_table,
+ MAIL_SERVER_PRE_INIT, pre_jail_init,
+ MAIL_SERVER_POST_INIT, post_jail_init,
+ MAIL_SERVER_SOLITARY,
+ 0);
+}
diff -r -U5 -N postfix-2.4.5.orig/src/global/Makefile.in
postfix-2.4.5/src/global/Makefile.in
--- postfix-2.4.5.orig/src/global/Makefile.in 2007-11-20
08:38:28.000000000 +0000
+++ postfix-2.4.5/src/global/Makefile.in 2007-12-01
10:31:34.000000000 +0000
-53,11 +53,11
        recipient_list.o record.o remove.o resolve_clnt.o resolve_local.o \
        rewrite_clnt.o scache_clnt.o scache_multi.o scache_single.o \
        sent.o smtp_stream.o split_addr.o string_list.o strip_addr.o \
        sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
        tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o \
- user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o \
+ user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o cache_client.o\
        verp_sender.o wildcard_inet_addr.o xtext.o
 HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
        conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
        deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
-76,11 +76,11
        qmgr_user.h qmqp_proto.h quote_821_local.h quote_822_local.h \
        quote_flags.h rcpt_buf.h rcpt_print.h rec_attr_map.h rec_streamlf.h \
        rec_type.h recipient_list.h record.h resolve_clnt.h resolve_local.h \
        rewrite_clnt.h scache.h sent.h smtp_stream.h split_addr.h \
        string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
- trace.h user_acl.h valid_mailhost_addr.h verify.h verify_clnt.h \
+ trace.h user_acl.h valid_mailhost_addr.h verify.h
verify_clnt.h cache_client.h\
        verp_sender.h wildcard_inet_addr.h xtext.h
 TESTSRC = rec2stream.c stream2rec.c recdump.c
 DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 INCL =
-88,11 +88,11
 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 \
        quote_821_local mail_conf_time mime_state strip_addr \
- verify_clnt xtext anvil_clnt scache ehlo_mask \
+ verify_clnt cache_client xtext anvil_clnt scache ehlo_mask \
        valid_mailhost_addr own_inet_addr

 LIBS = ../../lib/libutil.a
 LIB_DIR = ../../lib
 INC_DIR = ../../include
-263,10 +263,15
 verify_clnt: $(LIB) $(LIBS)
        mv $.o junk
        $(CC) $(CFLAGS) -DTEST -o $ $.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $.o

+cache_client: $(LIB) $(LIBS)
+ mv $.o junk
+ $(CC) $(CFLAGS) -DTEST -o $ $.c $(LIB) $(LIBS) $(SYSLIBS)
+ mv junk $.o
+
 xtext: $(LIB) $(LIBS)
        mv $.o junk
        $(CC) $(CFLAGS) -DTEST -o $ $.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $.o

diff -r -U5 -N postfix-2.4.5.orig/src/global/cache_client.c
postfix-2.4.5/src/global/cache_client.c
--- postfix-2.4.5.orig/src/global/cache_client.c 1970-01-01
01:00:00.000000000 +0100
+++ postfix-2.4.5/src/global/cache_client.c 2007-12-01
17:13:51.000000000 +0000
-0,0 +1,299
+/*++
+/* NAME
+/* cache_client 3
+/* SUMMARY
+/* generic cache client interface
+/* SYNOPSIS
+/* #include <cache_client.h>
+/*
+/* int cache_client_query(key, value)
+/* const char *key;
+/* VSTRING *value;
+/*
+/* int cache_client_update(key, value)
+/* const char *key;
+/* const char *value;
+/*
+/* DIAGNOSTICS
+/* These functions return CACHE_STAT_OK in case of success,
+/* CACHE_STAT_BAD in case of a malformed request, and
+/* CACHE_STAT_FAIL when the operation failed, for a missing
+/* key on lookup or delete.
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <attr.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <clnt_stream.h>
+#include <cache_client.h>
+
+CLNT_STREAM *cache_client;
+
+/* cache_client_init - initialize */
+
+static void cache_client_init(void)
+{
+ if (cache_client != 0)
+ msg_panic("cache_client_init: multiple initialization");
+ cache_client = clnt_stream_create(MAIL_CLASS_PRIVATE, var_cache_service,
+ var_ipc_idle_limit, var_ipc_ttl_limit);
+}
+
+/* cache_client_query */
+
+int cache_client_query(const char *key, VSTRING *value)
+{
+ VSTREAM *stream;
+ int request_status;
+ int count = 0;
+
+ /*
+ * Do client-server plumbing.
+ */
+ if (cache_client == 0)
+ cache_client_init();
+
+ for (;;) {
+ stream = clnt_stream_access(cache_client);
+ errno = 0;
+ count += 1;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, CACHE_REQ_QUERY,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || attr_scan(stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
+ ATTR_TYPE_STR, MAIL_ATTR_VALUE, value,
+ ATTR_TYPE_END) != 2) {
+ if (msg_verbose || count > 1 || (errno && errno != EPIPE
&& errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ var_cache_service);
+ } else {
+ break;
+ }
+ sleep(1);
+ clnt_stream_recover(cache_client);
+ }
+ return (request_status);
+}
+
+/* cache_client_delete */
+
+int cache_client_delete(const char *key)
+{
+ VSTREAM *stream;
+ int request_status;
+ int count = 0;
+
+ /*
+ * Do client-server plumbing.
+ */
+ if (cache_client == 0)
+ cache_client_init();
+
+ for (;;) {
+ stream = clnt_stream_access(cache_client);
+ errno = 0;
+ count += 1;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, CACHE_REQ_DELETE,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || attr_scan(stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
+ ATTR_TYPE_END) != 1) {
+ if (msg_verbose || count > 1 || (errno && errno != EPIPE
&& errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ var_cache_service);
+ } else {
+ break;
+ }
+ sleep(1);
+ clnt_stream_recover(cache_client);
+ }
+ return (request_status);
+}
+
+/* verify_clnt_update - request address status update */
+
+int cache_client_update(const char *key, const char *value)
+{
+ VSTREAM *stream;
+ int request_status;
+
+ /*
+ * Do client-server plumbing.
+ */
+ if (cache_client == 0)
+ cache_client_init();
+
+ for (;;) {
+ stream = clnt_stream_access(cache_client);
+ errno = 0;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, CACHE_REQ_UPDATE,
+ ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+ ATTR_TYPE_STR, MAIL_ATTR_VALUE, value,
+ ATTR_TYPE_END) != 0
+ || attr_scan(stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
+ ATTR_TYPE_END) != 1) {
+ if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ var_cache_service);
+ } else {
+ break;
+ }
+ sleep(1);
+ clnt_stream_recover(cache_client);
+ }
+ return (request_status);
+}
+
+ /*
+ * Proof-of-concept test client program.
+ */
+#ifdef TEST
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <msg_vstream.h>
+#include <stringops.h>
+#include <vstring_vstream.h>
+#include <mail_conf.h>
+
+#define STR(x) vstring_str(x)
+
+static NORETURN usage(char *myname)
+{
+ msg_fatal("usage: %s [-v]", myname);
+}
+
+static void query(char *query, VSTRING *buf)
+{
+ switch (cache_client_query(query, buf)) {
+ case CACHE_STAT_OK:
+ vstream_printf("value %s\n", STR(buf));
+ vstream_fflush(VSTREAM_OUT);
+ break;
+ case CACHE_STAT_BAD:
+ msg_warn("bad request format");
+ break;
+ case CACHE_STAT_FAIL:
+ vstream_printf("FAIL\n");
+ vstream_fflush(VSTREAM_OUT);
+ break;
+ }
+}
+
+static void delete(char *query)
+{
+ switch (cache_client_delete(query)) {
+ case CACHE_STAT_OK:
+ vstream_printf("OK\n");
+ vstream_fflush(VSTREAM_OUT);
+ break;
+ case CACHE_STAT_BAD:
+ msg_warn("bad request format");
+ break;
+ case CACHE_STAT_FAIL:
+ vstream_printf("FAIL\n");
+ vstream_fflush(VSTREAM_OUT);
+ break;
+ }
+}
+
+static void update(char *query)
+{
+ char *key;
+ char *value;
+ char *cp = query;
+
+ if ((query = mystrtok(&cp, " \t\r\n")) == 0) {
+ msg_warn("bad request format");
+ return;
+ }
+ while (*cp && ISSPACE(*cp))
+ cp++;
+ if (*cp == 0) {
+ msg_warn("bad request format");
+ return;
+ }
+ switch (cache_client_update(query, cp)) {
+ case CACHE_STAT_OK:
+ vstream_printf("OK\n");
+ vstream_fflush(VSTREAM_OUT);
+ break;
+ case CACHE_STAT_BAD:
+ msg_warn("bad request format");
+ break;
+ case CACHE_STAT_FAIL:
+ msg_warn("request failed");
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ VSTRING *buffer = vstring_alloc(1);
+ char *cp;
+ int ch;
+ char *command;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ mail_conf_read();
+ msg_info("using config files in %s", var_config_dir);
+ if (chdir(var_queue_dir) < 0)
+ msg_fatal("chdir %s: %m", var_queue_dir);
+
+ while ((ch = GETOPT(argc, argv, "v")) > 0) {
+ switch (ch) {
+ case 'v':
+ msg_verbose++;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+ if (argc - optind > 1)
+ usage(argv[0]);
+
+ while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
+ cp = STR(buffer);
+ if ((command = mystrtok(&cp, " \t\r\n")) == 0)
+ continue;
+ if (strcmp(command, "query") == 0)
+ query(cp, buffer);
+ else if (strcmp(command, "delete") == 0)
+ delete(cp);
+ else if (strcmp(command, "update") == 0)
+ update(cp);
+ else
+ msg_warn("unrecognized command: %s", command);
+ }
+ vstring_free(buffer);
+ return (0);
+}
+
+#endif
diff -r -U5 -N postfix-2.4.5.orig/src/global/cache_client.h
postfix-2.4.5/src/global/cache_client.h
--- postfix-2.4.5.orig/src/global/cache_client.h 1970-01-01
01:00:00.000000000 +0100
+++ postfix-2.4.5/src/global/cache_client.h 2007-12-01
10:31:25.000000000 +0000
-0,0 +1,45
+#ifndef _CACHE_CLIENT_H_INCLUDED_
+#define _CACHE_CLIENT_H_INCLUDED_
+
+/*++
+/* NAME
+/* cache_client 3h
+/* SUMMARY
+/* genric cache client interface
+/* SYNOPSIS
+/* #include <cache_client.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * Global library.
+ */
+#include <deliver_request.h>
+
+ /*
+ * Address verification requests.
+ */
+#define CACHE_REQ_QUERY "query"
+#define CACHE_REQ_UPDATE "update"
+#define CACHE_REQ_DELETE "delete"
+
+ /*
+ * Request (NOT: address) status codes.
+ */
+#define CACHE_STAT_OK 0
+#define CACHE_STAT_FAIL 1
+#define CACHE_STAT_BAD (-1)
+
+ /*
+ * Functional interface.
+ */
+extern int cache_client_query(const char *, VSTRING *);
+extern int cache_client_update(const char *, const char *);
+extern int cache_client_delete(const char *);
+
+#endif
diff -r -U5 -N postfix-2.4.5.orig/src/global/mail_params.c
postfix-2.4.5/src/global/mail_params.c
--- postfix-2.4.5.orig/src/global/mail_params.c 2007-11-20
08:38:28.000000000 +0000
+++ postfix-2.4.5/src/global/mail_params.c 2007-12-01
10:38:56.000000000 +0000
-261,10 +261,11
 char *var_rewrite_service;
 char *var_showq_service;
 char *var_error_service;
 char *var_flush_service;
 char *var_verify_service;
+char *var_cache_service;
 char *var_scache_service;
 char *var_trace_service;
 int var_db_create_buf;
 int var_db_read_buf;
 int var_mime_maxdepth;
-526,10 +527,11
        VAR_REWRITE_SERVICE, DEF_REWRITE_SERVICE, &var_rewrite_service, 1, 0,
        VAR_SHOWQ_SERVICE, DEF_SHOWQ_SERVICE, &var_showq_service, 1, 0,
        VAR_ERROR_SERVICE, DEF_ERROR_SERVICE, &var_error_service, 1, 0,
        VAR_FLUSH_SERVICE, DEF_FLUSH_SERVICE, &var_flush_service, 1, 0,
        VAR_VERIFY_SERVICE, DEF_VERIFY_SERVICE, &var_verify_service, 1, 0,
+ VAR_CACHE_SERVICE, DEF_CACHE_SERVICE, &var_cache_service, 1, 0,
        VAR_TRACE_SERVICE, DEF_TRACE_SERVICE, &var_trace_service, 1, 0,
        VAR_INT_FILT_CLASSES, DEF_INT_FILT_CLASSES, &var_int_filt_classes, 0, 0,
        0,
     };
     static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
diff -r -U5 -N postfix-2.4.5.orig/src/global/mail_params.h
postfix-2.4.5/src/global/mail_params.h
--- postfix-2.4.5.orig/src/global/mail_params.h 2007-11-29
08:01:46.000000000 +0000
+++ postfix-2.4.5/src/global/mail_params.h 2007-12-01
11:59:33.000000000 +0000
-1429,10 +1429,18

 #define VAR_SMTP_SASL_AUTH_SOFT_BOUNCE "smtp_sasl_auth_soft_bounce"
 #define DEF_SMTP_SASL_AUTH_SOFT_BOUNCE 0
 extern bool var_smtp_sasl_auth_soft_bounce;

+#define VAR_SMTP_SASL_AUTH_CACHE_ENABLE "smtp_sasl_auth_cache_enable"
+#define DEF_SMTP_SASL_AUTH_CACHE_ENABLE 0
+extern bool var_smtp_sasl_auth_cache_enable;
+
+#define VAR_SMTP_SASL_AUTH_CACHE_TIME "smtp_sasl_auth_cache_time"
+#define DEF_SMTP_SASL_AUTH_CACHE_TIME "90d"
+extern int var_smtp_sasl_auth_cache_time;
+
 #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"
-1528,10 +1536,18

 #define VAR_LMTP_SASL_AUTH_SOFT_BOUNCE "lmtp_sasl_auth_soft_bounce"
 #define DEF_LMTP_SASL_AUTH_SOFT_BOUNCE 0
 extern bool var_lmtp_sasl_auth_soft_bounce;

+#define VAR_LMTP_SASL_AUTH_CACHE_ENABLE
"lmtp_sasl_auth_fail_cache_enable"
+#define DEF_LMTP_SASL_AUTH_CACHE_ENABLE 0
+extern bool var_lmtp_sasl_auth_fail_enable;
+
+#define VAR_LMTP_SASL_AUTH_CACHE_TIME "lmtp_sasl_auth_cache_time"
+#define DEF_LMTP_SASL_AUTH_CACHE_TIME "90d"
+extern int var_lmtp_sasl_auth_cache_time;
+
 #define VAR_LMTP_SASL_PASSWD "lmtp_sasl_password_maps"
 #define DEF_LMTP_SASL_PASSWD ""
 extern char *var_lmtp_sasl_passwd;

 #define VAR_LMTP_SASL_OPTS "lmtp_sasl_security_options"
-2420,10 +2436,21
 #define VAR_VRFY_XPORT_MAPS "address_verify_transport_maps"
 #define DEF_VRFY_XPORT_MAPS "$" VAR_TRANSPORT_MAPS
 extern char *var_vrfy_xport_maps;

  /*
+ * generic cache service.
+ */
+#define VAR_CACHE_SERVICE "cache_service_name"
+#define DEF_CACHE_SERVICE MAIL_SERVICE_CACHE
+extern char *var_cache_service;
+
+#define VAR_CACHE_NAME "smtp_sasl_auth_cache_name"
+#define DEF_CACHE_NAME ""
+extern char *var_cache_name;
+
+ /*
   * Message delivery trace service.
   */
 #define VAR_TRACE_SERVICE "trace_service_name"
 #define DEF_TRACE_SERVICE MAIL_SERVICE_TRACE
 extern char *var_trace_service;
diff -r -U5 -N postfix-2.4.5.orig/src/global/mail_proto.h
postfix-2.4.5/src/global/mail_proto.h
--- postfix-2.4.5.orig/src/global/mail_proto.h 2006-11-01
20:03:12.000000000 +0000
+++ postfix-2.4.5/src/global/mail_proto.h 2007-12-01
10:29:31.000000000 +0000
-51,10 +51,11
 #define MAIL_SERVICE_SHOWQ "showq"
 #define MAIL_SERVICE_ERROR "error"
 #define MAIL_SERVICE_RETRY "retry"
 #define MAIL_SERVICE_FLUSH "flush"
 #define MAIL_SERVICE_VERIFY "verify"
+#define MAIL_SERVICE_CACHE "cache"
 #define MAIL_SERVICE_TRACE "trace"
 #define MAIL_SERVICE_RELAY "relay"
 #define MAIL_SERVICE_PROXYMAP "proxymap"
 #define MAIL_SERVICE_SCACHE "scache"

diff -r -U5 -N postfix-2.4.5.orig/src/smtp/lmtp_params.c
postfix-2.4.5/src/smtp/lmtp_params.c
--- postfix-2.4.5.orig/src/smtp/lmtp_params.c 2007-11-29
08:17:20.000000000 +0000
+++ postfix-2.4.5/src/smtp/lmtp_params.c 2007-12-01
10:40:00.000000000 +0000
-65,10 +65,11
        VAR_LMTP_REUSE_TIME, DEF_LMTP_REUSE_TIME, &var_smtp_reuse_time, 1, 0,
 #ifdef USE_TLS
        VAR_LMTP_STARTTLS_TMOUT, DEF_LMTP_STARTTLS_TMOUT,
&var_smtp_starttls_tmout, 1, 0,
 #endif
        VAR_SCACHE_PROTO_TMOUT, DEF_SCACHE_PROTO_TMOUT,
&var_scache_proto_tmout, 1, 0,
+ VAR_LMTP_SASL_AUTH_CACHE_TIME, DEF_LMTP_SASL_AUTH_CACHE_TIME,
&var_smtp_sasl_auth_cache_time, 0, 0,
        0,
     };
     static CONFIG_INT_TABLE lmtp_int_table[] = {
        VAR_LMTP_LINE_LIMIT, DEF_LMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
        VAR_LMTP_MXADDR_LIMIT, DEF_LMTP_MXADDR_LIMIT,
&var_smtp_mxaddr_limit, 0, 0,
-83,10 +84,11
     static CONFIG_BOOL_TABLE lmtp_bool_table[] = {
        VAR_LMTP_SKIP_5XX, DEF_LMTP_SKIP_5XX, &var_smtp_skip_5xx_greeting,
        VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
        VAR_LMTP_SASL_ENABLE, DEF_LMTP_SASL_ENABLE, &var_smtp_sasl_enable,
        VAR_LMTP_SASL_AUTH_SOFT_BOUNCE,
DEF_LMTP_SASL_AUTH_SOFT_BOUNCE, &var_smtp_sasl_auth_soft_bounce,
+ VAR_LMTP_SASL_AUTH_CACHE_ENABLE,
DEF_LMTP_SASL_AUTH_CACHE_ENABLE, &var_smtp_sasl_auth_cache_enable,
        VAR_LMTP_RAND_ADDR, DEF_LMTP_RAND_ADDR, &var_smtp_rand_addr,
        VAR_LMTP_QUOTE_821_ENV, DEF_LMTP_QUOTE_821_ENV, &var_smtp_quote_821_env,
        VAR_LMTP_DEFER_MXADDR, DEF_LMTP_DEFER_MXADDR, &var_smtp_defer_mxaddr,
        VAR_LMTP_SEND_XFORWARD, DEF_LMTP_SEND_XFORWARD, &var_smtp_send_xforward,
        VAR_LMTP_CACHE_DEMAND, DEF_LMTP_CACHE_DEMAND, &var_smtp_cache_demand,
diff -r -U5 -N postfix-2.4.5.orig/src/smtp/smtp.c postfix-2.4.5/src/smtp/smtp.c
--- postfix-2.4.5.orig/src/smtp/smtp.c 2007-11-29 08:03:17.000000000 +0000
+++ postfix-2.4.5/src/smtp/smtp.c 2007-12-01 11:04:39.000000000 +0000
-653,10 +653,12
 char *var_smtp_sasl_opts;
 char *var_smtp_sasl_path;
 char *var_smtp_sasl_passwd;
 bool var_smtp_sasl_enable;
 bool var_smtp_sasl_auth_soft_bounce;
+bool var_smtp_sasl_auth_cache_enable;
+int var_smtp_sasl_auth_cache_time;
 char *var_smtp_sasl_mechs;
 char *var_smtp_sasl_type;
 char *var_smtp_bind_addr;
 char *var_smtp_bind_addr6;
 bool var_smtp_rand_addr;
diff -r -U5 -N postfix-2.4.5.orig/src/smtp/smtp_params.c
postfix-2.4.5/src/smtp/smtp_params.c
--- postfix-2.4.5.orig/src/smtp/smtp_params.c 2007-11-29
08:16:48.000000000 +0000
+++ postfix-2.4.5/src/smtp/smtp_params.c 2007-12-01
10:24:47.000000000 +0000
-66,10 +66,11
        VAR_SMTP_REUSE_TIME, DEF_SMTP_REUSE_TIME, &var_smtp_reuse_time, 1, 0,
 #ifdef USE_TLS
        VAR_SMTP_STARTTLS_TMOUT, DEF_SMTP_STARTTLS_TMOUT,
&var_smtp_starttls_tmout, 1, 0,
 #endif
        VAR_SCACHE_PROTO_TMOUT, DEF_SCACHE_PROTO_TMOUT,
&var_scache_proto_tmout, 1, 0,
+ VAR_SMTP_SASL_AUTH_CACHE_TIME, DEF_SMTP_SASL_AUTH_CACHE_TIME,
&var_smtp_sasl_auth_cache_time, 0, 0,
        0,
     };
     static CONFIG_INT_TABLE smtp_int_table[] = {
        VAR_SMTP_LINE_LIMIT, DEF_SMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
        VAR_SMTP_MXADDR_LIMIT, DEF_SMTP_MXADDR_LIMIT,
&var_smtp_mxaddr_limit, 0, 0,
-87,10 +88,11
        VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
        VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
        VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
        VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
        VAR_SMTP_SASL_AUTH_SOFT_BOUNCE,
DEF_SMTP_SASL_AUTH_SOFT_BOUNCE, &var_smtp_sasl_auth_soft_bounce,
+ VAR_SMTP_SASL_AUTH_CACHE_ENABLE,
DEF_SMTP_SASL_AUTH_CACHE_ENABLE, &var_smtp_sasl_auth_cache_enable,
        VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
        VAR_SMTP_QUOTE_821_ENV, DEF_SMTP_QUOTE_821_ENV, &var_smtp_quote_821_env,
        VAR_SMTP_DEFER_MXADDR, DEF_SMTP_DEFER_MXADDR, &var_smtp_defer_mxaddr,
        VAR_SMTP_SEND_XFORWARD, DEF_SMTP_SEND_XFORWARD, &var_smtp_send_xforward,
        VAR_SMTP_CACHE_DEMAND, DEF_SMTP_CACHE_DEMAND, &var_smtp_cache_demand,
diff -r -U5 -N postfix-2.4.5.orig/src/smtp/smtp_sasl_glue.c
postfix-2.4.5/src/smtp/smtp_sasl_glue.c
--- postfix-2.4.5.orig/src/smtp/smtp_sasl_glue.c 2007-12-01
12:03:04.000000000 +0000
+++ postfix-2.4.5/src/smtp/smtp_sasl_glue.c 2007-12-01
14:45:57.000000000 +0000
-122,10 +122,13
  /*
   * Application-specific
   */
 #include "smtp.h"
 #include "smtp_sasl.h"
+#include "cache_client.h"
+#include "base64_code.h"
+#include <openssl/sha.h>

 #ifdef USE_SASL_AUTH

  /*
   * Per-host login/password information.
-257,10 +260,87
                             session->host, sasl_opts_val)) == 0)
        msg_fatal("SASL per-connection initialization failed");
     session->sasl_reply = vstring_alloc(20);
 }

+static void smtp_sasl_make_cache_key(VSTRING *buf,const char *host,
+ const char *user,const char *pass) {
+ VSTRING *tmp = vstring_alloc(10);
+ vstring_sprintf(tmp,"%s;%s;%s",host,user,pass);
+ base64_encode(buf,(const char*)SHA1((unsigned char*)STR(tmp),
+ (unsigned long)VSTRING_LEN(tmp),0),SHA_DIGEST_LENGTH);
+ vstring_free(tmp);
+}
+
+static void smtp_sasl_make_cache_value(VSTRING *buf,long timestamp,
+ const char *respdsn, const char *respstr) {
+ vstring_sprintf(buf, "%ld:%s:%s", timestamp, respdsn, respstr);
+}
+
+static int smtp_sasl_parse_cache_value(char *buf, long *timestamp,
+ char **respdsn, char **respstr) {
+ if ((*respdsn = split_at(buf,':')) != 0
+ && (*respstr = split_at(*respdsn,':')) != 0 && alldig(buf)) {
+ *timestamp = atol(buf);
+ return (0);
+ }
+ msg_warn("bad smtp_sasl_auth_cache_map entry: %.100s", buf);
+ return (-1);
+}
+
+/*
+ * Check auth failure cache, and fail if this (relayhost,user,pass)
+ * triple matches an entry that is new enough.
+ */
+static int smtp_sasl_cache_query(SMTP_SESSION *session, DSN_BUF *why) {
+ VSTRING *buf = vstring_alloc(10);
+ VSTRING *get_buf = vstring_alloc(10);
+ VSTRING *put_buf = vstring_alloc(10);
+ char *cached_respdsn = 0;
+ char *cached_respstr = 0;
+ long now = (long) time((time_t *) 0);
+ long timestamp = 0;
+ int status = 0;
+
+ smtp_sasl_make_cache_key(buf, session->host,
session->sasl_username, session->sasl_passwd);
+ if (cache_client_query(STR(buf), get_buf) == 0) {
+ if (vstring_strcpy(put_buf,STR(get_buf))
+ && smtp_sasl_parse_cache_value(STR(get_buf),&timestamp,
+ &cached_respdsn,&cached_respstr) == 0
+ && timestamp + var_smtp_sasl_auth_cache_time > now) {
+ dsb_update(why, cached_respdsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
+ session->host, var_procname, cached_respstr,
+ "SASL [CACHED] authentication failed; server %s : %s",
+ session->namaddr, STR(put_buf));
+ status = -1;
+ } else {
+ /* timeout or corrupt cache entry */
+ cache_client_delete(STR(buf));
+ }
+ }
+ vstring_free(put_buf);
+ vstring_free(get_buf);
+ vstring_free(buf);
+ return (status);
+}
+
+/*
+ * Add (relayhost,user,pass) triple to auth failure cache
+ */
+static void smtp_sasl_cache_update(SMTP_SESSION *session, SMTP_RESP *resp) {
+ VSTRING *buf = vstring_alloc(10);
+ VSTRING *put_buf = vstring_alloc(10);
+ long now = (long) time((time_t *) 0);
+
+ smtp_sasl_make_cache_key(buf, session->host,
session->sasl_username, session->sasl_passwd);
+ smtp_sasl_make_cache_value(put_buf, now, resp->dsn, resp->str);
+ cache_client_update(STR(buf), STR(put_buf));
+
+ vstring_free(put_buf);
+ vstring_free(buf);
+}
+
 /* smtp_sasl_authenticate - run authentication protocol */

 int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
 {
     const char *myname = "smtp_sasl_authenticate";
-277,10 +357,14

     if (msg_verbose)
        msg_info("%s: %s: SASL mechanisms %s",
                 myname, session->namaddr, session->sasl_mechanism_list);

+ if (var_smtp_sasl_auth_cache_enable) {
+ if (smtp_sasl_cache_query(session,why)) { return (0); }
+ }
+
     /*
      * Start the client side authentication protocol.
      */
     result = xsasl_client_first(session->sasl_client,
                                session->sasl_mechanism_list,
-348,10 +432,15
        dsb_update(why, resp->dsn, DSB_DEF_ACTION,
                   DSB_MTYPE_DNS, session->host,
                   var_procname, resp->str,
                   "SASL authentication failed; server %s said: %s",
                   session->namaddr, resp->str);
+
+ if (var_smtp_sasl_auth_cache_enable) {
+ smtp_sasl_cache_update(session, resp);
+ }
+
        return (0);
     }
     return (1);
 }

diff -r -U5 -N postfix-2.4.5.orig/src/util/dict_open.c
postfix-2.4.5/src/util/dict_open.c
--- postfix-2.4.5.orig/src/util/dict_open.c 2007-11-20
08:38:28.000000000 +0000
+++ postfix-2.4.5/src/util/dict_open.c 2007-12-01 16:32:05.000000000 +0000
-142,11 +142,11
 /*
 /* dict_put() stores the specified key and value into the named
 /* dictionary.
 /*
 /* dict_del() removes a dictionary entry, and returns non-zero
-/* in case of success.
+/* if the specified key does not exist in the named dictionary.
 /*
 /* dict_seq() iterates over all members in the named dictionary.
 /* func is define DICT_SEQ_FUN_FIRST (select first member) or
 /* DICT_SEQ_FUN_NEXT (select next member). A null result means
 /* there is more.