Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      6 
      7 /* SASL server API implementation
      8  * Rob Siemborski
      9  * Tim Martin
     10  * $Id: client.c,v 1.61 2003/04/16 19:36:00 rjs3 Exp $
     11  */
     12 /*
     13  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
     14  *
     15  * Redistribution and use in source and binary forms, with or without
     16  * modification, are permitted provided that the following conditions
     17  * are met:
     18  *
     19  * 1. Redistributions of source code must retain the above copyright
     20  *    notice, this list of conditions and the following disclaimer.
     21  *
     22  * 2. Redistributions in binary form must reproduce the above copyright
     23  *    notice, this list of conditions and the following disclaimer in
     24  *    the documentation and/or other materials provided with the
     25  *    distribution.
     26  *
     27  * 3. The name "Carnegie Mellon University" must not be used to
     28  *    endorse or promote products derived from this software without
     29  *    prior written permission. For permission or any other legal
     30  *    details, please contact
     31  *      Office of Technology Transfer
     32  *      Carnegie Mellon University
     33  *      5000 Forbes Avenue
     34  *      Pittsburgh, PA  15213-3890
     35  *      (412) 268-4387, fax: (412) 268-7395
     36  *      tech-transfer (at) andrew.cmu.edu
     37  *
     38  * 4. Redistributions of any form whatsoever must retain the following
     39  *    acknowledgment:
     40  *    "This product includes software developed by Computing Services
     41  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
     42  *
     43  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
     44  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     45  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
     46  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     47  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     48  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     49  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     50  */
     51 
     52 #include <config.h>
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <limits.h>
     56 #include <ctype.h>
     57 #include <string.h>
     58 #ifdef HAVE_UNISTD_H
     59 #include <unistd.h>
     60 #endif
     61 
     62 /* SASL Headers */
     63 #include "sasl.h"
     64 #include "saslplug.h"
     65 #include "saslutil.h"
     66 #include "saslint.h"
     67 
     68 #ifdef _SUN_SDK_
     69 DEFINE_STATIC_MUTEX(init_client_mutex);
     70 DEFINE_STATIC_MUTEX(client_active_mutex);
     71 /*
     72  * client_plug_mutex ensures only one client plugin is init'ed at a time
     73  * If a plugin is loaded more than once, the glob_context may be overwritten
     74  * which may lead to a memory leak. We keep glob_context with each mech
     75  * to avoid this problem.
     76  */
     77 DEFINE_STATIC_MUTEX(client_plug_mutex);
     78 #else
     79 static cmech_list_t *cmechlist; /* global var which holds the list */
     80 
     81 static sasl_global_callbacks_t global_callbacks;
     82 
     83 static int _sasl_client_active = 0;
     84 #endif /* _SUN_SDK_ */
     85 
     86 #ifdef _SUN_SDK_
     87 static int init_mechlist(_sasl_global_context_t *gctx)
     88 {
     89   cmech_list_t *cmechlist = gctx->cmechlist;
     90 #else
     91 static int init_mechlist()
     92 {
     93 #endif /* _SUN_SDK_ */
     94 
     95   cmechlist->mutex = sasl_MUTEX_ALLOC();
     96   if(!cmechlist->mutex) return SASL_FAIL;
     97 
     98 #ifdef _SUN_SDK_
     99   cmechlist->utils=
    100 	_sasl_alloc_utils(gctx, NULL, &gctx->client_global_callbacks);
    101 #else
    102   cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks);
    103 #endif /* _SUN_SDK_ */
    104   if (cmechlist->utils==NULL)
    105     return SASL_NOMEM;
    106 
    107   cmechlist->mech_list=NULL;
    108   cmechlist->mech_length=0;
    109 
    110   return SASL_OK;
    111 }
    112 
    113 #ifdef _SUN_SDK_
    114 static int client_done(_sasl_global_context_t *gctx) {
    115   cmech_list_t *cmechlist = gctx->cmechlist;
    116   _sasl_path_info_t *path_info, *p;
    117 #else
    118 static int client_done(void) {
    119 #endif /* _SUN_SDK_ */
    120   cmechanism_t *cm;
    121   cmechanism_t *cprevm;
    122 
    123 #ifdef _SUN_SDK_
    124   if(!gctx->sasl_client_active)
    125       return SASL_NOTINIT;
    126   if (LOCK_MUTEX(&client_active_mutex) < 0) {
    127 	return (SASL_FAIL);
    128   }
    129   gctx->sasl_client_active--;
    130 
    131   if(gctx->sasl_client_active) {
    132       /* Don't de-init yet! Our refcount is nonzero. */
    133       UNLOCK_MUTEX(&client_active_mutex);
    134       return SASL_CONTINUE;
    135   }
    136 #else
    137   if(!_sasl_client_active)
    138       return SASL_NOTINIT;
    139   else
    140       _sasl_client_active--;
    141 
    142   if(_sasl_client_active) {
    143       /* Don't de-init yet! Our refcount is nonzero. */
    144       return SASL_CONTINUE;
    145   }
    146 #endif /* _SUN_SDK_ */
    147 
    148   cm=cmechlist->mech_list; /* m point to begging of the list */
    149   while (cm!=NULL)
    150   {
    151     cprevm=cm;
    152     cm=cm->next;
    153 
    154     if (cprevm->plug->mech_free) {
    155 #ifdef _SUN_SDK_
    156 	cprevm->plug->mech_free(cprevm->glob_context, cmechlist->utils);
    157 #else
    158 	cprevm->plug->mech_free(cprevm->plug->glob_context,
    159 				cmechlist->utils);
    160 #endif /* _SUN_SDK_ */
    161     }
    162 
    163     sasl_FREE(cprevm->plugname);
    164     sasl_FREE(cprevm);
    165   }
    166   sasl_MUTEX_FREE(cmechlist->mutex);
    167   _sasl_free_utils(&cmechlist->utils);
    168   sasl_FREE(cmechlist);
    169 
    170 #ifdef _SUN_SDK_
    171   gctx->cmechlist = NULL;
    172   p = gctx->cplug_path_info;
    173   while((path_info = p) != NULL) {
    174     sasl_FREE(path_info->path);
    175     p = path_info->next;
    176     sasl_FREE(path_info);
    177   }
    178   gctx->cplug_path_info = NULL;
    179   UNLOCK_MUTEX(&client_active_mutex);
    180 #else
    181   cmechlist = NULL;
    182 #endif /* _SUN_SDK_ */
    183 
    184   return SASL_OK;
    185 }
    186 
    187 int sasl_client_add_plugin(const char *plugname,
    188 			   sasl_client_plug_init_t *entry_point)
    189 {
    190 #ifdef _SUN_SDK_
    191     return (_sasl_client_add_plugin(_sasl_gbl_ctx(), plugname, entry_point));
    192 }
    193 
    194 int _sasl_client_add_plugin(void *ctx,
    195                             const char *plugname,
    196                             sasl_client_plug_init_t *entry_point)
    197 {
    198   cmech_list_t *cmechlist;
    199 #ifdef _INTEGRATED_SOLARIS_
    200   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
    201   /* EXPORT DELETE START */
    202   /* CRYPT DELETE START */
    203   int sun_reg;
    204   /* CRYPT DELETE END */
    205   /* EXPORT DELETE END */
    206 #endif /* _INTEGRATED_SOLARIS_ */
    207   int i;
    208   cmechanism_t *m;
    209 #endif /* _SUN_SDK_ */
    210   int plugcount;
    211   sasl_client_plug_t *pluglist;
    212   cmechanism_t *mech;
    213   int result;
    214   int version;
    215   int lupe;
    216 
    217   if(!plugname || !entry_point) return SASL_BADPARAM;
    218 
    219 #ifdef _SUN_SDK_
    220   cmechlist = gctx->cmechlist;
    221 
    222   if (cmechlist == NULL) return SASL_BADPARAM;
    223 
    224   /* Check to see if this plugin has already been registered */
    225   m = cmechlist->mech_list;
    226   for (i = 0; i < cmechlist->mech_length; i++) {
    227     if (strcmp(plugname, m->plugname) == 0) {
    228 	return SASL_OK;
    229     }
    230     m = m->next;
    231   }
    232 
    233   result = LOCK_MUTEX(&client_plug_mutex);
    234   if (result != SASL_OK)
    235 	return result;
    236 
    237 #endif /* _SUN_SDK_ */
    238 
    239   result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version,
    240 		       &pluglist, &plugcount);
    241 
    242   /* EXPORT DELETE START */
    243   /* CRYPT DELETE START */
    244 #ifdef _INTEGRATED_SOLARIS_
    245   sun_reg = _is_sun_reg(pluglist);
    246 #endif /* _INTEGRATED_SOLARIS_ */
    247   /* CRYPT DELETE END */
    248   /* EXPORT DELETE END */
    249   if (result != SASL_OK)
    250   {
    251 #ifdef _SUN_SDK_
    252     UNLOCK_MUTEX(&client_plug_mutex);
    253     __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
    254 	      "entry_point failed in sasl_client_add_plugin for %s",
    255 	      plugname);
    256 #else
    257     _sasl_log(NULL, SASL_LOG_WARN,
    258 	      "entry_point failed in sasl_client_add_plugin for %s",
    259 	      plugname);
    260 #endif /* _SUN_SDK_ */
    261     return result;
    262   }
    263 
    264   if (version != SASL_CLIENT_PLUG_VERSION)
    265   {
    266 #ifdef _SUN_SDK_
    267     UNLOCK_MUTEX(&client_plug_mutex);
    268     __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
    269 	      "version conflict in sasl_client_add_plugin for %s", plugname);
    270 #else
    271     _sasl_log(NULL, SASL_LOG_WARN,
    272 	      "version conflict in sasl_client_add_plugin for %s", plugname);
    273 #endif /* _SUN_SDK_ */
    274     return SASL_BADVERS;
    275   }
    276 
    277 #ifdef _SUN_SDK_
    278     /* Check plugins to make sure mech_name is non-NULL */
    279     for (lupe=0;lupe < plugcount ;lupe++) {
    280 	if (pluglist[lupe].mech_name == NULL)
    281 	     break;
    282     }
    283     if (lupe < plugcount) {
    284 	UNLOCK_MUTEX(&client_plug_mutex);
    285 	__sasl_log(gctx, gctx->client_global_callbacks.callbacks,
    286 		SASL_LOG_ERR, "invalid client plugin %s", plugname);
    287 	return SASL_BADPROT;
    288     }
    289 #endif /* _SUN_SDK_ */
    290 
    291   for (lupe=0;lupe< plugcount ;lupe++)
    292     {
    293       mech = sasl_ALLOC(sizeof(cmechanism_t));
    294 #ifdef _SUN_SDK_
    295       if (! mech) {
    296 	UNLOCK_MUTEX(&client_plug_mutex);
    297 	return SASL_NOMEM;
    298       }
    299       mech->glob_context = pluglist->glob_context;
    300 #else
    301       if (! mech) return SASL_NOMEM;
    302 #endif /* _SUN_SDK_ */
    303 
    304       mech->plug=pluglist++;
    305       if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) {
    306 #ifdef _SUN_SDK_
    307 	UNLOCK_MUTEX(&client_plug_mutex);
    308 #endif /* _SUN_SDK_ */
    309 	sasl_FREE(mech);
    310 	return SASL_NOMEM;
    311       }
    312       /* EXPORT DELETE START */
    313       /* CRYPT DELETE START */
    314 #ifdef _INTEGRATED_SOLARIS_
    315       mech->sun_reg = sun_reg;
    316 #endif /* _INTEGRATED_SOLARIS_ */
    317      /* CRYPT DELETE END */
    318      /* EXPORT DELETE END */
    319       mech->version = version;
    320       mech->next = cmechlist->mech_list;
    321       cmechlist->mech_list = mech;
    322       cmechlist->mech_length++;
    323     }
    324 #ifdef _SUN_SDK_
    325     UNLOCK_MUTEX(&client_plug_mutex);
    326 #endif /* _SUN_SDK_ */
    327 
    328   return SASL_OK;
    329 }
    330 
    331 static int
    332 client_idle(sasl_conn_t *conn)
    333 {
    334   cmechanism_t *m;
    335 #ifdef _SUN_SDK_
    336   _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
    337    cmech_list_t *cmechlist = gctx->cmechlist;
    338 #endif /* _SUN_SDK_ */
    339 
    340   if (! cmechlist)
    341     return 0;
    342 
    343   for (m = cmechlist->mech_list;
    344        m;
    345        m = m->next)
    346     if (m->plug->idle
    347 #ifdef _SUN_SDK_
    348 	&&  m->plug->idle(m->glob_context,
    349 #else
    350 	&&  m->plug->idle(m->plug->glob_context,
    351 #endif /* _SUN_SDK_ */
    352 			  conn,
    353 			  conn ? ((sasl_client_conn_t *)conn)->cparams : NULL))
    354       return 1;
    355   return 0;
    356 }
    357 
    358 #ifdef _SUN_SDK_
    359 static int _load_client_plugins(_sasl_global_context_t *gctx)
    360 {
    361     int ret;
    362     const add_plugin_list_t _ep_list[] = {
    363       { "sasl_client_plug_init", (add_plugin_t *)_sasl_client_add_plugin },
    364       { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin },
    365       { NULL, NULL }
    366     };
    367     const sasl_callback_t *callbacks = gctx->client_global_callbacks.callbacks;
    368 
    369     ret = _sasl_load_plugins(gctx, 0, _ep_list,
    370 			     _sasl_find_getpath_callback(callbacks),
    371 			     _sasl_find_verifyfile_callback(callbacks));
    372     return (ret);
    373 }
    374 #endif /* _SUN_SDK_ */
    375 
    376 /* initialize the SASL client drivers
    377  *  callbacks      -- base callbacks for all client connections
    378  * returns:
    379  *  SASL_OK        -- Success
    380  *  SASL_NOMEM     -- Not enough memory
    381  *  SASL_BADVERS   -- Mechanism version mismatch
    382  *  SASL_BADPARAM  -- error in config file
    383  *  SASL_NOMECH    -- No mechanisms available
    384  *  ...
    385  */
    386 
    387 int sasl_client_init(const sasl_callback_t *callbacks)
    388 {
    389 #ifdef _SUN_SDK_
    390 	return _sasl_client_init(NULL, callbacks);
    391 }
    392 
    393 int _sasl_client_init(void *ctx,
    394 		      const sasl_callback_t *callbacks)
    395 {
    396   int ret;
    397   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
    398 
    399   if (gctx == NULL)
    400 	gctx = _sasl_gbl_ctx();
    401 
    402   ret = LOCK_MUTEX(&init_client_mutex);
    403   if (ret < 0) {
    404 	return (SASL_FAIL);
    405   }
    406   ret = LOCK_MUTEX(&client_active_mutex);
    407   if (ret < 0) {
    408 	UNLOCK_MUTEX(&init_client_mutex);
    409 	return (SASL_FAIL);
    410   }
    411   if(gctx->sasl_client_active) {
    412       /* We're already active, just increase our refcount */
    413       /* xxx do something with the callback structure? */
    414       gctx->sasl_client_active++;
    415       UNLOCK_MUTEX(&client_active_mutex);
    416       UNLOCK_MUTEX(&init_client_mutex);
    417       return SASL_OK;
    418   }
    419 
    420   gctx->client_global_callbacks.callbacks = callbacks;
    421   gctx->client_global_callbacks.appname = NULL;
    422 
    423   gctx->cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
    424   if (gctx->cmechlist==NULL) {
    425       UNLOCK_MUTEX(&init_client_mutex);
    426       UNLOCK_MUTEX(&client_active_mutex);
    427       return SASL_NOMEM;
    428   }
    429 
    430   gctx->sasl_client_active = 1;
    431   UNLOCK_MUTEX(&client_active_mutex);
    432 
    433   /* load plugins */
    434   ret=init_mechlist(gctx);
    435 
    436   if (ret!=SASL_OK) {
    437     client_done(gctx);
    438     UNLOCK_MUTEX(&init_client_mutex);
    439     return ret;
    440   }
    441   _sasl_client_add_plugin(gctx, "EXTERNAL", &external_client_plug_init);
    442 
    443   ret = _sasl_common_init(gctx, &gctx->client_global_callbacks, 0);
    444 #else
    445 int sasl_client_init(const sasl_callback_t *callbacks)
    446 {
    447   int ret;
    448   const add_plugin_list_t ep_list[] = {
    449       { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin },
    450       { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
    451       { NULL, NULL }
    452   };
    453 
    454   if(_sasl_client_active) {
    455       /* We're already active, just increase our refcount */
    456       /* xxx do something with the callback structure? */
    457       _sasl_client_active++;
    458       return SASL_OK;
    459   }
    460 
    461   global_callbacks.callbacks = callbacks;
    462   global_callbacks.appname = NULL;
    463 
    464   cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
    465   if (cmechlist==NULL) return SASL_NOMEM;
    466 
    467   /* We need to call client_done if we fail now */
    468   _sasl_client_active = 1;
    469 
    470   /* load plugins */
    471   ret=init_mechlist();
    472   if (ret!=SASL_OK) {
    473       client_done();
    474       return ret;
    475   }
    476 
    477   sasl_client_add_plugin("EXTERNAL", &external_client_plug_init);
    478 
    479   ret = _sasl_common_init(&global_callbacks);
    480 #endif /* _SUN_SDK_ */
    481 
    482   if (ret == SASL_OK)
    483 #ifdef _SUN_SDK_
    484       ret = _load_client_plugins(gctx);
    485 #else
    486       ret = _sasl_load_plugins(ep_list,
    487 			       _sasl_find_getpath_callback(callbacks),
    488 			       _sasl_find_verifyfile_callback(callbacks));
    489 #endif /* _SUN_SDK_ */
    490 
    491 #ifdef _SUN_SDK_
    492   if (ret == SASL_OK)
    493 	/* If sasl_client_init returns error, sasl_done() need not be called */
    494       ret = _sasl_build_mechlist(gctx);
    495   if (ret == SASL_OK) {
    496       gctx->sasl_client_cleanup_hook = &client_done;
    497       gctx->sasl_client_idle_hook = &client_idle;
    498   } else {
    499       client_done(gctx);
    500   }
    501   UNLOCK_MUTEX(&init_client_mutex);
    502 #else
    503   if (ret == SASL_OK) {
    504       _sasl_client_cleanup_hook = &client_done;
    505       _sasl_client_idle_hook = &client_idle;
    506 
    507       ret = _sasl_build_mechlist();
    508   } else {
    509       client_done();
    510   }
    511 #endif /* _SUN_SDK_ */
    512 
    513   return ret;
    514 }
    515 
    516 static void client_dispose(sasl_conn_t *pconn)
    517 {
    518   sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn;
    519 #ifdef _SUN_SDK_
    520   sasl_free_t *free_func = c_conn->cparams->utils->free;
    521 #endif /* _SUN_SDK_ */
    522 
    523   if (c_conn->mech && c_conn->mech->plug->mech_dispose) {
    524     c_conn->mech->plug->mech_dispose(pconn->context,
    525 				     c_conn->cparams->utils);
    526   }
    527 
    528   pconn->context = NULL;
    529 
    530   if (c_conn->clientFQDN)
    531 #ifdef _SUN_SDK_
    532       free_func(c_conn->clientFQDN);
    533 #else
    534       sasl_FREE(c_conn->clientFQDN);
    535 #endif /* _SUN_SDK_ */
    536 
    537   if (c_conn->cparams) {
    538       _sasl_free_utils(&(c_conn->cparams->utils));
    539 #ifdef _SUN_SDK_
    540       free_func(c_conn->cparams);
    541 #else
    542       sasl_FREE(c_conn->cparams);
    543 #endif /* _SUN_SDK_ */
    544   }
    545 
    546   _sasl_conn_dispose(pconn);
    547 }
    548 
    549 /* initialize a client exchange based on the specified mechanism
    550  *  service       -- registered name of the service using SASL (e.g. "imap")
    551  *  serverFQDN    -- the fully qualified domain name of the server
    552  *  iplocalport   -- client IPv4/IPv6 domain literal string with port
    553  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
    554  *  ipremoteport  -- server IPv4/IPv6 domain literal string with port
    555  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
    556  *  prompt_supp   -- list of client interactions supported
    557  *                   may also include sasl_getopt_t context & call
    558  *                   NULL prompt_supp = user/pass via SASL_INTERACT only
    559  *                   NULL proc = interaction supported via SASL_INTERACT
    560  *  secflags      -- security flags (see above)
    561  * in/out:
    562  *  pconn         -- connection negotiation structure
    563  *                   pointer to NULL => allocate new
    564  *                   non-NULL => recycle storage and go for next available mech
    565  *
    566  * Returns:
    567  *  SASL_OK       -- success
    568  *  SASL_NOMECH   -- no mechanism meets requested properties
    569  *  SASL_NOMEM    -- not enough memory
    570  */
    571 int sasl_client_new(const char *service,
    572 		    const char *serverFQDN,
    573 		    const char *iplocalport,
    574 		    const char *ipremoteport,
    575 		    const sasl_callback_t *prompt_supp,
    576 		    unsigned flags,
    577 		    sasl_conn_t **pconn)
    578 {
    579 #ifdef _SUN_SDK_
    580     return _sasl_client_new(NULL, service, serverFQDN, iplocalport,
    581 			    ipremoteport, prompt_supp, flags, pconn);
    582 }
    583 int _sasl_client_new(void *ctx,
    584 		     const char *service,
    585 		     const char *serverFQDN,
    586 		     const char *iplocalport,
    587 		     const char *ipremoteport,
    588 		     const sasl_callback_t *prompt_supp,
    589 		     unsigned flags,
    590 		     sasl_conn_t **pconn)
    591 {
    592   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
    593 #endif /* _SUN_SDK_ */
    594   int result;
    595   char name[MAXHOSTNAMELEN];
    596   sasl_client_conn_t *conn;
    597   sasl_utils_t *utils;
    598 
    599 #ifdef _SUN_SDK_
    600   if (gctx == NULL)
    601 	gctx = _sasl_gbl_ctx();
    602 
    603   if(gctx->sasl_client_active==0) return SASL_NOTINIT;
    604 #else
    605   if(_sasl_client_active==0) return SASL_NOTINIT;
    606 #endif /* _SUN_SDK_ */
    607 
    608   /* Remember, iplocalport and ipremoteport can be NULL and be valid! */
    609   if (!pconn || !service || !serverFQDN)
    610     return SASL_BADPARAM;
    611 
    612   *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t));
    613   if (*pconn==NULL) {
    614 #ifdef _SUN_SDK_
    615       __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
    616 		"Out of memory allocating connection context");
    617 #else
    618       _sasl_log(NULL, SASL_LOG_ERR,
    619 		"Out of memory allocating connection context");
    620 #endif /* _SUN_SDK_ */
    621       return SASL_NOMEM;
    622   }
    623   memset(*pconn, 0, sizeof(sasl_client_conn_t));
    624 
    625 #ifdef _SUN_SDK_
    626   (*pconn)->gctx = gctx;
    627 #endif /* _SUN_SDK_ */
    628 
    629   (*pconn)->destroy_conn = &client_dispose;
    630 
    631   conn = (sasl_client_conn_t *)*pconn;
    632 
    633   conn->mech = NULL;
    634 
    635   conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t));
    636   if (conn->cparams==NULL)
    637       MEMERROR(*pconn);
    638   memset(conn->cparams,0,sizeof(sasl_client_params_t));
    639 
    640   result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT,
    641 			   &client_idle, serverFQDN,
    642 			   iplocalport, ipremoteport,
    643 #ifdef _SUN_SDK_
    644 			   prompt_supp, &gctx->client_global_callbacks);
    645 #else
    646 			   prompt_supp, &global_callbacks);
    647 #endif /* _SUN_SDK_ */
    648 
    649   if (result != SASL_OK) RETURN(*pconn, result);
    650 
    651 #ifdef _SUN_SDK_
    652   utils=_sasl_alloc_utils(gctx, *pconn, &gctx->client_global_callbacks);
    653 #else
    654   utils=_sasl_alloc_utils(*pconn, &global_callbacks);
    655 #endif /* _SUN_SDK_ */
    656   if (utils==NULL)
    657       MEMERROR(*pconn);
    658 
    659   utils->conn= *pconn;
    660 
    661   /* Setup the non-lazy parts of cparams, the rest is done in
    662    * sasl_client_start */
    663   conn->cparams->utils = utils;
    664   conn->cparams->canon_user = &_sasl_canon_user;
    665   conn->cparams->flags = flags;
    666   conn->cparams->prompt_supp = (*pconn)->callbacks;
    667 
    668   /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
    669   memset(name, 0, sizeof(name));
    670   gethostname(name, MAXHOSTNAMELEN);
    671 
    672   result = _sasl_strdup(name, &conn->clientFQDN, NULL);
    673 
    674   if(result == SASL_OK) return SASL_OK;
    675 
    676 #ifdef _SUN_SDK_
    677   conn->cparams->iplocalport = (*pconn)->iplocalport;
    678   conn->cparams->iploclen = strlen((*pconn)->iplocalport);
    679   conn->cparams->ipremoteport = (*pconn)->ipremoteport;
    680   conn->cparams->ipremlen = strlen((*pconn)->ipremoteport);
    681 #endif /* _SUN_SDK_ */
    682 
    683   /* result isn't SASL_OK */
    684   _sasl_conn_dispose(*pconn);
    685   sasl_FREE(*pconn);
    686   *pconn = NULL;
    687 #ifdef _SUN_SDK_
    688   __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
    689 	"Out of memory in sasl_client_new");
    690 #else
    691   _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new");
    692 #endif /* _SUN_SDK_ */
    693   return result;
    694 }
    695 
    696 static int have_prompts(sasl_conn_t *conn,
    697 			const sasl_client_plug_t *mech)
    698 {
    699   static const unsigned long default_prompts[] = {
    700     SASL_CB_AUTHNAME,
    701     SASL_CB_PASS,
    702     SASL_CB_LIST_END
    703   };
    704 
    705   const unsigned long *prompt;
    706   int (*pproc)();
    707   void *pcontext;
    708   int result;
    709 
    710   for (prompt = (mech->required_prompts
    711 		 ? mech->required_prompts :
    712 		 default_prompts);
    713        *prompt != SASL_CB_LIST_END;
    714        prompt++) {
    715     result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext);
    716     if (result != SASL_OK && result != SASL_INTERACT)
    717       return 0;			/* we don't have this required prompt */
    718   }
    719 
    720   return 1; /* we have all the prompts */
    721 }
    722 
    723 /* select a mechanism for a connection
    724  *  mechlist      -- mechanisms server has available (punctuation ignored)
    725  *  secret        -- optional secret from previous session
    726  * output:
    727  *  prompt_need   -- on SASL_INTERACT, list of prompts needed to continue
    728  *  clientout     -- the initial client response to send to the server
    729  *  mech          -- set to mechanism name
    730  *
    731  * Returns:
    732  *  SASL_OK       -- success
    733  *  SASL_NOMEM    -- not enough memory
    734  *  SASL_NOMECH   -- no mechanism meets requested properties
    735  *  SASL_INTERACT -- user interaction needed to fill in prompt_need list
    736  */
    737 
    738 /* xxx confirm this with rfc 2222
    739  * SASL mechanism allowable characters are "AZaz-_"
    740  * seperators can be any other characters and of any length
    741  * even variable lengths between
    742  *
    743  * Apps should be encouraged to simply use space or comma space
    744  * though
    745  */
    746 int sasl_client_start(sasl_conn_t *conn,
    747 		      const char *mechlist,
    748 		      sasl_interact_t **prompt_need,
    749 		      const char **clientout,
    750 		      unsigned *clientoutlen,
    751 		      const char **mech)
    752 {
    753     sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
    754     char name[SASL_MECHNAMEMAX + 1];
    755     cmechanism_t *m=NULL,*bestm=NULL;
    756     size_t pos=0,place;
    757     size_t list_len;
    758     sasl_ssf_t bestssf = 0, minssf = 0;
    759     int result;
    760 #ifdef _SUN_SDK_
    761     _sasl_global_context_t *gctx = (conn == NULL) ?
    762 		_sasl_gbl_ctx() : conn->gctx;
    763     cmech_list_t *cmechlist;
    764 
    765     if(gctx->sasl_client_active==0) return SASL_NOTINIT;
    766     cmechlist = gctx->cmechlist;
    767 #else
    768     if(_sasl_client_active==0) return SASL_NOTINIT;
    769 #endif /* _SUN_SDK_ */
    770 
    771     if (!conn) return SASL_BADPARAM;
    772 
    773     /* verify parameters */
    774     if (mechlist == NULL)
    775 	PARAMERROR(conn);
    776 
    777     /* if prompt_need != NULL we've already been here
    778        and just need to do the continue step again */
    779 
    780     /* do a step */
    781     /* FIXME: Hopefully they only give us our own prompt_need back */
    782     if (prompt_need && *prompt_need != NULL) {
    783 	goto dostep;
    784     }
    785 
    786 #ifdef _SUN_SDK_
    787     if (c_conn->mech != NULL) {
    788 	if (c_conn->mech->plug->mech_dispose != NULL) {
    789 	    c_conn->mech->plug->mech_dispose(conn->context,
    790 		c_conn->cparams->utils);
    791 	    c_conn->mech = NULL;
    792 	}
    793     }
    794     memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
    795 
    796     (void) _load_client_plugins(gctx);
    797 #endif /* _SUN_SDK_ */
    798 
    799     if(conn->props.min_ssf < conn->external.ssf) {
    800 	minssf = 0;
    801     } else {
    802 	minssf = conn->props.min_ssf - conn->external.ssf;
    803     }
    804 
    805     /* parse mechlist */
    806     list_len = strlen(mechlist);
    807 
    808     while (pos<list_len)
    809     {
    810 	place=0;
    811 	while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos])
    812 				  || mechlist[pos] == '_'
    813 				  || mechlist[pos] == '-')) {
    814 	    name[place]=mechlist[pos];
    815 	    pos++;
    816 	    place++;
    817 	    if (SASL_MECHNAMEMAX < place) {
    818 		place--;
    819 		while(pos<list_len && (isalnum((unsigned char)mechlist[pos])
    820 				       || mechlist[pos] == '_'
    821 				       || mechlist[pos] == '-'))
    822 		    pos++;
    823 	    }
    824 	}
    825 	pos++;
    826 	name[place]=0;
    827 
    828 	if (! place) continue;
    829 
    830 	/* foreach in server list */
    831 	for (m = cmechlist->mech_list; m != NULL; m = m->next) {
    832 	    int myflags;
    833 
    834 	    /* Is this the mechanism the server is suggesting? */
    835 	    if (strcasecmp(m->plug->mech_name, name))
    836 		continue; /* no */
    837 
    838 	    /* Do we have the prompts for it? */
    839 	    if (!have_prompts(conn, m->plug))
    840 		break;
    841 
    842 	    /* Is it strong enough? */
    843 	    if (minssf > m->plug->max_ssf)
    844 		break;
    845 
    846 	    /* EXPORT DELETE START */
    847 	    /* CRYPT DELETE START */
    848 #ifdef _INTEGRATED_SOLARIS_
    849 	    /* If not SUN supplied mech, it has no strength */
    850 	    if (minssf > 0 && !m->sun_reg)
    851 		break;
    852 #endif /* _INTEGRATED_SOLARIS_ */
    853 	    /* CRYPT DELETE END */
    854 	    /* EXPORT DELETE END */
    855 
    856 	    /* Does it meet our security properties? */
    857 	    myflags = conn->props.security_flags;
    858 
    859 	    /* if there's an external layer this is no longer plaintext */
    860 	    if ((conn->props.min_ssf <= conn->external.ssf) &&
    861 		(conn->external.ssf > 1)) {
    862 		myflags &= ~SASL_SEC_NOPLAINTEXT;
    863 	    }
    864 
    865 	    if (((myflags ^ m->plug->security_flags) & myflags) != 0) {
    866 		break;
    867 	    }
    868 
    869 	    /* Can we meet it's features? */
    870 	    if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
    871 		&& !conn->serverFQDN) {
    872 		break;
    873 	    }
    874 
    875 	    /* Can it meet our features? */
    876 	    if ((conn->flags & SASL_NEED_PROXY) &&
    877 		!(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
    878 		break;
    879 	    }
    880 
    881 #ifdef PREFER_MECH
    882 	    /* EXPORT DELETE START */
    883 	    /* CRYPT DELETE START */
    884 #ifdef _INTEGRATED_SOLARIS_
    885 	    if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
    886 		bestm && (m->sun_reg && m->plug->max_ssf <= bestssf) ||
    887 		(m->plug->max_ssf == 0)) {
    888 #else
    889 	    /* CRYPT DELETE END */
    890 	    /* EXPORT DELETE END */
    891 	    if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
    892 		bestm && m->plug->max_ssf <= bestssf) {
    893 
    894 		/* EXPORT DELETE START */
    895 		/* CRYPT DELETE START */
    896 #endif /* _INTEGRATED_SOLARIS_ */
    897 		/* CRYPT DELETE END */
    898 		/* EXPORT DELETE END */
    899 
    900 		/* this mechanism isn't our favorite, and it's no better
    901 		   than what we already have! */
    902 		break;
    903 	    }
    904 #else
    905 	    /* EXPORT DELETE START */
    906 	    /* CRYPT DELETE START */
    907 #ifdef _INTEGRATED_SOLARIS_
    908 	    if (bestm && m->sun_reg && m->plug->max_ssf <= bestssf) {
    909 #else
    910 	    /* CRYPT DELETE END */
    911 	    /* EXPORT DELETE END */
    912 
    913 	    if (bestm && m->plug->max_ssf <= bestssf) {
    914 	    /* EXPORT DELETE START */
    915 	    /* CRYPT DELETE START */
    916 #endif /* _INTEGRATED_SOLARIS_ */
    917 	    /* CRYPT DELETE END */
    918 	    /* EXPORT DELETE END */
    919 
    920 		/* this mechanism is no better than what we already have! */
    921 		break;
    922 	    }
    923 #endif
    924 
    925 	    /* compare security flags, only take new mechanism if it has
    926 	     * all the security flags of the previous one.
    927 	     *
    928 	     * From the mechanisms we ship with, this yields the order:
    929 	     *
    930 	     * SRP
    931 	     * GSSAPI + KERBEROS_V4
    932 	     * DIGEST + OTP
    933 	     * CRAM + EXTERNAL
    934 	     * PLAIN + LOGIN + ANONYMOUS
    935 	     *
    936 	     * This might be improved on by comparing the numeric value of
    937 	     * the bitwise-or'd security flags, which splits DIGEST/OTP,
    938 	     * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
    939 	     * are depending on the numeric values of the flags (which may
    940 	     * change, and their ordering could be considered dumb luck.
    941 	     */
    942 
    943 	    if (bestm &&
    944 		((m->plug->security_flags ^ bestm->plug->security_flags) &
    945 		 bestm->plug->security_flags)) {
    946 		break;
    947 	    }
    948 
    949 	    if (mech) {
    950 		*mech = m->plug->mech_name;
    951 	    }
    952 	    /* EXPORT DELETE START */
    953 	    /* CRYPT DELETE START */
    954 #ifdef _INTEGRATED_SOLARIS_
    955 	    bestssf = m->sun_reg ? m->plug->max_ssf : 0;
    956 #else
    957 	    /* CRYPT DELETE END */
    958 	    /* EXPORT DELETE END */
    959 	    bestssf = m->plug->max_ssf;
    960 	    /* EXPORT DELETE START */
    961 	    /* CRYPT DELETE START */
    962 #endif /* _INTEGRATED_SOLARIS_ */
    963 	    /* CRYPT DELETE END */
    964 	    /* EXPORT DELETE END */
    965 	    bestm = m;
    966 	    break;
    967 	}
    968     }
    969 
    970     if (bestm == NULL) {
    971 #ifdef _INTEGRATED_SOLARIS_
    972 	sasl_seterror(conn, 0, gettext("No worthy mechs found"));
    973 #else
    974 	sasl_seterror(conn, 0, "No worthy mechs found");
    975 #endif /* _INTEGRATED_SOLARIS_ */
    976 	result = SASL_NOMECH;
    977 	goto done;
    978     }
    979 
    980     /* make (the rest of) cparams */
    981     c_conn->cparams->service = conn->service;
    982     c_conn->cparams->servicelen = strlen(conn->service);
    983 
    984     c_conn->cparams->serverFQDN = conn->serverFQDN;
    985     c_conn->cparams->slen = strlen(conn->serverFQDN);
    986 
    987     c_conn->cparams->clientFQDN = c_conn->clientFQDN;
    988     c_conn->cparams->clen = strlen(c_conn->clientFQDN);
    989 
    990     c_conn->cparams->external_ssf = conn->external.ssf;
    991     c_conn->cparams->props = conn->props;
    992     /* EXPORT DELETE START */
    993     /* CRYPT DELETE START */
    994 #ifdef _INTEGRATED_SOLARIS_
    995     if (!bestm->sun_reg) {
    996 	c_conn->cparams->props.min_ssf = 0;
    997 	c_conn->cparams->props.max_ssf = 0;
    998     }
    999     c_conn->base.sun_reg = bestm->sun_reg;
   1000 #endif /* _INTEGRATED_SOLARIS_ */
   1001     /* CRYPT DELETE END */
   1002     /* EXPORT DELETE END */
   1003     c_conn->mech = bestm;
   1004 
   1005     /* init that plugin */
   1006 #ifdef _SUN_SDK_
   1007     result = c_conn->mech->plug->mech_new(c_conn->mech->glob_context,
   1008 #else
   1009     result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context,
   1010 #endif /* _SUN_SDK_ */
   1011 					  c_conn->cparams,
   1012 					  &(conn->context));
   1013     if(result != SASL_OK) goto done;
   1014 
   1015     /* do a step -- but only if we can do a client-send-first */
   1016  dostep:
   1017     if(clientout) {
   1018         if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) {
   1019             *clientout = NULL;
   1020             *clientoutlen = 0;
   1021             result = SASL_CONTINUE;
   1022         } else {
   1023             result = sasl_client_step(conn, NULL, 0, prompt_need,
   1024                                       clientout, clientoutlen);
   1025         }
   1026     }
   1027     else
   1028 	result = SASL_CONTINUE;
   1029 
   1030  done:
   1031     RETURN(conn, result);
   1032 }
   1033 
   1034 /* do a single authentication step.
   1035  *  serverin    -- the server message received by the client, MUST have a NUL
   1036  *                 sentinel, not counted by serverinlen
   1037  * output:
   1038  *  prompt_need -- on SASL_INTERACT, list of prompts needed to continue
   1039  *  clientout   -- the client response to send to the server
   1040  *
   1041  * returns:
   1042  *  SASL_OK        -- success
   1043  *  SASL_INTERACT  -- user interaction needed to fill in prompt_need list
   1044  *  SASL_BADPROT   -- server protocol incorrect/cancelled
   1045  *  SASL_BADSERV   -- server failed mutual auth
   1046  */
   1047 
   1048 int sasl_client_step(sasl_conn_t *conn,
   1049 		     const char *serverin,
   1050 		     unsigned serverinlen,
   1051 		     sasl_interact_t **prompt_need,
   1052 		     const char **clientout,
   1053 		     unsigned *clientoutlen)
   1054 {
   1055   sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
   1056   int result;
   1057 
   1058 #ifdef _SUN_SDK_
   1059   _sasl_global_context_t *gctx = (conn == NULL) ?
   1060 		_sasl_gbl_ctx() : conn->gctx;
   1061 
   1062   if(gctx->sasl_client_active==0) return SASL_NOTINIT;
   1063 #else
   1064   if(_sasl_client_active==0) return SASL_NOTINIT;
   1065 #endif	/* _SUN_SDK_ */
   1066   if(!conn) return SASL_BADPARAM;
   1067 
   1068   /* check parameters */
   1069   if ((serverin==NULL) && (serverinlen>0))
   1070       PARAMERROR(conn);
   1071 
   1072   /* Don't do another step if the plugin told us that we're done */
   1073   if (conn->oparams.doneflag) {
   1074       _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
   1075       return SASL_FAIL;
   1076   }
   1077 
   1078   if(clientout) *clientout = NULL;
   1079   if(clientoutlen) *clientoutlen = 0;
   1080 
   1081   /* do a step */
   1082   result = c_conn->mech->plug->mech_step(conn->context,
   1083 					 c_conn->cparams,
   1084 					 serverin,
   1085 					 serverinlen,
   1086 					 prompt_need,
   1087 					 clientout, clientoutlen,
   1088 					 &conn->oparams);
   1089 
   1090   if (result == SASL_OK) {
   1091       /* So we're done on this end, but if both
   1092        * 1. the mech does server-send-last
   1093        * 2. the protocol does not
   1094        * we need to return no data */
   1095       if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
   1096 	  *clientout = "";
   1097 	  *clientoutlen = 0;
   1098       }
   1099 
   1100       if(!conn->oparams.maxoutbuf) {
   1101 	  conn->oparams.maxoutbuf = conn->props.maxbufsize;
   1102       }
   1103 
   1104       if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
   1105 #ifdef _SUN_SDK_
   1106 	_sasl_log(conn, SASL_LOG_ERR,
   1107 		  "mech did not call canon_user for both authzid and authid");
   1108 #else
   1109 	  sasl_seterror(conn, 0,
   1110 			"mech did not call canon_user for both authzid and authid");
   1111 #endif /* _SUN_SDK_ */
   1112 	  result = SASL_BADPROT;
   1113       }
   1114   }
   1115 
   1116   RETURN(conn,result);
   1117 }
   1118 
   1119 /* returns the length of all the mechanisms
   1120  * added up
   1121  */
   1122 
   1123 #ifdef _SUN_SDK_
   1124 static unsigned mech_names_len(_sasl_global_context_t *gctx)
   1125 {
   1126   cmech_list_t *cmechlist = gctx->cmechlist;
   1127 #else
   1128 static unsigned mech_names_len()
   1129 {
   1130 #endif /* _SUN_SDK_ */
   1131   cmechanism_t *listptr;
   1132   unsigned result = 0;
   1133 
   1134   for (listptr = cmechlist->mech_list;
   1135        listptr;
   1136        listptr = listptr->next)
   1137     result += strlen(listptr->plug->mech_name);
   1138 
   1139   return result;
   1140 }
   1141 
   1142 
   1143 int _sasl_client_listmech(sasl_conn_t *conn,
   1144 			  const char *prefix,
   1145 			  const char *sep,
   1146 			  const char *suffix,
   1147 			  const char **result,
   1148 			  unsigned *plen,
   1149 			  int *pcount)
   1150 {
   1151     cmechanism_t *m=NULL;
   1152     sasl_ssf_t minssf = 0;
   1153     int ret;
   1154     unsigned int resultlen;
   1155     int flag;
   1156     const char *mysep;
   1157 #ifdef _SUN_SDK_
   1158     _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
   1159     cmech_list_t *cmechlist;
   1160 
   1161     if(gctx->sasl_client_active==0) return SASL_NOTINIT;
   1162     cmechlist = gctx->cmechlist;
   1163 #else
   1164     if(_sasl_client_active == 0) return SASL_NOTINIT;
   1165 #endif /* _SUN_SDK_ */
   1166     if (!conn) return SASL_BADPARAM;
   1167     if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
   1168 
   1169     if (! result)
   1170 	PARAMERROR(conn);
   1171 
   1172 #ifdef _SUN_SDK_
   1173      (void) _load_client_plugins(gctx);
   1174 #endif /* _SUN_SDK_ */
   1175 
   1176     if (plen != NULL)
   1177 	*plen = 0;
   1178     if (pcount != NULL)
   1179 	*pcount = 0;
   1180 
   1181     if (sep) {
   1182 	mysep = sep;
   1183     } else {
   1184 	mysep = " ";
   1185     }
   1186 
   1187     if(conn->props.min_ssf < conn->external.ssf) {
   1188 	minssf = 0;
   1189     } else {
   1190 	minssf = conn->props.min_ssf - conn->external.ssf;
   1191     }
   1192 
   1193     if (! cmechlist || cmechlist->mech_length <= 0)
   1194 	INTERROR(conn, SASL_NOMECH);
   1195 
   1196     resultlen = (prefix ? strlen(prefix) : 0)
   1197 	+ (strlen(mysep) * (cmechlist->mech_length - 1))
   1198 #ifdef _SUN_SDK_
   1199 	+ mech_names_len(gctx)
   1200 #else
   1201 	+ mech_names_len()
   1202 #endif /* _SUN_SDK_ */
   1203 	+ (suffix ? strlen(suffix) : 0)
   1204 	+ 1;
   1205     ret = _buf_alloc(&conn->mechlist_buf,
   1206 		     &conn->mechlist_buf_len, resultlen);
   1207     if(ret != SASL_OK) MEMERROR(conn);
   1208 
   1209     if (prefix)
   1210 	strcpy (conn->mechlist_buf,prefix);
   1211     else
   1212 	*(conn->mechlist_buf) = '\0';
   1213 
   1214     flag = 0;
   1215     for (m = cmechlist->mech_list; m != NULL; m = m->next) {
   1216 	    /* do we have the prompts for it? */
   1217 	    if (!have_prompts(conn, m->plug))
   1218 		continue;
   1219 
   1220 	    /* is it strong enough? */
   1221 	    if (minssf > m->plug->max_ssf)
   1222 		continue;
   1223 
   1224 	    /* EXPORT DELETE START */
   1225 	    /* CRYPT DELETE START */
   1226 #ifdef _INTEGRATED_SOLARIS_
   1227 	    /* If not SUN supplied mech, it has no strength */
   1228 	    if (minssf > 0 && !m->sun_reg)
   1229 		continue;
   1230 #endif /* _INTEGRATED_SOLARIS_ */
   1231 	    /* CRYPT DELETE END */
   1232 	    /* EXPORT DELETE END */
   1233 
   1234 	    /* does it meet our security properties? */
   1235 	    if (((conn->props.security_flags ^ m->plug->security_flags)
   1236 		 & conn->props.security_flags) != 0) {
   1237 		continue;
   1238 	    }
   1239 
   1240 	    /* Can we meet it's features? */
   1241 	    if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
   1242 		&& !conn->serverFQDN) {
   1243 		continue;
   1244 	    }
   1245 
   1246 	    /* Can it meet our features? */
   1247 	    if ((conn->flags & SASL_NEED_PROXY) &&
   1248 		!(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
   1249 		break;
   1250 	    }
   1251 
   1252 	    /* Okay, we like it, add it to the list! */
   1253 
   1254 	    if (pcount != NULL)
   1255 		(*pcount)++;
   1256 
   1257 	    /* print seperator */
   1258 	    if (flag) {
   1259 		strcat(conn->mechlist_buf, mysep);
   1260 	    } else {
   1261 		flag = 1;
   1262 	    }
   1263 
   1264 	    /* now print the mechanism name */
   1265 	    strcat(conn->mechlist_buf, m->plug->mech_name);
   1266     }
   1267 
   1268   if (suffix)
   1269       strcat(conn->mechlist_buf,suffix);
   1270 
   1271   if (plen!=NULL)
   1272       *plen=strlen(conn->mechlist_buf);
   1273 
   1274   *result = conn->mechlist_buf;
   1275 
   1276   return SASL_OK;
   1277 }
   1278 
   1279 #ifdef _SUN_SDK_
   1280 sasl_string_list_t *_sasl_client_mechs(_sasl_global_context_t *gctx)
   1281 {
   1282   cmech_list_t *cmechlist = gctx->cmechlist;
   1283 #else
   1284 sasl_string_list_t *_sasl_client_mechs(void)
   1285 {
   1286 #endif /* _SUN_SDK_ */
   1287   cmechanism_t *listptr;
   1288   sasl_string_list_t *retval = NULL, *next=NULL;
   1289 
   1290 #ifdef _SUN_SDK_
   1291   if(!gctx->sasl_client_active) return NULL;
   1292 #else
   1293   if(!_sasl_client_active) return NULL;
   1294 #endif /* _SUN_SDK_ */
   1295 
   1296   /* make list */
   1297   for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
   1298       next = sasl_ALLOC(sizeof(sasl_string_list_t));
   1299 
   1300       if(!next && !retval) return NULL;
   1301       else if(!next) {
   1302 	  next = retval->next;
   1303 	  do {
   1304 	      sasl_FREE(retval);
   1305 	      retval = next;
   1306 	      next = retval->next;
   1307 	  } while(next);
   1308 	  return NULL;
   1309       }
   1310 
   1311       next->d = listptr->plug->mech_name;
   1312 
   1313       if(!retval) {
   1314 	  next->next = NULL;
   1315 	  retval = next;
   1316       } else {
   1317 	  next->next = retval;
   1318 	  retval = next;
   1319       }
   1320   }
   1321 
   1322   return retval;
   1323 }
   1324