1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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.
36 #include <sys/types.h>
44 #include "nss.h" /* Don't use <> here or it will include the system nss.h instead */
50 #include <glib/gi18n-lib.h>
51 #include <glib/gstdio.h>
53 #include "camel-certdb.h"
54 #include "camel-file-utils.h"
55 #include "camel-net-utils.h"
56 #include "camel-operation.h"
57 #include "camel-session.h"
58 #include "camel-stream-fs.h"
59 #include "camel-tcp-stream-ssl.h"
68 #define IO_TIMEOUT (PR_TicksPerSecond() * 4 * 60)
69 #define CONNECT_TIMEOUT (PR_TicksPerSecond () * 4 * 60)
71 #define CAMEL_TCP_STREAM_SSL_GET_PRIVATE(obj) \
72 (G_TYPE_INSTANCE_GET_PRIVATE \
73 ((obj), CAMEL_TYPE_TCP_STREAM_SSL, CamelTcpStreamSSLPrivate))
75 struct _CamelTcpStreamSSLPrivate {
76 CamelSession *session;
79 CamelTcpStreamSSLFlags flags;
82 G_DEFINE_TYPE (CamelTcpStreamSSL, camel_tcp_stream_ssl, CAMEL_TYPE_TCP_STREAM_RAW)
85 tcp_stream_ssl_get_cert_dir (void)
87 static gchar *cert_dir = NULL;
89 if (G_UNLIKELY (cert_dir == NULL)) {
90 const gchar *data_dir;
91 const gchar *home_dir;
94 home_dir = g_get_home_dir ();
95 data_dir = g_get_user_data_dir ();
97 cert_dir = g_build_filename (data_dir, "camel_certs", NULL);
99 /* Move the old certificate directory if present. */
100 old_dir = g_build_filename (home_dir, ".camel_certs", NULL);
101 if (g_file_test (old_dir, G_FILE_TEST_IS_DIR))
102 g_rename (old_dir, cert_dir);
105 g_mkdir_with_parents (cert_dir, 0700);
112 tcp_stream_ssl_dispose (GObject *object)
114 CamelTcpStreamSSLPrivate *priv;
116 priv = CAMEL_TCP_STREAM_SSL_GET_PRIVATE (object);
118 if (priv->session != NULL) {
119 g_object_unref (priv->session);
120 priv->session = NULL;
123 /* Chain up to parent's dispose() method. */
124 G_OBJECT_CLASS (camel_tcp_stream_ssl_parent_class)->dispose (object);
128 tcp_stream_ssl_finalize (GObject *object)
130 CamelTcpStreamSSLPrivate *priv;
132 priv = CAMEL_TCP_STREAM_SSL_GET_PRIVATE (object);
134 g_free (priv->expected_host);
136 /* Chain up to parent's finalize() method. */
137 G_OBJECT_CLASS (camel_tcp_stream_ssl_parent_class)->finalize (object);
141 /* Since this is default implementation, let NSS handle it. */
143 ssl_get_client_auth (gpointer data,
145 struct CERTDistNamesStr *caNames,
146 struct CERTCertificateStr **pRetCert,
147 struct SECKEYPrivateKeyStr **pRetKey)
149 SECStatus status = SECFailure;
150 SECKEYPrivateKey *privkey;
151 CERTCertificate *cert;
154 proto_win = SSL_RevealPinArg (sockfd);
156 if ((gchar *) data) {
157 cert = PK11_FindCertFromNickname ((gchar *) data, proto_win);
159 privKey = PK11_FindKeyByAnyCert (cert, proto_win);
163 CERT_DestroyCertificate (cert);
167 /* no nickname given, automatically find the right cert */
168 CERTCertNicknames *names;
171 names = CERT_GetCertNicknames (
172 CERT_GetDefaultCertDB (),
173 SEC_CERT_NICKNAMES_USER,
177 for (i = 0; i < names->numnicknames; i++) {
178 cert = PK11_FindCertFromNickname (
179 names->nicknames[i], proto_win);
183 /* Only check unexpired certs */
184 if (CERT_CheckCertValidTimes (cert, PR_Now (), PR_FALSE) != secCertTimeValid) {
185 CERT_DestroyCertificate (cert);
189 status = NSS_CmpCertChainWCANames (cert, caNames);
190 if (status == SECSuccess) {
191 privkey = PK11_FindKeyByAnyCert (cert, proto_win);
199 CERT_FreeNicknames (names);
204 if (status == SECSuccess) {
214 /* Since this is the default NSS implementation, no need for us to use this. */
216 ssl_auth_cert (gpointer data,
221 CERTCertificate *cert;
226 cert = SSL_PeerCertificate (sockfd);
227 pinarg = SSL_RevealPinArg (sockfd);
228 status = CERT_VerifyCertNow (
229 (CERTCertDBHandle *) data, cert,
230 checksig, certUsageSSLClient, pinarg);
232 if (status != SECSuccess)
235 /* Certificate is OK. Since this is the client side of an SSL
236 * connection, we need to verify that the name field in the cert
237 * matches the desired hostname. This is our defense against
238 * man-in-the-middle attacks.
241 /* SSL_RevealURL returns a hostname, not a URL. */
242 host = SSL_RevealURL (sockfd);
245 status = CERT_VerifyCertName (cert, host);
247 PR_SetError (SSL_ERROR_BAD_CERT_DOMAIN, 0);
258 CamelCert *camel_certdb_nss_cert_get (CamelCertDB *certdb, CERTCertificate *cert, const gchar *hostname);
259 CamelCert *camel_certdb_nss_cert_convert (CamelCertDB *certdb, CERTCertificate *cert);
260 void camel_certdb_nss_cert_set (CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert);
263 cert_fingerprint (CERTCertificate *cert)
268 guchar fingerprint[50], *f;
270 const gchar tohex[16] = "0123456789abcdef";
272 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
273 digest = g_alloca (length);
275 checksum = g_checksum_new (G_CHECKSUM_MD5);
276 g_checksum_update (checksum, cert->derCert.data, cert->derCert.len);
277 g_checksum_get_digest (checksum, digest, &length);
278 g_checksum_free (checksum);
280 for (i = 0,f = fingerprint; i < length; i++) {
283 *f++ = tohex[(c >> 4) & 0xf];
284 *f++ = tohex[c & 0xf];
288 /* The fingerprint is used as a file name, can't have
289 * colons in file names. Use underscore instead.
297 return g_strdup ((gchar *) fingerprint);
300 /* lookup a cert uses fingerprint to index an on-disk file */
302 camel_certdb_nss_cert_get (CamelCertDB *certdb,
303 CERTCertificate *cert,
304 const gchar *hostname)
309 fingerprint = cert_fingerprint (cert);
311 ccert = camel_certdb_get_host (certdb, hostname, fingerprint);
313 g_free (fingerprint);
317 if (ccert->rawcert == NULL) {
322 const gchar *cert_dir;
323 GError *error = NULL;
325 cert_dir = tcp_stream_ssl_get_cert_dir ();
326 filename = g_build_filename (cert_dir, fingerprint, NULL);
327 if (!g_file_get_contents (filename, &contents, &length, &error) ||
330 "Could not load cert %s: %s",
331 filename, error ? error->message : "Unknown error");
332 g_clear_error (&error);
334 /* failed to load the certificate, thus remove it from
335 * the CertDB, thus it can be re-added and properly saved */
336 camel_certdb_remove_host (certdb, hostname, fingerprint);
337 camel_certdb_touch (certdb);
338 g_free (fingerprint);
345 array = g_byte_array_sized_new (length);
346 g_byte_array_append (array, (guint8 *) contents, length);
349 ccert->rawcert = array;
352 g_free (fingerprint);
353 if (ccert->rawcert->len != cert->derCert.len
354 || memcmp (ccert->rawcert->data, cert->derCert.data, cert->derCert.len) != 0) {
355 g_warning ("rawcert != derCer");
356 camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
357 camel_certdb_touch (certdb);
363 /* Create a CamelCert corresponding to the NSS cert, with unknown trust. */
365 camel_certdb_nss_cert_convert (CamelCertDB *certdb,
366 CERTCertificate *cert)
371 fingerprint = cert_fingerprint (cert);
373 ccert = camel_certdb_cert_new (certdb);
374 camel_cert_set_issuer (certdb, ccert, CERT_NameToAscii (&cert->issuer));
375 camel_cert_set_subject (certdb, ccert, CERT_NameToAscii (&cert->subject));
376 /* hostname is set in caller */
377 /*camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);*/
378 camel_cert_set_fingerprint (certdb, ccert, fingerprint);
379 camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
380 g_free (fingerprint);
385 /* set the 'raw' cert (& save it) */
387 camel_certdb_nss_cert_set (CamelCertDB *certdb,
389 CERTCertificate *cert)
391 gchar *filename, *fingerprint;
393 const gchar *cert_dir;
395 fingerprint = ccert->fingerprint;
397 if (ccert->rawcert == NULL)
398 ccert->rawcert = g_byte_array_new ();
400 g_byte_array_set_size (ccert->rawcert, cert->derCert.len);
401 memcpy (ccert->rawcert->data, cert->derCert.data, cert->derCert.len);
403 cert_dir = tcp_stream_ssl_get_cert_dir ();
404 filename = g_build_filename (cert_dir, fingerprint, NULL);
406 stream = camel_stream_fs_new_with_name (
407 filename, O_WRONLY | O_CREAT | O_TRUNC, 0600, NULL);
408 if (stream != NULL) {
409 if (camel_stream_write (
410 stream, (const gchar *) ccert->rawcert->data,
411 ccert->rawcert->len, NULL, NULL) == -1) {
413 "Could not save cert: %s: %s",
414 filename, g_strerror (errno));
417 camel_stream_close (stream, NULL, NULL);
418 g_object_unref (stream);
421 "Could not save cert: %s: %s",
422 filename, g_strerror (errno));
429 /* used by the mozilla-like code below */
431 get_nickname (CERTCertificate *cert)
433 gchar *server, *nick = NULL;
435 PRBool status = PR_TRUE;
437 server = CERT_GetCommonName (&cert->subject);
441 for (i = 1; status == PR_TRUE; i++) {
444 nick = g_strdup_printf ("%s #%d", server, i);
446 nick = g_strdup (server);
448 status = SEC_CertNicknameConflict (server, &cert->derSubject, cert->dbhandle);
456 tcp_stream_cancelled (GCancellable *cancellable,
459 PR_Interrupt (thread);
463 ssl_bad_cert (gpointer data,
467 CamelCertDB *certdb = NULL;
468 CamelCert *ccert = NULL;
469 gboolean ccert_is_new = FALSE;
470 gchar *prompt, *cert_str, *fingerprint;
471 CamelTcpStreamSSL *ssl;
472 CERTCertificate *cert;
473 SECStatus status = SECFailure;
475 g_return_val_if_fail (data != NULL, SECFailure);
476 g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure);
480 cert = SSL_PeerCertificate (sockfd);
484 certdb = camel_certdb_get_default ();
485 ccert = camel_certdb_nss_cert_get (certdb, cert, ssl->priv->expected_host);
487 ccert = camel_certdb_nss_cert_convert (certdb, cert);
488 camel_cert_set_hostname (certdb, ccert, ssl->priv->expected_host);
489 /* Don't put in the certdb yet. Since we can only store one
490 * entry per hostname, we'd rather not ruin any existing entry
491 * for this hostname if the user rejects the new certificate. */
495 if (ccert->trust == CAMEL_CERT_TRUST_UNKNOWN) {
496 GSList *button_captions = NULL;
499 status = CERT_VerifyCertNow (cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
500 fingerprint = cert_fingerprint (cert);
501 cert_str = g_strdup_printf (_(
506 CERT_NameToAscii (&cert->issuer),
507 CERT_NameToAscii (&cert->subject),
509 status == SECSuccess ? _("GOOD") : _("BAD"));
510 g_free (fingerprint);
512 /* construct our user prompt */
513 prompt = g_strdup_printf (
514 _("SSL Certificate for '%s' is not trusted. "
515 "Do you wish to accept it?\n\n"
516 "Detailed information about the certificate:\n%s"),
517 ssl->priv->expected_host, cert_str);
520 button_captions = g_slist_append (button_captions, _("_Reject"));
521 button_captions = g_slist_append (button_captions, _("Accept _Temporarily"));
522 button_captions = g_slist_append (button_captions, _("_Accept Permanently"));
524 /* query the user to find out if we want to accept this certificate */
525 button_id = camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, button_captions);
526 g_slist_free (button_captions);
529 accept = button_id != 0;
531 camel_certdb_nss_cert_set (certdb, ccert, cert);
532 camel_certdb_put (certdb, ccert);
537 camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_NEVER);
539 case 1: /* Accept temporarily */
540 camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_TEMPORARY);
542 case 2: /* Accept permanently */
543 camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_FULLY);
545 default: /* anything else means failure and will ask again */
549 camel_certdb_touch (certdb);
551 accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
554 camel_certdb_cert_unref (certdb, ccert);
555 camel_certdb_save (certdb);
556 g_object_unref (certdb);
558 return accept ? SECSuccess : SECFailure;
567 error = PR_GetError ();
569 /* This code is basically what mozilla does - however it doesn't seem to work here
570 * very reliably :-/ */
571 while (go && status != SECSuccess) {
572 gchar *prompt = NULL;
574 printf ("looping, error '%d'\n", error);
577 case SEC_ERROR_UNKNOWN_ISSUER:
578 case SEC_ERROR_CA_CERT_INVALID:
579 case SEC_ERROR_UNTRUSTED_ISSUER:
580 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
581 /* add certificate */
582 printf ("unknown issuer, adding ... \n");
583 prompt = g_strdup_printf (_("Certificate problem: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
585 if (camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
587 nick = get_nickname (cert);
594 printf ("adding cert '%s'\n", nick);
597 cert->trust = (CERTCertTrust *) PORT_ArenaZAlloc (cert->arena, sizeof (CERTCertTrust));
598 CERT_DecodeTrustString (cert->trust, "P");
601 certs[0] = &cert->derCert;
602 /*CERT_ImportCerts (cert->dbhandle, certUsageSSLServer, 1, certs, NULL, TRUE, FALSE, nick);*/
603 CERT_ImportCerts (cert->dbhandle, certUsageUserCertImport, 1, certs, NULL, TRUE, FALSE, nick);
606 printf (" cert type %08x\n", cert->nsCertType);
608 memset ((gpointer) &trust, 0, sizeof (trust));
609 if (CERT_GetCertTrust (cert, &trust) != SECSuccess) {
610 CERT_DecodeTrustString (&trust, "P");
612 trust.sslFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED;
613 if (CERT_ChangeCertTrust (cert->dbhandle, cert, &trust) != SECSuccess) {
614 printf ("couldn't change cert trust?\n");
617 /*status = SECSuccess;*/
620 status = CERT_VerifyCertNow (cert->dbhandle, cert, TRUE, certUsageSSLServer, NULL);
621 error = PR_GetError ();
622 printf ("re-verify status %d, error %d\n", status, error);
625 printf (" cert type %08x\n", cert->nsCertType);
627 printf ("failed/cancelled\n");
632 case SSL_ERROR_BAD_CERT_DOMAIN:
633 printf ("bad domain\n");
635 prompt = g_strdup_printf (_("Bad certificate domain: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
637 if (camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
638 host = SSL_RevealURL (sockfd);
639 status = CERT_AddOKDomainName (cert, host);
640 printf ("add ok domain name : %s\n", status == SECFailure?"fail":"ok");
641 error = PR_GetError ();
642 if (status == SECFailure)
650 case SEC_ERROR_EXPIRED_CERTIFICATE:
651 printf ("expired\n");
653 prompt = g_strdup_printf (_("Certificate expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
655 if (camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
656 cert->timeOK = PR_TRUE;
657 status = CERT_VerifyCertNow (cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
658 error = PR_GetError ();
659 if (status == SECFailure)
667 case SEC_ERROR_CRL_EXPIRED:
668 printf ("crl expired\n");
670 prompt = g_strdup_printf (_("Certificate revocation list expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
672 if (camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
673 host = SSL_RevealURL (sockfd);
674 status = CERT_AddOKDomainName (cert, host);
681 printf ("generic error\n");
689 CERT_DestroyCertificate (cert);
696 enable_ssl (CamelTcpStreamSSL *ssl,
701 g_assert (fd != NULL);
703 ssl_fd = SSL_ImportFD (NULL, fd);
707 SSL_OptionSet (ssl_fd, SSL_SECURITY, PR_TRUE);
709 if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL2) {
710 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_TRUE);
711 SSL_OptionSet (ssl_fd, SSL_V2_COMPATIBLE_HELLO, PR_TRUE);
713 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL2, PR_FALSE);
714 SSL_OptionSet (ssl_fd, SSL_V2_COMPATIBLE_HELLO, PR_FALSE);
717 if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
718 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_TRUE);
720 SSL_OptionSet (ssl_fd, SSL_ENABLE_SSL3, PR_FALSE);
722 if (ssl->priv->flags & CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
723 SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_TRUE);
725 SSL_OptionSet (ssl_fd, SSL_ENABLE_TLS, PR_FALSE);
727 SSL_SetURL (ssl_fd, ssl->priv->expected_host);
729 /* NSS provides a default implementation for the SSL_GetClientAuthDataHook callback
730 * but does not enable it by default. It must be explicltly requested by the application.
731 * See: http://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1126622 */
732 SSL_GetClientAuthDataHook (ssl_fd, (SSLGetClientAuthData) &NSS_GetClientAuthData, NULL );
734 /* NSS provides _and_ installs a default implementation for the
735 * SSL_AuthCertificateHook callback so we _don't_ need to install one. */
736 SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl);
742 enable_ssl_or_close_fd (CamelTcpStreamSSL *ssl,
748 ssl_fd = enable_ssl (ssl, fd);
749 if (ssl_fd == NULL) {
752 _set_errno_from_pr_error (PR_GetError ());
754 PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
757 _set_g_error_from_errno (error, FALSE);
766 rehandshake_ssl (PRFileDesc *fd,
767 GCancellable *cancellable,
770 SECStatus status = SECSuccess;
771 gulong cancel_id = 0;
773 if (g_cancellable_set_error_if_cancelled (cancellable, error))
776 if (G_IS_CANCELLABLE (cancellable))
777 cancel_id = g_cancellable_connect (
778 cancellable, G_CALLBACK (tcp_stream_cancelled),
779 PR_GetCurrentThread (), (GDestroyNotify) NULL);
781 if (status == SECSuccess)
782 status = SSL_ResetHandshake (fd, FALSE);
784 if (status == SECSuccess)
785 status = SSL_ForceHandshake (fd);
788 g_cancellable_disconnect (cancellable, cancel_id);
790 if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
793 } else if (status == SECFailure) {
794 _set_error_from_pr_error (error);
797 return (status == SECSuccess);
801 tcp_stream_ssl_connect (CamelTcpStream *stream,
803 const gchar *service,
805 GCancellable *cancellable,
808 CamelTcpStreamSSL *ssl = CAMEL_TCP_STREAM_SSL (stream);
811 retval = CAMEL_TCP_STREAM_CLASS (
812 camel_tcp_stream_ssl_parent_class)->connect (
813 stream, host, service, fallback_port, cancellable, error);
817 if (ssl->priv->ssl_mode) {
821 d (g_print (" enabling SSL\n"));
823 fd = camel_tcp_stream_get_file_desc (stream);
824 ssl_fd = enable_ssl_or_close_fd (ssl, fd, error);
825 _camel_tcp_stream_raw_replace_file_desc (CAMEL_TCP_STREAM_RAW (stream), ssl_fd);
828 d (g_print (" could not enable SSL\n"));
831 d (g_print (" re-handshaking SSL\n"));
833 if (!rehandshake_ssl (ssl_fd, cancellable, error)) {
834 d (g_print (" failed\n"));
844 camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *class)
846 GObjectClass *object_class;
847 CamelTcpStreamClass *tcp_stream_class;
849 g_type_class_add_private (class, sizeof (CamelTcpStreamSSLPrivate));
851 object_class = G_OBJECT_CLASS (class);
852 object_class->dispose = tcp_stream_ssl_dispose;
853 object_class->finalize = tcp_stream_ssl_finalize;
855 tcp_stream_class = CAMEL_TCP_STREAM_CLASS (class);
856 tcp_stream_class->connect = tcp_stream_ssl_connect;
860 camel_tcp_stream_ssl_init (CamelTcpStreamSSL *stream)
862 stream->priv = CAMEL_TCP_STREAM_SSL_GET_PRIVATE (stream);
866 * camel_tcp_stream_ssl_new:
867 * @session: an active #CamelSession object
868 * @expected_host: host that the stream is expected to connect with
869 * @flags: a bitwise combination of #CamelTcpStreamSSLFlags
871 * Since the SSL certificate authenticator may need to prompt the
872 * user, a #CamelSession is needed. @expected_host is needed as a
873 * protection against an MITM attack.
875 * Returns: a new #CamelTcpStreamSSL stream preset in SSL mode
878 camel_tcp_stream_ssl_new (CamelSession *session,
879 const gchar *expected_host,
880 CamelTcpStreamSSLFlags flags)
882 CamelTcpStreamSSL *stream;
884 g_assert (CAMEL_IS_SESSION (session));
886 stream = g_object_new (CAMEL_TYPE_TCP_STREAM_SSL, NULL);
888 stream->priv->session = g_object_ref (session);
889 stream->priv->expected_host = g_strdup (expected_host);
890 stream->priv->ssl_mode = TRUE;
891 stream->priv->flags = flags;
893 return CAMEL_STREAM (stream);
897 * camel_tcp_stream_ssl_new_raw:
898 * @session: an active #CamelSession object
899 * @expected_host: host that the stream is expected to connect with
900 * @flags: a bitwise combination of #CamelTcpStreamSSLFlags
902 * Since the SSL certificate authenticator may need to prompt the
903 * user, a CamelSession is needed. @expected_host is needed as a
904 * protection against an MITM attack.
906 * Returns: a new #CamelTcpStreamSSL stream not yet toggled into SSL mode
909 camel_tcp_stream_ssl_new_raw (CamelSession *session,
910 const gchar *expected_host,
911 CamelTcpStreamSSLFlags flags)
913 CamelTcpStreamSSL *stream;
915 g_assert (CAMEL_IS_SESSION (session));
917 stream = g_object_new (CAMEL_TYPE_TCP_STREAM_SSL, NULL);
919 stream->priv->session = g_object_ref (session);
920 stream->priv->expected_host = g_strdup (expected_host);
921 stream->priv->ssl_mode = FALSE;
922 stream->priv->flags = flags;
924 return CAMEL_STREAM (stream);
928 * camel_tcp_stream_ssl_enable_ssl:
929 * @ssl: a #CamelTcpStreamSSL object
930 * @cancellable: optional #GCancellable object, or %NULL
931 * @error: return location for a #GError, or %NULL
933 * Toggles an ssl-capable stream into ssl mode (if it isn't already).
935 * Returns: %0 on success or %-1 on fail
938 camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl,
939 GCancellable *cancellable,
942 PRFileDesc *fd, *ssl_fd;
944 g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (ssl), -1);
946 fd = camel_tcp_stream_get_file_desc (CAMEL_TCP_STREAM (ssl));
948 if (fd && !ssl->priv->ssl_mode) {
949 if (!(ssl_fd = enable_ssl (ssl, fd))) {
950 _set_error_from_pr_error (error);
954 _camel_tcp_stream_raw_replace_file_desc (CAMEL_TCP_STREAM_RAW (ssl), ssl_fd);
955 ssl->priv->ssl_mode = TRUE;
957 if (!rehandshake_ssl (ssl_fd, cancellable, error))
961 ssl->priv->ssl_mode = TRUE;