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