* starttls.el: Undo the last change.
[elisp/starttls.git] / starttls.c
1 /* TLSv1 filter for STARTTLS extension.
2
3    Copyright (C) 1999, 2000 Daiki Ueno <ueno@unixuser.org>
4
5    Author: Daiki Ueno <ueno@unixuser.org>
6    Created: 1999-11-19
7    Keywords: TLS, OpenSSL
8
9    This file is not part of any package.
10
11    This program is free software; you can redistribute it and/or modify 
12    it under the terms of the GNU General Public License as published by 
13    the Free Software Foundation; either version 2, or (at your option)  
14    any later version.                                                   
15
16    This program is distributed in the hope that it will be useful,      
17    but WITHOUT ANY WARRANTY; without even the implied warranty of       
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        
19    GNU General Public License for more details.                         
20
21    You should have received a copy of the GNU General Public License    
22    along with GNU Emacs; see the file COPYING.  If not, write to the    
23    Free Software Foundation, Inc., 59 Temple Place - Suite 330,         
24    Boston, MA 02111-1307, USA.                                          
25
26 */
27
28 /*
29   How to compile: (OpenSSL is required)
30   
31   gcc -I/usr/local/ssl/include -o starttls starttls.c \
32     -L/usr/local/ssl/lib -lssl -lcrypto
33
34 */
35
36 #include <sys/types.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <unistd.h>
42
43 /* OpenSSL library. */
44
45 #include <openssl/lhash.h>
46 #include <openssl/bn.h>
47 #include <openssl/err.h>
48 #include <openssl/pem.h>
49 #include <openssl/x509.h>
50 #include <openssl/ssl.h>
51
52 #ifdef HAVE_SOCKS_H
53 #include <socks.h>
54 #endif 
55
56 #ifndef HAVE_GETADDRINFO
57 #include "addrinfo.h"
58 #endif /* !HAVE_GETADDRINFO */
59
60 #include <sys/time.h>
61 #include <sys/socket.h>
62 #include <sys/file.h>
63 #include <sys/ioctl.h>
64 #include <sys/stat.h>
65 #include <netdb.h>
66 #include <stdio.h>
67 #include <signal.h>
68 #include <fcntl.h>
69 #include <netinet/in.h>
70 #define _GNU_SOURCE
71 #include <getopt.h>
72
73 static SSL_CTX *tls_ctx = NULL;
74 static SSL *tls_conn = NULL;
75 static int tls_fd;
76
77 static char *opt_cert_file = NULL, *opt_key_file = NULL;
78 static int  opt_verify = 0;
79
80 static int
81 tls_ssl_ctx_new (cert_file, key_file)
82   const char *cert_file, *key_file;
83 {
84   SSL_load_error_strings ();
85   SSLeay_add_ssl_algorithms ();
86
87   tls_ctx = SSL_CTX_new (TLSv1_client_method());
88   if (!tls_ctx)
89     return -1;
90
91   SSL_CTX_set_options (tls_ctx, SSL_OP_ALL /* Work around all known bugs */); 
92
93   if (cert_file)
94     {
95       if (SSL_CTX_use_certificate_file (tls_ctx, cert_file, 
96                                         SSL_FILETYPE_PEM) <= 0)
97         return -1;
98       if (!key_file)
99         key_file = cert_file;
100       if (SSL_CTX_use_PrivateKey_file (tls_ctx, key_file, 
101                                        SSL_FILETYPE_PEM) <= 0)
102         return -1;
103       if (!SSL_CTX_check_private_key (tls_ctx))
104         return -1;
105     }
106
107   SSL_CTX_set_verify (tls_ctx, SSL_VERIFY_NONE, NULL);
108
109   return 0;
110 }
111
112 static int
113 tls_ssl_new(ctx, s)
114   SSL_CTX *ctx;
115   int s;
116 {
117   SSL_SESSION *session;
118   SSL_CIPHER *cipher;
119   X509   *peer;
120
121   tls_conn = (SSL *) SSL_new (ctx);
122   if (!tls_conn)
123     return -1;
124   SSL_clear(tls_conn);
125
126   if (!SSL_set_fd (tls_conn, s))
127     return -1;
128
129   SSL_set_connect_state (tls_conn);
130
131   if (SSL_connect (tls_conn) <= 0)
132     {
133       session = SSL_get_session (tls_conn);
134       if (session)
135         SSL_CTX_remove_session (ctx, session);
136       if (tls_conn!=NULL)
137         SSL_free (tls_conn);
138       return -1;
139     }
140
141   return 0;
142 }
143
144 static int
145 tls_connect (hostname, service)
146      const char *hostname, *service;
147 {
148   struct protoent *proto;
149   struct addrinfo *in, hints;
150   int server, false = 0;
151
152   proto = getprotobyname ("tcp");
153   if (!proto)
154     return -1;
155
156   memset (&hints, 0, sizeof (hints));
157   hints.ai_family = AF_UNSPEC;
158   hints.ai_socktype = SOCK_STREAM;
159   hints.ai_protocol = proto->p_proto;
160
161   if (getaddrinfo (hostname, service, &hints, &in) < 0) 
162     return -1;
163
164   server = socket (in->ai_family, in->ai_socktype, 0);
165   if (server < 0)
166     return -1;
167
168   if (setsockopt (server, SOL_SOCKET, SO_KEEPALIVE,
169                   (const char *) &false, sizeof (false))) 
170     return -1;
171
172   if (connect (server, in->ai_addr, in->ai_addrlen) < 0)
173     {
174       close (server);
175       return -1;
176     }
177
178   return server;
179 }
180
181 static void
182 tls_negotiate (sig)
183      int sig;
184 {
185   if (tls_ssl_ctx_new (opt_cert_file, opt_key_file) == -1)
186     return;
187
188   (void) tls_ssl_new (tls_ctx, tls_fd); /* Negotiation has done. */
189 }
190
191 static void
192 usage (progname)
193      const char *progname;
194 {
195   printf ("%s (%s) %s\n"
196           "Copyright (C) 1999 Free Software Foundation, Inc.\n"
197           "This program comes with ABSOLUTELY NO WARRANTY.\n"
198           "This is free software, and you are welcome to redistribute it\n"
199           "under certain conditions. See the file COPYING for details.\n\n"
200           "Usage: %s [options] host port\n\n"
201           "Options:\n\n"
202           " --cert-file [file]      specify certificate file\n"
203           " --key-file [file]       specify private key file\n"
204           " --verify [level]        set verification level\n",
205           progname, PACKAGE, VERSION, progname);
206 }
207      
208 int
209 main (argc, argv) 
210   int argc;
211   char **argv;
212 {
213   int in = fileno (stdin), out = fileno (stdout), 
214     nbuffer, wrote;
215   fd_set readfds, writefds;
216   char buffer[BUFSIZ], *retry;
217   struct sigaction act;
218
219   int this_option_optind = optind ? optind : 1;
220   int option_index = 0, c;
221   static struct option long_options[] =
222     {
223       {"cert-file", 1, 0, 'c'},
224       {"key-file", 1, 0, 'k'},
225       {"verify", 1, 0, 'v'},
226       {0, 0, 0, 0}
227     };
228
229   while (1)
230     {
231       c = getopt_long (argc, argv, "c:k:v:f", long_options, &option_index);
232       if (c == -1)
233         break;
234     
235       switch (c)
236         {
237         case 'c':
238           opt_cert_file = optarg;
239           break;
240         case 'k':
241           opt_key_file = optarg;
242           break;
243         case 'v':
244           opt_verify = atoi (optarg);
245           break;
246         default:
247           usage (basename (argv[0]));
248           return 1;
249         }
250     }
251
252   if (optind+2 != argc)
253     {
254       usage (basename (argv[0]));
255       return 1;
256     }
257
258   tls_fd = tls_connect (argv[optind], argv[optind+1]);
259   if (tls_fd < 0)
260     {
261       perror ("tls_connect");
262       return 1;
263     }
264
265   memset (&act, 0, sizeof (act));
266   act.sa_handler = tls_negotiate;
267   sigemptyset (&act.sa_mask);
268   act.sa_flags = SA_RESTART|SA_RESETHAND;
269   sigaction (SIGALRM, &act, NULL);
270
271   while (1)
272     {
273       FD_SET (tls_fd, &readfds);
274       FD_SET (in, &readfds);
275       if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1
276           && errno != EINTR )
277         {
278           perror ("select");
279           return 1;
280         }
281       if (FD_ISSET (in, &readfds))
282         {
283           nbuffer = read (in, buffer, sizeof buffer -1);
284
285           if (nbuffer == 0)
286             goto finish;
287           for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
288             {
289               FD_SET (tls_fd, &writefds);
290               if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1)
291                 {
292                   perror ("select");
293                   return 1;
294                 }
295               if (tls_conn) 
296                 wrote = SSL_write (tls_conn, retry, nbuffer);
297               else
298                 wrote = write (tls_fd, retry, nbuffer);
299               if (wrote < 0) goto finish;
300             }
301         }
302       if (FD_ISSET (tls_fd, &readfds))
303         {
304           if (tls_conn)
305             nbuffer = SSL_read (tls_conn, buffer, sizeof buffer -1);
306           else
307             nbuffer = read (tls_fd, buffer, sizeof buffer -1);
308           if (nbuffer == 0)
309             goto finish;
310           for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
311             {
312               FD_SET (out, &writefds);
313               if (select (out+1, NULL, &writefds, NULL, NULL) == -1)
314                 {
315                   perror ("select");
316                   return 1;
317                 }
318               wrote = write (out, retry, nbuffer);
319               if (wrote < 0) goto finish;
320             }
321         }
322     }
323
324  finish:
325   close (in);
326   close (out);
327   
328   return 0;
329 }