comparison src/sslconn.c @ 6703:36897b9e009f

[gaim-migrate @ 7229] Forgot the SSL wrapper code. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Tue, 02 Sep 2003 04:43:28 +0000
parents
children b0913ab92893
comparison
equal deleted inserted replaced
6702:302ee2792e91 6703:36897b9e009f
1 /**
2 * @file sslconn.c SSL API
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23 #include "internal.h"
24
25 #include "debug.h"
26 #include "sslconn.h"
27
28 #ifdef HAVE_NSS
29 # include <nspr.h>
30 # include <nss.h>
31 # include <pk11func.h>
32 # include <prio.h>
33 # include <secerr.h>
34 # include <secmod.h>
35 # include <ssl.h>
36 # include <sslerr.h>
37 # include <sslproto.h>
38
39 typedef struct
40 {
41 char *host;
42 int port;
43 void *user_data;
44 GaimSslInputFunction input_func;
45
46 int fd;
47 int inpa;
48
49 PRFileDesc *nss_fd;
50 PRFileDesc *nss_in;
51
52 } GaimSslData;
53
54 static gboolean _nss_initialized = FALSE;
55 static const PRIOMethods *_nss_methods = NULL;
56 static PRDescIdentity _identity;
57
58 static void
59 destroy_ssl_data(GaimSslData *data)
60 {
61 if (data->inpa) gaim_input_remove(data->inpa);
62 if (data->nss_in) PR_Close(data->nss_in);
63 if (data->nss_fd) PR_Close(data->nss_fd);
64 if (data->fd) close(data->fd);
65
66 if (data->host != NULL)
67 g_free(data->host);
68
69 g_free(data);
70 }
71
72 static void
73 init_nss(void)
74 {
75 if (_nss_initialized)
76 return;
77
78 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
79 NSS_NoDB_Init(NULL);
80
81 /* TODO: Fix this so autoconf does the work trying to find this lib. */
82 SECMOD_AddNewModule("Builtins", LIBDIR "/libnssckbi.so", 0, 0);
83 NSS_SetDomesticPolicy();
84
85 _identity = PR_GetUniqueIdentity("Gaim");
86 _nss_methods = PR_GetDefaultIOMethods();
87
88 _nss_initialized = TRUE;
89 }
90
91 static SECStatus
92 ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig, PRBool is_server)
93 {
94 return SECSuccess;
95
96 #if 0
97 CERTCertificate *cert;
98 void *pinArg;
99 SECStatus status;
100
101 cert = SSL_PeerCertificate(socket);
102 pinArg = SSL_RevealPinArg(socket);
103
104 status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
105 certUsageSSLClient, pinArg);
106
107 if (status != SECSuccess) {
108 gaim_debug(GAIM_DEBUG_ERROR, "msn", "CERT_VerifyCertNow failed\n");
109 CERT_DestroyCertificate(cert);
110 return status;
111 }
112
113 CERT_DestroyCertificate(cert);
114 return SECSuccess;
115 #endif
116 }
117
118 SECStatus
119 ssl_bad_cert(void *arg, PRFileDesc *socket)
120 {
121 SECStatus status = SECFailure;
122 PRErrorCode err;
123
124 if (arg == NULL)
125 return status;
126
127 *(PRErrorCode *)arg = err = PORT_GetError();
128
129 switch (err)
130 {
131 case SEC_ERROR_INVALID_AVA:
132 case SEC_ERROR_INVALID_TIME:
133 case SEC_ERROR_BAD_SIGNATURE:
134 case SEC_ERROR_EXPIRED_CERTIFICATE:
135 case SEC_ERROR_UNKNOWN_ISSUER:
136 case SEC_ERROR_UNTRUSTED_CERT:
137 case SEC_ERROR_CERT_VALID:
138 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
139 case SEC_ERROR_CRL_EXPIRED:
140 case SEC_ERROR_CRL_BAD_SIGNATURE:
141 case SEC_ERROR_EXTENSION_VALUE_INVALID:
142 case SEC_ERROR_CA_CERT_INVALID:
143 case SEC_ERROR_CERT_USAGES_INVALID:
144 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
145 status = SECSuccess;
146 break;
147
148 default:
149 status = SECFailure;
150 break;
151 }
152
153 gaim_debug(GAIM_DEBUG_ERROR, "msn",
154 "Bad certificate: %d\n");
155
156 return status;
157 }
158
159 static void
160 input_func(gpointer data, gint source, GaimInputCondition cond)
161 {
162 GaimSslData *ssl_data = (GaimSslData *)data;
163 char *cp, *ip, *sp;
164 int op, kp0, kp1;
165 int result;
166
167 result = SSL_SecurityStatus(ssl_data->nss_in, &op, &cp, &kp0,
168 &kp1, &ip, &sp);
169
170 gaim_debug(GAIM_DEBUG_MISC, "msn",
171 "bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
172 "subject DN: %s\n"
173 "issuer DN: %s\n",
174 cp, kp1, kp0, op, sp, ip);
175
176 PR_Free(cp);
177 PR_Free(ip);
178 PR_Free(sp);
179
180 ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
181 cond);
182 }
183
184 static void
185 ssl_connect_cb(gpointer data, gint source, GaimInputCondition cond)
186 {
187 PRSocketOptionData socket_opt;
188 GaimSslData *ssl_data = (GaimSslData *)data;
189
190 if (!_nss_initialized)
191 init_nss();
192
193 ssl_data->fd = source;
194
195 ssl_data->nss_fd = PR_ImportTCPSocket(ssl_data->fd);
196
197 if (ssl_data->nss_fd == NULL)
198 {
199 gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_fd == NULL!\n");
200
201 destroy_ssl_data(ssl_data);
202
203 return;
204 }
205
206 socket_opt.option = PR_SockOpt_Nonblocking;
207 socket_opt.value.non_blocking = PR_FALSE;
208
209 PR_SetSocketOption(ssl_data->nss_fd, &socket_opt);
210
211 ssl_data->nss_in = SSL_ImportFD(NULL, ssl_data->nss_fd);
212
213 if (ssl_data->nss_in == NULL)
214 {
215 gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_in == NUL!\n");
216
217 destroy_ssl_data(ssl_data);
218
219 return;
220 }
221
222 SSL_OptionSet(ssl_data->nss_in, SSL_SECURITY, PR_TRUE);
223 SSL_OptionSet(ssl_data->nss_in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
224
225 SSL_AuthCertificateHook(ssl_data->nss_in,
226 (SSLAuthCertificate)ssl_auth_cert,
227 (void *)CERT_GetDefaultCertDB());
228 SSL_BadCertHook(ssl_data->nss_in, (SSLBadCertHandler)ssl_bad_cert, NULL);
229
230 SSL_SetURL(ssl_data->nss_in, ssl_data->host);
231
232 SSL_ResetHandshake(ssl_data->nss_in, PR_FALSE);
233
234 if (SSL_ForceHandshake(ssl_data->nss_in))
235 {
236 gaim_debug(GAIM_DEBUG_ERROR, "ssl", "Handshake failed\n");
237
238 destroy_ssl_data(ssl_data);
239
240 return;
241 }
242
243 #if 0
244 ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
245 cond);
246 #endif
247
248 input_func(ssl_data, source, cond);
249 }
250 #endif /* HAVE_NSS */
251
252 gboolean
253 gaim_ssl_is_supported(void)
254 {
255 #ifdef HAVE_NSS
256 return TRUE;
257 #else
258 return FALSE;
259 #endif
260 }
261
262 GaimSslConnection *
263 gaim_ssl_connect(GaimAccount *account, const char *host, int port,
264 GaimSslInputFunction func, void *data)
265 {
266 #ifdef HAVE_NSS
267 int i;
268 GaimSslData *ssl_data;
269
270 g_return_val_if_fail(host != NULL, NULL);
271 g_return_val_if_fail(port != 0 && port != -1, NULL);
272 g_return_val_if_fail(func != NULL, NULL);
273 g_return_val_if_fail(gaim_ssl_is_supported(), NULL);
274
275 ssl_data = g_new0(GaimSslData, 1);
276
277 ssl_data->host = g_strdup(host);
278 ssl_data->port = port;
279 ssl_data->user_data = data;
280 ssl_data->input_func = func;
281
282 i = gaim_proxy_connect(account, host, port, ssl_connect_cb, ssl_data);
283
284 if (i < 0)
285 {
286 g_free(ssl_data->host);
287 g_free(ssl_data);
288
289 return NULL;
290 }
291
292 return (GaimSslConnection)ssl_data;
293 #else
294 g_return_val_if_fail(gaim_ssl_is_supported(), -1);
295 #endif
296 }
297
298 void
299 gaim_ssl_close(GaimSslConnection *gsc)
300 {
301 g_return_if_fail(gsc != NULL);
302
303 #ifdef HAVE_NSS
304 destroy_ssl_data((GaimSslData *)gsc);
305 #endif
306 }
307
308 size_t
309 gaim_ssl_read(GaimSslConnection *gsc, void *data, size_t len)
310 {
311 #ifdef HAVE_NSS
312 GaimSslData *ssl_data = (GaimSslData *)gsc;
313
314 g_return_val_if_fail(gsc != NULL, 0);
315 g_return_val_if_fail(data != NULL, 0);
316 g_return_val_if_fail(len > 0, 0);
317
318 return PR_Read(ssl_data->nss_in, data, len);
319 #else
320 return 0;
321 #endif
322 }
323
324 size_t
325 gaim_ssl_write(GaimSslConnection *gsc, const void *data, size_t len)
326 {
327 #ifdef HAVE_NSS
328 GaimSslData *ssl_data = (GaimSslData *)gsc;
329
330 g_return_val_if_fail(gsc != NULL, 0);
331 g_return_val_if_fail(data != NULL, 0);
332 g_return_val_if_fail(len > 0, 0);
333
334 return PR_Write(ssl_data->nss_in, data, len);
335 #else
336 return 0;
337 #endif
338 }
339
340 void
341 gaim_ssl_init(void)
342 {
343 }
344
345 void
346 gaim_ssl_uninit(void)
347 {
348 if (!_nss_initialized)
349 return;
350
351 #ifdef HAVE_NSS
352 PR_Cleanup();
353 #endif
354
355 _nss_initialized = FALSE;
356 _nss_methods = NULL;
357 }