1 /* gsasl.c --- Command line interface to libgsasl.
2  * Copyright (C) 2002-2021 Simon Josefsson
3  *
4  * This file is part of GNU SASL.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "internal.h"
22 #include "callbacks.h"
23 #include "imap.h"
24 #include "smtp.h"
25 
26 #include "sockets.h"
27 
28 #ifdef HAVE_LIBGNUTLS
29 #include <gnutls/gnutls.h>
30 #include <gnutls/x509.h>
31 gnutls_session_t session;
32 bool using_tls = false;
33 #endif
34 
35 char *b64cbtlsunique = NULL;
36 
37 struct gengetopt_args_info args_info;
38 int sockfd = 0;
39 
40 #ifdef HAVE_LIBGNUTLS
41 static bool
_handle_tlserror(int error)42 _handle_tlserror (int error)
43 {
44   int rc;
45 
46   switch (error)
47     {
48     case GNUTLS_E_REHANDSHAKE:
49       for (;;)
50 	{
51 	  rc = gnutls_handshake (session);
52 	  switch (rc)
53 	    {
54 	    case GNUTLS_E_INTERRUPTED:
55 	    case GNUTLS_E_AGAIN:
56 	      continue;
57 
58 	    case GNUTLS_E_GOT_APPLICATION_DATA:
59 	      /* TODO: signal this somehow? */
60 	      continue;
61 
62 	    case GNUTLS_E_WARNING_ALERT_RECEIVED:
63 	      fprintf (stderr, "ALERT: %s\n",
64 		       gnutls_alert_get_name (gnutls_alert_get (session)));
65 	      continue;
66 
67 	    default:
68 	      fprintf (stderr, "TLS rehandshake failed: %s\n",
69 		       gnutls_strerror (rc));
70 	      /* make every error fatal */
71 	      return false;
72 	    }
73 
74 	  return true;
75 	}
76 
77     case GNUTLS_E_INTERRUPTED:
78     case GNUTLS_E_AGAIN:
79       /* not fatal */
80       return true;
81 
82     default:
83       fprintf (stderr, "TLS error: %s\n", gnutls_strerror (error));
84       return false;
85     }
86 }
87 #endif
88 
89 static ssize_t
_recv(void * dst,size_t cnt)90 _recv (void *dst, size_t cnt)
91 {
92 #ifdef HAVE_LIBGNUTLS
93   if (using_tls)
94     {
95       ssize_t l = 0;
96       do
97 	{
98 	  l = gnutls_record_recv (session, dst, cnt);
99 
100 	  if (l < 0 && !_handle_tlserror (l))
101 	    break;
102 	}
103       while (l < 0);
104 
105       return l;
106     }
107 #endif
108 
109   return recv (sockfd, dst, cnt, 0);
110 }
111 
112 static ssize_t
_send(void const * src,size_t cnt)113 _send (void const *src, size_t cnt)
114 {
115 #ifdef HAVE_LIBGNUTLS
116   if (using_tls)
117     {
118       ssize_t l;
119       do
120 	{
121 	  if (cnt > 0)
122 	    l = gnutls_record_send (session, src, cnt);
123 	  else
124 	    l = 0;
125 
126 	  if (l < 0 && !_handle_tlserror (l))
127 	    break;
128 	}
129       while (l < 0);
130 
131       return l;
132     }
133 #endif
134 
135   return write (sockfd, src, cnt);
136 }
137 
138 int
writeln(const char * str)139 writeln (const char *str)
140 {
141   printf ("%s\n", str);
142 
143   if (sockfd)
144     {
145       ssize_t len = strlen (str);
146 
147       len = _send (str, len);
148       if (len != (ssize_t) strlen (str))
149 	return 0;
150 
151 #define CRLF "\r\n"
152 
153       len = _send (CRLF, strlen (CRLF));
154       if (len != strlen (CRLF))
155 	return 0;
156     }
157 
158   return 1;
159 }
160 
161 int
readln(char ** out)162 readln (char **out)
163 {
164   if (sockfd)
165     {
166       size_t allocated = 0, used = 0;
167       char *input = NULL;
168 
169       /* FIXME: Read larger chunks.  Problem: buffering too large reads? */
170 
171       do
172 	{
173 	  ssize_t nread;
174 
175 	  if (used == allocated)
176 	    input = x2realloc (input, &allocated);
177 
178 	  nread = _recv (&input[used], 1);
179 	  if (nread <= 0)
180 	    return 0;
181 
182 	  used += nread;
183 	}
184       while (input[used - 1] != '\n');
185 
186       if (used == allocated)
187 	input = x2realloc (input, &allocated);
188 
189       input[used] = '\0';
190 
191       *out = input;
192 
193       printf ("%s", *out);
194     }
195   else
196     {
197       *out = readline ("");
198       if (*out == NULL)
199 	return 0;
200     }
201 
202   return 1;
203 }
204 
205 static int
greeting(void)206 greeting (void)
207 {
208   if (args_info.imap_flag)
209     return imap_greeting ();
210   if (args_info.smtp_flag)
211     return smtp_greeting ();
212 
213   return 1;
214 }
215 
216 #ifdef HAVE_LIBGNUTLS
217 static int
has_starttls(void)218 has_starttls (void)
219 {
220   if (args_info.imap_flag)
221     return imap_has_starttls ();
222   if (args_info.smtp_flag)
223     return smtp_has_starttls ();
224 
225   return 0;
226 }
227 
228 static int
starttls(void)229 starttls (void)
230 {
231   if (args_info.imap_flag)
232     return imap_starttls ();
233   if (args_info.smtp_flag)
234     return smtp_starttls ();
235 
236   return 1;
237 }
238 #endif
239 
240 static int
select_mechanism(char ** mechlist)241 select_mechanism (char **mechlist)
242 {
243   char *in;
244 
245   if (args_info.imap_flag)
246     return imap_select_mechanism (mechlist);
247   if (args_info.smtp_flag)
248     return smtp_select_mechanism (mechlist);
249 
250   if (args_info.mechanism_arg)
251     *mechlist = args_info.mechanism_arg;
252   else if (args_info.server_flag)
253     {
254       if (!args_info.quiet_given)
255 	fprintf (stderr, _("Input list of SASL mechanisms:\n"));
256       if (!readln (&in))
257 	return 0;
258       *mechlist = in;
259     }
260   else				/* if (args_info.client_flag) */
261     {
262       if (!args_info.quiet_given)
263 	fprintf (stderr,
264 		 _("Input list of SASL mechanisms supported by server:\n"));
265       if (!readln (&in))
266 	return 0;
267 
268       *mechlist = in;
269     }
270 
271   return 1;
272 }
273 
274 static int
authenticate(const char * mech)275 authenticate (const char *mech)
276 {
277   if (args_info.imap_flag)
278     return imap_authenticate (mech);
279   if (args_info.smtp_flag)
280     return smtp_authenticate (mech);
281 
282   if (!args_info.quiet_given)
283     fprintf (stderr, _("Using mechanism:\n"));
284   puts (mech);
285 
286   return 1;
287 }
288 
289 static int
step_send(const char * data)290 step_send (const char *data)
291 {
292   if (args_info.imap_flag)
293     return imap_step_send (data);
294   if (args_info.smtp_flag)
295     return smtp_step_send (data);
296 
297   if (!args_info.quiet_given)
298     {
299       if (args_info.server_flag)
300 	fprintf (stderr, _("Output from server:\n"));
301       else
302 	fprintf (stderr, _("Output from client:\n"));
303     }
304   fprintf (stdout, "%s\n", data);
305 
306   return 1;
307 }
308 
309 /* Return 1 on token, 2 on protocol success, 3 on protocol fail, 0 on
310    errors. */
311 static int
step_recv(char ** data)312 step_recv (char **data)
313 {
314   if (args_info.imap_flag)
315     return imap_step_recv (data);
316   if (args_info.smtp_flag)
317     return smtp_step_recv (data);
318 
319   if (!readln (data))
320     return 0;
321 
322   return 1;
323 }
324 
325 static int
logout(void)326 logout (void)
327 {
328   if (args_info.imap_flag)
329     return imap_logout ();
330   if (args_info.smtp_flag)
331     return smtp_logout ();
332 
333   return 1;
334 }
335 
336 const char version_etc_copyright[] =
337   /* Do *not* mark this string for translation.  %s is a copyright
338      symbol suitable for this locale, and %d is the copyright
339      year.  */
340   "Copyright %s %d Simon Josefsson.";
341 
342 static void
343 usage (int status)
344   GSASL_ATTR_NO_RETRUN;
345 
usage(int status)346      static void usage (int status)
347 {
348   if (status != EXIT_SUCCESS)
349     fprintf (stderr, _("Try `%s --help' for more information.\n"),
350 	     program_name);
351   else
352     {
353       cmdline_parser_print_help ();
354       emit_bug_reporting_address ();
355     }
356   exit (status);
357 }
358 
359 #define DEFAULT_SALT_SIZE 12
360 
361 static void
mkpasswd(void)362 mkpasswd (void)
363 {
364   char salt_buf[DEFAULT_SALT_SIZE];
365   char *salt;
366   size_t saltlen;
367   char *b64salt;
368   char saltedpassword[GSASL_HASH_MAX_SIZE];
369   char *hexsaltedpassword;
370   size_t hexsaltedpasswordlen;
371   int hash = 0;
372   size_t hashlen = 0;
373   char clientkey[GSASL_HASH_MAX_SIZE];
374   char serverkey[GSASL_HASH_MAX_SIZE];
375   char storedkey[GSASL_HASH_MAX_SIZE];
376   char *b64serverkey, *b64storedkey;
377   size_t b64serverkeylen, b64storedkeylen;
378   int res;
379 
380   if (args_info.mechanism_arg == NULL)
381     error (EXIT_FAILURE, 0, _("required --mechanism missing"));
382 
383   if (strcmp (args_info.mechanism_arg, "SCRAM-SHA-1") == 0)
384     {
385       hash = GSASL_HASH_SHA1;
386       hashlen = GSASL_HASH_SHA1_SIZE;
387     }
388   else if (strcmp (args_info.mechanism_arg, "SCRAM-SHA-256") == 0)
389     {
390       hash = GSASL_HASH_SHA256;
391       hashlen = GSASL_HASH_SHA256_SIZE;
392     }
393   else
394     error (EXIT_FAILURE, 0, _("unsupported --mechanism for --mkpasswd: %s"),
395 	   args_info.mechanism_arg);
396 
397   if (args_info.iteration_count_arg <= 0)
398     error (EXIT_FAILURE, 0, _("iteration count must be positive: %d"),
399 	   args_info.iteration_count_arg);
400 
401   if (args_info.salt_given)
402     {
403       b64salt = args_info.salt_arg;
404 
405       res = gsasl_base64_from (b64salt, strlen (b64salt), &salt, &saltlen);
406       if (res != GSASL_OK)
407 	error (EXIT_FAILURE, 0, "%s: %s", gsasl_strerror (res), b64salt);
408     }
409   else
410     {
411       salt = salt_buf;
412       saltlen = sizeof (salt_buf);
413 
414       res = gsasl_nonce (salt, saltlen);
415       if (res != GSASL_OK)
416 	error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
417 
418       res = gsasl_base64_to (salt, saltlen, &b64salt, NULL);
419       if (res != GSASL_OK)
420 	error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
421     }
422 
423   if (args_info.password_arg == NULL)
424     args_info.password_arg = readutf8pass (_("Enter password: "));
425 
426   res = gsasl_scram_secrets_from_password (hash, args_info.password_arg,
427 					   args_info.iteration_count_arg,
428 					   salt, saltlen,
429 					   saltedpassword,
430 					   clientkey, serverkey, storedkey);
431   if (res != GSASL_OK)
432     error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
433 
434   res = gsasl_hex_to (saltedpassword, hashlen,
435 		      &hexsaltedpassword, &hexsaltedpasswordlen);
436   if (res != GSASL_OK)
437     error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
438 
439   res = gsasl_base64_to (storedkey, hashlen, &b64storedkey, &b64storedkeylen);
440   if (res != GSASL_OK)
441     error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
442 
443   res = gsasl_base64_to (serverkey, hashlen, &b64serverkey, &b64serverkeylen);
444   if (res != GSASL_OK)
445     error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
446 
447   printf ("{%s}%d,%s,%s,%s", args_info.mechanism_arg,
448 	  args_info.iteration_count_arg, b64salt, b64storedkey, b64serverkey);
449   if (args_info.verbose_given)
450     printf (",%s", hexsaltedpassword);
451   printf ("\n");
452 
453   if (salt != salt_buf)
454     free (salt);
455   if (b64salt != args_info.salt_arg)
456     free (b64salt);
457   free (b64serverkey);
458   free (b64storedkey);
459   free (hexsaltedpassword);
460 }
461 
462 
463 int
main(int argc,char * argv[])464 main (int argc, char *argv[])
465 {
466   Gsasl *ctx = NULL;
467   int res;
468   char *in;
469   char *connect_hostname = NULL;
470   char *connect_service = NULL;
471 #ifdef HAVE_LIBGNUTLS
472   gnutls_anon_client_credentials_t anoncred;
473   gnutls_certificate_credentials_t x509cred;
474 #endif
475 
476   set_program_name (argv[0]);
477   setlocale (LC_ALL, "");
478   bindtextdomain (PACKAGE, LOCALEDIR);
479   textdomain (PACKAGE);
480 
481   /* This is necessary for modern MinGW compilers that provide working
482      getaddrinfo function, which results in gnulib not detecting that
483      it is broken.  The proper fix is for gnulib to wrap the
484      getaddrinfo call and initialize Windows sockets in the
485      wrapper.  */
486   (void) gl_sockets_startup (SOCKETS_1_1);
487 
488   if (cmdline_parser (argc, argv, &args_info) != 0)
489     return EXIT_FAILURE;
490 
491   if (args_info.version_given)
492     {
493       const char *p = PACKAGE_NAME;
494       if (strcmp (gsasl_check_version (NULL), PACKAGE_VERSION) != 0)
495 	p = PACKAGE_STRING;
496       version_etc (stdout, "gsasl", p, gsasl_check_version (NULL),
497 		   "Simon Josefsson", (char *) NULL);
498       return EXIT_SUCCESS;
499     }
500 
501   if (args_info.help_given)
502     usage (EXIT_SUCCESS);
503 
504   if (!(args_info.client_flag || args_info.client_given) &&
505       !args_info.server_given &&
506       !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag
507       && !args_info.mkpasswd_given)
508     {
509       error (0, 0, _("missing argument"));
510       usage (EXIT_FAILURE);
511     }
512 
513   if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
514       (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
515     error (EXIT_FAILURE, 0,
516 	   _("need both --x509-cert-file and --x509-key-file"));
517 
518   if (args_info.starttls_flag && args_info.no_starttls_flag)
519     error (EXIT_FAILURE, 0,
520 	   _("cannot use both --starttls and --no-starttls"));
521 
522   if (args_info.smtp_flag && args_info.imap_flag)
523     error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));
524 
525   if (!args_info.connect_given && args_info.inputs_num == 0 &&
526       !args_info.client_given && !args_info.server_given &&
527       !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag
528       && !args_info.mkpasswd_given)
529     {
530       cmdline_parser_print_help ();
531       emit_bug_reporting_address ();
532       return EXIT_SUCCESS;
533     }
534 
535   if (args_info.connect_given)
536     {
537       if (strrchr (args_info.connect_arg, ':'))
538 	{
539 	  connect_hostname = xstrdup (args_info.connect_arg);
540 	  *strrchr (connect_hostname, ':') = '\0';
541 	  connect_service =
542 	    xstrdup (strrchr (args_info.connect_arg, ':') + 1);
543 	}
544       else
545 	{
546 	  connect_hostname = xstrdup (args_info.connect_arg);
547 	  if (args_info.smtp_flag)
548 	    connect_service = xstrdup ("smtp");
549 	  else
550 	    connect_service = xstrdup ("imap");
551 	}
552     }
553   else if (args_info.inputs_num > 0)
554     {
555       connect_hostname = args_info.inputs[0];
556       if (args_info.inputs_num > 1)
557 	connect_service = args_info.inputs[1];
558       else if (args_info.smtp_flag)
559 	connect_service = xstrdup ("smtp");
560       else
561 	connect_service = xstrdup ("imap");
562     }
563 
564   if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
565     {
566       if (strcmp (connect_service, "25") == 0 ||
567 	  strcmp (connect_service, "smtp") == 0 ||
568 	  strcmp (connect_service, "587") == 0 ||
569 	  strcmp (connect_service, "submission") == 0)
570 	args_info.smtp_flag = 1;
571       else if (strcmp (connect_service, "143") == 0 ||
572 	       strcmp (connect_service, "imap") == 0)
573 	args_info.imap_flag = 1;
574       else
575 	error (EXIT_FAILURE, 0,
576 	       _("cannot guess SASL profile (try --smtp or --imap)"));
577     }
578 
579   if (args_info.imap_flag && !args_info.service_given)
580     args_info.service_arg = xstrdup ("imap");
581 
582   if (args_info.smtp_flag && !args_info.service_given)
583     args_info.service_arg = xstrdup ("smtp");
584 
585   if (args_info.imap_flag || args_info.smtp_flag)
586     args_info.no_client_first_flag = 1;
587 
588   if (connect_hostname && !args_info.hostname_arg)
589     args_info.hostname_arg = xstrdup (connect_hostname);
590 
591   if (!isatty (STDOUT_FILENO))
592     setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
593 
594   res = gsasl_init (&ctx);
595   if (res != GSASL_OK)
596     error (EXIT_FAILURE, 0, _("initialization failure: %s"),
597 	   gsasl_strerror (res));
598 
599   gsasl_callback_set (ctx, callback);
600 
601   if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
602     {
603       char *mechs;
604 
605       if (args_info.client_mechanisms_flag)
606 	res = gsasl_client_mechlist (ctx, &mechs);
607       else
608 	res = gsasl_server_mechlist (ctx, &mechs);
609 
610       if (res != GSASL_OK)
611 	error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
612 	       gsasl_strerror (res));
613 
614       if (!args_info.quiet_given)
615 	{
616 	  if (args_info.client_mechanisms_flag)
617 	    fprintf (stderr,
618 		     _("This client supports the following mechanisms:\n"));
619 	  else
620 	    fprintf (stderr,
621 		     _("This server supports the following mechanisms:\n"));
622 	}
623 
624       fprintf (stdout, "%s\n", mechs);
625 
626       free (mechs);
627 
628       goto done;
629     }
630 
631   if (args_info.mkpasswd_given)
632     {
633       mkpasswd ();
634       goto done;
635     }
636 
637   if (args_info.connect_given || args_info.inputs_num > 0)
638     {
639       struct addrinfo hints;
640       struct addrinfo *ai0, *ai;
641 
642       memset (&hints, 0, sizeof (hints));
643       hints.ai_flags = AI_CANONNAME;
644       hints.ai_socktype = SOCK_STREAM;
645       res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
646       if (res != 0)
647 	error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
648 	       gai_strerror (res));
649 
650       for (ai = ai0; ai; ai = ai->ai_next)
651 	{
652 	  fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname ?
653 						    ai->ai_canonname :
654 						    connect_hostname));
655 
656 	  sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
657 	  if (sockfd < 0)
658 	    {
659 	      error (0, errno, "socket");
660 	      continue;
661 	    }
662 
663 	  if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
664 	    {
665 	      int save_errno = errno;
666 	      close (sockfd);
667 	      sockfd = -1;
668 	      error (0, save_errno, "connect");
669 	      continue;
670 	    }
671 	  break;
672 	}
673 
674       if (sockfd < 0)
675 	error (EXIT_FAILURE, errno, "socket");
676 
677       freeaddrinfo (ai);
678     }
679 
680   if (!greeting ())
681     return 1;
682 
683 #ifdef HAVE_LIBGNUTLS
684   if (sockfd && !args_info.no_starttls_flag &&
685       (args_info.starttls_flag || has_starttls ()))
686     {
687       res = gnutls_global_init ();
688       if (res < 0)
689 	error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
690 	       gnutls_strerror (res));
691 
692       res = gnutls_init (&session, GNUTLS_CLIENT);
693       if (res < 0)
694 	error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
695 	       gnutls_strerror (res));
696 
697       res = gnutls_set_default_priority (session);
698       if (res < 0)
699 	error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
700 	       gnutls_strerror (res));
701 
702       res =
703 	gnutls_server_name_set (session, GNUTLS_NAME_DNS, connect_hostname,
704 				strlen (connect_hostname));
705       if (res < 0)
706 	error (EXIT_FAILURE, 0, _("setting GnuTLS server name failed: %s"),
707 	       gnutls_strerror (res));
708 
709       res = gnutls_anon_allocate_client_credentials (&anoncred);
710       if (res < 0)
711 	error (EXIT_FAILURE, 0,
712 	       _("allocating anonymous GnuTLS credential: %s"),
713 	       gnutls_strerror (res));
714 
715       res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
716       if (res < 0)
717 	error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
718 	       gnutls_strerror (res));
719 
720       res = gnutls_certificate_allocate_credentials (&x509cred);
721       if (res < 0)
722 	error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
723 	       gnutls_strerror (res));
724 
725       if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
726 	res = gnutls_certificate_set_x509_key_file
727 	  (x509cred, args_info.x509_cert_file_arg,
728 	   args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
729       if (res != GNUTLS_E_SUCCESS)
730 	error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
731 	       gnutls_strerror (res));
732 
733       if (args_info.x509_ca_file_arg && *args_info.x509_ca_file_arg)
734 	{
735 	  res = gnutls_certificate_set_x509_trust_file
736 	    (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
737 	  if (res < 0)
738 	    error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
739 		   gnutls_strerror (res));
740 	  if (res == 0)
741 	    error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
742 	}
743       else if (!args_info.x509_ca_file_arg)
744 	{
745 	  res = gnutls_certificate_set_x509_system_trust (x509cred);
746 	  if (res < 0)
747 	    error (EXIT_FAILURE, 0, _("setting GnuTLS system trust: %s"),
748 		   gnutls_strerror (res));
749 	}
750 
751       res = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE,
752 				    x509cred);
753       if (res < 0)
754 	error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
755 	       gnutls_strerror (res));
756 
757       if (args_info.x509_ca_file_arg == NULL || *args_info.x509_ca_file_arg)
758 	gnutls_session_set_verify_cert (session, connect_hostname, 0);
759 
760       if (args_info.priority_arg)
761 	{
762 	  const char *err_pos;
763 
764 	  res = gnutls_priority_set_direct (session, args_info.priority_arg,
765 					    &err_pos);
766 	  if (res < 0)
767 	    error (EXIT_FAILURE, 0,
768 		   _("setting GnuTLS cipher priority (%s): %s\n"),
769 		   gnutls_strerror (res), err_pos);
770 	}
771 
772       gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t)
773 				(unsigned long) sockfd);
774 
775       if (!starttls ())
776 	return 1;
777 
778       do
779 	{
780 	  res = gnutls_handshake (session);
781 	}
782       while (res < 0 && gnutls_error_is_fatal (res) == 0);
783 
784       if (!args_info.quiet_given)
785 	{
786 	  int type;
787 	  unsigned status;
788 	  gnutls_datum_t out;
789 
790 	  type = gnutls_certificate_type_get (session);
791 	  status = gnutls_session_get_verify_cert_status (session);
792 	  gnutls_certificate_verification_status_print (status, type, &out,
793 							0);
794 	  fprintf (stderr, _("TLS X.509 Verification: %s\n"), out.data);
795 	  gnutls_free (out.data);
796 	}
797 
798       if (res < 0)
799 	error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
800 	       gnutls_strerror (res));
801 
802       if (args_info.verbose_given)
803 	{
804 	  char *desc = gnutls_session_get_desc (session);
805 	  const gnutls_datum_t *cert_list;
806 	  unsigned int cert_list_size = 0, i;
807 	  gnutls_x509_crt_t cert;
808 	  gnutls_datum_t out;
809 
810 	  fprintf (stderr, _("TLS session info: %s\n"), desc);
811 	  gnutls_free (desc);
812 	  fflush (stderr);
813 
814 	  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
815 
816 	  for (i = 0; i < cert_list_size; i++)
817 	    {
818 	      res = gnutls_x509_crt_init (&cert);
819 	      if (res < 0)
820 		continue;
821 
822 	      res = gnutls_x509_crt_import (cert, &cert_list[i],
823 					    GNUTLS_X509_FMT_DER);
824 	      if (res < 0)
825 		continue;
826 
827 	      res = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE,
828 					   &out);
829 	      if (res == 0)
830 		{
831 		  fprintf (stderr, _("TLS X.509 Certificate %u: %s\n"), i,
832 			   out.data);
833 		  gnutls_free (out.data);
834 		}
835 
836 	      gnutls_x509_crt_deinit (cert);
837 	    }
838 	}
839 
840       if (args_info.x509_ca_file_arg && *args_info.x509_ca_file_arg)
841 	{
842 	  unsigned int status;
843 
844 	  res = gnutls_certificate_verify_peers2 (session, &status);
845 	  if (res < 0)
846 	    error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
847 		   gnutls_strerror (res));
848 
849 	  if (status & GNUTLS_CERT_INVALID)
850 	    error (EXIT_FAILURE, 0, _("server certificate is not trusted"));
851 
852 	  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
853 	    error (EXIT_FAILURE, 0,
854 		   _("server certificate hasn't got a known issuer"));
855 
856 	  if (status & GNUTLS_CERT_REVOKED)
857 	    error (EXIT_FAILURE, 0, _("server certificate has been revoked"));
858 
859 	  if (status != 0)
860 	    error (EXIT_FAILURE, 0,
861 		   _("could not verify server certificate (rc=%u)"), status);
862 	}
863 
864 #if HAVE_GNUTLS_SESSION_CHANNEL_BINDING
865       if (!args_info.no_cb_flag)
866 	{
867 	  gnutls_datum_t cb;
868 
869 	  res = gnutls_session_channel_binding (session,
870 						GNUTLS_CB_TLS_UNIQUE, &cb);
871 	  if (res != GNUTLS_E_SUCCESS)
872 	    error (EXIT_FAILURE, 0, _("getting channel binding failed: %s"),
873 		   gnutls_strerror (res));
874 
875 	  res = gsasl_base64_to ((char *) cb.data, cb.size,
876 				 &b64cbtlsunique, NULL);
877 	  if (res != GSASL_OK)
878 	    error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
879 	}
880 #endif
881 
882       using_tls = true;
883     }
884 #endif
885 
886   if (args_info.client_flag || args_info.client_given
887       || args_info.server_given)
888     {
889       char *out;
890       char *b64output;
891       size_t output_len;
892       size_t b64output_len;
893       const char *mech;
894       Gsasl_session *xctx = NULL;
895 
896       if (!select_mechanism (&in))
897 	return 1;
898 
899       mech = gsasl_client_suggest_mechanism (ctx, in);
900       if (mech == NULL)
901 	{
902 	  fprintf (stderr, _("Cannot find mechanism...\n"));
903 	  goto done;
904 	}
905 
906       if (args_info.mechanism_arg)
907 	mech = args_info.mechanism_arg;
908 
909       if (!authenticate (mech))
910 	return 1;
911 
912       /* Authenticate using mechanism */
913 
914       if (args_info.server_flag)
915 	res = gsasl_server_start (ctx, mech, &xctx);
916       else
917 	res = gsasl_client_start (ctx, mech, &xctx);
918       if (res != GSASL_OK)
919 	error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
920 	       gsasl_strerror (res));
921 
922       in = NULL;
923       out = NULL;
924 
925       if (!args_info.server_flag && args_info.no_client_first_flag)
926 	{
927 	  res = GSASL_NEEDS_MORE;
928 	  goto no_client_first;
929 	}
930 
931       do
932 	{
933 	  int res2;
934 
935 	  res = gsasl_step64 (xctx, in, &out);
936 	  if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
937 	    break;
938 
939 	  if (!step_send (out))
940 	    return 1;
941 
942 	no_client_first:
943 	  if (!args_info.quiet_given &&
944 	      !args_info.imap_flag && !args_info.smtp_flag)
945 	    {
946 	      if (args_info.server_flag)
947 		fprintf (stderr, _("Enter base64 authentication data "
948 				   "from client (press RET if none):\n"));
949 	      else
950 		fprintf (stderr, _("Enter base64 authentication data "
951 				   "from server (press RET if none):\n"));
952 	    }
953 
954 	  /* Return 1 on token, 2 on protocol success, 3 on protocol fail, 0 on
955 	     errors. */
956 	  res2 = step_recv (&in);
957 	  if (!res2)
958 	    return 1;
959 	  if (res2 == 3)
960 	    error (EXIT_FAILURE, 0, _("server error"));
961 	  if (res2 == 2)
962 	    break;
963 	}
964       while (args_info.imap_flag || args_info.smtp_flag
965 	     || res == GSASL_NEEDS_MORE);
966 
967       if (res != GSASL_OK)
968 	error (EXIT_FAILURE, 0, _("mechanism error: %s"),
969 	       gsasl_strerror (res));
970 
971       if (!args_info.quiet_given)
972 	{
973 	  if (args_info.server_flag)
974 	    fprintf (stderr, _("Server authentication "
975 			       "finished (client trusted)...\n"));
976 	  else
977 	    fprintf (stderr, _("Client authentication "
978 			       "finished (server trusted)...\n"));
979 	  fflush (stderr);
980 	}
981 
982       /* Transfer application payload */
983       if (args_info.application_data_flag)
984 	{
985 	  struct pollfd pfd[2];
986 	  char *sockbuf = NULL;
987 	  /* we read chunks of 1000 bytes at a time */
988 	  size_t sockpos = 0, sockalloc = 0, sockalloc1 = 1000;
989 
990 	  /* Setup pollfd structs... */
991 	  pfd[0].fd = STDIN_FILENO;
992 	  pfd[0].events = POLLIN;
993 	  if (sockfd)
994 	    {
995 	      pfd[1].fd = sockfd;
996 	      pfd[1].events = POLLIN;
997 	    }
998 
999 	  if (!args_info.quiet_given)
1000 	    {
1001 	      fprintf (stderr,
1002 		       _("Enter application data (EOF to finish):\n"));
1003 	      fflush (stderr);
1004 	    }
1005 
1006 	  while (1)
1007 	    {
1008 	      int rc;
1009 
1010 	      pfd[0].revents = 0;
1011 	      pfd[1].revents = 0;
1012 
1013 	      rc = poll (pfd, sockfd ? 2 : 1, -1);
1014 	      if (rc < 0 && errno == EINTR)
1015 		continue;
1016 
1017 	      /* Always check for errors */
1018 	      if (rc < 0)
1019 		error (EXIT_FAILURE, errno, "poll");
1020 
1021 	      /* We got data to read from stdin.. */
1022 	      if ((pfd[0].revents & (POLLIN | POLLERR)) == POLLIN)
1023 		{
1024 		  char *line = NULL;
1025 		  size_t n;
1026 		  ssize_t len;
1027 
1028 		  len = getline (&line, &n, stdin);
1029 		  if (len <= 0)
1030 		    break;
1031 
1032 		  if (args_info.imap_flag || args_info.smtp_flag)
1033 		    {
1034 		      if (len < 2 || strcmp (&line[len - 2], "\r\n") != 0)
1035 			{
1036 			  line = xrealloc (line, len + 2);
1037 			  line[len - 1] = '\r';
1038 			  line[len] = '\n';
1039 			  line[len + 1] = '\0';
1040 			  len++;
1041 			}
1042 		    }
1043 		  else
1044 		    {
1045 		      len--;
1046 		      line[len] = '\0';
1047 		    }
1048 
1049 		  res = gsasl_encode (xctx, line, len, &out, &output_len);
1050 		  if (res != GSASL_OK)
1051 		    break;
1052 
1053 		  if (sockfd)
1054 		    {
1055 		      len = _send (out, output_len);
1056 		      if (len != (ssize_t) output_len)
1057 			error (EXIT_FAILURE, errno, "write");
1058 		    }
1059 		  else if (!(strlen (line) == output_len &&
1060 			     memcmp (line, out, output_len) == 0))
1061 		    {
1062 		      res = gsasl_base64_to (out, output_len,
1063 					     &b64output, &b64output_len);
1064 		      if (res != GSASL_OK)
1065 			break;
1066 
1067 		      if (!args_info.quiet_given)
1068 			fprintf (stderr, _("Base64 encoded application "
1069 					   "data to send:\n"));
1070 		      fprintf (stdout, "%s\n", b64output);
1071 
1072 		      free (b64output);
1073 		    }
1074 
1075 		  free (line);
1076 		  free (out);
1077 		}
1078 	      /* If there was an error, quit.  */
1079 	      else if (pfd[0].revents & (POLLERR | POLLHUP))
1080 		{
1081 		  error (0, 0, "poll stdin");
1082 		  break;
1083 		}
1084 
1085 	      /* We got data to read from the socket.. */
1086 	      if (sockfd && (pfd[1].revents & (POLLIN | POLLERR)) == POLLIN)
1087 		{
1088 		  ssize_t len;
1089 
1090 		  if (sockalloc == sockpos)
1091 		    sockbuf = x2realloc (sockbuf, &sockalloc1);
1092 		  sockalloc = sockalloc1;
1093 
1094 		  len = _recv (&sockbuf[sockpos], sockalloc - sockpos);
1095 		  if (len <= 0)
1096 		    break;
1097 
1098 		  sockpos += len;
1099 
1100 		  res = gsasl_decode (xctx, sockbuf, sockpos,
1101 				      &out, &output_len);
1102 		  if (res == GSASL_NEEDS_MORE)
1103 		    {
1104 #define MAX_INPUT_SIZE	0x100000
1105 		      if (sockpos > MAX_INPUT_SIZE)
1106 			error (EXIT_FAILURE, 0,
1107 			       _("SASL record too large: %zu\n"), sockpos);
1108 		      continue;
1109 		    }
1110 		  if (res != GSASL_OK)
1111 		    break;
1112 
1113 		  free (sockbuf);
1114 		  sockbuf = NULL;
1115 		  sockpos = 0;
1116 		  sockalloc = 0;
1117 		  sockalloc1 = 1000;
1118 
1119 		  printf ("%.*s", (int) output_len, out);
1120 		  free (out);
1121 		}
1122 	      /* If there was an error, quit.  */
1123 	      else if (pfd[1].revents & (POLLERR | POLLHUP))
1124 		{
1125 		  error (0, 0, "poll socket");
1126 		  break;
1127 		}
1128 	    }
1129 
1130 	  if (res != GSASL_OK)
1131 	    error (EXIT_FAILURE, 0, _("encoding error: %s"),
1132 		   gsasl_strerror (res));
1133 	}
1134 
1135       if (!args_info.quiet_given)
1136 	fprintf (stderr, _("Session finished...\n"));
1137 
1138       if (!logout ())
1139 	return 1;
1140 
1141       gsasl_finish (xctx);
1142     }
1143 
1144   if (sockfd)
1145     {
1146 #ifdef HAVE_LIBGNUTLS
1147       if (using_tls)
1148 	{
1149 	  res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
1150 	  if (res < 0)
1151 	    error (EXIT_FAILURE, 0,
1152 		   _("terminating GnuTLS session failed: %s"),
1153 		   gnutls_strerror (res));
1154 
1155 	}
1156 #endif
1157       shutdown (sockfd, SHUT_RDWR);
1158       close (sockfd);
1159     }
1160 
1161 done:
1162   gsasl_done (ctx);
1163 
1164 #ifdef HAVE_LIBGNUTLS
1165   if (using_tls)
1166     {
1167       gnutls_deinit (session);
1168       gnutls_anon_free_client_credentials (anoncred);
1169       gnutls_certificate_free_credentials (x509cred);
1170       gnutls_global_deinit ();
1171     }
1172 #endif
1173 
1174   return EXIT_SUCCESS;
1175 }
1176