src/mn-ssl.c (4221B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
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 3 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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <errno.h>
21 #include <glib.h>
22 #include <glib/gi18n.h>
23
24 typedef GMutex CRYPTO_dynlock_value;
25
26 #include <openssl/ssl.h>
27 #include <openssl/crypto.h>
28 #include <openssl/err.h>
29 #include "mn-ssl.h"
30
31 static gboolean attempted = FALSE;
32 static SSL_CTX *ctx = NULL;
33 static char *init_error = NULL;
34
35 static int num_locks;
36 static GMutex **locks;
37
38 /* a general purpose global SSL lock */
39 G_LOCK_DEFINE(mn_ssl);
40
41 static void
42 locking_cb (int mode, int n, const char *file, int line)
43 {
44 g_assert(n >= 0 && n < num_locks);
45
46 if ((mode & CRYPTO_LOCK) != 0)
47 g_mutex_lock(locks[n]);
48 else
49 g_mutex_unlock(locks[n]);
50 }
51
52 static unsigned long
53 id_cb (void)
54 {
55 return (unsigned long) g_thread_self();
56 }
57
58 static struct CRYPTO_dynlock_value *
59 dynlock_create_cb (const char *file, int line)
60 {
61 return (struct CRYPTO_dynlock_value *) g_mutex_new();
62 }
63
64 static void
65 dynlock_locking_cb (int mode,
66 struct CRYPTO_dynlock_value *lock,
67 const char *file,
68 int line)
69 {
70 if ((mode & CRYPTO_LOCK) != 0)
71 g_mutex_lock((GMutex *) lock);
72 else
73 g_mutex_unlock((GMutex *) lock);
74 }
75
76 static void
77 dynlock_destroy_cb (struct CRYPTO_dynlock_value *lock,
78 const char *file,
79 int line)
80 {
81 g_mutex_free((GMutex *) lock);
82 }
83
84 static void
85 init_threading (void)
86 {
87 int i;
88
89 num_locks = CRYPTO_num_locks();
90
91 locks = g_new(GMutex *, num_locks);
92 for (i = 0; i < num_locks; i++)
93 locks[i] = g_mutex_new();
94
95 CRYPTO_set_locking_callback(locking_cb);
96 CRYPTO_set_id_callback(id_cb);
97
98 CRYPTO_set_dynlock_create_callback(dynlock_create_cb);
99 CRYPTO_set_dynlock_lock_callback(dynlock_locking_cb);
100 CRYPTO_set_dynlock_destroy_callback(dynlock_destroy_cb);
101 }
102
103 SSL_CTX *
104 mn_ssl_init (GError **err)
105 {
106 /*
107 * SSL_CTX_new() will fail the second time it is called, so we just
108 * keep the same context for the whole application lifetime.
109 */
110
111 G_LOCK(mn_ssl);
112 if (! attempted)
113 {
114 SSL_library_init();
115 SSL_load_error_strings();
116
117 init_threading();
118
119 ctx = SSL_CTX_new(SSLv23_client_method());
120 if (ctx)
121 {
122 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
123 SSL_CTX_set_default_verify_paths(ctx); /* #19578 */
124 }
125 else
126 init_error = g_strdup(mn_ssl_get_error());
127
128 attempted = TRUE;
129 }
130
131 if (! ctx)
132 {
133 g_assert(init_error != NULL);
134 g_set_error(err, 0, 0, "%s", init_error);
135 }
136 G_UNLOCK(mn_ssl);
137
138 return ctx;
139 }
140
141 const char *
142 mn_ssl_get_error (void)
143 {
144 static GStaticPrivate buf_key = G_STATIC_PRIVATE_INIT;
145 char *buf;
146
147 /*
148 * We use a per-thread buffer so that the caller does not have to
149 * free the returned string.
150 */
151
152 buf = g_static_private_get(&buf_key);
153 if (! buf)
154 {
155 buf = g_new(char, 120); /* the size is specified by the manpage */
156 g_static_private_set(&buf_key, buf, g_free);
157 }
158
159 return ERR_error_string(ERR_get_error(), buf);
160 }
161
162 const char *
163 mn_ssl_get_io_error (const SSL *ssl, int ret)
164 {
165 g_return_val_if_fail(ssl != NULL, NULL);
166
167 switch (SSL_get_error(ssl, ret))
168 {
169 case SSL_ERROR_SYSCALL:
170 if (ERR_peek_error() == 0)
171 switch (ret)
172 {
173 case 0:
174 return "EOF";
175
176 case -1:
177 /*
178 * This assumes that a UNIX BIO is in use (it is always
179 * the case in MN).
180 */
181 return g_strerror(errno);
182 }
183 else
184 return mn_ssl_get_error();
185 break;
186
187 case SSL_ERROR_SSL:
188 return mn_ssl_get_error();
189 }
190
191 return _("unknown SSL/TLS error");
192 }