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