1 /* SASL client API implementation
4 * $Id: client.c,v 1.67 2006/04/26 15:33:41 mel Exp $
7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
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
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
28 * Pittsburgh, PA 15213-3890
29 * (412) 268-4387, fax: (412) 268-7395
30 * tech-transfer@andrew.cmu.edu
32 * 4. Redistributions of any form whatsoever must retain the following
34 * "This product includes software developed by Computing Services
35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
62 static cmech_list_t *cmechlist; /* global var which holds the list */
63 sasl_global_callbacks_t global_callbacks_client;
64 static int _sasl_client_active = 0;
66 static int init_mechlist()
68 cmechlist->mutex = sasl_MUTEX_ALLOC();
69 if(!cmechlist->mutex) return SASL_FAIL;
71 cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks_client);
72 if (cmechlist->utils==NULL)
75 cmechlist->mech_list=NULL;
76 cmechlist->mech_length=0;
81 static int client_done(void) {
85 if(!_sasl_client_active)
88 _sasl_client_active--;
90 if(_sasl_client_active) {
91 /* Don't de-init yet! Our refcount is nonzero. */
95 cm=cmechlist->mech_list; /* m point to begging of the list */
101 if (cprevm->m.plug->mech_free) {
102 cprevm->m.plug->mech_free(cprevm->m.plug->glob_context,
106 sasl_FREE(cprevm->m.plugname);
109 sasl_MUTEX_FREE(cmechlist->mutex);
110 _sasl_free_utils(&cmechlist->utils);
111 sasl_FREE(cmechlist);
118 int sasl_client_add_plugin(const char *plugname,
119 sasl_client_plug_init_t *entry_point)
122 sasl_client_plug_t *pluglist;
128 if(!plugname || !entry_point) return SASL_BADPARAM;
130 result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version,
131 &pluglist, &plugcount);
133 if (result != SASL_OK)
135 _sasl_log(NULL, SASL_LOG_WARN,
136 "entry_point failed in sasl_client_add_plugin for %s",
141 if (version != SASL_CLIENT_PLUG_VERSION)
143 _sasl_log(NULL, SASL_LOG_WARN,
144 "version conflict in sasl_client_add_plugin for %s", plugname);
148 for (lupe=0;lupe< plugcount ;lupe++)
150 mech = sasl_ALLOC(sizeof(cmechanism_t));
151 if (! mech) return SASL_NOMEM;
153 mech->m.plug=pluglist++;
154 if(_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) {
158 mech->m.version = version;
159 mech->next = cmechlist->mech_list;
160 cmechlist->mech_list = mech;
161 cmechlist->mech_length++;
168 client_idle(sasl_conn_t *conn)
174 for (m = cmechlist->mech_list;
178 && m->m.plug->idle(m->m.plug->glob_context,
180 conn ? ((sasl_client_conn_t *)conn)->cparams : NULL))
185 /* initialize the SASL client drivers
186 * callbacks -- base callbacks for all client connections
189 * SASL_NOMEM -- Not enough memory
190 * SASL_BADVERS -- Mechanism version mismatch
191 * SASL_BADPARAM -- error in config file
192 * SASL_NOMECH -- No mechanisms available
196 int sasl_client_init(const sasl_callback_t *callbacks)
199 const add_plugin_list_t ep_list[] = {
200 { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin },
201 { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
205 if(_sasl_client_active) {
206 /* We're already active, just increase our refcount */
207 /* xxx do something with the callback structure? */
208 _sasl_client_active++;
212 global_callbacks_client.callbacks = callbacks;
213 global_callbacks_client.appname = NULL;
215 cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
216 if (cmechlist==NULL) return SASL_NOMEM;
218 /* We need to call client_done if we fail now */
219 _sasl_client_active = 1;
228 sasl_client_add_plugin("EXTERNAL", &external_client_plug_init);
230 ret = _sasl_common_init(&global_callbacks_client);
233 ret = _sasl_load_plugins(ep_list,
234 _sasl_find_getpath_callback(callbacks),
235 _sasl_find_verifyfile_callback(callbacks));
237 if (ret == SASL_OK) {
238 _sasl_client_cleanup_hook = &client_done;
239 _sasl_client_idle_hook = &client_idle;
241 ret = _sasl_build_mechlist();
249 static void client_dispose(sasl_conn_t *pconn)
251 sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn;
253 if (c_conn->mech && c_conn->mech->m.plug->mech_dispose) {
254 c_conn->mech->m.plug->mech_dispose(pconn->context,
255 c_conn->cparams->utils);
258 pconn->context = NULL;
260 if (c_conn->clientFQDN)
261 sasl_FREE(c_conn->clientFQDN);
263 if (c_conn->cparams) {
264 _sasl_free_utils(&(c_conn->cparams->utils));
265 sasl_FREE(c_conn->cparams);
268 _sasl_conn_dispose(pconn);
271 /* initialize a client exchange based on the specified mechanism
272 * service -- registered name of the service using SASL (e.g. "imap")
273 * serverFQDN -- the fully qualified domain name of the server
274 * iplocalport -- client IPv4/IPv6 domain literal string with port
275 * (if NULL, then mechanisms requiring IPaddr are disabled)
276 * ipremoteport -- server IPv4/IPv6 domain literal string with port
277 * (if NULL, then mechanisms requiring IPaddr are disabled)
278 * prompt_supp -- list of client interactions supported
279 * may also include sasl_getopt_t context & call
280 * NULL prompt_supp = user/pass via SASL_INTERACT only
281 * NULL proc = interaction supported via SASL_INTERACT
282 * secflags -- security flags (see above)
284 * pconn -- connection negotiation structure
285 * pointer to NULL => allocate new
286 * non-NULL => recycle storage and go for next available mech
290 * SASL_NOMECH -- no mechanism meets requested properties
291 * SASL_NOMEM -- not enough memory
293 int sasl_client_new(const char *service,
294 const char *serverFQDN,
295 const char *iplocalport,
296 const char *ipremoteport,
297 const sasl_callback_t *prompt_supp,
302 char name[MAXHOSTNAMELEN];
303 sasl_client_conn_t *conn;
306 if(_sasl_client_active==0) return SASL_NOTINIT;
308 /* Remember, serverFQDN, iplocalport and ipremoteport can be NULL and be valid! */
309 if (!pconn || !service)
310 return SASL_BADPARAM;
312 *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t));
314 _sasl_log(NULL, SASL_LOG_ERR,
315 "Out of memory allocating connection context");
318 memset(*pconn, 0, sizeof(sasl_client_conn_t));
320 (*pconn)->destroy_conn = &client_dispose;
322 conn = (sasl_client_conn_t *)*pconn;
326 conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t));
327 if (conn->cparams==NULL)
329 memset(conn->cparams,0,sizeof(sasl_client_params_t));
331 result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT,
332 &client_idle, serverFQDN,
333 iplocalport, ipremoteport,
334 prompt_supp, &global_callbacks_client);
335 if (result != SASL_OK) RETURN(*pconn, result);
337 utils=_sasl_alloc_utils(*pconn, &global_callbacks_client);
343 /* Setup the non-lazy parts of cparams, the rest is done in
344 * sasl_client_start */
345 conn->cparams->utils = utils;
346 conn->cparams->canon_user = &_sasl_canon_user;
347 conn->cparams->flags = flags;
348 conn->cparams->prompt_supp = (*pconn)->callbacks;
350 /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
351 memset(name, 0, sizeof(name));
352 gethostname(name, MAXHOSTNAMELEN);
354 result = _sasl_strdup(name, &conn->clientFQDN, NULL);
356 if(result == SASL_OK) return SASL_OK;
358 /* result isn't SASL_OK */
359 _sasl_conn_dispose(*pconn);
362 _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new");
366 static int have_prompts(sasl_conn_t *conn,
367 const sasl_client_plug_t *mech)
369 static const unsigned long default_prompts[] = {
375 const unsigned long *prompt;
380 for (prompt = (mech->required_prompts
381 ? mech->required_prompts :
383 *prompt != SASL_CB_LIST_END;
385 result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext);
386 if (result != SASL_OK && result != SASL_INTERACT)
387 return 0; /* we don't have this required prompt */
390 return 1; /* we have all the prompts */
394 _mech_plus_p(const char *mech, size_t len)
396 return (len > 5 && strncasecmp(&mech[len - 5], "-PLUS", 5) == 0);
400 * Order PLUS mechanisms first. Returns NUL separated list of
404 _sasl_client_order_mechs(const sasl_utils_t *utils,
407 char **ordered_mechs,
412 size_t i, mechslen, start;
417 if (mechs == NULL || mechs[0] == '\0')
420 mechslen = strlen(mechs);
422 listp = list = utils->malloc(mechslen + 1);
426 /* xxx confirm this with rfc 2222
427 * SASL mechanism allowable characters are "AZaz-_"
428 * seperators can be any other characters and of any length
429 * even variable lengths between
431 * Apps should be encouraged to simply use space or comma space
434 #define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-')
436 for (i = start = 0; i <= mechslen; i++) {
437 if (!ismechchar(mechs[i])) {
438 const char *mechp = &mechs[start];
439 size_t len = i - start;
442 _mech_plus_p(mechp, len) == has_cb_data) {
443 memcpy(listp, mechp, len);
447 if (*server_can_cb == 0 && has_cb_data)
464 *ordered_mechs = list;
470 _sasl_cbinding_disp(sasl_client_params_t *cparams,
473 sasl_cbinding_disp_t *cbindingdisp)
476 * If negotiating mechanisms, then we fail immediately if the
477 * client requires channel binding and the server does not
478 * advertise support. Otherwise we send "y" (which later will
479 * become "p" if we select a supporting mechanism).
481 * If the client explicitly selected a mechanism, then we only
482 * send channel bindings if they're marked critical.
485 *cbindingdisp = SASL_CB_DISP_NONE;
487 if (SASL_CB_PRESENT(cparams)) {
489 if (!server_can_cb && SASL_CB_CRITICAL(cparams))
492 *cbindingdisp = SASL_CB_DISP_WANT;
493 } else if (SASL_CB_CRITICAL(cparams)) {
494 *cbindingdisp = SASL_CB_DISP_USED;
501 /* select a mechanism for a connection
502 * mechlist -- mechanisms server has available (punctuation ignored)
503 * secret -- optional secret from previous session
505 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
506 * clientout -- the initial client response to send to the server
507 * mech -- set to mechanism name
511 * SASL_NOMEM -- not enough memory
512 * SASL_NOMECH -- no mechanism meets requested properties
513 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
516 int sasl_client_start(sasl_conn_t *conn,
517 const char *mechlist,
518 sasl_interact_t **prompt_need,
519 const char **clientout,
520 unsigned *clientoutlen,
523 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
524 char *ordered_mechs = NULL, *name;
525 cmechanism_t *m=NULL,*bestm=NULL;
527 sasl_ssf_t bestssf = 0, minssf = 0;
528 int result, server_can_cb = 0;
529 sasl_cbinding_disp_t cbindingdisp;
531 if(_sasl_client_active==0) return SASL_NOTINIT;
533 if (!conn) return SASL_BADPARAM;
535 /* verify parameters */
536 if (mechlist == NULL)
539 /* if prompt_need != NULL we've already been here
540 and just need to do the continue step again */
543 /* FIXME: Hopefully they only give us our own prompt_need back */
544 if (prompt_need && *prompt_need != NULL) {
548 if(conn->props.min_ssf < conn->external.ssf) {
551 minssf = conn->props.min_ssf - conn->external.ssf;
554 /* Order mechanisms so -PLUS are preferred */
555 result = _sasl_client_order_mechs(c_conn->cparams->utils,
557 SASL_CB_PRESENT(c_conn->cparams),
565 * Determine channel binding disposition based on whether we
566 * are doing mechanism negotiation and whether server supports
569 result = _sasl_cbinding_disp(c_conn->cparams, (list_len > 1),
570 server_can_cb, &cbindingdisp);
574 for (i = 0, name = ordered_mechs; i < list_len; i++) {
575 /* foreach in client list */
576 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
579 if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, &plus))
582 /* Do we have the prompts for it? */
583 if (!have_prompts(conn, m->m.plug))
586 /* Is it strong enough? */
587 if (minssf > m->m.plug->max_ssf)
590 /* Does it meet our security properties? */
591 myflags = conn->props.security_flags;
593 /* if there's an external layer this is no longer plaintext */
594 if ((conn->props.min_ssf <= conn->external.ssf) &&
595 (conn->external.ssf > 1)) {
596 myflags &= ~SASL_SEC_NOPLAINTEXT;
599 if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) {
603 /* Can we meet it's features? */
604 if (cbindingdisp != SASL_CB_DISP_NONE &&
605 !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) {
609 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
610 && !conn->serverFQDN) {
614 /* Can it meet our features? */
615 if ((conn->flags & SASL_NEED_PROXY) &&
616 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
621 if (strcasecmp(m->m.plug->mech_name, PREFER_MECH) &&
622 bestm && m->m.plug->max_ssf <= bestssf) {
623 /* this mechanism isn't our favorite, and it's no better
624 than what we already have! */
628 if (bestm && m->m.plug->max_ssf <= bestssf) {
629 /* this mechanism is no better than what we already have! */
634 /* compare security flags, only take new mechanism if it has
635 * all the security flags of the previous one.
637 * From the mechanisms we ship with, this yields the order:
640 * GSSAPI + KERBEROS_V4
643 * PLAIN + LOGIN + ANONYMOUS
645 * This might be improved on by comparing the numeric value of
646 * the bitwise-or'd security flags, which splits DIGEST/OTP,
647 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
648 * are depending on the numeric values of the flags (which may
649 * change, and their ordering could be considered dumb luck.
653 ((m->m.plug->security_flags ^ bestm->m.plug->security_flags) &
654 bestm->m.plug->security_flags)) {
658 if (SASL_CB_PRESENT(c_conn->cparams) && plus) {
659 cbindingdisp = SASL_CB_DISP_USED;
663 *mech = m->m.plug->mech_name;
665 bestssf = m->m.plug->max_ssf;
669 name += strlen(name) + 1;
673 sasl_seterror(conn, 0, "No worthy mechs found");
674 result = SASL_NOMECH;
678 /* make (the rest of) cparams */
679 c_conn->cparams->service = conn->service;
680 c_conn->cparams->servicelen = (unsigned) strlen(conn->service);
682 if (conn->serverFQDN) {
683 c_conn->cparams->serverFQDN = conn->serverFQDN;
684 c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN);
687 c_conn->cparams->clientFQDN = c_conn->clientFQDN;
688 c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN);
690 c_conn->cparams->external_ssf = conn->external.ssf;
691 c_conn->cparams->props = conn->props;
692 c_conn->cparams->cbindingdisp = cbindingdisp;
693 c_conn->mech = bestm;
695 /* init that plugin */
696 result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context,
699 if(result != SASL_OK) goto done;
701 /* do a step -- but only if we can do a client-send-first */
704 if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
707 result = SASL_CONTINUE;
709 result = sasl_client_step(conn, NULL, 0, prompt_need,
710 clientout, clientoutlen);
714 result = SASL_CONTINUE;
717 if (ordered_mechs != NULL)
718 c_conn->cparams->utils->free(ordered_mechs);
719 RETURN(conn, result);
722 /* do a single authentication step.
723 * serverin -- the server message received by the client, MUST have a NUL
724 * sentinel, not counted by serverinlen
726 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
727 * clientout -- the client response to send to the server
731 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
732 * SASL_BADPROT -- server protocol incorrect/cancelled
733 * SASL_BADSERV -- server failed mutual auth
736 int sasl_client_step(sasl_conn_t *conn,
737 const char *serverin,
738 unsigned serverinlen,
739 sasl_interact_t **prompt_need,
740 const char **clientout,
741 unsigned *clientoutlen)
743 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
746 if(_sasl_client_active==0) return SASL_NOTINIT;
747 if(!conn) return SASL_BADPARAM;
749 /* check parameters */
750 if ((serverin==NULL) && (serverinlen>0))
753 /* Don't do another step if the plugin told us that we're done */
754 if (conn->oparams.doneflag) {
755 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
759 if(clientout) *clientout = NULL;
760 if(clientoutlen) *clientoutlen = 0;
763 result = c_conn->mech->m.plug->mech_step(conn->context,
768 clientout, clientoutlen,
771 if (result == SASL_OK) {
772 /* So we're done on this end, but if both
773 * 1. the mech does server-send-last
774 * 2. the protocol does not
775 * we need to return no data */
776 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
781 if(!conn->oparams.maxoutbuf) {
782 conn->oparams.maxoutbuf = conn->props.maxbufsize;
785 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
786 sasl_seterror(conn, 0,
787 "mech did not call canon_user for both authzid and authid");
788 result = SASL_BADPROT;
795 /* returns the length of all the mechanisms
799 static unsigned mech_names_len()
801 cmechanism_t *listptr;
804 for (listptr = cmechlist->mech_list;
806 listptr = listptr->next)
807 result += (unsigned) strlen(listptr->m.plug->mech_name);
813 int _sasl_client_listmech(sasl_conn_t *conn,
821 cmechanism_t *m=NULL;
822 sasl_ssf_t minssf = 0;
828 if(_sasl_client_active == 0) return SASL_NOTINIT;
829 if (!conn) return SASL_BADPARAM;
830 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
846 if(conn->props.min_ssf < conn->external.ssf) {
849 minssf = conn->props.min_ssf - conn->external.ssf;
852 if (! cmechlist || cmechlist->mech_length <= 0)
853 INTERROR(conn, SASL_NOMECH);
855 resultlen = (prefix ? strlen(prefix) : 0)
856 + (strlen(mysep) * (cmechlist->mech_length - 1))
858 + (suffix ? strlen(suffix) : 0)
860 ret = _buf_alloc(&conn->mechlist_buf,
861 &conn->mechlist_buf_len, resultlen);
862 if(ret != SASL_OK) MEMERROR(conn);
865 strcpy (conn->mechlist_buf,prefix);
867 *(conn->mechlist_buf) = '\0';
870 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
871 /* do we have the prompts for it? */
872 if (!have_prompts(conn, m->m.plug))
875 /* is it strong enough? */
876 if (minssf > m->m.plug->max_ssf)
879 /* does it meet our security properties? */
880 if (((conn->props.security_flags ^ m->m.plug->security_flags)
881 & conn->props.security_flags) != 0) {
885 /* Can we meet it's features? */
886 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
887 && !conn->serverFQDN) {
891 /* Can it meet our features? */
892 if ((conn->flags & SASL_NEED_PROXY) &&
893 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
897 /* Okay, we like it, add it to the list! */
902 /* print seperator */
904 strcat(conn->mechlist_buf, mysep);
909 /* now print the mechanism name */
910 strcat(conn->mechlist_buf, m->m.plug->mech_name);
914 strcat(conn->mechlist_buf,suffix);
917 *plen = (unsigned) strlen(conn->mechlist_buf);
919 *result = conn->mechlist_buf;
924 sasl_string_list_t *_sasl_client_mechs(void)
926 cmechanism_t *listptr;
927 sasl_string_list_t *retval = NULL, *next=NULL;
929 if(!_sasl_client_active) return NULL;
932 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
933 next = sasl_ALLOC(sizeof(sasl_string_list_t));
935 if(!next && !retval) return NULL;
946 next->d = listptr->m.plug->mech_name;
963 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
965 _sasl_print_mechanism (
966 client_sasl_mechanism_t *m,
967 sasl_info_callback_stage_t stage,
973 if (stage == SASL_INFO_LIST_START) {
974 printf ("List of client plugins follows\n");
976 } else if (stage == SASL_INFO_LIST_END) {
980 /* Process the mechanism */
981 printf ("Plugin \"%s\" ", m->plugname);
983 /* There is no delay loading for client side plugins */
986 printf (", \tAPI version: %d\n", m->version);
988 if (m->plug != NULL) {
989 printf ("\tSASL mechanism: %s, best SSF: %d\n",
993 printf ("\tsecurity flags:");
996 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
997 printf ("%cNO_ANONYMOUS", delimiter);
1001 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
1002 printf ("%cNO_PLAINTEXT", delimiter);
1006 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
1007 printf ("%cNO_ACTIVE", delimiter);
1011 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
1012 printf ("%cNO_DICTIONARY", delimiter);
1016 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
1017 printf ("%cFORWARD_SECRECY", delimiter);
1021 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
1022 printf ("%cPASS_CREDENTIALS", delimiter);
1026 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
1027 printf ("%cMUTUAL_AUTH", delimiter);
1033 printf ("\n\tfeatures:");
1036 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1037 printf ("%cWANT_CLIENT_FIRST", delimiter);
1041 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
1042 printf ("%cSERVER_FIRST", delimiter);
1046 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
1047 printf ("%cPROXY_AUTHENTICATION", delimiter);
1051 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
1052 printf ("%cNEED_SERVER_FQDN", delimiter);
1056 if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
1057 printf ("%cGSS_FRAMING", delimiter);
1061 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
1062 printf ("%cCHANNEL_BINDING", delimiter);
1067 /* Delay loading is not supported for the client side plugins:
1069 printf ("\n\twill be loaded from \"%s\"", m->f);
1077 /* Dump information about available client plugins */
1078 int sasl_client_plugin_info (
1079 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
1080 sasl_client_info_callback_t *info_cb,
1085 client_sasl_mechanism_t plug_data;
1087 char * mech_list = NULL;
1090 if (info_cb == NULL) {
1091 info_cb = _sasl_print_mechanism;
1094 if (cmechlist != NULL) {
1095 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
1097 if (c_mech_list == NULL) {
1098 m = cmechlist->mech_list; /* m point to beginning of the list */
1101 memcpy (&plug_data, &m->m, sizeof(plug_data));
1103 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1108 mech_list = strdup (c_mech_list);
1110 cur_mech = mech_list;
1112 while (cur_mech != NULL) {
1113 p = strchr (cur_mech, ' ');
1119 m = cmechlist->mech_list; /* m point to beginning of the list */
1122 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
1123 memcpy (&plug_data, &m->m, sizeof(plug_data));
1125 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1137 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
1142 return (SASL_NOTINIT);