[dovecot-cvs] dovecot/src/auth Makefile.am, 1.43,
1.44 auth-client-connection.c, 1.33,
1.34 auth-master-connection.c, 1.25,
1.26 auth-master-connection.h, 1.8,
1.9 auth-request-balancer-child.c, NONE,
1.1 auth-request-balancer-worker.c, NONE,
1.1 auth-request-balancer.h, NONE,
1.1 auth-request-handler-balancer.c, NONE,
1.1 auth-request-handler-default.c, NONE,
1.1 auth-request-handler.c, 1.1, 1.2 auth-request-handler.h,
1.1, 1.2 auth.c, 1.12, 1.13 auth.h, 1.10, 1.11 common.h, 1.10,
1.11 main.c, 1.38, 1.39 mech.h, 1.34, 1.35
cras at dovecot.org
cras at dovecot.org
Sun Jan 9 18:54:51 EET 2005
Update of /var/lib/cvs/dovecot/src/auth
In directory talvi:/tmp/cvs-serv30683/auth
Modified Files:
Makefile.am auth-client-connection.c auth-master-connection.c
auth-master-connection.h auth-request-handler.c
auth-request-handler.h auth.c auth.h common.h main.c mech.h
Added Files:
auth-request-balancer-child.c auth-request-balancer-worker.c
auth-request-balancer.h auth-request-handler-balancer.c
auth-request-handler-default.c
Log Message:
Changed the way multiple auth processes are handled. It no longer uses a pid
appended to socket name but instead there's a balancer process which
proxies the requests to worker processes.
Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -d -r1.43 -r1.44
--- Makefile.am 9 Jan 2005 00:49:18 -0000 1.43
+++ Makefile.am 9 Jan 2005 16:54:48 -0000 1.44
@@ -38,7 +38,11 @@
auth-master-connection.c \
auth-module.c \
auth-request.c \
+ auth-request-balancer-child.c \
+ auth-request-balancer-worker.c \
auth-request-handler.c \
+ auth-request-handler-balancer.c \
+ auth-request-handler-default.c \
db-ldap.c \
db-sql.c \
db-passwd-file.c \
@@ -81,6 +85,7 @@
auth-master-connection.h \
auth-module.h \
auth-request.h \
+ auth-request-balancer.h \
auth-request-handler.h \
db-ldap.h \
db-sql.h \
Index: auth-client-connection.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-client-connection.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- auth-client-connection.c 9 Jan 2005 00:49:18 -0000 1.33
+++ auth-client-connection.c 9 Jan 2005 16:54:48 -0000 1.34
@@ -98,16 +98,17 @@
conn->refcount++;
if (!AUTH_MASTER_IS_DUMMY(conn->master)) {
conn->request_handler =
- auth_request_handler_create(conn->auth,
- conn->connect_uid, pid, auth_callback, conn,
+ auth_request_handler_create(conn->auth, FALSE,
+ auth_callback, conn,
auth_master_request_callback, conn->master);
} else {
conn->request_handler =
- auth_request_handler_create(conn->auth,
- conn->connect_uid, pid,
+ auth_request_handler_create(conn->auth, FALSE,
auth_callback, conn,
NULL, NULL);
}
+ auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
+
conn->pid = pid;
return TRUE;
}
@@ -338,6 +339,8 @@
master->clients = next;
}
- timeout_remove(master->to_clients);
- master->to_clients = NULL;
+ if (master->to_clients != NULL) {
+ timeout_remove(master->to_clients);
+ master->to_clients = NULL;
+ }
}
Index: auth-master-connection.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-master-connection.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- auth-master-connection.c 9 Jan 2005 00:49:18 -0000 1.25
+++ auth-master-connection.c 9 Jan 2005 16:54:48 -0000 1.26
@@ -10,6 +10,7 @@
#include "network.h"
#include "userdb.h"
#include "auth-request-handler.h"
+#include "auth-request-balancer.h"
#include "auth-master-interface.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
@@ -22,7 +23,7 @@
struct auth_listener {
struct auth_master_connection *master;
- int client_listener;
+ enum listener_type type;
int fd;
char *path;
struct io *io;
@@ -37,6 +38,7 @@
static int master_output(void *context);
static void auth_master_connection_close(struct auth_master_connection *conn);
static int auth_master_connection_unref(struct auth_master_connection *conn);
+static void auth_listener_destroy(struct auth_listener *l);
void auth_master_request_callback(const char *reply, void *context)
{
@@ -233,7 +235,6 @@
void auth_master_connection_destroy(struct auth_master_connection *conn)
{
struct auth_listener **l;
- size_t i, size;
if (conn->destroyed)
return;
@@ -244,16 +245,9 @@
if (conn->fd != -1)
auth_master_connection_close(conn);
- l = buffer_get_modifyable_data(conn->listeners_buf, &size);
- size /= sizeof(*l);
- for (i = 0; i < size; i++) {
- net_disconnect(l[i]->fd);
- io_remove(l[i]->io);
- if (l[i]->path != NULL) {
- (void)unlink(l[i]->path);
- i_free(l[i]->path);
- }
- i_free(l[i]);
+ while (conn->listeners_buf->used > 0) {
+ l = buffer_get_modifyable_data(conn->listeners_buf, NULL);
+ auth_listener_destroy(*l);
}
buffer_free(conn->listeners_buf);
conn->listeners_buf = NULL;
@@ -280,30 +274,64 @@
fd = net_accept(l->fd, NULL, NULL);
if (fd < 0) {
if (fd < -1)
- i_fatal("accept() failed: %m");
+ i_fatal("accept(type %d) failed: %m", l->type);
} else {
net_set_nonblock(fd, TRUE);
- if (l->client_listener)
+ switch (l->type) {
+ case LISTENER_CLIENT:
(void)auth_client_connection_create(l->master, fd);
- else {
+ break;
+ case LISTENER_MASTER:
/* we'll just replace the previous master.. */
auth_master_connection_set_fd(l->master, fd);
- auth_master_connection_send_handshake(l->master);
+ auth_master_connection_send_handshake(l->master);
+ break;
+ case LISTENER_BALANCER:
+ /* worker process connected to us */
+ auth_request_balancer_add_child(fd);
+ auth_listener_destroy(l);
+ break;
}
}
}
void auth_master_connection_add_listener(struct auth_master_connection *conn,
- int fd, const char *path, int client)
+ int fd, const char *path,
+ enum listener_type type)
{
struct auth_listener *l;
l = i_new(struct auth_listener, 1);
l->master = conn;
- l->client_listener = client;
+ l->type = type;
l->fd = fd;
l->path = i_strdup(path);
l->io = io_add(fd, IO_READ, auth_accept, l);
buffer_append(conn->listeners_buf, &l, sizeof(l));
}
+
+static void auth_listener_destroy(struct auth_listener *l)
+{
+ struct auth_listener **lp;
+ size_t i, size;
+
+ lp = buffer_get_modifyable_data(l->master->listeners_buf, &size);
+ size /= sizeof(*lp);
+
+ for (i = 0; i < size; i++) {
+ if (lp[i] == l) {
+ buffer_delete(l->master->listeners_buf,
+ i * sizeof(l), sizeof(l));
+ break;
+ }
+ }
+
+ net_disconnect(l->fd);
+ io_remove(l->io);
+ if (l->path != NULL) {
+ (void)unlink(l->path);
+ i_free(l->path);
+ }
+ i_free(l);
+}
Index: auth-master-connection.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-master-connection.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- auth-master-connection.h 9 Jan 2005 00:49:18 -0000 1.8
+++ auth-master-connection.h 9 Jan 2005 16:54:48 -0000 1.9
@@ -1,6 +1,12 @@
#ifndef __AUTH_MASTER_CONNECTION_H
#define __AUTH_MASTER_CONNECTION_H
+enum listener_type {
+ LISTENER_MASTER,
+ LISTENER_CLIENT,
+ LISTENER_BALANCER
+};
+
struct auth_master_connection {
struct auth *auth;
@@ -30,6 +36,7 @@
void auth_master_request_callback(const char *reply, void *context);
void auth_master_connection_add_listener(struct auth_master_connection *conn,
- int fd, const char *path, int client);
+ int fd, const char *path,
+ enum listener_type type);
#endif
--- NEW FILE: auth-request-balancer-child.c ---
/* Copyright (C) 2002-2005 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "buffer.h"
#include "hash.h"
#include "istream.h"
#include "ostream.h"
#include "auth-request-balancer.h"
#include <stdlib.h>
#include <unistd.h>
struct auth_balancer_child {
unsigned int id;
int fd;
struct io *io;
struct istream *input;
struct ostream *output;
};
static buffer_t *balancer_children;
static struct hash_table *balancer_handlers;
static unsigned int balancer_next_idx = 0;
static void
auth_request_balancer_remove_child(struct auth_balancer_child *child);
static void balancer_input(void *context)
{
struct auth_balancer_child *child = context;
struct auth_request_handler *handler;
const char *line, *id_str;
unsigned int id;
switch (i_stream_read(child->input)) {
case 0:
return;
case -1:
/* disconnected */
auth_request_balancer_remove_child(child);
return;
case -2:
/* buffer full */
i_error("BUG: Auth balancer child sent us more than %d bytes",
(int)AUTH_BALANCER_MAX_LINE_LENGTH);
auth_request_balancer_remove_child(child);
return;
}
while ((line = i_stream_next_line(child->input)) != NULL) {
id_str = line;
line = strchr(line, '\t');
if (line == NULL)
continue;
t_push();
id = (unsigned int)strtoul(t_strcut(id_str, '\t'), NULL, 10);
handler = hash_lookup(balancer_handlers, POINTER_CAST(id));
t_pop();
if (handler != NULL) {
auth_request_handler_balancer_reply(handler,
line + 1);
}
}
}
static int balancer_output(void *context)
{
struct auth_balancer_child *child = context;
if (o_stream_flush(child->output) < 0) {
auth_request_balancer_remove_child(child);
return 1;
}
/* FIXME: throttle control.. */
return 1;
}
void auth_request_balancer_add_child(int fd)
{
static unsigned int balancer_id_counter = 0;
struct auth_balancer_child *child;
net_set_nonblock(fd, TRUE);
child = i_new(struct auth_balancer_child, 1);
child->id = ++balancer_id_counter;
child->fd = fd;
child->input =
i_stream_create_file(fd, default_pool,
AUTH_BALANCER_MAX_LINE_LENGTH, FALSE);
child->output =
o_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
o_stream_set_flush_callback(child->output, balancer_output, child);
child->io = io_add(fd, IO_READ, balancer_input, child);
buffer_append(balancer_children, &child, sizeof(child));
}
static void
auth_request_balancer_remove_child(struct auth_balancer_child *child)
{
struct auth_balancer_child **children;
size_t i, size;
children = buffer_get_modifyable_data(balancer_children, &size);
size /= sizeof(*children);
for (i = 0; i < size; i++) {
if (children[i] == child) {
buffer_delete(balancer_children,
i * sizeof(child), sizeof(child));
break;
}
}
i_assert(i != size);
if (child->io != NULL)
io_remove(child->io);
i_stream_unref(child->input);
o_stream_unref(child->output);
if (close(child->fd) < 0)
i_error("close(balancer) failed: %m");
i_free(child);
}
static void balancer_send(struct auth_balancer_child *child, const char *line)
{
struct const_iovec iov[2];
iov[0].iov_base = line;
iov[0].iov_len = strlen(line);
iov[1].iov_base = "\n";
iov[1].iov_len = 1;
(void)o_stream_sendv(child->output, iov, 2);
/* FIXME: throttle control */
}
unsigned int auth_request_balancer_send(const char *line)
{
struct auth_balancer_child **child, *min_child;
size_t size, used_size, min_size;
unsigned int i, start;
child = buffer_get_modifyable_data(balancer_children, &size);
size /= sizeof(*child);
start = i = balancer_next_idx % size;
balancer_next_idx++;
min_size = (size_t)-1;
min_child = NULL;
do {
used_size = o_stream_get_buffer_used_size(child[i]->output);
if (used_size == 0) {
/* nothing in output buffer, use this */
balancer_send(child[i], line);
return child[i]->id;
}
if (used_size < min_size) {
min_size = used_size;
min_child = child[i];
}
} while (++i != start);
/* min_child has the smallest amount of data in output buffer */
balancer_send(min_child, line);
return min_child->id;
}
void auth_request_balancer_send_to(unsigned int id, const char *line)
{
struct auth_balancer_child **child;
size_t i, size;
child = buffer_get_modifyable_data(balancer_children, &size);
size /= sizeof(*child);
for (i = 0; i < size; i++) {
if (child[i]->id == id) {
balancer_send(child[i], line);
return;
}
}
// FIXME: ?
}
void auth_request_balancer_add_handler(struct auth_request_handler *handler,
unsigned int connect_uid)
{
hash_insert(balancer_handlers, POINTER_CAST(connect_uid), handler);
}
void auth_request_balancer_remove_handler(unsigned int connect_uid)
{
hash_remove(balancer_handlers, POINTER_CAST(connect_uid));
}
void auth_request_balancer_child_init(void)
{
balancer_children = buffer_create_dynamic(default_pool, 32);
balancer_handlers =
hash_create(default_pool, default_pool, 0, NULL, NULL);
}
void auth_request_balancer_child_deinit(void)
{
while (balancer_children->used > 0) {
struct auth_balancer_child **child;
child = buffer_get_modifyable_data(balancer_children, NULL);
auth_request_balancer_remove_child(*child);
}
buffer_free(balancer_children);
hash_destroy(balancer_handlers);
}
--- NEW FILE: auth-request-balancer-worker.c ---
/* Copyright (C) 2002-2005 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "safe-memset.h"
#include "auth-request-handler.h"
#include "auth-request-balancer.h"
#include <stdlib.h>
#include <unistd.h>
struct auth_balancer_worker {
int fd;
struct auth_request_handler *request_handler;
struct io *io;
struct istream *input;
struct ostream *output;
};
static unsigned int next_uint(const char **line)
{
const char *p, *value = *line;
p = strchr(*line, '\t');
if (p == NULL)
*line += strlen(value);
else {
value = t_strdup_until(value, p);
*line = p + 1;
}
return (unsigned int)strtoul(value, NULL, 10);
}
static char *balancer_socket_path;
static struct timeout *to_connect;
static void
auth_client_handle_line(struct auth_balancer_worker *worker, const char *line)
{
struct auth_request_handler *rh = worker->request_handler;
unsigned int connect_uid, client_pid, id;
connect_uid = next_uint(&line);
client_pid = next_uint(&line);
auth_request_handler_set(rh, connect_uid, client_pid);
if (strncmp(line, "AUTH\t", 5) == 0)
(void)auth_request_handler_auth_begin(rh, line + 5);
else if (strncmp(line, "CONT\t", 5) == 0)
(void)auth_request_handler_auth_continue(rh, line + 5);
else if (strncmp(line, "REQUEST\t", 8) == 0) {
id = (unsigned int)strtoul(line + 8, NULL, 10);
(void)auth_request_handler_master_request(rh, id, id);
}
}
static void balancer_worker_input(void *context)
{
struct auth_balancer_worker *worker = context;
char *line;
switch (i_stream_read(worker->input)) {
case 0:
return;
case -1:
/* disconnected */
auth_request_balancer_worker_destroy(worker);
return;
case -2:
/* buffer full */
i_error("BUG: Auth balancer server sent us more than %d bytes",
(int)AUTH_BALANCER_MAX_LINE_LENGTH);
auth_request_balancer_worker_destroy(worker);
return;
}
while ((line = i_stream_next_line(worker->input)) != NULL) {
t_push();
auth_client_handle_line(worker, line);
safe_memset(line, 0, strlen(line));
t_pop();
}
}
static int balancer_worker_output(void *context)
{
struct auth_balancer_worker *worker = context;
if (o_stream_flush(worker->output) < 0) {
auth_request_balancer_worker_destroy(worker);
return 1;
}
/* FIXME: throttle control.. */
return 1;
}
static void auth_callback(const char *reply, void *context)
{
struct auth_balancer_worker *worker = context;
struct const_iovec iov[2];
if (reply == NULL) {
/* request handler was destroyed */
return;
}
iov[0].iov_base = reply;
iov[0].iov_len = strlen(reply);
iov[1].iov_base = "\n";
iov[1].iov_len = 1;
(void)o_stream_sendv(worker->output, iov, 2);
}
void auth_request_balancer_add_worker(struct auth *auth, int fd)
{
struct auth_balancer_worker *worker;
net_set_nonblock(fd, TRUE);
worker = i_new(struct auth_balancer_worker, 1);
worker->fd = fd;
worker->input =
i_stream_create_file(fd, default_pool,
AUTH_BALANCER_MAX_LINE_LENGTH, FALSE);
worker->output =
o_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
o_stream_set_flush_callback(worker->output, balancer_worker_output,
worker);
worker->io = io_add(fd, IO_READ, balancer_worker_input, worker);
worker->request_handler =
auth_request_handler_create(auth, TRUE, auth_callback, worker,
auth_callback, worker);
i_assert(auth->balancer_worker == NULL);
auth->balancer_worker = worker;
}
void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker)
{
io_loop_stop(ioloop);
auth_request_handler_unref(worker->request_handler);
if (worker->io != NULL)
io_remove(worker->io);
i_stream_unref(worker->input);
o_stream_unref(worker->output);
if (close(worker->fd) < 0)
i_error("close(balancer) failed: %m");
i_free(worker);
}
static int auth_request_balancer_connect(struct auth *auth)
{
int fd;
fd = net_connect_unix(balancer_socket_path);
if (fd < 0) {
if (errno != EAGAIN) {
i_fatal("net_connect_unix(%s) failed: %m",
balancer_socket_path);
}
/* busy */
return FALSE;
}
auth_request_balancer_add_worker(auth, fd);
return TRUE;
}
static void balancer_connect_timeout(void *context)
{
struct auth *auth = context;
if (auth_request_balancer_connect(auth)) {
timeout_remove(to_connect);
to_connect = NULL;
}
}
void auth_request_balancer_worker_init(struct auth *auth)
{
const char *name;
name = getenv("AUTH_NAME");
if (name == NULL) name = "auth";
balancer_socket_path = i_strconcat(name, "-balancer", NULL);
if (!auth_request_balancer_connect(auth)) {
/* couldn't connect to balancer yet, it's probably still
starting. try again later. */
to_connect = timeout_add(1000, balancer_connect_timeout, auth);
}
}
void auth_request_balancer_worker_deinit(void)
{
if (to_connect != NULL)
timeout_remove(to_connect);
i_free(balancer_socket_path);
}
--- NEW FILE: auth-request-balancer.h ---
#ifndef __AUTH_REQUEST_BALANCER_H
#define __AUTH_REQUEST_BALANCER_H
#include "auth-client-interface.h"
#define AUTH_BALANCER_MAX_LINE_LENGTH \
(AUTH_CLIENT_MAX_LINE_LENGTH + 64)
struct auth_request_handler;
void auth_request_balancer_add_child(int fd);
void auth_request_balancer_add_worker(struct auth *auth, int fd);
void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker);
void auth_request_balancer_add_handler(struct auth_request_handler *handler,
unsigned int connect_uid);
void auth_request_balancer_remove_handler(unsigned int connect_uid);
unsigned int auth_request_balancer_send(const char *line);
void auth_request_balancer_send_to(unsigned int id, const char *line);
void auth_request_handler_balancer_reply(struct auth_request_handler *handler,
const char *line);
void auth_request_balancer_child_init(void);
void auth_request_balancer_child_deinit(void);
void auth_request_balancer_worker_init(struct auth *auth);
void auth_request_balancer_worker_deinit(void);
#endif
--- NEW FILE: auth-request-handler-balancer.c ---
/* Copyright (C) 2005 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "hash.h"
#include "str.h"
#include "str-sanitize.h"
#include "mech.h"
#include "auth-client-interface.h"
#include "auth-request-handler.h"
#include "auth-request-balancer.h"
#include <stdlib.h>
struct auth_balancer_request {
unsigned int client_id;
unsigned int master_id;
unsigned int balancer_pid;
time_t created;
};
struct auth_request_handler {
int refcount;
pool_t pool;
struct hash_table *client_requests;
struct auth *auth;
unsigned int connect_uid, client_pid;
auth_request_callback_t *callback;
void *context;
auth_request_callback_t *master_callback;
void *master_context;
};
static struct auth_request_handler *
_create(struct auth *auth, int prepend_connect_uid,
auth_request_callback_t *callback, void *context,
auth_request_callback_t *master_callback, void *master_context)
{
struct auth_request_handler *handler;
pool_t pool;
i_assert(!prepend_connect_uid);
pool = pool_alloconly_create("auth request handler", 4096);
handler = p_new(pool, struct auth_request_handler, 1);
handler->refcount = 1;
handler->pool = pool;
handler->client_requests =
hash_create(default_pool, pool, 0, NULL, NULL);
handler->auth = auth;
handler->callback = callback;
handler->context = context;
handler->master_callback = master_callback;
handler->master_context = master_context;
return handler;
}
static void _set(struct auth_request_handler *handler,
unsigned int connect_uid, unsigned int client_pid)
{
i_assert(handler->connect_uid == 0);
handler->connect_uid = connect_uid;
handler->client_pid = client_pid;
auth_request_balancer_add_handler(handler, connect_uid);
}
static void _unref(struct auth_request_handler *handler)
{
struct hash_iterate_context *iter;
void *key, *value;
i_assert(handler->refcount > 0);
if (--handler->refcount > 0)
return;
auth_request_balancer_remove_handler(handler->connect_uid);
iter = hash_iterate_init(handler->client_requests);
while (hash_iterate(iter, &key, &value))
i_free(value);
hash_iterate_deinit(iter);
/* notify parent that we're done with all requests */
handler->callback(NULL, handler->context);
hash_destroy(handler->client_requests);
pool_unref(handler->pool);
}
static void auth_request_handler_remove(struct auth_request_handler *handler,
struct auth_balancer_request *request)
{
hash_remove(handler->client_requests, POINTER_CAST(request->client_id));
i_free(request);
}
static void _check_timeouts(struct auth_request_handler *handler)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(handler->client_requests);
while (hash_iterate(iter, &key, &value)) {
struct auth_balancer_request *request = value;
if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
auth_request_handler_remove(handler, request);
}
hash_iterate_deinit(iter);
}
static int _auth_begin(struct auth_request_handler *handler, const char *args)
{
struct auth_balancer_request *request;
struct mech_module *mech;
const char *mech_name;
string_t *str;
unsigned int id;
/* <id> <mechanism> [...] */
id = (unsigned int)strtoul(t_strcut(args, '\t'), NULL, 10);
mech_name = strchr(args, '\t');
if (mech_name == NULL) {
i_error("BUG: Authentication client %u "
"sent broken AUTH request", handler->client_pid);
return FALSE;
}
args = strchr(++mech_name, '\t');
mech_name = t_strcut(mech_name, '\t');
mech = mech_module_find(mech_name);
if (mech == NULL) {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested unsupported "
"authentication mechanism %s", handler->client_pid,
str_sanitize(mech_name, MAX_MECH_NAME_LEN));
return FALSE;
}
request = i_new(struct auth_balancer_request, 1);
request->created = ioloop_time;
request->client_id = id;
str = t_str_new(256);
str_printfa(str, "%u\t%u\tAUTH\t%u\t%s%s",
handler->connect_uid, handler->client_pid,
id, mech->mech_name, args == NULL ? "" : args);
request->balancer_pid = auth_request_balancer_send(str_c(str));
hash_insert(handler->client_requests, POINTER_CAST(id), request);
return TRUE;
}
static int
_auth_continue(struct auth_request_handler *handler, const char *args)
{
struct auth_balancer_request *request;
const char *data;
string_t *str;
unsigned int id;
data = strchr(args, '\t');
if (data++ == NULL) {
i_error("BUG: Authentication client sent broken CONT request");
return FALSE;
}
id = (unsigned int)strtoul(args, NULL, 10);
request = hash_lookup(handler->client_requests, POINTER_CAST(id));
if (request == NULL) {
data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id);
handler->callback(data, handler->context);
return TRUE;
}
str = t_str_new(128);
str_printfa(str, "%u\t%u\tCONT\t%u\t%s",
handler->connect_uid, handler->client_pid, id, data);
auth_request_balancer_send_to(request->balancer_pid, str_c(str));
return TRUE;
}
static void _master_request(struct auth_request_handler *handler,
unsigned int id, unsigned int client_id)
{
struct auth_balancer_request *request;
const char *reply;
string_t *str;
request = hash_lookup(handler->client_requests,
POINTER_CAST(client_id));
if (request == NULL || request->balancer_pid == 0) {
i_error("Master request %u.%u not found from balancer",
handler->client_pid, client_id);
reply = t_strdup_printf("NOTFOUND\t%u", id);
handler->master_callback(reply, handler->master_context);
return;
}
request->master_id = id;
str = t_str_new(128);
str_printfa(str, "%u\t%u\tREQUEST\t%u",
handler->connect_uid, handler->client_pid, client_id);
auth_request_balancer_send_to(request->balancer_pid, str_c(str));
}
static void _flush_failures(void)
{
}
static void _init(void)
{
auth_request_balancer_child_init();
}
static void _deinit(void)
{
auth_request_balancer_child_deinit();
}
struct auth_request_handler_api auth_request_handler_balancer = {
_create,
_unref,
_set,
_check_timeouts,
_auth_begin,
_auth_continue,
_master_request,
_flush_failures,
_init,
_deinit
};
void auth_request_handler_balancer_reply(struct auth_request_handler *handler,
const char *line)
{
struct auth_balancer_request *request;
const char *cmd, *id_str, *args;
unsigned int id;
/* <cmd> <id> [...] */
args = strchr(line, '\t');
if (args == NULL) {
i_error("Balancer worker sent invalid reply: %s", line);
return;
}
cmd = t_strdup_until(line, args);
id_str = args + 1;
args = strchr(id_str, '\t');
if (args == NULL)
args = "";
else
id_str = t_strdup_until(id_str, args);
id = (unsigned int)strtoul(id_str, NULL, 10);
request = hash_lookup(handler->client_requests, POINTER_CAST(id));
if (request == NULL) {
i_error("Balancer worker sent unknown request %u", id);
return;
}
if (request->master_id == 0) {
handler->callback(line, handler->context);
if (strcmp(cmd, "CONT") != 0 &&
(strcmp(cmd, "OK") != 0 ||
strstr(line, "\tnologin") != NULL ||
handler->master_callback == NULL)) {
/* this request doesn't have to wait for master
process to pick it up. delete it */
auth_request_handler_remove(handler, request);
}
} else {
/* replace client id with master id */
line = t_strdup_printf("%s\t%u%s", cmd,
request->master_id, args);
handler->master_callback(line, handler->master_context);
auth_request_handler_remove(handler, request);
}
}
--- NEW FILE: auth-request-handler-default.c ---
/* Copyright (C) 2005 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "buffer.h"
#include "base64.h"
#include "hash.h"
#include "str.h"
#include "str-sanitize.h"
#include "auth-request.h"
#include "auth-request-handler.h"
#include <stdlib.h>
struct auth_request_handler {
int refcount;
pool_t pool;
struct hash_table *requests;
struct auth *auth;
unsigned int connect_uid, client_pid;
auth_request_callback_t *callback;
void *context;
auth_request_callback_t *master_callback;
void *master_context;
unsigned int prepend_connect_uid:1;
};
static buffer_t *auth_failures_buf;
static struct timeout *to_auth_failures;
static struct auth_request_handler *
_create(struct auth *auth, int prepend_connect_uid,
auth_request_callback_t *callback, void *context,
auth_request_callback_t *master_callback, void *master_context)
{
struct auth_request_handler *handler;
pool_t pool;
pool = pool_alloconly_create("auth request handler", 4096);
handler = p_new(pool, struct auth_request_handler, 1);
handler->refcount = 1;
handler->pool = pool;
handler->requests = hash_create(default_pool, pool, 0, NULL, NULL);
handler->auth = auth;
handler->callback = callback;
handler->context = context;
handler->master_callback = master_callback;
handler->master_context = master_context;
handler->prepend_connect_uid = prepend_connect_uid;
return handler;
}
static void _set(struct auth_request_handler *handler,
unsigned int connect_uid, unsigned int client_pid)
{
handler->connect_uid = connect_uid;
handler->client_pid = client_pid;
}
static void _unref(struct auth_request_handler *handler)
{
struct hash_iterate_context *iter;
void *key, *value;
i_assert(handler->refcount > 0);
if (--handler->refcount > 0)
return;
iter = hash_iterate_init(handler->requests);
while (hash_iterate(iter, &key, &value))
auth_request_unref(value);
hash_iterate_deinit(iter);
/* notify parent that we're done with all requests */
handler->callback(NULL, handler->context);
hash_destroy(handler->requests);
pool_unref(handler->pool);
}
static void auth_request_handler_remove(struct auth_request_handler *handler,
struct auth_request *request)
{
hash_remove(handler->requests, POINTER_CAST(request->id));
auth_request_unref(request);
}
static void _check_timeouts(struct auth_request_handler *handler)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(handler->requests);
while (hash_iterate(iter, &key, &value)) {
struct auth_request *request = value;
if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
auth_request_handler_remove(handler, request);
}
hash_iterate_deinit(iter);
}
static const char *get_client_extra_fields(struct auth_request *request)
{
const char **fields;
unsigned int src, dest;
if (request->extra_fields == NULL)
return NULL;
/* we only wish to remove all fields prefixed with "userdb_" */
if (strstr(request->extra_fields, "userdb_") == NULL)
return request->extra_fields;
fields = t_strsplit(request->extra_fields, "\t");
for (src = dest = 0; fields[src] != NULL; src++) {
if (strncmp(fields[src], "userdb_", 7) == 0)
fields[dest++] = fields[src];
}
fields[dest] = NULL;
return t_strarray_join(fields, "\t");
}
static void auth_callback(struct auth_request *request,
enum auth_client_result result,
const void *reply, size_t reply_size)
{
struct auth_request_handler *handler = request->context;
string_t *str;
const char *fields;
t_push();
str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
if (handler->prepend_connect_uid)
str_printfa(str, "%u\t", request->connect_uid);
switch (result) {
case AUTH_CLIENT_RESULT_CONTINUE:
str_printfa(str, "CONT\t%u\t", request->id);
base64_encode(reply, reply_size, str);
request->accept_input = TRUE;
handler->callback(str_c(str), handler->context);
break;
case AUTH_CLIENT_RESULT_SUCCESS:
str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
if (reply_size > 0) {
str_append(str, "\tresp=");
base64_encode(reply, reply_size, str);
}
fields = get_client_extra_fields(request);
if (fields != NULL) {
str_append_c(str, '\t');
str_append(str, fields);
}
if (request->no_login || handler->master_callback == NULL) {
/* this request doesn't have to wait for master
process to pick it up. delete it */
auth_request_handler_remove(handler, request);
}
handler->callback(str_c(str), handler->context);
break;
case AUTH_CLIENT_RESULT_FAILURE:
str_printfa(str, "FAIL\t%u", request->id);
if (request->user != NULL)
str_printfa(str, "\tuser=%s", request->user);
if (request->internal_failure)
str_append(str, "\ttemp");
fields = get_client_extra_fields(request);
if (fields != NULL) {
str_append_c(str, '\t');
str_append(str, fields);
}
if (request->delayed_failure) {
/* we came here from flush_failures() */
handler->callback(str_c(str), handler->context);
break;
}
/* remove the request from requests-list */
auth_request_ref(request);
auth_request_handler_remove(handler, request);
if (request->no_failure_delay) {
/* passdb specifically requested not to delay the
reply. */
handler->callback(str_c(str), handler->context);
} else {
/* failure. don't announce it immediately to avoid
a) timing attacks, b) flooding */
request->delayed_failure = TRUE;
handler->refcount++;
buffer_append(auth_failures_buf,
&request, sizeof(request));
}
break;
}
/* NOTE: request may be destroyed now */
auth_request_handler_unref(handler);
t_pop();
}
static void auth_request_handler_auth_fail(struct auth_request_handler *handler,
struct auth_request *request,
const char *reason)
{
string_t *reply = t_str_new(64);
auth_request_log_info(request, request->mech->mech_name, "%s", reason);
if (handler->prepend_connect_uid)
str_printfa(reply, "%u\t", request->connect_uid);
str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason);
handler->callback(str_c(reply), handler->context);
auth_request_handler_remove(handler, request);
}
static int _auth_begin(struct auth_request_handler *handler, const char *args)
{
struct mech_module *mech;
struct auth_request *request;
const char *const *list, *name, *arg, *initial_resp;
const void *initial_resp_data;
size_t initial_resp_len;
unsigned int id;
buffer_t *buf;
int valid_client_cert;
/* <id> <mechanism> [...] */
list = t_strsplit(args, "\t");
if (list[0] == NULL || list[1] == NULL) {
i_error("BUG: Authentication client %u "
"sent broken AUTH request", handler->client_pid);
return FALSE;
}
id = (unsigned int)strtoul(list[0], NULL, 10);
mech = mech_module_find(list[1]);
if (mech == NULL) {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested unsupported "
"authentication mechanism %s", handler->client_pid,
str_sanitize(list[1], MAX_MECH_NAME_LEN));
return FALSE;
}
request = auth_request_new(handler->auth, mech, auth_callback, handler);
request->connect_uid = handler->connect_uid;
request->client_pid = handler->client_pid;
request->id = id;
/* parse optional parameters */
initial_resp = NULL;
valid_client_cert = FALSE;
for (list += 2; *list != NULL; list++) {
arg = strchr(*list, '=');
if (arg == NULL) {
name = *list;
arg = "";
} else {
name = t_strdup_until(*list, arg);
arg++;
}
if (strcmp(name, "lip") == 0)
(void)net_addr2ip(arg, &request->local_ip);
else if (strcmp(name, "rip") == 0)
(void)net_addr2ip(arg, &request->remote_ip);
else if (strcmp(name, "service") == 0)
request->service = p_strdup(request->pool, arg);
else if (strcmp(name, "resp") == 0)
initial_resp = arg;
else if (strcmp(name, "valid-client-cert") == 0)
valid_client_cert = TRUE;
}
if (request->service == NULL) {
i_error("BUG: Authentication client %u "
"didn't specify service in request",
handler->client_pid);
auth_request_unref(request);
return FALSE;
}
hash_insert(handler->requests, POINTER_CAST(id), request);
if (request->auth->ssl_require_client_cert && !valid_client_cert) {
/* we fail without valid certificate */
auth_request_handler_auth_fail(handler, request,
"Client didn't present valid SSL certificate");
return TRUE;
}
if (initial_resp == NULL) {
initial_resp_data = NULL;
initial_resp_len = 0;
} else {
size_t len = strlen(initial_resp);
buf = buffer_create_dynamic(pool_datastack_create(),
MAX_BASE64_DECODED_SIZE(len));
if (base64_decode(initial_resp, len, NULL, buf) < 0) {
auth_request_handler_auth_fail(handler, request,
"Invalid base64 data in initial response");
return TRUE;
}
initial_resp_data = buf->data;
initial_resp_len = buf->used;
}
/* handler is referenced until auth_callback is called. */
handler->refcount++;
auth_request_initial(request, initial_resp_data, initial_resp_len);
return TRUE;
}
static int
_auth_continue(struct auth_request_handler *handler, const char *args)
{
struct auth_request *request;
const char *data;
size_t data_len;
buffer_t *buf;
unsigned int id;
data = strchr(args, '\t');
if (data++ == NULL) {
i_error("BUG: Authentication client sent broken CONT request");
return FALSE;
}
id = (unsigned int)strtoul(args, NULL, 10);
request = hash_lookup(handler->requests, POINTER_CAST(id));
if (request == NULL) {
string_t *reply = t_str_new(64);
if (handler->prepend_connect_uid)
str_printfa(reply, "%u\t", handler->connect_uid);
str_printfa(reply, "FAIL\t%u\treason=Timeouted", id);
handler->callback(str_c(reply), handler->context);
return TRUE;
}
/* accept input only once after mechanism has sent a CONT reply */
if (!request->accept_input) {
auth_request_handler_auth_fail(handler, request,
"Unexpected continuation");
return TRUE;
}
request->accept_input = FALSE;
data_len = strlen(data);
buf = buffer_create_dynamic(pool_datastack_create(),
MAX_BASE64_DECODED_SIZE(data_len));
if (base64_decode(data, data_len, NULL, buf) < 0) {
auth_request_handler_auth_fail(handler, request,
"Invalid base64 data in continued response");
return TRUE;
}
/* handler is referenced until auth_callback is called. */
handler->refcount++;
auth_request_continue(request, buf->data, buf->used);
return TRUE;
}
static void append_user_reply(string_t *str, const struct user_data *user)
{
const char *p;
str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user,
dec2str(user->uid), dec2str(user->gid));
if (user->system_user != NULL)
str_printfa(str, "\tsystem_user=%s", user->system_user);
if (user->mail != NULL)
str_printfa(str, "\tmail=%s", user->mail);
p = user->home != NULL ? strstr(user->home, "/./") : NULL;
if (p == NULL) {
if (user->home != NULL)
str_printfa(str, "\thome=%s", user->home);
} else {
/* wu-ftpd like <chroot>/./<home> */
str_printfa(str, "\thome=%s\tchroot=%s",
p + 3, t_strdup_until(user->home, p));
}
}
static void userdb_callback(const struct user_data *user, void *context)
{
struct auth_request *request = context;
struct auth_request_handler *handler = request->context;
string_t *reply;
if (user != NULL) {
auth_request_log_debug(request, "userdb",
"uid=%s gid=%s home=%s mail=%s",
dec2str(user->uid), dec2str(user->gid),
user->home != NULL ? user->home : "",
user->mail != NULL ? user->mail : "");
}
reply = t_str_new(256);
if (handler->prepend_connect_uid)
str_printfa(reply, "%u\t", request->connect_uid);
if (user == NULL)
str_printfa(reply, "NOTFOUND\t%u", request->id);
else {
str_printfa(reply, "USER\t%u\t", request->id);
append_user_reply(reply, user);
}
handler->master_callback(str_c(reply), handler->master_context);
auth_request_unref(request);
auth_request_handler_unref(handler);
}
static void _master_request(struct auth_request_handler *handler,
unsigned int id, unsigned int client_id)
{
struct auth_request *request;
string_t *reply;
reply = t_str_new(64);
if (handler->prepend_connect_uid)
str_printfa(reply, "%u\t", handler->connect_uid);
request = hash_lookup(handler->requests, POINTER_CAST(client_id));
if (request == NULL) {
i_error("Master request %u.%u not found",
handler->client_pid, client_id);
str_printfa(reply, "NOTFOUND\t%u", id);
handler->master_callback(str_c(reply), handler->master_context);
return;
}
auth_request_ref(request);
auth_request_handler_remove(handler, request);
if (!request->successful) {
i_error("Master requested unfinished authentication request "
"%u.%u", handler->client_pid, client_id);
str_printfa(reply, "NOTFOUND\t%u", id);
handler->master_callback(str_c(reply), handler->master_context);
} else {
/* the request isn't being referenced anywhere anymore,
so we can do a bit of kludging.. replace the request's
old client_id with master's id. */
request->id = id;
request->context = handler;
/* handler is referenced until userdb_callback is called. */
handler->refcount++;
auth_request_lookup_user(request, userdb_callback, request);
}
}
static void _flush_failures(void)
{
struct auth_request **auth_request;
size_t i, size;
auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
size /= sizeof(*auth_request);
for (i = 0; i < size; i++) {
auth_request[i]->callback(auth_request[i],
AUTH_CLIENT_RESULT_FAILURE, NULL, 0);
auth_request_unref(auth_request[i]);
}
buffer_set_used_size(auth_failures_buf, 0);
}
static void auth_failure_timeout(void *context __attr_unused__)
{
_flush_failures();
}
static void _init(void)
{
auth_failures_buf = buffer_create_dynamic(default_pool, 1024);
to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL);
}
static void _deinit(void)
{
buffer_free(auth_failures_buf);
timeout_remove(to_auth_failures);
}
struct auth_request_handler_api auth_request_handler_default = {
_create,
_unref,
_set,
_check_timeouts,
_auth_begin,
_auth_continue,
_master_request,
_flush_failures,
_init,
_deinit
};
Index: auth-request-handler.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request-handler.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- auth-request-handler.c 9 Jan 2005 00:49:18 -0000 1.1
+++ auth-request-handler.c 9 Jan 2005 16:54:48 -0000 1.2
@@ -1,486 +1,73 @@
/* Copyright (C) 2005 Timo Sirainen */
#include "common.h"
-#include "ioloop.h"
-#include "buffer.h"
-#include "base64.h"
-#include "hash.h"
-#include "str.h"
-#include "str-sanitize.h"
-#include "auth-request.h"
#include "auth-request-handler.h"
+#include "auth-request-balancer.h"
-#include <stdlib.h>
-
-/* Used only for string sanitization. */
-#define MAX_MECH_NAME_LEN 64
-
-struct auth_request_handler {
- int refcount;
- pool_t pool;
- struct hash_table *requests;
-
- struct auth *auth;
- unsigned int connect_uid, client_pid;
-
- auth_request_callback_t *callback;
- void *context;
-
- auth_request_callback_t *master_callback;
- void *master_context;
-};
-
-static buffer_t *auth_failures_buf;
-static struct timeout *to_auth_failures;
+struct auth_request_handler_api *auth_request_handler_api;
struct auth_request_handler *
-auth_request_handler_create(struct auth *auth,
- unsigned int connect_uid, unsigned int client_pid,
+auth_request_handler_create(struct auth *auth, int prepend_connect_uid,
auth_request_callback_t *callback, void *context,
auth_request_callback_t *master_callback,
void *master_context)
{
- struct auth_request_handler *handler;
- pool_t pool;
-
- pool = pool_alloconly_create("auth request handler", 4096);
-
- handler = p_new(pool, struct auth_request_handler, 1);
- handler->refcount = 1;
- handler->pool = pool;
- handler->requests = hash_create(default_pool, pool, 0, NULL, NULL);
- handler->auth = auth;
- handler->connect_uid = connect_uid;
- handler->client_pid = client_pid;
- handler->callback = callback;
- handler->context = context;
- handler->master_callback = master_callback;
- handler->master_context = master_context;
- return handler;
-}
-
-void auth_request_handler_unref(struct auth_request_handler *handler)
-{
- struct hash_iterate_context *iter;
- void *key, *value;
-
- i_assert(handler->refcount > 0);
- if (--handler->refcount > 0)
- return;
-
- iter = hash_iterate_init(handler->requests);
- while (hash_iterate(iter, &key, &value))
- auth_request_unref(value);
- hash_iterate_deinit(iter);
-
- /* notify parent that we're done with all requests */
- handler->callback(NULL, handler->context);
-
- hash_destroy(handler->requests);
- pool_unref(handler->pool);
-}
-
-void auth_request_handler_check_timeouts(struct auth_request_handler *handler)
-{
- struct hash_iterate_context *iter;
- void *key, *value;
-
- iter = hash_iterate_init(handler->requests);
- while (hash_iterate(iter, &key, &value)) {
- struct auth_request *auth_request = value;
-
- if (auth_request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
- hash_remove(handler->requests, key);
- }
- hash_iterate_deinit(iter);
-}
-
-static void auth_request_handler_remove(struct auth_request_handler *handler,
- struct auth_request *request)
-{
- hash_remove(handler->requests, POINTER_CAST(request->id));
- auth_request_unref(request);
+ return auth_request_handler_api->
+ create(auth, prepend_connect_uid, callback, context,
+ master_callback, master_context);
}
-static const char *get_client_extra_fields(struct auth_request *request)
+void auth_request_handler_set(struct auth_request_handler *handler,
+ unsigned int connect_uid,
+ unsigned int client_pid)
{
- const char **fields;
- unsigned int src, dest;
-
- if (request->extra_fields == NULL)
- return NULL;
-
- /* we only wish to remove all fields prefixed with "userdb_" */
- if (strstr(request->extra_fields, "userdb_") == NULL)
- return request->extra_fields;
-
- fields = t_strsplit(request->extra_fields, "\t");
- for (src = dest = 0; fields[src] != NULL; src++) {
- if (strncmp(fields[src], "userdb_", 7) == 0)
- fields[dest++] = fields[src];
- }
- fields[dest] = NULL;
- return t_strarray_join(fields, "\t");
+ auth_request_handler_api->set(handler, connect_uid, client_pid);
}
-static void auth_callback(struct auth_request *request,
- enum auth_client_result result,
- const void *reply, size_t reply_size)
+void auth_request_handler_unref(struct auth_request_handler *handler)
{
- struct auth_request_handler *handler = request->context;
- string_t *str = NULL;
- const char *fields;
-
- t_push();
-
- switch (result) {
- case AUTH_CLIENT_RESULT_CONTINUE:
- str = t_str_new(32 + MAX_BASE64_ENCODED_SIZE(reply_size));
- str_printfa(str, "CONT\t%u\t", request->id);
- base64_encode(reply, reply_size, str);
- request->accept_input = TRUE;
- handler->callback(str_c(str), handler->context);
- break;
- case AUTH_CLIENT_RESULT_SUCCESS:
- str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
- str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
- if (reply_size > 0) {
- str_append(str, "\tresp=");
- base64_encode(reply, reply_size, str);
- }
- fields = get_client_extra_fields(request);
- if (fields != NULL) {
- str_append_c(str, '\t');
- str_append(str, fields);
- }
-
- if (request->no_login || handler->master_callback == NULL) {
- /* this request doesn't have to wait for master
- process to pick it up. delete it */
- auth_request_handler_remove(handler, request);
- }
- handler->callback(str_c(str), handler->context);
- break;
- case AUTH_CLIENT_RESULT_FAILURE:
- str = t_str_new(128);
- str_printfa(str, "FAIL\t%u", request->id);
- if (request->user != NULL)
- str_printfa(str, "\tuser=%s", request->user);
- if (request->internal_failure)
- str_append(str, "\ttemp");
- fields = get_client_extra_fields(request);
- if (fields != NULL) {
- str_append_c(str, '\t');
- str_append(str, fields);
- }
-
- if (request->delayed_failure) {
- /* we came here from flush_failures() */
- handler->callback(str_c(str), handler->context);
- break;
- }
-
- /* remove the request from requests-list */
- auth_request_ref(request);
- auth_request_handler_remove(handler, request);
-
- if (request->no_failure_delay) {
- /* passdb specifically requested not to delay the
- reply. */
- handler->callback(str_c(str), handler->context);
- } else {
- /* failure. don't announce it immediately to avoid
- a) timing attacks, b) flooding */
- request->delayed_failure = TRUE;
- handler->refcount++;
- buffer_append(auth_failures_buf,
- &request, sizeof(request));
- }
- break;
- }
- /* NOTE: request may be destroyed now */
-
- auth_request_handler_unref(handler);
-
- t_pop();
+ auth_request_handler_api->unref(handler);
}
-static void auth_request_handler_auth_fail(struct auth_request_handler *handler,
- struct auth_request *request,
- const char *reason)
+void auth_request_handler_check_timeouts(struct auth_request_handler *handler)
{
- const char *reply;
-
- auth_request_log_info(request, request->mech->mech_name, "%s", reason);
-
- reply = t_strdup_printf("FAIL\t%u\treason=%s", request->id, reason);
- handler->callback(reply, handler->context);
- auth_request_handler_remove(handler, request);
+ auth_request_handler_api->check_timeouts(handler);
}
int auth_request_handler_auth_begin(struct auth_request_handler *handler,
const char *args)
{
- struct mech_module *mech;
- struct auth_request *request;
- const char *const *list, *name, *arg, *initial_resp;
- const void *initial_resp_data;
- size_t initial_resp_len;
- unsigned int id;
- buffer_t *buf;
- int valid_client_cert;
-
- /* <id> <mechanism> [...] */
- list = t_strsplit(args, "\t");
- if (list[0] == NULL || list[1] == NULL) {
- i_error("BUG: Authentication client %u "
- "sent broken AUTH request", handler->client_pid);
- return FALSE;
- }
-
- id = (unsigned int)strtoul(list[0], NULL, 10);
-
- mech = mech_module_find(list[1]);
- if (mech == NULL) {
- /* unsupported mechanism */
- i_error("BUG: Authentication client %u requested unsupported "
- "authentication mechanism %s", handler->client_pid,
- str_sanitize(list[1], MAX_MECH_NAME_LEN));
- return FALSE;
- }
-
- request = auth_request_new(handler->auth, mech, auth_callback, handler);
- request->connect_uid = handler->connect_uid;
- request->client_pid = handler->client_pid;
- request->id = id;
-
- /* parse optional parameters */
- initial_resp = NULL;
- valid_client_cert = FALSE;
- for (list += 2; *list != NULL; list++) {
- arg = strchr(*list, '=');
- if (arg == NULL) {
- name = *list;
- arg = "";
- } else {
- name = t_strdup_until(*list, arg);
- arg++;
- }
-
- if (strcmp(name, "lip") == 0)
- (void)net_addr2ip(arg, &request->local_ip);
- else if (strcmp(name, "rip") == 0)
- (void)net_addr2ip(arg, &request->remote_ip);
- else if (strcmp(name, "service") == 0)
- request->service = p_strdup(request->pool, arg);
- else if (strcmp(name, "resp") == 0)
- initial_resp = arg;
- else if (strcmp(name, "valid-client-cert") == 0)
- valid_client_cert = TRUE;
- }
-
- if (request->service == NULL) {
- i_error("BUG: Authentication client %u "
- "didn't specify service in request",
- handler->client_pid);
- auth_request_unref(request);
- return FALSE;
- }
-
- hash_insert(handler->requests, POINTER_CAST(id), request);
-
- if (request->auth->ssl_require_client_cert && !valid_client_cert) {
- /* we fail without valid certificate */
- auth_request_handler_auth_fail(handler, request,
- "Client didn't present valid SSL certificate");
- return TRUE;
- }
-
- if (initial_resp == NULL) {
- initial_resp_data = NULL;
- initial_resp_len = 0;
- } else {
- size_t len = strlen(initial_resp);
- buf = buffer_create_dynamic(pool_datastack_create(),
- MAX_BASE64_DECODED_SIZE(len));
- if (base64_decode(initial_resp, len, NULL, buf) < 0) {
- auth_request_handler_auth_fail(handler, request,
- "Invalid base64 data in initial response");
- return TRUE;
- }
- initial_resp_data = buf->data;
- initial_resp_len = buf->used;
- }
-
- /* handler is referenced until auth_callback is called. */
- handler->refcount++;
- auth_request_initial(request, initial_resp_data, initial_resp_len);
- return TRUE;
+ return auth_request_handler_api->auth_begin(handler, args);
}
int auth_request_handler_auth_continue(struct auth_request_handler *handler,
const char *args)
{
- struct auth_request *request;
- const char *data;
- size_t data_len;
- buffer_t *buf;
- unsigned int id;
-
- data = strchr(args, '\t');
- if (data++ == NULL) {
- i_error("BUG: Authentication client sent broken CONT request");
- return FALSE;
- }
-
- id = (unsigned int)strtoul(args, NULL, 10);
-
- request = hash_lookup(handler->requests, POINTER_CAST(id));
- if (request == NULL) {
- data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id);
- handler->callback(data, handler->context);
- return TRUE;
- }
-
- /* accept input only once after mechanism has sent a CONT reply */
- if (!request->accept_input) {
- auth_request_handler_auth_fail(handler, request,
- "Unexpected continuation");
- return TRUE;
- }
- request->accept_input = FALSE;
-
- data_len = strlen(data);
- buf = buffer_create_dynamic(pool_datastack_create(),
- MAX_BASE64_DECODED_SIZE(data_len));
- if (base64_decode(data, data_len, NULL, buf) < 0) {
- auth_request_handler_auth_fail(handler, request,
- "Invalid base64 data in continued response");
- return TRUE;
- }
-
- /* handler is referenced until auth_callback is called. */
- handler->refcount++;
- auth_request_continue(request, buf->data, buf->used);
- return TRUE;
-}
-
-static void append_user_reply(string_t *str, const struct user_data *user)
-{
- const char *p;
-
- str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user,
- dec2str(user->uid), dec2str(user->gid));
-
- if (user->system_user != NULL)
- str_printfa(str, "\tsystem_user=%s", user->system_user);
- if (user->mail != NULL)
- str_printfa(str, "\tmail=%s", user->mail);
-
- p = user->home != NULL ? strstr(user->home, "/./") : NULL;
- if (p == NULL) {
- if (user->home != NULL)
- str_printfa(str, "\thome=%s", user->home);
- } else {
- /* wu-ftpd like <chroot>/./<home> */
- str_printfa(str, "\thome=%s\tchroot=%s",
- p + 3, t_strdup_until(user->home, p));
- }
-}
-
-static void userdb_callback(const struct user_data *user, void *context)
-{
- struct auth_request *request = context;
- struct auth_request_handler *handler = request->context;
- string_t *reply;
-
- if (user != NULL) {
- auth_request_log_debug(request, "userdb",
- "uid=%s gid=%s home=%s mail=%s",
- dec2str(user->uid), dec2str(user->gid),
- user->home != NULL ? user->home : "",
- user->mail != NULL ? user->mail : "");
- }
-
- reply = t_str_new(256);
- if (user == NULL)
- str_printfa(reply, "NOTFOUND\t%u", request->id);
- else {
- str_printfa(reply, "USER\t%u\t", request->id);
- append_user_reply(reply, user);
- }
- handler->master_callback(str_c(reply), handler->master_context);
-
- auth_request_unref(request);
- auth_request_handler_unref(handler);
+ return auth_request_handler_api->auth_continue(handler, args);
}
void auth_request_handler_master_request(struct auth_request_handler *handler,
unsigned int id,
unsigned int client_id)
{
- struct auth_request *request;
- const char *reply;
-
- request = hash_lookup(handler->requests, POINTER_CAST(client_id));
- if (request == NULL) {
- i_error("Master request %u.%u not found",
- handler->client_pid, client_id);
- reply = t_strdup_printf("NOTFOUND\t%u", id);
- handler->master_callback(reply, handler->master_context);
- return;
- }
-
- auth_request_ref(request);
- auth_request_handler_remove(handler, request);
-
- if (!request->successful) {
- i_error("Master requested unfinished authentication request "
- "%u.%u", handler->client_pid, client_id);
- reply = t_strdup_printf("NOTFOUND\t%u", id);
- handler->master_callback(reply, handler->master_context);
- } else {
- /* the request isn't being referenced anywhere anymore,
- so we can do a bit of kludging.. replace the request's
- old client_id with master's id. */
- request->id = id;
- request->context = handler;
-
- /* handler is referenced until userdb_callback is called. */
- handler->refcount++;
- auth_request_lookup_user(request, userdb_callback, request);
- }
+ auth_request_handler_api->master_request(handler, id, client_id);
}
void auth_request_handlers_flush_failures(void)
{
- struct auth_request **auth_request;
- size_t i, size;
-
- auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
- size /= sizeof(*auth_request);
-
- for (i = 0; i < size; i++) {
- auth_request[i]->callback(auth_request[i],
- AUTH_CLIENT_RESULT_FAILURE, NULL, 0);
- auth_request_unref(auth_request[i]);
- }
- buffer_set_used_size(auth_failures_buf, 0);
+ auth_request_handler_api->flush_failures();
}
-static void auth_failure_timeout(void *context __attr_unused__)
+void auth_request_handlers_init(int balancer)
{
- auth_request_handlers_flush_failures();
-}
+ /* use balancer if we have it */
+ auth_request_handler_api = balancer ?
+ &auth_request_handler_balancer : &auth_request_handler_default;
-void auth_request_handlers_init(void)
-{
- auth_failures_buf = buffer_create_dynamic(default_pool, 1024);
- to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL);
+ auth_request_handler_api->init();
}
void auth_request_handlers_deinit(void)
{
- buffer_free(auth_failures_buf);
- timeout_remove(to_auth_failures);
+ auth_request_handler_api->deinit();
}
Index: auth-request-handler.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request-handler.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- auth-request-handler.h 9 Jan 2005 00:49:18 -0000 1.1
+++ auth-request-handler.h 9 Jan 2005 16:54:48 -0000 1.2
@@ -1,16 +1,50 @@
#ifndef __AUTH_REQUEST_HANDLER_H
#define __AUTH_REQUEST_HANDLER_H
+struct auth_request;
+
typedef void auth_request_callback_t(const char *reply, void *context);
+struct auth_request_handler_api {
+ struct auth_request_handler *
+ (*create)(struct auth *auth, int prepend_connect_uid,
+ auth_request_callback_t *callback, void *context,
+ auth_request_callback_t *master_callback,
+ void *master_context);
+ void (*unref)(struct auth_request_handler *handler);
+
+ void (*set)(struct auth_request_handler *handler,
+ unsigned int connect_uid, unsigned int client_pid);
+
+ void (*check_timeouts)(struct auth_request_handler *handler);
+ int (*auth_begin)(struct auth_request_handler *handler,
+ const char *args);
+ int (*auth_continue)(struct auth_request_handler *handler,
+ const char *args);
+ void (*master_request)(struct auth_request_handler *handler,
+ unsigned int id, unsigned int client_id);
+
+ void (*flush_failures)(void);
+
+ void (*init)(void);
+ void (*deinit)(void);
+};
+
+extern struct auth_request_handler_api auth_request_handler_default;
+extern struct auth_request_handler_api auth_request_handler_balancer;
+extern struct auth_request_handler_api *auth_request_handler_api;
+
struct auth_request_handler *
-auth_request_handler_create(struct auth *auth,
- unsigned int connect_uid, unsigned int client_pid,
+auth_request_handler_create(struct auth *auth, int prepend_connect_uid,
auth_request_callback_t *callback, void *context,
auth_request_callback_t *master_callback,
void *master_context);
void auth_request_handler_unref(struct auth_request_handler *handler);
+void auth_request_handler_set(struct auth_request_handler *handler,
+ unsigned int connect_uid,
+ unsigned int client_pid);
+
void auth_request_handler_check_timeouts(struct auth_request_handler *handler);
int auth_request_handler_auth_begin(struct auth_request_handler *handler,
@@ -23,7 +57,7 @@
void auth_request_handlers_flush_failures(void);
-void auth_request_handlers_init(void);
+void auth_request_handlers_init(int balancer);
void auth_request_handlers_deinit(void);
#endif
Index: auth.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- auth.c 8 Jan 2005 16:56:04 -0000 1.12
+++ auth.c 9 Jan 2005 16:54:48 -0000 1.13
@@ -1,13 +1,18 @@
/* Copyright (C) 2005 Timo Sirainen */
#include "common.h"
+#include "network.h"
+#include "buffer.h"
#include "str.h"
#include "mech.h"
#include "userdb.h"
#include "passdb.h"
#include "auth.h"
+#include "auth-request-handler.h"
+#include "auth-request-balancer.h"
#include <stdlib.h>
+#include <unistd.h>
struct auth *auth_preinit(void)
{
@@ -169,5 +174,8 @@
userdb_deinit(auth);
passdb_deinit(auth);
+ if (auth->balancer_worker != NULL)
+ auth_request_balancer_worker_destroy(auth->balancer_worker);
+
str_free(auth->mech_handshake);
}
Index: auth.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- auth.h 8 Jan 2005 16:56:04 -0000 1.10
+++ auth.h 9 Jan 2005 16:54:48 -0000 1.11
@@ -1,7 +1,11 @@
#ifndef __AUTH_H
#define __AUTH_H
+struct auth_balancer_worker;
+
struct auth {
+ struct auth_balancer_worker *balancer_worker;
+
struct mech_module_list *mech_modules;
buffer_t *mech_handshake;
Index: common.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/common.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- common.h 9 Jan 2005 00:48:02 -0000 1.10
+++ common.h 9 Jan 2005 16:54:48 -0000 1.11
@@ -5,7 +5,8 @@
#include "auth.h"
#define MASTER_SOCKET_FD 0
-#define LOGIN_LISTEN_FD 3
+#define CLIENT_LISTEN_FD 3
+#define BALANCER_LISTEN_FD 4
extern struct ioloop *ioloop;
extern int standalone;
Index: main.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/main.c,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -d -r1.38 -r1.39
--- main.c 9 Jan 2005 16:51:03 -0000 1.38
+++ main.c 9 Jan 2005 16:54:48 -0000 1.39
@@ -12,6 +12,8 @@
#include "mech.h"
#include "auth.h"
#include "auth-request-handler.h"
+#include "auth-request-balancer.h"
+#include "auth-master-interface.h"
#include "auth-master-connection.h"
#include "auth-client-connection.h"
@@ -29,6 +31,7 @@
static buffer_t *masters_buf;
static struct auth *auth;
+static int balancer = FALSE, balancer_worker = FALSE;
static void sig_quit(int signo __attr_unused__)
{
@@ -156,11 +159,13 @@
master = auth_master_connection_create(auth, -1);
if (master_fd != -1) {
auth_master_connection_add_listener(master, master_fd,
- master_path, FALSE);
+ master_path,
+ LISTENER_MASTER);
}
if (client_fd != -1) {
auth_master_connection_add_listener(master, client_fd,
- client_path, TRUE);
+ client_path,
+ LISTENER_CLIENT);
}
auth_client_connections_init(master);
buffer_append(masters_buf, &master, sizeof(master));
@@ -181,7 +186,8 @@
password_schemes_init();
masters_buf = buffer_create_dynamic(default_pool, 64);
- add_extra_listeners();
+ if (!balancer_worker)
+ add_extra_listeners();
/* Password lookups etc. may require roots, allow it. */
restrict_access_by_env(FALSE);
@@ -192,13 +198,11 @@
struct auth_master_connection *master, **master_p;
size_t i, size;
- process_start_time = ioloop_time;
-
- process_start_time = ioloop_time;
+ process_start_time = ioloop_time;
mech_init();
auth_init(auth);
- auth_request_handlers_init();
+ auth_request_handlers_init(balancer);
lib_init_signals(sig_quit);
@@ -228,12 +232,23 @@
if (chdir("/") < 0)
i_fatal("chdir(/) failed: %m");
}
- } else {
+ } else if (!balancer_worker) {
master = auth_master_connection_create(auth, MASTER_SOCKET_FD);
- auth_master_connection_add_listener(master, LOGIN_LISTEN_FD,
- NULL, TRUE);
+ auth_master_connection_add_listener(master, CLIENT_LISTEN_FD,
+ NULL, LISTENER_CLIENT);
+ if (balancer) {
+ auth_master_connection_add_listener(master,
+ BALANCER_LISTEN_FD,
+ NULL,
+ LISTENER_BALANCER);
+ }
auth_client_connections_init(master);
buffer_append(masters_buf, &master, sizeof(master));
+ } else {
+ master = auth_master_connection_create(auth, MASTER_SOCKET_FD);
+ buffer_append(masters_buf, &master, sizeof(master));
+
+ auth_request_balancer_worker_init(auth);
}
/* everything initialized, notify masters that all is well */
@@ -253,6 +268,9 @@
auth_request_handlers_flush_failures();
+ if (balancer_worker)
+ auth_request_balancer_worker_deinit();
+
master = buffer_get_modifyable_data(masters_buf, &size);
size /= sizeof(*master);
for (i = 0; i < size; i++)
@@ -268,20 +286,32 @@
closelog();
}
-int main(int argc, char *argv[])
+int main(int argc __attr_unused__, char *argv[])
{
+ int foreground;
+
#ifdef DEBUG
if (getenv("GDB") == NULL)
- fd_debug_verify_leaks(4, 1024);
+ fd_debug_verify_leaks(BALANCER_LISTEN_FD + 1, 1024);
#endif
/* NOTE: we start rooted, so keep the code minimal until
restrict_access_by_env() is called */
lib_init();
ioloop = io_loop_create(system_pool);
+ while (argv[1] != NULL) {
+ if (strcmp(argv[1], "-F") == 0)
+ foreground = TRUE;
+ else if (strcmp(argv[1], "-b") == 0)
+ balancer = TRUE;
+ else if (strcmp(argv[1], "-bw") == 0)
+ balancer_worker = TRUE;
+ argv++;
+ }
+
drop_privileges();
- main_init(argc > 1 && strcmp(argv[1], "-F") == 0);
+ main_init(foreground);
io_loop_run(ioloop);
main_deinit();
Index: mech.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech.h,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- mech.h 8 Jan 2005 21:37:32 -0000 1.34
+++ mech.h 9 Jan 2005 16:54:48 -0000 1.35
@@ -17,6 +17,9 @@
#include "auth-request.h"
+/* Used only for string sanitization. */
+#define MAX_MECH_NAME_LEN 64
+
struct mech_module {
const char *mech_name;
More information about the dovecot-cvs
mailing list