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