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