move more CB selection logic to libsasl
[cyrus-sasl.git] / lib / server.c
1 /* SASL server API implementation
2  * Rob Siemborski
3  * Tim Martin
4  * $Id: server.c,v 1.146 2006/04/26 17:45:53 murch 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 /* local functions/structs don't start with sasl
47  */
48 #include <config.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <limits.h>
53 #ifndef macintosh
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #endif
57 #include <fcntl.h>
58 #include <string.h>
59 #include <ctype.h>
60
61 #include "sasl.h"
62 #include "saslint.h"
63 #include "saslplug.h"
64 #include "saslutil.h"
65
66 #ifdef sun
67 /* gotta define gethostname ourselves on suns */
68 extern int gethostname(char *, int);
69 #endif
70
71 #define DEFAULT_CHECKPASS_MECH "auxprop"
72
73 /* Contains functions:
74  * 
75  * sasl_server_init
76  * sasl_server_new
77  * sasl_listmech
78  * sasl_server_start
79  * sasl_server_step
80  * sasl_checkpass
81  * sasl_checkapop
82  * sasl_user_exists
83  * sasl_setpass
84  */
85
86 /* if we've initialized the server sucessfully */
87 static int _sasl_server_active = 0;
88
89 /* For access by other modules */
90 int _is_sasl_server_active(void) { return _sasl_server_active; }
91
92 static int _sasl_checkpass(sasl_conn_t *conn, 
93                            const char *user, unsigned userlen,
94                            const char *pass, unsigned passlen);
95
96 static mech_list_t *mechlist = NULL; /* global var which holds the list */
97
98 sasl_global_callbacks_t global_callbacks;
99
100 /* set the password for a user
101  *  conn        -- SASL connection
102  *  user        -- user name
103  *  pass        -- plaintext password, may be NULL to remove user
104  *  passlen     -- length of password, 0 = strlen(pass)
105  *  oldpass     -- NULL will sometimes work
106  *  oldpasslen  -- length of password, 0 = strlen(oldpass)
107  *  flags       -- see flags below
108  * 
109  * returns:
110  *  SASL_NOCHANGE  -- proper entry already exists
111  *  SASL_NOMECH    -- no authdb supports password setting as configured
112  *  SASL_NOVERIFY  -- user exists, but no settable password present
113  *  SASL_DISABLED  -- account disabled
114  *  SASL_PWLOCK    -- password locked
115  *  SASL_WEAKPASS  -- password too weak for security policy
116  *  SASL_NOUSERPASS -- user-supplied passwords not permitted
117  *  SASL_FAIL      -- OS error
118  *  SASL_BADPARAM  -- password too long
119  *  SASL_OK        -- successful
120  */
121
122 int sasl_setpass(sasl_conn_t *conn,
123                  const char *user,
124                  const char *pass, unsigned passlen,
125                  const char *oldpass,
126                  unsigned oldpasslen,
127                  unsigned flags)
128 {
129     int result = SASL_OK, tmpresult;
130     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
131     const char *password_request[] = { SASL_AUX_PASSWORD_PROP, NULL };
132     sasl_server_userdb_setpass_t *setpass_cb = NULL;
133     void *context = NULL;
134     int tried_setpass = 0;
135     mechanism_t *sm;
136     server_sasl_mechanism_t *m;
137     char *current_mech;
138      
139     if (!_sasl_server_active || !mechlist) return SASL_NOTINIT;
140
141     /* check params */
142     if (!conn) return SASL_BADPARAM;
143     if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
144      
145     if ((!(flags & SASL_SET_DISABLE) && passlen == 0)
146         || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE)))
147         PARAMERROR(conn);
148
149     /* Check that we have an active SASL mechanism */
150     if (sasl_getprop (conn,
151                       SASL_MECHNAME,
152                       (const void **) &current_mech) != SASL_OK) {
153         current_mech = NULL;
154     }
155
156     if ( (flags & SASL_SET_CURMECH_ONLY) &&
157          (current_mech == NULL) ) {
158         sasl_seterror( conn, SASL_NOLOG,
159                   "No current SASL mechanism available");
160         RETURN(conn, SASL_BADPARAM);
161     }
162
163     /* Do we want to store SASL_AUX_PASSWORD_PROP (plain text)?  and
164      * Do we have an auxprop backend that can store properties?
165      */
166     if ((flags & SASL_SET_DISABLE || !(flags & SASL_SET_NOPLAIN)) &&
167         sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) {
168
169         tried_setpass++;
170
171         if (flags & SASL_SET_DISABLE) {
172             pass = NULL;
173             passlen = 0;
174         }
175
176         result = prop_request(s_conn->sparams->propctx, password_request);
177         if (result == SASL_OK) {
178             result = prop_set(s_conn->sparams->propctx, SASL_AUX_PASSWORD_PROP,
179                               pass, passlen);
180         }
181         if (result == SASL_OK) {
182             result = sasl_auxprop_store(conn, s_conn->sparams->propctx, user);
183         }
184         if (result != SASL_OK) {
185             _sasl_log(conn, SASL_LOG_ERR,
186                       "setpass failed for %s: %z",
187                       user, result);
188         } else {
189             _sasl_log(conn, SASL_LOG_NOTE,
190                       "setpass succeeded for %s", user);
191         }
192     }
193
194     /* We want to preserve the current value of result, so we use tmpresult below */
195
196     /* call userdb callback function */
197     tmpresult = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS,
198                                &setpass_cb, &context);
199     if (tmpresult == SASL_OK && setpass_cb) {
200
201         tried_setpass++;
202
203         tmpresult = setpass_cb(conn, context, user, pass, passlen,
204                             s_conn->sparams->propctx, flags);
205         if(tmpresult != SASL_OK) {
206             result = tmpresult;
207             _sasl_log(conn, SASL_LOG_ERR,
208                       "setpass callback failed for %s: %z",
209                       user, tmpresult);
210         } else {
211             _sasl_log(conn, SASL_LOG_NOTE,
212                       "setpass callback succeeded for %s", user);
213         }
214     }
215
216     /* now we let the mechanisms set their secrets */
217     for (sm = mechlist->mech_list; sm; sm = sm->next) {
218         m = &sm->m;
219
220         if (!m->plug->setpass) {
221             /* can't set pass for this mech */
222             continue;
223         }
224
225         /* Invoke only one setpass for the currently selected mechanism,
226            if SASL_SET_CURMECH_ONLY is specified */
227         if ((flags & SASL_SET_CURMECH_ONLY) &&
228             (strcmp(current_mech, m->plug->mech_name) != 0)) {
229             continue;
230         }
231
232         tried_setpass++;
233
234         tmpresult = m->plug->setpass(m->plug->glob_context,
235                                      ((sasl_server_conn_t *)conn)->sparams,
236                                      user,
237                                      pass,
238                                      passlen,
239                                      oldpass, oldpasslen,
240                                      flags);
241         if (tmpresult == SASL_OK) {
242             _sasl_log(conn, SASL_LOG_NOTE,
243                       "%s: set secret for %s", m->plug->mech_name, user);
244
245             m->condition = SASL_OK; /* if we previously thought the
246                                        mechanism didn't have any user secrets 
247                                        we now think it does */
248
249         } else if (tmpresult == SASL_NOCHANGE) {
250             _sasl_log(conn, SASL_LOG_NOTE,
251                       "%s: secret not changed for %s", m->plug->mech_name, user);
252         } else {
253             result = tmpresult;
254             _sasl_log(conn, SASL_LOG_ERR,
255                       "%s: failed to set secret for %s: %z (%m)",
256                       m->plug->mech_name, user, tmpresult,
257 #ifndef WIN32
258                       errno
259 #else
260                       GetLastError()
261 #endif
262                       );
263         }
264     }
265
266     if (!tried_setpass) {
267         _sasl_log(conn, SASL_LOG_WARN,
268                   "secret not changed for %s: "
269                   "no writable auxprop plugin or setpass callback found",
270                   user);
271     }
272
273     RETURN(conn, result);
274 }
275
276 /* local mechanism which disposes of server */
277 static void server_dispose(sasl_conn_t *pconn)
278 {
279   sasl_server_conn_t *s_conn=  (sasl_server_conn_t *) pconn;
280   context_list_t *cur, *cur_next;
281   
282   if (s_conn->mech
283       && s_conn->mech->m.plug->mech_dispose) {
284     s_conn->mech->m.plug->mech_dispose(pconn->context,
285                                      s_conn->sparams->utils);
286   }
287   pconn->context = NULL;
288
289   for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
290       cur_next = cur->next;
291       if(cur->context)
292           cur->mech->m.plug->mech_dispose(cur->context, s_conn->sparams->utils);
293       sasl_FREE(cur);
294   }  
295   s_conn->mech_contexts = NULL;
296   
297   _sasl_free_utils(&s_conn->sparams->utils);
298
299   if (s_conn->sparams->propctx)
300       prop_dispose(&s_conn->sparams->propctx);
301
302   if (s_conn->appname)
303       sasl_FREE(s_conn->appname);
304
305   if (s_conn->user_realm)
306       sasl_FREE(s_conn->user_realm);
307
308   if (s_conn->sparams)
309       sasl_FREE(s_conn->sparams);
310
311   _sasl_conn_dispose(pconn);
312 }
313
314 static int init_mechlist(void)
315 {
316     sasl_utils_t *newutils = NULL;
317
318     mechlist->mutex = sasl_MUTEX_ALLOC();
319     if(!mechlist->mutex) return SASL_FAIL;
320
321     /* set util functions - need to do rest */
322     newutils = _sasl_alloc_utils(NULL, &global_callbacks);
323     if (newutils == NULL)
324         return SASL_NOMEM;
325
326     newutils->checkpass = &_sasl_checkpass;
327
328     mechlist->utils = newutils;
329     mechlist->mech_list=NULL;
330     mechlist->mech_length=0;
331
332     return SASL_OK;
333 }
334
335 /*
336  * parameters:
337  *  p - entry point
338  */
339 int sasl_server_add_plugin(const char *plugname,
340                            sasl_server_plug_init_t *p)
341 {
342     int plugcount;
343     sasl_server_plug_t *pluglist;
344     mechanism_t *mech;
345     sasl_server_plug_init_t *entry_point;
346     int result;
347     int version;
348     int lupe;
349
350     if(!plugname || !p) return SASL_BADPARAM;
351
352     entry_point = (sasl_server_plug_init_t *)p;
353
354     /* call into the shared library asking for information about it */
355     /* version is filled in with the version of the plugin */
356     result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version,
357                          &pluglist, &plugcount);
358
359     if ((result != SASL_OK) && (result != SASL_NOUSER)
360         && (result != SASL_CONTINUE)) {
361         _sasl_log(NULL, SASL_LOG_DEBUG,
362                   "server add_plugin entry_point error %z\n", result);
363         return result;
364     }
365
366     /* Make sure plugin is using the same SASL version as us */
367     if (version != SASL_SERVER_PLUG_VERSION)
368     {
369         _sasl_log(NULL, SASL_LOG_ERR,
370                   "version mismatch on plugin");
371         return SASL_BADVERS;
372     }
373
374     for (lupe=0;lupe < plugcount ;lupe++)
375     {
376         mech = sasl_ALLOC(sizeof(mechanism_t));
377         if (! mech) return SASL_NOMEM;
378         memset (mech, 0, sizeof(mechanism_t));
379
380         mech->m.plug = pluglist++;
381         if(_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) {
382             sasl_FREE(mech);
383             return SASL_NOMEM;
384         }
385         mech->m.version = version;
386
387         /* wheather this mech actually has any users in it's db */
388         mech->m.condition = result; /* SASL_OK, SASL_CONTINUE or SASL_NOUSER */
389
390         /* mech->m.f = NULL; */
391
392         mech->next = mechlist->mech_list;
393         mechlist->mech_list = mech;
394         mechlist->mech_length++;
395     }
396
397     return SASL_OK;
398 }
399
400 static int server_done(void) {
401   mechanism_t *m;
402   mechanism_t *prevm;
403
404   if(_sasl_server_active == 0)
405       return SASL_NOTINIT;
406   else
407       _sasl_server_active--;
408   
409   if(_sasl_server_active) {
410       /* Don't de-init yet! Our refcount is nonzero. */
411       return SASL_CONTINUE;
412   }
413
414   if (mechlist != NULL)
415   {
416       m=mechlist->mech_list; /* m point to beginning of the list */
417
418       while (m!=NULL)
419       {
420           prevm=m;
421           m=m->next;
422     
423           if (prevm->m.plug->mech_free) {
424               prevm->m.plug->mech_free(prevm->m.plug->glob_context,
425                                      mechlist->utils);
426           }
427
428           sasl_FREE(prevm->m.plugname);           
429           sasl_FREE(prevm);    
430       }
431       _sasl_free_utils(&mechlist->utils);
432       sasl_MUTEX_FREE(mechlist->mutex);
433       sasl_FREE(mechlist);
434       mechlist = NULL;
435   }
436
437   /* Free the auxprop plugins */
438   _sasl_auxprop_free();
439
440   global_callbacks.callbacks = NULL;
441   global_callbacks.appname = NULL;
442
443   return SASL_OK;
444 }
445
446 static int server_idle(sasl_conn_t *conn)
447 {
448     mechanism_t *m;
449     if (! mechlist)
450         return 0;
451     
452     for (m = mechlist->mech_list;
453          m != NULL;
454          m = m->next)
455         if (m->m.plug->idle
456             &&  m->m.plug->idle(m->m.plug->glob_context,
457                               conn,
458                               conn ? ((sasl_server_conn_t *)conn)->sparams : NULL))
459             return 1;
460
461     return 0;
462 }
463
464 static int load_config(const sasl_callback_t *verifyfile_cb)
465 {
466     int result;
467     const char *path_to_config = NULL;
468     size_t path_len;
469     char *config_filename = NULL;
470     size_t len;
471     const sasl_callback_t *getconfpath_cb = NULL;
472     const char * next;
473
474     /* If appname was not provided, behave as if there is no config file 
475         (see also sasl_config_init() */
476     if (global_callbacks.appname == NULL) {
477         return SASL_CONTINUE;
478     }
479
480     /* get the path to the config file */
481     getconfpath_cb = _sasl_find_getconfpath_callback( global_callbacks.callbacks );
482     if (getconfpath_cb == NULL) return SASL_BADPARAM;
483
484     /* getconfpath_cb->proc MUST be a sasl_getconfpath_t; if only C had a type
485        system */
486     result = ((sasl_getconfpath_t *)(getconfpath_cb->proc))(getconfpath_cb->context,
487                                                     &path_to_config);
488     if (result != SASL_OK) goto done;
489     if (path_to_config == NULL) path_to_config = "";
490
491     next = path_to_config;
492
493     while (next != NULL) {
494         next = strchr(path_to_config, PATHS_DELIMITER);
495
496         /* length = length of path + '/' + length of appname + ".conf" + 1
497             for '\0' */
498
499         if (next != NULL) {
500             path_len = next - path_to_config;
501             next++; /* Skip to the next path */
502         } else {
503             path_len = strlen(path_to_config);
504         }
505
506         len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1;
507
508         if (len > PATH_MAX ) {
509             result = SASL_FAIL;
510             goto done;
511         }
512
513         /* construct the filename for the config file */
514         config_filename = sasl_ALLOC((unsigned)len);
515         if (! config_filename) {
516             result = SASL_NOMEM;
517             goto done;
518         }
519
520         snprintf(config_filename, len, "%.*s%c%s.conf", path_len, path_to_config, 
521                 HIER_DELIMITER, global_callbacks.appname);
522
523         /* Ask the application if it's safe to use this file */
524         result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
525                                                 config_filename, SASL_VRFY_CONF);
526
527         /* returns SASL_CONTINUE if the config file doesn't exist */
528         if (result == SASL_OK) {
529             result = sasl_config_init(config_filename);
530
531             if (result != SASL_CONTINUE) {
532                 /* We are done */
533                 break;
534             }
535         }
536
537         if (config_filename) {
538             sasl_FREE(config_filename);
539             config_filename = NULL;
540         }
541
542         path_to_config = next;
543     }
544
545  done:
546     if (config_filename) sasl_FREE(config_filename);
547
548     return result;
549 }
550
551 /*
552  * Verify that all the callbacks are valid
553  */
554 static int verify_server_callbacks(const sasl_callback_t *callbacks)
555 {
556     if (callbacks == NULL) return SASL_OK;
557
558     while (callbacks->id != SASL_CB_LIST_END) {
559         if (callbacks->proc==NULL) return SASL_FAIL;
560
561         callbacks++;
562     }
563
564     return SASL_OK;
565 }
566
567 static char *grab_field(char *line, char **eofield)
568 {
569     int d = 0;
570     char *field;
571
572     while (isspace((int) *line)) line++;
573
574     /* find end of field */
575     while (line[d] && !isspace(((int) line[d]))) d++;
576     field = sasl_ALLOC(d + 1);
577     if (!field) { return NULL; }
578     memcpy(field, line, d);
579     field[d] = '\0';
580     *eofield = line + d;
581     
582     return field;
583 }
584
585 struct secflag_map_s {
586     char *name;
587     int value;
588 };
589
590 struct secflag_map_s secflag_map[] = {
591     { "noplaintext", SASL_SEC_NOPLAINTEXT },
592     { "noactive", SASL_SEC_NOACTIVE },
593     { "nodictionary", SASL_SEC_NODICTIONARY },
594     { "forward_secrecy", SASL_SEC_FORWARD_SECRECY },
595     { "noanonymous", SASL_SEC_NOANONYMOUS },
596     { "pass_credentials", SASL_SEC_PASS_CREDENTIALS },
597     { "mutual_auth", SASL_SEC_MUTUAL_AUTH },
598     { NULL, 0x0 }
599 };
600
601 static int parse_mechlist_file(const char *mechlistfile)
602 {
603     FILE *f;
604     char buf[1024];
605     char *t, *ptr;
606     int r = 0;
607
608     f = fopen(mechlistfile, "r");
609     if (!f) return SASL_FAIL;
610
611     r = SASL_OK;
612     while (fgets(buf, sizeof(buf), f) != NULL) {
613         mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t));
614         sasl_server_plug_t *nplug;
615
616         if (n == NULL) { r = SASL_NOMEM; break; }
617         n->m.version = SASL_SERVER_PLUG_VERSION;
618         n->m.condition = SASL_CONTINUE;
619         nplug = sasl_ALLOC(sizeof(sasl_server_plug_t));
620         if (nplug == NULL) { r = SASL_NOMEM; break; }
621         memset(nplug, 0, sizeof(sasl_server_plug_t));
622
623         /* each line is:
624            plugin-file WS mech_name WS max_ssf *(WS security_flag) RET
625         */
626         
627         /* grab file */
628         n->m.f = grab_field(buf, &ptr);
629
630         /* grab mech_name */
631         nplug->mech_name = grab_field(ptr, &ptr);
632
633         /* grab max_ssf */
634         nplug->max_ssf = strtol(ptr, &ptr, 10);
635
636         /* grab security flags */
637         while (*ptr != '\n') {
638             struct secflag_map_s *map;
639
640             /* read security flag */
641             t = grab_field(ptr, &ptr);
642             map = secflag_map;
643             while (map->name) {
644                 if (!strcasecmp(t, map->name)) {
645                     nplug->security_flags |= map->value;
646                     break;
647                 }
648                 map++;
649             }
650             if (!map->name) {
651                 _sasl_log(NULL, SASL_LOG_ERR,
652                           "%s: couldn't identify flag '%s'",
653                           nplug->mech_name, t);
654             }
655             free(t);
656         }
657
658         /* insert mechanism into mechlist */
659         n->m.plug = nplug;
660         n->next = mechlist->mech_list;
661         mechlist->mech_list = n;
662         mechlist->mech_length++;
663     }
664
665     fclose(f);
666     return r;
667 }
668
669 /* initialize server drivers, done once per process
670  *  callbacks      -- callbacks for all server connections; must include
671  *                    getopt callback
672  *  appname        -- name of calling application
673  *                    (for lower level logging and reading of the configuration file)
674  * results:
675  *  state          -- server state
676  * returns:
677  *  SASL_OK        -- success
678  *  SASL_BADPARAM  -- error in config file
679  *  SASL_NOMEM     -- memory failure
680  *  SASL_BADVERS   -- Mechanism version mismatch
681  */
682
683 int sasl_server_init(const sasl_callback_t *callbacks,
684                      const char *appname)
685 {
686     int ret;
687     const sasl_callback_t *vf;
688     const char *pluginfile = NULL;
689 #ifdef PIC
690     sasl_getopt_t *getopt;
691     void *context;
692 #endif
693
694     const add_plugin_list_t ep_list[] = {
695         { "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin },
696         { "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin },
697         { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
698         { NULL, NULL }
699     };
700
701     /* we require the appname (if present) to be short enough to be a path */
702     if (appname != NULL && strlen(appname) >= PATH_MAX)
703         return SASL_BADPARAM;
704
705     if (_sasl_server_active) {
706         /* We're already active, just increase our refcount */
707         /* xxx do something with the callback structure? */
708         _sasl_server_active++;
709         return SASL_OK;
710     }
711     
712     ret = _sasl_common_init(&global_callbacks);
713     if (ret != SASL_OK)
714         return ret;
715  
716     /* verify that the callbacks look ok */
717     ret = verify_server_callbacks(callbacks);
718     if (ret != SASL_OK)
719         return ret;
720
721     global_callbacks.callbacks = callbacks;
722     
723     /* A shared library calling sasl_server_init will pass NULL as appname.
724        This should retain the original appname. */
725     if (appname != NULL) {
726         global_callbacks.appname = appname;
727     }
728
729     /* If we fail now, we have to call server_done */
730     _sasl_server_active = 1;
731
732     /* allocate mechlist and set it to empty */
733     mechlist = sasl_ALLOC(sizeof(mech_list_t));
734     if (mechlist == NULL) {
735         server_done();
736         return SASL_NOMEM;
737     }
738
739     ret = init_mechlist();
740     if (ret != SASL_OK) {
741         server_done();
742         return ret;
743     }
744
745     vf = _sasl_find_verifyfile_callback(callbacks);
746
747     /* load config file if applicable */
748     ret = load_config(vf);
749     if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
750         server_done();
751         return ret;
752     }
753
754     /* load internal plugins */
755     sasl_server_add_plugin("EXTERNAL", &external_server_plug_init);
756
757 #ifdef PIC
758     /* delayed loading of plugins? (DSO only, as it doesn't
759      * make much [any] sense to delay in the static library case) */
760     if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) 
761            == SASL_OK) {
762         /* No sasl_conn_t was given to getcallback, so we provide the
763          * global callbacks structure */
764         ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL);
765     }
766 #endif
767     
768     if (pluginfile != NULL) {
769         /* this file should contain a list of plugins available.
770            we'll load on demand. */
771
772         /* Ask the application if it's safe to use this file */
773         ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context,
774                                                 pluginfile,
775                                                 SASL_VRFY_CONF);
776         if (ret != SASL_OK) {
777             _sasl_log(NULL, SASL_LOG_ERR,
778                       "unable to load plugin list %s: %z", pluginfile, ret);
779         }
780         
781         if (ret == SASL_OK) {
782             ret = parse_mechlist_file(pluginfile);
783         }
784     } else {
785         /* load all plugins now */
786         ret = _sasl_load_plugins(ep_list,
787                                  _sasl_find_getpath_callback(callbacks),
788                                  _sasl_find_verifyfile_callback(callbacks));
789     }
790
791     if (ret == SASL_OK) {
792         _sasl_server_cleanup_hook = &server_done;
793         _sasl_server_idle_hook = &server_idle;
794
795         ret = _sasl_build_mechlist();
796     } else {
797         server_done();
798     }
799
800     return ret;
801 }
802
803 /*
804  * Once we have the users plaintext password we 
805  * may want to transition them. That is put entries
806  * for them in the passwd database for other
807  * stronger mechanism
808  *
809  * for example PLAIN -> CRAM-MD5
810  */
811 static int
812 _sasl_transition(sasl_conn_t * conn,
813                  const char * pass,
814                  unsigned passlen)
815 {
816     const char *dotrans = "n";
817     sasl_getopt_t *getopt;
818     int result = SASL_OK;
819     void *context;
820     unsigned flags = 0;
821
822     if (! conn)
823         return SASL_BADPARAM;
824
825     if (! conn->oparams.authid)
826         PARAMERROR(conn);
827
828     /* check if this is enabled: default to false */
829     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
830     {
831         getopt(context, NULL, "auto_transition", &dotrans, NULL);
832         if (dotrans == NULL) dotrans = "n";
833     }
834
835
836     if (!strcmp(dotrans, "noplain")) flags |= SASL_SET_NOPLAIN;
837
838     if (flags || *dotrans == '1' || *dotrans == 'y' ||
839         (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') {
840         /* ok, it's on! */
841         _sasl_log(conn, SASL_LOG_NOTE, 
842                   "transitioning user %s to auxprop database",
843                   conn->oparams.authid);
844         result = sasl_setpass(conn,
845                               conn->oparams.authid,
846                               pass,
847                               passlen,
848                               NULL, 0, SASL_SET_CREATE | flags);
849     }
850
851     RETURN(conn,result);
852 }
853
854
855 /* create context for a single SASL connection
856  *  service        -- registered name of the service using SASL (e.g. "imap")
857  *  serverFQDN     -- Fully qualified domain name of server.  NULL means use
858  *                    gethostname() or equivalent.
859  *                    Useful for multi-homed servers.
860  *  user_realm     -- permits multiple user realms on server, NULL = default
861  *  iplocalport    -- server IPv4/IPv6 domain literal string with port
862  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
863  *  ipremoteport   -- client IPv4/IPv6 domain literal string with port
864  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
865  *  callbacks      -- callbacks (e.g., authorization, lang, new getopt context)
866  *  flags          -- usage flags (see above)
867  * returns:
868  *  pconn          -- new connection context
869  *
870  * returns:
871  *  SASL_OK        -- success
872  *  SASL_NOMEM     -- not enough memory
873  */
874
875 int sasl_server_new(const char *service,
876                     const char *serverFQDN,
877                     const char *user_realm,
878                     const char *iplocalport,
879                     const char *ipremoteport,
880                     const sasl_callback_t *callbacks,
881                     unsigned flags,
882                     sasl_conn_t **pconn)
883 {
884   int result;
885   sasl_server_conn_t *serverconn;
886   sasl_utils_t *utils;
887   sasl_getopt_t *getopt;
888   void *context;
889   const char *log_level, *auto_trans;
890
891   if (_sasl_server_active==0) return SASL_NOTINIT;
892   if (! pconn) return SASL_FAIL;
893   if (! service) return SASL_FAIL;
894
895   *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t));
896   if (*pconn==NULL) return SASL_NOMEM;
897
898   memset(*pconn, 0, sizeof(sasl_server_conn_t));
899
900   serverconn = (sasl_server_conn_t *)*pconn;
901
902   /* make sparams */
903   serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t));
904   if (serverconn->sparams==NULL)
905       MEMERROR(*pconn);
906
907   memset(serverconn->sparams, 0, sizeof(sasl_server_params_t));
908
909   (*pconn)->destroy_conn = &server_dispose;
910   result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER,
911                            &server_idle, serverFQDN,
912                            iplocalport, ipremoteport,
913                            callbacks, &global_callbacks);
914   if (result != SASL_OK)
915       goto done_error;
916
917
918   /* set util functions - need to do rest */
919   utils=_sasl_alloc_utils(*pconn, &global_callbacks);
920   if (!utils) {
921       result = SASL_NOMEM;
922       goto done_error;
923   }
924   
925   utils->checkpass = &_sasl_checkpass;
926
927   /* Setup the propctx -> We'll assume the default size */
928   serverconn->sparams->propctx=prop_new(0);
929   if(!serverconn->sparams->propctx) {
930       result = SASL_NOMEM;
931       goto done_error;
932   }
933
934   serverconn->sparams->service = (*pconn)->service;
935   serverconn->sparams->servicelen = (unsigned) strlen((*pconn)->service);
936
937   if (global_callbacks.appname && global_callbacks.appname[0] != '\0') {
938     result = _sasl_strdup (global_callbacks.appname,
939                            &serverconn->appname,
940                            NULL);
941     if (result != SASL_OK) {
942       result = SASL_NOMEM;
943       goto done_error;
944     }
945     serverconn->sparams->appname = serverconn->appname;
946     serverconn->sparams->applen = (unsigned) strlen(serverconn->sparams->appname);
947   } else {
948     serverconn->appname = NULL;
949     serverconn->sparams->appname = NULL;
950     serverconn->sparams->applen = 0;
951   }
952
953   serverconn->sparams->serverFQDN = (*pconn)->serverFQDN;
954   serverconn->sparams->slen = (unsigned) strlen((*pconn)->serverFQDN);
955
956   if (user_realm) {
957       result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL);
958       serverconn->sparams->urlen = (unsigned) strlen(user_realm);
959       serverconn->sparams->user_realm = serverconn->user_realm;
960   } else {
961       serverconn->user_realm = NULL;
962       /* the sparams is already zeroed */
963   }
964
965   serverconn->sparams->callbacks = callbacks;
966
967   log_level = auto_trans = NULL;
968   if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
969     getopt(context, NULL, "log_level", &log_level, NULL);
970     getopt(context, NULL, "auto_transition", &auto_trans, NULL);
971   }
972   serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR;
973
974   serverconn->sparams->utils = utils;
975
976   if (auto_trans &&
977       (*auto_trans == '1' || *auto_trans == 'y' || *auto_trans == 't' ||
978        (*auto_trans == 'o' && auto_trans[1] == 'n') ||
979        !strcmp(auto_trans, "noplain")) &&
980       sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) {
981       serverconn->sparams->transition = &_sasl_transition;
982   }
983
984   serverconn->sparams->canon_user = &_sasl_canon_user;
985   serverconn->sparams->props = serverconn->base.props;
986   serverconn->sparams->flags = flags;
987
988   if(result == SASL_OK) return SASL_OK;
989
990  done_error:
991   _sasl_conn_dispose(*pconn);
992   sasl_FREE(*pconn);
993   *pconn = NULL;
994   return result;
995 }
996
997 /*
998  * The rule is:
999  * IF mech strength + external strength < min ssf THEN FAIL
1000  * We also have to look at the security properties and make sure
1001  * that this mechanism has everything we want
1002  */
1003 static int mech_permitted(sasl_conn_t *conn,
1004                           mechanism_t *mech)
1005 {
1006     sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn;
1007     const sasl_server_plug_t *plug;
1008     int ret;
1009     int myflags;
1010     context_list_t *cur;
1011     sasl_getopt_t *getopt;
1012     void *context;
1013     sasl_ssf_t minssf = 0;
1014
1015     if(!conn) return SASL_NOMECH;
1016
1017     if(! mech || ! mech->m.plug) {
1018         PARAMERROR(conn);
1019         return SASL_NOMECH;
1020     }
1021     
1022     plug = mech->m.plug;
1023
1024     /* get the list of allowed mechanisms (default = all) */
1025     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1026             == SASL_OK) {
1027         const char *mlist = NULL;
1028
1029         getopt(context, NULL, "mech_list", &mlist, NULL);
1030
1031         /* if we have a list, check the plugin against it */
1032         if (mlist) {
1033             const char *cp;
1034
1035             while (*mlist) {
1036                 for (cp = mlist; *cp && !isspace((int) *cp); cp++);
1037                 if (((size_t) (cp - mlist) == strlen(plug->mech_name)) &&
1038                     !strncasecmp(mlist, plug->mech_name,
1039                                  strlen(plug->mech_name))) {
1040                     break;
1041                 }
1042                 mlist = cp;
1043                 while (*mlist && isspace((int) *mlist)) mlist++;
1044             }
1045
1046             if (!*mlist) return SASL_NOMECH;  /* reached EOS -> not in our list */
1047         }
1048     }
1049
1050     /* setup parameters for the call to mech_avail */
1051     s_conn->sparams->serverFQDN=conn->serverFQDN;
1052     s_conn->sparams->service=conn->service;
1053     s_conn->sparams->user_realm=s_conn->user_realm;
1054     s_conn->sparams->props=conn->props;
1055     s_conn->sparams->external_ssf=conn->external.ssf;
1056
1057     /* Check if we have banished this one already */
1058     for(cur = s_conn->mech_contexts; cur; cur=cur->next) {
1059         if(cur->mech == mech) {
1060             /* If it's not mech_avail'd, then stop now */
1061             if(!cur->context) return SASL_NOMECH;
1062             break;
1063         }
1064     }
1065     
1066     if (conn->props.min_ssf < conn->external.ssf) {
1067         minssf = 0;
1068     } else {
1069         minssf = conn->props.min_ssf - conn->external.ssf;
1070     }
1071     
1072     /* Generic mechanism */
1073     if (plug->max_ssf < minssf) {
1074         sasl_seterror(conn, SASL_NOLOG,
1075                       "mech %s is too weak", plug->mech_name);
1076         return SASL_TOOWEAK; /* too weak */
1077     }
1078
1079     context = NULL;
1080     if(plug->mech_avail
1081        && (ret = plug->mech_avail(plug->glob_context,
1082                            s_conn->sparams, (void **)&context)) != SASL_OK ) {
1083         if(ret == SASL_NOMECH) {
1084             /* Mark this mech as no good for this connection */
1085             cur = sasl_ALLOC(sizeof(context_list_t));
1086             if(!cur) {
1087                 MEMERROR(conn);
1088                 return SASL_NOMECH;
1089             }
1090             cur->context = NULL;
1091             cur->mech = mech;
1092             cur->next = s_conn->mech_contexts;
1093             s_conn->mech_contexts = cur;
1094         }
1095         
1096         /* SASL_NOTDONE might also get us here */
1097
1098         /* Error should be set by mech_avail call */
1099         return SASL_NOMECH;
1100     } else if(context) {
1101         /* Save this context */
1102         cur = sasl_ALLOC(sizeof(context_list_t));
1103         if(!cur) {
1104             MEMERROR(conn);
1105             return SASL_NOMECH;
1106         }
1107         cur->context = context;
1108         cur->mech = mech;
1109         cur->next = s_conn->mech_contexts;
1110         s_conn->mech_contexts = cur;
1111     }
1112     
1113     /* Generic mechanism */
1114     if (plug->max_ssf < minssf) {
1115         sasl_seterror(conn, SASL_NOLOG, "too weak");
1116         return SASL_TOOWEAK; /* too weak */
1117     }
1118
1119     /* if there are no users in the secrets database we can't use this 
1120        mechanism */
1121     if (mech->m.condition == SASL_NOUSER) {
1122         sasl_seterror(conn, 0, "no users in secrets db");
1123         return SASL_NOMECH;
1124     }
1125
1126     /* Can it meet our features? */
1127     if ((conn->flags & SASL_NEED_PROXY) &&
1128         !(plug->features & SASL_FEAT_ALLOWS_PROXY)) {
1129         return SASL_NOMECH;
1130     }
1131     
1132     /* security properties---if there are any flags that differ and are
1133        in what the connection are requesting, then fail */
1134     
1135     /* special case plaintext */
1136     myflags = conn->props.security_flags;
1137
1138     /* if there's an external layer this is no longer plaintext */
1139     if ((conn->props.min_ssf <= conn->external.ssf) && 
1140         (conn->external.ssf > 1)) {
1141         myflags &= ~SASL_SEC_NOPLAINTEXT;
1142     }
1143
1144     /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */
1145     if ((myflags &= (myflags ^ plug->security_flags)) != 0) {
1146         sasl_seterror(conn, SASL_NOLOG,
1147                       "security flags do not match required");
1148         return (myflags & SASL_SEC_NOPLAINTEXT) ? SASL_ENCRYPT : SASL_NOMECH;
1149     }
1150
1151     /* Check Features */
1152     if(plug->features & SASL_FEAT_GETSECRET) {
1153         /* We no longer support sasl_server_{get,put}secret */
1154         sasl_seterror(conn, 0,
1155                       "mech %s requires unprovided secret facility",
1156                       plug->mech_name);
1157         return SASL_NOMECH;
1158     }
1159
1160     return SASL_OK;
1161 }
1162
1163 /*
1164  * make the authorization 
1165  *
1166  */
1167
1168 static int do_authorization(sasl_server_conn_t *s_conn)
1169 {
1170     int ret;
1171     sasl_authorize_t *authproc;
1172     void *auth_context;
1173     
1174     /* now let's see if authname is allowed to proxy for username! */
1175     
1176     /* check the proxy callback */
1177     if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY,
1178                           &authproc, &auth_context) != SASL_OK) {
1179         INTERROR(&s_conn->base, SASL_NOAUTHZ);
1180     }
1181
1182     ret = authproc(&(s_conn->base), auth_context,
1183                    s_conn->base.oparams.user, s_conn->base.oparams.ulen,
1184                    s_conn->base.oparams.authid, s_conn->base.oparams.alen,
1185                    s_conn->user_realm,
1186                    (s_conn->user_realm ? (unsigned) strlen(s_conn->user_realm) : 0),
1187                    s_conn->sparams->propctx);
1188
1189     RETURN(&s_conn->base, ret);
1190 }
1191
1192
1193 /* start a mechanism exchange within a connection context
1194  *  mech           -- the mechanism name client requested
1195  *  clientin       -- client initial response (NUL terminated), NULL if empty
1196  *  clientinlen    -- length of initial response
1197  *  serverout      -- initial server challenge, NULL if done 
1198  *                    (library handles freeing this string)
1199  *  serveroutlen   -- length of initial server challenge
1200  * output:
1201  *  pconn          -- the connection negotiation state on success
1202  *
1203  * Same returns as sasl_server_step() or
1204  * SASL_NOMECH if mechanism not available.
1205  */
1206 int sasl_server_start(sasl_conn_t *conn,
1207                       const char *mech,
1208                       const char *clientin,
1209                       unsigned clientinlen,
1210                       const char **serverout,
1211                       unsigned *serveroutlen)
1212 {
1213     sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn;
1214     int result;
1215     context_list_t *cur, **prev;
1216     mechanism_t *m;
1217     int plus = 0;
1218
1219     if (_sasl_server_active==0) return SASL_NOTINIT;
1220
1221     /* make sure mech is valid mechanism
1222        if not return appropriate error */
1223     m=mechlist->mech_list;
1224
1225     /* check parameters */
1226     if(!conn) return SASL_BADPARAM;
1227     
1228     if (!mech || ((clientin==NULL) && (clientinlen>0)))
1229         PARAMERROR(conn);
1230
1231     if(serverout) *serverout = NULL;
1232     if(serveroutlen) *serveroutlen = 0;
1233
1234     while (m != NULL) {
1235         if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, &plus))
1236             break;
1237
1238         m = m->next;
1239     }
1240   
1241     if (m==NULL) {
1242         sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
1243         result = SASL_NOMECH;
1244         goto done;
1245     }
1246
1247     /* Make sure that we're willing to use this mech */
1248     if ((result = mech_permitted(conn, m)) != SASL_OK) {
1249         goto done;
1250     }
1251
1252     if (m->m.condition == SASL_CONTINUE) {
1253         sasl_server_plug_init_t *entry_point;
1254         void *library = NULL;
1255         sasl_server_plug_t *pluglist;
1256         int version, plugcount;
1257         int l = 0;
1258
1259         /* need to load this plugin */
1260         result = _sasl_get_plugin(m->m.f,
1261                     _sasl_find_verifyfile_callback(global_callbacks.callbacks),
1262                                   &library);
1263
1264         if (result == SASL_OK) {
1265             result = _sasl_locate_entry(library, "sasl_server_plug_init",
1266                                         (void **)&entry_point);
1267         }
1268
1269         if (result == SASL_OK) {
1270             result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
1271                                  &version, &pluglist, &plugcount);
1272         }
1273
1274         if (result == SASL_OK) {
1275             /* find the correct mechanism in this plugin */
1276             for (l = 0; l < plugcount; l++) {
1277                 if (!strcasecmp(pluglist[l].mech_name, 
1278                                 m->m.plug->mech_name)) break;
1279             }
1280             if (l == plugcount) {
1281                 result = SASL_NOMECH;
1282             }
1283         }
1284         if (result == SASL_OK) {
1285             /* check that the parameters are the same */
1286             if ((pluglist[l].max_ssf != m->m.plug->max_ssf) ||
1287                 (pluglist[l].security_flags != m->m.plug->security_flags)) {
1288                 _sasl_log(conn, SASL_LOG_ERR, 
1289                           "%s: security parameters don't match mechlist file",
1290                           pluglist[l].mech_name);
1291                 result = SASL_NOMECH;
1292             }
1293         }
1294         if (result == SASL_OK) {
1295             /* copy mechlist over */
1296             sasl_FREE((sasl_server_plug_t *) m->m.plug);
1297             m->m.plug = &pluglist[l];
1298             m->m.condition = SASL_OK;
1299         }
1300
1301         if (result != SASL_OK) {
1302             /* The library will eventually be freed, don't sweat it */
1303             RETURN(conn, result);
1304         }
1305     }
1306
1307     /* We used to setup sparams HERE, but now it's done
1308        inside of mech_permitted (which is called above) */
1309     prev = &s_conn->mech_contexts;
1310     for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
1311         if(cur->mech == m) {
1312             if(!cur->context) {
1313                 sasl_seterror(conn, 0,
1314                               "Got past mech_permitted with a disallowed mech!");
1315                 return SASL_NOMECH;
1316             }
1317             /* If we find it, we need to pull cur out of the
1318                list so it won't be freed later! */
1319             (*prev)->next = cur->next;
1320             conn->context = cur->context;
1321             sasl_FREE(cur);
1322         }
1323     }
1324
1325     s_conn->mech = m;
1326     
1327     if(!conn->context) {
1328         /* Note that we don't hand over a new challenge */
1329         result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context,
1330                                               s_conn->sparams,
1331                                               NULL,
1332                                               0,
1333                                               &(conn->context));
1334     } else {
1335         /* the work was already done by mech_avail! */
1336         result = SASL_OK;
1337     }
1338     
1339     if (result == SASL_OK) {
1340          if(clientin) {
1341             if(s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
1342                 /* Remote sent first, but mechanism does not support it.
1343                  * RFC 2222 says we fail at this point. */
1344                 sasl_seterror(conn, 0,
1345                               "Remote sent first but mech does not allow it.");
1346                 result = SASL_BADPROT;
1347             } else {
1348                 /* Mech wants client-first, so let them have it */
1349                 result = sasl_server_step(conn,
1350                                           clientin, clientinlen,
1351                                           serverout, serveroutlen);
1352             }
1353         } else {
1354             if(s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1355                 /* Mech wants client first anyway, so we should do that */
1356                 *serverout = "";
1357                 *serveroutlen = 0;
1358                 result = SASL_CONTINUE;
1359             } else {
1360                 /* Mech wants server-first, so let them have it */
1361                 result = sasl_server_step(conn,
1362                                           clientin, clientinlen,
1363                                           serverout, serveroutlen);
1364             }
1365         }
1366     }
1367
1368  done:
1369     if(   result != SASL_OK
1370        && result != SASL_CONTINUE
1371        && result != SASL_INTERACT) {
1372         if(conn->context) {
1373             s_conn->mech->m.plug->mech_dispose(conn->context,
1374                                              s_conn->sparams->utils);
1375             conn->context = NULL;
1376         }
1377     }
1378     
1379     RETURN(conn,result);
1380 }
1381
1382
1383 /* perform one step of the SASL exchange
1384  *  inputlen & input -- client data
1385  *                      NULL on first step if no optional client step
1386  *  outputlen & output -- set to the server data to transmit
1387  *                        to the client in the next step
1388  *                        (library handles freeing this)
1389  *
1390  * returns:
1391  *  SASL_OK        -- exchange is complete.
1392  *  SASL_CONTINUE  -- indicates another step is necessary.
1393  *  SASL_TRANS     -- entry for user exists, but not for mechanism
1394  *                    and transition is possible
1395  *  SASL_BADPARAM  -- service name needed
1396  *  SASL_BADPROT   -- invalid input from client
1397  *  ...
1398  */
1399
1400 int sasl_server_step(sasl_conn_t *conn,
1401                      const char *clientin,
1402                      unsigned clientinlen,
1403                      const char **serverout,
1404                      unsigned *serveroutlen)
1405 {
1406     int ret;
1407     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;  /* cast */
1408
1409     /* check parameters */
1410     if (_sasl_server_active==0) return SASL_NOTINIT;
1411     if (!conn) return SASL_BADPARAM;
1412     if ((clientin==NULL) && (clientinlen>0))
1413         PARAMERROR(conn);
1414
1415     /* If we've already done the last send, return! */
1416     if(s_conn->sent_last == 1) {
1417         return SASL_OK;
1418     }
1419
1420     /* Don't do another step if the plugin told us that we're done */
1421     if (conn->oparams.doneflag) {
1422         _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
1423         return SASL_FAIL;
1424     }
1425
1426     if(serverout) *serverout = NULL;
1427     if(serveroutlen) *serveroutlen = 0;
1428
1429     ret = s_conn->mech->m.plug->mech_step(conn->context,
1430                                         s_conn->sparams,
1431                                         clientin,
1432                                         clientinlen,
1433                                         serverout,
1434                                         serveroutlen,
1435                                         &conn->oparams);
1436
1437     if (ret == SASL_OK) {
1438         ret = do_authorization(s_conn);
1439     }
1440
1441     if (ret == SASL_OK) {
1442         /* if we're done, we need to watch out for the following:
1443          * 1. the mech does server-send-last
1444          * 2. the protocol does not
1445          *
1446          * in this case, return SASL_CONTINUE and remember we are done.
1447          */
1448         if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
1449             s_conn->sent_last = 1;
1450             ret = SASL_CONTINUE;
1451         }
1452         if(!conn->oparams.maxoutbuf) {
1453             conn->oparams.maxoutbuf = conn->props.maxbufsize;
1454         }
1455
1456         if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1457             sasl_seterror(conn, 0,
1458                           "mech did not call canon_user for both authzid " \
1459                           "and authid");
1460             ret = SASL_BADPROT;
1461         }       
1462     }
1463     
1464     if(   ret != SASL_OK
1465        && ret != SASL_CONTINUE
1466        && ret != SASL_INTERACT) {
1467         if(conn->context) {
1468             s_conn->mech->m.plug->mech_dispose(conn->context,
1469                                              s_conn->sparams->utils);
1470             conn->context = NULL;
1471         }
1472     }
1473
1474     RETURN(conn, ret);
1475 }
1476
1477 /* returns the length of all the mechanisms
1478  * added up 
1479  */
1480
1481 static unsigned mech_names_len()
1482 {
1483   mechanism_t *listptr;
1484   unsigned result = 0;
1485
1486   for (listptr = mechlist->mech_list;
1487        listptr;
1488        listptr = listptr->next)
1489     result += (unsigned) strlen(listptr->m.plug->mech_name);
1490
1491   return result;
1492 }
1493
1494 /* This returns a list of mechanisms in a NUL-terminated string
1495  *
1496  * The default behavior is to seperate with spaces if sep==NULL
1497  */
1498 int _sasl_server_listmech(sasl_conn_t *conn,
1499                           const char *user __attribute__((unused)),
1500                           const char *prefix,
1501                           const char *sep,
1502                           const char *suffix,
1503                           const char **result,
1504                           unsigned *plen,
1505                           int *pcount)
1506 {
1507   int lup;
1508   mechanism_t *listptr;
1509   int ret;
1510   size_t resultlen;
1511   int flag;
1512   const char *mysep;
1513   sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;  /* cast */
1514
1515   /* if there hasn't been a sasl_sever_init() fail */
1516   if (_sasl_server_active==0) return SASL_NOTINIT;
1517   if (!conn) return SASL_BADPARAM;
1518   if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
1519   
1520   if (! result)
1521       PARAMERROR(conn);
1522
1523   if (plen != NULL)
1524       *plen = 0;
1525   if (pcount != NULL)
1526       *pcount = 0;
1527
1528   if (sep) {
1529       mysep = sep;
1530   } else {
1531       mysep = " ";
1532   }
1533
1534   if (! mechlist || mechlist->mech_length <= 0)
1535       INTERROR(conn, SASL_NOMECH);
1536
1537   resultlen = (prefix ? strlen(prefix) : 0)
1538             + (strlen(mysep) * (mechlist->mech_length - 1) * 2)
1539             + (mech_names_len() * 2) /* including -PLUS variant */
1540             + (mechlist->mech_length * (sizeof("-PLUS") - 1))
1541             + (suffix ? strlen(suffix) : 0)
1542             + 1;
1543   ret = _buf_alloc(&conn->mechlist_buf,
1544                    &conn->mechlist_buf_len, resultlen);
1545   if(ret != SASL_OK) MEMERROR(conn);
1546
1547   if (prefix)
1548     strcpy (conn->mechlist_buf,prefix);
1549   else
1550     *(conn->mechlist_buf) = '\0';
1551
1552   listptr = mechlist->mech_list;  
1553    
1554   flag = 0;
1555   /* make list */
1556   for (lup = 0; lup < mechlist->mech_length; lup++) {
1557       /* currently, we don't use the "user" parameter for anything */
1558       if (mech_permitted(conn, listptr) == SASL_OK) {
1559           if (pcount != NULL)
1560               (*pcount)++;
1561
1562           /* print separator */
1563           if (flag) {
1564               strcat(conn->mechlist_buf, mysep);
1565           } else {
1566               flag = 1;
1567           }
1568
1569           /* now print the mechanism name */
1570           strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1571
1572           /* advertise -PLUS variant if mechanism and application support CB */
1573           if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) &&
1574               SASL_CB_PRESENT(s_conn->sparams)) {
1575             if (pcount != NULL)
1576                 (*pcount)++;
1577             strcat(conn->mechlist_buf, mysep);
1578             strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1579             strcat(conn->mechlist_buf, "-PLUS");
1580           }
1581       }
1582
1583       listptr = listptr->next;
1584   }
1585
1586   if (suffix)
1587       strcat(conn->mechlist_buf,suffix);
1588
1589   if (plen!=NULL)
1590       *plen = (unsigned) strlen(conn->mechlist_buf);
1591
1592   *result = conn->mechlist_buf;
1593
1594   return SASL_OK;  
1595 }
1596
1597 sasl_string_list_t *_sasl_server_mechs(void) 
1598 {
1599   mechanism_t *listptr;
1600   sasl_string_list_t *retval = NULL, *next=NULL;
1601
1602   if(!_sasl_server_active) return NULL;
1603
1604   /* make list */
1605   for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1606       next = sasl_ALLOC(sizeof(sasl_string_list_t));
1607
1608       if(!next && !retval) return NULL;
1609       else if(!next) {
1610           next = retval->next;
1611           do {
1612               sasl_FREE(retval);
1613               retval = next;
1614               next = retval->next;
1615           } while(next);
1616           return NULL;
1617       }
1618       
1619       next->d = listptr->m.plug->mech_name;
1620
1621       if(!retval) {
1622           next->next = NULL;
1623           retval = next;
1624       } else {
1625           next->next = retval;
1626           retval = next;
1627       }
1628   }
1629
1630   return retval;
1631 }
1632
1633 #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1634 static int is_mech(const char *t, const char *m)
1635 {
1636     size_t sl = strlen(m);
1637     return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1638 }
1639
1640 /* returns OK if it's valid */
1641 static int _sasl_checkpass(sasl_conn_t *conn,
1642                            const char *user,
1643                            unsigned userlen,
1644                            const char *pass,
1645                            unsigned passlen)
1646 {
1647     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1648     int result;
1649     sasl_getopt_t *getopt;
1650     sasl_server_userdb_checkpass_t *checkpass_cb;
1651     void *context;
1652     const char *mlist = NULL, *mech = NULL;
1653     struct sasl_verify_password_s *v;
1654     const char *service = conn->service;
1655
1656     if (!userlen) userlen = (unsigned) strlen(user);
1657     if (!passlen) passlen = (unsigned) strlen(pass);
1658
1659     /* call userdb callback function, if available */
1660     result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1661                                &checkpass_cb, &context);
1662     if(result == SASL_OK && checkpass_cb) {
1663         result = checkpass_cb(conn, context, user, pass, passlen,
1664                               s_conn->sparams->propctx);
1665         if(result == SASL_OK)
1666             return SASL_OK;
1667     }
1668
1669     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1670     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1671             == SASL_OK) {
1672         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1673     }
1674
1675     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1676
1677     result = SASL_NOMECH;
1678
1679     mech = mlist;
1680     while (*mech && result != SASL_OK) {
1681         for (v = _sasl_verify_password; v->name; v++) {
1682             if(is_mech(mech, v->name)) {
1683                 result = v->verify(conn, user, pass, service,
1684                                    s_conn->user_realm);
1685                 break;
1686             }
1687         }
1688         if (result != SASL_OK) {
1689             /* skip to next mech in list */
1690             while (*mech && !isspace((int) *mech)) mech++;
1691             while (*mech && isspace((int) *mech)) mech++;
1692         }
1693         else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1694             s_conn->sparams->transition(conn, pass, passlen);
1695         }
1696     }
1697
1698     if (result == SASL_NOMECH) {
1699         /* no mechanism available ?!? */
1700         _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1701     }
1702
1703     if (result != SASL_OK)
1704         sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1705
1706     RETURN(conn, result);
1707 }
1708
1709 /* check if a plaintext password is valid
1710  *   if user is NULL, check if plaintext passwords are enabled
1711  * inputs:
1712  *  user          -- user to query in current user_domain
1713  *  userlen       -- length of username, 0 = strlen(user)
1714  *  pass          -- plaintext password to check
1715  *  passlen       -- length of password, 0 = strlen(pass)
1716  * returns 
1717  *  SASL_OK       -- success
1718  *  SASL_NOMECH   -- mechanism not supported
1719  *  SASL_NOVERIFY -- user found, but no verifier
1720  *  SASL_NOUSER   -- user not found
1721  */
1722 int sasl_checkpass(sasl_conn_t *conn,
1723                    const char *user,
1724                    unsigned userlen,
1725                    const char *pass,
1726                    unsigned passlen)
1727 {
1728     int result;
1729     
1730     if (_sasl_server_active==0) return SASL_NOTINIT;
1731     
1732     /* check if it's just a query if we are enabled */
1733     if (!user)
1734         return SASL_OK;
1735
1736     if (!conn) return SASL_BADPARAM;
1737     
1738     /* check params */
1739     if (pass == NULL)
1740         PARAMERROR(conn);
1741
1742     /* canonicalize the username */
1743     result = _sasl_canon_user(conn, user, userlen,
1744                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1745                               &(conn->oparams));
1746     if(result != SASL_OK) RETURN(conn, result);
1747     user = conn->oparams.user;
1748
1749     /* Check the password */
1750     result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1751
1752     /* Do authorization */
1753     if(result == SASL_OK) {
1754       result = do_authorization((sasl_server_conn_t *)conn);
1755     }
1756
1757     RETURN(conn,result);
1758 }
1759
1760 /* check if a user exists on server
1761  *  conn          -- connection context (may be NULL, used to hold last error)
1762  *  service       -- registered name of the service using SASL (e.g. "imap")
1763  *  user_realm    -- permits multiple user realms on server, NULL = default
1764  *  user          -- NUL terminated user name
1765  *
1766  * returns:
1767  *  SASL_OK       -- success
1768  *  SASL_DISABLED -- account disabled [FIXME: currently not detected]
1769  *  SASL_NOUSER   -- user not found
1770  *  SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1771  *  SASL_NOMECH   -- no mechanisms enabled
1772  */
1773 int sasl_user_exists(sasl_conn_t *conn,
1774                      const char *service,
1775                      const char *user_realm,
1776                      const char *user) 
1777 {
1778     int result=SASL_NOMECH;
1779     const char *mlist = NULL, *mech = NULL;
1780     void *context;
1781     sasl_getopt_t *getopt;
1782     struct sasl_verify_password_s *v;
1783     
1784     /* check params */
1785     if (_sasl_server_active==0) return SASL_NOTINIT;
1786     if (!conn) return SASL_BADPARAM;
1787     if (!user || conn->type != SASL_CONN_SERVER) 
1788         PARAMERROR(conn);
1789
1790     if(!service) service = conn->service;
1791     
1792     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1793     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1794             == SASL_OK) {
1795         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1796     }
1797
1798     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1799
1800     result = SASL_NOMECH;
1801
1802     mech = mlist;
1803     while (*mech && result != SASL_OK) {
1804         for (v = _sasl_verify_password; v->name; v++) {
1805             if(is_mech(mech, v->name)) {
1806                 result = v->verify(conn, user, NULL, service, user_realm);
1807                 break;
1808             }
1809         }
1810         if (result != SASL_OK) {
1811             /* skip to next mech in list */
1812             while (*mech && !isspace((int) *mech)) mech++;
1813             while (*mech && isspace((int) *mech)) mech++;
1814         }
1815     }
1816
1817     /* Screen out the SASL_BADPARAM response
1818      * we'll get from not giving a password */
1819     if(result == SASL_BADPARAM) {
1820         result = SASL_OK;
1821     }
1822
1823     if (result == SASL_NOMECH) {
1824         /* no mechanism available ?!? */
1825         _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1826         sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1827     }
1828
1829     RETURN(conn, result);
1830 }
1831
1832 /* check if an apop exchange is valid
1833  *  (note this is an optional part of the SASL API)
1834  *  if challenge is NULL, just check if APOP is enabled
1835  * inputs:
1836  *  challenge     -- challenge which was sent to client
1837  *  challen       -- length of challenge, 0 = strlen(challenge)
1838  *  response      -- client response, "<user> <digest>" (RFC 1939)
1839  *  resplen       -- length of response, 0 = strlen(response)
1840  * returns 
1841  *  SASL_OK       -- success
1842  *  SASL_BADAUTH  -- authentication failed
1843  *  SASL_BADPARAM -- missing challenge
1844  *  SASL_BADPROT  -- protocol error (e.g., response in wrong format)
1845  *  SASL_NOVERIFY -- user found, but no verifier
1846  *  SASL_NOMECH   -- mechanism not supported
1847  *  SASL_NOUSER   -- user not found
1848  */
1849 int sasl_checkapop(sasl_conn_t *conn,
1850 #ifdef DO_SASL_CHECKAPOP
1851                    const char *challenge,
1852                    unsigned challen __attribute__((unused)),
1853                    const char *response,
1854                    unsigned resplen __attribute__((unused)))
1855 #else
1856                    const char *challenge __attribute__((unused)),
1857                    unsigned challen __attribute__((unused)),
1858                    const char *response __attribute__((unused)),
1859                    unsigned resplen __attribute__((unused)))
1860 #endif
1861 {
1862 #ifdef DO_SASL_CHECKAPOP
1863     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1864     char *user, *user_end;
1865     const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1866     size_t user_len;
1867     int result;
1868
1869     if (_sasl_server_active==0)
1870         return SASL_NOTINIT;
1871
1872     /* check if it's just a query if we are enabled */
1873     if(!challenge)
1874         return SASL_OK;
1875
1876     /* check params */
1877     if (!conn) return SASL_BADPARAM;
1878     if (!response)
1879         PARAMERROR(conn);
1880
1881     /* Parse out username and digest.
1882      *
1883      * Per RFC 1939, response must be "<user> <digest>", where
1884      * <digest> is a 16-octet value which is sent in hexadecimal
1885      * format, using lower-case ASCII characters.
1886      */
1887     user_end = strrchr(response, ' ');
1888     if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) 
1889     {
1890         sasl_seterror(conn, 0, "Bad Digest");
1891         RETURN(conn,SASL_BADPROT);
1892     }
1893  
1894     user_len = (size_t)(user_end - response);
1895     user = sasl_ALLOC(user_len + 1);
1896     memcpy(user, response, user_len);
1897     user[user_len] = '\0';
1898
1899     result = prop_request(s_conn->sparams->propctx, password_request);
1900     if(result != SASL_OK) 
1901     {
1902         sasl_FREE(user);
1903         RETURN(conn, result);
1904     }
1905
1906     /* erase the plaintext password */
1907     s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1908                                        password_request[0]);
1909
1910     /* Cannonify it */
1911     result = _sasl_canon_user(conn, user, user_len,
1912                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1913                               &(conn->oparams));
1914     sasl_FREE(user);
1915
1916     if(result != SASL_OK) RETURN(conn, result);
1917
1918     /* Do APOP verification */
1919     result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1920         challenge, user_end + 1, s_conn->user_realm);
1921
1922     /* Do authorization */
1923     if(result == SASL_OK) {
1924       result = do_authorization((sasl_server_conn_t *)conn);
1925     } else {
1926         /* If verification failed, we don't want to encourage getprop to work */
1927         conn->oparams.user = NULL;
1928         conn->oparams.authid = NULL;
1929     }
1930
1931     RETURN(conn, result);
1932 #else /* sasl_checkapop was disabled at compile time */
1933     sasl_seterror(conn, SASL_NOLOG,
1934         "sasl_checkapop called, but was disabled at compile time");
1935     RETURN(conn, SASL_NOMECH);
1936 #endif /* DO_SASL_CHECKAPOP */
1937 }
1938
1939 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
1940 static void
1941 _sasl_print_mechanism (
1942   server_sasl_mechanism_t *m,
1943   sasl_info_callback_stage_t stage,
1944   void *rock
1945 )
1946 {
1947     char delimiter;
1948
1949     if (stage == SASL_INFO_LIST_START) {
1950         printf ("List of server plugins follows\n");
1951         return;
1952     } else if (stage == SASL_INFO_LIST_END) {
1953         return;
1954     }
1955
1956     /* Process the mechanism */
1957     printf ("Plugin \"%s\" ", m->plugname);
1958
1959     switch (m->condition) {
1960         case SASL_OK:
1961             printf ("[loaded]");
1962             break;
1963
1964         case SASL_CONTINUE:
1965             printf ("[delayed]");
1966             break;
1967
1968         case SASL_NOUSER:
1969             printf ("[no users]");
1970             break;
1971
1972         default:
1973             printf ("[unknown]");
1974             break;
1975     }
1976
1977     printf (", \tAPI version: %d\n", m->version);
1978
1979     if (m->plug != NULL) {
1980         printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
1981                 m->plug->mech_name,
1982                 m->plug->max_ssf,
1983                 (m->plug->setpass != NULL) ? "yes" : "no"
1984                 );
1985
1986
1987         printf ("\tsecurity flags:");
1988         
1989         delimiter = ' ';
1990         if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
1991             printf ("%cNO_ANONYMOUS", delimiter);
1992             delimiter = '|';
1993         }
1994
1995         if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
1996             printf ("%cNO_PLAINTEXT", delimiter);
1997             delimiter = '|';
1998         }
1999         
2000         if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
2001             printf ("%cNO_ACTIVE", delimiter);
2002             delimiter = '|';
2003         }
2004
2005         if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
2006             printf ("%cNO_DICTIONARY", delimiter);
2007             delimiter = '|';
2008         }
2009
2010         if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2011             printf ("%cFORWARD_SECRECY", delimiter);
2012             delimiter = '|';
2013         }
2014
2015         if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2016             printf ("%cPASS_CREDENTIALS", delimiter);
2017             delimiter = '|';
2018         }
2019
2020         if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2021             printf ("%cMUTUAL_AUTH", delimiter);
2022             delimiter = '|';
2023         }
2024
2025
2026
2027         printf ("\n\tfeatures:");
2028         
2029         delimiter = ' ';
2030         if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2031             printf ("%cWANT_CLIENT_FIRST", delimiter);
2032             delimiter = '|';
2033         }
2034
2035         if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2036             printf ("%cSERVER_FIRST", delimiter);
2037             delimiter = '|';
2038         }
2039
2040         if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2041             printf ("%cPROXY_AUTHENTICATION", delimiter);
2042             delimiter = '|';
2043         }
2044
2045         if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2046             printf ("%cNEED_SERVER_FQDN", delimiter);
2047             delimiter = '|';
2048         }
2049
2050         /* Is this one used? */
2051         if (m->plug->features & SASL_FEAT_SERVICE) {
2052             printf ("%cSERVICE", delimiter);
2053             delimiter = '|';
2054         }
2055
2056         if (m->plug->features & SASL_FEAT_GETSECRET) {
2057             printf ("%cNEED_GETSECRET", delimiter);
2058             delimiter = '|';
2059         }
2060
2061         if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
2062             printf ("%cGSS_FRAMING", delimiter);
2063             delimiter = '|';
2064         }
2065
2066         if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
2067             printf ("%cCHANNEL_BINDING", delimiter);
2068             delimiter = '|';
2069         }
2070     }
2071
2072     if (m->f) {
2073         printf ("\n\twill be loaded from \"%s\"", m->f);
2074     }
2075
2076     printf ("\n");
2077 }
2078
2079 /* Dump information about available server plugins (separate functions should be
2080    used for canon and auxprop plugins */
2081 int sasl_server_plugin_info (
2082   const char *c_mech_list,              /* space separated mechanism list or NULL for ALL */
2083   sasl_server_info_callback_t *info_cb,
2084   void *info_cb_rock
2085 )
2086 {
2087     mechanism_t *m;
2088     server_sasl_mechanism_t plug_data;
2089     char * cur_mech;
2090     char *mech_list = NULL;
2091     char * p;
2092
2093     if (info_cb == NULL) {
2094         info_cb = _sasl_print_mechanism;
2095     }
2096
2097     if (mechlist != NULL) {
2098         info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2099
2100         if (c_mech_list == NULL) {
2101             m = mechlist->mech_list; /* m point to beginning of the list */
2102
2103             while (m != NULL) {
2104                 memcpy (&plug_data, &m->m, sizeof(plug_data));
2105
2106                 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2107             
2108                 m = m->next;
2109             }
2110         } else {
2111             mech_list = strdup(c_mech_list);
2112
2113             cur_mech = mech_list;
2114
2115             while (cur_mech != NULL) {
2116                 p = strchr (cur_mech, ' ');
2117                 if (p != NULL) {
2118                     *p = '\0';
2119                     p++;
2120                 }
2121
2122                 m = mechlist->mech_list; /* m point to beginning of the list */
2123
2124                 while (m != NULL) {
2125                     if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2126                         memcpy (&plug_data, &m->m, sizeof(plug_data));
2127
2128                         info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2129                     }
2130             
2131                     m = m->next;
2132                 }
2133
2134                 cur_mech = p;
2135             }
2136
2137             free (mech_list);
2138         }
2139
2140         info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2141
2142         return (SASL_OK);
2143     }
2144
2145     return (SASL_NOTINIT);
2146 }