Ticket #714: 0001-NSS-based-implementation-of-SSL-related-operations.patch

File 0001-NSS-based-implementation-of-SSL-related-operations.patch, 12.6 KB (added by Matěj Cepl <mcepl@…>, at 2012-07-24T15:12:22Z)

The last version of the patch

  • new file .gitignore

    From 625d0e0cca7464eebccd55c6744dd5ce773c6dde Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= <mcepl@redhat.com>
    Date: Tue, 24 Jul 2012 17:08:51 +0200
    Subject: [PATCH] NSS-based implementation of SSL-related operations.
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
     * no verification support
     * ssl_des3_encrypt implemented and functional.
    
    Signed-off-by: Matěj Cepl <mcepl@redhat.com>
    ---
     .gitignore    |    7 ++
     configure     |    6 +-
     lib/ssl_nss.c |  330 ++++++++++++++++++++++++++++++++++++++++++---------------
     3 files changed, 257 insertions(+), 86 deletions(-)
     create mode 100644 .gitignore
    
    diff --git a/.gitignore b/.gitignore
    new file mode 100644
    index 0000000..1899086
    - +  
     1*~
     2*.o
     3.depend/
     4Makefile.settings
     5bitlbee
     6bitlbee.pc
     7config.h
  • configure

    diff --git a/configure b/configure
    index 697a33b..c0d3735 100755
    a b if [ "$ret" = "0" ]; then 
    453453        exit 1
    454454fi;
    455455
    456 if [ "$msn" = "1" -a "$ssl" != "openssl" -a "$ssl" != "gnutls" ]; then
    457         # Needed for MSN only. OpenSSL exports nice cipher functions already,
     456if [ "$msn" = "1" -a "$ssl" != "openssl" -a "$ssl" != "gnutls" -a "$ssl" != "nss" ]; then
     457        # Needed for MSN only. OpenSSL and NSS export nice cipher functions already,
    458458        # in case of GnuTLS we should be able to use gcrypt. Otherwise, use
    459         # built-in stuff. (Since right now those are the only two supported
     459        # built-in stuff. (Since right now those are the only three supported
    460460        # SSL modules anyway, this is mostly unnecessary.)
    461461        echo 'DES=des.o' >> Makefile.settings
    462462fi
  • lib/ssl_nss.c

    diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
    index d50620d..4e7a79d 100644
    a b  
    3939#include <seccomon.h>
    4040#include <secerr.h>
    4141#include <sslerr.h>
     42#include <assert.h>
     43#include <unistd.h>
    4244
    4345int ssl_errno = 0;
    4446
    4547static gboolean initialized = FALSE;
    4648
    47 struct scd
    48 {
     49#define SSLDEBUG 0
     50
     51struct scd {
    4952        ssl_input_function func;
    5053        gpointer data;
    5154        int fd;
     55        char *hostname;
    5256        PRFileDesc *prfd;
    5357        gboolean established;
    5458        gboolean verify;
    5559};
    5660
    57 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
    58 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
     61static gboolean ssl_connected(gpointer data, gint source,
     62                              b_input_condition cond);
     63static gboolean ssl_starttls_real(gpointer data, gint source,
     64                                  b_input_condition cond);
    5965
    60 
    61 static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver)
     66static SECStatus nss_auth_cert(void *arg, PRFileDesc * socket, PRBool checksig,
     67                              PRBool isserver)
    6268{
    6369        return SECSuccess;
    6470}
    6571
    66 static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
     72static SECStatus nss_bad_cert(void *arg, PRFileDesc * socket)
    6773{
    6874        PRErrorCode err;
    6975
    70         if(!arg) return SECFailure;
     76        if (!arg)
     77                return SECFailure;
    7178
    72         *(PRErrorCode *)arg = err = PORT_GetError();
     79        *(PRErrorCode *) arg = err = PORT_GetError();
    7380
    74         switch(err) {
     81        switch (err) {
    7582        case SEC_ERROR_INVALID_AVA:
    7683        case SEC_ERROR_INVALID_TIME:
    7784        case SEC_ERROR_BAD_SIGNATURE:
    static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) 
    93100        }
    94101}
    95102
    96 
    97 void ssl_init( void )
     103void ssl_init(void)
    98104{
    99         PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
     105        PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
     106        // https://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
     107        // This NSS function is not intended for use with SSL, which
     108        // requires that the certificate and key database files be
     109        // opened. Relates to whole non-verification of servers for now.
    100110        NSS_NoDB_Init(NULL);
    101111        NSS_SetDomesticPolicy();
    102112        initialized = TRUE;
    103113}
    104114
    105 void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
     115void *ssl_connect(char *host, int port, gboolean verify,
     116                  ssl_input_function func, gpointer data)
    106117{
    107         struct scd *conn = g_new0( struct scd, 1 );
    108        
    109         conn->fd = proxy_connect( host, port, ssl_connected, conn );
     118        struct scd *conn = g_new0(struct scd, 1);
     119
     120        conn->fd = proxy_connect(host, port, ssl_connected, conn);
    110121        conn->func = func;
    111122        conn->data = data;
    112        
    113         if( conn->fd < 0 )
    114         {
    115                 g_free( conn );
    116                 return( NULL );
     123        conn->hostname = g_strdup(host);
     124
     125        if (conn->fd < 0) {
     126                g_free(conn->hostname);
     127                g_free(conn);
     128                return (NULL);
    117129        }
    118        
    119         if( !initialized )
    120         {
     130
     131        if (!initialized) {
    121132                ssl_init();
    122133        }
    123134
    124        
    125         return( conn );
     135        return (conn);
    126136}
    127137
    128 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
     138static gboolean ssl_starttls_real(gpointer data, gint source,
     139                                  b_input_condition cond)
    129140{
    130141        struct scd *conn = data;
    131142
    132         return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
     143        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
    133144}
    134145
    135 void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data )
     146void *ssl_starttls(int fd, char *hostname, gboolean verify,
     147                   ssl_input_function func, gpointer data)
    136148{
    137         struct scd *conn = g_new0( struct scd, 1 );
     149        struct scd *conn = g_new0(struct scd, 1);
    138150
    139151        conn->fd = fd;
    140152        conn->func = func;
    141153        conn->data = data;
     154        conn->hostname = hostname;
     155
     156        /* For now, SSL verification is globally enabled by setting the cafile
     157           setting in bitlbee.conf. Commented out by default because probably
     158           not everyone has this file in the same place and plenty of folks
     159           may not have the cert of their private Jabber server in it. */
    142160        conn->verify = verify && global.conf->cafile;
    143161
    144162        /* This function should be called via a (short) timeout instead of
    void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function 
    150168           In short, doing things like this makes the rest of the code a lot
    151169           simpler. */
    152170
    153         b_timeout_add( 1, ssl_starttls_real, conn );
     171        b_timeout_add(1, ssl_starttls_real, conn);
    154172
    155173        return conn;
    156174}
    157175
    158 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
     176static gboolean ssl_connected(gpointer data, gint source,
     177                              b_input_condition cond)
    159178{
    160179        struct scd *conn = data;
    161        
     180
    162181        /* Right now we don't have any verification functionality for NSS. */
    163182
    164         if( conn->verify )
    165         {
    166                 conn->func( conn->data, 1, NULL, cond );
    167                 if( source >= 0 ) closesocket( source );
    168                 g_free( conn );
     183        if (conn->verify) {
     184                conn->func(conn->data, 1, NULL, cond);
     185                if (source >= 0)
     186                        closesocket(source);
     187                g_free(conn->hostname);
     188                g_free(conn);
    169189
    170190                return FALSE;
    171191        }
    172        
    173         if( source == -1 )
     192
     193        if (source == -1)
    174194                goto ssl_connected_failure;
    175        
     195
    176196        /* Until we find out how to handle non-blocking I/O with NSS... */
    177         sock_make_blocking( conn->fd );
    178        
     197        sock_make_blocking(conn->fd);
     198
    179199        conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
     200        if (!conn->prfd)
     201                goto ssl_connected_failure;
    180202        SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
    181203        SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
    182         SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL);
    183         SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB());
     204        SSL_BadCertHook(conn->prfd, (SSLBadCertHandler) nss_bad_cert, NULL);
     205        SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate) nss_auth_cert,
     206                                (void *)CERT_GetDefaultCertDB());
     207        SSL_SetURL(conn->prfd, conn->hostname);
    184208        SSL_ResetHandshake(conn->prfd, PR_FALSE);
    185209
    186210        if (SSL_ForceHandshake(conn->prfd)) {
    187211                goto ssl_connected_failure;
    188212        }
    189        
    190        
     213
    191214        conn->established = TRUE;
    192         conn->func( conn->data, 0, conn, cond );
     215        conn->func(conn->data, 0, conn, cond);
    193216        return FALSE;
    194        
    195         ssl_connected_failure:
    196        
    197         conn->func( conn->data, 0, NULL, cond );
    198        
    199         PR_Close( conn -> prfd );
    200         if( source >= 0 ) closesocket( source );
    201         g_free( conn );
    202        
     217
     218 ssl_connected_failure:
     219
     220        conn->func(conn->data, 0, NULL, cond);
     221
     222        if (conn->prfd)
     223                PR_Close(conn->prfd);
     224        if (source >= 0)
     225                closesocket(source);
     226        g_free(conn->hostname);
     227        g_free(conn);
     228
    203229        return FALSE;
    204230}
    205231
    206 int ssl_read( void *conn, char *buf, int len )
     232int ssl_read(void *conn, char *buf, int len)
    207233{
    208         if( !((struct scd*)conn)->established )
    209                 return( 0 );
    210        
    211         return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) );
     234        int st;
     235        PRErrorCode PR_err;
     236
     237        if (!((struct scd *)conn)->established) {
     238                ssl_errno = SSL_NOHANDSHAKE;
     239                return -1;
     240        }
     241
     242        st = PR_Read(((struct scd *)conn)->prfd, buf, len);
     243        PR_err = PR_GetError();
     244
     245        ssl_errno = SSL_OK;
     246        if (PR_err == PR_WOULD_BLOCK_ERROR)
     247                ssl_errno = SSL_AGAIN;
     248
     249        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0)
     250                len = write(STDERR_FILENO, buf, st);
     251
     252        return st;
    212253}
    213254
    214 int ssl_write( void *conn, const char *buf, int len )
     255int ssl_write(void *conn, const char *buf, int len)
    215256{
    216         if( !((struct scd*)conn)->established )
    217                 return( 0 );
    218        
    219         return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
     257        int st;
     258        PRErrorCode PR_err;
     259
     260        if (!((struct scd *)conn)->established) {
     261                ssl_errno = SSL_NOHANDSHAKE;
     262                return -1;
     263        }
     264        st = PR_Write(((struct scd *)conn)->prfd, buf, len);
     265
     266        ssl_errno = SSL_OK;
     267        if (PR_err == PR_WOULD_BLOCK_ERROR)
     268                ssl_errno = SSL_AGAIN;
     269
     270        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0)
     271                len = write(2, buf, st);
     272
     273        return st;
    220274}
    221275
    222 int ssl_pending( void *conn )
     276int ssl_pending(void *conn)
    223277{
    224         struct scd *c = (struct scd *) conn;
     278        struct scd *c = (struct scd *)conn;
    225279
    226         if( c == NULL ) {
     280        if (c == NULL) {
    227281                return 0;
    228282        }
    229283
    230         return ( c->established && SSL_DataPending( c->prfd ) > 0 );
     284        return (c->established && SSL_DataPending(c->prfd) > 0);
    231285}
    232286
    233 void ssl_disconnect( void *conn_ )
     287void ssl_disconnect(void *conn_)
    234288{
    235289        struct scd *conn = conn_;
    236        
    237         PR_Close( conn->prfd );
    238         closesocket( conn->fd );
    239        
    240         g_free( conn );
     290
     291        // When we swich to NSS_Init, we should have here
     292        // NSS_Shutdown();
     293
     294        if (conn->prfd)
     295                PR_Close(conn->prfd);
     296
     297        g_free(conn->hostname);
     298        g_free(conn);
    241299}
    242300
    243 int ssl_getfd( void *conn )
     301int ssl_getfd(void *conn)
    244302{
    245         return( ((struct scd*)conn)->fd );
     303        return (((struct scd *)conn)->fd);
    246304}
    247305
    248 b_input_condition ssl_getdirection( void *conn )
     306b_input_condition ssl_getdirection(void *conn)
    249307{
    250308        /* Just in case someone calls us, let's return the most likely case: */
    251309        return B_EV_IO_READ;
    252310}
    253311
    254 char *ssl_verify_strerror( int code )
     312char *ssl_verify_strerror(int code)
     313{
     314        return
     315            g_strdup
     316            ("SSL certificate verification not supported by BitlBee NSS code.");
     317}
     318
     319size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
     320                        const unsigned char *input, size_t input_len,
     321                        const unsigned char *iv, unsigned char **res)
    255322{
    256         return g_strdup( "SSL certificate verification not supported by BitlBee NSS code." );
     323#define CIPHER_MECH CKM_DES3_CBC
     324#define MAX_OUTPUT_LEN 72
     325
     326        int len1;
     327        unsigned int len2;
     328
     329        PK11Context *ctx = NULL;
     330        PK11SlotInfo *slot = NULL;
     331        SECItem keyItem;
     332        SECItem ivItem;
     333        SECItem *secParam = NULL;
     334        PK11SymKey *symKey = NULL;
     335
     336        size_t rc;
     337        SECStatus rv;
     338
     339        if (!initialized) {
     340                ssl_init();
     341        }
     342
     343        keyItem.data = (unsigned char *)key;
     344        keyItem.len = key_len;
     345
     346        slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
     347        if (slot == NULL) {
     348                fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
     349                        PR_GetError());
     350                rc = 0;
     351                goto out;
     352        }
     353
     354        symKey =
     355            PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
     356                              &keyItem, NULL);
     357        if (symKey == NULL) {
     358                fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
     359                        PR_GetError());
     360                rc = 0;
     361                goto out;
     362        }
     363
     364        ivItem.data = (unsigned char *)iv;
     365        /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
     366        ivItem.len = 8;
     367
     368        secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
     369        if (secParam == NULL) {
     370                fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
     371                        PR_GetError());
     372                rc = 0;
     373                goto out;
     374        }
     375
     376        ctx =
     377            PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
     378                                       secParam);
     379        if (ctx == NULL) {
     380                fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
     381                        PR_GetError());
     382                rc = 0;
     383                goto out;
     384        }
     385
     386        *res = g_new0(unsigned char, MAX_OUTPUT_LEN);
     387
     388        rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
     389                           (unsigned char *)input, input_len);
     390        if (rv != SECSuccess) {
     391                fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
     392                        PR_GetError());
     393                rc = 0;
     394                goto out;
     395        }
     396
     397        assert(len1 <= MAX_OUTPUT_LEN);
     398
     399        rv = PK11_DigestFinal(ctx, *res + len1, &len2,
     400                              (unsigned int)MAX_OUTPUT_LEN - len1);
     401        if (rv != SECSuccess) {
     402                fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
     403                        PR_GetError());
     404                rc = 0;
     405                goto out;
     406        }
     407
     408        rc = len1 + len2;
     409
     410 out:
     411        if (ctx)
     412                PK11_DestroyContext(ctx, PR_TRUE);
     413        if (symKey)
     414                PK11_FreeSymKey(symKey);
     415        if (secParam)
     416                SECITEM_FreeItem(secParam, PR_TRUE);
     417        if (slot)
     418                PK11_FreeSlot(slot);
     419
     420        return rc;
    257421}