aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-tcp-stream-ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-tcp-stream-ssl.c')
-rw-r--r--camel/camel-tcp-stream-ssl.c56
1 files changed, 55 insertions, 1 deletions
diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c
index 42255562bc..e9afb768bc 100644
--- a/camel/camel-tcp-stream-ssl.c
+++ b/camel/camel-tcp-stream-ssl.c
@@ -44,6 +44,12 @@ static int stream_connect (CamelTcpStream *stream, struct hostent *host, int
static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
+/* callbacks */
+
+static SECStatus ssl_bad_cert (void *data, PRFileDesc *fd);
+static SECStatus ssl_auth_cert (void *data, PRFileDesc *fd, PRBool checksig, PRBool is_server);
+
+
static void
camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *camel_tcp_stream_ssl_class)
{
@@ -71,6 +77,8 @@ camel_tcp_stream_ssl_init (gpointer object, gpointer klass)
CamelTcpStreamSSL *stream = CAMEL_TCP_STREAM_SSL (object);
stream->sockfd = NULL;
+ stream->session = NULL;
+ stream->expected_host = NULL;
}
static void
@@ -80,6 +88,9 @@ camel_tcp_stream_ssl_finalize (CamelObject *object)
if (stream->sockfd != NULL)
PR_Close (stream->sockfd);
+
+ camel_object_unref (CAMEL_OBJECT (stream->session));
+ g_free (stream->expected_host);
}
@@ -105,16 +116,26 @@ camel_tcp_stream_ssl_get_type (void)
/**
* camel_tcp_stream_ssl_new:
+ * @session: camel session
+ * @expected_host: host that the stream is expected to connect with.
+ *
+ * Since the SSL certificate authenticator may need to prompt the
+ * user, a CamelSession is needed. #expected_host is needed as a
+ * protection against an MITM attack.
*
* Return value: a tcp stream
**/
CamelStream *
-camel_tcp_stream_ssl_new ()
+camel_tcp_stream_ssl_new (CamelSession *session, const char *expected_host)
{
CamelTcpStreamSSL *stream;
stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
+ camel_object_ref (CAMEL_OBJECT (session));
+ stream->session = session;
+ stream->expected_host = g_strdup (expected_host);
+
return CAMEL_STREAM (stream);
}
@@ -167,6 +188,34 @@ stream_close (CamelStream *stream)
}
+static SECStatus
+ssl_auth_cert (void *data, PRFileDesc *fd, PRBool checksig, PRBool is_server)
+{
+ return SSL_AuthCertificate (NULL, fd, TRUE, FALSE);
+}
+
+static SECStatus
+ssl_bad_cert (void *data, PRFileDesc *fd)
+{
+ CamelSession *session;
+ char *string;
+ PRInt32 len;
+
+ g_return_val_if_fail (data != NULL, SECFailure);
+ g_return_val_if_fail (CAMEL_IS_SESSION (data), SECFailure);
+
+ /* FIXME: International issues here?? */
+ len = PR_GetErrorTextLen (PR_GetError ());
+ string = g_malloc0 (len);
+ PR_GetErrorText (string);
+
+ session = CAMEL_SESSION (data);
+ if (camel_session_query_cert_authenticator (session, string))
+ return SECSuccess;
+
+ return SECFailure;
+}
+
static int
stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
{
@@ -186,6 +235,8 @@ stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
fd = PR_OpenTCPSocket (host->h_addrtype);
ssl_fd = SSL_ImportFD (NULL, fd);
+ SSL_SetUrl (ssl_fd, ssl->expected_host);
+
if (ssl_fd == NULL || PR_Connect (ssl_fd, &netaddr, timeout) == PR_FAILURE) {
if (ssl_fd != NULL)
PR_Close (ssl_fd);
@@ -193,6 +244,9 @@ stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
return -1;
}
+ SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, NULL);
+ SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl->session);
+
ssl->sockfd = ssl_fd;
return 0;