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
= 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
;
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
;
287 wVersionRequested
= MAKEWORD(2, 0);
288 r
= WSAStartup( wVersionRequested
, &wsaData
);
290 error (EXIT_FAILURE
, 0, _("Cannot initialize Windows sockets."));
294 if (cmdline_parser (argc
, argv
, &args_info
) != 0)
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."),
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 ();
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);
334 connect_hostname
= strdup (args_info
.connect_arg
);
335 if (args_info
.smtp_flag
)
336 connect_service
= strdup ("smtp");
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");
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;
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
);
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
)
384 if (args_info
.client_mechanisms_flag
)
385 res
= gsasl_client_mechlist (ctx
, &mechs
);
387 res
= gsasl_server_mechlist (ctx
, &mechs
);
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
)
397 _("This client supports the following mechanisms:\n"));
400 _("This server supports the following mechanisms:\n"));
403 fprintf (stdout
, "%s\n", mechs
);
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
);
423 error (EXIT_FAILURE
, 0, "%s: %s", connect_hostname
,
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
);
433 error (0, errno
, "socket");
437 if (connect (sockfd
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
439 int save_errno
= errno
;
442 error (0, save_errno
, "connect");
449 error (EXIT_FAILURE
, errno
, "socket");
452 saddrlen
= ai
->ai_addrlen
;
460 #ifdef HAVE_LIBGNUTLS
461 if (sockfd
&& !args_info
.no_starttls_flag
&&
462 (args_info
.starttls_flag
|| has_starttls ()))
464 res
= gnutls_global_init ();
466 error (EXIT_FAILURE
, 0, _("GnuTLS global initialization failed: %s"),
467 gnutls_strerror (res
));
469 res
= gnutls_init (&session
, GNUTLS_CLIENT
);
471 error (EXIT_FAILURE
, 0, _("GnuTLS initialization failed: %s"),
472 gnutls_strerror (res
));
474 res
= gnutls_set_default_priority (session
);
476 error (EXIT_FAILURE
, 0, _("setting GnuTLS defaults failed: %s"),
477 gnutls_strerror (res
));
479 res
= gnutls_anon_allocate_client_credentials (&anoncred
);
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
);
487 error (EXIT_FAILURE
, 0, _("setting anonymous GnuTLS credential: %s"),
488 gnutls_strerror (res
));
490 res
= gnutls_certificate_allocate_credentials (&x509cred
);
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
);
508 error (EXIT_FAILURE
, 0, _("no X.509 CAs found: %s"),
509 gnutls_strerror (res
));
511 error (EXIT_FAILURE
, 0, _("no X.509 CAs found"));
515 gnutls_credentials_set (session
, GNUTLS_CRD_CERTIFICATE
, x509cred
);
517 error (EXIT_FAILURE
, 0, _("setting X.509 GnuTLS credential: %s"),
518 gnutls_strerror (res
));
520 res
= gnutls_kx_set_priority (session
, kx_prio
);
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
);
530 res
= gnutls_handshake (session
);
532 error (EXIT_FAILURE
, 0, _("GnuTLS handshake failed: %s"),
533 gnutls_strerror (res
));
535 if (args_info
.x509_ca_file_arg
)
539 res
= gnutls_certificate_verify_peers2 (session
, &status
);
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"));
555 error (EXIT_FAILURE
, 0,
556 _("could not verify server certificate (rc=%d)"), status
);
563 if (args_info
.client_flag
|| args_info
.client_given
|| args_info
.server_given
)
568 size_t b64output_len
;
570 Gsasl_session
*xctx
= NULL
;
572 if (!select_mechanism (&in
))
575 mech
= gsasl_client_suggest_mechanism (ctx
, in
);
578 fprintf (stderr
, _("Cannot find mechanism...\n"));
582 if (args_info
.mechanism_arg
)
583 mech
= args_info
.mechanism_arg
;
585 if (!authenticate (mech
))
588 /* Authenticate using mechanism */
590 if (args_info
.server_flag
)
591 res
= gsasl_server_start (ctx
, mech
, &xctx
);
593 res
= gsasl_client_start (ctx
, mech
, &xctx
);
595 error (EXIT_FAILURE
, 0, _("mechanism unavailable: %s"),
596 gsasl_strerror (res
));
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
)
613 if (!step_send (out
))
616 if (res
!= GSASL_NEEDS_MORE
)
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"));
627 fprintf (stderr
, _("Enter base64 authentication data "
628 "from server (press RET if none):\n"));
631 if (!step_recv (&in
))
634 while (res
== GSASL_NEEDS_MORE
);
637 error (EXIT_FAILURE
, 0, _("mechanism error: %s"),
638 gsasl_strerror (res
));
643 if (!args_info
.quiet_given
)
645 if (args_info
.server_flag
)
646 fprintf (stderr
, _("Server authentication "
647 "finished (client trusted)...\n"));
649 fprintf (stderr
, _("Client authentication "
650 "finished (server trusted)...\n"));
653 /* Transfer application payload */
654 if (args_info
.application_data_flag
)
659 FD_SET (STDIN_FILENO
, &readfds
);
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
];
673 if (fgets (input
, MAX_LINE_LENGTH
- 2, stdin
) == NULL
)
675 if (args_info
.imap_flag
|| args_info
.smtp_flag
)
677 int pos
= strlen (input
);
678 input
[pos
- 1] = '\r';
680 input
[pos
+ 1] = '\0';
683 input
[strlen (input
) - 1] = '\0';
685 res
= gsasl_encode (xctx
, input
, strlen (input
),
693 #ifdef HAVE_LIBGNUTLS
695 len
= gnutls_record_send (session
, out
, output_len
);
698 len
= write (sockfd
, out
, output_len
);
699 if (len
!= output_len
)
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
);
710 if (!args_info
.quiet_given
)
711 fprintf (stderr
, _("Base64 encoded application "
713 fprintf (stdout
, "%s\n", b64output
);
721 if (sockfd
&& FD_ISSET (sockfd
, &readfds
))
729 FD_SET (STDIN_FILENO
, &readfds
);
731 FD_SET (sockfd
, &readfds
);
735 error (EXIT_FAILURE
, 0, _("encoding error: %s"),
736 gsasl_strerror (res
));
739 if (!args_info
.quiet_given
)
740 fprintf (stderr
, _("Session finished...\n"));
750 #ifdef HAVE_LIBGNUTLS
753 res
= gnutls_bye (session
, GNUTLS_SHUT_RDWR
);
755 error (EXIT_FAILURE
, 0,
756 _("terminating GnuTLS session failed: %s"),
757 gnutls_strerror (res
));
761 shutdown (sockfd
, SHUT_RDWR
);
767 #ifdef HAVE_LIBGNUTLS
770 gnutls_deinit (session
);
771 gnutls_anon_free_client_credentials (anoncred
);
772 gnutls_certificate_free_credentials (x509cred
);
773 gnutls_global_deinit ();