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