1/* SASL client API implementation 2 * Rob Siemborski 3 * Tim Martin 4 * $Id: client.c,v 1.86 2011/09/01 14:12:53 mel Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <limits.h> 50#include <ctype.h> 51#include <string.h> 52#ifdef HAVE_UNISTD_H 53#include <unistd.h> 54#endif 55 56/* SASL Headers */ 57#include "sasl.h" 58#include "saslplug.h" 59#include "saslutil.h" 60#include "saslint.h" 61 62static cmech_list_t *cmechlist; /* global var which holds the list */ 63static sasl_global_callbacks_t global_callbacks_client; 64static int _sasl_client_active = 0; 65 66static int init_mechlist() 67{ 68 cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks_client); 69 if (cmechlist->utils==NULL) 70 return SASL_NOMEM; 71 72 cmechlist->mech_list=NULL; 73 cmechlist->mech_length=0; 74 75 return SASL_OK; 76} 77 78int sasl_client_done(void) 79{ 80 int result = SASL_CONTINUE; 81 82 if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) { 83 return SASL_NOTINIT; 84 } 85 86 if (_sasl_client_cleanup_hook) { 87 result = _sasl_client_cleanup_hook(); 88 89 if (result == SASL_OK) { 90 _sasl_client_idle_hook = NULL; 91 _sasl_client_cleanup_hook = NULL; 92 } else { 93 return result; 94 } 95 } 96 97 if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { 98 return result; 99 } 100 101 sasl_common_done(); 102 103 return SASL_OK; 104} 105 106static int client_done(void) { 107 cmechanism_t *cm; 108 cmechanism_t *cprevm; 109 110 if (!_sasl_client_active) { 111 return SASL_NOTINIT; 112 } else { 113 _sasl_client_active--; 114 } 115 116 if(_sasl_client_active) { 117 /* Don't de-init yet! Our refcount is nonzero. */ 118 return SASL_CONTINUE; 119 } 120 121 cm = cmechlist->mech_list; /* m point to beginning of the list */ 122 while (cm != NULL) { 123 cprevm = cm; 124 cm = cm->next; 125 126 if (cprevm->m.plug->mech_free) { 127 cprevm->m.plug->mech_free(cprevm->m.plug->glob_context, 128 cmechlist->utils); 129 } 130 131 sasl_FREE(cprevm->m.plugname); 132 sasl_FREE(cprevm); 133 } 134 _sasl_free_utils(&cmechlist->utils); 135 sasl_FREE(cmechlist); 136 137 cmechlist = NULL; 138 139 return SASL_OK; 140} 141 142/* This is nearly identical to the version in server.c. 143 Keep in sync. */ 144static int mech_compare(const sasl_client_plug_t *a, 145 const sasl_client_plug_t *b) 146{ 147 unsigned sec_diff; 148 unsigned features_diff; 149 150 /* XXX the following is fairly arbitrary, but its independent 151 of the order in which the plugins are loaded 152 */ 153 sec_diff = a->security_flags ^ b->security_flags; 154 if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1; 155 if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1; 156 if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1; 157 if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1; 158 if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1; 159 if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1; 160 if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1; 161 if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1; 162 if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1; 163 if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1; 164 if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1; 165 if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1; 166 if (sec_diff & a->security_flags & SASL_SEC_DEVICE_AUTH) return 1; 167 if (sec_diff & b->security_flags & SASL_SEC_DEVICE_AUTH) return -1; 168 if (sec_diff & a->security_flags & SASL_SEC_NOLEGACY) return 1; 169 if (sec_diff & b->security_flags & SASL_SEC_NOLEGACY) return -1; 170 171 features_diff = a->features ^ b->features; 172 if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1; 173 if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1; 174 175 if (a->max_ssf > b->max_ssf) return 1; 176 if (a->max_ssf < b->max_ssf) return -1; 177 178 return 0; 179} 180 181int sasl_client_add_plugin(const char *plugname, 182 sasl_client_plug_init_t *entry_point) 183{ 184 int plugcount; 185 sasl_client_plug_t *pluglist; 186 cmechanism_t *mech, *mp; 187 int result; 188 int version; 189 int lupe; 190 191 if (!plugname || !entry_point) return SASL_BADPARAM; 192 193 result = entry_point(cmechlist->utils, 194 SASL_CLIENT_PLUG_VERSION, 195 &version, 196 &pluglist, 197 &plugcount); 198 199 if (result != SASL_OK) 200 { 201 _sasl_log(NULL, SASL_LOG_WARN, 202 "sasl_client_add_plugin(): entry_point(): failed for plugname %s: %z", 203 plugname, result); 204 return result; 205 } 206 207 if (version != SASL_CLIENT_PLUG_VERSION) 208 { 209 _sasl_log(NULL, SASL_LOG_WARN, 210 "version conflict in sasl_client_add_plugin for %s", plugname); 211 return SASL_BADVERS; 212 } 213 214 for (lupe=0; lupe < plugcount; lupe++, pluglist++) 215 { 216 mech = sasl_ALLOC(sizeof(cmechanism_t)); 217 if (!mech) return SASL_NOMEM; 218 219 mech->m.plug = pluglist; 220 if (_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) { 221 sasl_FREE(mech); 222 return SASL_NOMEM; 223 } 224 mech->m.version = version; 225 226 /* sort mech_list by relative "strength" */ 227 mp = cmechlist->mech_list; 228 if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) { 229 /* add mech to head of list */ 230 mech->next = cmechlist->mech_list; 231 cmechlist->mech_list = mech; 232 } else { 233 /* find where to insert mech into list */ 234 while (mp->next && 235 mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next; 236 mech->next = mp->next; 237 mp->next = mech; 238 } 239 240 cmechlist->mech_length++; 241 } 242 243 return SASL_OK; 244} 245 246static int 247client_idle(sasl_conn_t *conn) 248{ 249 cmechanism_t *m; 250 if (! cmechlist) 251 return 0; 252 253 for (m = cmechlist->mech_list; 254 m; 255 m = m->next) 256 if (m->m.plug->idle 257 && m->m.plug->idle(m->m.plug->glob_context, 258 conn, 259 conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) 260 return 1; 261 return 0; 262} 263 264/* initialize the SASL client drivers 265 * callbacks -- base callbacks for all client connections 266 * returns: 267 * SASL_OK -- Success 268 * SASL_NOMEM -- Not enough memory 269 * SASL_BADVERS -- Mechanism version mismatch 270 * SASL_BADPARAM -- error in config file 271 * SASL_NOMECH -- No mechanisms available 272 * ... 273 */ 274 275int sasl_client_init(const sasl_callback_t *callbacks) 276{ 277 int ret; 278 const add_plugin_list_t ep_list[] = { 279 { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, 280 { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, 281 { NULL, NULL } 282 }; 283 284 /* lock allocation type */ 285 _sasl_allocation_locked++; 286 287 if(_sasl_client_active) { 288 /* We're already active, just increase our refcount */ 289 /* xxx do something with the callback structure? */ 290 _sasl_client_active++; 291 return SASL_OK; 292 } 293 294 global_callbacks_client.callbacks = callbacks; 295 global_callbacks_client.appname = NULL; 296 297 cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); 298 if (cmechlist==NULL) return SASL_NOMEM; 299 300 /* We need to call client_done if we fail now */ 301 _sasl_client_active = 1; 302 303 /* load plugins */ 304 ret=init_mechlist(); 305 if (ret!=SASL_OK) { 306 client_done(); 307 return ret; 308 } 309 310 sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); 311 312 ret = _sasl_common_init(&global_callbacks_client); 313 314 if (ret == SASL_OK) 315 ret = _sasl_load_plugins(ep_list, 316 _sasl_find_getpath_callback(callbacks), 317 _sasl_find_verifyfile_callback(callbacks)); 318 319 if (ret == SASL_OK) { 320 _sasl_client_cleanup_hook = &client_done; 321 _sasl_client_idle_hook = &client_idle; 322 323 ret = _sasl_build_mechlist(); 324 } else { 325 client_done(); 326 } 327 328 return ret; 329} 330 331static void client_dispose(sasl_conn_t *pconn) 332{ 333 sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; 334 335 if (c_conn->mech && c_conn->mech->m.plug->mech_dispose) { 336 c_conn->mech->m.plug->mech_dispose(pconn->context, 337 c_conn->cparams->utils); 338 } 339 340 pconn->context = NULL; 341 342 if (c_conn->clientFQDN) 343 sasl_FREE(c_conn->clientFQDN); 344 345 if (c_conn->cparams) { 346 _sasl_free_utils(&(c_conn->cparams->utils)); 347 sasl_FREE(c_conn->cparams); 348 } 349 350 if (c_conn->mech_list != cmechlist->mech_list) { 351 /* free connection-specific mech_list */ 352 cmechanism_t *m, *prevm; 353 354 m = c_conn->mech_list; /* m point to beginning of the list */ 355 356 while (m) { 357 prevm = m; 358 m = m->next; 359 sasl_FREE(prevm); 360 } 361 } 362 363 _sasl_conn_dispose(pconn); 364} 365 366/* initialize a client exchange based on the specified mechanism 367 * service -- registered name of the service using SASL (e.g. "imap") 368 * serverFQDN -- the fully qualified domain name of the server 369 * iplocalport -- client IPv4/IPv6 domain literal string with port 370 * (if NULL, then mechanisms requiring IPaddr are disabled) 371 * ipremoteport -- server IPv4/IPv6 domain literal string with port 372 * (if NULL, then mechanisms requiring IPaddr are disabled) 373 * prompt_supp -- list of client interactions supported 374 * may also include sasl_getopt_t context & call 375 * NULL prompt_supp = user/pass via SASL_INTERACT only 376 * NULL proc = interaction supported via SASL_INTERACT 377 * secflags -- security flags (see above) 378 * in/out: 379 * pconn -- connection negotiation structure 380 * pointer to NULL => allocate new 381 * non-NULL => recycle storage and go for next available mech 382 * 383 * Returns: 384 * SASL_OK -- success 385 * SASL_NOMECH -- no mechanism meets requested properties 386 * SASL_NOMEM -- not enough memory 387 */ 388int sasl_client_new(const char *service, 389 const char *serverFQDN, 390 const char *iplocalport, 391 const char *ipremoteport, 392 const sasl_callback_t *prompt_supp, 393 unsigned flags, 394 sasl_conn_t **pconn) 395{ 396 int result; 397 char name[MAXHOSTNAMELEN]; 398 sasl_client_conn_t *conn; 399 sasl_utils_t *utils; 400 sasl_getopt_t *getopt; 401 void *context; 402 const char *mlist = NULL; 403 int plus = 0; 404 405 if (_sasl_client_active == 0) return SASL_NOTINIT; 406 407 /* Remember, serverFQDN, iplocalport and ipremoteport can be NULL and be valid! */ 408 if (!pconn || !service) 409 return SASL_BADPARAM; 410 411 *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); 412 if (*pconn==NULL) { 413 _sasl_log(NULL, SASL_LOG_ERR, 414 "Out of memory allocating connection context"); 415 return SASL_NOMEM; 416 } 417 memset(*pconn, 0, sizeof(sasl_client_conn_t)); 418 419 (*pconn)->destroy_conn = &client_dispose; 420 421 conn = (sasl_client_conn_t *)*pconn; 422 423 conn->mech = NULL; 424 425 conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); 426 if (conn->cparams==NULL) 427 MEMERROR(*pconn); 428 memset(conn->cparams,0,sizeof(sasl_client_params_t)); 429 430 result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, 431 &client_idle, serverFQDN, 432 iplocalport, ipremoteport, 433 prompt_supp, &global_callbacks_client); 434 if (result != SASL_OK) RETURN(*pconn, result); 435 436 utils = _sasl_alloc_utils(*pconn, &global_callbacks_client); 437 if (utils == NULL) { 438 MEMERROR(*pconn); 439 } 440 441 utils->conn= *pconn; 442 conn->cparams->utils = utils; 443 444 if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { 445 getopt(context, NULL, "client_mech_list", &mlist, NULL); 446 } 447 448 /* if we have a client_mech_list, create ordered list of 449 available mechanisms for this conn */ 450 if (mlist) { 451 const char *cp; 452 cmechanism_t *mptr, *tail = NULL; 453 cmechanism_t *new; 454 455 while (*mlist) { 456 /* find end of current mech name */ 457 for (cp = mlist; *cp && !isspace((int) *cp); cp++); 458 459 /* search for mech name in loaded plugins */ 460 for (mptr = cmechlist->mech_list; mptr; mptr = mptr->next) { 461 const sasl_client_plug_t *plug = mptr->m.plug; 462 463 if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) { 464 /* found a match */ 465 break; 466 } 467 } 468 if (mptr) { 469 new = sasl_ALLOC(sizeof(cmechanism_t)); 470 if (!new) { 471 result = SASL_NOMEM; 472 goto failed_client_new; 473 } 474 memcpy(&new->m, &mptr->m, sizeof(client_sasl_mechanism_t)); 475 new->next = NULL; 476 477 if (!conn->mech_list) { 478 conn->mech_list = new; 479 tail = conn->mech_list; 480 } else { 481 tail->next = new; 482 tail = new; 483 } 484 conn->mech_length++; 485 } 486 487 /* find next mech name */ 488 mlist = cp; 489 while (*mlist && isspace((int) *mlist)) mlist++; 490 } 491 } else { 492 conn->mech_list = cmechlist->mech_list; 493 conn->mech_length = cmechlist->mech_length; 494 } 495 496 if (conn->mech_list == NULL) { 497 sasl_seterror(*pconn, 0, "No worthy mechs found"); 498 result = SASL_NOMECH; 499 goto failed_client_new; 500 } 501 502 /* Setup the non-lazy parts of cparams, the rest is done in 503 * sasl_client_start */ 504 conn->cparams->utils = utils; 505 conn->cparams->canon_user = &_sasl_canon_user_lookup; 506 conn->cparams->flags = flags; 507 conn->cparams->prompt_supp = (*pconn)->callbacks; 508 509 /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ 510 memset(name, 0, sizeof(name)); 511 if (get_fqhostname (name, MAXHOSTNAMELEN, 0) != 0) { 512 return (SASL_FAIL); 513 } 514 515 result = _sasl_strdup(name, &conn->clientFQDN, NULL); 516 517 if (result == SASL_OK) return SASL_OK; 518 519failed_client_new: 520 /* result isn't SASL_OK */ 521 _sasl_conn_dispose(*pconn); 522 sasl_FREE(*pconn); 523 *pconn = NULL; 524 _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); 525 return result; 526} 527 528static int have_prompts(sasl_conn_t *conn, 529 const sasl_client_plug_t *mech) 530{ 531 static const unsigned long default_prompts[] = { 532 SASL_CB_AUTHNAME, 533 SASL_CB_PASS, 534 SASL_CB_LIST_END 535 }; 536 537 const unsigned long *prompt; 538 sasl_callback_ft pproc; 539 void *pcontext; 540 int result; 541 542 for (prompt = (mech->required_prompts 543 ? mech->required_prompts : 544 default_prompts); 545 *prompt != SASL_CB_LIST_END; 546 prompt++) { 547 result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); 548 if (result != SASL_OK && result != SASL_INTERACT) 549 return 0; /* we don't have this required prompt */ 550 } 551 552 return 1; /* we have all the prompts */ 553} 554 555static int 556_mech_plus_p(const char *mech, size_t len) 557{ 558 return (len > 5 && strncasecmp(&mech[len - 5], "-PLUS", 5) == 0); 559} 560 561static char *_sasl_sort_mechlist(const sasl_utils_t *utils, const char *unsorted); 562 563/* 564 * Order PLUS mechanisms first. Returns NUL separated list of 565 * *count items. 566 */ 567static int 568_sasl_client_order_mechs(const sasl_utils_t *utils, 569 const char *unordered_mechs, 570 int has_cb_data, 571 char **ordered_mechs, 572 size_t *count, 573 int *server_can_cb) 574{ 575 char *list, *listp, *mechs; 576 size_t i, mechslen, start; 577 578 *count = 0; 579 *server_can_cb = 0; 580 581 mechs = _sasl_sort_mechlist(utils, unordered_mechs); // APPLE: presort mechlist to match plugin order mech_compare(), 15938147 582 583 if (mechs == NULL || mechs[0] == '\0') 584 return SASL_NOMECH; 585 586 mechslen = strlen(mechs); 587 588 listp = list = utils->malloc(mechslen + 1); 589 if (list == NULL) 590 return SASL_NOMEM; 591 592 /* As per RFC 4422: 593 * SASL mechanism allowable characters are "AZ-_" 594 * separators can be any other characters and of any length 595 * even variable lengths between. 596 * 597 * But for convenience we accept lowercase ASCII. 598 * 599 * Apps should be encouraged to simply use space or comma space 600 * though 601 */ 602#define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-') 603#define NON_MECH_CHAR "\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&\'()*+,./:;<=>?@[\\]^`{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe" 604 do { 605 for (i = start = 0; i <= mechslen; i++) { 606 if (!ismechchar(mechs[i])) { 607 const char *mechp = &mechs[start]; 608 size_t len = i - start; 609 610 if (len != 0 && 611 _mech_plus_p(mechp, len) == has_cb_data) { 612 memcpy(listp, mechp, len); 613 listp[len] = '\0'; 614 listp += len + 1; 615 (*count)++; 616 if (*server_can_cb == 0 && has_cb_data) 617 *server_can_cb = 1; 618 } 619 start = ++i; 620 } 621 } 622 if (has_cb_data) 623 has_cb_data = 0; 624 else 625 break; 626 } while (1); 627 628 if (*count == 0) { 629 utils->free(list); 630 return SASL_NOMECH; 631 } 632 if (mechs) 633 utils->free(mechs); 634 635 *ordered_mechs = list; 636 637 return SASL_OK; 638} 639 640static char **_sasl_split_mechlist(const sasl_utils_t *utils, char *s, size_t *count_out) 641{ 642 char *token = NULL, *brk = NULL; 643 size_t i = 0, max_count = 2; 644 645 for (i = 0; i < strlen(s); ++i) { 646 if (!ismechchar(s[i])) max_count += 1; 647 } 648 649 char **array = utils->malloc(max_count * sizeof(char *)); 650 651 bzero(array, max_count * sizeof(char *)); 652 653 token = strtok_r(s, NON_MECH_CHAR, &brk); 654 i = 0; 655 while(token) { 656 array[i] = token; 657 i += 1; 658 if (i > max_count) return NULL; 659 660 token = strtok_r(NULL, NON_MECH_CHAR, &brk); 661 } 662 *count_out = i; 663 664 return array; 665} 666 667static char *_sasl_join_mechlist(const sasl_utils_t *utils, char **array) 668{ 669 size_t max_length = 1; 670 char *s = NULL; 671 672 for (size_t i = 0; array[i]; ++i) { 673 max_length += strlen(array[i]) + 1; 674 } 675 s = utils->malloc(max_length); 676 bzero(s, max_length); 677 678 for (size_t i = 0; array[i]; ++i) { 679 strlcat(s, array[i], max_length); 680 strlcat(s, " ", max_length); 681 } 682 683 return s; 684} 685 686/* Get the mechanism index from the loaded plugin list, which was sorted on load 687 * by mech_compare() in sasl_client_add_plugin(). */ 688static int _sasl_mech_index(const char *name) 689{ 690 cmechanism_t *m; 691 int plus = 0; 692 size_t i = 0; 693 694 for (m = cmechlist->mech_list; m != NULL; m = m->next) { 695 size_t name_len = strlen(name); 696 if (_sasl_is_equal_mech(name, m->m.plug->mech_name, name_len, &plus)) { 697 return i; 698 } 699 ++i; 700 } 701 702 return INT_MAX; 703} 704 705static int _sasl_mech_order(const char *a, const char *b) 706{ 707 return _sasl_mech_index(a) - _sasl_mech_index(b); 708} 709 710static char *_sasl_sort_mechlist(const sasl_utils_t *utils, const char *unsorted) 711{ 712 char *unsorted_copy = strdup(unsorted); 713 char *sorted = NULL; 714 size_t count = 0; 715 char **array = _sasl_split_mechlist(utils, unsorted_copy, &count); 716 if (!array) { 717 free(unsorted_copy); 718 return NULL; 719 } 720 721 qsort_b(array, count, sizeof(char *), ^(const void *a, const void *b) { 722 return _sasl_mech_order(*(const char **)a, *(const char **)b); 723 }); 724 725 sorted = _sasl_join_mechlist(utils, array); 726 utils->free(array); 727 utils->free(unsorted_copy); 728 729 return sorted; 730} 731 732static INLINE int 733_sasl_cbinding_disp(sasl_client_params_t *cparams, 734 int mech_nego, 735 int server_can_cb, 736 sasl_cbinding_disp_t *cbindingdisp) 737{ 738 /* 739 * If negotiating mechanisms, then we fail immediately if the 740 * client requires channel binding and the server does not 741 * advertise support. Otherwise we send "y" (which later will 742 * become "p" if we select a supporting mechanism). 743 * 744 * If the client explicitly selected a mechanism, then we only 745 * send channel bindings if they're marked critical. 746 */ 747 748 *cbindingdisp = SASL_CB_DISP_NONE; 749 750 if (SASL_CB_PRESENT(cparams)) { 751 if (mech_nego) { 752 if (!server_can_cb && SASL_CB_CRITICAL(cparams)) { 753 return SASL_NOMECH; 754 } else { 755 *cbindingdisp = SASL_CB_DISP_WANT; 756 } 757 } else if (SASL_CB_CRITICAL(cparams)) { 758 *cbindingdisp = SASL_CB_DISP_USED; 759 } 760 } 761 762 return SASL_OK; 763} 764 765/* select a mechanism for a connection 766 * mechlist -- mechanisms server has available (punctuation ignored) 767 * secret -- optional secret from previous session 768 * output: 769 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue 770 * clientout -- the initial client response to send to the server 771 * mech -- set to mechanism name 772 * 773 * Returns: 774 * SASL_OK -- success 775 * SASL_NOMEM -- not enough memory 776 * SASL_NOMECH -- no mechanism meets requested properties 777 * SASL_INTERACT -- user interaction needed to fill in prompt_need list 778 */ 779 780/* 781 * SASL mechanism allowable characters are "AZ-_" 782 * separators can be any other characters and of any length 783 * even variable lengths between. 784 * 785 * But for convenience we accept lowercase ASCII. 786 * 787 * Apps should be encouraged to simply use space or comma space 788 * though 789 */ 790int sasl_client_start(sasl_conn_t *conn, 791 const char *mechlist, 792 sasl_interact_t **prompt_need, 793 const char **clientout, 794 unsigned *clientoutlen, 795 const char **mech) 796{ 797 sasl_client_conn_t *c_conn = (sasl_client_conn_t *) conn; 798 char *ordered_mechs = NULL, *name; 799 cmechanism_t *m = NULL, *bestm = NULL; 800 size_t i, list_len, name_len; 801 sasl_ssf_t bestssf = 0, minssf = 0; 802 int result, server_can_cb = 0; 803 sasl_cbinding_disp_t cbindingdisp; 804 sasl_cbinding_disp_t cur_cbindingdisp; 805 sasl_cbinding_disp_t best_cbindingdisp = SASL_CB_DISP_NONE; 806 807 if (_sasl_client_active == 0) return SASL_NOTINIT; 808 809 if (!conn) return SASL_BADPARAM; 810 811 /* verify parameters */ 812 if (mechlist == NULL) { 813 PARAMERROR(conn); 814 } 815 816 /* if prompt_need != NULL we've already been here 817 and just need to do the continue step again */ 818 819 /* do a step */ 820 /* FIXME: Hopefully they only give us our own prompt_need back */ 821 if (prompt_need && *prompt_need != NULL) { 822 goto dostep; 823 } 824 825 if (conn->props.min_ssf < conn->external.ssf) { 826 minssf = 0; 827 } else { 828 minssf = conn->props.min_ssf - conn->external.ssf; 829 } 830 831 /* Order mechanisms so -PLUS are preferred */ 832 result = _sasl_client_order_mechs(c_conn->cparams->utils, 833 mechlist, 834 SASL_CB_PRESENT(c_conn->cparams), 835 &ordered_mechs, 836 &list_len, 837 &server_can_cb); 838 if (result != 0) 839 goto done; 840 841 /* 842 * Determine channel binding disposition based on whether we 843 * are doing mechanism negotiation and whether server supports 844 * channel bindings. 845 */ 846 result = _sasl_cbinding_disp(c_conn->cparams, 847 (list_len > 1), 848 server_can_cb, 849 &cbindingdisp); 850 if (result != 0) 851 goto done; 852 853 for (i = 0, name = ordered_mechs; i < list_len; i++) { 854 855 name_len = strlen(name); 856 857 /* for each mechanism in client's list */ 858 for (m = c_conn->mech_list; m != NULL; m = m->next) { 859 unsigned myflags; 860 int plus; 861 862 if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, name_len, &plus)) { 863 continue; 864 } 865 866 /* Do we have the prompts for it? */ 867 if (!have_prompts(conn, m->m.plug)) 868 break; 869 870 /* Is it strong enough? */ 871 if (minssf > m->m.plug->max_ssf) 872 break; 873 874 myflags = conn->props.security_flags; 875 876 /* if there's an external layer with a better SSF then this is no 877 * longer considered a plaintext mechanism 878 */ 879 if ((conn->props.min_ssf <= conn->external.ssf) && 880 (conn->external.ssf > 1)) { 881 myflags &= ~SASL_SEC_NOPLAINTEXT; 882 } 883 884 /* Does it meet our security properties? */ 885 if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) { 886 break; 887 } 888 889 /* Can we meet it's features? */ 890 if (cbindingdisp == SASL_CB_DISP_USED && 891 !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) { 892 break; 893 } 894 895 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN) 896 && !conn->serverFQDN) { 897 break; 898 } 899 900 /* Can it meet our features? */ 901 if ((conn->flags & SASL_NEED_PROXY) && 902 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { 903 break; 904 } 905 906 if ((conn->flags & SASL_NEED_HTTP) && 907 !(m->m.plug->features & SASL_FEAT_SUPPORTS_HTTP)) { 908 break; 909 } 910 911 /* compare security flags, only take new mechanism if it has 912 * all the security flags of the previous one. 913 * 914 * From the mechanisms we ship with, this yields the order: 915 * 916 * SRP 917 * GSSAPI + KERBEROS_V4 918 * DIGEST + OTP 919 * CRAM + EXTERNAL 920 * PLAIN + LOGIN + ANONYMOUS 921 * 922 * This might be improved on by comparing the numeric value of 923 * the bitwise-or'd security flags, which splits DIGEST/OTP, 924 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we 925 * are depending on the numeric values of the flags (which may 926 * change, and their ordering could be considered dumb luck. 927 */ 928 929 if (bestm && 930 ((m->m.plug->security_flags ^ bestm->m.plug->security_flags) & 931 bestm->m.plug->security_flags)) { 932 break; 933 } 934 935 if (SASL_CB_PRESENT(c_conn->cparams) && plus) { 936 cur_cbindingdisp = SASL_CB_DISP_USED; 937 } else { 938 cur_cbindingdisp = cbindingdisp; 939 } 940 941 if (bestm && (best_cbindingdisp > cur_cbindingdisp)) { 942 break; 943 } 944 945#ifdef PREFER_MECH 946 if (strcasecmp(m->m.plug->mech_name, PREFER_MECH) && 947 bestm && m->m.plug->max_ssf <= bestssf) { 948 /* this mechanism isn't our favorite, and it's no better 949 than what we already have! */ 950 break; 951 } 952#else 953 if (bestm && m->m.plug->max_ssf <= bestssf) { 954 /* this mechanism is no better than what we already have! */ 955 break; 956 } 957#endif 958 959 if (mech) { 960 *mech = m->m.plug->mech_name; 961 } 962 963 best_cbindingdisp = cur_cbindingdisp; 964 bestssf = m->m.plug->max_ssf; 965 bestm = m; 966 break; 967 } 968 name += strlen(name) + 1; 969 } 970 971 if (bestm == NULL) { 972 sasl_seterror(conn, 0, "No worthy mechs found"); 973 result = SASL_NOMECH; 974 goto done; 975 } 976 977 /* make (the rest of) cparams */ 978 c_conn->cparams->service = conn->service; 979 c_conn->cparams->servicelen = (unsigned) strlen(conn->service); 980 981 if (conn->serverFQDN) { 982 c_conn->cparams->serverFQDN = conn->serverFQDN; 983 c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN); 984 } 985 986 c_conn->cparams->clientFQDN = c_conn->clientFQDN; 987 c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN); 988 989 c_conn->cparams->external_ssf = conn->external.ssf; 990 c_conn->cparams->props = conn->props; 991 c_conn->cparams->cbindingdisp = best_cbindingdisp; 992 c_conn->mech = bestm; 993 994 /* init that plugin */ 995 result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context, 996 c_conn->cparams, 997 &(conn->context)); 998 if (result != SASL_OK) goto done; 999 1000 /* do a step -- but only if we can do a client-send-first */ 1001 dostep: 1002 if(clientout) { 1003 if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) { 1004 *clientout = NULL; 1005 *clientoutlen = 0; 1006 result = SASL_CONTINUE; 1007 } else { 1008 result = sasl_client_step(conn, NULL, 0, prompt_need, 1009 clientout, clientoutlen); 1010 } 1011 } 1012 else 1013 result = SASL_CONTINUE; 1014 1015 done: 1016 if (ordered_mechs != NULL) 1017 c_conn->cparams->utils->free(ordered_mechs); 1018 RETURN(conn, result); 1019} 1020 1021/* do a single authentication step. 1022 * serverin -- the server message received by the client, MUST have a NUL 1023 * sentinel, not counted by serverinlen 1024 * output: 1025 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue 1026 * clientout -- the client response to send to the server 1027 * 1028 * returns: 1029 * SASL_OK -- success 1030 * SASL_INTERACT -- user interaction needed to fill in prompt_need list 1031 * SASL_BADPROT -- server protocol incorrect/cancelled 1032 * SASL_BADSERV -- server failed mutual auth 1033 */ 1034 1035int sasl_client_step(sasl_conn_t *conn, 1036 const char *serverin, 1037 unsigned serverinlen, 1038 sasl_interact_t **prompt_need, 1039 const char **clientout, 1040 unsigned *clientoutlen) 1041{ 1042 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; 1043 int result; 1044 1045 if (_sasl_client_active == 0) return SASL_NOTINIT; 1046 if (!conn) return SASL_BADPARAM; 1047 1048 /* check parameters */ 1049 if ((serverin==NULL) && (serverinlen>0)) 1050 PARAMERROR(conn); 1051 1052 /* Don't do another step if the plugin told us that we're done */ 1053 if (conn->oparams.doneflag) { 1054 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag"); 1055 return SASL_FAIL; 1056 } 1057 1058 if(clientout) *clientout = NULL; 1059 if(clientoutlen) *clientoutlen = 0; 1060 1061 /* do a step */ 1062 result = c_conn->mech->m.plug->mech_step(conn->context, 1063 c_conn->cparams, 1064 serverin, 1065 serverinlen, 1066 prompt_need, 1067 clientout, clientoutlen, 1068 &conn->oparams); 1069 1070 if (result == SASL_OK) { 1071 /* So we're done on this end, but if both 1072 * 1. the mech does server-send-last 1073 * 2. the protocol does not 1074 * we need to return no data */ 1075 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { 1076 *clientout = ""; 1077 *clientoutlen = 0; 1078 } 1079 1080 if(!conn->oparams.maxoutbuf) { 1081 conn->oparams.maxoutbuf = conn->props.maxbufsize; 1082 } 1083 1084 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { 1085 sasl_seterror(conn, 0, 1086 "mech did not call canon_user for both authzid and authid"); 1087 result = SASL_BADPROT; 1088 } 1089 } 1090 1091 RETURN(conn,result); 1092} 1093 1094/* returns the length of all the mechanisms 1095 * added up 1096 */ 1097 1098static unsigned mech_names_len(cmechanism_t *mech_list) 1099{ 1100 cmechanism_t *listptr; 1101 unsigned result = 0; 1102 1103 for (listptr = mech_list; 1104 listptr; 1105 listptr = listptr->next) 1106 result += (unsigned) strlen(listptr->m.plug->mech_name); 1107 1108 return result; 1109} 1110 1111 1112int _sasl_client_listmech(sasl_conn_t *conn, 1113 const char *prefix, 1114 const char *sep, 1115 const char *suffix, 1116 const char **result, 1117 unsigned *plen, 1118 int *pcount) 1119{ 1120 sasl_client_conn_t *c_conn = (sasl_client_conn_t *)conn; 1121 cmechanism_t *m = NULL; 1122 sasl_ssf_t minssf = 0; 1123 int ret; 1124 size_t resultlen; 1125 int flag; 1126 const char *mysep; 1127 1128 if (_sasl_client_active == 0) return SASL_NOTINIT; 1129 if (!conn) return SASL_BADPARAM; 1130 if (conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); 1131 1132 if (! result) 1133 PARAMERROR(conn); 1134 1135 if (plen != NULL) 1136 *plen = 0; 1137 if (pcount != NULL) 1138 *pcount = 0; 1139 1140 if (sep) { 1141 mysep = sep; 1142 } else { 1143 mysep = " "; 1144 } 1145 1146 if (conn->props.min_ssf < conn->external.ssf) { 1147 minssf = 0; 1148 } else { 1149 minssf = conn->props.min_ssf - conn->external.ssf; 1150 } 1151 1152 if (!c_conn->mech_list || c_conn->mech_length <= 0) { 1153 INTERROR(conn, SASL_NOMECH); 1154 } 1155 1156 resultlen = (prefix ? strlen(prefix) : 0) 1157 + (strlen(mysep) * (c_conn->mech_length - 1)) 1158 + mech_names_len(c_conn->mech_list) 1159 + (suffix ? strlen(suffix) : 0) 1160 + 1; 1161 ret = _buf_alloc(&conn->mechlist_buf, 1162 &conn->mechlist_buf_len, 1163 resultlen); 1164 if (ret != SASL_OK) MEMERROR(conn); 1165 1166 if (prefix) { 1167 strcpy (conn->mechlist_buf,prefix); 1168 } else { 1169 *(conn->mechlist_buf) = '\0'; 1170 } 1171 1172 flag = 0; 1173 for (m = c_conn->mech_list; m != NULL; m = m->next) { 1174 /* do we have the prompts for it? */ 1175 if (!have_prompts(conn, m->m.plug)) { 1176 continue; 1177 } 1178 1179 /* is it strong enough? */ 1180 if (minssf > m->m.plug->max_ssf) { 1181 continue; 1182 } 1183 1184 /* does it meet our security properties? */ 1185 if (((conn->props.security_flags ^ m->m.plug->security_flags) 1186 & conn->props.security_flags) != 0) { 1187 continue; 1188 } 1189 1190 /* Can we meet it's features? */ 1191 if ((m->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->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { 1199 continue; 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->m.plug->mech_name); 1216 } 1217 1218 if (suffix) 1219 strcat(conn->mechlist_buf,suffix); 1220 1221 if (plen!=NULL) 1222 *plen = (unsigned) strlen(conn->mechlist_buf); 1223 1224 *result = conn->mechlist_buf; 1225 1226 return SASL_OK; 1227} 1228 1229sasl_string_list_t *_sasl_client_mechs(void) 1230{ 1231 cmechanism_t *listptr; 1232 sasl_string_list_t *retval = NULL, *next=NULL; 1233 1234 if(!_sasl_client_active) return NULL; 1235 1236 /* make list */ 1237 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) { 1238 next = sasl_ALLOC(sizeof(sasl_string_list_t)); 1239 1240 if(!next && !retval) return NULL; 1241 else if(!next) { 1242 next = retval->next; 1243 do { 1244 sasl_FREE(retval); 1245 retval = next; 1246 next = retval->next; 1247 } while(next); 1248 return NULL; 1249 } 1250 1251 next->d = listptr->m.plug->mech_name; 1252 1253 if(!retval) { 1254 next->next = NULL; 1255 retval = next; 1256 } else { 1257 next->next = retval; 1258 retval = next; 1259 } 1260 } 1261 1262 return retval; 1263} 1264 1265 1266 1267 1268/* It would be nice if we can show other information like Author, Company, Year, plugin version */ 1269static void 1270_sasl_print_mechanism ( 1271 client_sasl_mechanism_t *m, 1272 sasl_info_callback_stage_t stage, 1273 void *rock __attribute__((unused)) 1274) 1275{ 1276 char delimiter; 1277 1278 if (stage == SASL_INFO_LIST_START) { 1279 printf ("List of client plugins follows\n"); 1280 return; 1281 } else if (stage == SASL_INFO_LIST_END) { 1282 return; 1283 } 1284 1285 /* Process the mechanism */ 1286 printf ("Plugin \"%s\" ", m->plugname); 1287 1288 /* There is no delay loading for client side plugins */ 1289 printf ("[loaded]"); 1290 1291 printf (", \tAPI version: %d\n", m->version); 1292 1293 if (m->plug != NULL) { 1294 printf ("\tSASL mechanism: %s, best SSF: %d\n", 1295 m->plug->mech_name, 1296 m->plug->max_ssf); 1297 1298 printf ("\tsecurity flags:"); 1299 1300 delimiter = ' '; 1301 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) { 1302 printf ("%cNO_ANONYMOUS", delimiter); 1303 delimiter = '|'; 1304 } 1305 1306 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) { 1307 printf ("%cNO_PLAINTEXT", delimiter); 1308 delimiter = '|'; 1309 } 1310 1311 if (m->plug->security_flags & SASL_SEC_NOACTIVE) { 1312 printf ("%cNO_ACTIVE", delimiter); 1313 delimiter = '|'; 1314 } 1315 1316 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) { 1317 printf ("%cNO_DICTIONARY", delimiter); 1318 delimiter = '|'; 1319 } 1320 1321 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) { 1322 printf ("%cFORWARD_SECRECY", delimiter); 1323 delimiter = '|'; 1324 } 1325 1326 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) { 1327 printf ("%cPASS_CREDENTIALS", delimiter); 1328 delimiter = '|'; 1329 } 1330 1331 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) { 1332 printf ("%cMUTUAL_AUTH", delimiter); 1333 delimiter = '|'; 1334 } 1335 1336 1337 1338 printf ("\n\tfeatures:"); 1339 1340 delimiter = ' '; 1341 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { 1342 printf ("%cWANT_CLIENT_FIRST", delimiter); 1343 delimiter = '|'; 1344 } 1345 1346 if (m->plug->features & SASL_FEAT_SERVER_FIRST) { 1347 printf ("%cSERVER_FIRST", delimiter); 1348 delimiter = '|'; 1349 } 1350 1351 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) { 1352 printf ("%cPROXY_AUTHENTICATION", delimiter); 1353 delimiter = '|'; 1354 } 1355 1356 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) { 1357 printf ("%cNEED_SERVER_FQDN", delimiter); 1358 delimiter = '|'; 1359 } 1360 1361 if (m->plug->features & SASL_FEAT_GSS_FRAMING) { 1362 printf ("%cGSS_FRAMING", delimiter); 1363 delimiter = '|'; 1364 } 1365 1366 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) { 1367 printf ("%cCHANNEL_BINDING", delimiter); 1368 delimiter = '|'; 1369 } 1370 1371 if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) { 1372 printf ("%cSUPPORTS_HTTP", delimiter); 1373 delimiter = '|'; 1374 } 1375 } 1376 1377/* Delay loading is not supported for the client side plugins: 1378 if (m->f) { 1379 printf ("\n\twill be loaded from \"%s\"", m->f); 1380 } 1381 */ 1382 1383 printf ("\n"); 1384} 1385 1386 1387/* Dump information about available client plugins */ 1388int sasl_client_plugin_info ( 1389 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ 1390 sasl_client_info_callback_t *info_cb, 1391 void *info_cb_rock 1392) 1393{ 1394 cmechanism_t *m; 1395 client_sasl_mechanism_t plug_data; 1396 char * cur_mech; 1397 char * mech_list = NULL; 1398 char * p; 1399 1400 if (info_cb == NULL) { 1401 info_cb = _sasl_print_mechanism; 1402 } 1403 1404 if (cmechlist != NULL) { 1405 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); 1406 1407 if (c_mech_list == NULL) { 1408 m = cmechlist->mech_list; /* m point to beginning of the list */ 1409 1410 while (m != NULL) { 1411 memcpy (&plug_data, &m->m, sizeof(plug_data)); 1412 1413 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); 1414 1415 m = m->next; 1416 } 1417 } else { 1418 mech_list = strdup (c_mech_list); 1419 1420 cur_mech = mech_list; 1421 1422 while (cur_mech != NULL) { 1423 p = strchr (cur_mech, ' '); 1424 if (p != NULL) { 1425 *p = '\0'; 1426 p++; 1427 } 1428 1429 m = cmechlist->mech_list; /* m point to beginning of the list */ 1430 1431 while (m != NULL) { 1432 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) { 1433 memcpy (&plug_data, &m->m, sizeof(plug_data)); 1434 1435 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); 1436 } 1437 1438 m = m->next; 1439 } 1440 1441 cur_mech = p; 1442 } 1443 1444 free (mech_list); 1445 } 1446 1447 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); 1448 1449 return (SASL_OK); 1450 } 1451 1452 return (SASL_NOTINIT); 1453} 1454