AC_PREREQ([2.59])
-AC_INIT([Dovecot],[1.2.alpha2],[dovecot@dovecot.org])
+AC_INIT([Dovecot],[1.2.alpha3],[dovecot@dovecot.org])
AC_CONFIG_SRCDIR([src])
AM_INIT_AUTOMAKE
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
auth-stream.c \
auth-worker-client.c \
auth-worker-server.c \
+ db-checkpassword.c \
db-sql.c \
db-passwd-file.c \
main.c \
passdb-sql.c \
userdb.c \
userdb-blocking.c \
+ userdb-checkpassword.c \
userdb-nss.c \
userdb-passwd.c \
userdb-passwd-file.c \
db-sql.h \
db-passwd-file.h \
common.h \
+ db-checkpassword.h \
mech.h \
mycrypt.h \
otp-skey-common.h \
--- /dev/null
+/* 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
--- /dev/null
+#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;
+};
+
+enum checkpassword_sigchld_handler_result {
+ SIGCHLD_RESULT_UNKNOWN_CHILD = -1,
+ SIGCHLD_RESULT_DEAD_CHILD = -2,
+ SIGCHLD_RESULT_UNKNOWN_ERROR = -3,
+ SIGCHLD_RESULT_OK = 1,
+};
+
+
+void checkpassword_request_free(struct chkpw_auth_request *request);
+enum checkpassword_sigchld_handler_result
+checkpassword_sigchld_handler(const struct child_wait_status *child_wait_status,
+ struct chkpw_auth_request *request);
+void checkpassword_setup_env(struct auth_request *request);
+void checkpassword_child_input(struct chkpw_auth_request *request);
+void checkpassword_child_output(struct chkpw_auth_request *request);
+
+#endif
if (winbind->pid == -1)
return;
+ /* FIXME: use child-wait.h API */
if ((ret = waitpid(winbind->pid, &status, WNOHANG)) <= 0) {
if (ret < 0 && errno != ECHILD && errno != EINTR)
i_error("waitpid() failed: %m");
#include "common.h"
#include "passdb.h"
-#ifdef PASSDB_CHECKPASSWORD
+#ifdef PASSDB_CHECKPASSWORD
-#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 <stdlib.h>
-#include <unistd.h>
-#include <sys/wait.h>
+#include "db-checkpassword.h"
struct checkpassword_passdb_module {
struct passdb_module module;
struct hash_table *clients;
};
-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;
- verify_plain_callback_t *callback;
-
- int exit_status;
- unsigned int exited:1;
-};
-
-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");
- }
-}
+static struct child_wait *checkpassword_passdb_children = NULL;
static void checkpassword_request_finish(struct chkpw_auth_request *request,
enum passdb_result result)
struct passdb_module *_module = request->request->passdb->passdb;
struct checkpassword_passdb_module *module =
(struct checkpassword_passdb_module *)_module;
+ verify_plain_callback_t *callback =
+ (verify_plain_callback_t *)request->callback;
hash_remove(module->clients, POINTER_CAST(request->pid));
}
}
- request->callback(result, request->request);
- auth_request_unref(&request->request);
-
- checkpassword_request_close(request);
-
- if (request->input_buf != NULL)
- str_free(&request->input_buf);
+ callback(result, request->request);
- safe_memset(request->password, 0, strlen(request->password));
- i_free(request->password);
- i_free(request);
+ auth_request_unref(&request->request);
+ checkpassword_request_free(request);
}
static void
}
}
-static void sigchld_handler(int signo ATTR_UNUSED, void *context)
+static void sigchld_handler(const struct child_wait_status *status,
+ struct checkpassword_passdb_module *module)
{
- struct checkpassword_passdb_module *module = context;
- 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 && errno != EINTR)
- i_error("waitpid() failed: %m");
- return;
- }
-
- request = hash_lookup(module->clients, POINTER_CAST(pid));
- if (request == NULL) {
- /* unknown child finished */
- if (WIFSIGNALED(status)) {
- i_error("checkpassword: Unknown child %s died "
- "with signal %d", dec2str(pid),
- WTERMSIG(status));
- }
- continue;
- }
-
- if (WIFSIGNALED(status)) {
- i_error("checkpassword: Child %s died with signal %d",
- dec2str(pid), WTERMSIG(status));
- } else if (WIFEXITED(status)) {
- request->exited = TRUE;
- request->exit_status = WEXITSTATUS(status);
+ struct chkpw_auth_request *request =
+ hash_lookup(module->clients, POINTER_CAST(status->pid));
- auth_request_log_debug(request->request,
- "checkpassword", "exit_status=%d",
- request->exit_status);
-
- checkpassword_request_half_finish(request);
- request = NULL;
- } else {
- /* shouldn't happen */
- auth_request_log_debug(request->request,
- "checkpassword", "Child exited with status=%d",
- status);
- }
-
- if (request != NULL) {
- checkpassword_request_finish(request,
- PASSDB_RESULT_INTERNAL_FAILURE);
- }
- }
-}
-
-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));
+ switch (checkpassword_sigchld_handler(status, request)) {
+ case SIGCHLD_RESULT_UNKNOWN_CHILD:
+ case SIGCHLD_RESULT_DEAD_CHILD:
+ break;
+ case SIGCHLD_RESULT_UNKNOWN_ERROR:
+ checkpassword_request_finish(request,
+ PASSDB_RESULT_INTERNAL_FAILURE);
+ break;
+ case SIGCHLD_RESULT_OK:
+ checkpassword_request_half_finish(request);
+ request = NULL;
+ break;
}
}
auth_request_log_error(request, "checkpassword",
"dup2() failed: %m");
} else {
- /* 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);
- }
-
+ checkpassword_setup_env(request);
/* very simple argument splitting. */
cmd = t_strconcat(module->checkpassword_path, " ",
module->checkpassword_reply_path, NULL);
exit(2);
}
-static 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);
- checkpassword_request_half_finish(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));
- }
-}
-
-static 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);
- buffer_append(buf, request->password, strlen(request->password)+1);
- 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);
- checkpassword_request_finish(request,
- PASSDB_RESULT_INTERNAL_FAILURE);
- 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");
- }
- checkpassword_request_finish(request,
- PASSDB_RESULT_INTERNAL_FAILURE);
- 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;
-}
-
static void
checkpassword_verify_plain(struct auth_request *request, const char *password,
verify_plain_callback_t *callback)
chkpw_auth_request->password = i_strdup(password);
chkpw_auth_request->request = request;
chkpw_auth_request->callback = callback;
+ chkpw_auth_request->half_finish_callback =
+ checkpassword_request_half_finish;
+ chkpw_auth_request->finish_callback =
+ checkpassword_request_finish;
+ chkpw_auth_request->internal_failure_code =
+ PASSDB_RESULT_INTERNAL_FAILURE;
chkpw_auth_request->io_in =
io_add(fd_in[0], IO_READ, checkpassword_child_input,
chkpw_auth_request);
hash_insert(module->clients, POINTER_CAST(pid), chkpw_auth_request);
+
+ if (checkpassword_passdb_children != NULL)
+ child_wait_add_pid(checkpassword_passdb_children, pid);
+ else {
+ checkpassword_passdb_children =
+ child_wait_new_with_pid(pid, sigchld_handler, module);
+ }
}
static struct passdb_module *
return &module->module;
}
-static void checkpassword_init(struct passdb_module *module,
- const char *args ATTR_UNUSED)
-{
- lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, module);
-}
-
static void checkpassword_deinit(struct passdb_module *_module)
{
struct checkpassword_passdb_module *module =
struct hash_iterate_context *iter;
void *key, *value;
- lib_signals_unset_handler(SIGCHLD, sigchld_handler, module);
-
iter = hash_iterate_init(module->clients);
while (hash_iterate(iter, &key, &value)) {
checkpassword_request_finish(value,
}
hash_iterate_deinit(&iter);
hash_destroy(&module->clients);
+
+ if (checkpassword_passdb_children != NULL)
+ child_wait_free(&checkpassword_passdb_children);
}
struct passdb_module_interface passdb_checkpassword = {
"checkpassword",
checkpassword_preinit,
- checkpassword_init,
+ NULL,
checkpassword_deinit,
checkpassword_verify_plain,
--- /dev/null
+/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "userdb.h"
+
+#ifdef USERDB_CHECKPASSWORD
+
+#include "db-checkpassword.h"
+
+struct checkpassword_userdb_module {
+ struct userdb_module module;
+
+ const char *checkpassword_path, *checkpassword_reply_path;
+ struct hash_table *clients;
+};
+
+static struct child_wait *checkpassword_userdb_children = NULL;
+
+static void checkpassword_request_finish(struct chkpw_auth_request *request,
+ enum userdb_result result)
+{
+ struct userdb_module *_module = request->request->userdb->userdb;
+ struct checkpassword_userdb_module *module =
+ (struct checkpassword_userdb_module *)_module;
+ userdb_callback_t *callback =
+ (userdb_callback_t *)request->callback;
+
+ hash_remove(module->clients, POINTER_CAST(request->pid));
+
+ if (result == USERDB_RESULT_OK) {
+ if (strchr(str_c(request->input_buf), '\n') != NULL) {
+ auth_request_log_error(request->request,
+ "userdb-checkpassword",
+ "LF characters in checkpassword reply");
+ result = USERDB_RESULT_INTERNAL_FAILURE;
+ } else {
+ auth_request_init_userdb_reply(request->request);
+ auth_request_set_fields(request->request,
+ t_strsplit(str_c(request->input_buf), "\t"),
+ NULL);
+ }
+ }
+
+ callback(result, request->request);
+
+ auth_request_unref(&request->request);
+ checkpassword_request_free(request);
+}
+
+static void
+checkpassword_request_half_finish(struct chkpw_auth_request *request)
+{
+ if (!request->exited || request->fd_in != -1)
+ return;
+
+ switch (request->exit_status) {
+ case 3:
+ /* User does not exist. */
+ auth_request_log_info(request->request, "userdb-checkpassword",
+ "User unknown");
+ checkpassword_request_finish(request,
+ USERDB_RESULT_USER_UNKNOWN);
+ break;
+ case 2:
+ /* This is intentionally not 0. checkpassword-reply exits with
+ 2 on success when AUTHORIZED is set. */
+ if (request->input_buf != NULL) {
+ checkpassword_request_finish(request, USERDB_RESULT_OK);
+ break;
+ }
+ /* missing input - fall through */
+ default:
+ /* whatever error... */
+ auth_request_log_error(request->request, "userdb-checkpassword",
+ "Child %s exited with status %d",
+ dec2str(request->pid), request->exit_status);
+ checkpassword_request_finish(request,
+ USERDB_RESULT_INTERNAL_FAILURE);
+ break;
+ }
+}
+
+static void sigchld_handler(const struct child_wait_status *status,
+ struct checkpassword_userdb_module *module)
+{
+ struct chkpw_auth_request *request =
+ hash_lookup(module->clients, POINTER_CAST(status->pid));
+
+ switch (checkpassword_sigchld_handler(status, request)) {
+ case SIGCHLD_RESULT_UNKNOWN_CHILD:
+ case SIGCHLD_RESULT_DEAD_CHILD:
+ break;
+ case SIGCHLD_RESULT_UNKNOWN_ERROR:
+ checkpassword_request_finish(request,
+ USERDB_RESULT_INTERNAL_FAILURE);
+ break;
+ case SIGCHLD_RESULT_OK:
+ checkpassword_request_half_finish(request);
+ request = NULL;
+ break;
+ }
+}
+
+static void ATTR_NORETURN
+checkpassword_lookup_child(struct auth_request *request,
+ struct checkpassword_userdb_module *module,
+ int fd_in, int fd_out)
+{
+ const char *cmd, *const *args;
+
+ if (dup2(fd_out, 3) < 0 || dup2(fd_in, 4) < 0) {
+ auth_request_log_error(request, "userdb-checkpassword",
+ "dup2() failed: %m");
+ } else {
+ /* We want to retrieve user data and don't do
+ authorization, so we need to signalize the
+ checkpassword program that the password shall be
+ ignored by setting AUTHORIZED. This needs a
+ special checkpassword program which knows how to
+ handle this. */
+ env_put("AUTHORIZED=YES");
+ checkpassword_setup_env(request);
+ /* very simple argument splitting. */
+ cmd = t_strconcat(module->checkpassword_path, " ",
+ module->checkpassword_reply_path, NULL);
+ auth_request_log_debug(request, "userdb-checkpassword",
+ "execute: %s", cmd);
+
+ args = t_strsplit(cmd, " ");
+ execv(args[0], (char **)args);
+ auth_request_log_error(request, "userdb-checkpassword",
+ "execv(%s) failed: %m", args[0]);
+ }
+ exit(2);
+}
+
+static void
+checkpassword_lookup(struct auth_request *request, userdb_callback_t *callback)
+{
+ struct userdb_module *_module = request->userdb->userdb;
+ struct checkpassword_userdb_module *module =
+ (struct checkpassword_userdb_module *)_module;
+ 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) {
+ auth_request_log_error(request, "userdb-checkpassword",
+ "pipe() failed: %m");
+ callback(USERDB_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) {
+ auth_request_log_error(request, "userdb-checkpassword",
+ "fork() failed: %m");
+ callback(USERDB_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_lookup_child(request, module,
+ fd_in[1], fd_out[0]);
+ /* not reached */
+ }
+
+ if (close(fd_in[1]) < 0) {
+ auth_request_log_error(request, "userdb-checkpassword",
+ "close(fd_in[1]) failed: %m");
+ }
+ if (close(fd_out[0]) < 0) {
+ auth_request_log_error(request, "userdb-checkpassword",
+ "close(fd_out[0]) failed: %m");
+ }
+
+ 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->request = request;
+ chkpw_auth_request->callback = callback;
+ chkpw_auth_request->half_finish_callback =
+ checkpassword_request_half_finish;
+ chkpw_auth_request->finish_callback =
+ checkpassword_request_finish;
+ chkpw_auth_request->internal_failure_code =
+ USERDB_RESULT_INTERNAL_FAILURE;
+
+ 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(module->clients, POINTER_CAST(pid), chkpw_auth_request);
+
+ if (checkpassword_userdb_children != NULL)
+ child_wait_add_pid(checkpassword_userdb_children, pid);
+ else {
+ checkpassword_userdb_children =
+ child_wait_new_with_pid(pid, sigchld_handler, module);
+ }
+}
+
+static struct userdb_module *
+checkpassword_preinit(struct auth_userdb *auth_userdb, const char *args)
+{
+ struct checkpassword_userdb_module *module;
+
+ module = p_new(auth_userdb->auth->pool,
+ struct checkpassword_userdb_module, 1);
+ module->checkpassword_path = p_strdup(auth_userdb->auth->pool, args);
+ module->checkpassword_reply_path =
+ PKG_LIBEXECDIR"/checkpassword-reply";
+
+ module->clients =
+ hash_create(default_pool, default_pool, 0, NULL, NULL);
+
+ return &module->module;
+}
+
+static void checkpassword_deinit(struct userdb_module *_module)
+{
+ struct checkpassword_userdb_module *module =
+ (struct checkpassword_userdb_module *)_module;
+ struct hash_iterate_context *iter;
+ void *key, *value;
+
+ iter = hash_iterate_init(module->clients);
+ while (hash_iterate(iter, &key, &value)) {
+ checkpassword_request_finish(value,
+ USERDB_RESULT_INTERNAL_FAILURE);
+ }
+ hash_iterate_deinit(&iter);
+ hash_destroy(&module->clients);
+
+ if (checkpassword_userdb_children != NULL)
+ child_wait_free(&checkpassword_userdb_children);
+}
+
+struct userdb_module_interface userdb_checkpassword = {
+ "checkpassword",
+
+ checkpassword_preinit,
+ NULL,
+ checkpassword_deinit,
+
+ checkpassword_lookup,
+};
+#else
+struct userdb_module_interface userdb_checkpassword = {
+ MEMBER(name) "checkpassword"
+};
+#endif
extern struct userdb_module_interface userdb_ldap;
extern struct userdb_module_interface userdb_sql;
extern struct userdb_module_interface userdb_nss;
+extern struct userdb_module_interface userdb_checkpassword;
void userdbs_init(void)
{
userdb_register_module(&userdb_ldap);
userdb_register_module(&userdb_sql);
userdb_register_module(&userdb_nss);
+ userdb_register_module(&userdb_checkpassword);
}
void userdbs_deinit(void)