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