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