ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/tls_gnutls.c
Revision: 7196
Committed: Sat Jan 30 19:42:30 2016 UTC (9 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 8962 byte(s)
Log Message:
- tls_gnutls.c: explicitly disable SSLv3 for releases prior to 3.4

File Contents

# User Rev Content
1 michael 7105 /*
2     * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3     *
4 michael 7148 * Copyright (c) 2015 Attila Molnar <attilamolnar@hush.com>
5     * Copyright (c) 2015 Adam <Adam@anope.org>
6 michael 7164 * Copyright (c) 2015-2016 ircd-hybrid development team
7 michael 7105 *
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     */
23    
24     /*! \file tls_gnutls.c
25     * \brief Includes all GnuTLS-specific TLS functions
26     * \version $Id$
27     */
28    
29     #include "stdinc.h"
30     #include "tls.h"
31     #include "conf.h"
32     #include "log.h"
33 michael 7152 #include "misc.h"
34 michael 7105 #include "memory.h"
35    
36     #ifdef HAVE_TLS_GNUTLS
37    
38     void
39     tls_init(void)
40     {
41     }
42    
43     static void
44     tls_free_cred(tls_context_t cred)
45     {
46     gnutls_priority_deinit(cred->priorities);
47     gnutls_dh_params_deinit(cred->dh_params);
48     gnutls_certificate_free_credentials(cred->x509_cred);
49    
50     gnutls_global_deinit();
51    
52     xfree(cred);
53     }
54    
55     int
56     tls_new_cred(void)
57     {
58     int ret;
59     struct gnutls_context *context;
60    
61     if (!ConfigServerInfo.ssl_certificate_file || !ConfigServerInfo.rsa_private_key_file)
62     return 0;
63    
64     context = xcalloc(sizeof(*context));
65    
66     gnutls_global_init();
67    
68     ret = gnutls_certificate_allocate_credentials(&context->x509_cred);
69 michael 7134 if (ret != GNUTLS_E_SUCCESS)
70 michael 7105 {
71 michael 7134 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the TLS credentials -- %s", gnutls_strerror(ret));
72 michael 7105 xfree(context);
73     return 0;
74     }
75    
76 michael 7193 /* TBD: set ciphers based on serverinfo::ssl_cipher_list */
77 michael 7105
78 michael 7196 gnutls_priority_init(&context->priorities, "NORMAL:%SERVER_PRECEDENCE:!VERS-SSL3.0", NULL);
79 michael 7193
80 michael 7105 ret = gnutls_certificate_set_x509_key_file(context->x509_cred, ConfigServerInfo.ssl_certificate_file, ConfigServerInfo.rsa_private_key_file, GNUTLS_X509_FMT_PEM);
81 michael 7134 if (ret != GNUTLS_E_SUCCESS)
82 michael 7105 {
83     ilog(LOG_TYPE_IRCD, "Could not set TLS keys -- %s", gnutls_strerror(ret));
84    
85     gnutls_certificate_free_credentials(context->x509_cred);
86     gnutls_priority_deinit(context->priorities);
87     xfree(context);
88     return 0;
89     }
90    
91     gnutls_dh_params_init(&context->dh_params);
92    
93     if (ConfigServerInfo.ssl_dh_param_file)
94     {
95     gnutls_datum_t data;
96    
97     ret = gnutls_load_file(ConfigServerInfo.ssl_dh_param_file, &data);
98    
99     if (ret != GNUTLS_E_SUCCESS)
100     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_dh_param_file -- unable to load file -- %s", gnutls_strerror(ret));
101     else
102     {
103     ret = gnutls_dh_params_import_pkcs3(context->dh_params, &data, GNUTLS_X509_FMT_PEM);
104    
105     if (ret != GNUTLS_E_SUCCESS)
106     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_dh_param_file -- unable to import dh params -- %s", gnutls_strerror(ret));
107    
108     gnutls_free(data.data);
109     }
110     }
111    
112     gnutls_certificate_set_dh_params(context->x509_cred, context->dh_params);
113    
114     if (ConfigServerInfo.ssl_message_digest_algorithm == NULL)
115     ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
116     else
117     {
118     ConfigServerInfo.message_digest_algorithm = gnutls_digest_get_id(ConfigServerInfo.ssl_message_digest_algorithm);
119    
120     if (ConfigServerInfo.message_digest_algorithm == GNUTLS_DIG_UNKNOWN)
121     {
122     ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
123     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_message_digest_algorithm -- unknown message digest algorithm");
124     }
125     }
126    
127     if (ConfigServerInfo.tls_ctx && --ConfigServerInfo.tls_ctx->refs == 0)
128     tls_free_cred(ConfigServerInfo.tls_ctx);
129    
130     ConfigServerInfo.tls_ctx = context;
131     ++context->refs;
132    
133     return 1;
134     }
135    
136     const char *
137     tls_get_cipher(const tls_data_t *tls_data)
138     {
139     static char buffer[IRCD_BUFSIZE];
140    
141 michael 7120 snprintf(buffer, sizeof(buffer), "%s-%s-%s-%s",
142     gnutls_protocol_get_name(gnutls_protocol_get_version(tls_data->session)),
143 michael 7105 gnutls_kx_get_name(gnutls_kx_get(tls_data->session)),
144     gnutls_cipher_get_name(gnutls_cipher_get(tls_data->session)),
145     gnutls_mac_get_name(gnutls_mac_get(tls_data->session)));
146    
147     return buffer;
148     }
149    
150     int
151     tls_isusing(tls_data_t *tls_data)
152     {
153     return tls_data->session != NULL;
154     }
155    
156     void
157     tls_free(tls_data_t *tls_data)
158     {
159     gnutls_deinit(tls_data->session);
160     }
161    
162     int
163     tls_read(tls_data_t *tls_data, char *buf, size_t bufsize, int *want_write)
164     {
165     int length = gnutls_record_recv(tls_data->session, buf, bufsize);
166    
167     if (length <= 0)
168     {
169     switch (length)
170     {
171     case GNUTLS_E_AGAIN:
172     case GNUTLS_E_INTERRUPTED:
173     errno = EWOULDBLOCK;
174 michael 7146 return -1;
175 michael 7105 case 0: /* Closed */
176     default: /* Other error */
177 michael 7146 /* XXX can gnutls_strerror(length) if <0 for gnutls's idea of the reason */
178     return 0;
179 michael 7105 }
180     }
181    
182     return length;
183     }
184    
185     int
186     tls_write(tls_data_t *tls_data, const char *buf, size_t bufsize, int *want_read)
187     {
188     int length = gnutls_record_send(tls_data->session, buf, bufsize);
189    
190     if (length <= 0)
191     {
192     switch (length)
193     {
194     case GNUTLS_E_AGAIN:
195     case GNUTLS_E_INTERRUPTED:
196     case 0:
197     errno = EWOULDBLOCK;
198 michael 7146 return -1;
199 michael 7105 default:
200 michael 7146 return 0;
201 michael 7105 }
202     }
203    
204     return length;
205     }
206    
207     void
208     tls_shutdown(tls_data_t *tls_data)
209     {
210     gnutls_bye(tls_data->session, GNUTLS_SHUT_WR);
211    
212     if (--tls_data->context->refs == 0)
213     tls_free_cred(tls_data->context);
214     }
215    
216     int
217     tls_new(tls_data_t *tls_data, int fd, tls_role_t role)
218     {
219     gnutls_init(&tls_data->session, role == TLS_ROLE_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
220    
221     tls_data->context = ConfigServerInfo.tls_ctx;
222     ++tls_data->context->refs;
223    
224     gnutls_priority_set(tls_data->session, tls_data->context->priorities);
225     gnutls_credentials_set(tls_data->session, GNUTLS_CRD_CERTIFICATE, tls_data->context->x509_cred);
226     gnutls_dh_set_prime_bits(tls_data->session, 1024);
227     gnutls_transport_set_int(tls_data->session, fd);
228    
229     if (role == TLS_ROLE_SERVER)
230     /* Request client certificate if any. */
231     gnutls_certificate_server_set_request(tls_data->session, GNUTLS_CERT_REQUEST);
232    
233     return 1;
234     }
235    
236     int
237     tls_set_ciphers(tls_data_t *tls_data, const char *cipher_list)
238     {
239     int ret;
240     const char *prioerror;
241    
242     gnutls_priority_deinit(tls_data->context->priorities);
243    
244     ret = gnutls_priority_init(&tls_data->context->priorities, cipher_list, &prioerror);
245 michael 7134 if (ret != GNUTLS_E_SUCCESS)
246 michael 7105 {
247     /* GnuTLS did not understand the user supplied string, log and fall back to the default priorities */
248 michael 7196 ilog(LOG_TYPE_IRCD, "Failed to set GnuTLS priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL:%%SERVER_PRECEDENCE:!VERS-SSL3.0)",
249 michael 7105 cipher_list, gnutls_strerror(ret), (unsigned int)(prioerror - cipher_list));
250 michael 7196 gnutls_priority_init(&tls_data->context->priorities, "NORMAL:%SERVER_PRECEDENCE:!VERS-SSL3.0", NULL);
251 michael 7105 return 0;
252     }
253    
254     return 1;
255     }
256    
257     tls_handshake_status_t
258     tls_handshake(tls_data_t *tls_data, tls_role_t role, const char **errstr)
259     {
260     int ret = gnutls_handshake(tls_data->session);
261    
262     if (ret >= 0)
263     return TLS_HANDSHAKE_DONE;
264    
265     if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
266     {
267     /* Handshake needs resuming later, read() or write() would have blocked. */
268    
269     if (gnutls_record_get_direction(tls_data->session) == 0)
270     {
271     /* gnutls_handshake() wants to read() again. */
272     return TLS_HANDSHAKE_WANT_READ;
273     }
274     else
275     {
276     /* gnutls_handshake() wants to write() again. */
277     return TLS_HANDSHAKE_WANT_WRITE;
278     }
279     }
280     else
281     {
282     const char *error = gnutls_strerror(ret);
283    
284     if (errstr)
285     *errstr = error;
286    
287     return TLS_HANDSHAKE_ERROR;
288     }
289     }
290    
291     int
292 michael 7142 tls_verify_cert(tls_data_t *tls_data, tls_md_t digest, char **fingerprint)
293 michael 7105 {
294     int ret;
295     gnutls_x509_crt_t cert;
296     const gnutls_datum_t *cert_list;
297     unsigned int cert_list_size = 0;
298 michael 7134 unsigned char digestbuf[TLS_GNUTLS_MAX_HASH_SIZE];
299 michael 7105 size_t digest_size = sizeof(digestbuf);
300 michael 7134 char buf[TLS_GNUTLS_MAX_HASH_SIZE * 2 + 1];
301 michael 7105
302     cert_list = gnutls_certificate_get_peers(tls_data->session, &cert_list_size);
303     if (!cert_list)
304     return 1; /* No certificate */
305    
306     ret = gnutls_x509_crt_init(&cert);
307 michael 7134 if (ret != GNUTLS_E_SUCCESS)
308 michael 7105 return 1;
309    
310     ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
311 michael 7134 if (ret != GNUTLS_E_SUCCESS)
312 michael 7105 goto info_done_dealloc;
313    
314     ret = gnutls_x509_crt_get_fingerprint(cert, digest, digestbuf, &digest_size);
315 michael 7134 if (ret != GNUTLS_E_SUCCESS)
316 michael 7105 goto info_done_dealloc;
317    
318     binary_to_hex(digestbuf, buf, digest_size);
319     *fingerprint = xstrdup(buf);
320    
321 michael 7145 gnutls_x509_crt_deinit(cert);
322 michael 7105 return 1;
323    
324     info_done_dealloc:
325     gnutls_x509_crt_deinit(cert);
326     return 0;
327     }
328     #endif /* HAVE_TLS_GNUTLS */

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision