1 /* 2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* SASL server API implementation 7 * Rob Siemborski 8 * Tim Martin 9 * $Id: client.c,v 1.61 2003/04/16 19:36:00 rjs3 Exp $ 10 */ 11 /* 12 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in 23 * the documentation and/or other materials provided with the 24 * distribution. 25 * 26 * 3. The name "Carnegie Mellon University" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For permission or any other legal 29 * details, please contact 30 * Office of Technology Transfer 31 * Carnegie Mellon University 32 * 5000 Forbes Avenue 33 * Pittsburgh, PA 15213-3890 34 * (412) 268-4387, fax: (412) 268-7395 35 * tech-transfer@andrew.cmu.edu 36 * 37 * 4. Redistributions of any form whatsoever must retain the following 38 * acknowledgment: 39 * "This product includes software developed by Computing Services 40 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 41 * 42 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 43 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 44 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 45 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 47 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 48 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 49 */ 50 51 #include <config.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <limits.h> 55 #include <ctype.h> 56 #include <string.h> 57 #ifdef HAVE_UNISTD_H 58 #include <unistd.h> 59 #endif 60 61 /* SASL Headers */ 62 #include "sasl.h" 63 #include "saslplug.h" 64 #include "saslutil.h" 65 #include "saslint.h" 66 67 #ifdef _SUN_SDK_ 68 DEFINE_STATIC_MUTEX(init_client_mutex); 69 DEFINE_STATIC_MUTEX(client_active_mutex); 70 /* 71 * client_plug_mutex ensures only one client plugin is init'ed at a time 72 * If a plugin is loaded more than once, the glob_context may be overwritten 73 * which may lead to a memory leak. We keep glob_context with each mech 74 * to avoid this problem. 75 */ 76 DEFINE_STATIC_MUTEX(client_plug_mutex); 77 #else 78 static cmech_list_t *cmechlist; /* global var which holds the list */ 79 80 static sasl_global_callbacks_t global_callbacks; 81 82 static int _sasl_client_active = 0; 83 #endif /* _SUN_SDK_ */ 84 85 #ifdef _SUN_SDK_ 86 static int init_mechlist(_sasl_global_context_t *gctx) 87 { 88 cmech_list_t *cmechlist = gctx->cmechlist; 89 #else 90 static int init_mechlist() 91 { 92 #endif /* _SUN_SDK_ */ 93 94 cmechlist->mutex = sasl_MUTEX_ALLOC(); 95 if(!cmechlist->mutex) return SASL_FAIL; 96 97 #ifdef _SUN_SDK_ 98 cmechlist->utils= 99 _sasl_alloc_utils(gctx, NULL, &gctx->client_global_callbacks); 100 #else 101 cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks); 102 #endif /* _SUN_SDK_ */ 103 if (cmechlist->utils==NULL) 104 return SASL_NOMEM; 105 106 cmechlist->mech_list=NULL; 107 cmechlist->mech_length=0; 108 109 return SASL_OK; 110 } 111 112 #ifdef _SUN_SDK_ 113 static int client_done(_sasl_global_context_t *gctx) { 114 cmech_list_t *cmechlist = gctx->cmechlist; 115 _sasl_path_info_t *path_info, *p; 116 #else 117 static int client_done(void) { 118 #endif /* _SUN_SDK_ */ 119 cmechanism_t *cm; 120 cmechanism_t *cprevm; 121 122 #ifdef _SUN_SDK_ 123 if(!gctx->sasl_client_active) 124 return SASL_NOTINIT; 125 if (LOCK_MUTEX(&client_active_mutex) < 0) { 126 return (SASL_FAIL); 127 } 128 gctx->sasl_client_active--; 129 130 if(gctx->sasl_client_active) { 131 /* Don't de-init yet! Our refcount is nonzero. */ 132 UNLOCK_MUTEX(&client_active_mutex); 133 return SASL_CONTINUE; 134 } 135 #else 136 if(!_sasl_client_active) 137 return SASL_NOTINIT; 138 else 139 _sasl_client_active--; 140 141 if(_sasl_client_active) { 142 /* Don't de-init yet! Our refcount is nonzero. */ 143 return SASL_CONTINUE; 144 } 145 #endif /* _SUN_SDK_ */ 146 147 cm=cmechlist->mech_list; /* m point to begging of the list */ 148 while (cm!=NULL) 149 { 150 cprevm=cm; 151 cm=cm->next; 152 153 if (cprevm->plug->mech_free) { 154 #ifdef _SUN_SDK_ 155 cprevm->plug->mech_free(cprevm->glob_context, cmechlist->utils); 156 #else 157 cprevm->plug->mech_free(cprevm->plug->glob_context, 158 cmechlist->utils); 159 #endif /* _SUN_SDK_ */ 160 } 161 162 sasl_FREE(cprevm->plugname); 163 sasl_FREE(cprevm); 164 } 165 sasl_MUTEX_FREE(cmechlist->mutex); 166 _sasl_free_utils(&cmechlist->utils); 167 sasl_FREE(cmechlist); 168 169 #ifdef _SUN_SDK_ 170 gctx->cmechlist = NULL; 171 p = gctx->cplug_path_info; 172 while((path_info = p) != NULL) { 173 sasl_FREE(path_info->path); 174 p = path_info->next; 175 sasl_FREE(path_info); 176 } 177 gctx->cplug_path_info = NULL; 178 UNLOCK_MUTEX(&client_active_mutex); 179 #else 180 cmechlist = NULL; 181 #endif /* _SUN_SDK_ */ 182 183 return SASL_OK; 184 } 185 186 int sasl_client_add_plugin(const char *plugname, 187 sasl_client_plug_init_t *entry_point) 188 { 189 #ifdef _SUN_SDK_ 190 return (_sasl_client_add_plugin(_sasl_gbl_ctx(), plugname, entry_point)); 191 } 192 193 int _sasl_client_add_plugin(void *ctx, 194 const char *plugname, 195 sasl_client_plug_init_t *entry_point) 196 { 197 cmech_list_t *cmechlist; 198 #ifdef _INTEGRATED_SOLARIS_ 199 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; 200 int sun_reg; 201 #endif /* _INTEGRATED_SOLARIS_ */ 202 int i; 203 cmechanism_t *m; 204 #endif /* _SUN_SDK_ */ 205 int plugcount; 206 sasl_client_plug_t *pluglist; 207 cmechanism_t *mech; 208 int result; 209 int version; 210 int lupe; 211 212 if(!plugname || !entry_point) return SASL_BADPARAM; 213 214 #ifdef _SUN_SDK_ 215 cmechlist = gctx->cmechlist; 216 217 if (cmechlist == NULL) return SASL_BADPARAM; 218 219 /* Check to see if this plugin has already been registered */ 220 m = cmechlist->mech_list; 221 for (i = 0; i < cmechlist->mech_length; i++) { 222 if (strcmp(plugname, m->plugname) == 0) { 223 return SASL_OK; 224 } 225 m = m->next; 226 } 227 228 result = LOCK_MUTEX(&client_plug_mutex); 229 if (result != SASL_OK) 230 return result; 231 232 #endif /* _SUN_SDK_ */ 233 234 result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version, 235 &pluglist, &plugcount); 236 237 #ifdef _INTEGRATED_SOLARIS_ 238 sun_reg = _is_sun_reg(pluglist); 239 #endif /* _INTEGRATED_SOLARIS_ */ 240 if (result != SASL_OK) 241 { 242 #ifdef _SUN_SDK_ 243 UNLOCK_MUTEX(&client_plug_mutex); 244 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN, 245 "entry_point failed in sasl_client_add_plugin for %s", 246 plugname); 247 #else 248 _sasl_log(NULL, SASL_LOG_WARN, 249 "entry_point failed in sasl_client_add_plugin for %s", 250 plugname); 251 #endif /* _SUN_SDK_ */ 252 return result; 253 } 254 255 if (version != SASL_CLIENT_PLUG_VERSION) 256 { 257 #ifdef _SUN_SDK_ 258 UNLOCK_MUTEX(&client_plug_mutex); 259 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN, 260 "version conflict in sasl_client_add_plugin for %s", plugname); 261 #else 262 _sasl_log(NULL, SASL_LOG_WARN, 263 "version conflict in sasl_client_add_plugin for %s", plugname); 264 #endif /* _SUN_SDK_ */ 265 return SASL_BADVERS; 266 } 267 268 #ifdef _SUN_SDK_ 269 /* Check plugins to make sure mech_name is non-NULL */ 270 for (lupe=0;lupe < plugcount ;lupe++) { 271 if (pluglist[lupe].mech_name == NULL) 272 break; 273 } 274 if (lupe < plugcount) { 275 UNLOCK_MUTEX(&client_plug_mutex); 276 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, 277 SASL_LOG_ERR, "invalid client plugin %s", plugname); 278 return SASL_BADPROT; 279 } 280 #endif /* _SUN_SDK_ */ 281 282 for (lupe=0;lupe< plugcount ;lupe++) 283 { 284 mech = sasl_ALLOC(sizeof(cmechanism_t)); 285 #ifdef _SUN_SDK_ 286 if (! mech) { 287 UNLOCK_MUTEX(&client_plug_mutex); 288 return SASL_NOMEM; 289 } 290 mech->glob_context = pluglist->glob_context; 291 #else 292 if (! mech) return SASL_NOMEM; 293 #endif /* _SUN_SDK_ */ 294 295 mech->plug=pluglist++; 296 if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) { 297 #ifdef _SUN_SDK_ 298 UNLOCK_MUTEX(&client_plug_mutex); 299 #endif /* _SUN_SDK_ */ 300 sasl_FREE(mech); 301 return SASL_NOMEM; 302 } 303 #ifdef _INTEGRATED_SOLARIS_ 304 mech->sun_reg = sun_reg; 305 #endif /* _INTEGRATED_SOLARIS_ */ 306 mech->version = version; 307 mech->next = cmechlist->mech_list; 308 cmechlist->mech_list = mech; 309 cmechlist->mech_length++; 310 } 311 #ifdef _SUN_SDK_ 312 UNLOCK_MUTEX(&client_plug_mutex); 313 #endif /* _SUN_SDK_ */ 314 315 return SASL_OK; 316 } 317 318 static int 319 client_idle(sasl_conn_t *conn) 320 { 321 cmechanism_t *m; 322 #ifdef _SUN_SDK_ 323 _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx; 324 cmech_list_t *cmechlist = gctx->cmechlist; 325 #endif /* _SUN_SDK_ */ 326 327 if (! cmechlist) 328 return 0; 329 330 for (m = cmechlist->mech_list; 331 m; 332 m = m->next) 333 if (m->plug->idle 334 #ifdef _SUN_SDK_ 335 && m->plug->idle(m->glob_context, 336 #else 337 && m->plug->idle(m->plug->glob_context, 338 #endif /* _SUN_SDK_ */ 339 conn, 340 conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) 341 return 1; 342 return 0; 343 } 344 345 #ifdef _SUN_SDK_ 346 static int _load_client_plugins(_sasl_global_context_t *gctx) 347 { 348 int ret; 349 const add_plugin_list_t _ep_list[] = { 350 { "sasl_client_plug_init", (add_plugin_t *)_sasl_client_add_plugin }, 351 { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin }, 352 { NULL, NULL } 353 }; 354 const sasl_callback_t *callbacks = gctx->client_global_callbacks.callbacks; 355 356 ret = _sasl_load_plugins(gctx, 0, _ep_list, 357 _sasl_find_getpath_callback(callbacks), 358 _sasl_find_verifyfile_callback(callbacks)); 359 return (ret); 360 } 361 #endif /* _SUN_SDK_ */ 362 363 /* initialize the SASL client drivers 364 * callbacks -- base callbacks for all client connections 365 * returns: 366 * SASL_OK -- Success 367 * SASL_NOMEM -- Not enough memory 368 * SASL_BADVERS -- Mechanism version mismatch 369 * SASL_BADPARAM -- error in config file 370 * SASL_NOMECH -- No mechanisms available 371 * ... 372 */ 373 374 int sasl_client_init(const sasl_callback_t *callbacks) 375 { 376 #ifdef _SUN_SDK_ 377 return _sasl_client_init(NULL, callbacks); 378 } 379 380 int _sasl_client_init(void *ctx, 381 const sasl_callback_t *callbacks) 382 { 383 int ret; 384 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; 385 386 if (gctx == NULL) 387 gctx = _sasl_gbl_ctx(); 388 389 ret = LOCK_MUTEX(&init_client_mutex); 390 if (ret < 0) { 391 return (SASL_FAIL); 392 } 393 ret = LOCK_MUTEX(&client_active_mutex); 394 if (ret < 0) { 395 UNLOCK_MUTEX(&init_client_mutex); 396 return (SASL_FAIL); 397 } 398 if(gctx->sasl_client_active) { 399 /* We're already active, just increase our refcount */ 400 /* xxx do something with the callback structure? */ 401 gctx->sasl_client_active++; 402 UNLOCK_MUTEX(&client_active_mutex); 403 UNLOCK_MUTEX(&init_client_mutex); 404 return SASL_OK; 405 } 406 407 gctx->client_global_callbacks.callbacks = callbacks; 408 gctx->client_global_callbacks.appname = NULL; 409 410 gctx->cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); 411 if (gctx->cmechlist==NULL) { 412 UNLOCK_MUTEX(&init_client_mutex); 413 UNLOCK_MUTEX(&client_active_mutex); 414 return SASL_NOMEM; 415 } 416 417 gctx->sasl_client_active = 1; 418 UNLOCK_MUTEX(&client_active_mutex); 419 420 /* load plugins */ 421 ret=init_mechlist(gctx); 422 423 if (ret!=SASL_OK) { 424 client_done(gctx); 425 UNLOCK_MUTEX(&init_client_mutex); 426 return ret; 427 } 428 _sasl_client_add_plugin(gctx, "EXTERNAL", &external_client_plug_init); 429 430 ret = _sasl_common_init(gctx, &gctx->client_global_callbacks, 0); 431 #else 432 int sasl_client_init(const sasl_callback_t *callbacks) 433 { 434 int ret; 435 const add_plugin_list_t ep_list[] = { 436 { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, 437 { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, 438 { NULL, NULL } 439 }; 440 441 if(_sasl_client_active) { 442 /* We're already active, just increase our refcount */ 443 /* xxx do something with the callback structure? */ 444 _sasl_client_active++; 445 return SASL_OK; 446 } 447 448 global_callbacks.callbacks = callbacks; 449 global_callbacks.appname = NULL; 450 451 cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); 452 if (cmechlist==NULL) return SASL_NOMEM; 453 454 /* We need to call client_done if we fail now */ 455 _sasl_client_active = 1; 456 457 /* load plugins */ 458 ret=init_mechlist(); 459 if (ret!=SASL_OK) { 460 client_done(); 461 return ret; 462 } 463 464 sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); 465 466 ret = _sasl_common_init(&global_callbacks); 467 #endif /* _SUN_SDK_ */ 468 469 if (ret == SASL_OK) 470 #ifdef _SUN_SDK_ 471 ret = _load_client_plugins(gctx); 472 #else 473 ret = _sasl_load_plugins(ep_list, 474 _sasl_find_getpath_callback(callbacks), 475 _sasl_find_verifyfile_callback(callbacks)); 476 #endif /* _SUN_SDK_ */ 477 478 #ifdef _SUN_SDK_ 479 if (ret == SASL_OK) 480 /* If sasl_client_init returns error, sasl_done() need not be called */ 481 ret = _sasl_build_mechlist(gctx); 482 if (ret == SASL_OK) { 483 gctx->sasl_client_cleanup_hook = &client_done; 484 gctx->sasl_client_idle_hook = &client_idle; 485 } else { 486 client_done(gctx); 487 } 488 UNLOCK_MUTEX(&init_client_mutex); 489 #else 490 if (ret == SASL_OK) { 491 _sasl_client_cleanup_hook = &client_done; 492 _sasl_client_idle_hook = &client_idle; 493 494 ret = _sasl_build_mechlist(); 495 } else { 496 client_done(); 497 } 498 #endif /* _SUN_SDK_ */ 499 500 return ret; 501 } 502 503 static void client_dispose(sasl_conn_t *pconn) 504 { 505 sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; 506 #ifdef _SUN_SDK_ 507 sasl_free_t *free_func = c_conn->cparams->utils->free; 508 #endif /* _SUN_SDK_ */ 509 510 if (c_conn->mech && c_conn->mech->plug->mech_dispose) { 511 c_conn->mech->plug->mech_dispose(pconn->context, 512 c_conn->cparams->utils); 513 } 514 515 pconn->context = NULL; 516 517 if (c_conn->clientFQDN) 518 #ifdef _SUN_SDK_ 519 free_func(c_conn->clientFQDN); 520 #else 521 sasl_FREE(c_conn->clientFQDN); 522 #endif /* _SUN_SDK_ */ 523 524 if (c_conn->cparams) { 525 _sasl_free_utils(&(c_conn->cparams->utils)); 526 #ifdef _SUN_SDK_ 527 free_func(c_conn->cparams); 528 #else 529 sasl_FREE(c_conn->cparams); 530 #endif /* _SUN_SDK_ */ 531 } 532 533 _sasl_conn_dispose(pconn); 534 } 535 536 /* initialize a client exchange based on the specified mechanism 537 * service -- registered name of the service using SASL (e.g. "imap") 538 * serverFQDN -- the fully qualified domain name of the server 539 * iplocalport -- client IPv4/IPv6 domain literal string with port 540 * (if NULL, then mechanisms requiring IPaddr are disabled) 541 * ipremoteport -- server IPv4/IPv6 domain literal string with port 542 * (if NULL, then mechanisms requiring IPaddr are disabled) 543 * prompt_supp -- list of client interactions supported 544 * may also include sasl_getopt_t context & call 545 * NULL prompt_supp = user/pass via SASL_INTERACT only 546 * NULL proc = interaction supported via SASL_INTERACT 547 * secflags -- security flags (see above) 548 * in/out: 549 * pconn -- connection negotiation structure 550 * pointer to NULL => allocate new 551 * non-NULL => recycle storage and go for next available mech 552 * 553 * Returns: 554 * SASL_OK -- success 555 * SASL_NOMECH -- no mechanism meets requested properties 556 * SASL_NOMEM -- not enough memory 557 */ 558 int sasl_client_new(const char *service, 559 const char *serverFQDN, 560 const char *iplocalport, 561 const char *ipremoteport, 562 const sasl_callback_t *prompt_supp, 563 unsigned flags, 564 sasl_conn_t **pconn) 565 { 566 #ifdef _SUN_SDK_ 567 return _sasl_client_new(NULL, service, serverFQDN, iplocalport, 568 ipremoteport, prompt_supp, flags, pconn); 569 } 570 int _sasl_client_new(void *ctx, 571 const char *service, 572 const char *serverFQDN, 573 const char *iplocalport, 574 const char *ipremoteport, 575 const sasl_callback_t *prompt_supp, 576 unsigned flags, 577 sasl_conn_t **pconn) 578 { 579 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx; 580 #endif /* _SUN_SDK_ */ 581 int result; 582 char name[MAXHOSTNAMELEN]; 583 sasl_client_conn_t *conn; 584 sasl_utils_t *utils; 585 586 #ifdef _SUN_SDK_ 587 if (gctx == NULL) 588 gctx = _sasl_gbl_ctx(); 589 590 if(gctx->sasl_client_active==0) return SASL_NOTINIT; 591 #else 592 if(_sasl_client_active==0) return SASL_NOTINIT; 593 #endif /* _SUN_SDK_ */ 594 595 /* Remember, iplocalport and ipremoteport can be NULL and be valid! */ 596 if (!pconn || !service || !serverFQDN) 597 return SASL_BADPARAM; 598 599 *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); 600 if (*pconn==NULL) { 601 #ifdef _SUN_SDK_ 602 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR, 603 "Out of memory allocating connection context"); 604 #else 605 _sasl_log(NULL, SASL_LOG_ERR, 606 "Out of memory allocating connection context"); 607 #endif /* _SUN_SDK_ */ 608 return SASL_NOMEM; 609 } 610 memset(*pconn, 0, sizeof(sasl_client_conn_t)); 611 612 #ifdef _SUN_SDK_ 613 (*pconn)->gctx = gctx; 614 #endif /* _SUN_SDK_ */ 615 616 (*pconn)->destroy_conn = &client_dispose; 617 618 conn = (sasl_client_conn_t *)*pconn; 619 620 conn->mech = NULL; 621 622 conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); 623 if (conn->cparams==NULL) 624 MEMERROR(*pconn); 625 memset(conn->cparams,0,sizeof(sasl_client_params_t)); 626 627 result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, 628 &client_idle, serverFQDN, 629 iplocalport, ipremoteport, 630 #ifdef _SUN_SDK_ 631 prompt_supp, &gctx->client_global_callbacks); 632 #else 633 prompt_supp, &global_callbacks); 634 #endif /* _SUN_SDK_ */ 635 636 if (result != SASL_OK) RETURN(*pconn, result); 637 638 #ifdef _SUN_SDK_ 639 utils=_sasl_alloc_utils(gctx, *pconn, &gctx->client_global_callbacks); 640 #else 641 utils=_sasl_alloc_utils(*pconn, &global_callbacks); 642 #endif /* _SUN_SDK_ */ 643 if (utils==NULL) 644 MEMERROR(*pconn); 645 646 utils->conn= *pconn; 647 648 /* Setup the non-lazy parts of cparams, the rest is done in 649 * sasl_client_start */ 650 conn->cparams->utils = utils; 651 conn->cparams->canon_user = &_sasl_canon_user; 652 conn->cparams->flags = flags; 653 conn->cparams->prompt_supp = (*pconn)->callbacks; 654 655 /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ 656 memset(name, 0, sizeof(name)); 657 gethostname(name, MAXHOSTNAMELEN); 658 659 result = _sasl_strdup(name, &conn->clientFQDN, NULL); 660 661 if(result == SASL_OK) return SASL_OK; 662 663 #ifdef _SUN_SDK_ 664 conn->cparams->iplocalport = (*pconn)->iplocalport; 665 conn->cparams->iploclen = strlen((*pconn)->iplocalport); 666 conn->cparams->ipremoteport = (*pconn)->ipremoteport; 667 conn->cparams->ipremlen = strlen((*pconn)->ipremoteport); 668 #endif /* _SUN_SDK_ */ 669 670 /* result isn't SASL_OK */ 671 _sasl_conn_dispose(*pconn); 672 sasl_FREE(*pconn); 673 *pconn = NULL; 674 #ifdef _SUN_SDK_ 675 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR, 676 "Out of memory in sasl_client_new"); 677 #else 678 _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); 679 #endif /* _SUN_SDK_ */ 680 return result; 681 } 682 683 static int have_prompts(sasl_conn_t *conn, 684 const sasl_client_plug_t *mech) 685 { 686 static const unsigned long default_prompts[] = { 687 SASL_CB_AUTHNAME, 688 SASL_CB_PASS, 689 SASL_CB_LIST_END 690 }; 691 692 const unsigned long *prompt; 693 int (*pproc)(); 694 void *pcontext; 695 int result; 696 697 for (prompt = (mech->required_prompts 698 ? mech->required_prompts : 699 default_prompts); 700 *prompt != SASL_CB_LIST_END; 701 prompt++) { 702 result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); 703 if (result != SASL_OK && result != SASL_INTERACT) 704 return 0; /* we don't have this required prompt */ 705 } 706 707 return 1; /* we have all the prompts */ 708 } 709 710 /* select a mechanism for a connection 711 * mechlist -- mechanisms server has available (punctuation ignored) 712 * secret -- optional secret from previous session 713 * output: 714 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue 715 * clientout -- the initial client response to send to the server 716 * mech -- set to mechanism name 717 * 718 * Returns: 719 * SASL_OK -- success 720 * SASL_NOMEM -- not enough memory 721 * SASL_NOMECH -- no mechanism meets requested properties 722 * SASL_INTERACT -- user interaction needed to fill in prompt_need list 723 */ 724 725 /* xxx confirm this with rfc 2222 726 * SASL mechanism allowable characters are "AZaz-_" 727 * seperators can be any other characters and of any length 728 * even variable lengths between 729 * 730 * Apps should be encouraged to simply use space or comma space 731 * though 732 */ 733 int sasl_client_start(sasl_conn_t *conn, 734 const char *mechlist, 735 sasl_interact_t **prompt_need, 736 const char **clientout, 737 unsigned *clientoutlen, 738 const char **mech) 739 { 740 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; 741 char name[SASL_MECHNAMEMAX + 1]; 742 cmechanism_t *m=NULL,*bestm=NULL; 743 size_t pos=0,place; 744 size_t list_len; 745 sasl_ssf_t bestssf = 0, minssf = 0; 746 int result; 747 #ifdef _SUN_SDK_ 748 _sasl_global_context_t *gctx = (conn == NULL) ? 749 _sasl_gbl_ctx() : conn->gctx; 750 cmech_list_t *cmechlist; 751 752 if(gctx->sasl_client_active==0) return SASL_NOTINIT; 753 cmechlist = gctx->cmechlist; 754 #else 755 if(_sasl_client_active==0) return SASL_NOTINIT; 756 #endif /* _SUN_SDK_ */ 757 758 if (!conn) return SASL_BADPARAM; 759 760 /* verify parameters */ 761 if (mechlist == NULL) 762 PARAMERROR(conn); 763 764 /* if prompt_need != NULL we've already been here 765 and just need to do the continue step again */ 766 767 /* do a step */ 768 /* FIXME: Hopefully they only give us our own prompt_need back */ 769 if (prompt_need && *prompt_need != NULL) { 770 goto dostep; 771 } 772 773 #ifdef _SUN_SDK_ 774 if (c_conn->mech != NULL) { 775 if (c_conn->mech->plug->mech_dispose != NULL) { 776 c_conn->mech->plug->mech_dispose(conn->context, 777 c_conn->cparams->utils); 778 c_conn->mech = NULL; 779 } 780 } 781 memset(&conn->oparams, 0, sizeof(sasl_out_params_t)); 782 783 (void) _load_client_plugins(gctx); 784 #endif /* _SUN_SDK_ */ 785 786 if(conn->props.min_ssf < conn->external.ssf) { 787 minssf = 0; 788 } else { 789 minssf = conn->props.min_ssf - conn->external.ssf; 790 } 791 792 /* parse mechlist */ 793 list_len = strlen(mechlist); 794 795 while (pos<list_len) 796 { 797 place=0; 798 while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos]) 799 || mechlist[pos] == '_' 800 || mechlist[pos] == '-')) { 801 name[place]=mechlist[pos]; 802 pos++; 803 place++; 804 if (SASL_MECHNAMEMAX < place) { 805 place--; 806 while(pos<list_len && (isalnum((unsigned char)mechlist[pos]) 807 || mechlist[pos] == '_' 808 || mechlist[pos] == '-')) 809 pos++; 810 } 811 } 812 pos++; 813 name[place]=0; 814 815 if (! place) continue; 816 817 /* foreach in server list */ 818 for (m = cmechlist->mech_list; m != NULL; m = m->next) { 819 int myflags; 820 821 /* Is this the mechanism the server is suggesting? */ 822 if (strcasecmp(m->plug->mech_name, name)) 823 continue; /* no */ 824 825 /* Do we have the prompts for it? */ 826 if (!have_prompts(conn, m->plug)) 827 break; 828 829 /* Is it strong enough? */ 830 if (minssf > m->plug->max_ssf) 831 break; 832 833 #ifdef _INTEGRATED_SOLARIS_ 834 /* If not SUN supplied mech, it has no strength */ 835 if (minssf > 0 && !m->sun_reg) 836 break; 837 #endif /* _INTEGRATED_SOLARIS_ */ 838 839 /* Does it meet our security properties? */ 840 myflags = conn->props.security_flags; 841 842 /* if there's an external layer this is no longer plaintext */ 843 if ((conn->props.min_ssf <= conn->external.ssf) && 844 (conn->external.ssf > 1)) { 845 myflags &= ~SASL_SEC_NOPLAINTEXT; 846 } 847 848 if (((myflags ^ m->plug->security_flags) & myflags) != 0) { 849 break; 850 } 851 852 /* Can we meet it's features? */ 853 if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) 854 && !conn->serverFQDN) { 855 break; 856 } 857 858 /* Can it meet our features? */ 859 if ((conn->flags & SASL_NEED_PROXY) && 860 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { 861 break; 862 } 863 864 #ifdef PREFER_MECH 865 #ifdef _INTEGRATED_SOLARIS_ 866 if (strcasecmp(m->plug->mech_name, PREFER_MECH) && 867 bestm && (m->sun_reg && m->plug->max_ssf <= bestssf) || 868 (m->plug->max_ssf == 0)) { 869 #else 870 if (strcasecmp(m->plug->mech_name, PREFER_MECH) && 871 bestm && m->plug->max_ssf <= bestssf) { 872 #endif /* _INTEGRATED_SOLARIS_ */ 873 874 /* this mechanism isn't our favorite, and it's no better 875 than what we already have! */ 876 break; 877 } 878 #else 879 #ifdef _INTEGRATED_SOLARIS_ 880 if (bestm && m->sun_reg && m->plug->max_ssf <= bestssf) { 881 #else 882 883 if (bestm && m->plug->max_ssf <= bestssf) { 884 #endif /* _INTEGRATED_SOLARIS_ */ 885 886 /* this mechanism is no better than what we already have! */ 887 break; 888 } 889 #endif 890 891 /* compare security flags, only take new mechanism if it has 892 * all the security flags of the previous one. 893 * 894 * From the mechanisms we ship with, this yields the order: 895 * 896 * SRP 897 * GSSAPI + KERBEROS_V4 898 * DIGEST + OTP 899 * CRAM + EXTERNAL 900 * PLAIN + LOGIN + ANONYMOUS 901 * 902 * This might be improved on by comparing the numeric value of 903 * the bitwise-or'd security flags, which splits DIGEST/OTP, 904 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we 905 * are depending on the numeric values of the flags (which may 906 * change, and their ordering could be considered dumb luck. 907 */ 908 909 if (bestm && 910 ((m->plug->security_flags ^ bestm->plug->security_flags) & 911 bestm->plug->security_flags)) { 912 break; 913 } 914 915 if (mech) { 916 *mech = m->plug->mech_name; 917 } 918 #ifdef _INTEGRATED_SOLARIS_ 919 bestssf = m->sun_reg ? m->plug->max_ssf : 0; 920 #else 921 bestssf = m->plug->max_ssf; 922 #endif /* _INTEGRATED_SOLARIS_ */ 923 bestm = m; 924 break; 925 } 926 } 927 928 if (bestm == NULL) { 929 #ifdef _INTEGRATED_SOLARIS_ 930 sasl_seterror(conn, 0, gettext("No worthy mechs found")); 931 #else 932 sasl_seterror(conn, 0, "No worthy mechs found"); 933 #endif /* _INTEGRATED_SOLARIS_ */ 934 result = SASL_NOMECH; 935 goto done; 936 } 937 938 /* make (the rest of) cparams */ 939 c_conn->cparams->service = conn->service; 940 c_conn->cparams->servicelen = strlen(conn->service); 941 942 c_conn->cparams->serverFQDN = conn->serverFQDN; 943 c_conn->cparams->slen = strlen(conn->serverFQDN); 944 945 c_conn->cparams->clientFQDN = c_conn->clientFQDN; 946 c_conn->cparams->clen = strlen(c_conn->clientFQDN); 947 948 c_conn->cparams->external_ssf = conn->external.ssf; 949 c_conn->cparams->props = conn->props; 950 #ifdef _INTEGRATED_SOLARIS_ 951 if (!bestm->sun_reg) { 952 c_conn->cparams->props.min_ssf = 0; 953 c_conn->cparams->props.max_ssf = 0; 954 } 955 c_conn->base.sun_reg = bestm->sun_reg; 956 #endif /* _INTEGRATED_SOLARIS_ */ 957 c_conn->mech = bestm; 958 959 /* init that plugin */ 960 #ifdef _SUN_SDK_ 961 result = c_conn->mech->plug->mech_new(c_conn->mech->glob_context, 962 #else 963 result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context, 964 #endif /* _SUN_SDK_ */ 965 c_conn->cparams, 966 &(conn->context)); 967 if(result != SASL_OK) goto done; 968 969 /* do a step -- but only if we can do a client-send-first */ 970 dostep: 971 if(clientout) { 972 if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) { 973 *clientout = NULL; 974 *clientoutlen = 0; 975 result = SASL_CONTINUE; 976 } else { 977 result = sasl_client_step(conn, NULL, 0, prompt_need, 978 clientout, clientoutlen); 979 } 980 } 981 else 982 result = SASL_CONTINUE; 983 984 done: 985 RETURN(conn, result); 986 } 987 988 /* do a single authentication step. 989 * serverin -- the server message received by the client, MUST have a NUL 990 * sentinel, not counted by serverinlen 991 * output: 992 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue 993 * clientout -- the client response to send to the server 994 * 995 * returns: 996 * SASL_OK -- success 997 * SASL_INTERACT -- user interaction needed to fill in prompt_need list 998 * SASL_BADPROT -- server protocol incorrect/cancelled 999 * SASL_BADSERV -- server failed mutual auth 1000 */ 1001 1002 int sasl_client_step(sasl_conn_t *conn, 1003 const char *serverin, 1004 unsigned serverinlen, 1005 sasl_interact_t **prompt_need, 1006 const char **clientout, 1007 unsigned *clientoutlen) 1008 { 1009 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; 1010 int result; 1011 1012 #ifdef _SUN_SDK_ 1013 _sasl_global_context_t *gctx = (conn == NULL) ? 1014 _sasl_gbl_ctx() : conn->gctx; 1015 1016 if(gctx->sasl_client_active==0) return SASL_NOTINIT; 1017 #else 1018 if(_sasl_client_active==0) return SASL_NOTINIT; 1019 #endif /* _SUN_SDK_ */ 1020 if(!conn) return SASL_BADPARAM; 1021 1022 /* check parameters */ 1023 if ((serverin==NULL) && (serverinlen>0)) 1024 PARAMERROR(conn); 1025 1026 /* Don't do another step if the plugin told us that we're done */ 1027 if (conn->oparams.doneflag) { 1028 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag"); 1029 return SASL_FAIL; 1030 } 1031 1032 if(clientout) *clientout = NULL; 1033 if(clientoutlen) *clientoutlen = 0; 1034 1035 /* do a step */ 1036 result = c_conn->mech->plug->mech_step(conn->context, 1037 c_conn->cparams, 1038 serverin, 1039 serverinlen, 1040 prompt_need, 1041 clientout, clientoutlen, 1042 &conn->oparams); 1043 1044 if (result == SASL_OK) { 1045 /* So we're done on this end, but if both 1046 * 1. the mech does server-send-last 1047 * 2. the protocol does not 1048 * we need to return no data */ 1049 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { 1050 *clientout = ""; 1051 *clientoutlen = 0; 1052 } 1053 1054 if(!conn->oparams.maxoutbuf) { 1055 conn->oparams.maxoutbuf = conn->props.maxbufsize; 1056 } 1057 1058 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { 1059 #ifdef _SUN_SDK_ 1060 _sasl_log(conn, SASL_LOG_ERR, 1061 "mech did not call canon_user for both authzid and authid"); 1062 #else 1063 sasl_seterror(conn, 0, 1064 "mech did not call canon_user for both authzid and authid"); 1065 #endif /* _SUN_SDK_ */ 1066 result = SASL_BADPROT; 1067 } 1068 } 1069 1070 RETURN(conn,result); 1071 } 1072 1073 /* returns the length of all the mechanisms 1074 * added up 1075 */ 1076 1077 #ifdef _SUN_SDK_ 1078 static unsigned mech_names_len(_sasl_global_context_t *gctx) 1079 { 1080 cmech_list_t *cmechlist = gctx->cmechlist; 1081 #else 1082 static unsigned mech_names_len() 1083 { 1084 #endif /* _SUN_SDK_ */ 1085 cmechanism_t *listptr; 1086 unsigned result = 0; 1087 1088 for (listptr = cmechlist->mech_list; 1089 listptr; 1090 listptr = listptr->next) 1091 result += strlen(listptr->plug->mech_name); 1092 1093 return result; 1094 } 1095 1096 1097 int _sasl_client_listmech(sasl_conn_t *conn, 1098 const char *prefix, 1099 const char *sep, 1100 const char *suffix, 1101 const char **result, 1102 unsigned *plen, 1103 int *pcount) 1104 { 1105 cmechanism_t *m=NULL; 1106 sasl_ssf_t minssf = 0; 1107 int ret; 1108 unsigned int resultlen; 1109 int flag; 1110 const char *mysep; 1111 #ifdef _SUN_SDK_ 1112 _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx; 1113 cmech_list_t *cmechlist; 1114 1115 if(gctx->sasl_client_active==0) return SASL_NOTINIT; 1116 cmechlist = gctx->cmechlist; 1117 #else 1118 if(_sasl_client_active == 0) return SASL_NOTINIT; 1119 #endif /* _SUN_SDK_ */ 1120 if (!conn) return SASL_BADPARAM; 1121 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); 1122 1123 if (! result) 1124 PARAMERROR(conn); 1125 1126 #ifdef _SUN_SDK_ 1127 (void) _load_client_plugins(gctx); 1128 #endif /* _SUN_SDK_ */ 1129 1130 if (plen != NULL) 1131 *plen = 0; 1132 if (pcount != NULL) 1133 *pcount = 0; 1134 1135 if (sep) { 1136 mysep = sep; 1137 } else { 1138 mysep = " "; 1139 } 1140 1141 if(conn->props.min_ssf < conn->external.ssf) { 1142 minssf = 0; 1143 } else { 1144 minssf = conn->props.min_ssf - conn->external.ssf; 1145 } 1146 1147 if (! cmechlist || cmechlist->mech_length <= 0) 1148 INTERROR(conn, SASL_NOMECH); 1149 1150 resultlen = (prefix ? strlen(prefix) : 0) 1151 + (strlen(mysep) * (cmechlist->mech_length - 1)) 1152 #ifdef _SUN_SDK_ 1153 + mech_names_len(gctx) 1154 #else 1155 + mech_names_len() 1156 #endif /* _SUN_SDK_ */ 1157 + (suffix ? strlen(suffix) : 0) 1158 + 1; 1159 ret = _buf_alloc(&conn->mechlist_buf, 1160 &conn->mechlist_buf_len, resultlen); 1161 if(ret != SASL_OK) MEMERROR(conn); 1162 1163 if (prefix) 1164 strcpy (conn->mechlist_buf,prefix); 1165 else 1166 *(conn->mechlist_buf) = '\0'; 1167 1168 flag = 0; 1169 for (m = cmechlist->mech_list; m != NULL; m = m->next) { 1170 /* do we have the prompts for it? */ 1171 if (!have_prompts(conn, m->plug)) 1172 continue; 1173 1174 /* is it strong enough? */ 1175 if (minssf > m->plug->max_ssf) 1176 continue; 1177 1178 #ifdef _INTEGRATED_SOLARIS_ 1179 /* If not SUN supplied mech, it has no strength */ 1180 if (minssf > 0 && !m->sun_reg) 1181 continue; 1182 #endif /* _INTEGRATED_SOLARIS_ */ 1183 1184 /* does it meet our security properties? */ 1185 if (((conn->props.security_flags ^ m->plug->security_flags) 1186 & conn->props.security_flags) != 0) { 1187 continue; 1188 } 1189 1190 /* Can we meet it's features? */ 1191 if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) 1192 && !conn->serverFQDN) { 1193 continue; 1194 } 1195 1196 /* Can it meet our features? */ 1197 if ((conn->flags & SASL_NEED_PROXY) && 1198 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { 1199 break; 1200 } 1201 1202 /* Okay, we like it, add it to the list! */ 1203 1204 if (pcount != NULL) 1205 (*pcount)++; 1206 1207 /* print seperator */ 1208 if (flag) { 1209 strcat(conn->mechlist_buf, mysep); 1210 } else { 1211 flag = 1; 1212 } 1213 1214 /* now print the mechanism name */ 1215 strcat(conn->mechlist_buf, m->plug->mech_name); 1216 } 1217 1218 if (suffix) 1219 strcat(conn->mechlist_buf,suffix); 1220 1221 if (plen!=NULL) 1222 *plen=strlen(conn->mechlist_buf); 1223 1224 *result = conn->mechlist_buf; 1225 1226 return SASL_OK; 1227 } 1228 1229 #ifdef _SUN_SDK_ 1230 sasl_string_list_t *_sasl_client_mechs(_sasl_global_context_t *gctx) 1231 { 1232 cmech_list_t *cmechlist = gctx->cmechlist; 1233 #else 1234 sasl_string_list_t *_sasl_client_mechs(void) 1235 { 1236 #endif /* _SUN_SDK_ */ 1237 cmechanism_t *listptr; 1238 sasl_string_list_t *retval = NULL, *next=NULL; 1239 1240 #ifdef _SUN_SDK_ 1241 if(!gctx->sasl_client_active) return NULL; 1242 #else 1243 if(!_sasl_client_active) return NULL; 1244 #endif /* _SUN_SDK_ */ 1245 1246 /* make list */ 1247 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) { 1248 next = sasl_ALLOC(sizeof(sasl_string_list_t)); 1249 1250 if(!next && !retval) return NULL; 1251 else if(!next) { 1252 next = retval->next; 1253 do { 1254 sasl_FREE(retval); 1255 retval = next; 1256 next = retval->next; 1257 } while(next); 1258 return NULL; 1259 } 1260 1261 next->d = listptr->plug->mech_name; 1262 1263 if(!retval) { 1264 next->next = NULL; 1265 retval = next; 1266 } else { 1267 next->next = retval; 1268 retval = next; 1269 } 1270 } 1271 1272 return retval; 1273 } 1274