changeset 2162:95b2a6abc01e HEAD

Added checkpassword passdb. userdb has only beginnings.
author Timo Sirainen <tss@iki.fi>
date Fri, 18 Jun 2004 06:40:12 +0300
parents acc93754e91d
children 506302b8b2fb
files configure.in src/auth/.cvsignore src/auth/Makefile.am src/auth/checkpassword-reply.c src/auth/passdb-checkpassword.c src/auth/passdb.c src/auth/passdb.h
diffstat 7 files changed, 379 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Fri Jun 18 03:33:08 2004 +0300
+++ b/configure.in	Fri Jun 18 06:40:12 2004 +0300
@@ -1,7 +1,7 @@
 AC_INIT(src)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(dovecot, 1.0-test13)
+AM_INIT_AUTOMAKE(dovecot, 1.0-test17)
 
 AM_MAINTAINER_MODE
 
@@ -89,6 +89,15 @@
 	fi,
 	want_pam=yes)
 
+AC_ARG_WITH(checkpassword,
+[  --with-checkpassword    Build with checkpassword support (default)],
+	if test x$withval = xno; then
+		want_checkpassword=no
+	else
+		want_checkpassword=yes
+	fi,
+	want_checkpassword=yes)
+
 AC_ARG_WITH(bsdauth,
 [  --with-bsdauth          Build with BSD authentication support (default)],
 	if test x$withval = xno; then
@@ -986,6 +995,13 @@
 	])
 fi
 
+if test $want_checkpassword = yes; then
+        AC_DEFINE(USERDB_CHECKPASSWORD,, Build with checkpassword userdb support)
+        AC_DEFINE(PASSDB_CHECKPASSWORD,, Build with checkpassword passdb support)
+	userdb="$userdb checkpassword"
+	passdb="$passdb checkpassword"
+fi
+
 if test $want_bsdauth = yes; then
 	AC_CHECK_FUNC(auth_userokay, [
 		AC_DEFINE(PASSDB_BSDAUTH,, Build with BSD authentication support)
--- a/src/auth/.cvsignore	Fri Jun 18 03:33:08 2004 +0300
+++ b/src/auth/.cvsignore	Fri Jun 18 06:40:12 2004 +0300
@@ -7,3 +7,4 @@
 Makefile.in
 so_locations
 dovecot-auth
+checkpassword-reply
--- a/src/auth/Makefile.am	Fri Jun 18 03:33:08 2004 +0300
+++ b/src/auth/Makefile.am	Fri Jun 18 06:40:12 2004 +0300
@@ -1,11 +1,12 @@
 pkglibexecdir = $(libexecdir)/dovecot
 
-pkglibexec_PROGRAMS = dovecot-auth
+pkglibexec_PROGRAMS = dovecot-auth checkpassword-reply
 
 INCLUDES = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-settings \
 	-DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \
+	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
 	$(AUTH_CFLAGS)
 
 dovecot_auth_LDADD = \
@@ -37,6 +38,7 @@
 	passdb-passwd.c \
 	passdb-passwd-file.c \
 	passdb-pam.c \
+	passdb-checkpassword.c \
 	passdb-shadow.c \
 	passdb-vpopmail.c \
 	passdb-mysql.c \
@@ -70,3 +72,9 @@
 	password-scheme.h \
 	userdb.h \
 	userdb-vpopmail.h
+
+checkpassword_reply_LDADD = \
+	../lib/liblib.a
+
+checkpassword_reply_sources =
+	checkpassword-reply.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/checkpassword-reply.c	Fri Jun 18 06:40:12 2004 +0300
@@ -0,0 +1,26 @@
+/* simple checkpassword wrapper to send userdb data back to dovecot-auth */
+
+#include "lib.h"
+#include "str.h"
+#include "write-full.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(void)
+{
+	string_t *str;
+
+	lib_init();
+	str = t_str_new(1024);
+
+	str_printfa(str, "USER=%s\nHOME=%s\nSHELL=%s\nUID=%s\nGID=%s\n\n",
+		    getenv("USER"), getenv("HOME"), getenv("SHELL"),
+		    dec2str(getuid()), dec2str(getgid()));
+
+	if (write_full(4, str_data(str), str_len(str)) < 0) {
+		i_error("write_full() failed: %m");
+		exit(111);
+	}
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-checkpassword.c	Fri Jun 18 06:40:12 2004 +0300
@@ -0,0 +1,321 @@
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_CHECKPASSWORD
+
+#include "common.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "hash.h"
+#include "passdb.h"
+#include "safe-memset.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;
+
+	buffer_t *input_buf;
+	char *password;
+	unsigned int write_pos;
+
+	struct auth_request *request;
+        verify_plain_callback_t *callback;
+};
+
+static char *checkpassword_path, *checkpassword_reply_path;
+struct hash_table *clients;
+static struct timeout *to_wait;
+
+static void checkpassword_request_close(struct chkpw_auth_request *request)
+{
+	if (request->input_buf != NULL) {
+		buffer_free(request->input_buf);
+		request->input_buf = NULL;
+	}
+
+	if (request->fd_in != -1) {
+		if (close(request->fd_in) < 0)
+			i_error("checkpassword: close() failed: %m");
+		request->fd_in = -1;
+	}
+	if (request->io_in != NULL) {
+		io_remove(request->io_in);
+		request->io_in = NULL;
+	}
+
+	if (request->io_out != NULL)
+		io_remove(request->io_out);
+	if (request->fd_out != -1) {
+		if (close(request->fd_out) < 0)
+			i_error("checkpassword: close() failed: %m");
+	}
+}
+
+static void checkpassword_request_finish(struct chkpw_auth_request *request,
+					 enum passdb_result result)
+{
+	hash_remove(clients, POINTER_CAST(request->pid));
+
+	/* FIXME: store request->input_buf so userdb can fetch it */
+
+	if (auth_request_unref(request->request))
+		request->callback(result, request->request);
+
+        checkpassword_request_close(request);
+
+	safe_memset(request->password, 0, strlen(request->password));
+	i_free(request->password);
+	i_free(request);
+}
+
+static void wait_timeout(void *context __attr_unused__)
+{
+	struct chkpw_auth_request *request;
+	int status;
+	pid_t pid;
+
+	/* FIXME: if we ever do some other kind of forking, this needs fixing */
+	while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
+		if (pid == -1) {
+			if (errno == ECHILD) {
+				timeout_remove(to_wait);
+				to_wait = NULL;
+			} else if (errno != EINTR)
+				i_error("waitpid() failed: %m");
+			return;
+		}
+
+		request = hash_lookup(clients, POINTER_CAST(pid));
+
+		if (WIFSIGNALED(status)) {
+			i_error("checkpassword: Child %s died with signal %d",
+				dec2str(pid), WTERMSIG(status));
+		} else if (WIFEXITED(status) && request != NULL) {
+			switch (WEXITSTATUS(status)) {
+			case 0:
+				checkpassword_request_finish(request,
+							     PASSDB_RESULT_OK);
+				request = NULL;
+				break;
+			case 1:
+				checkpassword_request_finish(request,
+							     PASSDB_RESULT_OK);
+				request = NULL;
+				break;
+			case 2:
+				/* checkpassword is called with wrong
+				   parameters? unlikely */
+			case 111:
+				/* temporary problem, treat as internal error */
+			default:
+				/* whatever error.. */
+				i_error("checkpassword: "
+					"Child %s exited with status %d",
+					dec2str(pid), WEXITSTATUS(status));
+				break;
+			}
+		}
+
+		if (request != NULL) {
+			checkpassword_request_finish(request,
+				PASSDB_RESULT_INTERNAL_FAILURE);
+		}
+	}
+}
+
+static void checkpassword_verify_plain_child(int fd_in, int fd_out)
+{
+	char *args[3];
+
+	if (dup2(fd_out, 3) < 0)
+		i_error("checkpassword: dup2() failed: %m");
+	else if (dup2(fd_in, 4) < 0)
+		i_error("checkpassword: dup2() failed: %m");
+	else {
+		args[0] = checkpassword_path;
+		args[1] = checkpassword_reply_path;
+		args[2] = NULL;
+
+		execv(checkpassword_path, args);
+		i_error("checkpassword: execv(%s) failed: %m",
+			checkpassword_path);
+	}
+	exit(2);
+}
+
+static void checkpassword_child_input(void *context)
+{
+	struct chkpw_auth_request *request = context;
+	unsigned char buf[1024];
+	ssize_t ret;
+
+	ret = read(request->fd_in, buf, sizeof(buf));
+	if (ret <= 0) {
+		if (ret < 0)
+			i_error("checkpassword: read() failed: %m");
+		checkpassword_request_close(request);
+	} else {
+		if (request->input_buf == NULL) {
+			request->input_buf =
+				buffer_create_dynamic(default_pool,
+						      512, (size_t)-1);
+		}
+		buffer_append(request->input_buf, buf, ret);
+	}
+}
+
+static void checkpassword_child_output(void *context)
+{
+	/* 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 chkpw_auth_request *request = context;
+	struct auth_request *auth_request = request->request;
+	buffer_t *buf;
+	const unsigned char *data;
+	size_t size;
+	ssize_t ret;
+
+	buf = buffer_create_static(pool_datastack_create(), 512+1);
+	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);
+	buffer_append(buf, request->password, strlen(request->password)+1);
+	buffer_append_c(buf, '\0');
+	data = buffer_get_data(buf, &size);
+
+	ret = write(request->fd_out, data + request->write_pos,
+		    size - request->write_pos);
+	if (ret <= 0) {
+		if (ret < 0)
+			i_error("checkpassword: write() failed: %m");
+		checkpassword_request_close(request);
+		return;
+	}
+
+	request->write_pos += ret;
+	if (request->write_pos < size)
+		return;
+
+	if (close(request->fd_out) < 0)
+		i_error("checkpassword: close() failed: %m");
+        request->fd_out = -1;
+
+	io_remove(request->io_out);
+	request->io_out = NULL;
+}
+
+static void
+checkpassword_verify_plain(struct auth_request *request, const char *password,
+			   verify_plain_callback_t *callback)
+{
+	struct chkpw_auth_request *chkpw_auth_request;
+	int fd_in[2], fd_out[2];
+	pid_t pid;
+
+	fd_in[0] = -1;
+	if (pipe(fd_in) < 0 || pipe(fd_out) < 0) {
+		i_error("checkpassword(%s): pipe() failed: %m",
+			get_log_prefix(request));
+		callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
+		if (fd_in[0] != -1) {
+			(void)close(fd_in[0]);
+			(void)close(fd_in[1]);
+		}
+		return;
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		i_error("checkpassword(%s): fork() failed: %m",
+			get_log_prefix(request));
+		callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
+		(void)close(fd_in[0]);
+		(void)close(fd_in[1]);
+		(void)close(fd_out[0]);
+		(void)close(fd_out[1]);
+		return;
+	}
+
+	if (pid == 0) {
+		(void)close(fd_in[0]);
+		(void)close(fd_out[1]);
+		checkpassword_verify_plain_child(fd_in[1], fd_out[0]);
+	}
+
+	if (close(fd_in[1]) < 0) {
+		i_error("checkpassword(%s): close(fd_in[1]) failed: %m",
+			get_log_prefix(request));
+	}
+	if (close(fd_out[0]) < 0) {
+		i_error("checkpassword(%s): close(fd_out[0]) failed: %m",
+			get_log_prefix(request));
+	}
+
+	auth_request_ref(request);
+	chkpw_auth_request = i_new(struct chkpw_auth_request, 1);
+	chkpw_auth_request->fd_in = fd_in[0];
+	chkpw_auth_request->fd_out = fd_out[1];
+	chkpw_auth_request->pid = pid;
+	chkpw_auth_request->password = i_strdup(password);
+	chkpw_auth_request->request = request;
+	chkpw_auth_request->callback = callback;
+
+	chkpw_auth_request->io_in =
+		io_add(fd_in[0], IO_READ, checkpassword_child_input,
+		       chkpw_auth_request);
+	chkpw_auth_request->io_out =
+		io_add(fd_out[1], IO_WRITE, checkpassword_child_output,
+		       chkpw_auth_request);
+
+	hash_insert(clients, POINTER_CAST(pid), chkpw_auth_request);
+
+	if (to_wait == NULL) {
+		/* FIXME: we could use SIGCHLD */
+		to_wait = timeout_add(1000, wait_timeout, NULL);
+	}
+}
+
+static void checkpassword_init(const char *args)
+{
+	checkpassword_path = i_strdup(args);
+	checkpassword_reply_path =
+		i_strdup(PKG_LIBEXECDIR"/checkpassword-reply");
+
+	to_wait = NULL;
+	clients = hash_create(default_pool, default_pool, 0, NULL, NULL);
+}
+
+static void checkpassword_deinit(void)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		checkpassword_request_finish(value,
+					     PASSDB_RESULT_INTERNAL_FAILURE);
+	}
+	hash_iterate_deinit(iter);
+	hash_destroy(clients);
+
+	if (to_wait != NULL)
+		timeout_remove(to_wait);
+
+	i_free(checkpassword_path);
+	i_free(checkpassword_reply_path);
+}
+
+struct passdb_module passdb_checkpassword = {
+	checkpassword_init,
+	checkpassword_deinit,
+
+	checkpassword_verify_plain,
+	NULL
+};
+
+#endif
--- a/src/auth/passdb.c	Fri Jun 18 03:33:08 2004 +0300
+++ b/src/auth/passdb.c	Fri Jun 18 06:40:12 2004 +0300
@@ -120,6 +120,10 @@
 	if (strcasecmp(name, "pam") == 0)
 		passdb = &passdb_pam;
 #endif
+#ifdef PASSDB_CHECKPASSWORD
+	if (strcasecmp(name, "checkpassword") == 0)
+		passdb = &passdb_checkpassword;
+#endif
 #ifdef PASSDB_SHADOW
 	if (strcasecmp(name, "shadow") == 0)
 		passdb = &passdb_shadow;
--- a/src/auth/passdb.h	Fri Jun 18 03:33:08 2004 +0300
+++ b/src/auth/passdb.h	Fri Jun 18 06:40:12 2004 +0300
@@ -57,6 +57,7 @@
 extern struct passdb_module passdb_shadow;
 extern struct passdb_module passdb_passwd_file;
 extern struct passdb_module passdb_pam;
+extern struct passdb_module passdb_checkpassword;
 extern struct passdb_module passdb_vpopmail;
 extern struct passdb_module passdb_ldap;
 extern struct passdb_module passdb_pgsql;