Bump versions.
[gsasl.git] / src / gsasl.c
blob24ae46e9b9cf488124e7d1561b01a481d9bc5eff
1 /* gsasl.c --- Command line interface to libgsasl.
2 * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Simon Josefsson
4 * This file is part of GNU SASL.
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.
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.
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/>.
21 #include "internal.h"
22 #include "callbacks.h"
23 #include "imap.h"
24 #include "smtp.h"
26 #ifdef HAVE_WS2TCPIP_H
27 # include <ws2tcpip.h>
28 #endif
30 #ifdef HAVE_LIBGNUTLS
31 # include <gnutls/gnutls.h>
32 gnutls_session session;
33 bool using_tls = false;
34 #endif
36 #define MAX_LINE_LENGTH BUFSIZ
38 struct gengetopt_args_info args_info;
39 int sockfd = 0;
41 int
42 writeln (const char *str)
44 printf ("%s\n", str);
46 if (sockfd)
48 ssize_t len;
50 #ifdef HAVE_LIBGNUTLS
51 if (using_tls)
53 /* GnuTLS < 1.2.9 cannot handle data != NULL && count == 0,
54 it will return an error. */
55 if (len > 0)
56 len = gnutls_record_send (session, str, strlen (str));
57 else
58 len = 0;
60 else
61 #endif
62 len = write (sockfd, str, strlen (str));
63 if (len != strlen (str))
64 return 0;
66 #define CRLF "\r\n"
68 #ifdef HAVE_LIBGNUTLS
69 if (using_tls)
70 len = gnutls_record_send (session, CRLF, strlen (CRLF));
71 else
72 #endif
73 len = write (sockfd, CRLF, strlen (CRLF));
74 if (len != strlen (CRLF))
75 return 0;
78 return 1;
81 int
82 readln (char **out)
84 if (sockfd)
86 ssize_t len;
87 size_t j = 0;
88 char input[MAX_LINE_LENGTH];
90 /* FIXME: Optimize and remove size limit. */
94 j++;
96 #ifdef HAVE_LIBGNUTLS
97 if (using_tls)
98 len = gnutls_record_recv (session, &input[j - 1], 1);
99 else
100 #endif
101 len = recv (sockfd, &input[j - 1], 1, 0);
102 if (len <= 0)
103 return 0;
105 while (input[j - 1] != '\n' && j < MAX_LINE_LENGTH);
106 input[j] = '\0';
108 *out = strdup (input);
110 printf ("%s", *out);
112 else
114 *out = readline ("");
115 if (*out == NULL)
116 return 0;
119 return 1;
122 static int
123 greeting (void)
125 if (args_info.imap_flag)
126 return imap_greeting ();
127 if (args_info.smtp_flag)
128 return smtp_greeting ();
130 return 1;
133 static int
134 has_starttls (void)
136 if (args_info.imap_flag)
137 return imap_has_starttls ();
138 if (args_info.smtp_flag)
139 return smtp_has_starttls ();
141 return 0;
144 static int
145 starttls (void)
147 if (args_info.imap_flag)
148 return imap_starttls ();
149 if (args_info.smtp_flag)
150 return smtp_starttls ();
152 return 1;
155 static int
156 select_mechanism (char **mechlist)
158 char *in;
160 if (args_info.imap_flag)
161 return imap_select_mechanism (mechlist);
162 if (args_info.smtp_flag)
163 return smtp_select_mechanism (mechlist);
165 if (args_info.mechanism_arg)
166 *mechlist = args_info.mechanism_arg;
167 else if (args_info.server_flag)
169 if (!args_info.quiet_given)
170 fprintf (stderr, _("Choose SASL mechanism:\n"));
171 if (!readln (&in))
172 return 0;
173 *mechlist = in;
175 else /* if (args_info.client_flag) */
177 if (!args_info.quiet_given)
178 fprintf (stderr,
179 _("Input list of SASL mechanisms supported by server:\n"));
180 if (!readln (&in))
181 return 0;
183 *mechlist = in;
186 return 1;
189 static int
190 authenticate (const char *mech)
192 if (args_info.imap_flag)
193 return imap_authenticate (mech);
194 if (args_info.smtp_flag)
195 return smtp_authenticate (mech);
197 if (!args_info.quiet_given)
198 fprintf (stderr, _("Using mechanism:\n"));
199 puts (mech);
201 return 1;
204 static int
205 step_send (const char *data)
207 if (args_info.imap_flag)
208 return imap_step_send (data);
209 if (args_info.smtp_flag)
210 return smtp_step_send (data);
212 if (!args_info.quiet_given)
214 if (args_info.server_flag)
215 fprintf (stderr, _("Output from server:\n"));
216 else
217 fprintf (stderr, _("Output from client:\n"));
219 fprintf (stdout, "%s\n", data);
221 return 1;
224 static int
225 step_recv (char **data)
227 if (args_info.imap_flag)
228 return imap_step_recv (data);
229 if (args_info.smtp_flag)
230 return smtp_step_recv (data);
232 if (!readln (data))
233 return 0;
235 return 1;
238 static int
239 auth_finish (void)
241 if (args_info.imap_flag)
242 return imap_auth_finish ();
243 if (args_info.smtp_flag)
244 return smtp_auth_finish ();
246 return 1;
249 static int
250 logout (void)
252 if (args_info.imap_flag)
253 return imap_logout ();
254 if (args_info.smtp_flag)
255 return smtp_logout ();
257 return 1;
261 main (int argc, char *argv[])
263 Gsasl *ctx = NULL;
264 int res;
265 char *in;
266 char *connect_hostname;
267 char *connect_service;
268 #ifdef HAVE_LIBGNUTLS
269 const int kx_prio[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS,
270 GNUTLS_KX_DHE_RSA, GNUTLS_KX_ANON_DH, 0
272 gnutls_anon_client_credentials anoncred;
273 gnutls_certificate_credentials x509cred;
274 int ret, outerr;
275 #endif
277 set_program_name (argv[0]);
278 setlocale (LC_ALL, "");
279 bindtextdomain (PACKAGE, LOCALEDIR);
280 textdomain (PACKAGE);
282 #ifdef HAVE_WS2TCPIP_H
284 WORD wVersionRequested;
285 WSADATA wsaData;
286 int r;
288 wVersionRequested = MAKEWORD(2, 0);
289 r = WSAStartup( wVersionRequested, &wsaData);
290 if (r)
291 error (EXIT_FAILURE, 0, _("Cannot initialize Windows sockets."));
293 #endif
295 if (cmdline_parser (argc, argv, &args_info) != 0)
296 return 1;
298 if (!(args_info.client_flag || args_info.client_given) &&
299 !args_info.server_given &&
300 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
301 error (EXIT_FAILURE, 0,
302 _("missing argument\nTry `%s --help' for more information."),
303 program_name);
305 if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
306 (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
307 error (EXIT_FAILURE, 0,
308 _("need both --x509-cert-file and --x509-key-file"));
310 if (args_info.starttls_flag && args_info.no_starttls_flag)
311 error (EXIT_FAILURE, 0,
312 _("cannot use both --starttls and --no-starttls"));
314 if (args_info.smtp_flag && args_info.imap_flag)
315 error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));
317 if (!args_info.connect_given && args_info.inputs_num == 0 &&
318 !args_info.client_given && !args_info.server_given &&
319 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
321 cmdline_parser_print_help ();
322 return EXIT_SUCCESS;
325 if (args_info.connect_given)
327 if (strrchr (args_info.connect_arg, ':'))
329 connect_hostname = strdup (args_info.connect_arg);
330 *strrchr (connect_hostname, ':') = '\0';
331 connect_service = strdup (strrchr (args_info.connect_arg, ':') + 1);
333 else
335 connect_hostname = strdup (args_info.connect_arg);
336 if (args_info.smtp_flag)
337 connect_service = strdup ("smtp");
338 else
339 connect_service = strdup ("imap");
342 else if (args_info.inputs_num > 0)
344 connect_hostname = args_info.inputs[0];
345 if (args_info.inputs_num > 1)
346 connect_service = args_info.inputs[1];
347 else if (args_info.smtp_flag)
348 connect_service = strdup ("smtp");
349 else
350 connect_service = strdup ("imap");
353 if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
355 if (strcmp (connect_service, "25") == 0 ||
356 strcmp (connect_service, "smtp") == 0)
357 args_info.smtp_flag = 1;
358 else
359 args_info.imap_flag = 1;
362 if (args_info.imap_flag && !args_info.service_given)
363 args_info.service_arg = strdup ("imap");
365 if (args_info.smtp_flag && !args_info.service_given)
366 args_info.service_arg = strdup ("smtp");
368 if (args_info.imap_flag || args_info.smtp_flag)
369 args_info.no_client_first_flag = 1;
371 if (connect_hostname && !args_info.hostname_arg)
372 args_info.hostname_arg = strdup (connect_hostname);
374 res = gsasl_init (&ctx);
375 if (res != GSASL_OK)
376 error (EXIT_FAILURE, 0, _("initialization failure: %s"),
377 gsasl_strerror (res));
379 gsasl_callback_set (ctx, callback);
381 if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
383 char *mechs;
385 if (args_info.client_mechanisms_flag)
386 res = gsasl_client_mechlist (ctx, &mechs);
387 else
388 res = gsasl_server_mechlist (ctx, &mechs);
390 if (res != GSASL_OK)
391 error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
392 gsasl_strerror (res));
394 if (!args_info.quiet_given)
396 if (args_info.client_mechanisms_flag)
397 fprintf (stderr,
398 _("This client supports the following mechanisms:\n"));
399 else
400 fprintf (stderr,
401 _("This server supports the following mechanisms:\n"));
404 fprintf (stdout, "%s\n", mechs);
406 free (mechs);
408 return EXIT_SUCCESS;
411 if (args_info.connect_given || args_info.inputs_num > 0)
413 struct sockaddr connect_addr;
414 struct sockaddr *saddr = &connect_addr;
415 size_t saddrlen = sizeof (*saddr);
416 struct addrinfo hints;
417 struct addrinfo *ai0, *ai;
419 memset (&hints, 0, sizeof (hints));
420 hints.ai_flags = AI_CANONNAME;
421 hints.ai_socktype = SOCK_STREAM;
422 res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
423 if (res != 0)
424 error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
425 gai_strerror (res));
427 for (ai = ai0; ai; ai = ai->ai_next)
429 fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname));
431 sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
432 if (sockfd < 0)
434 error (0, errno, "socket");
435 continue;
438 if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
440 int save_errno = errno;
441 close (sockfd);
442 sockfd = -1;
443 error (0, save_errno, "connect");
444 continue;
446 break;
449 if (sockfd < 0)
450 error (EXIT_FAILURE, errno, "socket");
452 saddr = ai->ai_addr;
453 saddrlen = ai->ai_addrlen;
455 freeaddrinfo (ai);
458 if (!greeting ())
459 return 1;
461 #ifdef HAVE_LIBGNUTLS
462 if (sockfd && !args_info.no_starttls_flag &&
463 (args_info.starttls_flag || has_starttls ()))
465 res = gnutls_global_init ();
466 if (res < 0)
467 error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
468 gnutls_strerror (res));
470 res = gnutls_init (&session, GNUTLS_CLIENT);
471 if (res < 0)
472 error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
473 gnutls_strerror (res));
475 res = gnutls_set_default_priority (session);
476 if (res < 0)
477 error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
478 gnutls_strerror (res));
480 res = gnutls_anon_allocate_client_credentials (&anoncred);
481 if (res < 0)
482 error (EXIT_FAILURE, 0,
483 _("allocating anonymous GnuTLS credential: %s"),
484 gnutls_strerror (res));
486 res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
487 if (res < 0)
488 error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
489 gnutls_strerror (res));
491 res = gnutls_certificate_allocate_credentials (&x509cred);
492 if (res < 0)
493 error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
494 gnutls_strerror (res));
496 if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
497 res = gnutls_certificate_set_x509_key_file
498 (x509cred, args_info.x509_cert_file_arg,
499 args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
500 if (res != GNUTLS_E_SUCCESS)
501 error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
502 gnutls_strerror (res));
504 if (args_info.x509_ca_file_arg)
506 res = gnutls_certificate_set_x509_trust_file
507 (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
508 if (res < 0)
509 error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
510 gnutls_strerror (res));
511 if (res == 0)
512 error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
515 res =
516 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509cred);
517 if (res < 0)
518 error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
519 gnutls_strerror (res));
521 res = gnutls_kx_set_priority (session, kx_prio);
522 if (res < 0)
523 error (EXIT_FAILURE, 0, _("setting GnuTLS key exchange priority: %s"),
524 gnutls_strerror (res));
526 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) sockfd);
528 if (!starttls ())
529 return 1;
531 res = gnutls_handshake (session);
532 if (res < 0)
533 error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
534 gnutls_strerror (res));
536 if (args_info.x509_ca_file_arg)
538 unsigned int status;
540 res = gnutls_certificate_verify_peers2 (session, &status);
541 if (res < 0)
542 error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
543 gnutls_strerror (res));
545 if (status & GNUTLS_CERT_INVALID)
546 error (EXIT_FAILURE, 0, _("server certificate is not trusted"));
548 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
549 error (EXIT_FAILURE, 0,
550 _("server certificate hasn't got a known issuer"));
552 if (status & GNUTLS_CERT_REVOKED)
553 error (EXIT_FAILURE, 0, _("server certificate has been revoked"));
555 if (status != 0)
556 error (EXIT_FAILURE, 0,
557 _("could not verify server certificate (rc=%d)"), status);
560 using_tls = true;
562 #endif
564 if (args_info.client_flag || args_info.client_given || args_info.server_given)
566 char *out;
567 char *b64output;
568 size_t output_len;
569 size_t b64output_len;
570 const char *mech;
571 Gsasl_session *xctx = NULL;
573 if (!select_mechanism (&in))
574 return 1;
576 mech = gsasl_client_suggest_mechanism (ctx, in);
577 if (mech == NULL)
579 fprintf (stderr, _("Cannot find mechanism...\n"));
580 return 0;
583 if (args_info.mechanism_arg)
584 mech = args_info.mechanism_arg;
586 if (!authenticate (mech))
587 return 1;
589 /* Authenticate using mechanism */
591 if (args_info.server_flag)
592 res = gsasl_server_start (ctx, mech, &xctx);
593 else
594 res = gsasl_client_start (ctx, mech, &xctx);
595 if (res != GSASL_OK)
596 error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
597 gsasl_strerror (res));
599 in = NULL;
600 out = NULL;
602 if (!args_info.server_flag && args_info.no_client_first_flag)
604 res = GSASL_NEEDS_MORE;
605 goto no_client_first;
610 res = gsasl_step64 (xctx, in, &out);
611 if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
612 break;
614 if (!step_send (out))
615 return 1;
617 if (res != GSASL_NEEDS_MORE)
618 break;
620 no_client_first:
621 if (!args_info.quiet_given &&
622 !args_info.imap_flag && !args_info.smtp_flag)
624 if (args_info.server_flag)
625 fprintf (stderr, _("Enter base64 authentication data "
626 "from client (press RET if none):\n"));
627 else
628 fprintf (stderr, _("Enter base64 authentication data "
629 "from server (press RET if none):\n"));
632 if (!step_recv (&in))
633 return 1;
635 while (res == GSASL_NEEDS_MORE);
637 if (res != GSASL_OK)
638 error (EXIT_FAILURE, 0, _("mechanism error: %s"),
639 gsasl_strerror (res));
641 if (!auth_finish ())
642 return 1;
644 if (!args_info.quiet_given)
646 if (args_info.server_flag)
647 fprintf (stderr, _("Server authentication "
648 "finished (client trusted)...\n"));
649 else
650 fprintf (stderr, _("Client authentication "
651 "finished (server trusted)...\n"));
654 /* Transfer application payload */
655 if (args_info.application_data_flag)
657 fd_set readfds;
659 FD_ZERO (&readfds);
660 FD_SET (STDIN_FILENO, &readfds);
661 if (sockfd)
662 FD_SET (sockfd, &readfds);
664 if (!args_info.quiet_given)
665 fprintf (stderr, _("Enter application data (EOF to finish):\n"));
667 while (select (sockfd + 1, &readfds, NULL, NULL, NULL))
669 if (FD_ISSET (STDIN_FILENO, &readfds))
671 char input[MAX_LINE_LENGTH];
673 input[0] = '\0';
674 if (fgets (input, MAX_LINE_LENGTH - 2, stdin) == NULL)
675 break;
676 if (args_info.imap_flag || args_info.smtp_flag)
678 int pos = strlen (input);
679 input[pos - 1] = '\r';
680 input[pos] = '\n';
681 input[pos + 1] = '\0';
683 else
684 input[strlen (input) - 1] = '\0';
686 res = gsasl_encode (xctx, input, strlen (input),
687 &out, &output_len);
688 if (res != GSASL_OK)
689 break;
691 if (sockfd)
693 ssize_t len;
694 #ifdef HAVE_LIBGNUTLS
695 if (using_tls)
696 len = gnutls_record_send (session, out, output_len);
697 else
698 #endif
699 len = write (sockfd, out, output_len);
700 if (len != output_len)
701 return 0;
703 else if (!(strlen (input) == output_len &&
704 memcmp (input, out, output_len) == 0))
706 res = gsasl_base64_to (out, output_len,
707 &b64output, &b64output_len);
708 if (res != GSASL_OK)
709 break;
711 if (!args_info.quiet_given)
712 fprintf (stderr, _("Base64 encoded application "
713 "data to send:\n"));
714 fprintf (stdout, "%s\n", b64output);
716 free (b64output);
719 free (out);
722 if (sockfd && FD_ISSET (sockfd, &readfds))
724 if (!readln (&in))
725 break;
726 free (in);
729 FD_ZERO (&readfds);
730 FD_SET (STDIN_FILENO, &readfds);
731 if (sockfd)
732 FD_SET (sockfd, &readfds);
735 if (res != GSASL_OK)
736 error (EXIT_FAILURE, 0, _("encoding error: %s"), res,
737 gsasl_strerror (res));
740 if (!args_info.quiet_given)
741 fprintf (stderr, _("Session finished...\n"));
743 if (!logout ())
744 return 1;
746 gsasl_finish (xctx);
749 if (sockfd)
751 #ifdef HAVE_LIBGNUTLS
752 if (using_tls)
754 res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
755 if (res < 0)
756 error (EXIT_FAILURE, 0,
757 _("terminating GnuTLS session failed: %s"),
758 gnutls_strerror (res));
761 #endif
762 shutdown (sockfd, SHUT_RDWR);
763 close (sockfd);
766 gsasl_done (ctx);
768 #ifdef HAVE_LIBGNUTLS
769 if (using_tls)
771 gnutls_deinit (session);
772 gnutls_anon_free_client_credentials (anoncred);
773 gnutls_certificate_free_credentials (x509cred);
774 gnutls_global_deinit ();
776 #endif
778 return 0;