Add.
[gsasl.git] / src / gsasl.c
blobc0a262015c11833607b295ccdf728099e7c1df9b
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 = 0;
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 = NULL;
267 char *connect_service = NULL;
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 #endif
276 set_program_name (argv[0]);
277 setlocale (LC_ALL, "");
278 bindtextdomain (PACKAGE, LOCALEDIR);
279 textdomain (PACKAGE);
281 #ifdef HAVE_WS2TCPIP_H
283 WORD wVersionRequested;
284 WSADATA wsaData;
285 int r;
287 wVersionRequested = MAKEWORD(2, 0);
288 r = WSAStartup( wVersionRequested, &wsaData);
289 if (r)
290 error (EXIT_FAILURE, 0, _("Cannot initialize Windows sockets."));
292 #endif
294 if (cmdline_parser (argc, argv, &args_info) != 0)
295 return 1;
297 if (!(args_info.client_flag || args_info.client_given) &&
298 !args_info.server_given &&
299 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
300 error (EXIT_FAILURE, 0,
301 _("missing argument\nTry `%s --help' for more information."),
302 program_name);
304 if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
305 (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
306 error (EXIT_FAILURE, 0,
307 _("need both --x509-cert-file and --x509-key-file"));
309 if (args_info.starttls_flag && args_info.no_starttls_flag)
310 error (EXIT_FAILURE, 0,
311 _("cannot use both --starttls and --no-starttls"));
313 if (args_info.smtp_flag && args_info.imap_flag)
314 error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));
316 if (!args_info.connect_given && args_info.inputs_num == 0 &&
317 !args_info.client_given && !args_info.server_given &&
318 !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
320 cmdline_parser_print_help ();
321 return EXIT_SUCCESS;
324 if (args_info.connect_given)
326 if (strrchr (args_info.connect_arg, ':'))
328 connect_hostname = strdup (args_info.connect_arg);
329 *strrchr (connect_hostname, ':') = '\0';
330 connect_service = strdup (strrchr (args_info.connect_arg, ':') + 1);
332 else
334 connect_hostname = strdup (args_info.connect_arg);
335 if (args_info.smtp_flag)
336 connect_service = strdup ("smtp");
337 else
338 connect_service = strdup ("imap");
341 else if (args_info.inputs_num > 0)
343 connect_hostname = args_info.inputs[0];
344 if (args_info.inputs_num > 1)
345 connect_service = args_info.inputs[1];
346 else if (args_info.smtp_flag)
347 connect_service = strdup ("smtp");
348 else
349 connect_service = strdup ("imap");
352 if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
354 if (strcmp (connect_service, "25") == 0 ||
355 strcmp (connect_service, "smtp") == 0)
356 args_info.smtp_flag = 1;
357 else
358 args_info.imap_flag = 1;
361 if (args_info.imap_flag && !args_info.service_given)
362 args_info.service_arg = strdup ("imap");
364 if (args_info.smtp_flag && !args_info.service_given)
365 args_info.service_arg = strdup ("smtp");
367 if (args_info.imap_flag || args_info.smtp_flag)
368 args_info.no_client_first_flag = 1;
370 if (connect_hostname && !args_info.hostname_arg)
371 args_info.hostname_arg = strdup (connect_hostname);
373 res = gsasl_init (&ctx);
374 if (res != GSASL_OK)
375 error (EXIT_FAILURE, 0, _("initialization failure: %s"),
376 gsasl_strerror (res));
378 gsasl_callback_set (ctx, callback);
380 if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
382 char *mechs;
384 if (args_info.client_mechanisms_flag)
385 res = gsasl_client_mechlist (ctx, &mechs);
386 else
387 res = gsasl_server_mechlist (ctx, &mechs);
389 if (res != GSASL_OK)
390 error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
391 gsasl_strerror (res));
393 if (!args_info.quiet_given)
395 if (args_info.client_mechanisms_flag)
396 fprintf (stderr,
397 _("This client supports the following mechanisms:\n"));
398 else
399 fprintf (stderr,
400 _("This server supports the following mechanisms:\n"));
403 fprintf (stdout, "%s\n", mechs);
405 free (mechs);
407 return EXIT_SUCCESS;
410 if (args_info.connect_given || args_info.inputs_num > 0)
412 struct sockaddr connect_addr;
413 struct sockaddr *saddr = &connect_addr;
414 size_t saddrlen = sizeof (*saddr);
415 struct addrinfo hints;
416 struct addrinfo *ai0, *ai;
418 memset (&hints, 0, sizeof (hints));
419 hints.ai_flags = AI_CANONNAME;
420 hints.ai_socktype = SOCK_STREAM;
421 res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
422 if (res != 0)
423 error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
424 gai_strerror (res));
426 for (ai = ai0; ai; ai = ai->ai_next)
428 fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname));
430 sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
431 if (sockfd < 0)
433 error (0, errno, "socket");
434 continue;
437 if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
439 int save_errno = errno;
440 close (sockfd);
441 sockfd = -1;
442 error (0, save_errno, "connect");
443 continue;
445 break;
448 if (sockfd < 0)
449 error (EXIT_FAILURE, errno, "socket");
451 saddr = ai->ai_addr;
452 saddrlen = ai->ai_addrlen;
454 freeaddrinfo (ai);
457 if (!greeting ())
458 return 1;
460 #ifdef HAVE_LIBGNUTLS
461 if (sockfd && !args_info.no_starttls_flag &&
462 (args_info.starttls_flag || has_starttls ()))
464 res = gnutls_global_init ();
465 if (res < 0)
466 error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
467 gnutls_strerror (res));
469 res = gnutls_init (&session, GNUTLS_CLIENT);
470 if (res < 0)
471 error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
472 gnutls_strerror (res));
474 res = gnutls_set_default_priority (session);
475 if (res < 0)
476 error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
477 gnutls_strerror (res));
479 res = gnutls_anon_allocate_client_credentials (&anoncred);
480 if (res < 0)
481 error (EXIT_FAILURE, 0,
482 _("allocating anonymous GnuTLS credential: %s"),
483 gnutls_strerror (res));
485 res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
486 if (res < 0)
487 error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
488 gnutls_strerror (res));
490 res = gnutls_certificate_allocate_credentials (&x509cred);
491 if (res < 0)
492 error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
493 gnutls_strerror (res));
495 if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
496 res = gnutls_certificate_set_x509_key_file
497 (x509cred, args_info.x509_cert_file_arg,
498 args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
499 if (res != GNUTLS_E_SUCCESS)
500 error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
501 gnutls_strerror (res));
503 if (args_info.x509_ca_file_arg)
505 res = gnutls_certificate_set_x509_trust_file
506 (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
507 if (res < 0)
508 error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
509 gnutls_strerror (res));
510 if (res == 0)
511 error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
514 res =
515 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509cred);
516 if (res < 0)
517 error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
518 gnutls_strerror (res));
520 res = gnutls_kx_set_priority (session, kx_prio);
521 if (res < 0)
522 error (EXIT_FAILURE, 0, _("setting GnuTLS key exchange priority: %s"),
523 gnutls_strerror (res));
525 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) sockfd);
527 if (!starttls ())
528 return 1;
530 res = gnutls_handshake (session);
531 if (res < 0)
532 error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
533 gnutls_strerror (res));
535 if (args_info.x509_ca_file_arg)
537 unsigned int status;
539 res = gnutls_certificate_verify_peers2 (session, &status);
540 if (res < 0)
541 error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
542 gnutls_strerror (res));
544 if (status & GNUTLS_CERT_INVALID)
545 error (EXIT_FAILURE, 0, _("server certificate is not trusted"));
547 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
548 error (EXIT_FAILURE, 0,
549 _("server certificate hasn't got a known issuer"));
551 if (status & GNUTLS_CERT_REVOKED)
552 error (EXIT_FAILURE, 0, _("server certificate has been revoked"));
554 if (status != 0)
555 error (EXIT_FAILURE, 0,
556 _("could not verify server certificate (rc=%d)"), status);
559 using_tls = true;
561 #endif
563 if (args_info.client_flag || args_info.client_given || args_info.server_given)
565 char *out;
566 char *b64output;
567 size_t output_len;
568 size_t b64output_len;
569 const char *mech;
570 Gsasl_session *xctx = NULL;
572 if (!select_mechanism (&in))
573 return 1;
575 mech = gsasl_client_suggest_mechanism (ctx, in);
576 if (mech == NULL)
578 fprintf (stderr, _("Cannot find mechanism...\n"));
579 return 0;
582 if (args_info.mechanism_arg)
583 mech = args_info.mechanism_arg;
585 if (!authenticate (mech))
586 return 1;
588 /* Authenticate using mechanism */
590 if (args_info.server_flag)
591 res = gsasl_server_start (ctx, mech, &xctx);
592 else
593 res = gsasl_client_start (ctx, mech, &xctx);
594 if (res != GSASL_OK)
595 error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
596 gsasl_strerror (res));
598 in = NULL;
599 out = NULL;
601 if (!args_info.server_flag && args_info.no_client_first_flag)
603 res = GSASL_NEEDS_MORE;
604 goto no_client_first;
609 res = gsasl_step64 (xctx, in, &out);
610 if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
611 break;
613 if (!step_send (out))
614 return 1;
616 if (res != GSASL_NEEDS_MORE)
617 break;
619 no_client_first:
620 if (!args_info.quiet_given &&
621 !args_info.imap_flag && !args_info.smtp_flag)
623 if (args_info.server_flag)
624 fprintf (stderr, _("Enter base64 authentication data "
625 "from client (press RET if none):\n"));
626 else
627 fprintf (stderr, _("Enter base64 authentication data "
628 "from server (press RET if none):\n"));
631 if (!step_recv (&in))
632 return 1;
634 while (res == GSASL_NEEDS_MORE);
636 if (res != GSASL_OK)
637 error (EXIT_FAILURE, 0, _("mechanism error: %s"),
638 gsasl_strerror (res));
640 if (!auth_finish ())
641 return 1;
643 if (!args_info.quiet_given)
645 if (args_info.server_flag)
646 fprintf (stderr, _("Server authentication "
647 "finished (client trusted)...\n"));
648 else
649 fprintf (stderr, _("Client authentication "
650 "finished (server trusted)...\n"));
653 /* Transfer application payload */
654 if (args_info.application_data_flag)
656 fd_set readfds;
658 FD_ZERO (&readfds);
659 FD_SET (STDIN_FILENO, &readfds);
660 if (sockfd)
661 FD_SET (sockfd, &readfds);
663 if (!args_info.quiet_given)
664 fprintf (stderr, _("Enter application data (EOF to finish):\n"));
666 while (select (sockfd + 1, &readfds, NULL, NULL, NULL))
668 if (FD_ISSET (STDIN_FILENO, &readfds))
670 char input[MAX_LINE_LENGTH];
672 input[0] = '\0';
673 if (fgets (input, MAX_LINE_LENGTH - 2, stdin) == NULL)
674 break;
675 if (args_info.imap_flag || args_info.smtp_flag)
677 int pos = strlen (input);
678 input[pos - 1] = '\r';
679 input[pos] = '\n';
680 input[pos + 1] = '\0';
682 else
683 input[strlen (input) - 1] = '\0';
685 res = gsasl_encode (xctx, input, strlen (input),
686 &out, &output_len);
687 if (res != GSASL_OK)
688 break;
690 if (sockfd)
692 ssize_t len;
693 #ifdef HAVE_LIBGNUTLS
694 if (using_tls)
695 len = gnutls_record_send (session, out, output_len);
696 else
697 #endif
698 len = write (sockfd, out, output_len);
699 if (len != output_len)
700 return 0;
702 else if (!(strlen (input) == output_len &&
703 memcmp (input, out, output_len) == 0))
705 res = gsasl_base64_to (out, output_len,
706 &b64output, &b64output_len);
707 if (res != GSASL_OK)
708 break;
710 if (!args_info.quiet_given)
711 fprintf (stderr, _("Base64 encoded application "
712 "data to send:\n"));
713 fprintf (stdout, "%s\n", b64output);
715 free (b64output);
718 free (out);
721 if (sockfd && FD_ISSET (sockfd, &readfds))
723 if (!readln (&in))
724 break;
725 free (in);
728 FD_ZERO (&readfds);
729 FD_SET (STDIN_FILENO, &readfds);
730 if (sockfd)
731 FD_SET (sockfd, &readfds);
734 if (res != GSASL_OK)
735 error (EXIT_FAILURE, 0, _("encoding error: %s"),
736 gsasl_strerror (res));
739 if (!args_info.quiet_given)
740 fprintf (stderr, _("Session finished...\n"));
742 if (!logout ())
743 return 1;
745 gsasl_finish (xctx);
748 if (sockfd)
750 #ifdef HAVE_LIBGNUTLS
751 if (using_tls)
753 res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
754 if (res < 0)
755 error (EXIT_FAILURE, 0,
756 _("terminating GnuTLS session failed: %s"),
757 gnutls_strerror (res));
760 #endif
761 shutdown (sockfd, SHUT_RDWR);
762 close (sockfd);
765 gsasl_done (ctx);
767 #ifdef HAVE_LIBGNUTLS
768 if (using_tls)
770 gnutls_deinit (session);
771 gnutls_anon_free_client_credentials (anoncred);
772 gnutls_certificate_free_credentials (x509cred);
773 gnutls_global_deinit ();
775 #endif
777 return 0;