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