2N/A/*
2N/A * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/* canonusr.c - user canonicalization support
2N/A * Rob Siemborski
2N/A * $Id: canonusr.c,v 1.12 2003/02/13 19:55:53 rjs3 Exp $
2N/A */
2N/A/*
2N/A * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A *
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A *
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in
2N/A * the documentation and/or other materials provided with the
2N/A * distribution.
2N/A *
2N/A * 3. The name "Carnegie Mellon University" must not be used to
2N/A * endorse or promote products derived from this software without
2N/A * prior written permission. For permission or any other legal
2N/A * details, please contact
2N/A * Office of Technology Transfer
2N/A * Carnegie Mellon University
2N/A * 5000 Forbes Avenue
2N/A * Pittsburgh, PA 15213-3890
2N/A * (412) 268-4387, fax: (412) 268-7395
2N/A * tech-transfer@andrew.cmu.edu
2N/A *
2N/A * 4. Redistributions of any form whatsoever must retain the following
2N/A * acknowledgment:
2N/A * "This product includes software developed by Computing Services
2N/A * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
2N/A *
2N/A * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2N/A * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2N/A * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
2N/A * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2N/A * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
2N/A * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
2N/A * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2N/A */
2N/A
2N/A#include <config.h>
2N/A#include <sasl.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <prop.h>
2N/A#include <stdio.h>
2N/A
2N/A#include "saslint.h"
2N/A
2N/Atypedef struct canonuser_plug_list
2N/A{
2N/A struct canonuser_plug_list *next;
2N/A#ifdef _SUN_SDK_
2N/A char *name;
2N/A#else
2N/A char name[PATH_MAX];
2N/A#endif /* _SUN_SDK_ */
2N/A const sasl_canonuser_plug_t *plug;
2N/A} canonuser_plug_list_t;
2N/A
2N/A#ifndef _SUN_SDK_
2N/Astatic canonuser_plug_list_t *canonuser_head = NULL;
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A/* default behavior:
2N/A * eliminate leading & trailing whitespace,
2N/A * null-terminate, and get into the outparams
2N/A *
2N/A * (handled by INTERNAL plugin) */
2N/A/* Also does auxprop lookups once username is canonoicalized */
2N/A/* a zero ulen or alen indicates that it is strlen(value) */
2N/Aint _sasl_canon_user(sasl_conn_t *conn,
2N/A const char *user, unsigned ulen,
2N/A unsigned flags,
2N/A sasl_out_params_t *oparams)
2N/A{
2N/A canonuser_plug_list_t *ptr;
2N/A sasl_server_conn_t *sconn = NULL;
2N/A sasl_client_conn_t *cconn = NULL;
2N/A sasl_canon_user_t *cuser_cb;
2N/A sasl_getopt_t *getopt;
2N/A void *context;
2N/A int result;
2N/A const char *plugin_name = NULL;
2N/A char *user_buf;
2N/A unsigned *lenp;
2N/A
2N/A if(!conn) return SASL_BADPARAM;
2N/A if(!user || !oparams) return SASL_BADPARAM;
2N/A
2N/A if(flags & SASL_CU_AUTHID) {
2N/A user_buf = conn->authid_buf;
2N/A lenp = &(oparams->alen);
2N/A } else if (flags & SASL_CU_AUTHZID) {
2N/A user_buf = conn->user_buf;
2N/A lenp = &(oparams->ulen);
2N/A } else {
2N/A return SASL_BADPARAM;
2N/A }
2N/A
2N/A if(conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn;
2N/A else if(conn->type == SASL_CONN_CLIENT) cconn = (sasl_client_conn_t *)conn;
2N/A else return SASL_FAIL;
2N/A
2N/A if(!ulen) ulen = (unsigned int)strlen(user);
2N/A
2N/A /* check to see if we have a callback to make*/
2N/A result = _sasl_getcallback(conn, SASL_CB_CANON_USER,
2N/A &cuser_cb, &context);
2N/A if(result == SASL_OK && cuser_cb) {
2N/A result = cuser_cb(conn, context,
2N/A user, ulen,
2N/A flags, (conn->type == SASL_CONN_SERVER ?
2N/A ((sasl_server_conn_t *)conn)->user_realm :
2N/A NULL),
2N/A user_buf, CANON_BUF_SIZE, lenp);
2N/A
2N/A
2N/A if (result != SASL_OK) return result;
2N/A
2N/A /* Point the input copy at the stored buffer */
2N/A user = user_buf;
2N/A ulen = *lenp;
2N/A }
2N/A
2N/A /* which plugin are we supposed to use? */
2N/A result = _sasl_getcallback(conn, SASL_CB_GETOPT,
2N/A &getopt, &context);
2N/A if(result == SASL_OK && getopt) {
2N/A getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL);
2N/A }
2N/A
2N/A if(!plugin_name) {
2N/A /* Use Defualt */
2N/A plugin_name = "INTERNAL";
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A for(ptr = conn->gctx->canonuser_head; ptr; ptr = ptr->next) {
2N/A#else
2N/A for(ptr = canonuser_head; ptr; ptr = ptr->next) {
2N/A#endif /* _SUN_SDK_ */
2N/A /* A match is if we match the internal name of the plugin, or if
2N/A * we match the filename (old-style) */
2N/A if((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name))
2N/A || !strcmp(plugin_name, ptr->name)) break;
2N/A }
2N/A
2N/A /* We clearly don't have this one! */
2N/A if(!ptr) {
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A if (conn->type == SASL_CONN_CLIENT)
2N/A sasl_seterror(conn, 0,
2N/A gettext("desired canon_user plugin %s not found"),
2N/A plugin_name);
2N/A else
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "desired canon_user plugin %s not found",
2N/A plugin_name);
2N/A#else
2N/A sasl_seterror(conn, 0, "desired canon_user plugin %s not found",
2N/A plugin_name);
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A return SASL_NOMECH;
2N/A }
2N/A
2N/A if(sconn) {
2N/A /* we're a server */
2N/A result = ptr->plug->canon_user_server(ptr->plug->glob_context,
2N/A sconn->sparams,
2N/A user, ulen,
2N/A flags,
2N/A user_buf,
2N/A CANON_BUF_SIZE, lenp);
2N/A } else {
2N/A /* we're a client */
2N/A result = ptr->plug->canon_user_client(ptr->plug->glob_context,
2N/A cconn->cparams,
2N/A user, ulen,
2N/A flags,
2N/A user_buf,
2N/A CANON_BUF_SIZE, lenp);
2N/A }
2N/A
2N/A if(result != SASL_OK) return result;
2N/A
2N/A if((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) {
2N/A /* We did both, so we need to copy the result into
2N/A * the buffer for the authzid from the buffer for the authid */
2N/A memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE);
2N/A oparams->ulen = oparams->alen;
2N/A }
2N/A
2N/A /* Set the appropriate oparams (lengths have already been set by lenp) */
2N/A if(flags & SASL_CU_AUTHID) {
2N/A oparams->authid = conn->authid_buf;
2N/A }
2N/A
2N/A if (flags & SASL_CU_AUTHZID) {
2N/A oparams->user = conn->user_buf;
2N/A }
2N/A
2N/A#ifndef macintosh
2N/A /* do auxprop lookups (server only) */
2N/A if(sconn) {
2N/A if(flags & SASL_CU_AUTHID) {
2N/A _sasl_auxprop_lookup(sconn->sparams, 0,
2N/A oparams->authid, oparams->alen);
2N/A }
2N/A if(flags & SASL_CU_AUTHZID) {
2N/A _sasl_auxprop_lookup(sconn->sparams, SASL_AUXPROP_AUTHZID,
2N/A oparams->user, oparams->ulen);
2N/A }
2N/A }
2N/A#endif
2N/A
2N/A
2N/A#ifdef _SUN_SDK_
2N/A return (SASL_OK);
2N/A#else
2N/A RETURN(conn, SASL_OK);
2N/A#endif /* _SUN_SDK_ */
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Avoid _sasl_canonuser_free(_sasl_global_context_t *gctx)
2N/A{
2N/A canonuser_plug_list_t *ptr, *ptr_next;
2N/A const sasl_utils_t *sasl_global_utils = gctx->sasl_canonusr_global_utils;
2N/A
2N/A for(ptr = (canonuser_plug_list_t *)gctx->canonuser_head;
2N/A ptr; ptr = ptr_next) {
2N/A ptr_next = ptr->next;
2N/A if(ptr->plug->canon_user_free)
2N/A ptr->plug->canon_user_free(ptr->plug->glob_context,
2N/A sasl_global_utils);
2N/A sasl_FREE(ptr->name);
2N/A sasl_FREE(ptr);
2N/A }
2N/A
2N/A gctx->canonuser_head = NULL;
2N/A}
2N/A#else
2N/Avoid _sasl_canonuser_free()
2N/A{
2N/A canonuser_plug_list_t *ptr, *ptr_next;
2N/A
2N/A for(ptr = canonuser_head; ptr; ptr = ptr_next) {
2N/A ptr_next = ptr->next;
2N/A if(ptr->plug->canon_user_free)
2N/A ptr->plug->canon_user_free(ptr->plug->glob_context,
2N/A sasl_global_utils);
2N/A sasl_FREE(ptr);
2N/A }
2N/A
2N/A canonuser_head = NULL;
2N/A}
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A#ifdef _SUN_SDK_
2N/Aint sasl_canonuser_add_plugin(const char *plugname,
2N/A sasl_canonuser_init_t *canonuserfunc)
2N/A{
2N/A return (_sasl_canonuser_add_plugin(_sasl_gbl_ctx(), plugname,
2N/A canonuserfunc));
2N/A}
2N/A
2N/Aint _sasl_canonuser_add_plugin(void *ctx,
2N/A const char *plugname,
2N/A sasl_canonuser_init_t *canonuserfunc)
2N/A#else
2N/Aint sasl_canonuser_add_plugin(const char *plugname,
2N/A sasl_canonuser_init_t *canonuserfunc)
2N/A#endif /* _SUN_SDK_ */
2N/A{
2N/A int result, out_version;
2N/A canonuser_plug_list_t *new_item;
2N/A sasl_canonuser_plug_t *plug;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
2N/A const sasl_utils_t *sasl_global_utils;
2N/A canonuser_plug_list_t *l;
2N/A
2N/A /* Check to see if this plugin has already been registered */
2N/A for (l = gctx->canonuser_head; l != NULL; l = l->next) {
2N/A if (strcmp(plugname, l->name) == 0) {
2N/A return SASL_OK;
2N/A }
2N/A }
2N/A sasl_global_utils = gctx->sasl_canonusr_global_utils;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if(!plugname || strlen(plugname) > (PATH_MAX - 1)) {
2N/A sasl_seterror(NULL, 0,
2N/A "bad plugname passed to sasl_canonuser_add_plugin\n");
2N/A return SASL_BADPARAM;
2N/A }
2N/A
2N/A result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION,
2N/A &out_version, &plug, plugname);
2N/A
2N/A if(result != SASL_OK) {
2N/A#ifdef _SUN_SDK_
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks == NULL ?
2N/A gctx->client_global_callbacks.callbacks :
2N/A gctx->server_global_callbacks.callbacks,
2N/A SASL_LOG_ERR, "canonuserfunc error %i\n",result);
2N/A#else
2N/A _sasl_log(NULL, SASL_LOG_ERR, "canonuserfunc error %i\n",result);
2N/A#endif /* _SUN_SDK_ */
2N/A return result;
2N/A }
2N/A
2N/A if(!plug->canon_user_server && !plug->canon_user_client) {
2N/A /* We need atleast one of these implemented */
2N/A#ifdef _SUN_SDK_
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks == NULL ?
2N/A gctx->client_global_callbacks.callbacks :
2N/A gctx->server_global_callbacks.callbacks, SASL_LOG_ERR,
2N/A "canonuser plugin without either client or server side");
2N/A#else
2N/A _sasl_log(NULL, SASL_LOG_ERR,
2N/A "canonuser plugin without either client or server side");
2N/A#endif /* _SUN_SDK_ */
2N/A return SASL_BADPROT;
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A /* Check plugin to make sure name is non-NULL */
2N/A if (plug->name == NULL) {
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks == NULL ?
2N/A gctx->client_global_callbacks.callbacks :
2N/A gctx->server_global_callbacks.callbacks,
2N/A SASL_LOG_ERR, "invalid canonusr plugin %s", plugname);
2N/A return SASL_BADPROT;
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t));
2N/A if(!new_item) return SASL_NOMEM;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if(_sasl_strdup(plugname, &new_item->name, NULL) != SASL_OK) {
2N/A sasl_FREE(new_item);
2N/A return SASL_NOMEM;
2N/A }
2N/A#else
2N/A strncpy(new_item->name, plugname, PATH_MAX);
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A new_item->plug = plug;
2N/A#ifdef _SUN_SDK_
2N/A new_item->next = gctx->canonuser_head;
2N/A gctx->canonuser_head = new_item;
2N/A#else
2N/A new_item->next = canonuser_head;
2N/A canonuser_head = new_item;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A return SASL_OK;
2N/A}
2N/A
2N/A#ifdef MIN
2N/A#undef MIN
2N/A#endif
2N/A#define MIN(a,b) (((a) < (b))? (a):(b))
2N/A
2N/Astatic int _canonuser_internal(const sasl_utils_t *utils,
2N/A const char *user, unsigned ulen,
2N/A unsigned flags __attribute__((unused)),
2N/A char *out_user,
2N/A unsigned out_umax, unsigned *out_ulen)
2N/A{
2N/A unsigned i;
2N/A char *in_buf, *userin;
2N/A const char *begin_u;
2N/A unsigned u_apprealm = 0;
2N/A sasl_server_conn_t *sconn = NULL;
2N/A
2N/A if(!utils || !user) return SASL_BADPARAM;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A in_buf = utils->malloc((ulen + 2) * sizeof(char));
2N/A#else
2N/A in_buf = sasl_ALLOC((ulen + 2) * sizeof(char));
2N/A#endif /* _SUN_SDK_ */
2N/A if(!in_buf) return SASL_NOMEM;
2N/A
2N/A userin = in_buf;
2N/A
2N/A memcpy(userin, user, ulen);
2N/A userin[ulen] = '\0';
2N/A
2N/A /* Strip User ID */
2N/A for(i=0;isspace((int)userin[i]) && i<ulen;i++);
2N/A begin_u = &(userin[i]);
2N/A if(i>0) ulen -= i;
2N/A
2N/A for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--);
2N/A if(begin_u == &(userin[ulen])) {
2N/A#ifdef _SUN_SDK_
2N/A utils->free(in_buf);
2N/A#else
2N/A sasl_FREE(in_buf);
2N/A#endif /* _SUN_SDK_ */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A utils->seterror(utils->conn, 0, gettext("All-whitespace username."));
2N/A#else
2N/A utils->seterror(utils->conn, 0, "All-whitespace username.");
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A return SASL_FAIL;
2N/A }
2N/A
2N/A if(utils->conn && utils->conn->type == SASL_CONN_SERVER)
2N/A sconn = (sasl_server_conn_t *)utils->conn;
2N/A
2N/A /* Need to append realm if necessary (see sasl.h) */
2N/A if(sconn && sconn->user_realm && !strchr(user, '@')) {
2N/A u_apprealm = strlen(sconn->user_realm) + 1;
2N/A }
2N/A
2N/A /* Now Copy */
2N/A memcpy(out_user, begin_u, MIN(ulen, out_umax));
2N/A if(sconn && u_apprealm) {
2N/A if(ulen >= out_umax) return SASL_BUFOVER;
2N/A out_user[ulen] = '@';
2N/A memcpy(&(out_user[ulen+1]), sconn->user_realm,
2N/A MIN(u_apprealm-1, out_umax-ulen-1));
2N/A }
2N/A out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';
2N/A
2N/A if(ulen + u_apprealm > out_umax) return SASL_BUFOVER;
2N/A
2N/A if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax);
2N/A
2N/A#ifdef _SUN_SDK_
2N/A utils->free(in_buf);
2N/A#else
2N/A sasl_FREE(in_buf);
2N/A#endif /* _SUN_SDK_ */
2N/A return SASL_OK;
2N/A}
2N/A
2N/Astatic int _cu_internal_server(void *glob_context __attribute__((unused)),
2N/A sasl_server_params_t *sparams,
2N/A const char *user, unsigned ulen,
2N/A unsigned flags,
2N/A char *out_user,
2N/A unsigned out_umax, unsigned *out_ulen)
2N/A{
2N/A return _canonuser_internal(sparams->utils,
2N/A user, ulen,
2N/A flags, out_user, out_umax, out_ulen);
2N/A}
2N/A
2N/Astatic int _cu_internal_client(void *glob_context __attribute__((unused)),
2N/A sasl_client_params_t *cparams,
2N/A const char *user, unsigned ulen,
2N/A unsigned flags,
2N/A char *out_user,
2N/A unsigned out_umax, unsigned *out_ulen)
2N/A{
2N/A return _canonuser_internal(cparams->utils,
2N/A user, ulen,
2N/A flags, out_user, out_umax, out_ulen);
2N/A}
2N/A
2N/Astatic sasl_canonuser_plug_t canonuser_internal_plugin = {
2N/A 0, /* features */
2N/A 0, /* spare */
2N/A NULL, /* glob_context */
2N/A "INTERNAL", /* name */
2N/A NULL, /* canon_user_free */
2N/A _cu_internal_server,
2N/A _cu_internal_client,
2N/A NULL,
2N/A NULL,
2N/A NULL
2N/A};
2N/A
2N/Aint internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)),
2N/A int max_version,
2N/A int *out_version,
2N/A sasl_canonuser_plug_t **plug,
2N/A const char *plugname __attribute__((unused)))
2N/A{
2N/A if(!out_version || !plug) return SASL_BADPARAM;
2N/A
2N/A if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
2N/A
2N/A *out_version = SASL_CANONUSER_PLUG_VERSION;
2N/A
2N/A *plug = &canonuser_internal_plugin;
2N/A
2N/A return SASL_OK;
2N/A}