/*
 * Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: sasl-common.c,v 1.19 2007/07/28 16:45:03 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/net.h"
#include "sm/sasl.h"
#include "sm/io.h"

#if MTA_USE_SASL

static sasl_callback_t srvcallbacks[] =
{
#if 0
	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
#endif
	{	SASL_CB_LIST_END,	NULL,		NULL	}
};

#if !HAVE_SASL_VERSION_INFO
void sasl_version_info(const char **implementation,
		       const char **version_string,
		       int *version_major,
		       int *version_minor,
		       int *version_step,
		       int *version_patch)
{
	int v;
	sasl_version(implementation, &v);
	if (version_major != NULL)
		*version_major = (v >> 24) & 0xff;
	if (version_minor != NULL)
		*version_minor = (v >> 16) & 0xff;
	if (version_step != NULL)
		*version_step = v & 0xffff;
	if (version_patch != NULL)
		*version_patch = 0;
}
#endif

/*
**  SM_SASLVERSIONOK -- check Cyrus SASL version
**
**	Parameters:
**		none
**
**	Returns:
**		usual sm_error code.
*/

sm_ret_T
sm_saslversionok(void)
{
	int version_major, version_minor, version_step;

	sasl_version_info(NULL, NULL, &version_major, &version_minor,
			&version_step, NULL);
	if (SASL_VERSION_MAJOR == version_major &&
	    SASL_VERSION_MINOR == version_minor &&
	    SASL_VERSION_STEP == version_step)
		return SM_SUCCESS;
	else
		return sm_error_perm(SM_EM_SASL, SM_E_VER_MIX);
}

/*
**  SM_SASLVERSIONPRT -- show Cyrus SASL version
**
**	Parameters:
**		fp -- file for output
**
**	Returns:
**		usual sm_error code.
*/

sm_ret_T
sm_saslversionprt(sm_file_T *fp)
{
	const char *implementation, *version_string;
	int version_major, version_minor, version_step, version_patch;

	sasl_version_info(&implementation, &version_string,
		&version_major, &version_minor,
		&version_step, &version_patch);

	sm_io_fprintf(fp,
		"%s versions:\n"
		"compiled against: version=%d.%d.%d\n"
		"linked against:   version=%d.%d.%d\n"
#if 0
		"implementation=%s, version=%s, patch=%d\n"
#endif
		, implementation
		, SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP
		, version_major, version_minor, version_step
#if 0
		, implementation, version_string, version_patch
#endif
		);
	return sm_saslversionok();
}

/*
**  SM_SASL_INIT -- initialize SASL
**
**	Parameters:
**		server -- server side?
**		flags -- (security) flags
**		srvcbs -- server call backs
**		lctx -- log context
**		psasl_ctx -- (pointer to) SASL context (output)
**
**	Returns:
**		usual return code
*/

sm_ret_T
sm_sasl_init(bool server, uint32_t flags, sasl_callback_t *srvcbs, sm_log_ctx_P lctx, sm_sasl_ctx_P *psasl_ctx)
{
	sm_ret_T ret;
	sm_sasl_ctx_P sasl_ctx;

	SM_REQUIRE(psasl_ctx != NULL);

	ret = SM_SUCCESS;
	*psasl_ctx = NULL;
	sasl_ctx = sm_zalloc(sizeof(*sasl_ctx));
	if (sasl_ctx == NULL)
		return sm_err_temp(ENOMEM);
	sasl_ctx->sm_sasl_sec_flags = flags;
	sasl_ctx->sm_sasl_lctx = lctx;
#if 0
	sasl_set_alloc(sm_sasl_malloc, sm_sasl_calloc,
		       sm_sasl_realloc, sm_sasl_free);
#endif
	if (server)
		ret = sasl_server_init((srvcbs != NULL) ? srvcbs : srvcallbacks
				, "meta1");
	*psasl_ctx = sasl_ctx;
	return ret;
}

/*
**  ITEMINLIST -- does item appear in list?
**
**	Check whether item appears in list (which must be separated by a
**	character in delim) as a "word", i.e. it must appear at the begin
**	of the list or after a space, and it must end with a space or the
**	end of the list.
**
**	Parameters:
**		item -- item to search.
**		list -- list of items.
**		delim -- list of delimiters.
**
**	Returns:
**		pointer to occurrence (NULL if not found).
*/

char *
iteminlist(const char *item, char *list, const char *delim)
{
	char *s;
	int len;

	if (list == NULL || *list == '\0')
		return NULL;
	if (item == NULL || *item == '\0')
		return NULL;
	s = list;
	len = strlen(item);
	while (s != NULL && *s != '\0')
	{
		if (strncasecmp(s, item, len) == 0 &&
		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
			return s;
		s = strpbrk(s, delim);
		if (s != NULL)
			while (*++s == ' ')
				continue;
	}
	return NULL;
}

/*
**  SM_SASLMECHS -- get list of possible AUTH mechanisms
**
**	Parameters:
**		sasl_ctx -- meta1 SASL context
**		conn -- SASL connection (context)
**		mech_list -- list of allowed SASL mechanisms (output)
**
**	Returns:
**		number of mechs.
*/

sm_ret_T
sm_saslmechs(sm_sasl_ctx_P sasl_ctx, sasl_conn_t *conn, char **mech_list)
{
	int len, num, result;

	SM_REQUIRE(sasl_ctx != NULL);
	SM_REQUIRE(mech_list != NULL);
	SM_REQUIRE(conn != NULL);

	/* "user" is currently unused */
	result = sasl_listmech(conn, NULL, "", " ", "",
			(const char **)mech_list,
			(uint *)&len, &num);
	if (result != SASL_OK)
		num = 0;
	if (num == 0)
	{
		*mech_list = NULL;	/* be paranoid... */

		if (!SM_IS_FLAG(sasl_ctx->sm_sasl_flags, SASL_FL_NOMECH))
		{
			sm_log_write(sasl_ctx->sm_sasl_lctx,
				NULL, NULL, /* todo: create logging cat! */
				SM_LOG_WARN, 9,
				"sev=WARN, func=sm_saslmechs, status=no_mechanisms");
			SM_SET_FLAG(sasl_ctx->sm_sasl_flags, SASL_FL_NOMECH);
		}
	}
	return num;
}
#endif /* MTA_USE_SASL */


syntax highlighted by Code2HTML, v. 0.9.1