/* simple wrapper program for STARTTLS Copyright (C) 1999, 2000 Free Software Foundation, Inc. Author: Daiki Ueno Created: 1999-11-19 Keywords: TLS, OpenSSL This file is not part of any package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #define _GNU_SOURCE #include "getopt.h" static SSL_CTX *tls_ctx = NULL; static SSL *tls_conn = NULL; static int tls_fd; static char *opt_cert_file = NULL, *opt_key_file = NULL; static int opt_verify = 0; static int tls_ssl_ctx_new (cert_file, key_file) const char *cert_file, *key_file; { SSL_load_error_strings (); SSLeay_add_ssl_algorithms (); tls_ctx = SSL_CTX_new (TLSv1_client_method()); if (!tls_ctx) return -1; SSL_CTX_set_options (tls_ctx, SSL_OP_ALL /* Work around all known bugs */); if (cert_file) { if (SSL_CTX_use_certificate_file (tls_ctx, cert_file, SSL_FILETYPE_PEM) <= 0) return -1; if (!key_file) key_file = cert_file; if (SSL_CTX_use_PrivateKey_file (tls_ctx, key_file, SSL_FILETYPE_PEM) <= 0) return -1; if (!SSL_CTX_check_private_key (tls_ctx)) return -1; } SSL_CTX_set_verify (tls_ctx, SSL_VERIFY_NONE, NULL); return 0; } static int tls_ssl_new(ctx, s) SSL_CTX *ctx; int s; { SSL_SESSION *session; SSL_CIPHER *cipher; X509 *peer; tls_conn = (SSL *) SSL_new (ctx); if (!tls_conn) return -1; SSL_clear(tls_conn); if (!SSL_set_fd (tls_conn, s)) return -1; SSL_set_connect_state (tls_conn); if (SSL_connect (tls_conn) <= 0) { session = SSL_get_session (tls_conn); if (session) SSL_CTX_remove_session (ctx, session); if (tls_conn!=NULL) SSL_free (tls_conn); return -1; } return 0; } static int tls_connect (hostname, service) const char *hostname, *service; { int server, false = 0; #ifdef HAVE_ADDRINFO struct addrinfo *in, *in0, hints; #else struct hostent *host; struct servent *serv; struct sockaddr_in sin; #endif #ifdef HAVE_ADDRINFO memset (&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo (hostname, service, &hints, &in0)) return -1; for (in = in0; in; in = in->ai_next) { server = socket (in->ai_family, in->ai_socktype, in->ai_protocol); if (server < 0) continue; if (connect (server, in->ai_addr, in->ai_addrlen) < 0) { server = -1; continue; } break; } if (server < 0) return -1; #else memset (&sin, 0, sizeof (sin)); host = gethostbyname (hostname); if (!host) return -1; memcpy (&sin.sin_addr, host->h_addr, host->h_length); serv = getservbyname (service, "tcp"); if (serv) sin.sin_port = serv->s_port; else if (isdigit (service[0])) sin.sin_port = htons (atoi (service)); sin.sin_family = AF_INET; server = socket (sin.sin_family, SOCK_STREAM, 0); if (server == -1) return -1; if (connect (server, (struct sockaddr *)&sin, sizeof (sin)) < 0) { close (server); return -1; } #endif setsockopt (server, SOL_SOCKET, SO_KEEPALIVE, (const char *) &false, sizeof (false)); return server; } static void tls_negotiate (sig) int sig; { if (tls_ssl_ctx_new (opt_cert_file, opt_key_file) == -1) return; (void) tls_ssl_new (tls_ctx, tls_fd); /* Negotiation has done. */ } static void usage (progname) const char *progname; { printf ("%s (%s) %s\n" "Copyright (C) 1999 Free Software Foundation, Inc.\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions. See the file COPYING for details.\n\n" "Usage: %s [options] host port\n\n" "Options:\n\n" " --cert-file [file] specify certificate file\n" " --key-file [file] specify private key file\n" " --verify [level] set verification level\n", progname, PACKAGE, VERSION, progname); } int main (argc, argv) int argc; char **argv; { int in = fileno (stdin), out = fileno (stdout), nbuffer, wrote; #ifdef HAVE_POLL struct pollfd readfds[2], writefds[1]; #else fd_set readfds, writefds; #endif char buffer[BUFSIZ], *retry; struct sigaction act; int this_option_optind = optind ? optind : 1; int option_index = 0, c; static struct option long_options[] = { {"cert-file", 1, 0, 'c'}, {"key-file", 1, 0, 'k'}, {"verify", 1, 0, 'v'}, {0, 0, 0, 0} }; while (1) { c = getopt_long (argc, argv, "c:k:v:", long_options, &option_index); if (c == -1) break; switch (c) { case 'c': opt_cert_file = optarg; break; case 'k': opt_key_file = optarg; break; case 'v': opt_verify = atoi (optarg); break; default: usage (basename (argv[0])); return 1; } } if (optind+2 != argc) { usage (basename (argv[0])); return 1; } tls_fd = tls_connect (argv[optind], argv[optind+1]); if (tls_fd < 0) { perror ("tls_connect"); return 1; } memset (&act, 0, sizeof (act)); act.sa_handler = tls_negotiate; sigemptyset (&act.sa_mask); act.sa_flags = SA_RESTART|SA_RESETHAND; sigaction (SIGALRM, &act, NULL); #ifdef HAVE_POLL readfds[0].fd = in; readfds[1].fd = tls_fd; readfds[0].events = POLLIN; readfds[1].events = POLLIN; writefds[0].events = POLLOUT; #endif while (1) { #ifdef HAVE_POLL if (poll (readfds, 2, -1) == -1 && errno != EINTR) #else FD_ZERO (&readfds); FD_SET (tls_fd, &readfds); FD_SET (in, &readfds); if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1 && errno != EINTR ) #endif { perror ("poll"); return 1; } #ifdef HAVE_POLL if (readfds[0].revents & POLLIN) #else if (FD_ISSET (in, &readfds)) #endif { nbuffer = read (in, buffer, sizeof buffer -1); if (nbuffer == 0) goto finish; for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote) { #ifdef HAVE_POLL writefds[0].fd = tls_fd; if (poll (writefds, 1, -1) == -1) #else FD_ZERO (&writefds); FD_SET (tls_fd, &writefds); if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1) #endif { perror ("poll"); return 1; } if (tls_conn) wrote = SSL_write (tls_conn, retry, nbuffer); else wrote = write (tls_fd, retry, nbuffer); if (wrote < 0) goto finish; } } #ifdef HAVE_POLL if (readfds[1].revents & POLLIN) #else if (FD_ISSET (tls_fd, &readfds)) #endif { readtop: if (tls_conn) nbuffer = SSL_read (tls_conn, buffer, sizeof buffer -1); else nbuffer = read (tls_fd, buffer, sizeof buffer -1); if (nbuffer == 0) goto finish; for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote) { #ifdef HAVE_POLL writefds[0].fd = out; if (poll (writefds, 1, -1) == -1) #else FD_ZERO (&writefds); FD_SET (out, &writefds); if (select (out+1, NULL, &writefds, NULL, NULL) == -1) #endif { perror ("poll"); return 1; } wrote = write (out, retry, nbuffer); if (wrote < 0) goto finish; } if (tls_conn && SSL_pending(tls_conn)) goto readtop; } } finish: close (in); close (out); return 0; }