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