~ubuntu-branches/ubuntu/karmic/libtinymail/karmic

« back to all changes in this revision

Viewing changes to libtinymail-camel/camel-lite/camel/camel-tcp-stream-ssl.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-10-12 11:21:12 UTC
  • Revision ID: james.westby@ubuntu.com-20071012112112-fod9fs7yrooxjr7i
Tags: upstream-0.0.2
ImportĀ upstreamĀ versionĀ 0.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 *  Authors: Jeffrey Stedfast <fejj@ximian.com>
 
4
 *
 
5
 *  Copyright 2001 Ximian, Inc. (www.ximian.com)
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or
 
8
 * modify it under the terms of version 2 of the GNU Lesser General Public
 
9
 * License as published by the Free Software Foundation.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this program; if not, write to the
 
18
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
19
 * Boston, MA 02111-1307, USA.
 
20
 *
 
21
 */
 
22
 
 
23
/* NOTE: This is the default implementation of CamelTcpStreamSSL,
 
24
 * used when the Mozilla NSS libraries are used. If you configured
 
25
 * OpenSSL support instead, then this file won't be compiled and
 
26
 * the CamelTcpStreamSSL implementation in camel-tcp-stream-openssl.c
 
27
 * will be used instead.
 
28
 */
 
29
 
 
30
#ifdef HAVE_CONFIG_H
 
31
#include <config.h>
 
32
#endif
 
33
 
 
34
#ifdef HAVE_NSS
 
35
 
 
36
#include <errno.h>
 
37
#include <fcntl.h>
 
38
#include <string.h>
 
39
#include <unistd.h>
 
40
#include <sys/stat.h>
 
41
#include <sys/types.h>
 
42
 
 
43
#include <nspr.h>
 
44
#include <prio.h>
 
45
#include <prerror.h>
 
46
#include <prerr.h>
 
47
#include <secerr.h>
 
48
#include <sslerr.h>
 
49
#include "nss.h"    /* Don't use <> here or it will include the system nss.h instead */
 
50
#include <ssl.h>
 
51
#include <cert.h>
 
52
#include <certdb.h>
 
53
#include <pk11func.h>
 
54
 
 
55
#include <glib.h>
 
56
#include <glib/gi18n-lib.h>
 
57
#include <glib/gstdio.h>
 
58
 
 
59
#include <libedataserver/md5-utils.h>
 
60
 
 
61
#include "camel-certdb.h"
 
62
#include "camel-file-utils.h"
 
63
#include "camel-operation.h"
 
64
#include "camel-private.h"
 
65
#include "camel-session.h"
 
66
#include "camel-stream-fs.h"
 
67
#include "camel-tcp-stream-ssl.h"
 
68
 
 
69
static CamelTcpStreamClass *parent_class = NULL;
 
70
 
 
71
/* Returns the class for a CamelTcpStreamSSL */
 
72
#define CTSS_CLASS(so) CAMEL_TCP_STREAM_SSL_CLASS (CAMEL_OBJECT_GET_CLASS (so))
 
73
 
 
74
static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
 
75
static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
 
76
static int stream_flush  (CamelStream *stream);
 
77
static int stream_close  (CamelStream *stream);
 
78
 
 
79
static PRFileDesc *enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd);
 
80
 
 
81
static int stream_connect    (CamelTcpStream *stream, struct addrinfo *host);
 
82
static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
 
83
static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
 
84
static struct sockaddr *stream_get_local_address (CamelTcpStream *stream, socklen_t *len);
 
85
static struct sockaddr *stream_get_remote_address (CamelTcpStream *stream, socklen_t *len);
 
86
static ssize_t stream_read_nb (CamelTcpStream *stream, char *buffer, size_t n);
 
87
static ssize_t stream_read_idle (CamelStream *stream, char *buffer, size_t n);
 
88
 
 
89
struct _CamelTcpStreamSSLPrivate {
 
90
        PRFileDesc *sockfd;
 
91
        
 
92
        struct _CamelSession *session;
 
93
        char *expected_host;
 
94
        gboolean ssl_mode;
 
95
        guint32 flags;
 
96
        gboolean accepted;
 
97
        CamelService *service;
 
98
};
 
99
 
 
100
static void
 
101
camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *camel_tcp_stream_ssl_class)
 
102
{
 
103
        CamelTcpStreamClass *camel_tcp_stream_class =
 
104
                CAMEL_TCP_STREAM_CLASS (camel_tcp_stream_ssl_class);
 
105
        CamelStreamClass *camel_stream_class =
 
106
                CAMEL_STREAM_CLASS (camel_tcp_stream_ssl_class);
 
107
        
 
108
        parent_class = CAMEL_TCP_STREAM_CLASS (camel_type_get_global_classfuncs (camel_tcp_stream_get_type ()));
 
109
        
 
110
        /* virtual method overload */
 
111
        camel_stream_class->read_idle = stream_read_idle;
 
112
 
 
113
        camel_stream_class->read = stream_read;
 
114
        camel_stream_class->write = stream_write;
 
115
        camel_stream_class->flush = stream_flush;
 
116
        camel_stream_class->close = stream_close;
 
117
 
 
118
        camel_tcp_stream_class->read_nb = stream_read_nb;
 
119
        camel_tcp_stream_class->connect = stream_connect;
 
120
        camel_tcp_stream_class->getsockopt = stream_getsockopt;
 
121
        camel_tcp_stream_class->setsockopt = stream_setsockopt;
 
122
        camel_tcp_stream_class->get_local_address  = stream_get_local_address;
 
123
        camel_tcp_stream_class->get_remote_address = stream_get_remote_address;
 
124
}
 
125
 
 
126
static void
 
127
camel_tcp_stream_ssl_init (gpointer object, gpointer klass)
 
128
{
 
129
        CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
 
130
        
 
131
        stream->priv = g_new0 (struct _CamelTcpStreamSSLPrivate, 1);
 
132
}
 
133
 
 
134
static void
 
135
camel_tcp_stream_ssl_finalize (CamelObject *object)
 
136
{
 
137
        CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
 
138
        
 
139
        if (stream->priv->sockfd != NULL)
 
140
                PR_Close (stream->priv->sockfd);
 
141
 
 
142
        if (stream->priv->session)
 
143
                camel_object_unref(stream->priv->session);
 
144
 
 
145
        g_free (stream->priv->expected_host);
 
146
        
 
147
        g_free (stream->priv);
 
148
}
 
149
 
 
150
 
 
151
CamelType
 
152
camel_tcp_stream_ssl_get_type (void)
 
153
{
 
154
        static CamelType type = CAMEL_INVALID_TYPE;
 
155
        
 
156
        if (type == CAMEL_INVALID_TYPE) {
 
157
                type = camel_type_register (camel_tcp_stream_get_type (),
 
158
                                            "CamelTcpStreamSSL",
 
159
                                            sizeof (CamelTcpStreamSSL),
 
160
                                            sizeof (CamelTcpStreamSSLClass),
 
161
                                            (CamelObjectClassInitFunc) camel_tcp_stream_ssl_class_init,
 
162
                                            NULL,
 
163
                                            (CamelObjectInitFunc) camel_tcp_stream_ssl_init,
 
164
                                            (CamelObjectFinalizeFunc) camel_tcp_stream_ssl_finalize);
 
165
        }
 
166
        
 
167
        return type;
 
168
}
 
169
 
 
170
 
 
171
/**
 
172
 * camel_tcp_stream_ssl_new:
 
173
 * @session: an active #CamelSession object
 
174
 * @expected_host: host that the stream is expected to connect with
 
175
 * @flags: a bitwise combination of any of
 
176
 * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL2,
 
177
 * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL3 or
 
178
 * #CAMEL_TCP_STREAM_SSL_ENABLE_TLS
 
179
 *
 
180
 * Since the SSL certificate authenticator may need to prompt the
 
181
 * user, a #CamelSession is needed. @expected_host is needed as a
 
182
 * protection against an MITM attack.
 
183
 *
 
184
 * Returns a new #CamelTcpStreamSSL stream preset in SSL mode
 
185
 **/
 
186
CamelStream *
 
187
camel_tcp_stream_ssl_new (CamelService *service, const char *expected_host, guint32 flags)
 
188
{
 
189
        CamelTcpStreamSSL *stream;
 
190
 
 
191
        g_assert(CAMEL_IS_SESSION(service->session));
 
192
 
 
193
        stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
 
194
 
 
195
        stream->priv->session = service->session;
 
196
        camel_object_ref(service->session);
 
197
        stream->priv->expected_host = g_strdup (expected_host);
 
198
        stream->priv->ssl_mode = TRUE;
 
199
        stream->priv->flags = flags;
 
200
        stream->priv->service = service;
 
201
 
 
202
        return CAMEL_STREAM (stream);
 
203
}
 
204
 
 
205
 
 
206
/**
 
207
 * camel_tcp_stream_ssl_new_raw:
 
208
 * @service: an active #CamelService object
 
209
 * @expected_host: host that the stream is expected to connect with
 
210
 * @flags: a bitwise combination of any of
 
211
 * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL2,
 
212
 * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL3 or
 
213
 * #CAMEL_TCP_STREAM_SSL_ENABLE_TLS
 
214
 *
 
215
 * Since the SSL certificate authenticator may need to prompt the
 
216
 * user, a CamelSession is needed. @expected_host is needed as a
 
217
 * protection against an MITM attack.
 
218
 *
 
219
 * Returns a new #CamelTcpStreamSSL stream not yet toggled into SSL mode
 
220
 **/
 
221
CamelStream *
 
222
camel_tcp_stream_ssl_new_raw (CamelService *service, const char *expected_host, guint32 flags)
 
223
{
 
224
        CamelTcpStreamSSL *stream;
 
225
 
 
226
        g_assert(CAMEL_IS_SESSION(service->session));
 
227
        
 
228
        stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
 
229
        
 
230
        stream->priv->session = service->session;
 
231
        camel_object_ref(service->session);
 
232
        stream->priv->expected_host = g_strdup (expected_host);
 
233
        stream->priv->ssl_mode = FALSE;
 
234
        stream->priv->flags = flags;
 
235
        stream->priv->service = service;
 
236
 
 
237
        return CAMEL_STREAM (stream);
 
238
}
 
239
 
 
240
 
 
241
static void
 
242
set_errno (int code)
 
243
{
 
244
        /* FIXME: this should handle more. */
 
245
        switch (code) {
 
246
        case PR_INVALID_ARGUMENT_ERROR:
 
247
                errno = EINVAL;
 
248
                break;
 
249
        case PR_PENDING_INTERRUPT_ERROR:
 
250
                errno = EINTR;
 
251
                break;
 
252
        case PR_IO_PENDING_ERROR:
 
253
                errno = EAGAIN;
 
254
                break;
 
255
#ifdef EWOULDBLOCK
 
256
        case PR_WOULD_BLOCK_ERROR:
 
257
                errno = EWOULDBLOCK;
 
258
                break;
 
259
#endif
 
260
#ifdef EINPROGRESS
 
261
        case PR_IN_PROGRESS_ERROR:
 
262
                errno = EINPROGRESS;
 
263
                break;
 
264
#endif
 
265
#ifdef EALREADY
 
266
        case PR_ALREADY_INITIATED_ERROR:
 
267
                errno = EALREADY;
 
268
                break;
 
269
#endif
 
270
#ifdef EHOSTUNREACH
 
271
        case PR_NETWORK_UNREACHABLE_ERROR:
 
272
                errno = EHOSTUNREACH;
 
273
                break;
 
274
#endif
 
275
#ifdef ECONNREFUSED
 
276
        case PR_CONNECT_REFUSED_ERROR:
 
277
                errno = ECONNREFUSED;
 
278
                break;
 
279
#endif
 
280
#ifdef ETIMEDOUT
 
281
        case PR_CONNECT_TIMEOUT_ERROR:
 
282
        case PR_IO_TIMEOUT_ERROR:
 
283
                errno = ETIMEDOUT;
 
284
                break;
 
285
#endif
 
286
#ifdef ENOTCONN
 
287
        case PR_NOT_CONNECTED_ERROR:
 
288
                errno = ENOTCONN;
 
289
                break;
 
290
#endif
 
291
#ifdef ECONNRESET
 
292
        case PR_CONNECT_RESET_ERROR:
 
293
                errno = ECONNRESET;
 
294
                break;
 
295
#endif
 
296
        case PR_IO_ERROR:
 
297
        default:
 
298
                errno = EIO;
 
299
                break;
 
300
        }
 
301
}
 
302
 
 
303
 
 
304
/**
 
305
 * camel_tcp_stream_ssl_enable_ssl:
 
306
 * @ssl: a #CamelTcpStreamSSL object
 
307
 *
 
308
 * Toggles an ssl-capable stream into ssl mode (if it isn't already).
 
309
 *
 
310
 * Returns %0 on success or %-1 on fail
 
311
 **/
 
312
int
 
313
camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl)
 
314
{
 
315
        PRFileDesc *fd;
 
316
        int nonblock;
 
317
        PRSocketOptionData sockopts;
 
318
 
 
319
        /* get O_NONBLOCK options */
 
320
        sockopts.option = PR_SockOpt_Nonblocking;
 
321
        PR_GetSocketOption (ssl->priv->sockfd, &sockopts);
 
322
        sockopts.option = PR_SockOpt_Nonblocking;
 
323
        nonblock = sockopts.value.non_blocking;
 
324
        sockopts.value.non_blocking = FALSE;
 
325
        PR_SetSocketOption (ssl->priv->sockfd, &sockopts);
 
326
 
 
327
        g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (ssl), -1);
 
328
        
 
329
        if (ssl->priv->sockfd && !ssl->priv->ssl_mode) 
 
330
        {
 
331
 
 
332
                if (!(fd = enable_ssl (ssl, NULL))) 
 
333
                {
 
334
                        set_errno (PR_GetError ());
 
335
                        return -1;
 
336
                }
 
337
 
 
338
                ssl->priv->sockfd = fd;
 
339
 
 
340
                if (SSL_ResetHandshake (fd, FALSE) == SECFailure) 
 
341
                {
 
342
                        set_errno (PR_GetError ());
 
343
                        return -1;
 
344
                }
 
345
 
 
346
                if (SSL_ForceHandshake (fd) == SECFailure) 
 
347
                {
 
348
                        set_errno (PR_GetError ());
 
349
                        return -1;
 
350
                }
 
351
        }
 
352
 
 
353
        /* restore O_NONBLOCK options */
 
354
        sockopts.option = PR_SockOpt_Nonblocking;
 
355
        sockopts.value.non_blocking = nonblock;
 
356
        PR_SetSocketOption (ssl->priv->sockfd, &sockopts);
 
357
 
 
358
        ssl->priv->ssl_mode = TRUE;
 
359
 
 
360
        return 0;
 
361
}
 
362
 
 
363
 
 
364
static ssize_t
 
365
stream_read_nb (CamelTcpStream *stream, char *buffer, size_t n)
 
366
{
 
367
        CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
 
368
        ssize_t nread;
 
369
        PRSocketOptionData sockopts;
 
370
        PRPollDesc pollfds[2];
 
371
        gboolean nonblock;
 
372
 
 
373
        /* get O_NONBLOCK options */
 
374
        sockopts.option = PR_SockOpt_Nonblocking;
 
375
        PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
376
        sockopts.option = PR_SockOpt_Nonblocking;
 
377
        nonblock = sockopts.value.non_blocking;
 
378
        sockopts.value.non_blocking = TRUE;
 
379
        PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
380
 
 
381
        pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
382
        pollfds[0].in_flags = PR_POLL_READ;
 
383
        
 
384
        do {
 
385
                PRInt32 res;
 
386
 
 
387
                pollfds[0].out_flags = 0;
 
388
                pollfds[1].out_flags = 0;
 
389
                nread = -1;
 
390
 
 
391
                res = PR_Poll(pollfds, 1, NONBLOCKING_READ_TIMEOUT);
 
392
                if (res == -1)
 
393
                        set_errno(PR_GetError());
 
394
                else if (res == 0) {
 
395
#ifdef ETIMEDOUT
 
396
                        errno = ETIMEDOUT;
 
397
#else
 
398
                        errno = EIO;
 
399
#endif
 
400
                } else {
 
401
                         do { 
 
402
                                nread = -1;
 
403
                                if (PR_Available (tcp_stream_ssl->priv->sockfd) != 0)
 
404
                                        nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
 
405
                                if (nread == -1)
 
406
                                        set_errno (PR_GetError ());
 
407
                         } while (0 && (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR)); 
 
408
                }
 
409
        } while (0 && (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
 
410
                                 PR_GetError () == PR_IO_PENDING_ERROR ||
 
411
                                 PR_GetError () == PR_WOULD_BLOCK_ERROR)));
 
412
        
 
413
        /* restore O_NONBLOCK options */
 
414
        sockopts.option = PR_SockOpt_Nonblocking;
 
415
        sockopts.value.non_blocking = nonblock;
 
416
        PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
417
 
 
418
        return nread;
 
419
}
 
420
 
 
421
static ssize_t
 
422
stream_read (CamelStream *stream, char *buffer, size_t n)
 
423
{
 
424
        CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
 
425
        PRFileDesc *cancel_fd;
 
426
        ssize_t nread;
 
427
        
 
428
        if (camel_operation_cancel_check (NULL)) {
 
429
                errno = EINTR;
 
430
                return -1;
 
431
        }
 
432
        
 
433
        cancel_fd = camel_operation_cancel_prfd (NULL);
 
434
        if (cancel_fd == NULL) {
 
435
 
 
436
                PRSocketOptionData sockopts;
 
437
                PRPollDesc pollfds[1];
 
438
                gboolean nonblock;
 
439
                int error;
 
440
                
 
441
                /* get O_NONBLOCK options */
 
442
                sockopts.option = PR_SockOpt_Nonblocking;
 
443
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
444
                sockopts.option = PR_SockOpt_Nonblocking;
 
445
                nonblock = sockopts.value.non_blocking;
 
446
                sockopts.value.non_blocking = TRUE;
 
447
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
448
 
 
449
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
450
                pollfds[0].in_flags = PR_POLL_READ;
 
451
 
 
452
                do {
 
453
                        PRInt32 res;
 
454
 
 
455
                        pollfds[0].out_flags = 0;
 
456
                        nread = -1;
 
457
 
 
458
                        res = PR_Poll(pollfds, 1, PR_TicksPerSecond () * BLOCKING_READ_TIMEOUT);
 
459
 
 
460
                        if (res == -1)
 
461
                                set_errno(PR_GetError());
 
462
                        else if (res == 0) {
 
463
#ifdef ETIMEDOUT
 
464
                                errno = ETIMEDOUT;
 
465
#else
 
466
                                errno = EIO;
 
467
#endif
 
468
                        } else {
 
469
                                do {
 
470
                                        nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
 
471
                                        if (nread == -1)
 
472
                                                set_errno (PR_GetError ());
 
473
                                } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
474
                        }
 
475
                } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
 
476
                                         PR_GetError () == PR_IO_PENDING_ERROR ||
 
477
                                         PR_GetError () == PR_WOULD_BLOCK_ERROR));
 
478
                
 
479
                /* restore O_NONBLOCK options */
 
480
                error = errno;
 
481
                sockopts.option = PR_SockOpt_Nonblocking;
 
482
                sockopts.value.non_blocking = nonblock;
 
483
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
484
                errno = error;
 
485
 
 
486
        } else {
 
487
                PRSocketOptionData sockopts;
 
488
                PRPollDesc pollfds[2];
 
489
                gboolean nonblock;
 
490
                int error;
 
491
                
 
492
                /* get O_NONBLOCK options */
 
493
                sockopts.option = PR_SockOpt_Nonblocking;
 
494
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
495
                sockopts.option = PR_SockOpt_Nonblocking;
 
496
                nonblock = sockopts.value.non_blocking;
 
497
                sockopts.value.non_blocking = TRUE;
 
498
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
499
 
 
500
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
501
                pollfds[0].in_flags = PR_POLL_READ;
 
502
                pollfds[1].fd = cancel_fd;
 
503
                pollfds[1].in_flags = PR_POLL_READ;
 
504
                
 
505
                do {
 
506
                        PRInt32 res;
 
507
 
 
508
                        pollfds[0].out_flags = 0;
 
509
                        pollfds[1].out_flags = 0;
 
510
                        nread = -1;
 
511
 
 
512
                        res = PR_Poll(pollfds, 2, PR_TicksPerSecond () * BLOCKING_READ_TIMEOUT);
 
513
 
 
514
                        if (res == -1)
 
515
                                set_errno(PR_GetError());
 
516
                        else if (res == 0) {
 
517
#ifdef ETIMEDOUT
 
518
                                errno = ETIMEDOUT;
 
519
#else
 
520
                                errno = EIO;
 
521
#endif
 
522
                        } else if (pollfds[1].out_flags == PR_POLL_READ) {
 
523
                                errno = EINTR;
 
524
                                goto failed;
 
525
                        } else {
 
526
                                do {
 
527
                                        nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
 
528
                                        if (nread == -1)
 
529
                                                set_errno (PR_GetError ());
 
530
                                } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
531
                        }
 
532
                } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
 
533
                                         PR_GetError () == PR_IO_PENDING_ERROR ||
 
534
                                         PR_GetError () == PR_WOULD_BLOCK_ERROR));
 
535
                
 
536
                /* restore O_NONBLOCK options */
 
537
        failed:
 
538
                error = errno;
 
539
                sockopts.option = PR_SockOpt_Nonblocking;
 
540
                sockopts.value.non_blocking = nonblock;
 
541
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
542
                errno = error;
 
543
        }
 
544
        
 
545
        return nread;
 
546
}
 
547
 
 
548
 
 
549
 
 
550
static ssize_t 
 
551
stream_read_idle (CamelStream *stream, char *buffer, size_t n)
 
552
{
 
553
        CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
 
554
        PRFileDesc *cancel_fd;
 
555
        ssize_t nread;
 
556
        
 
557
        if (camel_operation_cancel_check (NULL)) {
 
558
                errno = EINTR;
 
559
                return -1;
 
560
        }
 
561
        
 
562
        cancel_fd = camel_operation_cancel_prfd (NULL);
 
563
        if (cancel_fd == NULL) {
 
564
 
 
565
                PRSocketOptionData sockopts;
 
566
                PRPollDesc pollfds[1];
 
567
                gboolean nonblock;
 
568
                int error;
 
569
                
 
570
                /* get O_NONBLOCK options */
 
571
                sockopts.option = PR_SockOpt_Nonblocking;
 
572
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
573
                sockopts.option = PR_SockOpt_Nonblocking;
 
574
                nonblock = sockopts.value.non_blocking;
 
575
                sockopts.value.non_blocking = TRUE;
 
576
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
577
 
 
578
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
579
                pollfds[0].in_flags = PR_POLL_READ;
 
580
 
 
581
                do {
 
582
                        PRInt32 res;
 
583
 
 
584
                        pollfds[0].out_flags = 0;
 
585
                        nread = -1;
 
586
 
 
587
                        res = PR_Poll(pollfds, 1, PR_TicksPerSecond () * IDLE_READ_TIMEOUT);
 
588
 
 
589
                        if (res == -1)
 
590
                                set_errno(PR_GetError());
 
591
                        else if (res == 0) {
 
592
#ifdef ETIMEDOUT
 
593
                                errno = ETIMEDOUT;
 
594
#else
 
595
                                errno = EIO;
 
596
#endif
 
597
                        } else {
 
598
                                do {
 
599
                                        nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
 
600
                                        if (nread == -1)
 
601
                                                set_errno (PR_GetError ());
 
602
                                } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
603
                        }
 
604
                } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
 
605
                                         PR_GetError () == PR_IO_PENDING_ERROR ||
 
606
                                         PR_GetError () == PR_WOULD_BLOCK_ERROR));
 
607
                
 
608
                /* restore O_NONBLOCK options */
 
609
                error = errno;
 
610
                sockopts.option = PR_SockOpt_Nonblocking;
 
611
                sockopts.value.non_blocking = nonblock;
 
612
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
613
                errno = error;
 
614
 
 
615
 
 
616
        } else {
 
617
                PRSocketOptionData sockopts;
 
618
                PRPollDesc pollfds[2];
 
619
                gboolean nonblock;
 
620
                int error;
 
621
                
 
622
                /* get O_NONBLOCK options */
 
623
                sockopts.option = PR_SockOpt_Nonblocking;
 
624
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
625
                sockopts.option = PR_SockOpt_Nonblocking;
 
626
                nonblock = sockopts.value.non_blocking;
 
627
                sockopts.value.non_blocking = TRUE;
 
628
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
629
 
 
630
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
631
                pollfds[0].in_flags = PR_POLL_READ;
 
632
                pollfds[1].fd = cancel_fd;
 
633
                pollfds[1].in_flags = PR_POLL_READ;
 
634
                
 
635
                do {
 
636
                        PRInt32 res;
 
637
 
 
638
                        pollfds[0].out_flags = 0;
 
639
                        pollfds[1].out_flags = 0;
 
640
                        nread = -1;
 
641
 
 
642
                        res = PR_Poll(pollfds, 2, PR_TicksPerSecond () * IDLE_READ_TIMEOUT);
 
643
 
 
644
                        if (res == -1)
 
645
                                set_errno(PR_GetError());
 
646
                        else if (res == 0) {
 
647
#ifdef ETIMEDOUT
 
648
                                errno = ETIMEDOUT;
 
649
#else
 
650
                                errno = EIO;
 
651
#endif
 
652
                        } else if (pollfds[1].out_flags == PR_POLL_READ) {
 
653
                                errno = EINTR;
 
654
                                goto failed;
 
655
                        } else {
 
656
                                do {
 
657
                                        nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
 
658
                                        if (nread == -1)
 
659
                                                set_errno (PR_GetError ());
 
660
                                } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
661
                        }
 
662
                } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
 
663
                                         PR_GetError () == PR_IO_PENDING_ERROR ||
 
664
                                         PR_GetError () == PR_WOULD_BLOCK_ERROR));
 
665
                
 
666
                /* restore O_NONBLOCK options */
 
667
        failed:
 
668
                error = errno;
 
669
                sockopts.option = PR_SockOpt_Nonblocking;
 
670
                sockopts.value.non_blocking = nonblock;
 
671
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
672
                errno = error;
 
673
        }
 
674
        
 
675
        return nread;
 
676
}
 
677
 
 
678
 
 
679
static ssize_t
 
680
stream_write (CamelStream *stream, const char *buffer, size_t n)
 
681
{
 
682
        CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
 
683
        ssize_t w, written = 0;
 
684
        PRFileDesc *cancel_fd;
 
685
        
 
686
        if (camel_operation_cancel_check (NULL)) {
 
687
                errno = EINTR;
 
688
                return -1;
 
689
        }
 
690
        
 
691
        cancel_fd = camel_operation_cancel_prfd (NULL);
 
692
        if (cancel_fd == NULL) {
 
693
 
 
694
                PRSocketOptionData sockopts;
 
695
                PRPollDesc pollfds[1];
 
696
                gboolean nonblock;
 
697
                int error;
 
698
                
 
699
                /* get O_NONBLOCK options */
 
700
                sockopts.option = PR_SockOpt_Nonblocking;
 
701
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
702
                sockopts.option = PR_SockOpt_Nonblocking;
 
703
                nonblock = sockopts.value.non_blocking;
 
704
                sockopts.value.non_blocking = TRUE;
 
705
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
706
                
 
707
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
708
                pollfds[0].in_flags = PR_POLL_WRITE;
 
709
 
 
710
                do {
 
711
                        PRInt32 res;
 
712
 
 
713
                        pollfds[0].out_flags = 0;
 
714
                        w = -1;
 
715
 
 
716
                        res = PR_Poll (pollfds, 1, PR_TicksPerSecond () * BLOCKING_WRITE_TIMEOUT);
 
717
                        if (res == -1) {
 
718
                                set_errno(PR_GetError());
 
719
                                if (PR_GetError () == PR_PENDING_INTERRUPT_ERROR)
 
720
                                        w = 0;
 
721
                        } else if (res == 0) {
 
722
#ifdef ETIMEDOUT
 
723
                                errno = ETIMEDOUT;
 
724
#else
 
725
                                errno = EIO;
 
726
#endif
 
727
                        } else {
 
728
                                do {
 
729
                                        w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
 
730
                                        if (w == -1)
 
731
                                                set_errno (PR_GetError ());
 
732
                                } while (w == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
733
                                
 
734
                                if (w == -1) {
 
735
                                        if (PR_GetError () == PR_IO_PENDING_ERROR ||
 
736
                                            PR_GetError () == PR_WOULD_BLOCK_ERROR)
 
737
                                                w = 0;
 
738
                                } else
 
739
                                        written += w;
 
740
                        }
 
741
                } while (w != -1 && written < n);
 
742
                
 
743
                /* restore O_NONBLOCK options */
 
744
                error = errno;
 
745
                sockopts.option = PR_SockOpt_Nonblocking;
 
746
                sockopts.value.non_blocking = nonblock;
 
747
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
748
                errno = error;
 
749
 
 
750
        } else {
 
751
                PRSocketOptionData sockopts;
 
752
                PRPollDesc pollfds[2];
 
753
                gboolean nonblock;
 
754
                int error;
 
755
                
 
756
                /* get O_NONBLOCK options */
 
757
                sockopts.option = PR_SockOpt_Nonblocking;
 
758
                PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
759
                sockopts.option = PR_SockOpt_Nonblocking;
 
760
                nonblock = sockopts.value.non_blocking;
 
761
                sockopts.value.non_blocking = TRUE;
 
762
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
763
                
 
764
                pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
 
765
                pollfds[0].in_flags = PR_POLL_WRITE;
 
766
                pollfds[1].fd = cancel_fd;
 
767
                pollfds[1].in_flags = PR_POLL_READ;
 
768
                
 
769
                do {
 
770
                        PRInt32 res;
 
771
 
 
772
                        pollfds[0].out_flags = 0;
 
773
                        pollfds[1].out_flags = 0;
 
774
                        w = -1;
 
775
 
 
776
                        res = PR_Poll (pollfds, 2, PR_TicksPerSecond () * BLOCKING_WRITE_TIMEOUT);
 
777
                        if (res == -1) {
 
778
                                set_errno(PR_GetError());
 
779
                                if (PR_GetError () == PR_PENDING_INTERRUPT_ERROR)
 
780
                                        w = 0;
 
781
                        } else if (res == 0) {
 
782
#ifdef ETIMEDOUT
 
783
                                errno = ETIMEDOUT;
 
784
#else
 
785
                                errno = EIO;
 
786
#endif
 
787
                        } else if (pollfds[1].out_flags == PR_POLL_READ) {
 
788
                                errno = EINTR;
 
789
                        } else {
 
790
                                do {
 
791
                                        w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
 
792
                                        if (w == -1)
 
793
                                                set_errno (PR_GetError ());
 
794
                                } while (w == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
 
795
                                
 
796
                                if (w == -1) {
 
797
                                        if (PR_GetError () == PR_IO_PENDING_ERROR ||
 
798
                                            PR_GetError () == PR_WOULD_BLOCK_ERROR)
 
799
                                                w = 0;
 
800
                                } else
 
801
                                        written += w;
 
802
                        }
 
803
                } while (w != -1 && written < n);
 
804
                
 
805
                /* restore O_NONBLOCK options */
 
806
                error = errno;
 
807
                sockopts.option = PR_SockOpt_Nonblocking;
 
808
                sockopts.value.non_blocking = nonblock;
 
809
                PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
 
810
                errno = error;
 
811
        }
 
812
        
 
813
        if (w == -1)
 
814
                return -1;
 
815
        
 
816
        return written;
 
817
}
 
818
 
 
819
static int
 
820
stream_flush (CamelStream *stream)
 
821
{
 
822
        /*return PR_Sync (((CamelTcpStreamSSL *)stream)->priv->sockfd);*/
 
823
        return 0;
 
824
}
 
825
 
 
826
static int
 
827
stream_close (CamelStream *stream)
 
828
{
 
829
        if (((CamelTcpStreamSSL *)stream)->priv->sockfd == NULL) {
 
830
                errno = EINVAL;
 
831
                return -1;
 
832
        }
 
833
 
 
834
        if (PR_Close (((CamelTcpStreamSSL *)stream)->priv->sockfd) == PR_FAILURE)
 
835
                return -1;
 
836
        
 
837
        ((CamelTcpStreamSSL *)stream)->priv->sockfd = NULL;
 
838
        
 
839
        return 0;
 
840
}
 
841
 
 
842
#if 0
 
843
/* Since this is default implementation, let NSS handle it. */
 
844
static SECStatus
 
845
ssl_get_client_auth (void *data, PRFileDesc *sockfd,
 
846
                     struct CERTDistNamesStr *caNames,
 
847
                     struct CERTCertificateStr **pRetCert,
 
848
                     struct SECKEYPrivateKeyStr **pRetKey) 
 
849
{
 
850
        SECStatus status = SECFailure;
 
851
        SECKEYPrivateKey *privkey;
 
852
        CERTCertificate *cert;
 
853
        void *proto_win;
 
854
        
 
855
        proto_win = SSL_RevealPinArg (sockfd);
 
856
        
 
857
        if ((char *) data) {
 
858
                cert = PK11_FindCertFromNickname ((char *) data, proto_win);
 
859
                if (cert) {
 
860
                        privKey = PK11_FindKeyByAnyCert (cert, proto_win);
 
861
                        if (privkey) {
 
862
                                status = SECSuccess;
 
863
                        } else {
 
864
                                CERT_DestroyCertificate (cert);
 
865
                        }
 
866
                }
 
867
        } else {
 
868
                /* no nickname given, automatically find the right cert */
 
869
                CERTCertNicknames *names;
 
870
                int i;
 
871
                
 
872
                names = CERT_GetCertNicknames (CERT_GetDefaultCertDB (), 
 
873
                                               SEC_CERT_NICKNAMES_USER,
 
874
                                               proto_win);
 
875
                
 
876
                if (names != NULL) {
 
877
                        for (i = 0; i < names->numnicknames; i++) {
 
878
                                cert = PK11_FindCertFromNickname (names->nicknames[i], 
 
879
                                                                  proto_win);
 
880
                                if (!cert)
 
881
                                        continue;
 
882
                                
 
883
                                /* Only check unexpired certs */
 
884
                                if (CERT_CheckCertValidTimes (cert, PR_Now (), PR_FALSE) != secCertTimeValid) {
 
885
                                        CERT_DestroyCertificate (cert);
 
886
                                        continue;
 
887
                                }
 
888
                                
 
889
                                status = NSS_CmpCertChainWCANames (cert, caNames);
 
890
                                if (status == SECSuccess) {
 
891
                                        privkey = PK11_FindKeyByAnyCert (cert, proto_win);
 
892
                                        if (privkey)
 
893
                                                break;
 
894
                                        
 
895
                                        status = SECFailure;
 
896
                                        break;
 
897
                                }
 
898
                                
 
899
                                CERT_FreeNicknames (names);
 
900
                        }
 
901
                }
 
902
        }
 
903
        
 
904
        if (status == SECSuccess) {
 
905
                *pRetCert = cert;
 
906
                *pRetKey  = privkey;
 
907
        }
 
908
        
 
909
        return status;
 
910
}
 
911
#endif
 
912
 
 
913
#if 0
 
914
/* Since this is the default NSS implementation, no need for us to use this. */
 
915
static SECStatus
 
916
ssl_auth_cert (void *data, PRFileDesc *sockfd, PRBool checksig, PRBool is_server)
 
917
{
 
918
        CERTCertificate *cert;
 
919
        SECStatus status;
 
920
        void *pinarg;
 
921
        char *host;
 
922
        
 
923
        cert = SSL_PeerCertificate (sockfd);
 
924
        pinarg = SSL_RevealPinArg (sockfd);
 
925
        status = CERT_VerifyCertNow ((CERTCertDBHandle *)data, cert,
 
926
                                     checksig, certUsageSSLClient, pinarg);
 
927
        
 
928
        if (status != SECSuccess)
 
929
                return SECFailure;
 
930
        
 
931
        /* Certificate is OK.  Since this is the client side of an SSL
 
932
         * connection, we need to verify that the name field in the cert
 
933
         * matches the desired hostname.  This is our defense against
 
934
         * man-in-the-middle attacks.
 
935
         */
 
936
        
 
937
        /* SSL_RevealURL returns a hostname, not a URL. */
 
938
        host = SSL_RevealURL (sockfd);
 
939
        
 
940
        if (host && *host) {
 
941
                status = CERT_VerifyCertName (cert, host);
 
942
        } else {
 
943
                PR_SetError (SSL_ERROR_BAD_CERT_DOMAIN, 0);
 
944
                status = SECFailure;
 
945
        }
 
946
        
 
947
        if (host)
 
948
                PR_Free (host);
 
949
        
 
950
        return secStatus;
 
951
}
 
952
#endif
 
953
 
 
954
CamelCert *camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert);
 
955
CamelCert *camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert);
 
956
void camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert);
 
957
 
 
958
static char *
 
959
cert_fingerprint(CERTCertificate *cert)
 
960
{
 
961
        unsigned char md5sum[16], fingerprint[50], *f;
 
962
        int i;
 
963
        const char tohex[16] = "0123456789abcdef";
 
964
 
 
965
        md5_get_digest ((const gchar *) cert->derCert.data, cert->derCert.len, md5sum);
 
966
        for (i=0,f = fingerprint; i<16; i++) {
 
967
                unsigned int c = md5sum[i];
 
968
 
 
969
                *f++ = tohex[(c >> 4) & 0xf];
 
970
                *f++ = tohex[c & 0xf];
 
971
#ifndef G_OS_WIN32
 
972
                *f++ = ':';
 
973
#else
 
974
                /* The fingerprint is used as a file name, can't have
 
975
                 * colons in file names. Use underscore instead.
 
976
                 */
 
977
                *f++ = '_';
 
978
#endif
 
979
        }
 
980
 
 
981
        fingerprint[47] = 0;
 
982
 
 
983
        return g_strdup((gchar*) fingerprint);
 
984
}
 
985
 
 
986
/* lookup a cert uses fingerprint to index an on-disk file */
 
987
CamelCert *
 
988
camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert)
 
989
{
 
990
        char *fingerprint, *path;
 
991
        CamelCert *ccert;
 
992
        struct stat st;
 
993
        size_t nread;
 
994
        ssize_t n;
 
995
        int fd;
 
996
        
 
997
        fingerprint = cert_fingerprint (cert);
 
998
        ccert = camel_certdb_get_cert (certdb, fingerprint);
 
999
        if (ccert == NULL) {
 
1000
                g_free (fingerprint);
 
1001
                return ccert;
 
1002
        }
 
1003
        
 
1004
        if (ccert->rawcert == NULL) {
 
1005
#ifndef G_OS_WIN32
 
1006
                path = g_strdup_printf ("%s/.camel_certs/%s", getenv ("HOME"), fingerprint);
 
1007
#else
 
1008
                path = g_build_filename (g_get_home_dir (), ".camel_certs", fingerprint, NULL);
 
1009
#endif
 
1010
                if (g_stat (path, &st) == -1
 
1011
                    || (fd = g_open (path, O_RDONLY | O_BINARY, 0)) == -1) {
 
1012
                        g_warning ("could not load cert %s: %s", path, strerror (errno));
 
1013
                        g_free (fingerprint);
 
1014
                        g_free (path);
 
1015
                        camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
 
1016
                        camel_certdb_touch (certdb);
 
1017
                        
 
1018
                        return ccert;
 
1019
                }
 
1020
                g_free(path);
 
1021
                
 
1022
                ccert->rawcert = g_byte_array_new ();
 
1023
                g_byte_array_set_size (ccert->rawcert, st.st_size);
 
1024
                
 
1025
                nread = 0;
 
1026
                do {
 
1027
                        do {
 
1028
                                n = read (fd, ccert->rawcert->data + nread, st.st_size - nread);
 
1029
                        } while (n == -1 && errno == EINTR);
 
1030
                        
 
1031
                        if (n > 0)
 
1032
                                nread += n;
 
1033
                } while (nread < st.st_size && n != -1);
 
1034
                
 
1035
                close (fd);
 
1036
                
 
1037
                if (nread != st.st_size) {
 
1038
                        /* g_warning ("cert size read truncated %s: %u != %ld", path, nread, st.st_size); */
 
1039
                        g_byte_array_free(ccert->rawcert, TRUE);
 
1040
                        ccert->rawcert = NULL;
 
1041
                        g_free(fingerprint);
 
1042
                        camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
 
1043
                        camel_certdb_touch(certdb);
 
1044
 
 
1045
                        return ccert;
 
1046
                }
 
1047
        }
 
1048
 
 
1049
        g_free(fingerprint);
 
1050
        if (ccert->rawcert->len != cert->derCert.len
 
1051
            || memcmp(ccert->rawcert->data, cert->derCert.data, cert->derCert.len) != 0) {
 
1052
                g_warning("rawcert != derCer");
 
1053
                camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
 
1054
                camel_certdb_touch(certdb);
 
1055
        }
 
1056
 
 
1057
        return ccert;
 
1058
}
 
1059
 
 
1060
/* add a cert to the certdb */
 
1061
CamelCert *
 
1062
camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert)
 
1063
{
 
1064
        CamelCert *ccert;
 
1065
        char *fingerprint;
 
1066
 
 
1067
        fingerprint = cert_fingerprint(cert);
 
1068
 
 
1069
        ccert = camel_certdb_cert_new(certdb);
 
1070
        camel_cert_set_issuer(certdb, ccert, CERT_NameToAscii(&cert->issuer));
 
1071
        camel_cert_set_subject(certdb, ccert, CERT_NameToAscii(&cert->subject));
 
1072
        /* hostname is set in caller */
 
1073
        /*camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);*/
 
1074
        camel_cert_set_fingerprint(certdb, ccert, fingerprint);
 
1075
        camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
 
1076
        g_free(fingerprint);
 
1077
 
 
1078
        camel_certdb_nss_cert_set(certdb, ccert, cert);
 
1079
 
 
1080
        camel_certdb_add(certdb, ccert);
 
1081
 
 
1082
        return ccert;
 
1083
}
 
1084
 
 
1085
/* set the 'raw' cert (& save it) */
 
1086
void
 
1087
camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert)
 
1088
{
 
1089
        char *dir, *path, *fingerprint;
 
1090
        CamelStream *stream;
 
1091
        struct stat st;
 
1092
        
 
1093
        fingerprint = ccert->fingerprint;
 
1094
        
 
1095
        if (ccert->rawcert == NULL)
 
1096
                ccert->rawcert = g_byte_array_new ();
 
1097
        
 
1098
        g_byte_array_set_size (ccert->rawcert, cert->derCert.len);
 
1099
        memcpy (ccert->rawcert->data, cert->derCert.data, cert->derCert.len);
 
1100
        
 
1101
#ifndef G_OS_WIN32
 
1102
        dir = g_strdup_printf ("%s/.camel_certs", getenv ("HOME"));
 
1103
#else
 
1104
        dir = g_build_filename (g_get_home_dir (), ".camel_certs", NULL);
 
1105
#endif
 
1106
        if (g_stat (dir, &st) == -1 && g_mkdir (dir, 0700) == -1) {
 
1107
                g_warning ("Could not create cert directory '%s': %s", dir, strerror (errno));
 
1108
                g_free (dir);
 
1109
                return;
 
1110
        }
 
1111
        
 
1112
        path = g_strdup_printf ("%s/%s", dir, fingerprint);
 
1113
        g_free (dir);
 
1114
        
 
1115
        stream = camel_stream_fs_new_with_name (path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 
1116
        if (stream != NULL) {
 
1117
                if (camel_stream_write (stream, (const char *) ccert->rawcert->data, ccert->rawcert->len) == -1) {
 
1118
                        g_warning ("Could not save cert: %s: %s", path, strerror (errno));
 
1119
                        g_unlink (path);
 
1120
                }
 
1121
                camel_stream_close (stream);
 
1122
                camel_object_unref (stream);
 
1123
        } else {
 
1124
                g_warning ("Could not save cert: %s: %s", path, strerror (errno));
 
1125
        }
 
1126
        
 
1127
        g_free (path);
 
1128
}
 
1129
 
 
1130
 
 
1131
#if 0
 
1132
/* used by the mozilla-like code below */
 
1133
static char *
 
1134
get_nickname(CERTCertificate *cert)
 
1135
{
 
1136
        char *server, *nick = NULL;
 
1137
        int i;
 
1138
        PRBool status = PR_TRUE;
 
1139
 
 
1140
        server = CERT_GetCommonName(&cert->subject);
 
1141
        if (server == NULL)
 
1142
                return NULL;
 
1143
 
 
1144
        for (i=1;status == PR_TRUE;i++) {
 
1145
                if (nick) {
 
1146
                        g_free(nick);
 
1147
                        nick = g_strdup_printf("%s #%d", server, i);
 
1148
                } else {
 
1149
                        nick = g_strdup(server);
 
1150
                }
 
1151
                status = SEC_CertNicknameConflict(server, &cert->derSubject, cert->dbhandle);
 
1152
        }
 
1153
 
 
1154
        return nick;
 
1155
}
 
1156
#endif
 
1157
 
 
1158
static SECStatus
 
1159
ssl_bad_cert (void *data, PRFileDesc *sockfd)
 
1160
{
 
1161
        gboolean accept;
 
1162
        CamelCertDB *certdb = NULL;
 
1163
        CamelCert *ccert = NULL;
 
1164
        char *prompt, *cert_str, *fingerprint;
 
1165
        CamelTcpStreamSSL *ssl;
 
1166
        CERTCertificate *cert;
 
1167
        SECStatus status = SECFailure;
 
1168
        struct _CamelTcpStreamSSLPrivate *priv;
 
1169
 
 
1170
        g_return_val_if_fail (data != NULL, SECFailure);
 
1171
        g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure);
 
1172
 
 
1173
        ssl = data;
 
1174
        priv = ssl->priv;
 
1175
        
 
1176
        cert = SSL_PeerCertificate (sockfd);
 
1177
        if (cert == NULL)
 
1178
                return SECFailure;
 
1179
 
 
1180
        certdb = camel_certdb_get_default();
 
1181
        ccert = camel_certdb_nss_cert_get(certdb, cert);
 
1182
        if (ccert == NULL) {
 
1183
                ccert = camel_certdb_nss_cert_add(certdb, cert);
 
1184
                camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);
 
1185
        }
 
1186
 
 
1187
        if (ccert->trust == CAMEL_CERT_TRUST_UNKNOWN) {
 
1188
                status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
 
1189
                fingerprint = cert_fingerprint(cert);
 
1190
                cert_str = g_strdup_printf (_("Issuer:            %s\n"
 
1191
                                              "Subject:           %s\n"
 
1192
                                              "Fingerprint:       %s\n"
 
1193
                                              "Signature:         %s"),
 
1194
                                            CERT_NameToAscii (&cert->issuer),
 
1195
                                            CERT_NameToAscii (&cert->subject),
 
1196
                                            fingerprint, status == SECSuccess?_("GOOD"):_("BAD"));
 
1197
                g_free(fingerprint);
 
1198
 
 
1199
                /* construct our user prompt */
 
1200
                prompt = g_strdup_printf (_("SSL Certificate check for %s:\n\n%s\n\nDo you wish to accept?"),
 
1201
                                          ssl->priv->expected_host, cert_str);
 
1202
                g_free (cert_str);
 
1203
        
 
1204
                /* query the user to find out if we want to accept this certificate */
 
1205
                accept = camel_session_alert_user_with_id (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, CAMEL_EXCEPTION_SERVICE_CERTIFICATE, prompt, TRUE, priv->service);
 
1206
                g_free(prompt);
 
1207
                if (accept) {
 
1208
                        camel_certdb_nss_cert_set(certdb, ccert, cert);
 
1209
                        camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_FULLY);
 
1210
                        camel_certdb_touch(certdb);
 
1211
                }
 
1212
        } else {
 
1213
                accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
 
1214
        }
 
1215
 
 
1216
        camel_certdb_cert_unref(certdb, ccert);
 
1217
        camel_object_unref(certdb);
 
1218
 
 
1219
        priv->accepted = accept;
 
1220
 
 
1221
        return accept ? SECSuccess : SECFailure;
 
1222
 
 
1223
#if 0
 
1224
        int i, error;
 
1225
        CERTCertTrust trust;
 
1226
        SECItem *certs[1];
 
1227
        int go = 1;
 
1228
        char *host, *nick;
 
1229
 
 
1230
        error = PR_GetError();
 
1231
 
 
1232
        /* This code is basically what mozilla does - however it doesn't seem to work here
 
1233
           very reliably :-/ */
 
1234
        while (go && status != SECSuccess) {
 
1235
                char *prompt = NULL;
 
1236
 
 
1237
                printf("looping, error '%d'\n", error);
 
1238
 
 
1239
                switch(error) {
 
1240
                case SEC_ERROR_UNKNOWN_ISSUER:
 
1241
                case SEC_ERROR_CA_CERT_INVALID:
 
1242
                case SEC_ERROR_UNTRUSTED_ISSUER:
 
1243
                case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
 
1244
                        /* add certificate */
 
1245
                        printf("unknown issuer, adding ... \n");
 
1246
                        prompt = g_strdup_printf(_("Certificate problem: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
 
1247
 
 
1248
                        CamelService *service = NULL; /* TODO: Is there a CamelService that we can use? */
 
1249
                        if (camel_session_alert_user_with_id(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, CAMEL_EXCEPTION_SERVICE_CERTIFICATE, prompt, TRUE, service)) {
 
1250
 
 
1251
                                nick = get_nickname(cert);
 
1252
                                if (NULL == nick) {
 
1253
                                        g_free(prompt);
 
1254
                                        status = SECFailure;
 
1255
                                        break;
 
1256
                                }
 
1257
 
 
1258
                                printf("adding cert '%s'\n", nick);
 
1259
 
 
1260
                                if (!cert->trust) {
 
1261
                                        cert->trust = (CERTCertTrust*)PORT_ArenaZAlloc(cert->arena, sizeof(CERTCertTrust));
 
1262
                                        CERT_DecodeTrustString(cert->trust, "P");
 
1263
                                }
 
1264
                
 
1265
                                certs[0] = &cert->derCert;
 
1266
                                /*CERT_ImportCerts (cert->dbhandle, certUsageSSLServer, 1, certs, NULL, TRUE, FALSE, nick);*/
 
1267
                                CERT_ImportCerts(cert->dbhandle, certUsageUserCertImport, 1, certs, NULL, TRUE, FALSE, nick);
 
1268
                                g_free(nick);
 
1269
 
 
1270
                                printf(" cert type %08x\n", cert->nsCertType);
 
1271
 
 
1272
                                memset((void*)&trust, 0, sizeof(trust));
 
1273
                                if (CERT_GetCertTrust(cert, &trust) != SECSuccess) {
 
1274
                                        CERT_DecodeTrustString(&trust, "P");
 
1275
                                }
 
1276
                                trust.sslFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED;
 
1277
                                if (CERT_ChangeCertTrust(cert->dbhandle, cert, &trust) != SECSuccess) {
 
1278
                                        printf("couldn't change cert trust?\n");
 
1279
                                }
 
1280
 
 
1281
                                /*status = SECSuccess;*/
 
1282
#if 1
 
1283
                                /* re-verify? */
 
1284
                                status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLServer, NULL);
 
1285
                                error = PR_GetError();
 
1286
                                printf("re-verify status %d, error %d\n", status, error);
 
1287
#endif
 
1288
 
 
1289
                                printf(" cert type %08x\n", cert->nsCertType);
 
1290
                        } else {
 
1291
                                printf("failed/cancelled\n");
 
1292
                                go = 0;
 
1293
                        }
 
1294
 
 
1295
                        break;
 
1296
                case SSL_ERROR_BAD_CERT_DOMAIN:
 
1297
                        printf("bad domain\n");
 
1298
 
 
1299
                        prompt = g_strdup_printf(_("Bad certificate domain: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
 
1300
 
 
1301
                        CamelService *service = NULL; /* TODO: Is there a CamelService that we can use? */
 
1302
                        if (camel_session_alert_user_with_id (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, CAMEL_EXCEPTION_SERVICE_CERTIFICATE, prompt, TRUE, service)) {
 
1303
                                host = SSL_RevealURL(sockfd);
 
1304
                                status = CERT_AddOKDomainName(cert, host);
 
1305
                                printf("add ok domain name : %s\n", status == SECFailure?"fail":"ok");
 
1306
                                error = PR_GetError();
 
1307
                                if (status == SECFailure)
 
1308
                                        go = 0;
 
1309
                        } else {
 
1310
                                go = 0;
 
1311
                        }
 
1312
 
 
1313
                        break;
 
1314
                        
 
1315
                case SEC_ERROR_EXPIRED_CERTIFICATE:
 
1316
                        printf("expired\n");
 
1317
 
 
1318
                        prompt = g_strdup_printf(_("Certificate expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
 
1319
 
 
1320
                        CamelService *service = NULL; /* TODO: Is there a CamelService that we can use? */
 
1321
                        if (camel_session_alert_user_with_id(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, CAMEL_EXCEPTION_SERVICE_CERTIFICATE, prompt, TRUE, service)) {
 
1322
                                cert->timeOK = PR_TRUE;
 
1323
                                status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
 
1324
                                error = PR_GetError();
 
1325
                                if (status == SECFailure)
 
1326
                                        go = 0;
 
1327
                        } else {
 
1328
                                go = 0;
 
1329
                        }
 
1330
 
 
1331
                        break;
 
1332
 
 
1333
                case SEC_ERROR_CRL_EXPIRED:
 
1334
                        printf("crl expired\n");
 
1335
 
 
1336
                        prompt = g_strdup_printf(_("Certificate revocation list expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
 
1337
 
 
1338
                        CamelService *service = NULL; /* TODO: Is there a CamelService that we can use? */
 
1339
                        if (camel_session_alert_user_with_id(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, CAMEL_EXCEPTION_SERVICE_CERTIFICATE, prompt, TRUE, service)) {
 
1340
                                host = SSL_RevealURL(sockfd);
 
1341
                                status = CERT_AddOKDomainName(cert, host);
 
1342
                        }
 
1343
 
 
1344
                        go = 0;
 
1345
                        break;
 
1346
 
 
1347
                default:
 
1348
                        printf("generic error\n");
 
1349
                        go = 0;
 
1350
                        break;
 
1351
                }
 
1352
 
 
1353
                g_free(prompt);
 
1354
        }
 
1355
 
 
1356
        CERT_DestroyCertificate(cert);
 
1357
 
 
1358
        return status;
 
1359
#endif
 
1360
}
 
1361
 
 
1362
static PRFileDesc *
 
1363
enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd)
 
1364
{
 
1365
        PRFileDesc *ssl_fd;
 
1366
        
 
1367
        ssl_fd = SSL_ImportFD (NULL, fd ? fd : ssl->priv->sockfd);
 
1368
        if (!ssl_fd)
 
1369
                return NULL;
 
1370
        
 
1371
        SSL_OptionSet (ssl_fd, SSL_SECURITY, PR_TRUE);
 
1372
 
 
1373
        if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL2)
 
1374
                SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_TRUE);
 
1375
        else
 
1376
                SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_FALSE);
 
1377
 
 
1378
        if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
 
1379
                SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_TRUE);
 
1380
        else
 
1381
                SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_FALSE);
 
1382
 
 
1383
        if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
 
1384
                SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_TRUE);
 
1385
        else
 
1386
                SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_FALSE);
 
1387
 
 
1388
        SSL_SetURL (ssl_fd, ssl->priv->expected_host);
 
1389
        
 
1390
        /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *) certNickname);*/
 
1391
        /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, (void *) CERT_GetDefaultCertDB ());*/
 
1392
        SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl);
 
1393
        
 
1394
        ssl->priv->ssl_mode = TRUE;
 
1395
        
 
1396
        return ssl_fd;
 
1397
}
 
1398
 
 
1399
static int
 
1400
sockaddr_to_praddr(struct sockaddr *s, int len, PRNetAddr *addr)
 
1401
{
 
1402
        /* We assume the ip addresses are the same size - they have to be anyway.
 
1403
           We could probably just use memcpy *shrug* */
 
1404
 
 
1405
        memset(addr, 0, sizeof(*addr));
 
1406
 
 
1407
        if (s->sa_family == AF_INET) {
 
1408
                struct sockaddr_in *sin = (struct sockaddr_in *)s;
 
1409
 
 
1410
                if (len < sizeof(*sin))
 
1411
                        return -1;
 
1412
 
 
1413
                addr->inet.family = PR_AF_INET;
 
1414
                addr->inet.port = sin->sin_port;
 
1415
                memcpy(&addr->inet.ip, &sin->sin_addr, sizeof(addr->inet.ip));
 
1416
 
 
1417
                return 0;
 
1418
        }
 
1419
#ifdef ENABLE_IPv6
 
1420
        else if (s->sa_family == PR_AF_INET6) {
 
1421
                struct sockaddr_in6 *sin = (struct sockaddr_in6 *)s;
 
1422
 
 
1423
                if (len < sizeof(*sin))
 
1424
                        return -1;
 
1425
 
 
1426
                addr->ipv6.family = PR_AF_INET6;
 
1427
                addr->ipv6.port = sin->sin6_port;
 
1428
                addr->ipv6.flowinfo = sin->sin6_flowinfo;
 
1429
                memcpy(&addr->ipv6.ip, &sin->sin6_addr, sizeof(addr->ipv6.ip));
 
1430
                addr->ipv6.scope_id = sin->sin6_scope_id;
 
1431
 
 
1432
                return 0;
 
1433
        }
 
1434
#endif
 
1435
 
 
1436
        return -1;
 
1437
}
 
1438
 
 
1439
static int
 
1440
socket_connect(CamelTcpStream *stream, struct addrinfo *host)
 
1441
{
 
1442
        CamelTcpStreamSSL *ssl = CAMEL_TCP_STREAM_SSL (stream);
 
1443
        PRNetAddr netaddr;
 
1444
        PRFileDesc *fd, *cancel_fd;
 
1445
 
 
1446
        if (sockaddr_to_praddr(host->ai_addr, host->ai_addrlen, &netaddr) != 0) {
 
1447
                errno = EINVAL;
 
1448
                return -1;
 
1449
        }
 
1450
        
 
1451
        fd = PR_OpenTCPSocket(netaddr.raw.family);
 
1452
        if (fd == NULL) {
 
1453
                set_errno (PR_GetError ());
 
1454
                return -1;
 
1455
        }
 
1456
        
 
1457
        if (ssl->priv->ssl_mode) {
 
1458
                PRFileDesc *ssl_fd;
 
1459
                
 
1460
                ssl_fd = enable_ssl (ssl, fd);
 
1461
                if (ssl_fd == NULL) {
 
1462
                        int errnosave;
 
1463
                        
 
1464
                        set_errno (PR_GetError ());
 
1465
                        errnosave = errno;
 
1466
                        PR_Close (fd);
 
1467
                        errno = errnosave;
 
1468
                        
 
1469
                        return -1;
 
1470
                }
 
1471
                
 
1472
                fd = ssl_fd;
 
1473
        }
 
1474
 
 
1475
        cancel_fd = camel_operation_cancel_prfd(NULL);
 
1476
 
 
1477
        if (PR_Connect (fd, &netaddr, cancel_fd?0:(CONNECT_TIMEOUT*1000)) == PR_FAILURE) {
 
1478
                int errnosave;
 
1479
                
 
1480
                set_errno (PR_GetError ());
 
1481
                if (PR_GetError () == PR_IN_PROGRESS_ERROR ||
 
1482
                    (cancel_fd && (PR_GetError () == PR_CONNECT_TIMEOUT_ERROR ||
 
1483
                                   PR_GetError () == PR_IO_TIMEOUT_ERROR))) {
 
1484
                        gboolean connected = FALSE;
 
1485
                        PRPollDesc poll[2];
 
1486
 
 
1487
                        poll[0].fd = fd;
 
1488
                        poll[0].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
 
1489
                        poll[1].fd = cancel_fd;
 
1490
                        poll[1].in_flags = PR_POLL_READ;
 
1491
                        
 
1492
                        do {
 
1493
                                poll[0].out_flags = 0;
 
1494
                                poll[1].out_flags = 0;
 
1495
 
 
1496
                                if (PR_Poll (poll, cancel_fd?2:1, (CONNECT_TIMEOUT*1000)) == PR_FAILURE) {
 
1497
                                        set_errno (PR_GetError ());
 
1498
                                        goto exception;
 
1499
                                }
 
1500
                                
 
1501
                                if (poll[1].out_flags == PR_POLL_READ) {
 
1502
                                        errno = EINTR;
 
1503
                                        goto exception;
 
1504
                                }
 
1505
 
 
1506
                                if (PR_ConnectContinue(fd, poll[0].out_flags) == PR_FAILURE) {
 
1507
                                        set_errno (PR_GetError ());
 
1508
                                        if (PR_GetError () != PR_IN_PROGRESS_ERROR)
 
1509
                                                goto exception;
 
1510
                                } else {
 
1511
                                        connected = TRUE;
 
1512
                                }
 
1513
                        } while (!connected);
 
1514
                } else {
 
1515
                exception:
 
1516
                        errnosave = errno;
 
1517
                        PR_Close (fd);
 
1518
                        ssl->priv->sockfd = NULL;
 
1519
                        errno = errnosave;
 
1520
                        
 
1521
                        return -1;
 
1522
                }
 
1523
                
 
1524
                errno = 0;
 
1525
        }
 
1526
        
 
1527
        ssl->priv->sockfd = fd;
 
1528
 
 
1529
        return 0;
 
1530
}
 
1531
 
 
1532
static int
 
1533
stream_connect(CamelTcpStream *stream, struct addrinfo *host)
 
1534
{
 
1535
        while (host) {
 
1536
                if (socket_connect(stream, host) == 0)
 
1537
                        return 0;
 
1538
                host = host->ai_next;
 
1539
        }
 
1540
 
 
1541
        return -1;
 
1542
}
 
1543
 
 
1544
static int
 
1545
stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data)
 
1546
{
 
1547
        PRSocketOptionData sodata;
 
1548
        
 
1549
        memset ((void *) &sodata, 0, sizeof (sodata));
 
1550
        memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
 
1551
        
 
1552
        if (PR_GetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
 
1553
                return -1;
 
1554
        
 
1555
        memcpy ((void *) data, (void *) &sodata, sizeof (CamelSockOptData));
 
1556
        
 
1557
        return 0;
 
1558
}
 
1559
 
 
1560
static int
 
1561
stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data)
 
1562
{
 
1563
        PRSocketOptionData sodata;
 
1564
        
 
1565
        memset ((void *) &sodata, 0, sizeof (sodata));
 
1566
        memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
 
1567
        
 
1568
        if (PR_SetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
 
1569
                return -1;
 
1570
        
 
1571
        return 0;
 
1572
}
 
1573
 
 
1574
static struct sockaddr *
 
1575
sockaddr_from_praddr(PRNetAddr *addr, socklen_t *len)
 
1576
{
 
1577
        /* We assume the ip addresses are the same size - they have to be anyway */
 
1578
 
 
1579
        if (addr->raw.family == PR_AF_INET) {
 
1580
                struct sockaddr_in *sin = g_malloc0(sizeof(*sin));
 
1581
 
 
1582
                sin->sin_family = AF_INET;
 
1583
                sin->sin_port = addr->inet.port;
 
1584
                memcpy(&sin->sin_addr, &addr->inet.ip, sizeof(sin->sin_addr));
 
1585
                *len = sizeof(*sin);
 
1586
 
 
1587
                return (struct sockaddr *)sin;
 
1588
        }
 
1589
#ifdef ENABLE_IPv6
 
1590
        else if (addr->raw.family == PR_AF_INET6) {
 
1591
                struct sockaddr_in6 *sin = g_malloc0(sizeof(*sin));
 
1592
 
 
1593
                sin->sin6_family = AF_INET6;
 
1594
                sin->sin6_port = addr->ipv6.port;
 
1595
                sin->sin6_flowinfo = addr->ipv6.flowinfo;
 
1596
                memcpy(&sin->sin6_addr, &addr->ipv6.ip, sizeof(sin->sin6_addr));
 
1597
                sin->sin6_scope_id = addr->ipv6.scope_id;
 
1598
                *len = sizeof(*sin);
 
1599
 
 
1600
                return (struct sockaddr *)sin;          
 
1601
        }
 
1602
#endif
 
1603
 
 
1604
        return NULL;
 
1605
}
 
1606
 
 
1607
static struct sockaddr *
 
1608
stream_get_local_address(CamelTcpStream *stream, socklen_t *len)
 
1609
{
 
1610
        PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
 
1611
        PRNetAddr addr;
 
1612
        
 
1613
        if (PR_GetSockName(sockfd, &addr) != PR_SUCCESS)
 
1614
                return NULL;
 
1615
 
 
1616
        return sockaddr_from_praddr(&addr, len);
 
1617
}
 
1618
 
 
1619
static struct sockaddr *
 
1620
stream_get_remote_address (CamelTcpStream *stream, socklen_t *len)
 
1621
{
 
1622
        PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
 
1623
        PRNetAddr addr;
 
1624
        
 
1625
        if (PR_GetPeerName(sockfd, &addr) != PR_SUCCESS)
 
1626
                return NULL;
 
1627
 
 
1628
        return sockaddr_from_praddr(&addr, len);
 
1629
}
 
1630
 
 
1631
#endif /* HAVE_NSS */