Nico Golde:
[apps/madmutt.git] / mutt_sasl.c
1 /*
2  * Copyright (C) 2000-3 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   dprint (1, (debugfile, "getnameinfo: "));
45   switch(ret)
46   {
47      case EAI_AGAIN:
48        dprint (1, (debugfile, "The name could not be resolved at this time.  Future attempts may succeed.\n"));
49        err=SASL_TRYAGAIN;
50        break;
51      case EAI_BADFLAGS:
52        dprint (1, (debugfile, "The flags had an invalid value.\n"));
53        err=SASL_BADPARAM;
54        break;
55      case EAI_FAIL:
56        dprint (1, (debugfile, "A non-recoverable error occurred.\n"));
57        err=SASL_FAIL;
58        break;
59      case EAI_FAMILY:
60        dprint (1, (debugfile, "The address family was not recognized or the address length was invalid for the specified family.\n"));
61        err=SASL_BADPROT;
62        break;
63      case EAI_MEMORY:
64        dprint (1, (debugfile, "There was a memory allocation failure.\n"));
65        err=SASL_NOMEM;
66        break;
67      case EAI_NONAME:
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 */
70        break;
71      case EAI_SYSTEM:
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 */
74        break;
75      default:
76        dprint (1, (debugfile, "Unknown error %d\n",ret));
77        err=SASL_FAIL; /* no real equivalent */
78        break;
79   }
80   return err;
81 }
82 #endif
83
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
88
89 #ifdef USE_SASL2
90 #define IP_PORT_BUFLEN 1024
91 #endif
92
93 static sasl_callback_t mutt_sasl_callbacks[5];
94
95 static int mutt_sasl_start (void);
96
97 /* callbacks */
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,
100   unsigned int* len);
101 static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
102   sasl_secret_t** psecret);
103
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,
109   size_t count);
110
111 #ifdef USE_SASL2
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];
116     int ret;
117     
118     if(!addr || !out) return SASL_BADPARAM;
119
120     ret=getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
121                    NI_NUMERICHOST |
122 #ifdef NI_WITHSCOPEID
123                    NI_WITHSCOPEID |
124 #endif
125                    NI_NUMERICSERV);
126     if(ret)
127       return getnameinfo_err(ret);
128
129     if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
130         return SASL_BUFOVER;
131
132     snprintf(out, outlen, "%s;%s", hbuf, pbuf);
133
134     return SASL_OK;
135 }
136 #endif
137
138 /* mutt_sasl_start: called before doing a SASL exchange - initialises library
139  *   (if neccessary). */
140 int mutt_sasl_start (void)
141 {
142   static unsigned char sasl_init = 0;
143
144   static sasl_callback_t callbacks[2];
145   int rc;
146
147   if (sasl_init)
148     return SASL_OK;
149
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;
154
155   callbacks[1].id = SASL_CB_LIST_END;
156   callbacks[1].proc = NULL;
157   callbacks[1].context = NULL;
158
159   rc = sasl_client_init (callbacks);
160
161   if (rc != SASL_OK)
162   {
163     dprint (1, (debugfile, "mutt_sasl_start: libsasl initialisation failed.\n"));
164     return SASL_FAIL;
165   }
166
167   sasl_init = 1;
168
169   return SASL_OK;
170 }
171
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)
176 {
177   sasl_security_properties_t secprops;
178 #ifdef USE_SASL2
179   struct sockaddr_storage local, remote;
180   socklen_t size;
181   char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN];
182 #else
183   sasl_external_properties_t extprops;
184 #endif
185   const char* service;
186   int rc;
187
188   if (mutt_sasl_start () != SASL_OK)
189     return -1;
190
191   switch (conn->account.type)
192   {
193     case M_ACCT_TYPE_IMAP:
194       service = "imap";
195       break;
196     case M_ACCT_TYPE_POP:
197       service = "pop";
198       break;
199     default:
200       dprint (1, (debugfile, "mutt_sasl_client_new: account type unset\n"));
201       return -1;
202   }
203
204 #ifdef USE_SASL2
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"));
208     return -1;
209   }
210   else 
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"));
213     return -1;
214   }
215   
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"));
219     return -1;
220   }
221   else 
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"));
224     return -1;
225   }
226
227 dprint(1,(debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport));
228   
229   rc = sasl_client_new (service, conn->account.host, iplocalport, ipremoteport,
230     mutt_sasl_get_callbacks (&conn->account), 0, saslconn);
231
232 #else
233   rc = sasl_client_new (service, conn->account.host,
234     mutt_sasl_get_callbacks (&conn->account), SASL_SECURITY_LAYER, saslconn);
235 #endif
236
237   if (rc != SASL_OK)
238   {
239     dprint (1, (debugfile,
240       "mutt_sasl_client_new: Error allocating SASL connection\n"));
241     return -1;
242   }
243
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 */
249   {
250     struct sockaddr_in local, remote;
251     socklen_t size;
252
253     size = sizeof (local);
254     if (getsockname (conn->fd, (struct sockaddr*) &local, &size))
255       return -1;
256
257     size = sizeof(remote);
258     if (getpeername(conn->fd, (struct sockaddr*) &remote, &size))
259       return -1;
260
261 #ifdef SASL_IP_LOCAL
262     if (sasl_setprop(*saslconn, SASL_IP_LOCAL, &local) != SASL_OK)
263     {
264       dprint (1, (debugfile,
265         "mutt_sasl_client_new: Error setting local IP address\n"));
266       return -1;
267     }
268 #endif
269
270 #ifdef SASL_IP_REMOTE
271     if (sasl_setprop(*saslconn, SASL_IP_REMOTE, &remote) != SASL_OK)
272     {
273       dprint (1, (debugfile,
274         "mutt_sasl_client_new: Error setting remote IP address\n"));
275       return -1;
276     }
277 #endif
278   }
279 #endif
280
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)
291   {
292     dprint (1, (debugfile,
293       "mutt_sasl_client_new: Error setting security properties\n"));
294     return -1;
295   }
296
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)
303   {
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)
307 #else
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)
312 #endif
313     {
314       dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n"));
315       return -1;
316     }
317 #ifdef USE_SASL2
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)
320      {
321       dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n"));
322       return -1;
323     }
324 #endif
325   }
326 #endif
327
328   return 0;
329 }
330
331 sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account)
332 {
333   sasl_callback_t* callback;
334
335   callback = mutt_sasl_callbacks;
336
337   callback->id = SASL_CB_AUTHNAME;
338   callback->proc = mutt_sasl_cb_authname;
339   callback->context = account;
340   callback++;
341
342   callback->id = SASL_CB_USER;
343   callback->proc = mutt_sasl_cb_authname;
344   callback->context = account;
345   callback++;
346
347   callback->id = SASL_CB_PASS;
348   callback->proc = mutt_sasl_cb_pass;
349   callback->context = account;
350   callback++;
351
352   callback->id = SASL_CB_GETREALM;
353   callback->proc = NULL;
354   callback->context = NULL;
355   callback++;
356
357   callback->id = SASL_CB_LIST_END;
358   callback->proc = NULL;
359   callback->context = NULL;
360
361   return mutt_sasl_callbacks;
362 }
363
364 int mutt_sasl_interact (sasl_interact_t* interaction)
365 {
366   char prompt[SHORT_STRING];
367   char resp[SHORT_STRING];
368
369   while (interaction->id != SASL_CB_LIST_END)
370   {
371     dprint (2, (debugfile, "mutt_sasl_interact: filling in SASL interaction %ld.\n", interaction->id));
372
373     snprintf (prompt, sizeof (prompt), "%s: ", interaction->prompt);
374     resp[0] = '\0';
375     if (mutt_get_field (prompt, resp, sizeof (resp), 0))
376       return SASL_FAIL;
377
378     interaction->len = mutt_strlen (resp)+1;
379     interaction->result = safe_malloc (interaction->len);
380     memcpy (interaction->result, resp, interaction->len);
381
382     interaction++;
383   }
384
385   return SASL_OK;
386 }
387
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. */
400
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)
405 {
406   SASL_DATA* sasldata = (SASL_DATA*) safe_malloc (sizeof (SASL_DATA));
407
408   sasldata->saslconn = saslconn;
409   /* get ssf so we know whether we have to (en|de)code read/write */
410 #ifdef USE_SASL2
411   sasl_getprop (saslconn, SASL_SSF, (const void**) &sasldata->ssf);
412 #else
413   sasl_getprop (saslconn, SASL_SSF, (void**) &sasldata->ssf);
414 #endif
415   dprint (3, (debugfile, "SASL protection strength: %u\n", *sasldata->ssf));
416   /* Add SASL SSF to transport SSF */
417   conn->ssf += *sasldata->ssf;
418 #ifdef USE_SASL2
419   sasl_getprop (saslconn, SASL_MAXOUTBUF, (const void**) &sasldata->pbufsize);
420 #else
421   sasl_getprop (saslconn, SASL_MAXOUTBUF, (void**) &sasldata->pbufsize);
422 #endif
423   dprint (3, (debugfile, "SASL protection buffer size: %u\n", *sasldata->pbufsize));
424
425   /* clear input buffer */
426   sasldata->buf = NULL;
427   sasldata->bpos = 0;
428   sasldata->blen = 0;
429
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;
436
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;
443 }
444
445 /* mutt_sasl_cb_log: callback to log SASL messages */
446 static int mutt_sasl_cb_log (void* context, int priority, const char* message)
447 {
448   dprint (priority, (debugfile, "SASL: %s\n", message));
449
450   return SASL_OK;
451 }
452
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,
456   unsigned* len)
457 {
458   ACCOUNT* account = (ACCOUNT*) context;
459
460   *result = NULL;
461   if (len)
462     *len = 0;
463
464   if (!account)
465     return SASL_BADPARAM;
466
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));
470
471   if (mutt_account_getuser (account))
472     return SASL_FAIL;
473
474   *result = account->user;
475
476   if (len)
477     *len = strlen (*result);
478
479   return SASL_OK;
480 }
481
482 static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
483   sasl_secret_t** psecret)
484 {
485   ACCOUNT* account = (ACCOUNT*) context;
486   int len;
487
488   if (!account || !psecret)
489     return SASL_BADPARAM;
490
491   dprint (2, (debugfile,
492     "mutt_sasl_cb_pass: getting password for %s@%s:%u\n", account->user,
493     account->host, account->port));
494
495   if (mutt_account_getpass (account))
496     return SASL_FAIL;
497
498   len = strlen (account->pass);
499
500   *psecret = (sasl_secret_t*) safe_malloc (sizeof (sasl_secret_t) + len);
501   (*psecret)->len = len;
502   strcpy ((*psecret)->data, account->pass);     /* __STRCPY_CHECKED__ */
503
504   return SASL_OK;
505 }
506
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)
512 {
513   SASL_DATA* sasldata;
514   int rc;
515
516   sasldata = (SASL_DATA*) conn->sockdata;
517   conn->sockdata = sasldata->sockdata;
518   rc = (sasldata->open) (conn);
519   conn->sockdata = sasldata;
520
521   return rc;
522 }
523
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)
527 {
528   SASL_DATA* sasldata;
529   int rc;
530
531   sasldata = (SASL_DATA*) conn->sockdata;
532
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;
539
540   /* release sasl resources */
541   sasl_dispose (&sasldata->saslconn);
542 #ifndef USE_SASL2
543   FREE (&sasldata->buf);
544 #endif
545   FREE (&sasldata);
546
547   /* call underlying close */
548   rc = (conn->close) (conn);
549
550   return rc;
551 }
552
553 static int mutt_sasl_conn_read (CONNECTION* conn, char* buf, size_t len)
554 {
555   SASL_DATA* sasldata;
556   int rc;
557
558   unsigned int olen;
559
560   sasldata = (SASL_DATA*) conn->sockdata;
561
562   /* if we still have data in our read buffer, copy it into buf */
563   if (sasldata->blen > sasldata->bpos)
564   {
565     olen = (sasldata->blen - sasldata->bpos > len) ? len :
566       sasldata->blen - sasldata->bpos;
567
568     memcpy (buf, sasldata->buf+sasldata->bpos, olen);
569     sasldata->bpos += olen;
570
571     return olen;
572   }
573   
574   conn->sockdata = sasldata->sockdata;
575
576 #ifndef USE_SASL2
577   FREE (&sasldata->buf);
578 #endif
579   sasldata->bpos = 0;
580   sasldata->blen = 0;
581
582   /* and decode the result, if necessary */
583   if (*sasldata->ssf)
584   {
585     do
586     {
587       /* call the underlying read function to fill the buffer */
588       rc = (sasldata->read) (conn, buf, len);
589       if (rc <= 0)
590         goto out;
591
592       rc = sasl_decode (sasldata->saslconn, buf, rc, &sasldata->buf,
593         &sasldata->blen);
594       if (rc != SASL_OK)
595       {
596         dprint (1, (debugfile, "SASL decode failed: %s\n",
597           sasl_errstring (rc, NULL, NULL)));
598         goto out;
599       }
600     }
601     while (!sasldata->blen);
602
603     olen = (sasldata->blen - sasldata->bpos > len) ? len :
604       sasldata->blen - sasldata->bpos;
605
606     memcpy (buf, sasldata->buf, olen);
607     sasldata->bpos += olen;
608
609     rc = olen;
610   }
611   else
612     rc = (sasldata->read) (conn, buf, len);
613
614   out:
615     conn->sockdata = sasldata;
616
617     return rc;
618 }
619
620 static int mutt_sasl_conn_write (CONNECTION* conn, const char* buf,
621   size_t len)
622 {
623   SASL_DATA* sasldata;
624   int rc;
625
626 #ifdef USE_SASL2
627   const char *pbuf;
628 #else
629   char* pbuf;
630 #endif
631   unsigned int olen, plen;
632
633   sasldata = (SASL_DATA*) conn->sockdata;
634   conn->sockdata = sasldata->sockdata;
635
636   /* encode data, if necessary */
637   if (*sasldata->ssf)
638   {
639     /* handle data larger than MAXOUTBUF */
640     do
641     {
642       olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len;
643
644       rc = sasl_encode (sasldata->saslconn, buf, olen, &pbuf, &plen);
645       if (rc != SASL_OK)
646       {
647         dprint (1, (debugfile, "SASL encoding failed: %s\n",
648           sasl_errstring (rc, NULL, NULL)));
649         goto fail;
650       }
651
652       rc = (sasldata->write) (conn, pbuf, plen);
653 #ifndef USE_SASL2
654       FREE (&pbuf);
655 #endif
656       if (rc != plen)
657         goto fail;
658
659       len -= olen;
660       buf += olen;
661     }
662     while (len > *sasldata->pbufsize);
663   }
664   else
665   /* just write using the underlying socket function */
666     rc = (sasldata->write) (conn, buf, len);
667   
668   conn->sockdata = sasldata;
669
670   return rc;
671
672  fail:
673   conn->sockdata = sasldata;
674   return -1;
675 }