source: lib/ssl_gnutls.c @ 0b1ae59

Last change on this file since 0b1ae59 was 098a75b, checked in by dequis <dx@…>, at 2015-03-22T13:35:08Z

Fix a bunch of memory leaks

  • irc_im.c:
    • bee_irc_user_msg: strdup leaks when otr swallows messages
    • bee_irc_user_action_response: GString leak in all ctcp replies
  • otr.c:
    • call g_slist_free() on the list of the otr_policy setting
    • otr_filter_msg_in: call otrl_tlv_free() if "tlvs" are returned
    • otr_filter_msg_out: don't g_strdup() if the message should be ignored
    • log_otr_message: g_strdup_vprintf() leaks always
  • nogaim.c:
    • imcb_ask_auth/imcb_ask_add: leaks in g_strdup_printf()
    • imcb_ask_add leaks imcb_ask_cb_data if the user already exists
    • add imcb_ask_cb_free() to correctly free its data
  • msn_util.c: add msn_buddy_ask_free(), ditto
  • storage_xml.c: pass_cr/password if base64_decode or arc_decode fail
  • ssl_gnutls.c: conn->hostname leak in error conditions, like invalid certs
  • jabber_util.c: jabber_buddy_by_ext_jid() leaks jid if it's not an ext jid
  • Property mode set to 100644
File size: 12.2 KB
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[9b67285]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]24*/
25
26#include <gnutls/gnutls.h>
[486ddb5]27#include <gnutls/x509.h>
[83e47ec]28#include <gcrypt.h>
[701acdd4]29#include <fcntl.h>
30#include <unistd.h>
[b7d3cc34]31#include "proxy.h"
32#include "ssl_client.h"
33#include "sock.h"
34#include "stdlib.h"
[486ddb5]35#include "bitlbee.h"
[b7d3cc34]36
[701acdd4]37int ssl_errno = 0;
38
[b7d3cc34]39static gboolean initialized = FALSE;
[2fb1262]40gnutls_certificate_credentials_t xcred;
[b7d3cc34]41
[56f260a]42#include <limits.h>
43
44#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
45#define GNUTLS_STUPID_CAST (long)
46#else
47#define GNUTLS_STUPID_CAST (int)
48#endif
49
[ca974d7]50#define SSLDEBUG 0
51
[5ebff60]52struct scd {
[3d64e5b]53        ssl_input_function func;
[b7d3cc34]54        gpointer data;
55        int fd;
56        gboolean established;
[701acdd4]57        int inpa;
[486ddb5]58        char *hostname;
59        gboolean verify;
[5ebff60]60
[2fb1262]61        gnutls_session_t session;
[b7d3cc34]62};
63
[9b67285]64static GHashTable *session_cache;
65
[5ebff60]66static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond);
67static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond);
68static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond);
[b7d3cc34]69
[5ebff60]70static void ssl_deinit(void);
[b7d3cc34]71
[5ebff60]72static void ssl_log(int level, const char *line)
[632f3d4]73{
[5ebff60]74        printf("%d %s", level, line);
[632f3d4]75}
76
[5ebff60]77void ssl_init(void)
[ba5add7]78{
[5ebff60]79        if (initialized) {
[83e47ec]80                return;
[5ebff60]81        }
82
[ba5add7]83        gnutls_global_init();
[5ebff60]84        gnutls_certificate_allocate_credentials(&xcred);
85        if (global.conf->cafile) {
86                gnutls_certificate_set_x509_trust_file(xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM);
87
[8f976e6]88                /* Not needed in GnuTLS 2.11+ (enabled by default there) so
89                   don't do it (resets possible other defaults). */
[5ebff60]90                if (!gnutls_check_version("2.11")) {
91                        gnutls_certificate_set_verify_flags(xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
92                }
[59cd92b]93        }
[ba5add7]94        initialized = TRUE;
[5ebff60]95
96        gnutls_global_set_log_function(ssl_log);
[632f3d4]97        /*
98        gnutls_global_set_log_level( 3 );
99        */
[5ebff60]100
101        session_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
102
103        atexit(ssl_deinit);
[59cd92b]104}
105
[5ebff60]106static void ssl_deinit(void)
[59cd92b]107{
108        gnutls_global_deinit();
[5ebff60]109        gnutls_certificate_free_credentials(xcred);
110        g_hash_table_destroy(session_cache);
[9b67285]111        session_cache = NULL;
[ba5add7]112}
113
[5ebff60]114void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
[b7d3cc34]115{
[5ebff60]116        struct scd *conn = g_new0(struct scd, 1);
117
[b7d3cc34]118        conn->func = func;
119        conn->data = data;
[701acdd4]120        conn->inpa = -1;
[5ebff60]121        conn->hostname = g_strdup(host);
[a72dc2b]122        conn->verify = verify && global.conf->cafile;
[5ebff60]123        conn->fd = proxy_connect(host, port, ssl_connected, conn);
124
125        if (conn->fd < 0) {
[098a75b]126                g_free(conn->hostname);
[5ebff60]127                g_free(conn);
[42127dc]128                return NULL;
[b7d3cc34]129        }
[5ebff60]130
[42127dc]131        return conn;
132}
133
[5ebff60]134void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
[42127dc]135{
[5ebff60]136        struct scd *conn = g_new0(struct scd, 1);
137
[42127dc]138        conn->fd = fd;
139        conn->func = func;
140        conn->data = data;
141        conn->inpa = -1;
[5ebff60]142        conn->hostname = g_strdup(hostname);
143
[486ddb5]144        /* For now, SSL verification is globally enabled by setting the cafile
145           setting in bitlbee.conf. Commented out by default because probably
146           not everyone has this file in the same place and plenty of folks
147           may not have the cert of their private Jabber server in it. */
148        conn->verify = verify && global.conf->cafile;
[5ebff60]149
[c1ed6527]150        /* This function should be called via a (short) timeout instead of
151           directly from here, because these SSL calls are *supposed* to be
152           *completely* asynchronous and not ready yet when this function
153           (or *_connect, for examle) returns. Also, errors are reported via
154           the callback function, not via this function's return value.
[5ebff60]155
[c1ed6527]156           In short, doing things like this makes the rest of the code a lot
157           simpler. */
[5ebff60]158
159        b_timeout_add(1, ssl_starttls_real, conn);
160
[42127dc]161        return conn;
[b7d3cc34]162}
163
[5ebff60]164static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
[c1ed6527]165{
166        struct scd *conn = data;
[5ebff60]167
168        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
[c1ed6527]169}
[701acdd4]170
[5ebff60]171static int verify_certificate_callback(gnutls_session_t session)
[486ddb5]172{
173        unsigned int status;
174        const gnutls_datum_t *cert_list;
175        unsigned int cert_list_size;
176        int gnutlsret;
177        int verifyret = 0;
178        gnutls_x509_crt_t cert;
[2fb1262]179        struct scd *conn;
[486ddb5]180
[5ebff60]181        conn = gnutls_session_get_ptr(session);
182
183        gnutlsret = gnutls_certificate_verify_peers2(session, &status);
184        if (gnutlsret < 0) {
[486ddb5]185                return VERIFY_CERT_ERROR;
[5ebff60]186        }
[486ddb5]187
[5ebff60]188        if (status & GNUTLS_CERT_INVALID) {
[486ddb5]189                verifyret |= VERIFY_CERT_INVALID;
[5ebff60]190        }
[486ddb5]191
[5ebff60]192        if (status & GNUTLS_CERT_REVOKED) {
[486ddb5]193                verifyret |= VERIFY_CERT_REVOKED;
[5ebff60]194        }
[486ddb5]195
[5ebff60]196        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
[486ddb5]197                verifyret |= VERIFY_CERT_SIGNER_NOT_FOUND;
[5ebff60]198        }
[486ddb5]199
[5ebff60]200        if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
[486ddb5]201                verifyret |= VERIFY_CERT_SIGNER_NOT_CA;
[5ebff60]202        }
[486ddb5]203
[5ebff60]204        if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
[486ddb5]205                verifyret |= VERIFY_CERT_INSECURE_ALGORITHM;
[5ebff60]206        }
[486ddb5]207
[5513f3e]208#ifdef GNUTLS_CERT_NOT_ACTIVATED
209        /* Amusingly, the GnuTLS function used above didn't check for expiry
210           until GnuTLS 2.8 or so. (See CVE-2009-1417) */
[5ebff60]211        if (status & GNUTLS_CERT_NOT_ACTIVATED) {
[486ddb5]212                verifyret |= VERIFY_CERT_NOT_ACTIVATED;
[5ebff60]213        }
[486ddb5]214
[5ebff60]215        if (status & GNUTLS_CERT_EXPIRED) {
[486ddb5]216                verifyret |= VERIFY_CERT_EXPIRED;
[5ebff60]217        }
[5513f3e]218#endif
[486ddb5]219
[5ebff60]220        if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509 || gnutls_x509_crt_init(&cert) < 0) {
[486ddb5]221                return VERIFY_CERT_ERROR;
[5ebff60]222        }
[486ddb5]223
[5ebff60]224        cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
225        if (cert_list == NULL || gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
[486ddb5]226                return VERIFY_CERT_ERROR;
[5ebff60]227        }
[486ddb5]228
[5ebff60]229        if (!gnutls_x509_crt_check_hostname(cert, conn->hostname)) {
[486ddb5]230                verifyret |= VERIFY_CERT_INVALID;
231                verifyret |= VERIFY_CERT_WRONG_HOSTNAME;
232        }
233
[5ebff60]234        gnutls_x509_crt_deinit(cert);
[486ddb5]235
236        return verifyret;
237}
238
[5ebff60]239struct ssl_session {
[9b67285]240        size_t size;
241        char data[];
242};
243
[5ebff60]244static void ssl_cache_add(struct scd *conn)
[9b67285]245{
[b7cd22d]246        size_t data_size = 0;
[9b67285]247        struct ssl_session *data;
248        char *hostname;
[5ebff60]249
250        if (!conn->hostname ||
251            gnutls_session_get_data(conn->session, NULL, &data_size) != 0) {
[9b67285]252                return;
[5ebff60]253        }
254
255        data = g_malloc(sizeof(struct ssl_session) + data_size);
256        if (gnutls_session_get_data(conn->session, data->data, &data_size) != 0) {
257                g_free(data);
[9b67285]258                return;
259        }
[5ebff60]260
261        hostname = g_strdup(conn->hostname);
262        g_hash_table_insert(session_cache, hostname, data);
[9b67285]263}
264
[5ebff60]265static void ssl_cache_resume(struct scd *conn)
[9b67285]266{
267        struct ssl_session *data;
[5ebff60]268
269        if (conn->hostname &&
270            (data = g_hash_table_lookup(session_cache, conn->hostname))) {
271                gnutls_session_set_data(conn->session, data->data, data->size);
272                g_hash_table_remove(session_cache, conn->hostname);
[9b67285]273        }
274}
275
[5ebff60]276char *ssl_verify_strerror(int code)
[78b8401]277{
[5ebff60]278        GString *ret = g_string_new("");
279
280        if (code & VERIFY_CERT_REVOKED) {
281                g_string_append(ret, "certificate has been revoked, ");
282        }
283        if (code & VERIFY_CERT_SIGNER_NOT_FOUND) {
284                g_string_append(ret, "certificate hasn't got a known issuer, ");
285        }
286        if (code & VERIFY_CERT_SIGNER_NOT_CA) {
287                g_string_append(ret, "certificate's issuer is not a CA, ");
288        }
289        if (code & VERIFY_CERT_INSECURE_ALGORITHM) {
290                g_string_append(ret, "certificate uses an insecure algorithm, ");
291        }
292        if (code & VERIFY_CERT_NOT_ACTIVATED) {
293                g_string_append(ret, "certificate has not been activated, ");
[78b8401]294        }
[5ebff60]295        if (code & VERIFY_CERT_EXPIRED) {
296                g_string_append(ret, "certificate has expired, ");
297        }
298        if (code & VERIFY_CERT_WRONG_HOSTNAME) {
299                g_string_append(ret, "certificate hostname mismatch, ");
300        }
301
302        if (ret->len == 0) {
303                g_string_free(ret, TRUE);
304                return NULL;
305        } else {
306                g_string_truncate(ret, ret->len - 2);
307                return g_string_free(ret, FALSE);
[78b8401]308        }
309}
310
[5ebff60]311static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]312{
313        struct scd *conn = data;
[5ebff60]314
315        if (source == -1) {
316                conn->func(conn->data, 0, NULL, cond);
[098a75b]317                g_free(conn->hostname);
[5ebff60]318                g_free(conn);
[2b7d2d1]319                return FALSE;
[701acdd4]320        }
[5ebff60]321
[83e47ec]322        ssl_init();
[5ebff60]323
324        gnutls_init(&conn->session, GNUTLS_CLIENT);
325        gnutls_session_set_ptr(conn->session, (void *) conn);
[80acb6d]326#if GNUTLS_VERSION_NUMBER < 0x020c00
[5ebff60]327        gnutls_transport_set_lowat(conn->session, 0);
[80acb6d]328#endif
[5ebff60]329        gnutls_set_default_priority(conn->session);
330        gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, xcred);
331        if (conn->hostname && !g_ascii_isdigit(conn->hostname[0])) {
332                gnutls_server_name_set(conn->session, GNUTLS_NAME_DNS,
333                                       conn->hostname, strlen(conn->hostname));
334        }
335
336        sock_make_nonblocking(conn->fd);
337        gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) GNUTLS_STUPID_CAST conn->fd);
338
339        ssl_cache_resume(conn);
340
341        return ssl_handshake(data, source, cond);
[701acdd4]342}
343
[5ebff60]344static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
[701acdd4]345{
346        struct scd *conn = data;
[486ddb5]347        int st, stver;
[5ebff60]348
[286cd48]349        /* This function returns false, so avoid calling b_event_remove again */
350        conn->inpa = -1;
[486ddb5]351
[5ebff60]352        if ((st = gnutls_handshake(conn->session)) < 0) {
353                if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
354                        conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn),
355                                                 ssl_handshake, data);
356                } else {
357                        conn->func(conn->data, 0, NULL, cond);
358
[098a75b]359                        ssl_disconnect(conn);
[486ddb5]360                }
[5ebff60]361        } else {
362                if (conn->verify && (stver = verify_certificate_callback(conn->session)) != 0) {
363                        conn->func(conn->data, stver, NULL, cond);
364
[098a75b]365                        ssl_disconnect(conn);
[5ebff60]366                } else {
[486ddb5]367                        /* For now we can't handle non-blocking perfectly everywhere... */
[5ebff60]368                        sock_make_blocking(conn->fd);
369
370                        ssl_cache_add(conn);
[486ddb5]371                        conn->established = TRUE;
[5ebff60]372                        conn->func(conn->data, 0, conn, cond);
[486ddb5]373                }
[701acdd4]374        }
[5ebff60]375
[2b7d2d1]376        return FALSE;
[b7d3cc34]377}
378
[5ebff60]379int ssl_read(void *conn, char *buf, int len)
[b7d3cc34]380{
[8a9afe4]381        int st;
[5ebff60]382
383        if (!((struct scd*) conn)->established) {
[701acdd4]384                ssl_errno = SSL_NOHANDSHAKE;
[80acb6d]385                return -1;
[701acdd4]386        }
[5ebff60]387
388        st = gnutls_record_recv(((struct scd*) conn)->session, buf, len);
389
[8a9afe4]390        ssl_errno = SSL_OK;
[5ebff60]391        if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
[8a9afe4]392                ssl_errno = SSL_AGAIN;
[5ebff60]393        }
394
395        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
396                len = write(2, buf, st);
397        }
398
[8a9afe4]399        return st;
[b7d3cc34]400}
401
[5ebff60]402int ssl_write(void *conn, const char *buf, int len)
[b7d3cc34]403{
[8a9afe4]404        int st;
[5ebff60]405
406        if (!((struct scd*) conn)->established) {
[701acdd4]407                ssl_errno = SSL_NOHANDSHAKE;
[80acb6d]408                return -1;
[701acdd4]409        }
[5ebff60]410
411        st = gnutls_record_send(((struct scd*) conn)->session, buf, len);
412
[8a9afe4]413        ssl_errno = SSL_OK;
[5ebff60]414        if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
[8a9afe4]415                ssl_errno = SSL_AGAIN;
[5ebff60]416        }
417
418        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
419                len = write(2, buf, st);
420        }
421
[8a9afe4]422        return st;
[b7d3cc34]423}
424
[5ebff60]425int ssl_pending(void *conn)
[8a2221a7]426{
[5ebff60]427        if (conn == NULL) {
[80acb6d]428                return 0;
[5ebff60]429        }
430
431        if (!((struct scd*) conn)->established) {
[80acb6d]432                ssl_errno = SSL_NOHANDSHAKE;
433                return 0;
434        }
[632f3d4]435
436#if GNUTLS_VERSION_NUMBER >= 0x03000d && GNUTLS_VERSION_NUMBER <= 0x030012
[5ebff60]437        if (ssl_errno == SSL_AGAIN) {
[632f3d4]438                return 0;
[5ebff60]439        }
[632f3d4]440#endif
[5ebff60]441
442        return gnutls_record_check_pending(((struct scd*) conn)->session) != 0;
[8a2221a7]443}
444
[5ebff60]445void ssl_disconnect(void *conn_)
[b7d3cc34]446{
447        struct scd *conn = conn_;
[5ebff60]448
449        if (conn->inpa != -1) {
450                b_event_remove(conn->inpa);
451        }
452
453        if (conn->established) {
454                gnutls_bye(conn->session, GNUTLS_SHUT_WR);
455        }
456
457        closesocket(conn->fd);
458
459        if (conn->session) {
460                gnutls_deinit(conn->session);
461        }
462        g_free(conn->hostname);
463        g_free(conn);
[b7d3cc34]464}
465
[5ebff60]466int ssl_getfd(void *conn)
[b7d3cc34]467{
[5ebff60]468        return(((struct scd*) conn)->fd);
[b7d3cc34]469}
[8a9afe4]470
[5ebff60]471b_input_condition ssl_getdirection(void *conn)
[8a9afe4]472{
[5ebff60]473        return(gnutls_record_get_direction(((struct scd*) conn)->session) ?
474               B_EV_IO_WRITE : B_EV_IO_READ);
[8a9afe4]475}
[83e47ec]476
[5ebff60]477size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input,
478                        size_t input_len, const unsigned char *iv, unsigned char **res)
[83e47ec]479{
480        gcry_cipher_hd_t gcr;
481        gcry_error_t st;
[5ebff60]482
[83e47ec]483        ssl_init();
[5ebff60]484
485        *res = g_malloc(input_len);
486        st = gcry_cipher_open(&gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0) ||
487             gcry_cipher_setkey(gcr, key, key_len) ||
488             gcry_cipher_setiv(gcr, iv, 8) ||
489             gcry_cipher_encrypt(gcr, *res, input_len, input, input_len);
490
491        gcry_cipher_close(gcr);
492
493        if (st == 0) {
[83e47ec]494                return input_len;
[5ebff60]495        }
496
497        g_free(*res);
[83e47ec]498        return 0;
499}
Note: See TracBrowser for help on using the repository browser.