c88b5050de51a36d14dfbdb25017ce31a4c25744
[silc.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 i/o channel object */
33 typedef struct
34 {
35         GIOChannel pad;
36         gint fd;
37         GIOChannel *giochan;
38         SSL *ssl;
39         X509 *cert;
40 } GIOSSLChannel;
41         
42 static void irssi_ssl_free(GIOChannel *handle)
43 {
44         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
45         g_io_channel_unref(chan->giochan);
46         SSL_free(chan->ssl);
47         g_free(chan);
48 }
49
50 #if GLIB_MAJOR_VERSION < 2
51
52 #ifdef G_CAN_INLINE
53 G_INLINE_FUNC
54 #else
55 static
56 #endif
57 GIOError ssl_errno(gint e)
58 {
59         switch(e)
60         {
61                 case EINVAL:
62                         return G_IO_ERROR_INVAL;
63                 case EINTR:
64                 case EAGAIN:
65                         return G_IO_ERROR_AGAIN;
66                 default:
67                         return G_IO_ERROR_INVAL;
68         }
69         /*UNREACH*/
70         return G_IO_ERROR_INVAL;
71 }
72
73 static GIOError irssi_ssl_cert_step(GIOSSLChannel *chan)
74 {
75         gint err;
76         switch(err = SSL_do_handshake(chan->ssl))
77         {
78                 case 1:
79                         if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
80                         {
81                                 g_warning("SSL server supplied no certificate");
82                                 return G_IO_ERROR_INVAL;
83                         }
84                         return G_IO_ERROR_NONE;
85                 default:
86                         if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
87                                 return G_IO_ERROR_AGAIN;
88                         return ssl_errno(errno);
89         }
90         /*UNREACH*/
91         return G_IO_ERROR_INVAL;
92 }
93
94 static GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret)
95 {
96         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
97         gint err;
98         
99         if(chan->cert == NULL)
100         {
101                 gint cert_err = irssi_ssl_cert_step(chan);
102                 if(cert_err != G_IO_ERROR_NONE)
103                         return cert_err;
104         }
105         
106         err = SSL_read(chan->ssl, buf, len);
107         if(err < 0)
108         {
109                 *ret = 0;
110                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
111                         return G_IO_ERROR_AGAIN;
112                 return ssl_errno(errno);
113         }
114         else
115         {
116                 *ret = err;
117                 return G_IO_ERROR_NONE;
118         }
119         /*UNREACH*/
120         return -1;
121 }
122
123 static GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint *ret)
124 {
125         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
126         gint err;
127
128         if(chan->cert == NULL)
129         {
130                 gint cert_err = irssi_ssl_cert_step(chan);
131                 if(cert_err != G_IO_ERROR_NONE)
132                         return cert_err;
133         }
134         
135
136         err = SSL_write(chan->ssl, (const char *)buf, len);
137         if(err < 0)
138         {
139                 *ret = 0;
140                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
141                         return G_IO_ERROR_AGAIN;
142                 return ssl_errno(errno);
143         }
144         else
145         {
146                 *ret = err;
147                 return G_IO_ERROR_NONE;
148         }
149         /*UNREACH*/
150         return G_IO_ERROR_INVAL;
151 }
152
153 static GIOError irssi_ssl_seek(GIOChannel *handle, gint offset, GSeekType type)
154 {
155         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
156         GIOError e;
157         e = g_io_channel_seek(chan->giochan, offset, type);
158         return (e == G_IO_ERROR_NONE) ? G_IO_ERROR_NONE : G_IO_ERROR_INVAL;
159 }
160
161 static void irssi_ssl_close(GIOChannel *handle)
162 {
163         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
164         g_io_channel_close(chan->giochan);
165 }
166
167 static guint irssi_ssl_create_watch(GIOChannel *handle, gint priority, GIOCondition cond,
168                              GIOFunc func, gpointer data, GDestroyNotify notify)
169 {
170         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
171
172         return chan->giochan->funcs->io_add_watch(handle, priority, cond, func, data, notify);
173 }
174
175 /* ssl function pointers */
176 static GIOFuncs irssi_ssl_channel_funcs =
177 {
178         irssi_ssl_read,
179         irssi_ssl_write,
180         irssi_ssl_seek,
181         irssi_ssl_close,
182         irssi_ssl_create_watch,
183         irssi_ssl_free
184 };
185
186 #else /* GLIB_MAJOR_VERSION < 2 */
187
188 #ifdef G_CAN_INLINE
189 G_INLINE_FUNC
190 #else
191 static
192 #endif
193 GIOStatus ssl_errno(gint e)
194 {
195         switch(e)
196         {
197                 case EINVAL:
198                         return G_IO_STATUS_ERROR;
199                 case EINTR:
200                 case EAGAIN:
201                         return G_IO_STATUS_AGAIN;
202                 default:
203                         return G_IO_STATUS_ERROR;
204         }
205         /*UNREACH*/
206         return G_IO_STATUS_ERROR;
207 }
208
209 static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
210 {
211         gint err;
212         switch(err = SSL_do_handshake(chan->ssl))
213         {
214                 case 1:
215                         if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
216                         {
217                                 g_warning("SSL server supplied no certificate");
218                                 return G_IO_STATUS_ERROR;
219                         }
220                         return G_IO_STATUS_NORMAL;
221                 default:
222                         if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
223                                 return G_IO_STATUS_AGAIN;
224                         return ssl_errno(errno);
225         }
226         /*UNREACH*/
227         return G_IO_STATUS_ERROR;
228 }
229
230 static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret, GError **gerr)
231 {
232         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
233         gint err;
234         
235         if(chan->cert == NULL)
236         {
237                 gint cert_err = irssi_ssl_cert_step(chan);
238                 if(cert_err != G_IO_STATUS_NORMAL)
239                         return cert_err;
240         }
241         
242         err = SSL_read(chan->ssl, buf, len);
243         if(err < 0)
244         {
245                 *ret = 0;
246                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
247                         return G_IO_STATUS_AGAIN;
248                 return ssl_errno(errno);
249         }
250         else
251         {
252                 *ret = err;
253                 return G_IO_STATUS_NORMAL;
254         }
255         /*UNREACH*/
256         return G_IO_STATUS_ERROR;
257 }
258
259 static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
260 {
261         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
262         gint err;
263
264         if(chan->cert == NULL)
265         {
266                 gint cert_err = irssi_ssl_cert_step(chan);
267                 if(cert_err != G_IO_STATUS_NORMAL)
268                         return cert_err;
269         }
270
271         err = SSL_write(chan->ssl, (const char *)buf, len);
272         if(err < 0)
273         {
274                 *ret = 0;
275                 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
276                         return G_IO_STATUS_AGAIN;
277                 return ssl_errno(errno);
278         }
279         else
280         {
281                 *ret = err;
282                 return G_IO_STATUS_NORMAL;
283         }
284         /*UNREACH*/
285         return G_IO_STATUS_ERROR;
286 }
287
288 static GIOStatus irssi_ssl_seek(GIOChannel *handle, gint64 offset, GSeekType type, GError **gerr)
289 {
290         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
291         GIOError e;
292         e = g_io_channel_seek(chan->giochan, offset, type);
293         return (e == G_IO_ERROR_NONE) ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
294 }
295
296 static GIOStatus irssi_ssl_close(GIOChannel *handle, GError **gerr)
297 {
298         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
299         g_io_channel_close(chan->giochan);
300
301         return G_IO_STATUS_NORMAL;
302 }
303
304 static GSource *irssi_ssl_create_watch(GIOChannel *handle, GIOCondition cond)
305 {
306         GIOSSLChannel *chan = (GIOSSLChannel *)handle;
307
308         return chan->giochan->funcs->io_create_watch(handle, cond);
309 }
310
311 static GIOStatus irssi_ssl_set_flags(GIOChannel *handle, GIOFlags flags, GError **gerr)
312 {
313     GIOSSLChannel *chan = (GIOSSLChannel *)handle;
314
315     return chan->giochan->funcs->io_set_flags(handle, flags, gerr);
316 }
317
318 static GIOFlags irssi_ssl_get_flags(GIOChannel *handle)
319 {
320     GIOSSLChannel *chan = (GIOSSLChannel *)handle;
321
322     return chan->giochan->funcs->io_get_flags(handle);
323 }
324
325 static GIOFuncs irssi_ssl_channel_funcs = {
326     irssi_ssl_read,
327     irssi_ssl_write,
328     irssi_ssl_seek,
329     irssi_ssl_close,
330     irssi_ssl_create_watch,
331     irssi_ssl_free,
332     irssi_ssl_set_flags,
333     irssi_ssl_get_flags
334 };
335
336 #endif
337
338 static SSL_CTX *ssl_ctx = NULL;
339
340 static gboolean irssi_ssl_init(void)
341 {
342         SSL_library_init();
343         SSL_load_error_strings();
344         
345         ssl_ctx = SSL_CTX_new(SSLv23_client_method());
346         if(!ssl_ctx)
347         {
348                 g_error("Initialization of the SSL library failed");
349                 return FALSE;
350         }
351
352         return TRUE;
353
354 }
355
356 static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
357 {
358         GIOSSLChannel *chan;
359         GIOChannel *gchan;
360         int err, fd;
361         SSL *ssl;
362         X509 *cert = NULL;
363
364         g_return_val_if_fail(handle != NULL, NULL);
365         
366         if(!ssl_ctx && !irssi_ssl_init())
367                 return NULL;
368
369         if(!(fd = g_io_channel_unix_get_fd(handle)))
370                 return NULL;
371
372         if(!(ssl = SSL_new(ssl_ctx)))
373         {
374                 g_warning("Failed to allocate SSL structure");
375                 return NULL;
376         }
377
378         if(!(err = SSL_set_fd(ssl, fd)))
379         {
380                 g_warning("Failed to associate socket to SSL stream");
381                 return NULL;
382         }
383
384         if((err = SSL_connect(ssl)) <= 0)
385         {
386                 switch(err = SSL_get_error(ssl, err))
387                 {
388                         case SSL_ERROR_SYSCALL:
389                                 if(errno == EINTR || errno == EAGAIN)
390                         case SSL_ERROR_WANT_READ:
391                         case SSL_ERROR_WANT_WRITE:
392                                         break;
393                         default:
394                                         return NULL;
395                 }
396         }
397         else if(!(cert = SSL_get_peer_certificate(ssl)))
398         {
399                 g_warning("SSL server supplied no certificate");
400                 return NULL;
401         }
402         else
403                 X509_free(cert);
404
405         chan = g_new0(GIOSSLChannel, 1);
406         chan->fd = fd;
407         chan->giochan = handle;
408         chan->ssl = ssl;
409         chan->cert = cert;
410         g_io_channel_ref(handle);
411
412         gchan = (GIOChannel *)chan;
413         gchan->funcs = &irssi_ssl_channel_funcs;
414         g_io_channel_init(gchan);
415         
416         return gchan;
417 }
418
419 GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
420 {
421         GIOChannel *gret = net_connect_ip(ip, port, my_ip);
422         gret = irssi_ssl_get_iochannel(gret);
423         return gret;
424 }
425
426 #else /* HAVE_OPENSSL */
427
428 GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
429 {
430         g_warning("Connection failed: SSL support not enabled in this build.");
431         errno = ENOSYS;
432         return NULL;
433 }
434
435 #endif /* ! HAVE_OPENSSL */