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 #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 < str_len (hbuf) + str_len (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 necessary). */
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?
247    */
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   if (conn->ssf) {
292 #ifdef USE_SASL2                /* I'm not sure this actually has an effect, at least with SASLv2 */
293     debug_print (2, ("External SSF: %d\n", conn->ssf));
294     if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK)
295 #else
296     memset (&extprops, 0, sizeof (extprops));
297     extprops.ssf = conn->ssf;
298     debug_print (2, ("External SSF: %d\n", extprops.ssf));
299     if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &extprops) != SASL_OK)
300 #endif
301     {
302       debug_print (1, ("Error setting external properties\n"));
303       return -1;
304     }
305 #ifdef USE_SASL2
306     debug_print (2, ("External authentication name: %s\n", conn->account.user));
307     if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) !=
308         SASL_OK) {
309       debug_print (1, ("Error setting external properties\n"));
310       return -1;
311     }
312 #endif
313   }
314
315   return 0;
316 }
317
318 sasl_callback_t *mutt_sasl_get_callbacks (ACCOUNT * account)
319 {
320   sasl_callback_t *callback;
321
322   callback = mutt_sasl_callbacks;
323
324   callback->id = SASL_CB_USER;
325   callback->proc = mutt_sasl_cb_authname;
326   callback->context = account;
327   callback++;
328
329   callback->id = SASL_CB_AUTHNAME;
330   callback->proc = mutt_sasl_cb_authname;
331   callback->context = account;
332   callback++;
333
334   callback->id = SASL_CB_PASS;
335   callback->proc = mutt_sasl_cb_pass;
336   callback->context = account;
337   callback++;
338
339   callback->id = SASL_CB_GETREALM;
340   callback->proc = NULL;
341   callback->context = NULL;
342   callback++;
343
344   callback->id = SASL_CB_LIST_END;
345   callback->proc = NULL;
346   callback->context = NULL;
347
348   return mutt_sasl_callbacks;
349 }
350
351 int mutt_sasl_interact (sasl_interact_t * interaction)
352 {
353   char prompt[SHORT_STRING];
354   char resp[SHORT_STRING];
355
356   while (interaction->id != SASL_CB_LIST_END) {
357     debug_print (2, ("filling in SASL interaction %ld.\n", interaction->id));
358
359     snprintf (prompt, sizeof (prompt), "%s: ", interaction->prompt);
360     resp[0] = '\0';
361     if (mutt_get_field (prompt, resp, sizeof (resp), 0))
362       return SASL_FAIL;
363
364     interaction->len = str_len (resp) + 1;
365     interaction->result = mem_malloc (interaction->len);
366     memcpy ((char*) interaction->result, resp, interaction->len);
367
368     interaction++;
369   }
370
371   return SASL_OK;
372 }
373
374 /* SASL can stack a protection layer on top of an existing connection.
375  * To handle this, we store a saslconn_t in conn->sockdata, and write
376  * wrappers which en/decode the read/write stream, then replace sockdata
377  * with an embedded copy of the old sockdata and call the underlying
378  * functions (which we've also preserved). I thought about trying to make
379  * a general stackable connection system, but it seemed like overkill -
380  * something is wrong if we have 15 filters on top of a socket. Anyway,
381  * anything else which wishes to stack can use the same method. The only
382  * disadvantage is we have to write wrappers for all the socket methods,
383  * even if we only stack over read and write. Thinking about it, the
384  * abstraction problem is that there is more in CONNECTION than there
385  * needs to be. Ideally it would have only (void*)data and methods. */
386
387 /* mutt_sasl_setup_conn: replace connection methods, sockdata with 
388  *   SASL wrappers, for protection layers. Also get ssf, as a fastpath
389  *   for the read/write methods. */
390 void mutt_sasl_setup_conn (CONNECTION * conn, sasl_conn_t * saslconn)
391 {
392   SASL_DATA *sasldata = (SASL_DATA *) mem_malloc (sizeof (SASL_DATA));
393
394   sasldata->saslconn = saslconn;
395   /* get ssf so we know whether we have to (en|de)code read/write */
396 #ifdef USE_SASL2
397   sasl_getprop (saslconn, SASL_SSF, (const void **) &sasldata->ssf);
398 #else
399   sasl_getprop (saslconn, SASL_SSF, (void **) &sasldata->ssf);
400 #endif
401   debug_print (3, ("SASL protection strength: %u\n", *sasldata->ssf));
402   /* Add SASL SSF to transport SSF */
403   conn->ssf += *sasldata->ssf;
404 #ifdef USE_SASL2
405   sasl_getprop (saslconn, SASL_MAXOUTBUF,
406                 (const void **) &sasldata->pbufsize);
407 #else
408   sasl_getprop (saslconn, SASL_MAXOUTBUF, (void **) &sasldata->pbufsize);
409 #endif
410   debug_print (3, ("SASL protection buffer size: %u\n", *sasldata->pbufsize));
411
412   /* clear input buffer */
413   sasldata->buf = NULL;
414   sasldata->bpos = 0;
415   sasldata->blen = 0;
416
417   /* preserve old functions */
418   sasldata->sockdata = conn->sockdata;
419   sasldata->msasl_open = conn->conn_open;
420   sasldata->msasl_close = conn->conn_close;
421   sasldata->msasl_read = conn->conn_read;
422   sasldata->msasl_write = conn->conn_write;
423
424   /* and set up new functions */
425   conn->sockdata = sasldata;
426   conn->conn_open = mutt_sasl_conn_open;
427   conn->conn_close = mutt_sasl_conn_close;
428   conn->conn_read = mutt_sasl_conn_read;
429   conn->conn_write = mutt_sasl_conn_write;
430 }
431
432 /* mutt_sasl_cb_log: callback to log SASL messages */
433 static int mutt_sasl_cb_log (void *context, int priority, const char *message)
434 {
435   debug_print (priority, ("SASL: %s\n", message));
436
437   return SASL_OK;
438 }
439
440 /* mutt_sasl_cb_authname: callback to retrieve authname or user from ACCOUNT */
441 static int mutt_sasl_cb_authname (void *context, int id, const char **result,
442                                   unsigned *len)
443 {
444   ACCOUNT *account = (ACCOUNT *) context;
445
446   *result = NULL;
447   if (len)
448     *len = 0;
449
450   if (!account)
451     return SASL_BADPARAM;
452
453   debug_print (2, ("getting %s for %s:%u\n",
454               id == SASL_CB_AUTHNAME ? "authname" : "user",
455               account->host, account->port));
456
457   if (id == SASL_CB_AUTHNAME) {
458     if (mutt_account_getlogin (account))
459       return SASL_FAIL;
460     *result = account->login;
461   } else {
462     if (mutt_account_getuser (account))
463       return SASL_FAIL;
464     *result = account->user;
465   }
466
467   if (len)
468     *len = str_len (*result);
469
470   return SASL_OK;
471 }
472
473 static int mutt_sasl_cb_pass (sasl_conn_t * conn, void *context, int id,
474                               sasl_secret_t ** psecret)
475 {
476   ACCOUNT *account = (ACCOUNT *) context;
477   int len;
478
479   if (!account || !psecret)
480     return SASL_BADPARAM;
481
482   debug_print (2, ("getting password for %s@%s:%u\n",
483               account->login, account->host, account->port));
484
485   if (mutt_account_getpass (account))
486     return SASL_FAIL;
487
488   len = str_len (account->pass);
489
490   *psecret = (sasl_secret_t *) mem_malloc (sizeof (sasl_secret_t) + len);
491   (*psecret)->len = len;
492   strcpy ((char*) (*psecret)->data, account->pass);     /* __STRCPY_CHECKED__ */
493
494   return SASL_OK;
495 }
496
497 /* mutt_sasl_conn_open: empty wrapper for underlying open function. We
498  *   don't know in advance that a connection will use SASL, so we
499  *   replace conn's methods with sasl methods when authentication
500  *   is successful, using mutt_sasl_setup_conn */
501 static int mutt_sasl_conn_open (CONNECTION * conn)
502 {
503   SASL_DATA *sasldata;
504   int rc;
505
506   sasldata = (SASL_DATA *) conn->sockdata;
507   conn->sockdata = sasldata->sockdata;
508   rc = (sasldata->msasl_open) (conn);
509   conn->sockdata = sasldata;
510
511   return rc;
512 }
513
514 /* mutt_sasl_conn_close: calls underlying close function and disposes of
515  *   the sasl_conn_t object, then restores connection to pre-sasl state */
516 static int mutt_sasl_conn_close (CONNECTION * conn)
517 {
518   SASL_DATA *sasldata;
519   int rc;
520
521   sasldata = (SASL_DATA *) conn->sockdata;
522
523   /* restore connection's underlying methods */
524   conn->sockdata = sasldata->sockdata;
525   conn->conn_open = sasldata->msasl_open;
526   conn->conn_close = sasldata->msasl_close;
527   conn->conn_read = sasldata->msasl_read;
528   conn->conn_write = sasldata->msasl_write;
529
530   /* release sasl resources */
531   sasl_dispose (&sasldata->saslconn);
532 #ifndef USE_SASL2
533   mem_free (&sasldata->buf);
534 #endif
535   mem_free (&sasldata);
536
537   /* call underlying close */
538   rc = (conn->conn_close) (conn);
539
540   return rc;
541 }
542
543 static int mutt_sasl_conn_read (CONNECTION * conn, char *buf, size_t len)
544 {
545   SASL_DATA *sasldata;
546   int rc;
547
548   unsigned int olen;
549
550   sasldata = (SASL_DATA *) conn->sockdata;
551
552   /* if we still have data in our read buffer, copy it into buf */
553   if (sasldata->blen > sasldata->bpos) {
554     olen = (sasldata->blen - sasldata->bpos > len) ? len :
555       sasldata->blen - sasldata->bpos;
556
557     memcpy (buf, sasldata->buf + sasldata->bpos, olen);
558     sasldata->bpos += olen;
559
560     return olen;
561   }
562
563   conn->sockdata = sasldata->sockdata;
564
565 #ifndef USE_SASL2
566   mem_free (&sasldata->buf);
567 #endif
568   sasldata->bpos = 0;
569   sasldata->blen = 0;
570
571   /* and decode the result, if necessary */
572   if (*sasldata->ssf) {
573     do {
574       /* call the underlying read function to fill the buffer */
575       rc = (sasldata->msasl_read) (conn, buf, len);
576       if (rc <= 0)
577         goto out;
578
579       rc = sasl_decode (sasldata->saslconn, buf, rc, &sasldata->buf,
580                         &sasldata->blen);
581       if (rc != SASL_OK) {
582         debug_print (1, ("SASL decode failed: %s\n",
583                     sasl_errstring (rc, NULL, NULL)));
584         goto out;
585       }
586     }
587     while (!sasldata->blen);
588
589     olen = (sasldata->blen - sasldata->bpos > len) ? len :
590       sasldata->blen - sasldata->bpos;
591
592     memcpy (buf, sasldata->buf, olen);
593     sasldata->bpos += olen;
594
595     rc = olen;
596   }
597   else
598     rc = (sasldata->msasl_read) (conn, buf, len);
599
600 out:
601   conn->sockdata = sasldata;
602
603   return rc;
604 }
605
606 static int mutt_sasl_conn_write (CONNECTION * conn, const char *buf,
607                                  size_t len)
608 {
609   SASL_DATA *sasldata;
610   int rc;
611
612 #ifdef USE_SASL2
613   const char *pbuf;
614 #else
615   char *pbuf;
616 #endif
617   unsigned int olen, plen;
618
619   sasldata = (SASL_DATA *) conn->sockdata;
620   conn->sockdata = sasldata->sockdata;
621
622   /* encode data, if necessary */
623   if (*sasldata->ssf) {
624     /* handle data larger than MAXOUTBUF */
625     do {
626       olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len;
627
628       rc = sasl_encode (sasldata->saslconn, buf, olen, &pbuf, &plen);
629       if (rc != SASL_OK) {
630         debug_print (1, ("SASL encoding failed: %s\n",
631                     sasl_errstring (rc, NULL, NULL)));
632         goto fail;
633       }
634
635       rc = (sasldata->msasl_write) (conn, pbuf, plen);
636 #ifndef USE_SASL2
637       mem_free (&pbuf);
638 #endif
639       if (rc != plen)
640         goto fail;
641
642       len -= olen;
643       buf += olen;
644     }
645     while (len > *sasldata->pbufsize);
646   }
647   else
648     /* just write using the underlying socket function */
649     rc = (sasldata->msasl_write) (conn, buf, len);
650
651   conn->sockdata = sasldata;
652
653   return rc;
654
655 fail:
656   conn->sockdata = sasldata;
657   return -1;
658 }