diff options
-rw-r--r-- | bitlbee.conf | 23 | ||||
-rw-r--r-- | conf.c | 14 | ||||
-rw-r--r-- | conf.h | 1 | ||||
-rw-r--r-- | doc/user-guide/commands.xml | 24 | ||||
-rw-r--r-- | lib/http_client.c | 21 | ||||
-rw-r--r-- | lib/ssl_bogus.c | 9 | ||||
-rw-r--r-- | lib/ssl_client.h | 21 | ||||
-rw-r--r-- | lib/ssl_gnutls.c | 145 | ||||
-rw-r--r-- | lib/ssl_nss.c | 27 | ||||
-rw-r--r-- | lib/ssl_openssl.c | 29 | ||||
-rw-r--r-- | protocols/jabber/io.c | 32 | ||||
-rw-r--r-- | protocols/jabber/jabber.c | 5 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 2 | ||||
-rw-r--r-- | protocols/msn/soap.c | 10 | ||||
-rw-r--r-- | protocols/skype/skype.c | 4 |
15 files changed, 323 insertions, 44 deletions
diff --git a/bitlbee.conf b/bitlbee.conf index c5dafd9f..50bb060f 100644 --- a/bitlbee.conf +++ b/bitlbee.conf @@ -115,9 +115,9 @@ ## ## (Obviously, the username and password are optional) ## -## Proxy = http://john:doe@proxy.localnet.com:8080 -## Proxy = socks4://socksproxy.localnet.com -## Proxy = socks5://socksproxy.localnet.com +# Proxy = http://john:doe@proxy.localnet.com:8080 +# Proxy = socks4://socksproxy.localnet.com +# Proxy = socks5://socksproxy.localnet.com ## Protocols offered by bitlbee ## @@ -125,8 +125,23 @@ ## allows to remove the support of protocol, even if compiled in. If ## nothing is given, there are no restrictions. ## -## Protocols = jabber yahoo +# Protocols = jabber yahoo +## Trusted CAs +## +## Path to a file containing a list of trusted certificate authorities used in +## the verification of server certificates. +## +## Uncomment this and make sure the file actually exists and contains all +## certificate authorities you're willing to accept (default value should +## work on at least Debian/Ubuntu systems with the "ca-certificates" package +## installed). As long as the line is commented out, SSL certificate +## verification is completely disabled. +## +## The location of this file may be different on other distros/OSes. For +## example, try /etc/ssl/ca-bundle.pem on OpenSUSE. +## +# CAfile = /etc/ssl/certs/ca-certificates.crt [defaults] @@ -66,6 +66,7 @@ conf_t *conf_load( int argc, char *argv[] ) conf->ft_max_kbps = G_MAXUINT; conf->ft_listen = NULL; conf->protocols = NULL; + conf->cafile = NULL; proxytype = 0; i = conf_loadini( conf, global.conf_file ); @@ -176,6 +177,14 @@ conf_t *conf_load( int argc, char *argv[] ) if( config_missing ) fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file ); + if( conf->cafile && access( conf->cafile, R_OK ) != 0 ) + { + /* Let's treat this as a serious problem so people won't think + they're secure when in fact they're not. */ + fprintf( stderr, "Error: Could not read CA file %s: %s\n", conf->cafile, strerror( errno ) ); + return NULL; + } + return conf; } @@ -339,6 +348,11 @@ static int conf_loadini( conf_t *conf, char *file ) g_strfreev( conf->protocols ); conf->protocols = g_strsplit_set( ini->value, " \t,;", -1 ); } + else if( g_strcasecmp( ini->key, "cafile" ) == 0 ) + { + g_free( conf->cafile ); + conf->cafile = g_strdup( ini->value ); + } else { fprintf( stderr, "Error: Unknown setting `%s` in configuration file (line %d).\n", ini->key, ini->line ); @@ -53,6 +53,7 @@ typedef struct conf int ft_max_kbps; char *ft_listen; char **protocols; + char *cafile; } conf_t; G_GNUC_MALLOC conf_t *conf_load( int argc, char *argv[] ); diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 3a9202dc..8fc58c9e 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -1391,7 +1391,11 @@ <description> <para> - Currently only available for Jabber connections. Set this to true if the server accepts SSL connections. + Currently only available for Jabber connections. Set this to true if you want to connect to the server on an SSL-enabled port (usually 5223). + </para> + + <para> + Please note that this method of establishing a secure connection to the server has long been deprecated. You are encouraged to look at the <emphasis>tls</emphasis> setting instead. </para> </description> </bitlbee-setting> @@ -1484,6 +1488,24 @@ </description> </bitlbee-setting> + <bitlbee-setting name="tls_verify" type="boolean" scope="account"> + <default>true</default> + + <description> + <para> + Currently only available for Jabber connections in combination with the <emphasis>tls</emphasis> setting. Set this to <emphasis>true</emphasis> if you want BitlBee to strictly verify the server's certificate against a list of trusted certificate authorities. + </para> + + <para> + The hostname used in the certificate verification is the value of the <emphasis>server</emphasis> setting if the latter is nonempty and the domain of the username else. If you get a hostname related error when connecting to Google Talk with a username from the gmail.com or googlemail.com domain, please try to empty the <emphasis>server</emphasis> setting. + </para> + + <para> + Please note that no certificate verification is performed when the <emphasis>ssl</emphasis> setting is used, or when the <emphasis>CAfile</emphasis> setting in <emphasis>bitlbee.conf</emphasis> is not set. + </para> + </description> + </bitlbee-setting> + <bitlbee-setting name="to_char" type="string" scope="global"> <default>": "</default> diff --git a/lib/http_client.c b/lib/http_client.c index 9d986412..98a99f7c 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -32,7 +32,7 @@ static gboolean http_connected( gpointer data, int source, b_input_condition cond ); -static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ); +static gboolean http_ssl_connected( gpointer data, int returncode, void *source, b_input_condition cond ); static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ); static void http_free( struct http_request *req ); @@ -46,7 +46,7 @@ struct http_request *http_dorequest( char *host, int port, int ssl, char *reques if( ssl ) { - req->ssl = ssl_connect( host, port, http_ssl_connected, req ); + req->ssl = ssl_connect( host, port, TRUE, http_ssl_connected, req ); if( req->ssl == NULL ) error = 1; } @@ -162,19 +162,30 @@ static gboolean http_connected( gpointer data, int source, b_input_condition con return FALSE; error: - req->status_string = g_strdup( "Error while writing HTTP request" ); + if( req->status_string == NULL ) + req->status_string = g_strdup( "Error while writing HTTP request" ); req->func( req ); http_free( req ); return FALSE; } -static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ) +static gboolean http_ssl_connected( gpointer data, int returncode, void *source, b_input_condition cond ) { struct http_request *req = data; if( source == NULL ) + { + if( returncode != 0 ) + { + char *err = ssl_verify_strerror( returncode ); + req->status_string = g_strdup_printf( + "Certificate verification problem 0x%x: %s", + returncode, err ? err : "Unknown" ); + g_free( err ); + } return http_connected( data, -1, cond ); + } req->fd = ssl_getfd( source ); @@ -438,7 +449,7 @@ got_reply: if( new_proto == PROTO_HTTPS ) { - req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req ); + req->ssl = ssl_connect( new_host, new_port, TRUE, http_ssl_connected, req ); if( req->ssl == NULL ) error = 1; } diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index f4ce5d4d..e134201d 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -31,7 +31,7 @@ void ssl_init( void ) { } -void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ) { return( NULL ); } @@ -55,7 +55,7 @@ int ssl_getfd( void *conn ) return( -1 ); } -void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ) { return NULL; } @@ -69,3 +69,8 @@ int ssl_pending( void *conn ) { return 0; } + +char *ssl_verify_strerror( int code ) +{ + return NULL; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h index 091335c5..d8822143 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -36,14 +36,25 @@ /* Some generic error codes. Especially SSL_AGAIN is important if you want to do asynchronous I/O. */ +#define NSS_VERIFY_ERROR -2 +#define OPENSSL_VERIFY_ERROR -1 #define SSL_OK 0 #define SSL_NOHANDSHAKE 1 #define SSL_AGAIN 2 +#define VERIFY_CERT_ERROR 2 +#define VERIFY_CERT_INVALID 4 +#define VERIFY_CERT_REVOKED 8 +#define VERIFY_CERT_SIGNER_NOT_FOUND 16 +#define VERIFY_CERT_SIGNER_NOT_CA 32 +#define VERIFY_CERT_INSECURE_ALGORITHM 64 +#define VERIFY_CERT_NOT_ACTIVATED 128 +#define VERIFY_CERT_EXPIRED 256 +#define VERIFY_CERT_WRONG_HOSTNAME 512 extern int ssl_errno; /* This is what your callback function should look like. */ -typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); +typedef gboolean (*ssl_input_function)(gpointer, int, void*, b_input_condition); /* Perform any global initialization the SSL library might need. */ @@ -52,11 +63,11 @@ G_MODULE_EXPORT void ssl_init( void ); /* Connect to host:port, call the given function when the connection is ready to be used for SSL traffic. This is all done asynchronously, no blocking I/O! (Except for the DNS lookups, for now...) */ -G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); +G_MODULE_EXPORT void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ); /* Start an SSL session on an existing fd. Useful for STARTTLS functionality, for example in Jabber. */ -G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); +G_MODULE_EXPORT void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ); /* Obviously you need special read/write functions to read data. */ G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); @@ -89,4 +100,8 @@ G_MODULE_EXPORT int ssl_getfd( void *conn ); the same action as the handler that just received the SSL_AGAIN.) */ G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); +/* Converts a verification bitfield passed to ssl_input_function into + a more useful string. Or NULL if it had no useful bits set. */ +G_MODULE_EXPORT char *ssl_verify_strerror( int code ); + G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index ccab8aca..b4bc72d5 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -24,6 +24,7 @@ */ #include <gnutls/gnutls.h> +#include <gnutls/x509.h> #include <gcrypt.h> #include <fcntl.h> #include <unistd.h> @@ -31,6 +32,7 @@ #include "ssl_client.h" #include "sock.h" #include "stdlib.h" +#include "bitlbee.h" int ssl_errno = 0; @@ -53,6 +55,8 @@ struct scd int fd; gboolean established; int inpa; + char *hostname; + gboolean verify; gnutls_session session; gnutls_certificate_credentials xcred; @@ -73,7 +77,7 @@ void ssl_init( void ) atexit( gnutls_global_deinit ); } -void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -81,6 +85,8 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data conn->func = func; conn->data = data; conn->inpa = -1; + conn->hostname = g_strdup( host ); + conn->verify = verify && global.conf->cafile; if( conn->fd < 0 ) { @@ -91,7 +97,7 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data return conn; } -void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -99,6 +105,13 @@ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) conn->func = func; conn->data = data; conn->inpa = -1; + conn->hostname = hostname; + + /* For now, SSL verification is globally enabled by setting the cafile + setting in bitlbee.conf. Commented out by default because probably + not everyone has this file in the same place and plenty of folks + may not have the cert of their private Jabber server in it. */ + conn->verify = verify && global.conf->cafile; /* This function should be called via a (short) timeout instead of directly from here, because these SSL calls are *supposed* to be @@ -121,13 +134,106 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } +static int verify_certificate_callback( gnutls_session_t session ) +{ + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + int gnutlsret; + int verifyret = 0; + gnutls_x509_crt_t cert; + const char *hostname; + + hostname = gnutls_session_get_ptr(session ); + + gnutlsret = gnutls_certificate_verify_peers2( session, &status ); + if( gnutlsret < 0 ) + return VERIFY_CERT_ERROR; + + if( status & GNUTLS_CERT_INVALID ) + verifyret |= VERIFY_CERT_INVALID; + + if( status & GNUTLS_CERT_REVOKED ) + verifyret |= VERIFY_CERT_REVOKED; + + if( status & GNUTLS_CERT_SIGNER_NOT_FOUND ) + verifyret |= VERIFY_CERT_SIGNER_NOT_FOUND; + + if( status & GNUTLS_CERT_SIGNER_NOT_CA ) + verifyret |= VERIFY_CERT_SIGNER_NOT_CA; + + if( status & GNUTLS_CERT_INSECURE_ALGORITHM ) + verifyret |= VERIFY_CERT_INSECURE_ALGORITHM; + + if( status & GNUTLS_CERT_NOT_ACTIVATED ) + verifyret |= VERIFY_CERT_NOT_ACTIVATED; + + if( status & GNUTLS_CERT_EXPIRED ) + verifyret |= VERIFY_CERT_EXPIRED; + + /* The following check is already performed inside + * gnutls_certificate_verify_peers2, so we don't need it. + + * if( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 ) + * return GNUTLS_E_CERTIFICATE_ERROR; + */ + + if( gnutls_x509_crt_init( &cert ) < 0 ) + return VERIFY_CERT_ERROR; + + cert_list = gnutls_certificate_get_peers( session, &cert_list_size ); + if( cert_list == NULL || gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER ) < 0 ) + return VERIFY_CERT_ERROR; + + if( !gnutls_x509_crt_check_hostname( cert, hostname ) ) + { + verifyret |= VERIFY_CERT_INVALID; + verifyret |= VERIFY_CERT_WRONG_HOSTNAME; + } + + gnutls_x509_crt_deinit( cert ); + + return verifyret; +} + +char *ssl_verify_strerror( int code ) +{ + GString *ret = g_string_new( "" ); + + if( code & VERIFY_CERT_REVOKED ) + g_string_append( ret, "certificate has been revoked, " ); + if( code & VERIFY_CERT_SIGNER_NOT_FOUND ) + g_string_append( ret, "certificate hasn't got a known issuer, " ); + if( code & VERIFY_CERT_SIGNER_NOT_CA ) + g_string_append( ret, "certificate's issuer is not a CA, " ); + if( code & VERIFY_CERT_INSECURE_ALGORITHM ) + g_string_append( ret, "certificate uses an insecure algorithm, " ); + if( code & VERIFY_CERT_NOT_ACTIVATED ) + g_string_append( ret, "certificate has not been activated, " ); + if( code & VERIFY_CERT_EXPIRED ) + g_string_append( ret, "certificate has expired, " ); + if( code & VERIFY_CERT_WRONG_HOSTNAME ) + g_string_append( ret, "certificate hostname mismatch, " ); + + if( ret->len == 0 ) + { + g_string_free( ret, TRUE ); + return NULL; + } + else + { + g_string_truncate( ret, ret->len - 2 ); + return g_string_free( ret, FALSE ); + } +} + static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) { struct scd *conn = data; if( source == -1 ) { - conn->func( conn->data, NULL, cond ); + conn->func( conn->data, 0, NULL, cond ); g_free( conn ); return FALSE; } @@ -135,7 +241,15 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con ssl_init(); gnutls_certificate_allocate_credentials( &conn->xcred ); + if( conn->verify && global.conf->cafile ) + { + gnutls_certificate_set_x509_trust_file( conn->xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM ); + gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT ); + } + gnutls_init( &conn->session, GNUTLS_CLIENT ); + if( conn->verify ) + gnutls_session_set_ptr( conn->session, (void *) conn->hostname ); #if GNUTLS_VERSION_NUMBER < 0x020c00 gnutls_transport_set_lowat( conn->session, 0 ); #endif @@ -151,7 +265,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) { struct scd *conn = data; - int st; + int st, stver; if( ( st = gnutls_handshake( conn->session ) ) < 0 ) { @@ -162,7 +276,7 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con } else { - conn->func( conn->data, NULL, cond ); + conn->func( conn->data, 0, NULL, cond ); gnutls_deinit( conn->session ); gnutls_certificate_free_credentials( conn->xcred ); @@ -173,11 +287,24 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con } else { - /* For now we can't handle non-blocking perfectly everywhere... */ - sock_make_blocking( conn->fd ); + if( conn->verify && ( stver = verify_certificate_callback( conn->session ) ) != 0 ) + { + conn->func( conn->data, stver, NULL, cond ); + + gnutls_deinit( conn->session ); + gnutls_certificate_free_credentials( conn->xcred ); + closesocket( conn->fd ); + + g_free( conn ); + } + else + { + /* For now we can't handle non-blocking perfectly everywhere... */ + sock_make_blocking( conn->fd ); - conn->established = TRUE; - conn->func( conn->data, conn, cond ); + conn->established = TRUE; + conn->func( conn->data, 0, conn, cond ); + } } return FALSE; diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index ec524ca6..5b573f9b 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -51,6 +51,7 @@ struct scd int fd; PRFileDesc *prfd; gboolean established; + gboolean verify; }; static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); @@ -101,7 +102,7 @@ void ssl_init( void ) initialized = TRUE; } -void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -131,13 +132,14 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } -void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); conn->fd = fd; conn->func = func; conn->data = data; + conn->verify = verify; /* This function should be called via a (short) timeout instead of directly from here, because these SSL calls are *supposed* to be @@ -157,6 +159,18 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con { struct scd *conn = data; + /* Right now we don't have any verification functionality for nss so we + fail in case verification has been requested by the user. */ + + if( conn->verify ) + { + conn->func( conn->data, NSS_VERIFY_ERROR, NULL, cond ); + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + } + if( source == -1 ) goto ssl_connected_failure; @@ -176,12 +190,12 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con conn->established = TRUE; - conn->func( conn->data, conn, cond ); + conn->func( conn->data, 0, conn, cond ); return FALSE; ssl_connected_failure: - conn->func( conn->data, NULL, cond ); + conn->func( conn->data, 0, NULL, cond ); PR_Close( conn -> prfd ); if( source >= 0 ) closesocket( source ); @@ -237,3 +251,8 @@ b_input_condition ssl_getdirection( void *conn ) /* Just in case someone calls us, let's return the most likely case: */ return B_EV_IO_READ; } + +char *ssl_verify_strerror( int code ) +{ + return g_strdup( "SSL certificate verification not supported by BitlBee NSS code." ); +} diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index 5f64042d..955c8274 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -44,6 +44,7 @@ struct scd gpointer data; int fd; gboolean established; + gboolean verify; int inpa; int lasterr; /* Necessary for SSL_get_error */ @@ -63,7 +64,7 @@ void ssl_init( void ) // SSLeay_add_ssl_algorithms(); } -void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -81,7 +82,7 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data return conn; } -void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -89,6 +90,7 @@ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) conn->func = func; conn->data = data; conn->inpa = -1; + conn->verify = verify; /* This function should be called via a (short) timeout instead of directly from here, because these SSL calls are *supposed* to be @@ -116,6 +118,18 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con struct scd *conn = data; SSL_METHOD *meth; + /* Right now we don't have any verification functionality for openssl so we + fail in case verification has been requested by the user. */ + + if( conn->verify ) + { + conn->func( conn->data, OPENSSL_VERIFY_ERROR, NULL, cond ); + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + } + if( source == -1 ) goto ssl_connected_failure; @@ -140,7 +154,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con return ssl_handshake( data, source, cond ); ssl_connected_failure: - conn->func( conn->data, NULL, cond ); + conn->func( conn->data, 0, NULL, cond ); if( conn->ssl ) { @@ -168,7 +182,7 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con conn->lasterr = SSL_get_error( conn->ssl, st ); if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) { - conn->func( conn->data, NULL, cond ); + conn->func( conn->data, 0, NULL, cond ); SSL_shutdown( conn->ssl ); SSL_free( conn->ssl ); @@ -186,7 +200,7 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con conn->established = TRUE; sock_make_blocking( conn->fd ); /* For now... */ - conn->func( conn->data, conn, cond ); + conn->func( conn->data, 0, conn, cond ); return FALSE; } @@ -273,6 +287,11 @@ b_input_condition ssl_getdirection( void *conn ) return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); } +char *ssl_verify_strerror( int code ) +{ + return g_strdup( "SSL certificate verification not supported by BitlBee OpenSSL code." ); +} + size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res) { int output_length = 0; diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index d3383375..385c45c4 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -278,7 +278,7 @@ gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition c return jabber_start_stream( ic ); } -gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond ) +gboolean jabber_connected_ssl( gpointer data, int returncode, void *source, b_input_condition cond ) { struct im_connection *ic = data; struct jabber_data *jd; @@ -294,8 +294,20 @@ gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition co already, set it to NULL here to prevent a double cleanup: */ jd->ssl = NULL; - imcb_error( ic, "Could not connect to server" ); - imc_logout( ic, TRUE ); + if( returncode != 0 ) + { + char *err = ssl_verify_strerror( returncode ); + imcb_error( ic, "Certificate verification problem 0x%x: %s", + returncode, err ? err : "Unknown" ); + g_free( err ); + imc_logout( ic, FALSE ); + } + else + { + imcb_error( ic, "Could not connect to server" ); + imc_logout( ic, TRUE ); + } + return FALSE; } @@ -399,7 +411,7 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; - char *xmlns; + char *xmlns, *tlsname; xmlns = xt_find_attr( node, "xmlns" ); @@ -425,7 +437,17 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data ) imcb_log( ic, "Converting stream to TLS" ); jd->flags |= JFLAG_STARTTLS_DONE; - jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic ); + + /* If the user specified a server for the account, use this server as the + * hostname in the certificate verification. Else we use the domain from + * the username. */ + if( ic->acc->server && *ic->acc->server ) + tlsname = ic->acc->server; + else + tlsname = jd->server; + + jd->ssl = ssl_starttls( jd->fd, tlsname, set_getbool( &ic->acc->set, "tls_verify" ), + jabber_connected_ssl, ic ); return XT_HANDLED; } diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index fae55ffe..2856f1b6 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -81,6 +81,9 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "tls", "try", set_eval_tls, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + s = set_add( &acc->set, "tls_verify", "true", set_eval_bool, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + s = set_add( &acc->set, "sasl", "true", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT; @@ -232,7 +235,7 @@ static void jabber_login( account_t *acc ) non-standard ports... */ if( set_getbool( &acc->set, "ssl" ) ) { - jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, ic ); + jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), FALSE, jabber_connected_ssl, ic ); jd->fd = jd->ssl ? ssl_getfd( jd->ssl ) : -1; } else diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 364d561c..aa552558 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -308,7 +308,7 @@ extern const struct jabber_away_state jabber_away_state_list[]; int jabber_write_packet( struct im_connection *ic, struct xt_node *node ); int jabber_write( struct im_connection *ic, char *buf, int len ); gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ); -gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond ); +gboolean jabber_connected_ssl( gpointer data, int returncode, void *source, b_input_condition cond ); gboolean jabber_start_stream( struct im_connection *ic ); void jabber_end_stream( struct im_connection *ic ); diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c index 7d9f3791..d9804f49 100644 --- a/protocols/msn/soap.c +++ b/protocols/msn/soap.c @@ -59,6 +59,7 @@ struct msn_soap_req_data void *data; struct im_connection *ic; int ttl; + char *error; char *url, *action, *payload; struct http_request *http_req; @@ -157,13 +158,17 @@ static void msn_soap_handle_response( struct http_request *http_req ) xt_free( parser ); } + if( http_req->status_code != 200 ) + soap_req->error = g_strdup( http_req->status_string ); + st = soap_req->handle_response( soap_req ); fail: g_free( soap_req->url ); g_free( soap_req->action ); g_free( soap_req->payload ); - soap_req->url = soap_req->action = soap_req->payload = NULL; + g_free( soap_req->error ); + soap_req->url = soap_req->action = soap_req->payload = soap_req->error = NULL; if( st == MSN_SOAP_RETRY && --soap_req->ttl ) { @@ -252,6 +257,7 @@ static void msn_soap_free( struct msn_soap_req_data *soap_req ) g_free( soap_req->url ); g_free( soap_req->action ); g_free( soap_req->payload ); + g_free( soap_req->error ); g_free( soap_req ); } @@ -409,7 +415,7 @@ static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap if( sd->secret == NULL ) { - msn_auth_got_passport_token( ic, NULL, sd->error ); + msn_auth_got_passport_token( ic, NULL, sd->error ? sd->error : soap_req->error ); return MSN_SOAP_OK; } diff --git a/protocols/skype/skype.c b/protocols/skype/skype.c index 5b1a6c30..760aeb3d 100644 --- a/protocols/skype/skype.c +++ b/protocols/skype/skype.c @@ -1156,7 +1156,7 @@ gboolean skype_start_stream(struct im_connection *ic) return st; } -gboolean skype_connected(gpointer data, void *source, b_input_condition cond) +gboolean skype_connected(gpointer data, int returncode, void *source, b_input_condition cond) { struct im_connection *ic = data; struct skype_data *sd = ic->proto_data; @@ -1184,7 +1184,7 @@ static void skype_login(account_t *acc) imcb_log(ic, "Connecting"); sd->ssl = ssl_connect(set_getstr(&acc->set, "server"), - set_getint(&acc->set, "port"), skype_connected, ic); + set_getint(&acc->set, "port"), FALSE, skype_connected, ic); sd->fd = sd->ssl ? ssl_getfd(sd->ssl) : -1; sd->username = g_strdup(acc->user); |