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@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 }