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