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