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.
23 #include "callbacks.h"
27 #ifdef HAVE_WS2TCPIP_H
28 # include <ws2tcpip.h>
32 # include <gnutls/gnutls.h>
33 gnutls_session session
;
34 bool using_tls
= false;
37 #define MAX_LINE_LENGTH BUFSIZ
39 struct gengetopt_args_info args_info
;
43 writeln (const char *str
)
54 /* GnuTLS < 1.2.9 cannot handle data != NULL && count == 0,
55 it will return an error. */
57 len
= gnutls_record_send (session
, str
, strlen (str
));
63 len
= write (sockfd
, str
, strlen (str
));
64 if (len
!= strlen (str
))
71 len
= gnutls_record_send (session
, CRLF
, strlen (CRLF
));
74 len
= write (sockfd
, CRLF
, strlen (CRLF
));
75 if (len
!= strlen (CRLF
))
89 char input
[MAX_LINE_LENGTH
];
91 /* FIXME: Optimize and remove size limit. */
99 len
= gnutls_record_recv (session
, &input
[j
- 1], 1);
102 len
= recv (sockfd
, &input
[j
- 1], 1, 0);
106 while (input
[j
- 1] != '\n' && j
< MAX_LINE_LENGTH
);
109 *out
= strdup (input
);
115 *out
= readline ("");
126 if (args_info
.imap_flag
)
127 return imap_greeting ();
128 if (args_info
.smtp_flag
)
129 return smtp_greeting ();
137 if (args_info
.imap_flag
)
138 return imap_has_starttls ();
139 if (args_info
.smtp_flag
)
140 return smtp_has_starttls ();
148 if (args_info
.imap_flag
)
149 return imap_starttls ();
150 if (args_info
.smtp_flag
)
151 return smtp_starttls ();
157 select_mechanism (char **mechlist
)
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"));
176 else /* if (args_info.client_flag) */
178 if (!args_info
.quiet_given
)
180 _("Input list of SASL mechanisms supported by server:\n"));
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"));
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"));
218 fprintf (stderr
, _("Output from client:\n"));
220 fprintf (stdout
, "%s\n", data
);
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
);
242 if (args_info
.imap_flag
)
243 return imap_auth_finish ();
244 if (args_info
.smtp_flag
)
245 return smtp_auth_finish ();
253 if (args_info
.imap_flag
)
254 return imap_logout ();
255 if (args_info
.smtp_flag
)
256 return smtp_logout ();
262 main (int argc
, char *argv
[])
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
;
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
;
289 wVersionRequested
= MAKEWORD(2, 0);
290 r
= WSAStartup( wVersionRequested
, &wsaData
);
292 error (EXIT_FAILURE
, 0, _("Cannot initialize Windows sockets."));
296 if (cmdline_parser (argc
, argv
, &args_info
) != 0)
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."),
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 ();
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);
336 connect_hostname
= strdup (args_info
.connect_arg
);
337 if (args_info
.smtp_flag
)
338 connect_service
= strdup ("smtp");
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");
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;
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
);
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
)
386 if (args_info
.client_mechanisms_flag
)
387 res
= gsasl_client_mechlist (ctx
, &mechs
);
389 res
= gsasl_server_mechlist (ctx
, &mechs
);
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
)
399 _("This client supports the following mechanisms:\n"));
402 _("This server supports the following mechanisms:\n"));
405 fprintf (stdout
, "%s\n", mechs
);
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
);
425 error (EXIT_FAILURE
, 0, "%s: %s", connect_hostname
,
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
);
435 error (0, errno
, "socket");
439 if (connect (sockfd
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
441 int save_errno
= errno
;
444 error (0, save_errno
, "connect");
451 error (EXIT_FAILURE
, errno
, "socket");
454 saddrlen
= ai
->ai_addrlen
;
462 #ifdef HAVE_LIBGNUTLS
463 if (sockfd
&& !args_info
.no_starttls_flag
&&
464 (args_info
.starttls_flag
|| has_starttls ()))
466 res
= gnutls_global_init ();
468 error (EXIT_FAILURE
, 0, _("GnuTLS global initialization failed: %s"),
469 gnutls_strerror (res
));
471 res
= gnutls_init (&session
, GNUTLS_CLIENT
);
473 error (EXIT_FAILURE
, 0, _("GnuTLS initialization failed: %s"),
474 gnutls_strerror (res
));
476 res
= gnutls_set_default_priority (session
);
478 error (EXIT_FAILURE
, 0, _("setting GnuTLS defaults failed: %s"),
479 gnutls_strerror (res
));
481 res
= gnutls_anon_allocate_client_credentials (&anoncred
);
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
);
489 error (EXIT_FAILURE
, 0, _("setting anonymous GnuTLS credential: %s"),
490 gnutls_strerror (res
));
492 res
= gnutls_certificate_allocate_credentials (&x509cred
);
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
);
510 error (EXIT_FAILURE
, 0, _("no X.509 CAs found: %s"),
511 gnutls_strerror (res
));
513 error (EXIT_FAILURE
, 0, _("no X.509 CAs found"));
517 gnutls_credentials_set (session
, GNUTLS_CRD_CERTIFICATE
, x509cred
);
519 error (EXIT_FAILURE
, 0, _("setting X.509 GnuTLS credential: %s"),
520 gnutls_strerror (res
));
522 res
= gnutls_kx_set_priority (session
, kx_prio
);
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
);
532 res
= gnutls_handshake (session
);
534 error (EXIT_FAILURE
, 0, _("GnuTLS handshake failed: %s"),
535 gnutls_strerror (res
));
537 if (args_info
.x509_ca_file_arg
)
541 res
= gnutls_certificate_verify_peers2 (session
, &status
);
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"));
557 error (EXIT_FAILURE
, 0,
558 _("could not verify server certificate (rc=%d)"), status
);
565 if (args_info
.client_flag
|| args_info
.client_given
|| args_info
.server_given
)
570 size_t b64output_len
;
572 Gsasl_session
*xctx
= NULL
;
574 if (!select_mechanism (&in
))
577 mech
= gsasl_client_suggest_mechanism (ctx
, in
);
580 fprintf (stderr
, _("Cannot find mechanism...\n"));
584 if (args_info
.mechanism_arg
)
585 mech
= args_info
.mechanism_arg
;
587 if (!authenticate (mech
))
590 /* Authenticate using mechanism */
592 if (args_info
.server_flag
)
593 res
= gsasl_server_start (ctx
, mech
, &xctx
);
595 res
= gsasl_client_start (ctx
, mech
, &xctx
);
597 error (EXIT_FAILURE
, 0, _("mechanism unavailable: %s"),
598 gsasl_strerror (res
));
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
)
615 if (!step_send (out
))
618 if (res
!= GSASL_NEEDS_MORE
)
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"));
629 fprintf (stderr
, _("Enter base64 authentication data "
630 "from server (press RET if none):\n"));
633 if (!step_recv (&in
))
636 while (res
== GSASL_NEEDS_MORE
);
639 error (EXIT_FAILURE
, 0, _("mechanism error: %s"),
640 gsasl_strerror (res
));
645 if (!args_info
.quiet_given
)
647 if (args_info
.server_flag
)
648 fprintf (stderr
, _("Server authentication "
649 "finished (client trusted)...\n"));
651 fprintf (stderr
, _("Client authentication "
652 "finished (server trusted)...\n"));
655 /* Transfer application payload */
656 if (args_info
.application_data_flag
)
661 FD_SET (STDIN_FILENO
, &readfds
);
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
];
675 if (fgets (input
, MAX_LINE_LENGTH
- 2, stdin
) == NULL
)
677 if (args_info
.imap_flag
|| args_info
.smtp_flag
)
679 int pos
= strlen (input
);
680 input
[pos
- 1] = '\r';
682 input
[pos
+ 1] = '\0';
685 input
[strlen (input
) - 1] = '\0';
687 res
= gsasl_encode (xctx
, input
, strlen (input
),
695 #ifdef HAVE_LIBGNUTLS
697 len
= gnutls_record_send (session
, out
, output_len
);
700 len
= write (sockfd
, out
, output_len
);
701 if (len
!= output_len
)
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
);
712 if (!args_info
.quiet_given
)
713 fprintf (stderr
, _("Base64 encoded application "
715 fprintf (stdout
, "%s\n", b64output
);
723 if (sockfd
&& FD_ISSET (sockfd
, &readfds
))
731 FD_SET (STDIN_FILENO
, &readfds
);
733 FD_SET (sockfd
, &readfds
);
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"));
752 #ifdef HAVE_LIBGNUTLS
755 res
= gnutls_bye (session
, GNUTLS_SHUT_RDWR
);
757 error (EXIT_FAILURE
, 0,
758 _("terminating GnuTLS session failed: %s"),
759 gnutls_strerror (res
));
763 shutdown (sockfd
, SHUT_RDWR
);
769 #ifdef HAVE_LIBGNUTLS
772 gnutls_deinit (session
);
773 gnutls_anon_free_client_credentials (anoncred
);
774 gnutls_certificate_free_credentials (x509cred
);
775 gnutls_global_deinit ();