[dovecot-cvs] dovecot/src/pop3-login .cvsignore,NONE,1.1 Makefile.am,NONE,1.1 client-authenticate.c,NONE,1.1 client-authenticate.h,NONE,1.1 client.c,NONE,1.1 client.h,NONE,1.1 common.h,NONE,1.1

cras at procontrol.fi cras at procontrol.fi
Tue Jan 28 23:35:28 EET 2003


Update of /home/cvs/dovecot/src/pop3-login
In directory danu:/tmp/cvs-serv28480/src/pop3-login

Added Files:
	.cvsignore Makefile.am client-authenticate.c 
	client-authenticate.h client.c client.h common.h 
Log Message:
Moved common login process code to login-common, created pop3-login.



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

--- NEW FILE: Makefile.am ---
pkglibexecdir = $(libexecdir)/dovecot

pkglibexec_PROGRAMS = pop3-login

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

pop3_login_LDADD = \
	../login-common/liblogin-common.a \
	../lib/liblib.a \
	$(SSL_LIBS)

pop3_login_SOURCES = \
	client.c \
	client-authenticate.c

noinst_HEADERS = \
	common.h \
	client.h

--- NEW FILE: client-authenticate.c ---
/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "base64.h"
#include "buffer.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "safe-memset.h"
#include "str.h"
#include "imap-parser.h"
#include "auth-connection.h"
#include "../auth/auth-mech-desc.h"
#include "client.h"
#include "client-authenticate.h"
#include "master.h"

static struct auth_mech_desc *auth_mech_find(const char *name)
{
	int i;

	for (i = 0; i < AUTH_MECH_COUNT; i++) {
		if (auth_mech_desc[i].name != NULL &&
		    strcasecmp(auth_mech_desc[i].name, name) == 0)
			return &auth_mech_desc[i];
	}

	return NULL;
}

static void client_auth_abort(struct pop3_client *client, const char *msg)
{
	if (client->auth_request != NULL) {
		auth_abort_request(client->auth_request);
		client->auth_request = NULL;
	}

	client_send_line(client, msg != NULL ? msg :
			 "-ERR Authentication failed.");
	o_stream_flush(client->output);

	/* get back to normal client input */
	if (client->io != NULL)
		io_remove(client->io);
	client->io = client->common.fd == -1 ? NULL :
		io_add(client->common.fd, IO_READ, client_input, client);

	client_unref(client);
}

static void master_callback(struct client *_client, int success)
{
	struct pop3_client *client = (struct pop3_client *) _client;
	const char *reason = NULL;

	if (success)
		reason = t_strconcat("Login: ", client->virtual_user, NULL);
	else {
		reason = t_strconcat("Internal login failure: ",
				     client->virtual_user, NULL);
		client_send_line(client, "* BYE Internal login failure.");
	}

	client_destroy(client, reason);
	client_unref(client);
}

static void client_send_auth_data(struct pop3_client *client,
				  const unsigned char *data, size_t size)
{
	buffer_t *buf;

	t_push();

	buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1);
	buffer_append(buf, "+ ", 2);
	base64_encode(data, size, buf);
	buffer_append(buf, "\r\n", 2);

	o_stream_send(client->output, buffer_get_data(buf, NULL),
		      buffer_get_used_size(buf));
	o_stream_flush(client->output);

	t_pop();
}

static const char *auth_login_get_str(struct auth_login_reply *reply,
				      const unsigned char *data, size_t idx)
{
	size_t stop;

	if (idx >= reply->data_size || idx >= reply->reply_idx)
		return NULL;

	stop = reply->reply_idx < reply->data_size ?
		reply->reply_idx-1 : reply->data_size;

	return t_strndup(data, stop);
}

static int auth_callback(struct auth_request *request,
			 struct auth_login_reply *reply,
			 const unsigned char *data, void *context)
{
	struct pop3_client *client = context;
	const char *user, *realm;

	if (reply == NULL) {
		/* failed */
		client->auth_request = NULL;
		client_auth_abort(client, "-ERR Authentication process died.");
		return FALSE;
	}

	switch (reply->result) {
	case AUTH_LOGIN_RESULT_CONTINUE:
		client->auth_request = request;
		return TRUE;

	case AUTH_LOGIN_RESULT_SUCCESS:
		client->auth_request = NULL;

		user = auth_login_get_str(reply, data, reply->username_idx);
		realm = auth_login_get_str(reply, data, reply->realm_idx);

		i_free(client->virtual_user);
		client->virtual_user = realm == NULL ?
			i_strdup(user) : i_strconcat(user, "@", realm, NULL);

		/* we should be able to log in. if we fail, just
		   disconnect the client. */
		client_send_line(client, "+OK Logged in.");

		master_request_imap(&client->common, master_callback,
				    request->conn->pid, request->id);

		/* disable IO until we're back from master */
		if (client->io != NULL) {
			io_remove(client->io);
			client->io = NULL;
		}
		return FALSE;

	case AUTH_LOGIN_RESULT_FAILURE:
		/* see if we have error message */
		client->auth_request = NULL;

		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
			client_auth_abort(client, t_strconcat(
				"-ERR Authentication failed: ",
				(const char *) data, NULL));
		} else {
			/* default error message */
			client_auth_abort(client, NULL);
		}
		return FALSE;
	}

	i_unreached();
}

static void login_callback(struct auth_request *request,
			   struct auth_login_reply *reply,
			   const unsigned char *data, struct client *_client)
{
	struct pop3_client *client = (struct pop3_client *) _client;
	const void *ptr;
	size_t size;

	if (auth_callback(request, reply, data, client)) {
		ptr = buffer_get_data(client->plain_login, &size);
		auth_continue_request(request, ptr, size);

		buffer_set_used_size(client->plain_login, 0);
	}
}

int cmd_user(struct pop3_client *client, const char *args)
{
	if (!client->tls && disable_plaintext_auth) {
		client_send_line(client,
				 "-ERR Plaintext authentication disabled.");
		return TRUE;
	}

	/* authorization ID \0 authentication ID \0 pass */
	buffer_set_used_size(client->plain_login, 0);
	buffer_append_c(client->plain_login, '\0');
	buffer_append(client->plain_login, args, strlen(args));

	client_send_line(client, "+OK");
	return TRUE;
}

int cmd_pass(struct pop3_client *client, const char *args)
{
	const char *error;

	if (buffer_get_used_size(client->plain_login) == 0) {
		client_send_line(client, "-ERR No username given.");
		return TRUE;
	}

	buffer_append_c(client->plain_login, '\0');
	buffer_append(client->plain_login, args, strlen(args));

	client_ref(client);
	if (auth_init_request(AUTH_MECH_PLAIN, login_callback,
			      &client->common, &error)) {
		/* don't read any input from client until login is finished */
		if (client->io != NULL) {
			io_remove(client->io);
			client->io = NULL;
		}
		return TRUE;
	} else {
		client_send_line(client,
			t_strconcat("-ERR Login failed: ", error, NULL));
		client_unref(client);
		return TRUE;
	}
}

static void authenticate_callback(struct auth_request *request,
				  struct auth_login_reply *reply,
				  const unsigned char *data,
				  struct client *_client)
{
	struct pop3_client *client = (struct pop3_client *) _client;

	if (auth_callback(request, reply, data, client))
		client_send_auth_data(client, data, reply->data_size);
}

static void client_auth_input(void *context)
{
	struct pop3_client *client = context;
	buffer_t *buf;
	char *line;
	size_t linelen, bufsize;

	if (!client_read(client))
		return;

	/* @UNSAFE */
	line = i_stream_next_line(client->input);
	if (line == NULL)
		return;

	if (strcmp(line, "*") == 0) {
		client_auth_abort(client, "-ERR Authentication aborted");
		return;
	}

	linelen = strlen(line);
	buf = buffer_create_static_hard(data_stack_pool, linelen);

	if (base64_decode((const unsigned char *) line, linelen,
			  NULL, buf) <= 0) {
		/* failed */
		client_auth_abort(client, "-ERR Invalid base64 data");
	} else if (client->auth_request == NULL) {
		client_auth_abort(client, "-ERR Don't send unrequested data");
	} else {
		auth_continue_request(client->auth_request,
				      buffer_get_data(buf, NULL),
				      buffer_get_used_size(buf));
	}

	/* clear sensitive data */
	safe_memset(line, 0, linelen);

	bufsize = buffer_get_used_size(buf);
	safe_memset(buffer_free_without_data(buf), 0, bufsize);
}

int cmd_auth(struct pop3_client *client, const char *args)
{
	struct auth_mech_desc *mech;
	const char *error;

	/* we have only one argument: authentication mechanism name */
	mech = auth_mech_find(args);
	if (mech == NULL) {
		client_send_line(client,
				 "-ERR Unsupported authentication mechanism.");
		return TRUE;
	}

	if (!client->tls && mech->plaintext && disable_plaintext_auth) {
		client_send_line(client,
				 "-ERR Plaintext authentication disabled.");
		return TRUE;
	}

	client_ref(client);
	if (auth_init_request(mech->mech, authenticate_callback,
			      &client->common, &error)) {
		/* following input data will go to authentication */
		if (client->io != NULL)
			io_remove(client->io);
		client->io = io_add(client->common.fd, IO_READ,
				    client_auth_input, client);
	} else {
		client_send_line(client, t_strconcat(
			"-ERR Authentication failed: ", error, NULL));
		client_unref(client);
	}

	return TRUE;
}

--- NEW FILE: client-authenticate.h ---
#ifndef __CLIENT_AUTHENTICATE_H
#define __CLIENT_AUTHENTICATE_H

int cmd_user(struct pop3_client *client, const char *args);
int cmd_pass(struct pop3_client *client, const char *args);
int cmd_auth(struct pop3_client *client, const char *args);

#endif

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

#include "common.h"
#include "buffer.h"
#include "hash.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "process-title.h"
#include "safe-memset.h"
#include "strescape.h"
#include "client.h"
#include "client-authenticate.h"
#include "ssl-proxy.h"

/* max. length of input command line (spec says 512) */
#define MAX_INBUF_SIZE 2048

/* Disconnect client after idling this many seconds */
#define CLIENT_LOGIN_IDLE_TIMEOUT 60

/* Disconnect client when it sends too many bad commands */
#define CLIENT_MAX_BAD_COMMANDS 10

/* When max. number of simultaneous connections is reached, few of the
   oldest connections are disconnected. Since we have to go through the whole
   client hash, it's faster if we disconnect multiple clients. */
#define CLIENT_DESTROY_OLDEST_COUNT 16

static struct hash_table *clients;
static struct timeout *to_idle;

static void client_set_title(struct pop3_client *client)
{
	const char *host;

	if (!verbose_proctitle || !process_per_connection)
		return;

	host = net_ip2host(&client->common.ip);
	if (host == NULL)
		host = "??";

	process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
					  host));
}

static int cmd_stls(struct pop3_client *client)
{
	int fd_ssl;

	if (client->tls) {
		client_send_line(client, "-ERR TLS is already active.");
		return TRUE;
	}

	if (!ssl_initialized) {
		client_send_line(client, "-ERR TLS support isn't enabled.");
		return TRUE;
	}

	client_send_line(client, "+OK Begin TLS negotiation now.");
	o_stream_flush(client->output);

	/* must be removed before ssl_proxy_new(), since it may
	   io_add() the same fd. */
	if (client->io != NULL) {
		io_remove(client->io);
		client->io = NULL;
	}

	fd_ssl = ssl_proxy_new(client->common.fd);
	if (fd_ssl != -1) {
		client->tls = TRUE;
                client_set_title(client);

		client->common.fd = fd_ssl;

		i_stream_unref(client->input);
		o_stream_unref(client->output);

		client->input = i_stream_create_file(fd_ssl, default_pool,
						     8192, FALSE);
		client->output = o_stream_create_file(fd_ssl, default_pool,
						      1024, IO_PRIORITY_DEFAULT,
						      FALSE);
	} else {
		client_send_line(client, "-ERR TLS handehake failed.");
		client_destroy(client, "TLS handshake failed");
	}

	client->io = io_add(client->common.fd, IO_READ, client_input, client);
	return TRUE;
}

static int cmd_quit(struct pop3_client *client)
{
	client_send_line(client, "+OK Logging out");
	client_destroy(client, "Aborted login");
	return TRUE;
}

static int client_command_execute(struct pop3_client *client, const char *cmd,
				  const char *args)
{
	cmd = str_ucase(t_strdup_noconst(cmd));
	if (strcmp(cmd, "USER") == 0)
		return cmd_user(client, args);
	if (strcmp(cmd, "PASS") == 0)
		return cmd_pass(client, args);
	if (strcmp(cmd, "AUTH") == 0)
		return cmd_auth(client, args);
	if (strcmp(cmd, "STLS") == 0)
		return cmd_stls(client);
	if (strcmp(cmd, "QUIT") == 0)
		return cmd_quit(client);

	return FALSE;
}

int client_read(struct pop3_client *client)
{
	switch (i_stream_read(client->input)) {
	case -2:
		/* buffer full */
		client_send_line(client, "-ERR Input line too long, aborting");
		client_destroy(client, "Disconnected: Input buffer full");
		return FALSE;
	case -1:
		/* disconnected */
		client_destroy(client, "Disconnected");
		return FALSE;
	default:
		/* something was read */
		return TRUE;
	}
}

void client_input(void *context)
{
	struct pop3_client *client = context;
	char *line, *args;

	client->last_input = ioloop_time;

	if (!client_read(client))
		return;

	client_ref(client);

	o_stream_cork(client->output);
	while (!client->output->closed &&
	       (line = i_stream_next_line(client->input)) != NULL) {
		args = strchr(line, ' ');
		if (args == NULL)
			args = "";
		else
			*args++ = '\0';

		if (client_command_execute(client, line, args))
			client->bad_counter = 0;
		else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
			client_send_line(client, "-ERR Too many bad commands.");
			client_destroy(client,
				       "Disconnected: Too many bad commands");
		}
	}

	if (client_unref(client))
		o_stream_flush(client->output);
}

static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
				       void *context)
{
	struct pop3_client *client = key;
	struct pop3_client *const *destroy_clients;
	buffer_t *destroy_buf = context;
	size_t i, count;

	destroy_clients = buffer_get_data(destroy_buf, &count);
	count /= sizeof(struct pop3_client *);

	for (i = 0; i < count; i++) {
		if (destroy_clients[i]->created > client->created) {
			buffer_insert(destroy_buf,
				      i * sizeof(struct pop3_client *),
				      &client, sizeof(struct pop3_client *));
			break;
		}
	}
}

static void client_destroy_oldest(void)
{
	struct pop3_client *const *destroy_clients;
	buffer_t *destroy_buf;
	size_t i, count;

	/* find the oldest clients and put them to destroy-buffer */
	destroy_buf = buffer_create_static_hard(data_stack_pool,
						sizeof(struct pop3_client *) *
						CLIENT_DESTROY_OLDEST_COUNT);
	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);

	/* then kill them */
	destroy_clients = buffer_get_data(destroy_buf, &count);
	count /= sizeof(struct pop3_client *);

	for (i = 0; i < count; i++) {
		client_destroy(destroy_clients[i],
			       "Disconnected: Connection queue full");
	}
}

struct client *client_create(int fd, struct ip_addr *ip, int ssl)
{
	struct pop3_client *client;

	if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
	    hash_size(clients) >= max_logging_users) {
		/* reached max. users count, kill few of the
		   oldest connections */
		client_destroy_oldest();
	}

	/* always use nonblocking I/O */
	net_set_nonblock(fd, TRUE);

	client = i_new(struct pop3_client, 1);
	client->created = ioloop_time;
	client->refcount = 1;
	client->tls = ssl;

	client->common.ip = *ip;
	client->common.fd = fd;
	client->io = io_add(fd, IO_READ, client_input, client);
	client->input = i_stream_create_file(fd, default_pool, 8192, FALSE);
	client->output = o_stream_create_file(fd, default_pool, 1024,
					      IO_PRIORITY_DEFAULT, FALSE);
	client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);

	client->last_input = ioloop_time;
	hash_insert(clients, client, client);

	main_ref();

	client_send_line(client, "+OK " PACKAGE " ready.");
	client_set_title(client);
	return &client->common;
}

void client_destroy(struct pop3_client *client, const char *reason)
{
	if (reason != NULL)
		client_syslog(client, reason);

	hash_remove(clients, client);

	i_stream_close(client->input);
	o_stream_close(client->output);

	if (client->io != NULL) {
		io_remove(client->io);
		client->io = NULL;
	}

	net_disconnect(client->common.fd);
	client->common.fd = -1;

	i_free(client->virtual_user);
	client_unref(client);
}

void client_ref(struct pop3_client *client)
{
	client->refcount++;
}

int client_unref(struct pop3_client *client)
{
	if (--client->refcount > 0)
		return TRUE;

	i_stream_unref(client->input);
	o_stream_unref(client->output);

	buffer_free(client->plain_login);
	i_free(client);

	main_unref();
	return FALSE;
}

void client_send_line(struct pop3_client *client, const char *line)
{
	o_stream_send_str(client->output, line);
	o_stream_send(client->output, "\r\n", 2);
}

void client_syslog(struct pop3_client *client, const char *text)
{
	const char *host;

	host = net_ip2host(&client->common.ip);
	if (host == NULL)
		host = "??";

	i_info("%s [%s]", text, host);
}

static void client_hash_check_idle(void *key, void *value __attr_unused__,
				   void *context __attr_unused__)
{
	struct pop3_client *client = key;

	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
		client_destroy(client, "Disconnected: Inactivity");
}

static void idle_timeout(void *context __attr_unused__)
{
	hash_foreach(clients, client_hash_check_idle, NULL);
}

unsigned int clients_get_count(void)
{
	return hash_size(clients);
}

static void client_hash_destroy(void *key, void *value __attr_unused__,
				void *context __attr_unused__)
{
	client_destroy(key, NULL);
}

void clients_destroy_all(void)
{
	hash_foreach(clients, client_hash_destroy, NULL);
}

void clients_init(void)
{
	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
	to_idle = timeout_add(1000, idle_timeout, NULL);
}

void clients_deinit(void)
{
	clients_destroy_all();
	hash_destroy(clients);

	timeout_remove(to_idle);
}

--- NEW FILE: client.h ---
#ifndef __CLIENT_H
#define __CLIENT_H

#include "network.h"
#include "master.h"
#include "client-common.h"

struct pop3_client {
	struct client common;

	time_t created;
	int refcount;

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

	time_t last_input;
	unsigned int bad_counter;

	buffer_t *plain_login;
	struct auth_request *auth_request;
	char *virtual_user;

	unsigned int tls:1;
};

struct client *client_create(int fd, struct ip_addr *ip, int ssl);
void client_destroy(struct pop3_client *client, const char *reason);

void client_ref(struct pop3_client *client);
int client_unref(struct pop3_client *client);

void client_send_line(struct pop3_client *client, const char *line);
void client_syslog(struct pop3_client *client, const char *text);

int client_read(struct pop3_client *client);
void client_input(void *context);

unsigned int clients_get_count(void);
void clients_destroy_all(void);

void clients_init(void);
void clients_deinit(void);

#endif

--- NEW FILE: common.h ---
#ifndef __COMMON_H
#define __COMMON_H

#include "lib.h"
#include "../auth/auth-login-interface.h"

extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
extern unsigned int max_logging_users;
extern unsigned int login_process_uid;

void main_ref(void);
void main_unref(void);

void main_close_listen(void);

#endif




More information about the dovecot-cvs mailing list