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 **) ¤t_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