Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-tcp-stream-ssl.c
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., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, 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 #define IO_TIMEOUT (PR_TicksPerSecond() * 4 * 60)
70 #define CONNECT_TIMEOUT (PR_TicksPerSecond () * 4 * 60)
71
72 static CamelTcpStreamClass *parent_class = NULL;
73
74 /* Returns the class for a CamelTcpStreamSSL */
75 #define CTSS_CLASS(so) CAMEL_TCP_STREAM_SSL_CLASS (CAMEL_OBJECT_GET_CLASS (so))
76
77 static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
78 static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
79 static int stream_flush  (CamelStream *stream);
80 static int stream_close  (CamelStream *stream);
81
82 static PRFileDesc *enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd);
83
84 static int stream_connect    (CamelTcpStream *stream, struct addrinfo *host);
85 static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
86 static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
87 static struct sockaddr *stream_get_local_address (CamelTcpStream *stream, socklen_t *len);
88 static struct sockaddr *stream_get_remote_address (CamelTcpStream *stream, socklen_t *len);
89
90 struct _CamelTcpStreamSSLPrivate {
91         PRFileDesc *sockfd;
92         
93         struct _CamelSession *session;
94         char *expected_host;
95         gboolean ssl_mode;
96         guint32 flags;
97 };
98
99 static void
100 camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *camel_tcp_stream_ssl_class)
101 {
102         CamelTcpStreamClass *camel_tcp_stream_class =
103                 CAMEL_TCP_STREAM_CLASS (camel_tcp_stream_ssl_class);
104         CamelStreamClass *camel_stream_class =
105                 CAMEL_STREAM_CLASS (camel_tcp_stream_ssl_class);
106         
107         parent_class = CAMEL_TCP_STREAM_CLASS (camel_type_get_global_classfuncs (camel_tcp_stream_get_type ()));
108         
109         /* virtual method overload */
110         camel_stream_class->read = stream_read;
111         camel_stream_class->write = stream_write;
112         camel_stream_class->flush = stream_flush;
113         camel_stream_class->close = stream_close;
114         
115         camel_tcp_stream_class->connect = stream_connect;
116         camel_tcp_stream_class->getsockopt = stream_getsockopt;
117         camel_tcp_stream_class->setsockopt = stream_setsockopt;
118         camel_tcp_stream_class->get_local_address  = stream_get_local_address;
119         camel_tcp_stream_class->get_remote_address = stream_get_remote_address;
120 }
121
122 static void
123 camel_tcp_stream_ssl_init (gpointer object, gpointer klass)
124 {
125         CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
126         
127         stream->priv = g_new0 (struct _CamelTcpStreamSSLPrivate, 1);
128 }
129
130 static void
131 camel_tcp_stream_ssl_finalize (CamelObject *object)
132 {
133         CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
134         
135         if (stream->priv->sockfd != NULL)
136                 PR_Close (stream->priv->sockfd);
137
138         if (stream->priv->session)
139                 camel_object_unref(stream->priv->session);
140
141         g_free (stream->priv->expected_host);
142         
143         g_free (stream->priv);
144 }
145
146
147 CamelType
148 camel_tcp_stream_ssl_get_type (void)
149 {
150         static CamelType type = CAMEL_INVALID_TYPE;
151         
152         if (type == CAMEL_INVALID_TYPE) {
153                 type = camel_type_register (camel_tcp_stream_get_type (),
154                                             "CamelTcpStreamSSL",
155                                             sizeof (CamelTcpStreamSSL),
156                                             sizeof (CamelTcpStreamSSLClass),
157                                             (CamelObjectClassInitFunc) camel_tcp_stream_ssl_class_init,
158                                             NULL,
159                                             (CamelObjectInitFunc) camel_tcp_stream_ssl_init,
160                                             (CamelObjectFinalizeFunc) camel_tcp_stream_ssl_finalize);
161         }
162         
163         return type;
164 }
165
166
167 /**
168  * camel_tcp_stream_ssl_new:
169  * @session: an active #CamelSession object
170  * @expected_host: host that the stream is expected to connect with
171  * @flags: a bitwise combination of any of
172  * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL2,
173  * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL3 or
174  * #CAMEL_TCP_STREAM_SSL_ENABLE_TLS
175  *
176  * Since the SSL certificate authenticator may need to prompt the
177  * user, a #CamelSession is needed. @expected_host is needed as a
178  * protection against an MITM attack.
179  *
180  * Returns a new #CamelTcpStreamSSL stream preset in SSL mode
181  **/
182 CamelStream *
183 camel_tcp_stream_ssl_new (CamelSession *session, const char *expected_host, guint32 flags)
184 {
185         CamelTcpStreamSSL *stream;
186
187         g_assert(CAMEL_IS_SESSION(session));
188
189         stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
190         
191         stream->priv->session = session;
192         camel_object_ref(session);
193         stream->priv->expected_host = g_strdup (expected_host);
194         stream->priv->ssl_mode = TRUE;
195         stream->priv->flags = flags;
196         
197         return CAMEL_STREAM (stream);
198 }
199
200
201 /**
202  * camel_tcp_stream_ssl_new_raw:
203  * @session: an active #CamelSession object
204  * @expected_host: host that the stream is expected to connect with
205  * @flags: a bitwise combination of any of
206  * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL2,
207  * #CAMEL_TCP_STREAM_SSL_ENABLE_SSL3 or
208  * #CAMEL_TCP_STREAM_SSL_ENABLE_TLS
209  *
210  * Since the SSL certificate authenticator may need to prompt the
211  * user, a CamelSession is needed. @expected_host is needed as a
212  * protection against an MITM attack.
213  *
214  * Returns a new #CamelTcpStreamSSL stream not yet toggled into SSL mode
215  **/
216 CamelStream *
217 camel_tcp_stream_ssl_new_raw (CamelSession *session, const char *expected_host, guint32 flags)
218 {
219         CamelTcpStreamSSL *stream;
220
221         g_assert(CAMEL_IS_SESSION(session));
222         
223         stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
224         
225         stream->priv->session = session;
226         camel_object_ref(session);
227         stream->priv->expected_host = g_strdup (expected_host);
228         stream->priv->ssl_mode = FALSE;
229         stream->priv->flags = flags;
230         
231         return CAMEL_STREAM (stream);
232 }
233
234
235 static void
236 set_errno (int code)
237 {
238         /* FIXME: this should handle more. */
239         switch (code) {
240         case PR_INVALID_ARGUMENT_ERROR:
241                 errno = EINVAL;
242                 break;
243         case PR_PENDING_INTERRUPT_ERROR:
244                 errno = EINTR;
245                 break;
246         case PR_IO_PENDING_ERROR:
247                 errno = EAGAIN;
248                 break;
249 #ifdef EWOULDBLOCK
250         case PR_WOULD_BLOCK_ERROR:
251                 errno = EWOULDBLOCK;
252                 break;
253 #endif
254 #ifdef EINPROGRESS
255         case PR_IN_PROGRESS_ERROR:
256                 errno = EINPROGRESS;
257                 break;
258 #endif
259 #ifdef EALREADY
260         case PR_ALREADY_INITIATED_ERROR:
261                 errno = EALREADY;
262                 break;
263 #endif
264 #ifdef EHOSTUNREACH
265         case PR_NETWORK_UNREACHABLE_ERROR:
266                 errno = EHOSTUNREACH;
267                 break;
268 #endif
269 #ifdef ECONNREFUSED
270         case PR_CONNECT_REFUSED_ERROR:
271                 errno = ECONNREFUSED;
272                 break;
273 #endif
274 #ifdef ETIMEDOUT
275         case PR_CONNECT_TIMEOUT_ERROR:
276         case PR_IO_TIMEOUT_ERROR:
277                 errno = ETIMEDOUT;
278                 break;
279 #endif
280 #ifdef ENOTCONN
281         case PR_NOT_CONNECTED_ERROR:
282                 errno = ENOTCONN;
283                 break;
284 #endif
285 #ifdef ECONNRESET
286         case PR_CONNECT_RESET_ERROR:
287                 errno = ECONNRESET;
288                 break;
289 #endif
290         case PR_IO_ERROR:
291         default:
292                 errno = EIO;
293                 break;
294         }
295 }
296
297
298 /**
299  * camel_tcp_stream_ssl_enable_ssl:
300  * @ssl: a #CamelTcpStreamSSL object
301  *
302  * Toggles an ssl-capable stream into ssl mode (if it isn't already).
303  *
304  * Returns %0 on success or %-1 on fail
305  **/
306 int
307 camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl)
308 {
309         PRFileDesc *fd;
310         
311         g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (ssl), -1);
312         
313         if (ssl->priv->sockfd && !ssl->priv->ssl_mode) {
314                 if (!(fd = enable_ssl (ssl, NULL))) {
315                         set_errno (PR_GetError ());
316                         return -1;
317                 }
318                 
319                 ssl->priv->sockfd = fd;
320                 
321                 if (SSL_ResetHandshake (fd, FALSE) == SECFailure) {
322                         set_errno (PR_GetError ());
323                         return -1;
324                 }
325                 
326                 if (SSL_ForceHandshake (fd) == -1) {
327                         set_errno (PR_GetError ());
328                         return -1;
329                 }
330         }
331         
332         ssl->priv->ssl_mode = TRUE;
333         
334         return 0;
335 }
336
337
338 static ssize_t
339 stream_read (CamelStream *stream, char *buffer, size_t n)
340 {
341         CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
342         PRFileDesc *cancel_fd;
343         ssize_t nread;
344         
345         if (camel_operation_cancel_check (NULL)) {
346                 errno = EINTR;
347                 return -1;
348         }
349         
350         cancel_fd = camel_operation_cancel_prfd (NULL);
351         if (cancel_fd == NULL) {
352                 do {
353                         nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
354                         if (nread == -1)
355                                 set_errno (PR_GetError ());
356                 } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
357                                          PR_GetError () == PR_IO_PENDING_ERROR ||
358                                          PR_GetError () == PR_WOULD_BLOCK_ERROR));
359         } else {
360                 PRSocketOptionData sockopts;
361                 PRPollDesc pollfds[2];
362                 gboolean nonblock;
363                 int error;
364                 
365                 /* get O_NONBLOCK options */
366                 sockopts.option = PR_SockOpt_Nonblocking;
367                 PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
368                 sockopts.option = PR_SockOpt_Nonblocking;
369                 nonblock = sockopts.value.non_blocking;
370                 sockopts.value.non_blocking = TRUE;
371                 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
372
373                 pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
374                 pollfds[0].in_flags = PR_POLL_READ;
375                 pollfds[1].fd = cancel_fd;
376                 pollfds[1].in_flags = PR_POLL_READ;
377                 
378                 do {
379                         PRInt32 res;
380
381                         pollfds[0].out_flags = 0;
382                         pollfds[1].out_flags = 0;
383                         nread = -1;
384
385                         res = PR_Poll(pollfds, 2, IO_TIMEOUT);
386                         if (res == -1)
387                                 set_errno(PR_GetError());
388                         else if (res == 0) {
389 #ifdef ETIMEDOUT
390                                 errno = ETIMEDOUT;
391 #else
392                                 errno = EIO;
393 #endif
394                         } else if (pollfds[1].out_flags == PR_POLL_READ) {
395                                 errno = EINTR;
396                                 goto failed;
397                         } else {
398                                 do {
399                                         nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
400                                         if (nread == -1)
401                                                 set_errno (PR_GetError ());
402                                 } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
403                         }
404                 } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
405                                          PR_GetError () == PR_IO_PENDING_ERROR ||
406                                          PR_GetError () == PR_WOULD_BLOCK_ERROR));
407                 
408                 /* restore O_NONBLOCK options */
409         failed:
410                 error = errno;
411                 sockopts.option = PR_SockOpt_Nonblocking;
412                 sockopts.value.non_blocking = nonblock;
413                 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
414                 errno = error;
415         }
416         
417         return nread;
418 }
419
420 static ssize_t
421 stream_write (CamelStream *stream, const char *buffer, size_t n)
422 {
423         CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
424         ssize_t w, written = 0;
425         PRFileDesc *cancel_fd;
426         
427         if (camel_operation_cancel_check (NULL)) {
428                 errno = EINTR;
429                 return -1;
430         }
431         
432         cancel_fd = camel_operation_cancel_prfd (NULL);
433         if (cancel_fd == NULL) {
434                 do {
435                         do {
436                                 w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
437                                 if (w == -1)
438                                         set_errno (PR_GetError ());
439                         } while (w == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
440                                              PR_GetError () == PR_IO_PENDING_ERROR ||
441                                              PR_GetError () == PR_WOULD_BLOCK_ERROR));
442                         
443                         if (w > 0)
444                                 written += w;
445                 } while (w != -1 && written < n);
446         } else {
447                 PRSocketOptionData sockopts;
448                 PRPollDesc pollfds[2];
449                 gboolean nonblock;
450                 int error;
451                 
452                 /* get O_NONBLOCK options */
453                 sockopts.option = PR_SockOpt_Nonblocking;
454                 PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
455                 sockopts.option = PR_SockOpt_Nonblocking;
456                 nonblock = sockopts.value.non_blocking;
457                 sockopts.value.non_blocking = TRUE;
458                 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
459                 
460                 pollfds[0].fd = tcp_stream_ssl->priv->sockfd;
461                 pollfds[0].in_flags = PR_POLL_WRITE;
462                 pollfds[1].fd = cancel_fd;
463                 pollfds[1].in_flags = PR_POLL_READ;
464                 
465                 do {
466                         PRInt32 res;
467
468                         pollfds[0].out_flags = 0;
469                         pollfds[1].out_flags = 0;
470                         w = -1;
471
472                         res = PR_Poll (pollfds, 2, IO_TIMEOUT);
473                         if (res == -1) {
474                                 set_errno(PR_GetError());
475                                 if (PR_GetError () == PR_PENDING_INTERRUPT_ERROR)
476                                         w = 0;
477                         } else if (res == 0) {
478 #ifdef ETIMEDOUT
479                                 errno = ETIMEDOUT;
480 #else
481                                 errno = EIO;
482 #endif
483                         } else if (pollfds[1].out_flags == PR_POLL_READ) {
484                                 errno = EINTR;
485                         } else {
486                                 do {
487                                         w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
488                                         if (w == -1)
489                                                 set_errno (PR_GetError ());
490                                 } while (w == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
491                                 
492                                 if (w == -1) {
493                                         if (PR_GetError () == PR_IO_PENDING_ERROR ||
494                                             PR_GetError () == PR_WOULD_BLOCK_ERROR)
495                                                 w = 0;
496                                 } else
497                                         written += w;
498                         }
499                 } while (w != -1 && written < n);
500                 
501                 /* restore O_NONBLOCK options */
502                 error = errno;
503                 sockopts.option = PR_SockOpt_Nonblocking;
504                 sockopts.value.non_blocking = nonblock;
505                 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
506                 errno = error;
507         }
508         
509         if (w == -1)
510                 return -1;
511         
512         return written;
513 }
514
515 static int
516 stream_flush (CamelStream *stream)
517 {
518         /*return PR_Sync (((CamelTcpStreamSSL *)stream)->priv->sockfd);*/
519         return 0;
520 }
521
522 static int
523 stream_close (CamelStream *stream)
524 {
525         if (((CamelTcpStreamSSL *)stream)->priv->sockfd == NULL) {
526                 errno = EINVAL;
527                 return -1;
528         }
529
530         if (PR_Close (((CamelTcpStreamSSL *)stream)->priv->sockfd) == PR_FAILURE)
531                 return -1;
532         
533         ((CamelTcpStreamSSL *)stream)->priv->sockfd = NULL;
534         
535         return 0;
536 }
537
538 #if 0
539 /* Since this is default implementation, let NSS handle it. */
540 static SECStatus
541 ssl_get_client_auth (void *data, PRFileDesc *sockfd,
542                      struct CERTDistNamesStr *caNames,
543                      struct CERTCertificateStr **pRetCert,
544                      struct SECKEYPrivateKeyStr **pRetKey) 
545 {
546         SECStatus status = SECFailure;
547         SECKEYPrivateKey *privkey;
548         CERTCertificate *cert;
549         void *proto_win;
550         
551         proto_win = SSL_RevealPinArg (sockfd);
552         
553         if ((char *) data) {
554                 cert = PK11_FindCertFromNickname ((char *) data, proto_win);
555                 if (cert) {
556                         privKey = PK11_FindKeyByAnyCert (cert, proto_win);
557                         if (privkey) {
558                                 status = SECSuccess;
559                         } else {
560                                 CERT_DestroyCertificate (cert);
561                         }
562                 }
563         } else {
564                 /* no nickname given, automatically find the right cert */
565                 CERTCertNicknames *names;
566                 int i;
567                 
568                 names = CERT_GetCertNicknames (CERT_GetDefaultCertDB (), 
569                                                SEC_CERT_NICKNAMES_USER,
570                                                proto_win);
571                 
572                 if (names != NULL) {
573                         for (i = 0; i < names->numnicknames; i++) {
574                                 cert = PK11_FindCertFromNickname (names->nicknames[i], 
575                                                                   proto_win);
576                                 if (!cert)
577                                         continue;
578                                 
579                                 /* Only check unexpired certs */
580                                 if (CERT_CheckCertValidTimes (cert, PR_Now (), PR_FALSE) != secCertTimeValid) {
581                                         CERT_DestroyCertificate (cert);
582                                         continue;
583                                 }
584                                 
585                                 status = NSS_CmpCertChainWCANames (cert, caNames);
586                                 if (status == SECSuccess) {
587                                         privkey = PK11_FindKeyByAnyCert (cert, proto_win);
588                                         if (privkey)
589                                                 break;
590                                         
591                                         status = SECFailure;
592                                         break;
593                                 }
594                                 
595                                 CERT_FreeNicknames (names);
596                         }
597                 }
598         }
599         
600         if (status == SECSuccess) {
601                 *pRetCert = cert;
602                 *pRetKey  = privkey;
603         }
604         
605         return status;
606 }
607 #endif
608
609 #if 0
610 /* Since this is the default NSS implementation, no need for us to use this. */
611 static SECStatus
612 ssl_auth_cert (void *data, PRFileDesc *sockfd, PRBool checksig, PRBool is_server)
613 {
614         CERTCertificate *cert;
615         SECStatus status;
616         void *pinarg;
617         char *host;
618         
619         cert = SSL_PeerCertificate (sockfd);
620         pinarg = SSL_RevealPinArg (sockfd);
621         status = CERT_VerifyCertNow ((CERTCertDBHandle *)data, cert,
622                                      checksig, certUsageSSLClient, pinarg);
623         
624         if (status != SECSuccess)
625                 return SECFailure;
626         
627         /* Certificate is OK.  Since this is the client side of an SSL
628          * connection, we need to verify that the name field in the cert
629          * matches the desired hostname.  This is our defense against
630          * man-in-the-middle attacks.
631          */
632         
633         /* SSL_RevealURL returns a hostname, not a URL. */
634         host = SSL_RevealURL (sockfd);
635         
636         if (host && *host) {
637                 status = CERT_VerifyCertName (cert, host);
638         } else {
639                 PR_SetError (SSL_ERROR_BAD_CERT_DOMAIN, 0);
640                 status = SECFailure;
641         }
642         
643         if (host)
644                 PR_Free (host);
645         
646         return secStatus;
647 }
648 #endif
649
650 CamelCert *camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert);
651 CamelCert *camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert);
652 void camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert);
653
654 static char *
655 cert_fingerprint(CERTCertificate *cert)
656 {
657         unsigned char md5sum[16], fingerprint[50], *f;
658         int i;
659         const char tohex[16] = "0123456789abcdef";
660
661         md5_get_digest (cert->derCert.data, cert->derCert.len, md5sum);
662         for (i=0,f = fingerprint; i<16; i++) {
663                 unsigned int c = md5sum[i];
664
665                 *f++ = tohex[(c >> 4) & 0xf];
666                 *f++ = tohex[c & 0xf];
667 #ifndef G_OS_WIN32
668                 *f++ = ':';
669 #else
670                 /* The fingerprint is used as a file name, can't have
671                  * colons in file names. Use underscore instead.
672                  */
673                 *f++ = '_';
674 #endif
675         }
676
677         fingerprint[47] = 0;
678
679         return g_strdup(fingerprint);
680 }
681
682 /* lookup a cert uses fingerprint to index an on-disk file */
683 CamelCert *
684 camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert)
685 {
686         char *fingerprint;
687         CamelCert *ccert;
688
689         fingerprint = cert_fingerprint (cert);
690         ccert = camel_certdb_get_cert (certdb, fingerprint);
691         if (ccert == NULL) {
692                 g_free (fingerprint);
693                 return ccert;
694         }
695
696         if (ccert->rawcert == NULL) {
697                 GByteArray *array;
698                 gchar *filename;
699                 gchar *contents;
700                 gsize length;
701                 GError *error = NULL;
702
703                 filename = g_build_filename (
704                         g_get_home_dir (), ".camel_certs", fingerprint, NULL);
705                 g_file_get_contents (filename, &contents, &length, &error);
706                 if (error != NULL) {
707                         g_warning (
708                                 "Could not load cert %s: %s",
709                                 filename, error->message);
710                         g_error_free (error);
711
712                         camel_cert_set_trust (
713                                 certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
714                         camel_certdb_touch (certdb);
715                         g_free (fingerprint);
716                         g_free (filename);
717
718                         return ccert;
719                 }
720                 g_free (filename);
721
722                 array = g_byte_array_sized_new (length);
723                 g_byte_array_append (array, (guint8 *) contents, length);
724                 g_free (contents);
725
726                 ccert->rawcert = array;
727         }
728
729         g_free(fingerprint);
730         if (ccert->rawcert->len != cert->derCert.len
731             || memcmp(ccert->rawcert->data, cert->derCert.data, cert->derCert.len) != 0) {
732                 g_warning("rawcert != derCer");
733                 camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
734                 camel_certdb_touch(certdb);
735         }
736
737         return ccert;
738 }
739
740 /* add a cert to the certdb */
741 CamelCert *
742 camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert)
743 {
744         CamelCert *ccert;
745         char *fingerprint;
746
747         fingerprint = cert_fingerprint(cert);
748
749         ccert = camel_certdb_cert_new(certdb);
750         camel_cert_set_issuer(certdb, ccert, CERT_NameToAscii(&cert->issuer));
751         camel_cert_set_subject(certdb, ccert, CERT_NameToAscii(&cert->subject));
752         /* hostname is set in caller */
753         /*camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);*/
754         camel_cert_set_fingerprint(certdb, ccert, fingerprint);
755         camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
756         g_free(fingerprint);
757
758         camel_certdb_nss_cert_set(certdb, ccert, cert);
759
760         camel_certdb_add(certdb, ccert);
761
762         return ccert;
763 }
764
765 /* set the 'raw' cert (& save it) */
766 void
767 camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert)
768 {
769         char *dir, *path, *fingerprint;
770         CamelStream *stream;
771         struct stat st;
772         
773         fingerprint = ccert->fingerprint;
774         
775         if (ccert->rawcert == NULL)
776                 ccert->rawcert = g_byte_array_new ();
777         
778         g_byte_array_set_size (ccert->rawcert, cert->derCert.len);
779         memcpy (ccert->rawcert->data, cert->derCert.data, cert->derCert.len);
780         
781 #ifndef G_OS_WIN32
782         dir = g_strdup_printf ("%s/.camel_certs", getenv ("HOME"));
783 #else
784         dir = g_build_filename (g_get_home_dir (), ".camel_certs", NULL);
785 #endif
786         if (g_stat (dir, &st) == -1 && g_mkdir (dir, 0700) == -1) {
787                 g_warning ("Could not create cert directory '%s': %s", dir, strerror (errno));
788                 g_free (dir);
789                 return;
790         }
791         
792         path = g_strdup_printf ("%s/%s", dir, fingerprint);
793         g_free (dir);
794         
795         stream = camel_stream_fs_new_with_name (path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
796         if (stream != NULL) {
797                 if (camel_stream_write (stream, ccert->rawcert->data, ccert->rawcert->len) == -1) {
798                         g_warning ("Could not save cert: %s: %s", path, strerror (errno));
799                         g_unlink (path);
800                 }
801                 camel_stream_close (stream);
802                 camel_object_unref (stream);
803         } else {
804                 g_warning ("Could not save cert: %s: %s", path, strerror (errno));
805         }
806         
807         g_free (path);
808 }
809
810
811 #if 0
812 /* used by the mozilla-like code below */
813 static char *
814 get_nickname(CERTCertificate *cert)
815 {
816         char *server, *nick = NULL;
817         int i;
818         PRBool status = PR_TRUE;
819
820         server = CERT_GetCommonName(&cert->subject);
821         if (server == NULL)
822                 return NULL;
823
824         for (i=1;status == PR_TRUE;i++) {
825                 if (nick) {
826                         g_free(nick);
827                         nick = g_strdup_printf("%s #%d", server, i);
828                 } else {
829                         nick = g_strdup(server);
830                 }
831                 status = SEC_CertNicknameConflict(server, &cert->derSubject, cert->dbhandle);
832         }
833
834         return nick;
835 }
836 #endif
837
838 static SECStatus
839 ssl_bad_cert (void *data, PRFileDesc *sockfd)
840 {
841         gboolean accept;
842         CamelCertDB *certdb = NULL;
843         CamelCert *ccert = NULL;
844         char *prompt, *cert_str, *fingerprint;
845         CamelTcpStreamSSL *ssl;
846         CERTCertificate *cert;
847         SECStatus status = SECFailure;
848
849         g_return_val_if_fail (data != NULL, SECFailure);
850         g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure);
851
852         ssl = data;
853         
854         cert = SSL_PeerCertificate (sockfd);
855         if (cert == NULL)
856                 return SECFailure;
857
858         certdb = camel_certdb_get_default();
859         ccert = camel_certdb_nss_cert_get(certdb, cert);
860         if (ccert == NULL) {
861                 ccert = camel_certdb_nss_cert_add(certdb, cert);
862                 camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);
863         }
864
865         if (ccert->trust == CAMEL_CERT_TRUST_UNKNOWN) {
866                 status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
867                 fingerprint = cert_fingerprint(cert);
868                 cert_str = g_strdup_printf (_("Issuer:            %s\n"
869                                               "Subject:           %s\n"
870                                               "Fingerprint:       %s\n"
871                                               "Signature:         %s"),
872                                             CERT_NameToAscii (&cert->issuer),
873                                             CERT_NameToAscii (&cert->subject),
874                                             fingerprint, status == SECSuccess?_("GOOD"):_("BAD"));
875                 g_free(fingerprint);
876
877                 /* construct our user prompt */
878                 prompt = g_strdup_printf (_("SSL Certificate check for %s:\n\n%s\n\nDo you wish to accept?"),
879                                           ssl->priv->expected_host, cert_str);
880                 g_free (cert_str);
881         
882                 /* query the user to find out if we want to accept this certificate */
883                 accept = camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE);
884                 g_free(prompt);
885                 if (accept) {
886                         camel_certdb_nss_cert_set(certdb, ccert, cert);
887                         camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_FULLY);
888                         camel_certdb_touch(certdb);
889                 }
890         } else {
891                 accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
892         }
893
894         camel_certdb_cert_unref(certdb, ccert);
895         camel_object_unref(certdb);
896
897         return accept ? SECSuccess : SECFailure;
898
899 #if 0
900         int i, error;
901         CERTCertTrust trust;
902         SECItem *certs[1];
903         int go = 1;
904         char *host, *nick;
905
906         error = PR_GetError();
907
908         /* This code is basically what mozilla does - however it doesn't seem to work here
909            very reliably :-/ */
910         while (go && status != SECSuccess) {
911                 char *prompt = NULL;
912
913                 printf("looping, error '%d'\n", error);
914
915                 switch(error) {
916                 case SEC_ERROR_UNKNOWN_ISSUER:
917                 case SEC_ERROR_CA_CERT_INVALID:
918                 case SEC_ERROR_UNTRUSTED_ISSUER:
919                 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
920                         /* add certificate */
921                         printf("unknown issuer, adding ... \n");
922                         prompt = g_strdup_printf(_("Certificate problem: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
923
924                         if (camel_session_alert_user(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
925
926                                 nick = get_nickname(cert);
927                                 if (NULL == nick) {
928                                         g_free(prompt);
929                                         status = SECFailure;
930                                         break;
931                                 }
932
933                                 printf("adding cert '%s'\n", nick);
934
935                                 if (!cert->trust) {
936                                         cert->trust = (CERTCertTrust*)PORT_ArenaZAlloc(cert->arena, sizeof(CERTCertTrust));
937                                         CERT_DecodeTrustString(cert->trust, "P");
938                                 }
939                 
940                                 certs[0] = &cert->derCert;
941                                 /*CERT_ImportCerts (cert->dbhandle, certUsageSSLServer, 1, certs, NULL, TRUE, FALSE, nick);*/
942                                 CERT_ImportCerts(cert->dbhandle, certUsageUserCertImport, 1, certs, NULL, TRUE, FALSE, nick);
943                                 g_free(nick);
944
945                                 printf(" cert type %08x\n", cert->nsCertType);
946
947                                 memset((void*)&trust, 0, sizeof(trust));
948                                 if (CERT_GetCertTrust(cert, &trust) != SECSuccess) {
949                                         CERT_DecodeTrustString(&trust, "P");
950                                 }
951                                 trust.sslFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED;
952                                 if (CERT_ChangeCertTrust(cert->dbhandle, cert, &trust) != SECSuccess) {
953                                         printf("couldn't change cert trust?\n");
954                                 }
955
956                                 /*status = SECSuccess;*/
957 #if 1
958                                 /* re-verify? */
959                                 status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLServer, NULL);
960                                 error = PR_GetError();
961                                 printf("re-verify status %d, error %d\n", status, error);
962 #endif
963
964                                 printf(" cert type %08x\n", cert->nsCertType);
965                         } else {
966                                 printf("failed/cancelled\n");
967                                 go = 0;
968                         }
969
970                         break;
971                 case SSL_ERROR_BAD_CERT_DOMAIN:
972                         printf("bad domain\n");
973
974                         prompt = g_strdup_printf(_("Bad certificate domain: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
975
976                         if (camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
977                                 host = SSL_RevealURL(sockfd);
978                                 status = CERT_AddOKDomainName(cert, host);
979                                 printf("add ok domain name : %s\n", status == SECFailure?"fail":"ok");
980                                 error = PR_GetError();
981                                 if (status == SECFailure)
982                                         go = 0;
983                         } else {
984                                 go = 0;
985                         }
986
987                         break;
988                         
989                 case SEC_ERROR_EXPIRED_CERTIFICATE:
990                         printf("expired\n");
991
992                         prompt = g_strdup_printf(_("Certificate expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
993
994                         if (camel_session_alert_user(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
995                                 cert->timeOK = PR_TRUE;
996                                 status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
997                                 error = PR_GetError();
998                                 if (status == SECFailure)
999                                         go = 0;
1000                         } else {
1001                                 go = 0;
1002                         }
1003
1004                         break;
1005
1006                 case SEC_ERROR_CRL_EXPIRED:
1007                         printf("crl expired\n");
1008
1009                         prompt = g_strdup_printf(_("Certificate revocation list expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
1010
1011                         if (camel_session_alert_user(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
1012                                 host = SSL_RevealURL(sockfd);
1013                                 status = CERT_AddOKDomainName(cert, host);
1014                         }
1015
1016                         go = 0;
1017                         break;
1018
1019                 default:
1020                         printf("generic error\n");
1021                         go = 0;
1022                         break;
1023                 }
1024
1025                 g_free(prompt);
1026         }
1027
1028         CERT_DestroyCertificate(cert);
1029
1030         return status;
1031 #endif
1032 }
1033
1034 static PRFileDesc *
1035 enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd)
1036 {
1037         PRFileDesc *ssl_fd;
1038         
1039         ssl_fd = SSL_ImportFD (NULL, fd ? fd : ssl->priv->sockfd);
1040         if (!ssl_fd)
1041                 return NULL;
1042         
1043         SSL_OptionSet (ssl_fd, SSL_SECURITY, PR_TRUE);
1044         if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL2)
1045                 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_TRUE);
1046         else
1047                 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_FALSE);
1048         if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
1049                 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_TRUE);
1050         else
1051                 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_FALSE);
1052         if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
1053                 SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_TRUE);
1054         else
1055                 SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_FALSE);
1056         
1057         SSL_SetURL (ssl_fd, ssl->priv->expected_host);
1058         
1059         /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *) certNickname);*/
1060         /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, (void *) CERT_GetDefaultCertDB ());*/
1061         SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl);
1062         
1063         ssl->priv->ssl_mode = TRUE;
1064         
1065         return ssl_fd;
1066 }
1067
1068 static int
1069 sockaddr_to_praddr(struct sockaddr *s, int len, PRNetAddr *addr)
1070 {
1071         /* We assume the ip addresses are the same size - they have to be anyway.
1072            We could probably just use memcpy *shrug* */
1073
1074         memset(addr, 0, sizeof(*addr));
1075
1076         if (s->sa_family == AF_INET) {
1077                 struct sockaddr_in *sin = (struct sockaddr_in *)s;
1078
1079                 if (len < sizeof(*sin))
1080                         return -1;
1081
1082                 addr->inet.family = PR_AF_INET;
1083                 addr->inet.port = sin->sin_port;
1084                 memcpy(&addr->inet.ip, &sin->sin_addr, sizeof(addr->inet.ip));
1085
1086                 return 0;
1087         }
1088 #ifdef ENABLE_IPv6
1089         else if (s->sa_family == PR_AF_INET6) {
1090                 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)s;
1091
1092                 if (len < sizeof(*sin))
1093                         return -1;
1094
1095                 addr->ipv6.family = PR_AF_INET6;
1096                 addr->ipv6.port = sin->sin6_port;
1097                 addr->ipv6.flowinfo = sin->sin6_flowinfo;
1098                 memcpy(&addr->ipv6.ip, &sin->sin6_addr, sizeof(addr->ipv6.ip));
1099                 addr->ipv6.scope_id = sin->sin6_scope_id;
1100
1101                 return 0;
1102         }
1103 #endif
1104
1105         return -1;
1106 }
1107
1108 static int
1109 socket_connect(CamelTcpStream *stream, struct addrinfo *host)
1110 {
1111         CamelTcpStreamSSL *ssl = CAMEL_TCP_STREAM_SSL (stream);
1112         PRNetAddr netaddr;
1113         PRFileDesc *fd, *cancel_fd;
1114
1115         if (sockaddr_to_praddr(host->ai_addr, host->ai_addrlen, &netaddr) != 0) {
1116                 errno = EINVAL;
1117                 return -1;
1118         }
1119         
1120         fd = PR_OpenTCPSocket(netaddr.raw.family);
1121         if (fd == NULL) {
1122                 set_errno (PR_GetError ());
1123                 return -1;
1124         }
1125         
1126         if (ssl->priv->ssl_mode) {
1127                 PRFileDesc *ssl_fd;
1128                 
1129                 ssl_fd = enable_ssl (ssl, fd);
1130                 if (ssl_fd == NULL) {
1131                         int errnosave;
1132                         
1133                         set_errno (PR_GetError ());
1134                         errnosave = errno;
1135                         PR_Close (fd);
1136                         errno = errnosave;
1137                         
1138                         return -1;
1139                 }
1140                 
1141                 fd = ssl_fd;
1142         }
1143
1144         cancel_fd = camel_operation_cancel_prfd(NULL);
1145
1146         if (PR_Connect (fd, &netaddr, cancel_fd?0:CONNECT_TIMEOUT) == PR_FAILURE) {
1147                 int errnosave;
1148                 
1149                 set_errno (PR_GetError ());
1150                 if (PR_GetError () == PR_IN_PROGRESS_ERROR ||
1151                     (cancel_fd && (PR_GetError () == PR_CONNECT_TIMEOUT_ERROR ||
1152                                    PR_GetError () == PR_IO_TIMEOUT_ERROR))) {
1153                         gboolean connected = FALSE;
1154                         PRPollDesc poll[2];
1155
1156                         poll[0].fd = fd;
1157                         poll[0].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
1158                         poll[1].fd = cancel_fd;
1159                         poll[1].in_flags = PR_POLL_READ;
1160                         
1161                         do {
1162                                 poll[0].out_flags = 0;
1163                                 poll[1].out_flags = 0;
1164
1165                                 if (PR_Poll (poll, cancel_fd?2:1, CONNECT_TIMEOUT) == PR_FAILURE) {
1166                                         set_errno (PR_GetError ());
1167                                         goto exception;
1168                                 }
1169                                 
1170                                 if (poll[1].out_flags == PR_POLL_READ) {
1171                                         errno = EINTR;
1172                                         goto exception;
1173                                 }
1174
1175                                 if (PR_ConnectContinue(fd, poll[0].out_flags) == PR_FAILURE) {
1176                                         set_errno (PR_GetError ());
1177                                         if (PR_GetError () != PR_IN_PROGRESS_ERROR)
1178                                                 goto exception;
1179                                 } else {
1180                                         connected = TRUE;
1181                                 }
1182                         } while (!connected);
1183                 } else {
1184                 exception:
1185                         errnosave = errno;
1186                         PR_Close (fd);
1187                         ssl->priv->sockfd = NULL;
1188                         errno = errnosave;
1189                         
1190                         return -1;
1191                 }
1192                 
1193                 errno = 0;
1194         }
1195         
1196         ssl->priv->sockfd = fd;
1197
1198         return 0;
1199 }
1200
1201 static int
1202 stream_connect(CamelTcpStream *stream, struct addrinfo *host)
1203 {
1204         while (host) {
1205                 if (socket_connect(stream, host) == 0)
1206                         return 0;
1207                 host = host->ai_next;
1208         }
1209
1210         return -1;
1211 }
1212
1213 static int
1214 stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data)
1215 {
1216         PRSocketOptionData sodata;
1217         
1218         memset ((void *) &sodata, 0, sizeof (sodata));
1219         memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
1220         
1221         if (PR_GetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
1222                 return -1;
1223         
1224         memcpy ((void *) data, (void *) &sodata, sizeof (CamelSockOptData));
1225         
1226         return 0;
1227 }
1228
1229 static int
1230 stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data)
1231 {
1232         PRSocketOptionData sodata;
1233         
1234         memset ((void *) &sodata, 0, sizeof (sodata));
1235         memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
1236         
1237         if (PR_SetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
1238                 return -1;
1239         
1240         return 0;
1241 }
1242
1243 static struct sockaddr *
1244 sockaddr_from_praddr(PRNetAddr *addr, socklen_t *len)
1245 {
1246         /* We assume the ip addresses are the same size - they have to be anyway */
1247
1248         if (addr->raw.family == PR_AF_INET) {
1249                 struct sockaddr_in *sin = g_malloc0(sizeof(*sin));
1250
1251                 sin->sin_family = AF_INET;
1252                 sin->sin_port = addr->inet.port;
1253                 memcpy(&sin->sin_addr, &addr->inet.ip, sizeof(sin->sin_addr));
1254                 *len = sizeof(*sin);
1255
1256                 return (struct sockaddr *)sin;
1257         }
1258 #ifdef ENABLE_IPv6
1259         else if (addr->raw.family == PR_AF_INET6) {
1260                 struct sockaddr_in6 *sin = g_malloc0(sizeof(*sin));
1261
1262                 sin->sin6_family = AF_INET6;
1263                 sin->sin6_port = addr->ipv6.port;
1264                 sin->sin6_flowinfo = addr->ipv6.flowinfo;
1265                 memcpy(&sin->sin6_addr, &addr->ipv6.ip, sizeof(sin->sin6_addr));
1266                 sin->sin6_scope_id = addr->ipv6.scope_id;
1267                 *len = sizeof(*sin);
1268
1269                 return (struct sockaddr *)sin;          
1270         }
1271 #endif
1272
1273         return NULL;
1274 }
1275
1276 static struct sockaddr *
1277 stream_get_local_address(CamelTcpStream *stream, socklen_t *len)
1278 {
1279         PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
1280         PRNetAddr addr;
1281         
1282         if (PR_GetSockName(sockfd, &addr) != PR_SUCCESS)
1283                 return NULL;
1284
1285         return sockaddr_from_praddr(&addr, len);
1286 }
1287
1288 static struct sockaddr *
1289 stream_get_remote_address (CamelTcpStream *stream, socklen_t *len)
1290 {
1291         PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
1292         PRNetAddr addr;
1293         
1294         if (PR_GetPeerName(sockfd, &addr) != PR_SUCCESS)
1295                 return NULL;
1296
1297         return sockaddr_from_praddr(&addr, len);
1298 }
1299
1300 #endif /* HAVE_NSS */