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);
423 for (start = p = mechs, i = 0; *p != '\0'; p++) {
424 if (isspace(*p) || p[1] == '\0') {
425 size_t len = p - start;
430 if (_mech_plus_p(start, len) == has_cb_data) {
431 memcpy(listp, start, len);
435 if (*server_can_cb == 0 && has_cb_data)
448 *ordered_mechs = list;
453 /* select a mechanism for a connection
454 * mechlist -- mechanisms server has available (punctuation ignored)
455 * secret -- optional secret from previous session
457 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
458 * clientout -- the initial client response to send to the server
459 * mech -- set to mechanism name
463 * SASL_NOMEM -- not enough memory
464 * SASL_NOMECH -- no mechanism meets requested properties
465 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
468 /* xxx confirm this with rfc 2222
469 * SASL mechanism allowable characters are "AZaz-_"
470 * seperators can be any other characters and of any length
471 * even variable lengths between
473 * Apps should be encouraged to simply use space or comma space
476 int sasl_client_start(sasl_conn_t *conn,
477 const char *mechlist,
478 sasl_interact_t **prompt_need,
479 const char **clientout,
480 unsigned *clientoutlen,
483 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
484 char *ordered_mechs = NULL, *name;
485 cmechanism_t *m=NULL,*bestm=NULL;
487 sasl_ssf_t bestssf = 0, minssf = 0;
488 int result, server_can_cb = 0;
489 unsigned int cbindingdisp = 0;
491 if(_sasl_client_active==0) return SASL_NOTINIT;
493 if (!conn) return SASL_BADPARAM;
495 /* verify parameters */
496 if (mechlist == NULL)
499 /* if prompt_need != NULL we've already been here
500 and just need to do the continue step again */
503 /* FIXME: Hopefully they only give us our own prompt_need back */
504 if (prompt_need && *prompt_need != NULL) {
508 if(conn->props.min_ssf < conn->external.ssf) {
511 minssf = conn->props.min_ssf - conn->external.ssf;
514 result = _sasl_client_order_mechs(c_conn->cparams->utils,
516 SASL_CB_PRESENT(c_conn->cparams),
523 if (SASL_CB_PRESENT(c_conn->cparams)) {
524 if (server_can_cb == 0 && SASL_CB_CRITICAL(c_conn->cparams)) {
525 result = SASL_NOMECH;
528 cbindingdisp = SASL_CB_DISP_WANT;
531 cbindingdisp = SASL_CB_DISP_NONE;
534 for (i = 0, name = ordered_mechs; i < list_len; i++) {
535 /* foreach in client list */
536 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
539 if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, &plus))
542 /* Do we have the prompts for it? */
543 if (!have_prompts(conn, m->m.plug))
546 /* Is it strong enough? */
547 if (minssf > m->m.plug->max_ssf)
550 /* Does it meet our security properties? */
551 myflags = conn->props.security_flags;
553 /* if there's an external layer this is no longer plaintext */
554 if ((conn->props.min_ssf <= conn->external.ssf) &&
555 (conn->external.ssf > 1)) {
556 myflags &= ~SASL_SEC_NOPLAINTEXT;
559 if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) {
563 /* Can we meet it's features? */
564 if (SASL_CB_PRESENT(c_conn->cparams) &&
565 !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) {
569 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
570 && !conn->serverFQDN) {
574 /* Can it meet our features? */
575 if ((conn->flags & SASL_NEED_PROXY) &&
576 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
581 if (strcasecmp(m->m.plug->mech_name, PREFER_MECH) &&
582 bestm && m->m.plug->max_ssf <= bestssf) {
583 /* this mechanism isn't our favorite, and it's no better
584 than what we already have! */
588 if (bestm && m->m.plug->max_ssf <= bestssf) {
589 /* this mechanism is no better than what we already have! */
594 /* compare security flags, only take new mechanism if it has
595 * all the security flags of the previous one.
597 * From the mechanisms we ship with, this yields the order:
600 * GSSAPI + KERBEROS_V4
603 * PLAIN + LOGIN + ANONYMOUS
605 * This might be improved on by comparing the numeric value of
606 * the bitwise-or'd security flags, which splits DIGEST/OTP,
607 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
608 * are depending on the numeric values of the flags (which may
609 * change, and their ordering could be considered dumb luck.
613 ((m->m.plug->security_flags ^ bestm->m.plug->security_flags) &
614 bestm->m.plug->security_flags)) {
618 /* Prefer server advertised CB mechanisms */
619 if (SASL_CB_PRESENT(c_conn->cparams) && plus) {
620 cbindingdisp = SASL_CB_DISP_USED;
624 *mech = m->m.plug->mech_name;
626 bestssf = m->m.plug->max_ssf;
630 name += strlen(name) + 1;
634 sasl_seterror(conn, 0, "No worthy mechs found");
635 result = SASL_NOMECH;
639 /* make (the rest of) cparams */
640 c_conn->cparams->service = conn->service;
641 c_conn->cparams->servicelen = (unsigned) strlen(conn->service);
643 if (conn->serverFQDN) {
644 c_conn->cparams->serverFQDN = conn->serverFQDN;
645 c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN);
648 c_conn->cparams->clientFQDN = c_conn->clientFQDN;
649 c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN);
651 c_conn->cparams->external_ssf = conn->external.ssf;
652 c_conn->cparams->props = conn->props;
653 c_conn->cparams->cbindingdisp = cbindingdisp;
654 c_conn->mech = bestm;
656 /* init that plugin */
657 result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context,
660 if(result != SASL_OK) goto done;
662 /* do a step -- but only if we can do a client-send-first */
665 if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
668 result = SASL_CONTINUE;
670 result = sasl_client_step(conn, NULL, 0, prompt_need,
671 clientout, clientoutlen);
675 result = SASL_CONTINUE;
678 if (ordered_mechs != NULL)
679 c_conn->cparams->utils->free(ordered_mechs);
680 RETURN(conn, result);
683 /* do a single authentication step.
684 * serverin -- the server message received by the client, MUST have a NUL
685 * sentinel, not counted by serverinlen
687 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
688 * clientout -- the client response to send to the server
692 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
693 * SASL_BADPROT -- server protocol incorrect/cancelled
694 * SASL_BADSERV -- server failed mutual auth
697 int sasl_client_step(sasl_conn_t *conn,
698 const char *serverin,
699 unsigned serverinlen,
700 sasl_interact_t **prompt_need,
701 const char **clientout,
702 unsigned *clientoutlen)
704 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
707 if(_sasl_client_active==0) return SASL_NOTINIT;
708 if(!conn) return SASL_BADPARAM;
710 /* check parameters */
711 if ((serverin==NULL) && (serverinlen>0))
714 /* Don't do another step if the plugin told us that we're done */
715 if (conn->oparams.doneflag) {
716 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
720 if(clientout) *clientout = NULL;
721 if(clientoutlen) *clientoutlen = 0;
724 result = c_conn->mech->m.plug->mech_step(conn->context,
729 clientout, clientoutlen,
732 if (result == SASL_OK) {
733 /* So we're done on this end, but if both
734 * 1. the mech does server-send-last
735 * 2. the protocol does not
736 * we need to return no data */
737 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
742 if(!conn->oparams.maxoutbuf) {
743 conn->oparams.maxoutbuf = conn->props.maxbufsize;
746 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
747 sasl_seterror(conn, 0,
748 "mech did not call canon_user for both authzid and authid");
749 result = SASL_BADPROT;
756 /* returns the length of all the mechanisms
760 static unsigned mech_names_len()
762 cmechanism_t *listptr;
765 for (listptr = cmechlist->mech_list;
767 listptr = listptr->next)
768 result += (unsigned) strlen(listptr->m.plug->mech_name);
774 int _sasl_client_listmech(sasl_conn_t *conn,
782 cmechanism_t *m=NULL;
783 sasl_ssf_t minssf = 0;
789 if(_sasl_client_active == 0) return SASL_NOTINIT;
790 if (!conn) return SASL_BADPARAM;
791 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
807 if(conn->props.min_ssf < conn->external.ssf) {
810 minssf = conn->props.min_ssf - conn->external.ssf;
813 if (! cmechlist || cmechlist->mech_length <= 0)
814 INTERROR(conn, SASL_NOMECH);
816 resultlen = (prefix ? strlen(prefix) : 0)
817 + (strlen(mysep) * (cmechlist->mech_length - 1))
819 + (suffix ? strlen(suffix) : 0)
821 ret = _buf_alloc(&conn->mechlist_buf,
822 &conn->mechlist_buf_len, resultlen);
823 if(ret != SASL_OK) MEMERROR(conn);
826 strcpy (conn->mechlist_buf,prefix);
828 *(conn->mechlist_buf) = '\0';
831 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
832 /* do we have the prompts for it? */
833 if (!have_prompts(conn, m->m.plug))
836 /* is it strong enough? */
837 if (minssf > m->m.plug->max_ssf)
840 /* does it meet our security properties? */
841 if (((conn->props.security_flags ^ m->m.plug->security_flags)
842 & conn->props.security_flags) != 0) {
846 /* Can we meet it's features? */
847 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
848 && !conn->serverFQDN) {
852 /* Can it meet our features? */
853 if ((conn->flags & SASL_NEED_PROXY) &&
854 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
858 /* Okay, we like it, add it to the list! */
863 /* print seperator */
865 strcat(conn->mechlist_buf, mysep);
870 /* now print the mechanism name */
871 strcat(conn->mechlist_buf, m->m.plug->mech_name);
875 strcat(conn->mechlist_buf,suffix);
878 *plen = (unsigned) strlen(conn->mechlist_buf);
880 *result = conn->mechlist_buf;
885 sasl_string_list_t *_sasl_client_mechs(void)
887 cmechanism_t *listptr;
888 sasl_string_list_t *retval = NULL, *next=NULL;
890 if(!_sasl_client_active) return NULL;
893 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
894 next = sasl_ALLOC(sizeof(sasl_string_list_t));
896 if(!next && !retval) return NULL;
907 next->d = listptr->m.plug->mech_name;
924 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
926 _sasl_print_mechanism (
927 client_sasl_mechanism_t *m,
928 sasl_info_callback_stage_t stage,
934 if (stage == SASL_INFO_LIST_START) {
935 printf ("List of client plugins follows\n");
937 } else if (stage == SASL_INFO_LIST_END) {
941 /* Process the mechanism */
942 printf ("Plugin \"%s\" ", m->plugname);
944 /* There is no delay loading for client side plugins */
947 printf (", \tAPI version: %d\n", m->version);
949 if (m->plug != NULL) {
950 printf ("\tSASL mechanism: %s, best SSF: %d\n",
954 printf ("\tsecurity flags:");
957 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
958 printf ("%cNO_ANONYMOUS", delimiter);
962 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
963 printf ("%cNO_PLAINTEXT", delimiter);
967 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
968 printf ("%cNO_ACTIVE", delimiter);
972 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
973 printf ("%cNO_DICTIONARY", delimiter);
977 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
978 printf ("%cFORWARD_SECRECY", delimiter);
982 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
983 printf ("%cPASS_CREDENTIALS", delimiter);
987 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
988 printf ("%cMUTUAL_AUTH", delimiter);
994 printf ("\n\tfeatures:");
997 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
998 printf ("%cWANT_CLIENT_FIRST", delimiter);
1002 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
1003 printf ("%cSERVER_FIRST", delimiter);
1007 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
1008 printf ("%cPROXY_AUTHENTICATION", delimiter);
1012 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
1013 printf ("%cNEED_SERVER_FQDN", delimiter);
1017 if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
1018 printf ("%cGSS_FRAMING", delimiter);
1022 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
1023 printf ("%cCHANNEL_BINDING", delimiter);
1028 /* Delay loading is not supported for the client side plugins:
1030 printf ("\n\twill be loaded from \"%s\"", m->f);
1038 /* Dump information about available client plugins */
1039 int sasl_client_plugin_info (
1040 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
1041 sasl_client_info_callback_t *info_cb,
1046 client_sasl_mechanism_t plug_data;
1048 char * mech_list = NULL;
1051 if (info_cb == NULL) {
1052 info_cb = _sasl_print_mechanism;
1055 if (cmechlist != NULL) {
1056 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
1058 if (c_mech_list == NULL) {
1059 m = cmechlist->mech_list; /* m point to beginning of the list */
1062 memcpy (&plug_data, &m->m, sizeof(plug_data));
1064 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1069 mech_list = strdup (c_mech_list);
1071 cur_mech = mech_list;
1073 while (cur_mech != NULL) {
1074 p = strchr (cur_mech, ' ');
1080 m = cmechlist->mech_list; /* m point to beginning of the list */
1083 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
1084 memcpy (&plug_data, &m->m, sizeof(plug_data));
1086 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1098 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
1103 return (SASL_NOTINIT);