source: lib/ssl_gnutls.c @ 19a8088

Last change on this file since 19a8088 was f920d9e, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-19T07:51:35Z

Added starttls code to ssl_openssl.c so GnuTLS isn't the only supported
SSL module in this branch anymore.

  • Property mode set to 100644
File size: 5.7 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* SSL module - GnuTLS version                                          */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include <gnutls/gnutls.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include "proxy.h"
30#include "ssl_client.h"
31#include "sock.h"
32#include "stdlib.h"
33
34int ssl_errno = 0;
35
36static gboolean initialized = FALSE;
37
38struct scd
39{
40        ssl_input_function func;
41        gpointer data;
42        int fd;
43        gboolean established;
44        int inpa;
45       
46        gnutls_session session;
47        gnutls_certificate_credentials xcred;
48};
49
50static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
51static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
52static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
53
54
55void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
56{
57        struct scd *conn = g_new0( struct scd, 1 );
58       
59        conn->fd = proxy_connect( host, port, ssl_connected, conn );
60        conn->func = func;
61        conn->data = data;
62        conn->inpa = -1;
63       
64        if( conn->fd < 0 )
65        {
66                g_free( conn );
67                return NULL;
68        }
69       
70        return conn;
71}
72
73void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
74{
75        struct scd *conn = g_new0( struct scd, 1 );
76       
77        conn->fd = fd;
78        conn->func = func;
79        conn->data = data;
80        conn->inpa = -1;
81       
82        /* This function should be called via a (short) timeout instead of
83           directly from here, because these SSL calls are *supposed* to be
84           *completely* asynchronous and not ready yet when this function
85           (or *_connect, for examle) returns. Also, errors are reported via
86           the callback function, not via this function's return value.
87           
88           In short, doing things like this makes the rest of the code a lot
89           simpler. */
90       
91        b_timeout_add( 1, ssl_starttls_real, conn );
92       
93        return conn;
94}
95
96static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
97{
98        struct scd *conn = data;
99       
100        return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
101}
102
103static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
104{
105        struct scd *conn = data;
106       
107        if( source == -1 )
108        {
109                conn->func( conn->data, NULL, cond );
110                g_free( conn );
111                return FALSE;
112        }
113       
114        if( !initialized )
115        {
116                gnutls_global_init();
117                initialized = TRUE;
118                atexit( gnutls_global_deinit );
119        }
120       
121        gnutls_certificate_allocate_credentials( &conn->xcred );
122        gnutls_init( &conn->session, GNUTLS_CLIENT );
123        gnutls_set_default_priority( conn->session );
124        gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
125       
126        sock_make_nonblocking( conn->fd );
127        gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd );
128       
129        return ssl_handshake( data, source, cond );
130}
131
132static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
133{
134        struct scd *conn = data;
135        int st;
136       
137        if( ( st = gnutls_handshake( conn->session ) ) < 0 )
138        {
139                if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
140                {
141                        conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ),
142                                                  ssl_handshake, data );
143                }
144                else
145                {
146                        conn->func( conn->data, NULL, cond );
147                       
148                        gnutls_deinit( conn->session );
149                        gnutls_certificate_free_credentials( conn->xcred );
150                        closesocket( conn->fd );
151                       
152                        g_free( conn );
153                }
154        }
155        else
156        {
157                /* For now we can't handle non-blocking perfectly everywhere... */
158                sock_make_blocking( conn->fd );
159               
160                conn->established = TRUE;
161                conn->func( conn->data, conn, cond );
162        }
163       
164        return FALSE;
165}
166
167int ssl_read( void *conn, char *buf, int len )
168{
169        int st;
170       
171        if( !((struct scd*)conn)->established )
172        {
173                ssl_errno = SSL_NOHANDSHAKE;
174                return( -1 );
175        }
176       
177        st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
178       
179        ssl_errno = SSL_OK;
180        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
181                ssl_errno = SSL_AGAIN;
182       
183        return st;
184}
185
186int ssl_write( void *conn, const char *buf, int len )
187{
188        int st;
189       
190        if( !((struct scd*)conn)->established )
191        {
192                ssl_errno = SSL_NOHANDSHAKE;
193                return( -1 );
194        }
195       
196        st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
197       
198        ssl_errno = SSL_OK;
199        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
200                ssl_errno = SSL_AGAIN;
201       
202        return st;
203}
204
205void ssl_disconnect( void *conn_ )
206{
207        struct scd *conn = conn_;
208       
209        if( conn->inpa != -1 )
210                b_event_remove( conn->inpa );
211       
212        if( conn->established )
213                gnutls_bye( conn->session, GNUTLS_SHUT_WR );
214       
215        closesocket( conn->fd );
216       
217        gnutls_deinit( conn->session );
218        gnutls_certificate_free_credentials( conn->xcred );
219        g_free( conn );
220}
221
222int ssl_getfd( void *conn )
223{
224        return( ((struct scd*)conn)->fd );
225}
226
227b_input_condition ssl_getdirection( void *conn )
228{
229        return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
230                GAIM_INPUT_WRITE : GAIM_INPUT_READ );
231}
Note: See TracBrowser for help on using the repository browser.