1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2011 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* SSL module - GnuTLS version */
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.
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.
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
26 #include <gnutls/gnutls.h>
27 #include <gnutls/x509.h>
32 #include "ssl_client.h"
39 static gboolean initialized
= FALSE
;
40 gnutls_certificate_credentials xcred
;
44 #if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
45 #define GNUTLS_STUPID_CAST (long)
47 #define GNUTLS_STUPID_CAST (int)
54 ssl_input_function func
;
62 gnutls_session session
;
65 static gboolean
ssl_connected( gpointer data
, gint source
, b_input_condition cond
);
66 static gboolean
ssl_starttls_real( gpointer data
, gint source
, b_input_condition cond
);
67 static gboolean
ssl_handshake( gpointer data
, gint source
, b_input_condition cond
);
69 static void ssl_deinit( void );
71 static void ssl_log( int level
, const char *line
)
73 printf( "%d %s", level
, line
);
82 gnutls_certificate_allocate_credentials( &xcred
);
83 if( global
.conf
->cafile
)
85 gnutls_certificate_set_x509_trust_file( xcred
, global
.conf
->cafile
, GNUTLS_X509_FMT_PEM
);
87 /* Not needed in GnuTLS 2.11+ but we support older versions for now. */
88 gnutls_certificate_set_verify_flags( xcred
, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
);
92 gnutls_global_set_log_function( ssl_log
);
94 gnutls_global_set_log_level( 3 );
100 static void ssl_deinit( void )
102 gnutls_global_deinit();
103 gnutls_certificate_free_credentials( xcred
);
106 void *ssl_connect( char *host
, int port
, gboolean verify
, ssl_input_function func
, gpointer data
)
108 struct scd
*conn
= g_new0( struct scd
, 1 );
110 conn
->fd
= proxy_connect( host
, port
, ssl_connected
, conn
);
114 conn
->hostname
= g_strdup( host
);
115 conn
->verify
= verify
&& global
.conf
->cafile
;
126 void *ssl_starttls( int fd
, char *hostname
, gboolean verify
, ssl_input_function func
, gpointer data
)
128 struct scd
*conn
= g_new0( struct scd
, 1 );
134 conn
->hostname
= hostname
;
136 /* For now, SSL verification is globally enabled by setting the cafile
137 setting in bitlbee.conf. Commented out by default because probably
138 not everyone has this file in the same place and plenty of folks
139 may not have the cert of their private Jabber server in it. */
140 conn
->verify
= verify
&& global
.conf
->cafile
;
142 /* This function should be called via a (short) timeout instead of
143 directly from here, because these SSL calls are *supposed* to be
144 *completely* asynchronous and not ready yet when this function
145 (or *_connect, for examle) returns. Also, errors are reported via
146 the callback function, not via this function's return value.
148 In short, doing things like this makes the rest of the code a lot
151 b_timeout_add( 1, ssl_starttls_real
, conn
);
156 static gboolean
ssl_starttls_real( gpointer data
, gint source
, b_input_condition cond
)
158 struct scd
*conn
= data
;
160 return ssl_connected( conn
, conn
->fd
, B_EV_IO_WRITE
);
163 static int verify_certificate_callback( gnutls_session_t session
)
166 const gnutls_datum_t
*cert_list
;
167 unsigned int cert_list_size
;
170 gnutls_x509_crt_t cert
;
171 const char *hostname
;
173 hostname
= gnutls_session_get_ptr( session
);
175 gnutlsret
= gnutls_certificate_verify_peers2( session
, &status
);
177 return VERIFY_CERT_ERROR
;
179 if( status
& GNUTLS_CERT_INVALID
)
180 verifyret
|= VERIFY_CERT_INVALID
;
182 if( status
& GNUTLS_CERT_REVOKED
)
183 verifyret
|= VERIFY_CERT_REVOKED
;
185 if( status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
186 verifyret
|= VERIFY_CERT_SIGNER_NOT_FOUND
;
188 if( status
& GNUTLS_CERT_SIGNER_NOT_CA
)
189 verifyret
|= VERIFY_CERT_SIGNER_NOT_CA
;
191 if( status
& GNUTLS_CERT_INSECURE_ALGORITHM
)
192 verifyret
|= VERIFY_CERT_INSECURE_ALGORITHM
;
194 #ifdef GNUTLS_CERT_NOT_ACTIVATED
195 /* Amusingly, the GnuTLS function used above didn't check for expiry
196 until GnuTLS 2.8 or so. (See CVE-2009-1417) */
197 if( status
& GNUTLS_CERT_NOT_ACTIVATED
)
198 verifyret
|= VERIFY_CERT_NOT_ACTIVATED
;
200 if( status
& GNUTLS_CERT_EXPIRED
)
201 verifyret
|= VERIFY_CERT_EXPIRED
;
204 if( gnutls_certificate_type_get( session
) != GNUTLS_CRT_X509
|| gnutls_x509_crt_init( &cert
) < 0 )
205 return VERIFY_CERT_ERROR
;
207 cert_list
= gnutls_certificate_get_peers( session
, &cert_list_size
);
208 if( cert_list
== NULL
|| gnutls_x509_crt_import( cert
, &cert_list
[0], GNUTLS_X509_FMT_DER
) < 0 )
209 return VERIFY_CERT_ERROR
;
211 if( !gnutls_x509_crt_check_hostname( cert
, hostname
) )
213 verifyret
|= VERIFY_CERT_INVALID
;
214 verifyret
|= VERIFY_CERT_WRONG_HOSTNAME
;
217 gnutls_x509_crt_deinit( cert
);
222 char *ssl_verify_strerror( int code
)
224 GString
*ret
= g_string_new( "" );
226 if( code
& VERIFY_CERT_REVOKED
)
227 g_string_append( ret
, "certificate has been revoked, " );
228 if( code
& VERIFY_CERT_SIGNER_NOT_FOUND
)
229 g_string_append( ret
, "certificate hasn't got a known issuer, " );
230 if( code
& VERIFY_CERT_SIGNER_NOT_CA
)
231 g_string_append( ret
, "certificate's issuer is not a CA, " );
232 if( code
& VERIFY_CERT_INSECURE_ALGORITHM
)
233 g_string_append( ret
, "certificate uses an insecure algorithm, " );
234 if( code
& VERIFY_CERT_NOT_ACTIVATED
)
235 g_string_append( ret
, "certificate has not been activated, " );
236 if( code
& VERIFY_CERT_EXPIRED
)
237 g_string_append( ret
, "certificate has expired, " );
238 if( code
& VERIFY_CERT_WRONG_HOSTNAME
)
239 g_string_append( ret
, "certificate hostname mismatch, " );
243 g_string_free( ret
, TRUE
);
248 g_string_truncate( ret
, ret
->len
- 2 );
249 return g_string_free( ret
, FALSE
);
253 static gboolean
ssl_connected( gpointer data
, gint source
, b_input_condition cond
)
255 struct scd
*conn
= data
;
259 conn
->func( conn
->data
, 0, NULL
, cond
);
266 gnutls_init( &conn
->session
, GNUTLS_CLIENT
);
268 gnutls_session_set_ptr( conn
->session
, (void *) conn
->hostname
);
269 #if GNUTLS_VERSION_NUMBER < 0x020c00
270 gnutls_transport_set_lowat( conn
->session
, 0 );
272 gnutls_set_default_priority( conn
->session
);
273 gnutls_credentials_set( conn
->session
, GNUTLS_CRD_CERTIFICATE
, xcred
);
275 sock_make_nonblocking( conn
->fd
);
276 gnutls_transport_set_ptr( conn
->session
, (gnutls_transport_ptr
) GNUTLS_STUPID_CAST conn
->fd
);
278 return ssl_handshake( data
, source
, cond
);
281 static gboolean
ssl_handshake( gpointer data
, gint source
, b_input_condition cond
)
283 struct scd
*conn
= data
;
286 if( ( st
= gnutls_handshake( conn
->session
) ) < 0 )
288 if( st
== GNUTLS_E_AGAIN
|| st
== GNUTLS_E_INTERRUPTED
)
290 conn
->inpa
= b_input_add( conn
->fd
, ssl_getdirection( conn
),
291 ssl_handshake
, data
);
295 conn
->func( conn
->data
, 0, NULL
, cond
);
297 gnutls_deinit( conn
->session
);
298 closesocket( conn
->fd
);
305 if( conn
->verify
&& ( stver
= verify_certificate_callback( conn
->session
) ) != 0 )
307 conn
->func( conn
->data
, stver
, NULL
, cond
);
309 gnutls_deinit( conn
->session
);
310 closesocket( conn
->fd
);
316 /* For now we can't handle non-blocking perfectly everywhere... */
317 sock_make_blocking( conn
->fd
);
319 conn
->established
= TRUE
;
320 conn
->func( conn
->data
, 0, conn
, cond
);
327 int ssl_read( void *conn
, char *buf
, int len
)
331 if( !((struct scd
*)conn
)->established
)
333 ssl_errno
= SSL_NOHANDSHAKE
;
337 st
= gnutls_record_recv( ((struct scd
*)conn
)->session
, buf
, len
);
340 if( st
== GNUTLS_E_AGAIN
|| st
== GNUTLS_E_INTERRUPTED
)
341 ssl_errno
= SSL_AGAIN
;
343 if( SSLDEBUG
&& getenv( "BITLBEE_DEBUG" ) && st
> 0 ) len
= write( 2, buf
, st
);
348 int ssl_write( void *conn
, const char *buf
, int len
)
352 if( !((struct scd
*)conn
)->established
)
354 ssl_errno
= SSL_NOHANDSHAKE
;
358 st
= gnutls_record_send( ((struct scd
*)conn
)->session
, buf
, len
);
361 if( st
== GNUTLS_E_AGAIN
|| st
== GNUTLS_E_INTERRUPTED
)
362 ssl_errno
= SSL_AGAIN
;
364 if( SSLDEBUG
&& getenv( "BITLBEE_DEBUG" ) && st
> 0 ) len
= write( 2, buf
, st
);
369 int ssl_pending( void *conn
)
374 if( !((struct scd
*)conn
)->established
)
376 ssl_errno
= SSL_NOHANDSHAKE
;
380 #if GNUTLS_VERSION_NUMBER >= 0x03000d && GNUTLS_VERSION_NUMBER <= 0x030012
381 if( ssl_errno
== SSL_AGAIN
)
385 return gnutls_record_check_pending( ((struct scd
*)conn
)->session
) != 0;
388 void ssl_disconnect( void *conn_
)
390 struct scd
*conn
= conn_
;
392 if( conn
->inpa
!= -1 )
393 b_event_remove( conn
->inpa
);
395 if( conn
->established
)
396 gnutls_bye( conn
->session
, GNUTLS_SHUT_WR
);
398 closesocket( conn
->fd
);
401 gnutls_deinit( conn
->session
);
405 int ssl_getfd( void *conn
)
407 return( ((struct scd
*)conn
)->fd
);
410 b_input_condition
ssl_getdirection( void *conn
)
412 return( gnutls_record_get_direction( ((struct scd
*)conn
)->session
) ?
413 B_EV_IO_WRITE
: B_EV_IO_READ
);
416 size_t ssl_des3_encrypt( const unsigned char *key
, size_t key_len
, const unsigned char *input
,
417 size_t input_len
, const unsigned char *iv
, unsigned char **res
)
419 gcry_cipher_hd_t gcr
;
424 *res
= g_malloc( input_len
);
425 st
= gcry_cipher_open( &gcr
, GCRY_CIPHER_3DES
, GCRY_CIPHER_MODE_CBC
, 0 ) ||
426 gcry_cipher_setkey( gcr
, key
, key_len
) ||
427 gcry_cipher_setiv( gcr
, iv
, 8 ) ||
428 gcry_cipher_encrypt( gcr
, *res
, input_len
, input
, input_len
);
430 gcry_cipher_close( gcr
);