/*
* 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
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* SASL Headers */
#include "sasl.h"
#include "saslplug.h"
#include "saslutil.h"
#include "saslint.h"
#ifdef _SUN_SDK_
/*
* 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.
*/
#else
static int _sasl_client_active = 0;
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
{
#else
static int init_mechlist()
{
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
return SASL_NOMEM;
cmechlist->mech_length=0;
return SASL_OK;
}
#ifdef _SUN_SDK_
#else
static int client_done(void) {
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
if(!gctx->sasl_client_active)
return SASL_NOTINIT;
if (LOCK_MUTEX(&client_active_mutex) < 0) {
return (SASL_FAIL);
}
if(gctx->sasl_client_active) {
/* Don't de-init yet! Our refcount is nonzero. */
return SASL_CONTINUE;
}
#else
if(!_sasl_client_active)
return SASL_NOTINIT;
else
if(_sasl_client_active) {
/* Don't de-init yet! Our refcount is nonzero. */
return SASL_CONTINUE;
}
#endif /* _SUN_SDK_ */
{
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
}
}
#ifdef _SUN_SDK_
p = gctx->cplug_path_info;
}
#else
#endif /* _SUN_SDK_ */
return SASL_OK;
}
{
#ifdef _SUN_SDK_
}
const char *plugname,
{
#ifdef _INTEGRATED_SOLARIS_
int sun_reg;
#endif /* _INTEGRATED_SOLARIS_ */
int i;
cmechanism_t *m;
#endif /* _SUN_SDK_ */
int plugcount;
int result;
int version;
int lupe;
#ifdef _SUN_SDK_
/* Check to see if this plugin has already been registered */
for (i = 0; i < cmechlist->mech_length; i++) {
return SASL_OK;
}
m = m->next;
}
return result;
#endif /* _SUN_SDK_ */
#ifdef _INTEGRATED_SOLARIS_
#endif /* _INTEGRATED_SOLARIS_ */
{
#ifdef _SUN_SDK_
"entry_point failed in sasl_client_add_plugin for %s",
plugname);
#else
"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_
"version conflict in sasl_client_add_plugin for %s", plugname);
#else
"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 */
break;
}
return SASL_BADPROT;
}
#endif /* _SUN_SDK_ */
{
#ifdef _SUN_SDK_
if (! mech) {
return SASL_NOMEM;
}
#else
if (! mech) return SASL_NOMEM;
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
return SASL_NOMEM;
}
#ifdef _INTEGRATED_SOLARIS_
#endif /* _INTEGRATED_SOLARIS_ */
cmechlist->mech_length++;
}
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
return SASL_OK;
}
static int
{
cmechanism_t *m;
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
if (! cmechlist)
return 0;
m;
m = m->next)
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
conn,
return 1;
return 0;
}
#ifdef _SUN_SDK_
{
int ret;
};
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
* ...
*/
{
#ifdef _SUN_SDK_
}
const sasl_callback_t *callbacks)
{
int ret;
gctx = _sasl_gbl_ctx();
if (ret < 0) {
return (SASL_FAIL);
}
if (ret < 0) {
return (SASL_FAIL);
}
if(gctx->sasl_client_active) {
/* We're already active, just increase our refcount */
/* xxx do something with the callback structure? */
return SASL_OK;
}
return SASL_NOMEM;
}
/* load plugins */
return ret;
}
#else
{
int ret;
const add_plugin_list_t ep_list[] = {
};
if(_sasl_client_active) {
/* We're already active, just increase our refcount */
/* xxx do something with the callback structure? */
return SASL_OK;
}
/* We need to call client_done if we fail now */
_sasl_client_active = 1;
/* load plugins */
ret=init_mechlist();
client_done();
return ret;
}
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
/* If sasl_client_init returns error, sasl_done() need not be called */
} else {
}
#else
ret = _sasl_build_mechlist();
} else {
client_done();
}
#endif /* _SUN_SDK_ */
return ret;
}
{
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
}
if (c_conn->clientFQDN)
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
}
}
/* 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
* (if NULL, then mechanisms requiring IPaddr are disabled)
* (if NULL, then mechanisms requiring IPaddr are disabled)
* prompt_supp -- list of client interactions supported
* may also include sasl_getopt_t context & call
* NULL proc = interaction supported via SASL_INTERACT
* secflags -- security flags (see above)
* 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
*/
const char *serverFQDN,
const char *iplocalport,
const char *ipremoteport,
const sasl_callback_t *prompt_supp,
unsigned flags,
sasl_conn_t **pconn)
{
#ifdef _SUN_SDK_
}
const char *service,
const char *serverFQDN,
const char *iplocalport,
const char *ipremoteport,
const sasl_callback_t *prompt_supp,
unsigned flags,
sasl_conn_t **pconn)
{
#endif /* _SUN_SDK_ */
int result;
#ifdef _SUN_SDK_
gctx = _sasl_gbl_ctx();
#else
if(_sasl_client_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
/* Remember, iplocalport and ipremoteport can be NULL and be valid! */
return SASL_BADPARAM;
#ifdef _SUN_SDK_
"Out of memory allocating connection context");
#else
"Out of memory allocating connection context");
#endif /* _SUN_SDK_ */
return SASL_NOMEM;
}
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/* Setup the non-lazy parts of cparams, the rest is done in
* sasl_client_start */
/* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
/* result isn't SASL_OK */
#ifdef _SUN_SDK_
"Out of memory in sasl_client_new");
#else
#endif /* _SUN_SDK_ */
return result;
}
const sasl_client_plug_t *mech)
{
static const unsigned long default_prompts[] = {
};
const unsigned long *prompt;
int (*pproc)();
void *pcontext;
int result;
? mech->required_prompts :
*prompt != SASL_CB_LIST_END;
prompt++) {
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
*/
const char *mechlist,
const char **clientout,
unsigned *clientoutlen,
const char **mech)
{
int result;
#ifdef _SUN_SDK_
#else
if(_sasl_client_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
if (!conn) return SASL_BADPARAM;
/* verify parameters */
/* 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 */
goto dostep;
}
#ifdef _SUN_SDK_
}
}
(void) _load_client_plugins(gctx);
#endif /* _SUN_SDK_ */
minssf = 0;
} else {
}
/* parse mechlist */
{
place=0;
pos++;
place++;
if (SASL_MECHNAMEMAX < place) {
place--;
pos++;
}
}
pos++;
if (! place) continue;
/* foreach in server list */
int myflags;
/* Is this the mechanism the server is suggesting? */
continue; /* no */
/* Do we have the prompts for it? */
break;
/* Is it strong enough? */
break;
#ifdef _INTEGRATED_SOLARIS_
/* If not SUN supplied mech, it has no strength */
break;
#endif /* _INTEGRATED_SOLARIS_ */
/* Does it meet our security properties? */
/* if there's an external layer this is no longer plaintext */
}
break;
}
/* Can we meet it's features? */
&& !conn->serverFQDN) {
break;
}
/* Can it meet our features? */
break;
}
#ifdef PREFER_MECH
#ifdef _INTEGRATED_SOLARIS_
#else
#endif /* _INTEGRATED_SOLARIS_ */
/* this mechanism isn't our favorite, and it's no better
than what we already have! */
break;
}
#else
#ifdef _INTEGRATED_SOLARIS_
#else
#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
* are depending on the numeric values of the flags (which may
* change, and their ordering could be considered dumb luck.
*/
if (bestm &&
break;
}
if (mech) {
}
#ifdef _INTEGRATED_SOLARIS_
#else
#endif /* _INTEGRATED_SOLARIS_ */
bestm = m;
break;
}
}
#ifdef _INTEGRATED_SOLARIS_
#else
#endif /* _INTEGRATED_SOLARIS_ */
goto done;
}
/* make (the rest of) cparams */
#ifdef _INTEGRATED_SOLARIS_
}
#endif /* _INTEGRATED_SOLARIS_ */
/* init that plugin */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/* do a step -- but only if we can do a client-send-first */
if(clientout) {
*clientoutlen = 0;
} else {
}
}
else
done:
}
/* 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_BADSERV -- server failed mutual auth
*/
const char *serverin,
unsigned serverinlen,
const char **clientout,
unsigned *clientoutlen)
{
int result;
#ifdef _SUN_SDK_
#else
if(_sasl_client_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
if(!conn) return SASL_BADPARAM;
/* check parameters */
/* Don't do another step if the plugin told us that we're done */
return SASL_FAIL;
}
if(clientoutlen) *clientoutlen = 0;
/* do a step */
/* 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 */
*clientout = "";
*clientoutlen = 0;
}
}
#ifdef _SUN_SDK_
"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_ */
}
}
}
/* returns the length of all the mechanisms
* added up
*/
#ifdef _SUN_SDK_
{
#else
static unsigned mech_names_len()
{
#endif /* _SUN_SDK_ */
unsigned result = 0;
return result;
}
const char *prefix,
const char *sep,
const char *suffix,
const char **result,
unsigned *plen,
int *pcount)
{
cmechanism_t *m=NULL;
int ret;
unsigned int resultlen;
int flag;
const char *mysep;
#ifdef _SUN_SDK_
#else
if(_sasl_client_active == 0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
if (!conn) return SASL_BADPARAM;
if (! result)
#ifdef _SUN_SDK_
(void) _load_client_plugins(gctx);
#endif /* _SUN_SDK_ */
*plen = 0;
*pcount = 0;
if (sep) {
} else {
mysep = " ";
}
minssf = 0;
} else {
}
#ifdef _SUN_SDK_
#else
+ mech_names_len()
#endif /* _SUN_SDK_ */
+ 1;
if (prefix)
else
flag = 0;
/* do we have the prompts for it? */
continue;
/* is it strong enough? */
continue;
#ifdef _INTEGRATED_SOLARIS_
/* If not SUN supplied mech, it has no strength */
continue;
#endif /* _INTEGRATED_SOLARIS_ */
/* does it meet our security properties? */
continue;
}
/* Can we meet it's features? */
&& !conn->serverFQDN) {
continue;
}
/* Can it meet our features? */
break;
}
/* Okay, we like it, add it to the list! */
(*pcount)++;
/* print seperator */
if (flag) {
} else {
flag = 1;
}
/* now print the mechanism name */
}
if (suffix)
return SASL_OK;
}
#ifdef _SUN_SDK_
{
#else
{
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
if(!_sasl_client_active) return NULL;
#endif /* _SUN_SDK_ */
/* make list */
else if(!next) {
do {
} while(next);
return NULL;
}
if(!retval) {
} else {
}
}
return retval;
}