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,
413 const char *p, *start = NULL;
418 listp = list = utils->malloc(strlen(mechs) + 1);
422 /* xxx confirm this with rfc 2222
423 * SASL mechanism allowable characters are "AZaz-_"
424 * seperators can be any other characters and of any length
425 * even variable lengths between
427 * Apps should be encouraged to simply use space or comma space
430 #define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-')
432 for (start = p = mechs, i = 0; *p != '\0'; p++) {
433 if (!ismechchar(*p) || p[1] == '\0') {
434 size_t len = p - start;
439 if (_mech_plus_p(start, len) == has_cb_data) {
440 memcpy(listp, start, len);
444 if (*server_can_cb == 0 && has_cb_data)
457 *ordered_mechs = list;
462 /* select a mechanism for a connection
463 * mechlist -- mechanisms server has available (punctuation ignored)
464 * secret -- optional secret from previous session
466 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
467 * clientout -- the initial client response to send to the server
468 * mech -- set to mechanism name
472 * SASL_NOMEM -- not enough memory
473 * SASL_NOMECH -- no mechanism meets requested properties
474 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
477 int sasl_client_start(sasl_conn_t *conn,
478 const char *mechlist,
479 sasl_interact_t **prompt_need,
480 const char **clientout,
481 unsigned *clientoutlen,
484 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
485 char *ordered_mechs = NULL, *name;
486 cmechanism_t *m=NULL,*bestm=NULL;
488 sasl_ssf_t bestssf = 0, minssf = 0;
489 int result, server_can_cb = 0;
490 unsigned int cbindingdisp;
492 if(_sasl_client_active==0) return SASL_NOTINIT;
494 if (!conn) return SASL_BADPARAM;
496 /* verify parameters */
497 if (mechlist == NULL)
500 /* if prompt_need != NULL we've already been here
501 and just need to do the continue step again */
504 /* FIXME: Hopefully they only give us our own prompt_need back */
505 if (prompt_need && *prompt_need != NULL) {
509 if(conn->props.min_ssf < conn->external.ssf) {
512 minssf = conn->props.min_ssf - conn->external.ssf;
515 result = _sasl_client_order_mechs(c_conn->cparams->utils,
517 SASL_CB_PRESENT(c_conn->cparams),
524 if (SASL_CB_PRESENT(c_conn->cparams)) {
525 if (server_can_cb == 0 && SASL_CB_CRITICAL(c_conn->cparams)) {
526 result = SASL_BADBINDING;
529 cbindingdisp = SASL_CB_DISP_WANT;
532 cbindingdisp = SASL_CB_DISP_NONE;
535 for (i = 0, name = ordered_mechs; i < list_len; i++) {
536 /* foreach in client list */
537 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
540 if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, &plus))
543 /* Do we have the prompts for it? */
544 if (!have_prompts(conn, m->m.plug))
547 /* Is it strong enough? */
548 if (minssf > m->m.plug->max_ssf)
551 /* Does it meet our security properties? */
552 myflags = conn->props.security_flags;
554 /* if there's an external layer this is no longer plaintext */
555 if ((conn->props.min_ssf <= conn->external.ssf) &&
556 (conn->external.ssf > 1)) {
557 myflags &= ~SASL_SEC_NOPLAINTEXT;
560 if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) {
564 /* Can we meet it's features? */
565 if (SASL_CB_PRESENT(c_conn->cparams) &&
566 !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) {
570 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
571 && !conn->serverFQDN) {
575 /* Can it meet our features? */
576 if ((conn->flags & SASL_NEED_PROXY) &&
577 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
582 if (strcasecmp(m->m.plug->mech_name, PREFER_MECH) &&
583 bestm && m->m.plug->max_ssf <= bestssf) {
584 /* this mechanism isn't our favorite, and it's no better
585 than what we already have! */
589 if (bestm && m->m.plug->max_ssf <= bestssf) {
590 /* this mechanism is no better than what we already have! */
595 /* compare security flags, only take new mechanism if it has
596 * all the security flags of the previous one.
598 * From the mechanisms we ship with, this yields the order:
601 * GSSAPI + KERBEROS_V4
604 * PLAIN + LOGIN + ANONYMOUS
606 * This might be improved on by comparing the numeric value of
607 * the bitwise-or'd security flags, which splits DIGEST/OTP,
608 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
609 * are depending on the numeric values of the flags (which may
610 * change, and their ordering could be considered dumb luck.
614 ((m->m.plug->security_flags ^ bestm->m.plug->security_flags) &
615 bestm->m.plug->security_flags)) {
619 /* Prefer server advertised CB mechanisms */
620 if (SASL_CB_PRESENT(c_conn->cparams) && plus) {
621 cbindingdisp = SASL_CB_DISP_USED;
625 *mech = m->m.plug->mech_name;
627 bestssf = m->m.plug->max_ssf;
631 name += strlen(name) + 1;
635 sasl_seterror(conn, 0, "No worthy mechs found");
636 result = SASL_NOMECH;
640 /* make (the rest of) cparams */
641 c_conn->cparams->service = conn->service;
642 c_conn->cparams->servicelen = (unsigned) strlen(conn->service);
644 if (conn->serverFQDN) {
645 c_conn->cparams->serverFQDN = conn->serverFQDN;
646 c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN);
649 c_conn->cparams->clientFQDN = c_conn->clientFQDN;
650 c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN);
652 c_conn->cparams->external_ssf = conn->external.ssf;
653 c_conn->cparams->props = conn->props;
654 c_conn->cparams->cbindingdisp = cbindingdisp;
655 c_conn->mech = bestm;
657 /* init that plugin */
658 result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context,
661 if(result != SASL_OK) goto done;
663 /* do a step -- but only if we can do a client-send-first */
666 if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
669 result = SASL_CONTINUE;
671 result = sasl_client_step(conn, NULL, 0, prompt_need,
672 clientout, clientoutlen);
676 result = SASL_CONTINUE;
679 if (ordered_mechs != NULL)
680 c_conn->cparams->utils->free(ordered_mechs);
681 RETURN(conn, result);
684 /* do a single authentication step.
685 * serverin -- the server message received by the client, MUST have a NUL
686 * sentinel, not counted by serverinlen
688 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
689 * clientout -- the client response to send to the server
693 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
694 * SASL_BADPROT -- server protocol incorrect/cancelled
695 * SASL_BADSERV -- server failed mutual auth
698 int sasl_client_step(sasl_conn_t *conn,
699 const char *serverin,
700 unsigned serverinlen,
701 sasl_interact_t **prompt_need,
702 const char **clientout,
703 unsigned *clientoutlen)
705 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
708 if(_sasl_client_active==0) return SASL_NOTINIT;
709 if(!conn) return SASL_BADPARAM;
711 /* check parameters */
712 if ((serverin==NULL) && (serverinlen>0))
715 /* Don't do another step if the plugin told us that we're done */
716 if (conn->oparams.doneflag) {
717 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
721 if(clientout) *clientout = NULL;
722 if(clientoutlen) *clientoutlen = 0;
725 result = c_conn->mech->m.plug->mech_step(conn->context,
730 clientout, clientoutlen,
733 if (result == SASL_OK) {
734 /* So we're done on this end, but if both
735 * 1. the mech does server-send-last
736 * 2. the protocol does not
737 * we need to return no data */
738 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
743 if(!conn->oparams.maxoutbuf) {
744 conn->oparams.maxoutbuf = conn->props.maxbufsize;
747 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
748 sasl_seterror(conn, 0,
749 "mech did not call canon_user for both authzid and authid");
750 result = SASL_BADPROT;
757 /* returns the length of all the mechanisms
761 static unsigned mech_names_len()
763 cmechanism_t *listptr;
766 for (listptr = cmechlist->mech_list;
768 listptr = listptr->next)
769 result += (unsigned) strlen(listptr->m.plug->mech_name);
775 int _sasl_client_listmech(sasl_conn_t *conn,
783 cmechanism_t *m=NULL;
784 sasl_ssf_t minssf = 0;
790 if(_sasl_client_active == 0) return SASL_NOTINIT;
791 if (!conn) return SASL_BADPARAM;
792 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
808 if(conn->props.min_ssf < conn->external.ssf) {
811 minssf = conn->props.min_ssf - conn->external.ssf;
814 if (! cmechlist || cmechlist->mech_length <= 0)
815 INTERROR(conn, SASL_NOMECH);
817 resultlen = (prefix ? strlen(prefix) : 0)
818 + (strlen(mysep) * (cmechlist->mech_length - 1))
820 + (suffix ? strlen(suffix) : 0)
822 ret = _buf_alloc(&conn->mechlist_buf,
823 &conn->mechlist_buf_len, resultlen);
824 if(ret != SASL_OK) MEMERROR(conn);
827 strcpy (conn->mechlist_buf,prefix);
829 *(conn->mechlist_buf) = '\0';
832 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
833 /* do we have the prompts for it? */
834 if (!have_prompts(conn, m->m.plug))
837 /* is it strong enough? */
838 if (minssf > m->m.plug->max_ssf)
841 /* does it meet our security properties? */
842 if (((conn->props.security_flags ^ m->m.plug->security_flags)
843 & conn->props.security_flags) != 0) {
847 /* Can we meet it's features? */
848 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
849 && !conn->serverFQDN) {
853 /* Can it meet our features? */
854 if ((conn->flags & SASL_NEED_PROXY) &&
855 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
859 /* Okay, we like it, add it to the list! */
864 /* print seperator */
866 strcat(conn->mechlist_buf, mysep);
871 /* now print the mechanism name */
872 strcat(conn->mechlist_buf, m->m.plug->mech_name);
876 strcat(conn->mechlist_buf,suffix);
879 *plen = (unsigned) strlen(conn->mechlist_buf);
881 *result = conn->mechlist_buf;
886 sasl_string_list_t *_sasl_client_mechs(void)
888 cmechanism_t *listptr;
889 sasl_string_list_t *retval = NULL, *next=NULL;
891 if(!_sasl_client_active) return NULL;
894 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
895 next = sasl_ALLOC(sizeof(sasl_string_list_t));
897 if(!next && !retval) return NULL;
908 next->d = listptr->m.plug->mech_name;
925 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
927 _sasl_print_mechanism (
928 client_sasl_mechanism_t *m,
929 sasl_info_callback_stage_t stage,
935 if (stage == SASL_INFO_LIST_START) {
936 printf ("List of client plugins follows\n");
938 } else if (stage == SASL_INFO_LIST_END) {
942 /* Process the mechanism */
943 printf ("Plugin \"%s\" ", m->plugname);
945 /* There is no delay loading for client side plugins */
948 printf (", \tAPI version: %d\n", m->version);
950 if (m->plug != NULL) {
951 printf ("\tSASL mechanism: %s, best SSF: %d\n",
955 printf ("\tsecurity flags:");
958 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
959 printf ("%cNO_ANONYMOUS", delimiter);
963 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
964 printf ("%cNO_PLAINTEXT", delimiter);
968 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
969 printf ("%cNO_ACTIVE", delimiter);
973 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
974 printf ("%cNO_DICTIONARY", delimiter);
978 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
979 printf ("%cFORWARD_SECRECY", delimiter);
983 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
984 printf ("%cPASS_CREDENTIALS", delimiter);
988 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
989 printf ("%cMUTUAL_AUTH", delimiter);
995 printf ("\n\tfeatures:");
998 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
999 printf ("%cWANT_CLIENT_FIRST", delimiter);
1003 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
1004 printf ("%cSERVER_FIRST", delimiter);
1008 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
1009 printf ("%cPROXY_AUTHENTICATION", delimiter);
1013 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
1014 printf ("%cNEED_SERVER_FQDN", delimiter);
1018 if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
1019 printf ("%cGSS_FRAMING", delimiter);
1023 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
1024 printf ("%cCHANNEL_BINDING", delimiter);
1029 /* Delay loading is not supported for the client side plugins:
1031 printf ("\n\twill be loaded from \"%s\"", m->f);
1039 /* Dump information about available client plugins */
1040 int sasl_client_plugin_info (
1041 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
1042 sasl_client_info_callback_t *info_cb,
1047 client_sasl_mechanism_t plug_data;
1049 char * mech_list = NULL;
1052 if (info_cb == NULL) {
1053 info_cb = _sasl_print_mechanism;
1056 if (cmechlist != NULL) {
1057 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
1059 if (c_mech_list == NULL) {
1060 m = cmechlist->mech_list; /* m point to beginning of the list */
1063 memcpy (&plug_data, &m->m, sizeof(plug_data));
1065 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1070 mech_list = strdup (c_mech_list);
1072 cur_mech = mech_list;
1074 while (cur_mech != NULL) {
1075 p = strchr (cur_mech, ' ');
1081 m = cmechlist->mech_list; /* m point to beginning of the list */
1084 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
1085 memcpy (&plug_data, &m->m, sizeof(plug_data));
1087 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1099 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
1104 return (SASL_NOTINIT);