2005-11-09 [colin] 1.9.100cvs5
[claws.git] / src / common / ssl.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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 "sylpheed.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 void *SSL_connect_thread(void *data)
88 {
89         thread_data *td = (thread_data *)data;
90         int result = SSL_connect(td->ssl);
91         td->done = TRUE; /* let the caller thread join() */
92         return GINT_TO_POINTER(result);
93 }
94 #endif
95
96 gint SSL_connect_nb(SSL *ssl)
97 {
98 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
99         thread_data *td = g_new0(thread_data, 1);
100         pthread_t pt;
101         void *res = NULL;
102         
103         td->ssl  = ssl;
104         td->done = FALSE;
105         
106         /* try to create a thread to initialize the SSL connection,
107          * fallback to blocking method in case of problem 
108          */
109         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, 
110                         SSL_connect_thread, td) != 0)
111                 return SSL_connect(ssl);
112         
113         debug_print("waiting for SSL_connect thread...\n");
114         while(!td->done) {
115                 /* don't let the interface freeze while waiting */
116                 sylpheed_do_idle();
117         }
118
119         /* get the thread's return value and clean its resources */
120         pthread_join(pt, &res);
121         g_free(td);
122
123         debug_print("SSL_connect thread returned %d\n", 
124                         GPOINTER_TO_INT(res));
125         
126         return GPOINTER_TO_INT(res);
127 #else
128         return SSL_connect(ssl);
129 #endif
130 }
131
132 gboolean ssl_init_socket(SockInfo *sockinfo)
133 {
134         return ssl_init_socket_with_method(sockinfo, SSL_METHOD_SSLv23);
135 }
136
137 gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method)
138 {
139         X509 *server_cert;
140         SSL *ssl;
141
142         ssl = SSL_new(ssl_ctx);
143         if (ssl == NULL) {
144                 g_warning(_("Error creating ssl context\n"));
145                 return FALSE;
146         }
147
148         switch (method) {
149         case SSL_METHOD_SSLv23:
150                 debug_print("Setting SSLv23 client method\n");
151                 SSL_set_ssl_method(ssl, SSLv23_client_method());
152                 break;
153         case SSL_METHOD_TLSv1:
154                 debug_print("Setting TLSv1 client method\n");
155                 SSL_set_ssl_method(ssl, TLSv1_client_method());
156                 break;
157         default:
158                 break;
159         }
160
161         SSL_set_fd(ssl, sockinfo->sock);
162         if (SSL_connect_nb(ssl) == -1) {
163                 g_warning(_("SSL connect failed (%s)\n"),
164                             ERR_error_string(ERR_get_error(), NULL));
165                 SSL_free(ssl);
166                 return FALSE;
167         }
168
169         /* Get the cipher */
170
171         debug_print("SSL connection using %s\n", SSL_get_cipher(ssl));
172
173         /* Get server's certificate (note: beware of dynamic allocation) */
174         if ((server_cert = SSL_get_peer_certificate(ssl)) == NULL) {
175                 debug_print("server_cert is NULL ! this _should_not_ happen !\n");
176                 SSL_free(ssl);
177                 return FALSE;
178         }
179
180
181         if (!ssl_certificate_check(server_cert, sockinfo->hostname, sockinfo->port)) {
182                 X509_free(server_cert);
183                 SSL_free(ssl);
184                 return FALSE;
185         }
186
187
188         X509_free(server_cert);
189         sockinfo->ssl = ssl;
190
191         return TRUE;
192 }
193
194 void ssl_done_socket(SockInfo *sockinfo)
195 {
196         if (sockinfo->ssl) {
197                 SSL_free(sockinfo->ssl);
198         }
199 }
200
201 #endif /* USE_OPENSSL */