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