Require autoconf 2.59.
[elisp/starttls.git] / starttls.c
index db74ed2..4159c5f 100644 (file)
@@ -1,9 +1,8 @@
-/* TLSv1 filter for STARTTLS extension.
+/* simple wrapper program for STARTTLS
 
-   Copyright (C) 1999, 2000 Daiki Ueno <ueno@unixuser.org>
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
 
    Author: Daiki Ueno <ueno@unixuser.org>
-       Kenichi OKADA <okada@opaopa.org>
    Created: 1999-11-19
    Keywords: TLS, OpenSSL
 
 
 */
 
-/*
-  How to compile: (OpenSSL is required)
-  
-  gcc -I/usr/local/ssl/include -o starttls starttls.c \
-    -L/usr/local/ssl/lib -lssl -lcrypto
-
-*/
-
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include <unistd.h>
 
-/* OpenSSL library. */
-
-#include <openssl/lhash.h>
-#include <openssl/bn.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-#include <openssl/x509.h>
-#include <openssl/ssl.h>
-
-#ifdef HAVE_SOCKS_H
-#include <socks.h>
-#endif 
-
-#ifndef HAVE_GETADDRINFO
-#include "getaddrinfo.h"
-#endif /* !HAVE_GETADDRINFO */
-
+#include <errno.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/file.h>
 #include <signal.h>
 #include <fcntl.h>
 #include <netinet/in.h>
-#define _GNU_SOURCE
+#include <poll.h>
 #include <getopt.h>
+#include "getaddrinfo.h"
+#include "gettext.h"
+#include "inet_ntop.h"
+#include "strdup.h"
 
-#ifdef HAVE_BASENAME
-# ifdef HAVE_LIBGEN_H
-#  include <libgen.h>
-#  ifdef basename
-#   undef basename
-#  endif
-# endif
-# include <string.h>
-#else
-inline char *
-basename(path) 
-     const char *path;
-{ 
-  char *p = rindex((path), '/');
-  return p ? p + 1 : (path);
-}
-#endif
-
-#define true 1
-
-static SSL_CTX *tls_ctx = NULL;
-static SSL *tls_conn = NULL;
-static int tls_fd;
+extern void tls_negotiate (int, const char *, const char *);
+extern int tls_write(int, const char *, int);
+extern int tls_read(int, char *, int);
+extern int tls_pending();
 
 static char *opt_cert_file = NULL, *opt_key_file = NULL;
-static int  opt_verify = 0;
-static int opt_force;
+static bool opt_assuan = false;
+static int tls_fd = -1;
 
-static int
-tls_ssl_ctx_new (cert_file, key_file)
-  const char *cert_file, *key_file;
+static void
+usage (progname)
+     const char *progname;
 {
-  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;
+  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"
+         " --help                  show this help\n",
+         progname, PACKAGE, VERSION, progname);
 }
 
-static int
-tls_ssl_new(ctx, s)
-  SSL_CTX *ctx;
-  int s;
+static void
+do_tls_negotiate(sig)
+     int sig;
 {
-  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;
+  tls_negotiate (tls_fd, opt_cert_file, opt_key_file);
 }
 
-static int
-tls_connect (hostname, service)
+int
+tcp_connect (hostname, service)
      const char *hostname, *service;
 {
-  struct protoent *proto;
-  struct addrinfo *in, hints;
-  int server, false = 0;
-
-  proto = getprotobyname ("tcp");
-  if (!proto)
-    return -1;
+  int server, _false = 0;
+  struct addrinfo *in, *in0, hints;
 
   memset (&hints, 0, sizeof (hints));
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
-  hints.ai_protocol = proto->p_proto;
-
-  if (getaddrinfo (hostname, service, &hints, &in) < 0) 
+  if (getaddrinfo (hostname, service, &hints, &in0))
     return -1;
 
-  server = socket (in->ai_family, in->ai_socktype, 0);
-  if (server < 0)
-    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 (setsockopt (server, SOL_SOCKET, SO_KEEPALIVE,
-                 (const char *) &false, sizeof (false))) 
+  if (server < 0)
     return -1;
 
-  if (connect (server, in->ai_addr, in->ai_addrlen) < 0)
-    {
-      close (server);
-      return -1;
-    }
+  setsockopt (server, SOL_SOCKET, SO_KEEPALIVE, (const char *) &_false,
+             sizeof (_false));
 
   return server;
 }
 
-static void
-tls_negotiate (sig)
-     int sig;
+int
+redirect (fd, buffer, nbuffer, write_function)
+     int fd;
+     char *buffer;
+     int nbuffer;
+     int (*write_function) (int, const char *, int);
 {
-  if (tls_ssl_ctx_new (opt_cert_file, opt_key_file) == -1)
-    return;
+  sigset_t mask, orig_mask;
+  struct pollfd writefds[1];
+  int wrote;
+  char *retry;
 
-  (void) tls_ssl_new (tls_ctx, tls_fd); /* Negotiation has done. */
-}
+  sigemptyset (&mask);
+  sigaddset (&mask, SIGALRM);
 
-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"
-         " --force                 force negotiate\n",
-         progname, PACKAGE, VERSION, progname);
+  writefds[0].fd = fd;
+  writefds[0].events = POLLOUT;
+
+  for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
+    {
+      int ready;
+
+      sigprocmask (SIG_SETMASK, &mask, &orig_mask);
+      ready = poll (writefds, 1, -1);
+      sigprocmask (SIG_SETMASK, &orig_mask, NULL);
+      if (ready == -1 && errno != EINTR)
+       {
+         if (opt_assuan)
+           printf ("ERR 1 poll: %s\r\n", strerror (errno));
+         return 1;
+       }
+      wrote = (*write_function)(fd, retry, nbuffer);
+      if (wrote == -1)
+       {
+         if (opt_assuan)
+           printf ("ERR 1 write: %s\r\n", strerror (errno));
+         return 1;
+       }
+    }
 }
-     
+
 int
 main (argc, argv) 
-  int argc;
-  char **argv;
+     int argc;
+     char **argv;
 {
-  int in = fileno (stdin), out = fileno (stdout), 
+  int in = fileno (stdin), out = fileno (stdout),
     nbuffer, wrote;
-  fd_set readfds, writefds;
-  char buffer[BUFSIZ], *retry;
+  struct pollfd readfds[2];
+  char buffer[BUFSIZ];
   struct sigaction act;
+  sigset_t mask, orig_mask;
 
   int this_option_optind = optind ? optind : 1;
   int option_index = 0, c;
@@ -245,14 +178,14 @@ main (argc, argv)
     {
       {"cert-file", 1, 0, 'c'},
       {"key-file", 1, 0, 'k'},
-      {"verify", 1, 0, 'v'},
-      {"force", 0, 0, 'f'},
+      {"help", 0, 0, 'h'},
+      {"assuan", 0, 0, 'A'},
       {0, 0, 0, 0}
     };
 
   while (1)
     {
-      c = getopt_long (argc, argv, "c:k:v:f", long_options, &option_index);
+      c = getopt_long (argc, argv, "c:k:hA", long_options, &option_index);
       if (c == -1)
        break;
     
@@ -264,91 +197,72 @@ main (argc, argv)
        case 'k':
          opt_key_file = optarg;
          break;
-       case 'v':
-         opt_verify = atoi (optarg);
-         break;
-       case 'f':
-         opt_force = true;
+       case 'h':
+         usage (argv[0]);
+         return 0;
+       case 'A':
+         opt_assuan = true;
          break;
        default:
-         usage (basename (argv[0]));
+         usage (argv[0]);
          return 1;
        }
     }
 
   if (optind+2 != argc)
     {
-      usage (basename (argv[0]));
+      usage (argv[0]);
       return 1;
     }
 
-  tls_fd = tls_connect (argv[optind], argv[optind+1]);
+  tls_fd = tcp_connect (argv[optind], argv[optind+1]);
   if (tls_fd < 0)
     {
-      perror ("tls_connect");
+      perror ("tcp_connect");
       return 1;
     }
 
   memset (&act, 0, sizeof (act));
-  act.sa_handler = tls_negotiate;
+  act.sa_handler = do_tls_negotiate;
   sigemptyset (&act.sa_mask);
   act.sa_flags = SA_RESTART|SA_RESETHAND;
   sigaction (SIGALRM, &act, NULL);
 
-  if (opt_force == true)
-    tls_negotiate();
+  sigemptyset (&mask);
+  sigaddset (&mask, SIGALRM);
+
+  readfds[0].fd = in;
+  readfds[1].fd = tls_fd;
+  readfds[0].events = POLLIN;
+  readfds[1].events = POLLIN;
 
   while (1)
     {
-      FD_SET (tls_fd, &readfds);
-      FD_SET (in, &readfds);
-      if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1
-         && errno != EINTR )
+      int ready;
+
+      sigprocmask (SIG_SETMASK, &mask, &orig_mask);
+      ready = poll (readfds, 2, -1);
+      sigprocmask (SIG_SETMASK, &orig_mask, NULL);
+      if (ready == -1 && errno != EINTR)
        {
-         perror ("select");
+         if (opt_assuan)
+           printf ("ERR 1 poll: %s\r\n", strerror (errno));
          return 1;
        }
-      if (FD_ISSET (in, &readfds))
+      if (readfds[0].revents & POLLIN)
        {
          nbuffer = read (in, buffer, sizeof buffer -1);
-
          if (nbuffer == 0)
            goto finish;
-         for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
-           {
-             FD_SET (tls_fd, &writefds);
-             if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1)
-               {
-                 perror ("select");
-                 return 1;
-               }
-             if (tls_conn) 
-               wrote = SSL_write (tls_conn, retry, nbuffer);
-             else
-               wrote = write (tls_fd, retry, nbuffer);
-             if (wrote < 0) goto finish;
-           }
+         redirect (tls_fd, buffer, nbuffer, tls_write);
        }
-      if (FD_ISSET (tls_fd, &readfds))
-       {
-         if (tls_conn)
-           nbuffer = SSL_read (tls_conn, buffer, sizeof buffer -1);
-         else
-           nbuffer = read (tls_fd, buffer, sizeof buffer -1);
+      if (readfds[1].revents & POLLIN)
+       do {
+         nbuffer = tls_read (tls_fd, buffer, sizeof buffer -1);
          if (nbuffer == 0)
            goto finish;
-         for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
-           {
-             FD_SET (out, &writefds);
-             if (select (out+1, NULL, &writefds, NULL, NULL) == -1)
-               {
-                 perror ("select");
-                 return 1;
-               }
-             wrote = write (out, retry, nbuffer);
-             if (wrote < 0) goto finish;
-           }
-       }
+         redirect (out, buffer, nbuffer, write);
+       } while (tls_pending ());
     }
 
  finish: