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