source: lib/ssl_nss.c @ 4a9c6b0

Last change on this file since 4a9c6b0 was 0db6618, checked in by dequis <dx@…>, at 2015-10-26T08:28:10Z

Use proxy_disconnect() in http, ssl, jabber, oscar

Twitter and MSN are all HTTP/SSL, so they don't need it either.

The out of tree facebook and steam plugins are also covered by the
HTTP/SSL changes.

Yahoo is written in a weird way and doesn't seem to need it (it seems it
doesn't immediately stop connections when you tell it to logout)

  • Property mode set to 100644
File size: 10.4 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 - NSS version                                             */
8
9/* Copyright 2005 Jelmer Vernooij                                       */
10
11/*
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  GNU General Public License for more details.
21
22  You should have received a copy of the GNU General Public License with
23  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
24  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
25  Fifth Floor, Boston, MA  02110-1301  USA
26*/
27
28#include "bitlbee.h"
29#include "proxy.h"
30#include "ssl_client.h"
31#include "sock.h"
32#include <nspr.h>
33#include <prio.h>
34#include <sslproto.h>
35#include <nss.h>
36#include <pk11pub.h>
37#include <private/pprio.h>
38#include <ssl.h>
39#include <seccomon.h>
40#include <secerr.h>
41#include <sslerr.h>
42#include <assert.h>
43#include <unistd.h>
44
45int ssl_errno = 0;
46
47static gboolean initialized = FALSE;
48
49#define SSLDEBUG 0
50
51struct scd {
52        ssl_input_function func;
53        gpointer data;
54        int fd;
55        char *hostname;
56        PRFileDesc *prfd;
57        gboolean established;
58        gboolean verify;
59};
60
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);
65
66static SECStatus nss_auth_cert(void *arg, PRFileDesc * socket, PRBool checksig,
67                               PRBool isserver)
68{
69        return SECSuccess;
70}
71
72static SECStatus nss_bad_cert(void *arg, PRFileDesc * socket)
73{
74        PRErrorCode err;
75
76        if (!arg) {
77                return SECFailure;
78        }
79
80        *(PRErrorCode *) arg = err = PORT_GetError();
81
82        switch (err) {
83        case SEC_ERROR_INVALID_AVA:
84        case SEC_ERROR_INVALID_TIME:
85        case SEC_ERROR_BAD_SIGNATURE:
86        case SEC_ERROR_EXPIRED_CERTIFICATE:
87        case SEC_ERROR_UNKNOWN_ISSUER:
88        case SEC_ERROR_UNTRUSTED_CERT:
89        case SEC_ERROR_CERT_VALID:
90        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
91        case SEC_ERROR_CRL_EXPIRED:
92        case SEC_ERROR_CRL_BAD_SIGNATURE:
93        case SEC_ERROR_EXTENSION_VALUE_INVALID:
94        case SEC_ERROR_CA_CERT_INVALID:
95        case SEC_ERROR_CERT_USAGES_INVALID:
96        case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
97                return SECSuccess;
98
99        default:
100                return SECFailure;
101        }
102}
103
104void ssl_init(void)
105{
106        PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
107        // https://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
108        // This NSS function is not intended for use with SSL, which
109        // requires that the certificate and key database files be
110        // opened. Relates to whole non-verification of servers for now.
111        NSS_NoDB_Init(NULL);
112        NSS_SetDomesticPolicy();
113        initialized = TRUE;
114}
115
116void *ssl_connect(char *host, int port, gboolean verify,
117                  ssl_input_function func, gpointer data)
118{
119        struct scd *conn = g_new0(struct scd, 1);
120
121        conn->fd = proxy_connect(host, port, ssl_connected, conn);
122        conn->func = func;
123        conn->data = data;
124        conn->hostname = g_strdup(host);
125
126        if (conn->fd < 0) {
127                g_free(conn->hostname);
128                g_free(conn);
129                return (NULL);
130        }
131
132        if (!initialized) {
133                ssl_init();
134        }
135
136        return (conn);
137}
138
139static gboolean ssl_starttls_real(gpointer data, gint source,
140                                  b_input_condition cond)
141{
142        struct scd *conn = data;
143
144        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
145}
146
147void *ssl_starttls(int fd, char *hostname, gboolean verify,
148                   ssl_input_function func, gpointer data)
149{
150        struct scd *conn = g_new0(struct scd, 1);
151
152        conn->fd = fd;
153        conn->func = func;
154        conn->data = data;
155        conn->hostname = g_strdup(hostname);
156
157        /* For now, SSL verification is globally enabled by setting the cafile
158           setting in bitlbee.conf. Commented out by default because probably
159           not everyone has this file in the same place and plenty of folks
160           may not have the cert of their private Jabber server in it. */
161        conn->verify = verify && global.conf->cafile;
162
163        /* This function should be called via a (short) timeout instead of
164           directly from here, because these SSL calls are *supposed* to be
165           *completely* asynchronous and not ready yet when this function
166           (or *_connect, for examle) returns. Also, errors are reported via
167           the callback function, not via this function's return value.
168
169           In short, doing things like this makes the rest of the code a lot
170           simpler. */
171
172        b_timeout_add(1, ssl_starttls_real, conn);
173
174        return conn;
175}
176
177static gboolean ssl_connected(gpointer data, gint source,
178                              b_input_condition cond)
179{
180        struct scd *conn = data;
181
182        /* Right now we don't have any verification functionality for NSS. */
183
184        if (conn->verify) {
185                conn->func(conn->data, 1, NULL, cond);
186                if (source >= 0) {
187                        closesocket(source);
188                }
189                g_free(conn->hostname);
190                g_free(conn);
191
192                return FALSE;
193        }
194
195        if (source == -1) {
196                goto ssl_connected_failure;
197        }
198
199        /* Until we find out how to handle non-blocking I/O with NSS... */
200        sock_make_blocking(conn->fd);
201
202        conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
203        if (!conn->prfd) {
204                goto ssl_connected_failure;
205        }
206        SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
207        SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
208        SSL_BadCertHook(conn->prfd, (SSLBadCertHandler) nss_bad_cert, NULL);
209        SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate) nss_auth_cert,
210                                (void *) CERT_GetDefaultCertDB());
211        SSL_SetURL(conn->prfd, conn->hostname);
212        SSL_ResetHandshake(conn->prfd, PR_FALSE);
213
214        if (SSL_ForceHandshake(conn->prfd)) {
215                goto ssl_connected_failure;
216        }
217
218        conn->established = TRUE;
219        conn->func(conn->data, 0, conn, cond);
220        return FALSE;
221
222ssl_connected_failure:
223
224        conn->func(conn->data, 0, NULL, cond);
225
226        if (conn->prfd) {
227                PR_Close(conn->prfd);
228        } else if (source >= 0) {
229                /* proxy_disconnect() would be redundant here */
230                closesocket(source);
231        }
232        g_free(conn->hostname);
233        g_free(conn);
234
235        return FALSE;
236}
237
238int ssl_read(void *conn, char *buf, int len)
239{
240        int st;
241        PRErrorCode PR_err;
242
243        if (!((struct scd *) conn)->established) {
244                ssl_errno = SSL_NOHANDSHAKE;
245                return -1;
246        }
247
248        st = PR_Read(((struct scd *) conn)->prfd, buf, len);
249        PR_err = PR_GetError();
250
251        ssl_errno = SSL_OK;
252        if (PR_err == PR_WOULD_BLOCK_ERROR) {
253                ssl_errno = SSL_AGAIN;
254        }
255
256        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
257                len = write(STDERR_FILENO, buf, st);
258        }
259
260        return st;
261}
262
263int ssl_write(void *conn, const char *buf, int len)
264{
265        int st;
266        PRErrorCode PR_err;
267
268        if (!((struct scd *) conn)->established) {
269                ssl_errno = SSL_NOHANDSHAKE;
270                return -1;
271        }
272        st = PR_Write(((struct scd *) conn)->prfd, buf, len);
273        PR_err = PR_GetError();
274
275        ssl_errno = SSL_OK;
276        if (PR_err == PR_WOULD_BLOCK_ERROR) {
277                ssl_errno = SSL_AGAIN;
278        }
279
280        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
281                len = write(2, buf, st);
282        }
283
284        return st;
285}
286
287int ssl_pending(void *conn)
288{
289        struct scd *c = (struct scd *) conn;
290
291        if (c == NULL) {
292                return 0;
293        }
294
295        return (c->established && SSL_DataPending(c->prfd) > 0);
296}
297
298void ssl_disconnect(void *conn_)
299{
300        struct scd *conn = conn_;
301
302        // When we swich to NSS_Init, we should have here
303        // NSS_Shutdown();
304
305        if (conn->prfd) {
306                PR_Close(conn->prfd);
307        } else if (conn->fd) {
308                proxy_disconnect(conn->fd);
309        }
310
311        g_free(conn->hostname);
312        g_free(conn);
313}
314
315int ssl_getfd(void *conn)
316{
317        return (((struct scd *) conn)->fd);
318}
319
320b_input_condition ssl_getdirection(void *conn)
321{
322        /* Just in case someone calls us, let's return the most likely case: */
323        return B_EV_IO_READ;
324}
325
326char *ssl_verify_strerror(int code)
327{
328        return
329                g_strdup
330                        ("SSL certificate verification not supported by BitlBee NSS code.");
331}
332
333size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
334                        const unsigned char *input, size_t input_len,
335                        const unsigned char *iv, unsigned char **res)
336{
337#define CIPHER_MECH CKM_DES3_CBC
338#define MAX_OUTPUT_LEN 72
339
340        int len1;
341        unsigned int len2;
342
343        PK11Context *ctx = NULL;
344        PK11SlotInfo *slot = NULL;
345        SECItem keyItem;
346        SECItem ivItem;
347        SECItem *secParam = NULL;
348        PK11SymKey *symKey = NULL;
349
350        size_t rc;
351        SECStatus rv;
352
353        if (!initialized) {
354                ssl_init();
355        }
356
357        keyItem.data = (unsigned char *) key;
358        keyItem.len = key_len;
359
360        slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
361        if (slot == NULL) {
362                fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
363                        PR_GetError());
364                rc = 0;
365                goto out;
366        }
367
368        symKey =
369                PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
370                                  &keyItem, NULL);
371        if (symKey == NULL) {
372                fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
373                        PR_GetError());
374                rc = 0;
375                goto out;
376        }
377
378        ivItem.data = (unsigned char *) iv;
379        /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
380        ivItem.len = 8;
381
382        secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
383        if (secParam == NULL) {
384                fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
385                        PR_GetError());
386                rc = 0;
387                goto out;
388        }
389
390        ctx =
391                PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
392                                           secParam);
393        if (ctx == NULL) {
394                fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
395                        PR_GetError());
396                rc = 0;
397                goto out;
398        }
399
400        *res = g_new0(unsigned char, MAX_OUTPUT_LEN);
401
402        rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
403                           (unsigned char *) input, input_len);
404        if (rv != SECSuccess) {
405                fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
406                        PR_GetError());
407                rc = 0;
408                goto out;
409        }
410
411        assert(len1 <= MAX_OUTPUT_LEN);
412
413        rv = PK11_DigestFinal(ctx, *res + len1, &len2,
414                              (unsigned int) MAX_OUTPUT_LEN - len1);
415        if (rv != SECSuccess) {
416                fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
417                        PR_GetError());
418                rc = 0;
419                goto out;
420        }
421
422        rc = len1 + len2;
423
424out:
425        if (ctx) {
426                PK11_DestroyContext(ctx, PR_TRUE);
427        }
428        if (symKey) {
429                PK11_FreeSymKey(symKey);
430        }
431        if (secParam) {
432                SECITEM_FreeItem(secParam, PR_TRUE);
433        }
434        if (slot) {
435                PK11_FreeSlot(slot);
436        }
437
438        return rc;
439}
Note: See TracBrowser for help on using the repository browser.