2007-01-20 [colin] 2.7.1cvs40
[claws.git] / src / common / ssl.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #if USE_OPENSSL
25
26 #include "defs.h"
27
28 #include <glib.h>
29 #include <glib/gi18n.h>
30
31 #include "claws.h"
32 #include "utils.h"
33 #include "ssl.h"
34 #include "ssl_certificate.h"
35
36 #ifdef HAVE_LIBETPAN
37 #include <libetpan/mailstream_ssl.h>
38 #endif
39
40 #ifdef USE_PTHREAD
41 #include <pthread.h>
42 #endif
43
44 #ifdef USE_PTHREAD
45 typedef struct _thread_data {
46         SSL *ssl;
47         gboolean done;
48 } thread_data;
49 #endif
50
51
52 static SSL_CTX *ssl_ctx;
53
54 void ssl_init(void)
55 {
56         SSL_METHOD *meth;
57
58         /* Global system initialization*/
59         SSL_library_init();
60         SSL_load_error_strings();
61
62 #ifdef HAVE_LIBETPAN
63         mailstream_openssl_init_not_required();
64 #endif  
65
66         /* Create our context*/
67         meth = SSLv23_client_method();
68         ssl_ctx = SSL_CTX_new(meth);
69
70         /* Set default certificate paths */
71         SSL_CTX_set_default_verify_paths(ssl_ctx);
72         
73 #if (OPENSSL_VERSION_NUMBER < 0x0090600fL)
74         SSL_CTX_set_verify_depth(ssl_ctx,1);
75 #endif
76 }
77
78 void ssl_done(void)
79 {
80         if (!ssl_ctx)
81                 return;
82         
83         SSL_CTX_free(ssl_ctx);
84 }
85
86 #ifdef USE_PTHREAD
87 static void *SSL_connect_thread(void *data)
88 {
89         thread_data *td = (thread_data *)data;
90         int result = -1;
91
92         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
93         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
94
95         result = SSL_connect(td->ssl);
96         td->done = TRUE; /* let the caller thread join() */
97         return GINT_TO_POINTER(result);
98 }
99 #endif
100
101 static gint SSL_connect_nb(SSL *ssl)
102 {
103 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
104         thread_data *td = g_new0(thread_data, 1);
105         pthread_t pt;
106         void *res = NULL;
107         time_t start_time = time(NULL);
108         gboolean killed = FALSE;
109         
110         td->ssl  = ssl;
111         td->done = FALSE;
112         
113         /* try to create a thread to initialize the SSL connection,
114          * fallback to blocking method in case of problem 
115          */
116         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, 
117                         SSL_connect_thread, td) != 0)
118                 return SSL_connect(ssl);
119         
120         debug_print("waiting for SSL_connect thread...\n");
121         while(!td->done) {
122                 /* don't let the interface freeze while waiting */
123                 claws_do_idle();
124                 if (time(NULL) - start_time > 30) {
125                         pthread_cancel(pt);
126                         td->done = TRUE;
127                         killed = TRUE;
128                 }
129         }
130
131         /* get the thread's return value and clean its resources */
132         pthread_join(pt, &res);
133         g_free(td);
134         
135         if (killed) {
136                 res = GINT_TO_POINTER(-1);
137         }
138         debug_print("SSL_connect thread returned %d\n", 
139                         GPOINTER_TO_INT(res));
140         
141         return GPOINTER_TO_INT(res);
142 #else
143         return SSL_connect(ssl);
144 #endif
145 }
146
147 gboolean ssl_init_socket(SockInfo *sockinfo)
148 {
149         return ssl_init_socket_with_method(sockinfo, SSL_METHOD_SSLv23);
150 }
151
152 gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method)
153 {
154         X509 *server_cert;
155         SSL *ssl;
156
157         ssl = SSL_new(ssl_ctx);
158         if (ssl == NULL) {
159                 g_warning(_("Error creating ssl context\n"));
160                 return FALSE;
161         }
162
163         switch (method) {
164         case SSL_METHOD_SSLv23:
165                 debug_print("Setting SSLv23 client method\n");
166                 SSL_set_ssl_method(ssl, SSLv23_client_method());
167                 break;
168         case SSL_METHOD_TLSv1:
169                 debug_print("Setting TLSv1 client method\n");
170                 SSL_set_ssl_method(ssl, TLSv1_client_method());
171                 break;
172         default:
173                 break;
174         }
175
176         SSL_set_fd(ssl, sockinfo->sock);
177         if (SSL_connect_nb(ssl) == -1) {
178                 g_warning(_("SSL connect failed (%s)\n"),
179                             ERR_error_string(ERR_get_error(), NULL));
180                 SSL_free(ssl);
181                 return FALSE;
182         }
183
184         /* Get the cipher */
185
186         debug_print("SSL connection using %s\n", SSL_get_cipher(ssl));
187
188         /* Get server's certificate (note: beware of dynamic allocation) */
189         if ((server_cert = SSL_get_peer_certificate(ssl)) == NULL) {
190                 debug_print("server_cert is NULL ! this _should_not_ happen !\n");
191                 SSL_free(ssl);
192                 return FALSE;
193         }
194
195
196         if (!ssl_certificate_check(server_cert, sockinfo->canonical_name, sockinfo->hostname, sockinfo->port)) {
197                 X509_free(server_cert);
198                 SSL_free(ssl);
199                 return FALSE;
200         }
201
202
203         X509_free(server_cert);
204         sockinfo->ssl = ssl;
205
206         return TRUE;
207 }
208
209 void ssl_done_socket(SockInfo *sockinfo)
210 {
211         if (sockinfo && sockinfo->ssl) {
212                 SSL_free(sockinfo->ssl);
213                 sockinfo->ssl = NULL;
214         }
215 }
216
217 #endif /* USE_OPENSSL */