dovecot-1.2: Added support for userdb checkpassword. Patch by Sa...

dovecot at dovecot.org dovecot at dovecot.org
Wed Oct 22 00:29:59 EEST 2008


details:   http://hg.dovecot.org/dovecot-1.2/rev/33eae1ca0be0
changeset: 8307:33eae1ca0be0
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Oct 22 00:29:54 2008 +0300
description:
Added support for userdb checkpassword. Patch by Sascha Wilde.

diffstat:

8 files changed, 585 insertions(+), 251 deletions(-)
configure.in                    |    4 
src/auth/Makefile.am            |    3 
src/auth/db-checkpassword.c     |  209 +++++++++++++++++++++++++++
src/auth/db-checkpassword.h     |   54 +++++++
src/auth/mech-winbind.c         |    1 
src/auth/passdb-checkpassword.c |  295 +++++----------------------------------
src/auth/userdb-checkpassword.c |  268 +++++++++++++++++++++++++++++++++++
src/auth/userdb.c               |    2 

diffs (truncated from 1008 to 300 lines):

diff -r 3e8f847f68a4 -r 33eae1ca0be0 configure.in
--- a/configure.in	Wed Oct 22 00:28:46 2008 +0300
+++ b/configure.in	Wed Oct 22 00:29:54 2008 +0300
@@ -1,5 +1,5 @@ AC_PREREQ([2.59])
 AC_PREREQ([2.59])
-AC_INIT([Dovecot],[1.2.alpha2],[dovecot at dovecot.org])
+AC_INIT([Dovecot],[1.2.alpha3],[dovecot at dovecot.org])
 AC_CONFIG_SRCDIR([src])
 
 AM_INIT_AUTOMAKE
@@ -1762,7 +1762,9 @@ fi
 
 if test $want_checkpassword != no; then
         AC_DEFINE(PASSDB_CHECKPASSWORD,, Build with checkpassword passdb support)
+        AC_DEFINE(USERDB_CHECKPASSWORD,, Build with checkpassword userdb support)
 	passdb="$passdb checkpassword"
+	userdb="$userdb checkpassword"
 fi
 
 if test $want_bsdauth != no; then
diff -r 3e8f847f68a4 -r 33eae1ca0be0 src/auth/Makefile.am
--- a/src/auth/Makefile.am	Wed Oct 22 00:28:46 2008 +0300
+++ b/src/auth/Makefile.am	Wed Oct 22 00:29:54 2008 +0300
@@ -67,6 +67,7 @@ dovecot_auth_SOURCES = \
 	auth-stream.c \
 	auth-worker-client.c \
 	auth-worker-server.c \
+	db-checkpassword.c \
 	db-sql.c \
 	db-passwd-file.c \
 	main.c \
@@ -98,6 +99,7 @@ dovecot_auth_SOURCES = \
 	passdb-sql.c \
 	userdb.c \
 	userdb-blocking.c \
+	userdb-checkpassword.c \
 	userdb-nss.c \
 	userdb-passwd.c \
 	userdb-passwd-file.c \
@@ -125,6 +127,7 @@ headers = \
 	db-sql.h \
 	db-passwd-file.h \
 	common.h \
+	db-checkpassword.h \
 	mech.h \
 	mycrypt.h \
 	otp-skey-common.h \
diff -r 3e8f847f68a4 -r 33eae1ca0be0 src/auth/db-checkpassword.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/db-checkpassword.c	Wed Oct 22 00:29:54 2008 +0300
@@ -0,0 +1,209 @@
+/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+
+#if defined(PASSDB_CHECKPASSWORD) || defined(USERDB_CHECKPASSWORD)
+
+#include "db-checkpassword.h"
+
+static void env_put_extra_fields(const char *extra_fields)
+{
+	const char *const *tmp;
+	const char *key, *p;
+
+	for (tmp = t_strsplit(extra_fields, "\t"); *tmp != NULL; tmp++) {
+		key = t_str_ucase(t_strcut(*tmp, '='));
+		p = strchr(*tmp, '=');
+		if (p == NULL)
+			env_put(t_strconcat(key, "=1", NULL));
+		else
+			env_put(t_strconcat(key, p, NULL));
+	}
+}
+
+static void checkpassword_request_close(struct chkpw_auth_request *request)
+{
+	if (request->io_in != NULL)
+		io_remove(&request->io_in);
+	if (request->io_out != NULL)
+		io_remove(&request->io_out);
+
+	if (request->fd_in != -1) {
+		if (close(request->fd_in) < 0)
+			i_error("checkpassword: close() failed: %m");
+		request->fd_in = -1;
+	}
+	if (request->fd_out != -1) {
+		if (close(request->fd_out) < 0)
+			i_error("checkpassword: close() failed: %m");
+	}
+}
+
+void checkpassword_request_free(struct chkpw_auth_request *request)
+{
+	checkpassword_request_close(request);
+	if (request->input_buf != NULL)
+		str_free(&request->input_buf);
+
+	if (request->password != NULL) {
+		safe_memset(request->password, 0, strlen(request->password));
+		i_free(request->password);
+	}
+	i_free(request);
+}
+
+enum checkpassword_sigchld_handler_result
+checkpassword_sigchld_handler(const struct child_wait_status *child_wait_status,
+			      struct chkpw_auth_request *request)
+{
+	int status = child_wait_status->status;
+	pid_t pid = child_wait_status->pid;
+
+	if (request == NULL) {
+		i_error("checkpassword: sighandler called for unknown child %d", pid);
+		return SIGCHLD_RESULT_UNKNOWN_CHILD;
+	}
+
+	if (WIFSIGNALED(status)) {
+		i_error("checkpassword: Child %s died with signal %d",
+			dec2str(pid), WTERMSIG(status));
+		return SIGCHLD_RESULT_DEAD_CHILD;
+	} else if (WIFEXITED(status)) {
+		request->exited = TRUE;
+		request->exit_status = WEXITSTATUS(status);
+
+		auth_request_log_debug(request->request,
+				       "checkpassword", "exit_status=%d",
+				       request->exit_status);
+		return SIGCHLD_RESULT_OK;
+	} else {
+		/* shouldn't happen */
+		auth_request_log_debug(request->request, "checkpassword",
+				       "Child exited with status=%d", status);
+		return SIGCHLD_RESULT_UNKNOWN_ERROR;
+	}
+}
+
+void checkpassword_setup_env(struct auth_request *request)
+{
+	/* Besides passing the standard username and password in a
+	   pipe, also pass some other possibly interesting information
+	   via environment. Use UCSPI names for local/remote IPs. */
+	env_put("PROTO=TCP"); /* UCSPI */
+	env_put(t_strconcat("SERVICE=", request->service, NULL));
+	if (request->local_ip.family != 0) {
+		env_put(t_strconcat("TCPLOCALIP=",
+				    net_ip2addr(&request->local_ip), NULL));
+		/* FIXME: for backwards compatibility only,
+		   remove some day */
+		env_put(t_strconcat("LOCAL_IP=",
+				    net_ip2addr(&request->local_ip), NULL));
+	}
+	if (request->remote_ip.family != 0) {
+		env_put(t_strconcat("TCPREMOTEIP=",
+				    net_ip2addr(&request->remote_ip), NULL));
+		/* FIXME: for backwards compatibility only,
+		   remove some day */
+		env_put(t_strconcat("REMOTE_IP=",
+				    net_ip2addr(&request->remote_ip), NULL));
+	}
+	if (request->local_port != 0) {
+		env_put(t_strdup_printf("TCPLOCALPORT=%u",
+					request->local_port));
+	}
+	if (request->remote_port != 0) {
+		env_put(t_strdup_printf("TCPREMOTEPORT=%u",
+					request->remote_port));
+	}
+	if (request->master_user != NULL) {
+		env_put(t_strconcat("MASTER_USER=",
+				    request->master_user, NULL));
+	}
+	if (request->extra_fields != NULL) {
+		const char *fields =
+			auth_stream_reply_export(request->extra_fields);
+
+		/* extra fields could come from master db */
+		env_put_extra_fields(fields);
+	}
+}
+
+void checkpassword_child_input(struct chkpw_auth_request *request)
+{
+	unsigned char buf[1024];
+	ssize_t ret;
+
+	ret = read(request->fd_in, buf, sizeof(buf));
+	if (ret <= 0) {
+		if (ret < 0) {
+			auth_request_log_error(request->request,
+				"checkpassword", "read() failed: %m");
+		}
+
+		auth_request_log_debug(request->request, "checkpassword",
+				       "Received no input");
+		checkpassword_request_close(request);
+		request->half_finish_callback(request);
+	} else {
+		if (request->input_buf == NULL)
+			request->input_buf = str_new(default_pool, 512);
+		str_append_n(request->input_buf, buf, ret);
+
+		auth_request_log_debug(request->request, "checkpassword",
+			"Received input: %s", str_c(request->input_buf));
+	}
+}
+
+void checkpassword_child_output(struct chkpw_auth_request *request)
+{
+	/* Send: username \0 password \0 timestamp \0.
+	   Must be 512 bytes or less. The "timestamp" parameter is actually
+	   useful only for APOP authentication. We don't support it, so
+	   keep it empty */
+	struct auth_request *auth_request = request->request;
+	buffer_t *buf;
+	const unsigned char *data;
+	size_t size;
+	ssize_t ret;
+
+	buf = buffer_create_dynamic(pool_datastack_create(), 512+1);
+	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);
+        if (request->password != NULL)
+                buffer_append(buf, request->password, strlen(request->password)+1);
+        else
+                buffer_append_c(buf, '\0');
+	buffer_append_c(buf, '\0');
+	data = buffer_get_data(buf, &size);
+
+	if (size > 512) {
+		auth_request_log_error(request->request, "checkpassword",
+			"output larger than 512 bytes: %"PRIuSIZE_T, size);
+		request->finish_callback(request,
+					 request->internal_failure_code);
+		return;
+	}
+
+	ret = write(request->fd_out, data + request->write_pos,
+		    size - request->write_pos);
+	if (ret <= 0) {
+		if (ret < 0) {
+			auth_request_log_error(request->request,
+				"checkpassword", "write() failed: %m");
+		}
+		request->finish_callback(request,
+					 request->internal_failure_code);
+		return;
+	}
+
+	request->write_pos += ret;
+	if (request->write_pos < size)
+		return;
+
+	io_remove(&request->io_out);
+
+	if (close(request->fd_out) < 0)
+		i_error("checkpassword: close() failed: %m");
+	request->fd_out = -1;
+}
+
+#endif
diff -r 3e8f847f68a4 -r 33eae1ca0be0 src/auth/db-checkpassword.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/db-checkpassword.h	Wed Oct 22 00:29:54 2008 +0300
@@ -0,0 +1,54 @@
+#ifndef CHECKPASSWORD_COMMON_H
+#define CHECKPASSWORD_COMMON_H
+
+#include "auth-request.h"
+#include "lib-signals.h"
+#include "buffer.h"
+#include "str.h"
+#include "ioloop.h"
+#include "hash.h"
+#include "env-util.h"
+#include "safe-memset.h"
+#include "child-wait.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+
+struct chkpw_auth_request {
+	int fd_out, fd_in;
+	struct io *io_out, *io_in;
+	pid_t pid;
+
+	string_t *input_buf;
+	char *password;
+	unsigned int write_pos;
+
+	struct auth_request *request;
+	void *callback;
+	void (*half_finish_callback)();
+	void (*finish_callback)();
+        int internal_failure_code;
+
+	int exit_status;
+	unsigned int exited:1;
+};


More information about the dovecot-cvs mailing list