* starttls.el: New file.
[elisp/flim.git] / starttls.c
1 /* TLSv1 filter for STARTTLS extension.
2
3    Copyright (C) 1999 Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
4
5    Author: Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
6    Created: 1999-11-19                                                  
7    Keywords: TLS, OpenSSL
8
9    This file is part of FLIM (Faithful Library about Internet Message).
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 #include <sys/time.h>
57 #include <sys/socket.h>
58 #include <sys/file.h>
59 #include <sys/ioctl.h>
60 #include <sys/stat.h>
61 #include <sys/time.h>
62 #include <netdb.h>
63 #include <stdio.h>
64 #include <signal.h>
65 #include <fcntl.h>
66 #include <netinet/in.h>
67 #include <signal.h>
68
69 static SSL *tls_conn = NULL;
70 static int tls_fd;
71
72 static SSL_CTX *
73 tls_ssl_ctx_new (cert_file, key_file)
74   const char *cert_file, key_file;
75 {
76   SSL_CTX *tls_ctx;
77
78   SSL_load_error_strings ();
79   SSLeay_add_ssl_algorithms ();
80
81   if ((tls_ctx = SSL_CTX_new (TLSv1_client_method())) == NULL)
82     return NULL;
83
84   SSL_CTX_set_options (tls_ctx, SSL_OP_ALL /* Work around all known bugs */); 
85
86   if (cert_file) {
87     if (SSL_CTX_use_certificate_file (tls_ctx, NULL, SSL_FILETYPE_PEM) <= 0)
88       return NULL;
89     if (SSL_CTX_use_PrivateKey_file (tls_ctx, NULL, SSL_FILETYPE_PEM) <= 0)
90       return NULL;
91     if (!SSL_CTX_check_private_key (tls_ctx))
92       return NULL;
93   }
94
95   SSL_CTX_set_verify (tls_ctx, SSL_VERIFY_NONE, NULL);
96
97   return tls_ctx;
98 }
99
100 static SSL *
101 tls_ssl_new(tls_ctx, s)
102   SSL_CTX *tls_ctx;
103   int s;
104 {
105   SSL_SESSION *session;
106   SSL_CIPHER *cipher;
107   X509   *peer;
108
109   if ((tls_conn = (SSL *) SSL_new (tls_ctx)) == NULL)
110     return NULL;
111   SSL_clear(tls_conn);
112
113   if (!SSL_set_fd (tls_conn, s))
114     return NULL;
115
116   SSL_set_connect_state (tls_conn);
117
118   if (SSL_connect (tls_conn) <= 0) {
119     session = SSL_get_session (tls_conn);
120     if (session) {
121       SSL_CTX_remove_session (tls_ctx, session);
122     }
123     if (tls_conn!=NULL)
124       SSL_free (tls_conn);
125     return NULL;
126   }
127
128   return tls_conn;
129 }
130
131 static int
132 tls_connect (hostname, service)
133      const char *hostname, *service;
134 {
135   struct protoent *proto;
136   struct addrinfo *in, hints;
137   int server, false = 0;
138
139   if ((proto = getprotobyname ("tcp")) == NULL)
140     return -1;
141   
142   memset (&hints, 0, sizeof (hints));
143   hints.ai_family = AF_UNSPEC;
144   hints.ai_socktype = SOCK_STREAM;
145   hints.ai_protocol = proto->p_proto;
146   if (getaddrinfo (hostname, service, &hints, &in) < 0) 
147     return -1;
148
149   if ((server = socket (in->ai_family, in->ai_socktype, 0)) < 0)
150     return -1;
151
152   if (setsockopt (server, SOL_SOCKET, SO_KEEPALIVE,
153                   (const char *) &false, sizeof (false))) 
154     return -1;
155
156   if (connect (server, in->ai_addr, in->ai_addrlen) < 0) {
157     close (server);
158     return -1;
159   }
160
161   return server;
162 }
163
164 static void
165 tls_negotiate (sig)
166      int sig;
167 {
168   SSL_CTX *tls_ctx;
169
170   if ((tls_ctx = tls_ssl_ctx_new (NULL, NULL)) == NULL)
171     return;
172
173   tls_conn = tls_ssl_new (tls_ctx, tls_fd); /* Negotiation has done. */
174 }
175
176 int
177 main (argc, argv) 
178   int argc;
179   char **argv;
180 {
181   int in = fileno (stdin), out = fileno (stdout), nbuffer, wrote;
182   fd_set readfds, writefds;
183   char buffer[BUFSIZ], *retry;
184   struct sigaction act;
185
186   if ((tls_fd = tls_connect (argv[1], argv[2])) < 0) {
187     perror ("tls_connect");
188     return 1;
189   }
190
191   memset (&act, 0, sizeof (act));
192   act.sa_handler = tls_negotiate;
193   sigemptyset (&act.sa_mask);
194   act.sa_flags = SA_RESTART|SA_RESETHAND;
195   sigaction (SIGALRM, &act, NULL);
196
197   while (1) {
198     FD_SET (tls_fd, &readfds);
199     FD_SET (in, &readfds);
200     if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1 && 
201         errno != EINTR ) {
202       perror ("select");
203       return 1;
204     }
205     if (FD_ISSET (in, &readfds)) {
206       nbuffer = read (in, buffer, sizeof buffer -1);
207
208       if (nbuffer == 0)
209         goto finish;
210       for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote) {
211         FD_SET (tls_fd, &writefds);
212         if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1) {
213           perror ("select");
214           return 1;
215         }
216         if (tls_conn) 
217           wrote = SSL_write (tls_conn, retry, nbuffer);
218         else
219           wrote = write (tls_fd, retry, nbuffer);
220         if (wrote < 0) goto finish;
221       }
222     }
223     if (FD_ISSET (tls_fd, &readfds)) {
224       if (tls_conn)
225         nbuffer = SSL_read (tls_conn, buffer, sizeof buffer -1);
226       else
227         nbuffer = read (tls_fd, buffer, sizeof buffer -1);
228       if (nbuffer == 0)
229         goto finish;
230       for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote) {
231         FD_SET (out, &writefds);
232         if (select (out+1, NULL, &writefds, NULL, NULL) == -1) {
233           perror ("select");
234           return 1;
235         }
236         wrote = write (out, retry, nbuffer);
237         if (wrote < 0) goto finish;
238       }
239     }
240   }
241
242  finish:
243   close (in);
244   close (out);
245   
246   return 0;
247 }