source: lib/ssl_openssl.c @ cad74fb

Last change on this file since cad74fb 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
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* SSL module - OpenSSL 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;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
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
33#include "bitlbee.h"
34#include "proxy.h"
35#include "ssl_client.h"
36#include "sock.h"
37
38int ssl_errno = 0;
39
40static gboolean initialized = FALSE;
41
42struct scd {
43        ssl_input_function func;
44        gpointer data;
45        int fd;
46        gboolean established;
47        gboolean verify;
48        char *hostname;
49
50        int inpa;
51        int lasterr;            /* Necessary for SSL_get_error */
52        SSL *ssl;
53};
54
55static SSL_CTX *ssl_ctx;
56
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);
61
62
63void ssl_init(void)
64{
65        const SSL_METHOD *meth;
66
67#if OPENSSL_VERSION_NUMBER < 0x10100000L
68        SSL_library_init();
69
70        meth = SSLv23_client_method();
71        ssl_ctx = SSL_CTX_new(meth);
72        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
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
78
79        initialized = TRUE;
80}
81
82void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
83{
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);
89                return NULL;
90        }
91
92        conn->func = func;
93        conn->data = data;
94        conn->inpa = -1;
95        conn->hostname = g_strdup(host);
96
97        return conn;
98}
99
100void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
101{
102        struct scd *conn = g_new0(struct scd, 1);
103
104        conn->fd = fd;
105        conn->func = func;
106        conn->data = data;
107        conn->inpa = -1;
108        conn->verify = verify && global.conf->cafile;
109        conn->hostname = g_strdup(hostname);
110
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.
116
117           In short, doing things like this makes the rest of the code a lot
118           simpler. */
119
120        b_timeout_add(1, ssl_starttls_real, conn);
121
122        return conn;
123}
124
125static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
126{
127        struct scd *conn = data;
128
129        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
130}
131
132static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
133{
134        struct scd *conn = data;
135
136        if (conn->verify) {
137                /* Right now we don't have any verification functionality for OpenSSL. */
138                conn->func(conn->data, 1, NULL, cond);
139                if (source >= 0) {
140                        proxy_disconnect(source);
141                }
142                ssl_conn_free(conn);
143
144                return FALSE;
145        }
146
147        if (source == -1) {
148                goto ssl_connected_failure;
149        }
150
151        if (!initialized) {
152                ssl_init();
153        }
154
155
156        if (ssl_ctx == NULL) {
157                goto ssl_connected_failure;
158        }
159
160        conn->ssl = SSL_new(ssl_ctx);
161        if (conn->ssl == NULL) {
162                goto ssl_connected_failure;
163        }
164
165        /* We can do at least the handshake with non-blocking I/O */
166        sock_make_nonblocking(conn->fd);
167        SSL_set_fd(conn->ssl, conn->fd);
168
169        if (conn->hostname && !g_hostname_is_ip_address(conn->hostname)) {
170                SSL_set_tlsext_host_name(conn->ssl, conn->hostname);
171        }
172
173        return ssl_handshake(data, source, cond);
174
175ssl_connected_failure:
176        conn->func(conn->data, 0, NULL, cond);
177        ssl_disconnect(conn);
178        return FALSE;
179
180}
181
182static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
183{
184        struct scd *conn = data;
185        int st;
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);
192                        return FALSE;
193                }
194
195                conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
196                return FALSE;
197        }
198
199        conn->established = TRUE;
200        sock_make_blocking(conn->fd);           /* For now... */
201        conn->func(conn->data, 0, conn, cond);
202        return FALSE;
203}
204
205int ssl_read(void *conn, char *buf, int len)
206{
207        int st;
208
209        if (!((struct scd*) conn)->established) {
210                ssl_errno = SSL_NOHANDSHAKE;
211                return -1;
212        }
213
214        st = SSL_read(((struct scd*) conn)->ssl, buf, len);
215
216        ssl_errno = SSL_OK;
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) {
221                        ssl_errno = SSL_AGAIN;
222                }
223        }
224
225        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
226                write(1, buf, st);
227        }
228
229        return st;
230}
231
232int ssl_write(void *conn, const char *buf, int len)
233{
234        int st;
235
236        if (!((struct scd*) conn)->established) {
237                ssl_errno = SSL_NOHANDSHAKE;
238                return -1;
239        }
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
247        ssl_errno = SSL_OK;
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) {
252                        ssl_errno = SSL_AGAIN;
253                }
254        }
255
256        return st;
257}
258
259int ssl_pending(void *conn)
260{
261        return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
262               SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
263}
264
265static void ssl_conn_free(struct scd *conn)
266{
267        SSL_free(conn->ssl);
268        g_free(conn->hostname);
269        g_free(conn);
270
271}
272
273void ssl_disconnect(void *conn_)
274{
275        struct scd *conn = conn_;
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
285        proxy_disconnect(conn->fd);
286
287        ssl_conn_free(conn);
288}
289
290int ssl_getfd(void *conn)
291{
292        return(((struct scd*) conn)->fd);
293}
294
295b_input_condition ssl_getdirection(void *conn)
296{
297        return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
298}
299
300char *ssl_verify_strerror(int code)
301{
302        return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
303}
304
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)
307{
308        int output_length = 0;
309        EVP_CIPHER_CTX *ctx;
310
311        *res = g_new0(unsigned char, 72);
312
313        /* Don't set key or IV because we will modify the parameters */
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);
318        /* We finished modifying parameters so now we can set key and IV */
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);
323        //EVP_cleanup();
324
325        return output_length;
326}
Note: See TracBrowser for help on using the repository browser.