src/config/doveconf
src/lda/dovecot-lda
src/dict/dict
+src/dns/dns-client
src/doveadm/doveadm
src/dsync/dsync
src/imap-login/imap-login
src/util/maildirlock
src/util/rawlog
src/util/script
+src/util/tcpwrap
src/plugins/quota/rquota_xdr.c
src/plugins/quota/rquota.h
TEST_WITH(libcap, $withval),
want_libcap=auto)
+AC_ARG_WITH(libwrap,
+[ --with-libwrap Build with libwrap, ie. TCP-wrappers (auto)],
+ TEST_WITH(libwrap, $withval),
+ want_libwrap=auto)
+
AC_ARG_WITH(ssl,
[ --with-ssl=gnutls|openssl Build with GNUTLS or OpenSSL (default)],
if test x$withval = xno; then
])
fi
+have_libwrap=no
+if test $want_libwrap != no; then
+ AC_CHECK_HEADER(tcpd.h, [
+ AC_CHECK_LIB(wrap, request_init, [
+ AC_DEFINE(HAVE_LIBWRAP,, Define if you have libwrap)
+ LIBWRAP_LIBS=-lwrap
+ AC_SUBST(LIBWRAP_LIBS)
+ have_libwrap=yes
+ ], [
+ if test "$want_libwrap" = "yes"; then
+ AC_ERROR([Can't build with libwrap support: libwrap not found])
+ fi
+ ])
+ LIBS=$old_LIBS
+ ], [
+ if test "$want_libwrap" = "yes"; then
+ AC_ERROR([Can't build with libwrap support: tcpd.h not found])
+ fi
+ ])
+fi
+AM_CONDITIONAL(TCPWRAPPERS, test "$have_libwrap" = "yes")
+
AC_DEFINE(PACKAGE_WEBPAGE, "http://www.dovecot.org/", Support URL)
dnl * after -lsocket and -lnsl tests, inet_aton() may be in them
# these networks. Typically you'd specify your IMAP proxy servers here.
#login_trusted_networks =
+# Sepace separated list of login access check sockets (e.g. tcpwrap)
+#login_access_sockets =
+
# Show more verbose process titles (in ps). Currently shows user name and
# IP address. Useful for seeing who are actually using the IMAP processes
# (eg. shared mailboxes or if same uid is used for multiple accounts).
-DPKG_STATEDIR=\""$(statedir)"\"
liblogin_la_SOURCES = \
+ access-lookup.c \
client-common.c \
client-common-auth.c \
login-proxy.c \
$(SSL_LIBS)
headers = \
+ access-lookup.h \
client-common.h \
login-common.h \
login-proxy.h \
--- /dev/null
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "network.h"
+#include "fdpass.h"
+#include "access-lookup.h"
+
+#include <unistd.h>
+
+#define ACCESS_LOOKUP_TIMEOUT_MSECS (1000*60)
+
+struct access_lookup {
+ int refcount;
+
+ int fd;
+ char *path;
+
+ struct io *io;
+ struct timeout *to;
+
+ access_lookup_callback_t *callback;
+ void *context;
+};
+
+static void access_lookup_input(struct access_lookup *lookup)
+{
+ unsigned char buf[3];
+ ssize_t ret;
+ bool success = FALSE;
+
+ ret = read(lookup->fd, buf, sizeof(buf));
+ if (ret < 0) {
+ i_error("read(%s) failed: %m", lookup->path);
+ } else if (ret == 0) {
+ /* connection close -> no success */
+ } else if (ret == 2 && buf[0] == '0' && buf[1] == '\n') {
+ /* no success */
+ } else if (ret == 2 && buf[0] == '1' && buf[1] == '\n') {
+ success = TRUE;
+ } else {
+ i_error("access(%s): Invalid input", lookup->path);
+ }
+
+ lookup->refcount++;
+ lookup->callback(success, lookup->context);
+ if (lookup->refcount > 1)
+ access_lookup_destroy(&lookup);
+ access_lookup_destroy(&lookup);
+}
+
+static void access_lookup_timeout(struct access_lookup *lookup)
+{
+ i_error("access(%s): Timed out while waiting for reply", lookup->path);
+
+ lookup->refcount++;
+ lookup->callback(FALSE, lookup->context);
+ if (lookup->refcount > 1)
+ access_lookup_destroy(&lookup);
+ access_lookup_destroy(&lookup);
+}
+
+struct access_lookup *
+access_lookup(const char *path, int client_fd, const char *daemon_name,
+ access_lookup_callback_t *callback, void *context)
+{
+ struct access_lookup *lookup;
+ const char *cmd;
+ ssize_t ret;
+ int fd;
+
+ fd = net_connect_unix(path);
+ if (fd == -1) {
+ i_error("connect(%s) failed: %m", path);
+ return NULL;
+ }
+
+ cmd = t_strconcat(daemon_name, "\n", NULL);
+ ret = fd_send(fd, client_fd, cmd, strlen(cmd));
+ if (ret != (ssize_t)strlen(cmd)) {
+ if (ret < 0)
+ i_error("fd_send(%s) failed: %m", path);
+ else
+ i_error("fd_send(%s) didn't write enough bytes", path);
+ (void)close(fd);
+ return NULL;
+ }
+
+ lookup = i_new(struct access_lookup, 1);
+ lookup->refcount = 1;
+ lookup->fd = fd;
+ lookup->path = i_strdup(path);
+ lookup->io = io_add(fd, IO_READ, access_lookup_input, lookup);
+ lookup->to = timeout_add(ACCESS_LOOKUP_TIMEOUT_MSECS,
+ access_lookup_timeout, lookup);
+ lookup->callback = callback;
+ lookup->context = context;
+ return lookup;
+}
+
+void access_lookup_destroy(struct access_lookup **_lookup)
+{
+ struct access_lookup *lookup = *_lookup;
+
+ i_assert(lookup->refcount > 0);
+ if (--lookup->refcount > 0)
+ return;
+
+ *_lookup = NULL;
+
+ if (lookup->to != NULL)
+ timeout_remove(&lookup->to);
+ io_remove(&lookup->io);
+ if (close(lookup->fd) < 0)
+ i_error("close(%s) failed: %m", lookup->path);
+
+ i_free(lookup->path);
+ i_free(lookup);
+}
--- /dev/null
+#ifndef ACCESS_LOOKUP_H
+#define ACCESS_LOOKUP_H
+
+typedef void access_lookup_callback_t(bool success, void *context);
+
+struct access_lookup *
+access_lookup(const char *path, int client_fd, const char *daemon_name,
+ access_lookup_callback_t *callback, void *context);
+void access_lookup_destroy(struct access_lookup **lookup);
+
+#endif
DEF(SET_STR_VARS, login_greeting),
DEF(SET_STR, login_log_format_elements),
DEF(SET_STR, login_log_format),
+ DEF(SET_STR, login_access_sockets),
DEF(SET_ENUM, ssl),
DEF(SET_STR, ssl_ca),
.login_greeting = PACKAGE_NAME" ready.",
.login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l %c",
.login_log_format = "%$: %s",
+ .login_access_sockets = "",
.ssl = "yes:no:required",
.ssl_ca = "",
const char *login_trusted_networks;
const char *login_greeting;
const char *login_log_format_elements, *login_log_format;
+ const char *login_access_sockets;
const char *ssl;
const char *ssl_ca;
#include "master-service.h"
#include "master-interface.h"
#include "client-common.h"
+#include "access-lookup.h"
#include "anvil-client.h"
#include "auth-client.h"
#include "ssl-proxy.h"
#include <unistd.h>
#include <syslog.h>
+struct login_access_lookup {
+ struct master_service_connection conn;
+ struct io *io;
+
+ char **sockets, **next_socket;
+ struct access_lookup *access;
+};
+
struct auth_client *auth_client;
struct master_auth *master_auth;
bool closing_down;
static bool shutting_down = FALSE;
static bool ssl_connections = FALSE;
+static void login_access_lookup_next(struct login_access_lookup *lookup);
+
void login_refresh_proctitle(void)
{
struct client *client = clients;
}
}
-static void client_connected(const struct master_service_connection *conn)
+static void
+client_connected_finish(const struct master_service_connection *conn)
{
struct client *client;
struct ssl_proxy *proxy;
if (fd_ssl == -1) {
net_disconnect(conn->fd);
pool_unref(&pool);
+ master_service_client_connection_destroyed(master_service);
return;
}
client->local_port = local_port;
}
+static void login_access_lookup_free(struct login_access_lookup *lookup)
+{
+ if (lookup->io != NULL)
+ io_remove(&lookup->io);
+ if (lookup->access != NULL)
+ access_lookup_destroy(&lookup->access);
+ if (lookup->conn.fd != -1) {
+ if (close(lookup->conn.fd) < 0)
+ i_error("close(client) failed: %m");
+ master_service_client_connection_destroyed(master_service);
+ }
+
+ p_strsplit_free(default_pool, lookup->sockets);
+ i_free(lookup);
+}
+
+static void login_access_callback(bool success, void *context)
+{
+ struct login_access_lookup *lookup = context;
+
+ if (!success) {
+ i_info("access(%s): Client refused (rip=%s)",
+ *lookup->next_socket,
+ net_ip2addr(&lookup->conn.remote_ip));
+ login_access_lookup_free(lookup);
+ } else {
+ lookup->next_socket++;
+ login_access_lookup_next(lookup);
+ }
+}
+
+static void login_access_lookup_next(struct login_access_lookup *lookup)
+{
+ if (*lookup->next_socket == NULL) {
+ /* last one */
+ client_connected_finish(&lookup->conn);
+ lookup->conn.fd = -1;
+ login_access_lookup_free(lookup);
+ return;
+ }
+ lookup->access = access_lookup(*lookup->next_socket, lookup->conn.fd,
+ login_protocol, login_access_callback,
+ lookup);
+ if (lookup->access == NULL)
+ login_access_lookup_free(lookup);
+}
+
+static void client_input_error(struct login_access_lookup *lookup)
+{
+ char c;
+ int ret;
+
+ ret = recv(lookup->conn.fd, &c, 1, MSG_PEEK);
+ if (ret <= 0) {
+ i_info("access(%s): Client disconnected during lookup (rip=%s)",
+ *lookup->next_socket,
+ net_ip2addr(&lookup->conn.remote_ip));
+ login_access_lookup_free(lookup);
+ } else {
+ /* actual input. stop listening until lookup is done. */
+ io_remove(&lookup->io);
+ }
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+ const char *access_sockets =
+ global_login_settings->login_access_sockets;
+ struct login_access_lookup *lookup;
+
+ if (*access_sockets == '\0') {
+ /* no access checks */
+ client_connected_finish(conn);
+ return;
+ }
+
+ lookup = i_new(struct login_access_lookup, 1);
+ lookup->conn = *conn;
+ lookup->io = io_add(conn->fd, IO_READ, client_input_error, lookup);
+ lookup->sockets = p_strsplit_spaces(default_pool, access_sockets, " ");
+ lookup->next_socket = lookup->sockets;
+
+ login_access_lookup_next(lookup);
+}
+
static void auth_connect_notify(struct auth_client *client ATTR_UNUSED,
bool connected, void *context ATTR_UNUSED)
{
pkglibexec_PROGRAMS = \
rawlog \
script \
+ $(TCPWRAP_BIN) \
gdbhelper \
imap-utf7 \
listview \
script_SOURCES = \
script.c
+if TCPWRAPPERS
+TCPWRAP_BIN = tcpwrap
+tcpwrap_LDADD = $(LIBDOVECOT) $(MODULE_LIBS) $(LIBWRAP_LIBS)
+tcpwrap_DEPENDENCIES = $(LIBDOVECOT_DEPS)
+tcpwrap_SOURCES = \
+ tcpwrap.c \
+ tcpwrap-settings.c
+endif
+
gdbhelper_LDADD = $(LIBDOVECOT)
gdbhelper_DEPENDENCIES = $(LIBDOVECOT_DEPS)
gdbhelper_SOURCES = \
--- /dev/null
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "settings-parser.h"
+#include "service-settings.h"
+
+#include <stddef.h>
+
+struct service_settings tcpwrap_service_settings = {
+ .name = "tcpwrap",
+ .protocol = "",
+ .type = "",
+ .executable = "tcpwrap",
+ .user = "dovecot",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .vsz_limit = -1U,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
--- /dev/null
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "fdpass.h"
+#include "write-full.h"
+#include "restrict-access.h"
+#include "master-service.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <tcpd.h>
+
+struct tcpwrap_client {
+ int fd;
+ struct io *io;
+ struct timeout *to;
+};
+
+#define INPUT_TIMEOUT_MSECS (1000*10)
+
+static struct tcpwrap_client *tcpwrap_client = NULL;
+
+static void tcpwrap_client_destroy(struct tcpwrap_client **client);
+
+static void tcpwrap_client_handle(struct tcpwrap_client *client, int check_fd,
+ const char *daemon_name)
+{
+ struct request_info request;
+
+ request_init(&request, RQ_DAEMON, daemon_name,
+ RQ_FILE, check_fd, 0);
+ fromhost(&request);
+
+ if (!hosts_access(&request))
+ (void)write_full(client->fd, "0\n", 2);
+ else
+ (void)write_full(client->fd, "1\n", 2);
+ exit(0);
+}
+
+static void tcpwrap_client_input(struct tcpwrap_client *client)
+{
+ unsigned char buf[1024];
+ ssize_t ret;
+ int check_fd = -1;
+
+ ret = fd_read(client->fd, buf, sizeof(buf), &check_fd);
+ if (ret <= 0) {
+ i_error("fd_read() failed: %m");
+ } else if (ret > 1 && (size_t)ret < sizeof(buf) && buf[ret-1] == '\n') {
+ tcpwrap_client_handle(client, check_fd, t_strndup(buf, ret-1));
+ } else {
+ i_error("Invalid input from client");
+ }
+
+ if (check_fd != -1) {
+ if (close(check_fd) < 0)
+ i_error("close(fdread fd) failed: %m");
+ }
+ tcpwrap_client_destroy(&client);
+}
+
+static void tcpwrap_client_timeout(struct tcpwrap_client *client)
+{
+ tcpwrap_client_destroy(&client);
+}
+
+static struct tcpwrap_client *tcpwrap_client_create(int fd)
+{
+ struct tcpwrap_client *client;
+
+ client = i_new(struct tcpwrap_client, 1);
+ client->fd = fd;
+ client->io = io_add(fd, IO_READ, tcpwrap_client_input, client);
+ client->to = timeout_add(INPUT_TIMEOUT_MSECS, tcpwrap_client_timeout,
+ client);
+ return client;
+}
+
+static void tcpwrap_client_destroy(struct tcpwrap_client **_client)
+{
+ struct tcpwrap_client *client = *_client;
+
+ *_client = NULL;
+
+ timeout_remove(&client->to);
+ io_remove(&client->io);
+ if (close(client->fd) < 0)
+ i_error("close() failed: %m");
+ i_free(client);
+
+ tcpwrap_client = NULL;
+ master_service_client_connection_destroyed(master_service);
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+ if (tcpwrap_client != NULL) {
+ i_error("tcpwrap must be configured with client_limit=1");
+ (void)close(conn->fd);
+ return;
+ }
+ tcpwrap_client = tcpwrap_client_create(conn->fd);
+}
+
+int main(int argc, char *argv[])
+{
+ master_service = master_service_init("tcpwrap", 0,
+ &argc, &argv, NULL);
+ if (master_getopt(master_service) > 0)
+ return FATAL_DEFAULT;
+
+ master_service_init_log(master_service, "tcpwrap: ");
+ restrict_access_by_env(NULL, FALSE);
+ restrict_access_allow_coredumps(TRUE);
+
+ master_service_init_finish(master_service);
+
+ master_service_run(master_service, client_connected);
+ if (tcpwrap_client != NULL)
+ tcpwrap_client_destroy(&tcpwrap_client);
+
+ master_service_deinit(&master_service);
+ return 0;
+}