Changeset 164352e for lib/ssl_gnutls.c


Ignore:
Timestamp:
2011-12-24T18:02:39Z (13 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
34ded90
Parents:
e306fbf (diff), 96f954d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merging mainline.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • lib/ssl_gnutls.c

    re306fbf r164352e  
    2525
    2626#include <gnutls/gnutls.h>
     27#include <gnutls/x509.h>
    2728#include <gcrypt.h>
    2829#include <fcntl.h>
     
    3233#include "sock.h"
    3334#include "stdlib.h"
     35#include "bitlbee.h"
    3436
    3537int ssl_errno = 0;
     
    5456        gboolean established;
    5557        int inpa;
     58        char *hostname;
     59        gboolean verify;
    5660       
    5761        gnutls_session session;
     
    7478}
    7579
    76 void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
     80void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
    7781{
    7882        struct scd *conn = g_new0( struct scd, 1 );
     
    8286        conn->data = data;
    8387        conn->inpa = -1;
     88        conn->hostname = g_strdup( host );
     89        conn->verify = verify && global.conf->cafile;
    8490       
    8591        if( conn->fd < 0 )
     
    9298}
    9399
    94 void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
     100void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data )
    95101{
    96102        struct scd *conn = g_new0( struct scd, 1 );
     
    100106        conn->data = data;
    101107        conn->inpa = -1;
     108        conn->hostname = hostname;
     109       
     110        /* For now, SSL verification is globally enabled by setting the cafile
     111           setting in bitlbee.conf. Commented out by default because probably
     112           not everyone has this file in the same place and plenty of folks
     113           may not have the cert of their private Jabber server in it. */
     114        conn->verify = verify && global.conf->cafile;
    102115       
    103116        /* This function should be called via a (short) timeout instead of
     
    122135}
    123136
     137static int verify_certificate_callback( gnutls_session_t session )
     138{
     139        unsigned int status;
     140        const gnutls_datum_t *cert_list;
     141        unsigned int cert_list_size;
     142        int gnutlsret;
     143        int verifyret = 0;
     144        gnutls_x509_crt_t cert;
     145        const char *hostname;
     146       
     147        hostname = gnutls_session_get_ptr(session );
     148
     149        gnutlsret = gnutls_certificate_verify_peers2( session, &status );
     150        if( gnutlsret < 0 )
     151                return VERIFY_CERT_ERROR;
     152
     153        if( status & GNUTLS_CERT_INVALID )
     154                verifyret |= VERIFY_CERT_INVALID;
     155
     156        if( status & GNUTLS_CERT_REVOKED )
     157                verifyret |= VERIFY_CERT_REVOKED;
     158
     159        if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
     160                verifyret |= VERIFY_CERT_SIGNER_NOT_FOUND;
     161
     162        if( status & GNUTLS_CERT_SIGNER_NOT_CA )
     163                verifyret |= VERIFY_CERT_SIGNER_NOT_CA;
     164
     165        if( status & GNUTLS_CERT_INSECURE_ALGORITHM )
     166                verifyret |= VERIFY_CERT_INSECURE_ALGORITHM;
     167
     168#ifdef GNUTLS_CERT_NOT_ACTIVATED
     169        /* Amusingly, the GnuTLS function used above didn't check for expiry
     170           until GnuTLS 2.8 or so. (See CVE-2009-1417) */
     171        if( status & GNUTLS_CERT_NOT_ACTIVATED )
     172                verifyret |= VERIFY_CERT_NOT_ACTIVATED;
     173
     174        if( status & GNUTLS_CERT_EXPIRED )
     175                verifyret |= VERIFY_CERT_EXPIRED;
     176#endif
     177
     178        /* The following check is already performed inside
     179         * gnutls_certificate_verify_peers2, so we don't need it.
     180
     181         * if( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 )
     182         * return GNUTLS_E_CERTIFICATE_ERROR;
     183         */
     184
     185        if( gnutls_x509_crt_init( &cert ) < 0 )
     186                return VERIFY_CERT_ERROR;
     187
     188        cert_list = gnutls_certificate_get_peers( session, &cert_list_size );
     189        if( cert_list == NULL || gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER ) < 0 )
     190                return VERIFY_CERT_ERROR;
     191
     192        if( !gnutls_x509_crt_check_hostname( cert, hostname ) )
     193        {
     194                verifyret |= VERIFY_CERT_INVALID;
     195                verifyret |= VERIFY_CERT_WRONG_HOSTNAME;
     196        }
     197
     198        gnutls_x509_crt_deinit( cert );
     199
     200        return verifyret;
     201}
     202
     203char *ssl_verify_strerror( int code )
     204{
     205        GString *ret = g_string_new( "" );
     206       
     207        if( code & VERIFY_CERT_REVOKED )
     208                g_string_append( ret, "certificate has been revoked, " );
     209        if( code & VERIFY_CERT_SIGNER_NOT_FOUND )
     210                g_string_append( ret, "certificate hasn't got a known issuer, " );
     211        if( code & VERIFY_CERT_SIGNER_NOT_CA )
     212                g_string_append( ret, "certificate's issuer is not a CA, " );
     213        if( code & VERIFY_CERT_INSECURE_ALGORITHM )
     214                g_string_append( ret, "certificate uses an insecure algorithm, " );
     215        if( code & VERIFY_CERT_NOT_ACTIVATED )
     216                g_string_append( ret, "certificate has not been activated, " );
     217        if( code & VERIFY_CERT_EXPIRED )
     218                g_string_append( ret, "certificate has expired, " );
     219        if( code & VERIFY_CERT_WRONG_HOSTNAME )
     220                g_string_append( ret, "certificate hostname mismatch, " );
     221       
     222        if( ret->len == 0 )
     223        {
     224                g_string_free( ret, TRUE );
     225                return NULL;
     226        }
     227        else
     228        {
     229                g_string_truncate( ret, ret->len - 2 );
     230                return g_string_free( ret, FALSE );
     231        }
     232}
     233
    124234static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
    125235{
     
    128238        if( source == -1 )
    129239        {
    130                 conn->func( conn->data, NULL, cond );
     240                conn->func( conn->data, 0, NULL, cond );
    131241                g_free( conn );
    132242                return FALSE;
     
    136246       
    137247        gnutls_certificate_allocate_credentials( &conn->xcred );
     248        if( conn->verify && global.conf->cafile )
     249        {
     250                gnutls_certificate_set_x509_trust_file( conn->xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM );
     251                gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
     252        }
     253
    138254        gnutls_init( &conn->session, GNUTLS_CLIENT );
     255        if( conn->verify )
     256                gnutls_session_set_ptr( conn->session, (void *) conn->hostname );
    139257#if GNUTLS_VERSION_NUMBER < 0x020c00
    140258        gnutls_transport_set_lowat( conn->session, 0 );
     
    152270{
    153271        struct scd *conn = data;
    154         int st;
     272        int st, stver;
    155273       
    156274        if( ( st = gnutls_handshake( conn->session ) ) < 0 )
     
    163281                else
    164282                {
    165                         conn->func( conn->data, NULL, cond );
     283                        conn->func( conn->data, 0, NULL, cond );
    166284                       
    167285                        gnutls_deinit( conn->session );
     
    174292        else
    175293        {
    176                 /* For now we can't handle non-blocking perfectly everywhere... */
    177                 sock_make_blocking( conn->fd );
     294                if( conn->verify && ( stver = verify_certificate_callback( conn->session ) ) != 0 )
     295                {
     296                        conn->func( conn->data, stver, NULL, cond );
     297
     298                        gnutls_deinit( conn->session );
     299                        gnutls_certificate_free_credentials( conn->xcred );
     300                        closesocket( conn->fd );
     301
     302                        g_free( conn );
     303                }
     304                else
     305                {
     306                        /* For now we can't handle non-blocking perfectly everywhere... */
     307                        sock_make_blocking( conn->fd );
    178308               
    179                 conn->established = TRUE;
    180                 conn->func( conn->data, conn, cond );
     309                        conn->established = TRUE;
     310                        conn->func( conn->data, 0, conn, cond );
     311                }
    181312        }
    182313       
Note: See TracChangeset for help on using the changeset viewer.