minix/crypto/external/bsd/libsaslc/dist/src/xsess.c
Lionel Sambuc 0a6a1f1d05 NetBSD re-synchronization of the source tree
This brings our tree to NetBSD 7.0, as found on -current on the
10-10-2015.

This updates:
 - LLVM to 3.6.1
 - GCC to GCC 5.1
 - Replace minix/commands/zdump with usr.bin/zdump
 - external/bsd/libelf has moved to /external/bsd/elftoolchain/
 - Import ctwm
 - Drop sprintf from libminc

Change-Id: I149836ac18e9326be9353958bab9b266efb056f0
2016-01-13 20:32:14 +01:00

662 lines
18 KiB
C

/* $NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $ */
/*
* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Mateusz Kocielski.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: xsess.c,v 1.8 2015/08/08 10:38:35 shm Exp $");
#include <assert.h>
#include <saslc.h>
#include <stdio.h>
#include <string.h>
#include "crypto.h"
#include "dict.h"
#include "error.h"
#include "list.h"
#include "msg.h"
#include "mech.h"
#include "parser.h"
#include "saslc_private.h"
/*
* TODO:
*
* 1) Add hooks to allow saslc_sess_encode() and saslc_sess_decode()
* to output and input, respectively, base64 encoded data much like
* what sess_saslc_cont() does according to the SASLC_FLAGS_BASE64_*
* flags. For saslc_sess_decode() it seems it would be easiest to do
* this in saslc__buffer32_fetch() pushing any extra buffering into
* the BIO_* routines, but I haven't thought this through carefully
* yet.
*/
static inline char *
skip_WS(char *p)
{
while (*p == ' ' || *p == '\t')
p++;
return p;
}
/**
* @brief convert a comma and/or space delimited list into a comma
* delimited list of the form:
* ( *LWS element *( *LWS "," *LWS element ))
* @param str string to convert.
*/
static void
normalize_list_string(char *opts)
{
char *p;
p = opts;
while (p != NULL) {
p = strchr(p, ' ');
if (p == NULL)
break;
if (p > opts && p[-1] != ',')
*p++ = ',';
p = skip_WS(p + 1);
}
}
/**
* @brief get the security flags from a comma delimited string.
* @param sec_opt the security option comman delimited string.
* @return the flags on success, or -1 on error (no memory).
*/
static int
get_security_flags(const char *sec_opts)
{
static const named_flag_t flag_tbl[] = {
{ "noanonymous", FLAG_ANONYMOUS },
{ "nodictionary", FLAG_DICTIONARY },
{ "noplaintext", FLAG_PLAINTEXT },
{ "noactive", FLAG_ACTIVE },
{ "mutual", FLAG_MUTUAL },
{ NULL, FLAG_NONE }
};
list_t *list;
char *opts;
uint32_t flags;
int rv;
if (sec_opts == NULL)
return 0;
if ((opts = strdup(sec_opts)) == NULL)
return -1;
normalize_list_string(opts);
rv = saslc__list_parse(&list, opts);
free(opts);
if (rv == -1)
return -1;
flags = saslc__list_flags(list, flag_tbl);
saslc__list_free(list);
return flags;
}
/**
* @brief compare the mechanism flags with the security option flags
* passed by the user and make sure the mechanism is OK.
* @param mech mechanism to check.
* @param flags security option flags passed by saslc_sess_init().
* @return true if the mechanism is permitted and false if not.
*/
static bool
mechanism_flags_OK(const saslc__mech_list_node_t *mech, uint32_t flags)
{
uint32_t reqflags, rejflags;
if (mech == NULL)
return false;
reqflags = flags & REQ_FLAGS;
rejflags = flags & REJ_FLAGS;
if ((mech->mech->flags & rejflags) != 0)
return false;
if ((mech->mech->flags & reqflags) != reqflags)
return false;
return true;
}
/**
* @brief chooses first supported mechanism from the mechs list for
* the sasl session.
* @param ctx sasl context
* @param mechs comma or space separated list of mechanisms
* e.g., "PLAIN,LOGIN" or "PLAIN LOGIN".
* @param sec_opts comma or space separated list of security options
* @return pointer to the mech on success, NULL if none mechanism is chosen
*
* Note: this uses SASLC_PROP_SECURITY from the context dictionary.
* Note: this function is not case sensitive with regard to mechs or sec_opts.
*/
static const saslc__mech_t *
saslc__sess_choose_mech(saslc_t *ctx, const char *mechs, const char *sec_opts)
{
list_t *list, *l;
char *tmpstr;
const saslc__mech_list_node_t *m;
uint32_t flags;
int rv;
rv = get_security_flags(sec_opts);
if (rv == -1)
goto nomem;
flags = rv;
sec_opts = saslc__dict_get(ctx->prop, SASLC_PROP_SECURITY);
if (sec_opts != NULL) {
rv = get_security_flags(sec_opts);
if (rv == -1)
goto nomem;
flags |= rv;
}
if ((tmpstr = strdup(mechs)) == NULL)
goto nomem;
normalize_list_string(tmpstr);
rv = saslc__list_parse(&list, tmpstr);
free(tmpstr);
if (rv == -1)
goto nomem;
m = NULL;
for (l = list; l != NULL; l = l->next) {
m = saslc__mech_list_get(ctx->mechanisms, l->value);
if (mechanism_flags_OK(m, flags))
break;
}
saslc__list_free(list);
if (m == NULL) {
saslc__error_set(ERR(ctx), ERROR_MECH,
"mechanism not supported");
return NULL;
}
return m->mech;
nomem:
saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
return NULL;
}
/**
* @brief sasl session initializaion. Function initializes session
* property dictionary, chooses best mechanism, creates mech session.
* @param ctx sasl context
* @param mechs comma or space separated list of mechanisms eg. "PLAIN,LOGIN"
* or "PLAIN LOGIN". Note that this function is not case sensitive.
* @return pointer to the sasl session on success, NULL on failure
*/
saslc_sess_t *
saslc_sess_init(saslc_t *ctx, const char *mechs, const char *sec_opts)
{
saslc_sess_t *sess;
const char *debug;
saslc__mech_list_node_t *m;
if ((sess = calloc(1, sizeof(*sess))) == NULL) {
saslc__error_set_errno(ERR(ctx), ERROR_NOMEM);
return NULL;
}
/* mechanism initialization */
if ((sess->mech = saslc__sess_choose_mech(ctx, mechs, sec_opts))
== NULL)
goto error;
/* XXX: special early check of mechanism dictionary for debug flag */
m = saslc__mech_list_get(ctx->mechanisms, sess->mech->name);
if (m != NULL) {
debug = saslc__dict_get(m->prop, SASLC_PROP_DEBUG);
if (debug != NULL)
saslc_debug = saslc__parser_is_true(debug);
}
/* create mechanism session */
if (sess->mech->create(sess) == -1)
goto error;
/* properties */
if ((sess->prop = saslc__dict_create()) == NULL) {
saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL);
goto error;
}
sess->context = ctx;
ctx->refcnt++;
saslc__msg_dbg("mechanism: %s\n", saslc_sess_getmech(sess));
return sess;
error:
free(sess);
return NULL;
}
/**
* @brief ends sasl session, destroys and deallocates internal
* resources
* @param sess sasl session
*/
void
saslc_sess_end(saslc_sess_t *sess)
{
sess->mech->destroy(sess);
saslc__dict_destroy(sess->prop);
sess->context->refcnt--;
free(sess);
}
/**
* @brief sets property for the session. If property already exists in
* the session, then previous value is replaced by the new value.
* @param sess sasl session
* @param name property name
* @param value property value (if NULL, simply remove previous key)
* @return 0 on success, -1 on failure
*/
int
saslc_sess_setprop(saslc_sess_t *sess, const char *key, const char *value)
{
/* if the key exists in the session dictionary, remove it */
(void)saslc__dict_remove(sess->prop, key);
if (value == NULL) /* simply remove previous value and return */
return 0;
switch (saslc__dict_insert(sess->prop, key, value)) {
case DICT_OK:
return 0;
case DICT_VALBAD:
saslc__error_set(ERR(sess), ERROR_BADARG, "bad value");
break;
case DICT_KEYINVALID:
saslc__error_set(ERR(sess), ERROR_BADARG, "bad key");
break;
case DICT_NOMEM:
saslc__error_set(ERR(sess), ERROR_NOMEM, NULL);
break;
case DICT_KEYEXISTS:
case DICT_KEYNOTFOUND:
assert(/*CONSTCOND*/0); /* impossible */
break;
}
return -1;
}
/**
* @brief gets property from the session. Dictionaries are used
* in following order: session dictionary, context dictionary (global
* configuration), mechanism dicionary.
* @param sess sasl session
* @param key property name
* @return property value on success, NULL on failure.
*/
const char *
saslc_sess_getprop(saslc_sess_t *sess, const char *key)
{
const char *r;
saslc__mech_list_node_t *m;
/* get property from the session dictionary */
if ((r = saslc__dict_get(sess->prop, key)) != NULL) {
saslc__msg_dbg("%s: session dict: %s=%s", __func__, key, r);
return r;
}
/* get property from the context dictionary */
if ((r = saslc__dict_get(sess->context->prop, key)) != NULL) {
saslc__msg_dbg("%s: context dict: %s=%s", __func__, key, r);
return r;
}
/* get property from the mechanism dictionary */
if ((m = saslc__mech_list_get(sess->context->mechanisms,
sess->mech->name)) == NULL)
return NULL;
if ((r = saslc__dict_get(m->prop, key)) != NULL)
saslc__msg_dbg("%s: mech %s dict: %s=%s", __func__,
saslc_sess_getmech(sess), key, r);
else
saslc__msg_dbg("%s: %s not found", __func__, key);
return r;
}
/**
* @brief set the sess->flags accordingly according to the properties.
* @param sess saslc session
*/
static uint32_t
saslc__sess_get_flags(saslc_sess_t *sess)
{
const char *base64io;
uint32_t flags;
/* set default flags */
flags = SASLC_FLAGS_DEFAULT;
base64io = saslc_sess_getprop(sess, SASLC_PROP_BASE64IO);
if (base64io != NULL) {
if (saslc__parser_is_true(base64io))
flags |= SASLC_FLAGS_BASE64;
else
flags &= ~SASLC_FLAGS_BASE64;
}
return flags;
}
/**
* @brief does one step of the sasl authentication, input data
* and its lenght are stored in in and inlen, output is stored in out and
* outlen. This function is a wrapper for mechanism step functions.
* Additionaly it checks if session is not already authorized and handles
* steps mech_sess structure.
* @param sess saslc session
* @param in input data
* @param inlen input data length
* @param out output data
* @param outlen output data length
* @return MECH_OK - on success, no more steps are needed
* MECH_ERROR - on error, additionaly errno in sess is setup
* MECH_STEP - more steps are needed
*/
int
saslc_sess_cont(saslc_sess_t *sess, const void *in, size_t inlen,
void **out, size_t *outlen)
{
saslc__mech_sess_t *ms;
const char *debug;
void *dec;
int rv;
ms = sess->mech_sess;
if (ms->status == STATUS_AUTHENTICATED) {
saslc__error_set(ERR(sess), ERROR_MECH,
"session authenticated");
return MECH_ERROR;
}
if (ms->step == 0) {
sess->flags = saslc__sess_get_flags(sess);
/* XXX: final check for any session debug flag setting */
debug = saslc__dict_get(sess->prop, SASLC_PROP_DEBUG);
if (debug != NULL)
saslc_debug = saslc__parser_is_true(debug);
}
saslc__msg_dbg("%s: encoded: inlen=%zu in='%s'", __func__, inlen,
in ? (const char *)in : "<null>");
if (inlen == 0 || (sess->flags & SASLC_FLAGS_BASE64_IN) == 0)
dec = NULL;
else {
if (saslc__crypto_decode_base64(in, inlen, &dec, &inlen)
== -1) {
saslc__error_set(ERR(sess), ERROR_MECH,
"base64 decode failed");
return MECH_ERROR;
}
in = dec;
}
saslc__msg_dbg("%s: decoded: inlen=%zu in='%s'", __func__, inlen,
in ? (const char *)in : "<null>");
rv = sess->mech->cont(sess, in, inlen, out, outlen);
if (dec != NULL)
free(dec);
if (rv == MECH_ERROR)
return MECH_ERROR;
saslc__msg_dbg("%s: out='%s'", __func__,
*outlen ? (char *)*out : "<null>");
if (*outlen == 0)
*out = NULL; /* XXX: unnecessary? */
else if ((sess->flags & SASLC_FLAGS_BASE64_OUT) != 0) {
char *enc;
size_t enclen;
if (saslc__crypto_encode_base64(*out, *outlen, &enc, &enclen)
== -1) {
free(*out);
return MECH_ERROR;
}
free(*out);
*out = enc;
*outlen = enclen;
}
if (rv == MECH_OK)
ms->status = STATUS_AUTHENTICATED;
ms->step++;
return rv;
}
/**
* @brief copies input data to an allocated buffer. The caller is
* responsible for freeing the buffer.
* @param sess sasl session
* @param xxcode codec to encode or decode one block of data
* @param in input data
* @param inlen input data length
* @param out output data
* @param outlen output data length
* @return number of bytes copied on success, -1 on failure
*/
static ssize_t
saslc__sess_copyout(saslc_sess_t *sess, const void *in, size_t inlen,
void **out, size_t *outlen)
{
*out = malloc(inlen);
if (*out == NULL) {
*outlen = 0;
saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
return -1;
}
*outlen = inlen;
memcpy(*out, in, inlen);
return inlen;
}
/**
* @brief encodes or decode data using method established during the
* authentication. Input data is stored in in and inlen and output
* data is stored in out and outlen. The caller is responsible for
* freeing the output buffer.
* @param sess sasl session
* @param xxcode codec to encode or decode one block of data
* @param in input data
* @param inlen input data length
* @param out output data
* @param outlen output data length
* @return number of bytes consumed on success, 0 if insufficient data
* to process, -1 on failure
*
* 'xxcode' encodes or decodes a single block of data and stores the
* resulting block and its length in 'out' and 'outlen', respectively.
* It should return the number of bytes it digested or -1 on error.
* If it was unable to process a complete block, it should return zero
* and remember the partial block internally. If it is called with
* 'inlen' = 0, it should flush out any remaining partial block data
* and return the number of stored bytes it flushed or zero if there
* were none (relevant for the encoder only).
*/
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)
{
saslc__mech_sess_t *ms;
unsigned char *p;
void *buf, *pkt;
size_t buflen, pktlen;
ssize_t len, ate;
ms = sess->mech_sess;
if (xxcode == NULL) {
saslc__error_set(ERR(sess), ERROR_MECH,
"security layer is not supported by mechanism");
return -1;
}
if (ms->status != STATUS_AUTHENTICATED) {
saslc__error_set(ERR(sess), ERROR_MECH,
"session is not authenticated");
return -1;
}
if (ms->qop == QOP_NONE)
return saslc__sess_copyout(sess, in, inlen, out, outlen);
p = NULL;
buf = NULL;
buflen = 0;
ate = 0;
do {
len = xxcode(sess, in, inlen, &pkt, &pktlen);
if (len == -1) {
free(buf);
return -1;
}
ate += len;
in = (const char *)in + len;
if (inlen < (size_t)len)
inlen = 0;
else
inlen -= len;
if (pktlen == 0) /* nothing processed, done */
continue;
buflen += pktlen;
p = buf;
if ((buf = realloc(buf, buflen)) == NULL) {
/* we should free memory if realloc(2) failed */
free(p);
saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
return -1;
}
p = buf;
p += buflen - pktlen;
memcpy(p, pkt, pktlen);
free(pkt);
} while (inlen > 0);
*out = buf;
*outlen = buflen;
return ate;
}
/**
* @brief encodes data using method established during the
* authentication. Input data is stored in in and inlen and output
* data is stored in out and outlen. The caller is responsible for
* freeing the output buffer.
* @param sess sasl session
* @param in input data
* @param inlen input data length
* @param out output data
* @param outlen output data length
* @return 0 on success, -1 on failure
*
* This will output a sequence of full blocks. When all data has been
* processed, this should be called one more time with inlen = 0 to
* flush any partial block left in the encoder.
*/
ssize_t
saslc_sess_encode(saslc_sess_t *sess, const void *in, size_t inlen,
void **out, size_t *outlen)
{
return saslc__sess_xxcode(sess, sess->mech->encode,
in, inlen, out, outlen);
}
/**
* @brief decodes data using method established during the
* authentication. Input data is stored in in and inlen and output
* data is stored in out and outlen. The caller is responsible for
* freeing the output buffer.
* @param sess sasl session
* @param in input data
* @param inlen input data length
* @param out output data
* @param outlen output data length
* @return 0 on success, -1 on failure
*/
ssize_t
saslc_sess_decode(saslc_sess_t *sess, const void *in, size_t inlen,
void **out, size_t *outlen)
{
return saslc__sess_xxcode(sess, sess->mech->decode,
in, inlen, out, outlen);
}
/**
* @brief gets string message of the error.
* @param sess sasl session
* @return pointer to the error message
*/
const char *
saslc_sess_strerror(saslc_sess_t *sess)
{
return saslc__error_get_strerror(ERR(sess));
}
/**
* @brief gets name of the mechanism used in the sasl session
* @param sess sasl session
* @return pointer to the mechanism name
*/
const char *
saslc_sess_getmech(saslc_sess_t *sess)
{
return sess->mech->name;
}