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