Andreas Krennmair:
[apps/madmutt.git] / mutt_sasl.c
1 /*
2  * Copyright (C) 2000-5 Brendan Cully <brendan@kublai.com>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */
18
19 /* common SASL helper routines */
20
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "mutt.h"
26 #include "account.h"
27 #include "mutt_sasl.h"
28 #include "mutt_socket.h"
29
30 #ifdef USE_SASL2
31 #include <errno.h>
32 #include <netdb.h>
33 #include <sasl/sasl.h>
34 #else
35 #include <sasl.h>
36 #endif
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39
40 #ifdef USE_SASL2
41 static int getnameinfo_err (int ret)
42 {
43   int err;
44
45   dprint (1, (debugfile, "getnameinfo: "));
46   switch (ret) {
47   case EAI_AGAIN:
48     dprint (1,
49             (debugfile,
50              "The name could not be resolved at this time.  Future attempts may succeed.\n"));
51     err = SASL_TRYAGAIN;
52     break;
53   case EAI_BADFLAGS:
54     dprint (1, (debugfile, "The flags had an invalid value.\n"));
55     err = SASL_BADPARAM;
56     break;
57   case EAI_FAIL:
58     dprint (1, (debugfile, "A non-recoverable error occurred.\n"));
59     err = SASL_FAIL;
60     break;
61   case EAI_FAMILY:
62     dprint (1,
63             (debugfile,
64              "The address family was not recognized or the address length was invalid for the specified family.\n"));
65     err = SASL_BADPROT;
66     break;
67   case EAI_MEMORY:
68     dprint (1, (debugfile, "There was a memory allocation failure.\n"));
69     err = SASL_NOMEM;
70     break;
71   case EAI_NONAME:
72     dprint (1,
73             (debugfile,
74              "The name does not resolve for the supplied parameters.  NI_NAMEREQD is set and the host's name cannot be located, or both nodename and servname were null.\n"));
75     err = SASL_FAIL;            /* no real equivalent */
76     break;
77   case EAI_SYSTEM:
78     dprint (1,
79             (debugfile,
80              "A system error occurred.  The error code can be found in errno(%d,%s)).\n",
81              errno, strerror (errno)));
82     err = SASL_FAIL;            /* no real equivalent */
83     break;
84   default:
85     dprint (1, (debugfile, "Unknown error %d\n", ret));
86     err = SASL_FAIL;            /* no real equivalent */
87     break;
88   }
89   return err;
90 }
91 #endif
92
93 /* arbitrary. SASL will probably use a smaller buffer anyway. OTOH it's
94  * been a while since I've had access to an SASL server which negotiated
95  * a protection buffer. */
96 #define M_SASL_MAXBUF 65536
97
98 #ifdef USE_SASL2
99 #define IP_PORT_BUFLEN 1024
100 #endif
101
102 static sasl_callback_t mutt_sasl_callbacks[5];
103
104 static int mutt_sasl_start (void);
105
106 /* callbacks */
107 static int mutt_sasl_cb_log (void *context, int priority,
108                              const char *message);
109 static int mutt_sasl_cb_authname (void *context, int id, const char **result,
110                                   unsigned int *len);
111 static int mutt_sasl_cb_pass (sasl_conn_t * conn, void *context, int id,
112                               sasl_secret_t ** psecret);
113
114 /* socket wrappers for a SASL security layer */
115 static int mutt_sasl_conn_open (CONNECTION * conn);
116 static int mutt_sasl_conn_close (CONNECTION * conn);
117 static int mutt_sasl_conn_read (CONNECTION * conn, char *buf, size_t len);
118 static int mutt_sasl_conn_write (CONNECTION * conn, const char *buf,
119                                  size_t count);
120
121 #ifdef USE_SASL2
122 /* utility function, stolen from sasl2 sample code */
123 static int iptostring (const struct sockaddr *addr, socklen_t addrlen,
124                        char *out, unsigned outlen)
125 {
126   char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
127   int ret;
128
129   if (!addr || !out)
130     return SASL_BADPARAM;
131
132   ret = getnameinfo (addr, addrlen, hbuf, sizeof (hbuf), pbuf, sizeof (pbuf),
133                      NI_NUMERICHOST |
134 #ifdef NI_WITHSCOPEID
135                      NI_WITHSCOPEID |
136 #endif
137                      NI_NUMERICSERV);
138   if (ret)
139     return getnameinfo_err (ret);
140
141   if (outlen < strlen (hbuf) + strlen (pbuf) + 2)
142     return SASL_BUFOVER;
143
144   snprintf (out, outlen, "%s;%s", hbuf, pbuf);
145
146   return SASL_OK;
147 }
148 #endif
149
150 /* mutt_sasl_start: called before doing a SASL exchange - initialises library
151  *   (if neccessary). */
152 int mutt_sasl_start (void)
153 {
154   static unsigned char sasl_init = 0;
155
156   static sasl_callback_t callbacks[2];
157   int rc;
158
159   if (sasl_init)
160     return SASL_OK;
161
162   /* set up default logging callback */
163   callbacks[0].id = SASL_CB_LOG;
164   callbacks[0].proc = mutt_sasl_cb_log;
165   callbacks[0].context = NULL;
166
167   callbacks[1].id = SASL_CB_LIST_END;
168   callbacks[1].proc = NULL;
169   callbacks[1].context = NULL;
170
171   rc = sasl_client_init (callbacks);
172
173   if (rc != SASL_OK) {
174     dprint (1,
175             (debugfile, "mutt_sasl_start: libsasl initialisation failed.\n"));
176     return SASL_FAIL;
177   }
178
179   sasl_init = 1;
180
181   return SASL_OK;
182 }
183
184 /* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various
185  * security properties. If this turns out to be fine for POP too we can
186  * probably stop exporting mutt_sasl_get_callbacks(). */
187 int mutt_sasl_client_new (CONNECTION * conn, sasl_conn_t ** saslconn)
188 {
189   sasl_security_properties_t secprops;
190
191 #ifdef USE_SASL2
192   struct sockaddr_storage local, remote;
193   socklen_t size;
194   char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN];
195 #else
196   sasl_external_properties_t extprops;
197 #endif
198   const char *service;
199   int rc;
200
201   if (mutt_sasl_start () != SASL_OK)
202     return -1;
203
204   switch (conn->account.type) {
205   case M_ACCT_TYPE_IMAP:
206     service = "imap";
207     break;
208   case M_ACCT_TYPE_POP:
209     service = "pop";
210     break;
211   default:
212     dprint (1, (debugfile, "mutt_sasl_client_new: account type unset\n"));
213     return -1;
214   }
215
216 #ifdef USE_SASL2
217   size = sizeof (local);
218   if (getsockname (conn->fd, (struct sockaddr *) &local, &size)) {
219     dprint (1,
220             (debugfile,
221              "mutt_sasl_client_new: getsockname for local failed\n"));
222     return -1;
223   }
224   else
225     if (iptostring
226         ((struct sockaddr *) &local, size, iplocalport,
227          IP_PORT_BUFLEN) != SASL_OK) {
228     dprint (1,
229             (debugfile,
230              "mutt_sasl_client_new: iptostring for local failed\n"));
231     return -1;
232   }
233
234   size = sizeof (remote);
235   if (getpeername (conn->fd, (struct sockaddr *) &remote, &size)) {
236     dprint (1,
237             (debugfile,
238              "mutt_sasl_client_new: getsockname for remote failed\n"));
239     return -1;
240   }
241   else
242     if (iptostring
243         ((struct sockaddr *) &remote, size, ipremoteport,
244          IP_PORT_BUFLEN) != SASL_OK) {
245     dprint (1,
246             (debugfile,
247              "mutt_sasl_client_new: iptostring for remote failed\n"));
248     return -1;
249   }
250
251   dprint (1,
252           (debugfile, "local ip: %s, remote ip:%s\n", iplocalport,
253            ipremoteport));
254
255   rc =
256     sasl_client_new (service, conn->account.host, iplocalport, ipremoteport,
257                      mutt_sasl_get_callbacks (&conn->account), 0, saslconn);
258
259 #else
260   rc = sasl_client_new (service, conn->account.host,
261                         mutt_sasl_get_callbacks (&conn->account),
262                         SASL_SECURITY_LAYER, saslconn);
263 #endif
264
265   if (rc != SASL_OK) {
266     dprint (1, (debugfile,
267                 "mutt_sasl_client_new: Error allocating SASL connection\n"));
268     return -1;
269   }
270
271   /*** set sasl IP properties, necessary for use with krb4 ***/
272   /* Do we need to fail if this fails? I would assume having these unset
273    * would just disable KRB4. Who wrote this code? I'm not sure how this
274    * interacts with the NSS code either, since that mucks with the fd. */
275 #ifndef USE_SASL2               /* with SASLv2 this all happens in sasl_client_new */
276   {
277     struct sockaddr_in local, remote;
278     socklen_t size;
279
280     size = sizeof (local);
281     if (getsockname (conn->fd, (struct sockaddr *) &local, &size))
282       return -1;
283
284     size = sizeof (remote);
285     if (getpeername (conn->fd, (struct sockaddr *) &remote, &size))
286       return -1;
287
288 #ifdef SASL_IP_LOCAL
289     if (sasl_setprop (*saslconn, SASL_IP_LOCAL, &local) != SASL_OK) {
290       dprint (1, (debugfile,
291                   "mutt_sasl_client_new: Error setting local IP address\n"));
292       return -1;
293     }
294 #endif
295
296 #ifdef SASL_IP_REMOTE
297     if (sasl_setprop (*saslconn, SASL_IP_REMOTE, &remote) != SASL_OK) {
298       dprint (1, (debugfile,
299                   "mutt_sasl_client_new: Error setting remote IP address\n"));
300       return -1;
301     }
302 #endif
303   }
304 #endif
305
306   /* set security properties. We use NOPLAINTEXT globally, since we can
307    * just fall back to LOGIN in the IMAP case anyway. If that doesn't
308    * work for POP, we can make it a flag or move this code into
309    * imap/auth_sasl.c */
310   memset (&secprops, 0, sizeof (secprops));
311   /* Work around a casting bug in the SASL krb4 module */
312   secprops.max_ssf = 0x7fff;
313   secprops.maxbufsize = M_SASL_MAXBUF;
314   secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
315   if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) {
316     dprint (1, (debugfile,
317                 "mutt_sasl_client_new: Error setting security properties\n"));
318     return -1;
319   }
320
321   /* we currently don't have an SSF finder for NSS (I don't know the API).
322    * If someone does it'd probably be trivial to write mutt_nss_get_ssf().
323    * I have a feeling more SSL code could be shared between those two files,
324    * but I haven't looked into it yet, since I still don't know the APIs. */
325 #if (defined(USE_SSL) || defined(USE_GNUTLS) && !defined(USE_NSS))
326   if (conn->account.flags & M_ACCT_SSL) {
327 #ifdef USE_SASL2                /* I'm not sure this actually has an effect, at least with SASLv2 */
328     dprint (2, (debugfile, "External SSF: %d\n", conn->ssf));
329     if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK)
330 #else
331     memset (&extprops, 0, sizeof (extprops));
332     extprops.ssf = conn->ssf;
333     dprint (2, (debugfile, "External SSF: %d\n", extprops.ssf));
334     if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &extprops) != SASL_OK)
335 #endif
336     {
337       dprint (1,
338               (debugfile,
339                "mutt_sasl_client_new: Error setting external properties\n"));
340       return -1;
341     }
342 #ifdef USE_SASL2
343     dprint (2,
344             (debugfile, "External authentication name: %s\n",
345              conn->account.user));
346     if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) !=
347         SASL_OK) {
348       dprint (1,
349               (debugfile,
350                "mutt_sasl_client_new: Error setting external properties\n"));
351       return -1;
352     }
353 #endif
354   }
355 #endif
356
357   return 0;
358 }
359
360 sasl_callback_t *mutt_sasl_get_callbacks (ACCOUNT * account)
361 {
362   sasl_callback_t *callback;
363
364   callback = mutt_sasl_callbacks;
365
366   callback->id = SASL_CB_AUTHNAME;
367   callback->proc = mutt_sasl_cb_authname;
368   callback->context = account;
369   callback++;
370
371   callback->id = SASL_CB_USER;
372   callback->proc = mutt_sasl_cb_authname;
373   callback->context = account;
374   callback++;
375
376   callback->id = SASL_CB_PASS;
377   callback->proc = mutt_sasl_cb_pass;
378   callback->context = account;
379   callback++;
380
381   callback->id = SASL_CB_GETREALM;
382   callback->proc = NULL;
383   callback->context = NULL;
384   callback++;
385
386   callback->id = SASL_CB_LIST_END;
387   callback->proc = NULL;
388   callback->context = NULL;
389
390   return mutt_sasl_callbacks;
391 }
392
393 int mutt_sasl_interact (sasl_interact_t * interaction)
394 {
395   char prompt[SHORT_STRING];
396   char resp[SHORT_STRING];
397
398   while (interaction->id != SASL_CB_LIST_END) {
399     dprint (2,
400             (debugfile,
401              "mutt_sasl_interact: filling in SASL interaction %ld.\n",
402              interaction->id));
403
404     snprintf (prompt, sizeof (prompt), "%s: ", interaction->prompt);
405     resp[0] = '\0';
406     if (mutt_get_field (prompt, resp, sizeof (resp), 0))
407       return SASL_FAIL;
408
409     interaction->len = mutt_strlen (resp) + 1;
410     interaction->result = safe_malloc (interaction->len);
411     memcpy (interaction->result, resp, interaction->len);
412
413     interaction++;
414   }
415
416   return SASL_OK;
417 }
418
419 /* SASL can stack a protection layer on top of an existing connection.
420  * To handle this, we store a saslconn_t in conn->sockdata, and write
421  * wrappers which en/decode the read/write stream, then replace sockdata
422  * with an embedded copy of the old sockdata and call the underlying
423  * functions (which we've also preserved). I thought about trying to make
424  * a general stackable connection system, but it seemed like overkill -
425  * something is wrong if we have 15 filters on top of a socket. Anyway,
426  * anything else which wishes to stack can use the same method. The only
427  * disadvantage is we have to write wrappers for all the socket methods,
428  * even if we only stack over read and write. Thinking about it, the
429  * abstraction problem is that there is more in CONNECTION than there
430  * needs to be. Ideally it would have only (void*)data and methods. */
431
432 /* mutt_sasl_setup_conn: replace connection methods, sockdata with 
433  *   SASL wrappers, for protection layers. Also get ssf, as a fastpath
434  *   for the read/write methods. */
435 void mutt_sasl_setup_conn (CONNECTION * conn, sasl_conn_t * saslconn)
436 {
437   SASL_DATA *sasldata = (SASL_DATA *) safe_malloc (sizeof (SASL_DATA));
438
439   sasldata->saslconn = saslconn;
440   /* get ssf so we know whether we have to (en|de)code read/write */
441 #ifdef USE_SASL2
442   sasl_getprop (saslconn, SASL_SSF, (const void **) &sasldata->ssf);
443 #else
444   sasl_getprop (saslconn, SASL_SSF, (void **) &sasldata->ssf);
445 #endif
446   dprint (3, (debugfile, "SASL protection strength: %u\n", *sasldata->ssf));
447   /* Add SASL SSF to transport SSF */
448   conn->ssf += *sasldata->ssf;
449 #ifdef USE_SASL2
450   sasl_getprop (saslconn, SASL_MAXOUTBUF,
451                 (const void **) &sasldata->pbufsize);
452 #else
453   sasl_getprop (saslconn, SASL_MAXOUTBUF, (void **) &sasldata->pbufsize);
454 #endif
455   dprint (3,
456           (debugfile, "SASL protection buffer size: %u\n",
457            *sasldata->pbufsize));
458
459   /* clear input buffer */
460   sasldata->buf = NULL;
461   sasldata->bpos = 0;
462   sasldata->blen = 0;
463
464   /* preserve old functions */
465   sasldata->sockdata = conn->sockdata;
466   sasldata->msasl_open = conn->conn_open;
467   sasldata->msasl_close = conn->conn_close;
468   sasldata->msasl_read = conn->conn_read;
469   sasldata->msasl_write = conn->conn_write;
470
471   /* and set up new functions */
472   conn->sockdata = sasldata;
473   conn->conn_open = mutt_sasl_conn_open;
474   conn->conn_close = mutt_sasl_conn_close;
475   conn->conn_read = mutt_sasl_conn_read;
476   conn->conn_write = mutt_sasl_conn_write;
477 }
478
479 /* mutt_sasl_cb_log: callback to log SASL messages */
480 static int mutt_sasl_cb_log (void *context, int priority, const char *message)
481 {
482   dprint (priority, (debugfile, "SASL: %s\n", message));
483
484   return SASL_OK;
485 }
486
487 /* mutt_sasl_cb_authname: callback to retrieve authname or user (mutt
488  *   doesn't distinguish, even if some SASL plugins do) from ACCOUNT */
489 static int mutt_sasl_cb_authname (void *context, int id, const char **result,
490                                   unsigned *len)
491 {
492   ACCOUNT *account = (ACCOUNT *) context;
493
494   *result = NULL;
495   if (len)
496     *len = 0;
497
498   if (!account)
499     return SASL_BADPARAM;
500
501   dprint (2, (debugfile, "mutt_sasl_cb_authname: getting %s for %s:%u\n",
502               id == SASL_CB_AUTHNAME ? "authname" : "user",
503               account->host, account->port));
504
505   if (mutt_account_getuser (account))
506     return SASL_FAIL;
507
508   *result = account->user;
509
510   if (len)
511     *len = strlen (*result);
512
513   return SASL_OK;
514 }
515
516 static int mutt_sasl_cb_pass (sasl_conn_t * conn, void *context, int id,
517                               sasl_secret_t ** psecret)
518 {
519   ACCOUNT *account = (ACCOUNT *) context;
520   int len;
521
522   if (!account || !psecret)
523     return SASL_BADPARAM;
524
525   dprint (2, (debugfile,
526               "mutt_sasl_cb_pass: getting password for %s@%s:%u\n",
527               account->user, account->host, account->port));
528
529   if (mutt_account_getpass (account))
530     return SASL_FAIL;
531
532   len = strlen (account->pass);
533
534   *psecret = (sasl_secret_t *) safe_malloc (sizeof (sasl_secret_t) + len);
535   (*psecret)->len = len;
536   strcpy ((*psecret)->data, account->pass);     /* __STRCPY_CHECKED__ */
537
538   return SASL_OK;
539 }
540
541 /* mutt_sasl_conn_open: empty wrapper for underlying open function. We
542  *   don't know in advance that a connection will use SASL, so we
543  *   replace conn's methods with sasl methods when authentication
544  *   is successful, using mutt_sasl_setup_conn */
545 static int mutt_sasl_conn_open (CONNECTION * conn)
546 {
547   SASL_DATA *sasldata;
548   int rc;
549
550   sasldata = (SASL_DATA *) conn->sockdata;
551   conn->sockdata = sasldata->sockdata;
552   rc = (sasldata->msasl_open) (conn);
553   conn->sockdata = sasldata;
554
555   return rc;
556 }
557
558 /* mutt_sasl_conn_close: calls underlying close function and disposes of
559  *   the sasl_conn_t object, then restores connection to pre-sasl state */
560 static int mutt_sasl_conn_close (CONNECTION * conn)
561 {
562   SASL_DATA *sasldata;
563   int rc;
564
565   sasldata = (SASL_DATA *) conn->sockdata;
566
567   /* restore connection's underlying methods */
568   conn->sockdata = sasldata->sockdata;
569   conn->conn_open = sasldata->msasl_open;
570   conn->conn_close = sasldata->msasl_close;
571   conn->conn_read = sasldata->msasl_read;
572   conn->conn_write = sasldata->msasl_write;
573
574   /* release sasl resources */
575   sasl_dispose (&sasldata->saslconn);
576 #ifndef USE_SASL2
577   FREE (&sasldata->buf);
578 #endif
579   FREE (&sasldata);
580
581   /* call underlying close */
582   rc = (conn->conn_close) (conn);
583
584   return rc;
585 }
586
587 static int mutt_sasl_conn_read (CONNECTION * conn, char *buf, size_t len)
588 {
589   SASL_DATA *sasldata;
590   int rc;
591
592   unsigned int olen;
593
594   sasldata = (SASL_DATA *) conn->sockdata;
595
596   /* if we still have data in our read buffer, copy it into buf */
597   if (sasldata->blen > sasldata->bpos) {
598     olen = (sasldata->blen - sasldata->bpos > len) ? len :
599       sasldata->blen - sasldata->bpos;
600
601     memcpy (buf, sasldata->buf + sasldata->bpos, olen);
602     sasldata->bpos += olen;
603
604     return olen;
605   }
606
607   conn->sockdata = sasldata->sockdata;
608
609 #ifndef USE_SASL2
610   FREE (&sasldata->buf);
611 #endif
612   sasldata->bpos = 0;
613   sasldata->blen = 0;
614
615   /* and decode the result, if necessary */
616   if (*sasldata->ssf) {
617     do {
618       /* call the underlying read function to fill the buffer */
619       rc = (sasldata->msasl_read) (conn, buf, len);
620       if (rc <= 0)
621         goto out;
622
623       rc = sasl_decode (sasldata->saslconn, buf, rc, &sasldata->buf,
624                         &sasldata->blen);
625       if (rc != SASL_OK) {
626         dprint (1, (debugfile, "SASL decode failed: %s\n",
627                     sasl_errstring (rc, NULL, NULL)));
628         goto out;
629       }
630     }
631     while (!sasldata->blen);
632
633     olen = (sasldata->blen - sasldata->bpos > len) ? len :
634       sasldata->blen - sasldata->bpos;
635
636     memcpy (buf, sasldata->buf, olen);
637     sasldata->bpos += olen;
638
639     rc = olen;
640   }
641   else
642     rc = (sasldata->msasl_read) (conn, buf, len);
643
644 out:
645   conn->sockdata = sasldata;
646
647   return rc;
648 }
649
650 static int mutt_sasl_conn_write (CONNECTION * conn, const char *buf,
651                                  size_t len)
652 {
653   SASL_DATA *sasldata;
654   int rc;
655
656 #ifdef USE_SASL2
657   const char *pbuf;
658 #else
659   char *pbuf;
660 #endif
661   unsigned int olen, plen;
662
663   sasldata = (SASL_DATA *) conn->sockdata;
664   conn->sockdata = sasldata->sockdata;
665
666   /* encode data, if necessary */
667   if (*sasldata->ssf) {
668     /* handle data larger than MAXOUTBUF */
669     do {
670       olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len;
671
672       rc = sasl_encode (sasldata->saslconn, buf, olen, &pbuf, &plen);
673       if (rc != SASL_OK) {
674         dprint (1, (debugfile, "SASL encoding failed: %s\n",
675                     sasl_errstring (rc, NULL, NULL)));
676         goto fail;
677       }
678
679       rc = (sasldata->msasl_write) (conn, pbuf, plen);
680 #ifndef USE_SASL2
681       FREE (&pbuf);
682 #endif
683       if (rc != plen)
684         goto fail;
685
686       len -= olen;
687       buf += olen;
688     }
689     while (len > *sasldata->pbufsize);
690   }
691   else
692     /* just write using the underlying socket function */
693     rc = (sasldata->msasl_write) (conn, buf, len);
694
695   conn->sockdata = sasldata;
696
697   return rc;
698
699 fail:
700   conn->sockdata = sasldata;
701   return -1;
702 }