1/* pluginviewer.c -- Plugin Viewer for CMU SASL 2 * Alexey Melnikov, Isode Ltd. 3 * 4 * $Id: pluginviewer.c,v 1.11 2011/09/01 14:12:18 mel Exp $ 5 */ 6/* 7 * Copyright (c) 2004 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#include <config.h> 46#include <limits.h> 47#include <stdio.h> 48#include <string.h> 49#include <stdlib.h> 50#ifdef WIN32 51# include <winsock.h> 52__declspec(dllimport) char *optarg; 53__declspec(dllimport) int optind; 54__declspec(dllimport) int getsubopt(char **optionp, const char * const *tokens, char **valuep); 55#else /* WIN32 */ 56# include <netinet/in.h> 57#endif /* WIN32 */ 58#include <sasl.h> 59#include <saslutil.h> 60#include <saslplug.h> 61 62#ifdef macintosh 63#include <sioux.h> 64#include <parse_cmd_line.h> 65#define MAX_ARGC (100) 66int xxx_main(int argc, char *argv[]); 67int main(void) 68{ 69 char *argv[MAX_ARGC]; 70 int argc; 71 char line[400]; 72 SIOUXSettings.asktosaveonclose = 0; 73 SIOUXSettings.showstatusline = 1; 74 argc=parse_cmd_line(MAX_ARGC,argv,sizeof(line),line); 75 return xxx_main(argc,argv); 76} 77#define main xxx_main 78#endif 79 80#ifdef HAVE_GETOPT_H 81#include <getopt.h> 82#endif 83#ifdef HAVE_UNISTD_H 84#include <unistd.h> 85#endif 86 87#ifndef HAVE_GETSUBOPT 88int getsubopt(char **optionp, const char * const *tokens, char **valuep); 89#endif 90 91static const char 92build_ident[] = "$Build: pluginviewer " PACKAGE "-" VERSION " $"; 93 94static const char *progname = NULL; 95/* SASL authentication methods (client or server side). NULL means all. */ 96static char *sasl_mech = NULL; 97/* auxprop methods. NULL means all. */ 98static char *auxprop_mech = NULL; 99 100#define N_CALLBACKS (16) 101 102#define NOT_NULL (void *) -1 103 104#define SAMPLE_SEC_BUF_SIZE (2048) 105 106static const char *bit_subopts[] = { 107#define OPT_MIN (0) 108 "min", 109#define OPT_MAX (1) 110 "max", 111 NULL 112}; 113 114static const char *ext_subopts[] = { 115#define OPT_EXT_SSF (0) 116 "ssf", 117#define OPT_EXT_ID (1) 118 "id", 119 NULL 120}; 121 122static const char *flag_subopts[] = { 123#define OPT_NOPLAIN (0) 124 "noplain", 125#define OPT_NOACTIVE (1) 126 "noactive", 127#define OPT_NODICT (2) 128 "nodict", 129#define OPT_FORWARDSEC (3) 130 "forwardsec", 131#define OPT_NOANONYMOUS (4) 132 "noanonymous", 133#define OPT_PASSCRED (5) 134 "passcred", 135 NULL 136}; 137 138/* Whitespace separated list of mechanisms to allow (e.g. 'plain otp'). 139 Used to restrict the mechanisms to a subset of the installed plugins. 140 Default: NULL (i.e. all available) */ 141#define SASL_OPT_MECH_LIST "mech_list" 142/* Name of canon_user plugin to use, default is "INTERNAL" */ 143#define SASL_OPT_CANON_USER_PLUGIN "canon_user_plugin" 144/* Name of auxiliary plugin to use, you may specify a space-separated list 145 of plugin names, and the plugins will be queried in order. Default is NULL (i.e. query all) */ 146#define SASL_OPT_AUXPROP_PLUGIN "auxprop_plugin" 147 148static sasl_conn_t *server_conn = NULL; 149static sasl_conn_t *client_conn = NULL; 150 151static void 152free_conn(void) 153{ 154 if (server_conn) { 155 sasl_dispose(&server_conn); 156 } 157 if (client_conn) { 158 sasl_dispose(&client_conn); 159 } 160} 161 162static int 163sasl_my_log(void *context __attribute__((unused)), 164 int priority, 165 const char *message) 166{ 167 const char *label; 168 169 if (! message) { 170 return SASL_BADPARAM; 171 } 172 173 switch (priority) { 174 case SASL_LOG_ERR: 175 label = "Error"; 176 break; 177 case SASL_LOG_NOTE: 178 label = "Info"; 179 break; 180 default: 181 label = "Other"; 182 break; 183 } 184 185 fprintf(stderr, "%s: SASL %s: %s\n", 186 progname, label, message); 187 188 return SASL_OK; 189} 190 191static int 192getpath(void *context, 193 const char ** path) 194{ 195 const char *searchpath = (const char *) context; 196 197 if (! path) { 198 return SASL_BADPARAM; 199 } 200 201 if (searchpath) { 202 *path = searchpath; 203 } else { 204 *path = PLUGINDIR; 205 } 206 207 return SASL_OK; 208} 209 210static int 211plugview_sasl_getopt ( 212 void *context, 213 const char *plugin_name, 214 const char *option, 215 const char **result, 216 unsigned *len 217) 218{ 219 if (strcasecmp (option, SASL_OPT_MECH_LIST) == 0) { 220 /* Whitespace separated list of mechanisms to allow (e.g. 'plain otp'). 221 Used to restrict the mechanisms to a subset of the installed plugins. 222 Default: NULL (i.e. all available) */ 223 if (result != NULL) { 224 *result = sasl_mech; 225 } 226 227 if (len != NULL) { 228 /* This might be NULL, which means "all mechanisms" */ 229 *len = sasl_mech ? strlen(sasl_mech) : 0; 230 } 231 return (SASL_OK); 232 } 233 else { 234 /* Unrecognized */ 235 return (SASL_FAIL); 236 } 237} 238 239static void 240sasldebug(int why, const char *what, const char *errstr) 241{ 242 fprintf(stderr, "%s: %s: %s", 243 progname, 244 what, 245 sasl_errstring(why, NULL, NULL)); 246 if (errstr) { 247 fprintf(stderr, " (%s)\n", errstr); 248 } else { 249 putc('\n', stderr); 250 } 251} 252 253static void 254saslfail(int why, const char *what, const char *errstr) 255{ 256 sasldebug(why, what, errstr); 257 free_conn(); 258 /* Call sasl_done twice - one for the client side SASL and 259 one for the server side. */ 260 sasl_done(); 261 sasl_done(); 262 exit(EXIT_FAILURE); 263} 264 265static void 266fail(const char *what) 267{ 268 fprintf(stderr, "%s: %s\n", 269 progname, what); 270 exit(EXIT_FAILURE); 271} 272 273/* Produce a space separated list of installed mechanisms */ 274static void 275list_installed_server_mechanisms ( 276 server_sasl_mechanism_t *m, 277 sasl_info_callback_stage_t stage, 278 void *rock 279) 280{ 281 char ** list_of_mechs = (char **) rock; 282 char * new_list; 283 284 if (stage == SASL_INFO_LIST_START || stage == SASL_INFO_LIST_END) { 285 return; 286 } 287 288 if (m->plug != NULL) { 289 if (*list_of_mechs == NULL) { 290 *list_of_mechs = strdup(m->plug->mech_name); 291 } else { 292 /* This is suboptimal, but works */ 293 new_list = malloc (strlen(*list_of_mechs) + strlen(m->plug->mech_name) + 2); 294 sprintf (new_list, "%s %s", *list_of_mechs, m->plug->mech_name); 295 free (*list_of_mechs); 296 *list_of_mechs = new_list; 297 } 298 } 299} 300 301/* Produce a space separated list of installed mechanisms */ 302static void 303list_installed_client_mechanisms ( 304 client_sasl_mechanism_t *m, 305 sasl_info_callback_stage_t stage, 306 void *rock 307) 308{ 309 char ** list_of_mechs = (char **) rock; 310 char * new_list; 311 312 if (stage == SASL_INFO_LIST_START || stage == SASL_INFO_LIST_END) { 313 return; 314 } 315 316 if (m->plug != NULL) { 317 if (*list_of_mechs == NULL) { 318 *list_of_mechs = strdup(m->plug->mech_name); 319 } else { 320 /* This is suboptimal, but works */ 321 new_list = malloc (strlen(*list_of_mechs) + strlen(m->plug->mech_name) + 2); 322 sprintf (new_list, "%s %s", *list_of_mechs, m->plug->mech_name); 323 free (*list_of_mechs); 324 *list_of_mechs = new_list; 325 } 326 } 327} 328 329/* Produce a space separated list of installed mechanisms */ 330static void 331list_installed_auxprop_mechanisms ( 332 sasl_auxprop_plug_t *m, 333 sasl_info_callback_stage_t stage, 334 void *rock 335) 336{ 337 char ** list_of_mechs = (char **) rock; 338 char * new_list; 339 340 if (stage == SASL_INFO_LIST_START || stage == SASL_INFO_LIST_END) { 341 return; 342 } 343 344 if (*list_of_mechs == NULL) { 345 *list_of_mechs = strdup(m->name); 346 } else { 347 /* This is suboptimal, but works */ 348 new_list = malloc (strlen(*list_of_mechs) + strlen(m->name) + 2); 349 sprintf (new_list, "%s %s", *list_of_mechs, m->name); 350 free (*list_of_mechs); 351 *list_of_mechs = new_list; 352 } 353} 354 355int 356main(int argc, char *argv[]) 357{ 358 int c = 0; 359 int errflag = 0; 360 int result; 361 sasl_security_properties_t secprops; 362 sasl_ssf_t extssf = 0; 363 const char *ext_authid = NULL; 364 char *options, *value; 365 const char *available_mechs = NULL; 366 unsigned len; 367 int count; 368 sasl_callback_t callbacks[N_CALLBACKS], *callback; 369 char *searchpath = NULL; 370 char *service = "test"; 371 char * list_of_server_mechs = NULL; 372 char * list_of_client_mechs = NULL; 373 char * list_of_auxprop_mechs = NULL; 374 int list_all_plugins = 1; /* By default we list all plugins */ 375 int list_client_auth_plugins = 0; 376 int list_server_auth_plugins = 0; 377 int list_auxprop_plugins = 0; 378 379#ifdef WIN32 380 /* initialize winsock */ 381 WSADATA wsaData; 382 383 result = WSAStartup( MAKEWORD(2, 0), &wsaData ); 384 if ( result != 0) { 385 saslfail(SASL_FAIL, "Initializing WinSockets", NULL); 386 } 387#endif 388 389 progname = strrchr(argv[0], HIER_DELIMITER); 390 if (progname) { 391 progname++; 392 } else { 393 progname = argv[0]; 394 } 395 396 /* Init defaults... */ 397 memset(&secprops, 0L, sizeof(secprops)); 398 secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE; 399 secprops.max_ssf = UINT_MAX; 400 401 while ((c = getopt(argc, argv, "acshb:e:m:f:p:x:?")) != EOF) 402 switch (c) { 403 case 'a': 404 list_auxprop_plugins = 1; 405 list_all_plugins = 0; 406 break; 407 408 case 'x': 409 auxprop_mech = optarg; 410 break; 411 412 case 'c': 413 list_client_auth_plugins = 1; 414 list_all_plugins = 0; 415 break; 416 417 case 's': 418 list_server_auth_plugins = 1; 419 list_all_plugins = 0; 420 break; 421 422 case 'b': 423 options = optarg; 424 while (*options != '\0') { 425 switch(getsubopt(&options, (const char * const *)bit_subopts, &value)) { 426 case OPT_MIN: 427 if (! value) { 428 errflag = 1; 429 } else { 430 secprops.min_ssf = atoi(value); 431 } 432 break; 433 case OPT_MAX: 434 if (! value) { 435 errflag = 1; 436 } else { 437 secprops.max_ssf = atoi(value); 438 } 439 break; 440 default: 441 errflag = 1; 442 break; 443 } 444 } 445 break; 446 447 case 'e': 448 options = optarg; 449 while (*options != '\0') { 450 switch(getsubopt(&options, (const char * const *)ext_subopts, &value)) { 451 case OPT_EXT_SSF: 452 if (! value) { 453 errflag = 1; 454 } else { 455 extssf = atoi(value); 456 } 457 break; 458 case OPT_MAX: 459 if (! value) { 460 errflag = 1; 461 } else { 462 ext_authid = value; 463 } 464 break; 465 default: 466 errflag = 1; 467 break; 468 } 469 } 470 break; 471 472 case 'm': 473 sasl_mech = optarg; 474 break; 475 476 case 'f': 477 options = optarg; 478 while (*options != '\0') { 479 switch(getsubopt(&options, (const char * const *)flag_subopts, &value)) { 480 case OPT_NOPLAIN: 481 secprops.security_flags |= SASL_SEC_NOPLAINTEXT; 482 break; 483 case OPT_NOACTIVE: 484 secprops.security_flags |= SASL_SEC_NOACTIVE; 485 break; 486 case OPT_NODICT: 487 secprops.security_flags |= SASL_SEC_NODICTIONARY; 488 break; 489 case OPT_FORWARDSEC: 490 secprops.security_flags |= SASL_SEC_FORWARD_SECRECY; 491 break; 492 case OPT_NOANONYMOUS: 493 secprops.security_flags |= SASL_SEC_NOANONYMOUS; 494 break; 495 case OPT_PASSCRED: 496 secprops.security_flags |= SASL_SEC_PASS_CREDENTIALS; 497 break; 498 default: 499 errflag = 1; 500 break; 501 } 502 if (value) errflag = 1; 503 } 504 break; 505 506 case 'p': 507 searchpath = optarg; 508 break; 509 510 default: /* unknown flag */ 511 errflag = 1; 512 break; 513 } 514 515 if (optind != argc) { 516 /* We don't *have* extra arguments */ 517 errflag = 1; 518 } 519 520 if (errflag) { 521 fprintf(stderr, "%s: Usage: %s [-a] [-s] [-c] [-b min=N,max=N] [-e ssf=N,id=ID] [-m MECHS] [-x AUXPROP_MECH] [-f FLAGS] [-i local=IP,remote=IP] [-p PATH]\n" 522 "\t-a\tlist auxprop plugins\n" 523 "\t-s\tlist server authentication (SASL) plugins\n" 524 "\t-c\tlist client authentication (SASL) plugins\n" 525 "\t-b ...\t#bits to use for encryption\n" 526 "\t\tmin=N\tminumum #bits to use (1 => integrity)\n" 527 "\t\tmax=N\tmaximum #bits to use\n" 528 "\t-e ...\tassume external encryption\n" 529 "\t\tssf=N\texternal mech provides N bits of encryption\n" 530 "\t\tid=ID\texternal mech provides authentication id ID\n" 531 "\t-m MECHS\tforce to use one of MECHS SASL mechanism\n" 532 "\t-x AUXPROP_MECHS\tforce to use one of AUXPROP_MECHS auxprop plugins\n" 533 "\t-f ...\tset security flags\n" 534 "\t\tnoplain\t\tno plaintext password send during authentication\n" 535 "\t\tnoactive\trequire security vs. active attacks\n" 536 "\t\tnodict\t\trequire security vs. passive dictionary attacks\n" 537 "\t\tforwardsec\trequire forward secrecy\n" 538 "\t\tmaximum\t\trequire all security flags\n" 539 "\t\tpasscred\tattempt to pass client credentials\n" 540#ifdef WIN32 541 "\t-p PATH\tsemicolon-separated search path for mechanisms\n", 542#else 543 "\t-p PATH\tcolon-separated search path for mechanisms\n", 544#endif 545 progname, progname); 546 exit(EXIT_FAILURE); 547 } 548 549 /* Fill in the callbacks that we're providing... */ 550 callback = callbacks; 551 552 /* log */ 553 callback->id = SASL_CB_LOG; 554 callback->proc = (sasl_callback_ft)&sasl_my_log; 555 callback->context = NULL; 556 ++callback; 557 558 /* getpath */ 559 if (searchpath) { 560 callback->id = SASL_CB_GETPATH; 561 callback->proc = (sasl_callback_ft)&getpath; 562 callback->context = searchpath; 563 ++callback; 564 } 565 566 /* getopt */ 567 /* NOTE: this will return "sasl_mech" option, however this HAS NO EFFECT 568 on client side SASL plugins, which just never query this option */ 569 callback->id = SASL_CB_GETOPT; 570 callback->proc = (sasl_callback_ft)&plugview_sasl_getopt; 571 callback->context = NULL; 572 ++callback; 573 574 /* The following callbacks are for a client connection only. 575 We reuse the same callbacks variable and the server side doesn't like 576 proc == NULL. So we just put something there, != NULL! */ 577 callback->id = SASL_CB_AUTHNAME; 578 callback->proc = NOT_NULL; 579 callback->context = NULL; 580 ++callback; 581 582 583 callback->id = SASL_CB_PASS; 584 callback->proc = NOT_NULL; 585 callback->context = NULL; 586 ++callback; 587 588 /* termination */ 589 callback->id = SASL_CB_LIST_END; 590 callback->proc = NULL; 591 callback->context = NULL; 592 ++callback; 593 594 /* FIXME: In general case this is not going to work of course, 595 as some plugins will need more callbacks then others. */ 596 if (N_CALLBACKS < callback - callbacks) { 597 fail("Out of callback space; recompile with larger N_CALLBACKS"); 598 } 599 600 result = sasl_client_init(callbacks); 601 if (result != SASL_OK) { 602 saslfail(result, "Initializing client side of libsasl", NULL); 603 } 604 605 result = sasl_server_init(callbacks, "pluginviewer"); 606 if (result != SASL_OK) { 607 saslfail(result, "Initializing server side of libsasl", NULL); 608 } 609 610 if (list_all_plugins || list_auxprop_plugins) { 611 list_of_auxprop_mechs = NULL; 612 613 auxprop_plugin_info (NULL, /* list all auxprop mechanisms */ 614 &list_installed_auxprop_mechanisms, 615 (void *) &list_of_auxprop_mechs); 616 617 printf ("Installed and properly configured auxprop mechanisms are:\n%s\n", 618 (list_of_auxprop_mechs == NULL) ? "<none>" : list_of_auxprop_mechs); 619 620 free (list_of_auxprop_mechs); 621 622 623 auxprop_plugin_info (auxprop_mech, NULL, NULL); 624 } 625 626 /* TODO: add listing of canonicalization plugins, if needed. */ 627 628 if (list_all_plugins || list_server_auth_plugins) { 629 /* SASL server plugins */ 630 /* List all loaded plugins first */ 631 list_of_server_mechs = NULL; 632 633 sasl_server_plugin_info (NULL, /* list all SASL mechanisms */ 634 &list_installed_server_mechanisms, 635 (void *) &list_of_server_mechs); 636 637 printf ("Installed and properly configured SASL (server side) mechanisms are:\n %s\n", list_of_server_mechs); 638 639 free (list_of_server_mechs); 640 641 /* Now list plugins matching the criteria */ 642 result = sasl_server_new(service, 643 /* Has to be any non NULL value */ 644 "test.example.com", /* localdomain */ 645 NULL, /* userdomain */ 646 NULL, /* iplocal */ 647 NULL, /* ipremote */ 648 NULL, 649 0, 650 &server_conn); 651 if (result != SASL_OK) { 652 saslfail(result, "Allocating sasl connection state (server side)", NULL); 653 } 654 655 /* The following two options are required for SASL EXTERNAL */ 656 if (extssf) { 657 result = sasl_setprop(server_conn, 658 SASL_SSF_EXTERNAL, 659 &extssf); 660 661 if (result != SASL_OK) { 662 saslfail(result, "Setting external SSF", NULL); 663 } 664 } 665 666 if (ext_authid) { 667 result = sasl_setprop(server_conn, 668 SASL_AUTH_EXTERNAL, 669 &ext_authid); 670 671 if (result != SASL_OK) { 672 saslfail(result, "Setting external authid", NULL); 673 } 674 } 675 676 result = sasl_setprop(server_conn, 677 SASL_SEC_PROPS, 678 &secprops); 679 680 if (result != SASL_OK) { 681 saslfail(result, "Setting security properties", NULL); 682 } 683 684 /* NOTE - available_mechs must not be freed */ 685 result = sasl_listmech(server_conn, 686 ext_authid, 687 NULL, 688 " ", 689 NULL, 690 &available_mechs, 691 &len, 692 &count); 693 if (result != SASL_OK) { 694 saslfail(result, "Listing SASL mechanisms", NULL); 695 } 696 697 /* NOTE: available_mechs contains subset of sasl_mech */ 698 699 if (count > 0) { 700 printf ("Available SASL (server side) mechanisms matching your criteria are:\n %s\n", available_mechs); 701 702 /* Dump information about the requested SASL mechanism */ 703 sasl_server_plugin_info (available_mechs, NULL, NULL); 704 } else { 705 printf ("No server side SASL mechanisms matching your criteria found\n"); 706 } 707 } 708 709 if (list_all_plugins || list_client_auth_plugins) { 710 /* SASL client plugins */ 711 /* List all loaded plugins first */ 712 list_of_client_mechs = NULL; 713 714 sasl_client_plugin_info (NULL, /* list all SASL mechanisms */ 715 &list_installed_client_mechanisms, 716 (void *) &list_of_client_mechs); 717 718 printf ("Installed and properly configured SASL (client side) mechanisms are:\n %s\n", 719 (list_of_client_mechs != NULL) ? list_of_client_mechs : "<none>"); 720 721 free (list_of_client_mechs); 722 723 /* Now list plugins matching the criteria */ 724 result = sasl_client_new(service, 725 /* Has to be any non NULL value */ 726 "test.example.com", /* fqdn */ 727 NULL, /* iplocal */ 728 NULL, /* ipremote */ 729 NULL, 730 0, 731 &client_conn); 732 733 if (result != SASL_OK) { 734 saslfail(result, "Allocating sasl connection state (client side)", NULL); 735 } 736 737 /* The following two options are required for SSF */ 738 if (extssf) { 739 result = sasl_setprop(client_conn, 740 SASL_SSF_EXTERNAL, 741 &extssf); 742 743 if (result != SASL_OK) { 744 saslfail(result, "Setting external SSF", NULL); 745 } 746 } 747 748 if (ext_authid) { 749 result = sasl_setprop(client_conn, 750 SASL_AUTH_EXTERNAL, 751 &ext_authid); 752 753 if (result != SASL_OK) { 754 saslfail(result, "Setting external authid", NULL); 755 } 756 } 757 758 result = sasl_setprop(client_conn, 759 SASL_SEC_PROPS, 760 &secprops); 761 762 if (result != SASL_OK) { 763 saslfail(result, "Setting security properties", NULL); 764 } 765 766 /* NOTE - available_mechs must not be freed */ 767 result = sasl_listmech(client_conn, 768 ext_authid, 769 NULL, 770 " ", 771 NULL, 772 &available_mechs, 773 &len, 774 &count); 775 if (result != SASL_OK) { 776 saslfail(result, "Listing SASL mechanisms", NULL); 777 } 778 779 if (count > 0) { 780 printf ("Available SASL (client side) mechanisms matching your criteria are:\n %s\n", available_mechs); 781 782 /* Dump information about the requested SASL mechanism */ 783 sasl_client_plugin_info (sasl_mech, NULL, NULL); 784 } else { 785 printf ("No client side SASL mechanisms matching your criteria found\n"); 786 } 787 } 788 789 free_conn(); 790 /* Call sasl_done twice - one for the client side SASL and 791 one for the server side. */ 792 sasl_done(); 793 sasl_done(); 794 795#ifdef WIN32 796 WSACleanup(); 797#endif 798 799 return (EXIT_SUCCESS); 800} 801