2 * Copyright (C) 2000-3 Brendan Cully <brendan@kublai.com>
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.
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.
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.
19 /* common SASL helper routines */
27 #include "mutt_sasl.h"
28 #include "mutt_socket.h"
33 #include <sasl/sasl.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
41 static int getnameinfo_err(int ret)
44 dprint (1, (debugfile, "getnameinfo: "));
48 dprint (1, (debugfile, "The name could not be resolved at this time. Future attempts may succeed.\n"));
52 dprint (1, (debugfile, "The flags had an invalid value.\n"));
56 dprint (1, (debugfile, "A non-recoverable error occurred.\n"));
60 dprint (1, (debugfile, "The address family was not recognized or the address length was invalid for the specified family.\n"));
64 dprint (1, (debugfile, "There was a memory allocation failure.\n"));
68 dprint (1, (debugfile, "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"));
69 err=SASL_FAIL; /* no real equivalent */
72 dprint (1, (debugfile, "A system error occurred. The error code can be found in errno(%d,%s)).\n",errno,strerror(errno)));
73 err=SASL_FAIL; /* no real equivalent */
76 dprint (1, (debugfile, "Unknown error %d\n",ret));
77 err=SASL_FAIL; /* no real equivalent */
84 /* arbitrary. SASL will probably use a smaller buffer anyway. OTOH it's
85 * been a while since I've had access to an SASL server which negotiated
86 * a protection buffer. */
87 #define M_SASL_MAXBUF 65536
90 #define IP_PORT_BUFLEN 1024
93 static sasl_callback_t mutt_sasl_callbacks[5];
95 static int mutt_sasl_start (void);
98 static int mutt_sasl_cb_log (void* context, int priority, const char* message);
99 static int mutt_sasl_cb_authname (void* context, int id, const char** result,
101 static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
102 sasl_secret_t** psecret);
104 /* socket wrappers for a SASL security layer */
105 static int mutt_sasl_conn_open (CONNECTION* conn);
106 static int mutt_sasl_conn_close (CONNECTION* conn);
107 static int mutt_sasl_conn_read (CONNECTION* conn, char* buf, size_t len);
108 static int mutt_sasl_conn_write (CONNECTION* conn, const char* buf,
112 /* utility function, stolen from sasl2 sample code */
113 static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
114 char *out, unsigned outlen) {
115 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
118 if(!addr || !out) return SASL_BADPARAM;
120 ret=getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
122 #ifdef NI_WITHSCOPEID
127 return getnameinfo_err(ret);
129 if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
132 snprintf(out, outlen, "%s;%s", hbuf, pbuf);
138 /* mutt_sasl_start: called before doing a SASL exchange - initialises library
139 * (if neccessary). */
140 int mutt_sasl_start (void)
142 static unsigned char sasl_init = 0;
144 static sasl_callback_t callbacks[2];
150 /* set up default logging callback */
151 callbacks[0].id = SASL_CB_LOG;
152 callbacks[0].proc = mutt_sasl_cb_log;
153 callbacks[0].context = NULL;
155 callbacks[1].id = SASL_CB_LIST_END;
156 callbacks[1].proc = NULL;
157 callbacks[1].context = NULL;
159 rc = sasl_client_init (callbacks);
163 dprint (1, (debugfile, "mutt_sasl_start: libsasl initialisation failed.\n"));
172 /* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various
173 * security properties. If this turns out to be fine for POP too we can
174 * probably stop exporting mutt_sasl_get_callbacks(). */
175 int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
177 sasl_security_properties_t secprops;
179 struct sockaddr_storage local, remote;
181 char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN];
183 sasl_external_properties_t extprops;
188 if (mutt_sasl_start () != SASL_OK)
191 switch (conn->account.type)
193 case M_ACCT_TYPE_IMAP:
196 case M_ACCT_TYPE_POP:
200 dprint (1, (debugfile, "mutt_sasl_client_new: account type unset\n"));
205 size = sizeof (local);
206 if (getsockname (conn->fd, (struct sockaddr *)&local, &size)){
207 dprint (1, (debugfile, "mutt_sasl_client_new: getsockname for local failed\n"));
211 if (iptostring((struct sockaddr *)&local, size, iplocalport, IP_PORT_BUFLEN) != SASL_OK){
212 dprint (1, (debugfile, "mutt_sasl_client_new: iptostring for local failed\n"));
216 size = sizeof (remote);
217 if (getpeername (conn->fd, (struct sockaddr *)&remote, &size)){
218 dprint (1, (debugfile, "mutt_sasl_client_new: getsockname for remote failed\n"));
222 if (iptostring((struct sockaddr *)&remote, size, ipremoteport, IP_PORT_BUFLEN) != SASL_OK){
223 dprint (1, (debugfile, "mutt_sasl_client_new: iptostring for remote failed\n"));
227 dprint(1,(debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport));
229 rc = sasl_client_new (service, conn->account.host, iplocalport, ipremoteport,
230 mutt_sasl_get_callbacks (&conn->account), 0, saslconn);
233 rc = sasl_client_new (service, conn->account.host,
234 mutt_sasl_get_callbacks (&conn->account), SASL_SECURITY_LAYER, saslconn);
239 dprint (1, (debugfile,
240 "mutt_sasl_client_new: Error allocating SASL connection\n"));
244 /*** set sasl IP properties, necessary for use with krb4 ***/
245 /* Do we need to fail if this fails? I would assume having these unset
246 * would just disable KRB4. Who wrote this code? I'm not sure how this
247 * interacts with the NSS code either, since that mucks with the fd. */
248 #ifndef USE_SASL2 /* with SASLv2 this all happens in sasl_client_new */
250 struct sockaddr_in local, remote;
253 size = sizeof (local);
254 if (getsockname (conn->fd, (struct sockaddr*) &local, &size))
257 size = sizeof(remote);
258 if (getpeername(conn->fd, (struct sockaddr*) &remote, &size))
262 if (sasl_setprop(*saslconn, SASL_IP_LOCAL, &local) != SASL_OK)
264 dprint (1, (debugfile,
265 "mutt_sasl_client_new: Error setting local IP address\n"));
270 #ifdef SASL_IP_REMOTE
271 if (sasl_setprop(*saslconn, SASL_IP_REMOTE, &remote) != SASL_OK)
273 dprint (1, (debugfile,
274 "mutt_sasl_client_new: Error setting remote IP address\n"));
281 /* set security properties. We use NOPLAINTEXT globally, since we can
282 * just fall back to LOGIN in the IMAP case anyway. If that doesn't
283 * work for POP, we can make it a flag or move this code into
284 * imap/auth_sasl.c */
285 memset (&secprops, 0, sizeof (secprops));
286 /* Work around a casting bug in the SASL krb4 module */
287 secprops.max_ssf = 0x7fff;
288 secprops.maxbufsize = M_SASL_MAXBUF;
289 secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
290 if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK)
292 dprint (1, (debugfile,
293 "mutt_sasl_client_new: Error setting security properties\n"));
297 /* we currently don't have an SSF finder for NSS (I don't know the API).
298 * If someone does it'd probably be trivial to write mutt_nss_get_ssf().
299 * I have a feeling more SSL code could be shared between those two files,
300 * but I haven't looked into it yet, since I still don't know the APIs. */
301 #if (defined(USE_SSL) || defined(USE_GNUTLS) && !defined(USE_NSS))
302 if (conn->account.flags & M_ACCT_SSL)
304 #ifdef USE_SASL2 /* I'm not sure this actually has an effect, at least with SASLv2 */
305 dprint (2, (debugfile, "External SSF: %d\n", conn->ssf));
306 if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK)
308 memset (&extprops, 0, sizeof (extprops));
309 extprops.ssf = conn->ssf;
310 dprint (2, (debugfile, "External SSF: %d\n", extprops.ssf));
311 if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &extprops) != SASL_OK)
314 dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n"));
318 dprint (2, (debugfile, "External authentication name: %s\n", conn->account.user));
319 if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK)
321 dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n"));
331 sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account)
333 sasl_callback_t* callback;
335 callback = mutt_sasl_callbacks;
337 callback->id = SASL_CB_AUTHNAME;
338 callback->proc = mutt_sasl_cb_authname;
339 callback->context = account;
342 callback->id = SASL_CB_USER;
343 callback->proc = mutt_sasl_cb_authname;
344 callback->context = account;
347 callback->id = SASL_CB_PASS;
348 callback->proc = mutt_sasl_cb_pass;
349 callback->context = account;
352 callback->id = SASL_CB_GETREALM;
353 callback->proc = NULL;
354 callback->context = NULL;
357 callback->id = SASL_CB_LIST_END;
358 callback->proc = NULL;
359 callback->context = NULL;
361 return mutt_sasl_callbacks;
364 int mutt_sasl_interact (sasl_interact_t* interaction)
366 char prompt[SHORT_STRING];
367 char resp[SHORT_STRING];
369 while (interaction->id != SASL_CB_LIST_END)
371 dprint (2, (debugfile, "mutt_sasl_interact: filling in SASL interaction %ld.\n", interaction->id));
373 snprintf (prompt, sizeof (prompt), "%s: ", interaction->prompt);
375 if (mutt_get_field (prompt, resp, sizeof (resp), 0))
378 interaction->len = mutt_strlen (resp)+1;
379 interaction->result = safe_malloc (interaction->len);
380 memcpy (interaction->result, resp, interaction->len);
388 /* SASL can stack a protection layer on top of an existing connection.
389 * To handle this, we store a saslconn_t in conn->sockdata, and write
390 * wrappers which en/decode the read/write stream, then replace sockdata
391 * with an embedded copy of the old sockdata and call the underlying
392 * functions (which we've also preserved). I thought about trying to make
393 * a general stackable connection system, but it seemed like overkill -
394 * something is wrong if we have 15 filters on top of a socket. Anyway,
395 * anything else which wishes to stack can use the same method. The only
396 * disadvantage is we have to write wrappers for all the socket methods,
397 * even if we only stack over read and write. Thinking about it, the
398 * abstraction problem is that there is more in CONNECTION than there
399 * needs to be. Ideally it would have only (void*)data and methods. */
401 /* mutt_sasl_setup_conn: replace connection methods, sockdata with
402 * SASL wrappers, for protection layers. Also get ssf, as a fastpath
403 * for the read/write methods. */
404 void mutt_sasl_setup_conn (CONNECTION* conn, sasl_conn_t* saslconn)
406 SASL_DATA* sasldata = (SASL_DATA*) safe_malloc (sizeof (SASL_DATA));
408 sasldata->saslconn = saslconn;
409 /* get ssf so we know whether we have to (en|de)code read/write */
411 sasl_getprop (saslconn, SASL_SSF, (const void**) &sasldata->ssf);
413 sasl_getprop (saslconn, SASL_SSF, (void**) &sasldata->ssf);
415 dprint (3, (debugfile, "SASL protection strength: %u\n", *sasldata->ssf));
416 /* Add SASL SSF to transport SSF */
417 conn->ssf += *sasldata->ssf;
419 sasl_getprop (saslconn, SASL_MAXOUTBUF, (const void**) &sasldata->pbufsize);
421 sasl_getprop (saslconn, SASL_MAXOUTBUF, (void**) &sasldata->pbufsize);
423 dprint (3, (debugfile, "SASL protection buffer size: %u\n", *sasldata->pbufsize));
425 /* clear input buffer */
426 sasldata->buf = NULL;
430 /* preserve old functions */
431 sasldata->sockdata = conn->sockdata;
432 sasldata->open = conn->open;
433 sasldata->close = conn->close;
434 sasldata->read = conn->read;
435 sasldata->write = conn->write;
437 /* and set up new functions */
438 conn->sockdata = sasldata;
439 conn->open = mutt_sasl_conn_open;
440 conn->close = mutt_sasl_conn_close;
441 conn->read = mutt_sasl_conn_read;
442 conn->write = mutt_sasl_conn_write;
445 /* mutt_sasl_cb_log: callback to log SASL messages */
446 static int mutt_sasl_cb_log (void* context, int priority, const char* message)
448 dprint (priority, (debugfile, "SASL: %s\n", message));
453 /* mutt_sasl_cb_authname: callback to retrieve authname or user (mutt
454 * doesn't distinguish, even if some SASL plugins do) from ACCOUNT */
455 static int mutt_sasl_cb_authname (void* context, int id, const char** result,
458 ACCOUNT* account = (ACCOUNT*) context;
465 return SASL_BADPARAM;
467 dprint (2, (debugfile, "mutt_sasl_cb_authname: getting %s for %s:%u\n",
468 id == SASL_CB_AUTHNAME ? "authname" : "user",
469 account->host, account->port));
471 if (mutt_account_getuser (account))
474 *result = account->user;
477 *len = strlen (*result);
482 static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
483 sasl_secret_t** psecret)
485 ACCOUNT* account = (ACCOUNT*) context;
488 if (!account || !psecret)
489 return SASL_BADPARAM;
491 dprint (2, (debugfile,
492 "mutt_sasl_cb_pass: getting password for %s@%s:%u\n", account->user,
493 account->host, account->port));
495 if (mutt_account_getpass (account))
498 len = strlen (account->pass);
500 *psecret = (sasl_secret_t*) safe_malloc (sizeof (sasl_secret_t) + len);
501 (*psecret)->len = len;
502 strcpy ((*psecret)->data, account->pass); /* __STRCPY_CHECKED__ */
507 /* mutt_sasl_conn_open: empty wrapper for underlying open function. We
508 * don't know in advance that a connection will use SASL, so we
509 * replace conn's methods with sasl methods when authentication
510 * is successful, using mutt_sasl_setup_conn */
511 static int mutt_sasl_conn_open (CONNECTION* conn)
516 sasldata = (SASL_DATA*) conn->sockdata;
517 conn->sockdata = sasldata->sockdata;
518 rc = (sasldata->open) (conn);
519 conn->sockdata = sasldata;
524 /* mutt_sasl_conn_close: calls underlying close function and disposes of
525 * the sasl_conn_t object, then restores connection to pre-sasl state */
526 static int mutt_sasl_conn_close (CONNECTION* conn)
531 sasldata = (SASL_DATA*) conn->sockdata;
533 /* restore connection's underlying methods */
534 conn->sockdata = sasldata->sockdata;
535 conn->open = sasldata->open;
536 conn->close = sasldata->close;
537 conn->read = sasldata->read;
538 conn->write = sasldata->write;
540 /* release sasl resources */
541 sasl_dispose (&sasldata->saslconn);
543 FREE (&sasldata->buf);
547 /* call underlying close */
548 rc = (conn->close) (conn);
553 static int mutt_sasl_conn_read (CONNECTION* conn, char* buf, size_t len)
560 sasldata = (SASL_DATA*) conn->sockdata;
562 /* if we still have data in our read buffer, copy it into buf */
563 if (sasldata->blen > sasldata->bpos)
565 olen = (sasldata->blen - sasldata->bpos > len) ? len :
566 sasldata->blen - sasldata->bpos;
568 memcpy (buf, sasldata->buf+sasldata->bpos, olen);
569 sasldata->bpos += olen;
574 conn->sockdata = sasldata->sockdata;
577 FREE (&sasldata->buf);
582 /* and decode the result, if necessary */
587 /* call the underlying read function to fill the buffer */
588 rc = (sasldata->read) (conn, buf, len);
592 rc = sasl_decode (sasldata->saslconn, buf, rc, &sasldata->buf,
596 dprint (1, (debugfile, "SASL decode failed: %s\n",
597 sasl_errstring (rc, NULL, NULL)));
601 while (!sasldata->blen);
603 olen = (sasldata->blen - sasldata->bpos > len) ? len :
604 sasldata->blen - sasldata->bpos;
606 memcpy (buf, sasldata->buf, olen);
607 sasldata->bpos += olen;
612 rc = (sasldata->read) (conn, buf, len);
615 conn->sockdata = sasldata;
620 static int mutt_sasl_conn_write (CONNECTION* conn, const char* buf,
631 unsigned int olen, plen;
633 sasldata = (SASL_DATA*) conn->sockdata;
634 conn->sockdata = sasldata->sockdata;
636 /* encode data, if necessary */
639 /* handle data larger than MAXOUTBUF */
642 olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len;
644 rc = sasl_encode (sasldata->saslconn, buf, olen, &pbuf, &plen);
647 dprint (1, (debugfile, "SASL encoding failed: %s\n",
648 sasl_errstring (rc, NULL, NULL)));
652 rc = (sasldata->write) (conn, pbuf, plen);
662 while (len > *sasldata->pbufsize);
665 /* just write using the underlying socket function */
666 rc = (sasldata->write) (conn, buf, len);
668 conn->sockdata = sasldata;
673 conn->sockdata = sasldata;