source: lib/ssl_openssl.c

Last change on this file was 5eab9260, checked in by Christopher Brannon <chris@…>, at 2018-09-09T22:04:41Z

SSL: correctly handle hostnames starting with a digit when SNI is needed.

I noticed that I was unable to connect to a host with bitlbee.
Tracking it down, I found that the problem was that the host needed SNI
but bitlbee wasn't trying to use it. The hostname started with a digit,
so bitlbee was assuming that it was an IP address.

  • Property mode set to 100644
File size: 8.0 KB
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[def3650]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
[26fdfc5]7/* SSL module - OpenSSL version                                         */
[b7d3cc34]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 <openssl/crypto.h>
27#include <openssl/rand.h>
28#include <openssl/x509.h>
29#include <openssl/pem.h>
30#include <openssl/ssl.h>
31#include <openssl/err.h>
32
[59c03bd]33#include "bitlbee.h"
[b7d3cc34]34#include "proxy.h"
35#include "ssl_client.h"
36#include "sock.h"
37
[701acdd4]38int ssl_errno = 0;
39
[b7d3cc34]40static gboolean initialized = FALSE;
41
[5ebff60]42struct scd {
[26fdfc5]43        ssl_input_function func;
[b7d3cc34]44        gpointer data;
45        int fd;
46        gboolean established;
[486ddb5]47        gboolean verify;
[3f661849]48        char *hostname;
[5ebff60]49
[26fdfc5]50        int inpa;
[5ebff60]51        int lasterr;            /* Necessary for SSL_get_error */
[b7d3cc34]52        SSL *ssl;
53};
54
[def3650]55static SSL_CTX *ssl_ctx;
56
[5ebff60]57static void ssl_conn_free(struct scd *conn);
58static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond);
59static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond);
60static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond);
[b7d3cc34]61
62
[5ebff60]63void ssl_init(void)
[ba5add7]64{
[def3650]65        const SSL_METHOD *meth;
[5ebff60]66
[5c163e5]67#if OPENSSL_VERSION_NUMBER < 0x10100000L
[8e9e2b7]68        SSL_library_init();
[5ebff60]69
[82e55d2]70        meth = SSLv23_client_method();
[5ebff60]71        ssl_ctx = SSL_CTX_new(meth);
[82e55d2]72        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
[5c163e5]73#else
74        meth = TLS_client_method();
75        ssl_ctx = SSL_CTX_new(meth);
76        SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
77#endif
[5ebff60]78
[def3650]79        initialized = TRUE;
[ba5add7]80}
81
[5ebff60]82void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
[b7d3cc34]83{
[5ebff60]84        struct scd *conn = g_new0(struct scd, 1);
85
86        conn->fd = proxy_connect(host, port, ssl_connected, conn);
87        if (conn->fd < 0) {
88                ssl_conn_free(conn);
[f920d9e]89                return NULL;
[b7d3cc34]90        }
[5ebff60]91
[8a2221a7]92        conn->func = func;
93        conn->data = data;
94        conn->inpa = -1;
[5ebff60]95        conn->hostname = g_strdup(host);
96
[f920d9e]97        return conn;
98}
99
[5ebff60]100void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
[f920d9e]101{
[5ebff60]102        struct scd *conn = g_new0(struct scd, 1);
103
[f920d9e]104        conn->fd = fd;
105        conn->func = func;
106        conn->data = data;
107        conn->inpa = -1;
[200e151]108        conn->verify = verify && global.conf->cafile;
[5ebff60]109        conn->hostname = g_strdup(hostname);
110
[f920d9e]111        /* This function should be called via a (short) timeout instead of
112           directly from here, because these SSL calls are *supposed* to be
113           *completely* asynchronous and not ready yet when this function
114           (or *_connect, for examle) returns. Also, errors are reported via
115           the callback function, not via this function's return value.
[5ebff60]116
[f920d9e]117           In short, doing things like this makes the rest of the code a lot
118           simpler. */
[5ebff60]119
120        b_timeout_add(1, ssl_starttls_real, conn);
121
[f920d9e]122        return conn;
123}
124
[5ebff60]125static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
[f920d9e]126{
127        struct scd *conn = data;
[5ebff60]128
129        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
[f920d9e]130}
131
[5ebff60]132static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
[f920d9e]133{
134        struct scd *conn = data;
[5ebff60]135
136        if (conn->verify) {
[3f661849]137                /* Right now we don't have any verification functionality for OpenSSL. */
[5ebff60]138                conn->func(conn->data, 1, NULL, cond);
139                if (source >= 0) {
[0db6618]140                        proxy_disconnect(source);
[5ebff60]141                }
142                ssl_conn_free(conn);
[486ddb5]143
144                return FALSE;
145        }
146
[5ebff60]147        if (source == -1) {
[f920d9e]148                goto ssl_connected_failure;
[5ebff60]149        }
150
151        if (!initialized) {
[ba5add7]152                ssl_init();
[b7d3cc34]153        }
[5ebff60]154
155
156        if (ssl_ctx == NULL) {
[f920d9e]157                goto ssl_connected_failure;
[5ebff60]158        }
159
160        conn->ssl = SSL_new(ssl_ctx);
161        if (conn->ssl == NULL) {
[f920d9e]162                goto ssl_connected_failure;
[5ebff60]163        }
164
[309cb9e]165        /* We can do at least the handshake with non-blocking I/O */
[5ebff60]166        sock_make_nonblocking(conn->fd);
167        SSL_set_fd(conn->ssl, conn->fd);
168
[5eab9260]169        if (conn->hostname && !g_hostname_is_ip_address(conn->hostname)) {
[5ebff60]170                SSL_set_tlsext_host_name(conn->ssl, conn->hostname);
171        }
172
173        return ssl_handshake(data, source, cond);
[f920d9e]174
175ssl_connected_failure:
[5ebff60]176        conn->func(conn->data, 0, NULL, cond);
177        ssl_disconnect(conn);
[f920d9e]178        return FALSE;
179
[5ebff60]180}
[26fdfc5]181
[5ebff60]182static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
[26fdfc5]183{
184        struct scd *conn = data;
185        int st;
[5ebff60]186
187        if ((st = SSL_connect(conn->ssl)) < 0) {
188                conn->lasterr = SSL_get_error(conn->ssl, st);
189                if (conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE) {
190                        conn->func(conn->data, 0, NULL, cond);
191                        ssl_disconnect(conn);
[f920d9e]192                        return FALSE;
193                }
[5ebff60]194
195                conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
[309cb9e]196                return FALSE;
[26fdfc5]197        }
[5ebff60]198
[b7d3cc34]199        conn->established = TRUE;
[5ebff60]200        sock_make_blocking(conn->fd);           /* For now... */
201        conn->func(conn->data, 0, conn, cond);
[309cb9e]202        return FALSE;
[b7d3cc34]203}
204
[5ebff60]205int ssl_read(void *conn, char *buf, int len)
[b7d3cc34]206{
[26fdfc5]207        int st;
[5ebff60]208
209        if (!((struct scd*) conn)->established) {
[26fdfc5]210                ssl_errno = SSL_NOHANDSHAKE;
211                return -1;
212        }
[5ebff60]213
214        st = SSL_read(((struct scd*) conn)->ssl, buf, len);
215
[26fdfc5]216        ssl_errno = SSL_OK;
[5ebff60]217        if (st <= 0) {
218                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
219                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
220                    SSL_ERROR_WANT_WRITE) {
[26fdfc5]221                        ssl_errno = SSL_AGAIN;
[5ebff60]222                }
[26fdfc5]223        }
[5ebff60]224
225        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
226                write(1, buf, st);
227        }
228
[26fdfc5]229        return st;
[b7d3cc34]230}
231
[5ebff60]232int ssl_write(void *conn, const char *buf, int len)
[b7d3cc34]233{
[26fdfc5]234        int st;
[5ebff60]235
236        if (!((struct scd*) conn)->established) {
[26fdfc5]237                ssl_errno = SSL_NOHANDSHAKE;
238                return -1;
239        }
[5ebff60]240
241        st = SSL_write(((struct scd*) conn)->ssl, buf, len);
242
243        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
244                write(1, buf, st);
245        }
246
[26fdfc5]247        ssl_errno = SSL_OK;
[5ebff60]248        if (st <= 0) {
249                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
250                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
251                    SSL_ERROR_WANT_WRITE) {
[26fdfc5]252                        ssl_errno = SSL_AGAIN;
[5ebff60]253                }
[26fdfc5]254        }
[5ebff60]255
[26fdfc5]256        return st;
[b7d3cc34]257}
258
[5ebff60]259int ssl_pending(void *conn)
[8a2221a7]260{
[5ebff60]261        return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
262               SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
[8a2221a7]263}
264
[5ebff60]265static void ssl_conn_free(struct scd *conn)
[3f661849]266{
[5ebff60]267        SSL_free(conn->ssl);
268        g_free(conn->hostname);
269        g_free(conn);
270
[3f661849]271}
272
[5ebff60]273void ssl_disconnect(void *conn_)
[b7d3cc34]274{
275        struct scd *conn = conn_;
[5ebff60]276
277        if (conn->inpa != -1) {
278                b_event_remove(conn->inpa);
279        }
280
281        if (conn->established) {
282                SSL_shutdown(conn->ssl);
283        }
284
[0db6618]285        proxy_disconnect(conn->fd);
[5ebff60]286
287        ssl_conn_free(conn);
[b7d3cc34]288}
289
[5ebff60]290int ssl_getfd(void *conn)
[b7d3cc34]291{
[5ebff60]292        return(((struct scd*) conn)->fd);
[b7d3cc34]293}
[26fdfc5]294
[5ebff60]295b_input_condition ssl_getdirection(void *conn)
[26fdfc5]296{
[5ebff60]297        return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
[26fdfc5]298}
[523fb23]299
[5ebff60]300char *ssl_verify_strerror(int code)
[78b8401]301{
[5ebff60]302        return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
[78b8401]303}
304
[5ebff60]305size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len,
306                        const unsigned char *iv, unsigned char **res)
[523fb23]307{
[5ebff60]308        int output_length = 0;
[5c163e5]309        EVP_CIPHER_CTX *ctx;
[5ebff60]310
[523fb23]311        *res = g_new0(unsigned char, 72);
[5ebff60]312
[523fb23]313        /* Don't set key or IV because we will modify the parameters */
[5c163e5]314        ctx = EVP_CIPHER_CTX_new();
315        EVP_CipherInit_ex(ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
316        EVP_CIPHER_CTX_set_key_length(ctx, key_len);
317        EVP_CIPHER_CTX_set_padding(ctx, 0);
[523fb23]318        /* We finished modifying parameters so now we can set key and IV */
[5c163e5]319        EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1);
320        EVP_CipherUpdate(ctx, *res, &output_length, input, input_len);
321        EVP_CipherFinal_ex(ctx, *res, &output_length);
322        EVP_CIPHER_CTX_free(ctx);
[50b8978]323        //EVP_cleanup();
[5ebff60]324
[523fb23]325        return output_length;
326}
Note: See TracBrowser for help on using the repository browser.