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