]>
Commit | Line | Data |
---|---|---|
fc8711d1 AC |
1 | /* |
2 | * libratbox: a library used by ircd-ratbox and other things | |
3 | * gnutls.c: gnutls related code | |
4 | * | |
5 | * Copyright (C) 2007-2008 ircd-ratbox development team | |
6 | * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org> | |
fc8711d1 AC |
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |
21 | * USA | |
22 | * | |
3202e249 | 23 | * $Id: gnutls.c 26092 2008-09-19 15:13:52Z androsyn $ |
fc8711d1 AC |
24 | */ |
25 | ||
26 | #include <libratbox_config.h> | |
27 | #include <ratbox_lib.h> | |
fc8711d1 AC |
28 | #include <commio-int.h> |
29 | #include <commio-ssl.h> | |
c2ac22cc VY |
30 | #ifdef HAVE_GNUTLS |
31 | ||
fc8711d1 | 32 | #include <gnutls/gnutls.h> |
c2ac22cc VY |
33 | #include <gcrypt.h> |
34 | ||
35 | static gnutls_certificate_credentials x509; | |
36 | static gnutls_dh_params dh_params; | |
fc8711d1 | 37 | |
c2ac22cc VY |
38 | |
39 | ||
40 | #define SSL_P(x) *((gnutls_session_t *)F->ssl) | |
fc8711d1 AC |
41 | |
42 | void | |
3202e249 | 43 | rb_ssl_shutdown(rb_fde_t *F) |
fc8711d1 | 44 | { |
c2ac22cc | 45 | int i; |
fc8711d1 AC |
46 | if(F == NULL || F->ssl == NULL) |
47 | return; | |
3202e249 | 48 | for(i = 0; i < 4; i++) |
c2ac22cc VY |
49 | { |
50 | if(gnutls_bye(SSL_P(F), GNUTLS_SHUT_RDWR) == GNUTLS_E_SUCCESS) | |
51 | break; | |
52 | } | |
53 | gnutls_deinit(SSL_P(F)); | |
54 | rb_free(F->ssl); | |
55 | } | |
fc8711d1 | 56 | |
c2ac22cc VY |
57 | unsigned int |
58 | rb_ssl_handshake_count(rb_fde_t *F) | |
59 | { | |
60 | return F->handshake_count; | |
61 | } | |
62 | ||
63 | void | |
64 | rb_ssl_clear_handshake_count(rb_fde_t *F) | |
65 | { | |
66 | F->handshake_count = 0; | |
fc8711d1 AC |
67 | } |
68 | ||
69 | static void | |
3202e249 | 70 | rb_ssl_timeout(rb_fde_t *F, void *notused) |
fc8711d1 AC |
71 | { |
72 | lrb_assert(F->accept != NULL); | |
73 | F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); | |
74 | } | |
75 | ||
c2ac22cc | 76 | |
3202e249 VY |
77 | static int |
78 | do_ssl_handshake(rb_fde_t *F, PF * callback) | |
c2ac22cc VY |
79 | { |
80 | int ret; | |
3202e249 | 81 | int flags; |
c2ac22cc VY |
82 | |
83 | ret = gnutls_handshake(SSL_P(F)); | |
84 | if(ret < 0) | |
85 | { | |
86 | if((ret == GNUTLS_E_INTERRUPTED && rb_ignore_errno(errno)) || ret == GNUTLS_E_AGAIN) | |
87 | { | |
88 | if(gnutls_record_get_direction(SSL_P(F)) == 0) | |
89 | flags = RB_SELECT_READ; | |
90 | else | |
91 | flags = RB_SELECT_WRITE; | |
3202e249 | 92 | rb_setselect(F, flags, callback, NULL); |
c2ac22cc VY |
93 | return 0; |
94 | } | |
95 | F->ssl_errno = ret; | |
96 | return -1; | |
3202e249 VY |
97 | } |
98 | return 1; /* handshake is finished..go about life */ | |
c2ac22cc VY |
99 | } |
100 | ||
fc8711d1 | 101 | static void |
3202e249 | 102 | rb_ssl_tryaccept(rb_fde_t *F, void *data) |
fc8711d1 | 103 | { |
c2ac22cc | 104 | int ret; |
fc8711d1 AC |
105 | struct acceptdata *ad; |
106 | ||
c2ac22cc | 107 | lrb_assert(F->accept != NULL); |
fc8711d1 | 108 | |
c2ac22cc | 109 | ret = do_ssl_handshake(F, rb_ssl_tryaccept); |
3202e249 VY |
110 | |
111 | switch (ret) | |
c2ac22cc | 112 | { |
3202e249 VY |
113 | case -1: |
114 | F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); | |
115 | break; | |
116 | case 0: | |
117 | /* do_ssl_handshake does the rb_setselect stuff */ | |
118 | return; | |
119 | default: | |
120 | break; | |
121 | ||
122 | ||
fc8711d1 AC |
123 | } |
124 | rb_settimeout(F, 0, NULL, NULL); | |
125 | rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); | |
3202e249 | 126 | |
fc8711d1 AC |
127 | ad = F->accept; |
128 | F->accept = NULL; | |
3202e249 | 129 | ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); |
fc8711d1 | 130 | rb_free(ad); |
c2ac22cc | 131 | |
fc8711d1 AC |
132 | } |
133 | ||
134 | void | |
3202e249 | 135 | rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout) |
fc8711d1 | 136 | { |
c2ac22cc | 137 | gnutls_session_t *ssl; |
fc8711d1 | 138 | new_F->type |= RB_FD_SSL; |
c2ac22cc | 139 | ssl = new_F->ssl = rb_malloc(sizeof(gnutls_session_t)); |
fc8711d1 | 140 | new_F->accept = rb_malloc(sizeof(struct acceptdata)); |
3202e249 | 141 | |
fc8711d1 AC |
142 | new_F->accept->callback = cb; |
143 | new_F->accept->data = data; | |
144 | rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); | |
145 | ||
146 | new_F->accept->addrlen = 0; | |
147 | ||
3202e249 | 148 | gnutls_init(ssl, GNUTLS_SERVER); |
c2ac22cc VY |
149 | gnutls_set_default_priority(*ssl); |
150 | gnutls_credentials_set(*ssl, GNUTLS_CRD_CERTIFICATE, x509); | |
151 | gnutls_dh_set_prime_bits(*ssl, 1024); | |
152 | gnutls_transport_set_ptr(*ssl, (gnutls_transport_ptr_t) (long int)new_F->fd); | |
153 | if(do_ssl_handshake(new_F, rb_ssl_tryaccept)) | |
fc8711d1 | 154 | { |
c2ac22cc | 155 | struct acceptdata *ad = new_F->accept; |
fc8711d1 | 156 | new_F->accept = NULL; |
3202e249 VY |
157 | ad->callback(new_F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); |
158 | rb_free(ad); | |
fc8711d1 | 159 | } |
c2ac22cc | 160 | |
fc8711d1 AC |
161 | } |
162 | ||
c2ac22cc VY |
163 | |
164 | ||
165 | ||
fc8711d1 | 166 | void |
3202e249 | 167 | rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen) |
fc8711d1 | 168 | { |
fc8711d1 | 169 | new_F->type |= RB_FD_SSL; |
c2ac22cc | 170 | new_F->ssl = rb_malloc(sizeof(gnutls_session_t)); |
fc8711d1 AC |
171 | new_F->accept = rb_malloc(sizeof(struct acceptdata)); |
172 | ||
173 | new_F->accept->callback = F->accept->callback; | |
174 | new_F->accept->data = F->accept->data; | |
175 | rb_settimeout(new_F, 10, rb_ssl_timeout, NULL); | |
176 | memcpy(&new_F->accept->S, st, addrlen); | |
177 | new_F->accept->addrlen = addrlen; | |
178 | ||
3202e249 | 179 | gnutls_init((gnutls_session_t *) new_F->ssl, GNUTLS_SERVER); |
c2ac22cc VY |
180 | gnutls_set_default_priority(SSL_P(new_F)); |
181 | gnutls_credentials_set(SSL_P(new_F), GNUTLS_CRD_CERTIFICATE, x509); | |
182 | gnutls_dh_set_prime_bits(SSL_P(new_F), 1024); | |
a9fb3ed0 | 183 | gnutls_transport_set_ptr(SSL_P(new_F), (gnutls_transport_ptr_t) (long int)rb_get_fd(new_F)); |
c2ac22cc | 184 | if(do_ssl_handshake(F, rb_ssl_tryaccept)) |
fc8711d1 | 185 | { |
c2ac22cc VY |
186 | struct acceptdata *ad = F->accept; |
187 | F->accept = NULL; | |
3202e249 VY |
188 | ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); |
189 | rb_free(ad); | |
c2ac22cc VY |
190 | } |
191 | } | |
fc8711d1 | 192 | |
fc8711d1 | 193 | |
fc8711d1 | 194 | |
fc8711d1 AC |
195 | |
196 | static ssize_t | |
3202e249 | 197 | rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count) |
fc8711d1 AC |
198 | { |
199 | ssize_t ret; | |
c2ac22cc | 200 | gnutls_session_t *ssl = F->ssl; |
fc8711d1 AC |
201 | |
202 | if(r_or_w == 0) | |
c2ac22cc | 203 | ret = gnutls_record_recv(*ssl, rbuf, count); |
fc8711d1 | 204 | else |
c2ac22cc | 205 | ret = gnutls_record_send(*ssl, wbuf, count); |
fc8711d1 AC |
206 | |
207 | if(ret < 0) | |
208 | { | |
3202e249 | 209 | switch (ret) |
fc8711d1 AC |
210 | { |
211 | case GNUTLS_E_AGAIN: | |
fc8711d1 | 212 | case GNUTLS_E_INTERRUPTED: |
c2ac22cc | 213 | if(rb_ignore_errno(errno)) |
fc8711d1 | 214 | { |
c2ac22cc VY |
215 | if(gnutls_record_get_direction(*ssl) == 0) |
216 | return RB_RW_SSL_NEED_READ; | |
217 | else | |
218 | return RB_RW_SSL_NEED_WRITE; | |
219 | break; | |
fc8711d1 | 220 | } |
fc8711d1 | 221 | default: |
c2ac22cc VY |
222 | F->ssl_errno = ret; |
223 | errno = EIO; | |
224 | return RB_RW_IO_ERROR; | |
fc8711d1 | 225 | } |
fc8711d1 AC |
226 | } |
227 | return ret; | |
228 | } | |
229 | ||
230 | ssize_t | |
3202e249 | 231 | rb_ssl_read(rb_fde_t *F, void *buf, size_t count) |
fc8711d1 AC |
232 | { |
233 | return rb_ssl_read_or_write(0, F, buf, NULL, count); | |
234 | } | |
235 | ||
236 | ssize_t | |
3202e249 | 237 | rb_ssl_write(rb_fde_t *F, const void *buf, size_t count) |
fc8711d1 AC |
238 | { |
239 | return rb_ssl_read_or_write(1, F, NULL, buf, count); | |
240 | } | |
241 | ||
c2ac22cc VY |
242 | static void |
243 | rb_gcry_random_seed(void *unused) | |
244 | { | |
245 | gcry_fast_random_poll(); | |
246 | } | |
247 | ||
fc8711d1 AC |
248 | int |
249 | rb_init_ssl(void) | |
250 | { | |
fc8711d1 | 251 | gnutls_global_init(); |
3202e249 | 252 | |
c2ac22cc | 253 | if(gnutls_certificate_allocate_credentials(&x509) != GNUTLS_E_SUCCESS) |
fc8711d1 | 254 | { |
c2ac22cc VY |
255 | rb_lib_log("rb_init_ssl: Unable to allocate SSL/TLS certificate credentials"); |
256 | return 0; | |
fc8711d1 | 257 | } |
c2ac22cc VY |
258 | rb_event_addish("rb_gcry_random_seed", rb_gcry_random_seed, NULL, 300); |
259 | return 1; | |
260 | } | |
fc8711d1 | 261 | |
c2ac22cc | 262 | static void |
3202e249 | 263 | rb_free_datum_t(gnutls_datum_t * d) |
c2ac22cc VY |
264 | { |
265 | rb_free(d->data); | |
266 | rb_free(d); | |
267 | } | |
fc8711d1 | 268 | |
c2ac22cc VY |
269 | static gnutls_datum_t * |
270 | rb_load_file_into_datum_t(const char *file) | |
271 | { | |
272 | FILE *f; | |
273 | gnutls_datum_t *datum; | |
274 | struct stat fileinfo; | |
275 | if((f = fopen(file, "r")) == NULL) | |
276 | return NULL; | |
277 | if(fstat(fileno(f), &fileinfo)) | |
278 | return NULL; | |
279 | ||
280 | datum = rb_malloc(sizeof(gnutls_datum_t)); | |
281 | ||
3202e249 | 282 | if(fileinfo.st_size > 131072) /* deal with retards */ |
c2ac22cc VY |
283 | datum->size = 131072; |
284 | else | |
3202e249 VY |
285 | datum->size = fileinfo.st_size; |
286 | ||
c2ac22cc VY |
287 | datum->data = rb_malloc(datum->size + 1); |
288 | fread(datum->data, datum->size, 1, f); | |
289 | fclose(f); | |
3202e249 | 290 | return datum; |
fc8711d1 AC |
291 | } |
292 | ||
293 | int | |
294 | rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile) | |
295 | { | |
c2ac22cc VY |
296 | int ret; |
297 | gnutls_datum_t *d_cert, *d_key; | |
298 | if(cert == NULL) | |
299 | { | |
300 | rb_lib_log("rb_setup_ssl_server: No certificate file"); | |
301 | return 0; | |
302 | } | |
fc8711d1 | 303 | |
c2ac22cc | 304 | if((d_cert = rb_load_file_into_datum_t(cert)) == NULL) |
fc8711d1 | 305 | { |
c2ac22cc | 306 | rb_lib_log("rb_setup_ssl_server: Error loading certificate: %s", strerror(errno)); |
fc8711d1 AC |
307 | return 0; |
308 | } | |
309 | ||
c2ac22cc VY |
310 | if((d_key = rb_load_file_into_datum_t(keyfile)) == NULL) |
311 | { | |
312 | rb_lib_log("rb_setup_ssl_server: Error loading key: %s", strerror(errno)); | |
313 | return 0; | |
314 | } | |
3202e249 VY |
315 | |
316 | ||
317 | if((ret = | |
318 | gnutls_certificate_set_x509_key_mem(x509, d_cert, d_key, | |
319 | GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) | |
c2ac22cc | 320 | { |
3202e249 VY |
321 | rb_lib_log("rb_setup_ssl_server: Error loading certificate or key file: %s", |
322 | gnutls_strerror(ret)); | |
c2ac22cc VY |
323 | return 0; |
324 | } | |
325 | rb_free_datum_t(d_cert); | |
326 | rb_free_datum_t(d_key); | |
3202e249 | 327 | |
c2ac22cc VY |
328 | if(dhfile != NULL) |
329 | { | |
330 | if(gnutls_dh_params_init(&dh_params) == GNUTLS_E_SUCCESS) | |
331 | { | |
332 | gnutls_datum_t *data; | |
333 | int xret; | |
334 | data = rb_load_file_into_datum_t(dhfile); | |
335 | if(data != NULL) | |
336 | { | |
3202e249 VY |
337 | xret = gnutls_dh_params_import_pkcs3(dh_params, data, |
338 | GNUTLS_X509_FMT_PEM); | |
c2ac22cc | 339 | if(xret < 0) |
3202e249 VY |
340 | rb_lib_log |
341 | ("rb_setup_ssl_server: Error parsing DH file: %s\n", | |
342 | gnutls_strerror(xret)); | |
c2ac22cc VY |
343 | rb_free_datum_t(data); |
344 | } | |
345 | gnutls_certificate_set_dh_params(x509, dh_params); | |
3202e249 VY |
346 | } |
347 | else | |
c2ac22cc VY |
348 | rb_lib_log("rb_setup_ssl_server: Unable to setup DH parameters"); |
349 | } | |
fc8711d1 AC |
350 | return 1; |
351 | } | |
352 | ||
353 | int | |
3202e249 | 354 | rb_ssl_listen(rb_fde_t *F, int backlog) |
fc8711d1 AC |
355 | { |
356 | F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; | |
357 | return listen(F->fd, backlog); | |
358 | } | |
359 | ||
360 | struct ssl_connect | |
361 | { | |
362 | CNCB *callback; | |
363 | void *data; | |
364 | int timeout; | |
365 | }; | |
366 | ||
367 | static void | |
3202e249 | 368 | rb_ssl_connect_realcb(rb_fde_t *F, int status, struct ssl_connect *sconn) |
fc8711d1 AC |
369 | { |
370 | F->connect->callback = sconn->callback; | |
371 | F->connect->data = sconn->data; | |
372 | rb_free(sconn); | |
373 | rb_connect_callback(F, status); | |
374 | } | |
375 | ||
376 | static void | |
3202e249 | 377 | rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data) |
fc8711d1 AC |
378 | { |
379 | rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); | |
380 | } | |
381 | ||
382 | static void | |
3202e249 | 383 | rb_ssl_tryconn_cb(rb_fde_t *F, void *data) |
fc8711d1 AC |
384 | { |
385 | struct ssl_connect *sconn = data; | |
c2ac22cc VY |
386 | int ret; |
387 | ||
3202e249 VY |
388 | ret = do_ssl_handshake(F, rb_ssl_tryconn_cb); |
389 | ||
390 | switch (ret) | |
391 | { | |
392 | case -1: | |
393 | rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); | |
394 | break; | |
395 | case 0: | |
396 | /* do_ssl_handshake does the rb_setselect stuff */ | |
397 | return; | |
398 | default: | |
399 | break; | |
400 | ||
401 | ||
402 | } | |
c2ac22cc | 403 | rb_ssl_connect_realcb(F, RB_OK, sconn); |
fc8711d1 AC |
404 | } |
405 | ||
406 | static void | |
3202e249 | 407 | rb_ssl_tryconn(rb_fde_t *F, int status, void *data) |
fc8711d1 | 408 | { |
fc8711d1 | 409 | struct ssl_connect *sconn = data; |
fc8711d1 AC |
410 | if(status != RB_OK) |
411 | { | |
412 | rb_ssl_connect_realcb(F, status, sconn); | |
413 | return; | |
414 | } | |
415 | ||
416 | F->type |= RB_FD_SSL; | |
417 | ||
3202e249 | 418 | |
fc8711d1 | 419 | rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); |
c2ac22cc | 420 | F->ssl = rb_malloc(sizeof(gnutls_session_t)); |
3202e249 VY |
421 | gnutls_init(F->ssl, GNUTLS_CLIENT); |
422 | gnutls_set_default_priority(SSL_P(F)); | |
c2ac22cc VY |
423 | gnutls_dh_set_prime_bits(SSL_P(F), 1024); |
424 | gnutls_transport_set_ptr(SSL_P(F), (gnutls_transport_ptr_t) (long int)F->fd); | |
425 | ||
426 | if(do_ssl_handshake(F, rb_ssl_tryconn_cb)) | |
fc8711d1 AC |
427 | { |
428 | rb_ssl_connect_realcb(F, RB_OK, sconn); | |
429 | } | |
430 | } | |
431 | ||
432 | void | |
3202e249 | 433 | rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest, |
fc8711d1 AC |
434 | struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout) |
435 | { | |
436 | struct ssl_connect *sconn; | |
437 | if(F == NULL) | |
438 | return; | |
439 | ||
440 | sconn = rb_malloc(sizeof(struct ssl_connect)); | |
441 | sconn->data = data; | |
442 | sconn->callback = callback; | |
443 | sconn->timeout = timeout; | |
444 | rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout); | |
c2ac22cc | 445 | |
fc8711d1 AC |
446 | } |
447 | ||
448 | void | |
3202e249 | 449 | rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout) |
fc8711d1 | 450 | { |
fc8711d1 | 451 | struct ssl_connect *sconn; |
fc8711d1 AC |
452 | if(F == NULL) |
453 | return; | |
454 | ||
455 | sconn = rb_malloc(sizeof(struct ssl_connect)); | |
456 | sconn->data = data; | |
457 | sconn->callback = callback; | |
458 | sconn->timeout = timeout; | |
459 | F->connect = rb_malloc(sizeof(struct conndata)); | |
460 | F->connect->callback = callback; | |
461 | F->connect->data = data; | |
462 | F->type |= RB_FD_SSL; | |
c2ac22cc | 463 | F->ssl = rb_malloc(sizeof(gnutls_session_t)); |
3202e249 VY |
464 | |
465 | gnutls_init(F->ssl, GNUTLS_CLIENT); | |
466 | gnutls_set_default_priority(SSL_P(F)); | |
c2ac22cc VY |
467 | gnutls_dh_set_prime_bits(SSL_P(F), 1024); |
468 | gnutls_transport_set_ptr(SSL_P(F), (gnutls_transport_ptr_t) (long int)F->fd); | |
3202e249 | 469 | |
fc8711d1 | 470 | rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); |
c2ac22cc VY |
471 | |
472 | if(do_ssl_handshake(F, rb_ssl_tryconn_cb)) | |
fc8711d1 AC |
473 | { |
474 | rb_ssl_connect_realcb(F, RB_OK, sconn); | |
475 | } | |
476 | } | |
477 | ||
fc8711d1 AC |
478 | int |
479 | rb_init_prng(const char *path, prng_seed_t seed_type) | |
480 | { | |
c2ac22cc VY |
481 | gcry_fast_random_poll(); |
482 | return 1; | |
fc8711d1 AC |
483 | } |
484 | ||
485 | int | |
486 | rb_get_random(void *buf, size_t length) | |
487 | { | |
c2ac22cc VY |
488 | gcry_randomize(buf, length, GCRY_STRONG_RANDOM); |
489 | return 1; | |
fc8711d1 AC |
490 | } |
491 | ||
a9fb3ed0 VY |
492 | int |
493 | rb_get_pseudo_random(void *buf, size_t length) | |
494 | { | |
495 | gcry_randomize(buf, length, GCRY_WEAK_RANDOM); | |
496 | return 1; | |
497 | } | |
fc8711d1 AC |
498 | |
499 | const char * | |
3202e249 | 500 | rb_get_ssl_strerror(rb_fde_t *F) |
fc8711d1 AC |
501 | { |
502 | return gnutls_strerror(F->ssl_errno); | |
503 | } | |
504 | ||
505 | int | |
506 | rb_supports_ssl(void) | |
507 | { | |
508 | return 1; | |
509 | } | |
510 | ||
511 | #endif /* HAVE_GNUTLS */ |