[dovecot-cvs] dovecot/src/lib-auth .cvsignore,NONE,1.1 Makefile.am,NONE,1.1 auth-client.c,NONE,1.1 auth-client.h,NONE,1.1 auth-server-connection.c,NONE,1.1 auth-server-connection.h,NONE,1.1 auth-server-request.c,NONE,1.1 auth-server-request.h,NONE,1.1

cras at procontrol.fi cras at procontrol.fi
Fri Aug 22 06:42:15 EEST 2003


Update of /home/cvs/dovecot/src/lib-auth
In directory danu:/tmp/cvs-serv8684/src/lib-auth

Added Files:
	.cvsignore Makefile.am auth-client.c auth-client.h 
	auth-server-connection.c auth-server-connection.h 
	auth-server-request.c auth-server-request.h 
Log Message:
Moved client side code for auth process handling to lib-auth. Some other login process cleanups.



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

--- NEW FILE: Makefile.am ---
noinst_LIBRARIES = libauth.a

INCLUDES = \
	-I$(top_srcdir)/src/lib

libauth_a_SOURCES = \
	auth-client.c \
	auth-server-connection.c \
	auth-server-request.c

noinst_HEADERS = \
	auth-client.h

--- NEW FILE: auth-client.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "hash.h"
#include "auth-client.h"
#include "auth-server-connection.h"

#include <dirent.h>
#include <sys/stat.h>

struct auth_client *auth_client_new(unsigned int client_pid)
{
	struct auth_client *client;

	client = i_new(struct auth_client, 1);
	client->pid = client_pid;

	auth_client_connect_missing_servers(client);
	return client;
}

void auth_client_free(struct auth_client *client)
{
	struct auth_server_connection *next;

	while (client->connections != NULL) {
		next = client->connections->next;
		auth_server_connection_destroy(client->connections, FALSE);
		client->connections = next;
	}

	if (client->to_reconnect != NULL)
		timeout_remove(client->to_reconnect);
	i_free(client);
}

enum auth_mech auth_client_get_available_mechs(struct auth_client *client)
{
	return client->available_auth_mechs;
}

int auth_client_is_connected(struct auth_client *client)
{
	return client->to_reconnect == NULL &&
		client->conn_waiting_handshake_count == 0;
}

void auth_client_set_connect_notify(struct auth_client *client,
				    auth_connect_notify_callback_t *callback,
				    void *context)
{
	client->connect_notify_callback = callback;
	client->connect_notify_context = context;
}

static void reconnect_timeout(void *context)
{
	struct auth_client *client = context;

	auth_client_connect_missing_servers(client);
}

void auth_client_connect_missing_servers(struct auth_client *client)
{
	DIR *dirp;
	struct dirent *dp;
	struct stat st;
	int reconnect;

	/* we're chrooted into */
	dirp = opendir(".");
	if (dirp == NULL) {
		i_fatal("opendir(.) failed when trying to get list of "
			"authentication servers: %m");
	}

	reconnect = FALSE;
	while ((dp = readdir(dirp)) != NULL) {
		if (dp->d_name[0] == '.')
			continue;

		if (auth_server_connection_find_path(client, dp->d_name) != NULL) {
			/* already connected */
			continue;
		}

		if (stat(dp->d_name, &st) == 0 && S_ISSOCK(st.st_mode)) {
			if (auth_server_connection_new(client,
						       dp->d_name) == NULL)
				reconnect = TRUE;
		}
	}

	if (closedir(dirp) < 0)
		i_error("closedir() failed: %m");

	if (reconnect || client->connections == NULL) {
		if (client->to_reconnect == NULL) {
			client->to_reconnect =
				timeout_add(5000, reconnect_timeout, client);
		}
	} else if (client->to_reconnect != NULL) {
		timeout_remove(client->to_reconnect);
		client->to_reconnect = NULL;
	}

	client->connect_notify_callback(client,
					auth_client_is_connected(client),
					client->connect_notify_context);
}

--- NEW FILE: auth-client.h ---
#ifndef __AUTH_CLIENT_H
#define __AUTH_CLIENT_H

#include "../auth/auth-client-interface.h"

struct auth_client;
struct auth_request;

/* reply is NULL if auth connection died */
typedef void auth_request_callback_t(struct auth_request *request,
				     struct auth_client_request_reply *reply,
				     const unsigned char *data, void *context);

typedef void auth_connect_notify_callback_t(struct auth_client *client,
					    int connected, void *context);

/* Create new authentication client. */
struct auth_client *auth_client_new(unsigned int client_pid);
void auth_client_free(struct auth_client *client);

int auth_client_is_connected(struct auth_client *client);
void auth_client_set_connect_notify(struct auth_client *client,
				    auth_connect_notify_callback_t *callback,
				    void *context);
enum auth_mech auth_client_get_available_mechs(struct auth_client *client);

void auth_client_connect_missing_servers(struct auth_client *client);

/* Create a new authentication request. callback is called whenever something
   happens for the request. */
struct auth_request *
auth_client_request_new(struct auth_client *client,
			enum auth_mech mech, enum auth_protocol protocol,
			auth_request_callback_t *callback, void *context,
			const char **error_r);

/* Continue authentication. Call when
   reply->result == AUTH_CLIENT_REQUEST_CONTINUE */
void auth_client_request_continue(struct auth_request *request,
				  const unsigned char *data, size_t data_size);

/* Abort ongoing authentication request. */
void auth_client_request_abort(struct auth_request *request);

/* Return ID of this request. */
unsigned int auth_client_request_get_id(struct auth_request *request);

/* Return the PID of the server that handled this request. */
unsigned int auth_client_request_get_server_pid(struct auth_request *request);

#endif

--- NEW FILE: auth-server-connection.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "hash.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "network.h"
#include "auth-client.h"
#include "auth-server-connection.h"
#include "auth-server-request.h"

#include <unistd.h>

/* Maximum size for an auth reply. 50kB should be more than enough. */
#define MAX_INBUF_SIZE (1024*50)

#define MAX_OUTBUF_SIZE \
	(sizeof(struct auth_client_request_continue) + \
	 AUTH_CLIENT_MAX_REQUEST_DATA_SIZE)

static void update_available_auth_mechs(struct auth_client *client)
{
	struct auth_server_connection *conn;

        client->available_auth_mechs = 0;
	for (conn = client->connections; conn != NULL; conn = conn->next)
                client->available_auth_mechs |= conn->available_auth_mechs;
}

static void auth_handle_handshake(struct auth_server_connection *conn,
				  struct auth_client_handshake_reply *handshake)
{
	if (handshake->server_pid == 0) {
		i_error("BUG: Auth server said it's PID 0");
		auth_server_connection_destroy(conn, FALSE);
		return;
	}

	conn->pid = handshake->server_pid;
	conn->available_auth_mechs = handshake->auth_mechanisms;
	conn->handshake_received = TRUE;

        conn->client->conn_waiting_handshake_count--;
	update_available_auth_mechs(conn->client);

	if (auth_client_is_connected(conn->client)) {
		conn->client->connect_notify_callback(conn->client, TRUE,
				conn->client->connect_notify_context);
	}
}

static void auth_client_input(void *context)
{
	struct auth_server_connection *conn = context;
	struct auth_client_handshake_reply handshake;
	const unsigned char *data;
	size_t size;

	switch (i_stream_read(conn->input)) {
	case 0:
		return;
	case -1:
		/* disconnected */
		auth_server_connection_destroy(conn, TRUE);
		return;
	case -2:
		/* buffer full - can't happen unless auth is buggy */
		i_error("BUG: Auth server sent us more than %d bytes of data",
			MAX_INBUF_SIZE);
		auth_server_connection_destroy(conn, FALSE);
		return;
	}

	if (!conn->handshake_received) {
		data = i_stream_get_data(conn->input, &size);
		if (size == sizeof(handshake)) {
			memcpy(&handshake, data, sizeof(handshake));
			i_stream_skip(conn->input, sizeof(handshake));

			auth_handle_handshake(conn, &handshake);
		} else if (size > sizeof(handshake)) {
			i_error("BUG: Auth server sent us too large handshake "
				"(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
				sizeof(handshake));
			auth_server_connection_destroy(conn, FALSE);
		}
		return;
	}

	if (!conn->reply_received) {
		data = i_stream_get_data(conn->input, &size);
		if (size < sizeof(conn->reply))
			return;

		memcpy(&conn->reply, data, sizeof(conn->reply));
		i_stream_skip(conn->input, sizeof(conn->reply));
		conn->reply_received = TRUE;
	}

	data = i_stream_get_data(conn->input, &size);
	if (size < conn->reply.data_size)
		return;

	/* we've got a full reply */
	conn->reply_received = FALSE;
	auth_server_request_handle_reply(conn, &conn->reply, data);
	i_stream_skip(conn->input, conn->reply.data_size);
}

struct auth_server_connection *
auth_server_connection_new(struct auth_client *client, const char *path)
{
	struct auth_server_connection *conn;
	struct auth_client_handshake_request handshake;
	pool_t pool;
	int fd;

	fd = net_connect_unix(path);
	if (fd == -1) {
		i_error("Can't connect to auth server at %s: %m", path);
		return NULL;
	}

	/* use blocking connection since we depend on auth server -
	   if it's slow, just wait */

	pool = pool_alloconly_create("Auth connection", 1024);
	conn = p_new(pool, struct auth_server_connection, 1);
	conn->pool = pool;

	conn->client = client;
	conn->path = p_strdup(pool, path);
	conn->fd = fd;
	conn->io = io_add(fd, IO_READ, auth_client_input, conn);
	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
					   FALSE);
	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
					    FALSE);
	conn->requests = hash_create(default_pool, pool, 100, NULL, NULL);

	conn->next = client->connections;
	client->connections = conn;

	/* send our handshake */
	memset(&handshake, 0, sizeof(handshake));
	handshake.client_pid = client->pid;

        client->conn_waiting_handshake_count++;
	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
		errno = conn->output->stream_errno;
		i_warning("Error sending handshake to auth server: %m");
		auth_server_connection_destroy(conn, TRUE);
		return NULL;
	}
	return conn;
}

void auth_server_connection_destroy(struct auth_server_connection *conn,
				    int reconnect)
{
	struct auth_client *client = conn->client;
	struct auth_server_connection **pos;

        pos = &conn->client->connections;
	for (; *pos != NULL; pos = &(*pos)->next) {
		if (*pos == conn) {
			*pos = conn->next;
			break;
		}
	}

	if (!conn->handshake_received)
		client->conn_waiting_handshake_count--;

	io_remove(conn->io);
	if (close(conn->fd) < 0)
		i_error("close(auth) failed: %m");
	conn->fd = -1;

	auth_server_requests_remove_all(conn);
	hash_destroy(conn->requests);

	i_stream_unref(conn->input);
	o_stream_unref(conn->output);
	pool_unref(conn->pool);

	if (reconnect)
		auth_client_connect_missing_servers(client);
	else {
		client->connect_notify_callback(client,
				auth_client_is_connected(client),
				client->connect_notify_context);
	}
}

struct auth_server_connection *
auth_server_connection_find_path(struct auth_client *client, const char *path)
{
	struct auth_server_connection *conn;

	for (conn = client->connections; conn != NULL; conn = conn->next) {
		if (strcmp(conn->path, path) == 0)
			return conn;
	}

	return NULL;
}

struct auth_server_connection *
auth_server_connection_find_mech(struct auth_client *client,
				 enum auth_mech mech, const char **error_r)
{
	struct auth_server_connection *conn;

	for (conn = client->connections; conn != NULL; conn = conn->next) {
		if ((conn->available_auth_mechs & mech))
			return conn;
	}

	if ((client->available_auth_mechs & mech) == 0)
		*error_r = "Unsupported authentication mechanism";
	else {
		*error_r = "Authentication server isn't connected, "
			"try again later..";
	}

	return NULL;
}

--- NEW FILE: auth-server-connection.h ---
#ifndef __AUTH_SERVER_CONNECTION_H
#define __AUTH_SERVER_CONNECTION_H

struct auth_client {
	unsigned int pid;

	struct auth_server_connection *connections;
	struct timeout *to_reconnect;

	unsigned int conn_waiting_handshake_count;

	enum auth_mech available_auth_mechs;
	unsigned int request_id_counter;

	auth_connect_notify_callback_t *connect_notify_callback;
	void *connect_notify_context;
};

struct auth_server_connection {
	struct auth_server_connection *next;

	pool_t pool;
	struct auth_client *client;
	const char *path;
	int fd;

	struct io *io;
	struct istream *input;
	struct ostream *output;

	unsigned int pid;
	enum auth_mech available_auth_mechs;
        struct auth_client_request_reply reply;

        struct hash_table *requests;

	unsigned int handshake_received:1;
	unsigned int reply_received:1;
};

struct auth_server_connection *
auth_server_connection_new(struct auth_client *client, const char *path);
void auth_server_connection_destroy(struct auth_server_connection *conn,
				    int reconnect);

struct auth_server_connection *
auth_server_connection_find_path(struct auth_client *client, const char *path);

struct auth_server_connection *
auth_server_connection_find_mech(struct auth_client *client,
				 enum auth_mech mech, const char **error_r);

#endif

--- NEW FILE: auth-server-request.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "hash.h"
#include "ostream.h"
#include "auth-client.h"
#include "auth-server-connection.h"
#include "auth-server-request.h"

struct auth_request {
        enum auth_mech mech;
        struct auth_server_connection *conn;

	unsigned int id;

	auth_request_callback_t *callback;
	void *context;

	unsigned int init_sent:1;
};

void auth_server_request_handle_reply(struct auth_server_connection *conn,
				      struct auth_client_request_reply *reply,
				      const unsigned char *data)
{
	struct auth_request *request;

	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
	if (request == NULL) {
		i_error("BUG: Auth server sent us reply with unknown ID %u",
			reply->id);
		return;
	}

	request->callback(request, reply, data, request->context);

	if (reply->result != AUTH_CLIENT_RESULT_CONTINUE) {
		hash_remove(conn->requests, POINTER_CAST(request->id));
		i_free(request);
	}
}

static void request_hash_remove(void *key __attr_unused__, void *value,
				void *context __attr_unused__)
{
	struct auth_request *request = value;

	request->callback(request, NULL, NULL, request->context);
	request->conn = NULL;
}

void auth_server_requests_remove_all(struct auth_server_connection *conn)
{
	hash_foreach(conn->requests, request_hash_remove, NULL);
}

struct auth_request *
auth_client_request_new(struct auth_client *client,
			enum auth_mech mech, enum auth_protocol protocol,
			auth_request_callback_t *callback, void *context,
			const char **error_r)
{
	struct auth_server_connection *conn;
	struct auth_request *request;
	struct auth_client_request_new auth_request;

	conn = auth_server_connection_find_mech(client, mech, error_r);
	if (conn == NULL)
		return NULL;

	request = i_new(struct auth_request, 1);
	request->mech = mech;
	request->conn = conn;
	request->id = ++client->request_id_counter;
	if (request->id == 0) {
		/* wrapped - ID 0 not allowed */
		request->id = ++client->request_id_counter;
	}
	request->callback = callback;
	request->context = context;

	hash_insert(conn->requests, POINTER_CAST(request->id), request);

	/* send request to auth */
	auth_request.type = AUTH_CLIENT_REQUEST_NEW;
	auth_request.id = request->id;
	auth_request.protocol = protocol;
	auth_request.mech = request->mech;
	if (o_stream_send(request->conn->output, &auth_request,
			  sizeof(auth_request)) < 0) {
		errno = request->conn->output->stream_errno;
		i_warning("Error sending request to auth process: %m");
		auth_server_connection_destroy(request->conn, TRUE);
	}
	return request;
}

void auth_client_request_continue(struct auth_request *request,
				  const unsigned char *data, size_t data_size)
{
	struct auth_client_request_continue auth_request;

	/* send continued request to auth */
	auth_request.type = AUTH_CLIENT_REQUEST_CONTINUE;
	auth_request.id = request->id;
	auth_request.data_size = data_size;

	if (o_stream_send(request->conn->output, &auth_request,
			  sizeof(auth_request)) < 0 ||
	    o_stream_send(request->conn->output, data, data_size) < 0) {
		errno = request->conn->output->stream_errno;
		i_warning("Error sending continue request to auth process: %m");
		auth_server_connection_destroy(request->conn, TRUE);
	}
}

void auth_client_request_abort(struct auth_request *request)
{
	void *id = POINTER_CAST(request->id);

	if (hash_lookup(request->conn->requests, id) != NULL)
		hash_remove(request->conn->requests, id);
	i_free(request);
}

unsigned int auth_client_request_get_id(struct auth_request *request)
{
	return request->id;
}

unsigned int auth_client_request_get_server_pid(struct auth_request *request)
{
	return request->conn->pid;
}

--- NEW FILE: auth-server-request.h ---
#ifndef __AUTH_SERVER_REQUEST_H
#define __AUTH_SERVER_REQUEST_H

void auth_server_request_handle_reply(struct auth_server_connection *conn,
				      struct auth_client_request_reply *reply,
				      const unsigned char *data);

void auth_server_requests_remove_all(struct auth_server_connection *conn);

#endif



More information about the dovecot-cvs mailing list