xref: /minix/crypto/external/bsd/libsaslc/dist/src/xsess.c (revision 0a6a1f1d)
1 /* $NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $ */
2 
3 /*
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Mateusz Kocielski.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $");
40 
41 #include <assert.h>
42 #include <saslc.h>
43 #include <stdio.h>
44 #include <string.h>
45 
46 #include "crypto.h"
47 #include "dict.h"
48 #include "error.h"
49 #include "list.h"
50 #include "msg.h"
51 #include "mech.h"
52 #include "parser.h"
53 #include "saslc_private.h"
54 
55 /*
56  * TODO:
57  *
58  * 1) Add hooks to allow saslc_sess_encode() and saslc_sess_decode()
59  * to output and input, respectively, base64 encoded data much like
60  * what sess_saslc_cont() does according to the SASLC_FLAGS_BASE64_*
61  * flags.  For saslc_sess_decode() it seems it would be easiest to do
62  * this in saslc__buffer32_fetch() pushing any extra buffering into
63  * the BIO_* routines, but I haven't thought this through carefully
64  * yet.
65  */
66 
67 static inline char *
skip_WS(char * p)68 skip_WS(char *p)
69 {
70 
71 	while (*p == ' ' || *p == '\t')
72 		p++;
73 	return p;
74 }
75 
76 /**
77  * @brief convert a comma and/or space delimited list into a comma
78  * delimited list of the form:
79  *   ( *LWS element *( *LWS "," *LWS element ))
80  * @param str string to convert.
81  */
82 static void
normalize_list_string(char * opts)83 normalize_list_string(char *opts)
84 {
85 	char *p;
86 
87 	p = opts;
88 	while (p != NULL) {
89 		p = strchr(p, ' ');
90 		if (p == NULL)
91 			break;
92 		if (p > opts && p[-1] != ',')
93 			*p++ = ',';
94 		p = skip_WS(p + 1);
95 	}
96 }
97 
98 /**
99  * @brief get the security flags from a comma delimited string.
100  * @param sec_opt the security option comman delimited string.
101  * @return the flags on success, or -1 on error (no memory).
102  */
103 static int
get_security_flags(const char * sec_opts)104 get_security_flags(const char *sec_opts)
105 {
106 	static const named_flag_t flag_tbl[] = {
107 		{ "noanonymous",	FLAG_ANONYMOUS },
108 		{ "nodictionary",	FLAG_DICTIONARY },
109 		{ "noplaintext",	FLAG_PLAINTEXT },
110 		{ "noactive",		FLAG_ACTIVE },
111 		{ "mutual",		FLAG_MUTUAL },
112 		{ NULL,			FLAG_NONE }
113 	};
114 	list_t *list;
115 	char *opts;
116 	uint32_t flags;
117 	int rv;
118 
119 	if (sec_opts == NULL)
120 		return 0;
121 
122 	if ((opts = strdup(sec_opts)) == NULL)
123 		return -1;
124 
125 	normalize_list_string(opts);
126 	rv = saslc__list_parse(&list, opts);
127 	free(opts);
128 	if (rv == -1)
129 		return -1;
130 	flags = saslc__list_flags(list, flag_tbl);
131 	saslc__list_free(list);
132 	return flags;
133 }
134 
135 /**
136  * @brief compare the mechanism flags with the security option flags
137  * passed by the user and make sure the mechanism is OK.
138  * @param mech mechanism to check.
139  * @param flags security option flags passed by saslc_sess_init().
140  * @return true if the mechanism is permitted and false if not.
141  */
142 static bool
mechanism_flags_OK(const saslc__mech_list_node_t * mech,uint32_t flags)143 mechanism_flags_OK(const saslc__mech_list_node_t *mech, uint32_t flags)
144 {
145 	uint32_t reqflags, rejflags;
146 
147 	if (mech == NULL)
148 		return false;
149 
150 	reqflags = flags & REQ_FLAGS;
151 	rejflags = flags & REJ_FLAGS;
152 
153 	if ((mech->mech->flags & rejflags) != 0)
154 		return false;
155 
156 	if ((mech->mech->flags & reqflags) != reqflags)
157 		return false;
158 
159 	return true;
160 }
161 
162 /**
163  * @brief chooses first supported mechanism from the mechs list for
164  * the sasl session.
165  * @param ctx sasl context
166  * @param mechs comma or space separated list of mechanisms
167  * e.g., "PLAIN,LOGIN" or "PLAIN LOGIN".
168  * @param sec_opts comma or space separated list of security options
169  * @return pointer to the mech on success, NULL if none mechanism is chosen
170  *
171  * Note: this uses SASLC_PROP_SECURITY from the context dictionary.
172  * Note: this function is not case sensitive with regard to mechs or sec_opts.
173  */
174 static const saslc__mech_t *
saslc__sess_choose_mech(saslc_t * ctx,const char * mechs,const char * sec_opts)175 saslc__sess_choose_mech(saslc_t *ctx, const char *mechs, const char *sec_opts)
176 {
177 	list_t *list, *l;
178 	char *tmpstr;
179 	const saslc__mech_list_node_t *m;
180 	uint32_t flags;
181 	int rv;
182 
183 	rv = get_security_flags(sec_opts);
184 	if (rv == -1)
185 		goto nomem;
186 	flags = rv;
187 
188 	sec_opts = saslc__dict_get(ctx->prop, SASLC_PROP_SECURITY);
189 	if (sec_opts != NULL) {
190 		rv = get_security_flags(sec_opts);
191 		if (rv == -1)
192 			goto nomem;
193 		flags |= rv;
194 	}
195 	if ((tmpstr = strdup(mechs)) == NULL)
196 		goto nomem;
197 
198 	normalize_list_string(tmpstr);
199 	rv = saslc__list_parse(&list, tmpstr);
200 	free(tmpstr);
201 	if (rv == -1)
202 		goto nomem;
203 
204 	m = NULL;
205 	for (l = list; l != NULL; l = l->next) {
206 		m = saslc__mech_list_get(ctx->mechanisms, l->value);
207 		if (mechanism_flags_OK(m, flags))
208 			break;
209 	}
210 	saslc__list_free(list);
211 
212 	if (m == NULL) {
213 		saslc__error_set(ERR(ctx), ERROR_MECH,
214 		    "mechanism not supported");
215 		return NULL;
216 	}
217 	return m->mech;
218  nomem:
219 	saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
220 	return NULL;
221 }
222 
223 /**
224  * @brief sasl session initializaion. Function initializes session
225  * property dictionary, chooses best mechanism, creates mech session.
226  * @param ctx sasl context
227  * @param mechs comma or space separated list of mechanisms eg. "PLAIN,LOGIN"
228  * or "PLAIN LOGIN".  Note that this function is not case sensitive.
229  * @return pointer to the sasl session on success, NULL on failure
230  */
231 saslc_sess_t *
saslc_sess_init(saslc_t * ctx,const char * mechs,const char * sec_opts)232 saslc_sess_init(saslc_t *ctx, const char *mechs, const char *sec_opts)
233 {
234 	saslc_sess_t *sess;
235 	const char *debug;
236 	saslc__mech_list_node_t *m;
237 
238 	if ((sess = calloc(1, sizeof(*sess))) == NULL) {
239 		saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
240 		return NULL;
241 	}
242 
243 	/* mechanism initialization */
244 	if ((sess->mech = saslc__sess_choose_mech(ctx, mechs, sec_opts))
245 	    == NULL)
246 		goto error;
247 
248 	/* XXX: special early check of mechanism dictionary for debug flag */
249 	m = saslc__mech_list_get(ctx->mechanisms, sess->mech->name);
250 	if (m != NULL) {
251 		debug = saslc__dict_get(m->prop, SASLC_PROP_DEBUG);
252 		if (debug != NULL)
253 			saslc_debug = saslc__parser_is_true(debug);
254 	}
255 
256 	/* create mechanism session */
257 	if (sess->mech->create(sess) == -1)
258 		goto error;
259 
260 	/* properties */
261 	if ((sess->prop = saslc__dict_create()) == NULL) {
262 		saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL);
263 		goto error;
264 	}
265 
266 	sess->context = ctx;
267 	ctx->refcnt++;
268 
269 	saslc__msg_dbg("mechanism: %s\n", saslc_sess_getmech(sess));
270 
271 	return sess;
272  error:
273 	free(sess);
274 	return NULL;
275 }
276 
277 /**
278  * @brief ends sasl session, destroys and deallocates internal
279  * resources
280  * @param sess sasl session
281  */
282 void
saslc_sess_end(saslc_sess_t * sess)283 saslc_sess_end(saslc_sess_t *sess)
284 {
285 
286 	sess->mech->destroy(sess);
287 	saslc__dict_destroy(sess->prop);
288 	sess->context->refcnt--;
289 	free(sess);
290 }
291 
292 /**
293  * @brief sets property for the session. If property already exists in
294  * the session, then previous value is replaced by the new value.
295  * @param sess sasl session
296  * @param name property name
297  * @param value property value (if NULL, simply remove previous key)
298  * @return 0 on success, -1 on failure
299  */
300 int
saslc_sess_setprop(saslc_sess_t * sess,const char * key,const char * value)301 saslc_sess_setprop(saslc_sess_t *sess, const char *key, const char *value)
302 {
303 
304 	/* if the key exists in the session dictionary, remove it */
305 	(void)saslc__dict_remove(sess->prop, key);
306 
307 	if (value == NULL)	/* simply remove previous value and return */
308 		return 0;
309 
310 	switch (saslc__dict_insert(sess->prop, key, value)) {
311 	case DICT_OK:
312 		return 0;
313 
314 	case DICT_VALBAD:
315 		saslc__error_set(ERR(sess), ERROR_BADARG, "bad value");
316 		break;
317 	case DICT_KEYINVALID:
318 		saslc__error_set(ERR(sess), ERROR_BADARG, "bad key");
319 		break;
320 	case DICT_NOMEM:
321 		saslc__error_set(ERR(sess), ERROR_NOMEM, NULL);
322 		break;
323 	case DICT_KEYEXISTS:
324 	case DICT_KEYNOTFOUND:
325 		assert(/*CONSTCOND*/0); /* impossible */
326 		break;
327 	}
328 	return -1;
329 }
330 
331 /**
332  * @brief gets property from the session. Dictionaries are used
333  * in following order: session dictionary, context dictionary (global
334  * configuration), mechanism dicionary.
335  * @param sess sasl session
336  * @param key property name
337  * @return property value on success, NULL on failure.
338  */
339 const char *
saslc_sess_getprop(saslc_sess_t * sess,const char * key)340 saslc_sess_getprop(saslc_sess_t *sess, const char *key)
341 {
342 	const char *r;
343 	saslc__mech_list_node_t *m;
344 
345 	/* get property from the session dictionary */
346 	if ((r = saslc__dict_get(sess->prop, key)) != NULL) {
347 		saslc__msg_dbg("%s: session dict: %s=%s", __func__, key, r);
348 		return r;
349 	}
350 
351 	/* get property from the context dictionary */
352 	if ((r = saslc__dict_get(sess->context->prop, key)) != NULL) {
353 		saslc__msg_dbg("%s: context dict: %s=%s", __func__, key, r);
354 		return r;
355 	}
356 
357 	/* get property from the mechanism dictionary */
358 	if ((m = saslc__mech_list_get(sess->context->mechanisms,
359 	    sess->mech->name)) == NULL)
360 		return NULL;
361 
362 	if ((r = saslc__dict_get(m->prop, key)) != NULL)
363 		saslc__msg_dbg("%s: mech %s dict: %s=%s", __func__,
364 		    saslc_sess_getmech(sess), key, r);
365 	else
366 		saslc__msg_dbg("%s: %s not found", __func__, key);
367 	return r;
368 }
369 
370 /**
371  * @brief set the sess->flags accordingly according to the properties.
372  * @param sess saslc session
373  */
374 static uint32_t
saslc__sess_get_flags(saslc_sess_t * sess)375 saslc__sess_get_flags(saslc_sess_t *sess)
376 {
377 	const char *base64io;
378 	uint32_t flags;
379 
380 	/* set default flags */
381 	flags = SASLC_FLAGS_DEFAULT;
382 
383 	base64io = saslc_sess_getprop(sess, SASLC_PROP_BASE64IO);
384 	if (base64io != NULL) {
385 		if (saslc__parser_is_true(base64io))
386 			flags |= SASLC_FLAGS_BASE64;
387 		else
388 			flags &= ~SASLC_FLAGS_BASE64;
389 	}
390 	return flags;
391 }
392 
393 /**
394  * @brief does one step of the sasl authentication, input data
395  * and its lenght are stored in in and inlen, output is stored in out and
396  * outlen. This function is a wrapper for mechanism step functions.
397  * Additionaly it checks if session is not already authorized and handles
398  * steps mech_sess structure.
399  * @param sess saslc session
400  * @param in input data
401  * @param inlen input data length
402  * @param out output data
403  * @param outlen output data length
404  * @return MECH_OK - on success, no more steps are needed
405  * MECH_ERROR - on error, additionaly errno in sess is setup
406  * MECH_STEP - more steps are needed
407  */
408 int
saslc_sess_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)409 saslc_sess_cont(saslc_sess_t *sess, const void *in, size_t inlen,
410     void **out, size_t *outlen)
411 {
412 	saslc__mech_sess_t *ms;
413 	const char *debug;
414 	void *dec;
415 	int rv;
416 
417 	ms = sess->mech_sess;
418 	if (ms->status == STATUS_AUTHENTICATED) {
419 		saslc__error_set(ERR(sess), ERROR_MECH,
420 		    "session authenticated");
421 		return MECH_ERROR;
422 	}
423 	if (ms->step == 0) {
424 		sess->flags = saslc__sess_get_flags(sess);
425 
426 		/* XXX: final check for any session debug flag setting */
427 		debug = saslc__dict_get(sess->prop, SASLC_PROP_DEBUG);
428 		if (debug != NULL)
429 			saslc_debug = saslc__parser_is_true(debug);
430 	}
431 
432 	saslc__msg_dbg("%s: encoded: inlen=%zu in='%s'", __func__, inlen,
433 	    in ? (const char *)in : "<null>");
434 	if (inlen == 0 || (sess->flags & SASLC_FLAGS_BASE64_IN) == 0)
435 		dec = NULL;
436 	else {
437 		if (saslc__crypto_decode_base64(in, inlen, &dec, &inlen)
438 		    == -1) {
439 			saslc__error_set(ERR(sess), ERROR_MECH,
440 			    "base64 decode failed");
441 			return MECH_ERROR;
442 		}
443 		in = dec;
444 	}
445 	saslc__msg_dbg("%s: decoded: inlen=%zu in='%s'", __func__, inlen,
446 	    in ? (const char *)in : "<null>");
447 	rv = sess->mech->cont(sess, in, inlen, out, outlen);
448 	if (dec != NULL)
449 		free(dec);
450 	if (rv == MECH_ERROR)
451 		return MECH_ERROR;
452 
453 	saslc__msg_dbg("%s: out='%s'", __func__,
454 	    *outlen ? (char *)*out : "<null>");
455 	if (*outlen == 0)
456 		*out = NULL;	/* XXX: unnecessary? */
457 	else if ((sess->flags & SASLC_FLAGS_BASE64_OUT) != 0) {
458 		char *enc;
459 		size_t enclen;
460 
461 		if (saslc__crypto_encode_base64(*out, *outlen, &enc, &enclen)
462 		    == -1) {
463 			free(*out);
464 			return MECH_ERROR;
465 		}
466 		free(*out);
467 		*out = enc;
468 		*outlen = enclen;
469 	}
470 	if (rv == MECH_OK)
471 		ms->status = STATUS_AUTHENTICATED;
472 
473 	ms->step++;
474 	return rv;
475 }
476 
477 /**
478  * @brief copies input data to an allocated buffer.  The caller is
479  * responsible for freeing the buffer.
480  * @param sess sasl session
481  * @param xxcode codec to encode or decode one block of data
482  * @param in input data
483  * @param inlen input data length
484  * @param out output data
485  * @param outlen output data length
486  * @return number of bytes copied on success, -1 on failure
487  */
488 static ssize_t
saslc__sess_copyout(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)489 saslc__sess_copyout(saslc_sess_t *sess, const void *in, size_t inlen,
490     void **out, size_t *outlen)
491 {
492 
493 	*out = malloc(inlen);
494 	if (*out == NULL) {
495 		*outlen = 0;
496 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
497 		return -1;
498 	}
499 	*outlen = inlen;
500 	memcpy(*out, in, inlen);
501 	return inlen;
502 }
503 
504 /**
505  * @brief encodes or decode data using method established during the
506  * authentication. Input data is stored in in and inlen and output
507  * data is stored in out and outlen.  The caller is responsible for
508  * freeing the output buffer.
509  * @param sess sasl session
510  * @param xxcode codec to encode or decode one block of data
511  * @param in input data
512  * @param inlen input data length
513  * @param out output data
514  * @param outlen output data length
515  * @return number of bytes consumed on success, 0 if insufficient data
516  * to process, -1 on failure
517  *
518  * 'xxcode' encodes or decodes a single block of data and stores the
519  * resulting block and its length in 'out' and 'outlen', respectively.
520  * It should return the number of bytes it digested or -1 on error.
521  * If it was unable to process a complete block, it should return zero
522  * and remember the partial block internally.  If it is called with
523  * 'inlen' = 0, it should flush out any remaining partial block data
524  * and return the number of stored bytes it flushed or zero if there
525  * were none (relevant for the encoder only).
526  */
527 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)528 saslc__sess_xxcode(saslc_sess_t *sess, saslc__mech_xxcode_t xxcode,
529     const void *in, size_t inlen, void **out, size_t *outlen)
530 {
531 	saslc__mech_sess_t *ms;
532 	unsigned char *p;
533 	void *buf, *pkt;
534 	size_t buflen, pktlen;
535 	ssize_t len, ate;
536 
537 	ms = sess->mech_sess;
538 
539 	if (xxcode == NULL) {
540 		saslc__error_set(ERR(sess), ERROR_MECH,
541 		    "security layer is not supported by mechanism");
542 		return -1;
543 	}
544 	if (ms->status != STATUS_AUTHENTICATED) {
545 		saslc__error_set(ERR(sess), ERROR_MECH,
546 		    "session is not authenticated");
547 		return -1;
548 	}
549 
550 	if (ms->qop == QOP_NONE)
551 		return saslc__sess_copyout(sess, in, inlen, out, outlen);
552 
553 	p = NULL;
554 	buf = NULL;
555 	buflen = 0;
556 	ate = 0;
557 	do {
558 		len = xxcode(sess, in, inlen, &pkt, &pktlen);
559 		if (len == -1) {
560 			free(buf);
561 			return -1;
562 		}
563 
564 		ate += len;
565 		in = (const char *)in + len;
566 		if (inlen < (size_t)len)
567 			inlen = 0;
568 		else
569 			inlen -= len;
570 
571 		if (pktlen == 0)	/* nothing processed, done */
572 			continue;
573 
574 		buflen += pktlen;
575 		p = buf;
576 		if ((buf = realloc(buf, buflen)) == NULL) {
577 			/* we should free memory if realloc(2) failed */
578 			free(p);
579 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
580 			return -1;
581 		}
582 		p = buf;
583 		p += buflen - pktlen;
584 		memcpy(p, pkt, pktlen);
585 		free(pkt);
586 	} while (inlen > 0);
587 
588 	*out = buf;
589 	*outlen = buflen;
590 	return ate;
591 }
592 
593 /**
594  * @brief encodes data using method established during the
595  * authentication. Input data is stored in in and inlen and output
596  * data is stored in out and outlen.  The caller is responsible for
597  * freeing the output buffer.
598  * @param sess sasl session
599  * @param in input data
600  * @param inlen input data length
601  * @param out output data
602  * @param outlen output data length
603  * @return 0 on success, -1 on failure
604  *
605  * This will output a sequence of full blocks.  When all data has been
606  * processed, this should be called one more time with inlen = 0 to
607  * flush any partial block left in the encoder.
608  */
609 ssize_t
saslc_sess_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)610 saslc_sess_encode(saslc_sess_t *sess, const void *in, size_t inlen,
611 	void **out, size_t *outlen)
612 {
613 
614 	return saslc__sess_xxcode(sess, sess->mech->encode,
615 	    in, inlen, out, outlen);
616 }
617 
618 /**
619  * @brief decodes data using method established during the
620  * authentication. Input data is stored in in and inlen and output
621  * data is stored in out and outlen.  The caller is responsible for
622  * freeing the output buffer.
623  * @param sess sasl session
624  * @param in input data
625  * @param inlen input data length
626  * @param out output data
627  * @param outlen output data length
628  * @return 0 on success, -1 on failure
629  */
630 ssize_t
saslc_sess_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)631 saslc_sess_decode(saslc_sess_t *sess, const void *in, size_t inlen,
632     void **out, size_t *outlen)
633 {
634 
635 	return saslc__sess_xxcode(sess, sess->mech->decode,
636 	    in, inlen, out, outlen);
637 }
638 
639 /**
640  * @brief gets string message of the error.
641  * @param sess sasl session
642  * @return pointer to the error message
643  */
644 const char *
saslc_sess_strerror(saslc_sess_t * sess)645 saslc_sess_strerror(saslc_sess_t *sess)
646 {
647 
648 	return saslc__error_get_strerror(ERR(sess));
649 }
650 
651 /**
652  * @brief gets name of the mechanism used in the sasl session
653  * @param sess sasl session
654  * @return pointer to the mechanism name
655  */
656 const char *
saslc_sess_getmech(saslc_sess_t * sess)657 saslc_sess_getmech(saslc_sess_t *sess)
658 {
659 
660 	return sess->mech->name;
661 }
662