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 void mutt_sasl_done (void) {
433   sasl_done ();
434 }
435
436 /* mutt_sasl_cb_log: callback to log SASL messages */
437 static int mutt_sasl_cb_log (void *context, int priority, const char *message)
438 {
439   debug_print (priority, ("SASL: %s\n", message));
440
441   return SASL_OK;
442 }
443
444 /* mutt_sasl_cb_authname: callback to retrieve authname or user from ACCOUNT */
445 static int mutt_sasl_cb_authname (void *context, int id, const char **result,
446                                   unsigned *len)
447 {
448   ACCOUNT *account = (ACCOUNT *) context;
449
450   *result = NULL;
451   if (len)
452     *len = 0;
453
454   if (!account)
455     return SASL_BADPARAM;
456
457   debug_print (2, ("getting %s for %s:%u\n",
458               id == SASL_CB_AUTHNAME ? "authname" : "user",
459               account->host, account->port));
460
461   if (id == SASL_CB_AUTHNAME) {
462     if (mutt_account_getlogin (account))
463       return SASL_FAIL;
464     *result = account->login;
465   } else {
466     if (mutt_account_getuser (account))
467       return SASL_FAIL;
468     *result = account->user;
469   }
470
471   if (len)
472     *len = str_len (*result);
473
474   return SASL_OK;
475 }
476
477 static int mutt_sasl_cb_pass (sasl_conn_t * conn, void *context, int id,
478                               sasl_secret_t ** psecret)
479 {
480   ACCOUNT *account = (ACCOUNT *) context;
481   int len;
482
483   if (!account || !psecret)
484     return SASL_BADPARAM;
485
486   debug_print (2, ("getting password for %s@%s:%u\n",
487               account->login, account->host, account->port));
488
489   if (mutt_account_getpass (account))
490     return SASL_FAIL;
491
492   len = str_len (account->pass);
493
494   *psecret = (sasl_secret_t *) mem_malloc (sizeof (sasl_secret_t) + len);
495   (*psecret)->len = len;
496   strcpy ((char*) (*psecret)->data, account->pass);     /* __STRCPY_CHECKED__ */
497
498   return SASL_OK;
499 }
500
501 /* mutt_sasl_conn_open: empty wrapper for underlying open function. We
502  *   don't know in advance that a connection will use SASL, so we
503  *   replace conn's methods with sasl methods when authentication
504  *   is successful, using mutt_sasl_setup_conn */
505 static int mutt_sasl_conn_open (CONNECTION * conn)
506 {
507   SASL_DATA *sasldata;
508   int rc;
509
510   sasldata = (SASL_DATA *) conn->sockdata;
511   conn->sockdata = sasldata->sockdata;
512   rc = (sasldata->msasl_open) (conn);
513   conn->sockdata = sasldata;
514
515   return rc;
516 }
517
518 /* mutt_sasl_conn_close: calls underlying close function and disposes of
519  *   the sasl_conn_t object, then restores connection to pre-sasl state */
520 static int mutt_sasl_conn_close (CONNECTION * conn)
521 {
522   SASL_DATA *sasldata;
523   int rc;
524
525   sasldata = (SASL_DATA *) conn->sockdata;
526
527   /* restore connection's underlying methods */
528   conn->sockdata = sasldata->sockdata;
529   conn->conn_open = sasldata->msasl_open;
530   conn->conn_close = sasldata->msasl_close;
531   conn->conn_read = sasldata->msasl_read;
532   conn->conn_write = sasldata->msasl_write;
533
534   /* release sasl resources */
535   sasl_dispose (&sasldata->saslconn);
536 #ifndef USE_SASL2
537   mem_free (&sasldata->buf);
538 #endif
539   mem_free (&sasldata);
540
541   /* call underlying close */
542   rc = (conn->conn_close) (conn);
543
544   return rc;
545 }
546
547 static int mutt_sasl_conn_read (CONNECTION * conn, char *buf, size_t len)
548 {
549   SASL_DATA *sasldata;
550   int rc;
551
552   unsigned int olen;
553
554   sasldata = (SASL_DATA *) conn->sockdata;
555
556   /* if we still have data in our read buffer, copy it into buf */
557   if (sasldata->blen > sasldata->bpos) {
558     olen = (sasldata->blen - sasldata->bpos > len) ? len :
559       sasldata->blen - sasldata->bpos;
560
561     memcpy (buf, sasldata->buf + sasldata->bpos, olen);
562     sasldata->bpos += olen;
563
564     return olen;
565   }
566
567   conn->sockdata = sasldata->sockdata;
568
569 #ifndef USE_SASL2
570   mem_free (&sasldata->buf);
571 #endif
572   sasldata->bpos = 0;
573   sasldata->blen = 0;
574
575   /* and decode the result, if necessary */
576   if (*sasldata->ssf) {
577     do {
578       /* call the underlying read function to fill the buffer */
579       rc = (sasldata->msasl_read) (conn, buf, len);
580       if (rc <= 0)
581         goto out;
582
583       rc = sasl_decode (sasldata->saslconn, buf, rc, &sasldata->buf,
584                         &sasldata->blen);
585       if (rc != SASL_OK) {
586         debug_print (1, ("SASL decode failed: %s\n",
587                     sasl_errstring (rc, NULL, NULL)));
588         goto out;
589       }
590     }
591     while (!sasldata->blen);
592
593     olen = (sasldata->blen - sasldata->bpos > len) ? len :
594       sasldata->blen - sasldata->bpos;
595
596     memcpy (buf, sasldata->buf, olen);
597     sasldata->bpos += olen;
598
599     rc = olen;
600   }
601   else
602     rc = (sasldata->msasl_read) (conn, buf, len);
603
604 out:
605   conn->sockdata = sasldata;
606
607   return rc;
608 }
609
610 static int mutt_sasl_conn_write (CONNECTION * conn, const char *buf,
611                                  size_t len)
612 {
613   SASL_DATA *sasldata;
614   int rc;
615
616 #ifdef USE_SASL2
617   const char *pbuf;
618 #else
619   char *pbuf;
620 #endif
621   unsigned int olen, plen;
622
623   sasldata = (SASL_DATA *) conn->sockdata;
624   conn->sockdata = sasldata->sockdata;
625
626   /* encode data, if necessary */
627   if (*sasldata->ssf) {
628     /* handle data larger than MAXOUTBUF */
629     do {
630       olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len;
631
632       rc = sasl_encode (sasldata->saslconn, buf, olen, &pbuf, &plen);
633       if (rc != SASL_OK) {
634         debug_print (1, ("SASL encoding failed: %s\n",
635                     sasl_errstring (rc, NULL, NULL)));
636         goto fail;
637       }
638
639       rc = (sasldata->msasl_write) (conn, pbuf, plen);
640 #ifndef USE_SASL2
641       mem_free (&pbuf);
642 #endif
643       if (rc != plen)
644         goto fail;
645
646       len -= olen;
647       buf += olen;
648     }
649     while (len > *sasldata->pbufsize);
650   }
651   else
652     /* just write using the underlying socket function */
653     rc = (sasldata->msasl_write) (conn, buf, len);
654
655   conn->sockdata = sasldata;
656
657   return rc;
658
659 fail:
660   conn->sockdata = sasldata;
661   return -1;
662 }