1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2001 Ximian, Inc. (www.ximian.com)
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.
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.
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.
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.
41 #include <sys/types.h>
49 #include "nss.h" /* Don't use <> here or it will include the system nss.h instead */
56 #include <glib/gi18n-lib.h>
57 #include <glib/gstdio.h>
59 #include <libedataserver/md5-utils.h>
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"
69 #define IO_TIMEOUT (PR_TicksPerSecond() * 4 * 60)
70 #define CONNECT_TIMEOUT (PR_TicksPerSecond () * 4 * 60)
72 static CamelTcpStreamClass *parent_class = NULL;
74 /* Returns the class for a CamelTcpStreamSSL */
75 #define CTSS_CLASS(so) CAMEL_TCP_STREAM_SSL_CLASS (CAMEL_OBJECT_GET_CLASS (so))
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);
82 static PRFileDesc *enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd);
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);
90 struct _CamelTcpStreamSSLPrivate {
93 struct _CamelSession *session;
100 camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *camel_tcp_stream_ssl_class)
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);
107 parent_class = CAMEL_TCP_STREAM_CLASS (camel_type_get_global_classfuncs (camel_tcp_stream_get_type ()));
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;
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;
123 camel_tcp_stream_ssl_init (gpointer object, gpointer klass)
125 CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
127 stream->priv = g_new0 (struct _CamelTcpStreamSSLPrivate, 1);
131 camel_tcp_stream_ssl_finalize (CamelObject *object)
133 CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
135 if (stream->priv->sockfd != NULL)
136 PR_Close (stream->priv->sockfd);
138 if (stream->priv->session)
139 camel_object_unref(stream->priv->session);
141 g_free (stream->priv->expected_host);
143 g_free (stream->priv);
148 camel_tcp_stream_ssl_get_type (void)
150 static CamelType type = CAMEL_INVALID_TYPE;
152 if (type == CAMEL_INVALID_TYPE) {
153 type = camel_type_register (camel_tcp_stream_get_type (),
155 sizeof (CamelTcpStreamSSL),
156 sizeof (CamelTcpStreamSSLClass),
157 (CamelObjectClassInitFunc) camel_tcp_stream_ssl_class_init,
159 (CamelObjectInitFunc) camel_tcp_stream_ssl_init,
160 (CamelObjectFinalizeFunc) camel_tcp_stream_ssl_finalize);
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
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.
180 * Returns a new #CamelTcpStreamSSL stream preset in SSL mode
183 camel_tcp_stream_ssl_new (CamelSession *session, const char *expected_host, guint32 flags)
185 CamelTcpStreamSSL *stream;
187 g_assert(CAMEL_IS_SESSION(session));
189 stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
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;
197 return CAMEL_STREAM (stream);
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
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.
214 * Returns a new #CamelTcpStreamSSL stream not yet toggled into SSL mode
217 camel_tcp_stream_ssl_new_raw (CamelSession *session, const char *expected_host, guint32 flags)
219 CamelTcpStreamSSL *stream;
221 g_assert(CAMEL_IS_SESSION(session));
223 stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
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;
231 return CAMEL_STREAM (stream);
238 /* FIXME: this should handle more. */
240 case PR_INVALID_ARGUMENT_ERROR:
243 case PR_PENDING_INTERRUPT_ERROR:
246 case PR_IO_PENDING_ERROR:
250 case PR_WOULD_BLOCK_ERROR:
255 case PR_IN_PROGRESS_ERROR:
260 case PR_ALREADY_INITIATED_ERROR:
265 case PR_NETWORK_UNREACHABLE_ERROR:
266 errno = EHOSTUNREACH;
270 case PR_CONNECT_REFUSED_ERROR:
271 errno = ECONNREFUSED;
275 case PR_CONNECT_TIMEOUT_ERROR:
276 case PR_IO_TIMEOUT_ERROR:
281 case PR_NOT_CONNECTED_ERROR:
286 case PR_CONNECT_RESET_ERROR:
299 * camel_tcp_stream_ssl_enable_ssl:
300 * @ssl: a #CamelTcpStreamSSL object
302 * Toggles an ssl-capable stream into ssl mode (if it isn't already).
304 * Returns %0 on success or %-1 on fail
307 camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl)
311 g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (ssl), -1);
313 if (ssl->priv->sockfd && !ssl->priv->ssl_mode) {
314 if (!(fd = enable_ssl (ssl, NULL))) {
315 set_errno (PR_GetError ());
319 ssl->priv->sockfd = fd;
321 if (SSL_ResetHandshake (fd, FALSE) == SECFailure) {
322 set_errno (PR_GetError ());
326 if (SSL_ForceHandshake (fd) == -1) {
327 set_errno (PR_GetError ());
332 ssl->priv->ssl_mode = TRUE;
339 stream_read (CamelStream *stream, char *buffer, size_t n)
341 CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
342 PRFileDesc *cancel_fd;
345 if (camel_operation_cancel_check (NULL)) {
350 cancel_fd = camel_operation_cancel_prfd (NULL);
351 if (cancel_fd == NULL) {
353 nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
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));
360 PRSocketOptionData sockopts;
361 PRPollDesc pollfds[2];
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);
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;
381 pollfds[0].out_flags = 0;
382 pollfds[1].out_flags = 0;
385 res = PR_Poll(pollfds, 2, IO_TIMEOUT);
387 set_errno(PR_GetError());
394 } else if (pollfds[1].out_flags == PR_POLL_READ) {
399 nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n);
401 set_errno (PR_GetError ());
402 } while (nread == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
404 } while (nread == -1 && (PR_GetError () == PR_PENDING_INTERRUPT_ERROR ||
405 PR_GetError () == PR_IO_PENDING_ERROR ||
406 PR_GetError () == PR_WOULD_BLOCK_ERROR));
408 /* restore O_NONBLOCK options */
411 sockopts.option = PR_SockOpt_Nonblocking;
412 sockopts.value.non_blocking = nonblock;
413 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
421 stream_write (CamelStream *stream, const char *buffer, size_t n)
423 CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream);
424 ssize_t w, written = 0;
425 PRFileDesc *cancel_fd;
427 if (camel_operation_cancel_check (NULL)) {
432 cancel_fd = camel_operation_cancel_prfd (NULL);
433 if (cancel_fd == NULL) {
436 w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
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));
445 } while (w != -1 && written < n);
447 PRSocketOptionData sockopts;
448 PRPollDesc pollfds[2];
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);
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;
468 pollfds[0].out_flags = 0;
469 pollfds[1].out_flags = 0;
472 res = PR_Poll (pollfds, 2, IO_TIMEOUT);
474 set_errno(PR_GetError());
475 if (PR_GetError () == PR_PENDING_INTERRUPT_ERROR)
477 } else if (res == 0) {
483 } else if (pollfds[1].out_flags == PR_POLL_READ) {
487 w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written);
489 set_errno (PR_GetError ());
490 } while (w == -1 && PR_GetError () == PR_PENDING_INTERRUPT_ERROR);
493 if (PR_GetError () == PR_IO_PENDING_ERROR ||
494 PR_GetError () == PR_WOULD_BLOCK_ERROR)
499 } while (w != -1 && written < n);
501 /* restore O_NONBLOCK options */
503 sockopts.option = PR_SockOpt_Nonblocking;
504 sockopts.value.non_blocking = nonblock;
505 PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts);
516 stream_flush (CamelStream *stream)
518 /*return PR_Sync (((CamelTcpStreamSSL *)stream)->priv->sockfd);*/
523 stream_close (CamelStream *stream)
525 if (((CamelTcpStreamSSL *)stream)->priv->sockfd == NULL) {
530 if (PR_Close (((CamelTcpStreamSSL *)stream)->priv->sockfd) == PR_FAILURE)
533 ((CamelTcpStreamSSL *)stream)->priv->sockfd = NULL;
539 /* Since this is default implementation, let NSS handle it. */
541 ssl_get_client_auth (void *data, PRFileDesc *sockfd,
542 struct CERTDistNamesStr *caNames,
543 struct CERTCertificateStr **pRetCert,
544 struct SECKEYPrivateKeyStr **pRetKey)
546 SECStatus status = SECFailure;
547 SECKEYPrivateKey *privkey;
548 CERTCertificate *cert;
551 proto_win = SSL_RevealPinArg (sockfd);
554 cert = PK11_FindCertFromNickname ((char *) data, proto_win);
556 privKey = PK11_FindKeyByAnyCert (cert, proto_win);
560 CERT_DestroyCertificate (cert);
564 /* no nickname given, automatically find the right cert */
565 CERTCertNicknames *names;
568 names = CERT_GetCertNicknames (CERT_GetDefaultCertDB (),
569 SEC_CERT_NICKNAMES_USER,
573 for (i = 0; i < names->numnicknames; i++) {
574 cert = PK11_FindCertFromNickname (names->nicknames[i],
579 /* Only check unexpired certs */
580 if (CERT_CheckCertValidTimes (cert, PR_Now (), PR_FALSE) != secCertTimeValid) {
581 CERT_DestroyCertificate (cert);
585 status = NSS_CmpCertChainWCANames (cert, caNames);
586 if (status == SECSuccess) {
587 privkey = PK11_FindKeyByAnyCert (cert, proto_win);
595 CERT_FreeNicknames (names);
600 if (status == SECSuccess) {
610 /* Since this is the default NSS implementation, no need for us to use this. */
612 ssl_auth_cert (void *data, PRFileDesc *sockfd, PRBool checksig, PRBool is_server)
614 CERTCertificate *cert;
619 cert = SSL_PeerCertificate (sockfd);
620 pinarg = SSL_RevealPinArg (sockfd);
621 status = CERT_VerifyCertNow ((CERTCertDBHandle *)data, cert,
622 checksig, certUsageSSLClient, pinarg);
624 if (status != SECSuccess)
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.
633 /* SSL_RevealURL returns a hostname, not a URL. */
634 host = SSL_RevealURL (sockfd);
637 status = CERT_VerifyCertName (cert, host);
639 PR_SetError (SSL_ERROR_BAD_CERT_DOMAIN, 0);
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);
655 cert_fingerprint(CERTCertificate *cert)
657 unsigned char md5sum[16], fingerprint[50], *f;
659 const char tohex[16] = "0123456789abcdef";
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];
665 *f++ = tohex[(c >> 4) & 0xf];
666 *f++ = tohex[c & 0xf];
670 /* The fingerprint is used as a file name, can't have
671 * colons in file names. Use underscore instead.
679 return g_strdup(fingerprint);
682 /* lookup a cert uses fingerprint to index an on-disk file */
684 camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert)
689 fingerprint = cert_fingerprint (cert);
690 ccert = camel_certdb_get_cert (certdb, fingerprint);
692 g_free (fingerprint);
696 if (ccert->rawcert == NULL) {
701 GError *error = NULL;
703 filename = g_build_filename (
704 g_get_home_dir (), ".camel_certs", fingerprint, NULL);
705 g_file_get_contents (filename, &contents, &length, &error);
708 "Could not load cert %s: %s",
709 filename, error->message);
710 g_error_free (error);
712 camel_cert_set_trust (
713 certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
714 camel_certdb_touch (certdb);
715 g_free (fingerprint);
722 array = g_byte_array_sized_new (length);
723 g_byte_array_append (array, (guint8 *) contents, length);
726 ccert->rawcert = array;
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);
740 /* add a cert to the certdb */
742 camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert)
747 fingerprint = cert_fingerprint(cert);
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);
758 camel_certdb_nss_cert_set(certdb, ccert, cert);
760 camel_certdb_add(certdb, ccert);
765 /* set the 'raw' cert (& save it) */
767 camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert)
769 char *dir, *path, *fingerprint;
773 fingerprint = ccert->fingerprint;
775 if (ccert->rawcert == NULL)
776 ccert->rawcert = g_byte_array_new ();
778 g_byte_array_set_size (ccert->rawcert, cert->derCert.len);
779 memcpy (ccert->rawcert->data, cert->derCert.data, cert->derCert.len);
782 dir = g_strdup_printf ("%s/.camel_certs", getenv ("HOME"));
784 dir = g_build_filename (g_get_home_dir (), ".camel_certs", NULL);
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));
792 path = g_strdup_printf ("%s/%s", dir, fingerprint);
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));
801 camel_stream_close (stream);
802 camel_object_unref (stream);
804 g_warning ("Could not save cert: %s: %s", path, strerror (errno));
812 /* used by the mozilla-like code below */
814 get_nickname(CERTCertificate *cert)
816 char *server, *nick = NULL;
818 PRBool status = PR_TRUE;
820 server = CERT_GetCommonName(&cert->subject);
824 for (i=1;status == PR_TRUE;i++) {
827 nick = g_strdup_printf("%s #%d", server, i);
829 nick = g_strdup(server);
831 status = SEC_CertNicknameConflict(server, &cert->derSubject, cert->dbhandle);
839 ssl_bad_cert (void *data, PRFileDesc *sockfd)
842 CamelCertDB *certdb = NULL;
843 CamelCert *ccert = NULL;
844 char *prompt, *cert_str, *fingerprint;
845 CamelTcpStreamSSL *ssl;
846 CERTCertificate *cert;
847 SECStatus status = SECFailure;
849 g_return_val_if_fail (data != NULL, SECFailure);
850 g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure);
854 cert = SSL_PeerCertificate (sockfd);
858 certdb = camel_certdb_get_default();
859 ccert = camel_certdb_nss_cert_get(certdb, cert);
861 ccert = camel_certdb_nss_cert_add(certdb, cert);
862 camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);
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"
872 CERT_NameToAscii (&cert->issuer),
873 CERT_NameToAscii (&cert->subject),
874 fingerprint, status == SECSuccess?_("GOOD"):_("BAD"));
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);
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);
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);
891 accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
894 camel_certdb_cert_unref(certdb, ccert);
895 camel_object_unref(certdb);
897 return accept ? SECSuccess : SECFailure;
906 error = PR_GetError();
908 /* This code is basically what mozilla does - however it doesn't seem to work here
910 while (go && status != SECSuccess) {
913 printf("looping, error '%d'\n", 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);
924 if (camel_session_alert_user(ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
926 nick = get_nickname(cert);
933 printf("adding cert '%s'\n", nick);
936 cert->trust = (CERTCertTrust*)PORT_ArenaZAlloc(cert->arena, sizeof(CERTCertTrust));
937 CERT_DecodeTrustString(cert->trust, "P");
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);
945 printf(" cert type %08x\n", cert->nsCertType);
947 memset((void*)&trust, 0, sizeof(trust));
948 if (CERT_GetCertTrust(cert, &trust) != SECSuccess) {
949 CERT_DecodeTrustString(&trust, "P");
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");
956 /*status = SECSuccess;*/
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);
964 printf(" cert type %08x\n", cert->nsCertType);
966 printf("failed/cancelled\n");
971 case SSL_ERROR_BAD_CERT_DOMAIN:
972 printf("bad domain\n");
974 prompt = g_strdup_printf(_("Bad certificate domain: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
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)
989 case SEC_ERROR_EXPIRED_CERTIFICATE:
992 prompt = g_strdup_printf(_("Certificate expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
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)
1006 case SEC_ERROR_CRL_EXPIRED:
1007 printf("crl expired\n");
1009 prompt = g_strdup_printf(_("Certificate revocation list expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
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);
1020 printf("generic error\n");
1028 CERT_DestroyCertificate(cert);
1035 enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd)
1039 ssl_fd = SSL_ImportFD (NULL, fd ? fd : ssl->priv->sockfd);
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);
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);
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);
1055 SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_FALSE);
1057 SSL_SetURL (ssl_fd, ssl->priv->expected_host);
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);
1063 ssl->priv->ssl_mode = TRUE;
1069 sockaddr_to_praddr(struct sockaddr *s, int len, PRNetAddr *addr)
1071 /* We assume the ip addresses are the same size - they have to be anyway.
1072 We could probably just use memcpy *shrug* */
1074 memset(addr, 0, sizeof(*addr));
1076 if (s->sa_family == AF_INET) {
1077 struct sockaddr_in *sin = (struct sockaddr_in *)s;
1079 if (len < sizeof(*sin))
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));
1089 else if (s->sa_family == PR_AF_INET6) {
1090 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)s;
1092 if (len < sizeof(*sin))
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;
1109 socket_connect(CamelTcpStream *stream, struct addrinfo *host)
1111 CamelTcpStreamSSL *ssl = CAMEL_TCP_STREAM_SSL (stream);
1113 PRFileDesc *fd, *cancel_fd;
1115 if (sockaddr_to_praddr(host->ai_addr, host->ai_addrlen, &netaddr) != 0) {
1120 fd = PR_OpenTCPSocket(netaddr.raw.family);
1122 set_errno (PR_GetError ());
1126 if (ssl->priv->ssl_mode) {
1129 ssl_fd = enable_ssl (ssl, fd);
1130 if (ssl_fd == NULL) {
1133 set_errno (PR_GetError ());
1144 cancel_fd = camel_operation_cancel_prfd(NULL);
1146 if (PR_Connect (fd, &netaddr, cancel_fd?0:CONNECT_TIMEOUT) == PR_FAILURE) {
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;
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;
1162 poll[0].out_flags = 0;
1163 poll[1].out_flags = 0;
1165 if (PR_Poll (poll, cancel_fd?2:1, CONNECT_TIMEOUT) == PR_FAILURE) {
1166 set_errno (PR_GetError ());
1170 if (poll[1].out_flags == PR_POLL_READ) {
1175 if (PR_ConnectContinue(fd, poll[0].out_flags) == PR_FAILURE) {
1176 set_errno (PR_GetError ());
1177 if (PR_GetError () != PR_IN_PROGRESS_ERROR)
1182 } while (!connected);
1187 ssl->priv->sockfd = NULL;
1196 ssl->priv->sockfd = fd;
1202 stream_connect(CamelTcpStream *stream, struct addrinfo *host)
1205 if (socket_connect(stream, host) == 0)
1207 host = host->ai_next;
1214 stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data)
1216 PRSocketOptionData sodata;
1218 memset ((void *) &sodata, 0, sizeof (sodata));
1219 memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
1221 if (PR_GetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
1224 memcpy ((void *) data, (void *) &sodata, sizeof (CamelSockOptData));
1230 stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data)
1232 PRSocketOptionData sodata;
1234 memset ((void *) &sodata, 0, sizeof (sodata));
1235 memcpy ((void *) &sodata, (void *) data, sizeof (CamelSockOptData));
1237 if (PR_SetSocketOption (((CamelTcpStreamSSL *)stream)->priv->sockfd, &sodata) == PR_FAILURE)
1243 static struct sockaddr *
1244 sockaddr_from_praddr(PRNetAddr *addr, socklen_t *len)
1246 /* We assume the ip addresses are the same size - they have to be anyway */
1248 if (addr->raw.family == PR_AF_INET) {
1249 struct sockaddr_in *sin = g_malloc0(sizeof(*sin));
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);
1256 return (struct sockaddr *)sin;
1259 else if (addr->raw.family == PR_AF_INET6) {
1260 struct sockaddr_in6 *sin = g_malloc0(sizeof(*sin));
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);
1269 return (struct sockaddr *)sin;
1276 static struct sockaddr *
1277 stream_get_local_address(CamelTcpStream *stream, socklen_t *len)
1279 PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
1282 if (PR_GetSockName(sockfd, &addr) != PR_SUCCESS)
1285 return sockaddr_from_praddr(&addr, len);
1288 static struct sockaddr *
1289 stream_get_remote_address (CamelTcpStream *stream, socklen_t *len)
1291 PRFileDesc *sockfd = CAMEL_TCP_STREAM_SSL (stream)->priv->sockfd;
1294 if (PR_GetPeerName(sockfd, &addr) != PR_SUCCESS)
1297 return sockaddr_from_praddr(&addr, len);
1300 #endif /* HAVE_NSS */