/* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* SASL server API implementation * Rob Siemborski * Tim Martin * $Id: client.c,v 1.61 2003/04/16 19:36:00 rjs3 Exp $ */ /* * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * 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. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* SASL Headers */ #include "sasl.h" #include "saslplug.h" #include "saslutil.h" #include "saslint.h" #ifdef _SUN_SDK_ DEFINE_STATIC_MUTEX(init_client_mutex); DEFINE_STATIC_MUTEX(client_active_mutex); /* * client_plug_mutex ensures only one client plugin is init'ed at a time * If a plugin is loaded more than once, the glob_context may be overwritten * which may lead to a memory leak. We keep glob_context with each mech * to avoid this problem. */ DEFINE_STATIC_MUTEX(client_plug_mutex); #else static cmech_list_t *cmechlist; /* global var which holds the list */ static sasl_global_callbacks_t global_callbacks; static int _sasl_client_active = 0; #endif /* _SUN_SDK_ */ #ifdef _SUN_SDK_ static int init_mechlist(_sasl_global_context_t *gctx) { cmech_list_t *cmechlist = gctx->cmechlist; #else static int init_mechlist() { #endif /* _SUN_SDK_ */ cmechlist->mutex = sasl_MUTEX_ALLOC(); if(!cmechlist->mutex) return SASL_FAIL; #ifdef _SUN_SDK_ cmechlist->utils= _sasl_alloc_utils(gctx, NULL, &gctx->client_global_callbacks); #else cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks); #endif /* _SUN_SDK_ */ if (cmechlist->utils==NULL) return SASL_NOMEM; cmechlist->mech_list=NULL; cmechlist->mech_length=0; return SASL_OK; } #ifdef _SUN_SDK_ static int client_done(_sasl_global_context_t *gctx) { cmech_list_t *cmechlist = gctx->cmechlist; _sasl_path_info_t *path_info, *p; #else static int client_done(void) { #endif /* _SUN_SDK_ */ cmechanism_t *cm; cmechanism_t *cprevm; #ifdef _SUN_SDK_ if(!gctx->sasl_client_active) return SASL_NOTINIT; if (LOCK_MUTEX(&client_active_mutex) < 0) { return (SASL_FAIL); } gctx->sasl_client_active--; if(gctx->sasl_client_active) { /* Don't de-init yet! Our refcount is nonzero. */ UNLOCK_MUTEX(&client_active_mutex); return SASL_CONTINUE; } #else if(!_sasl_client_active) return SASL_NOTINIT; else _sasl_client_active--; if(_sasl_client_active) { /* Don't de-init yet! Our refcount is nonzero. */ return SASL_CONTINUE; } #endif /* _SUN_SDK_ */ cm=cmechlist->mech_list; /* m point to begging of the list */ while (cm!=NULL) { cprevm=cm; cm=cm->next; if (cprevm->plug->mech_free) { #ifdef _SUN_SDK_ cprevm->plug->mech_free(cprevm->glob_context, cmechlist->utils); #else cprevm->plug->mech_free(cprevm->plug->glob_context, cmechlist->utils); #endif /* _SUN_SDK_ */ } sasl_FREE(cprevm->plugname); sasl_FREE(cprevm); } sasl_MUTEX_FREE(cmechlist->mutex); _sasl_free_utils(&cmechlist->utils); sasl_FREE(cmechlist); #ifdef _SUN_SDK_ gctx->cmechlist = NULL; p = gctx->cplug_path_info; while((path_info = p) != NULL) { sasl_FREE(path_info->path); p = path_info->next; sasl_FREE(path_info); } gctx->cplug_path_info = NULL; UNLOCK_MUTEX(&client_active_mutex); #else cmechlist = NULL; #endif /* _SUN_SDK_ */ return SASL_OK; } int sasl_client_add_plugin(const char *plugname, sasl_client_plug_init_t *entry_point) { #ifdef _SUN_SDK_ return (_sasl_client_add_plugin(_sasl_gbl_ctx(), plugname, entry_point)); } int _sasl_client_add_plugin(void *ctx, const char *plugname, sasl_client_plug_init_t *entry_point) { cmech_list_t *cmechlist; #ifdef _INTEGRATED_SOLARIS_ _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; int sun_reg; #endif /* _INTEGRATED_SOLARIS_ */ int i; cmechanism_t *m; #endif /* _SUN_SDK_ */ int plugcount; sasl_client_plug_t *pluglist; cmechanism_t *mech; int result; int version; int lupe; if(!plugname || !entry_point) return SASL_BADPARAM; #ifdef _SUN_SDK_ cmechlist = gctx->cmechlist; if (cmechlist == NULL) return SASL_BADPARAM; /* Check to see if this plugin has already been registered */ m = cmechlist->mech_list; for (i = 0; i < cmechlist->mech_length; i++) { if (strcmp(plugname, m->plugname) == 0) { return SASL_OK; } m = m->next; } result = LOCK_MUTEX(&client_plug_mutex); if (result != SASL_OK) return result; #endif /* _SUN_SDK_ */ result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version, &pluglist, &plugcount); #ifdef _INTEGRATED_SOLARIS_ sun_reg = _is_sun_reg(pluglist); #endif /* _INTEGRATED_SOLARIS_ */ if (result != SASL_OK) { #ifdef _SUN_SDK_ UNLOCK_MUTEX(&client_plug_mutex); __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN, "entry_point failed in sasl_client_add_plugin for %s", plugname); #else _sasl_log(NULL, SASL_LOG_WARN, "entry_point failed in sasl_client_add_plugin for %s", plugname); #endif /* _SUN_SDK_ */ return result; } if (version != SASL_CLIENT_PLUG_VERSION) { #ifdef _SUN_SDK_ UNLOCK_MUTEX(&client_plug_mutex); __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN, "version conflict in sasl_client_add_plugin for %s", plugname); #else _sasl_log(NULL, SASL_LOG_WARN, "version conflict in sasl_client_add_plugin for %s", plugname); #endif /* _SUN_SDK_ */ return SASL_BADVERS; } #ifdef _SUN_SDK_ /* Check plugins to make sure mech_name is non-NULL */ for (lupe=0;lupe < plugcount ;lupe++) { if (pluglist[lupe].mech_name == NULL) break; } if (lupe < plugcount) { UNLOCK_MUTEX(&client_plug_mutex); __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR, "invalid client plugin %s", plugname); return SASL_BADPROT; } #endif /* _SUN_SDK_ */ for (lupe=0;lupe< plugcount ;lupe++) { mech = sasl_ALLOC(sizeof(cmechanism_t)); #ifdef _SUN_SDK_ if (! mech) { UNLOCK_MUTEX(&client_plug_mutex); return SASL_NOMEM; } mech->glob_context = pluglist->glob_context; #else if (! mech) return SASL_NOMEM; #endif /* _SUN_SDK_ */ mech->plug=pluglist++; if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) { #ifdef _SUN_SDK_ UNLOCK_MUTEX(&client_plug_mutex); #endif /* _SUN_SDK_ */ sasl_FREE(mech); return SASL_NOMEM; } #ifdef _INTEGRATED_SOLARIS_ mech->sun_reg = sun_reg; #endif /* _INTEGRATED_SOLARIS_ */ mech->version = version; mech->next = cmechlist->mech_list; cmechlist->mech_list = mech; cmechlist->mech_length++; } #ifdef _SUN_SDK_ UNLOCK_MUTEX(&client_plug_mutex); #endif /* _SUN_SDK_ */ return SASL_OK; } static int client_idle(sasl_conn_t *conn) { cmechanism_t *m; #ifdef _SUN_SDK_ _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx; cmech_list_t *cmechlist = gctx->cmechlist; #endif /* _SUN_SDK_ */ if (! cmechlist) return 0; for (m = cmechlist->mech_list; m; m = m->next) if (m->plug->idle #ifdef _SUN_SDK_ && m->plug->idle(m->glob_context, #else && m->plug->idle(m->plug->glob_context, #endif /* _SUN_SDK_ */ conn, conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) return 1; return 0; } #ifdef _SUN_SDK_ static int _load_client_plugins(_sasl_global_context_t *gctx) { int ret; const add_plugin_list_t _ep_list[] = { { "sasl_client_plug_init", (add_plugin_t *)_sasl_client_add_plugin }, { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin }, { NULL, NULL } }; const sasl_callback_t *callbacks = gctx->client_global_callbacks.callbacks; ret = _sasl_load_plugins(gctx, 0, _ep_list, _sasl_find_getpath_callback(callbacks), _sasl_find_verifyfile_callback(callbacks)); return (ret); } #endif /* _SUN_SDK_ */ /* initialize the SASL client drivers * callbacks -- base callbacks for all client connections * returns: * SASL_OK -- Success * SASL_NOMEM -- Not enough memory * SASL_BADVERS -- Mechanism version mismatch * SASL_BADPARAM -- error in config file * SASL_NOMECH -- No mechanisms available * ... */ int sasl_client_init(const sasl_callback_t *callbacks) { #ifdef _SUN_SDK_ return _sasl_client_init(NULL, callbacks); } int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks) { int ret; _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; if (gctx == NULL) gctx = _sasl_gbl_ctx(); ret = LOCK_MUTEX(&init_client_mutex); if (ret < 0) { return (SASL_FAIL); } ret = LOCK_MUTEX(&client_active_mutex); if (ret < 0) { UNLOCK_MUTEX(&init_client_mutex); return (SASL_FAIL); } if(gctx->sasl_client_active) { /* We're already active, just increase our refcount */ /* xxx do something with the callback structure? */ gctx->sasl_client_active++; UNLOCK_MUTEX(&client_active_mutex); UNLOCK_MUTEX(&init_client_mutex); return SASL_OK; } gctx->client_global_callbacks.callbacks = callbacks; gctx->client_global_callbacks.appname = NULL; gctx->cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); if (gctx->cmechlist==NULL) { UNLOCK_MUTEX(&init_client_mutex); UNLOCK_MUTEX(&client_active_mutex); return SASL_NOMEM; } gctx->sasl_client_active = 1; UNLOCK_MUTEX(&client_active_mutex); /* load plugins */ ret=init_mechlist(gctx); if (ret!=SASL_OK) { client_done(gctx); UNLOCK_MUTEX(&init_client_mutex); return ret; } _sasl_client_add_plugin(gctx, "EXTERNAL", &external_client_plug_init); ret = _sasl_common_init(gctx, &gctx->client_global_callbacks, 0); #else int sasl_client_init(const sasl_callback_t *callbacks) { int ret; const add_plugin_list_t ep_list[] = { { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, { NULL, NULL } }; if(_sasl_client_active) { /* We're already active, just increase our refcount */ /* xxx do something with the callback structure? */ _sasl_client_active++; return SASL_OK; } global_callbacks.callbacks = callbacks; global_callbacks.appname = NULL; cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); if (cmechlist==NULL) return SASL_NOMEM; /* We need to call client_done if we fail now */ _sasl_client_active = 1; /* load plugins */ ret=init_mechlist(); if (ret!=SASL_OK) { client_done(); return ret; } sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); ret = _sasl_common_init(&global_callbacks); #endif /* _SUN_SDK_ */ if (ret == SASL_OK) #ifdef _SUN_SDK_ ret = _load_client_plugins(gctx); #else ret = _sasl_load_plugins(ep_list, _sasl_find_getpath_callback(callbacks), _sasl_find_verifyfile_callback(callbacks)); #endif /* _SUN_SDK_ */ #ifdef _SUN_SDK_ if (ret == SASL_OK) /* If sasl_client_init returns error, sasl_done() need not be called */ ret = _sasl_build_mechlist(gctx); if (ret == SASL_OK) { gctx->sasl_client_cleanup_hook = &client_done; gctx->sasl_client_idle_hook = &client_idle; } else { client_done(gctx); } UNLOCK_MUTEX(&init_client_mutex); #else if (ret == SASL_OK) { _sasl_client_cleanup_hook = &client_done; _sasl_client_idle_hook = &client_idle; ret = _sasl_build_mechlist(); } else { client_done(); } #endif /* _SUN_SDK_ */ return ret; } static void client_dispose(sasl_conn_t *pconn) { sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; #ifdef _SUN_SDK_ sasl_free_t *free_func = c_conn->cparams->utils->free; #endif /* _SUN_SDK_ */ if (c_conn->mech && c_conn->mech->plug->mech_dispose) { c_conn->mech->plug->mech_dispose(pconn->context, c_conn->cparams->utils); } pconn->context = NULL; if (c_conn->clientFQDN) #ifdef _SUN_SDK_ free_func(c_conn->clientFQDN); #else sasl_FREE(c_conn->clientFQDN); #endif /* _SUN_SDK_ */ if (c_conn->cparams) { _sasl_free_utils(&(c_conn->cparams->utils)); #ifdef _SUN_SDK_ free_func(c_conn->cparams); #else sasl_FREE(c_conn->cparams); #endif /* _SUN_SDK_ */ } _sasl_conn_dispose(pconn); } /* initialize a client exchange based on the specified mechanism * service -- registered name of the service using SASL (e.g. "imap") * serverFQDN -- the fully qualified domain name of the server * iplocalport -- client IPv4/IPv6 domain literal string with port * (if NULL, then mechanisms requiring IPaddr are disabled) * ipremoteport -- server IPv4/IPv6 domain literal string with port * (if NULL, then mechanisms requiring IPaddr are disabled) * prompt_supp -- list of client interactions supported * may also include sasl_getopt_t context & call * NULL prompt_supp = user/pass via SASL_INTERACT only * NULL proc = interaction supported via SASL_INTERACT * secflags -- security flags (see above) * in/out: * pconn -- connection negotiation structure * pointer to NULL => allocate new * non-NULL => recycle storage and go for next available mech * * Returns: * SASL_OK -- success * SASL_NOMECH -- no mechanism meets requested properties * SASL_NOMEM -- not enough memory */ int sasl_client_new(const char *service, const char *serverFQDN, const char *iplocalport, const char *ipremoteport, const sasl_callback_t *prompt_supp, unsigned flags, sasl_conn_t **pconn) { #ifdef _SUN_SDK_ return _sasl_client_new(NULL, service, serverFQDN, iplocalport, ipremoteport, prompt_supp, flags, pconn); } int _sasl_client_new(void *ctx, const char *service, const char *serverFQDN, const char *iplocalport, const char *ipremoteport, const sasl_callback_t *prompt_supp, unsigned flags, sasl_conn_t **pconn) { _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; #endif /* _SUN_SDK_ */ int result; char name[MAXHOSTNAMELEN]; sasl_client_conn_t *conn; sasl_utils_t *utils; #ifdef _SUN_SDK_ if (gctx == NULL) gctx = _sasl_gbl_ctx(); if(gctx->sasl_client_active==0) return SASL_NOTINIT; #else if(_sasl_client_active==0) return SASL_NOTINIT; #endif /* _SUN_SDK_ */ /* Remember, iplocalport and ipremoteport can be NULL and be valid! */ if (!pconn || !service || !serverFQDN) return SASL_BADPARAM; *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); if (*pconn==NULL) { #ifdef _SUN_SDK_ __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR, "Out of memory allocating connection context"); #else _sasl_log(NULL, SASL_LOG_ERR, "Out of memory allocating connection context"); #endif /* _SUN_SDK_ */ return SASL_NOMEM; } memset(*pconn, 0, sizeof(sasl_client_conn_t)); #ifdef _SUN_SDK_ (*pconn)->gctx = gctx; #endif /* _SUN_SDK_ */ (*pconn)->destroy_conn = &client_dispose; conn = (sasl_client_conn_t *)*pconn; conn->mech = NULL; conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); if (conn->cparams==NULL) MEMERROR(*pconn); memset(conn->cparams,0,sizeof(sasl_client_params_t)); result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, &client_idle, serverFQDN, iplocalport, ipremoteport, #ifdef _SUN_SDK_ prompt_supp, &gctx->client_global_callbacks); #else prompt_supp, &global_callbacks); #endif /* _SUN_SDK_ */ if (result != SASL_OK) RETURN(*pconn, result); #ifdef _SUN_SDK_ utils=_sasl_alloc_utils(gctx, *pconn, &gctx->client_global_callbacks); #else utils=_sasl_alloc_utils(*pconn, &global_callbacks); #endif /* _SUN_SDK_ */ if (utils==NULL) MEMERROR(*pconn); utils->conn= *pconn; /* Setup the non-lazy parts of cparams, the rest is done in * sasl_client_start */ conn->cparams->utils = utils; conn->cparams->canon_user = &_sasl_canon_user; conn->cparams->flags = flags; conn->cparams->prompt_supp = (*pconn)->callbacks; /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ memset(name, 0, sizeof(name)); gethostname(name, MAXHOSTNAMELEN); result = _sasl_strdup(name, &conn->clientFQDN, NULL); if(result == SASL_OK) return SASL_OK; #ifdef _SUN_SDK_ conn->cparams->iplocalport = (*pconn)->iplocalport; conn->cparams->iploclen = strlen((*pconn)->iplocalport); conn->cparams->ipremoteport = (*pconn)->ipremoteport; conn->cparams->ipremlen = strlen((*pconn)->ipremoteport); #endif /* _SUN_SDK_ */ /* result isn't SASL_OK */ _sasl_conn_dispose(*pconn); sasl_FREE(*pconn); *pconn = NULL; #ifdef _SUN_SDK_ __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR, "Out of memory in sasl_client_new"); #else _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); #endif /* _SUN_SDK_ */ return result; } static int have_prompts(sasl_conn_t *conn, const sasl_client_plug_t *mech) { static const unsigned long default_prompts[] = { SASL_CB_AUTHNAME, SASL_CB_PASS, SASL_CB_LIST_END }; const unsigned long *prompt; int (*pproc)(); void *pcontext; int result; for (prompt = (mech->required_prompts ? mech->required_prompts : default_prompts); *prompt != SASL_CB_LIST_END; prompt++) { result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); if (result != SASL_OK && result != SASL_INTERACT) return 0; /* we don't have this required prompt */ } return 1; /* we have all the prompts */ } /* select a mechanism for a connection * mechlist -- mechanisms server has available (punctuation ignored) * secret -- optional secret from previous session * output: * prompt_need -- on SASL_INTERACT, list of prompts needed to continue * clientout -- the initial client response to send to the server * mech -- set to mechanism name * * Returns: * SASL_OK -- success * SASL_NOMEM -- not enough memory * SASL_NOMECH -- no mechanism meets requested properties * SASL_INTERACT -- user interaction needed to fill in prompt_need list */ /* xxx confirm this with rfc 2222 * SASL mechanism allowable characters are "AZaz-_" * seperators can be any other characters and of any length * even variable lengths between * * Apps should be encouraged to simply use space or comma space * though */ int sasl_client_start(sasl_conn_t *conn, const char *mechlist, sasl_interact_t **prompt_need, const char **clientout, unsigned *clientoutlen, const char **mech) { sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; char name[SASL_MECHNAMEMAX + 1]; cmechanism_t *m=NULL,*bestm=NULL; size_t pos=0,place; size_t list_len; sasl_ssf_t bestssf = 0, minssf = 0; int result; #ifdef _SUN_SDK_ _sasl_global_context_t *gctx = (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx; cmech_list_t *cmechlist; if(gctx->sasl_client_active==0) return SASL_NOTINIT; cmechlist = gctx->cmechlist; #else if(_sasl_client_active==0) return SASL_NOTINIT; #endif /* _SUN_SDK_ */ if (!conn) return SASL_BADPARAM; /* verify parameters */ if (mechlist == NULL) PARAMERROR(conn); /* if prompt_need != NULL we've already been here and just need to do the continue step again */ /* do a step */ /* FIXME: Hopefully they only give us our own prompt_need back */ if (prompt_need && *prompt_need != NULL) { goto dostep; } #ifdef _SUN_SDK_ if (c_conn->mech != NULL) { if (c_conn->mech->plug->mech_dispose != NULL) { c_conn->mech->plug->mech_dispose(conn->context, c_conn->cparams->utils); c_conn->mech = NULL; } } memset(&conn->oparams, 0, sizeof(sasl_out_params_t)); (void) _load_client_plugins(gctx); #endif /* _SUN_SDK_ */ if(conn->props.min_ssf < conn->external.ssf) { minssf = 0; } else { minssf = conn->props.min_ssf - conn->external.ssf; } /* parse mechlist */ list_len = strlen(mechlist); while (posmech_list; m != NULL; m = m->next) { int myflags; /* Is this the mechanism the server is suggesting? */ if (strcasecmp(m->plug->mech_name, name)) continue; /* no */ /* Do we have the prompts for it? */ if (!have_prompts(conn, m->plug)) break; /* Is it strong enough? */ if (minssf > m->plug->max_ssf) break; #ifdef _INTEGRATED_SOLARIS_ /* If not SUN supplied mech, it has no strength */ if (minssf > 0 && !m->sun_reg) break; #endif /* _INTEGRATED_SOLARIS_ */ /* Does it meet our security properties? */ myflags = conn->props.security_flags; /* if there's an external layer this is no longer plaintext */ if ((conn->props.min_ssf <= conn->external.ssf) && (conn->external.ssf > 1)) { myflags &= ~SASL_SEC_NOPLAINTEXT; } if (((myflags ^ m->plug->security_flags) & myflags) != 0) { break; } /* Can we meet it's features? */ if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) && !conn->serverFQDN) { break; } /* Can it meet our features? */ if ((conn->flags & SASL_NEED_PROXY) && !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { break; } #ifdef PREFER_MECH #ifdef _INTEGRATED_SOLARIS_ if (strcasecmp(m->plug->mech_name, PREFER_MECH) && bestm && (m->sun_reg && m->plug->max_ssf <= bestssf) || (m->plug->max_ssf == 0)) { #else if (strcasecmp(m->plug->mech_name, PREFER_MECH) && bestm && m->plug->max_ssf <= bestssf) { #endif /* _INTEGRATED_SOLARIS_ */ /* this mechanism isn't our favorite, and it's no better than what we already have! */ break; } #else #ifdef _INTEGRATED_SOLARIS_ if (bestm && m->sun_reg && m->plug->max_ssf <= bestssf) { #else if (bestm && m->plug->max_ssf <= bestssf) { #endif /* _INTEGRATED_SOLARIS_ */ /* this mechanism is no better than what we already have! */ break; } #endif /* compare security flags, only take new mechanism if it has * all the security flags of the previous one. * * From the mechanisms we ship with, this yields the order: * * SRP * GSSAPI + KERBEROS_V4 * DIGEST + OTP * CRAM + EXTERNAL * PLAIN + LOGIN + ANONYMOUS * * This might be improved on by comparing the numeric value of * the bitwise-or'd security flags, which splits DIGEST/OTP, * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we * are depending on the numeric values of the flags (which may * change, and their ordering could be considered dumb luck. */ if (bestm && ((m->plug->security_flags ^ bestm->plug->security_flags) & bestm->plug->security_flags)) { break; } if (mech) { *mech = m->plug->mech_name; } #ifdef _INTEGRATED_SOLARIS_ bestssf = m->sun_reg ? m->plug->max_ssf : 0; #else bestssf = m->plug->max_ssf; #endif /* _INTEGRATED_SOLARIS_ */ bestm = m; break; } } if (bestm == NULL) { #ifdef _INTEGRATED_SOLARIS_ sasl_seterror(conn, 0, gettext("No worthy mechs found")); #else sasl_seterror(conn, 0, "No worthy mechs found"); #endif /* _INTEGRATED_SOLARIS_ */ result = SASL_NOMECH; goto done; } /* make (the rest of) cparams */ c_conn->cparams->service = conn->service; c_conn->cparams->servicelen = strlen(conn->service); c_conn->cparams->serverFQDN = conn->serverFQDN; c_conn->cparams->slen = strlen(conn->serverFQDN); c_conn->cparams->clientFQDN = c_conn->clientFQDN; c_conn->cparams->clen = strlen(c_conn->clientFQDN); c_conn->cparams->external_ssf = conn->external.ssf; c_conn->cparams->props = conn->props; #ifdef _INTEGRATED_SOLARIS_ if (!bestm->sun_reg) { c_conn->cparams->props.min_ssf = 0; c_conn->cparams->props.max_ssf = 0; } c_conn->base.sun_reg = bestm->sun_reg; #endif /* _INTEGRATED_SOLARIS_ */ c_conn->mech = bestm; /* init that plugin */ #ifdef _SUN_SDK_ result = c_conn->mech->plug->mech_new(c_conn->mech->glob_context, #else result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context, #endif /* _SUN_SDK_ */ c_conn->cparams, &(conn->context)); if(result != SASL_OK) goto done; /* do a step -- but only if we can do a client-send-first */ dostep: if(clientout) { if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) { *clientout = NULL; *clientoutlen = 0; result = SASL_CONTINUE; } else { result = sasl_client_step(conn, NULL, 0, prompt_need, clientout, clientoutlen); } } else result = SASL_CONTINUE; done: RETURN(conn, result); } /* do a single authentication step. * serverin -- the server message received by the client, MUST have a NUL * sentinel, not counted by serverinlen * output: * prompt_need -- on SASL_INTERACT, list of prompts needed to continue * clientout -- the client response to send to the server * * returns: * SASL_OK -- success * SASL_INTERACT -- user interaction needed to fill in prompt_need list * SASL_BADPROT -- server protocol incorrect/cancelled * SASL_BADSERV -- server failed mutual auth */ int sasl_client_step(sasl_conn_t *conn, const char *serverin, unsigned serverinlen, sasl_interact_t **prompt_need, const char **clientout, unsigned *clientoutlen) { sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; int result; #ifdef _SUN_SDK_ _sasl_global_context_t *gctx = (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx; if(gctx->sasl_client_active==0) return SASL_NOTINIT; #else if(_sasl_client_active==0) return SASL_NOTINIT; #endif /* _SUN_SDK_ */ if(!conn) return SASL_BADPARAM; /* check parameters */ if ((serverin==NULL) && (serverinlen>0)) PARAMERROR(conn); /* Don't do another step if the plugin told us that we're done */ if (conn->oparams.doneflag) { _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag"); return SASL_FAIL; } if(clientout) *clientout = NULL; if(clientoutlen) *clientoutlen = 0; /* do a step */ result = c_conn->mech->plug->mech_step(conn->context, c_conn->cparams, serverin, serverinlen, prompt_need, clientout, clientoutlen, &conn->oparams); if (result == SASL_OK) { /* So we're done on this end, but if both * 1. the mech does server-send-last * 2. the protocol does not * we need to return no data */ if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { *clientout = ""; *clientoutlen = 0; } if(!conn->oparams.maxoutbuf) { conn->oparams.maxoutbuf = conn->props.maxbufsize; } if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { #ifdef _SUN_SDK_ _sasl_log(conn, SASL_LOG_ERR, "mech did not call canon_user for both authzid and authid"); #else sasl_seterror(conn, 0, "mech did not call canon_user for both authzid and authid"); #endif /* _SUN_SDK_ */ result = SASL_BADPROT; } } RETURN(conn,result); } /* returns the length of all the mechanisms * added up */ #ifdef _SUN_SDK_ static unsigned mech_names_len(_sasl_global_context_t *gctx) { cmech_list_t *cmechlist = gctx->cmechlist; #else static unsigned mech_names_len() { #endif /* _SUN_SDK_ */ cmechanism_t *listptr; unsigned result = 0; for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) result += strlen(listptr->plug->mech_name); return result; } int _sasl_client_listmech(sasl_conn_t *conn, const char *prefix, const char *sep, const char *suffix, const char **result, unsigned *plen, int *pcount) { cmechanism_t *m=NULL; sasl_ssf_t minssf = 0; int ret; unsigned int resultlen; int flag; const char *mysep; #ifdef _SUN_SDK_ _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx; cmech_list_t *cmechlist; if(gctx->sasl_client_active==0) return SASL_NOTINIT; cmechlist = gctx->cmechlist; #else if(_sasl_client_active == 0) return SASL_NOTINIT; #endif /* _SUN_SDK_ */ if (!conn) return SASL_BADPARAM; if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); if (! result) PARAMERROR(conn); #ifdef _SUN_SDK_ (void) _load_client_plugins(gctx); #endif /* _SUN_SDK_ */ if (plen != NULL) *plen = 0; if (pcount != NULL) *pcount = 0; if (sep) { mysep = sep; } else { mysep = " "; } if(conn->props.min_ssf < conn->external.ssf) { minssf = 0; } else { minssf = conn->props.min_ssf - conn->external.ssf; } if (! cmechlist || cmechlist->mech_length <= 0) INTERROR(conn, SASL_NOMECH); resultlen = (prefix ? strlen(prefix) : 0) + (strlen(mysep) * (cmechlist->mech_length - 1)) #ifdef _SUN_SDK_ + mech_names_len(gctx) #else + mech_names_len() #endif /* _SUN_SDK_ */ + (suffix ? strlen(suffix) : 0) + 1; ret = _buf_alloc(&conn->mechlist_buf, &conn->mechlist_buf_len, resultlen); if(ret != SASL_OK) MEMERROR(conn); if (prefix) strcpy (conn->mechlist_buf,prefix); else *(conn->mechlist_buf) = '\0'; flag = 0; for (m = cmechlist->mech_list; m != NULL; m = m->next) { /* do we have the prompts for it? */ if (!have_prompts(conn, m->plug)) continue; /* is it strong enough? */ if (minssf > m->plug->max_ssf) continue; #ifdef _INTEGRATED_SOLARIS_ /* If not SUN supplied mech, it has no strength */ if (minssf > 0 && !m->sun_reg) continue; #endif /* _INTEGRATED_SOLARIS_ */ /* does it meet our security properties? */ if (((conn->props.security_flags ^ m->plug->security_flags) & conn->props.security_flags) != 0) { continue; } /* Can we meet it's features? */ if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) && !conn->serverFQDN) { continue; } /* Can it meet our features? */ if ((conn->flags & SASL_NEED_PROXY) && !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { break; } /* Okay, we like it, add it to the list! */ if (pcount != NULL) (*pcount)++; /* print seperator */ if (flag) { strcat(conn->mechlist_buf, mysep); } else { flag = 1; } /* now print the mechanism name */ strcat(conn->mechlist_buf, m->plug->mech_name); } if (suffix) strcat(conn->mechlist_buf,suffix); if (plen!=NULL) *plen=strlen(conn->mechlist_buf); *result = conn->mechlist_buf; return SASL_OK; } #ifdef _SUN_SDK_ sasl_string_list_t *_sasl_client_mechs(_sasl_global_context_t *gctx) { cmech_list_t *cmechlist = gctx->cmechlist; #else sasl_string_list_t *_sasl_client_mechs(void) { #endif /* _SUN_SDK_ */ cmechanism_t *listptr; sasl_string_list_t *retval = NULL, *next=NULL; #ifdef _SUN_SDK_ if(!gctx->sasl_client_active) return NULL; #else if(!_sasl_client_active) return NULL; #endif /* _SUN_SDK_ */ /* make list */ for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) { next = sasl_ALLOC(sizeof(sasl_string_list_t)); if(!next && !retval) return NULL; else if(!next) { next = retval->next; do { sasl_FREE(retval); retval = next; next = retval->next; } while(next); return NULL; } next->d = listptr->plug->mech_name; if(!retval) { next->next = NULL; retval = next; } else { next->next = retval; retval = next; } } return retval; }