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 */
393 static inline int sasl_is_plus_mech(const char *mech)
395 size_t len = strlen(mech);
403 return (strcmp(p, "-PLUS") == 0);
406 /* select a mechanism for a connection
407 * mechlist -- mechanisms server has available (punctuation ignored)
408 * secret -- optional secret from previous session
410 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
411 * clientout -- the initial client response to send to the server
412 * mech -- set to mechanism name
416 * SASL_NOMEM -- not enough memory
417 * SASL_NOMECH -- no mechanism meets requested properties
418 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
421 /* xxx confirm this with rfc 2222
422 * SASL mechanism allowable characters are "AZaz-_"
423 * seperators can be any other characters and of any length
424 * even variable lengths between
426 * Apps should be encouraged to simply use space or comma space
429 int sasl_client_start(sasl_conn_t *conn,
430 const char *mechlist,
431 sasl_interact_t **prompt_need,
432 const char **clientout,
433 unsigned *clientoutlen,
436 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
437 char name[SASL_MECHNAMEMAX + 1];
438 cmechanism_t *m=NULL,*bestm=NULL;
441 sasl_ssf_t bestssf = 0, minssf = 0;
444 if(_sasl_client_active==0) return SASL_NOTINIT;
446 if (!conn) return SASL_BADPARAM;
448 /* verify parameters */
449 if (mechlist == NULL)
452 /* if prompt_need != NULL we've already been here
453 and just need to do the continue step again */
456 /* FIXME: Hopefully they only give us our own prompt_need back */
457 if (prompt_need && *prompt_need != NULL) {
461 if(conn->props.min_ssf < conn->external.ssf) {
464 minssf = conn->props.min_ssf - conn->external.ssf;
468 list_len = strlen(mechlist);
473 while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos])
474 || mechlist[pos] == '_'
475 || mechlist[pos] == '-')) {
476 name[place]=mechlist[pos];
479 if (SASL_MECHNAMEMAX < place) {
481 while(pos<list_len && (isalnum((unsigned char)mechlist[pos])
482 || mechlist[pos] == '_'
483 || mechlist[pos] == '-'))
490 if (! place) continue;
492 /* foreach in client list */
493 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
496 /* Is this the mechanism the server is suggesting? */
497 if (strcasecmp(m->m.plug->mech_name, name))
500 /* Do we have the prompts for it? */
501 if (!have_prompts(conn, m->m.plug))
504 /* Is it strong enough? */
505 if (minssf > m->m.plug->max_ssf)
508 /* Does it meet our security properties? */
509 myflags = conn->props.security_flags;
511 /* if there's an external layer this is no longer plaintext */
512 if ((conn->props.min_ssf <= conn->external.ssf) &&
513 (conn->external.ssf > 1)) {
514 myflags &= ~SASL_SEC_NOPLAINTEXT;
517 if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) {
521 /* Can we meet it's features? */
522 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
523 && !conn->serverFQDN) {
527 /* Can it meet our features? */
528 if ((conn->flags & SASL_NEED_PROXY) &&
529 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
533 /* If client requires channel binding, prefer -PLUS mech */
534 if (c_conn->cparams->chanbindingslen != 0) {
535 if (sasl_is_plus_mech(name))
536 c_conn->cparams->chanbindingsflag = SASL_CB_FLAG_USED;
538 c_conn->cparams->chanbindingsflag = SASL_CB_FLAG_WANT;
540 c_conn->cparams->chanbindingsflag = SASL_CB_FLAG_NONE;
544 if (strcasecmp(m->m.plug->mech_name, PREFER_MECH) &&
545 bestm && m->m.plug->max_ssf <= bestssf) {
546 /* this mechanism isn't our favorite, and it's no better
547 than what we already have! */
551 if (bestm && m->m.plug->max_ssf <= bestssf) {
552 /* this mechanism is no better than what we already have! */
557 /* compare security flags, only take new mechanism if it has
558 * all the security flags of the previous one.
560 * From the mechanisms we ship with, this yields the order:
563 * GSSAPI + KERBEROS_V4
566 * PLAIN + LOGIN + ANONYMOUS
568 * This might be improved on by comparing the numeric value of
569 * the bitwise-or'd security flags, which splits DIGEST/OTP,
570 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
571 * are depending on the numeric values of the flags (which may
572 * change, and their ordering could be considered dumb luck.
576 ((m->m.plug->security_flags ^ bestm->m.plug->security_flags) &
577 bestm->m.plug->security_flags)) {
582 *mech = m->m.plug->mech_name;
584 bestssf = m->m.plug->max_ssf;
591 sasl_seterror(conn, 0, "No worthy mechs found");
592 result = SASL_NOMECH;
596 /* make (the rest of) cparams */
597 c_conn->cparams->service = conn->service;
598 c_conn->cparams->servicelen = (unsigned) strlen(conn->service);
600 if (conn->serverFQDN) {
601 c_conn->cparams->serverFQDN = conn->serverFQDN;
602 c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN);
605 c_conn->cparams->clientFQDN = c_conn->clientFQDN;
606 c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN);
608 c_conn->cparams->external_ssf = conn->external.ssf;
609 c_conn->cparams->props = conn->props;
610 c_conn->mech = bestm;
612 /* init that plugin */
613 result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context,
616 if(result != SASL_OK) goto done;
618 /* do a step -- but only if we can do a client-send-first */
621 if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
624 result = SASL_CONTINUE;
626 result = sasl_client_step(conn, NULL, 0, prompt_need,
627 clientout, clientoutlen);
631 result = SASL_CONTINUE;
634 RETURN(conn, result);
637 /* do a single authentication step.
638 * serverin -- the server message received by the client, MUST have a NUL
639 * sentinel, not counted by serverinlen
641 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
642 * clientout -- the client response to send to the server
646 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
647 * SASL_BADPROT -- server protocol incorrect/cancelled
648 * SASL_BADSERV -- server failed mutual auth
651 int sasl_client_step(sasl_conn_t *conn,
652 const char *serverin,
653 unsigned serverinlen,
654 sasl_interact_t **prompt_need,
655 const char **clientout,
656 unsigned *clientoutlen)
658 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
661 if(_sasl_client_active==0) return SASL_NOTINIT;
662 if(!conn) return SASL_BADPARAM;
664 /* check parameters */
665 if ((serverin==NULL) && (serverinlen>0))
668 /* Don't do another step if the plugin told us that we're done */
669 if (conn->oparams.doneflag) {
670 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
674 if(clientout) *clientout = NULL;
675 if(clientoutlen) *clientoutlen = 0;
678 result = c_conn->mech->m.plug->mech_step(conn->context,
683 clientout, clientoutlen,
686 if (result == SASL_OK) {
687 /* So we're done on this end, but if both
688 * 1. the mech does server-send-last
689 * 2. the protocol does not
690 * we need to return no data */
691 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
696 if(!conn->oparams.maxoutbuf) {
697 conn->oparams.maxoutbuf = conn->props.maxbufsize;
700 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
701 sasl_seterror(conn, 0,
702 "mech did not call canon_user for both authzid and authid");
703 result = SASL_BADPROT;
710 /* returns the length of all the mechanisms
714 static unsigned mech_names_len()
716 cmechanism_t *listptr;
719 for (listptr = cmechlist->mech_list;
721 listptr = listptr->next)
722 result += (unsigned) strlen(listptr->m.plug->mech_name);
728 int _sasl_client_listmech(sasl_conn_t *conn,
736 cmechanism_t *m=NULL;
737 sasl_ssf_t minssf = 0;
743 if(_sasl_client_active == 0) return SASL_NOTINIT;
744 if (!conn) return SASL_BADPARAM;
745 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
761 if(conn->props.min_ssf < conn->external.ssf) {
764 minssf = conn->props.min_ssf - conn->external.ssf;
767 if (! cmechlist || cmechlist->mech_length <= 0)
768 INTERROR(conn, SASL_NOMECH);
770 resultlen = (prefix ? strlen(prefix) : 0)
771 + (strlen(mysep) * (cmechlist->mech_length - 1))
773 + (suffix ? strlen(suffix) : 0)
775 ret = _buf_alloc(&conn->mechlist_buf,
776 &conn->mechlist_buf_len, resultlen);
777 if(ret != SASL_OK) MEMERROR(conn);
780 strcpy (conn->mechlist_buf,prefix);
782 *(conn->mechlist_buf) = '\0';
785 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
786 /* do we have the prompts for it? */
787 if (!have_prompts(conn, m->m.plug))
790 /* is it strong enough? */
791 if (minssf > m->m.plug->max_ssf)
794 /* does it meet our security properties? */
795 if (((conn->props.security_flags ^ m->m.plug->security_flags)
796 & conn->props.security_flags) != 0) {
800 /* Can we meet it's features? */
801 if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
802 && !conn->serverFQDN) {
806 /* Can it meet our features? */
807 if ((conn->flags & SASL_NEED_PROXY) &&
808 !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
812 /* Okay, we like it, add it to the list! */
817 /* print seperator */
819 strcat(conn->mechlist_buf, mysep);
824 /* now print the mechanism name */
825 strcat(conn->mechlist_buf, m->m.plug->mech_name);
829 strcat(conn->mechlist_buf,suffix);
832 *plen = (unsigned) strlen(conn->mechlist_buf);
834 *result = conn->mechlist_buf;
839 sasl_string_list_t *_sasl_client_mechs(void)
841 cmechanism_t *listptr;
842 sasl_string_list_t *retval = NULL, *next=NULL;
844 if(!_sasl_client_active) return NULL;
847 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
848 next = sasl_ALLOC(sizeof(sasl_string_list_t));
850 if(!next && !retval) return NULL;
861 next->d = listptr->m.plug->mech_name;
878 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
880 _sasl_print_mechanism (
881 client_sasl_mechanism_t *m,
882 sasl_info_callback_stage_t stage,
888 if (stage == SASL_INFO_LIST_START) {
889 printf ("List of client plugins follows\n");
891 } else if (stage == SASL_INFO_LIST_END) {
895 /* Process the mechanism */
896 printf ("Plugin \"%s\" ", m->plugname);
898 /* There is no delay loading for client side plugins */
901 printf (", \tAPI version: %d\n", m->version);
903 if (m->plug != NULL) {
904 printf ("\tSASL mechanism: %s, best SSF: %d\n",
908 printf ("\tsecurity flags:");
911 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
912 printf ("%cNO_ANONYMOUS", delimiter);
916 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
917 printf ("%cNO_PLAINTEXT", delimiter);
921 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
922 printf ("%cNO_ACTIVE", delimiter);
926 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
927 printf ("%cNO_DICTIONARY", delimiter);
931 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
932 printf ("%cFORWARD_SECRECY", delimiter);
936 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
937 printf ("%cPASS_CREDENTIALS", delimiter);
941 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
942 printf ("%cMUTUAL_AUTH", delimiter);
948 printf ("\n\tfeatures:");
951 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
952 printf ("%cWANT_CLIENT_FIRST", delimiter);
956 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
957 printf ("%cSERVER_FIRST", delimiter);
961 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
962 printf ("%cPROXY_AUTHENTICATION", delimiter);
966 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
967 printf ("%cNEED_SERVER_FQDN", delimiter);
972 /* Delay loading is not supported for the client side plugins:
974 printf ("\n\twill be loaded from \"%s\"", m->f);
982 /* Dump information about available client plugins */
983 int sasl_client_plugin_info (
984 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
985 sasl_client_info_callback_t *info_cb,
990 client_sasl_mechanism_t plug_data;
992 char * mech_list = NULL;
995 if (info_cb == NULL) {
996 info_cb = _sasl_print_mechanism;
999 if (cmechlist != NULL) {
1000 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
1002 if (c_mech_list == NULL) {
1003 m = cmechlist->mech_list; /* m point to beginning of the list */
1006 memcpy (&plug_data, &m->m, sizeof(plug_data));
1008 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1013 mech_list = strdup (c_mech_list);
1015 cur_mech = mech_list;
1017 while (cur_mech != NULL) {
1018 p = strchr (cur_mech, ' ');
1024 m = cmechlist->mech_list; /* m point to beginning of the list */
1027 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
1028 memcpy (&plug_data, &m->m, sizeof(plug_data));
1030 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
1042 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
1047 return (SASL_NOTINIT);