cleanups to minimise merging hassle
[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         /* Validate channel bindings */
1457         switch (conn->oparams.cbindingdisp) {
1458         case SASL_CB_DISP_NONE:
1459             if (SASL_CB_CRITICAL(s_conn->sparams)) {
1460                 sasl_seterror(conn, 0,
1461                               "server requires channel binding but client provided none");
1462                 ret = SASL_BADAUTH;
1463             }
1464             break;
1465         case SASL_CB_DISP_WANT:
1466             if (SASL_CB_PRESENT(s_conn->sparams)) {
1467                 sasl_seterror(conn, 0,
1468                               "client incorrectly assumed server had no channel binding");
1469                 ret = SASL_BADAUTH;
1470             }
1471             break;
1472         case SASL_CB_DISP_USED:
1473             if (!SASL_CB_PRESENT(s_conn->sparams)) {
1474                 sasl_seterror(conn, 0,
1475                               "client provided channel binding but server had none");
1476                 ret = SASL_BADAUTH;
1477             } else if (strcmp(conn->oparams.cbindingname,
1478                        s_conn->sparams->cbinding->name) != 0) {
1479                 sasl_seterror(conn, 0,
1480                               "client channel binding %s does not match server %s",
1481                               conn->oparams.cbindingname, s_conn->sparams->cbinding->name);
1482                 ret = SASL_BADAUTH;
1483             }
1484             break;
1485         }
1486
1487         if (ret == SASL_OK &&
1488             (conn->oparams.user == NULL || conn->oparams.authid == NULL)) {
1489             sasl_seterror(conn, 0,
1490                           "mech did not call canon_user for both authzid " \
1491                           "and authid");
1492             ret = SASL_BADPROT;
1493         }       
1494     }
1495     
1496     if(   ret != SASL_OK
1497        && ret != SASL_CONTINUE
1498        && ret != SASL_INTERACT) {
1499         if(conn->context) {
1500             s_conn->mech->m.plug->mech_dispose(conn->context,
1501                                              s_conn->sparams->utils);
1502             conn->context = NULL;
1503         }
1504     }
1505
1506     RETURN(conn, ret);
1507 }
1508
1509 /* returns the length of all the mechanisms
1510  * added up 
1511  */
1512
1513 static unsigned mech_names_len()
1514 {
1515   mechanism_t *listptr;
1516   unsigned result = 0;
1517
1518   for (listptr = mechlist->mech_list;
1519        listptr;
1520        listptr = listptr->next)
1521     result += (unsigned) strlen(listptr->m.plug->mech_name);
1522
1523   return result;
1524 }
1525
1526 /* This returns a list of mechanisms in a NUL-terminated string
1527  *
1528  * The default behavior is to seperate with spaces if sep==NULL
1529  */
1530 int _sasl_server_listmech(sasl_conn_t *conn,
1531                           const char *user __attribute__((unused)),
1532                           const char *prefix,
1533                           const char *sep,
1534                           const char *suffix,
1535                           const char **result,
1536                           unsigned *plen,
1537                           int *pcount)
1538 {
1539   int lup;
1540   mechanism_t *listptr;
1541   int ret;
1542   size_t resultlen;
1543   int flag;
1544   const char *mysep;
1545   sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;  /* cast */
1546
1547   /* if there hasn't been a sasl_sever_init() fail */
1548   if (_sasl_server_active==0) return SASL_NOTINIT;
1549   if (!conn) return SASL_BADPARAM;
1550   if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
1551   
1552   if (! result)
1553       PARAMERROR(conn);
1554
1555   if (plen != NULL)
1556       *plen = 0;
1557   if (pcount != NULL)
1558       *pcount = 0;
1559
1560   if (sep) {
1561       mysep = sep;
1562   } else {
1563       mysep = " ";
1564   }
1565
1566   if (! mechlist || mechlist->mech_length <= 0)
1567       INTERROR(conn, SASL_NOMECH);
1568
1569   resultlen = (prefix ? strlen(prefix) : 0)
1570             + (strlen(mysep) * (mechlist->mech_length - 1) * 2)
1571             + (mech_names_len() * 2) /* including -PLUS variant */
1572             + (mechlist->mech_length * (sizeof("-PLUS") - 1))
1573             + (suffix ? strlen(suffix) : 0)
1574             + 1;
1575   ret = _buf_alloc(&conn->mechlist_buf,
1576                    &conn->mechlist_buf_len, resultlen);
1577   if(ret != SASL_OK) MEMERROR(conn);
1578
1579   if (prefix)
1580     strcpy (conn->mechlist_buf,prefix);
1581   else
1582     *(conn->mechlist_buf) = '\0';
1583
1584   listptr = mechlist->mech_list;  
1585    
1586   flag = 0;
1587   /* make list */
1588   for (lup = 0; lup < mechlist->mech_length; lup++) {
1589       /* currently, we don't use the "user" parameter for anything */
1590       if (mech_permitted(conn, listptr) == SASL_OK) {
1591           /*
1592            * If the server would never succeed in the authentication of
1593            * he non-PLUS-variant due to policy reasons, it MUST advertise
1594            * only the PLUS-variant.
1595            */
1596           if (!SASL_CB_PRESENT(s_conn->sparams) ||
1597               !SASL_CB_CRITICAL(s_conn->sparams)) {
1598             if (pcount != NULL)
1599               (*pcount)++;
1600             if (flag)
1601               strcat(conn->mechlist_buf, mysep);
1602             else
1603               flag = 1;
1604             strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1605           }
1606           /*
1607            * If the server cannot support channel binding, it SHOULD
1608            * advertise only the non-PLUS-variant. Here, supporting channel
1609            * binding means the underlying SASL mechanism supports it and
1610            * the application has set some channel binding data.
1611            */
1612           if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) &&
1613               SASL_CB_PRESENT(s_conn->sparams)) {
1614             if (pcount != NULL)
1615                 (*pcount)++;
1616             if (flag)
1617               strcat(conn->mechlist_buf, mysep);
1618             else
1619               flag = 1;
1620             strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1621             strcat(conn->mechlist_buf, "-PLUS");
1622           }
1623       }
1624
1625       listptr = listptr->next;
1626   }
1627
1628   if (suffix)
1629       strcat(conn->mechlist_buf,suffix);
1630
1631   if (plen!=NULL)
1632       *plen = (unsigned) strlen(conn->mechlist_buf);
1633
1634   *result = conn->mechlist_buf;
1635
1636   return SASL_OK;  
1637 }
1638
1639 sasl_string_list_t *_sasl_server_mechs(void) 
1640 {
1641   mechanism_t *listptr;
1642   sasl_string_list_t *retval = NULL, *next=NULL;
1643
1644   if(!_sasl_server_active) return NULL;
1645
1646   /* make list */
1647   for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1648       next = sasl_ALLOC(sizeof(sasl_string_list_t));
1649
1650       if(!next && !retval) return NULL;
1651       else if(!next) {
1652           next = retval->next;
1653           do {
1654               sasl_FREE(retval);
1655               retval = next;
1656               next = retval->next;
1657           } while(next);
1658           return NULL;
1659       }
1660       
1661       next->d = listptr->m.plug->mech_name;
1662
1663       if(!retval) {
1664           next->next = NULL;
1665           retval = next;
1666       } else {
1667           next->next = retval;
1668           retval = next;
1669       }
1670   }
1671
1672   return retval;
1673 }
1674
1675 #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1676 static int is_mech(const char *t, const char *m)
1677 {
1678     size_t sl = strlen(m);
1679     return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1680 }
1681
1682 /* returns OK if it's valid */
1683 static int _sasl_checkpass(sasl_conn_t *conn,
1684                            const char *user,
1685                            unsigned userlen,
1686                            const char *pass,
1687                            unsigned passlen)
1688 {
1689     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1690     int result;
1691     sasl_getopt_t *getopt;
1692     sasl_server_userdb_checkpass_t *checkpass_cb;
1693     void *context;
1694     const char *mlist = NULL, *mech = NULL;
1695     struct sasl_verify_password_s *v;
1696     const char *service = conn->service;
1697
1698     if (!userlen) userlen = (unsigned) strlen(user);
1699     if (!passlen) passlen = (unsigned) strlen(pass);
1700
1701     /* call userdb callback function, if available */
1702     result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1703                                &checkpass_cb, &context);
1704     if(result == SASL_OK && checkpass_cb) {
1705         result = checkpass_cb(conn, context, user, pass, passlen,
1706                               s_conn->sparams->propctx);
1707         if(result == SASL_OK)
1708             return SASL_OK;
1709     }
1710
1711     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1712     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1713             == SASL_OK) {
1714         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1715     }
1716
1717     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1718
1719     result = SASL_NOMECH;
1720
1721     mech = mlist;
1722     while (*mech && result != SASL_OK) {
1723         for (v = _sasl_verify_password; v->name; v++) {
1724             if(is_mech(mech, v->name)) {
1725                 result = v->verify(conn, user, pass, service,
1726                                    s_conn->user_realm);
1727                 break;
1728             }
1729         }
1730         if (result != SASL_OK) {
1731             /* skip to next mech in list */
1732             while (*mech && !isspace((int) *mech)) mech++;
1733             while (*mech && isspace((int) *mech)) mech++;
1734         }
1735         else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1736             s_conn->sparams->transition(conn, pass, passlen);
1737         }
1738     }
1739
1740     if (result == SASL_NOMECH) {
1741         /* no mechanism available ?!? */
1742         _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1743     }
1744
1745     if (result != SASL_OK)
1746         sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1747
1748     RETURN(conn, result);
1749 }
1750
1751 /* check if a plaintext password is valid
1752  *   if user is NULL, check if plaintext passwords are enabled
1753  * inputs:
1754  *  user          -- user to query in current user_domain
1755  *  userlen       -- length of username, 0 = strlen(user)
1756  *  pass          -- plaintext password to check
1757  *  passlen       -- length of password, 0 = strlen(pass)
1758  * returns 
1759  *  SASL_OK       -- success
1760  *  SASL_NOMECH   -- mechanism not supported
1761  *  SASL_NOVERIFY -- user found, but no verifier
1762  *  SASL_NOUSER   -- user not found
1763  */
1764 int sasl_checkpass(sasl_conn_t *conn,
1765                    const char *user,
1766                    unsigned userlen,
1767                    const char *pass,
1768                    unsigned passlen)
1769 {
1770     int result;
1771     
1772     if (_sasl_server_active==0) return SASL_NOTINIT;
1773     
1774     /* check if it's just a query if we are enabled */
1775     if (!user)
1776         return SASL_OK;
1777
1778     if (!conn) return SASL_BADPARAM;
1779     
1780     /* check params */
1781     if (pass == NULL)
1782         PARAMERROR(conn);
1783
1784     /* canonicalize the username */
1785     result = _sasl_canon_user(conn, user, userlen,
1786                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1787                               &(conn->oparams));
1788     if(result != SASL_OK) RETURN(conn, result);
1789     user = conn->oparams.user;
1790
1791     /* Check the password */
1792     result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1793
1794     /* Do authorization */
1795     if(result == SASL_OK) {
1796       result = do_authorization((sasl_server_conn_t *)conn);
1797     }
1798
1799     RETURN(conn,result);
1800 }
1801
1802 /* check if a user exists on server
1803  *  conn          -- connection context (may be NULL, used to hold last error)
1804  *  service       -- registered name of the service using SASL (e.g. "imap")
1805  *  user_realm    -- permits multiple user realms on server, NULL = default
1806  *  user          -- NUL terminated user name
1807  *
1808  * returns:
1809  *  SASL_OK       -- success
1810  *  SASL_DISABLED -- account disabled [FIXME: currently not detected]
1811  *  SASL_NOUSER   -- user not found
1812  *  SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1813  *  SASL_NOMECH   -- no mechanisms enabled
1814  */
1815 int sasl_user_exists(sasl_conn_t *conn,
1816                      const char *service,
1817                      const char *user_realm,
1818                      const char *user) 
1819 {
1820     int result=SASL_NOMECH;
1821     const char *mlist = NULL, *mech = NULL;
1822     void *context;
1823     sasl_getopt_t *getopt;
1824     struct sasl_verify_password_s *v;
1825     
1826     /* check params */
1827     if (_sasl_server_active==0) return SASL_NOTINIT;
1828     if (!conn) return SASL_BADPARAM;
1829     if (!user || conn->type != SASL_CONN_SERVER) 
1830         PARAMERROR(conn);
1831
1832     if(!service) service = conn->service;
1833     
1834     /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1835     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1836             == SASL_OK) {
1837         getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1838     }
1839
1840     if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1841
1842     result = SASL_NOMECH;
1843
1844     mech = mlist;
1845     while (*mech && result != SASL_OK) {
1846         for (v = _sasl_verify_password; v->name; v++) {
1847             if(is_mech(mech, v->name)) {
1848                 result = v->verify(conn, user, NULL, service, user_realm);
1849                 break;
1850             }
1851         }
1852         if (result != SASL_OK) {
1853             /* skip to next mech in list */
1854             while (*mech && !isspace((int) *mech)) mech++;
1855             while (*mech && isspace((int) *mech)) mech++;
1856         }
1857     }
1858
1859     /* Screen out the SASL_BADPARAM response
1860      * we'll get from not giving a password */
1861     if(result == SASL_BADPARAM) {
1862         result = SASL_OK;
1863     }
1864
1865     if (result == SASL_NOMECH) {
1866         /* no mechanism available ?!? */
1867         _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1868         sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1869     }
1870
1871     RETURN(conn, result);
1872 }
1873
1874 /* check if an apop exchange is valid
1875  *  (note this is an optional part of the SASL API)
1876  *  if challenge is NULL, just check if APOP is enabled
1877  * inputs:
1878  *  challenge     -- challenge which was sent to client
1879  *  challen       -- length of challenge, 0 = strlen(challenge)
1880  *  response      -- client response, "<user> <digest>" (RFC 1939)
1881  *  resplen       -- length of response, 0 = strlen(response)
1882  * returns 
1883  *  SASL_OK       -- success
1884  *  SASL_BADAUTH  -- authentication failed
1885  *  SASL_BADPARAM -- missing challenge
1886  *  SASL_BADPROT  -- protocol error (e.g., response in wrong format)
1887  *  SASL_NOVERIFY -- user found, but no verifier
1888  *  SASL_NOMECH   -- mechanism not supported
1889  *  SASL_NOUSER   -- user not found
1890  */
1891 int sasl_checkapop(sasl_conn_t *conn,
1892 #ifdef DO_SASL_CHECKAPOP
1893                    const char *challenge,
1894                    unsigned challen __attribute__((unused)),
1895                    const char *response,
1896                    unsigned resplen __attribute__((unused)))
1897 #else
1898                    const char *challenge __attribute__((unused)),
1899                    unsigned challen __attribute__((unused)),
1900                    const char *response __attribute__((unused)),
1901                    unsigned resplen __attribute__((unused)))
1902 #endif
1903 {
1904 #ifdef DO_SASL_CHECKAPOP
1905     sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1906     char *user, *user_end;
1907     const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1908     size_t user_len;
1909     int result;
1910
1911     if (_sasl_server_active==0)
1912         return SASL_NOTINIT;
1913
1914     /* check if it's just a query if we are enabled */
1915     if(!challenge)
1916         return SASL_OK;
1917
1918     /* check params */
1919     if (!conn) return SASL_BADPARAM;
1920     if (!response)
1921         PARAMERROR(conn);
1922
1923     /* Parse out username and digest.
1924      *
1925      * Per RFC 1939, response must be "<user> <digest>", where
1926      * <digest> is a 16-octet value which is sent in hexadecimal
1927      * format, using lower-case ASCII characters.
1928      */
1929     user_end = strrchr(response, ' ');
1930     if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) 
1931     {
1932         sasl_seterror(conn, 0, "Bad Digest");
1933         RETURN(conn,SASL_BADPROT);
1934     }
1935  
1936     user_len = (size_t)(user_end - response);
1937     user = sasl_ALLOC(user_len + 1);
1938     memcpy(user, response, user_len);
1939     user[user_len] = '\0';
1940
1941     result = prop_request(s_conn->sparams->propctx, password_request);
1942     if(result != SASL_OK) 
1943     {
1944         sasl_FREE(user);
1945         RETURN(conn, result);
1946     }
1947
1948     /* erase the plaintext password */
1949     s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1950                                        password_request[0]);
1951
1952     /* Cannonify it */
1953     result = _sasl_canon_user(conn, user, user_len,
1954                               SASL_CU_AUTHID | SASL_CU_AUTHZID,
1955                               &(conn->oparams));
1956     sasl_FREE(user);
1957
1958     if(result != SASL_OK) RETURN(conn, result);
1959
1960     /* Do APOP verification */
1961     result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1962         challenge, user_end + 1, s_conn->user_realm);
1963
1964     /* Do authorization */
1965     if(result == SASL_OK) {
1966       result = do_authorization((sasl_server_conn_t *)conn);
1967     } else {
1968         /* If verification failed, we don't want to encourage getprop to work */
1969         conn->oparams.user = NULL;
1970         conn->oparams.authid = NULL;
1971     }
1972
1973     RETURN(conn, result);
1974 #else /* sasl_checkapop was disabled at compile time */
1975     sasl_seterror(conn, SASL_NOLOG,
1976         "sasl_checkapop called, but was disabled at compile time");
1977     RETURN(conn, SASL_NOMECH);
1978 #endif /* DO_SASL_CHECKAPOP */
1979 }
1980
1981 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
1982 static void
1983 _sasl_print_mechanism (
1984   server_sasl_mechanism_t *m,
1985   sasl_info_callback_stage_t stage,
1986   void *rock
1987 )
1988 {
1989     char delimiter;
1990
1991     if (stage == SASL_INFO_LIST_START) {
1992         printf ("List of server plugins follows\n");
1993         return;
1994     } else if (stage == SASL_INFO_LIST_END) {
1995         return;
1996     }
1997
1998     /* Process the mechanism */
1999     printf ("Plugin \"%s\" ", m->plugname);
2000
2001     switch (m->condition) {
2002         case SASL_OK:
2003             printf ("[loaded]");
2004             break;
2005
2006         case SASL_CONTINUE:
2007             printf ("[delayed]");
2008             break;
2009
2010         case SASL_NOUSER:
2011             printf ("[no users]");
2012             break;
2013
2014         default:
2015             printf ("[unknown]");
2016             break;
2017     }
2018
2019     printf (", \tAPI version: %d\n", m->version);
2020
2021     if (m->plug != NULL) {
2022         printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
2023                 m->plug->mech_name,
2024                 m->plug->max_ssf,
2025                 (m->plug->setpass != NULL) ? "yes" : "no"
2026                 );
2027
2028
2029         printf ("\tsecurity flags:");
2030         
2031         delimiter = ' ';
2032         if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
2033             printf ("%cNO_ANONYMOUS", delimiter);
2034             delimiter = '|';
2035         }
2036
2037         if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
2038             printf ("%cNO_PLAINTEXT", delimiter);
2039             delimiter = '|';
2040         }
2041         
2042         if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
2043             printf ("%cNO_ACTIVE", delimiter);
2044             delimiter = '|';
2045         }
2046
2047         if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
2048             printf ("%cNO_DICTIONARY", delimiter);
2049             delimiter = '|';
2050         }
2051
2052         if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2053             printf ("%cFORWARD_SECRECY", delimiter);
2054             delimiter = '|';
2055         }
2056
2057         if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2058             printf ("%cPASS_CREDENTIALS", delimiter);
2059             delimiter = '|';
2060         }
2061
2062         if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2063             printf ("%cMUTUAL_AUTH", delimiter);
2064             delimiter = '|';
2065         }
2066
2067
2068
2069         printf ("\n\tfeatures:");
2070         
2071         delimiter = ' ';
2072         if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2073             printf ("%cWANT_CLIENT_FIRST", delimiter);
2074             delimiter = '|';
2075         }
2076
2077         if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2078             printf ("%cSERVER_FIRST", delimiter);
2079             delimiter = '|';
2080         }
2081
2082         if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2083             printf ("%cPROXY_AUTHENTICATION", delimiter);
2084             delimiter = '|';
2085         }
2086
2087         if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2088             printf ("%cNEED_SERVER_FQDN", delimiter);
2089             delimiter = '|';
2090         }
2091
2092         /* Is this one used? */
2093         if (m->plug->features & SASL_FEAT_SERVICE) {
2094             printf ("%cSERVICE", delimiter);
2095             delimiter = '|';
2096         }
2097
2098         if (m->plug->features & SASL_FEAT_GETSECRET) {
2099             printf ("%cNEED_GETSECRET", delimiter);
2100             delimiter = '|';
2101         }
2102
2103         if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
2104             printf ("%cGSS_FRAMING", delimiter);
2105             delimiter = '|';
2106         }
2107
2108         if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
2109             printf ("%cCHANNEL_BINDING", delimiter);
2110             delimiter = '|';
2111         }
2112     }
2113
2114     if (m->f) {
2115         printf ("\n\twill be loaded from \"%s\"", m->f);
2116     }
2117
2118     printf ("\n");
2119 }
2120
2121 /* Dump information about available server plugins (separate functions should be
2122    used for canon and auxprop plugins */
2123 int sasl_server_plugin_info (
2124   const char *c_mech_list,              /* space separated mechanism list or NULL for ALL */
2125   sasl_server_info_callback_t *info_cb,
2126   void *info_cb_rock
2127 )
2128 {
2129     mechanism_t *m;
2130     server_sasl_mechanism_t plug_data;
2131     char * cur_mech;
2132     char *mech_list = NULL;
2133     char * p;
2134
2135     if (info_cb == NULL) {
2136         info_cb = _sasl_print_mechanism;
2137     }
2138
2139     if (mechlist != NULL) {
2140         info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2141
2142         if (c_mech_list == NULL) {
2143             m = mechlist->mech_list; /* m point to beginning of the list */
2144
2145             while (m != NULL) {
2146                 memcpy (&plug_data, &m->m, sizeof(plug_data));
2147
2148                 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2149             
2150                 m = m->next;
2151             }
2152         } else {
2153             mech_list = strdup(c_mech_list);
2154
2155             cur_mech = mech_list;
2156
2157             while (cur_mech != NULL) {
2158                 p = strchr (cur_mech, ' ');
2159                 if (p != NULL) {
2160                     *p = '\0';
2161                     p++;
2162                 }
2163
2164                 m = mechlist->mech_list; /* m point to beginning of the list */
2165
2166                 while (m != NULL) {
2167                     if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2168                         memcpy (&plug_data, &m->m, sizeof(plug_data));
2169
2170                         info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2171                     }
2172             
2173                     m = m->next;
2174                 }
2175
2176                 cur_mech = p;
2177             }
2178
2179             free (mech_list);
2180         }
2181
2182         info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2183
2184         return (SASL_OK);
2185     }
2186
2187     return (SASL_NOTINIT);
2188 }