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