view src/auth/mech.c @ 1076:e22f21b9ec49 HEAD

more fixes
author Timo Sirainen <tss@iki.fi>
date Sun, 02 Feb 2003 02:19:34 +0200
parents f1401fa7ab03
children 98dca3b6b209
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "buffer.h"
#include "hash.h"
#include "mech.h"
#include "login-connection.h"

#include <stdlib.h>

struct mech_module_list {
	struct mech_module_list *next;

	struct mech_module module;
};

enum auth_mech auth_mechanisms;
const char *const *auth_realms;

static int set_use_cyrus_sasl;
static struct mech_module_list *mech_modules;
static struct auth_login_reply failure_reply;

void mech_register_module(struct mech_module *module)
{
	struct mech_module_list *list;

	i_assert((auth_mechanisms & module->mech) == 0);

	auth_mechanisms |= module->mech;

	list = i_new(struct mech_module_list, 1);
	list->module = *module;

	list->next = mech_modules;
	mech_modules = list;
}

void mech_unregister_module(struct mech_module *module)
{
	struct mech_module_list **pos, *list;

	if ((auth_mechanisms & module->mech) == 0)
		return; /* not registered */

        auth_mechanisms &= ~module->mech;

	for (pos = &mech_modules; *pos != NULL; pos = &(*pos)->next) {
		if ((*pos)->module.mech == module->mech) {
			list = *pos;
			*pos = (*pos)->next;
			i_free(list);
			break;
		}
	}
}

void mech_request_new(struct login_connection *conn,
		      struct auth_login_request_new *request,
		      mech_callback_t *callback)
{
	struct mech_module_list *list;
	struct auth_request *auth_request;

	if ((auth_mechanisms & request->mech) == 0) {
		/* unsupported mechanism */
		i_error("BUG: login requested unsupported "
			"auth mechanism %d", request->mech);
		failure_reply.id = request->id;
		callback(&failure_reply, NULL, conn);
		return;
	}

#ifdef USE_CYRUS_SASL2
	if (set_use_cyrus_sasl) {
		auth_request = mech_cyrus_sasl_new(conn, request, callback);
	} else
#endif
	{
		auth_request = NULL;

		for (list = mech_modules; list != NULL; list = list->next) {
			if (list->module.mech == request->mech) {
				auth_request =
					list->module.auth_new(conn, request->id,
							      callback);
				break;
			}
		}
	}

	if (auth_request != NULL) {
		auth_request->protocol = request->protocol;
		hash_insert(conn->auth_requests, POINTER_CAST(request->id),
			    auth_request);
	}
}

void mech_request_continue(struct login_connection *conn,
			   struct auth_login_request_continue *request,
			   const unsigned char *data,
			   mech_callback_t *callback)
{
	struct auth_request *auth_request;

	auth_request = hash_lookup(conn->auth_requests,
				   POINTER_CAST(request->id));
	if (auth_request == NULL) {
		/* timeouted */
		failure_reply.id = request->id;
		callback(&failure_reply, NULL, conn);
	} else {
		if (!auth_request->auth_continue(conn, auth_request,
						 request, data, callback))
                        mech_request_free(conn, auth_request, request->id);
	}
}

void mech_request_free(struct login_connection *conn,
		       struct auth_request *auth_request, unsigned int id)
{
	auth_request->auth_free(auth_request);
	hash_remove(conn->auth_requests, POINTER_CAST(id));
}

void mech_init_login_reply(struct auth_login_reply *reply)
{
	memset(reply, 0, sizeof(*reply));

	reply->username_idx = (unsigned int)-1;
	reply->realm_idx = (unsigned int)-1;
	reply->reply_idx = (unsigned int)-1;
}

void *mech_auth_success(struct auth_login_reply *reply,
			struct auth_request *auth_request,
			const void *data, size_t data_size)
{
	buffer_t *buf;

	buf = buffer_create_dynamic(data_stack_pool, 256, (size_t)-1);

	reply->username_idx = 0;
	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);

	if (auth_request->realm == NULL)
		reply->realm_idx = (size_t)-1;
	else {
		reply->realm_idx = buffer_get_used_size(buf);
		buffer_append(buf, auth_request->realm,
			      strlen(auth_request->realm)+1);
	}

	if (data_size == 0)
		reply->reply_idx = (size_t)-1;
	else {
		reply->reply_idx = buffer_get_used_size(buf);
		buffer_append(buf, data, data_size);
	}

	reply->result = AUTH_LOGIN_RESULT_SUCCESS;
	reply->data_size = buffer_get_used_size(buf);
	return buffer_get_modifyable_data(buf, NULL);
}

void mech_auth_finish(struct auth_request *auth_request,
		      const void *data, size_t data_size, int success)
{
	struct auth_login_reply reply;
	void *reply_data;

	memset(&reply, 0, sizeof(reply));
	reply.id = auth_request->id;

	if (success) {
		reply_data = mech_auth_success(&reply, auth_request,
					       data, data_size);
		reply.result = AUTH_LOGIN_RESULT_SUCCESS;
	} else {
		reply_data = NULL;
		reply.result = AUTH_LOGIN_RESULT_FAILURE;
	}

	auth_request->callback(&reply, reply_data, auth_request->conn);

	if (!success) {
		mech_request_free(auth_request->conn, auth_request,
				  auth_request->id);
	}
}

extern struct mech_module mech_plain;
extern struct mech_module mech_digest_md5;

void mech_init(void)
{
	const char *const *mechanisms;
	const char *env;

        mech_modules = NULL;
	auth_mechanisms = 0;

	memset(&failure_reply, 0, sizeof(failure_reply));
	failure_reply.result = AUTH_LOGIN_RESULT_FAILURE;

	/* register wanted mechanisms */
	env = getenv("MECHANISMS");
	if (env == NULL || *env == '\0')
		i_fatal("MECHANISMS environment is unset");

	mechanisms = t_strsplit(env, " ");
	while (*mechanisms != NULL) {
		if (strcasecmp(*mechanisms, "PLAIN") == 0)
			mech_register_module(&mech_plain);
		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
			mech_register_module(&mech_digest_md5);
		else {
			i_fatal("Unknown authentication mechanism '%s'",
				*mechanisms);
		}

		mechanisms++;
	}

	if (auth_mechanisms == 0)
		i_fatal("No authentication mechanisms configured");

	/* get our realm - note that we allocate from data stack so
	   this function should never be called inside I/O loop or anywhere
	   else where t_pop() is called */
	env = getenv("REALMS");
	if (env == NULL)
		env = "";
	auth_realms = t_strsplit(env, " ");

	set_use_cyrus_sasl = getenv("USE_CYRUS_SASL") != NULL;

#ifdef USE_CYRUS_SASL2
	if (set_use_cyrus_sasl)
		mech_cyrus_sasl_init_lib();
#endif
}

void mech_deinit(void)
{
	mech_unregister_module(&mech_plain);
	mech_unregister_module(&mech_digest_md5);
}