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 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