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