summaryrefslogtreecommitdiffstats
path: root/src/xsasl
diff options
context:
space:
mode:
Diffstat (limited to '')
l---------src/xsasl/.indent.pro1
-rw-r--r--src/xsasl/Makefile.in165
-rw-r--r--src/xsasl/README109
-rw-r--r--src/xsasl/xsasl.h146
-rw-r--r--src/xsasl/xsasl_client.c240
-rw-r--r--src/xsasl/xsasl_cyrus.h47
-rw-r--r--src/xsasl/xsasl_cyrus_client.c585
-rw-r--r--src/xsasl/xsasl_cyrus_common.h39
-rw-r--r--src/xsasl/xsasl_cyrus_log.c104
-rw-r--r--src/xsasl/xsasl_cyrus_security.c87
-rw-r--r--src/xsasl/xsasl_cyrus_server.c640
-rw-r--r--src/xsasl/xsasl_dovecot.h41
-rw-r--r--src/xsasl/xsasl_dovecot_server.c747
-rw-r--r--src/xsasl/xsasl_server.c270
14 files changed, 3221 insertions, 0 deletions
diff --git a/src/xsasl/.indent.pro b/src/xsasl/.indent.pro
new file mode 120000
index 0000000..5c837ec
--- /dev/null
+++ b/src/xsasl/.indent.pro
@@ -0,0 +1 @@
+../../.indent.pro \ No newline at end of file
diff --git a/src/xsasl/Makefile.in b/src/xsasl/Makefile.in
new file mode 100644
index 0000000..ad48302
--- /dev/null
+++ b/src/xsasl/Makefile.in
@@ -0,0 +1,165 @@
+SHELL = /bin/sh
+SRCS = xsasl_server.c xsasl_cyrus_server.c xsasl_cyrus_log.c \
+ xsasl_cyrus_security.c xsasl_client.c xsasl_cyrus_client.c \
+ xsasl_dovecot_server.c
+OBJS = xsasl_server.o xsasl_cyrus_server.o xsasl_cyrus_log.o \
+ xsasl_cyrus_security.o xsasl_client.o xsasl_cyrus_client.o \
+ xsasl_dovecot_server.o
+HDRS = xsasl.h
+TESTSRC =
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+INCL =
+LIB = libxsasl.a
+TESTPROG=
+
+LIBS = ../../lib/lib$(LIB_PERFIX)global$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PERFIX)util$(LIB_SUFFIX)
+LIB_DIR = ../../lib
+INC_DIR = ../../include
+MAKES =
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+all: $(LIB)
+
+$(OBJS): ../../conf/makedefs.out
+
+Makefile: Makefile.in
+ cat ../../conf/makedefs.out $? >$@
+
+test: $(TESTPROG)
+
+tests:
+
+root_tests:
+
+$(LIB): $(OBJS)
+ $(_AR) $(ARFL) $(LIB) $?
+ $(_RANLIB) $(LIB)
+
+$(LIB_DIR)/$(LIB): $(LIB)
+ cp $(LIB) $(LIB_DIR)
+ $(_RANLIB) $(LIB_DIR)/$(LIB)
+
+update: $(LIB_DIR)/$(LIB) $(HDRS)
+ -for i in $(HDRS); \
+ do \
+ cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \
+ done
+ cd $(INC_DIR); chmod 644 $(HDRS)
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h 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 $(LIB) *core $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+foo: $(LIB) $(LIBS)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+ mv junk $@.o
+
+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 | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+xsasl_client.o: ../../include/argv.h
+xsasl_client.o: ../../include/check_arg.h
+xsasl_client.o: ../../include/msg.h
+xsasl_client.o: ../../include/mymalloc.h
+xsasl_client.o: ../../include/sys_defs.h
+xsasl_client.o: ../../include/vbuf.h
+xsasl_client.o: ../../include/vstream.h
+xsasl_client.o: ../../include/vstring.h
+xsasl_client.o: xsasl.h
+xsasl_client.o: xsasl_client.c
+xsasl_client.o: xsasl_cyrus.h
+xsasl_cyrus_client.o: ../../include/argv.h
+xsasl_cyrus_client.o: ../../include/check_arg.h
+xsasl_cyrus_client.o: ../../include/mail_params.h
+xsasl_cyrus_client.o: ../../include/msg.h
+xsasl_cyrus_client.o: ../../include/mymalloc.h
+xsasl_cyrus_client.o: ../../include/stringops.h
+xsasl_cyrus_client.o: ../../include/sys_defs.h
+xsasl_cyrus_client.o: ../../include/vbuf.h
+xsasl_cyrus_client.o: ../../include/vstream.h
+xsasl_cyrus_client.o: ../../include/vstring.h
+xsasl_cyrus_client.o: xsasl.h
+xsasl_cyrus_client.o: xsasl_cyrus.h
+xsasl_cyrus_client.o: xsasl_cyrus_client.c
+xsasl_cyrus_client.o: xsasl_cyrus_common.h
+xsasl_cyrus_log.o: ../../include/msg.h
+xsasl_cyrus_log.o: ../../include/sys_defs.h
+xsasl_cyrus_log.o: xsasl_cyrus_common.h
+xsasl_cyrus_log.o: xsasl_cyrus_log.c
+xsasl_cyrus_security.o: ../../include/check_arg.h
+xsasl_cyrus_security.o: ../../include/name_mask.h
+xsasl_cyrus_security.o: ../../include/sys_defs.h
+xsasl_cyrus_security.o: ../../include/vbuf.h
+xsasl_cyrus_security.o: ../../include/vstring.h
+xsasl_cyrus_security.o: xsasl_cyrus_common.h
+xsasl_cyrus_security.o: xsasl_cyrus_security.c
+xsasl_cyrus_server.o: ../../include/argv.h
+xsasl_cyrus_server.o: ../../include/check_arg.h
+xsasl_cyrus_server.o: ../../include/mail_params.h
+xsasl_cyrus_server.o: ../../include/msg.h
+xsasl_cyrus_server.o: ../../include/mymalloc.h
+xsasl_cyrus_server.o: ../../include/name_mask.h
+xsasl_cyrus_server.o: ../../include/stringops.h
+xsasl_cyrus_server.o: ../../include/sys_defs.h
+xsasl_cyrus_server.o: ../../include/vbuf.h
+xsasl_cyrus_server.o: ../../include/vstream.h
+xsasl_cyrus_server.o: ../../include/vstring.h
+xsasl_cyrus_server.o: xsasl.h
+xsasl_cyrus_server.o: xsasl_cyrus.h
+xsasl_cyrus_server.o: xsasl_cyrus_common.h
+xsasl_cyrus_server.o: xsasl_cyrus_server.c
+xsasl_dovecot_server.o: ../../include/argv.h
+xsasl_dovecot_server.o: ../../include/check_arg.h
+xsasl_dovecot_server.o: ../../include/connect.h
+xsasl_dovecot_server.o: ../../include/iostuff.h
+xsasl_dovecot_server.o: ../../include/mail_params.h
+xsasl_dovecot_server.o: ../../include/msg.h
+xsasl_dovecot_server.o: ../../include/myaddrinfo.h
+xsasl_dovecot_server.o: ../../include/mymalloc.h
+xsasl_dovecot_server.o: ../../include/name_mask.h
+xsasl_dovecot_server.o: ../../include/split_at.h
+xsasl_dovecot_server.o: ../../include/stringops.h
+xsasl_dovecot_server.o: ../../include/sys_defs.h
+xsasl_dovecot_server.o: ../../include/vbuf.h
+xsasl_dovecot_server.o: ../../include/vstream.h
+xsasl_dovecot_server.o: ../../include/vstring.h
+xsasl_dovecot_server.o: ../../include/vstring_vstream.h
+xsasl_dovecot_server.o: xsasl.h
+xsasl_dovecot_server.o: xsasl_dovecot.h
+xsasl_dovecot_server.o: xsasl_dovecot_server.c
+xsasl_server.o: ../../include/argv.h
+xsasl_server.o: ../../include/check_arg.h
+xsasl_server.o: ../../include/msg.h
+xsasl_server.o: ../../include/mymalloc.h
+xsasl_server.o: ../../include/sys_defs.h
+xsasl_server.o: ../../include/vbuf.h
+xsasl_server.o: ../../include/vstream.h
+xsasl_server.o: ../../include/vstring.h
+xsasl_server.o: xsasl.h
+xsasl_server.o: xsasl_cyrus.h
+xsasl_server.o: xsasl_dovecot.h
+xsasl_server.o: xsasl_server.c
diff --git a/src/xsasl/README b/src/xsasl/README
new file mode 100644
index 0000000..d1b2f5a
--- /dev/null
+++ b/src/xsasl/README
@@ -0,0 +1,109 @@
+Purpose of this document
+========================
+
+This document describes how to add your own SASL implementation to
+Postfix. You don't have to provide both the server and client side.
+You can provide just one and omit the other. The examples below
+assume you do both.
+
+The plug-in API is described in cyrus_server.c and cyrus_client.c.
+It was unavoidably contaminated^h^h^h^h^h^h^h^h^h^h^h^hinfluenced
+by Cyrus SASL and may need revision as other implementations are
+added.
+
+For an example of how the plug-in interface is implemented, have a
+look at the xsasl/xsasl_cyrus_client.c and xsasl/xsasl_cyrus_server.c.
+
+Configuration features
+======================
+
+There are two configuration parameters that allow you to pass
+information from main.cf into the plug_in:
+
+ smtpd_sasl_path, smtpd_sasl_security_options
+ smtp_sasl_path, smtp_sasl_security_options
+ lmtp_sasl_path, lmtp_sasl_security_options
+
+As usual, newline characters are removed from multi-line parameter
+values, and $name is expanded recursively. The parameter values
+are passed to the plug-in without any further processing. The
+following restrictions are imposed by the main.cf file parser:
+
+- parameter values never contain newlines,
+
+- parameter values never start or end with whitespace characters.
+
+The _path parameter value is passed only once during process
+initialization (i.e. it is a class variable). The path typically
+specifies the location of a configuration file or rendez-vous point.
+The _security_options parameter value is passed each time SASL is
+turned on for a connection (i.e. it is an instance variable). The
+options may depend on whether or not TLS encryption is turned on.
+Remember that one Postfix process may perform up to 100 mail
+transactions during its life time. Things that happen in one
+transaction must not affect later transactions.
+
+Adding Postfix support for your own SASL implementation
+=======================================================
+
+To add your own SASL implementation, say, FOOBAR:
+
+- Copy xsasl/xsasl_cyrus.h to xsasl/xsasl_foobar.h and replace
+ CYRUS by FOOBAR:
+
+ #if defined(USE_SASL_AUTH) && defined(USE_FOOBAR_SASL)
+ /*
+ * SASL protocol interface
+ */
+ #define XSASL_TYPE_FOOBAR "foobar"
+ extern XSASL_SERVER_IMPL *xsasl_foobar_server_init(const char *, const char *);
+ extern XSASL_CLIENT_IMPL *xsasl_foobar_client_init(const char *, const char *);
+ #endif
+
+- Edit xsasl/xsasl_server.c, add your #include <xsasl_foobar.h> line
+ under #include <xsasl_cyrus.h> at the top, and add your initialization
+ function in the table at the bottom as shown below:
+
+ static XSASL_SERVER_IMPL_INFO server_impl_info[] = {
+ #ifdef XSASL_TYPE_CYRUS
+ XSASL_TYPE_CYRUS, xsasl_cyrus_server_init,
+ #endif
+ #ifdef XSASL_TYPE_FOOBAR
+ XSASL_TYPE_FOOBAR, xsasl_foobar_server_init,
+ #endif
+ 0,
+ };
+
+- Repeat the (almost) same procedure for xsasl/xsasl_client.c.
+
+- Create your own xsasl/xsasl_foobar_{client,server}.c and support
+ files. Perhaps it's convenient to copy the cyrus files, rip out
+ the function bodies, and replace CYRUS by FOOBAR.
+
+- List your source files in Makefile.in. Don't forget to do "make
+ depend" after you do "make makefiles" in the step that follows
+ after this one.
+
+ SRCS = xsasl_server.c xsasl_cyrus_server.c xsasl_cyrus_log.c \
+ xsasl_cyrus_security.c xsasl_client.c xsasl_cyrus_client.c \
+ xsasl_foobar_client.c xsasl_foobar_server.c
+ OBJS = xsasl_server.o xsasl_cyrus_server.o xsasl_cyrus_log.o \
+ xsasl_cyrus_security.o xsasl_client.o xsasl_cyrus_client.o \
+ xsasl_foobar_client.o xsasl_foobar_server.o
+
+- Create the Postfix makefiles from the top-level directory:
+
+ % make makefiles CCARGS='-DUSE_SASL_AUTH -DUSE_FOOBAR_SASL \
+ -DDEF_CLIENT_SASL_TYPE=\"foobar\" -DDEF_SERVER_SASL_TYPE=\"foobar\" \
+ -I/some/where/include' AUXLIBS='-L/some/where/lib -lfoobar'
+
+ Yes, you can have different default SASL implementation types for
+ the client and server plug-ins.
+
+ Of course you don't have to override the default SASL implementation
+ type; it is shown here as an example.
+
+
+- Don't forget to do "make depend" in the xsasl directory.
+
+- Document your build and configuration with a README document.
diff --git a/src/xsasl/xsasl.h b/src/xsasl/xsasl.h
new file mode 100644
index 0000000..b494d7e
--- /dev/null
+++ b/src/xsasl/xsasl.h
@@ -0,0 +1,146 @@
+#ifndef _XSASL_H_INCLUDED_
+#define _XSASL_H_INCLUDED_
+
+/*++
+/* NAME
+/* xsasl 3h
+/* SUMMARY
+/* Postfix SASL plug-in interface
+/* SYNOPSIS
+/* #include <xsasl.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Generic server object. Specific instances extend this with their own
+ * private data.
+ */
+typedef struct XSASL_SERVER {
+ void (*free) (struct XSASL_SERVER *);
+ int (*first) (struct XSASL_SERVER *, const char *, const char *, VSTRING *);
+ int (*next) (struct XSASL_SERVER *, const char *, VSTRING *);
+ const char *(*get_mechanism_list) (struct XSASL_SERVER *);
+ const char *(*get_username) (struct XSASL_SERVER *);
+} XSASL_SERVER;
+
+#define xsasl_server_free(server) (server)->free(server)
+#define xsasl_server_first(server, method, init_resp, reply) \
+ (server)->first((server), (method), (init_resp), (reply))
+#define xsasl_server_next(server, request, reply) \
+ (server)->next((server), (request), (reply))
+#define xsasl_server_get_mechanism_list(server) \
+ (server)->get_mechanism_list((server))
+#define xsasl_server_get_username(server) \
+ (server)->get_username((server))
+
+ /*
+ * Generic server implementation. Specific instances extend this with their
+ * own private data.
+ */
+typedef struct XSASL_SERVER_CREATE_ARGS {
+ VSTREAM *stream;
+ int addr_family;
+ const char *server_addr;
+ const char *server_port;
+ const char *client_addr;
+ const char *client_port;
+ const char *service;
+ const char *user_realm;
+ const char *security_options;
+ int tls_flag;
+} XSASL_SERVER_CREATE_ARGS;
+
+typedef struct XSASL_SERVER_IMPL {
+ XSASL_SERVER *(*create) (struct XSASL_SERVER_IMPL *, XSASL_SERVER_CREATE_ARGS *);
+ void (*done) (struct XSASL_SERVER_IMPL *);
+} XSASL_SERVER_IMPL;
+
+extern XSASL_SERVER_IMPL *xsasl_server_init(const char *, const char *);
+extern ARGV *xsasl_server_types(void);
+
+#define xsasl_server_create(impl, args) \
+ (impl)->create((impl), (args))
+#define XSASL_SERVER_CREATE(impl, args, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ xsasl_server_create((impl), (((args)->a1), ((args)->a2), ((args)->a3), \
+ ((args)->a4), ((args)->a5), ((args)->a6), ((args)->a7), ((args)->a8), \
+ ((args)->a9), ((args)->a10), (args)))
+#define xsasl_server_done(impl) (impl)->done((impl));
+
+ /*
+ * Generic client object. Specific instances extend this with their own
+ * private data.
+ */
+typedef struct XSASL_CLIENT {
+ void (*free) (struct XSASL_CLIENT *);
+ int (*first) (struct XSASL_CLIENT *, const char *, const char *, const char *, const char **, VSTRING *);
+ int (*next) (struct XSASL_CLIENT *, const char *, VSTRING *);
+} XSASL_CLIENT;
+
+#define xsasl_client_free(client) (client)->free(client)
+#define xsasl_client_first(client, server, method, user, pass, init_resp) \
+ (client)->first((client), (server), (method), (user), (pass), (init_resp))
+#define xsasl_client_next(client, request, reply) \
+ (client)->next((client), (request), (reply))
+#define xsasl_client_set_password(client, user, pass) \
+ (client)->set_password((client), (user), (pass))
+
+ /*
+ * Generic client implementation. Specific instances extend this with their
+ * own private data.
+ */
+typedef struct XSASL_CLIENT_CREATE_ARGS {
+ VSTREAM *stream;
+ const char *service;
+ const char *server_name;
+ const char *security_options;
+} XSASL_CLIENT_CREATE_ARGS;
+
+typedef struct XSASL_CLIENT_IMPL {
+ XSASL_CLIENT *(*create) (struct XSASL_CLIENT_IMPL *, XSASL_CLIENT_CREATE_ARGS *);
+ void (*done) (struct XSASL_CLIENT_IMPL *);
+} XSASL_CLIENT_IMPL;
+
+extern XSASL_CLIENT_IMPL *xsasl_client_init(const char *, const char *);
+extern ARGV *xsasl_client_types(void);
+
+#define xsasl_client_create(impl, args) \
+ (impl)->create((impl), (args))
+#define XSASL_CLIENT_CREATE(impl, args, a1, a2, a3, a4) \
+ xsasl_client_create((impl), (((args)->a1), ((args)->a2), ((args)->a3), \
+ ((args)->a4), (args)))
+#define xsasl_client_done(impl) (impl)->done((impl));
+
+ /*
+ * Status codes.
+ */
+#define XSASL_AUTH_OK 1 /* Success */
+#define XSASL_AUTH_MORE 2 /* Need another c/s protocol exchange */
+#define XSASL_AUTH_DONE 3 /* Authentication completed */
+#define XSASL_AUTH_FORM 4 /* Cannot decode response */
+#define XSASL_AUTH_FAIL 5 /* Error */
+#define XSASL_AUTH_TEMP 6 /* Temporary error condition */
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#endif
diff --git a/src/xsasl/xsasl_client.c b/src/xsasl/xsasl_client.c
new file mode 100644
index 0000000..0bddd41
--- /dev/null
+++ b/src/xsasl/xsasl_client.c
@@ -0,0 +1,240 @@
+/*++
+/* NAME
+/* xsasl_client 3
+/* SUMMARY
+/* Postfix SASL client plug-in interface
+/* SYNOPSIS
+/* #include <xsasl.h>
+/*
+/* XSASL_CLIENT_IMPL *xsasl_client_init(client_type, path_info)
+/* const char *client_type;
+/* const char *path_info;
+/*
+/* void xsasl_client_done(implementation)
+/* XSASL_CLIENT_IMPL *implementation;
+/*
+/* ARGV *xsasl_client_types()
+/*
+/* .in +4
+/* typedef struct XSASL_CLIENT_CREATE_ARGS {
+/* VSTREAM *stream;
+/* const char *service;
+/* const char *server_name;
+/* const char *security_options;
+/* } XSASL_CLIENT_CREATE_ARGS;
+/* .in -4
+/*
+/* XSASL_CLIENT *xsasl_client_create(implementation, create_args)
+/* XSASL_CLIENT_IMPL *implementation;
+/* XSASL_CLIENT_CREATE_ARGS *create_args;
+/*
+/* XSASL_CLIENT *XSASL_CLIENT_CREATE(implementation, create_args,
+/* stream = stream_val,
+/* ...,
+/* security_options = prop_val)
+/* XSASL_CLIENT_IMPL *implementation;
+/* XSASL_CLIENT_CREATE_ARGS *create_args;
+/*
+/* void xsasl_client_free(client)
+/* XSASL_CLIENT *client;
+/*
+/* int xsasl_client_first(client, stream, mech_list, username,
+/* password, auth_method, init_resp)
+/* XSASL_CLIENT *client;
+/* const char *mech_list;
+/* const char *username;
+/* const char *password;
+/* const char **auth_method;
+/* VSTRING *init_resp;
+/*
+/* int xsasl_client_next(client, server_reply, client_reply)
+/* XSASL_CLIENT *client;
+/* const char *server_reply;
+/* VSTRING *client_reply;
+/* DESCRIPTION
+/* The XSASL_CLIENT abstraction implements a generic interface
+/* to one or more SASL authentication implementations.
+/*
+/* xsasl_client_init() is called once during process initialization.
+/* It selects a SASL implementation by name, specifies the
+/* location of a configuration file or rendez-vous point, and
+/* returns an implementation handle that can be used to generate
+/* SASL client instances. This function is typically used to
+/* initialize the underlying implementation.
+/*
+/* xsasl_client_done() disposes of an implementation handle,
+/* and allows the underlying implementation to release resources.
+/*
+/* xsasl_client_types() lists the available implementation types.
+/* The result should be destroyed by the caller.
+/*
+/* xsasl_client_create() is called at the start of an SMTP
+/* session. It generates a Postfix SASL plug-in client instance
+/* for the specified service and server name, with the specified
+/* security properties. The stream handle is stored so that
+/* encryption can be turned on after successful negotiations.
+/*
+/* XSASL_CLIENT_CREATE() is a macro that provides an interface
+/* with named parameters. Named parameters do not have to
+/* appear in a fixed order. The parameter names correspond to
+/* the member names of the XSASL_CLIENT_CREATE_ARGS structure.
+/*
+/* xsasl_client_free() is called at the end of an SMTP session.
+/* It destroys a SASL client instance, and disables further
+/* read/write operations if encryption was turned on.
+/*
+/* xsasl_client_first() produces the client input for the AUTH
+/* command. The input is an authentication method list from
+/* an EHLO response, a username and a password. On return, the
+/* method argument specifies the authentication method; storage
+/* space is owned by the underlying implementation. The initial
+/* response and client non-error replies are BASE64 encoded.
+/* Client error replies are 7-bit ASCII text without control
+/* characters, and without BASE64 encoding. They are meant for
+/* the local application, not for transmission to the server.
+/* The client may negotiate encryption of the client-server
+/* connection.
+/*
+/* The result is one of the following:
+/* .IP XSASL_AUTH_OK
+/* Success.
+/* .IP XSASL_AUTH_FORM
+/* The server reply is incorrectly formatted. The client error
+/* reply explains why.
+/* .IP XSASL_AUTH_FAIL
+/* Other error. The client error reply explains why.
+/* .PP
+/* xsasl_client_next() supports the subsequent stages of the
+/* AUTH protocol. Both the client reply and client non-error
+/* responses are BASE64 encoded. See xsasl_client_first() for
+/* other details.
+/*
+/* Arguments:
+/* .IP client
+/* SASL plug-in client handle.
+/* .IP client_reply
+/* BASE64 encoded non-error client reply, or ASCII error
+/* description for the user.
+/* .IP client_type
+/* The name of a Postfix SASL client plug_in implementation.
+/* .IP client_types
+/* Null-terminated array of strings with SASL client plug-in
+/* implementation names.
+/* .IP init_resp
+/* The AUTH command initial response.
+/* .IP implementation
+/* Implementation handle that was obtained with xsasl_client_init().
+/* .IP mech_list
+/* List of SASL mechanisms as announced by the server.
+/* .IP auth_method
+/* The AUTH command authentication method.
+/* .IP password
+/* Information from the Postfix SASL password file or equivalent.
+/* .IP path_info
+/* The value of the smtp_sasl_path parameter or equivalent.
+/* This specifies the implementation-dependent location of a
+/* configuration file, rendez-vous point, etc., and is passed
+/* unchanged to the plug-in.
+/* .IP security_options
+/* The value of the smtp_sasl_security_options parameter or
+/* equivalent. This is passed unchanged to the plug-in.
+/* .IP server_name
+/* The remote server fully qualified hostname.
+/* .IP server_reply
+/* BASE64 encoded server reply without SMTP reply code or
+/* enhanced status code.
+/* .IP service
+/* The service that is implemented by the local client (typically,
+/* "lmtp" or "smtp").
+/* .IP stream
+/* The connection between client and server.
+/* When SASL encryption is negotiated, the plug-in will
+/* transparently intercept the socket read/write operations.
+/* .IP username
+/* Information from the Postfix SASL password file.
+/* SECURITY
+/* .ad
+/* .fi
+/* The caller does not sanitize the server reply. It is the
+/* responsibility of the underlying SASL client implementation
+/* to produce 7-bit ASCII without control characters as client
+/* non-error and error replies.
+/* DIAGNOSTICS
+/* In case of error, xsasl_client_init() and xsasl_client_create()
+/* log a warning and return a null pointer.
+/*
+/* Functions that normally return XSASL_AUTH_OK will log a warning
+/* and return an appropriate result value.
+/*
+/* Panic: interface violation.
+/*
+/* Fatal errors: out of memory.
+/* SEE ALSO
+/* cyrus_security(3) Cyrus SASL security features
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this
+/* software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* SASL implementations. */
+
+#include <xsasl.h>
+#include <xsasl_cyrus.h>
+
+ /*
+ * Lookup table for available SASL client implementations.
+ */
+typedef struct {
+ char *client_type;
+ struct XSASL_CLIENT_IMPL *(*client_init) (const char *, const char *);
+} XSASL_CLIENT_IMPL_INFO;
+
+static const XSASL_CLIENT_IMPL_INFO client_impl_info[] = {
+#ifdef XSASL_TYPE_CYRUS
+ XSASL_TYPE_CYRUS, xsasl_cyrus_client_init,
+#endif
+ 0,
+};
+
+/* xsasl_client_init - look up client implementation by name */
+
+XSASL_CLIENT_IMPL *xsasl_client_init(const char *client_type,
+ const char *path_info)
+{
+ const XSASL_CLIENT_IMPL_INFO *xp;
+
+ for (xp = client_impl_info; xp->client_type; xp++)
+ if (strcmp(client_type, xp->client_type) == 0)
+ return (xp->client_init(client_type, path_info));
+ msg_warn("unsupported SASL client implementation: %s", client_type);
+ return (0);
+}
+
+/* xsasl_client_types - report available implementation types */
+
+ARGV *xsasl_client_types(void)
+{
+ const XSASL_CLIENT_IMPL_INFO *xp;
+ ARGV *argv = argv_alloc(1);
+
+ for (xp = client_impl_info; xp->client_type; xp++)
+ argv_add(argv, xp->client_type, ARGV_END);
+ return (argv);
+}
diff --git a/src/xsasl/xsasl_cyrus.h b/src/xsasl/xsasl_cyrus.h
new file mode 100644
index 0000000..ad8557e
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus.h
@@ -0,0 +1,47 @@
+#ifndef _XSASL_CYRUS_H_INCLUDED_
+#define _XSASL_CYRUS_H_INCLUDED_
+
+/*++
+/* NAME
+/* xsasl_cyrus 3h
+/* SUMMARY
+/* Cyrus SASL plug-in
+/* SYNOPSIS
+/* #include <xsasl_cyrus.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * XSASL library.
+ */
+#include <xsasl.h>
+
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+ /*
+ * SASL protocol interface
+ */
+#define XSASL_TYPE_CYRUS "cyrus"
+
+extern XSASL_SERVER_IMPL *xsasl_cyrus_server_init(const char *, const char *);
+extern XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(const char *, const char *);
+
+ /*
+ * Internal definitions for client and server module.
+ */
+typedef int (*XSASL_CYRUS_CB) (void);
+
+#endif
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/src/xsasl/xsasl_cyrus_client.c b/src/xsasl/xsasl_cyrus_client.c
new file mode 100644
index 0000000..fc799c9
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus_client.c
@@ -0,0 +1,585 @@
+/*++
+/* NAME
+/* xsasl_cyrus_client 3
+/* SUMMARY
+/* Cyrus SASL client-side plug-in
+/* SYNOPSIS
+/* #include <xsasl_cyrus_client.h>
+/*
+/* XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(client_type, path_info)
+/* const char *client_type;
+/* DESCRIPTION
+/* This module implements the Cyrus SASL client-side authentication
+/* plug-in.
+/*
+/* xsasl_cyrus_client_init() initializes the Cyrus SASL library and
+/* returns an implementation handle that can be used to generate
+/* SASL client instances.
+/*
+/* Arguments:
+/* .IP client_type
+/* The plug-in SASL client type (cyrus). This argument is
+/* ignored, but it could be used when one implementation
+/* provides multiple variants.
+/* .IP path_info
+/* Implementation-specific information to specify the location
+/* of a configuration file, rendez-vous point, etc. This
+/* information is ignored by the Cyrus SASL client plug-in.
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/*
+/* Panic: interface violation.
+/*
+/* Other: the routines log a warning and return an error result
+/* as specified in xsasl_client(3).
+/* SEE ALSO
+/* xsasl_client(3) Client API
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Original author:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+ * Utility library
+ */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+ /*
+ * Global library
+ */
+#include <mail_params.h>
+
+ /*
+ * Application-specific
+ */
+#include <xsasl.h>
+#include <xsasl_cyrus.h>
+#include <xsasl_cyrus_common.h>
+
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+#include <sasl.h>
+#include <saslutil.h>
+
+/*
+ * Silly little macros.
+ */
+#define STR(s) vstring_str(s)
+
+ /*
+ * Macros to handle API differences between SASLv1 and SASLv2. Specifics:
+ *
+ * The SASL_LOG_* constants were renamed in SASLv2.
+ *
+ * SASLv2's sasl_client_new takes two new parameters to specify local and
+ * remote IP addresses for auth mechs that use them.
+ *
+ * SASLv2's sasl_client_start function no longer takes the secret parameter.
+ *
+ * SASLv2's sasl_decode64 function takes an extra parameter for the length of
+ * the output buffer.
+ *
+ * The other major change is that SASLv2 now takes more responsibility for
+ * deallocating memory that it allocates internally. Thus, some of the
+ * function parameters are now 'const', to make sure we don't try to free
+ * them too. This is dealt with in the code later on.
+ */
+#if SASL_VERSION_MAJOR < 2
+/* SASL version 1.x */
+#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
+ sasl_client_new(srv, fqdn, prompt, secflags, pconn)
+#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
+ sasl_client_start(conn, mechlst, secret, prompt, clout, cllen, mech)
+#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
+ sasl_decode64(in, inlen, out, outlen)
+typedef char *CLIENTOUT_TYPE;
+
+#endif
+
+#if SASL_VERSION_MAJOR >= 2
+/* SASL version > 2.x */
+#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
+ sasl_client_new(srv, fqdn, lport, rport, prompt, secflags, pconn)
+#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
+ sasl_client_start(conn, mechlst, prompt, clout, cllen, mech)
+#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
+ sasl_decode64(in, inlen, out, outmaxlen, outlen)
+typedef const char *CLIENTOUT_TYPE;
+
+#endif
+
+ /*
+ * The XSASL_CYRUS_CLIENT object is derived from the generic XSASL_CLIENT
+ * object.
+ */
+typedef struct {
+ XSASL_CLIENT xsasl; /* generic members, must be first */
+ VSTREAM *stream; /* client-server connection */
+ sasl_conn_t *sasl_conn; /* SASL context */
+ VSTRING *decoded; /* decoded server challenge */
+ sasl_callback_t *callbacks; /* user/password lookup */
+ char *username;
+ char *password;
+} XSASL_CYRUS_CLIENT;
+
+ /*
+ * Forward declarations.
+ */
+static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *);
+static XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *,
+ XSASL_CLIENT_CREATE_ARGS *);
+static int xsasl_cyrus_client_set_security(XSASL_CLIENT *, const char *);
+static int xsasl_cyrus_client_first(XSASL_CLIENT *, const char *, const char *,
+ const char *, const char **, VSTRING *);
+static int xsasl_cyrus_client_next(XSASL_CLIENT *, const char *, VSTRING *);
+static void xsasl_cyrus_client_free(XSASL_CLIENT *);
+
+/* xsasl_cyrus_client_get_user - username lookup call-back routine */
+
+static int xsasl_cyrus_client_get_user(void *context, int unused_id,
+ const char **result,
+ unsigned *len)
+{
+ const char *myname = "xsasl_cyrus_client_get_user";
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, client->username);
+
+ /*
+ * Sanity check.
+ */
+ if (client->password == 0)
+ msg_panic("%s: no username looked up", myname);
+
+ *result = client->username;
+ if (len)
+ *len = strlen(client->username);
+ return (SASL_OK);
+}
+
+/* xsasl_cyrus_client_get_passwd - password lookup call-back routine */
+
+static int xsasl_cyrus_client_get_passwd(sasl_conn_t *conn, void *context,
+ int id, sasl_secret_t **psecret)
+{
+ const char *myname = "xsasl_cyrus_client_get_passwd";
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
+ int len;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, client->password);
+
+ /*
+ * Sanity check.
+ */
+ if (!conn || !psecret || id != SASL_CB_PASS)
+ return (SASL_BADPARAM);
+ if (client->password == 0)
+ msg_panic("%s: no password looked up", myname);
+
+ /*
+ * Convert the password into a counted string.
+ */
+ len = strlen(client->password);
+ if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
+ return (SASL_NOMEM);
+ (*psecret)->len = len;
+ memcpy((*psecret)->data, client->password, len + 1);
+
+ return (SASL_OK);
+}
+
+/* xsasl_cyrus_client_init - initialize Cyrus SASL library */
+
+XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(const char *unused_client_type,
+ const char *unused_path_info)
+{
+ XSASL_CLIENT_IMPL *xp;
+ int sasl_status;
+
+ /*
+ * Global callbacks. These have no per-session context.
+ */
+ static sasl_callback_t callbacks[] = {
+ {SASL_CB_LOG, (XSASL_CYRUS_CB) &xsasl_cyrus_log, 0},
+ {SASL_CB_LIST_END, 0, 0}
+ };
+
+#if SASL_VERSION_MAJOR >= 2 && (SASL_VERSION_MINOR >= 2 \
+ || (SASL_VERSION_MINOR == 1 && SASL_VERSION_STEP >= 19))
+ int sasl_major;
+ int sasl_minor;
+ int sasl_step;
+
+ /*
+ * DLL hell guard.
+ */
+ sasl_version_info((const char **) 0, (const char **) 0,
+ &sasl_major, &sasl_minor,
+ &sasl_step, (int *) 0);
+ if (sasl_major != SASL_VERSION_MAJOR
+#if 0
+ || sasl_minor != SASL_VERSION_MINOR
+ || sasl_step != SASL_VERSION_STEP
+#endif
+ ) {
+ msg_warn("incorrect SASL library version. "
+ "Postfix was built with include files from version %d.%d.%d, "
+ "but the run-time library version is %d.%d.%d",
+ SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
+ sasl_major, sasl_minor, sasl_step);
+ return (0);
+ }
+#endif
+
+ if (*var_cyrus_conf_path) {
+#ifdef SASL_PATH_TYPE_CONFIG /* Cyrus SASL 2.1.22 */
+ if (sasl_set_path(SASL_PATH_TYPE_CONFIG,
+ var_cyrus_conf_path) != SASL_OK)
+ msg_warn("failed to set Cyrus SASL configuration path: \"%s\"",
+ var_cyrus_conf_path);
+#else
+ msg_warn("%s is not empty, but setting the Cyrus SASL configuration "
+ "path is not supported with SASL library version %d.%d.%d",
+ VAR_CYRUS_CONF_PATH, SASL_VERSION_MAJOR,
+ SASL_VERSION_MINOR, SASL_VERSION_STEP);
+#endif
+ }
+
+ /*
+ * Initialize the SASL library.
+ */
+ if ((sasl_status = sasl_client_init(callbacks)) != SASL_OK) {
+ msg_warn("SASL library initialization error: %s",
+ xsasl_cyrus_strerror(sasl_status));
+ return (0);
+ }
+
+ /*
+ * Return a generic XSASL_CLIENT_IMPL object. We don't need to extend it
+ * with our own methods or data.
+ */
+ xp = (XSASL_CLIENT_IMPL *) mymalloc(sizeof(*xp));
+ xp->create = xsasl_cyrus_client_create;
+ xp->done = xsasl_cyrus_client_done;
+ return (xp);
+}
+
+/* xsasl_cyrus_client_done - dispose of implementation */
+
+static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *impl)
+{
+ myfree((void *) impl);
+ sasl_done();
+}
+
+/* xsasl_cyrus_client_create - per-session SASL initialization */
+
+XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *unused_impl,
+ XSASL_CLIENT_CREATE_ARGS *args)
+{
+ XSASL_CYRUS_CLIENT *client = 0;
+ static sasl_callback_t callbacks[] = {
+ {SASL_CB_USER, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_user, 0},
+ {SASL_CB_AUTHNAME, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_user, 0},
+ {SASL_CB_PASS, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_passwd, 0},
+ {SASL_CB_LIST_END, 0, 0}
+ };
+ sasl_conn_t *sasl_conn = 0;
+ sasl_callback_t *custom_callbacks = 0;
+ sasl_callback_t *cp;
+ int sasl_status;
+
+ /*
+ * The optimizer will eliminate code duplication and/or dead code.
+ */
+#define XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(x) \
+ do { \
+ if (client) { \
+ xsasl_cyrus_client_free(&client->xsasl); \
+ } else { \
+ if (custom_callbacks) \
+ myfree((void *) custom_callbacks); \
+ if (sasl_conn) \
+ sasl_dispose(&sasl_conn); \
+ } \
+ return (x); \
+ } while (0)
+
+ /*
+ * Per-session initialization. Provide each session with its own callback
+ * context.
+ */
+#define NULL_SECFLAGS 0
+
+ custom_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
+ memcpy((void *) custom_callbacks, callbacks, sizeof(callbacks));
+
+#define NULL_SERVER_ADDR ((char *) 0)
+#define NULL_CLIENT_ADDR ((char *) 0)
+
+ if ((sasl_status = SASL_CLIENT_NEW(args->service, args->server_name,
+ NULL_CLIENT_ADDR, NULL_SERVER_ADDR,
+ var_cyrus_sasl_authzid ? custom_callbacks :
+ custom_callbacks + 1, NULL_SECFLAGS,
+ &sasl_conn)) != SASL_OK) {
+ msg_warn("per-session SASL client initialization: %s",
+ xsasl_cyrus_strerror(sasl_status));
+ XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
+ }
+
+ /*
+ * Extend the XSASL_CLIENT object with our own state. We use long-lived
+ * conversion buffers rather than local variables to avoid memory leaks
+ * in case of read/write timeout or I/O error.
+ *
+ * XXX If we enable SASL encryption, there needs to be a way to inform the
+ * application, so that they can turn off connection caching, refuse
+ * STARTTLS, etc.
+ */
+ client = (XSASL_CYRUS_CLIENT *) mymalloc(sizeof(*client));
+ client->xsasl.free = xsasl_cyrus_client_free;
+ client->xsasl.first = xsasl_cyrus_client_first;
+ client->xsasl.next = xsasl_cyrus_client_next;
+ client->stream = args->stream;
+ client->sasl_conn = sasl_conn;
+ client->callbacks = custom_callbacks;
+ client->decoded = vstring_alloc(20);
+ client->username = 0;
+ client->password = 0;
+
+ for (cp = custom_callbacks; cp->id != SASL_CB_LIST_END; cp++)
+ cp->context = (void *) client;
+
+ if (xsasl_cyrus_client_set_security(&client->xsasl,
+ args->security_options)
+ != XSASL_AUTH_OK)
+ XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
+
+ return (&client->xsasl);
+}
+
+/* xsasl_cyrus_client_set_security - set security properties */
+
+static int xsasl_cyrus_client_set_security(XSASL_CLIENT *xp,
+ const char *sasl_opts_val)
+{
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
+ sasl_security_properties_t sec_props;
+ int sasl_status;
+
+ /*
+ * Per-session security properties. XXX This routine is not sufficiently
+ * documented. What is the purpose of all this?
+ */
+ memset(&sec_props, 0, sizeof(sec_props));
+ sec_props.min_ssf = 0;
+ sec_props.max_ssf = 0; /* don't allow real SASL
+ * security layer */
+ if (*sasl_opts_val == 0) {
+ sec_props.security_flags = 0;
+ } else {
+ sec_props.security_flags =
+ xsasl_cyrus_security_parse_opts(sasl_opts_val);
+ if (sec_props.security_flags == 0) {
+ msg_warn("bad per-session SASL security properties");
+ return (XSASL_AUTH_FAIL);
+ }
+ }
+ sec_props.maxbufsize = 0;
+ sec_props.property_names = 0;
+ sec_props.property_values = 0;
+ if ((sasl_status = sasl_setprop(client->sasl_conn, SASL_SEC_PROPS,
+ &sec_props)) != SASL_OK) {
+ msg_warn("set per-session SASL security properties: %s",
+ xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FAIL);
+ }
+ return (XSASL_AUTH_OK);
+}
+
+/* xsasl_cyrus_client_first - run authentication protocol */
+
+static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
+ const char *mechanism_list,
+ const char *username,
+ const char *password,
+ const char **mechanism,
+ VSTRING *init_resp)
+{
+ const char *myname = "xsasl_cyrus_client_first";
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
+ unsigned enc_length;
+ unsigned enc_length_out;
+ CLIENTOUT_TYPE clientout;
+ unsigned clientoutlen;
+ int sasl_status;
+
+#define NO_SASL_SECRET 0
+#define NO_SASL_INTERACTION 0
+
+ /*
+ * Save the username and password for the call-backs.
+ */
+ if (client->username)
+ myfree(client->username);
+ client->username = mystrdup(username);
+ if (client->password)
+ myfree(client->password);
+ client->password = mystrdup(password);
+
+ /*
+ * Start the client side authentication protocol.
+ */
+ sasl_status = SASL_CLIENT_START((sasl_conn_t *) client->sasl_conn,
+ mechanism_list,
+ NO_SASL_SECRET, NO_SASL_INTERACTION,
+ &clientout, &clientoutlen, mechanism);
+ if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
+ vstring_strcpy(init_resp, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FAIL);
+ }
+
+ /*
+ * Generate the AUTH command and the optional initial client response.
+ * sasl_encode64() produces four bytes for each complete or incomplete
+ * triple of input bytes. Allocate an extra byte for string termination.
+ */
+#define ENCODE64_LENGTH(n) ((((n) + 2) / 3) * 4)
+
+ if (clientoutlen > 0) {
+ if (msg_verbose) {
+ escape(client->decoded, clientout, clientoutlen);
+ msg_info("%s: uncoded initial reply: %s",
+ myname, STR(client->decoded));
+ }
+ enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
+ VSTRING_RESET(init_resp); /* Fix 200512 */
+ VSTRING_SPACE(init_resp, enc_length);
+ if ((sasl_status = sasl_encode64(clientout, clientoutlen,
+ STR(init_resp),
+ vstring_avail(init_resp),
+ &enc_length_out)) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch: %s",
+ myname, xsasl_cyrus_strerror(sasl_status));
+ vstring_set_payload_size(init_resp, enc_length_out);
+#if SASL_VERSION_MAJOR < 2
+ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
+#endif
+ } else {
+ vstring_strcpy(init_resp, "");
+ }
+ return (XSASL_AUTH_OK);
+}
+
+/* xsasl_cyrus_client_next - continue authentication */
+
+static int xsasl_cyrus_client_next(XSASL_CLIENT *xp, const char *server_reply,
+ VSTRING *client_reply)
+{
+ const char *myname = "xsasl_cyrus_client_next";
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
+ unsigned enc_length;
+ unsigned enc_length_out;
+ CLIENTOUT_TYPE clientout;
+ unsigned clientoutlen;
+ unsigned serverinlen;
+ int sasl_status;
+
+ /*
+ * Process a server challenge.
+ */
+ serverinlen = strlen(server_reply);
+ VSTRING_RESET(client->decoded); /* Fix 200512 */
+ VSTRING_SPACE(client->decoded, serverinlen);
+ if ((sasl_status = SASL_DECODE64(server_reply, serverinlen,
+ STR(client->decoded),
+ vstring_avail(client->decoded),
+ &enc_length)) != SASL_OK) {
+ vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FORM);
+ }
+ if (msg_verbose)
+ msg_info("%s: decoded challenge: %.*s",
+ myname, (int) enc_length, STR(client->decoded));
+ sasl_status = sasl_client_step(client->sasl_conn, STR(client->decoded),
+ enc_length, NO_SASL_INTERACTION,
+ &clientout, &clientoutlen);
+ if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
+ vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FAIL);
+ }
+
+ /*
+ * Send a client response.
+ */
+ if (clientoutlen > 0) {
+ if (msg_verbose)
+ msg_info("%s: uncoded client response %.*s",
+ myname, (int) clientoutlen, clientout);
+ enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
+ VSTRING_RESET(client_reply); /* Fix 200512 */
+ VSTRING_SPACE(client_reply, enc_length);
+ if ((sasl_status = sasl_encode64(clientout, clientoutlen,
+ STR(client_reply),
+ vstring_avail(client_reply),
+ &enc_length_out)) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch: %s",
+ myname, xsasl_cyrus_strerror(sasl_status));
+#if SASL_VERSION_MAJOR < 2
+ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
+#endif
+ } else {
+ /* XXX Can't happen. */
+ vstring_strcpy(client_reply, "");
+ }
+ return (XSASL_AUTH_OK);
+}
+
+/* xsasl_cyrus_client_free - per-session cleanup */
+
+void xsasl_cyrus_client_free(XSASL_CLIENT *xp)
+{
+ XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
+
+ if (client->username)
+ myfree(client->username);
+ if (client->password)
+ myfree(client->password);
+ if (client->sasl_conn)
+ sasl_dispose(&client->sasl_conn);
+ myfree((void *) client->callbacks);
+ vstring_free(client->decoded);
+ myfree((void *) client);
+}
+
+#endif
diff --git a/src/xsasl/xsasl_cyrus_common.h b/src/xsasl/xsasl_cyrus_common.h
new file mode 100644
index 0000000..5447378
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus_common.h
@@ -0,0 +1,39 @@
+#ifndef _CYRUS_COMMON_H_INCLUDED_
+#define _CYRUS_COMMON_H_INCLUDED_
+
+/*++
+/* NAME
+/* cyrus_common 3h
+/* SUMMARY
+/* Cyrus SASL plug-in helpers
+/* SYNOPSIS
+/* #include <cyrus_common.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+#define NO_SASL_LANGLIST ((const char *) 0)
+#define NO_SASL_OUTLANG ((const char **) 0)
+#define xsasl_cyrus_strerror(status) \
+ sasl_errstring((status), NO_SASL_LANGLIST, NO_SASL_OUTLANG)
+extern int xsasl_cyrus_log(void *, int, const char *);
+extern int xsasl_cyrus_security_parse_opts(const char *);
+
+#endif
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/src/xsasl/xsasl_cyrus_log.c b/src/xsasl/xsasl_cyrus_log.c
new file mode 100644
index 0000000..7bf25c3
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus_log.c
@@ -0,0 +1,104 @@
+/*++
+/* NAME
+/* xsasl_cyrus_log 3
+/* SUMMARY
+/* Cyrus SASL logging call-back routine
+/* SYNOPSIS
+/* #include <xsasl_cyrus_common.h>
+/*
+/* int xsasl_cyrus_log(context, priority, text)
+/* void *context;
+/* int priority;
+/* const char *text;
+/* DESCRIPTION
+/* xsasl_cyrus_log() logs a Cyrus message.
+/* DIAGNOSTICS:
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* Application-specific */
+
+#include <xsasl_cyrus_common.h>
+
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+#include <sasl.h>
+#include <saslutil.h>
+
+/* xsasl_cyrus_log - logging callback */
+
+int xsasl_cyrus_log(void *unused_context, int priority,
+ const char *message)
+{
+ switch (priority) {
+ case SASL_LOG_ERR: /* unusual errors */
+#ifdef SASL_LOG_WARN /* non-fatal warnings (Cyrus-SASL v2) */
+ case SASL_LOG_WARN:
+#endif
+#ifdef SASL_LOG_WARNING /* non-fatal warnings (Cyrus-SASL v1) */
+ case SASL_LOG_WARNING:
+#endif
+ msg_warn("SASL authentication problem: %s", message);
+ break;
+#ifdef SASL_LOG_INFO
+ case SASL_LOG_INFO: /* other info (Cyrus-SASL v1) */
+ if (msg_verbose)
+ msg_info("SASL authentication info: %s", message);
+ break;
+#endif
+#ifdef SASL_LOG_NOTE
+ case SASL_LOG_NOTE: /* other info (Cyrus-SASL v2) */
+ if (msg_verbose)
+ msg_info("SASL authentication info: %s", message);
+ break;
+#endif
+#ifdef SASL_LOG_FAIL
+ case SASL_LOG_FAIL: /* authentication failures
+ * (Cyrus-SASL v2) */
+ msg_warn("SASL authentication failure: %s", message);
+ break;
+#endif
+#ifdef SASL_LOG_DEBUG
+ case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE
+ * (Cyrus-SASL v2) */
+ if (msg_verbose > 1)
+ msg_info("SASL authentication debug: %s", message);
+ break;
+#endif
+#ifdef SASL_LOG_TRACE
+ case SASL_LOG_TRACE: /* traces of internal
+ * protocols (Cyrus-SASL v2) */
+ if (msg_verbose > 1)
+ msg_info("SASL authentication trace: %s", message);
+ break;
+#endif
+#ifdef SASL_LOG_PASS
+ case SASL_LOG_PASS: /* traces of internal
+ * protocols, including
+ * passwords (Cyrus-SASL v2) */
+ if (msg_verbose > 1)
+ msg_info("SASL authentication pass: %s", message);
+ break;
+#endif
+ }
+ return (SASL_OK);
+}
+
+#endif
diff --git a/src/xsasl/xsasl_cyrus_security.c b/src/xsasl/xsasl_cyrus_security.c
new file mode 100644
index 0000000..7ca7216
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus_security.c
@@ -0,0 +1,87 @@
+/*++
+/* NAME
+/* xsasl_cyrus_security 3
+/* SUMMARY
+/* convert Cyrus SASL security properties to bit mask
+/* SYNOPSIS
+/* #include <xsasl_cyrus_common.h>
+/*
+/* int xsasl_cyrus_security_parse_opts(properties)
+/* const char *properties;
+/* DESCRIPTION
+/* xsasl_cyrus_security_parse_opts() converts a list of security
+/* properties to a bit mask. The result is zero in case of error.
+/*
+/* Arguments:
+/* .IP properties
+/* A comma or space separated list of zero or more of the
+/* following:
+/* .RS
+/* .IP noplaintext
+/* Disallow authentication methods that use plaintext passwords.
+/* .IP noactive
+/* Disallow authentication methods that are vulnerable to
+/* non-dictionary active attacks.
+/* .IP nodictionary
+/* Disallow authentication methods that are vulnerable to
+/* passive dictionary attack.
+/* .IP forward_secrecy
+/* Require forward secrecy between sessions.
+/* .IP noanonymous
+/* Disallow anonymous logins.
+/* .RE
+/* DIAGNOSTICS:
+/* Warning: bad input.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <name_mask.h>
+
+/* Application-specific. */
+
+#include <xsasl_cyrus_common.h>
+
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+#include <sasl.h>
+
+ /*
+ * SASL Security options.
+ */
+static const NAME_MASK xsasl_cyrus_sec_mask[] = {
+ "noplaintext", SASL_SEC_NOPLAINTEXT,
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+#ifdef SASL_SEC_FORWARD_SECRECY
+ "forward_secrecy", SASL_SEC_FORWARD_SECRECY,
+#endif
+ "noanonymous", SASL_SEC_NOANONYMOUS,
+#if SASL_VERSION_MAJOR >= 2
+ "mutual_auth", SASL_SEC_MUTUAL_AUTH,
+#endif
+ 0,
+};
+
+/* xsasl_cyrus_security - parse security options */
+
+int xsasl_cyrus_security_parse_opts(const char *sasl_opts_val)
+{
+ return (name_mask_opt("SASL security options", xsasl_cyrus_sec_mask,
+ sasl_opts_val, NAME_MASK_RETURN));
+}
+
+#endif
diff --git a/src/xsasl/xsasl_cyrus_server.c b/src/xsasl/xsasl_cyrus_server.c
new file mode 100644
index 0000000..4bf2ed2
--- /dev/null
+++ b/src/xsasl/xsasl_cyrus_server.c
@@ -0,0 +1,640 @@
+/*++
+/* NAME
+/* xsasl_cyrus_server 3
+/* SUMMARY
+/* Cyrus SASL server-side plug-in
+/* SYNOPSIS
+/* #include <xsasl_cyrus_server.h>
+/*
+/* XSASL_SERVER_IMPL *xsasl_cyrus_server_init(server_type, path_info)
+/* const char *server_type;
+/* const char *path_info;
+/* DESCRIPTION
+/* This module implements the Cyrus SASL server-side authentication
+/* plug-in.
+/*
+/* xsasl_cyrus_server_init() initializes the Cyrus SASL library and
+/* returns an implementation handle that can be used to generate
+/* SASL server instances.
+/*
+/* Arguments:
+/* .IP server_type
+/* The server type (cyrus). This argument is ignored, but it
+/* could be used when one implementation provides multiple
+/* variants.
+/* .IP path_info
+/* The base name of the SASL server configuration file (example:
+/* smtpd becomes /usr/lib/sasl2/smtpd.conf).
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/*
+/* Panic: interface violation.
+/*
+/* Other: the routines log a warning and return an error result
+/* as specified in xsasl_server(3).
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Initial implementation by:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <name_mask.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <xsasl.h>
+#include <xsasl_cyrus.h>
+#include <xsasl_cyrus_common.h>
+
+#if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
+
+#include <sasl.h>
+#include <saslutil.h>
+
+/*
+ * Silly little macros.
+ */
+#define STR(s) vstring_str(s)
+
+ /*
+ * Macros to handle API differences between SASLv1 and SASLv2. Specifics:
+ *
+ * The SASL_LOG_* constants were renamed in SASLv2.
+ *
+ * SASLv2's sasl_server_new takes two new parameters to specify local and
+ * remote IP addresses for auth mechs that use them.
+ *
+ * SASLv2's sasl_server_start and sasl_server_step no longer have the errstr
+ * parameter.
+ *
+ * SASLv2's sasl_decode64 function takes an extra parameter for the length of
+ * the output buffer.
+ *
+ * The other major change is that SASLv2 now takes more responsibility for
+ * deallocating memory that it allocates internally. Thus, some of the
+ * function parameters are now 'const', to make sure we don't try to free
+ * them too. This is dealt with in the code later on.
+ */
+
+#if SASL_VERSION_MAJOR < 2
+/* SASL version 1.x */
+#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
+ sasl_server_new(srv, fqdn, rlm, cb, secflags, pconn)
+#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
+ sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen, err)
+#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
+ sasl_server_step(conn, clin, clinlen, srvout, srvoutlen, err)
+#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
+ sasl_decode64(in, inlen, out, outlen)
+typedef char *MECHANISM_TYPE;
+typedef unsigned MECHANISM_COUNT_TYPE;
+typedef char *SERVEROUT_TYPE;
+typedef void *VOID_SERVEROUT_TYPE;
+
+#endif
+
+#if SASL_VERSION_MAJOR >= 2
+/* SASL version > 2.x */
+#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
+ sasl_server_new(srv, fqdn, rlm, lport, rport, cb, secflags, pconn)
+#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
+ sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen)
+#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
+ sasl_server_step(conn, clin, clinlen, srvout, srvoutlen)
+#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
+ sasl_decode64(in, inlen, out, outmaxlen, outlen)
+typedef const char *MECHANISM_TYPE;
+typedef int MECHANISM_COUNT_TYPE;
+typedef const char *SERVEROUT_TYPE;
+typedef const void *VOID_SERVEROUT_TYPE;
+
+#endif
+
+#ifndef NO_IP_CYRUS_SASL_AUTH
+#define USE_IP_CYRUS_SASL_AUTH
+#endif
+
+ /*
+ * The XSASL_CYRUS_SERVER object is derived from the generic XSASL_SERVER
+ * object.
+ */
+typedef struct {
+ XSASL_SERVER xsasl; /* generic members, must be first */
+ VSTREAM *stream; /* client-server connection */
+ sasl_conn_t *sasl_conn; /* SASL context */
+ VSTRING *decoded; /* decoded challenge or response */
+ char *username; /* authenticated user */
+ char *mechanism_list; /* applicable mechanisms */
+} XSASL_CYRUS_SERVER;
+
+ /*
+ * Forward declarations.
+ */
+static void xsasl_cyrus_server_done(XSASL_SERVER_IMPL *);
+static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *,
+ XSASL_SERVER_CREATE_ARGS *);
+static void xsasl_cyrus_server_free(XSASL_SERVER *);
+static int xsasl_cyrus_server_first(XSASL_SERVER *, const char *,
+ const char *, VSTRING *);
+static int xsasl_cyrus_server_next(XSASL_SERVER *, const char *, VSTRING *);
+static int xsasl_cyrus_server_set_security(XSASL_SERVER *, const char *);
+static const char *xsasl_cyrus_server_get_mechanism_list(XSASL_SERVER *);
+static const char *xsasl_cyrus_server_get_username(XSASL_SERVER *);
+
+ /*
+ * SASL callback interface structure. These call-backs have no per-session
+ * context.
+ */
+#define NO_CALLBACK_CONTEXT 0
+
+static sasl_callback_t callbacks[] = {
+ {SASL_CB_LOG, (XSASL_CYRUS_CB) &xsasl_cyrus_log, NO_CALLBACK_CONTEXT},
+ {SASL_CB_LIST_END, 0, 0}
+};
+
+/* xsasl_cyrus_server_init - create implementation handle */
+
+XSASL_SERVER_IMPL *xsasl_cyrus_server_init(const char *unused_server_type,
+ const char *path_info)
+{
+ const char *myname = "xsasl_cyrus_server_init";
+ XSASL_SERVER_IMPL *xp;
+ int sasl_status;
+
+#if SASL_VERSION_MAJOR >= 2 && (SASL_VERSION_MINOR >= 2 \
+ || (SASL_VERSION_MINOR == 1 && SASL_VERSION_STEP >= 19))
+ int sasl_major;
+ int sasl_minor;
+ int sasl_step;
+
+ /*
+ * DLL hell guard.
+ */
+ sasl_version_info((const char **) 0, (const char **) 0,
+ &sasl_major, &sasl_minor,
+ &sasl_step, (int *) 0);
+ if (sasl_major != SASL_VERSION_MAJOR
+#if 0
+ || sasl_minor != SASL_VERSION_MINOR
+ || sasl_step != SASL_VERSION_STEP
+#endif
+ ) {
+ msg_warn("incorrect SASL library version. "
+ "Postfix was built with include files from version %d.%d.%d, "
+ "but the run-time library version is %d.%d.%d",
+ SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
+ sasl_major, sasl_minor, sasl_step);
+ return (0);
+ }
+#endif
+
+ if (*var_cyrus_conf_path) {
+#ifdef SASL_PATH_TYPE_CONFIG /* Cyrus SASL 2.1.22 */
+ if (sasl_set_path(SASL_PATH_TYPE_CONFIG,
+ var_cyrus_conf_path) != SASL_OK)
+ msg_warn("failed to set Cyrus SASL configuration path: \"%s\"",
+ var_cyrus_conf_path);
+#else
+ msg_warn("%s is not empty, but setting the Cyrus SASL configuration "
+ "path is not supported with SASL library version %d.%d.%d",
+ VAR_CYRUS_CONF_PATH, SASL_VERSION_MAJOR,
+ SASL_VERSION_MINOR, SASL_VERSION_STEP);
+#endif
+ }
+
+ /*
+ * Initialize the library: load SASL plug-in routines, etc.
+ */
+ if (msg_verbose)
+ msg_info("%s: SASL config file is %s.conf", myname, path_info);
+ if ((sasl_status = sasl_server_init(callbacks, path_info)) != SASL_OK) {
+ msg_warn("SASL per-process initialization failed: %s",
+ xsasl_cyrus_strerror(sasl_status));
+ return (0);
+ }
+
+ /*
+ * Return a generic XSASL_SERVER_IMPL object. We don't need to extend it
+ * with our own methods or data.
+ */
+ xp = (XSASL_SERVER_IMPL *) mymalloc(sizeof(*xp));
+ xp->create = xsasl_cyrus_server_create;
+ xp->done = xsasl_cyrus_server_done;
+ return (xp);
+}
+
+/* xsasl_cyrus_server_done - dispose of implementation */
+
+static void xsasl_cyrus_server_done(XSASL_SERVER_IMPL *impl)
+{
+ myfree((void *) impl);
+ sasl_done();
+}
+
+/* xsasl_cyrus_server_create - create server instance */
+
+static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *unused_impl,
+ XSASL_SERVER_CREATE_ARGS *args)
+{
+ const char *myname = "xsasl_cyrus_server_create";
+ char *server_addr_port = 0;
+ char *client_addr_port = 0;
+ sasl_conn_t *sasl_conn = 0;
+ XSASL_CYRUS_SERVER *server = 0;
+ int sasl_status;
+
+ if (msg_verbose)
+ msg_info("%s: SASL service=%s, realm=%s",
+ myname, args->service, args->user_realm ?
+ args->user_realm : "(null)");
+
+ /*
+ * The optimizer will eliminate code duplication and/or dead code.
+ */
+#define XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(x) \
+ do { \
+ if (server) { \
+ xsasl_cyrus_server_free(&server->xsasl); \
+ } else { \
+ if (sasl_conn) \
+ sasl_dispose(&sasl_conn); \
+ } \
+ XSASL_CYRUS_SERVER_CREATE_RETURN(x); \
+ } while (0)
+
+#define XSASL_CYRUS_SERVER_CREATE_RETURN(x) \
+ do { \
+ if (server_addr_port) \
+ myfree(server_addr_port); \
+ if (client_addr_port) \
+ myfree(client_addr_port); \
+ return (x); \
+ } while (0)
+
+ /*
+ * Set up a new server context.
+ */
+#define NO_SECURITY_LAYERS (0)
+#define NO_SESSION_CALLBACKS ((sasl_callback_t *) 0)
+#define NO_AUTH_REALM ((char *) 0)
+
+#if SASL_VERSION_MAJOR >= 2 && defined(USE_IP_CYRUS_SASL_AUTH)
+
+ /*
+ * Get IP address and port of local and remote endpoints for SASL. Some
+ * implementation supports "[ipv6addr]:port" and "ipv4addr:port" (e.g.,
+ * https://illumos.org/man/3sasl/sasl_server_new), They still support the
+ * historical "address;port" syntax, so we stick with that for now.
+ */
+ server_addr_port = (*args->server_addr && *args->server_port ?
+ concatenate(args->server_addr, ";",
+ args->server_port, (char *) 0) : 0);
+ client_addr_port = (*args->client_addr && *args->client_port ?
+ concatenate(args->client_addr, ";",
+ args->client_port, (char *) 0) : 0);
+#else
+
+ /*
+ * Don't give any IP address information to SASL.
+ */
+#endif
+
+ if ((sasl_status =
+ SASL_SERVER_NEW(args->service, var_myhostname,
+ args->user_realm ? args->user_realm : NO_AUTH_REALM,
+ server_addr_port, client_addr_port,
+ NO_SESSION_CALLBACKS, NO_SECURITY_LAYERS,
+ &sasl_conn)) != SASL_OK) {
+ msg_warn("SASL per-connection server initialization: %s",
+ xsasl_cyrus_strerror(sasl_status));
+ XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(0);
+ }
+
+ /*
+ * Extend the XSASL_SERVER object with our own data. We use long-lived
+ * conversion buffers rather than local variables to avoid memory leaks
+ * in case of read/write timeout or I/O error.
+ */
+ server = (XSASL_CYRUS_SERVER *) mymalloc(sizeof(*server));
+ server->xsasl.free = xsasl_cyrus_server_free;
+ server->xsasl.first = xsasl_cyrus_server_first;
+ server->xsasl.next = xsasl_cyrus_server_next;
+ server->xsasl.get_mechanism_list = xsasl_cyrus_server_get_mechanism_list;
+ server->xsasl.get_username = xsasl_cyrus_server_get_username;
+ server->stream = args->stream;
+ server->sasl_conn = sasl_conn;
+ server->decoded = vstring_alloc(20);
+ server->username = 0;
+ server->mechanism_list = 0;
+
+ if (xsasl_cyrus_server_set_security(&server->xsasl, args->security_options)
+ != XSASL_AUTH_OK)
+ XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(0);
+
+ XSASL_CYRUS_SERVER_CREATE_RETURN(&server->xsasl);
+}
+
+/* xsasl_cyrus_server_set_security - set security properties */
+
+static int xsasl_cyrus_server_set_security(XSASL_SERVER *xp,
+ const char *sasl_opts_val)
+{
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+ sasl_security_properties_t sec_props;
+ int sasl_status;
+
+ /*
+ * Security options. Some information can be found in the sasl.h include
+ * file.
+ */
+ memset(&sec_props, 0, sizeof(sec_props));
+ sec_props.min_ssf = 0;
+ sec_props.max_ssf = 0; /* don't allow real SASL
+ * security layer */
+ if (*sasl_opts_val == 0) {
+ sec_props.security_flags = 0;
+ } else {
+ sec_props.security_flags =
+ xsasl_cyrus_security_parse_opts(sasl_opts_val);
+ if (sec_props.security_flags == 0) {
+ msg_warn("bad per-session SASL security properties");
+ return (XSASL_AUTH_FAIL);
+ }
+ }
+ sec_props.maxbufsize = 0;
+ sec_props.property_names = 0;
+ sec_props.property_values = 0;
+
+ if ((sasl_status = sasl_setprop(server->sasl_conn, SASL_SEC_PROPS,
+ &sec_props)) != SASL_OK) {
+ msg_warn("SASL per-connection security setup; %s",
+ xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FAIL);
+ }
+ return (XSASL_AUTH_OK);
+}
+
+/* xsasl_cyrus_server_get_mechanism_list - get available mechanisms */
+
+static const char *xsasl_cyrus_server_get_mechanism_list(XSASL_SERVER *xp)
+{
+ const char *myname = "xsasl_cyrus_server_get_mechanism_list";
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+ MECHANISM_TYPE mechanism_list;
+ MECHANISM_COUNT_TYPE mechanism_count;
+ int sasl_status;
+
+ /*
+ * Get the list of authentication mechanisms.
+ */
+#define UNSUPPORTED_USER ((char *) 0)
+#define IGNORE_MECHANISM_LEN ((unsigned *) 0)
+
+ if ((sasl_status = sasl_listmech(server->sasl_conn, UNSUPPORTED_USER,
+ "", " ", "",
+ &mechanism_list,
+ IGNORE_MECHANISM_LEN,
+ &mechanism_count)) != SASL_OK) {
+ msg_warn("%s: %s", myname, xsasl_cyrus_strerror(sasl_status));
+ return (0);
+ }
+ if (mechanism_count <= 0) {
+ msg_warn("%s: no applicable SASL mechanisms", myname);
+ return (0);
+ }
+ server->mechanism_list = mystrdup(mechanism_list);
+#if SASL_VERSION_MAJOR < 2
+ /* SASL version 1 doesn't free memory that it allocates. */
+ free(mechanism_list);
+#endif
+ return (server->mechanism_list);
+}
+
+/* xsasl_cyrus_server_free - destroy server instance */
+
+static void xsasl_cyrus_server_free(XSASL_SERVER *xp)
+{
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+
+ sasl_dispose(&server->sasl_conn);
+ vstring_free(server->decoded);
+ if (server->username)
+ myfree(server->username);
+ if (server->mechanism_list)
+ myfree(server->mechanism_list);
+ myfree((void *) server);
+}
+
+/* xsasl_cyrus_server_auth_response - encode server first/next response */
+
+static int xsasl_cyrus_server_auth_response(int sasl_status,
+ SERVEROUT_TYPE serverout,
+ unsigned serveroutlen,
+ VSTRING *reply)
+{
+ const char *myname = "xsasl_cyrus_server_auth_response";
+ unsigned enc_length;
+ unsigned enc_length_out;
+
+ /*
+ * Encode the server first/next non-error response; otherwise return the
+ * unencoded error text that corresponds to the SASL error status.
+ *
+ * Regarding the hairy expression below: output from sasl_encode64() 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.
+ */
+ if (sasl_status == SASL_OK) {
+ vstring_strcpy(reply, "");
+ return (XSASL_AUTH_DONE);
+ } else if (sasl_status == SASL_CONTINUE) {
+ if (msg_verbose)
+ msg_info("%s: uncoded server challenge: %.*s",
+ myname, (int) serveroutlen, serverout);
+ enc_length = ((serveroutlen + 2) / 3) * 4 + 1;
+ VSTRING_RESET(reply); /* Fix 200512 */
+ VSTRING_SPACE(reply, enc_length);
+ if ((sasl_status = sasl_encode64(serverout, serveroutlen,
+ STR(reply), vstring_avail(reply),
+ &enc_length_out)) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch: %s",
+ myname, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_MORE);
+ } else {
+ if (sasl_status == SASL_NOUSER) /* privacy */
+ sasl_status = SASL_BADAUTH;
+ vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
+ switch (sasl_status) {
+ case SASL_FAIL:
+ case SASL_NOMEM:
+ case SASL_TRYAGAIN:
+ case SASL_UNAVAIL:
+ return XSASL_AUTH_TEMP;
+ default:
+ return (XSASL_AUTH_FAIL);
+ }
+ }
+}
+
+/* xsasl_cyrus_server_first - per-session authentication */
+
+int xsasl_cyrus_server_first(XSASL_SERVER *xp, const char *sasl_method,
+ const char *init_response, VSTRING *reply)
+{
+ const char *myname = "xsasl_cyrus_server_first";
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+ char *dec_buffer;
+ unsigned dec_length;
+ unsigned reply_len;
+ unsigned serveroutlen;
+ int sasl_status;
+ SERVEROUT_TYPE serverout = 0;
+ int xsasl_status;
+
+#if SASL_VERSION_MAJOR < 2
+ const char *errstr = 0;
+
+#endif
+
+#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
+
+ if (msg_verbose)
+ msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
+ IFELSE(init_response, ", init_response ", ""),
+ IFELSE(init_response, init_response, ""));
+
+ /*
+ * SASL authentication protocol start-up. Process any initial client
+ * response that was sent along in the AUTH command.
+ */
+ if (init_response) {
+ reply_len = strlen(init_response);
+ VSTRING_RESET(server->decoded); /* Fix 200512 */
+ VSTRING_SPACE(server->decoded, reply_len);
+ if ((sasl_status = SASL_DECODE64(init_response, reply_len,
+ dec_buffer = STR(server->decoded),
+ vstring_avail(server->decoded),
+ &dec_length)) != SASL_OK) {
+ vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FORM);
+ }
+ if (msg_verbose)
+ msg_info("%s: decoded initial response %s", myname, dec_buffer);
+ } else {
+ dec_buffer = 0;
+ dec_length = 0;
+ }
+ sasl_status = SASL_SERVER_START(server->sasl_conn, sasl_method, dec_buffer,
+ dec_length, &serverout,
+ &serveroutlen, &errstr);
+ xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
+ serveroutlen, reply);
+#if SASL_VERSION_MAJOR < 2
+ /* SASL version 1 doesn't free memory that it allocates. */
+ free(serverout);
+#endif
+ return (xsasl_status);
+}
+
+/* xsasl_cyrus_server_next - continue authentication */
+
+static int xsasl_cyrus_server_next(XSASL_SERVER *xp, const char *request,
+ VSTRING *reply)
+{
+ const char *myname = "xsasl_cyrus_server_next";
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+ unsigned dec_length;
+ unsigned request_len;
+ unsigned serveroutlen;
+ int sasl_status;
+ SERVEROUT_TYPE serverout = 0;
+ int xsasl_status;
+
+#if SASL_VERSION_MAJOR < 2
+ const char *errstr = 0;
+
+#endif
+
+ request_len = strlen(request);
+ VSTRING_RESET(server->decoded); /* Fix 200512 */
+ VSTRING_SPACE(server->decoded, request_len);
+ if ((sasl_status = SASL_DECODE64(request, request_len,
+ STR(server->decoded),
+ vstring_avail(server->decoded),
+ &dec_length)) != SASL_OK) {
+ vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
+ return (XSASL_AUTH_FORM);
+ }
+ if (msg_verbose)
+ msg_info("%s: decoded response: %.*s",
+ myname, (int) dec_length, STR(server->decoded));
+ sasl_status = SASL_SERVER_STEP(server->sasl_conn, STR(server->decoded),
+ dec_length, &serverout,
+ &serveroutlen, &errstr);
+ xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
+ serveroutlen, reply);
+#if SASL_VERSION_MAJOR < 2
+ /* SASL version 1 doesn't free memory that it allocates. */
+ free(serverout);
+#endif
+ return (xsasl_status);
+}
+
+/* xsasl_cyrus_server_get_username - get authenticated username */
+
+static const char *xsasl_cyrus_server_get_username(XSASL_SERVER *xp)
+{
+ const char *myname = "xsasl_cyrus_server_get_username";
+ XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
+ VOID_SERVEROUT_TYPE serverout = 0;
+ int sasl_status;
+
+ /*
+ * XXX Do not free(serverout).
+ */
+ if (server->username)
+ myfree(server->username);
+ sasl_status = sasl_getprop(server->sasl_conn, SASL_USERNAME, &serverout);
+ if (sasl_status != SASL_OK || serverout == 0) {
+ server->username = 0;
+ } else {
+ server->username = mystrdup(serverout);
+ printable(server->username, '?');
+ }
+ return (server->username);
+}
+
+#endif
diff --git a/src/xsasl/xsasl_dovecot.h b/src/xsasl/xsasl_dovecot.h
new file mode 100644
index 0000000..f99850e
--- /dev/null
+++ b/src/xsasl/xsasl_dovecot.h
@@ -0,0 +1,41 @@
+#ifndef _XSASL_DOVECOT_H_INCLUDED_
+#define _XSASL_DOVECOT_H_INCLUDED_
+
+/*++
+/* NAME
+/* xsasl_dovecot 3h
+/* SUMMARY
+/* Dovecot SASL plug-in
+/* SYNOPSIS
+/* #include <xsasl_dovecot.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * XSASL library.
+ */
+#include <xsasl.h>
+
+#if defined(USE_SASL_AUTH)
+
+ /*
+ * SASL protocol interface
+ */
+#define XSASL_TYPE_DOVECOT "dovecot"
+
+extern XSASL_SERVER_IMPL *xsasl_dovecot_server_init(const char *, const char *);
+
+#endif
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/src/xsasl/xsasl_dovecot_server.c b/src/xsasl/xsasl_dovecot_server.c
new file mode 100644
index 0000000..1d1c570
--- /dev/null
+++ b/src/xsasl/xsasl_dovecot_server.c
@@ -0,0 +1,747 @@
+/*++
+/* NAME
+/* xsasl_dovecot_server 3
+/* SUMMARY
+/* Dovecot SASL server-side plug-in
+/* SYNOPSIS
+/* XSASL_SERVER_IMPL *xsasl_dovecot_server_init(server_type, appl_name)
+/* const char *server_type;
+/* const char *appl_name;
+/* DESCRIPTION
+/* This module implements the Dovecot SASL server-side authentication
+/* plug-in.
+/*
+/* .IP server_type
+/* The plug-in type that was specified to xsasl_server_init().
+/* The argument is ignored, because the Dovecot plug-in
+/* implements only one plug-in type.
+/* .IP path_info
+/* The location of the Dovecot authentication server's UNIX-domain
+/* socket. Note: the Dovecot plug-in uses late binding, therefore
+/* all connect operations are done with Postfix privileges.
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/*
+/* Panic: interface violation.
+/*
+/* Other: the routines log a warning and return an error result
+/* as specified in xsasl_server(3).
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Initial implementation by:
+/* Timo Sirainen
+/* Procontrol
+/* Finland
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <connect.h>
+#include <split_at.h>
+#include <stringops.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <name_mask.h>
+#include <argv.h>
+#include <myaddrinfo.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <xsasl.h>
+#include <xsasl_dovecot.h>
+
+#ifdef USE_SASL_AUTH
+
+/* Major version changes are not backwards compatible,
+ minor version numbers can be ignored. */
+#define AUTH_PROTOCOL_MAJOR_VERSION 1
+#define AUTH_PROTOCOL_MINOR_VERSION 0
+
+ /*
+ * Enforce read/write time limits, so that we can produce accurate
+ * diagnostics instead of getting killed by the watchdog timer.
+ */
+#define AUTH_TIMEOUT 10
+
+ /*
+ * Security property bitmasks.
+ */
+#define SEC_PROPS_NOPLAINTEXT (1 << 0)
+#define SEC_PROPS_NOACTIVE (1 << 1)
+#define SEC_PROPS_NODICTIONARY (1 << 2)
+#define SEC_PROPS_NOANONYMOUS (1 << 3)
+#define SEC_PROPS_FWD_SECRECY (1 << 4)
+#define SEC_PROPS_MUTUAL_AUTH (1 << 5)
+#define SEC_PROPS_PRIVATE (1 << 6)
+
+#define SEC_PROPS_POS_MASK (SEC_PROPS_MUTUAL_AUTH | SEC_PROPS_FWD_SECRECY)
+#define SEC_PROPS_NEG_MASK (SEC_PROPS_NOPLAINTEXT | SEC_PROPS_NOACTIVE | \
+ SEC_PROPS_NODICTIONARY | SEC_PROPS_NOANONYMOUS)
+
+ /*
+ * Security properties as specified in the Postfix main.cf file.
+ */
+static const NAME_MASK xsasl_dovecot_conf_sec_props[] = {
+ "noplaintext", SEC_PROPS_NOPLAINTEXT,
+ "noactive", SEC_PROPS_NOACTIVE,
+ "nodictionary", SEC_PROPS_NODICTIONARY,
+ "noanonymous", SEC_PROPS_NOANONYMOUS,
+ "forward_secrecy", SEC_PROPS_FWD_SECRECY,
+ "mutual_auth", SEC_PROPS_MUTUAL_AUTH,
+ 0, 0,
+};
+
+ /*
+ * Security properties as specified in the Dovecot protocol. See
+ * http://wiki.dovecot.org/Authentication_Protocol.
+ */
+static const NAME_MASK xsasl_dovecot_serv_sec_props[] = {
+ "plaintext", SEC_PROPS_NOPLAINTEXT,
+ "active", SEC_PROPS_NOACTIVE,
+ "dictionary", SEC_PROPS_NODICTIONARY,
+ "anonymous", SEC_PROPS_NOANONYMOUS,
+ "forward-secrecy", SEC_PROPS_FWD_SECRECY,
+ "mutual-auth", SEC_PROPS_MUTUAL_AUTH,
+ "private", SEC_PROPS_PRIVATE,
+ 0, 0,
+};
+
+ /*
+ * Class variables.
+ */
+typedef struct XSASL_DCSRV_MECH {
+ char *mech_name; /* mechanism name */
+ int sec_props; /* mechanism properties */
+ struct XSASL_DCSRV_MECH *next;
+} XSASL_DCSRV_MECH;
+
+typedef struct {
+ XSASL_SERVER_IMPL xsasl;
+ VSTREAM *sasl_stream;
+ char *socket_path;
+ XSASL_DCSRV_MECH *mechanism_list; /* unfiltered mechanism list */
+ unsigned int request_id_counter;
+} XSASL_DOVECOT_SERVER_IMPL;
+
+ /*
+ * The XSASL_DOVECOT_SERVER object is derived from the generic XSASL_SERVER
+ * object.
+ */
+typedef struct {
+ XSASL_SERVER xsasl; /* generic members, must be first */
+ XSASL_DOVECOT_SERVER_IMPL *impl;
+ unsigned int last_request_id;
+ char *service;
+ char *username; /* authenticated user */
+ VSTRING *sasl_line;
+ unsigned int sec_props; /* Postfix mechanism filter */
+ int tls_flag; /* TLS enabled in this session */
+ char *mechanism_list; /* filtered mechanism list */
+ ARGV *mechanism_argv; /* ditto */
+ char *client_addr; /* remote IP address */
+ char *server_addr; /* remote IP address */
+} XSASL_DOVECOT_SERVER;
+
+ /*
+ * Forward declarations.
+ */
+static void xsasl_dovecot_server_done(XSASL_SERVER_IMPL *);
+static XSASL_SERVER *xsasl_dovecot_server_create(XSASL_SERVER_IMPL *,
+ XSASL_SERVER_CREATE_ARGS *);
+static void xsasl_dovecot_server_free(XSASL_SERVER *);
+static int xsasl_dovecot_server_first(XSASL_SERVER *, const char *,
+ const char *, VSTRING *);
+static int xsasl_dovecot_server_next(XSASL_SERVER *, const char *, VSTRING *);
+static const char *xsasl_dovecot_server_get_mechanism_list(XSASL_SERVER *);
+static const char *xsasl_dovecot_server_get_username(XSASL_SERVER *);
+
+/* xsasl_dovecot_server_mech_append - append server mechanism entry */
+
+static void xsasl_dovecot_server_mech_append(XSASL_DCSRV_MECH **mech_list,
+ const char *mech_name, int sec_props)
+{
+ XSASL_DCSRV_MECH **mpp;
+ XSASL_DCSRV_MECH *mp;
+
+ for (mpp = mech_list; *mpp != 0; mpp = &mpp[0]->next)
+ /* void */ ;
+
+ mp = (XSASL_DCSRV_MECH *) mymalloc(sizeof(*mp));
+ mp->mech_name = mystrdup(mech_name);
+ mp->sec_props = sec_props;
+ mp->next = 0;
+ *mpp = mp;
+}
+
+/* xsasl_dovecot_server_mech_free - destroy server mechanism list */
+
+static void xsasl_dovecot_server_mech_free(XSASL_DCSRV_MECH *mech_list)
+{
+ XSASL_DCSRV_MECH *mp;
+ XSASL_DCSRV_MECH *next;
+
+ for (mp = mech_list; mp != 0; mp = next) {
+ myfree(mp->mech_name);
+ next = mp->next;
+ myfree((void *) mp);
+ }
+}
+
+/* xsasl_dovecot_server_mech_filter - filter server mechanism list */
+
+static char *xsasl_dovecot_server_mech_filter(ARGV *mechanism_argv,
+ XSASL_DCSRV_MECH *mechanism_list,
+ unsigned int conf_props)
+{
+ const char *myname = "xsasl_dovecot_server_mech_filter";
+ unsigned int pos_conf_props = (conf_props & SEC_PROPS_POS_MASK);
+ unsigned int neg_conf_props = (conf_props & SEC_PROPS_NEG_MASK);
+ VSTRING *mechanisms_str = vstring_alloc(10);
+ XSASL_DCSRV_MECH *mp;
+
+ /*
+ * Match Postfix properties against Dovecot server properties.
+ */
+ for (mp = mechanism_list; mp != 0; mp = mp->next) {
+ if ((mp->sec_props & pos_conf_props) == pos_conf_props
+ && (mp->sec_props & neg_conf_props) == 0) {
+ if (VSTRING_LEN(mechanisms_str) > 0)
+ VSTRING_ADDCH(mechanisms_str, ' ');
+ vstring_strcat(mechanisms_str, mp->mech_name);
+ argv_add(mechanism_argv, mp->mech_name, (char *) 0);
+ if (msg_verbose)
+ msg_info("%s: keep mechanism: %s", myname, mp->mech_name);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: skip mechanism: %s", myname, mp->mech_name);
+ }
+ }
+ return (vstring_export(mechanisms_str));
+}
+
+/* xsasl_dovecot_server_connect - initial auth server handshake */
+
+static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp)
+{
+ const char *myname = "xsasl_dovecot_server_connect";
+ VSTRING *line_str;
+ VSTREAM *sasl_stream;
+ char *line, *cmd, *mech_name;
+ unsigned int major_version, minor_version;
+ int fd, success, have_mech_line;
+ int sec_props;
+ const char *path;
+
+ if (msg_verbose)
+ msg_info("%s: Connecting", myname);
+
+ /*
+ * Not documented, but necessary for testing.
+ */
+ path = xp->socket_path;
+ if (strncmp(path, "inet:", 5) == 0) {
+ fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT);
+ } else {
+ if (strncmp(path, "unix:", 5) == 0)
+ path += 5;
+ fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT);
+ }
+ if (fd < 0) {
+ msg_warn("SASL: Connect to Dovecot auth socket '%s' failed: %m",
+ xp->socket_path);
+ return (-1);
+ }
+ sasl_stream = vstream_fdopen(fd, O_RDWR);
+ vstream_control(sasl_stream,
+ CA_VSTREAM_CTL_PATH(xp->socket_path),
+ CA_VSTREAM_CTL_TIMEOUT(AUTH_TIMEOUT),
+ CA_VSTREAM_CTL_END);
+
+ /* XXX Encapsulate for logging. */
+ vstream_fprintf(sasl_stream,
+ "VERSION\t%u\t%u\n"
+ "CPID\t%u\n",
+ AUTH_PROTOCOL_MAJOR_VERSION,
+ AUTH_PROTOCOL_MINOR_VERSION,
+ (unsigned int) getpid());
+ if (vstream_fflush(sasl_stream) == VSTREAM_EOF) {
+ msg_warn("SASL: Couldn't send handshake: %m");
+ return (-1);
+ }
+ success = 0;
+ have_mech_line = 0;
+ line_str = vstring_alloc(256);
+ /* XXX Encapsulate for logging. */
+ while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) {
+ line = vstring_str(line_str);
+
+ if (msg_verbose)
+ msg_info("%s: auth reply: %s", myname, line);
+
+ cmd = line;
+ line = split_at(line, '\t');
+
+ if (strcmp(cmd, "VERSION") == 0) {
+ if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) {
+ msg_warn("SASL: Protocol version error");
+ break;
+ }
+ if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) {
+ /* Major version is different from ours. */
+ msg_warn("SASL: Protocol version mismatch (%d vs. %d)",
+ major_version, AUTH_PROTOCOL_MAJOR_VERSION);
+ break;
+ }
+ } else if (strcmp(cmd, "MECH") == 0 && line != NULL) {
+ mech_name = line;
+ have_mech_line = 1;
+ line = split_at(line, '\t');
+ if (line != 0) {
+ sec_props =
+ name_mask_delim_opt(myname,
+ xsasl_dovecot_serv_sec_props,
+ line, "\t",
+ NAME_MASK_ANY_CASE | NAME_MASK_IGNORE);
+ if ((sec_props & SEC_PROPS_PRIVATE) != 0)
+ continue;
+ } else
+ sec_props = 0;
+ xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name,
+ sec_props);
+ } else if (strcmp(cmd, "SPID") == 0) {
+
+ /*
+ * Unfortunately the auth protocol handshake wasn't designed well
+ * to differentiate between auth-client/userdb/master.
+ * auth-userdb and auth-master send VERSION + SPID lines only and
+ * nothing afterwards, while auth-client sends VERSION + MECH +
+ * SPID + CUID + more. The simplest way that we can determine if
+ * we've connected to the correct socket is to see if MECH line
+ * exists or not (alternatively we'd have to have a small timeout
+ * after SPID to see if CUID is sent or not).
+ */
+ if (!have_mech_line) {
+ msg_warn("SASL: Connected to wrong auth socket (auth-master instead of auth-client)");
+ break;
+ }
+ } else if (strcmp(cmd, "DONE") == 0) {
+ /* Handshake finished. */
+ success = 1;
+ break;
+ } else {
+ /* ignore any unknown commands */
+ }
+ }
+ vstring_free(line_str);
+
+ if (!success) {
+ /* handshake failed */
+ (void) vstream_fclose(sasl_stream);
+ return (-1);
+ }
+ xp->sasl_stream = sasl_stream;
+ return (0);
+}
+
+/* xsasl_dovecot_server_disconnect - dispose of server connection state */
+
+static void xsasl_dovecot_server_disconnect(XSASL_DOVECOT_SERVER_IMPL *xp)
+{
+ if (xp->sasl_stream) {
+ (void) vstream_fclose(xp->sasl_stream);
+ xp->sasl_stream = 0;
+ }
+ if (xp->mechanism_list) {
+ xsasl_dovecot_server_mech_free(xp->mechanism_list);
+ xp->mechanism_list = 0;
+ }
+}
+
+/* xsasl_dovecot_server_init - create implementation handle */
+
+XSASL_SERVER_IMPL *xsasl_dovecot_server_init(const char *server_type,
+ const char *path_info)
+{
+ XSASL_DOVECOT_SERVER_IMPL *xp;
+
+ xp = (XSASL_DOVECOT_SERVER_IMPL *) mymalloc(sizeof(*xp));
+ xp->xsasl.create = xsasl_dovecot_server_create;
+ xp->xsasl.done = xsasl_dovecot_server_done;
+ xp->socket_path = mystrdup(path_info);
+ xp->sasl_stream = 0;
+ xp->mechanism_list = 0;
+ xp->request_id_counter = 0;
+ return (&xp->xsasl);
+}
+
+/* xsasl_dovecot_server_done - dispose of implementation */
+
+static void xsasl_dovecot_server_done(XSASL_SERVER_IMPL *impl)
+{
+ XSASL_DOVECOT_SERVER_IMPL *xp = (XSASL_DOVECOT_SERVER_IMPL *) impl;
+
+ xsasl_dovecot_server_disconnect(xp);
+ myfree(xp->socket_path);
+ myfree((void *) impl);
+}
+
+/* xsasl_dovecot_server_create - create server instance */
+
+static XSASL_SERVER *xsasl_dovecot_server_create(XSASL_SERVER_IMPL *impl,
+ XSASL_SERVER_CREATE_ARGS *args)
+{
+ const char *myname = "xsasl_dovecot_server_create";
+ XSASL_DOVECOT_SERVER *server;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *) &ss;
+ SOCKADDR_SIZE salen;
+ MAI_HOSTADDR_STR server_addr;
+
+ if (msg_verbose)
+ msg_info("%s: SASL service=%s, realm=%s",
+ myname, args->service, args->user_realm ?
+ args->user_realm : "(null)");
+
+ /*
+ * Extend the XSASL_SERVER_IMPL object with our own data. We use
+ * long-lived conversion buffers rather than local variables to avoid
+ * memory leaks in case of read/write timeout or I/O error.
+ */
+ server = (XSASL_DOVECOT_SERVER *) mymalloc(sizeof(*server));
+ server->xsasl.free = xsasl_dovecot_server_free;
+ server->xsasl.first = xsasl_dovecot_server_first;
+ server->xsasl.next = xsasl_dovecot_server_next;
+ server->xsasl.get_mechanism_list = xsasl_dovecot_server_get_mechanism_list;
+ server->xsasl.get_username = xsasl_dovecot_server_get_username;
+ server->impl = (XSASL_DOVECOT_SERVER_IMPL *) impl;
+ server->sasl_line = vstring_alloc(256);
+ server->username = 0;
+ server->service = mystrdup(args->service);
+ server->last_request_id = 0;
+ server->mechanism_list = 0;
+ server->mechanism_argv = 0;
+ server->tls_flag = args->tls_flag;
+ server->sec_props =
+ name_mask_opt(myname, xsasl_dovecot_conf_sec_props,
+ args->security_options,
+ NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
+ server->client_addr = mystrdup(args->client_addr);
+
+ /*
+ * XXX Temporary code until smtpd_peer.c is updated.
+ */
+ if (args->server_addr && *args->server_addr) {
+ server->server_addr = mystrdup(args->server_addr);
+ } else {
+ salen = sizeof(ss);
+ if (getsockname(vstream_fileno(args->stream), sa, &salen) < 0
+ || sockaddr_to_hostaddr(sa, salen, &server_addr, 0, 0) != 0)
+ server_addr.buf[0] = 0;
+ server->server_addr = mystrdup(server_addr.buf);
+ }
+
+ return (&server->xsasl);
+}
+
+/* xsasl_dovecot_server_get_mechanism_list - get available mechanisms */
+
+static const char *xsasl_dovecot_server_get_mechanism_list(XSASL_SERVER *xp)
+{
+ XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
+
+ if (!server->impl->sasl_stream) {
+ if (xsasl_dovecot_server_connect(server->impl) < 0)
+ return (0);
+ }
+ if (server->mechanism_list == 0) {
+ server->mechanism_argv = argv_alloc(2);
+ server->mechanism_list =
+ xsasl_dovecot_server_mech_filter(server->mechanism_argv,
+ server->impl->mechanism_list,
+ server->sec_props);
+ }
+ return (server->mechanism_list[0] ? server->mechanism_list : 0);
+}
+
+/* xsasl_dovecot_server_free - destroy server instance */
+
+static void xsasl_dovecot_server_free(XSASL_SERVER *xp)
+{
+ XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
+
+ vstring_free(server->sasl_line);
+ if (server->username)
+ myfree(server->username);
+ if (server->mechanism_list) {
+ myfree(server->mechanism_list);
+ argv_free(server->mechanism_argv);
+ }
+ myfree(server->service);
+ myfree(server->server_addr);
+ myfree(server->client_addr);
+ myfree((void *) server);
+}
+
+/* xsasl_dovecot_server_auth_response - encode server first/next response */
+
+static int xsasl_dovecot_parse_reply(XSASL_DOVECOT_SERVER *server, char **line)
+{
+ char *id;
+
+ if (*line == NULL) {
+ msg_warn("SASL: Protocol error");
+ return -1;
+ }
+ id = *line;
+ *line = split_at(*line, '\t');
+
+ if (strtoul(id, NULL, 0) != server->last_request_id) {
+ /* reply to another request, shouldn't really happen.. */
+ return -1;
+ }
+ return 0;
+}
+
+static void xsasl_dovecot_parse_reply_args(XSASL_DOVECOT_SERVER *server,
+ char *line, VSTRING *reply,
+ int success)
+{
+ char *next;
+
+ if (server->username) {
+ myfree(server->username);
+ server->username = 0;
+ }
+
+ /*
+ * Note: TAB is part of the Dovecot protocol and must not appear in
+ * legitimate Dovecot usernames, otherwise the protocol would break.
+ */
+ for (; line != NULL; line = next) {
+ next = split_at(line, '\t');
+ if (strncmp(line, "user=", 5) == 0) {
+ server->username = mystrdup(line + 5);
+ printable(server->username, '?');
+ } else if (strncmp(line, "reason=", 7) == 0) {
+ if (!success) {
+ printable(line + 7, '?');
+ vstring_strcpy(reply, line + 7);
+ }
+ }
+ }
+}
+
+/* xsasl_dovecot_handle_reply - receive and process auth reply */
+
+static int xsasl_dovecot_handle_reply(XSASL_DOVECOT_SERVER *server,
+ VSTRING *reply)
+{
+ const char *myname = "xsasl_dovecot_handle_reply";
+ char *line, *cmd;
+
+ /* XXX Encapsulate for logging. */
+ while (vstring_get_nonl(server->sasl_line,
+ server->impl->sasl_stream) != VSTREAM_EOF) {
+ line = vstring_str(server->sasl_line);
+
+ if (msg_verbose)
+ msg_info("%s: auth reply: %s", myname, line);
+
+ cmd = line;
+ line = split_at(line, '\t');
+
+ if (strcmp(cmd, "OK") == 0) {
+ if (xsasl_dovecot_parse_reply(server, &line) == 0) {
+ /* authentication successful */
+ xsasl_dovecot_parse_reply_args(server, line, reply, 1);
+ if (server->username == 0) {
+ msg_warn("missing Dovecot server %s username field", cmd);
+ vstring_strcpy(reply, "Authentication backend error");
+ return XSASL_AUTH_FAIL;
+ }
+ return XSASL_AUTH_DONE;
+ }
+ } else if (strcmp(cmd, "CONT") == 0) {
+ if (xsasl_dovecot_parse_reply(server, &line) == 0) {
+ if (line == 0) {
+ msg_warn("missing Dovecot server %s reply field", cmd);
+ vstring_strcpy(reply, "Authentication backend error");
+ return XSASL_AUTH_FAIL;
+ }
+ vstring_strcpy(reply, line);
+ return XSASL_AUTH_MORE;
+ }
+ } else if (strcmp(cmd, "FAIL") == 0) {
+ if (xsasl_dovecot_parse_reply(server, &line) == 0) {
+ /* authentication failure */
+ xsasl_dovecot_parse_reply_args(server, line, reply, 0);
+ return XSASL_AUTH_FAIL;
+ }
+ } else {
+ /* ignore */
+ }
+ }
+
+ vstring_strcpy(reply, "Connection lost to authentication server");
+ return XSASL_AUTH_TEMP;
+}
+
+/* is_valid_base64 - input sanitized */
+
+static int is_valid_base64(const char *data)
+{
+
+ /*
+ * XXX Maybe use ISALNUM() (isascii && isalnum, i.e. locale independent).
+ */
+ for (; *data != '\0'; data++) {
+ if (!((*data >= '0' && *data <= '9') ||
+ (*data >= 'a' && *data <= 'z') ||
+ (*data >= 'A' && *data <= 'Z') ||
+ *data == '+' || *data == '/' || *data == '='))
+ return 0;
+ }
+ return 1;
+}
+
+/* xsasl_dovecot_server_first - per-session authentication */
+
+int xsasl_dovecot_server_first(XSASL_SERVER *xp, const char *sasl_method,
+ const char *init_response, VSTRING *reply)
+{
+ const char *myname = "xsasl_dovecot_server_first";
+ XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
+ int i;
+ char **cpp;
+
+#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
+
+ if (msg_verbose)
+ msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
+ IFELSE(init_response, ", init_response ", ""),
+ IFELSE(init_response, init_response, ""));
+
+ if (server->mechanism_argv == 0)
+ msg_panic("%s: no mechanism list", myname);
+
+ for (cpp = server->mechanism_argv->argv; /* see below */ ; cpp++) {
+ if (*cpp == 0) {
+ vstring_strcpy(reply, "Invalid authentication mechanism");
+ return XSASL_AUTH_FAIL;
+ }
+ if (strcasecmp(sasl_method, *cpp) == 0)
+ break;
+ }
+ if (init_response)
+ if (!is_valid_base64(init_response)) {
+ vstring_strcpy(reply, "Invalid base64 data in initial response");
+ return XSASL_AUTH_FAIL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (!server->impl->sasl_stream) {
+ if (xsasl_dovecot_server_connect(server->impl) < 0)
+ return XSASL_AUTH_TEMP;
+ }
+ /* send the request */
+ server->last_request_id = ++server->impl->request_id_counter;
+ /* XXX Encapsulate for logging. */
+ vstream_fprintf(server->impl->sasl_stream,
+ "AUTH\t%u\t%s\tservice=%s\tnologin\tlip=%s\trip=%s",
+ server->last_request_id, sasl_method,
+ server->service, server->server_addr,
+ server->client_addr);
+ if (server->tls_flag)
+ /* XXX Encapsulate for logging. */
+ vstream_fputs("\tsecured", server->impl->sasl_stream);
+ if (init_response) {
+
+ /*
+ * initial response is already base64 encoded, so we can send it
+ * directly.
+ */
+ /* XXX Encapsulate for logging. */
+ vstream_fprintf(server->impl->sasl_stream,
+ "\tresp=%s", init_response);
+ }
+ /* XXX Encapsulate for logging. */
+ VSTREAM_PUTC('\n', server->impl->sasl_stream);
+
+ if (vstream_fflush(server->impl->sasl_stream) != VSTREAM_EOF)
+ break;
+
+ if (i == 1) {
+ vstring_strcpy(reply, "Can't connect to authentication server");
+ return XSASL_AUTH_TEMP;
+ }
+
+ /*
+ * Reconnect and try again.
+ */
+ xsasl_dovecot_server_disconnect(server->impl);
+ }
+
+ return xsasl_dovecot_handle_reply(server, reply);
+}
+
+/* xsasl_dovecot_server_next - continue authentication */
+
+static int xsasl_dovecot_server_next(XSASL_SERVER *xp, const char *request,
+ VSTRING *reply)
+{
+ XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
+
+ if (!is_valid_base64(request)) {
+ vstring_strcpy(reply, "Invalid base64 data in continued response");
+ return XSASL_AUTH_FAIL;
+ }
+ /* XXX Encapsulate for logging. */
+ vstream_fprintf(server->impl->sasl_stream,
+ "CONT\t%u\t%s\n", server->last_request_id, request);
+ if (vstream_fflush(server->impl->sasl_stream) == VSTREAM_EOF) {
+ vstring_strcpy(reply, "Connection lost to authentication server");
+ return XSASL_AUTH_TEMP;
+ }
+ return xsasl_dovecot_handle_reply(server, reply);
+}
+
+/* xsasl_dovecot_server_get_username - get authenticated username */
+
+static const char *xsasl_dovecot_server_get_username(XSASL_SERVER *xp)
+{
+ XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
+
+ return (server->username);
+}
+
+#endif
diff --git a/src/xsasl/xsasl_server.c b/src/xsasl/xsasl_server.c
new file mode 100644
index 0000000..e8d7e16
--- /dev/null
+++ b/src/xsasl/xsasl_server.c
@@ -0,0 +1,270 @@
+/*++
+/* NAME
+/* xsasl-server 3
+/* SUMMARY
+/* Postfix SASL server plug-in interface
+/* SYNOPSIS
+/* #include <xsasl.h>
+/*
+/* XSASL_SERVER_IMPL *xsasl_server_init(server_type, path_info)
+/* const char *server_type;
+/* const char *path_info;
+/*
+/* void xsasl_server_done(implementation)
+/* XSASL_SERVER_IMPL *implementation;
+/*
+/* ARGV *xsasl_server_types()
+/*
+/* .in +4
+/* typedef struct XSASL_SERVER_CREATE_ARGS {
+/* VSTREAM *stream;
+/* const char *server_addr;
+/* const char *client_addr;
+/* const char *service;
+/* const char *user_realm;
+/* const char *security_options;
+/* int tls_flag;
+/* } XSASL_SERVER_CREATE_ARGS;
+/* .in -4
+/*
+/* XSASL_SERVER *xsasl_server_create(implementation, args)
+/* XSASL_SERVER_IMPL *implementation;
+/* XSASL_SERVER_CREATE_ARGS *args;
+/*
+/* XSASL_SERVER *XSASL_SERVER_CREATE(implementation, args,
+/* stream = stream_value,
+/* ...,
+/* tls_flag = tls_flag_value)
+/* XSASL_SERVER_IMPL *implementation;
+/* XSASL_SERVER_CREATE_ARGS *args;
+/*
+/* void xsasl_server_free(server)
+/* XSASL_SERVER *server;
+/*
+/* int xsasl_server_first(server, auth_method, init_resp, server_reply)
+/* XSASL_SERVER *server;
+/* const char *auth_method;
+/* const char *init_resp;
+/* VSTRING *server_reply;
+/*
+/* int xsasl_server_next(server, client_request, server_reply)
+/* XSASL_SERVER *server;
+/* const char *client_request;
+/* VSTRING *server_reply;
+/*
+/* const char *xsasl_server_get_mechanism_list(server)
+/* XSASL_SERVER *server;
+/*
+/* const char *xsasl_server_get_username(server)
+/* XSASL_SERVER *server;
+/* DESCRIPTION
+/* The XSASL_SERVER abstraction implements a generic interface
+/* to one or more SASL authentication implementations.
+/*
+/* xsasl_server_init() is called once during process initialization.
+/* It selects a SASL implementation by name, specifies the
+/* location of a configuration file or rendez-vous point, and
+/* returns an implementation handle that can be used to generate
+/* SASL server instances. This function is typically used to
+/* initialize the underlying implementation.
+/*
+/* xsasl_server_done() disposes of an implementation handle,
+/* and allows the underlying implementation to release resources.
+/*
+/* xsasl_server_types() lists the available implementation types.
+/* The result should be destroyed by the caller.
+/*
+/* xsasl_server_create() is called at the start of an SMTP
+/* session. It generates a Postfix SASL plug-in server instance
+/* for the specified service and authentication realm, and
+/* with the specified security properties. Specify a null
+/* pointer when no realm should be used. The stream handle is
+/* stored so that encryption can be turned on after successful
+/* negotiations. Specify zero-length strings when a client or
+/* server address is unavailable.
+/*
+/* XSASL_SERVER_CREATE() is a macro that provides an interface
+/* with named parameters. Named parameters do not have to
+/* appear in a fixed order. The parameter names correspond to
+/* the member names of the XSASL_SERVER_CREATE_ARGS structure.
+/*
+/* xsasl_server_free() is called at the end of an SMTP session.
+/* It destroys a SASL server instance, and disables further
+/* read/write operations if encryption was turned on.
+/*
+/* xsasl_server_first() produces the server response for the
+/* client AUTH command. The client input are an authentication
+/* method, and an optional initial response or null pointer.
+/* The initial response and server non-error replies are BASE64
+/* encoded. Server error replies are 7-bit ASCII text without
+/* control characters, without BASE64 encoding, and without
+/* SMTP reply code or enhanced status code.
+/*
+/* The result is one of the following:
+/* .IP XSASL_AUTH_MORE
+/* More client input is needed. The server reply specifies
+/* what.
+/* .IP XSASL_AUTH_DONE
+/* Authentication completed successfully.
+/* .IP XSASL_AUTH_FORM
+/* The client input is incorrectly formatted. The server error
+/* reply explains why.
+/* .IP XSASL_AUTH_FAIL
+/* Authentication failed. The server error reply explains why.
+/* .PP
+/* xsasl_server_next() supports the subsequent stages of the
+/* client-server AUTH protocol. Both the client input and
+/* server non-error responses are BASE64 encoded. See
+/* xsasl_server_first() for other details.
+/*
+/* xsasl_server_get_mechanism_list() returns the authentication
+/* mechanisms that match the security properties, as a white-space
+/* separated list. This is meant to be used in the SMTP EHLO
+/* reply.
+/*
+/* xsasl_server_get_username() returns the stored username
+/* after successful authentication.
+/*
+/* Arguments:
+/* .IP addr_family
+/* The network address family: AF_INET6 or AF_INET.
+/* .IP auth_method
+/* AUTH command authentication method.
+/* .IP client_addr
+/* IPv4 or IPv6 address (no surrounding [] or ipv6: prefix),
+/* or zero-length string if unavailable.
+/* .IP client_port
+/* TCP port or zero-length string if unavailable.
+/* .IP init_resp
+/* AUTH command initial response or null pointer.
+/* .IP implementation
+/* Implementation handle that was obtained with xsasl_server_init().
+/* .IP path_info
+/* The value of the smtpd_sasl_path parameter or equivalent.
+/* This specifies the implementation-dependent location of a
+/* configuration file, rendez-vous point, etc., and is passed
+/* unchanged to the plug-in.
+/* .IP security_options
+/* The value of the smtpd_security_options parameter or
+/* equivalent. This is passed unchanged to the plug-in.
+/* .IP server
+/* SASL plug-in server handle.
+/* .IP server_addr
+/* IPv4 or IPv6 address (no surrounding [] or ipv6: prefix),
+/* or zero-length string if unavailable.
+/* .IP server_port
+/* TCP port or zero-length string if unavailable.
+/* .IP server_reply
+/* BASE64 encoded server non-error reply (without SMTP reply
+/* code or enhanced status code), or ASCII error description.
+/* .IP server_type
+/* The name of a Postfix SASL server plug_in implementation.
+/* .IP server_types
+/* Null-terminated array of strings with SASL server plug-in
+/* implementation names.
+/* .IP service
+/* The service that is implemented by the local server, typically
+/* "smtp" or "lmtp".
+/* .IP stream
+/* The connection between client and server. When SASL
+/* encryption is negotiated, the plug-in will transparently
+/* intercept the socket read/write operations.
+/* .IP user_realm
+/* Authentication domain or null pointer.
+/* SECURITY
+/* .ad
+/* .fi
+/* The caller does not sanitize client input. It is the
+/* responsibility of the underlying SASL server implementation
+/* to produce 7-bit ASCII without control characters as server
+/* non-error and error replies, and as the result from
+/* xsasl_server_method() and xsasl_server_username().
+/* DIAGNOSTICS
+/* In case of failure, xsasl_server_init(), xsasl_server_create(),
+/* xsasl_server_get_mechanism_list() and xsasl_server_get_username()
+/* log a warning and return a null pointer.
+/*
+/* Functions that normally return XSASL_AUTH_OK will log a warning
+/* and return an appropriate result value.
+/*
+/* Fatal errors: out of memory.
+/*
+/* Panic: interface violations.
+/* SEE ALSO
+/* cyrus_security(3) Cyrus SASL security features
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this
+/* software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* SASL implementations. */
+
+#include <xsasl.h>
+#include <xsasl_cyrus.h>
+#include <xsasl_dovecot.h>
+
+ /*
+ * Lookup table for available SASL server implementations.
+ */
+typedef struct {
+ char *server_type;
+ struct XSASL_SERVER_IMPL *(*server_init) (const char *, const char *);
+} XSASL_SERVER_IMPL_INFO;
+
+static const XSASL_SERVER_IMPL_INFO server_impl_info[] = {
+#ifdef XSASL_TYPE_CYRUS
+ {XSASL_TYPE_CYRUS, xsasl_cyrus_server_init},
+#endif
+#ifdef XSASL_TYPE_DOVECOT
+ {XSASL_TYPE_DOVECOT, xsasl_dovecot_server_init},
+#endif
+ {0, 0}
+};
+
+/* xsasl_server_init - look up server implementation by name */
+
+XSASL_SERVER_IMPL *xsasl_server_init(const char *server_type,
+ const char *path_info)
+{
+ const XSASL_SERVER_IMPL_INFO *xp;
+
+ for (xp = server_impl_info; xp->server_type; xp++)
+ if (strcmp(server_type, xp->server_type) == 0)
+ return (xp->server_init(server_type, path_info));
+ msg_warn("unsupported SASL server implementation: %s", server_type);
+ return (0);
+}
+
+/* xsasl_server_types - report available implementation types */
+
+ARGV *xsasl_server_types(void)
+{
+ const XSASL_SERVER_IMPL_INFO *xp;
+ ARGV *argv = argv_alloc(1);
+
+ for (xp = server_impl_info; xp->server_type; xp++)
+ argv_add(argv, xp->server_type, ARGV_END);
+ return (argv);
+}