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