xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/xsess.c (revision 029f8c2148231860b4e3e27aee4fc4dc7fe7dfb8)
1*029f8c21Sshm /* $NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $ */
2231558cbSagc 
3231558cbSagc /*
4231558cbSagc  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5231558cbSagc  * All rights reserved.
6231558cbSagc  *
7231558cbSagc  * This code is derived from software contributed to The NetBSD Foundation
8231558cbSagc  * by Mateusz Kocielski.
9231558cbSagc  *
10231558cbSagc  * Redistribution and use in source and binary forms, with or without
11231558cbSagc  * modification, are permitted provided that the following conditions
12231558cbSagc  * are met:
13231558cbSagc  * 1. Redistributions of source code must retain the above copyright
14231558cbSagc  *    notice, this list of conditions and the following disclaimer.
15231558cbSagc  * 2. Redistributions in binary form must reproduce the above copyright
16231558cbSagc  *    notice, this list of conditions and the following disclaimer in the
17231558cbSagc  *    documentation and/or other materials provided with the distribution.
18231558cbSagc  * 3. All advertising materials mentioning features or use of this software
19231558cbSagc  *    must display the following acknowledgement:
20231558cbSagc  *        This product includes software developed by the NetBSD
21231558cbSagc  *        Foundation, Inc. and its contributors.
22231558cbSagc  * 4. Neither the name of The NetBSD Foundation nor the names of its
23231558cbSagc  *    contributors may be used to endorse or promote products derived
24231558cbSagc  *    from this software without specific prior written permission.
25231558cbSagc  *
26231558cbSagc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27231558cbSagc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28231558cbSagc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29231558cbSagc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30231558cbSagc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31231558cbSagc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32231558cbSagc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33231558cbSagc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34231558cbSagc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35231558cbSagc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36231558cbSagc  * POSSIBILITY OF SUCH DAMAGE.
37231558cbSagc  */
3819c14409Schristos #include <sys/cdefs.h>
39*029f8c21Sshm __RCSID("$NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $");
40231558cbSagc 
4119c14409Schristos #include <assert.h>
4219c14409Schristos #include <saslc.h>
43231558cbSagc #include <stdio.h>
44231558cbSagc #include <string.h>
4519c14409Schristos 
4619c14409Schristos #include "crypto.h"
47231558cbSagc #include "dict.h"
48231558cbSagc #include "error.h"
4919c14409Schristos #include "list.h"
5019c14409Schristos #include "msg.h"
51231558cbSagc #include "mech.h"
5219c14409Schristos #include "parser.h"
53231558cbSagc #include "saslc_private.h"
54231558cbSagc 
5519c14409Schristos /*
5619c14409Schristos  * TODO:
5719c14409Schristos  *
5819c14409Schristos  * 1) Add hooks to allow saslc_sess_encode() and saslc_sess_decode()
5919c14409Schristos  * to output and input, respectively, base64 encoded data much like
6019c14409Schristos  * what sess_saslc_cont() does according to the SASLC_FLAGS_BASE64_*
6119c14409Schristos  * flags.  For saslc_sess_decode() it seems it would be easiest to do
6219c14409Schristos  * this in saslc__buffer32_fetch() pushing any extra buffering into
6319c14409Schristos  * the BIO_* routines, but I haven't thought this through carefully
6419c14409Schristos  * yet.
65231558cbSagc  */
66231558cbSagc 
6719c14409Schristos static inline char *
skip_WS(char * p)6819c14409Schristos skip_WS(char *p)
69231558cbSagc {
70231558cbSagc 
7119c14409Schristos 	while (*p == ' ' || *p == '\t')
7219c14409Schristos 		p++;
7319c14409Schristos 	return p;
7419c14409Schristos }
7519c14409Schristos 
7619c14409Schristos /**
7719c14409Schristos  * @brief convert a comma and/or space delimited list into a comma
7819c14409Schristos  * delimited list of the form:
7919c14409Schristos  *   ( *LWS element *( *LWS "," *LWS element ))
8019c14409Schristos  * @param str string to convert.
8119c14409Schristos  */
8219c14409Schristos static void
normalize_list_string(char * opts)8319c14409Schristos normalize_list_string(char *opts)
8419c14409Schristos {
8519c14409Schristos 	char *p;
8619c14409Schristos 
8719c14409Schristos 	p = opts;
8819c14409Schristos 	while (p != NULL) {
8919c14409Schristos 		p = strchr(p, ' ');
9019c14409Schristos 		if (p == NULL)
9119c14409Schristos 			break;
9219c14409Schristos 		if (p > opts && p[-1] != ',')
9319c14409Schristos 			*p++ = ',';
949e697204Sjoerg 		p = skip_WS(p + 1);
9519c14409Schristos 	}
9619c14409Schristos }
9719c14409Schristos 
9809484ebbSchristos /**
9909484ebbSchristos  * @brief get the security flags from a comma delimited string.
10009484ebbSchristos  * @param sec_opt the security option comman delimited string.
10109484ebbSchristos  * @return the flags on success, or -1 on error (no memory).
10209484ebbSchristos  */
10319c14409Schristos static int
get_security_flags(const char * sec_opts)10419c14409Schristos get_security_flags(const char *sec_opts)
10519c14409Schristos {
10619c14409Schristos 	static const named_flag_t flag_tbl[] = {
10719c14409Schristos 		{ "noanonymous",	FLAG_ANONYMOUS },
10819c14409Schristos 		{ "nodictionary",	FLAG_DICTIONARY },
10919c14409Schristos 		{ "noplaintext",	FLAG_PLAINTEXT },
11019c14409Schristos 		{ "noactive",		FLAG_ACTIVE },
11119c14409Schristos 		{ "mutual",		FLAG_MUTUAL },
11219c14409Schristos 		{ NULL,			FLAG_NONE }
11319c14409Schristos 	};
11419c14409Schristos 	list_t *list;
11519c14409Schristos 	char *opts;
11619c14409Schristos 	uint32_t flags;
11709484ebbSchristos 	int rv;
11819c14409Schristos 
11919c14409Schristos 	if (sec_opts == NULL)
12019c14409Schristos 		return 0;
12119c14409Schristos 
12219c14409Schristos 	if ((opts = strdup(sec_opts)) == NULL)
12319c14409Schristos 		return -1;
12419c14409Schristos 
12519c14409Schristos 	normalize_list_string(opts);
12609484ebbSchristos 	rv = saslc__list_parse(&list, opts);
12719c14409Schristos 	free(opts);
12809484ebbSchristos 	if (rv == -1)
12919c14409Schristos 		return -1;
13019c14409Schristos 	flags = saslc__list_flags(list, flag_tbl);
13119c14409Schristos 	saslc__list_free(list);
13219c14409Schristos 	return flags;
13319c14409Schristos }
13419c14409Schristos 
13519c14409Schristos /**
13619c14409Schristos  * @brief compare the mechanism flags with the security option flags
13719c14409Schristos  * passed by the user and make sure the mechanism is OK.
13819c14409Schristos  * @param mech mechanism to check.
13919c14409Schristos  * @param flags security option flags passed by saslc_sess_init().
14019c14409Schristos  * @return true if the mechanism is permitted and false if not.
14119c14409Schristos  */
14219c14409Schristos static bool
mechanism_flags_OK(const saslc__mech_list_node_t * mech,uint32_t flags)14319c14409Schristos mechanism_flags_OK(const saslc__mech_list_node_t *mech, uint32_t flags)
14419c14409Schristos {
14519c14409Schristos 	uint32_t reqflags, rejflags;
14619c14409Schristos 
14719c14409Schristos 	if (mech == NULL)
14819c14409Schristos 		return false;
14919c14409Schristos 
15019c14409Schristos 	reqflags = flags & REQ_FLAGS;
15119c14409Schristos 	rejflags = flags & REJ_FLAGS;
15219c14409Schristos 
15319c14409Schristos 	if ((mech->mech->flags & rejflags) != 0)
15419c14409Schristos 		return false;
15519c14409Schristos 
15619c14409Schristos 	if ((mech->mech->flags & reqflags) != reqflags)
15719c14409Schristos 		return false;
15819c14409Schristos 
15919c14409Schristos 	return true;
16019c14409Schristos }
16119c14409Schristos 
16219c14409Schristos /**
16319c14409Schristos  * @brief chooses first supported mechanism from the mechs list for
16419c14409Schristos  * the sasl session.
16519c14409Schristos  * @param ctx sasl context
16619c14409Schristos  * @param mechs comma or space separated list of mechanisms
167beea8b97Schristos  * e.g., "PLAIN,LOGIN" or "PLAIN LOGIN".
16819c14409Schristos  * @param sec_opts comma or space separated list of security options
169beea8b97Schristos  * @return pointer to the mech on success, NULL if none mechanism is chosen
17019c14409Schristos  *
17119c14409Schristos  * Note: this uses SASLC_PROP_SECURITY from the context dictionary.
172beea8b97Schristos  * Note: this function is not case sensitive with regard to mechs or sec_opts.
17319c14409Schristos  */
17419c14409Schristos static const saslc__mech_t *
saslc__sess_choose_mech(saslc_t * ctx,const char * mechs,const char * sec_opts)17519c14409Schristos saslc__sess_choose_mech(saslc_t *ctx, const char *mechs, const char *sec_opts)
17619c14409Schristos {
17719c14409Schristos 	list_t *list, *l;
17819c14409Schristos 	char *tmpstr;
17919c14409Schristos 	const saslc__mech_list_node_t *m;
18019c14409Schristos 	uint32_t flags;
18119c14409Schristos 	int rv;
18219c14409Schristos 
18319c14409Schristos 	rv = get_security_flags(sec_opts);
18409484ebbSchristos 	if (rv == -1)
18509484ebbSchristos 		goto nomem;
18619c14409Schristos 	flags = rv;
187231558cbSagc 
18819c14409Schristos 	sec_opts = saslc__dict_get(ctx->prop, SASLC_PROP_SECURITY);
18919c14409Schristos 	if (sec_opts != NULL) {
19019c14409Schristos 		rv = get_security_flags(sec_opts);
19109484ebbSchristos 		if (rv == -1)
19209484ebbSchristos 			goto nomem;
19319c14409Schristos 		flags |= rv;
194231558cbSagc 	}
19509484ebbSchristos 	if ((tmpstr = strdup(mechs)) == NULL)
19609484ebbSchristos 		goto nomem;
19709484ebbSchristos 
19819c14409Schristos 	normalize_list_string(tmpstr);
19909484ebbSchristos 	rv = saslc__list_parse(&list, tmpstr);
20019c14409Schristos 	free(tmpstr);
20109484ebbSchristos 	if (rv == -1)
20209484ebbSchristos 		goto nomem;
20309484ebbSchristos 
20409484ebbSchristos 	m = NULL;
20519c14409Schristos 	for (l = list; l != NULL; l = l->next) {
20619c14409Schristos 		m = saslc__mech_list_get(ctx->mechanisms, l->value);
20719c14409Schristos 		if (mechanism_flags_OK(m, flags))
20819c14409Schristos 			break;
20919c14409Schristos 	}
21019c14409Schristos 	saslc__list_free(list);
211231558cbSagc 
21209484ebbSchristos 	if (m == NULL) {
21309484ebbSchristos 		saslc__error_set(ERR(ctx), ERROR_MECH,
21409484ebbSchristos 		    "mechanism not supported");
21509484ebbSchristos 		return NULL;
21609484ebbSchristos 	}
21709484ebbSchristos 	return m->mech;
21809484ebbSchristos  nomem:
21909484ebbSchristos 	saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
22009484ebbSchristos 	return NULL;
221231558cbSagc }
222231558cbSagc 
223231558cbSagc /**
224231558cbSagc  * @brief sasl session initializaion. Function initializes session
225231558cbSagc  * property dictionary, chooses best mechanism, creates mech session.
226231558cbSagc  * @param ctx sasl context
22719c14409Schristos  * @param mechs comma or space separated list of mechanisms eg. "PLAIN,LOGIN"
22819c14409Schristos  * or "PLAIN LOGIN".  Note that this function is not case sensitive.
229231558cbSagc  * @return pointer to the sasl session on success, NULL on failure
230231558cbSagc  */
231231558cbSagc saslc_sess_t *
saslc_sess_init(saslc_t * ctx,const char * mechs,const char * sec_opts)23219c14409Schristos saslc_sess_init(saslc_t *ctx, const char *mechs, const char *sec_opts)
233231558cbSagc {
23419c14409Schristos 	saslc_sess_t *sess;
23519c14409Schristos 	const char *debug;
23619c14409Schristos 	saslc__mech_list_node_t *m;
237231558cbSagc 
23819c14409Schristos 	if ((sess = calloc(1, sizeof(*sess))) == NULL) {
239231558cbSagc 		saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
240231558cbSagc 		return NULL;
241231558cbSagc 	}
242231558cbSagc 
243231558cbSagc 	/* mechanism initialization */
24419c14409Schristos 	if ((sess->mech = saslc__sess_choose_mech(ctx, mechs, sec_opts))
24509484ebbSchristos 	    == NULL)
2466b638291Sagc 		goto error;
247231558cbSagc 
24819c14409Schristos 	/* XXX: special early check of mechanism dictionary for debug flag */
24919c14409Schristos 	m = saslc__mech_list_get(ctx->mechanisms, sess->mech->name);
25019c14409Schristos 	if (m != NULL) {
25119c14409Schristos 		debug = saslc__dict_get(m->prop, SASLC_PROP_DEBUG);
25219c14409Schristos 		if (debug != NULL)
25319c14409Schristos 			saslc_debug = saslc__parser_is_true(debug);
25419c14409Schristos 	}
25519c14409Schristos 
256231558cbSagc 	/* create mechanism session */
25719c14409Schristos 	if (sess->mech->create(sess) == -1)
2586b638291Sagc 		goto error;
259231558cbSagc 
260231558cbSagc 	/* properties */
26119c14409Schristos 	if ((sess->prop = saslc__dict_create()) == NULL) {
262231558cbSagc 		saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL);
2636b638291Sagc 		goto error;
264231558cbSagc 	}
265231558cbSagc 
266231558cbSagc 	sess->context = ctx;
26719c14409Schristos 	ctx->refcnt++;
26819c14409Schristos 
26919c14409Schristos 	saslc__msg_dbg("mechanism: %s\n", saslc_sess_getmech(sess));
270231558cbSagc 
271231558cbSagc 	return sess;
2726b638291Sagc  error:
2736b638291Sagc 	free(sess);
2746b638291Sagc 	return NULL;
275231558cbSagc }
276231558cbSagc 
277231558cbSagc /**
278231558cbSagc  * @brief ends sasl session, destroys and deallocates internal
279231558cbSagc  * resources
280231558cbSagc  * @param sess sasl session
281231558cbSagc  */
282231558cbSagc void
saslc_sess_end(saslc_sess_t * sess)283231558cbSagc saslc_sess_end(saslc_sess_t *sess)
284231558cbSagc {
28519c14409Schristos 
286231558cbSagc 	sess->mech->destroy(sess);
287231558cbSagc 	saslc__dict_destroy(sess->prop);
28819c14409Schristos 	sess->context->refcnt--;
289231558cbSagc 	free(sess);
290231558cbSagc }
291231558cbSagc 
292231558cbSagc /**
29319c14409Schristos  * @brief sets property for the session. If property already exists in
29419c14409Schristos  * the session, then previous value is replaced by the new value.
295231558cbSagc  * @param sess sasl session
296231558cbSagc  * @param name property name
29719c14409Schristos  * @param value property value (if NULL, simply remove previous key)
298231558cbSagc  * @return 0 on success, -1 on failure
299231558cbSagc  */
300231558cbSagc int
saslc_sess_setprop(saslc_sess_t * sess,const char * key,const char * value)30119c14409Schristos saslc_sess_setprop(saslc_sess_t *sess, const char *key, const char *value)
302231558cbSagc {
303231558cbSagc 
30419c14409Schristos 	/* if the key exists in the session dictionary, remove it */
30519c14409Schristos 	(void)saslc__dict_remove(sess->prop, key);
30619c14409Schristos 
30719c14409Schristos 	if (value == NULL)	/* simply remove previous value and return */
30819c14409Schristos 		return 0;
30919c14409Schristos 
31019c14409Schristos 	switch (saslc__dict_insert(sess->prop, key, value)) {
31119c14409Schristos 	case DICT_OK:
31219c14409Schristos 		return 0;
31319c14409Schristos 
314231558cbSagc 	case DICT_VALBAD:
315231558cbSagc 		saslc__error_set(ERR(sess), ERROR_BADARG, "bad value");
31619c14409Schristos 		break;
317231558cbSagc 	case DICT_KEYINVALID:
31819c14409Schristos 		saslc__error_set(ERR(sess), ERROR_BADARG, "bad key");
31919c14409Schristos 		break;
320231558cbSagc 	case DICT_NOMEM:
321231558cbSagc 		saslc__error_set(ERR(sess), ERROR_NOMEM, NULL);
322231558cbSagc 		break;
323231558cbSagc 	case DICT_KEYEXISTS:
32419c14409Schristos 	case DICT_KEYNOTFOUND:
325231558cbSagc 		assert(/*CONSTCOND*/0); /* impossible */
32619c14409Schristos 		break;
327231558cbSagc 	}
32819c14409Schristos 	return -1;
329231558cbSagc }
330231558cbSagc 
331231558cbSagc /**
332231558cbSagc  * @brief gets property from the session. Dictionaries are used
333231558cbSagc  * in following order: session dictionary, context dictionary (global
334231558cbSagc  * configuration), mechanism dicionary.
335231558cbSagc  * @param sess sasl session
33619c14409Schristos  * @param key property name
33719c14409Schristos  * @return property value on success, NULL on failure.
338231558cbSagc  */
339231558cbSagc const char *
saslc_sess_getprop(saslc_sess_t * sess,const char * key)34019c14409Schristos saslc_sess_getprop(saslc_sess_t *sess, const char *key)
341231558cbSagc {
342231558cbSagc 	const char *r;
343231558cbSagc 	saslc__mech_list_node_t *m;
344231558cbSagc 
345231558cbSagc 	/* get property from the session dictionary */
34619c14409Schristos 	if ((r = saslc__dict_get(sess->prop, key)) != NULL) {
34719c14409Schristos 		saslc__msg_dbg("%s: session dict: %s=%s", __func__, key, r);
348231558cbSagc 		return r;
34919c14409Schristos 	}
350231558cbSagc 
351231558cbSagc 	/* get property from the context dictionary */
35219c14409Schristos 	if ((r = saslc__dict_get(sess->context->prop, key)) != NULL) {
35319c14409Schristos 		saslc__msg_dbg("%s: context dict: %s=%s", __func__, key, r);
354231558cbSagc 		return r;
35519c14409Schristos 	}
356231558cbSagc 
35719c14409Schristos 	/* get property from the mechanism dictionary */
35819c14409Schristos 	if ((m = saslc__mech_list_get(sess->context->mechanisms,
35919c14409Schristos 	    sess->mech->name)) == NULL)
36019c14409Schristos 		return NULL;
361231558cbSagc 
36219c14409Schristos 	if ((r = saslc__dict_get(m->prop, key)) != NULL)
36319c14409Schristos 		saslc__msg_dbg("%s: mech %s dict: %s=%s", __func__,
36419c14409Schristos 		    saslc_sess_getmech(sess), key, r);
36519c14409Schristos 	else
36619c14409Schristos 		saslc__msg_dbg("%s: %s not found", __func__, key);
36719c14409Schristos 	return r;
36819c14409Schristos }
36919c14409Schristos 
37019c14409Schristos /**
37119c14409Schristos  * @brief set the sess->flags accordingly according to the properties.
37219c14409Schristos  * @param sess saslc session
37319c14409Schristos  */
37419c14409Schristos static uint32_t
saslc__sess_get_flags(saslc_sess_t * sess)37519c14409Schristos saslc__sess_get_flags(saslc_sess_t *sess)
37619c14409Schristos {
37719c14409Schristos 	const char *base64io;
37819c14409Schristos 	uint32_t flags;
37919c14409Schristos 
38019c14409Schristos 	/* set default flags */
38119c14409Schristos 	flags = SASLC_FLAGS_DEFAULT;
38219c14409Schristos 
38319c14409Schristos 	base64io = saslc_sess_getprop(sess, SASLC_PROP_BASE64IO);
38419c14409Schristos 	if (base64io != NULL) {
38519c14409Schristos 		if (saslc__parser_is_true(base64io))
38619c14409Schristos 			flags |= SASLC_FLAGS_BASE64;
38719c14409Schristos 		else
38819c14409Schristos 			flags &= ~SASLC_FLAGS_BASE64;
38919c14409Schristos 	}
39019c14409Schristos 	return flags;
391231558cbSagc }
392231558cbSagc 
393231558cbSagc /**
3946b638291Sagc  * @brief does one step of the sasl authentication, input data
395231558cbSagc  * and its lenght are stored in in and inlen, output is stored in out and
39619c14409Schristos  * outlen. This function is a wrapper for mechanism step functions.
397231558cbSagc  * Additionaly it checks if session is not already authorized and handles
398231558cbSagc  * steps mech_sess structure.
399231558cbSagc  * @param sess saslc session
400231558cbSagc  * @param in input data
401231558cbSagc  * @param inlen input data length
402231558cbSagc  * @param out output data
403231558cbSagc  * @param outlen output data length
404231558cbSagc  * @return MECH_OK - on success, no more steps are needed
405231558cbSagc  * MECH_ERROR - on error, additionaly errno in sess is setup
406231558cbSagc  * MECH_STEP - more steps are needed
407231558cbSagc  */
408231558cbSagc int
saslc_sess_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)40919c14409Schristos saslc_sess_cont(saslc_sess_t *sess, const void *in, size_t inlen,
41019c14409Schristos     void **out, size_t *outlen)
411231558cbSagc {
41219c14409Schristos 	saslc__mech_sess_t *ms;
41319c14409Schristos 	const char *debug;
41419c14409Schristos 	void *dec;
41519c14409Schristos 	int rv;
416231558cbSagc 
41719c14409Schristos 	ms = sess->mech_sess;
41819c14409Schristos 	if (ms->status == STATUS_AUTHENTICATED) {
419231558cbSagc 		saslc__error_set(ERR(sess), ERROR_MECH,
420231558cbSagc 		    "session authenticated");
421231558cbSagc 		return MECH_ERROR;
422231558cbSagc 	}
42319c14409Schristos 	if (ms->step == 0) {
42419c14409Schristos 		sess->flags = saslc__sess_get_flags(sess);
425231558cbSagc 
42619c14409Schristos 		/* XXX: final check for any session debug flag setting */
42719c14409Schristos 		debug = saslc__dict_get(sess->prop, SASLC_PROP_DEBUG);
42819c14409Schristos 		if (debug != NULL)
42919c14409Schristos 			saslc_debug = saslc__parser_is_true(debug);
43019c14409Schristos 	}
431231558cbSagc 
43216e81cb9Schristos 	saslc__msg_dbg("%s: encoded: inlen=%zu in='%s'", __func__, inlen,
43319c14409Schristos 	    in ? (const char *)in : "<null>");
43419c14409Schristos 	if (inlen == 0 || (sess->flags & SASLC_FLAGS_BASE64_IN) == 0)
43519c14409Schristos 		dec = NULL;
43619c14409Schristos 	else {
43719c14409Schristos 		if (saslc__crypto_decode_base64(in, inlen, &dec, &inlen)
43819c14409Schristos 		    == -1) {
43919c14409Schristos 			saslc__error_set(ERR(sess), ERROR_MECH,
44019c14409Schristos 			    "base64 decode failed");
44119c14409Schristos 			return MECH_ERROR;
44219c14409Schristos 		}
44319c14409Schristos 		in = dec;
44419c14409Schristos 	}
44516e81cb9Schristos 	saslc__msg_dbg("%s: decoded: inlen=%zu in='%s'", __func__, inlen,
44619c14409Schristos 	    in ? (const char *)in : "<null>");
44719c14409Schristos 	rv = sess->mech->cont(sess, in, inlen, out, outlen);
44819c14409Schristos 	if (dec != NULL)
44919c14409Schristos 		free(dec);
45019c14409Schristos 	if (rv == MECH_ERROR)
45119c14409Schristos 		return MECH_ERROR;
452231558cbSagc 
45319c14409Schristos 	saslc__msg_dbg("%s: out='%s'", __func__,
45419c14409Schristos 	    *outlen ? (char *)*out : "<null>");
45519c14409Schristos 	if (*outlen == 0)
45619c14409Schristos 		*out = NULL;	/* XXX: unnecessary? */
45719c14409Schristos 	else if ((sess->flags & SASLC_FLAGS_BASE64_OUT) != 0) {
45819c14409Schristos 		char *enc;
45919c14409Schristos 		size_t enclen;
4606b638291Sagc 
46119c14409Schristos 		if (saslc__crypto_encode_base64(*out, *outlen, &enc, &enclen)
46219c14409Schristos 		    == -1) {
46319c14409Schristos 			free(*out);
46419c14409Schristos 			return MECH_ERROR;
46519c14409Schristos 		}
46619c14409Schristos 		free(*out);
46719c14409Schristos 		*out = enc;
46819c14409Schristos 		*outlen = enclen;
46919c14409Schristos 	}
47019c14409Schristos 	if (rv == MECH_OK)
47119c14409Schristos 		ms->status = STATUS_AUTHENTICATED;
47219c14409Schristos 
47319c14409Schristos 	ms->step++;
47419c14409Schristos 	return rv;
47519c14409Schristos }
47619c14409Schristos 
47719c14409Schristos /**
47819c14409Schristos  * @brief copies input data to an allocated buffer.  The caller is
47919c14409Schristos  * responsible for freeing the buffer.
48019c14409Schristos  * @param sess sasl session
48119c14409Schristos  * @param xxcode codec to encode or decode one block of data
48219c14409Schristos  * @param in input data
48319c14409Schristos  * @param inlen input data length
48419c14409Schristos  * @param out output data
48519c14409Schristos  * @param outlen output data length
48619c14409Schristos  * @return number of bytes copied on success, -1 on failure
48719c14409Schristos  */
48819c14409Schristos static ssize_t
saslc__sess_copyout(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)48919c14409Schristos saslc__sess_copyout(saslc_sess_t *sess, const void *in, size_t inlen,
49019c14409Schristos     void **out, size_t *outlen)
49119c14409Schristos {
49219c14409Schristos 
49319c14409Schristos 	*out = malloc(inlen);
49419c14409Schristos 	if (*out == NULL) {
49519c14409Schristos 		*outlen = 0;
49619c14409Schristos 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
49719c14409Schristos 		return -1;
49819c14409Schristos 	}
49919c14409Schristos 	*outlen = inlen;
50019c14409Schristos 	memcpy(*out, in, inlen);
50119c14409Schristos 	return inlen;
50219c14409Schristos }
50319c14409Schristos 
50419c14409Schristos /**
50519c14409Schristos  * @brief encodes or decode data using method established during the
50619c14409Schristos  * authentication. Input data is stored in in and inlen and output
50719c14409Schristos  * data is stored in out and outlen.  The caller is responsible for
50819c14409Schristos  * freeing the output buffer.
50919c14409Schristos  * @param sess sasl session
51019c14409Schristos  * @param xxcode codec to encode or decode one block of data
51119c14409Schristos  * @param in input data
51219c14409Schristos  * @param inlen input data length
51319c14409Schristos  * @param out output data
51419c14409Schristos  * @param outlen output data length
51519c14409Schristos  * @return number of bytes consumed on success, 0 if insufficient data
51619c14409Schristos  * to process, -1 on failure
51719c14409Schristos  *
51819c14409Schristos  * 'xxcode' encodes or decodes a single block of data and stores the
51919c14409Schristos  * resulting block and its length in 'out' and 'outlen', respectively.
52019c14409Schristos  * It should return the number of bytes it digested or -1 on error.
52119c14409Schristos  * If it was unable to process a complete block, it should return zero
52219c14409Schristos  * and remember the partial block internally.  If it is called with
52319c14409Schristos  * 'inlen' = 0, it should flush out any remaining partial block data
52419c14409Schristos  * and return the number of stored bytes it flushed or zero if there
52519c14409Schristos  * were none (relevant for the encoder only).
52619c14409Schristos  */
52719c14409Schristos static ssize_t
saslc__sess_xxcode(saslc_sess_t * sess,saslc__mech_xxcode_t xxcode,const void * in,size_t inlen,void ** out,size_t * outlen)52819c14409Schristos saslc__sess_xxcode(saslc_sess_t *sess, saslc__mech_xxcode_t xxcode,
52919c14409Schristos     const void *in, size_t inlen, void **out, size_t *outlen)
53019c14409Schristos {
53119c14409Schristos 	saslc__mech_sess_t *ms;
53219c14409Schristos 	unsigned char *p;
53319c14409Schristos 	void *buf, *pkt;
53419c14409Schristos 	size_t buflen, pktlen;
53519c14409Schristos 	ssize_t len, ate;
53619c14409Schristos 
53719c14409Schristos 	ms = sess->mech_sess;
53819c14409Schristos 
53919c14409Schristos 	if (xxcode == NULL) {
54019c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
54119c14409Schristos 		    "security layer is not supported by mechanism");
54219c14409Schristos 		return -1;
54319c14409Schristos 	}
54419c14409Schristos 	if (ms->status != STATUS_AUTHENTICATED) {
54519c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
54619c14409Schristos 		    "session is not authenticated");
54719c14409Schristos 		return -1;
54819c14409Schristos 	}
54919c14409Schristos 
55019c14409Schristos 	if (ms->qop == QOP_NONE)
55119c14409Schristos 		return saslc__sess_copyout(sess, in, inlen, out, outlen);
55219c14409Schristos 
55319c14409Schristos 	p = NULL;
55419c14409Schristos 	buf = NULL;
55519c14409Schristos 	buflen = 0;
55619c14409Schristos 	ate = 0;
55719c14409Schristos 	do {
55819c14409Schristos 		len = xxcode(sess, in, inlen, &pkt, &pktlen);
559*029f8c21Sshm 		if (len == -1) {
560*029f8c21Sshm 			free(buf);
56119c14409Schristos 			return -1;
562*029f8c21Sshm 		}
56319c14409Schristos 
56419c14409Schristos 		ate += len;
56519c14409Schristos 		in = (const char *)in + len;
56619c14409Schristos 		if (inlen < (size_t)len)
56719c14409Schristos 			inlen = 0;
56819c14409Schristos 		else
56919c14409Schristos 			inlen -= len;
57019c14409Schristos 
57119c14409Schristos 		if (pktlen == 0)	/* nothing processed, done */
57219c14409Schristos 			continue;
57319c14409Schristos 
57419c14409Schristos 		buflen += pktlen;
575*029f8c21Sshm 		p = buf;
57619c14409Schristos 		if ((buf = realloc(buf, buflen)) == NULL) {
577*029f8c21Sshm 			/* we should free memory if realloc(2) failed */
578*029f8c21Sshm 			free(p);
57919c14409Schristos 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
58019c14409Schristos 			return -1;
58119c14409Schristos 		}
58219c14409Schristos 		p = buf;
58319c14409Schristos 		p += buflen - pktlen;
58419c14409Schristos 		memcpy(p, pkt, pktlen);
58519c14409Schristos 		free(pkt);
58619c14409Schristos 	} while (inlen > 0);
58719c14409Schristos 
58819c14409Schristos 	*out = buf;
58919c14409Schristos 	*outlen = buflen;
59019c14409Schristos 	return ate;
591231558cbSagc }
592231558cbSagc 
593231558cbSagc /**
594231558cbSagc  * @brief encodes data using method established during the
59519c14409Schristos  * authentication. Input data is stored in in and inlen and output
59619c14409Schristos  * data is stored in out and outlen.  The caller is responsible for
59719c14409Schristos  * freeing the output buffer.
598231558cbSagc  * @param sess sasl session
599231558cbSagc  * @param in input data
600231558cbSagc  * @param inlen input data length
601231558cbSagc  * @param out output data
602231558cbSagc  * @param outlen output data length
603231558cbSagc  * @return 0 on success, -1 on failure
60419c14409Schristos  *
60519c14409Schristos  * This will output a sequence of full blocks.  When all data has been
60619c14409Schristos  * processed, this should be called one more time with inlen = 0 to
60719c14409Schristos  * flush any partial block left in the encoder.
608231558cbSagc  */
60919c14409Schristos ssize_t
saslc_sess_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)610231558cbSagc saslc_sess_encode(saslc_sess_t *sess, const void *in, size_t inlen,
611231558cbSagc 	void **out, size_t *outlen)
612231558cbSagc {
613231558cbSagc 
61419c14409Schristos 	return saslc__sess_xxcode(sess, sess->mech->encode,
61519c14409Schristos 	    in, inlen, out, outlen);
616231558cbSagc }
617231558cbSagc 
618231558cbSagc /**
619231558cbSagc  * @brief decodes data using method established during the
62019c14409Schristos  * authentication. Input data is stored in in and inlen and output
62119c14409Schristos  * data is stored in out and outlen.  The caller is responsible for
62219c14409Schristos  * freeing the output buffer.
623231558cbSagc  * @param sess sasl session
624231558cbSagc  * @param in input data
625231558cbSagc  * @param inlen input data length
626231558cbSagc  * @param out output data
627231558cbSagc  * @param outlen output data length
628231558cbSagc  * @return 0 on success, -1 on failure
629231558cbSagc  */
63019c14409Schristos ssize_t
saslc_sess_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)631231558cbSagc saslc_sess_decode(saslc_sess_t *sess, const void *in, size_t inlen,
632231558cbSagc     void **out, size_t *outlen)
633231558cbSagc {
634231558cbSagc 
63519c14409Schristos 	return saslc__sess_xxcode(sess, sess->mech->decode,
63619c14409Schristos 	    in, inlen, out, outlen);
637231558cbSagc }
638231558cbSagc 
639231558cbSagc /**
64019c14409Schristos  * @brief gets string message of the error.
641231558cbSagc  * @param sess sasl session
642231558cbSagc  * @return pointer to the error message
643231558cbSagc  */
644231558cbSagc const char *
saslc_sess_strerror(saslc_sess_t * sess)645231558cbSagc saslc_sess_strerror(saslc_sess_t *sess)
646231558cbSagc {
64719c14409Schristos 
648231558cbSagc 	return saslc__error_get_strerror(ERR(sess));
649231558cbSagc }
650231558cbSagc 
651231558cbSagc /**
65219c14409Schristos  * @brief gets name of the mechanism used in the sasl session
653231558cbSagc  * @param sess sasl session
654231558cbSagc  * @return pointer to the mechanism name
655231558cbSagc  */
656231558cbSagc const char *
saslc_sess_getmech(saslc_sess_t * sess)65719c14409Schristos saslc_sess_getmech(saslc_sess_t *sess)
658231558cbSagc {
65919c14409Schristos 
660231558cbSagc 	return sess->mech->name;
661231558cbSagc }
662