14192
|
1 /**
|
|
2 * @file ssl-gnutls.c GNUTLS SSL plugin.
|
|
3 *
|
|
4 * gaim
|
|
5 *
|
|
6 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
|
|
7 *
|
|
8 * This program is free software; you can redistribute it and/or modify
|
|
9 * it under the terms of the GNU General Public License as published by
|
|
10 * the Free Software Foundation; either version 2 of the License, or
|
|
11 * (at your option) any later version.
|
|
12 *
|
|
13 * This program is distributed in the hope that it will be useful,
|
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 * GNU General Public License for more details.
|
|
17 *
|
|
18 * You should have received a copy of the GNU General Public License
|
|
19 * along with this program; if not, write to the Free Software
|
|
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
21 */
|
|
22 #include "internal.h"
|
|
23 #include "debug.h"
|
|
24 #include "plugin.h"
|
|
25 #include "sslconn.h"
|
|
26 #include "version.h"
|
|
27
|
|
28 #define SSL_GNUTLS_PLUGIN_ID "ssl-gnutls"
|
|
29
|
|
30 #ifdef HAVE_GNUTLS
|
|
31
|
|
32 #include <gnutls/gnutls.h>
|
|
33
|
|
34 typedef struct
|
|
35 {
|
|
36 gnutls_session session;
|
|
37 guint handshake_handler;
|
|
38 } GaimSslGnutlsData;
|
|
39
|
|
40 #define GAIM_SSL_GNUTLS_DATA(gsc) ((GaimSslGnutlsData *)gsc->private_data)
|
|
41
|
|
42 static gnutls_certificate_client_credentials xcred;
|
|
43
|
|
44 static void
|
|
45 ssl_gnutls_init_gnutls(void)
|
|
46 {
|
|
47 gnutls_global_init();
|
|
48
|
|
49 gnutls_certificate_allocate_credentials(&xcred);
|
|
50 gnutls_certificate_set_x509_trust_file(xcred, "ca.pem",
|
|
51 GNUTLS_X509_FMT_PEM);
|
|
52 }
|
|
53
|
|
54 static gboolean
|
|
55 ssl_gnutls_init(void)
|
|
56 {
|
|
57 return TRUE;
|
|
58 }
|
|
59
|
|
60 static void
|
|
61 ssl_gnutls_uninit(void)
|
|
62 {
|
|
63 gnutls_global_deinit();
|
|
64
|
|
65 gnutls_certificate_free_credentials(xcred);
|
|
66 }
|
|
67
|
|
68
|
|
69 static void ssl_gnutls_handshake_cb(gpointer data, gint source,
|
|
70 GaimInputCondition cond)
|
|
71 {
|
|
72 GaimSslConnection *gsc = data;
|
|
73 GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
|
|
74 ssize_t ret;
|
|
75
|
|
76 gaim_debug_info("gnutls", "Handshaking\n");
|
|
77 ret = gnutls_handshake(gnutls_data->session);
|
|
78
|
|
79 if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
|
|
80 return;
|
|
81
|
|
82 gaim_input_remove(gnutls_data->handshake_handler);
|
|
83 gnutls_data->handshake_handler = 0;
|
|
84
|
|
85 if(ret != 0) {
|
|
86 gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret);
|
|
87
|
|
88 if(gsc->error_cb != NULL)
|
|
89 gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED,
|
|
90 gsc->connect_cb_data);
|
|
91
|
|
92 gaim_ssl_close(gsc);
|
|
93 } else {
|
|
94 gaim_debug_info("gnutls", "Handshake complete\n");
|
|
95
|
|
96 gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
|
|
97 }
|
|
98
|
|
99 }
|
|
100
|
|
101
|
|
102 static void
|
|
103 ssl_gnutls_connect(GaimSslConnection *gsc)
|
|
104 {
|
|
105 GaimSslGnutlsData *gnutls_data;
|
|
106 static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 };
|
|
107
|
|
108 gnutls_data = g_new0(GaimSslGnutlsData, 1);
|
|
109 gsc->private_data = gnutls_data;
|
|
110
|
|
111 gnutls_init(&gnutls_data->session, GNUTLS_CLIENT);
|
|
112 gnutls_set_default_priority(gnutls_data->session);
|
|
113
|
|
114 gnutls_certificate_type_set_priority(gnutls_data->session,
|
|
115 cert_type_priority);
|
|
116
|
|
117 gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE,
|
|
118 xcred);
|
|
119
|
|
120 gnutls_transport_set_ptr(gnutls_data->session, GINT_TO_POINTER(gsc->fd));
|
|
121
|
|
122 gnutls_data->handshake_handler = gaim_input_add(gsc->fd,
|
|
123 GAIM_INPUT_READ, ssl_gnutls_handshake_cb, gsc);
|
|
124
|
|
125 ssl_gnutls_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ);
|
|
126 }
|
|
127
|
|
128 static void
|
|
129 ssl_gnutls_close(GaimSslConnection *gsc)
|
|
130 {
|
|
131 GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
|
|
132
|
|
133 if(!gnutls_data)
|
|
134 return;
|
|
135
|
|
136 if(gnutls_data->handshake_handler)
|
|
137 gaim_input_remove(gnutls_data->handshake_handler);
|
|
138
|
|
139 gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR);
|
|
140
|
|
141 gnutls_deinit(gnutls_data->session);
|
|
142
|
|
143 g_free(gnutls_data);
|
|
144 gsc->private_data = NULL;
|
|
145 }
|
|
146
|
|
147 static size_t
|
|
148 ssl_gnutls_read(GaimSslConnection *gsc, void *data, size_t len)
|
|
149 {
|
|
150 GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
|
|
151 ssize_t s;
|
|
152
|
|
153 s = gnutls_record_recv(gnutls_data->session, data, len);
|
|
154
|
|
155 if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) {
|
|
156 s = -1;
|
|
157 errno = EAGAIN;
|
|
158 } else if(s < 0) {
|
|
159 gaim_debug_error("gnutls", "receive failed: %d\n", s);
|
|
160 s = 0;
|
|
161 }
|
|
162
|
|
163 return s;
|
|
164 }
|
|
165
|
|
166 static size_t
|
|
167 ssl_gnutls_write(GaimSslConnection *gsc, const void *data, size_t len)
|
|
168 {
|
|
169 GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
|
|
170 ssize_t s = 0;
|
|
171
|
|
172 /* XXX: when will gnutls_data be NULL? */
|
|
173 if(gnutls_data)
|
|
174 s = gnutls_record_send(gnutls_data->session, data, len);
|
|
175
|
|
176 if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) {
|
|
177 s = -1;
|
|
178 errno = EAGAIN;
|
|
179 } else if(s < 0) {
|
|
180 gaim_debug_error("gnutls", "send failed: %d\n", s);
|
|
181 s = 0;
|
|
182 }
|
|
183
|
|
184 return s;
|
|
185 }
|
|
186
|
|
187 static GaimSslOps ssl_ops =
|
|
188 {
|
|
189 ssl_gnutls_init,
|
|
190 ssl_gnutls_uninit,
|
|
191 ssl_gnutls_connect,
|
|
192 ssl_gnutls_close,
|
|
193 ssl_gnutls_read,
|
|
194 ssl_gnutls_write
|
|
195 };
|
|
196
|
|
197 #endif /* HAVE_GNUTLS */
|
|
198
|
|
199 static gboolean
|
|
200 plugin_load(GaimPlugin *plugin)
|
|
201 {
|
|
202 #ifdef HAVE_GNUTLS
|
|
203 if(!gaim_ssl_get_ops()) {
|
|
204 gaim_ssl_set_ops(&ssl_ops);
|
|
205 }
|
|
206
|
|
207 /* Init GNUTLS now so others can use it even if sslconn never does */
|
|
208 ssl_gnutls_init_gnutls();
|
|
209
|
|
210 return TRUE;
|
|
211 #else
|
|
212 return FALSE;
|
|
213 #endif
|
|
214 }
|
|
215
|
|
216 static gboolean
|
|
217 plugin_unload(GaimPlugin *plugin)
|
|
218 {
|
|
219 #ifdef HAVE_GNUTLS
|
|
220 if(gaim_ssl_get_ops() == &ssl_ops) {
|
|
221 gaim_ssl_set_ops(NULL);
|
|
222 }
|
|
223 #endif
|
|
224
|
|
225 return TRUE;
|
|
226 }
|
|
227
|
|
228 static GaimPluginInfo info =
|
|
229 {
|
|
230 GAIM_PLUGIN_MAGIC,
|
|
231 GAIM_MAJOR_VERSION,
|
|
232 GAIM_MINOR_VERSION,
|
|
233 GAIM_PLUGIN_STANDARD, /**< type */
|
|
234 NULL, /**< ui_requirement */
|
|
235 GAIM_PLUGIN_FLAG_INVISIBLE, /**< flags */
|
|
236 NULL, /**< dependencies */
|
|
237 GAIM_PRIORITY_DEFAULT, /**< priority */
|
|
238
|
|
239 SSL_GNUTLS_PLUGIN_ID, /**< id */
|
|
240 N_("GNUTLS"), /**< name */
|
|
241 VERSION, /**< version */
|
|
242 /** summary */
|
|
243 N_("Provides SSL support through GNUTLS."),
|
|
244 /** description */
|
|
245 N_("Provides SSL support through GNUTLS."),
|
|
246 "Christian Hammond <chipx86@gnupdate.org>",
|
|
247 GAIM_WEBSITE, /**< homepage */
|
|
248
|
|
249 plugin_load, /**< load */
|
|
250 plugin_unload, /**< unload */
|
|
251 NULL, /**< destroy */
|
|
252
|
|
253 NULL, /**< ui_info */
|
|
254 NULL, /**< extra_info */
|
|
255 NULL, /**< prefs_info */
|
|
256 NULL /**< actions */
|
|
257 };
|
|
258
|
|
259 static void
|
|
260 init_plugin(GaimPlugin *plugin)
|
|
261 {
|
|
262 }
|
|
263
|
|
264 GAIM_INIT_PLUGIN(ssl_gnutls, init_plugin, info)
|