source: lib/ssl_nss.c @ dd95ce4

Last change on this file since dd95ce4 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 9.9 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., 59 Temple Place,
25  Suite 330, Boston, MA  02111-1307  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        *(PRErrorCode *) arg = err = PORT_GetError();
80
81        switch (err) {
82        case SEC_ERROR_INVALID_AVA:
83        case SEC_ERROR_INVALID_TIME:
84        case SEC_ERROR_BAD_SIGNATURE:
85        case SEC_ERROR_EXPIRED_CERTIFICATE:
86        case SEC_ERROR_UNKNOWN_ISSUER:
87        case SEC_ERROR_UNTRUSTED_CERT:
88        case SEC_ERROR_CERT_VALID:
89        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
90        case SEC_ERROR_CRL_EXPIRED:
91        case SEC_ERROR_CRL_BAD_SIGNATURE:
92        case SEC_ERROR_EXTENSION_VALUE_INVALID:
93        case SEC_ERROR_CA_CERT_INVALID:
94        case SEC_ERROR_CERT_USAGES_INVALID:
95        case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
96                return SECSuccess;
97
98        default:
99                return SECFailure;
100        }
101}
102
103void ssl_init(void)
104{
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.
110        NSS_NoDB_Init(NULL);
111        NSS_SetDomesticPolicy();
112        initialized = TRUE;
113}
114
115void *ssl_connect(char *host, int port, gboolean verify,
116                  ssl_input_function func, gpointer data)
117{
118        struct scd *conn = g_new0(struct scd, 1);
119
120        conn->fd = proxy_connect(host, port, ssl_connected, conn);
121        conn->func = func;
122        conn->data = data;
123        conn->hostname = g_strdup(host);
124
125        if (conn->fd < 0) {
126                g_free(conn->hostname);
127                g_free(conn);
128                return (NULL);
129        }
130
131        if (!initialized) {
132                ssl_init();
133        }
134
135        return (conn);
136}
137
138static gboolean ssl_starttls_real(gpointer data, gint source,
139                                  b_input_condition cond)
140{
141        struct scd *conn = data;
142
143        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
144}
145
146void *ssl_starttls(int fd, char *hostname, gboolean verify,
147                   ssl_input_function func, gpointer data)
148{
149        struct scd *conn = g_new0(struct scd, 1);
150
151        conn->fd = fd;
152        conn->func = func;
153        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. */
160        conn->verify = verify && global.conf->cafile;
161
162        /* This function should be called via a (short) timeout instead of
163           directly from here, because these SSL calls are *supposed* to be
164           *completely* asynchronous and not ready yet when this function
165           (or *_connect, for examle) returns. Also, errors are reported via
166           the callback function, not via this function's return value.
167
168           In short, doing things like this makes the rest of the code a lot
169           simpler. */
170
171        b_timeout_add(1, ssl_starttls_real, conn);
172
173        return conn;
174}
175
176static gboolean ssl_connected(gpointer data, gint source,
177                              b_input_condition cond)
178{
179        struct scd *conn = data;
180
181        /* Right now we don't have any verification functionality for NSS. */
182
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);
189
190                return FALSE;
191        }
192
193        if (source == -1)
194                goto ssl_connected_failure;
195
196        /* Until we find out how to handle non-blocking I/O with NSS... */
197        sock_make_blocking(conn->fd);
198
199        conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
200        if (!conn->prfd)
201                goto ssl_connected_failure;
202        SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
203        SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
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);
208        SSL_ResetHandshake(conn->prfd, PR_FALSE);
209
210        if (SSL_ForceHandshake(conn->prfd)) {
211                goto ssl_connected_failure;
212        }
213
214        conn->established = TRUE;
215        conn->func(conn->data, 0, conn, cond);
216        return FALSE;
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
229        return FALSE;
230}
231
232int ssl_read(void *conn, char *buf, int len)
233{
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;
253}
254
255int ssl_write(void *conn, const char *buf, int len)
256{
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        PR_err = PR_GetError();
266
267        ssl_errno = SSL_OK;
268        if (PR_err == PR_WOULD_BLOCK_ERROR)
269                ssl_errno = SSL_AGAIN;
270
271        if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0)
272                len = write(2, buf, st);
273
274        return st;
275}
276
277int ssl_pending(void *conn)
278{
279        struct scd *c = (struct scd *)conn;
280
281        if (c == NULL) {
282                return 0;
283        }
284
285        return (c->established && SSL_DataPending(c->prfd) > 0);
286}
287
288void ssl_disconnect(void *conn_)
289{
290        struct scd *conn = conn_;
291
292        // When we swich to NSS_Init, we should have here
293        // NSS_Shutdown();
294
295        if (conn->prfd)
296                PR_Close(conn->prfd);
297
298        g_free(conn->hostname);
299        g_free(conn);
300}
301
302int ssl_getfd(void *conn)
303{
304        return (((struct scd *)conn)->fd);
305}
306
307b_input_condition ssl_getdirection(void *conn)
308{
309        /* Just in case someone calls us, let's return the most likely case: */
310        return B_EV_IO_READ;
311}
312
313char *ssl_verify_strerror(int code)
314{
315        return
316            g_strdup
317            ("SSL certificate verification not supported by BitlBee NSS code.");
318}
319
320size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
321                        const unsigned char *input, size_t input_len,
322                        const unsigned char *iv, unsigned char **res)
323{
324#define CIPHER_MECH CKM_DES3_CBC
325#define MAX_OUTPUT_LEN 72
326
327        int len1;
328        unsigned int len2;
329
330        PK11Context *ctx = NULL;
331        PK11SlotInfo *slot = NULL;
332        SECItem keyItem;
333        SECItem ivItem;
334        SECItem *secParam = NULL;
335        PK11SymKey *symKey = NULL;
336
337        size_t rc;
338        SECStatus rv;
339
340        if (!initialized) {
341                ssl_init();
342        }
343
344        keyItem.data = (unsigned char *)key;
345        keyItem.len = key_len;
346
347        slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
348        if (slot == NULL) {
349                fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
350                        PR_GetError());
351                rc = 0;
352                goto out;
353        }
354
355        symKey =
356            PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
357                              &keyItem, NULL);
358        if (symKey == NULL) {
359                fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
360                        PR_GetError());
361                rc = 0;
362                goto out;
363        }
364
365        ivItem.data = (unsigned char *)iv;
366        /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
367        ivItem.len = 8;
368
369        secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
370        if (secParam == NULL) {
371                fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
372                        PR_GetError());
373                rc = 0;
374                goto out;
375        }
376
377        ctx =
378            PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
379                                       secParam);
380        if (ctx == NULL) {
381                fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
382                        PR_GetError());
383                rc = 0;
384                goto out;
385        }
386
387        *res = g_new0(unsigned char, MAX_OUTPUT_LEN);
388
389        rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
390                           (unsigned char *)input, input_len);
391        if (rv != SECSuccess) {
392                fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
393                        PR_GetError());
394                rc = 0;
395                goto out;
396        }
397
398        assert(len1 <= MAX_OUTPUT_LEN);
399
400        rv = PK11_DigestFinal(ctx, *res + len1, &len2,
401                              (unsigned int)MAX_OUTPUT_LEN - len1);
402        if (rv != SECSuccess) {
403                fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
404                        PR_GetError());
405                rc = 0;
406                goto out;
407        }
408
409        rc = len1 + len2;
410
411 out:
412        if (ctx)
413                PK11_DestroyContext(ctx, PR_TRUE);
414        if (symKey)
415                PK11_FreeSymKey(symKey);
416        if (secParam)
417                SECITEM_FreeItem(secParam, PR_TRUE);
418        if (slot)
419                PK11_FreeSlot(slot);
420
421        return rc;
422}
Note: See TracBrowser for help on using the repository browser.