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