source: lib/ssl_nss.c @ d628339

Last change on this file since d628339 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 10.3 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        }
229        if (source >= 0) {
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        }
308
309        g_free(conn->hostname);
310        g_free(conn);
311}
312
313int ssl_getfd(void *conn)
314{
315        return (((struct scd *) conn)->fd);
316}
317
318b_input_condition ssl_getdirection(void *conn)
319{
320        /* Just in case someone calls us, let's return the most likely case: */
321        return B_EV_IO_READ;
322}
323
324char *ssl_verify_strerror(int code)
325{
326        return
327                g_strdup
328                        ("SSL certificate verification not supported by BitlBee NSS code.");
329}
330
331size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
332                        const unsigned char *input, size_t input_len,
333                        const unsigned char *iv, unsigned char **res)
334{
335#define CIPHER_MECH CKM_DES3_CBC
336#define MAX_OUTPUT_LEN 72
337
338        int len1;
339        unsigned int len2;
340
341        PK11Context *ctx = NULL;
342        PK11SlotInfo *slot = NULL;
343        SECItem keyItem;
344        SECItem ivItem;
345        SECItem *secParam = NULL;
346        PK11SymKey *symKey = NULL;
347
348        size_t rc;
349        SECStatus rv;
350
351        if (!initialized) {
352                ssl_init();
353        }
354
355        keyItem.data = (unsigned char *) key;
356        keyItem.len = key_len;
357
358        slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
359        if (slot == NULL) {
360                fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
361                        PR_GetError());
362                rc = 0;
363                goto out;
364        }
365
366        symKey =
367                PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
368                                  &keyItem, NULL);
369        if (symKey == NULL) {
370                fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
371                        PR_GetError());
372                rc = 0;
373                goto out;
374        }
375
376        ivItem.data = (unsigned char *) iv;
377        /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
378        ivItem.len = 8;
379
380        secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
381        if (secParam == NULL) {
382                fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
383                        PR_GetError());
384                rc = 0;
385                goto out;
386        }
387
388        ctx =
389                PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
390                                           secParam);
391        if (ctx == NULL) {
392                fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
393                        PR_GetError());
394                rc = 0;
395                goto out;
396        }
397
398        *res = g_new0(unsigned char, MAX_OUTPUT_LEN);
399
400        rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
401                           (unsigned char *) input, input_len);
402        if (rv != SECSuccess) {
403                fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
404                        PR_GetError());
405                rc = 0;
406                goto out;
407        }
408
409        assert(len1 <= MAX_OUTPUT_LEN);
410
411        rv = PK11_DigestFinal(ctx, *res + len1, &len2,
412                              (unsigned int) MAX_OUTPUT_LEN - len1);
413        if (rv != SECSuccess) {
414                fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
415                        PR_GetError());
416                rc = 0;
417                goto out;
418        }
419
420        rc = len1 + len2;
421
422out:
423        if (ctx) {
424                PK11_DestroyContext(ctx, PR_TRUE);
425        }
426        if (symKey) {
427                PK11_FreeSymKey(symKey);
428        }
429        if (secParam) {
430                SECITEM_FreeItem(secParam, PR_TRUE);
431        }
432        if (slot) {
433                PK11_FreeSlot(slot);
434        }
435
436        return rc;
437}
Note: See TracBrowser for help on using the repository browser.