]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added checkpassword passdb. userdb has only beginnings.
authorTimo Sirainen <tss@iki.fi>
Fri, 18 Jun 2004 03:40:12 +0000 (06:40 +0300)
committerTimo Sirainen <tss@iki.fi>
Fri, 18 Jun 2004 03:40:12 +0000 (06:40 +0300)
--HG--
branch : HEAD

configure.in
src/auth/.cvsignore
src/auth/Makefile.am
src/auth/checkpassword-reply.c [new file with mode: 0644]
src/auth/passdb-checkpassword.c [new file with mode: 0644]
src/auth/passdb.c
src/auth/passdb.h

index 27b6fd712d6c1fe9d57423da89837c0422c5a932..83da60649ac2e1c6efa53b867fa1bf3c9ddfe3b0 100644 (file)
@@ -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 @@ AC_ARG_WITH(pam,
        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 @@ if test $want_pam = yes; then
        ])
 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)
index c6ce7f42b37e2464c2fc4571f1b3e59dc19651af..21442d01d012514002086a428d4cd6477c4370fc 100644 (file)
@@ -7,3 +7,4 @@ Makefile
 Makefile.in
 so_locations
 dovecot-auth
+checkpassword-reply
index 1126d0c9322819fc4535202da733ef6c2fc58a1a..515a25c4b3717886d895134aeb17477cca743105 100644 (file)
@@ -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 @@ dovecot_auth_SOURCES = \
        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 @@ noinst_HEADERS = \
        password-scheme.h \
        userdb.h \
        userdb-vpopmail.h
+
+checkpassword_reply_LDADD = \
+       ../lib/liblib.a
+
+checkpassword_reply_sources =
+       checkpassword-reply.c
diff --git a/src/auth/checkpassword-reply.c b/src/auth/checkpassword-reply.c
new file mode 100644 (file)
index 0000000..8168a82
--- /dev/null
@@ -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;
+}
diff --git a/src/auth/passdb-checkpassword.c b/src/auth/passdb-checkpassword.c
new file mode 100644 (file)
index 0000000..bd456a3
--- /dev/null
@@ -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
index 558c8f135cbe73e2e65c255708fcc8430d9948aa..e1e1de1d4388a34ae3c165fefc4a0c34fe49868b 100644 (file)
@@ -120,6 +120,10 @@ void passdb_init(void)
        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;
index c613f0f2763b7b3e0c9c8ec475b92544f91373c1..8f0c89e9fd0d3a8f7f8389a67a1f598eab6a2987 100644 (file)
@@ -57,6 +57,7 @@ extern struct passdb_module passdb_bsdauth;
 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;