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