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