Merged with Irssi CVS.
[crypto.git] / apps / irssi / src / core / network-openssl.c
1 /*
2  network-ssl.c : SSL support
3
4     Copyright (C) 2002 vjt
5
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 2 of the License, or
9     (at your option) any later version.
10
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.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "network.h"
23
24 #ifdef HAVE_OPENSSL
25
26 #include <openssl/crypto.h>
27 #include <openssl/x509.h>
28 #include <openssl/pem.h>
29 #include <openssl/ssl.h>
30 #include <openssl/err.h>
31
32 /* ssl read */
33 GIOError irssi_ssl_read(GIOChannel *, gchar *, guint, guint *);
34 /* ssl write */
35 GIOError irssi_ssl_write(GIOChannel *, gchar *, guint, guint*);
36 /* ssl seek */
37 GIOError irssi_ssl_seek(GIOChannel *, gint, GSeekType);
38 /* ssl close */
39 void irssi_ssl_close(GIOChannel *);
40 #if GLIB_MAJOR_VERSION < 2
41 /* ssl create watch */
42 guint irssi_ssl_create_watch(GIOChannel *, gint, GIOCondition, GIOFunc, gpointer, GDestroyNotify);
43 #else
44 GSource *irssi_ssl_create_watch(GIOChannel *, GIOCondition);
45 #endif
46 /* ssl free */
47 void irssi_ssl_free(GIOChannel *);
48
49 /* ssl i/o channel object */
50 typedef struct
51 {
52         GIOChannel pad;
53         gint fd;
54         GIOChannel *giochan;
55         SSL *ssl;
56         X509 *cert;
57 } GIOSSLChannel;
58         
59 /* ssl function pointers */
60 GIOFuncs irssi_ssl_channel_funcs =
61 {
62         irssi_ssl_read,
63         irssi_ssl_write,
64         irssi_ssl_seek,
65         irssi_ssl_close,
66         irssi_ssl_create_watch,
67         irssi_ssl_free
68 };
69
70 SSL_CTX *ssl_ctx = NULL;
71
72 #ifdef G_CAN_INLINE
73 G_INLINE_FUNC
74 #endif
75 gint ssl_errno(gint e)
76 {
77         switch(e)
78         {
79                 case EINVAL:
80                         return G_IO_ERROR_INVAL;
81                 case EINTR:
82                 case EAGAIN:
83                         return G_IO_ERROR_AGAIN;
84                 default:
85                         return G_IO_ERROR_INVAL;
86         }
87         /*UNREACH*/
88         return -1;
89 }
90
91 gboolean irssi_ssl_cert_step(GIOSSLChannel *chan)
92 {
93         gint err;
94         switch(err = SSL_do_handshake(chan->ssl))
95         {
96                 case 1:
97                         if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
98                         {
99                                 g_warning("SSL server supplied no certificate");
100                                 return G_IO_ERROR_INVAL;
101                         }
102                         return G_IO_ERROR_NONE;
103                 default:
104                         if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
105                                 return G_IO_ERROR_AGAIN;
106                         return ssl_errno(errno);
107         }
108         /*UNREACH*/
109         return -1;
110 }
111
112 GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret)
113 {
114         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
115         gint err;
116         
117         if(chan->cert == NULL)
118         {
119                 gint cert_err = irssi_ssl_cert_step(chan);
120                 if(cert_err != G_IO_ERROR_NONE)
121                         return cert_err;
122         }
123         
124         err = SSL_read(chan->ssl, buf, len);
125         if(err < 0)
126         {
127                 *ret = 0;
128                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
129                         return G_IO_ERROR_AGAIN;
130                 return ssl_errno(errno);
131         }
132         else
133         {
134                 *ret = err;
135                 return G_IO_ERROR_NONE;
136         }
137         /*UNREACH*/
138         return -1;
139 }
140
141 GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint *ret)
142 {
143         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
144         gint err;
145
146         if(chan->cert == NULL)
147         {
148                 gint cert_err = irssi_ssl_cert_step(chan);
149                 if(cert_err != G_IO_ERROR_NONE)
150                         return cert_err;
151         }
152         
153
154         err = SSL_write(chan->ssl, (const char *)buf, len);
155         if(err < 0)
156         {
157                 *ret = 0;
158                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
159                         return G_IO_ERROR_AGAIN;
160                 return ssl_errno(errno);
161         }
162         else
163         {
164                 *ret = err;
165                 return G_IO_ERROR_NONE;
166         }
167         /*UNREACH*/
168         return -1;
169 }
170
171 GIOError irssi_ssl_seek(GIOChannel *handle, gint offset, GSeekType type)
172 {
173         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
174         GIOError e;
175         e = g_io_channel_seek(chan->giochan, offset, type);
176         return (e == G_IO_ERROR_NONE) ? G_IO_ERROR_NONE : G_IO_ERROR_INVAL;
177 }
178
179 void irssi_ssl_close(GIOChannel *handle)
180 {
181         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
182         g_io_channel_close(chan->giochan);
183 }
184
185 #if GLIB_MAJOR_VERSION < 2
186 guint irssi_ssl_create_watch(GIOChannel *handle, gint priority, GIOCondition cond,
187                              GIOFunc func, gpointer data, GDestroyNotify notify)
188 {
189         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
190
191         return chan->giochan->funcs->io_add_watch(handle, priority, cond, func, data, notify);
192 }
193 #else
194 GSource *irssi_ssl_create_watch(GIOChannel *handle, GIOCondition cond)
195 {
196         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
197
198         return chan->giochan->funcs->io_create_watch(handle, cond);
199 }
200 #endif
201
202 void irssi_ssl_free(GIOChannel *handle)
203 {
204         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
205         g_io_channel_unref(chan->giochan);
206         SSL_free(chan->ssl);
207         g_free(chan);
208 }
209
210 gboolean irssi_ssl_init(void)
211 {
212         SSL_library_init();
213         SSL_load_error_strings();
214         
215         ssl_ctx = SSL_CTX_new(SSLv23_client_method());
216         if(!ssl_ctx)
217         {
218                 g_error("Initialization of the SSL library failed");
219                 return FALSE;
220         }
221
222         return TRUE;
223
224 }
225
226 GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
227 {
228         GIOSSLChannel *chan;
229         GIOChannel *gchan;
230         int err, fd;
231         SSL *ssl;
232         X509 *cert = NULL;
233
234         g_return_val_if_fail(handle != NULL, NULL);
235         
236         if(!ssl_ctx && !irssi_ssl_init())
237                 return NULL;
238
239         if(!(fd = g_io_channel_unix_get_fd(handle)))
240                 return NULL;
241
242         if(!(ssl = SSL_new(ssl_ctx)))
243         {
244                 g_warning("Failed to allocate SSL structure");
245                 return NULL;
246         }
247
248         if(!(err = SSL_set_fd(ssl, fd)))
249         {
250                 g_warning("Failed to associate socket to SSL stream");
251                 return NULL;
252         }
253
254         if((err = SSL_connect(ssl)) <= 0)
255         {
256                 switch(err = SSL_get_error(ssl, err))
257                 {
258                         case SSL_ERROR_SYSCALL:
259                                 if(errno == EINTR || errno == EAGAIN)
260                         case SSL_ERROR_WANT_READ:
261                         case SSL_ERROR_WANT_WRITE:
262                                         break;
263                         default:
264                                         return NULL;
265                 }
266         }
267         else if(!(cert = SSL_get_peer_certificate(ssl)))
268         {
269                 g_warning("SSL server supplied no certificate");
270                 return NULL;
271         }
272         else
273                 X509_free(cert);
274
275         chan = g_new0(GIOSSLChannel, 1);
276         chan->fd = fd;
277         chan->giochan = handle;
278         chan->ssl = ssl;
279         chan->cert = cert;
280         g_io_channel_ref(handle);
281
282         gchan = (GIOChannel *)chan;
283         gchan->funcs = &irssi_ssl_channel_funcs;
284         g_io_channel_init(gchan);
285         
286         return gchan;
287 }
288
289 GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
290 {
291         GIOChannel *gret = net_connect_ip(ip, port, my_ip);
292         gret = irssi_ssl_get_iochannel(gret);
293         return gret;
294 }
295
296 #else /* HAVE_OPENSSL */
297
298 GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
299 {
300         g_warning("Connection failed: SSL support not enabled in this build.");
301         errno = ENOSYS;
302         return NULL;
303 }
304
305 #endif /* ! HAVE_OPENSSL */