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/>.
22 #include "callbacks.h"
26 #ifdef HAVE_WS2TCPIP_H
27 # include <ws2tcpip.h>
31 # include <gnutls/gnutls.h>
32 gnutls_session session
;
33 bool using_tls
= false;
36 #define MAX_LINE_LENGTH BUFSIZ
38 struct gengetopt_args_info args_info
;
42 writeln (const char *str
)
53 /* GnuTLS < 1.2.9 cannot handle data != NULL && count == 0,
54 it will return an error. */
56 len
= gnutls_record_send (session
, str
, strlen (str
));
62 len
= write (sockfd
, str
, strlen (str
));
63 if (len
!= strlen (str
))
70 len
= gnutls_record_send (session
, CRLF
, strlen (CRLF
));
73 len
= write (sockfd
, CRLF
, strlen (CRLF
));
74 if (len
!= strlen (CRLF
))
88 char input
[MAX_LINE_LENGTH
];
90 /* FIXME: Optimize and remove size limit. */
98 len
= gnutls_record_recv (session
, &input
[j
- 1], 1);
101 len
= recv (sockfd
, &input
[j
- 1], 1, 0);
105 while (input
[j
- 1] != '\n' && j
< MAX_LINE_LENGTH
);
108 *out
= strdup (input
);
114 *out
= readline ("");
125 if (args_info
.imap_flag
)
126 return imap_greeting ();
127 if (args_info
.smtp_flag
)
128 return smtp_greeting ();
136 if (args_info
.imap_flag
)
137 return imap_has_starttls ();
138 if (args_info
.smtp_flag
)
139 return smtp_has_starttls ();
147 if (args_info
.imap_flag
)
148 return imap_starttls ();
149 if (args_info
.smtp_flag
)
150 return smtp_starttls ();
156 select_mechanism (char **mechlist
)
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"));
175 else /* if (args_info.client_flag) */
177 if (!args_info
.quiet_given
)
179 _("Input list of SASL mechanisms supported by server:\n"));
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"));
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"));
217 fprintf (stderr
, _("Output from client:\n"));
219 fprintf (stdout
, "%s\n", data
);
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
);
241 if (args_info
.imap_flag
)
242 return imap_auth_finish ();
243 if (args_info
.smtp_flag
)
244 return smtp_auth_finish ();
252 if (args_info
.imap_flag
)
253 return imap_logout ();
254 if (args_info
.smtp_flag
)
255 return smtp_logout ();
261 main (int argc
, char *argv
[])
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
;
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
;
288 wVersionRequested
= MAKEWORD(2, 0);
289 r
= WSAStartup( wVersionRequested
, &wsaData
);
291 error (EXIT_FAILURE
, 0, _("Cannot initialize Windows sockets."));
295 if (cmdline_parser (argc
, argv
, &args_info
) != 0)
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."),
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 ();
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);
335 connect_hostname
= strdup (args_info
.connect_arg
);
336 if (args_info
.smtp_flag
)
337 connect_service
= strdup ("smtp");
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");
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;
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
);
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
)
385 if (args_info
.client_mechanisms_flag
)
386 res
= gsasl_client_mechlist (ctx
, &mechs
);
388 res
= gsasl_server_mechlist (ctx
, &mechs
);
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
)
398 _("This client supports the following mechanisms:\n"));
401 _("This server supports the following mechanisms:\n"));
404 fprintf (stdout
, "%s\n", mechs
);
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
);
424 error (EXIT_FAILURE
, 0, "%s: %s", connect_hostname
,
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
);
434 error (0, errno
, "socket");
438 if (connect (sockfd
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
440 int save_errno
= errno
;
443 error (0, save_errno
, "connect");
450 error (EXIT_FAILURE
, errno
, "socket");
453 saddrlen
= ai
->ai_addrlen
;
461 #ifdef HAVE_LIBGNUTLS
462 if (sockfd
&& !args_info
.no_starttls_flag
&&
463 (args_info
.starttls_flag
|| has_starttls ()))
465 res
= gnutls_global_init ();
467 error (EXIT_FAILURE
, 0, _("GnuTLS global initialization failed: %s"),
468 gnutls_strerror (res
));
470 res
= gnutls_init (&session
, GNUTLS_CLIENT
);
472 error (EXIT_FAILURE
, 0, _("GnuTLS initialization failed: %s"),
473 gnutls_strerror (res
));
475 res
= gnutls_set_default_priority (session
);
477 error (EXIT_FAILURE
, 0, _("setting GnuTLS defaults failed: %s"),
478 gnutls_strerror (res
));
480 res
= gnutls_anon_allocate_client_credentials (&anoncred
);
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
);
488 error (EXIT_FAILURE
, 0, _("setting anonymous GnuTLS credential: %s"),
489 gnutls_strerror (res
));
491 res
= gnutls_certificate_allocate_credentials (&x509cred
);
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
);
509 error (EXIT_FAILURE
, 0, _("no X.509 CAs found: %s"),
510 gnutls_strerror (res
));
512 error (EXIT_FAILURE
, 0, _("no X.509 CAs found"));
516 gnutls_credentials_set (session
, GNUTLS_CRD_CERTIFICATE
, x509cred
);
518 error (EXIT_FAILURE
, 0, _("setting X.509 GnuTLS credential: %s"),
519 gnutls_strerror (res
));
521 res
= gnutls_kx_set_priority (session
, kx_prio
);
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
);
531 res
= gnutls_handshake (session
);
533 error (EXIT_FAILURE
, 0, _("GnuTLS handshake failed: %s"),
534 gnutls_strerror (res
));
536 if (args_info
.x509_ca_file_arg
)
540 res
= gnutls_certificate_verify_peers2 (session
, &status
);
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"));
556 error (EXIT_FAILURE
, 0,
557 _("could not verify server certificate (rc=%d)"), status
);
564 if (args_info
.client_flag
|| args_info
.client_given
|| args_info
.server_given
)
569 size_t b64output_len
;
571 Gsasl_session
*xctx
= NULL
;
573 if (!select_mechanism (&in
))
576 mech
= gsasl_client_suggest_mechanism (ctx
, in
);
579 fprintf (stderr
, _("Cannot find mechanism...\n"));
583 if (args_info
.mechanism_arg
)
584 mech
= args_info
.mechanism_arg
;
586 if (!authenticate (mech
))
589 /* Authenticate using mechanism */
591 if (args_info
.server_flag
)
592 res
= gsasl_server_start (ctx
, mech
, &xctx
);
594 res
= gsasl_client_start (ctx
, mech
, &xctx
);
596 error (EXIT_FAILURE
, 0, _("mechanism unavailable: %s"),
597 gsasl_strerror (res
));
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
)
614 if (!step_send (out
))
617 if (res
!= GSASL_NEEDS_MORE
)
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"));
628 fprintf (stderr
, _("Enter base64 authentication data "
629 "from server (press RET if none):\n"));
632 if (!step_recv (&in
))
635 while (res
== GSASL_NEEDS_MORE
);
638 error (EXIT_FAILURE
, 0, _("mechanism error: %s"),
639 gsasl_strerror (res
));
644 if (!args_info
.quiet_given
)
646 if (args_info
.server_flag
)
647 fprintf (stderr
, _("Server authentication "
648 "finished (client trusted)...\n"));
650 fprintf (stderr
, _("Client authentication "
651 "finished (server trusted)...\n"));
654 /* Transfer application payload */
655 if (args_info
.application_data_flag
)
660 FD_SET (STDIN_FILENO
, &readfds
);
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
];
674 if (fgets (input
, MAX_LINE_LENGTH
- 2, stdin
) == NULL
)
676 if (args_info
.imap_flag
|| args_info
.smtp_flag
)
678 int pos
= strlen (input
);
679 input
[pos
- 1] = '\r';
681 input
[pos
+ 1] = '\0';
684 input
[strlen (input
) - 1] = '\0';
686 res
= gsasl_encode (xctx
, input
, strlen (input
),
694 #ifdef HAVE_LIBGNUTLS
696 len
= gnutls_record_send (session
, out
, output_len
);
699 len
= write (sockfd
, out
, output_len
);
700 if (len
!= output_len
)
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
);
711 if (!args_info
.quiet_given
)
712 fprintf (stderr
, _("Base64 encoded application "
714 fprintf (stdout
, "%s\n", b64output
);
722 if (sockfd
&& FD_ISSET (sockfd
, &readfds
))
730 FD_SET (STDIN_FILENO
, &readfds
);
732 FD_SET (sockfd
, &readfds
);
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"));
751 #ifdef HAVE_LIBGNUTLS
754 res
= gnutls_bye (session
, GNUTLS_SHUT_RDWR
);
756 error (EXIT_FAILURE
, 0,
757 _("terminating GnuTLS session failed: %s"),
758 gnutls_strerror (res
));
762 shutdown (sockfd
, SHUT_RDWR
);
768 #ifdef HAVE_LIBGNUTLS
771 gnutls_deinit (session
);
772 gnutls_anon_free_client_credentials (anoncred
);
773 gnutls_certificate_free_credentials (x509cred
);
774 gnutls_global_deinit ();