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