1533c107705730725c59b06cc446932ca58ba9d8
[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
1218     if (_sasl_server_active==0) return SASL_NOTINIT;
1219
1220     /* make sure mech is valid mechanism
1221        if not return appropriate error */
1222     m=mechlist->mech_list;
1223
1224     /* check parameters */
1225     if(!conn) return SASL_BADPARAM;
1226     
1227     if (!mech || ((clientin==NULL) && (clientinlen>0)))
1228         PARAMERROR(conn);
1229
1230     if(serverout) *serverout = NULL;
1231     if(serveroutlen) *serveroutlen = 0;
1232
1233     while (m!=NULL)
1234     {
1235         if ( strcasecmp(mech, m->m.plug->mech_name)==0)
1236         {
1237             break;
1238         }
1239         m=m->next;
1240     }
1241   
1242     if (m==NULL) {
1243         sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
1244         result = SASL_NOMECH;
1245         goto done;
1246     }
1247
1248     /* Make sure that we're willing to use this mech */
1249     if ((result = mech_permitted(conn, m)) != SASL_OK) {
1250         goto done;
1251     }
1252
1253     if (m->m.condition == SASL_CONTINUE) {
1254         sasl_server_plug_init_t *entry_point;
1255         void *library = NULL;
1256         sasl_server_plug_t *pluglist;
1257         int version, plugcount;
1258         int l = 0;
1259
1260         /* need to load this plugin */
1261         result = _sasl_get_plugin(m->m.f,
1262                     _sasl_find_verifyfile_callback(global_callbacks.callbacks),
1263                                   &library);
1264
1265         if (result == SASL_OK) {
1266             result = _sasl_locate_entry(library, "sasl_server_plug_init",
1267                                         (void **)&entry_point);
1268         }
1269
1270         if (result == SASL_OK) {
1271             result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
1272                                  &version, &pluglist, &plugcount);
1273         }
1274
1275         if (result == SASL_OK) {
1276             /* find the correct mechanism in this plugin */
1277             for (l = 0; l < plugcount; l++) {
1278                 if (!strcasecmp(pluglist[l].mech_name, 
1279                                 m->m.plug->mech_name)) break;
1280             }
1281             if (l == plugcount) {
1282                 result = SASL_NOMECH;
1283             }
1284         }
1285         if (result == SASL_OK) {
1286             /* check that the parameters are the same */
1287             if ((pluglist[l].max_ssf != m->m.plug->max_ssf) ||
1288                 (pluglist[l].security_flags != m->m.plug->security_flags)) {
1289                 _sasl_log(conn, SASL_LOG_ERR, 
1290                           "%s: security parameters don't match mechlist file",
1291                           pluglist[l].mech_name);
1292                 result = SASL_NOMECH;
1293             }
1294         }
1295         if (result == SASL_OK) {
1296             /* copy mechlist over */
1297             sasl_FREE((sasl_server_plug_t *) m->m.plug);
1298             m->m.plug = &pluglist[l];
1299             m->m.condition = SASL_OK;
1300         }
1301
1302         if (result != SASL_OK) {
1303             /* The library will eventually be freed, don't sweat it */
1304             RETURN(conn, result);
1305         }
1306     }
1307
1308     /* We used to setup sparams HERE, but now it's done
1309        inside of mech_permitted (which is called above) */
1310     prev = &s_conn->mech_contexts;
1311     for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
1312         if(cur->mech == m) {
1313             if(!cur->context) {
1314                 sasl_seterror(conn, 0,
1315                               "Got past mech_permitted with a disallowed mech!");
1316                 return SASL_NOMECH;
1317             }
1318             /* If we find it, we need to pull cur out of the
1319                list so it won't be freed later! */
1320             (*prev)->next = cur->next;
1321             conn->context = cur->context;
1322             sasl_FREE(cur);
1323         }
1324     }
1325
1326     s_conn->mech = m;
1327     
1328     if(!conn->context) {
1329         /* Note that we don't hand over a new challenge */
1330         result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context,
1331                                               s_conn->sparams,
1332                                               NULL,
1333                                               0,
1334                                               &(conn->context));
1335     } else {
1336         /* the work was already done by mech_avail! */
1337         result = SASL_OK;
1338     }
1339     
1340     if (result == SASL_OK) {
1341          if(clientin) {
1342             if(s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
1343                 /* Remote sent first, but mechanism does not support it.
1344                  * RFC 2222 says we fail at this point. */
1345                 sasl_seterror(conn, 0,
1346                               "Remote sent first but mech does not allow it.");
1347                 result = SASL_BADPROT;
1348             } else {
1349                 /* Mech wants client-first, so let them have it */
1350                 result = sasl_server_step(conn,
1351                                           clientin, clientinlen,
1352                                           serverout, serveroutlen);
1353             }
1354         } else {
1355             if(s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1356                 /* Mech wants client first anyway, so we should do that */
1357                 *serverout = "";
1358                 *serveroutlen = 0;
1359                 result = SASL_CONTINUE;
1360             } else {
1361                 /* Mech wants server-first, so let them have it */
1362                 result = sasl_server_step(conn,
1363                                           clientin, clientinlen,
1364                                           serverout, serveroutlen);
1365             }
1366         }
1367     }
1368
1369  done:
1370     if(   result != SASL_OK
1371        && result != SASL_CONTINUE
1372        && result != SASL_INTERACT) {
1373         if(conn->context) {
1374             s_conn->mech->m.plug->mech_dispose(conn->context,
1375                                              s_conn->sparams->utils);
1376             conn->context = NULL;
1377         }
1378     }
1379     
1380     RETURN(conn,result);
1381 }
1382
1383
1384 /* perform one step of the SASL exchange
1385  *  inputlen & input -- client data
1386  *                      NULL on first step if no optional client step
1387  *  outputlen & output -- set to the server data to transmit
1388  *                        to the client in the next step
1389  *                        (library handles freeing this)
1390  *
1391  * returns:
1392  *  SASL_OK        -- exchange is complete.
1393  *  SASL_CONTINUE  -- indicates another step is necessary.
1394  *  SASL_TRANS     -- entry for user exists, but not for mechanism
1395  *                    and transition is possible
1396  *  SASL_BADPARAM  -- service name needed
1397  *  SASL_BADPROT   -- invalid input from client
1398  *  ...
1399  */
1400
1401 int sasl_server_step(sasl_conn_t *conn,
1402                      const char *clientin,
1403                      unsigned clientinlen,
1404                      const char **serverout,
1405                      unsigned *serveroutlen)
1406 {
1407     int ret;
1408     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;  /* cast */
1409
1410     /* check parameters */
1411     if (_sasl_server_active==0) return SASL_NOTINIT;
1412     if (!conn) return SASL_BADPARAM;
1413     if ((clientin==NULL) && (clientinlen>0))
1414         PARAMERROR(conn);
1415
1416     /* If we've already done the last send, return! */
1417     if(s_conn->sent_last == 1) {
1418         return SASL_OK;
1419     }
1420
1421     /* Don't do another step if the plugin told us that we're done */
1422     if (conn->oparams.doneflag) {
1423         _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
1424         return SASL_FAIL;
1425     }
1426
1427     if(serverout) *serverout = NULL;
1428     if(serveroutlen) *serveroutlen = 0;
1429
1430     ret = s_conn->mech->m.plug->mech_step(conn->context,
1431                                         s_conn->sparams,
1432                                         clientin,
1433                                         clientinlen,
1434                                         serverout,
1435                                         serveroutlen,
1436                                         &conn->oparams);
1437
1438     if (ret == SASL_OK) {
1439         ret = do_authorization(s_conn);
1440     }
1441
1442     if (ret == SASL_OK) {
1443         /* if we're done, we need to watch out for the following:
1444          * 1. the mech does server-send-last
1445          * 2. the protocol does not
1446          *
1447          * in this case, return SASL_CONTINUE and remember we are done.
1448          */
1449         if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
1450             s_conn->sent_last = 1;
1451             ret = SASL_CONTINUE;
1452         }
1453         if(!conn->oparams.maxoutbuf) {
1454             conn->oparams.maxoutbuf = conn->props.maxbufsize;
1455         }
1456
1457         if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1458             sasl_seterror(conn, 0,
1459                           "mech did not call canon_user for both authzid " \
1460                           "and authid");
1461             ret = SASL_BADPROT;
1462         }       
1463     }
1464     
1465     if(   ret != SASL_OK
1466        && ret != SASL_CONTINUE
1467        && ret != SASL_INTERACT) {
1468         if(conn->context) {
1469             s_conn->mech->m.plug->mech_dispose(conn->context,
1470                                              s_conn->sparams->utils);
1471             conn->context = NULL;
1472         }
1473     }
1474
1475     RETURN(conn, ret);
1476 }
1477
1478 /* returns the length of all the mechanisms
1479  * added up 
1480  */
1481
1482 static unsigned mech_names_len()
1483 {
1484   mechanism_t *listptr;
1485   unsigned result = 0;
1486
1487   for (listptr = mechlist->mech_list;
1488        listptr;
1489        listptr = listptr->next)
1490     result += (unsigned) strlen(listptr->m.plug->mech_name);
1491
1492   return result;
1493 }
1494
1495 /* This returns a list of mechanisms in a NUL-terminated string
1496  *
1497  * The default behavior is to seperate with spaces if sep==NULL
1498  */
1499 int _sasl_server_listmech(sasl_conn_t *conn,
1500                           const char *user __attribute__((unused)),
1501                           const char *prefix,
1502                           const char *sep,
1503                           const char *suffix,
1504                           const char **result,
1505                           unsigned *plen,
1506                           int *pcount)
1507 {
1508   int lup;
1509   mechanism_t *listptr;
1510   int ret;
1511   size_t resultlen;
1512   int flag;
1513   const char *mysep;
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))
1539             + mech_names_len()
1540             + (suffix ? strlen(suffix) : 0)
1541             + 1;
1542   ret = _buf_alloc(&conn->mechlist_buf,
1543                    &conn->mechlist_buf_len, resultlen);
1544   if(ret != SASL_OK) MEMERROR(conn);
1545
1546   if (prefix)
1547     strcpy (conn->mechlist_buf,prefix);
1548   else
1549     *(conn->mechlist_buf) = '\0';
1550
1551   listptr = mechlist->mech_list;  
1552    
1553   flag = 0;
1554   /* make list */
1555   for (lup = 0; lup < mechlist->mech_length; lup++) {
1556       /* currently, we don't use the "user" parameter for anything */
1557       if (mech_permitted(conn, listptr) == SASL_OK) {
1558           if (pcount != NULL)
1559               (*pcount)++;
1560
1561           /* print separator */
1562           if (flag) {
1563               strcat(conn->mechlist_buf, mysep);
1564           } else {
1565               flag = 1;
1566           }
1567
1568           /* now print the mechanism name */
1569           strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1570       }
1571
1572       listptr = listptr->next;
1573   }
1574
1575   if (suffix)
1576       strcat(conn->mechlist_buf,suffix);
1577
1578   if (plen!=NULL)
1579       *plen = (unsigned) strlen(conn->mechlist_buf);
1580
1581   *result = conn->mechlist_buf;
1582
1583   return SASL_OK;  
1584 }
1585
1586 sasl_string_list_t *_sasl_server_mechs(void) 
1587 {
1588   mechanism_t *listptr;
1589   sasl_string_list_t *retval = NULL, *next=NULL;
1590
1591   if(!_sasl_server_active) return NULL;
1592
1593   /* make list */
1594   for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1595       next = sasl_ALLOC(sizeof(sasl_string_list_t));
1596
1597       if(!next && !retval) return NULL;
1598       else if(!next) {
1599           next = retval->next;
1600           do {
1601               sasl_FREE(retval);
1602               retval = next;
1603               next = retval->next;
1604           } while(next);
1605           return NULL;
1606       }
1607       
1608       next->d = listptr->m.plug->mech_name;
1609
1610       if(!retval) {
1611           next->next = NULL;
1612           retval = next;
1613       } else {
1614           next->next = retval;
1615           retval = next;
1616       }
1617   }
1618
1619   return retval;
1620 }
1621
1622 #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1623 static int is_mech(const char *t, const char *m)
1624 {
1625     size_t sl = strlen(m);
1626     return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1627 }
1628
1629 /* returns OK if it's valid */
1630 static int _sasl_checkpass(sasl_conn_t *conn,
1631                            const char *user,
1632                            unsigned userlen,
1633                            const char *pass,
1634                            unsigned passlen)
1635 {
1636     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1637     int result;
1638     sasl_getopt_t *getopt;
1639     sasl_server_userdb_checkpass_t *checkpass_cb;
1640     void *context;
1641     const char *mlist = NULL, *mech = NULL;
1642     struct sasl_verify_password_s *v;
1643     const char *service = conn->service;
1644
1645     if (!userlen) userlen = (unsigned) strlen(user);
1646     if (!passlen) passlen = (unsigned) strlen(pass);
1647
1648     /* call userdb callback function, if available */
1649     result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1650                                &checkpass_cb, &context);
1651     if(result == SASL_OK && checkpass_cb) {
1652         result = checkpass_cb(conn, context, user, pass, passlen,
1653                               s_conn->sparams->propctx);
1654         if(result == SASL_OK)
1655             return SASL_OK;
1656     }
1657
1658     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1659     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1660             == SASL_OK) {
1661         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1662     }
1663
1664     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1665
1666     result = SASL_NOMECH;
1667
1668     mech = mlist;
1669     while (*mech && result != SASL_OK) {
1670         for (v = _sasl_verify_password; v->name; v++) {
1671             if(is_mech(mech, v->name)) {
1672                 result = v->verify(conn, user, pass, service,
1673                                    s_conn->user_realm);
1674                 break;
1675             }
1676         }
1677         if (result != SASL_OK) {
1678             /* skip to next mech in list */
1679             while (*mech && !isspace((int) *mech)) mech++;
1680             while (*mech && isspace((int) *mech)) mech++;
1681         }
1682         else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1683             s_conn->sparams->transition(conn, pass, passlen);
1684         }
1685     }
1686
1687     if (result == SASL_NOMECH) {
1688         /* no mechanism available ?!? */
1689         _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1690     }
1691
1692     if (result != SASL_OK)
1693         sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1694
1695     RETURN(conn, result);
1696 }
1697
1698 /* check if a plaintext password is valid
1699  *   if user is NULL, check if plaintext passwords are enabled
1700  * inputs:
1701  *  user          -- user to query in current user_domain
1702  *  userlen       -- length of username, 0 = strlen(user)
1703  *  pass          -- plaintext password to check
1704  *  passlen       -- length of password, 0 = strlen(pass)
1705  * returns 
1706  *  SASL_OK       -- success
1707  *  SASL_NOMECH   -- mechanism not supported
1708  *  SASL_NOVERIFY -- user found, but no verifier
1709  *  SASL_NOUSER   -- user not found
1710  */
1711 int sasl_checkpass(sasl_conn_t *conn,
1712                    const char *user,
1713                    unsigned userlen,
1714                    const char *pass,
1715                    unsigned passlen)
1716 {
1717     int result;
1718     
1719     if (_sasl_server_active==0) return SASL_NOTINIT;
1720     
1721     /* check if it's just a query if we are enabled */
1722     if (!user)
1723         return SASL_OK;
1724
1725     if (!conn) return SASL_BADPARAM;
1726     
1727     /* check params */
1728     if (pass == NULL)
1729         PARAMERROR(conn);
1730
1731     /* canonicalize the username */
1732     result = _sasl_canon_user(conn, user, userlen,
1733                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1734                               &(conn->oparams));
1735     if(result != SASL_OK) RETURN(conn, result);
1736     user = conn->oparams.user;
1737
1738     /* Check the password */
1739     result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1740
1741     /* Do authorization */
1742     if(result == SASL_OK) {
1743       result = do_authorization((sasl_server_conn_t *)conn);
1744     }
1745
1746     RETURN(conn,result);
1747 }
1748
1749 /* check if a user exists on server
1750  *  conn          -- connection context (may be NULL, used to hold last error)
1751  *  service       -- registered name of the service using SASL (e.g. "imap")
1752  *  user_realm    -- permits multiple user realms on server, NULL = default
1753  *  user          -- NUL terminated user name
1754  *
1755  * returns:
1756  *  SASL_OK       -- success
1757  *  SASL_DISABLED -- account disabled [FIXME: currently not detected]
1758  *  SASL_NOUSER   -- user not found
1759  *  SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1760  *  SASL_NOMECH   -- no mechanisms enabled
1761  */
1762 int sasl_user_exists(sasl_conn_t *conn,
1763                      const char *service,
1764                      const char *user_realm,
1765                      const char *user) 
1766 {
1767     int result=SASL_NOMECH;
1768     const char *mlist = NULL, *mech = NULL;
1769     void *context;
1770     sasl_getopt_t *getopt;
1771     struct sasl_verify_password_s *v;
1772     
1773     /* check params */
1774     if (_sasl_server_active==0) return SASL_NOTINIT;
1775     if (!conn) return SASL_BADPARAM;
1776     if (!user || conn->type != SASL_CONN_SERVER) 
1777         PARAMERROR(conn);
1778
1779     if(!service) service = conn->service;
1780     
1781     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1782     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1783             == SASL_OK) {
1784         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1785     }
1786
1787     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1788
1789     result = SASL_NOMECH;
1790
1791     mech = mlist;
1792     while (*mech && result != SASL_OK) {
1793         for (v = _sasl_verify_password; v->name; v++) {
1794             if(is_mech(mech, v->name)) {
1795                 result = v->verify(conn, user, NULL, service, user_realm);
1796                 break;
1797             }
1798         }
1799         if (result != SASL_OK) {
1800             /* skip to next mech in list */
1801             while (*mech && !isspace((int) *mech)) mech++;
1802             while (*mech && isspace((int) *mech)) mech++;
1803         }
1804     }
1805
1806     /* Screen out the SASL_BADPARAM response
1807      * we'll get from not giving a password */
1808     if(result == SASL_BADPARAM) {
1809         result = SASL_OK;
1810     }
1811
1812     if (result == SASL_NOMECH) {
1813         /* no mechanism available ?!? */
1814         _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1815         sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1816     }
1817
1818     RETURN(conn, result);
1819 }
1820
1821 /* check if an apop exchange is valid
1822  *  (note this is an optional part of the SASL API)
1823  *  if challenge is NULL, just check if APOP is enabled
1824  * inputs:
1825  *  challenge     -- challenge which was sent to client
1826  *  challen       -- length of challenge, 0 = strlen(challenge)
1827  *  response      -- client response, "<user> <digest>" (RFC 1939)
1828  *  resplen       -- length of response, 0 = strlen(response)
1829  * returns 
1830  *  SASL_OK       -- success
1831  *  SASL_BADAUTH  -- authentication failed
1832  *  SASL_BADPARAM -- missing challenge
1833  *  SASL_BADPROT  -- protocol error (e.g., response in wrong format)
1834  *  SASL_NOVERIFY -- user found, but no verifier
1835  *  SASL_NOMECH   -- mechanism not supported
1836  *  SASL_NOUSER   -- user not found
1837  */
1838 int sasl_checkapop(sasl_conn_t *conn,
1839 #ifdef DO_SASL_CHECKAPOP
1840                    const char *challenge,
1841                    unsigned challen __attribute__((unused)),
1842                    const char *response,
1843                    unsigned resplen __attribute__((unused)))
1844 #else
1845                    const char *challenge __attribute__((unused)),
1846                    unsigned challen __attribute__((unused)),
1847                    const char *response __attribute__((unused)),
1848                    unsigned resplen __attribute__((unused)))
1849 #endif
1850 {
1851 #ifdef DO_SASL_CHECKAPOP
1852     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1853     char *user, *user_end;
1854     const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1855     size_t user_len;
1856     int result;
1857
1858     if (_sasl_server_active==0)
1859         return SASL_NOTINIT;
1860
1861     /* check if it's just a query if we are enabled */
1862     if(!challenge)
1863         return SASL_OK;
1864
1865     /* check params */
1866     if (!conn) return SASL_BADPARAM;
1867     if (!response)
1868         PARAMERROR(conn);
1869
1870     /* Parse out username and digest.
1871      *
1872      * Per RFC 1939, response must be "<user> <digest>", where
1873      * <digest> is a 16-octet value which is sent in hexadecimal
1874      * format, using lower-case ASCII characters.
1875      */
1876     user_end = strrchr(response, ' ');
1877     if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) 
1878     {
1879         sasl_seterror(conn, 0, "Bad Digest");
1880         RETURN(conn,SASL_BADPROT);
1881     }
1882  
1883     user_len = (size_t)(user_end - response);
1884     user = sasl_ALLOC(user_len + 1);
1885     memcpy(user, response, user_len);
1886     user[user_len] = '\0';
1887
1888     result = prop_request(s_conn->sparams->propctx, password_request);
1889     if(result != SASL_OK) 
1890     {
1891         sasl_FREE(user);
1892         RETURN(conn, result);
1893     }
1894
1895     /* erase the plaintext password */
1896     s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1897                                        password_request[0]);
1898
1899     /* Cannonify it */
1900     result = _sasl_canon_user(conn, user, user_len,
1901                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1902                               &(conn->oparams));
1903     sasl_FREE(user);
1904
1905     if(result != SASL_OK) RETURN(conn, result);
1906
1907     /* Do APOP verification */
1908     result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1909         challenge, user_end + 1, s_conn->user_realm);
1910
1911     /* Do authorization */
1912     if(result == SASL_OK) {
1913       result = do_authorization((sasl_server_conn_t *)conn);
1914     } else {
1915         /* If verification failed, we don't want to encourage getprop to work */
1916         conn->oparams.user = NULL;
1917         conn->oparams.authid = NULL;
1918     }
1919
1920     RETURN(conn, result);
1921 #else /* sasl_checkapop was disabled at compile time */
1922     sasl_seterror(conn, SASL_NOLOG,
1923         "sasl_checkapop called, but was disabled at compile time");
1924     RETURN(conn, SASL_NOMECH);
1925 #endif /* DO_SASL_CHECKAPOP */
1926 }
1927
1928 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
1929 static void
1930 _sasl_print_mechanism (
1931   server_sasl_mechanism_t *m,
1932   sasl_info_callback_stage_t stage,
1933   void *rock
1934 )
1935 {
1936     char delimiter;
1937
1938     if (stage == SASL_INFO_LIST_START) {
1939         printf ("List of server plugins follows\n");
1940         return;
1941     } else if (stage == SASL_INFO_LIST_END) {
1942         return;
1943     }
1944
1945     /* Process the mechanism */
1946     printf ("Plugin \"%s\" ", m->plugname);
1947
1948     switch (m->condition) {
1949         case SASL_OK:
1950             printf ("[loaded]");
1951             break;
1952
1953         case SASL_CONTINUE:
1954             printf ("[delayed]");
1955             break;
1956
1957         case SASL_NOUSER:
1958             printf ("[no users]");
1959             break;
1960
1961         default:
1962             printf ("[unknown]");
1963             break;
1964     }
1965
1966     printf (", \tAPI version: %d\n", m->version);
1967
1968     if (m->plug != NULL) {
1969         printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
1970                 m->plug->mech_name,
1971                 m->plug->max_ssf,
1972                 (m->plug->setpass != NULL) ? "yes" : "no"
1973                 );
1974
1975
1976         printf ("\tsecurity flags:");
1977         
1978         delimiter = ' ';
1979         if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
1980             printf ("%cNO_ANONYMOUS", delimiter);
1981             delimiter = '|';
1982         }
1983
1984         if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
1985             printf ("%cNO_PLAINTEXT", delimiter);
1986             delimiter = '|';
1987         }
1988         
1989         if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
1990             printf ("%cNO_ACTIVE", delimiter);
1991             delimiter = '|';
1992         }
1993
1994         if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
1995             printf ("%cNO_DICTIONARY", delimiter);
1996             delimiter = '|';
1997         }
1998
1999         if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2000             printf ("%cFORWARD_SECRECY", delimiter);
2001             delimiter = '|';
2002         }
2003
2004         if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2005             printf ("%cPASS_CREDENTIALS", delimiter);
2006             delimiter = '|';
2007         }
2008
2009         if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2010             printf ("%cMUTUAL_AUTH", delimiter);
2011             delimiter = '|';
2012         }
2013
2014
2015
2016         printf ("\n\tfeatures:");
2017         
2018         delimiter = ' ';
2019         if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2020             printf ("%cWANT_CLIENT_FIRST", delimiter);
2021             delimiter = '|';
2022         }
2023
2024         if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2025             printf ("%cSERVER_FIRST", delimiter);
2026             delimiter = '|';
2027         }
2028
2029         if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2030             printf ("%cPROXY_AUTHENTICATION", delimiter);
2031             delimiter = '|';
2032         }
2033
2034         if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2035             printf ("%cNEED_SERVER_FQDN", delimiter);
2036             delimiter = '|';
2037         }
2038
2039         /* Is this one used? */
2040         if (m->plug->features & SASL_FEAT_SERVICE) {
2041             printf ("%cSERVICE", delimiter);
2042             delimiter = '|';
2043         }
2044
2045         if (m->plug->features & SASL_FEAT_GETSECRET) {
2046             printf ("%cNEED_GETSECRET", delimiter);
2047             delimiter = '|';
2048         }
2049     }
2050
2051     if (m->f) {
2052         printf ("\n\twill be loaded from \"%s\"", m->f);
2053     }
2054
2055     printf ("\n");
2056 }
2057
2058 /* Dump information about available server plugins (separate functions should be
2059    used for canon and auxprop plugins */
2060 int sasl_server_plugin_info (
2061   const char *c_mech_list,              /* space separated mechanism list or NULL for ALL */
2062   sasl_server_info_callback_t *info_cb,
2063   void *info_cb_rock
2064 )
2065 {
2066     mechanism_t *m;
2067     server_sasl_mechanism_t plug_data;
2068     char * cur_mech;
2069     char *mech_list = NULL;
2070     char * p;
2071
2072     if (info_cb == NULL) {
2073         info_cb = _sasl_print_mechanism;
2074     }
2075
2076     if (mechlist != NULL) {
2077         info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2078
2079         if (c_mech_list == NULL) {
2080             m = mechlist->mech_list; /* m point to beginning of the list */
2081
2082             while (m != NULL) {
2083                 memcpy (&plug_data, &m->m, sizeof(plug_data));
2084
2085                 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2086             
2087                 m = m->next;
2088             }
2089         } else {
2090             mech_list = strdup(c_mech_list);
2091
2092             cur_mech = mech_list;
2093
2094             while (cur_mech != NULL) {
2095                 p = strchr (cur_mech, ' ');
2096                 if (p != NULL) {
2097                     *p = '\0';
2098                     p++;
2099                 }
2100
2101                 m = mechlist->mech_list; /* m point to beginning of the list */
2102
2103                 while (m != NULL) {
2104                     if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2105                         memcpy (&plug_data, &m->m, sizeof(plug_data));
2106
2107                         info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2108                     }
2109             
2110                     m = m->next;
2111                 }
2112
2113                 cur_mech = p;
2114             }
2115
2116             free (mech_list);
2117         }
2118
2119         info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2120
2121         return (SASL_OK);
2122     }
2123
2124     return (SASL_NOTINIT);
2125 }