1 // MaCoPiX = Mascot Construnctive Pilot for X
2 // (ActX / Gtk+ Evolution)
3 //
4 //
5 // ssl.c
6 // Management of SSL (OpenSSL) for POP3 access
7 // originated from Libsylph 2.4.0 (see blow)
8 //
9 // Copyright 2002-2007 K.Chimari
10 // http://rosegray.sakura.ne.jp/
11 //
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 //
27
28
29 /*
30 * LibSylph -- E-Mail client library
31 * Copyright (C) 1999-2006 Hiroyuki Yamamoto
32 *
33 * This library is free software; you can redistribute it and/or
34 * modify it under the terms of the GNU Lesser General Public
35 * License as published by the Free Software Foundation; either
36 * version 2.1 of the License, or (at your option) any later version.
37 *
38 * This library is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41 * Lesser General Public License for more details.
42 *
43 * You should have received a copy of the GNU Lesser General Public
44 * License along with this library; if not, write to the Free Software
45 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 */
47
48 #ifdef HAVE_CONFIG_H
49 # include "config.h"
50 #endif
51
52 #ifdef USE_SSL
53
54 #include "main.h"
55
56 #include<unistd.h>
57 #include "ssl.h"
58
59 #ifdef USE_WIN32
60 #define usleep(x) g_usleep(x)
61 #endif
62
63 static SSL_CTX *ssl_ctx_SSLv23 = NULL;
64 static SSL_CTX *ssl_ctx_TLSv1 = NULL;
65
66 static GSList *trust_list = NULL;
67 static GSList *reject_list = NULL;
68
69 extern void pop_debug_print(const gchar *format, ...);
70 #ifdef USE_WIN32
71 extern gchar *get_win_home();
72 #endif
73 extern gchar *get_rc_dir();
74
75 gboolean is_dir_exist(const gchar *dir);
76
77
find_certs_file(const gchar * certs_dir)78 static gchar *find_certs_file(const gchar *certs_dir)
79 {
80 gchar *certs_file;
81
82 #define LOOK_FOR(crt) \
83 { \
84 certs_file = g_strconcat(certs_dir, G_DIR_SEPARATOR_S, crt, NULL); \
85 pop_debug_print("looking for %s\n", certs_file); \
86 if (access(certs_file,F_OK)==0) \
87 return certs_file; \
88 if(certs_file) g_free(certs_file); \
89 }
90
91 if (certs_dir) {
92 LOOK_FOR("ca-certificates.crt");
93 LOOK_FOR("ca-bundle.crt");
94 LOOK_FOR("ca-root.crt");
95 LOOK_FOR("certs.crt");
96 }
97
98 #undef LOOK_FOR
99
100 return NULL;
101 }
102
ssl_init(void)103 void ssl_init(void)
104 {
105 gchar *certs_file = NULL, *certs_dir;
106
107 SSL_library_init();
108 SSL_load_error_strings();
109
110 certs_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "certs", NULL);
111 if (!is_dir_exist(certs_dir)) {
112 pop_debug_print("ssl_init(): %s doesn't exist, or not a directory.\n",
113 certs_dir);
114 if(certs_dir) g_free(certs_dir);
115 #ifdef USE_WIN32
116 certs_dir = g_strconcat(get_win_home(), G_DIR_SEPARATOR_S
117 "etc" G_DIR_SEPARATOR_S
118 "ssl" G_DIR_SEPARATOR_S "certs", NULL);
119 #else
120 certs_dir = g_strdup("/etc/ssl/certs");
121 #endif
122 if (!is_dir_exist(certs_dir)) {
123 pop_debug_print("ssl_init(): %s doesn't exist, or not a directory.\n",
124 certs_dir);
125 if(certs_dir) g_free(certs_dir);
126 certs_dir = NULL;
127 }
128 }
129 if (certs_dir)
130 pop_debug_print("ssl_init(): certs dir %s found.\n", certs_dir);
131
132 certs_file = find_certs_file(get_rc_dir());
133
134 if (certs_dir && !certs_file)
135 certs_file = find_certs_file(certs_dir);
136
137 if (!certs_file) {
138 #ifdef USE_WIN32
139 certs_dir = g_strconcat(get_win_home(),
140 G_DIR_SEPARATOR_S "etc"
141 G_DIR_SEPARATOR_S "ssl", NULL);
142 certs_file = find_certs_file(certs_dir);
143 if(certs_dir) g_free(certs_dir);
144 certs_dir = NULL;
145 if (!certs_file) {
146 certs_dir = g_strconcat(get_win_home(),
147 G_DIR_SEPARATOR_S "etc", NULL);
148 certs_file = find_certs_file(certs_dir);
149 if(certs_dir) g_free(certs_dir);
150 certs_dir = NULL;
151 }
152 #else
153 certs_file = find_certs_file("/etc/ssl");
154 if (!certs_file)
155 certs_file = find_certs_file("/etc");
156 #endif
157 }
158
159 if (certs_file)
160 pop_debug_print("ssl_init(): certs file %s found.\n", certs_file);
161
162 ssl_ctx_SSLv23 = SSL_CTX_new(SSLv23_client_method());
163 if (ssl_ctx_SSLv23 == NULL) {
164 pop_debug_print("SSLv23 not available\n");
165 } else {
166 pop_debug_print("SSLv23 available\n");
167 if ((certs_file || certs_dir)
168 #ifdef USE_OPENSSL
169 &&
170 !SSL_CTX_load_verify_locations(ssl_ctx_SSLv23, certs_file, certs_dir)
171 #endif
172 )
173 g_warning("SSLv23 SSL_CTX_load_verify_locations failed.\n");
174 }
175
176 ssl_ctx_TLSv1 = SSL_CTX_new(TLSv1_client_method());
177 if (ssl_ctx_TLSv1 == NULL) {
178 pop_debug_print("TLSv1 not available\n");
179 } else {
180 pop_debug_print("TLSv1 available\n");
181 if ((certs_file || certs_dir)
182 #ifdef USE_OPENSSL
183 &&
184 !SSL_CTX_load_verify_locations(ssl_ctx_TLSv1, certs_file, certs_dir)
185 #endif
186 )
187 g_warning("TLSv1 SSL_CTX_load_verify_locations failed.\n");
188 }
189
190 if(certs_dir) g_free(certs_dir);
191 }
192
ssl_done(void)193 void ssl_done(void)
194 {
195 GSList *cur;
196
197 pop_debug_print("SSL done : in\n");
198
199 for (cur = trust_list; cur != NULL; cur = cur->next)
200 X509_free((X509 *)cur->data);
201 g_slist_free(trust_list);
202 trust_list = NULL;
203 for (cur = reject_list; cur != NULL; cur = cur->next)
204 X509_free((X509 *)cur->data);
205 g_slist_free(reject_list);
206 reject_list = NULL;
207
208 if (ssl_ctx_SSLv23) {
209 SSL_CTX_free(ssl_ctx_SSLv23);
210 ssl_ctx_SSLv23 = NULL;
211 }
212
213 if (ssl_ctx_TLSv1) {
214 SSL_CTX_free(ssl_ctx_TLSv1);
215 ssl_ctx_TLSv1 = NULL;
216 }
217 pop_debug_print("SSL done : out\n");
218 }
219
ssl_init_socket(gint fd,char * hostname,gint ssl_cert_res,gchar ** subject,gchar ** issue,glong * verify)220 SSL *ssl_init_socket(gint fd, char *hostname, gint ssl_cert_res,
221 gchar **subject, gchar **issue, glong *verify)
222 {
223 return ssl_init_socket_with_method(fd, hostname, ssl_cert_res,
224 subject, issue, verify, SSL_METHOD_SSLv23);
225 }
226
x509_cmp_func(gconstpointer a,gconstpointer b)227 static gint x509_cmp_func(gconstpointer a, gconstpointer b)
228 {
229 const X509 *xa = a;
230 const X509 *xb = b;
231
232 return X509_cmp(xa, xb);
233 }
234
235 #ifdef USE_GNUTLS
X509_dup(const X509 * cert)236 X509* X509_dup(const X509* cert) {
237 X509* ret;
238 ret = (X509*)malloc(sizeof(X509));
239 ret->size = cert->size;
240 ret->data = (unsigned char *)malloc(sizeof(unsigned char)*ret->size);
241 memcpy(ret->data,cert->data,ret->size);
242 return ret;
243 }
244
X509_cmp(const X509 * cert1,const X509 * cert2)245 int X509_cmp(const X509* cert1,const X509* cert2) {
246 if (cert1->size != cert2->size) {
247 return (cert1->size!=cert2->size);
248 }
249 return memcmp(cert1->data,cert2->data,cert1->size);
250 }
251 #endif //USE_GNUTLS
252
ssl_init_socket_with_method(gint fd,char * hostname,gint ssl_cert_res,gchar ** subject,gchar ** issue,glong * verify,SSLMethod method)253 SSL *ssl_init_socket_with_method(gint fd, char *hostname, gint ssl_cert_res,
254 gchar **subject, gchar **issue,
255 glong *verify, SSLMethod method)
256 {
257 X509 *server_cert;
258 gint err, ret;
259 SSL *ssl;
260
261 switch (method) {
262 case SSL_METHOD_SSLv23:
263 if (!ssl_ctx_SSLv23) {
264 g_warning("SSL method not available\n");
265 return(NULL);
266 }
267 ssl = SSL_new(ssl_ctx_SSLv23);
268 break;
269 case SSL_METHOD_TLSv1:
270 if (!ssl_ctx_TLSv1) {
271 g_warning("SSL method not available\n");
272 return(NULL);
273 }
274 ssl = SSL_new(ssl_ctx_TLSv1);
275 break;
276 default:
277 g_warning("Unknown SSL method *PROGRAM BUG*\n");
278 return(NULL);
279 break;
280 }
281
282 if (ssl == NULL) {
283 g_warning("Error creating ssl context\n");
284 return(NULL);
285 }
286
287 SSL_set_fd(ssl, fd);
288 while ((ret = SSL_connect(ssl)) != 1) {
289 err = SSL_get_error(ssl, ret);
290 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
291 usleep(100000);
292 g_warning("SSL_connect(): try again\n");
293 continue;
294 }
295 g_warning("SSL_connect() failed with error %d, ret = %d (%s)\n",
296 err, ret, ERR_error_string(ERR_get_error(), NULL));
297 ssl_done_socket(ssl);
298 return(NULL);
299 }
300
301 /* Get the cipher */
302
303 pop_debug_print("SSL connection using %s\n",
304 SSL_get_cipher(ssl));
305
306 /* Get server's certificate (note: beware of dynamic allocation) */
307
308 if ((server_cert = SSL_get_peer_certificate(ssl)) != NULL) {
309 gchar *str;
310 glong verify_result;
311 gchar *str_issuer, *str_subject;
312
313 str_issuer=NULL;
314 str_subject=NULL;
315
316 pop_debug_print("Server certificate:\n");
317
318 if ((str = g_strdup(X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0))) != NULL) {
319 pop_debug_print(" Subject: %s\n", str);
320 if(ssl_cert_res==SSL_CERT_NONE)
321 str_subject=g_strdup(str);
322 if(str) g_free(str);
323 }
324
325 if ((str = g_strdup(X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0))) != NULL) {
326 pop_debug_print(" Issuer: %s\n", str);
327 if(ssl_cert_res==SSL_CERT_NONE)
328 str_issuer=g_strdup(str);
329 if(str) g_free(str);
330 }
331
332 #ifdef USE_GNUTLS
333 verify_result = 0 /*SSL_get_verify_result(ssl)*/;
334 if (verify_result == 0 /*X509_V_OK*/) {
335 #else //OPEN_SSL
336 verify_result = SSL_get_verify_result(ssl);
337 if (verify_result == X509_V_OK) {
338 #endif
339 pop_debug_print("SSL verify OK\n");
340 X509_free(server_cert);
341 return(ssl);
342 } else if (g_slist_find_custom(trust_list, server_cert,
343 x509_cmp_func)) {
344 pop_debug_print("SSL certificate of %s previously accepted\n", hostname);
345 X509_free(server_cert);
346 return(ssl);
347 } else if (g_slist_find_custom(reject_list, server_cert,
348 x509_cmp_func)) {
349 pop_debug_print("SSL certificate of %s previously rejected\n", hostname);
350 X509_free(server_cert);
351 ssl_done_socket(ssl);
352 return(NULL);
353 }
354
355 pop_debug_print("%s: SSL certificate verify failed (%ld: %s)\n",
356 hostname, verify_result,
357 #ifdef USE_GNUTLS
358 /*X509_verify_cert_error_string(verify_result)*/"FIXME");
359 #else
360 X509_verify_cert_error_string(verify_result));
361 #endif
362
363 {
364 gint res;
365
366 //res = ssl_manager_verify_cert(fd, hostname,
367 // server_cert, verify_result);
368 // It is difficult to call GUI
369 // from forked background job (UNIX ver)
370 // So, basical temp accept should be applied.
371 switch(ssl_cert_res){
372 case SSL_CERT_NONE:
373 #ifdef USE_GNUTLS
374 if(verify_result == /*X509_V_OK*/0){
375 #else
376 if(verify_result == X509_V_OK){
377 #endif
378 res=0;
379 }
380 else{
381 if(*subject) g_free(*subject);
382 *subject = g_strdup(str_subject);
383 if(str_subject) g_free(str_subject);
384
385 if(*issue) g_free(*issue);
386 *issue = g_strdup(str_issuer);
387 if(str_issuer) g_free(str_issuer);
388
389 *verify=verify_result;
390
391 X509_free(server_cert);
392 ssl_done_socket(ssl);
393 return(NULL);
394 }
395 break;
396 case SSL_CERT_DENY:
397 res=-1;
398 break;
399 case SSL_CERT_ACCEPT:
400 res=0;
401 break;
402 }
403
404 /* 0: accept 1: temporarily accept -1: reject */
405 if (res < 0) {
406 pop_debug_print("SSL certificate of %s rejected\n",
407 hostname);
408 #if 0
409 reject_list = g_slist_prepend
410 (reject_list, X509_dup(server_cert));
411 #endif
412 X509_free(server_cert);
413 ssl_done_socket(ssl);
414 return(NULL);
415 } else if (res > 0) {
416 pop_debug_print("Temporarily accept SSL certificate of %s\n", hostname);
417 trust_list = g_slist_prepend
418 (trust_list, X509_dup(server_cert));
419 } else {
420 pop_debug_print("Permanently accept SSL certificate of %s\n", hostname);
421 /* TODO: save server cert */
422 trust_list = g_slist_prepend
423 (trust_list, X509_dup(server_cert));
424 }
425 }
426
427 X509_free(server_cert);
428 } else {
429 g_warning("%s: couldn't get SSL certificate\n",
430 hostname);
431 ssl_done_socket(ssl);
432 return(NULL);
433 }
434
435 return(ssl);
436 }
437
438 void ssl_done_socket(SSL *ssl)
439 {
440 if (ssl) {
441 SSL_free(ssl);
442 }
443 }
444
445 gboolean is_dir_exist(const gchar *dir)
446 {
447 if (dir == NULL)
448 return FALSE;
449
450 #ifdef USE_GTK2
451 return g_file_test(dir, G_FILE_TEST_IS_DIR);
452 #else
453 if(access(dir,F_OK)) return(TRUE);
454 else return(FALSE);
455 #endif
456 }
457
458 #endif /* USE_SSL */
459
460