*** empty log message ***
[gsasl.git] / src / gsasl.c
blobf25ab7201bf4f5fa942aaa6207949789f0276a09
1 /* gsasl.c --- Command line interface to libgsasl.
2 * Copyright (C) 2002, 2003, 2004, 2005, 2006 Simon Josefsson
4 * This file is part of GNU SASL.
6 * GNU SASL 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 2 of the License, or
9 * (at your option) any later version.
11 * GNU SASL 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.
16 * You should have received a copy of the GNU General Public License
17 * along with GNU SASL; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "internal.h"
23 #include "callbacks.h"
24 #include "imap.h"
25 #include "smtp.h"
27 #ifdef HAVE_WS2TCPIP_H
28 # include <ws2tcpip.h>
29 #endif
31 #ifdef HAVE_LIBGNUTLS
32 # include <gnutls/gnutls.h>
33 gnutls_session session;
34 bool using_tls = false;
35 #endif
37 #define MAX_LINE_LENGTH BUFSIZ
39 struct gengetopt_args_info args_info;
40 int sockfd = 0;
42 int
43 writeln (const char *str)
45 printf ("%s\n", str);
47 if (sockfd)
49 ssize_t len;
51 #ifdef HAVE_LIBGNUTLS
52 if (using_tls)
54 /* GnuTLS < 1.2.9 cannot handle data != NULL && count == 0,
55 it will return an error. */
56 if (len > 0)
57 len = gnutls_record_send (session, str, strlen (str));
58 else
59 len = 0;
61 else
62 #endif
63 len = write (sockfd, str, strlen (str));
64 if (len != strlen (str))
65 return 0;
67 #define CRLF "\r\n"
69 #ifdef HAVE_LIBGNUTLS
70 if (using_tls)
71 len = gnutls_record_send (session, CRLF, strlen (CRLF));
72 else
73 #endif
74 len = write (sockfd, CRLF, strlen (CRLF));
75 if (len != strlen (CRLF))
76 return 0;
79 return 1;
82 int
83 readln (char **out)
85 if (sockfd)
87 ssize_t len;
88 size_t j = 0;
89 char input[MAX_LINE_LENGTH];
91 /* FIXME: Optimize and remove size limit. */
95 j++;
97 #ifdef HAVE_LIBGNUTLS
98 if (using_tls)
99 len = gnutls_record_recv (session, &input[j - 1], 1);
100 else
101 #endif
102 len = recv (sockfd, &input[j - 1], 1, 0);
103 if (len <= 0)
104 return 0;
106 while (input[j - 1] != '\n' && j < MAX_LINE_LENGTH);
107 input[j] = '\0';
109 *out = strdup (input);
111 printf ("%s", *out);
113 else
115 *out = readline ("");
116 if (*out == NULL)
117 return 0;
120 return 1;
123 static int
124 greeting (void)
126 if (args_info.imap_flag)
127 return imap_greeting ();
128 if (args_info.smtp_flag)
129 return smtp_greeting ();
131 return 1;
134 static int
135 has_starttls (void)
137 if (args_info.imap_flag)
138 return imap_has_starttls ();
139 if (args_info.smtp_flag)
140 return smtp_has_starttls ();
142 return 0;
145 static int
146 starttls (void)
148 if (args_info.imap_flag)
149 return imap_starttls ();
150 if (args_info.smtp_flag)
151 return smtp_starttls ();
153 return 1;
156 static int
157 select_mechanism (char **mechlist)
159 char *in;
161 if (args_info.imap_flag)
162 return imap_select_mechanism (mechlist);
163 if (args_info.smtp_flag)
164 return smtp_select_mechanism (mechlist);
166 if (args_info.mechanism_arg)
167 *mechlist = args_info.mechanism_arg;
168 else if (args_info.server_flag)
170 if (!args_info.quiet_given)
171 fprintf (stderr, _("Choose SASL mechanism:\n"));
172 if (!readln (&in))
173 return 0;
174 *mechlist = in;
176 else /* if (args_info.client_flag) */
178 if (!args_info.quiet_given)
179 fprintf (stderr,
180 _("Input list of SASL mechanisms supported by server:\n"));
181 if (!readln (&in))
182 return 0;
184 *mechlist = in;
187 return 1;
190 static int
191 authenticate (const char *mech)
193 if (args_info.imap_flag)
194 return imap_authenticate (mech);
195 if (args_info.smtp_flag)
196 return smtp_authenticate (mech);
198 if (!args_info.quiet_given)
199 fprintf (stderr, _("Using mechanism:\n"));
200 puts (mech);
202 return 1;
205 static int
206 step_send (const char *data)
208 if (args_info.imap_flag)
209 return imap_step_send (data);
210 if (args_info.smtp_flag)
211 return smtp_step_send (data);
213 if (!args_info.quiet_given)
215 if (args_info.server_flag)
216 fprintf (stderr, _("Output from server:\n"));
217 else
218 fprintf (stderr, _("Output from client:\n"));
220 fprintf (stdout, "%s\n", data);
222 return 1;
225 static int
226 step_recv (char **data)
228 if (args_info.imap_flag)
229 return imap_step_recv (data);
230 if (args_info.smtp_flag)
231 return smtp_step_recv (data);
233 if (!readln (data))
234 return 0;
236 return 1;
239 static int
240 auth_finish (void)
242 if (args_info.imap_flag)
243 return imap_auth_finish ();
244 if (args_info.smtp_flag)
245 return smtp_auth_finish ();
247 return 1;
250 static int
251 logout (void)
253 if (args_info.imap_flag)
254 return imap_logout ();
255 if (args_info.smtp_flag)
256 return smtp_logout ();
258 return 1;
262 main (int argc, char *argv[])
264 Gsasl *ctx = NULL;
265 int res;
266 char *in;
267 char *connect_hostname;
268 char *connect_service;
269 #ifdef HAVE_LIBGNUTLS
270 const int kx_prio[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS,
271 GNUTLS_KX_DHE_RSA, GNUTLS_KX_ANON_DH, 0
273 gnutls_anon_client_credentials anoncred;
274 gnutls_certificate_credentials x509cred;
275 int ret, outerr;
276 #endif
278 set_program_name (argv[0]);
279 setlocale (LC_ALL, "");
280 bindtextdomain (PACKAGE, LOCALEDIR);
281 textdomain (PACKAGE);
283 #ifdef HAVE_WS2TCPIP_H
285 WORD wVersionRequested;
286 WSADATA wsaData;
287 int r;
289 wVersionRequested = MAKEWORD(2, 0);
290 r = WSAStartup( wVersionRequested, &wsaData);
291 if (r)
292 error (EXIT_FAILURE, 0, _("Cannot initialize Windows sockets."));
294 #endif
296 if (cmdline_parser (argc, argv, &args_info) != 0)
297 return 1;
299 if (!(args_info.client_flag || args_info.client_given) &&
300 !args_info.server_given &&
301 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
302 error (EXIT_FAILURE, 0,
303 _("missing argument\nTry `%s --help' for more information."),
304 program_name);
306 if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
307 (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
308 error (EXIT_FAILURE, 0,
309 _("need both --x509-cert-file and --x509-key-file"));
311 if (args_info.starttls_flag && args_info.no_starttls_flag)
312 error (EXIT_FAILURE, 0,
313 _("cannot use both --starttls and --no-starttls"));
315 if (args_info.smtp_flag && args_info.imap_flag)
316 error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));
318 if (!args_info.connect_given && args_info.inputs_num == 0 &&
319 !args_info.client_given && !args_info.server_given &&
320 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
322 cmdline_parser_print_help ();
323 return EXIT_SUCCESS;
326 if (args_info.connect_given)
328 if (strrchr (args_info.connect_arg, ':'))
330 connect_hostname = strdup (args_info.connect_arg);
331 *strrchr (connect_hostname, ':') = '\0';
332 connect_service = strdup (strrchr (args_info.connect_arg, ':') + 1);
334 else
336 connect_hostname = strdup (args_info.connect_arg);
337 if (args_info.smtp_flag)
338 connect_service = strdup ("smtp");
339 else
340 connect_service = strdup ("imap");
343 else if (args_info.inputs_num > 0)
345 connect_hostname = args_info.inputs[0];
346 if (args_info.inputs_num > 1)
347 connect_service = args_info.inputs[1];
348 else if (args_info.smtp_flag)
349 connect_service = strdup ("smtp");
350 else
351 connect_service = strdup ("imap");
354 if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
356 if (strcmp (connect_service, "25") == 0 ||
357 strcmp (connect_service, "smtp") == 0)
358 args_info.smtp_flag = 1;
359 else
360 args_info.imap_flag = 1;
363 if (args_info.imap_flag && !args_info.service_given)
364 args_info.service_arg = strdup ("imap");
366 if (args_info.smtp_flag && !args_info.service_given)
367 args_info.service_arg = strdup ("smtp");
369 if (args_info.imap_flag || args_info.smtp_flag)
370 args_info.no_client_first_flag = 1;
372 if (connect_hostname && !args_info.hostname_arg)
373 args_info.hostname_arg = strdup (connect_hostname);
375 res = gsasl_init (&ctx);
376 if (res != GSASL_OK)
377 error (EXIT_FAILURE, 0, _("initialization failure: %s"),
378 gsasl_strerror (res));
380 gsasl_callback_set (ctx, callback);
382 if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
384 char *mechs;
386 if (args_info.client_mechanisms_flag)
387 res = gsasl_client_mechlist (ctx, &mechs);
388 else
389 res = gsasl_server_mechlist (ctx, &mechs);
391 if (res != GSASL_OK)
392 error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
393 gsasl_strerror (res));
395 if (!args_info.quiet_given)
397 if (args_info.client_mechanisms_flag)
398 fprintf (stderr,
399 _("This client supports the following mechanisms:\n"));
400 else
401 fprintf (stderr,
402 _("This server supports the following mechanisms:\n"));
405 fprintf (stdout, "%s\n", mechs);
407 free (mechs);
409 return EXIT_SUCCESS;
412 if (args_info.connect_given || args_info.inputs_num > 0)
414 struct sockaddr connect_addr;
415 struct sockaddr *saddr = &connect_addr;
416 size_t saddrlen = sizeof (*saddr);
417 struct addrinfo hints;
418 struct addrinfo *ai0, *ai;
420 memset (&hints, 0, sizeof (hints));
421 hints.ai_flags = AI_CANONNAME;
422 hints.ai_socktype = SOCK_STREAM;
423 res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
424 if (res != 0)
425 error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
426 gai_strerror (res));
428 for (ai = ai0; ai; ai = ai->ai_next)
430 fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname));
432 sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
433 if (sockfd < 0)
435 error (0, errno, "socket");
436 continue;
439 if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
441 int save_errno = errno;
442 close (sockfd);
443 sockfd = -1;
444 error (0, save_errno, "connect");
445 continue;
447 break;
450 if (sockfd < 0)
451 error (EXIT_FAILURE, errno, "socket");
453 saddr = ai->ai_addr;
454 saddrlen = ai->ai_addrlen;
456 freeaddrinfo (ai);
459 if (!greeting ())
460 return 1;
462 #ifdef HAVE_LIBGNUTLS
463 if (sockfd && !args_info.no_starttls_flag &&
464 (args_info.starttls_flag || has_starttls ()))
466 res = gnutls_global_init ();
467 if (res < 0)
468 error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
469 gnutls_strerror (res));
471 res = gnutls_init (&session, GNUTLS_CLIENT);
472 if (res < 0)
473 error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
474 gnutls_strerror (res));
476 res = gnutls_set_default_priority (session);
477 if (res < 0)
478 error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
479 gnutls_strerror (res));
481 res = gnutls_anon_allocate_client_credentials (&anoncred);
482 if (res < 0)
483 error (EXIT_FAILURE, 0,
484 _("allocating anonymous GnuTLS credential: %s"),
485 gnutls_strerror (res));
487 res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
488 if (res < 0)
489 error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
490 gnutls_strerror (res));
492 res = gnutls_certificate_allocate_credentials (&x509cred);
493 if (res < 0)
494 error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
495 gnutls_strerror (res));
497 if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
498 res = gnutls_certificate_set_x509_key_file
499 (x509cred, args_info.x509_cert_file_arg,
500 args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
501 if (res != GNUTLS_E_SUCCESS)
502 error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
503 gnutls_strerror (res));
505 if (args_info.x509_ca_file_arg)
507 res = gnutls_certificate_set_x509_trust_file
508 (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
509 if (res < 0)
510 error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
511 gnutls_strerror (res));
512 if (res == 0)
513 error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
516 res =
517 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509cred);
518 if (res < 0)
519 error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
520 gnutls_strerror (res));
522 res = gnutls_kx_set_priority (session, kx_prio);
523 if (res < 0)
524 error (EXIT_FAILURE, 0, _("setting GnuTLS key exchange priority: %s"),
525 gnutls_strerror (res));
527 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) sockfd);
529 if (!starttls ())
530 return 1;
532 res = gnutls_handshake (session);
533 if (res < 0)
534 error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
535 gnutls_strerror (res));
537 if (args_info.x509_ca_file_arg)
539 unsigned int status;
541 res = gnutls_certificate_verify_peers2 (session, &status);
542 if (res < 0)
543 error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
544 gnutls_strerror (res));
546 if (status & GNUTLS_CERT_INVALID)
547 error (EXIT_FAILURE, 0, _("server certificate is not trusted"));
549 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
550 error (EXIT_FAILURE, 0,
551 _("server certificate hasn't got a known issuer"));
553 if (status & GNUTLS_CERT_REVOKED)
554 error (EXIT_FAILURE, 0, _("server certificate has been revoked"));
556 if (status != 0)
557 error (EXIT_FAILURE, 0,
558 _("could not verify server certificate (rc=%d)"), status);
561 using_tls = true;
563 #endif
565 if (args_info.client_flag || args_info.client_given || args_info.server_given)
567 char *out;
568 char *b64output;
569 size_t output_len;
570 size_t b64output_len;
571 const char *mech;
572 Gsasl_session *xctx = NULL;
574 if (!select_mechanism (&in))
575 return 1;
577 mech = gsasl_client_suggest_mechanism (ctx, in);
578 if (mech == NULL)
580 fprintf (stderr, _("Cannot find mechanism...\n"));
581 return 0;
584 if (args_info.mechanism_arg)
585 mech = args_info.mechanism_arg;
587 if (!authenticate (mech))
588 return 1;
590 /* Authenticate using mechanism */
592 if (args_info.server_flag)
593 res = gsasl_server_start (ctx, mech, &xctx);
594 else
595 res = gsasl_client_start (ctx, mech, &xctx);
596 if (res != GSASL_OK)
597 error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
598 gsasl_strerror (res));
600 in = NULL;
601 out = NULL;
603 if (!args_info.server_flag && args_info.no_client_first_flag)
605 res = GSASL_NEEDS_MORE;
606 goto no_client_first;
611 res = gsasl_step64 (xctx, in, &out);
612 if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
613 break;
615 if (!step_send (out))
616 return 1;
618 if (res != GSASL_NEEDS_MORE)
619 break;
621 no_client_first:
622 if (!args_info.quiet_given &&
623 !args_info.imap_flag && !args_info.smtp_flag)
625 if (args_info.server_flag)
626 fprintf (stderr, _("Enter base64 authentication data "
627 "from client (press RET if none):\n"));
628 else
629 fprintf (stderr, _("Enter base64 authentication data "
630 "from server (press RET if none):\n"));
633 if (!step_recv (&in))
634 return 1;
636 while (res == GSASL_NEEDS_MORE);
638 if (res != GSASL_OK)
639 error (EXIT_FAILURE, 0, _("mechanism error: %s"),
640 gsasl_strerror (res));
642 if (!auth_finish ())
643 return 1;
645 if (!args_info.quiet_given)
647 if (args_info.server_flag)
648 fprintf (stderr, _("Server authentication "
649 "finished (client trusted)...\n"));
650 else
651 fprintf (stderr, _("Client authentication "
652 "finished (server trusted)...\n"));
655 /* Transfer application payload */
656 if (args_info.application_data_flag)
658 fd_set readfds;
660 FD_ZERO (&readfds);
661 FD_SET (STDIN_FILENO, &readfds);
662 if (sockfd)
663 FD_SET (sockfd, &readfds);
665 if (!args_info.quiet_given)
666 fprintf (stderr, _("Enter application data (EOF to finish):\n"));
668 while (select (sockfd + 1, &readfds, NULL, NULL, NULL))
670 if (FD_ISSET (STDIN_FILENO, &readfds))
672 char input[MAX_LINE_LENGTH];
674 input[0] = '\0';
675 if (fgets (input, MAX_LINE_LENGTH - 2, stdin) == NULL)
676 break;
677 if (args_info.imap_flag || args_info.smtp_flag)
679 int pos = strlen (input);
680 input[pos - 1] = '\r';
681 input[pos] = '\n';
682 input[pos + 1] = '\0';
684 else
685 input[strlen (input) - 1] = '\0';
687 res = gsasl_encode (xctx, input, strlen (input),
688 &out, &output_len);
689 if (res != GSASL_OK)
690 break;
692 if (sockfd)
694 ssize_t len;
695 #ifdef HAVE_LIBGNUTLS
696 if (using_tls)
697 len = gnutls_record_send (session, out, output_len);
698 else
699 #endif
700 len = write (sockfd, out, output_len);
701 if (len != output_len)
702 return 0;
704 else if (!(strlen (input) == output_len &&
705 memcmp (input, out, output_len) == 0))
707 res = gsasl_base64_to (out, output_len,
708 &b64output, &b64output_len);
709 if (res != GSASL_OK)
710 break;
712 if (!args_info.quiet_given)
713 fprintf (stderr, _("Base64 encoded application "
714 "data to send:\n"));
715 fprintf (stdout, "%s\n", b64output);
717 free (b64output);
720 free (out);
723 if (sockfd && FD_ISSET (sockfd, &readfds))
725 if (!readln (&in))
726 break;
727 free (in);
730 FD_ZERO (&readfds);
731 FD_SET (STDIN_FILENO, &readfds);
732 if (sockfd)
733 FD_SET (sockfd, &readfds);
736 if (res != GSASL_OK)
737 error (EXIT_FAILURE, 0, _("encoding error: %s"), res,
738 gsasl_strerror (res));
741 if (!args_info.quiet_given)
742 fprintf (stderr, _("Session finished...\n"));
744 if (!logout ())
745 return 1;
747 gsasl_finish (xctx);
750 if (sockfd)
752 #ifdef HAVE_LIBGNUTLS
753 if (using_tls)
755 res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
756 if (res < 0)
757 error (EXIT_FAILURE, 0,
758 _("terminating GnuTLS session failed: %s"),
759 gnutls_strerror (res));
762 #endif
763 shutdown (sockfd, SHUT_RDWR);
764 close (sockfd);
767 gsasl_done (ctx);
769 #ifdef HAVE_LIBGNUTLS
770 if (using_tls)
772 gnutls_deinit (session);
773 gnutls_anon_free_client_credentials (anoncred);
774 gnutls_certificate_free_credentials (x509cred);
775 gnutls_global_deinit ();
777 #endif
779 return 0;