xref: /illumos-gate/usr/src/lib/libsasl/lib/client.c (revision 7c478bd9)
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 }
1324